Skip to content

Commit be0a437

Browse files
committed
Unify hook signatures around a single state object
1 parent 7003a19 commit be0a437

File tree

13 files changed

+146
-138
lines changed

13 files changed

+146
-138
lines changed

readme.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,9 @@ Hooks allow modifications during the request lifecycle. Hook functions may be as
347347
Type: `Function[]`\
348348
Default: `[]`
349349

350-
This hook enables you to modify the request right before it is sent. Ky will make no further changes to the request after this. The hook function receives the normalized request, options, and a state object. You could, for example, modify the `request.headers` here.
350+
This hook enables you to modify the request right before it is sent. Ky will make no further changes to the request after this. The hook function receives a state object with the normalized request, options, and retry count. You could, for example, modify the `request.headers` here.
351351

352-
The `state.retryCount` is `0` for the initial request and increments with each retry. This allows you to distinguish between initial requests and retries, which is useful when you need different behavior for retries (e.g., avoiding overwriting headers set in `beforeRetry`).
352+
The `retryCount` is `0` for the initial request and increments with each retry. This allows you to distinguish between initial requests and retries, which is useful when you need different behavior for retries (e.g., avoiding overwriting headers set in `beforeRetry`).
353353

354354
The hook can return a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) to replace the outgoing request, or return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to completely avoid making an HTTP request. This can be used to mock a request, check an internal cache, etc. An **important** consideration when returning a request or response from this hook is that any remaining `beforeRequest` hooks will be skipped, so you may want to only return them from the last hook.
355355

