@kubb/plugin-axios 5.9.1 → 5.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1506,7 +1506,7 @@ var compatibility = {
1506
1506
  };
1507
1507
  //#endregion
1508
1508
  //#region package.json
1509
- var version = "5.9.1";
1509
+ var version = "5.9.2";
1510
1510
  //#endregion
1511
1511
  //#region src/templates.ts
1512
1512
  /** Absolute path to the axios client template, copied into `.kubb/client.ts`. */
package/dist/index.js CHANGED
@@ -1480,7 +1480,7 @@ var compatibility = {
1480
1480
  };
1481
1481
  //#endregion
1482
1482
  //#region package.json
1483
- var version = "5.9.1";
1483
+ var version = "5.9.2";
1484
1484
  //#endregion
1485
1485
  //#region src/templates.ts
1486
1486
  /** Absolute path to the axios client template, copied into `.kubb/client.ts`. */
@@ -6,27 +6,21 @@ import { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalA
6
6
  * HTTP status codes treated as a success, everything else is an error.
7
7
  */
8
8
  export type SuccessStatusCode = '200' | '201' | '202' | '203' | '204' | '205' | '206' | '207' | '208' | '226';
9
- /**
10
- * The success members of a per-status responses record.
11
- */
12
9
  export type SuccessOf<TResponses> = TResponses[Extract<keyof TResponses, SuccessStatusCode>];
13
- /**
14
- * The error members of a per-status responses record, every documented status that is not a 2xx.
15
- */
16
10
  export type ErrorOf<TResponses> = TResponses[Exclude<keyof TResponses, SuccessStatusCode>];
17
11
  /**
18
12
  * Converts a response record's string status key to its numeric literal, leaving non-numeric keys like `default` as `number`.
19
13
  */
20
14
  export type ToStatusNumber<TStatus> = TStatus extends `${infer TNumber extends number}` ? TNumber : number;
21
15
  /**
22
- * The plain body of a per-status response, unwrapping the `{ contentType; data }` union so an error result keeps the bare body union on `error`.
16
+ * Unwraps content-type variants to keep the bare body on `error`.
23
17
  */
24
18
  export type DataOf<T> = T extends {
25
19
  contentType: string;
26
20
  data: infer TData;
27
21
  } ? TData : T;
28
22
  /**
29
- * The success variant for a single status, flattened so the negotiated `contentType` sits next to `data` and `switch (result.contentType)` narrows it.
23
+ * Keeps `contentType` beside `data` so switching on it narrows the body.
30
24
  */
31
25
  export type SuccessVariant<TStatus, TEntry, TRequest, TResponse> = TEntry extends {
32
26
  contentType: string;
@@ -50,7 +44,7 @@ export type SuccessVariant<TStatus, TEntry, TRequest, TResponse> = TEntry extend
50
44
  response: TResponse;
51
45
  };
52
46
  /**
53
- * One result variant for a single documented status, keyed by the numeric `status` so a `switch (result.status)` narrows `data` or `error`.
47
+ * Narrows `data` or `error` when switching on the numeric status.
54
48
  */
55
49
  export type ResultByStatus<TResponses, TStatus extends keyof TResponses, TRequest, TResponse> = TStatus extends SuccessStatusCode ? SuccessVariant<TStatus, TResponses[TStatus], TRequest, TResponse> : {
56
50
  status: ToStatusNumber<TStatus>;
@@ -60,16 +54,13 @@ export type ResultByStatus<TResponses, TStatus extends keyof TResponses, TReques
60
54
  request: TRequest;
61
55
  response: TResponse;
62
56
  };
63
- /**
64
- * The union of every documented status' result variant.
65
- */
66
57
  export type ResultUnion<TResponses, TRequest, TResponse> = { [TStatus in keyof TResponses]: ResultByStatus<TResponses, TStatus, TRequest, TResponse>; }[keyof TResponses];
67
58
  /**
68
- * The union of just the success (2xx) status variants, selected by status code so an untyped error payload can never widen `data`.
59
+ * Selects success statuses without letting untyped error payloads widen `data`.
69
60
  */
70
61
  export type SuccessResultUnion<TResponses, TRequest, TResponse> = { [TStatus in Extract<keyof TResponses, SuccessStatusCode>]: ResultByStatus<TResponses, TStatus, TRequest, TResponse>; }[Extract<keyof TResponses, SuccessStatusCode>];
71
62
  /**
72
- * The shape every generated function returns, discriminated by the top-level `status`, narrowing to the 2xx variants under `throwOnError` and to every documented status without it.
63
+ * Narrows `data` and `error` by status. With `throwOnError`, includes only success variants.
73
64
  */
74
65
  export type RequestResult<TResponses, ThrowOnError extends boolean = true, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = ThrowOnError extends true ? [SuccessResultUnion<TResponses, TRequest, TResponse>] extends [never] ? {
75
66
  status: number;
@@ -100,11 +91,15 @@ export type Unwrappable<T extends {
100
91
  /**
101
92
  * Attaches `unwrap()` to a result promise, which rejects with `error` when the result carried one.
102
93
  *
103
- * @example Full result
104
- * `const { data, error } = await getPetById({ path: { petId: 1 } })`
94
+ * @example
95
+ * ```ts
96
+ * const { data, error } = await getPetById({ path: { petId: 1 } })
97
+ * ```
105
98
  *
106
- * @example Success body only
107
- * `const pet = await getPetById({ path: { petId: 1 } }).unwrap()`
99
+ * @example
100
+ * ```ts
101
+ * const pet = await getPetById({ path: { petId: 1 } }).unwrap()
102
+ * ```
108
103
  */
109
104
  export declare function withUnwrap<T extends {
110
105
  data: unknown;
@@ -117,15 +112,11 @@ export declare function withUnwrap<T extends {
117
112
  export type UnwrappedResult<TResponses, ThrowOnError extends boolean = true, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = ThrowOnError extends true ? RequestResult<TResponses, true, TRequest, TResponse>['data'] : RequestResult<TResponses, ThrowOnError, TRequest, TResponse>;
118
113
  /**
119
114
  * Returns the success body when `throwOnError` is true, or the full result when it is false.
120
- * Backs generated operations with `returnType: 'data'`.
121
115
  */
122
116
  export declare function unwrapResult<T extends {
123
117
  data: unknown;
124
118
  error: unknown;
125
119
  }>(promise: Promise<T>, throwOnError: boolean | undefined): Promise<T | T['data']>;
126
- /**
127
- * The data-shaped keys of the grouped options object, which `Options` re-adds typed per operation.
128
- */
129
120
  export type DataShape = {
130
121
  body?: unknown;
131
122
  cookies?: unknown;
@@ -135,41 +126,33 @@ export type DataShape = {
135
126
  };
136
127
  export type ResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream' | 'formdata';
137
128
  /**
138
- * Turns a raw response body into a parsed value, registered per media type as a codec's `deserialize` to handle formats the runtime does not decode itself.
129
+ * Decodes a response body for a codec's media type.
139
130
  */
140
131
  export type Deserializer<T = unknown> = (raw: unknown, contentType: string) => T | Promise<T>;
141
132
  /**
142
- * Serializes a request body for a single media type, registered per content type as a codec's `serialize` to encode formats the default serializer does not handle.
133
+ * Encodes a request body for a codec's media type.
143
134
  */
144
135
  export type ContentBodySerializer = (body: unknown, contentType?: string) => unknown;
145
136
  /**
146
- * A per-content-type codec registered on `codecs`, keyed by content type. `serialize` encodes the
147
- * request body for that media type and `deserialize` decodes the response body. Either half is
148
- * optional, so a codec can handle one direction.
137
+ * Encodes requests or decodes responses for a media type. Either direction can be omitted.
149
138
  */
150
139
  export type Codec = {
151
140
  serialize?: ContentBodySerializer;
152
141
  deserialize?: Deserializer;
153
142
  };
154
143
  /**
155
- * The per-call content type selection, where a bare string sets the request content type and the object form also sets the response format sent as `Accept`.
144
+ * A string sets the request content type. The object form can also set `Accept`.
156
145
  */
157
146
  export type ContentType = string | {
158
147
  request?: string;
159
148
  response?: string;
160
149
  };
161
150
  /**
162
- * A Standard Schema validator (zod, valibot, arktype) that parses a value before it is sent or after
163
- * it is received. `runValidator` runs it through `validateStandardSchema`. Wired through the per-call
164
- * `validator.request` / `validator.response` / `validator.error` hooks (`error` runs on the error body when a
165
- * non-2xx call does not throw), and `validator.path` / `validator.query` / `validator.headers` for the
166
- * request params.
151
+ * Validates request bodies and params, success bodies, or non-throwing error bodies with Standard Schema.
167
152
  */
168
153
  export type Validator<T = unknown> = StandardSchemaValidator<T>;
169
154
  /**
170
- * The failing value and the call it came from, handed to `onValidationError` alongside the `ParseError`.
171
- * `direction` says which side rejected it: the request, the success body, or the error body. `source`
172
- * says which part of it: the body, or one of the request param groups.
155
+ * Identifies the value and request that failed validation in `onValidationError`.
173
156
  */
174
157
  export type ValidationErrorContext = {
175
158
  value: unknown;
@@ -180,9 +163,7 @@ export type ValidationErrorContext = {
180
163
  status?: number;
181
164
  };
182
165
  /**
183
- * Decides what a failed validation does. Returning nothing rethrows the `ParseError`; returning
184
- * `{ value }` resolves the call with that value instead, so a drifted body can be reported and still
185
- * delivered. The box keeps an explicit `{ value: undefined }` substitution distinct from declining.
166
+ * Return `{ value }` to substitute a failed value, including `undefined`. Returning nothing rethrows the `ParseError`.
186
167
  */
187
168
  export type ValidationErrorHandler = (error: ParseError, context: ValidationErrorContext) => {
188
169
  value: unknown;
@@ -200,7 +181,7 @@ export type Auth = {
200
181
  scopes?: Array<string>;
201
182
  };
202
183
  /**
203
- * The raw token a consumer returns for a scheme (or `user:password` for basic), or `undefined` to skip it.
184
+ * Return `user:password` for basic auth, or `undefined` to skip a scheme.
204
185
  */
205
186
  export type AuthToken = string | undefined;
206
187
  /**
@@ -211,9 +192,6 @@ export type AuthResolver = AuthToken | ((auth: Auth) => AuthToken | Promise<Auth
211
192
  * Extra axios config the runtime spreads onto every request, an escape hatch for per-call fields it does not set itself such as `timeout`, `proxy`, and the progress callbacks.
212
193
  */
213
194
  export type AxiosOptions = AxiosRequestConfig;
214
- /**
215
- * The request a generated function hands to the runtime, with `body` / `headers` / `path` / `query` from the grouped options.
216
- */
217
195
  export type RequestConfig<TBody = unknown, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
218
196
  baseURL?: string;
219
197
  url?: string;
@@ -248,8 +226,7 @@ export type RequestConfig<TBody = unknown, TRequest = AxiosRequestConfig, TRespo
248
226
  auth?: AuthResolver;
249
227
  };
250
228
  /**
251
- * The grouped options object passed to every generated function: the request config minus the
252
- * data-shaped keys and the literal `url`, plus the per-operation `<Name>Request`.
229
+ * Combines client options with the operation's typed request params and body.
253
230
  */
254
231
  export type Options<TData extends DataShape, ThrowOnError extends boolean = true, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = Omit<RequestConfig<unknown, TRequest, TResponse>, keyof DataShape | 'url'> & TData & {
255
232
  client?: ClientInstance<TRequest, TResponse>;
@@ -270,9 +247,6 @@ export type ClientConfig = {
270
247
  onValidationError?: ValidationErrorHandler;
271
248
  auth?: AuthResolver;
272
249
  };
273
- /**
274
- * The result a resolved call produces before it is cast to `RequestResult` by the generated wrapper.
275
- */
276
250
  export type CallResult<TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
277
251
  status: number;
278
252
  data: unknown;
@@ -283,7 +257,7 @@ export type CallResult<TRequest = AxiosRequestConfig, TResponse = AxiosResponse>
283
257
  };
284
258
  export type InterceptorFn<T, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = (value: T, requestConfig?: RequestConfig<unknown, TRequest, TResponse>) => T | Promise<T>;
285
259
  /**
286
- * A single interceptor channel with a transport-agnostic `use` / `eject` / `update` API, backed by axios's native interceptor managers.
260
+ * Registers, removes, or replaces handlers on axios's native interceptor managers.
287
261
  */
288
262
  export type InterceptorChannel<T, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
289
263
  use: (fn: InterceptorFn<T, TRequest, TResponse>) => number;
@@ -291,7 +265,7 @@ export type InterceptorChannel<T, TRequest = AxiosRequestConfig, TResponse = Axi
291
265
  update: (id: number, fn: InterceptorFn<T, TRequest, TResponse>) => void;
292
266
  };
293
267
  /**
294
- * The three interceptor channels every client instance exposes, wrapping axios's native managers with `error` mapped onto the response rejection handler.
268
+ * Maps error interceptors to axios's response rejection handler.
295
269
  */
296
270
  export type Interceptors<TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
297
271
  request: InterceptorChannel<InternalAxiosRequestConfig, TRequest, TResponse>;
@@ -347,7 +321,7 @@ export declare function resolveAuth(params: {
347
321
  query: Record<string, unknown>;
348
322
  }): Promise<void>;
349
323
  /**
350
- * Builds the shared client core bound to an axios instance (defaulting to `defaultTransport`, else `axios.create()`), with `throwOnError` riding axios's `validateStatus`.
324
+ * Uses the configured axios instance or creates one. `throwOnError` sets axios's default `validateStatus`.
351
325
  */
352
326
  export declare function createClientCore<TRequest = AxiosRequestConfig, TResponse = AxiosResponse>(options?: {
353
327
  defaultTransport?: AxiosInstance;
@@ -370,16 +344,14 @@ export type ServerSentEvent<TData = unknown> = {
370
344
  */
371
345
  export declare function parseEventStream<TData = unknown>(stream: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>): AsyncGenerator<ServerSentEvent<TData>>;
372
346
  /**
373
- * The resolved shape returned by a generated `text/event-stream` operation: the typed event
374
- * `stream` plus the native `response`.
347
+ * Returns parsed events alongside the native response.
375
348
  */
376
349
  export type EventStreamResult<TData = unknown, TResponse = AxiosResponse> = {
377
350
  stream: AsyncGenerator<ServerSentEvent<TData>>;
378
351
  response: TResponse;
379
352
  };
380
353
  /**
381
- * Wraps a transport result whose `data` is a streaming body into an `EventStreamResult`, exposing
382
- * the parsed events as a typed async iterator. Generated SSE operations call this.
354
+ * Exposes a streaming response body as typed Server-Sent Events.
383
355
  */
384
356
  export declare function toEventStream<TData = unknown>(result: Promise<{
385
357
  data: unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-axios",
3
- "version": "5.9.1",
3
+ "version": "5.9.2",
4
4
  "description": "Generate a slim, type-safe HTTP client with Kubb based on axios.",
5
5
  "keywords": [
6
6
  "api-client",
@@ -80,19 +80,19 @@
80
80
  "registry": "https://registry.npmjs.org/"
81
81
  },
82
82
  "dependencies": {
83
- "@kubb/plugin-ts": "^5.1.0",
84
- "@kubb/plugin-zod": "^5.7.1"
83
+ "@kubb/plugin-ts": "^5.2.0",
84
+ "@kubb/plugin-zod": "^5.7.2"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@internals/client": "0.0.0",
88
88
  "@internals/shared": "0.0.0",
89
89
  "axios": "^1.20.0",
90
- "kubb": "^5.13.0",
90
+ "kubb": "^5.14.0",
91
91
  "typescript": "^7.0.2"
92
92
  },
93
93
  "peerDependencies": {
94
94
  "axios": "^1.20.0",
95
- "kubb": "^5.13.0"
95
+ "kubb": "^5.14.0"
96
96
  },
97
97
  "peerDependenciesMeta": {
98
98
  "axios": {
@@ -9,14 +9,8 @@ import { ParseError, type StandardSchemaValidator, validateStandardSchema } from
9
9
  */
10
10
  export type SuccessStatusCode = '200' | '201' | '202' | '203' | '204' | '205' | '206' | '207' | '208' | '226'
11
11
 
12
- /**
13
- * The success members of a per-status responses record.
14
- */
15
12
  export type SuccessOf<TResponses> = TResponses[Extract<keyof TResponses, SuccessStatusCode>]
16
13
 
17
- /**
18
- * The error members of a per-status responses record, every documented status that is not a 2xx.
19
- */
20
14
  export type ErrorOf<TResponses> = TResponses[Exclude<keyof TResponses, SuccessStatusCode>]
21
15
 
22
16
  /**
@@ -25,12 +19,12 @@ export type ErrorOf<TResponses> = TResponses[Exclude<keyof TResponses, SuccessSt
25
19
  export type ToStatusNumber<TStatus> = TStatus extends `${infer TNumber extends number}` ? TNumber : number
26
20
 
27
21
  /**
28
- * The plain body of a per-status response, unwrapping the `{ contentType; data }` union so an error result keeps the bare body union on `error`.
22
+ * Unwraps content-type variants to keep the bare body on `error`.
29
23
  */
30
24
  export type DataOf<T> = T extends { contentType: string; data: infer TData } ? TData : T
31
25
 
32
26
  /**
33
- * The success variant for a single status, flattened so the negotiated `contentType` sits next to `data` and `switch (result.contentType)` narrows it.
27
+ * Keeps `contentType` beside `data` so switching on it narrows the body.
34
28
  */
35
29
  export type SuccessVariant<TStatus, TEntry, TRequest, TResponse> = TEntry extends { contentType: string; data: unknown }
36
30
  ? TEntry extends { contentType: infer TContentType; data: infer TData }
@@ -39,7 +33,7 @@ export type SuccessVariant<TStatus, TEntry, TRequest, TResponse> = TEntry extend
39
33
  : { status: ToStatusNumber<TStatus>; data: TEntry; error: undefined; contentType: string | undefined; request: TRequest; response: TResponse }
40
34
 
41
35
  /**
42
- * One result variant for a single documented status, keyed by the numeric `status` so a `switch (result.status)` narrows `data` or `error`.
36
+ * Narrows `data` or `error` when switching on the numeric status.
43
37
  */
44
38
  export type ResultByStatus<TResponses, TStatus extends keyof TResponses, TRequest, TResponse> = TStatus extends SuccessStatusCode
45
39
  ? SuccessVariant<TStatus, TResponses[TStatus], TRequest, TResponse>
@@ -52,22 +46,19 @@ export type ResultByStatus<TResponses, TStatus extends keyof TResponses, TReques
52
46
  response: TResponse
53
47
  }
54
48
 
55
- /**
56
- * The union of every documented status' result variant.
57
- */
58
49
  export type ResultUnion<TResponses, TRequest, TResponse> = {
59
50
  [TStatus in keyof TResponses]: ResultByStatus<TResponses, TStatus, TRequest, TResponse>
60
51
  }[keyof TResponses]
61
52
 
62
53
  /**
63
- * The union of just the success (2xx) status variants, selected by status code so an untyped error payload can never widen `data`.
54
+ * Selects success statuses without letting untyped error payloads widen `data`.
64
55
  */
65
56
  export type SuccessResultUnion<TResponses, TRequest, TResponse> = {
66
57
  [TStatus in Extract<keyof TResponses, SuccessStatusCode>]: ResultByStatus<TResponses, TStatus, TRequest, TResponse>
67
58
  }[Extract<keyof TResponses, SuccessStatusCode>]
68
59
 
69
60
  /**
70
- * The shape every generated function returns, discriminated by the top-level `status`, narrowing to the 2xx variants under `throwOnError` and to every documented status without it.
61
+ * Narrows `data` and `error` by status. With `throwOnError`, includes only success variants.
71
62
  */
72
63
  export type RequestResult<TResponses, ThrowOnError extends boolean = true, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = ThrowOnError extends true
73
64
  ? [SuccessResultUnion<TResponses, TRequest, TResponse>] extends [never]
@@ -94,11 +85,15 @@ export type Unwrappable<T extends { data: unknown; error: unknown }> = Promise<T
94
85
  /**
95
86
  * Attaches `unwrap()` to a result promise, which rejects with `error` when the result carried one.
96
87
  *
97
- * @example Full result
98
- * `const { data, error } = await getPetById({ path: { petId: 1 } })`
88
+ * @example
89
+ * ```ts
90
+ * const { data, error } = await getPetById({ path: { petId: 1 } })
91
+ * ```
99
92
  *
100
- * @example Success body only
101
- * `const pet = await getPetById({ path: { petId: 1 } }).unwrap()`
93
+ * @example
94
+ * ```ts
95
+ * const pet = await getPetById({ path: { petId: 1 } }).unwrap()
96
+ * ```
102
97
  */
103
98
  export function withUnwrap<T extends { data: unknown; error: unknown }>(promise: Promise<T>): Unwrappable<T> {
104
99
  const unwrappable = promise as Unwrappable<T>
@@ -123,33 +118,27 @@ export type UnwrappedResult<
123
118
 
124
119
  /**
125
120
  * Returns the success body when `throwOnError` is true, or the full result when it is false.
126
- * Backs generated operations with `returnType: 'data'`.
127
121
  */
128
122
  export function unwrapResult<T extends { data: unknown; error: unknown }>(promise: Promise<T>, throwOnError: boolean | undefined): Promise<T | T['data']> {
129
123
  return promise.then((result) => ((throwOnError ?? true) ? result.data : result))
130
124
  }
131
125
 
132
- /**
133
- * The data-shaped keys of the grouped options object, which `Options` re-adds typed per operation.
134
- */
135
126
  export type DataShape = { body?: unknown; cookies?: unknown; headers?: unknown; path?: unknown; query?: unknown }
136
127
 
137
128
  export type ResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream' | 'formdata'
138
129
 
139
130
  /**
140
- * Turns a raw response body into a parsed value, registered per media type as a codec's `deserialize` to handle formats the runtime does not decode itself.
131
+ * Decodes a response body for a codec's media type.
141
132
  */
142
133
  export type Deserializer<T = unknown> = (raw: unknown, contentType: string) => T | Promise<T>
143
134
 
144
135
  /**
145
- * Serializes a request body for a single media type, registered per content type as a codec's `serialize` to encode formats the default serializer does not handle.
136
+ * Encodes a request body for a codec's media type.
146
137
  */
147
138
  export type ContentBodySerializer = (body: unknown, contentType?: string) => unknown
148
139
 
149
140
  /**
150
- * A per-content-type codec registered on `codecs`, keyed by content type. `serialize` encodes the
151
- * request body for that media type and `deserialize` decodes the response body. Either half is
152
- * optional, so a codec can handle one direction.
141
+ * Encodes requests or decodes responses for a media type. Either direction can be omitted.
153
142
  */
154
143
  export type Codec = {
155
144
  serialize?: ContentBodySerializer
@@ -157,23 +146,17 @@ export type Codec = {
157
146
  }
158
147
 
159
148
  /**
160
- * The per-call content type selection, where a bare string sets the request content type and the object form also sets the response format sent as `Accept`.
149
+ * A string sets the request content type. The object form can also set `Accept`.
161
150
  */
162
151
  export type ContentType = string | { request?: string; response?: string }
163
152
 
164
153
  /**
165
- * A Standard Schema validator (zod, valibot, arktype) that parses a value before it is sent or after
166
- * it is received. `runValidator` runs it through `validateStandardSchema`. Wired through the per-call
167
- * `validator.request` / `validator.response` / `validator.error` hooks (`error` runs on the error body when a
168
- * non-2xx call does not throw), and `validator.path` / `validator.query` / `validator.headers` for the
169
- * request params.
154
+ * Validates request bodies and params, success bodies, or non-throwing error bodies with Standard Schema.
170
155
  */
171
156
  export type Validator<T = unknown> = StandardSchemaValidator<T>
172
157
 
173
158
  /**
174
- * The failing value and the call it came from, handed to `onValidationError` alongside the `ParseError`.
175
- * `direction` says which side rejected it: the request, the success body, or the error body. `source`
176
- * says which part of it: the body, or one of the request param groups.
159
+ * Identifies the value and request that failed validation in `onValidationError`.
177
160
  */
178
161
  export type ValidationErrorContext = {
179
162
  value: unknown
@@ -185,9 +168,7 @@ export type ValidationErrorContext = {
185
168
  }
186
169
 
187
170
  /**
188
- * Decides what a failed validation does. Returning nothing rethrows the `ParseError`; returning
189
- * `{ value }` resolves the call with that value instead, so a drifted body can be reported and still
190
- * delivered. The box keeps an explicit `{ value: undefined }` substitution distinct from declining.
171
+ * Return `{ value }` to substitute a failed value, including `undefined`. Returning nothing rethrows the `ParseError`.
191
172
  */
192
173
  export type ValidationErrorHandler = (error: ParseError, context: ValidationErrorContext) => { value: unknown } | void | Promise<{ value: unknown } | void>
193
174
 
@@ -203,7 +184,7 @@ export type Auth = {
203
184
  }
204
185
 
205
186
  /**
206
- * The raw token a consumer returns for a scheme (or `user:password` for basic), or `undefined` to skip it.
187
+ * Return `user:password` for basic auth, or `undefined` to skip a scheme.
207
188
  */
208
189
  export type AuthToken = string | undefined
209
190
 
@@ -217,9 +198,6 @@ export type AuthResolver = AuthToken | ((auth: Auth) => AuthToken | Promise<Auth
217
198
  */
218
199
  export type AxiosOptions = AxiosRequestConfig
219
200
 
220
- /**
221
- * The request a generated function hands to the runtime, with `body` / `headers` / `path` / `query` from the grouped options.
222
- */
223
201
  export type RequestConfig<TBody = unknown, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
224
202
  baseURL?: string
225
203
  url?: string
@@ -248,8 +226,7 @@ export type RequestConfig<TBody = unknown, TRequest = AxiosRequestConfig, TRespo
248
226
  }
249
227
 
250
228
  /**
251
- * The grouped options object passed to every generated function: the request config minus the
252
- * data-shaped keys and the literal `url`, plus the per-operation `<Name>Request`.
229
+ * Combines client options with the operation's typed request params and body.
253
230
  */
254
231
  export type Options<TData extends DataShape, ThrowOnError extends boolean = true, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = Omit<
255
232
  RequestConfig<unknown, TRequest, TResponse>,
@@ -276,9 +253,6 @@ export type ClientConfig = {
276
253
  auth?: AuthResolver
277
254
  }
278
255
 
279
- /**
280
- * The result a resolved call produces before it is cast to `RequestResult` by the generated wrapper.
281
- */
282
256
  export type CallResult<TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
283
257
  status: number
284
258
  data: unknown
@@ -294,7 +268,7 @@ export type InterceptorFn<T, TRequest = AxiosRequestConfig, TResponse = AxiosRes
294
268
  ) => T | Promise<T>
295
269
 
296
270
  /**
297
- * A single interceptor channel with a transport-agnostic `use` / `eject` / `update` API, backed by axios's native interceptor managers.
271
+ * Registers, removes, or replaces handlers on axios's native interceptor managers.
298
272
  */
299
273
  export type InterceptorChannel<T, TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
300
274
  use: (fn: InterceptorFn<T, TRequest, TResponse>) => number
@@ -303,7 +277,7 @@ export type InterceptorChannel<T, TRequest = AxiosRequestConfig, TResponse = Axi
303
277
  }
304
278
 
305
279
  /**
306
- * The three interceptor channels every client instance exposes, wrapping axios's native managers with `error` mapped onto the response rejection handler.
280
+ * Maps error interceptors to axios's response rejection handler.
307
281
  */
308
282
  export type Interceptors<TRequest = AxiosRequestConfig, TResponse = AxiosResponse> = {
309
283
  request: InterceptorChannel<InternalAxiosRequestConfig, TRequest, TResponse>
@@ -410,13 +384,6 @@ function getHeader(headers: Record<string, string>, name: string): string | unde
410
384
  return key ? headers[key] : undefined
411
385
  }
412
386
 
413
- function hasHeader(headers: Record<string, string>, name: string): boolean {
414
- return Object.keys(headers).some((k) => k.toLowerCase() === name.toLowerCase())
415
- }
416
-
417
- /**
418
- * Joins the URL parts, interpolates URL-encoded `{param}` segments, and appends the serialized query, backing `getUrl`.
419
- */
420
387
  function serializeUrl({
421
388
  parts,
422
389
  pathParams,
@@ -437,69 +404,48 @@ function serializeUrl({
437
404
  return path + (search ? `?${search}` : '')
438
405
  }
439
406
 
440
- /**
441
- * Wraps an axios interceptor registration behind the shared `use` / `eject` / `update` API, mapping a stable external id onto axios's own so `update` can swap a handler in place.
442
- * `detach` / `attach` move every registered handler to another instance, keeping the external ids.
443
- */
444
- function createInterceptorChannel<T, TRequest, TResponse>(
445
- register: (fn: InterceptorFn<T, TRequest, TResponse>) => number,
446
- ejectNative: (id: number) => void,
447
- getRequestConfig: (value: T) => RequestConfig<unknown, TRequest, TResponse> | undefined,
448
- getContextId: (value: T) => number | undefined,
449
- setContextId: (value: T, id: number | undefined) => void,
450
- nextSeq?: () => number,
451
- ): InterceptorChannel<T, TRequest, TResponse> & {
407
+ function createInterceptorChannel<T, TRequest, TResponse>({
408
+ register,
409
+ ejectNative,
410
+ nextSeq,
411
+ }: {
412
+ register: (fn: InterceptorFn<T, TRequest, TResponse>) => number
413
+ ejectNative: (id: number) => void
414
+ nextSeq: () => number
415
+ }): InterceptorChannel<T, TRequest, TResponse> & {
452
416
  detach: () => void
453
- attach: () => void
454
417
  getEntries: () => Array<{ seq: number; attach: () => void }>
455
418
  } {
456
- const ids = new Map<number, number>()
457
- const handlers = new Map<number, { fn: InterceptorFn<T, TRequest, TResponse>; seq: number }>()
419
+ const handlers = new Map<number, { fn: InterceptorFn<T, TRequest, TResponse>; seq: number; nativeId: number }>()
458
420
  let counter = 0
459
- const getSeq = nextSeq ?? (() => ++counter)
460
- const registerWithContext = (fn: InterceptorFn<T, TRequest, TResponse>) =>
461
- register(async (value) => {
462
- const contextId = getContextId(value)
463
- const result = await fn(value, getRequestConfig(value))
464
- if (contextId !== undefined) setContextId(result, contextId)
465
- return result
466
- })
467
421
 
468
422
  return {
469
423
  use(fn) {
470
424
  const id = ++counter
471
- const seq = getSeq()
472
- handlers.set(id, { fn, seq })
473
- ids.set(id, registerWithContext(fn))
425
+ handlers.set(id, { fn, seq: nextSeq(), nativeId: register(fn) })
474
426
  return id
475
427
  },
476
428
  eject(id) {
477
- const nativeId = ids.get(id)
478
- if (nativeId === undefined) return
479
- ejectNative(nativeId)
480
- ids.delete(id)
429
+ const entry = handlers.get(id)
430
+ if (!entry) return
431
+ ejectNative(entry.nativeId)
481
432
  handlers.delete(id)
482
433
  },
483
434
  update(id, fn) {
484
- const nativeId = ids.get(id)
485
- if (nativeId === undefined) return
486
- ejectNative(nativeId)
487
- const seq = getSeq()
488
- handlers.set(id, { fn, seq })
489
- ids.set(id, registerWithContext(fn))
435
+ const entry = handlers.get(id)
436
+ if (!entry) return
437
+ ejectNative(entry.nativeId)
438
+ handlers.set(id, { fn, seq: nextSeq(), nativeId: register(fn) })
490
439
  },
491
440
  detach() {
492
- for (const nativeId of ids.values()) ejectNative(nativeId)
493
- ids.clear()
494
- },
495
- attach() {
496
- const sorted = Array.from(handlers.entries()).sort((a, b) => a[1].seq - b[1].seq)
497
- for (const [id, entry] of sorted) ids.set(id, registerWithContext(entry.fn))
441
+ for (const entry of handlers.values()) ejectNative(entry.nativeId)
498
442
  },
499
443
  getEntries() {
500
- return Array.from(handlers.entries()).map(([id, entry]) => ({
444
+ return Array.from(handlers.values(), (entry) => ({
501
445
  seq: entry.seq,
502
- attach: () => ids.set(id, registerWithContext(entry.fn)),
446
+ attach() {
447
+ entry.nativeId = register(entry.fn)
448
+ },
503
449
  }))
504
450
  },
505
451
  }
@@ -521,18 +467,23 @@ export async function resolveAuth(params: {
521
467
  const token = typeof auth === 'function' ? await auth(scheme) : auth
522
468
  if (token === undefined) continue
523
469
 
524
- if (scheme.type === 'apiKey') {
525
- const name = scheme.name ?? 'Authorization'
526
- if (scheme.in === 'query') {
527
- if (query[name] === undefined) query[name] = token
528
- } else if (scheme.in === 'cookie') {
529
- headers['Cookie'] = [headers['Cookie'], `${name}=${token}`].filter(Boolean).join('; ')
530
- } else if (!hasHeader(headers, name)) {
531
- headers[name] = token
470
+ if (scheme.type !== 'apiKey') {
471
+ if (getHeader(headers, 'Authorization') === undefined) {
472
+ headers['Authorization'] = scheme.scheme === 'basic' ? `Basic ${btoa(token)}` : `Bearer ${token}`
532
473
  }
533
- } else if (!hasHeader(headers, 'Authorization')) {
534
- headers['Authorization'] = scheme.scheme === 'basic' ? `Basic ${btoa(token)}` : `Bearer ${token}`
474
+ return
475
+ }
476
+
477
+ const name = scheme.name ?? 'Authorization'
478
+ if (scheme.in === 'query') {
479
+ if (query[name] === undefined) query[name] = token
480
+ return
481
+ }
482
+ if (scheme.in === 'cookie') {
483
+ headers['Cookie'] = [headers['Cookie'], `${name}=${token}`].filter(Boolean).join('; ')
484
+ return
535
485
  }
486
+ if (getHeader(headers, name) === undefined) headers[name] = token
536
487
  return
537
488
  }
538
489
  }
@@ -559,35 +510,17 @@ async function runValidator<T>({
559
510
  }
560
511
  }
561
512
 
562
- /**
563
- * The base media type of a `Content-Type` value, lowercased and stripped of any `; charset=...` parameters.
564
- */
565
513
  function baseContentType(value: string | null | undefined): string | undefined {
566
514
  if (!value) return undefined
567
515
  return value.split(';')[0]!.trim().toLowerCase() || undefined
568
516
  }
569
517
 
570
- /**
571
- * Reads the negotiated response content type from the response headers as a base media type.
572
- */
573
518
  function getResponseContentType(headers: Record<string, unknown> | undefined): string | undefined {
574
519
  if (!headers) return undefined
575
520
  const value = headers['content-type'] ?? headers['Content-Type']
576
521
  return baseContentType(typeof value === 'string' ? value : undefined)
577
522
  }
578
523
 
579
- /**
580
- * Normalizes the `contentType` option to its `{ request, response }` form, treating a bare string as the request content type.
581
- */
582
- function resolveContentType(contentType: ContentType | undefined): { request?: string; response?: string } {
583
- if (typeof contentType === 'string') return { request: contentType }
584
- return contentType ?? {}
585
- }
586
-
587
- /**
588
- * The per-concern serializers for a call, the per-call serializer winning over the client's and
589
- * falling back to the defaults.
590
- */
591
524
  function resolveSerializers({ config, requestConfig }: { config: { serializer?: Serializers }; requestConfig: { serializer?: Serializers } }) {
592
525
  return {
593
526
  querySerializer: requestConfig.serializer?.query ?? config.serializer?.query ?? defaultQuerySerializer,
@@ -596,11 +529,6 @@ function resolveSerializers({ config, requestConfig }: { config: { serializer?:
596
529
  }
597
530
  }
598
531
 
599
- /**
600
- * Resolves everything a call needs before it touches axios: merged headers with the negotiated
601
- * content type, auth on headers or query, serialized cookies, the validated and serialized body,
602
- * and the final axios request config with `throwOnError` riding `validateStatus`.
603
- */
604
532
  async function resolveRequest<TBody, TRequest, TResponse>({
605
533
  config,
606
534
  requestConfig,
@@ -626,9 +554,10 @@ async function resolveRequest<TBody, TRequest, TResponse>({
626
554
  )
627
555
 
628
556
  const headers = serializeHeaders(applyHeaderStyles(validatedHeaders as HeadersInit | undefined, requestConfig.styles?.header))
629
- const { request: requestContentTypeOption, response: responseContentType } = resolveContentType(requestConfig.contentType)
557
+ const contentType = requestConfig.contentType
558
+ const { request: requestContentTypeOption, response: responseContentType } = typeof contentType === 'string' ? { request: contentType } : (contentType ?? {})
630
559
  const requestContentType = requestContentTypeOption ?? getHeader(headers, 'content-type')
631
- if (responseContentType && !hasHeader(headers, 'accept')) {
560
+ if (responseContentType && getHeader(headers, 'accept') === undefined) {
632
561
  headers['Accept'] = responseContentType
633
562
  }
634
563
 
@@ -658,17 +587,18 @@ async function resolveRequest<TBody, TRequest, TResponse>({
658
587
  const body = contentCodec?.serialize
659
588
  ? contentCodec.serialize(validatedBody, requestContentType)
660
589
  : bodySerializer({ body: validatedBody, contentType: requestContentType, encoding: requestConfig.styles?.body })
661
- // A FormData body must keep its Content-Type unset so axios appends the multipart boundary.
590
+ if (requestContentTypeOption) headers['Content-Type'] = requestContentTypeOption
591
+ if (body instanceof URLSearchParams && getHeader(headers, 'content-type') === undefined) {
592
+ headers['Content-Type'] = 'application/x-www-form-urlencoded'
593
+ }
594
+ if (usesDefaultBodySerializer && isDefaultJsonBody(validatedBody) && getHeader(headers, 'content-type') === undefined) {
595
+ headers['Content-Type'] = 'application/json'
596
+ }
597
+ // The transport must set the multipart boundary, even when a content type was explicitly provided.
662
598
  if (body instanceof FormData) {
663
599
  for (const key of Object.keys(headers)) {
664
600
  if (key.toLowerCase() === 'content-type') delete headers[key]
665
601
  }
666
- } else if (requestContentTypeOption) {
667
- headers['Content-Type'] = requestContentTypeOption
668
- } else if (body instanceof URLSearchParams && !hasHeader(headers, 'content-type')) {
669
- headers['Content-Type'] = 'application/x-www-form-urlencoded'
670
- } else if (usesDefaultBodySerializer && isDefaultJsonBody(validatedBody) && !hasHeader(headers, 'content-type')) {
671
- headers['Content-Type'] = 'application/json'
672
602
  }
673
603
 
674
604
  const pathParams = (validatedPath ?? {}) as Record<string, unknown>
@@ -683,7 +613,7 @@ async function resolveRequest<TBody, TRequest, TResponse>({
683
613
  const options = config.options || requestConfig.options ? { ...config.options, ...requestConfig.options } : undefined
684
614
 
685
615
  const axiosConfig: AxiosRequestConfig = {
686
- ...options, // timeout, proxy, maxRedirects, decompress, onUploadProgress, …
616
+ ...options,
687
617
  url,
688
618
  baseURL: requestConfig.baseURL ?? config.baseURL,
689
619
  method: requestConfig.method ?? 'GET',
@@ -708,61 +638,7 @@ async function resolveRequest<TBody, TRequest, TResponse>({
708
638
  }
709
639
 
710
640
  /**
711
- * Turns an axios response into the call result: decodes the body through the matching codec and
712
- * validates the success or error body. A thrown axios error never reaches this, so `error` here is
713
- * always the body of a non-2xx response that `validateStatus` let through.
714
- */
715
- async function settleResponse<TRequest, TResponse>({
716
- response,
717
- codecs,
718
- validator,
719
- onValidationError,
720
- }: {
721
- response: AxiosResponse
722
- codecs: Record<string, Codec>
723
- validator: { response?: Validator; error?: Validator } | undefined
724
- onValidationError: ValidationErrorHandler | undefined
725
- }): Promise<CallResult<TRequest, TResponse>> {
726
- const isSuccess = response.status >= 200 && response.status < 300
727
- const contentType = getResponseContentType(response.headers as Record<string, unknown>)
728
- let decoded: unknown = response.data
729
- if (contentType) {
730
- const codec = codecs[contentType]
731
- if (codec?.deserialize) decoded = await codec.deserialize(response.data, contentType)
732
- }
733
- const validationContext = {
734
- method: response.config?.method?.toUpperCase(),
735
- url: response.config?.url,
736
- status: response.status,
737
- }
738
- const data = isSuccess
739
- ? await runValidator({
740
- validator: validator?.response,
741
- value: decoded,
742
- context: { direction: 'response', source: 'body', ...validationContext },
743
- onValidationError,
744
- })
745
- : undefined
746
- const error = isSuccess
747
- ? undefined
748
- : await runValidator({
749
- validator: validator?.error,
750
- value: decoded,
751
- context: { direction: 'error', source: 'body', ...validationContext },
752
- onValidationError,
753
- })
754
- return {
755
- status: response.status,
756
- data,
757
- error,
758
- contentType,
759
- request: response.config as TRequest,
760
- response: response as TResponse,
761
- }
762
- }
763
-
764
- /**
765
- * Builds the shared client core bound to an axios instance (defaulting to `defaultTransport`, else `axios.create()`), with `throwOnError` riding axios's `validateStatus`.
641
+ * Uses the configured axios instance or creates one. `throwOnError` sets axios's default `validateStatus`.
766
642
  */
767
643
  export function createClientCore<TRequest = AxiosRequestConfig, TResponse = AxiosResponse>(
768
644
  options: { defaultTransport?: AxiosInstance } & ClientConfig = {},
@@ -778,51 +654,54 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
778
654
  const axiosConfig = 'config' in value ? value.config : value
779
655
  return (axiosConfig as (AxiosRequestConfig & { __kubbRequestContext?: number }) | undefined)?.__kubbRequestContext
780
656
  }
781
- const getRequestConfig = (value: AxiosRequestConfig | AxiosResponse | AxiosError) => {
782
- const id = getContextId(value)
783
- return id === undefined ? undefined : requestContexts.get(id)
784
- }
785
657
  const setContextId = (value: AxiosRequestConfig | AxiosResponse | AxiosError, id: number | undefined) => {
786
658
  if (!value || typeof value !== 'object') return
787
659
  const axiosConfig = 'config' in value ? value.config : value
788
660
  if (!axiosConfig) return
789
661
  const contextualConfig = axiosConfig as AxiosRequestConfig & { __kubbRequestContext?: number }
790
- if (id === undefined) delete contextualConfig.__kubbRequestContext
791
- else contextualConfig.__kubbRequestContext = id
662
+ if (id === undefined) {
663
+ delete contextualConfig.__kubbRequestContext
664
+ return
665
+ }
666
+ contextualConfig.__kubbRequestContext = id
792
667
  }
793
668
 
669
+ const withContext =
670
+ <T extends AxiosRequestConfig | AxiosResponse | AxiosError>(fn: InterceptorFn<T, TRequest, TResponse>) =>
671
+ async (value: T) => {
672
+ const contextId = getContextId(value)
673
+ const requestConfig = contextId === undefined ? undefined : requestContexts.get(contextId)
674
+ const result = await fn(value, requestConfig)
675
+ if (contextId !== undefined) setContextId(result, contextId)
676
+ return result
677
+ }
678
+
794
679
  let sequenceCounter = 0
795
680
  const nextSeq = () => ++sequenceCounter
796
681
 
797
682
  // Read at call time so the channels follow a transport swapped in through setConfig.
798
683
  const channels = {
799
- request: createInterceptorChannel<InternalAxiosRequestConfig, TRequest, TResponse>(
800
- (fn) => instance.interceptors.request.use(fn),
801
- (id) => instance.interceptors.request.eject(id),
802
- getRequestConfig,
803
- getContextId,
804
- setContextId,
684
+ request: createInterceptorChannel<InternalAxiosRequestConfig, TRequest, TResponse>({
805
685
  nextSeq,
806
- ),
807
- response: createInterceptorChannel<AxiosResponse, TRequest, TResponse>(
808
- (fn) => instance.interceptors.response.use(fn),
809
- (id) => instance.interceptors.response.eject(id),
810
- getRequestConfig,
811
- getContextId,
812
- setContextId,
686
+ register: (fn) => instance.interceptors.request.use(withContext(fn)),
687
+ ejectNative: (id) => instance.interceptors.request.eject(id),
688
+ }),
689
+ response: createInterceptorChannel<AxiosResponse, TRequest, TResponse>({
813
690
  nextSeq,
814
- ),
815
- error: createInterceptorChannel<AxiosError, TRequest, TResponse>(
816
- (fn) =>
817
- instance.interceptors.response.use(undefined, (error: unknown) =>
818
- Promise.resolve(fn(error as AxiosError)).then((result) => Promise.reject(result ?? error)),
819
- ),
820
- (id) => instance.interceptors.response.eject(id),
821
- getRequestConfig,
822
- getContextId,
823
- setContextId,
691
+ register: (fn) => instance.interceptors.response.use(withContext(fn)),
692
+ ejectNative: (id) => instance.interceptors.response.eject(id),
693
+ }),
694
+ error: createInterceptorChannel<AxiosError, TRequest, TResponse>({
824
695
  nextSeq,
825
- ),
696
+ register: (fn) => {
697
+ const onError = withContext(fn)
698
+ return instance.interceptors.response.use(undefined, async (error: unknown) => {
699
+ const result = await onError(error as AxiosError)
700
+ throw result ?? error
701
+ })
702
+ },
703
+ ejectNative: (id) => instance.interceptors.response.eject(id),
704
+ }),
826
705
  }
827
706
  const channelList = [channels.request, channels.response, channels.error]
828
707
  const interceptors: Interceptors<TRequest, TResponse> = {
@@ -839,34 +718,52 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
839
718
  ;(axiosConfig as AxiosRequestConfig & { __kubbRequestContext: number }).__kubbRequestContext = contextId
840
719
 
841
720
  try {
842
- try {
843
- const response = await activeInstance.request<unknown, AxiosResponse>(axiosConfig)
844
- const result = await settleResponse<TRequest, TResponse>({
845
- response,
846
- codecs,
847
- validator: requestConfig.validator,
848
- onValidationError: requestConfig.onValidationError ?? config.onValidationError,
721
+ const response = await activeInstance.request<unknown, AxiosResponse>(axiosConfig)
722
+ const validator = requestConfig.validator
723
+ const onValidationError = requestConfig.onValidationError ?? config.onValidationError
724
+ const isSuccess = response.status >= 200 && response.status < 300
725
+ const contentType = getResponseContentType(response.headers as Record<string, unknown>)
726
+ const codec = contentType ? codecs[contentType] : undefined
727
+ const decoded: unknown = contentType && codec?.deserialize ? await codec.deserialize(response.data, contentType) : response.data
728
+ const data = await runValidator({
729
+ validator: isSuccess ? validator?.response : validator?.error,
730
+ value: decoded,
731
+ context: {
732
+ direction: isSuccess ? 'response' : 'error',
733
+ source: 'body',
734
+ method: response.config?.method?.toUpperCase(),
735
+ url: response.config?.url,
736
+ status: response.status,
737
+ },
738
+ onValidationError,
739
+ })
740
+ const result = {
741
+ status: response.status,
742
+ data: isSuccess ? data : undefined,
743
+ error: isSuccess ? undefined : data,
744
+ contentType,
745
+ request: response.config as TRequest,
746
+ response: response as TResponse,
747
+ }
748
+ setContextId(response, undefined)
749
+ return result
750
+ } catch (error) {
751
+ const axiosError = error as AxiosError
752
+ setContextId(axiosError, undefined)
753
+ if (axiosError.response) setContextId(axiosError.response, undefined)
754
+ if (throwOnError && axiosError.response) {
755
+ throw new ResponseError({
756
+ data: axiosError.response.data,
757
+ status: axiosError.response.status,
758
+ statusText: axiosError.response.statusText,
759
+ contentType: getResponseContentType(axiosError.response.headers as Record<string, unknown>),
760
+ request: axiosError.config as TRequest,
761
+ response: axiosError.response as TResponse,
762
+ method: axiosError.config?.method?.toUpperCase(),
763
+ url: [axiosError.config?.baseURL?.replace(/\/+$/, ''), axiosError.config?.url].filter(Boolean).join(''),
849
764
  })
850
- setContextId(response, undefined)
851
- return result
852
- } catch (error) {
853
- const axiosError = error as AxiosError
854
- setContextId(axiosError, undefined)
855
- if (axiosError.response) setContextId(axiosError.response, undefined)
856
- if (throwOnError && axiosError.response) {
857
- throw new ResponseError({
858
- data: axiosError.response.data,
859
- status: axiosError.response.status,
860
- statusText: axiosError.response.statusText,
861
- contentType: getResponseContentType(axiosError.response.headers as Record<string, unknown>),
862
- request: axiosError.config as TRequest,
863
- response: axiosError.response as TResponse,
864
- method: axiosError.config?.method?.toUpperCase(),
865
- url: [axiosError.config?.baseURL?.replace(/\/+$/, ''), axiosError.config?.url].filter(Boolean).join(''),
866
- })
867
- }
868
- throw error
869
765
  }
766
+ throw error
870
767
  } finally {
871
768
  setContextId(axiosConfig, undefined)
872
769
  requestContexts.delete(contextId)
@@ -880,9 +777,8 @@ export function createClientCore<TRequest = AxiosRequestConfig, TResponse = Axio
880
777
  if (nextInstance !== instance) {
881
778
  for (const channel of channelList) channel.detach()
882
779
  instance = nextInstance
883
- channels.request.attach()
884
- const responseEntries = [...channels.response.getEntries(), ...channels.error.getEntries()].sort((a, b) => a.seq - b.seq)
885
- for (const entry of responseEntries) entry.attach()
780
+ const entries = channelList.flatMap((channel) => channel.getEntries()).sort((a, b) => a.seq - b.seq)
781
+ for (const entry of entries) entry.attach()
886
782
  }
887
783
  return config
888
784
  }
@@ -947,10 +843,20 @@ function parseEvent<TData>(raw: string): ServerSentEvent<TData> | undefined {
947
843
  const index = line.indexOf(':')
948
844
  const field = index === -1 ? line : line.slice(0, index)
949
845
  const value = index === -1 ? '' : line.slice(index + 1).replace(/^ /, '')
950
- if (field === 'data') data.push(value)
951
- else if (field === 'event') event.event = value
952
- else if (field === 'id') event.id = value
953
- else if (field === 'retry' && Number.isFinite(Number(value))) event.retry = Number(value)
846
+ switch (field) {
847
+ case 'data':
848
+ data.push(value)
849
+ break
850
+ case 'event':
851
+ event.event = value
852
+ break
853
+ case 'id':
854
+ event.id = value
855
+ break
856
+ case 'retry':
857
+ if (Number.isFinite(Number(value))) event.retry = Number(value)
858
+ break
859
+ }
954
860
  }
955
861
 
956
862
  if (!seen) return undefined
@@ -990,8 +896,7 @@ export async function* parseEventStream<TData = unknown>(
990
896
  }
991
897
 
992
898
  /**
993
- * The resolved shape returned by a generated `text/event-stream` operation: the typed event
994
- * `stream` plus the native `response`.
899
+ * Returns parsed events alongside the native response.
995
900
  */
996
901
  export type EventStreamResult<TData = unknown, TResponse = AxiosResponse> = {
997
902
  stream: AsyncGenerator<ServerSentEvent<TData>>
@@ -999,8 +904,7 @@ export type EventStreamResult<TData = unknown, TResponse = AxiosResponse> = {
999
904
  }
1000
905
 
1001
906
  /**
1002
- * Wraps a transport result whose `data` is a streaming body into an `EventStreamResult`, exposing
1003
- * the parsed events as a typed async iterator. Generated SSE operations call this.
907
+ * Exposes a streaming response body as typed Server-Sent Events.
1004
908
  */
1005
909
  export async function toEventStream<TData = unknown>(result: Promise<{ data: unknown; response: AxiosResponse }>): Promise<EventStreamResult<TData>> {
1006
910
  const { data, response } = await result
@@ -150,12 +150,17 @@ function jsonReplacer(_key: string, value: unknown): unknown {
150
150
 
151
151
  function appendFormDataValue({ formData, key, value, contentType }: { formData: FormData; key: string; value: unknown; contentType?: string }): void {
152
152
  if (value === undefined || value === null) return
153
- if (value instanceof Blob) formData.append(key, value)
154
- else if (typeof value === 'object' && !(value instanceof Date)) {
153
+ if (value instanceof Blob) {
154
+ formData.append(key, value)
155
+ return
156
+ }
157
+ if (typeof value === 'object' && !(value instanceof Date)) {
155
158
  const json = JSON.stringify(value, jsonReplacer)
156
159
  // A part's media type can only be set by wrapping the value in a typed Blob.
157
160
  formData.append(key, contentType ? new Blob([json], { type: contentType }) : json)
158
- } else formData.append(key, toValue(value))
161
+ return
162
+ }
163
+ formData.append(key, toValue(value))
159
164
  }
160
165
 
161
166
  /**
@@ -179,8 +184,11 @@ export const defaultBodySerializer: BodySerializer = ({ body, contentType, encod
179
184
  const formData = new FormData()
180
185
  for (const [key, value] of Object.entries(body as Record<string, unknown>)) {
181
186
  const partContentType = encoding?.[key]?.contentType
182
- if (Array.isArray(value)) for (const item of value) appendFormDataValue({ formData, key, value: item, contentType: partContentType })
183
- else appendFormDataValue({ formData, key, value, contentType: partContentType })
187
+ if (Array.isArray(value)) {
188
+ for (const item of value) appendFormDataValue({ formData, key, value: item, contentType: partContentType })
189
+ continue
190
+ }
191
+ appendFormDataValue({ formData, key, value, contentType: partContentType })
184
192
  }
185
193
  return formData
186
194
  }