@kubb/plugin-fetch 5.14.0-canary.20261109T134631 → 6.0.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/dist/index.js CHANGED
@@ -1,1709 +1,13 @@
1
- import { createRequire } from "node:module";
2
- import { Diagnostics, Resolver, Url, addGenerator, ast, camelCase, createResolver, defineExtension, defineGenerator, injectFile, macroDedupeUnion, pascalCase, setMacros, setResolver, useConfig } from "@kubb/kit";
3
- import path from "node:path";
4
- import { pluginZodName } from "@kubb/plugin-zod";
5
- import { File, Function, jsxRenderer } from "@kubb/renderer-jsx";
6
- import { createFunctionParameter, createFunctionParameters, functionPrinter, pluginTsName } from "@kubb/plugin-ts";
7
- import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
8
1
  import { fileURLToPath } from "node:url";
9
- // ../../internals/shared/src/operation.ts
10
- /**
11
- * Builds the `ResolverFileParams` every operation generator passes to
12
- * `resolver.file`: a file named `name`, tagged by the operation's first
13
- * tag (or `'default'`), at the operation's path. Centralizes the entry object
14
- * that was repeated at dozens of call sites across the client and query plugins.
15
- *
16
- * @example
17
- * ```ts
18
- * resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
19
- * ```
20
- */
21
- function operationFileEntry(node, name, extname = ".ts") {
22
- return {
23
- name,
24
- extname,
25
- tag: node.tags[0] ?? "default",
26
- path: node.path
27
- };
28
- }
29
- /**
30
- * Resolves a dependency plugin's generated file for `node.operationId`, cached in `memo` (the
31
- * build's `ctx.memo`) under the resolver's own plugin name and the operation. Several dependents reading the
32
- * same dependency for the same operation in one pass (a query plugin's several hook generators, the
33
- * MCP handler, ...) share one computed name and path instead of each calling `resolver.file` again.
34
- *
35
- * @example Cache `plugin-ts`'s file for the current operation
36
- * ```ts
37
- * const fileTs = resolveDependencyOperationFile({ memo: ctx.memo, node, resolver: tsResolver, root, output })
38
- * ```
39
- */
40
- function resolveDependencyOperationFile(options) {
41
- const { memo, node, resolver, root, output, group } = options;
42
- return memo(`${resolver.pluginName}:operationFile:${node.operationId}`, () => resolver.file({
43
- ...operationFileEntry(node, node.operationId),
44
- root,
45
- output,
46
- group: group ?? void 0
47
- }));
48
- }
49
- function getOperationLink(node, link) {
50
- if (!link) return null;
51
- if (typeof link === "function") return link(node) ?? null;
52
- return node.path ? `{@link ${Url.toPath(node.path)}}` : null;
53
- }
54
- /**
55
- * Derives the shared `ContentTypeInfo` shape from a list of content types, tracking whether several
56
- * are present and the union, default, and form-data flags the client uses to pick one.
57
- */
58
- function buildContentTypeInfo(contentTypes) {
59
- const isMultipleContentTypes = contentTypes.length > 1;
60
- return {
61
- contentTypes,
62
- isMultipleContentTypes,
63
- contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
64
- defaultContentType: contentTypes[0] ?? "application/json",
65
- hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
66
- };
67
- }
68
- function getContentTypeInfo(node) {
69
- return buildContentTypeInfo(node.requestBody?.content?.map((e) => e.contentType) ?? []);
70
- }
71
- /**
72
- * The request-body counterpart for the primary success response: the content types it documents and
73
- * whether several are present, so the client can let a caller pick which one to accept.
74
- */
75
- function getResponseContentTypeInfo(node) {
76
- return buildContentTypeInfo(getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? []);
77
- }
78
- /**
79
- * Reads the single base content type of an operation's primary success response, lowercased and
80
- * stripped of any `; charset=...` suffix. Returns `undefined` when the response declares zero or
81
- * more than one content type, since neither case has a single type to act on.
82
- */
83
- function getPrimarySuccessContentType(node) {
84
- const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? [];
85
- if (contentTypes.length !== 1) return void 0;
86
- return contentTypes[0].split(";")[0].trim().toLowerCase();
87
- }
88
- /**
89
- * The `text/event-stream` content of the primary success response, when it is the only content type. A response that also
90
- * declares another type stays a normal call with its query hooks.
91
- */
92
- function getEventStreamContent(node) {
93
- return getPrimarySuccessContentType(node) === "text/event-stream" ? getPrimarySuccessResponse(node)?.content?.[0] : void 0;
94
- }
95
- /**
96
- * Whether an operation streams its primary success response as Server-Sent Events (`text/event-stream`). The client
97
- * generator uses this to return a typed event stream instead of a one-shot `RequestResult`.
98
- */
99
- function isEventStream(node) {
100
- return getEventStreamContent(node) !== void 0;
101
- }
102
- /**
103
- * How the document types a `text/event-stream` response, as the adapter read it from `itemSchema`: `event` when each item is
104
- * the parsed event with its own `event` and `data`, `data` when it is only the payload. Returns `null` without an event stream.
105
- */
106
- function getEventStream(node) {
107
- const content = getEventStreamContent(node);
108
- return content ? content.stream?.shape ?? "data" : null;
109
- }
110
- function baseContentType(contentType) {
111
- return contentType.split(";")[0].trim().toLowerCase();
112
- }
113
- function isJsonContentType(baseType) {
114
- return baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json";
115
- }
116
- /**
117
- * Parser for one response content entry.
118
- *
119
- * JSON stays unset so the runtime detects it. `text/event-stream` is a stream. A binary schema
120
- * (`format: binary` / `contentMediaType`) is a blob even when the media type is not a well-known
121
- * binary type. Other `text/*` is text, and `application/octet-stream`, `application/pdf`,
122
- * `image/*`, `audio/*`, and `video/*` are blobs.
123
- */
124
- function responseTypeForContent(contentType, schema) {
125
- const baseType = baseContentType(contentType);
126
- if (!baseType) return void 0;
127
- if (isJsonContentType(baseType)) return void 0;
128
- if (baseType === "text/event-stream") return "stream";
129
- if (schema?.type === "blob") return "blob";
130
- if (baseType.startsWith("text/")) return "text";
131
- if (baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType)) return "blob";
132
- }
133
- /**
134
- * Derives the default `responseType` string for an operation from its primary success response.
135
- *
136
- * One content type uses {@link responseTypeForContent}. Several content types that all use the same
137
- * parser collapse to that string, so a call can keep `responseType: 'blob'`. Mixed parsers return
138
- * `undefined`; {@link getResponseTypeMap} carries the per-type choice.
139
- */
140
- function getResponseType(node) {
141
- const entries = getPrimarySuccessResponse(node)?.content ?? [];
142
- if (entries.length === 0) return void 0;
143
- const responseTypes = entries.map((entry) => responseTypeForContent(entry.contentType, entry.schema));
144
- const first = responseTypes[0];
145
- if (!first) return void 0;
146
- if (responseTypes.every((responseType) => responseType === first)) return first;
147
- }
148
- /**
149
- * Maps each base content type of a primary success response to its parser when those parsers differ.
150
- *
151
- * Keys are lowercased and stripped of parameters. JSON entries are kept as `'json'` so a catch-all
152
- * entry cannot claim a JSON response. Returns `undefined` when {@link getResponseType} already
153
- * covers the response with one string, or when no content type needs a parser other than JSON.
154
- * The runtime matches the response `Content-Type` against this map instead of guessing from the media type.
155
- */
156
- function getResponseTypeMap(node) {
157
- if (getResponseType(node)) return void 0;
158
- const entries = getPrimarySuccessResponse(node)?.content ?? [];
159
- if (entries.length <= 1) return void 0;
160
- const map = {};
161
- for (const entry of entries) {
162
- const baseType = baseContentType(entry.contentType);
163
- const responseType = responseTypeForContent(entry.contentType, entry.schema) ?? (isJsonContentType(baseType) ? "json" : void 0);
164
- if (baseType && responseType) map[baseType] = responseType;
165
- }
166
- return Object.values(map).some((responseType) => responseType !== "json") ? map : void 0;
167
- }
168
- /**
169
- * Which of the grouped request options an operation carries.
170
- */
171
- function getRequestGroups(node) {
172
- const { path, query, header } = getOperationParameters(node);
173
- return {
174
- path: path.length > 0,
175
- query: query.length > 0,
176
- body: Boolean(node.requestBody?.content?.[0]?.schema),
177
- headers: header.length > 0
178
- };
179
- }
180
- /**
181
- * Resolves which grouped request options an operation carries together with whether each group
182
- * holds a required member. The grouped parameter stays optional only when nothing inside it is
183
- * required, matching the generated `RequestConfig` type.
184
- */
185
- function getRequestGroupOptionality(node) {
186
- const groups = getRequestGroups(node);
187
- const { path, query, header } = getOperationParameters(node);
188
- const hasRequiredPath = path.some((param) => param.required);
189
- const hasRequiredQuery = query.some((param) => param.required);
190
- const hasRequiredHeader = header.some((param) => param.required);
191
- return {
192
- groups,
193
- hasRequiredPath,
194
- hasRequiredQuery,
195
- hasRequiredHeader,
196
- isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !(groups.body && node.requestBody?.required === true)
197
- };
198
- }
199
- function buildOperationComments(node, options = {}) {
200
- const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
201
- const linkComment = getOperationLink(node, link);
202
- const filteredComments = (linkPosition === "beforeDeprecated" ? [
203
- node.description && `@description ${node.description}`,
204
- node.summary && `@summary ${node.summary}`,
205
- linkComment,
206
- node.deprecated && "@deprecated"
207
- ] : [
208
- node.description && `@description ${node.description}`,
209
- node.summary && `@summary ${node.summary}`,
210
- node.deprecated && "@deprecated",
211
- linkComment
212
- ]).filter((comment) => Boolean(comment));
213
- if (!splitLines) return filteredComments;
214
- return filteredComments.flatMap((text) => text.split(/\r?\n/).map((line) => line.trim())).filter((comment) => Boolean(comment));
215
- }
216
- const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
217
- /**
218
- * An OpenAPI 3.2 `querystring` parameter is the whole query string, so a form-encoded object becomes one query parameter
219
- * per property, typed and serialized like any other.
220
- * Another content type, such as JSON, stays one parameter named after it, and the runtime sends its value as the whole query string.
221
- */
222
- function spreadQuerystring(param) {
223
- const schema = param.schema.type === "ref" ? param.schema.schema : param.schema;
224
- const formEncoded = !param.contentType || param.contentType.split(";")[0].trim().toLowerCase() === "application/x-www-form-urlencoded";
225
- if (schema?.type !== "object" || !formEncoded) return [param];
226
- return schema.properties.map((property) => ast.factory.createParameter({
227
- name: property.name,
228
- in: "query",
229
- schema: property.schema,
230
- required: param.required && property.required
231
- }));
232
- }
233
- /**
234
- * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
235
- * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
236
- * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
237
- * same parameters once per plugin.
238
- */
239
- function getOperationParameters(node) {
240
- const cached = operationParameterGroupsByNode.get(node);
241
- if (cached) return cached;
242
- const dedupe = (params) => {
243
- const seen = /* @__PURE__ */ new Set();
244
- return params.filter((param) => {
245
- if (seen.has(param.name)) return false;
246
- seen.add(param.name);
247
- return true;
248
- });
249
- };
250
- const group = (location) => dedupe(node.parameters.filter((param) => param.in === location));
251
- const groups = {
252
- path: group("path"),
253
- query: dedupe([...group("query"), ...group("querystring").flatMap(spreadQuerystring)]),
254
- header: group("header"),
255
- cookie: group("cookie")
256
- };
257
- operationParameterGroupsByNode.set(node, groups);
258
- return groups;
259
- }
260
- /**
261
- * Builds the combined `{ body, path, query, headers }` options object schema for an operation,
262
- * referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s
263
- * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same
264
- * shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod
265
- * printer to emit `z.object(…)` rather than a record.
266
- */
267
- function hasRequestContentTypeChoice(node) {
268
- return (node.requestBody?.content ?? []).filter((entry) => entry.schema).length > 1;
269
- }
270
- function getStatusCodeNumber(statusCode) {
271
- const code = Number(statusCode);
272
- return Number.isNaN(code) ? null : code;
273
- }
274
- function isSuccessStatusCode(statusCode) {
275
- const code = getStatusCodeNumber(statusCode);
276
- return code !== null && code >= 200 && code < 300;
277
- }
278
- function getSuccessResponses(responses) {
279
- return responses.filter((response) => isSuccessStatusCode(response.statusCode));
280
- }
281
- function getOperationSuccessResponses(node) {
282
- return getSuccessResponses(node.responses);
283
- }
284
- function getPrimarySuccessResponse(node) {
285
- return getOperationSuccessResponses(node)[0] ?? null;
286
- }
287
- // ../../internals/utils/src/reserved.ts
288
- /**
289
- * JavaScript and Java reserved words.
290
- * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
291
- */
292
- const reservedWords = /* @__PURE__ */ new Set([
293
- "abstract",
294
- "arguments",
295
- "await",
296
- "boolean",
297
- "break",
298
- "byte",
299
- "case",
300
- "catch",
301
- "char",
302
- "class",
303
- "const",
304
- "continue",
305
- "debugger",
306
- "default",
307
- "delete",
308
- "do",
309
- "double",
310
- "else",
311
- "enum",
312
- "eval",
313
- "export",
314
- "extends",
315
- "false",
316
- "final",
317
- "finally",
318
- "float",
319
- "for",
320
- "function",
321
- "goto",
322
- "if",
323
- "implements",
324
- "import",
325
- "in",
326
- "instanceof",
327
- "int",
328
- "interface",
329
- "let",
330
- "long",
331
- "native",
332
- "new",
333
- "null",
334
- "package",
335
- "private",
336
- "protected",
337
- "public",
338
- "return",
339
- "short",
340
- "static",
341
- "super",
342
- "switch",
343
- "synchronized",
344
- "this",
345
- "throw",
346
- "throws",
347
- "transient",
348
- "true",
349
- "try",
350
- "typeof",
351
- "var",
352
- "void",
353
- "volatile",
354
- "while",
355
- "with",
356
- "yield",
357
- "Array",
358
- "Date",
359
- "hasOwnProperty",
360
- "Infinity",
361
- "isFinite",
362
- "isNaN",
363
- "isPrototypeOf",
364
- "length",
365
- "Math",
366
- "name",
367
- "NaN",
368
- "Number",
369
- "Object",
370
- "prototype",
371
- "String",
372
- "toString",
373
- "undefined",
374
- "valueOf"
375
- ]);
376
- /**
377
- * Returns `true` when `name` is a syntactically valid JavaScript variable name.
378
- *
379
- * @example
380
- * ```ts
381
- * isValidVarName('status') // true
382
- * isValidVarName('class') // false (reserved word)
383
- * isValidVarName('42foo') // false (starts with digit)
384
- * ```
385
- */
386
- function isValidVarName(name) {
387
- if (!name || reservedWords.has(name)) return false;
388
- return isIdentifier(name);
389
- }
390
- /**
391
- * Returns `name` when it's a syntactically valid JavaScript variable name,
392
- * otherwise prefixes it with `_` so the result is a valid identifier.
393
- *
394
- * Useful for sanitizing OpenAPI schema names or operation IDs that start with
395
- * a digit (e.g. `409`, `504AccountCancel`) before using them as exported
396
- * variable, type, or function names.
397
- *
398
- * @example
399
- * ```ts
400
- * ensureValidVarName('409') // '_409'
401
- * ensureValidVarName('504AccountCancel') // '_504AccountCancel'
402
- * ensureValidVarName('Pet') // 'Pet'
403
- * ensureValidVarName('class') // '_class'
404
- * ```
405
- */
406
- function ensureValidVarName(name) {
407
- if (!name || isValidVarName(name)) return name;
408
- return `_${name}`;
409
- }
410
- /**
411
- * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
412
- *
413
- * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
414
- * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
415
- * deciding whether an object key needs quoting.
416
- *
417
- * @example
418
- * ```ts
419
- * isIdentifier('name') // true
420
- * isIdentifier('x-total')// false
421
- * ```
422
- */
423
- function isIdentifier(name) {
424
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
425
- }
426
- // ../../internals/utils/src/codegen.ts
427
- /**
428
- * Builds a JSDoc comment block from an array of lines. Returns `fallback` when there are no
429
- * comments.
430
- *
431
- * @example
432
- * ```ts
433
- * buildJSDoc(['@type string', '@example hello'])
434
- * // '/**\n * @type string\n * @example hello\n *\/\n '
435
- * ```
436
- */
437
- function buildJSDoc(comments, options = {}) {
438
- const { indent = " * ", suffix = "\n ", fallback = " " } = options;
439
- if (comments.length === 0) return fallback;
440
- return `/**\n${comments.map((c) => `${indent}${c}`).join("\n")}\n */${suffix}`;
441
- }
442
- // ../../internals/shared/src/resolver.ts
443
- /**
444
- * The shared top-level `name` of the operation plugins, computed once per operation name and build.
445
- */
446
- function resolveOperationName(name) {
447
- return this.memo(`operation:${name}`, () => ensureValidVarName(camelCase(name)));
448
- }
449
- /**
450
- * Builds the allocation key for a declaration an operation owns. `scope` names which declaration
451
- * it is, so two of them never share an allocation even when a custom resolver gives both the same
452
- * name. Leave `scope` empty for the operation's own declaration.
453
- *
454
- * @example
455
- * ```ts
456
- * resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
457
- * ```
458
- */
459
- function operationKey(node, ...scope) {
460
- return [
461
- "operation",
462
- node.method,
463
- node.path,
464
- ...scope.map(String)
465
- ];
466
- }
467
- // ../../internals/shared/src/group.ts
468
- /**
469
- * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
470
- * shared default naming so every plugin groups output consistently:
471
- *
472
- * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
473
- * - other groups use the camelCased group (`pet store` → `petStore`).
474
- *
475
- * A user-provided `group.name` always wins over the default namer, so callers stay in
476
- * control of their output folders. Returns `null` when grouping is disabled, matching the
477
- * per-plugin convention.
478
- *
479
- * @param group - The user-supplied group option, or `undefined` to disable grouping.
480
- *
481
- * @example
482
- * ```ts
483
- * createGroupConfig(group) // shared across every plugin
484
- * ```
485
- */
486
- function createGroupConfig(group) {
487
- if (!group) return null;
488
- const defaultName = (ctx) => {
489
- if (group.type === "path") return `${ctx.group.split("/")[1]}`;
490
- return camelCase(ctx.group);
491
- };
492
- return {
493
- ...group,
494
- name: group.name ? group.name : defaultName
495
- };
496
- }
497
- // ../../internals/client/src/builders/validatorOptions.ts
498
- /**
499
- * Returns `true` when any direction of the validator uses zod (used for dependency checks).
500
- */
501
- function isValidatorEnabled(validator) {
502
- if (!validator) return false;
503
- if (validator === "zod") return true;
504
- return Boolean(validator.request || validator.params || validator.response);
505
- }
506
- /**
507
- * Returns `'zod'` when request body parsing is enabled, `null` otherwise. The string shorthand
508
- * `'zod'` validates the response only, so it does not enable request parsing.
509
- */
510
- function resolveRequestValidator(validator) {
511
- if (!validator || validator === "zod") return null;
512
- return validator.request ?? null;
513
- }
514
- /**
515
- * Returns `'zod'` when path, query, and header params parsing is enabled, `null` otherwise. Only the
516
- * object form `{ params: 'zod' }` enables it.
517
- */
518
- function resolveParamsValidator(validator) {
519
- if (!validator || validator === "zod") return null;
520
- return validator.params ?? null;
521
- }
522
- /**
523
- * Returns `'zod'` when response parsing is enabled, `null` otherwise. The string shorthand `'zod'`
524
- * maps to response parsing.
525
- */
526
- function resolveResponseValidator(validator) {
527
- if (!validator) return null;
528
- if (validator === "zod") return "zod";
529
- return validator.response ?? null;
530
- }
531
- /**
532
- * Resolves the zod schema name a generated client validates an error body with on the non-throw path.
533
- * Uses the error-only `<operation>ErrorSchema` (the union of non-2xx statuses); returns `null` when the
534
- * operation documents no error responses with a schema.
535
- */
536
- function buildZodErrorParse(node, zodResolver) {
537
- if (!node.responses.some((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema))) return null;
538
- return zodResolver.response.error?.(node) || null;
539
- }
540
- // ../../internals/client/src/builders/generics.ts
541
- /**
542
- * Builds the `RequestResult` generic arguments for one operation: the per-status responses record
543
- * plus the per-call `ThrowOnError` flag. `SuccessOf` / `ErrorOf` split the record inside the
544
- * runtime, so this only names the record and threads `ThrowOnError`.
545
- *
546
- * @example
547
- * `buildRequestResultGenerics({ node, types }) // 'AddPetResponses, ThrowOnError'`
548
- */
549
- function buildRequestResultGenerics({ node, types }) {
550
- return `${types.response.responses(node)}, ThrowOnError`;
551
- }
552
- /**
553
- * Builds the full return type an operation's function signature uses: `Unwrappable<RequestResult>`
554
- * for the default `returnType: 'full'`, already a promise so no further wrapping is needed, a plain
555
- * `Promise<RequestResult>` for the internal `'plain'` shape, or a
556
- * `Promise` of the runtime's `UnwrappedResult` when `returnType: 'data'` narrows a resolved call
557
- * down to the bare success body.
558
- *
559
- * @example
560
- * `buildResultType({ node, types, returnType: 'data' }) // 'Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>'`
561
- */
562
- function buildResultType({ node, types, returnType }) {
563
- const generics = buildRequestResultGenerics({
564
- node,
565
- types
566
- });
567
- if (returnType === "data") return `Promise<UnwrappedResult<${generics}>>`;
568
- if (returnType === "plain") return `Promise<RequestResult<${generics}>>`;
569
- return `Unwrappable<RequestResult<${generics}>>`;
570
- }
571
- // ../../internals/client/src/builders/returnStatement.ts
572
- /**
573
- * Builds the return statement of a generated operation function. With the default
574
- * `returnType: 'full'` the runtime call already resolves to `{ data, error, request, response }`.
575
- * The generated code casts that result to the operation's `RequestResult`, then wraps it with
576
- * `withUnwrap`, so the caller can `await` it directly or call `.unwrap()` for the bare success
577
- * body. With `returnType: 'data'` it instead routes the call through the runtime's `unwrapResult`,
578
- * which narrows the resolved value down to the bare success body the same way `RequestResult`
579
- * already does, using the plugin default when the call leaves `throwOnError` unset.
580
- * With `returnType: 'plain'` it returns the runtime call as is, cast to `RequestResult`, with no helper.
581
- *
582
- * Cast first, wrap second, for the `'full'` path. That order keeps `withUnwrap`'s generic inferred
583
- * as `RequestResult` instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
584
- * straight to `Unwrappable<B>` does not work: the `.then` overload stays pinned to `A`, and `as`
585
- * rejects that two-generic swap even where `A` and `B` on their own would satisfy it.
586
- *
587
- * @example
588
- * `return withUnwrap(request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>)`
589
- * @example
590
- * `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError ?? false) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
591
- * @example
592
- * `return request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>`
593
- */
594
- function buildReturnStatement({ node, types, callConfig, returnType, throwOnErrorDefault }) {
595
- const request = returnType !== "plain" && hasRequestContentTypeChoice(node) ? `request<${types.response.options(node)}['body']>` : "request";
596
- if (returnType === "data") return `return unwrapResult(${request}(${callConfig}), config.throwOnError ?? ${throwOnErrorDefault}) as ${buildResultType({
597
- node,
598
- types,
599
- returnType
600
- })}`;
601
- if (returnType === "plain") return `return ${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
602
- node,
603
- types
604
- })}>>`;
605
- return `return withUnwrap(${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
606
- node,
607
- types
608
- })}>>)`;
609
- }
610
- /**
611
- * Builds the return type and statement of a `text/event-stream` operation. The item type is `TypedEvent<...>` when each item
612
- * declares its own `event` and `data`, and otherwise the payload type, which `EventStreamResult` wraps in `ServerSentEvent`. The call's
613
- * promise goes through `toEventStream` with the `client` for its defaults, the call's `config`, the shape and the per-event
614
- * validator. A `returnType: 'plain'` client receives only the promise,
615
- * `toEventStream(request(config))`, and keeps its response validator in the call config.
616
- */
617
- function buildEventStreamReturn({ node, types, callConfig, returnType, validatorHooks, throwOnErrorDefault }) {
618
- const shape = getEventStream(node) ?? "data";
619
- const responses = types.response.responses(node);
620
- const success = `SuccessOf<${responses}>`;
621
- const eventType = shape === "event" ? `TypedEvent<${success}>` : success;
622
- if (returnType === "plain") return {
623
- returnType: `Promise<EventStreamResult<${eventType}>>`,
624
- statement: `return toEventStream<${eventType}>(request(${callConfig}))`
625
- };
626
- const generics = `${responses}, ThrowOnError${shape === "event" ? `, ${eventType}` : ""}`;
627
- const call = `toEventStream<${eventType}>(request(${callConfig}), { client: request, config, ${[`shape: '${shape}'`, validatorHooks?.response ? `validator: ${validatorHooks.response}` : null].filter(Boolean).join(", ")} })`;
628
- if (returnType === "data") {
629
- const result = `Promise<UnwrappedStreamResult<${generics}>>`;
630
- return {
631
- returnType: result,
632
- statement: `return unwrapResult(${call}, config.throwOnError ?? ${throwOnErrorDefault}) as ${result}`
633
- };
634
- }
635
- return {
636
- returnType: `Unwrappable<StreamResult<${generics}>>`,
637
- statement: `return withUnwrap(${call} as Promise<StreamResult<${generics}>>)`
638
- };
639
- }
640
- /** The stream types an operation file imports: `StreamResult` and `StreamOptions`, `EventStreamResult` for a plain client, and `TypedEvent` for named events. */
641
- function buildEventStreamImports({ returnType, typed }) {
642
- return [...{
643
- plain: ["EventStreamResult"],
644
- full: ["StreamResult", "StreamOptions"],
645
- data: ["UnwrappedStreamResult", "StreamOptions"]
646
- }[returnType], ...typed ? ["TypedEvent"] : []];
647
- }
648
- // ../../internals/client/src/builders/security.ts
649
- function serializeAuth(auth) {
650
- const parts = [`type: '${auth.type}'`];
651
- if (auth.scheme) parts.push(`scheme: '${auth.scheme}'`);
652
- if (auth.name) parts.push(`name: '${auth.name}'`);
653
- if (auth.in) parts.push(`in: '${auth.in}'`);
654
- if (auth.scopes?.length) parts.push(`scopes: [${auth.scopes.map((scope) => `'${scope}'`).join(", ")}]`);
655
- return `{ ${parts.join(", ")} }`;
656
- }
657
- /**
658
- * Maps an OpenAPI security scheme to the inline `Auth` object, or `null` when the runtime cannot
659
- * place it (an unresolved `$ref`, or an `apiKey` without a name or outside `header` / `query` /
660
- * `cookie`). `http` schemes other than `basic` are treated as bearer.
661
- */
662
- function resolveSecurityScheme(scheme) {
663
- if (!scheme || "$ref" in scheme) return null;
664
- if (scheme.type === "apiKey") {
665
- if (!scheme.name || scheme.in !== "header" && scheme.in !== "query" && scheme.in !== "cookie") return null;
666
- return {
667
- type: "apiKey",
668
- name: scheme.name,
669
- in: scheme.in
670
- };
671
- }
672
- if (scheme.type === "http") return {
673
- type: "http",
674
- scheme: scheme.scheme?.toLowerCase() === "basic" ? "basic" : "bearer"
675
- };
676
- if (scheme.type === "oauth2") return { type: "oauth2" };
677
- if (scheme.type === "openIdConnect") return { type: "openIdConnect" };
678
- return null;
679
- }
680
- /**
681
- * Derives the per-operation security metadata from the OpenAPI document. The operation's own
682
- * `security` overrides the global `security` (an explicit empty array disables auth), and every
683
- * referenced scheme is resolved from `components.securitySchemes` into a flat, de-duplicated list of
684
- * `Auth` objects the runtime walks in order.
685
- *
686
- * @example
687
- * `getOperationSecurity({ document, method: 'POST', path: '/pet' })`
688
- * `// [{ type: 'http', scheme: 'bearer' }]`
689
- */
690
- function getOperationSecurity({ document, method, path }) {
691
- if (!document) return void 0;
692
- const requirements = (document.paths?.[path]?.[method.toLowerCase()])?.security ?? document.security;
693
- if (!requirements?.length) return void 0;
694
- const definitions = document.components?.securitySchemes ?? {};
695
- const security = [];
696
- const seen = /* @__PURE__ */ new Set();
697
- for (const requirement of requirements) for (const [schemeName, scopes] of Object.entries(requirement)) {
698
- if (seen.has(schemeName)) continue;
699
- seen.add(schemeName);
700
- const auth = resolveSecurityScheme(definitions[schemeName]);
701
- if (auth) security.push(scopes.length ? {
702
- ...auth,
703
- scopes
704
- } : auth);
705
- }
706
- return security.length ? security : void 0;
707
- }
708
- /**
709
- * Serializes the per-operation security into the literal emitted on each generated call's `security`
710
- * field. The runtime `resolveAuth` helper walks it, calling the configured `auth` resolver per entry.
711
- *
712
- * @example
713
- * `buildSecurityMetadata({ security: [{ type: 'http', scheme: 'bearer' }] }) // "[{ type: 'http', scheme: 'bearer' }]"`
714
- */
715
- function buildSecurityMetadata({ security }) {
716
- if (!security?.length) return null;
717
- return `[${security.map(serializeAuth).join(", ")}]`;
718
- }
719
- // ../../internals/client/src/builders/validator.ts
720
- /**
721
- * Builds the validator-hook references for one operation. Request body and params validation run before the send.
722
- * Response validation runs on the success body only. Returns `null` references when the matching
723
- * direction is disabled or the schema is absent.
724
- */
725
- function buildValidatorHooks({ node, validator, zodResolver }) {
726
- const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
727
- const parsesResponse = zodResolver && resolveResponseValidator(validator) === "zod";
728
- const response = parsesResponse ? zodResolver.response.response(node) || null : null;
729
- const error = parsesResponse ? buildZodErrorParse(node, zodResolver) : null;
730
- const request = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body(node) || null : null;
731
- const paramGroups = getOperationParameters(node);
732
- const resolveParamsName = (kind, params) => zodResolver && resolveParamsValidator(validator) === "zod" && params.length > 0 ? zodResolver.param[kind](node, params[0]) : null;
733
- const path = resolveParamsName("path", paramGroups.path);
734
- const query = resolveParamsName("query", paramGroups.query);
735
- const headers = resolveParamsName("headers", paramGroups.header);
736
- const importedZodNames = [
737
- response,
738
- error,
739
- request,
740
- path,
741
- query,
742
- headers
743
- ].filter((name) => Boolean(name));
744
- return {
745
- request,
746
- path,
747
- query,
748
- headers: headers ? `toHeadersStandardSchema(${headers}, ${headers}.shape)` : null,
749
- response,
750
- error,
751
- importedZodNames
752
- };
753
- }
754
- /**
755
- * Prints the `validator: { … }` entry of a call config from the hooks named in `keys`, or `null` when none is set.
756
- */
757
- function buildValidatorLiteral(hooks, keys) {
758
- const entries = keys.flatMap((key) => hooks?.[key] ? [`${key}: ${hooks[key]}`] : []);
759
- return entries.length ? `validator: { ${entries.join(", ")} }` : null;
760
- }
761
- // ../../internals/client/src/builders/contentType.ts
762
- /**
763
- * Builds the `contentType` entry baked into the call config, or `null` without a request body.
764
- *
765
- * `merge` is `true` when the generated function must destructure `contentType` from `options` and
766
- * merge it over the baked request default, because the caller can also pick a response content type.
767
- */
768
- function buildContentType({ node }) {
769
- const { defaultContentType } = getContentTypeInfo(node);
770
- const requestContentType = Boolean(node.requestBody?.content?.[0]?.schema) ? defaultContentType : null;
771
- if (!requestContentType) return {
772
- literal: null,
773
- merge: false
774
- };
775
- const merge = getResponseContentTypeInfo(node).isMultipleContentTypes;
776
- return {
777
- merge,
778
- literal: merge ? `contentType: { request: '${requestContentType}', ...(typeof contentType === 'string' ? { request: contentType } : contentType) }` : `contentType: { request: '${requestContentType}' }`
779
- };
780
- }
781
- // ../../internals/client/src/builders/styles.ts
782
- /**
783
- * Renders a parameter name as an object-literal key, quoted when it is not a bare identifier. Every
784
- * location keeps the spec name: the runtime looks path styles up by the `{name}` placeholder in the
785
- * URL, and the other locations by the key it serializes.
786
- */
787
- function toKey(name) {
788
- return isValidVarName(name) ? name : JSON.stringify(name);
789
- }
790
- /**
791
- * Serializes one parameter's metadata into a `{ style, explode, allowReserved, contentType }`
792
- * literal, or `null` when the parameter carries none. Path and query carry the serialization
793
- * `style`; header and cookie use a fixed style (`simple` and `form`), so only `explode` is emitted
794
- * for them. `allowReserved` only applies to query parameters, and `contentType` to every location
795
- * except path.
796
- */
797
- function serializeParameter(parameter) {
798
- const parts = [];
799
- if ((parameter.in === "path" || parameter.in === "query") && parameter.style) parts.push(`style: '${parameter.style}'`);
800
- if (parameter.explode !== void 0) parts.push(`explode: ${parameter.explode}`);
801
- if (parameter.in === "query" && parameter.allowReserved !== void 0) parts.push(`allowReserved: ${parameter.allowReserved}`);
802
- if (parameter.in !== "path" && parameter.contentType) parts.push(`contentType: '${parameter.contentType}'`);
803
- if (parameter.in === "querystring") parts.push("querystring: true");
804
- return parts.length > 0 ? `{ ${parts.join(", ")} }` : null;
805
- }
806
- /**
807
- * Serializes the `encoding` of the request body's default content type into a `{ property: { ... } }`
808
- * literal, or `null` when it carries none. Only an operation with a single request content type
809
- * emits it, because the runtime applies `styles.body` to whichever type the caller selects.
810
- */
811
- function serializeBodyEncoding(node) {
812
- const content = node.requestBody?.content;
813
- const entries = Object.entries(content?.length === 1 ? content[0]?.encoding ?? {} : {}).map(([property, { contentType, style, explode, allowReserved }]) => {
814
- const parts = [
815
- style && `style: ${JSON.stringify(style)}`,
816
- explode !== void 0 && `explode: ${explode}`,
817
- allowReserved !== void 0 && `allowReserved: ${allowReserved}`,
818
- contentType && `contentType: ${JSON.stringify(contentType)}`
819
- ].filter(Boolean);
820
- return `${isValidVarName(property) ? property : JSON.stringify(property)}: { ${parts.join(", ")} }`;
821
- });
822
- return entries.length > 0 ? `body: { ${entries.join(", ")} }` : null;
823
- }
824
- /**
825
- * Builds the per-operation `styles` literal from the operation's parameters, grouped by location.
826
- * Entries are keyed by the spec name of the parameter.
827
- * Only parameters whose source defines `style`, `explode`, `allowReserved`, or `contentType` are
828
- * emitted, so calls without serialization metadata keep the runtime defaults and existing output
829
- * is unchanged. A form request body adds a `body` group keyed by property name from its `encoding`.
830
- * Returns `null` when nothing carries metadata.
831
- *
832
- * @example
833
- * ```ts
834
- * // a path param with { style: 'matrix', explode: true } and a query param with { explode: false }
835
- * buildStyles({ node }) // "{ path: { id: { style: 'matrix', explode: true } }, query: { tags: { explode: false } } }"
836
- * ```
837
- */
838
- function buildStyles({ node }) {
839
- if (!ast.isHttpOperationNode(node)) return null;
840
- const groups = {
841
- path: [],
842
- query: [],
843
- header: [],
844
- cookie: []
845
- };
846
- for (const [location, parameters] of Object.entries(getOperationParameters(node))) for (const parameter of parameters) {
847
- const literal = serializeParameter(parameter);
848
- if (literal) groups[location].push(`${toKey(parameter.name)}: ${literal}`);
849
- }
850
- const entries = [...Object.keys(groups).filter((location) => groups[location].length > 0).map((location) => `${location}: { ${groups[location].join(", ")} }`), serializeBodyEncoding(node)].filter(Boolean);
851
- return entries.length > 0 ? `{ ${entries.join(", ")} }` : null;
852
- }
853
- // ../../internals/client/src/builders/callConfig.ts
854
- /**
855
- * `responseType` baked into a call. One parser is a string. Several parsers become a map from content
856
- * type to parser so the runtime can choose from the response `Content-Type`.
857
- */
858
- function buildResponseTypeLiteral(node) {
859
- const responseType = getResponseType(node);
860
- if (responseType) return `responseType: '${responseType}'`;
861
- const responseTypes = getResponseTypeMap(node);
862
- if (!responseTypes) return null;
863
- return `responseType: { ${Object.entries(responseTypes).map(([contentType, type]) => `${JSON.stringify(contentType)}: '${type}'`).join(", ")} }`;
864
- }
865
- /**
866
- * Builds the call config literal a generated operation forwards to the client, shared by the standalone
867
- * functions and the SDK methods. The `throwOnError` fallback is applied after the `...config` spread.
868
- *
869
- * `mergeContentType` is `true` when the caller must destructure `contentType` from `options`, and
870
- * `eventStream` is `true` when the operation returns `text/event-stream`. Unless `returnType` is `'plain'`, a
871
- * stream's literal leaves the `response` validator to `toEventStream`, which runs it per event.
872
- */
873
- function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault, returnType }) {
874
- const eventStream = isEventStream(node);
875
- const securityLiteral = buildSecurityMetadata({ security });
876
- const stylesLiteral = buildStyles({ node });
877
- const validatorLiteral = buildValidatorLiteral(validatorHooks, eventStream && returnType !== "plain" ? [
878
- "request",
879
- "path",
880
- "query",
881
- "headers",
882
- "error"
883
- ] : [
884
- "request",
885
- "path",
886
- "query",
887
- "headers",
888
- "response",
889
- "error"
890
- ]);
891
- const { literal: contentTypeLiteral, merge: mergeContentType } = buildContentType({ node });
892
- return {
893
- callConfig: `{ ${[
894
- `method: '${node.method.toUpperCase()}'`,
895
- `url: '${node.path}'`,
896
- securityLiteral ? `security: ${securityLiteral}` : null,
897
- stylesLiteral ? `styles: ${stylesLiteral}` : null,
898
- validatorLiteral,
899
- contentTypeLiteral,
900
- buildResponseTypeLiteral(node),
901
- "...config",
902
- `throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
903
- ].filter(Boolean).join(", ")} }`,
904
- mergeContentType,
905
- eventStream
906
- };
907
- }
908
- // ../../internals/client/src/builders/signature.ts
909
- const declarationPrinter = functionPrinter({ mode: "declaration" });
910
- /**
911
- * Builds the grouped-options signature for one operation: a single `options` object whose `TData`
912
- * is the `<Name>Options` type, and a `RequestResult` return type keyed to the per-status responses
913
- * record. There are no positional arguments.
914
- *
915
- * The generated file imports `<Name>Options` and `<Name>Responses` and uses them directly, so no
916
- * per-operation input type has to be emitted. Both names come from `types`, which is `plugin-ts` or
917
- * `plugin-zod`'s inferred types (see `resolveOperationTypes`).
918
- */
919
- function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDefault, eventStream = false }) {
920
- const optionsName = types.response.options(node);
921
- const optionsType = `${eventStream && returnType !== "plain" ? "StreamOptions" : "Options"}<${optionsName}, ThrowOnError>`;
922
- const { isOptional } = getRequestGroupOptionality(node);
923
- return {
924
- paramsSignature: declarationPrinter.print(createFunctionParameters({ params: [createFunctionParameter({
925
- name: "options",
926
- type: optionsType,
927
- ...isOptional ? { default: "{}" } : {}
928
- })] })) ?? "",
929
- returnType: buildResultType({
930
- node,
931
- types,
932
- returnType
933
- }),
934
- generics: [`ThrowOnError extends boolean = ${throwOnErrorDefault}`]
935
- };
936
- }
937
- // ../../internals/client/src/builders/operationBody.ts
938
- /**
939
- * The pieces of a generated operation: its generics, `options` parameter, return type, return statement, and whether the
940
- * body destructures `contentType` from `options`. Shared by the standalone functions and the SDK methods, so a
941
- * `text/event-stream` operation is wired to `toEventStream` in one place.
942
- */
943
- function buildOperationBody({ node, types, validatorHooks, security, returnType, throwOnErrorDefault }) {
944
- const { callConfig, mergeContentType, eventStream } = buildCallConfig({
945
- node,
946
- validatorHooks,
947
- security,
948
- throwOnErrorDefault,
949
- returnType
950
- });
951
- const signature = buildGroupedOptionsSignature({
952
- node,
953
- types,
954
- returnType,
955
- throwOnErrorDefault,
956
- eventStream
957
- });
958
- const stream = eventStream ? buildEventStreamReturn({
959
- node,
960
- types,
961
- callConfig,
962
- returnType,
963
- validatorHooks,
964
- throwOnErrorDefault
965
- }) : null;
966
- return {
967
- generics: signature.generics,
968
- paramsSignature: signature.paramsSignature,
969
- returnType: stream?.returnType ?? signature.returnType,
970
- statement: stream?.statement ?? buildReturnStatement({
971
- node,
972
- types,
973
- callConfig,
974
- returnType,
975
- throwOnErrorDefault
976
- }),
977
- mergeContentType
978
- };
979
- }
980
- // ../../internals/client/src/components/Operation.tsx
981
- /**
982
- * Renders one client operation: the grouped `<Name>Request` type and the function that forwards a
983
- * single `options` object to the resolved client and returns the `Unwrappable<RequestResult>`. The
984
- * type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
985
- * the source.
986
- */
987
- function Operation({ name, node, types, validatorHooks, returnType, throwOnErrorDefault, security }) {
988
- if (!ast.isHttpOperationNode(node)) return null;
989
- const body = buildOperationBody({
990
- node,
991
- types,
992
- validatorHooks,
993
- security,
994
- returnType,
995
- throwOnErrorDefault
996
- });
997
- return /* @__PURE__ */ jsx(File.Source, {
998
- name,
999
- isExportable: true,
1000
- isIndexable: true,
1001
- children: /* @__PURE__ */ jsxs(Function, {
1002
- name,
1003
- export: true,
1004
- generics: body.generics,
1005
- params: body.paramsSignature,
1006
- returnType: body.returnType,
1007
- JSDoc: { comments: buildOperationComments(node, {
1008
- link: "urlPath",
1009
- linkPosition: "beforeDeprecated",
1010
- splitLines: true
1011
- }) },
1012
- children: [
1013
- body.mergeContentType ? "const { client: request = client, contentType, ...config } = options" : "const { client: request = client, ...config } = options",
1014
- /* @__PURE__ */ jsx("br", {}),
1015
- body.statement
1016
- ]
1017
- })
1018
- });
1019
- }
1020
- // ../../internals/client/src/components/StreamRuntime.tsx
1021
- /**
1022
- * The `footer` of `.kubb/client.ts`: the `baseURL` set at generation time, and the `sse.ts` re-export when the document
1023
- * streams events, so `ServerSentEvent` and `toEventStream` stay importable from the client as in earlier releases.
1024
- */
1025
- function buildClientFooter({ baseURL, sse }) {
1026
- const baseURLExpression = baseURL ? baseURL.includes("${") ? `\`${baseURL.replaceAll("`", "\\`")}\`` : JSON.stringify(baseURL) : void 0;
1027
- return [baseURLExpression ? `client.setConfig({ baseURL: ${baseURLExpression} })` : null, sse ? `export * from './sse.ts'` : null].filter(Boolean).join("\n") || void 0;
1028
- }
1029
- /**
1030
- * Writes the footer of the `.kubb/client.ts` the plugin injected, the one place it is built, and copies `sse.ts` into
1031
- * `.kubb/` only for a document with a `text/event-stream` operation, so a document without streams ships no stream runtime.
1032
- * `sse` is set by the plugins that ship the runtime, so a custom client from `@kubb/plugin-client` gets neither file.
1033
- */
1034
- function StreamRuntime({ nodes, root, sse, baseURL }) {
1035
- if (!sse) return null;
1036
- const streams = nodes.some(isEventStream);
1037
- const footer = buildClientFooter({
1038
- baseURL,
1039
- sse: streams
1040
- });
1041
- return /* @__PURE__ */ jsxs(Fragment, { children: [streams && /* @__PURE__ */ jsx(File, {
1042
- baseName: "sse.ts",
1043
- path: path.resolve(root, ".kubb/sse.ts"),
1044
- copy: sse
1045
- }), footer && /* @__PURE__ */ jsx(File, {
1046
- baseName: "client.ts",
1047
- path: path.resolve(root, ".kubb/client.ts"),
1048
- footer
1049
- })] });
1050
- }
1051
- // ../../internals/client/src/resolveOperationTypes.ts
1052
- /**
1053
- * A `plugin-ts` shaped resolver whose operation type names point at the types `pluginZod({ inferred })`
1054
- * exports next to each schema, so code written against `plugin-ts` names picks up the zod types.
1055
- */
1056
- function toZodTypeResolver(resolver) {
1057
- const type = (name) => resolver.schema.type(name);
1058
- return {
1059
- ...resolver,
1060
- file: resolver.file.bind(resolver),
1061
- memo: (key, compute) => resolver.memo(`zodTypes:${key}`, compute),
1062
- param: {
1063
- name: (node, p) => type(resolver.param.name(node, p)),
1064
- path: (node, p) => type(resolver.param.path(node, p)),
1065
- query: (node, p) => type(resolver.param.query(node, p)),
1066
- headers: (node, p) => type(resolver.param.headers(node, p))
1067
- },
1068
- response: {
1069
- body: (node) => type(resolver.response.body(node)),
1070
- status: (node, statusCode) => type(resolver.response.status(node, statusCode)),
1071
- response: (node) => type(resolver.response.response(node)),
1072
- responses: (node) => type(resolver.response.responses(node)),
1073
- options: (node) => resolver.response.options(node)
1074
- }
1075
- };
1076
- }
1077
- /**
1078
- * Picks the plugin generated clients, query hooks, and MCP handlers take their operation types from.
1079
- *
1080
- * `pluginZod({ inferred: 'codec' })` wins, so a codec param takes its domain value. Otherwise `plugin-ts`
1081
- * wins when it is in the pipeline, and without it any `pluginZod({ inferred })`. Throws
1082
- * `KUBB_MISSING_DEPENDENCY` for `pluginName` when neither is available.
1083
- *
1084
- * @example
1085
- * ```ts
1086
- * const types = resolveOperationTypes(ctx.driver, ctx.plugin.name)
1087
- * types.response.options(node) // 'GetPetByIdOptions', or 'GetPetByIdOptionsSchemaType' from zod
1088
- * ```
1089
- */
1090
- function resolveOperationTypes(driver, pluginName) {
1091
- return driver.memo(`operationTypes:${pluginName}`, () => {
1092
- const pluginTs = driver.getPlugin(pluginTsName);
1093
- const pluginZod = driver.getPlugin(pluginZodName);
1094
- if (pluginTs && pluginZod?.options?.inferred !== "codec") {
1095
- const resolver = driver.getResolver(pluginTsName);
1096
- return {
1097
- response: resolver.response,
1098
- resolver,
1099
- output: pluginTs.options?.output,
1100
- group: pluginTs.options?.group ?? void 0
1101
- };
1102
- }
1103
- if (!pluginZod?.options?.inferred) throw new Diagnostics.Error({
1104
- code: Diagnostics.code.missingDependency,
1105
- severity: "error",
1106
- message: `Plugin "${pluginName}" has no source for operation types.`,
1107
- help: `Add \`pluginTs()\` before "${pluginName}", or add \`pluginZod({ inferred: true })\`.`,
1108
- location: { kind: "config" },
1109
- plugin: pluginName
1110
- });
1111
- const resolver = toZodTypeResolver(driver.getResolver(pluginZodName));
1112
- return {
1113
- response: resolver.response,
1114
- resolver,
1115
- output: pluginZod.options.output,
1116
- group: pluginZod.options.group ?? void 0
1117
- };
1118
- });
1119
- }
1120
- // ../../internals/client/src/generators/clientGenerator.tsx
1121
- /**
1122
- * Builds the built-in per-operation generator shared by the client plugins (`@kubb/plugin-fetch`,
1123
- * `@kubb/plugin-axios`). Emits one async function per OpenAPI operation using the shared
1124
- * `Operation` component: a grouped `<Name>Request` type and a function that forwards a single
1125
- * `options` object to the bundled `client` and returns the `Unwrappable<RequestResult>`. Only the
1126
- * generator `name` and the runtime templates differ between plugins. Every other resolution, import,
1127
- * and rendering step is identical.
1128
- *
1129
- * `sse` is the Server-Sent Events runtime template, copied into `.kubb/sse.ts` only for a document with a
1130
- * `text/event-stream` operation.
1131
- */
1132
- function createClientGenerator(name, { sse } = {}) {
1133
- return defineGenerator({
1134
- name,
1135
- renderer: jsxRenderer,
1136
- operations(nodes, ctx) {
1137
- return /* @__PURE__ */ jsx(StreamRuntime, {
1138
- nodes,
1139
- root: ctx.root,
1140
- sse,
1141
- baseURL: ctx.options.baseURL
1142
- });
1143
- },
1144
- operation(node, ctx) {
1145
- if (!ast.isHttpOperationNode(node)) return null;
1146
- const { config, driver, resolver, root } = ctx;
1147
- const { output, validator, returnType, throwOnErrorDefault, group, importPath } = ctx.options;
1148
- const types = resolveOperationTypes(driver, ctx.plugin.name);
1149
- const pluginZod = isValidatorEnabled(validator) ? driver.getPlugin(pluginZodName) : null;
1150
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1151
- const importedTypeNames = [types.response.options(node), types.response.responses(node)];
1152
- const validatorHooks = buildValidatorHooks({
1153
- node,
1154
- validator,
1155
- zodResolver
1156
- });
1157
- const importedZodNames = validatorHooks.importedZodNames;
1158
- const operationName = resolver.name(node.operationId);
1159
- const meta = {
1160
- name: resolver.resolveName({
1161
- key: operationKey(node),
1162
- name: operationName
1163
- }),
1164
- file: resolver.file({
1165
- ...operationFileEntry(node, node.operationId),
1166
- root,
1167
- output,
1168
- group: group ?? void 0
1169
- }),
1170
- fileTypes: resolveDependencyOperationFile({
1171
- memo: ctx.memo,
1172
- node,
1173
- resolver: types.resolver,
1174
- root,
1175
- output: types.output ?? output,
1176
- group: types.group
1177
- }),
1178
- fileZod: zodResolver && pluginZod?.options ? zodResolver.file({
1179
- ...operationFileEntry(node, node.operationId),
1180
- root,
1181
- output: pluginZod.options.output ?? output,
1182
- group: pluginZod.options?.group ?? void 0
1183
- }) : null
1184
- };
1185
- const security = getOperationSecurity({
1186
- document: ctx.adapter.document,
1187
- method: node.method,
1188
- path: node.path
1189
- });
1190
- const clientPath = importPath ?? path.resolve(root, ".kubb/client.ts");
1191
- const clientRoot = importPath === void 0 ? meta.file.path : void 0;
1192
- const standardSchemaPath = importPath ?? path.resolve(root, ".kubb/standardSchema.ts");
1193
- const ssePath = importPath ?? path.resolve(root, ".kubb/sse.ts");
1194
- const stream = getEventStream(node);
1195
- const clientValueImports = (() => {
1196
- if (returnType === "plain") return ["client"];
1197
- if (returnType === "data") return ["client", "unwrapResult"];
1198
- return ["client", "withUnwrap"];
1199
- })();
1200
- const clientTypeImports = (() => {
1201
- if (stream) return {
1202
- plain: ["Options", "SuccessOf"],
1203
- data: ["SuccessOf"],
1204
- full: ["SuccessOf", "Unwrappable"]
1205
- }[returnType];
1206
- if (returnType === "plain") return ["Options", "RequestResult"];
1207
- if (returnType === "data") return ["Options", "UnwrappedResult"];
1208
- return [
1209
- "Options",
1210
- "Unwrappable",
1211
- "RequestResult"
1212
- ];
1213
- })();
1214
- return /* @__PURE__ */ jsxs(File, {
1215
- baseName: meta.file.baseName,
1216
- path: meta.file.path,
1217
- meta: meta.file.meta,
1218
- banner: resolver.default.banner(ctx.meta, {
1219
- output,
1220
- config,
1221
- file: {
1222
- path: meta.file.path,
1223
- baseName: meta.file.baseName
1224
- }
1225
- }),
1226
- footer: resolver.default.footer(ctx.meta, {
1227
- output,
1228
- config,
1229
- file: {
1230
- path: meta.file.path,
1231
- baseName: meta.file.baseName
1232
- }
1233
- }),
1234
- children: [
1235
- /* @__PURE__ */ jsx(File.Import, {
1236
- name: clientValueImports,
1237
- root: clientRoot,
1238
- path: clientPath
1239
- }),
1240
- /* @__PURE__ */ jsx(File.Import, {
1241
- name: clientTypeImports,
1242
- root: clientRoot,
1243
- path: clientPath,
1244
- isTypeOnly: true
1245
- }),
1246
- stream && /* @__PURE__ */ jsx(File.Import, {
1247
- name: ["toEventStream"],
1248
- root: clientRoot,
1249
- path: ssePath
1250
- }),
1251
- stream && /* @__PURE__ */ jsx(File.Import, {
1252
- name: buildEventStreamImports({
1253
- returnType,
1254
- typed: stream === "event"
1255
- }),
1256
- root: clientRoot,
1257
- path: ssePath,
1258
- isTypeOnly: true
1259
- }),
1260
- meta.fileTypes && importedTypeNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1261
- name: Array.from(new Set(importedTypeNames)),
1262
- root: meta.file.path,
1263
- path: meta.fileTypes.path,
1264
- isTypeOnly: true
1265
- }),
1266
- meta.fileZod && importedZodNames.length > 0 && /* @__PURE__ */ jsx(File.Import, {
1267
- name: importedZodNames,
1268
- root: meta.file.path,
1269
- path: meta.fileZod.path
1270
- }),
1271
- validatorHooks.headers && /* @__PURE__ */ jsx(File.Import, {
1272
- name: ["toHeadersStandardSchema"],
1273
- root: clientRoot,
1274
- path: standardSchemaPath
1275
- }),
1276
- /* @__PURE__ */ jsx(Operation, {
1277
- name: meta.name,
1278
- node,
1279
- types,
1280
- validatorHooks,
1281
- returnType,
1282
- throwOnErrorDefault,
1283
- security
1284
- })
1285
- ]
1286
- });
1287
- }
1288
- });
1289
- }
1290
- // ../../internals/client/src/components/SdkClient.tsx
1291
- /**
1292
- * Builds one instance method of a generated SDK class; a per-call `options.client` overrides `this.client`.
1293
- */
1294
- function buildSdkMethod({ node, name, types, validatorHooks, security, returnType, throwOnErrorDefault }) {
1295
- if (!ast.isHttpOperationNode(node)) return "";
1296
- const body = buildOperationBody({
1297
- node,
1298
- types,
1299
- validatorHooks,
1300
- security,
1301
- returnType,
1302
- throwOnErrorDefault
1303
- });
1304
- const generics = body.generics.length ? `<${body.generics.join(", ")}>` : "";
1305
- const jsdoc = buildJSDoc(buildOperationComments(node, {
1306
- link: "urlPath",
1307
- linkPosition: "beforeDeprecated",
1308
- splitLines: true
1309
- }));
1310
- const methodBody = [
1311
- body.mergeContentType ? "const { client: request = this.client, contentType, ...config } = options" : "const { client: request = this.client, ...config } = options",
1312
- "",
1313
- body.statement
1314
- ].map((line) => line ? ` ${line}` : "").join("\n");
1315
- return `${jsdoc} public ${name}${generics}(${body.paramsSignature}): ${body.returnType} {\n${methodBody}\n }`;
1316
- }
1317
- /**
1318
- * Renders one instance class per tag with one method per operation. The constructor takes a client
1319
- * config object and builds its own client through `createClient`, so each environment is a separate
1320
- * instance: `const api = new PetClient({ baseURL }); api.getPetById(...)`. A per-call `client` option
1321
- * still overrides the instance client for a one-off call.
1322
- */
1323
- function SdkClient({ name, operations, returnType, throwOnErrorDefault }) {
1324
- const methods = operations.map(({ node, name: methodName, types, validatorHooks, security }) => buildSdkMethod({
1325
- node,
1326
- name: methodName,
1327
- types,
1328
- validatorHooks,
1329
- security,
1330
- returnType,
1331
- throwOnErrorDefault
1332
- }));
1333
- const classCode = `export class ${name} {\n${[
1334
- " private readonly client: ClientInstance",
1335
- "",
1336
- " constructor(config: ClientConfig = {}) {",
1337
- " this.client = createClient(config)",
1338
- " }"
1339
- ].join("\n")}\n\n${methods.join("\n\n")}\n}`;
1340
- return /* @__PURE__ */ jsx(File.Source, {
1341
- name,
1342
- isExportable: true,
1343
- isIndexable: true,
1344
- children: classCode
1345
- });
1346
- }
1347
- // ../../internals/client/src/components/SdkFacade.tsx
1348
- /**
1349
- * Renders a composed root SDK class that instantiates every tag client from one shared config, so
1350
- * `new PetStore({ baseURL }).petClient.getPetById(...)` reaches an operation through a single entry
1351
- * point bound to one environment. The per-tag clients are read-only fields built in the constructor.
1352
- */
1353
- function SdkFacade({ name, members }) {
1354
- const fields = members.map((member) => ` readonly ${member.propName}: ${member.className}`);
1355
- const assignments = members.map((member) => ` this.${member.propName} = new ${member.className}(config)`);
1356
- const classCode = `export class ${name} {\n${[
1357
- ...fields,
1358
- "",
1359
- " constructor(config: ClientConfig = {}) {",
1360
- ...assignments,
1361
- " }"
1362
- ].join("\n")}\n}`;
1363
- return /* @__PURE__ */ jsx(File.Source, {
1364
- name,
1365
- isExportable: true,
1366
- isIndexable: true,
1367
- children: classCode
1368
- });
1369
- }
1370
- // ../../internals/client/src/generators/sdkGenerator.tsx
1371
- function resolveTypeImportNames(node, types) {
1372
- return [types.response.options(node), types.response.responses(node)];
1373
- }
1374
- /**
1375
- * Groups operations into one controller per tag. Operations without a tag fall back to a single
1376
- * `Client`/`ApiClient` controller, matching the resolver's default naming.
1377
- */
1378
- function buildControllers(nodes, ctx, types, validatorEnabled) {
1379
- const { driver, resolver, root } = ctx;
1380
- const { output, group, validator } = ctx.options;
1381
- const pluginZod = validatorEnabled ? driver.getPlugin(pluginZodName) : null;
1382
- const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
1383
- const document = ctx.adapter.document;
1384
- function buildOperationData(node) {
1385
- const typeFile = types.resolver.file({
1386
- ...operationFileEntry(node, node.operationId),
1387
- root,
1388
- output: types.output ?? output,
1389
- group: types.group
1390
- });
1391
- const zodFile = zodResolver && pluginZod?.options ? zodResolver.file({
1392
- ...operationFileEntry(node, node.operationId),
1393
- root,
1394
- output: pluginZod.options?.output ?? output,
1395
- group: pluginZod.options?.group ?? void 0
1396
- }) : null;
1397
- const security = ast.isHttpOperationNode(node) ? getOperationSecurity({
1398
- document,
1399
- method: node.method,
1400
- path: node.path
1401
- }) : void 0;
1402
- const validatorHooks = zodResolver ? buildValidatorHooks({
1403
- node,
1404
- validator,
1405
- zodResolver
1406
- }) : null;
1407
- const name = resolver.name(node.operationId);
1408
- return {
1409
- node,
1410
- name: resolver.resolveName({
1411
- key: operationKey(node),
1412
- name
1413
- }),
1414
- types,
1415
- typeFile,
1416
- zodFile,
1417
- validatorHooks,
1418
- security
1419
- };
1420
- }
1421
- return nodes.reduce((acc, operationNode) => {
1422
- if (!ast.isHttpOperationNode(operationNode)) return acc;
1423
- const tag = operationNode.tags[0];
1424
- const name = tag ? ensureValidVarName(group?.name?.({ group: camelCase(tag) }) ?? resolver.groupName(tag)) : resolver.className("ApiClient");
1425
- const file = resolver.file({
1426
- name,
1427
- extname: ".ts",
1428
- tag,
1429
- root,
1430
- output,
1431
- group: group ?? void 0
1432
- });
1433
- const operationData = buildOperationData(operationNode);
1434
- const previous = acc.find((item) => item.file.path === file.path);
1435
- if (previous) previous.operations.push(operationData);
1436
- else acc.push({
1437
- name,
1438
- tag,
1439
- file,
1440
- operations: [operationData]
1441
- });
1442
- return acc;
1443
- }, []);
1444
- }
1445
- function collectImportsByFile(ops, pick) {
1446
- const namesByPath = /* @__PURE__ */ new Map();
1447
- const filesByPath = /* @__PURE__ */ new Map();
1448
- ops.forEach((op) => {
1449
- const { file, names } = pick(op);
1450
- if (!file || names.length === 0) return;
1451
- if (!namesByPath.has(file.path)) namesByPath.set(file.path, /* @__PURE__ */ new Set());
1452
- const set = namesByPath.get(file.path);
1453
- names.forEach((n) => set.add(n));
1454
- filesByPath.set(file.path, file);
1455
- });
1456
- return {
1457
- namesByPath,
1458
- filesByPath
1459
- };
1460
- }
1461
- /**
1462
- * Builds the class-based SDK generator for a client plugin (`@kubb/plugin-fetch`,
1463
- * `@kubb/plugin-axios`). Only registered when `sdk` is set; otherwise the plugin keeps its
1464
- * standalone per-operation functions.
1465
- *
1466
- * Every tag client is an instance class whose constructor takes a client config and builds its own
1467
- * client, so each environment is a separate instance. With `sdk.mode: 'tag'` (the default) it
1468
- * emits one class per tag and, when `sdk.name` is set, a composed root that instantiates every tag
1469
- * client. With `sdk.mode: 'flat'` it emits one class named by `sdk.name`, with every operation as a
1470
- * direct method.
1471
- *
1472
- * `sse` is the Server-Sent Events runtime template, copied into `.kubb/sse.ts` only for a document with a
1473
- * `text/event-stream` operation.
1474
- */
1475
- function createSdkGenerator({ sse } = {}) {
1476
- return defineGenerator({
1477
- name: "sdk",
1478
- renderer: jsxRenderer,
1479
- operations(nodes, ctx) {
1480
- const { config, resolver, root } = ctx;
1481
- const { output, group, validator, returnType, throwOnErrorDefault, sdk } = ctx.options;
1482
- if (!sdk) return null;
1483
- if (sdk.mode === "tag" && output.mode === "file") throw new Diagnostics.Error({
1484
- code: Diagnostics.code.invalidPluginOptions,
1485
- severity: "error",
1486
- message: `Plugin "${ctx.plugin.name}" resolves \`output.mode\` to 'file' but also configures \`sdk.mode: 'tag'\`.`,
1487
- help: "A single-file output has nothing to split tag classes into. Set `sdk.mode: 'flat'` to emit one class into that file, or give `output.path` an extensionless directory name (or set `output.mode: 'directory'` explicitly) so each tag gets its own file.",
1488
- location: { kind: "config" },
1489
- plugin: ctx.plugin.name
1490
- });
1491
- const types = resolveOperationTypes(ctx.driver, ctx.plugin.name);
1492
- const validatorEnabled = isValidatorEnabled(validator);
1493
- const controllers = buildControllers(nodes, ctx, types, validatorEnabled);
1494
- const clientPath = path.resolve(root, ".kubb/client.ts");
1495
- const standardSchemaPath = path.resolve(root, ".kubb/standardSchema.ts");
1496
- const ssePath = path.resolve(root, ".kubb/sse.ts");
1497
- const banner = (file) => resolver.default.banner(ctx.meta, {
1498
- output,
1499
- config,
1500
- file: {
1501
- path: file.path,
1502
- baseName: file.baseName
1503
- }
1504
- });
1505
- const footer = (file) => resolver.default.footer(ctx.meta, {
1506
- output,
1507
- config,
1508
- file: {
1509
- path: file.path,
1510
- baseName: file.baseName
1511
- }
1512
- });
1513
- const renderClassFile = (className, file, ops) => {
1514
- const { namesByPath: typeNamesByPath, filesByPath: typeFilesByPath } = collectImportsByFile(ops, (op) => ({
1515
- file: op.typeFile,
1516
- names: resolveTypeImportNames(op.node, op.types)
1517
- }));
1518
- const { namesByPath: zodNamesByPath, filesByPath: zodFilesByPath } = validatorEnabled ? collectImportsByFile(ops, (op) => ({
1519
- file: op.zodFile,
1520
- names: op.validatorHooks?.importedZodNames ?? []
1521
- })) : {
1522
- namesByPath: /* @__PURE__ */ new Map(),
1523
- filesByPath: /* @__PURE__ */ new Map()
1524
- };
1525
- const shapes = new Set(ops.map((op) => getEventStream(op.node)).filter(Boolean));
1526
- const hasEventStream = shapes.size > 0;
1527
- const hasResult = ops.some((op) => !isEventStream(op.node));
1528
- return /* @__PURE__ */ jsxs(File, {
1529
- baseName: file.baseName,
1530
- path: file.path,
1531
- meta: file.meta,
1532
- banner: banner(file),
1533
- footer: footer(file),
1534
- children: [
1535
- /* @__PURE__ */ jsx(File.Import, {
1536
- name: ["createClient", returnType === "data" ? "unwrapResult" : "withUnwrap"],
1537
- root: file.path,
1538
- path: clientPath
1539
- }),
1540
- /* @__PURE__ */ jsx(File.Import, {
1541
- name: [
1542
- "ClientConfig",
1543
- "ClientInstance",
1544
- "Options",
1545
- ...returnType === "data" ? [] : ["Unwrappable"],
1546
- ...hasResult ? [returnType === "data" ? "UnwrappedResult" : "RequestResult"] : [],
1547
- ...hasEventStream ? ["SuccessOf"] : []
1548
- ],
1549
- root: file.path,
1550
- path: clientPath,
1551
- isTypeOnly: true
1552
- }),
1553
- hasEventStream && /* @__PURE__ */ jsx(File.Import, {
1554
- name: ["toEventStream"],
1555
- root: file.path,
1556
- path: ssePath
1557
- }),
1558
- hasEventStream && /* @__PURE__ */ jsx(File.Import, {
1559
- name: buildEventStreamImports({
1560
- returnType,
1561
- typed: shapes.has("event")
1562
- }),
1563
- root: file.path,
1564
- path: ssePath,
1565
- isTypeOnly: true
1566
- }),
1567
- validator === "zod" && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ jsx(File.Import, {
1568
- name: ["z"],
1569
- path: "zod",
1570
- isTypeOnly: true
1571
- }),
1572
- Array.from(typeNamesByPath.entries()).map(([filePath, set]) => /* @__PURE__ */ jsx(File.Import, {
1573
- name: Array.from(set),
1574
- root: file.path,
1575
- path: typeFilesByPath.get(filePath).path,
1576
- isTypeOnly: true
1577
- }, filePath)),
1578
- validatorEnabled && Array.from(zodNamesByPath.entries()).map(([filePath, set]) => /* @__PURE__ */ jsx(File.Import, {
1579
- name: Array.from(set),
1580
- root: file.path,
1581
- path: zodFilesByPath.get(filePath).path
1582
- }, filePath)),
1583
- ops.some((op) => op.validatorHooks?.headers) && /* @__PURE__ */ jsx(File.Import, {
1584
- name: ["toHeadersStandardSchema"],
1585
- root: file.path,
1586
- path: standardSchemaPath
1587
- }),
1588
- /* @__PURE__ */ jsx(SdkClient, {
1589
- name: className,
1590
- operations: ops,
1591
- returnType,
1592
- throwOnErrorDefault
1593
- })
1594
- ]
1595
- }, file.path);
1596
- };
1597
- const renderFacadeFile = (name) => {
1598
- const sdkFile = resolver.file({
1599
- name,
1600
- extname: ".ts",
1601
- root,
1602
- output,
1603
- group: group ?? void 0
1604
- });
1605
- const members = controllers.map(({ name, tag }) => ({
1606
- className: name,
1607
- propName: resolver.propertyName(tag ?? name)
1608
- }));
1609
- return /* @__PURE__ */ jsxs(File, {
1610
- baseName: sdkFile.baseName,
1611
- path: sdkFile.path,
1612
- meta: sdkFile.meta,
1613
- banner: banner(sdkFile),
1614
- footer: footer(sdkFile),
1615
- children: [
1616
- /* @__PURE__ */ jsx(File.Import, {
1617
- name: ["ClientConfig"],
1618
- root: sdkFile.path,
1619
- path: clientPath,
1620
- isTypeOnly: true
1621
- }),
1622
- controllers.map(({ name, file }) => /* @__PURE__ */ jsx(File.Import, {
1623
- name: [name],
1624
- root: sdkFile.path,
1625
- path: file.path
1626
- }, name)),
1627
- /* @__PURE__ */ jsx(SdkFacade, {
1628
- name: resolver.className(name),
1629
- members
1630
- })
1631
- ]
1632
- }, sdkFile.path);
1633
- };
1634
- const files = sdk.mode === "flat" ? renderClassFile(resolver.className(sdk.name ?? "sdk"), resolver.file({
1635
- name: sdk.name ?? "sdk",
1636
- extname: ".ts",
1637
- root,
1638
- output,
1639
- group: group ?? void 0
1640
- }), controllers.flatMap((controller) => controller.operations)) : [...controllers.map(({ name, file, operations: ops }) => renderClassFile(name, file, ops)), sdk.name && renderFacadeFile(sdk.name)];
1641
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(StreamRuntime, {
1642
- nodes,
1643
- root,
1644
- sse,
1645
- baseURL: ctx.options.baseURL
1646
- }), files] });
1647
- }
1648
- });
1649
- }
1650
- // ../../internals/client/src/macros.ts
1651
- /**
1652
- * Macros the client plugins apply by default, ahead of any user macros. `macroDedupeUnion`
1653
- * drops repeated union members, keeping the generated response and error
1654
- * unions tidy. A plugin wires them with `ctx.setMacros([...defaultMacros, ...userMacros])`.
1655
- */
1656
- const defaultMacros = [macroDedupeUnion];
1657
- // ../../internals/client/src/resolver.ts
1658
- /**
1659
- * Default resolver shared by the client plugins. Functions use safe camelCase identifiers and
1660
- * files inherit the built-in casing; classes and tag groups use PascalCase.
1661
- *
1662
- * @example
1663
- * ```ts
1664
- * resolverClient.name('show pet by id') // 'showPetById'
1665
- * resolverClient.groupName('pet') // 'PetClient'
1666
- * ```
1667
- */
1668
- const resolverClient = createResolver({
1669
- pluginName: "plugin-contract-client",
1670
- name: resolveOperationName,
1671
- className(name) {
1672
- return ensureValidVarName(pascalCase(name));
1673
- },
1674
- groupName(name) {
1675
- return ensureValidVarName(pascalCase(`${name} Client`));
1676
- },
1677
- propertyName(name) {
1678
- return ensureValidVarName(camelCase(name));
1679
- }
1680
- });
1681
- // src/templates.ts
1682
- const require = createRequire(import.meta.url);
1683
- /** Absolute path to the fetch client template, copied into `.kubb/client.ts`. */
1684
- const fetchClientTemplatePath = fileURLToPath(new URL("../templates/client.ts", import.meta.url));
1685
- /**
1686
- * Absolute paths to the runtime templates `@kubb/plugin-client` ships for every client plugin, copied into `.kubb/core.ts`,
1687
- * `.kubb/serializers.ts`, `.kubb/standardSchema.ts`, and `.kubb/sse.ts`.
1688
- */
1689
- const coreTemplatePath = require.resolve("@kubb/plugin-client/templates/core.ts");
1690
- const serializersTemplatePath = require.resolve("@kubb/plugin-client/templates/serializers.ts");
1691
- const standardSchemaTemplatePath = require.resolve("@kubb/plugin-client/templates/standardSchema.ts");
1692
- const sseTemplatePath = require.resolve("@kubb/plugin-client/templates/sse.ts");
1693
- // src/generators/clientGenerator.tsx
1694
- /**
1695
- * Built-in operation generator for `@kubb/plugin-fetch`. Emits one async function per OpenAPI
1696
- * operation using the shared `Operation` component: a grouped `<Name>Request` type and a function
1697
- * that forwards a single `options` object to the bundled `client` and returns the `RequestResult`.
1698
- */
1699
- const clientGenerator = createClientGenerator("fetch", { sse: sseTemplatePath });
2
+ import { defineExtension } from "@kubb/kit";
3
+ import { clientDependencies, resolveClientOptions, setupClient } from "@kubb/plugin-client";
1700
4
  // kubb-extension.json
