@kubb/plugin-fetch 5.6.1 → 5.6.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 +294 -327
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +295 -328
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/plugin.ts +26 -5
package/dist/index.js
CHANGED
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
import "./rolldown-runtime-C0LytTxp.js";
|
|
2
|
-
import { Diagnostics, Resolver, Url, addGenerator, ast, createResolver, defineExtension, defineGenerator, injectFile, macroSimplifyUnion, setMacros, setResolver, useConfig } from "kubb/kit";
|
|
2
|
+
import { Diagnostics, Resolver, Url, addGenerator, assertPluginOption, ast, createResolver, defineExtension, defineGenerator, injectFile, macroSimplifyUnion, setMacros, setResolver, useConfig } from "kubb/kit";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { pluginZodName } from "@kubb/plugin-zod";
|
|
5
5
|
import { File, Function, jsxRenderer } from "kubb/jsx";
|
|
6
|
+
import { createFunctionParameter, createFunctionParameters, functionPrinter, pluginTsName } from "@kubb/plugin-ts";
|
|
6
7
|
import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
|
|
7
|
-
import { pluginZodName } from "@kubb/plugin-zod";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
//#region ../../internals/shared/src/params.ts
|
|
10
|
-
/**
|
|
11
|
-
* Drops parameters that share the same name, keeping the first.
|
|
12
|
-
*
|
|
13
|
-
* A malformed spec can declare the same parameter name twice within one `in` location. Both would
|
|
14
|
-
* resolve to the same output property, so emitting both would yield an object type with a duplicate
|
|
15
|
-
* member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
|
|
16
|
-
* parameter names flow through unchanged, so no two distinct names ever collide here anymore.
|
|
17
|
-
*/
|
|
18
|
-
function dedupeParams(params) {
|
|
19
|
-
const seen = /* @__PURE__ */ new Set();
|
|
20
|
-
return params.filter((param) => {
|
|
21
|
-
if (seen.has(param.name)) return false;
|
|
22
|
-
seen.add(param.name);
|
|
23
|
-
return true;
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
//#endregion
|
|
27
9
|
//#region ../../internals/shared/src/operation.ts
|
|
28
10
|
/**
|
|
29
11
|
* Builds the `ResolverFileParams` every operation generator passes to
|
|
@@ -185,11 +167,19 @@ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
|
185
167
|
function getOperationParameters(node) {
|
|
186
168
|
const cached = operationParameterGroupsByNode.get(node);
|
|
187
169
|
if (cached) return cached;
|
|
170
|
+
const group = (location) => {
|
|
171
|
+
const seen = /* @__PURE__ */ new Set();
|
|
172
|
+
return node.parameters.filter((param) => {
|
|
173
|
+
if (param.in !== location || seen.has(param.name)) return false;
|
|
174
|
+
seen.add(param.name);
|
|
175
|
+
return true;
|
|
176
|
+
});
|
|
177
|
+
};
|
|
188
178
|
const groups = {
|
|
189
|
-
path:
|
|
190
|
-
query:
|
|
191
|
-
header:
|
|
192
|
-
cookie:
|
|
179
|
+
path: group("path"),
|
|
180
|
+
query: group("query"),
|
|
181
|
+
header: group("header"),
|
|
182
|
+
cookie: group("cookie")
|
|
193
183
|
};
|
|
194
184
|
operationParameterGroupsByNode.set(node, groups);
|
|
195
185
|
return groups;
|
|
@@ -438,6 +428,134 @@ function createGroupConfig(group) {
|
|
|
438
428
|
};
|
|
439
429
|
}
|
|
440
430
|
//#endregion
|
|
431
|
+
//#region ../../internals/client/src/builders/validatorOptions.ts
|
|
432
|
+
/**
|
|
433
|
+
* Returns `true` when any direction of the validator uses zod (used for dependency checks).
|
|
434
|
+
*/
|
|
435
|
+
function isValidatorEnabled(validator) {
|
|
436
|
+
if (!validator) return false;
|
|
437
|
+
if (validator === "zod") return true;
|
|
438
|
+
return Boolean(validator.request || validator.response);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Returns `'zod'` when request body parsing is enabled, `null` otherwise. The string shorthand
|
|
442
|
+
* `'zod'` validates the response only, so it does not enable request parsing.
|
|
443
|
+
*/
|
|
444
|
+
function resolveRequestValidator(validator) {
|
|
445
|
+
if (!validator || validator === "zod") return null;
|
|
446
|
+
return validator.request ?? null;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Returns `'zod'` when response parsing is enabled, `null` otherwise. The string shorthand `'zod'`
|
|
450
|
+
* maps to response parsing.
|
|
451
|
+
*/
|
|
452
|
+
function resolveResponseValidator(validator) {
|
|
453
|
+
if (!validator) return null;
|
|
454
|
+
if (validator === "zod") return "zod";
|
|
455
|
+
return validator.response ?? null;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Resolves the zod schema name a generated client validates an error body with on the non-throw path.
|
|
459
|
+
* Uses the error-only `<operation>ErrorSchema` (the union of non-2xx statuses); returns `null` when the
|
|
460
|
+
* operation documents no error responses with a schema.
|
|
461
|
+
*/
|
|
462
|
+
function buildZodErrorParse(node, zodResolver) {
|
|
463
|
+
if (!node.responses.some((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema))) return null;
|
|
464
|
+
return zodResolver.response.error?.(node) || null;
|
|
465
|
+
}
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region ../../internals/client/src/builders/validator.ts
|
|
468
|
+
/**
|
|
469
|
+
* Builds the validator-hook references for one operation. Request validation runs before the send;
|
|
470
|
+
* response validation runs on the success body only. Returns `null` references when the matching
|
|
471
|
+
* direction is disabled or the schema is absent.
|
|
472
|
+
*/
|
|
473
|
+
function buildValidatorHooks({ node, validator, zodResolver }) {
|
|
474
|
+
const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
|
|
475
|
+
const parsesResponse = zodResolver && resolveResponseValidator(validator) === "zod";
|
|
476
|
+
const response = parsesResponse ? zodResolver.response.response(node) || null : null;
|
|
477
|
+
const error = parsesResponse ? buildZodErrorParse(node, zodResolver) : null;
|
|
478
|
+
const request = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body(node) || null : null;
|
|
479
|
+
return {
|
|
480
|
+
request,
|
|
481
|
+
response,
|
|
482
|
+
error,
|
|
483
|
+
importedZodNames: [
|
|
484
|
+
response,
|
|
485
|
+
error,
|
|
486
|
+
request
|
|
487
|
+
].filter((name) => Boolean(name))
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
//#endregion
|
|
491
|
+
//#region ../../internals/client/src/builders/security.ts
|
|
492
|
+
function serializeAuth(auth) {
|
|
493
|
+
const parts = [`type: '${auth.type}'`];
|
|
494
|
+
if (auth.scheme) parts.push(`scheme: '${auth.scheme}'`);
|
|
495
|
+
if (auth.name) parts.push(`name: '${auth.name}'`);
|
|
496
|
+
if (auth.in) parts.push(`in: '${auth.in}'`);
|
|
497
|
+
return `{ ${parts.join(", ")} }`;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Maps an OpenAPI security scheme to the inline `Auth` object, or `null` when the runtime cannot
|
|
501
|
+
* place it (an unresolved `$ref`, or an `apiKey` without a name or outside `header` / `query` /
|
|
502
|
+
* `cookie`). `http` schemes other than `basic` are treated as bearer.
|
|
503
|
+
*/
|
|
504
|
+
function resolveSecurityScheme(scheme) {
|
|
505
|
+
if (!scheme || "$ref" in scheme) return null;
|
|
506
|
+
if (scheme.type === "apiKey") {
|
|
507
|
+
if (!scheme.name || scheme.in !== "header" && scheme.in !== "query" && scheme.in !== "cookie") return null;
|
|
508
|
+
return {
|
|
509
|
+
type: "apiKey",
|
|
510
|
+
name: scheme.name,
|
|
511
|
+
in: scheme.in
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
if (scheme.type === "http") return {
|
|
515
|
+
type: "http",
|
|
516
|
+
scheme: scheme.scheme?.toLowerCase() === "basic" ? "basic" : "bearer"
|
|
517
|
+
};
|
|
518
|
+
if (scheme.type === "oauth2") return { type: "oauth2" };
|
|
519
|
+
if (scheme.type === "openIdConnect") return { type: "openIdConnect" };
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Derives the per-operation security metadata from the OpenAPI document. The operation's own
|
|
524
|
+
* `security` overrides the global `security` (an explicit empty array disables auth), and every
|
|
525
|
+
* referenced scheme is resolved from `components.securitySchemes` into a flat, de-duplicated list of
|
|
526
|
+
* `Auth` objects the runtime walks in order.
|
|
527
|
+
*
|
|
528
|
+
* @example
|
|
529
|
+
* `getOperationSecurity({ document, method: 'POST', path: '/pet' })`
|
|
530
|
+
* `// [{ type: 'http', scheme: 'bearer' }]`
|
|
531
|
+
*/
|
|
532
|
+
function getOperationSecurity({ document, method, path }) {
|
|
533
|
+
if (!document) return void 0;
|
|
534
|
+
const requirements = (document.paths?.[path]?.[method.toLowerCase()])?.security ?? document.security;
|
|
535
|
+
if (!requirements?.length) return void 0;
|
|
536
|
+
const definitions = document.components?.securitySchemes ?? {};
|
|
537
|
+
const security = [];
|
|
538
|
+
const seen = /* @__PURE__ */ new Set();
|
|
539
|
+
for (const requirement of requirements) for (const schemeName of Object.keys(requirement)) {
|
|
540
|
+
if (seen.has(schemeName)) continue;
|
|
541
|
+
seen.add(schemeName);
|
|
542
|
+
const auth = resolveSecurityScheme(definitions[schemeName]);
|
|
543
|
+
if (auth) security.push(auth);
|
|
544
|
+
}
|
|
545
|
+
return security.length ? security : void 0;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Serializes the per-operation security into the literal emitted on each generated call's `security`
|
|
549
|
+
* field. The runtime `resolveAuth` helper walks it, calling the configured `auth` resolver per entry.
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* `buildSecurityMetadata({ security: [{ type: 'http', scheme: 'bearer' }] }) // "[{ type: 'http', scheme: 'bearer' }]"`
|
|
553
|
+
*/
|
|
554
|
+
function buildSecurityMetadata({ security }) {
|
|
555
|
+
if (!security?.length) return null;
|
|
556
|
+
return `[${security.map(serializeAuth).join(", ")}]`;
|
|
557
|
+
}
|
|
558
|
+
//#endregion
|
|
441
559
|
//#region ../../internals/client/src/builders/contentType.ts
|
|
442
560
|
/**
|
|
443
561
|
* Builds the `contentType` entry baked into the call config, or `null` for the JSON default.
|
|
@@ -530,74 +648,6 @@ function buildReturnStatement({ node, types, callConfig, returnType, throwOnErro
|
|
|
530
648
|
})}>>)`;
|
|
531
649
|
}
|
|
532
650
|
//#endregion
|
|
533
|
-
//#region ../../internals/client/src/builders/security.ts
|
|
534
|
-
function serializeAuth(auth) {
|
|
535
|
-
const parts = [`type: '${auth.type}'`];
|
|
536
|
-
if (auth.scheme) parts.push(`scheme: '${auth.scheme}'`);
|
|
537
|
-
if (auth.name) parts.push(`name: '${auth.name}'`);
|
|
538
|
-
if (auth.in) parts.push(`in: '${auth.in}'`);
|
|
539
|
-
return `{ ${parts.join(", ")} }`;
|
|
540
|
-
}
|
|
541
|
-
/**
|
|
542
|
-
* Maps an OpenAPI security scheme to the inline `Auth` object, or `null` when the runtime cannot
|
|
543
|
-
* place it (an unresolved `$ref`, or an `apiKey` without a name or outside `header` / `query` /
|
|
544
|
-
* `cookie`). `http` schemes other than `basic` are treated as bearer.
|
|
545
|
-
*/
|
|
546
|
-
function resolveSecurityScheme(scheme) {
|
|
547
|
-
if (!scheme || "$ref" in scheme) return null;
|
|
548
|
-
if (scheme.type === "apiKey") {
|
|
549
|
-
if (!scheme.name || scheme.in !== "header" && scheme.in !== "query" && scheme.in !== "cookie") return null;
|
|
550
|
-
return {
|
|
551
|
-
type: "apiKey",
|
|
552
|
-
name: scheme.name,
|
|
553
|
-
in: scheme.in
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
if (scheme.type === "http") return {
|
|
557
|
-
type: "http",
|
|
558
|
-
scheme: scheme.scheme?.toLowerCase() === "basic" ? "basic" : "bearer"
|
|
559
|
-
};
|
|
560
|
-
if (scheme.type === "oauth2") return { type: "oauth2" };
|
|
561
|
-
if (scheme.type === "openIdConnect") return { type: "openIdConnect" };
|
|
562
|
-
return null;
|
|
563
|
-
}
|
|
564
|
-
/**
|
|
565
|
-
* Derives the per-operation security metadata from the OpenAPI document. The operation's own
|
|
566
|
-
* `security` overrides the global `security` (an explicit empty array disables auth), and every
|
|
567
|
-
* referenced scheme is resolved from `components.securitySchemes` into a flat, de-duplicated list of
|
|
568
|
-
* `Auth` objects the runtime walks in order.
|
|
569
|
-
*
|
|
570
|
-
* @example
|
|
571
|
-
* `getOperationSecurity({ document, method: 'POST', path: '/pet' })`
|
|
572
|
-
* `// [{ type: 'http', scheme: 'bearer' }]`
|
|
573
|
-
*/
|
|
574
|
-
function getOperationSecurity({ document, method, path }) {
|
|
575
|
-
if (!document) return void 0;
|
|
576
|
-
const requirements = (document.paths?.[path]?.[method.toLowerCase()])?.security ?? document.security;
|
|
577
|
-
if (!requirements?.length) return void 0;
|
|
578
|
-
const definitions = document.components?.securitySchemes ?? {};
|
|
579
|
-
const security = [];
|
|
580
|
-
const seen = /* @__PURE__ */ new Set();
|
|
581
|
-
for (const requirement of requirements) for (const schemeName of Object.keys(requirement)) {
|
|
582
|
-
if (seen.has(schemeName)) continue;
|
|
583
|
-
seen.add(schemeName);
|
|
584
|
-
const auth = resolveSecurityScheme(definitions[schemeName]);
|
|
585
|
-
if (auth) security.push(auth);
|
|
586
|
-
}
|
|
587
|
-
return security.length ? security : void 0;
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Serializes the per-operation security into the literal emitted on each generated call's `security`
|
|
591
|
-
* field. The runtime `resolveAuth` helper walks it, calling the configured `auth` resolver per entry.
|
|
592
|
-
*
|
|
593
|
-
* @example
|
|
594
|
-
* `buildSecurityMetadata({ security: [{ type: 'http', scheme: 'bearer' }] }) // "[{ type: 'http', scheme: 'bearer' }]"`
|
|
595
|
-
*/
|
|
596
|
-
function buildSecurityMetadata({ security }) {
|
|
597
|
-
if (!security?.length) return null;
|
|
598
|
-
return `[${security.map(serializeAuth).join(", ")}]`;
|
|
599
|
-
}
|
|
600
|
-
//#endregion
|
|
601
651
|
//#region ../../internals/client/src/builders/signature.ts
|
|
602
652
|
const declarationPrinter = functionPrinter({ mode: "declaration" });
|
|
603
653
|
/**
|
|
@@ -627,161 +677,6 @@ function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDef
|
|
|
627
677
|
};
|
|
628
678
|
}
|
|
629
679
|
//#endregion
|
|
630
|
-
//#region ../../internals/client/src/builders/validatorOptions.ts
|
|
631
|
-
/**
|
|
632
|
-
* Returns `true` when any direction of the validator uses zod (used for dependency checks).
|
|
633
|
-
*/
|
|
634
|
-
function isValidatorEnabled(validator) {
|
|
635
|
-
if (!validator) return false;
|
|
636
|
-
if (validator === "zod") return true;
|
|
637
|
-
return Boolean(validator.request || validator.response);
|
|
638
|
-
}
|
|
639
|
-
/**
|
|
640
|
-
* Returns `'zod'` when request body parsing is enabled, `null` otherwise. The string shorthand
|
|
641
|
-
* `'zod'` validates the response only, so it does not enable request parsing.
|
|
642
|
-
*/
|
|
643
|
-
function resolveRequestValidator(validator) {
|
|
644
|
-
if (!validator || validator === "zod") return null;
|
|
645
|
-
return validator.request ?? null;
|
|
646
|
-
}
|
|
647
|
-
/**
|
|
648
|
-
* Returns `'zod'` when query-parameters parsing is enabled, `null` otherwise. Only the object form
|
|
649
|
-
* `{ request: 'zod' }` enables it.
|
|
650
|
-
*/
|
|
651
|
-
function resolveQueryParamsValidator(validator) {
|
|
652
|
-
if (!validator || validator === "zod") return null;
|
|
653
|
-
return validator.request ?? null;
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Returns `'zod'` when response parsing is enabled, `null` otherwise. The string shorthand `'zod'`
|
|
657
|
-
* maps to response parsing.
|
|
658
|
-
*/
|
|
659
|
-
function resolveResponseValidator(validator) {
|
|
660
|
-
if (!validator) return null;
|
|
661
|
-
if (validator === "zod") return "zod";
|
|
662
|
-
return validator.response ?? null;
|
|
663
|
-
}
|
|
664
|
-
/**
|
|
665
|
-
* Resolves the zod expression a generated client validates a success response with. Only success
|
|
666
|
-
* (2xx) bodies reach the parse under the throw-on-error contract, so the success-only
|
|
667
|
-
* `<operation>ResponseSchema` is used; error bodies are never zod-parsed.
|
|
668
|
-
*/
|
|
669
|
-
function buildZodResponseParse(node, zodResolver) {
|
|
670
|
-
const name = zodResolver.response.response(node);
|
|
671
|
-
return name ? {
|
|
672
|
-
expression: name,
|
|
673
|
-
importNames: [name]
|
|
674
|
-
} : null;
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Resolves the zod expression a generated client validates an error body with on the non-throw path.
|
|
678
|
-
* Uses the error-only `<operation>ErrorSchema` (the union of non-2xx statuses); returns `null` when the
|
|
679
|
-
* operation documents no error responses with a schema.
|
|
680
|
-
*/
|
|
681
|
-
function buildZodErrorParse(node, zodResolver) {
|
|
682
|
-
if (!node.responses.some((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema))) return null;
|
|
683
|
-
const name = zodResolver.response.error?.(node);
|
|
684
|
-
return name ? {
|
|
685
|
-
expression: name,
|
|
686
|
-
importNames: [name]
|
|
687
|
-
} : null;
|
|
688
|
-
}
|
|
689
|
-
//#endregion
|
|
690
|
-
//#region ../../internals/client/src/builders/validator.ts
|
|
691
|
-
/**
|
|
692
|
-
* Builds the validator-hook references for one operation. Request validation runs before the send;
|
|
693
|
-
* response validation runs on the success body only. Returns `null` references when the matching
|
|
694
|
-
* direction is disabled or the schema is absent.
|
|
695
|
-
*/
|
|
696
|
-
function buildValidatorHooks({ node, validator, zodResolver }) {
|
|
697
|
-
const importedZodNames = [];
|
|
698
|
-
const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
|
|
699
|
-
const zodRequestName = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body(node) : null;
|
|
700
|
-
const request = zodRequestName ?? null;
|
|
701
|
-
if (zodRequestName) importedZodNames.push(zodRequestName);
|
|
702
|
-
const responseParse = zodResolver && resolveResponseValidator(validator) === "zod" ? buildZodResponseParse(node, zodResolver) : null;
|
|
703
|
-
const response = responseParse ? responseParse.expression : null;
|
|
704
|
-
if (responseParse) importedZodNames.push(...responseParse.importNames);
|
|
705
|
-
const errorParse = zodResolver && resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver) : null;
|
|
706
|
-
const error = errorParse ? errorParse.expression : null;
|
|
707
|
-
if (errorParse) importedZodNames.push(...errorParse.importNames);
|
|
708
|
-
return {
|
|
709
|
-
request,
|
|
710
|
-
response,
|
|
711
|
-
error,
|
|
712
|
-
importedZodNames
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
//#endregion
|
|
716
|
-
//#region ../../internals/client/src/builders/sdkMethod.ts
|
|
717
|
-
/**
|
|
718
|
-
* Builds the call config literal forwarded to the contract client, mirroring the shared `Operation`
|
|
719
|
-
* component: `{ method, url, security?, validator?, ...config }`. The `...config` spread carries every
|
|
720
|
-
* per-call field. The plugin's `throwOnError` fallback is applied after the spread so it also wins
|
|
721
|
-
* over a client instance's runtime config when the call omits an override.
|
|
722
|
-
*/
|
|
723
|
-
function buildCallConfig({ node, validator, zodResolver, security, throwOnErrorDefault }) {
|
|
724
|
-
const validators = buildValidatorHooks({
|
|
725
|
-
node,
|
|
726
|
-
validator,
|
|
727
|
-
zodResolver
|
|
728
|
-
});
|
|
729
|
-
const validatorEntries = [validators.request ? `request: ${validators.request}` : null, validators.response ? `response: ${validators.response}` : null].filter(Boolean);
|
|
730
|
-
const validatorLiteral = validatorEntries.length ? `validator: { ${validatorEntries.join(", ")} }` : null;
|
|
731
|
-
const securityLiteral = buildSecurityMetadata({ security });
|
|
732
|
-
const { literal: contentTypeLiteral } = buildContentType({ node });
|
|
733
|
-
return `{ ${[
|
|
734
|
-
`method: '${node.method.toUpperCase()}'`,
|
|
735
|
-
`url: '${node.path}'`,
|
|
736
|
-
securityLiteral ? `security: ${securityLiteral}` : null,
|
|
737
|
-
validatorLiteral,
|
|
738
|
-
contentTypeLiteral,
|
|
739
|
-
"...config",
|
|
740
|
-
`throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
|
|
741
|
-
].filter(Boolean).join(", ")} }`;
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Builds a single instance method for a generated SDK class. The body forwards the single grouped
|
|
745
|
-
* `options` object to the instance's own client (`this.client`, built once in the constructor) and
|
|
746
|
-
* returns the `Unwrappable<RequestResult>`. A per-call `options.client` still overrides the
|
|
747
|
-
* instance client, so one operation can be routed to a different environment without a new
|
|
748
|
-
* instance.
|
|
749
|
-
*/
|
|
750
|
-
function buildSdkMethod({ node, name, types, zodResolver, validator, security, returnType, throwOnErrorDefault }) {
|
|
751
|
-
if (!ast.isHttpOperationNode(node)) return "";
|
|
752
|
-
const signature = buildGroupedOptionsSignature({
|
|
753
|
-
node,
|
|
754
|
-
types,
|
|
755
|
-
returnType,
|
|
756
|
-
throwOnErrorDefault
|
|
757
|
-
});
|
|
758
|
-
const returnStatement = buildReturnStatement({
|
|
759
|
-
node,
|
|
760
|
-
types,
|
|
761
|
-
callConfig: buildCallConfig({
|
|
762
|
-
node,
|
|
763
|
-
validator,
|
|
764
|
-
zodResolver,
|
|
765
|
-
security,
|
|
766
|
-
throwOnErrorDefault
|
|
767
|
-
}),
|
|
768
|
-
returnType,
|
|
769
|
-
throwOnErrorDefault
|
|
770
|
-
});
|
|
771
|
-
const generics = signature.generics.length ? `<${signature.generics.join(", ")}>` : "";
|
|
772
|
-
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
773
|
-
link: "urlPath",
|
|
774
|
-
linkPosition: "beforeDeprecated",
|
|
775
|
-
splitLines: true
|
|
776
|
-
}));
|
|
777
|
-
const methodBody = [
|
|
778
|
-
buildContentType({ node }).merge ? "const { client: request = this.client, contentType, ...config } = options" : "const { client: request = this.client, ...config } = options",
|
|
779
|
-
"",
|
|
780
|
-
returnStatement
|
|
781
|
-
].map((line) => line ? ` ${line}` : "").join("\n");
|
|
782
|
-
return `${jsdoc} public ${name}${generics}(${signature.paramsSignature}): ${signature.returnType} {\n${methodBody}\n }`;
|
|
783
|
-
}
|
|
784
|
-
//#endregion
|
|
785
680
|
//#region ../../internals/client/src/builders/styles.ts
|
|
786
681
|
/**
|
|
787
682
|
* Renders a parameter name as an object-literal key, quoted when it is not a bare identifier.
|
|
@@ -842,7 +737,7 @@ function buildStyles({ node }) {
|
|
|
842
737
|
* type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
|
|
843
738
|
* the source.
|
|
844
739
|
*/
|
|
845
|
-
function Operation({ name, node, types, zodResolver, validator, returnType, throwOnErrorDefault, security
|
|
740
|
+
function Operation({ name, node, types, zodResolver, validator, returnType, throwOnErrorDefault, security }) {
|
|
846
741
|
if (!ast.isHttpOperationNode(node)) return null;
|
|
847
742
|
const signature = buildGroupedOptionsSignature({
|
|
848
743
|
node,
|
|
@@ -889,11 +784,11 @@ function Operation({ name, node, types, zodResolver, validator, returnType, thro
|
|
|
889
784
|
});
|
|
890
785
|
return /* @__PURE__ */ jsx(File.Source, {
|
|
891
786
|
name,
|
|
892
|
-
isExportable,
|
|
893
|
-
isIndexable,
|
|
787
|
+
isExportable: true,
|
|
788
|
+
isIndexable: true,
|
|
894
789
|
children: /* @__PURE__ */ jsxs(Function, {
|
|
895
790
|
name,
|
|
896
|
-
export:
|
|
791
|
+
export: true,
|
|
897
792
|
generics: signature.generics,
|
|
898
793
|
params: signature.paramsSignature,
|
|
899
794
|
returnType: functionReturnType,
|
|
@@ -911,62 +806,24 @@ function Operation({ name, node, types, zodResolver, validator, returnType, thro
|
|
|
911
806
|
});
|
|
912
807
|
}
|
|
913
808
|
//#endregion
|
|
914
|
-
//#region ../../internals/client/src/components/SdkClient.tsx
|
|
915
|
-
/**
|
|
916
|
-
* Renders one instance class per tag with one method per operation. The constructor takes a client
|
|
917
|
-
* config object and builds its own client through `createClient`, so each environment is a separate
|
|
918
|
-
* instance: `const api = new PetClient({ baseURL }); api.getPetById(...)`. A per-call `client` option
|
|
919
|
-
* still overrides the instance client for a one-off call.
|
|
920
|
-
*/
|
|
921
|
-
function SdkClient({ name, isExportable = true, isIndexable = true, operations, validator, returnType, throwOnErrorDefault, children }) {
|
|
922
|
-
const methods = operations.map(({ node, name: methodName, types, zodResolver, security }) => buildSdkMethod({
|
|
923
|
-
node,
|
|
924
|
-
name: methodName,
|
|
925
|
-
types,
|
|
926
|
-
zodResolver,
|
|
927
|
-
validator,
|
|
928
|
-
security,
|
|
929
|
-
returnType,
|
|
930
|
-
throwOnErrorDefault
|
|
931
|
-
}));
|
|
932
|
-
const classCode = `export class ${name} {\n${[
|
|
933
|
-
" private readonly client: ClientInstance",
|
|
934
|
-
"",
|
|
935
|
-
" constructor(config: ClientConfig = {}) {",
|
|
936
|
-
" this.client = createClient(config)",
|
|
937
|
-
" }"
|
|
938
|
-
].join("\n")}\n\n${methods.join("\n\n")}\n}`;
|
|
939
|
-
return /* @__PURE__ */ jsxs(File.Source, {
|
|
940
|
-
name,
|
|
941
|
-
isExportable,
|
|
942
|
-
isIndexable,
|
|
943
|
-
children: [classCode, children]
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
//#endregion
|
|
947
809
|
//#region ../../internals/client/src/resolveOperationTypes.ts
|
|
948
810
|
/**
|
|
949
|
-
* Warning a client generator reports when neither plugin can supply the operation types, so the
|
|
950
|
-
* pipeline says why nothing was generated instead of silently emitting no files.
|
|
951
|
-
*/
|
|
952
|
-
const MISSING_OPERATION_TYPES_WARNING = "Skipped: no operation types available. Add `pluginTs()`, or `pluginZod({ inferred: true })` to type the client from its zod schemas.";
|
|
953
|
-
/**
|
|
954
811
|
* Picks the plugin a generated client takes its `Options` and `Responses` types from.
|
|
955
812
|
*
|
|
956
813
|
* `plugin-ts` wins whenever it is in the pipeline, so existing configs keep the types they generate
|
|
957
814
|
* today. Without it, `pluginZod({ inferred: true })` takes over: its inferred options and responses
|
|
958
815
|
* types are built from the same shared schemas as the `plugin-ts` ones, so a client typed off
|
|
959
816
|
* `z.infer` carries what the codecs decode to (a `Temporal` instance, a `Date`) rather than the raw
|
|
960
|
-
* wire type.
|
|
817
|
+
* wire type. Throws `KUBB_MISSING_DEPENDENCY` for `pluginName` when neither is available, since the
|
|
818
|
+
* client has nothing to type against.
|
|
961
819
|
*
|
|
962
820
|
* @example
|
|
963
821
|
* ```ts
|
|
964
|
-
* const types = resolveOperationTypes(ctx.driver)
|
|
965
|
-
* if (!types) return null
|
|
822
|
+
* const types = resolveOperationTypes(ctx.driver, ctx.plugin.name)
|
|
966
823
|
* types.response.options(node) // 'GetPetByIdOptions', or 'GetPetByIdOptionsSchemaType' from zod
|
|
967
824
|
* ```
|
|
968
825
|
*/
|
|
969
|
-
function resolveOperationTypes(driver) {
|
|
826
|
+
function resolveOperationTypes(driver, pluginName) {
|
|
970
827
|
const pluginTs = driver.getPlugin(pluginTsName);
|
|
971
828
|
if (pluginTs) {
|
|
972
829
|
const resolver = driver.getResolver(pluginTsName);
|
|
@@ -978,7 +835,14 @@ function resolveOperationTypes(driver) {
|
|
|
978
835
|
};
|
|
979
836
|
}
|
|
980
837
|
const pluginZod = driver.getPlugin(pluginZodName);
|
|
981
|
-
if (!pluginZod?.options?.inferred)
|
|
838
|
+
if (!pluginZod?.options?.inferred) throw new Diagnostics.Error({
|
|
839
|
+
code: Diagnostics.code.missingDependency,
|
|
840
|
+
severity: "error",
|
|
841
|
+
message: `Plugin "${pluginName}" has no source for operation types.`,
|
|
842
|
+
help: `Add \`pluginTs()\` before "${pluginName}", or add \`pluginZod({ inferred: true })\`.`,
|
|
843
|
+
location: { kind: "config" },
|
|
844
|
+
plugin: pluginName
|
|
845
|
+
});
|
|
982
846
|
const resolver = driver.getResolver(pluginZodName);
|
|
983
847
|
return {
|
|
984
848
|
response: {
|
|
@@ -1008,20 +872,15 @@ function createClientGenerator(name, resolveClientPath) {
|
|
|
1008
872
|
if (!ast.isHttpOperationNode(node)) return null;
|
|
1009
873
|
const { config, driver, resolver, root } = ctx;
|
|
1010
874
|
const { output, validator, returnType, throwOnErrorDefault, group } = ctx.options;
|
|
1011
|
-
const types = resolveOperationTypes(driver);
|
|
1012
|
-
if (!types) {
|
|
1013
|
-
ctx.warn(MISSING_OPERATION_TYPES_WARNING);
|
|
1014
|
-
return null;
|
|
1015
|
-
}
|
|
875
|
+
const types = resolveOperationTypes(driver, ctx.plugin.name);
|
|
1016
876
|
const pluginZod = resolveResponseValidator(validator) === "zod" || resolveRequestValidator(validator) === "zod" ? driver.getPlugin(pluginZodName) : null;
|
|
1017
877
|
const zodResolver = pluginZod ? driver.getResolver(pluginZodName) : null;
|
|
1018
|
-
const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
|
|
1019
878
|
const importedTypeNames = [types.response.options(node), types.response.responses(node)];
|
|
1020
|
-
const importedZodNames =
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
879
|
+
const importedZodNames = buildValidatorHooks({
|
|
880
|
+
node,
|
|
881
|
+
validator,
|
|
882
|
+
zodResolver
|
|
883
|
+
}).importedZodNames;
|
|
1025
884
|
const meta = {
|
|
1026
885
|
name: resolver.name(node.operationId),
|
|
1027
886
|
file: resolver.file({
|
|
@@ -1133,13 +992,106 @@ function createClientGenerator(name, resolveClientPath) {
|
|
|
1133
992
|
});
|
|
1134
993
|
}
|
|
1135
994
|
//#endregion
|
|
995
|
+
//#region ../../internals/client/src/components/SdkClient.tsx
|
|
996
|
+
/**
|
|
997
|
+
* Builds the call config literal forwarded to the contract client; the `throwOnError` fallback is applied after the `...config` spread.
|
|
998
|
+
*/
|
|
999
|
+
function buildCallConfig({ node, validator, zodResolver, security, throwOnErrorDefault }) {
|
|
1000
|
+
const validators = buildValidatorHooks({
|
|
1001
|
+
node,
|
|
1002
|
+
validator,
|
|
1003
|
+
zodResolver
|
|
1004
|
+
});
|
|
1005
|
+
const validatorEntries = [validators.request ? `request: ${validators.request}` : null, validators.response ? `response: ${validators.response}` : null].filter(Boolean);
|
|
1006
|
+
const validatorLiteral = validatorEntries.length ? `validator: { ${validatorEntries.join(", ")} }` : null;
|
|
1007
|
+
const securityLiteral = buildSecurityMetadata({ security });
|
|
1008
|
+
const { literal: contentTypeLiteral } = buildContentType({ node });
|
|
1009
|
+
return `{ ${[
|
|
1010
|
+
`method: '${node.method.toUpperCase()}'`,
|
|
1011
|
+
`url: '${node.path}'`,
|
|
1012
|
+
securityLiteral ? `security: ${securityLiteral}` : null,
|
|
1013
|
+
validatorLiteral,
|
|
1014
|
+
contentTypeLiteral,
|
|
1015
|
+
"...config",
|
|
1016
|
+
`throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
|
|
1017
|
+
].filter(Boolean).join(", ")} }`;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Builds one instance method of a generated SDK class; a per-call `options.client` overrides `this.client`.
|
|
1021
|
+
*/
|
|
1022
|
+
function buildSdkMethod({ node, name, types, zodResolver, validator, security, returnType, throwOnErrorDefault }) {
|
|
1023
|
+
if (!ast.isHttpOperationNode(node)) return "";
|
|
1024
|
+
const signature = buildGroupedOptionsSignature({
|
|
1025
|
+
node,
|
|
1026
|
+
types,
|
|
1027
|
+
returnType,
|
|
1028
|
+
throwOnErrorDefault
|
|
1029
|
+
});
|
|
1030
|
+
const returnStatement = buildReturnStatement({
|
|
1031
|
+
node,
|
|
1032
|
+
types,
|
|
1033
|
+
callConfig: buildCallConfig({
|
|
1034
|
+
node,
|
|
1035
|
+
validator,
|
|
1036
|
+
zodResolver,
|
|
1037
|
+
security,
|
|
1038
|
+
throwOnErrorDefault
|
|
1039
|
+
}),
|
|
1040
|
+
returnType,
|
|
1041
|
+
throwOnErrorDefault
|
|
1042
|
+
});
|
|
1043
|
+
const generics = signature.generics.length ? `<${signature.generics.join(", ")}>` : "";
|
|
1044
|
+
const jsdoc = buildJSDoc(buildOperationComments(node, {
|
|
1045
|
+
link: "urlPath",
|
|
1046
|
+
linkPosition: "beforeDeprecated",
|
|
1047
|
+
splitLines: true
|
|
1048
|
+
}));
|
|
1049
|
+
const methodBody = [
|
|
1050
|
+
buildContentType({ node }).merge ? "const { client: request = this.client, contentType, ...config } = options" : "const { client: request = this.client, ...config } = options",
|
|
1051
|
+
"",
|
|
1052
|
+
returnStatement
|
|
1053
|
+
].map((line) => line ? ` ${line}` : "").join("\n");
|
|
1054
|
+
return `${jsdoc} public ${name}${generics}(${signature.paramsSignature}): ${signature.returnType} {\n${methodBody}\n }`;
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Renders one instance class per tag with one method per operation. The constructor takes a client
|
|
1058
|
+
* config object and builds its own client through `createClient`, so each environment is a separate
|
|
1059
|
+
* instance: `const api = new PetClient({ baseURL }); api.getPetById(...)`. A per-call `client` option
|
|
1060
|
+
* still overrides the instance client for a one-off call.
|
|
1061
|
+
*/
|
|
1062
|
+
function SdkClient({ name, operations, validator, returnType, throwOnErrorDefault }) {
|
|
1063
|
+
const methods = operations.map(({ node, name: methodName, types, zodResolver, security }) => buildSdkMethod({
|
|
1064
|
+
node,
|
|
1065
|
+
name: methodName,
|
|
1066
|
+
types,
|
|
1067
|
+
zodResolver,
|
|
1068
|
+
validator,
|
|
1069
|
+
security,
|
|
1070
|
+
returnType,
|
|
1071
|
+
throwOnErrorDefault
|
|
1072
|
+
}));
|
|
1073
|
+
const classCode = `export class ${name} {\n${[
|
|
1074
|
+
" private readonly client: ClientInstance",
|
|
1075
|
+
"",
|
|
1076
|
+
" constructor(config: ClientConfig = {}) {",
|
|
1077
|
+
" this.client = createClient(config)",
|
|
1078
|
+
" }"
|
|
1079
|
+
].join("\n")}\n\n${methods.join("\n\n")}\n}`;
|
|
1080
|
+
return /* @__PURE__ */ jsx(File.Source, {
|
|
1081
|
+
name,
|
|
1082
|
+
isExportable: true,
|
|
1083
|
+
isIndexable: true,
|
|
1084
|
+
children: classCode
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
//#endregion
|
|
1136
1088
|
//#region ../../internals/client/src/components/SdkFacade.tsx
|
|
1137
1089
|
/**
|
|
1138
1090
|
* Renders a composed root SDK class that instantiates every tag client from one shared config, so
|
|
1139
1091
|
* `new PetStore({ baseURL }).petClient.getPetById(...)` reaches an operation through a single entry
|
|
1140
1092
|
* point bound to one environment. The per-tag clients are read-only fields built in the constructor.
|
|
1141
1093
|
*/
|
|
1142
|
-
function SdkFacade({ name,
|
|
1094
|
+
function SdkFacade({ name, members }) {
|
|
1143
1095
|
const fields = members.map((member) => ` readonly ${member.propName}: ${member.className}`);
|
|
1144
1096
|
const assignments = members.map((member) => ` this.${member.propName} = new ${member.className}(config)`);
|
|
1145
1097
|
const classCode = `export class ${name} {\n${[
|
|
@@ -1149,11 +1101,11 @@ function SdkFacade({ name, isExportable = true, isIndexable = true, members, chi
|
|
|
1149
1101
|
...assignments,
|
|
1150
1102
|
" }"
|
|
1151
1103
|
].join("\n")}\n}`;
|
|
1152
|
-
return /* @__PURE__ */
|
|
1104
|
+
return /* @__PURE__ */ jsx(File.Source, {
|
|
1153
1105
|
name,
|
|
1154
|
-
isExportable,
|
|
1155
|
-
isIndexable,
|
|
1156
|
-
children:
|
|
1106
|
+
isExportable: true,
|
|
1107
|
+
isIndexable: true,
|
|
1108
|
+
children: classCode
|
|
1157
1109
|
});
|
|
1158
1110
|
}
|
|
1159
1111
|
//#endregion
|
|
@@ -1162,13 +1114,7 @@ function resolveTypeImportNames(node, types) {
|
|
|
1162
1114
|
return [types.response.options(node), types.response.responses(node)];
|
|
1163
1115
|
}
|
|
1164
1116
|
function resolveZodImportNames(node, zodResolver, validator) {
|
|
1165
|
-
|
|
1166
|
-
return [
|
|
1167
|
-
resolveResponseValidator(validator) === "zod" ? zodResolver.response.response(node) : null,
|
|
1168
|
-
resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,
|
|
1169
|
-
resolveRequestValidator(validator) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.response.body(node) : null,
|
|
1170
|
-
resolveQueryParamsValidator(validator) === "zod" && queryParams.length > 0 ? zodResolver.param.query(node, queryParams[0]) : null
|
|
1171
|
-
].filter((n) => Boolean(n));
|
|
1117
|
+
return [resolveResponseValidator(validator) === "zod" ? zodResolver.response.response(node) : null, resolveRequestValidator(validator) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.response.body(node) : null].filter((n) => Boolean(n));
|
|
1172
1118
|
}
|
|
1173
1119
|
/**
|
|
1174
1120
|
* Groups operations into one controller per tag. Operations without a tag fall back to a single
|
|
@@ -1275,12 +1221,7 @@ function createSdkGenerator() {
|
|
|
1275
1221
|
location: { kind: "config" },
|
|
1276
1222
|
plugin: ctx.plugin.name
|
|
1277
1223
|
});
|
|
1278
|
-
const
|
|
1279
|
-
if (!types) {
|
|
1280
|
-
ctx.warn(MISSING_OPERATION_TYPES_WARNING);
|
|
1281
|
-
return null;
|
|
1282
|
-
}
|
|
1283
|
-
const controllers = buildControllers(nodes, ctx, types);
|
|
1224
|
+
const controllers = buildControllers(nodes, ctx, resolveOperationTypes(ctx.driver, ctx.plugin.name));
|
|
1284
1225
|
const clientPath = path.resolve(root, ".kubb/client.ts");
|
|
1285
1226
|
const banner = (file) => resolver.default.banner(ctx.meta, {
|
|
1286
1227
|
output,
|
|
@@ -1461,7 +1402,7 @@ var compatibility = {
|
|
|
1461
1402
|
};
|
|
1462
1403
|
//#endregion
|
|
1463
1404
|
//#region package.json
|
|
1464
|
-
var version = "5.6.
|
|
1405
|
+
var version = "5.6.2";
|
|
1465
1406
|
//#endregion
|
|
1466
1407
|
//#region src/templates.ts
|
|
1467
1408
|
/** Absolute path to the fetch client template, copied into `.kubb/client.ts`. */
|
|
@@ -1509,10 +1450,10 @@ const pluginFetch = defineExtension({
|
|
|
1509
1450
|
version,
|
|
1510
1451
|
compatibility
|
|
1511
1452
|
},
|
|
1512
|
-
dependencies: {
|
|
1453
|
+
dependencies: (options) => ({
|
|
1513
1454
|
[pluginTsName]: { optional: true },
|
|
1514
|
-
[pluginZodName]: { optional:
|
|
1515
|
-
},
|
|
1455
|
+
[pluginZodName]: { optional: !isValidatorEnabled(options.validator) }
|
|
1456
|
+
}),
|
|
1516
1457
|
setup(options) {
|
|
1517
1458
|
const config = useConfig();
|
|
1518
1459
|
const { output = {
|
|
@@ -1535,6 +1476,32 @@ const pluginFetch = defineExtension({
|
|
|
1535
1476
|
} : void 0,
|
|
1536
1477
|
resolver: userResolver ? Resolver.merge(resolverClient, userResolver) : resolverClient
|
|
1537
1478
|
};
|
|
1479
|
+
const allowedReturnTypes = [
|
|
1480
|
+
"full",
|
|
1481
|
+
"data",
|
|
1482
|
+
"plain"
|
|
1483
|
+
];
|
|
1484
|
+
assertPluginOption({
|
|
1485
|
+
plugin: pluginFetchName,
|
|
1486
|
+
option: "returnType",
|
|
1487
|
+
value: returnType,
|
|
1488
|
+
allowed: allowedReturnTypes
|
|
1489
|
+
});
|
|
1490
|
+
for (const [index, entry] of override.entries()) {
|
|
1491
|
+
if (entry.options.returnType === void 0) continue;
|
|
1492
|
+
assertPluginOption({
|
|
1493
|
+
plugin: pluginFetchName,
|
|
1494
|
+
option: `override[${index}].options.returnType`,
|
|
1495
|
+
value: entry.options.returnType,
|
|
1496
|
+
allowed: allowedReturnTypes
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
if (resolved.sdk) assertPluginOption({
|
|
1500
|
+
plugin: pluginFetchName,
|
|
1501
|
+
option: "sdk.mode",
|
|
1502
|
+
value: resolved.sdk.mode,
|
|
1503
|
+
allowed: ["tag", "flat"]
|
|
1504
|
+
});
|
|
1538
1505
|
setResolver(resolved.resolver);
|
|
1539
1506
|
setMacros([...defaultMacros, ...options.macros ?? []]);
|
|
1540
1507
|
addGenerator(resolved.sdk ? createSdkGenerator() : clientGenerator);
|