diff --git a/playground/index.ts b/playground/index.ts index da1a9c3b..b3ad7447 100644 --- a/playground/index.ts +++ b/playground/index.ts @@ -1,7 +1,9 @@ import { $fetch } from '../src/node' async function main () { - const r = await $fetch('http://google.com/404') + // const r = await $fetch('http://google.com/404') + const r = await $fetch('http://httpstat.us/500') + // const r = await $fetch('http://httpstat/500') // eslint-disable-next-line no-console console.log(r) } diff --git a/src/error.ts b/src/error.ts index fb77c053..58d1715d 100644 --- a/src/error.ts +++ b/src/error.ts @@ -7,27 +7,20 @@ export class FetchError extends Error { data?: T } -export function createFetchError (request: FetchRequest, response: FetchResponse): FetchError { - const message = `${response.status} ${response.statusText} (${request.toString()})` - const error: FetchError = new FetchError(message) +export function createFetchError (request: FetchRequest, error?: Error, response?: FetchResponse): FetchError { + let message = '' + if (request && response) { + message = `${response.status} ${response.statusText} (${request.toString()})` + } + if (error) { + message = `${error.message} (${message})` + } - Object.defineProperty(error, 'request', { get () { return request } }) - Object.defineProperty(error, 'response', { get () { return response } }) - Object.defineProperty(error, 'data', { get () { return response.data } }) + const fetchError: FetchError = new FetchError(message) - const stack = error.stack - Object.defineProperty(error, 'stack', { get () { return normalizeStack(stack) } }) + Object.defineProperty(fetchError, 'request', { get () { return request } }) + Object.defineProperty(fetchError, 'response', { get () { return response } }) + Object.defineProperty(fetchError, 'data', { get () { return response && response.data } }) - return error -} - -function normalizeStack (stack: string = '') { - return stack.split('\n') - .filter(l => - !l.includes('createFetchError') && - !l.includes('at $fetch') && - !l.includes('at $fetchRaw') && - !l.includes('processTicksAndRejections') - ) - .join('\n') + return fetchError } diff --git a/src/fetch.ts b/src/fetch.ts index e064d64a..cdf65025 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -46,8 +46,26 @@ export function setHeader (options: FetchOptions, _key: string, value: string) { } export function createFetch ({ fetch }: CreateFetchOptions): $Fetch { - const $fetchRaw: $Fetch['raw'] = async function (request, opts = {}) { - const hasPayload = payloadMethods.includes((opts.method || '').toLowerCase()) + function onError (request: FetchRequest, opts: FetchOptions, error?: Error, response?: FetchResponse): Promise> { + // Retry + if (opts.retry !== false) { + const hasPayload = payloadMethods.includes((opts.method || '').toLowerCase()) + const retries = typeof opts.retry === 'number' ? opts.retry : (hasPayload ? 0 : 1) + if (retries > 0) { + return $fetchRaw(request, { + ...opts, + retry: retries - 1 + }) + } + } + + // Throw normalized error + const err = createFetchError(request, error, response) + Error.captureStackTrace(err, $fetchRaw) + throw err + } + + const $fetchRaw: $Fetch['raw'] = async function $fetchRaw (request, opts = {}) { if (typeof request === 'string') { if (opts.baseURL) { request = joinURL(opts.baseURL, request) @@ -55,31 +73,20 @@ export function createFetch ({ fetch }: CreateFetchOptions): $Fetch { if (opts.params) { request = withQuery(request, opts.params) } + const hasPayload = payloadMethods.includes((opts.method || '').toLowerCase()) if (opts.body && opts.body.toString() === '[object Object]' && hasPayload) { opts.body = JSON.stringify(opts.body) setHeader(opts, 'content-type', 'application/json') } } - const response: FetchResponse = await fetch(request, opts as RequestInit) + const response: FetchResponse = await fetch(request, opts as RequestInit).catch(error => onError(request, opts, error, undefined)) const text = await response.text() const parseFn = opts.parseResponse || destr response.data = parseFn(text) - if (!response.ok) { - if (opts.retry !== false) { - const retries = typeof opts.retry === 'number' ? opts.retry : (hasPayload ? 0 : 1) - if (retries > 0) { - return $fetchRaw(request, { - ...opts, - retry: retries - 1 - }) - } - } - throw createFetchError(request, response) - } - return response + return response.ok ? response : onError(request, opts, undefined, response) } - const $fetch = function (request, opts) { + const $fetch = function $fetch (request, opts) { return $fetchRaw(request, opts).then(r => r.data) } as $Fetch