@@ -359,7 +359,7 @@ import ky from 'ky';
359359
const api = ky.extend({
360360
hooks: {
361361
beforeRequest: [
362-
(request, options, {retryCount}) => {
362+
({request, retryCount}) => {
363363
// Only set default auth header on initial request, not on retries
364364
// (retries may have refreshed token set by beforeRetry)
365365
if (retryCount === 0) {
@@ -378,7 +378,7 @@ const response = await api.get('https://example.com/api/users');
378378
Type: `Function[]`\
379379
Default: `[]`
380380

381-
This hook enables you to modify the request right before retry. Ky will make no further changes to the request after this. The hook function receives an object with the normalized request and options, an error instance, and the retry count. You could, for example, modify `request.headers` here.
381+
This hook enables you to modify the request right before retry. Ky will make no further changes to the request after this. The hook function receives a state object with the normalized request, options, an error instance, and the retry count. You could, for example, modify `request.headers` here.
382382

383383
The hook can return a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) to replace the outgoing retry request, or return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to skip the retry and use that response instead. **Note:** Returning a request or response skips remaining `beforeRetry` hooks.
384384

@@ -455,17 +455,17 @@ const response = await ky('https://example.com/api', {
455455
Type: `Function[]`\
456456
Default: `[]`
457457

458-
This hook enables you to modify the `HTTPError` right before it is thrown. The hook function receives an `HTTPError` and a state object as arguments and should return an instance of `HTTPError`.
458+
This hook enables you to modify the `HTTPError` right before it is thrown. The hook function receives a state object with an `HTTPError` and retry count, and should return an instance of `HTTPError`.
459459

460-
The `state.retryCount` is `0` for the initial request and increments with each retry. This allows you to distinguish between the initial request and retries, which is useful when you need different error handling based on retry attempts (e.g., showing different error messages on the final attempt).
460+
The `retryCount` is `0` for the initial request and increments with each retry. This allows you to distinguish between the initial request and retries, which is useful when you need different error handling based on retry attempts (e.g., showing different error messages on the final attempt).
461461

462462
```js
463463
import ky from 'ky';
464464

465465
await ky('https://example.com', {
466466
hooks: {
467467
beforeError: [
468-
error => {
468+
({error}) => {
469469
if (
470470
typeof error.data === 'object'
471471
&& error.data !== null
@@ -479,9 +479,9 @@ await ky('https://example.com', {
479479
},
480480

481481
// Or show different message based on retry count
482-
(error, state) => {
483-
if (state.retryCount === error.options.retry.limit) {
484-
error.message = `${error.message} (failed after ${state.retryCount} retries)`;
482+
({error, retryCount}) => {
483+
if (retryCount === error.options.retry.limit) {
484+
error.message = `${error.message} (failed after ${retryCount} retries)`;
485485
}
486486

487487
return error;
@@ -496,19 +496,19 @@ await ky('https://example.com', {
496496
Type: `Function[]`\
497497
Default: `[]`
498498

499-
This hook enables you to read and optionally modify the response. The hook function receives normalized request, options, a clone of the response, and a state object. The return value of the hook function will be used by Ky as the response object if it's an instance of [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
499+
This hook enables you to read and optionally modify the response. The hook function receives a state object with the normalized request, options, a clone of the response, and retry count. The return value of the hook function will be used by Ky as the response object if it's an instance of [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
500500

501501
You can also force a retry by returning [`ky.retry(options)`](#kyretryoptions). This is useful when you need to retry based on the response body content, even if the response has a successful status code. The retry will respect the `retry.limit` option and be observable in `beforeRetry` hooks.
502502

503-
The `state.retryCount` is `0` for the initial request and increments with each retry. This allows you to distinguish between initial requests and retries, which is useful when you need different behavior for retries (e.g., showing a notification only on the final retry).
503+
The `retryCount` is `0` for the initial request and increments with each retry. This allows you to distinguish between initial requests and retries, which is useful when you need different behavior for retries (e.g., showing a notification only on the final retry).
504504

505505
```js
506506
import ky from 'ky';
507507

508508
const response = await ky('https://example.com', {
509509
hooks: {
510510
afterResponse: [
511-
(_request, _options, response) => {
511+
({response}) => {
512512
// You could do something with the response, for example, logging.
513513
log(response);
514514

@@ -517,8 +517,8 @@ const response = await ky('https://example.com', {
517517
},
518518

519519
// Or retry with a fresh token on a 401 error
520-
async (request, _options, response, state) => {
521-
if (response.status === 401 && state.retryCount === 0) {
520+
async ({request, response, retryCount}) => {
521+
if (response.status === 401 && retryCount === 0) {
522522
// Only refresh on first 401, not on subsequent retries
523523
const {token} = await ky.post('https://example.com/auth/refresh').json();
524524

@@ -533,7 +533,7 @@ const response = await ky('https://example.com', {
533533
},
534534

535535
// Or force retry based on response body content
536-
async (request, options, response) => {
536+
async ({response}) => {
537537
if (response.status === 200) {
538538
const data = await response.clone().json();
539539
if (data.error?.code === 'RATE_LIMIT') {
@@ -547,7 +547,7 @@ const response = await ky('https://example.com', {
547547
},
548548

549549
// Or show a notification only on the last retry for 5xx errors
550-
(request, options, response, {retryCount}) => {
550+
({options, response, retryCount}) => {
551551
if (response.status >= 500 && response.status <= 599) {
552552
if (retryCount === options.retry.limit) {
553553
showNotification('Request failed after all retries');
@@ -713,7 +713,7 @@ import ky from 'ky';
713713
const api = ky.create({
714714
hooks: {
715715
beforeRequest: [
716-
(request, options) => {
716+
({request, options}) => {
717717
const {token} = options.context;
718718
if (token) {
719719
request.headers.set('Authorization', `Bearer ${token}`);
@@ -937,7 +937,7 @@ import ky, {isForceRetryError} from 'ky';
937937
const api = ky.extend({
938938
hooks: {
939939
afterResponse: [
940-
async (request, options, response) => {
940+
async ({request, response}) => {
941941
// Retry based on response body content
942942
if (response.status === 200) {
943943
const data = await response.clone().json();
@@ -1035,7 +1035,7 @@ You can also use the `beforeError` hook:
10351035
await ky('https://example.com', {
10361036
hooks: {
10371037
beforeError: [
1038-
error => {
1038+
({error}) => {
10391039
if (error.data !== undefined) {
10401040
error.message = `${error.message}: ${JSON.stringify(error.data)}`;
10411041
}
@@ -1094,7 +1094,7 @@ const response = await ky.post(url, {
10941094
body: formData,
10951095
hooks: {
10961096
beforeRequest: [
1097-
request => {
1097+
({request}) => {
10981098
const newFormData = new FormData();
10991099

11001100
// Modify FormData as needed
@@ -1281,7 +1281,7 @@ interface CustomError extends HTTPError {
12811281
const api = ky.extend({
12821282
hooks: {
12831283
beforeError: [
1284-
async error => {
1284+
async ({error}) => {
12851285
(error as CustomError).customProperty = 'value';
12861286
return error;
12871287
}

source/core/Ky.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ export class Ky {
6666
let modifiedResponse;
6767
try {
6868
// eslint-disable-next-line no-await-in-loop
69-
modifiedResponse = await hook(
70-
ky.request,
71-
ky.#getNormalizedOptions(),
72-
clonedResponse,
73-
{retryCount: ky.#retryCount},
74-
);
69+
modifiedResponse = await hook({
70+
request: ky.request,
71+
options: ky.#getNormalizedOptions(),
72+
response: clonedResponse,
73+
retryCount: ky.#retryCount,
74+
});
7575
} catch (error) {
7676
// Cancel both responses to prevent memory leaks when hook throws
7777
ky.#cancelResponseBody(clonedResponse);
@@ -115,7 +115,10 @@ export class Ky {
115115

116116
for (const hook of ky.#options.hooks.beforeError) {
117117
// eslint-disable-next-line no-await-in-loop
118-
error = await hook(error, {retryCount: ky.#retryCount});
118+
error = await hook({
119+
error,
120+
retryCount: ky.#retryCount,
121+
});
119122
}
120123

121124
throw error;
@@ -591,11 +594,11 @@ export class Ky {
591594

592595
for (const hook of this.#options.hooks.beforeRequest) {
593596
// eslint-disable-next-line no-await-in-loop
594-
const result = await hook(
595-
this.request,
596-
this.#getNormalizedOptions(),
597-
{retryCount: this.#retryCount},
598-
);
597+
const result = await hook({
598+
request: this.request,
599+
options: this.#getNormalizedOptions(),
600+
retryCount: this.#retryCount,
601+
});
599602

600603
if (result instanceof Response) {
601604
return result;

source/core/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ import ky, {isForceRetryError} from 'ky';
163163
const api = ky.extend({
164164
hooks: {
165165
afterResponse: [
166-
async (request, options, response) => {
166+
async ({request, response}) => {
167167
// Retry based on response body content
168168
if (response.status === 200) {
169169
const data = await response.clone().json();

0 commit comments

Comments
 (0)