@kubb/plugin-fetch 5.5.0-canary.20260924T110844 → 5.5.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-fetch",
3
- "version": "5.5.0-canary.20260924T110844",
3
+ "version": "5.5.0",
4
4
  "description": "Generate a slim, type-safe HTTP client with Kubb based on the native Fetch api.",
5
5
  "keywords": [
6
6
  "api-client",
@@ -65,17 +65,17 @@
65
65
  "registry": "https://registry.npmjs.org/"
66
66
  },
67
67
  "dependencies": {
68
- "@kubb/plugin-ts": "^5.1.0-canary.20260924T110844",
69
- "@kubb/plugin-zod": "^5.3.0-canary.20260924T110844"
68
+ "@kubb/plugin-ts": "^5.0.2",
69
+ "@kubb/plugin-zod": "^5.2.1"
70
70
  },
71
71
  "devDependencies": {
72
- "@internals/client": "0.1.0-canary.20260924T110844",
73
- "@internals/shared": "0.1.0-canary.20260924T110844",
74
- "kubb": "^5.3.13",
72
+ "@internals/client": "0.0.0",
73
+ "@internals/shared": "0.0.0",
74
+ "kubb": "^5.3.14",
75
75
  "typescript": "^6.0.3"
76
76
  },
77
77
  "peerDependencies": {
78
- "kubb": "^5.3.13"
78
+ "kubb": "^5.3.14"
79
79
  },
