Comparing version 1.4.0 to 1.5.0
@@ -74,34 +74,9 @@ import { HTTPError } from '../errors/HTTPError.js'; | ||
} | ||
request; | ||
abortController; | ||
_retryCount = 0; | ||
_input; | ||
_options; | ||
// eslint-disable-next-line complexity | ||
constructor(input, options = {}) { | ||
Object.defineProperty(this, "request", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "abortController", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_retryCount", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 0 | ||
}); | ||
Object.defineProperty(this, "_input", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "_options", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
this._input = input; | ||
@@ -179,24 +154,24 @@ const credentials = this._input instanceof Request && 'credentials' in Request.prototype | ||
this._retryCount++; | ||
if (this._retryCount <= this._options.retry.limit && !(error instanceof TimeoutError)) { | ||
if (error instanceof HTTPError) { | ||
if (!this._options.retry.statusCodes.includes(error.response.status)) { | ||
return 0; | ||
if (this._retryCount > this._options.retry.limit || error instanceof TimeoutError) { | ||
throw error; | ||
} | ||
if (error instanceof HTTPError) { | ||
if (!this._options.retry.statusCodes.includes(error.response.status)) { | ||
throw error; | ||
} | ||
const retryAfter = error.response.headers.get('Retry-After'); | ||
if (retryAfter && this._options.retry.afterStatusCodes.includes(error.response.status)) { | ||
let after = Number(retryAfter) * 1000; | ||
if (Number.isNaN(after)) { | ||
after = Date.parse(retryAfter) - Date.now(); | ||
} | ||
const retryAfter = error.response.headers.get('Retry-After'); | ||
if (retryAfter && this._options.retry.afterStatusCodes.includes(error.response.status)) { | ||
let after = Number(retryAfter) * 1000; | ||
if (Number.isNaN(after)) { | ||
after = Date.parse(retryAfter) - Date.now(); | ||
} | ||
const max = this._options.retry.maxRetryAfter ?? after; | ||
return after < max ? after : max; | ||
} | ||
if (error.response.status === 413) { | ||
return 0; | ||
} | ||
const max = this._options.retry.maxRetryAfter ?? after; | ||
return after < max ? after : max; | ||
} | ||
const retryDelay = this._options.retry.delay(this._retryCount); | ||
return Math.min(this._options.retry.backoffLimit, retryDelay); | ||
if (error.response.status === 413) { | ||
throw error; | ||
} | ||
} | ||
return 0; | ||
const retryDelay = this._options.retry.delay(this._retryCount); | ||
return Math.min(this._options.retry.backoffLimit, retryDelay); | ||
} | ||
@@ -215,20 +190,20 @@ _decorateResponse(response) { | ||
const ms = Math.min(this._calculateRetryDelay(error), maxSafeTimeout); | ||
if (ms !== 0 && this._retryCount > 0) { | ||
await delay(ms, { signal: this._options.signal }); | ||
for (const hook of this._options.hooks.beforeRetry) { | ||
// eslint-disable-next-line no-await-in-loop | ||
const hookResult = await hook({ | ||
request: this.request, | ||
options: this._options, | ||
error: error, | ||
retryCount: this._retryCount, | ||
}); | ||
// If `stop` is returned from the hook, the retry process is stopped | ||
if (hookResult === stop) { | ||
return; | ||
} | ||
if (this._retryCount < 1) { | ||
throw error; | ||
} | ||
await delay(ms, { signal: this._options.signal }); | ||
for (const hook of this._options.hooks.beforeRetry) { | ||
// eslint-disable-next-line no-await-in-loop | ||
const hookResult = await hook({ | ||
request: this.request, | ||
options: this._options, | ||
error: error, | ||
retryCount: this._retryCount, | ||
}); | ||
// If `stop` is returned from the hook, the retry process is stopped | ||
if (hookResult === stop) { | ||
return; | ||
} | ||
return this._retry(function_); | ||
} | ||
throw error; | ||
return this._retry(function_); | ||
} | ||
@@ -235,0 +210,0 @@ } |
import type { NormalizedOptions } from '../types/options.js'; | ||
import type { KyRequest } from '../types/request.js'; | ||
import type { KyResponse } from '../types/response.js'; | ||
export declare class HTTPError extends Error { | ||
response: Response; | ||
request: Request; | ||
response: KyResponse; | ||
request: KyRequest; | ||
options: NormalizedOptions; | ||
constructor(response: Response, request: Request, options: NormalizedOptions); | ||
} |
// eslint-lint-disable-next-line @typescript-eslint/naming-convention | ||
export class HTTPError extends Error { | ||
response; | ||
request; | ||
options; | ||
constructor(response, request, options) { | ||
@@ -9,20 +12,2 @@ const code = (response.status || response.status === 0) ? response.status : ''; | ||
super(`Request failed with ${reason}: ${request.method} ${request.url}`); | ||
Object.defineProperty(this, "response", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "request", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
Object.defineProperty(this, "options", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
this.name = 'HTTPError'; | ||
@@ -29,0 +14,0 @@ this.response = response; |
@@ -0,4 +1,5 @@ | ||
import type { KyRequest } from '../types/request.js'; | ||
export declare class TimeoutError extends Error { | ||
request: Request; | ||
request: KyRequest; | ||
constructor(request: Request); | ||
} |
export class TimeoutError extends Error { | ||
request; | ||
constructor(request) { | ||
super(`Request timed out: ${request.method} ${request.url}`); | ||
Object.defineProperty(this, "request", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: void 0 | ||
}); | ||
this.name = 'TimeoutError'; | ||
@@ -11,0 +6,0 @@ this.request = request; |
@@ -9,4 +9,5 @@ /*! MIT License © Sindre Sorhus */ | ||
export type { ResponsePromise } from './types/ResponsePromise.js'; | ||
export type { KyRequest } from './types/request.js'; | ||
export type { KyResponse } from './types/response.js'; | ||
export { HTTPError } from './errors/HTTPError.js'; | ||
export { TimeoutError } from './errors/TimeoutError.js'; |
@@ -13,3 +13,8 @@ /*! MIT License © Sindre Sorhus */ | ||
ky.create = (newDefaults) => createInstance(validateAndMerge(newDefaults)); | ||
ky.extend = (newDefaults) => createInstance(validateAndMerge(defaults, newDefaults)); | ||
ky.extend = (newDefaults) => { | ||
if (typeof newDefaults === 'function') { | ||
newDefaults = newDefaults(defaults ?? {}); | ||
} | ||
return createInstance(validateAndMerge(defaults, newDefaults)); | ||
}; | ||
ky.stop = stop; | ||
@@ -16,0 +21,0 @@ return ky; |
import { type stop } from '../core/constants.js'; | ||
import { type HTTPError } from '../index.js'; | ||
import type { KyRequest, KyResponse, HTTPError } from '../index.js'; | ||
import type { NormalizedOptions } from './options.js'; | ||
export type BeforeRequestHook = (request: Request, options: NormalizedOptions) => Request | Response | void | Promise<Request | Response | void>; | ||
export type BeforeRequestHook = (request: KyRequest, options: NormalizedOptions) => Request | Response | void | Promise<Request | Response | void>; | ||
export type BeforeRetryState = { | ||
request: Request; | ||
request: KyRequest; | ||
options: NormalizedOptions; | ||
@@ -12,3 +12,3 @@ error: Error; | ||
export type BeforeRetryHook = (options: BeforeRetryState) => typeof stop | void | Promise<typeof stop | void>; | ||
export type AfterResponseHook = (request: Request, options: NormalizedOptions, response: Response) => Response | void | Promise<Response | void>; | ||
export type AfterResponseHook = (request: KyRequest, options: NormalizedOptions, response: KyResponse) => Response | void | Promise<Response | void>; | ||
export type BeforeErrorHook = (error: HTTPError) => HTTPError | Promise<HTTPError>; | ||
@@ -15,0 +15,0 @@ export type Hooks = { |
@@ -75,5 +75,22 @@ import { type stop } from '../core/constants.js'; | ||
You can also refer to parent defaults by providing a function to `.extend()`. | ||
@example | ||
``` | ||
import ky from 'ky'; | ||
const api = ky.create({prefixUrl: 'https://example.com/api'}); | ||
const usersApi = api.extend((options) => ({prefixUrl: `${options.prefixUrl}/users`})); | ||
const response = await usersApi.get('123'); | ||
//=> 'https://example.com/api/users/123' | ||
const response = await api.get('version'); | ||
//=> 'https://example.com/api/version' | ||
``` | ||
@returns A new Ky instance. | ||
*/ | ||
extend: (defaultOptions: Options) => KyInstance; | ||
extend: (defaultOptions: Options | ((parentOptions: Options) => Options)) => KyInstance; | ||
/** | ||
@@ -80,0 +97,0 @@ A `Symbol` that can be returned by a `beforeRetry` hook to stop the retry. This will also short circuit the remaining `beforeRetry` hooks. |
@@ -102,6 +102,8 @@ import type { LiteralUnion, Required } from './common.js'; | ||
/** | ||
An object representing `limit`, `methods`, `statusCodes` and `maxRetryAfter` fields for maximum retry count, allowed methods, allowed status codes and maximum [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) time. | ||
An object representing `limit`, `methods`, `statusCodes`, `afterStatusCodes`, and `maxRetryAfter` fields for maximum retry count, allowed methods, allowed status codes, status codes allowed to use the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) time, and maximum [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) time. | ||
If `retry` is a number, it will be used as `limit` and other defaults will remain in place. | ||
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 `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. | ||
@@ -108,0 +110,0 @@ |
@@ -27,2 +27,5 @@ type UndiciHeadersInit = string[][] | Record<string, string | readonly string[]> | Headers; | ||
}; | ||
export type KyRequest = { | ||
json: <T = unknown>() => Promise<T>; | ||
} & Request; | ||
export {}; |
@@ -31,5 +31,4 @@ import { requestMethods } from '../core/constants.js'; | ||
...retry, | ||
afterStatusCodes: retryAfterStatusCodes, | ||
}; | ||
}; | ||
//# sourceMappingURL=normalize.js.map |
{ | ||
"name": "ky", | ||
"version": "1.4.0", | ||
"description": "Tiny and elegant HTTP client based on the browser Fetch API", | ||
"version": "1.5.0", | ||
"description": "Tiny and elegant HTTP client based on the Fetch API", | ||
"license": "MIT", | ||
@@ -57,3 +57,3 @@ "repository": "sindresorhus/ky", | ||
"devDependencies": { | ||
"@sindresorhus/tsconfig": "^4.0.0", | ||
"@sindresorhus/tsconfig": "^6.0.0", | ||
"@type-challenges/utils": "^0.1.1", | ||
@@ -63,3 +63,3 @@ "@types/body-parser": "^1.19.2", | ||
"@types/express": "^4.17.17", | ||
"@types/node": "^20.5.7", | ||
"@types/node": "^20.14.12", | ||
"ava": "^5.3.1", | ||
@@ -70,10 +70,10 @@ "body-parser": "^1.20.2", | ||
"delay": "^6.0.0", | ||
"expect-type": "^0.16.0", | ||
"expect-type": "^0.19.0", | ||
"express": "^4.18.2", | ||
"pify": "^6.1.0", | ||
"playwright": "^1.40.1", | ||
"playwright": "^1.45.3", | ||
"raw-body": "^2.5.2", | ||
"tsx": "^4.7.0", | ||
"typescript": "^5.2.2", | ||
"xo": "^0.56.0" | ||
"tsx": "^4.16.2", | ||
"typescript": "^5.5.4", | ||
"xo": "^0.58.0" | ||
}, | ||
@@ -91,3 +91,4 @@ "xo": { | ||
"@typescript-eslint/no-unsafe-call": "off", | ||
"@typescript-eslint/naming-convention": "off" | ||
"@typescript-eslint/naming-convention": "off", | ||
"n/no-unsupported-features/node-builtins": "off" | ||
} | ||
@@ -94,0 +95,0 @@ }, |
@@ -54,3 +54,3 @@ <div align="center"> | ||
> Ky is a tiny and elegant HTTP client based on the browser [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) | ||
> Ky is a tiny and elegant HTTP client based on the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch) | ||
@@ -60,5 +60,5 @@ [![Coverage Status](https://codecov.io/gh/sindresorhus/ky/branch/main/graph/badge.svg)](https://codecov.io/gh/sindresorhus/ky) | ||
Ky targets [modern browsers](#browser-support), Node.js, and Deno. | ||
Ky targets [modern browsers](#browser-support), Node.js, Bun, and Deno. | ||
It's just a tiny file with no dependencies. | ||
It's just a tiny package with no dependencies. | ||
@@ -76,2 +76,3 @@ ## Benefits over plain `fetch` | ||
- Hooks | ||
- TypeScript niceties (e.g. `.json()` resolves to `unknown`, not `any`; `.json<T>()` can be used too) | ||
@@ -218,2 +219,3 @@ ## Install | ||
- `statusCodes`: [`408`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) [`413`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413) [`429`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) [`500`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) [`502`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502) [`503`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503) [`504`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504) | ||
- `afterStatusCodes`: [`413`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413), [`429`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429), [`503`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503) | ||
- `maxRetryAfter`: `undefined` | ||
@@ -223,6 +225,8 @@ - `backoffLimit`: `undefined` | ||
An object representing `limit`, `methods`, `statusCodes` and `maxRetryAfter` fields for maximum retry count, allowed methods, allowed status codes and maximum [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) time. | ||
An object representing `limit`, `methods`, `statusCodes`, `afterStatusCodes`, and `maxRetryAfter` fields for maximum retry count, allowed methods, allowed status codes, status codes allowed to use the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) time, and maximum [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) time. | ||
If `retry` is a number, it will be used as `limit` and other defaults will remain in place. | ||
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 `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`. | ||
@@ -518,2 +522,18 @@ | ||
You can also refer to parent defaults by providing a function to `.extend()`. | ||
```js | ||
import ky from 'ky'; | ||
const api = ky.create({prefixUrl: 'https://example.com/api'}); | ||
const usersApi = api.extend((options) => ({prefixUrl: `${options.prefixUrl}/users`})); | ||
const response = await usersApi.get('123'); | ||
//=> 'https://example.com/api/users/123' | ||
const response = await api.get('version'); | ||
//=> 'https://example.com/api/version' | ||
``` | ||
### ky.create(defaultOptions) | ||
@@ -694,3 +714,3 @@ | ||
console.log(json.title); | ||
//=> 'delectus aut autem | ||
//=> 'delectus aut autem' | ||
</script> | ||
@@ -727,2 +747,3 @@ ``` | ||
- [fetch-extras](https://github.com/sindresorhus/fetch-extras) - Useful utilities for working with Fetch | ||
- [got](https://github.com/sindresorhus/got) - Simplified HTTP requests for Node.js | ||
@@ -729,0 +750,0 @@ - [ky-hooks-change-case](https://github.com/alice-health/ky-hooks-change-case) - Ky hooks to modify cases on requests and responses of objects |
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
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
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
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
153043
749
1154