Comparing version 1.5.0 to 1.6.0
import { HTTPError } from '../errors/HTTPError.js'; | ||
import { TimeoutError } from '../errors/TimeoutError.js'; | ||
import { deepMerge, mergeHeaders } from '../utils/merge.js'; | ||
import { mergeHeaders, mergeHooks } from '../utils/merge.js'; | ||
import { normalizeRequestMethod, normalizeRetryOptions } from '../utils/normalize.js'; | ||
@@ -82,10 +82,6 @@ import timeout from '../utils/timeout.js'; | ||
this._input = input; | ||
const credentials = this._input instanceof Request && 'credentials' in Request.prototype | ||
? this._input.credentials | ||
: undefined; | ||
this._options = { | ||
...(credentials && { credentials }), // For exactOptionalPropertyTypes | ||
...options, | ||
headers: mergeHeaders(this._input.headers, options.headers), | ||
hooks: deepMerge({ | ||
hooks: mergeHooks({ | ||
beforeRequest: [], | ||
@@ -118,8 +114,6 @@ beforeRetry: [], | ||
this.abortController = new globalThis.AbortController(); | ||
if (this._options.signal) { | ||
const originalSignal = this._options.signal; | ||
this._options.signal.addEventListener('abort', () => { | ||
this.abortController.abort(originalSignal.reason); | ||
}); | ||
} | ||
const originalSignal = this._options.signal ?? this._input.signal; | ||
originalSignal?.addEventListener('abort', () => { | ||
this.abortController.abort(originalSignal.reason); | ||
}); | ||
this._options.signal = this.abortController.signal; | ||
@@ -162,3 +156,6 @@ } | ||
} | ||
const retryAfter = error.response.headers.get('Retry-After'); | ||
const retryAfter = error.response.headers.get('Retry-After') | ||
?? error.response.headers.get('RateLimit-Reset') | ||
?? error.response.headers.get('X-RateLimit-Reset') // GitHub | ||
?? error.response.headers.get('X-Rate-Limit-Reset'); // Twitter | ||
if (retryAfter && this._options.retry.afterStatusCodes.includes(error.response.status)) { | ||
@@ -169,2 +166,6 @@ let after = Number(retryAfter) * 1000; | ||
} | ||
else if (after >= Date.parse('2024-01-01')) { | ||
// A large number is treated as a timestamp (fixed threshold protects against clock skew) | ||
after -= Date.now(); | ||
} | ||
const max = this._options.retry.maxRetryAfter ?? after; | ||
@@ -171,0 +172,0 @@ return after < max ? after : max; |
@@ -106,3 +106,3 @@ import type { LiteralUnion, Required } from './common.js'; | ||
If the response provides an HTTP status contained in `afterStatusCodes`, Ky will wait until the date or timeout given in the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header has passed to retry the request. If the provided status code is not in the list, the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header will be ignored. | ||
If the response provides an HTTP status contained in `afterStatusCodes`, Ky will wait until the date or timeout given in the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header has passed to retry the request. If `Retry-After` is missing, the non-standard [`RateLimit-Reset`](https://www.ietf.org/archive/id/draft-polli-ratelimit-headers-02.html#section-3.3) header is used in its place as a fallback. If the provided status code is not in the list, the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header will be ignored. | ||
@@ -109,0 +109,0 @@ If `maxRetryAfter` is set to `undefined`, it will use `options.timeout`. If [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header is greater than `maxRetryAfter`, it will cancel the request. |
import type { KyHeadersInit, Options } from '../types/options.js'; | ||
import type { Hooks } from '../types/hooks.js'; | ||
export declare const validateAndMerge: (...sources: Array<Partial<Options> | undefined>) => Partial<Options>; | ||
export declare const mergeHeaders: (source1?: KyHeadersInit, source2?: KyHeadersInit) => Headers; | ||
export declare const mergeHooks: (original?: Hooks, incoming?: Hooks) => Required<Hooks>; | ||
export declare const deepMerge: <T>(...sources: Array<Partial<T> | undefined>) => T; |
@@ -24,2 +24,13 @@ import { isObject } from './is.js'; | ||
}; | ||
function newHookValue(original, incoming, property) { | ||
return (Object.hasOwn(incoming, property) && incoming[property] === undefined) | ||
? [] | ||
: deepMerge(original[property] ?? [], incoming[property] ?? []); | ||
} | ||
export const mergeHooks = (original = {}, incoming = {}) => ({ | ||
beforeRequest: newHookValue(original, incoming, 'beforeRequest'), | ||
beforeRetry: newHookValue(original, incoming, 'beforeRetry'), | ||
afterResponse: newHookValue(original, incoming, 'afterResponse'), | ||
beforeError: newHookValue(original, incoming, 'beforeError'), | ||
}); | ||
// TODO: Make this strongly-typed (no `any`). | ||
@@ -29,2 +40,3 @@ export const deepMerge = (...sources) => { | ||
let headers = {}; | ||
let hooks = {}; | ||
for (const source of sources) { | ||
@@ -44,2 +56,6 @@ if (Array.isArray(source)) { | ||
} | ||
if (isObject(source.hooks)) { | ||
hooks = mergeHooks(hooks, source.hooks); | ||
returnValue.hooks = hooks; | ||
} | ||
if (isObject(source.headers)) { | ||
@@ -46,0 +62,0 @@ headers = mergeHeaders(headers, source.headers); |
{ | ||
"name": "ky", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"description": "Tiny and elegant HTTP client based on the Fetch API", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -15,17 +15,3 @@ <div align="center"> | ||
<br> | ||
<a href="https://serpapi.com#gh-light-mode-only"> | ||
<div> | ||
<img src="https://sindresorhus.com/assets/thanks/serpapi-logo-light.svg" width="130" alt="SerpApi"> | ||
</div> | ||
<b>API to get search engine results with ease.</b> | ||
</a> | ||
<a href="https://serpapi.com#gh-dark-mode-only"> | ||
<div> | ||
<img src="https://sindresorhus.com/assets/thanks/serpapi-logo-dark.svg" width="120" alt="SerpApi"> | ||
</div> | ||
<b>API to get search engine results with ease.</b> | ||
</a> | ||
<br> | ||
<br> | ||
<br> | ||
<a href="https://logto.io/?ref=sindre"> | ||
@@ -226,3 +212,3 @@ <div> | ||
If the response provides an HTTP status contained in `afterStatusCodes`, Ky will wait until the date or timeout given in the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header has passed to retry the request. If the provided status code is not in the list, the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header will be ignored. | ||
If the response provides an HTTP status contained in `afterStatusCodes`, Ky will wait until the date, timeout, or timestamp given in the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header has passed to retry the request. If `Retry-After` is missing, the non-standard [`RateLimit-Reset`](https://www.ietf.org/archive/id/draft-polli-ratelimit-headers-05.html#section-3.3) header is used in its place as a fallback. If the provided status code is not in the list, the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header will be ignored. | ||
@@ -492,2 +478,4 @@ If `maxRetryAfter` is set to `undefined`, it will use `options.timeout`. If [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header is greater than `maxRetryAfter`, it will use `maxRetryAfter`. | ||
Similarly, you can remove existing `hooks` entries by extending the hook with an explicit `undefined`. | ||
```js | ||
@@ -502,3 +490,7 @@ import ky from 'ky'; | ||
unicorn: 'unicorn' | ||
} | ||
}, | ||
hooks: { | ||
beforeRequest: [ () => console.log('before 1') ], | ||
afterResponse: [ () => console.log('after 1') ], | ||
}, | ||
}); | ||
@@ -509,2 +501,6 @@ | ||
rainbow: undefined | ||
}, | ||
hooks: { | ||
beforeRequest: undefined, | ||
afterResponse: [ () => console.log('after 2') ], | ||
} | ||
@@ -514,2 +510,4 @@ }); | ||
const response = await extended(url).json(); | ||
//=> after 1 | ||
//=> after 2 | ||
@@ -516,0 +514,0 @@ console.log('rainbow' in response); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
156367
1174
747