80
80
  "engines": {
81
81
  "node": ">=22"
package/src/plugin.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import path from 'node:path'
2
- import { createSdkGenerator, defaultMacros, resolverClient } from '@internals/client'
2
+ import { createSdkGenerator, defaultMacros, resolverClient, runtimeTemplate } from '@internals/client'
3
3
  import { createGroupConfig } from '@internals/shared'
4
4
  import { definePlugin, Resolver } from 'kubb/kit'
5
5
  import { pluginTsName } from '@kubb/plugin-ts'
@@ -43,6 +43,7 @@ export const pluginFetch = definePlugin<PluginFetch>((options) => {
43
43
  include,
44
44
  override = [],
45
45
  baseURL,
46
+ throwOnErrorDefault = true,
46
47
  validator = false,
47
48
  returnType = 'full',
48
49
  group,
@@ -57,6 +58,7 @@ export const pluginFetch = definePlugin<PluginFetch>((options) => {
57
58
  override,
58
59
  group: createGroupConfig(group),
59
60
  baseURL,
61
+ throwOnErrorDefault,
60
62
  validator,
61
63
  returnType,
62
64
  sdk: sdk ? { mode: sdk.mode ?? 'tag', name: sdk.name } : undefined,
@@ -93,7 +95,8 @@ export const pluginFetch = definePlugin<PluginFetch>((options) => {
93
95
  ctx.injectFile({
94
96
  baseName: 'client.ts',
95
97
  path: path.resolve(root, '.kubb/client.ts'),
96
- copy: fetchClientTemplatePath,
98
+ // Keep sources empty so the barrel exports the whole runtime module.
99
+ banner: runtimeTemplate(fetchClientTemplatePath, ctx.config),
97
100
  footer: baseURLExpression ? `client.setConfig({ baseURL: ${baseURLExpression} })` : undefined,
98
101
  })
99
102
 
@@ -1,6 +1,6 @@
1
- import { applyHeaderStyles, defaultBodySerializer, defaultPathSerializer, defaultQuerySerializer, isDefaultJsonBody, serializeCookies } from './serializers'
2
- import type { HeadersInit, PathParamStyle, PathSerializer, RequestBody, Serializers, Styles } from './serializers'
3
- import { ParseError, type StandardSchemaValidator, validateStandardSchema } from './standardSchema'
1
+ import { applyHeaderStyles, defaultBodySerializer, defaultPathSerializer, defaultQuerySerializer, isDefaultJsonBody, serializeCookies } from './serializers.ts'
2
+ import type { HeadersInit, PathParamStyle, PathSerializer, RequestBody, Serializers, Styles } from './serializers.ts'
3
+ import { ParseError, type StandardSchemaValidator, validateStandardSchema } from './standardSchema.ts'
4
4
 
5
5
  /**
6
6
  * HTTP status codes treated as a success, everything else is an error.
@@ -109,21 +109,16 @@ export function withUnwrap<T extends { data: unknown; error: unknown }>(promise:
109
109
  }
110
110
 
111
111
  /**
112
- * The shape a generated operation returns when `returnType: 'data'` is set: the bare success body
113
- * once `throwOnError` (on by default) narrows away the error branch, falling back to the full
114
- * `RequestResult` when a call sets `throwOnError: false` and still needs `error` to discriminate a
115
- * failed response.
112
+ * The shape a generated operation returns with `returnType: 'data'`: the success body when
113
+ * `throwOnError` is true, or the full `RequestResult` when it is false.
116
114
  */
117
115
  export type UnwrappedResult<TResponses, ThrowOnError extends boolean = true, TRequest = Request, TResponse = Response> = ThrowOnError extends true
118
116
  ? RequestResult<TResponses, true, TRequest, TResponse>['data']
119
117
  : RequestResult<TResponses, ThrowOnError, TRequest, TResponse>
120
118
 
121
119
  /**
122
- * Narrows a resolved call down to its success body once `throwOnError` (on by default) rules out
123
- * the error branch, the same default the runtime itself applies. Falls back to the full result for
124
- * a call that sets `throwOnError: false`, since that path still needs `error` to discriminate a
125
- * failed response. Backs `returnType: 'data'`, mirroring how `toEventStream` centralizes the
126
- * post-processing for `text/event-stream` operations.
120
+ * Returns the success body when `throwOnError` is true, or the full result when it is false.
121
+ * Backs generated operations with `returnType: 'data'`.
127
122
  */
128
123
  export function unwrapResult<T extends { data: unknown; error: unknown }>(promise: Promise<T>, throwOnError: boolean | undefined): Promise<T | T['data']> {
129
124
  return promise.then((result) => ((throwOnError ?? true) ? result.data : result))
@@ -317,25 +312,25 @@ export type CallResult<TRequest = Request, TResponse = Response> = {
317
312
  response: TResponse
318
313
  }
319
314
 
320
- export type InterceptorFn<T> = (value: T) => T | Promise<T>
315
+ export type InterceptorFn<T, TContext = never> = (value: T, context?: TContext) => T | Promise<T>
321
316
 
322
317
  /**
323
318
  * A single interceptor channel with a transport-agnostic `use` / `eject` / `update` API.
324
319
  */
325
- export type InterceptorStack<T> = {
326
- use: (fn: InterceptorFn<T>) => number
320
+ export type InterceptorStack<T, TContext = never> = {
321
+ use: (fn: InterceptorFn<T, TContext>) => number
327
322
  eject: (id: number) => void
328
- update: (id: number, fn: InterceptorFn<T>) => void
329
- run: (value: T) => Promise<T>
323
+ update: (id: number, fn: InterceptorFn<T, TContext>) => void
324
+ run: (value: T, context?: TContext) => Promise<T>
330
325
  }
331
326
 
332
327
  /**
333
328
  * The three interceptor channels every client instance exposes.
334
329
  */
335
330
  export type Interceptors<TRequest = Request, TResponse = Response> = {
336
- request: InterceptorStack<ResolvedRequest>
337
- response: InterceptorStack<TransportResult<unknown, TRequest, TResponse>>
338
- error: InterceptorStack<ResponseError<unknown, TRequest, TResponse>>
331
+ request: InterceptorStack<ResolvedRequest, RequestConfig<unknown, TRequest, TResponse>>
332
+ response: InterceptorStack<TransportResult<unknown, TRequest, TResponse>, RequestConfig<unknown, TRequest, TResponse>>
333
+ error: InterceptorStack<ResponseError<unknown, TRequest, TResponse>, RequestConfig<unknown, TRequest, TResponse>>
339
334
  }
340
335
 
341
336
  /**
@@ -426,8 +421,8 @@ function serializeUrl({
426
421
  /**
427
422
  * Creates a transport-agnostic interceptor channel that runs interceptors in registration order.
428
423
  */
429
- export function createInterceptorStack<T>(): InterceptorStack<T> {
430
- let entries: Array<{ id: number; fn: InterceptorFn<T> }> = []
424
+ export function createInterceptorStack<T, TContext = never>(): InterceptorStack<T, TContext> {
425
+ let entries: Array<{ id: number; fn: InterceptorFn<T, TContext> }> = []
431
426
  let counter = 0
432
427
  return {
433
428
  use(fn) {
@@ -442,10 +437,10 @@ export function createInterceptorStack<T>(): InterceptorStack<T> {
442
437
  const entry = entries.find((item) => item.id === id)
443
438
  if (entry) entry.fn = fn
444
439
  },
445
- async run(value) {
440
+ async run(value, context) {
446
441
  let result = value
447
442
  for (const entry of entries) {
448
- result = await entry.fn(result)
443
+ result = await entry.fn(result, context)
449
444
  }
450
445
  return result
451
446
  },
@@ -640,6 +635,7 @@ async function settleResult<TRequest, TResponse>({
640
635
  validator,
641
636
  onValidationError,
642
637
  errorInterceptors,
638
+ requestConfig,
643
639
  }: {
644
640
  result: TransportResult<unknown, TRequest, TResponse>
645
641
  request: ResolvedRequest
@@ -647,7 +643,8 @@ async function settleResult<TRequest, TResponse>({
647
643
  throwOnError: boolean
648
644
  validator: { response?: Validator; error?: Validator } | undefined
649
645
  onValidationError: ValidationErrorHandler | undefined
650
- errorInterceptors: InterceptorStack<ResponseError<unknown, TRequest, TResponse>>
646
+ errorInterceptors: InterceptorStack<ResponseError<unknown, TRequest, TResponse>, RequestConfig<unknown, TRequest, TResponse>>
647
+ requestConfig: RequestConfig<unknown, TRequest, TResponse>
651
648
  }): Promise<CallResult<TRequest, TResponse>> {
652
649
  const isSuccess = result.status >= 200 && result.status < 300
653
650
  const contentType = result.contentType ?? getResponseContentType(result.headers)
@@ -684,7 +681,7 @@ async function settleResult<TRequest, TResponse>({
684
681
  request: result.request,
685
682
  response: result.response,
686
683
  })
687
- await errorInterceptors.run(responseError)
684
+ await errorInterceptors.run(responseError, requestConfig)
688
685
  throw responseError
689
686
  }
690
687
  return { status: result.status, data: undefined, error, contentType, request: result.request, response: result.response }
@@ -700,17 +697,17 @@ export function createClientCore<TRequest = Request, TResponse = Response>(
700
697
  let config: ClientConfig<TRequest, TResponse> = { ...initialConfig }
701
698
 
702
699
  const interceptors: Interceptors<TRequest, TResponse> = {
703
- request: createInterceptorStack<ResolvedRequest>(),
704
- response: createInterceptorStack<TransportResult<unknown, TRequest, TResponse>>(),
705
- error: createInterceptorStack<ResponseError<unknown, TRequest, TResponse>>(),
700
+ request: createInterceptorStack<ResolvedRequest, RequestConfig<unknown, TRequest, TResponse>>(),
701
+ response: createInterceptorStack<TransportResult<unknown, TRequest, TResponse>, RequestConfig<unknown, TRequest, TResponse>>(),
702
+ error: createInterceptorStack<ResponseError<unknown, TRequest, TResponse>, RequestConfig<unknown, TRequest, TResponse>>(),
706
703
  }
707
704
 
708
705
  const client = (async <TBody = unknown>(requestConfig: RequestConfig<TBody, TRequest, TResponse>): Promise<CallResult<TRequest, TResponse>> => {
709
706
  const transport = requestConfig.transport ?? config.transport ?? defaultTransport
710
707
  const { request, codecs } = await resolveRequest({ config, requestConfig })
711
708
 
712
- const resolvedRequest = await interceptors.request.run(request)
713
- const result = await interceptors.response.run(await transport(resolvedRequest))
709
+ const resolvedRequest = await interceptors.request.run(request, requestConfig)
710
+ const result = await interceptors.response.run(await transport(resolvedRequest), requestConfig)
714
711
 
715
712
  return settleResult({
716
713
  result,
@@ -720,6 +717,7 @@ export function createClientCore<TRequest = Request, TResponse = Response>(
720
717
  validator: requestConfig.validator,
721
718
  onValidationError: requestConfig.onValidationError ?? config.onValidationError,
722
719
  errorInterceptors: interceptors.error,
720
+ requestConfig,
723
721
  })
724
722
  }) as ClientInstance<TRequest, TResponse>
725
723