diff --git a/README.md b/README.md index 8f44eae..f37d6d1 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,10 @@ const { url, status, response } = await gretch( are good for code that needs to run on every request, like adding tracking headers and logging errors. +Hooks should be defined as an array. That way you can compose multiple hooks +per-request, and define and merge default hooks when [creating +instances](#creating-instances). + #### `before` The `before` hook runs just prior to the request being made. You can even modify @@ -227,24 +231,29 @@ object, and the full options object. ```js const response = await gretch('/api/user/12', { hooks: { - before (request, options) { - request.headers.set('Tracking-ID', 'abcde') - } + before: [ + (request, options) => { + request.headers.set('Tracking-ID', 'abcde') + } + ] } }).json() ``` #### `after` -The `after` hook has the opportunity to read the `gretchen` response. It +The `after` runs after the request has resolved and any body interface methods +have been called. It has the opportunity to read the `gretchen` response. It _cannot_ modify it. This is mostly useful for logging. ```js const response = await gretch('/api/user/12', { hooks: { - after ({ url, status, data, error }) { - sentry.captureMessage(`${url} returned ${status}`) - } + after: [ + ({ url, status, data, error }, options) => { + sentry.captureMessage(`${url} returned ${status}`) + } + ] } }).json() ``` diff --git a/index.ts b/index.ts index 2b10bbf..10bd751 100644 --- a/index.ts +++ b/index.ts @@ -34,9 +34,15 @@ export type GretchResponse = response: Response } +export type GretchBeforeHook = (request: Request, opts: GretchOptions) => void +export type GretchAfterHook = ( + response: GretchResponse, + opts: GretchOptions +) => void + export type GretchHooks = { - before?: (request: Request, opts: GretchOptions) => void - after?: (response: GretchResponse, opts: GretchOptions) => void + before?: GretchBeforeHook | GretchBeforeHook[] + after?: GretchAfterHook | GretchAfterHook[] } export type GretchOptions = { @@ -97,7 +103,7 @@ export function gretch ( baseURL !== undefined ? normalizeURL(url, { baseURL }) : url const request = new Request(normalizedURL, options) - if (hooks.before) hooks.before(request, opts) + if (hooks.before) [].concat(hooks.before).forEach(hook => hook(request, opts)) const fetcher = () => timeout @@ -152,7 +158,8 @@ export function gretch ( response } - if (hooks.after) hooks.after(res, opts) + if (hooks.after) + [].concat(hooks.after).forEach(hook => hook({ ...res }, opts)) return res } diff --git a/lib/merge.ts b/lib/merge.ts index a10534e..a08bf2c 100644 --- a/lib/merge.ts +++ b/lib/merge.ts @@ -25,10 +25,12 @@ export function merge ( headersToObj(new Headers(a[k])), headersToObj(new Headers(v)) ) - } else if (v.pop) { + } else if (v.pop && a[k].pop) { c[k] = [...(a[k] || []), ...v] - } else { + } else if (typeof a[k] === 'object' && !a[k].pop) { c[k] = merge(a[k], v) + } else { + c[k] = v } } else { c[k] = v diff --git a/test/index.test.ts b/test/index.test.ts index fbe285d..0685c49 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -267,16 +267,25 @@ export default (test, assert) => { await gretch(`http://127.0.0.1:${port}`, { timeout: 50000, hooks: { - before (request) { + before (request, opts) { + assert(request.url) + assert(opts.timeout) hooks++ }, - after ({ status }) { - hooks++ - } + after: [ + (response, opts) => { + assert(response.status) + assert(opts.timeout) + hooks++ + }, + () => { + hooks++ + } + ] } }).json() - assert(hooks === 2) + assert(hooks === 3) server.close() diff --git a/test/merge.test.ts b/test/merge.test.ts index bbb0893..e43a3a0 100644 --- a/test/merge.test.ts +++ b/test/merge.test.ts @@ -51,22 +51,30 @@ export default (test, assert) => { assert.equal(o.headers['x-out'], 'out') }) - test('overwrites values', () => { + test('overwrites mixed values', () => { const o = merge( { timeout: 100, - retry: { - attempts: 3 + retry: false, + hooks: { + after () {} } }, { timeout: 200, - retry: false + retry: { + attempts: 3 + }, + hooks: { + after: [() => {}] + } } ) assert.equal(o.timeout, 200) - assert.equal(o.retry, false) + // @ts-ignore + assert.equal(o.retry.attempts, 3) + assert(Array.isArray(o.hooks.after)) }) test('merges hooks', () => {