1701
5
  var compatibility = {
1702
- "kubb": ">=5.16.0",
6
+ "kubb": ">=5.18.4",
1703
7
  "node": ">=22.22"
1704
8
  };
1705
9
  // package.json
1706
- var version = "5.14.0-canary.20261109T134631";
10
+ var version = "6.0.0";
1707
11
  // src/plugin.ts
1708
12
  /**
1709
13
  * Canonical plugin name for `@kubb/plugin-fetch`. Used for driver lookups and cross-plugin
@@ -1739,59 +43,18 @@ const pluginFetch = defineExtension({
1739
43
  version,
1740
44
  compatibility
1741
45
  },
1742
- dependencies: (options) => ({
1743
- [pluginTsName]: { optional: true },
1744
- [pluginZodName]: { optional: !isValidatorEnabled(options.validator) }
1745
- }),
46
+ dependencies: clientDependencies,
1746
47
  setup(options) {
1747
- const config = useConfig();
1748
- const { output = {
1749
- path: "clients",
1750
- barrel: { type: "named" }
1751
- }, exclude = [], include, override = [], baseURL, throwOnErrorDefault = true, validator = false, returnType = "full", group, sdk, resolver: userResolver } = options;
1752
- const resolved = {
1753
- output,
1754
- exclude,
1755
- include,
1756
- override,
1757
- group: createGroupConfig(group),
1758
- baseURL,
1759
- throwOnErrorDefault,
1760
- validator,
1761
- returnType,
1762
- sdk: sdk ? {
1763
- mode: sdk.mode ?? "tag",
1764
- name: sdk.name
1765
- } : void 0,
1766
- resolver: userResolver ? Resolver.merge(resolverClient, userResolver) : resolverClient
1767
- };
1768
- setResolver(resolved.resolver);
1769
- setMacros([...defaultMacros, ...options.macros ?? []]);
1770
- addGenerator(resolved.sdk ? createSdkGenerator({ sse: sseTemplatePath }) : clientGenerator);
1771
- const root = path.resolve(config.root, config.output.path);
1772
- injectFile({
1773
- baseName: "serializers.ts",
1774
- path: path.resolve(root, ".kubb/serializers.ts"),
1775
- copy: serializersTemplatePath
1776
- });
1777
- injectFile({
1778
- baseName: "core.ts",
1779
- path: path.resolve(root, ".kubb/core.ts"),
1780
- copy: coreTemplatePath
1781
- });
1782
- injectFile({
1783
- baseName: "client.ts",
1784
- path: path.resolve(root, ".kubb/client.ts"),
1785
- copy: fetchClientTemplatePath
1786
- });
1787
- injectFile({
1788
- baseName: "standardSchema.ts",
1789
- path: path.resolve(root, ".kubb/standardSchema.ts"),
1790
- copy: standardSchemaTemplatePath
48
+ const resolved = resolveClientOptions(options);
49
+ setupClient({
50
+ name: "fetch",
51
+ resolved,
52
+ macros: options.macros,
53
+ client: fileURLToPath(new URL("../templates/client.ts", import.meta.url))
1791
54
  });
1792
55
  return resolved;
1793
56
  }
1794
57
  });
1795
- export { clientGenerator, pluginFetch as default, pluginFetch, pluginFetchName };
58
+ export { pluginFetch as default, pluginFetch, pluginFetchName };
1796
59
 
1797
60
  //# sourceMappingURL=index.js.map