@kubb/plugin-client 5.5.10 → 5.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -31,24 +31,6 @@ let _kubb_plugin_ts = require("@kubb/plugin-ts");
31
31
  let _kubb_plugin_zod = require("@kubb/plugin-zod");
32
32
  let kubb_jsx = require("kubb/jsx");
33
33
  let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
34
- //#region ../../internals/shared/src/params.ts
35
- /**
36
- * Drops parameters that share the same name, keeping the first.
37
- *
38
- * A malformed spec can declare the same parameter name twice within one `in` location. Both would
39
- * resolve to the same output property, so emitting both would yield an object type with a duplicate
40
- * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
41
- * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
42
- */
43
- function dedupeParams(params) {
44
- const seen = /* @__PURE__ */ new Set();
45
- return params.filter((param) => {
46
- if (seen.has(param.name)) return false;
47
- seen.add(param.name);
48
- return true;
49
- });
50
- }
51
- //#endregion
52
34
  //#region ../../internals/shared/src/operation.ts
53
35
  /**
54
36
  * Builds the `ResolverFileParams` every operation generator passes to
@@ -210,11 +192,19 @@ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
210
192
  function getOperationParameters(node) {
211
193
  const cached = operationParameterGroupsByNode.get(node);
212
194
  if (cached) return cached;
195
+ const group = (location) => {
196
+ const seen = /* @__PURE__ */ new Set();
197
+ return node.parameters.filter((param) => {
198
+ if (param.in !== location || seen.has(param.name)) return false;
199
+ seen.add(param.name);
200
+ return true;
201
+ });
202
+ };
213
203
  const groups = {
214
- path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
215
- query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
216
- header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
217
- cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
204
+ path: group("path"),
205
+ query: group("query"),
206
+ header: group("header"),
207
+ cookie: group("cookie")
218
208
  };
219
209
  operationParameterGroupsByNode.set(node, groups);
220
210
  return groups;
@@ -453,7 +443,135 @@ var compatibility = {
453
443
  };
454
444
  //#endregion
455
445
  //#region package.json
456
- var version = "5.5.10";
446
+ var version = "5.5.12";
447
+ //#endregion
448
+ //#region ../../internals/client/src/builders/validatorOptions.ts
449
+ /**
450
+ * Returns `true` when any direction of the validator uses zod (used for dependency checks).
451
+ */
452
+ function isValidatorEnabled(validator) {
453
+ if (!validator) return false;
454
+ if (validator === "zod") return true;
455
+ return Boolean(validator.request || validator.response);
456
+ }
457
+ /**
458
+ * Returns `'zod'` when request body parsing is enabled, `null` otherwise. The string shorthand
459
+ * `'zod'` validates the response only, so it does not enable request parsing.
460
+ */
461
+ function resolveRequestValidator(validator) {
462
+ if (!validator || validator === "zod") return null;
463
+ return validator.request ?? null;
464
+ }
465
+ /**
466
+ * Returns `'zod'` when response parsing is enabled, `null` otherwise. The string shorthand `'zod'`
467
+ * maps to response parsing.
468
+ */
469
+ function resolveResponseValidator(validator) {
470
+ if (!validator) return null;
471
+ if (validator === "zod") return "zod";
472
+ return validator.response ?? null;
473
+ }
474
+ /**
475
+ * Resolves the zod schema name a generated client validates an error body with on the non-throw path.
476
+ * Uses the error-only `<operation>ErrorSchema` (the union of non-2xx statuses); returns `null` when the
477
+ * operation documents no error responses with a schema.
478
+ */
479
+ function buildZodErrorParse(node, zodResolver) {
480
+ if (!node.responses.some((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema))) return null;
481
+ return zodResolver.response.error?.(node) || null;
482
+ }
483
+ //#endregion
484
+ //#region ../../internals/client/src/builders/validator.ts
485
+ /**
486
+ * Builds the validator-hook references for one operation. Request validation runs before the send;
487
+ * response validation runs on the success body only. Returns `null` references when the matching
488
+ * direction is disabled or the schema is absent.
489
+ */
490
+ function buildValidatorHooks({ node, validator, zodResolver }) {
491
+ const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
492
+ const parsesResponse = zodResolver && resolveResponseValidator(validator) === "zod";
493
+ const response = parsesResponse ? zodResolver.response.response(node) || null : null;
494
+ const error = parsesResponse ? buildZodErrorParse(node, zodResolver) : null;
495
+ const request = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body(node) || null : null;
496
+ return {
497
+ request,
498
+ response,
499
+ error,
500
+ importedZodNames: [
501
+ response,
502
+ error,
503
+ request
504
+ ].filter((name) => Boolean(name))
505
+ };
506
+ }
507
+ //#endregion
508
+ //#region ../../internals/client/src/builders/security.ts
509
+ function serializeAuth(auth) {
510
+ const parts = [`type: '${auth.type}'`];
511
+ if (auth.scheme) parts.push(`scheme: '${auth.scheme}'`);
512
+ if (auth.name) parts.push(`name: '${auth.name}'`);
513
+ if (auth.in) parts.push(`in: '${auth.in}'`);
514
+ return `{ ${parts.join(", ")} }`;
515
+ }
516
+ /**
517
+ * Maps an OpenAPI security scheme to the inline `Auth` object, or `null` when the runtime cannot
518
+ * place it (an unresolved `$ref`, or an `apiKey` without a name or outside `header` / `query` /
519
+ * `cookie`). `http` schemes other than `basic` are treated as bearer.
520
+ */
521
+ function resolveSecurityScheme(scheme) {
522
+ if (!scheme || "$ref" in scheme) return null;
523
+ if (scheme.type === "apiKey") {
524
+ if (!scheme.name || scheme.in !== "header" && scheme.in !== "query" && scheme.in !== "cookie") return null;
525
+ return {
526
+ type: "apiKey",
527
+ name: scheme.name,
528
+ in: scheme.in
529
+ };
530
+ }
531
+ if (scheme.type === "http") return {
532
+ type: "http",
533
+ scheme: scheme.scheme?.toLowerCase() === "basic" ? "basic" : "bearer"
534
+ };
535
+ if (scheme.type === "oauth2") return { type: "oauth2" };
536
+ if (scheme.type === "openIdConnect") return { type: "openIdConnect" };
537
+ return null;
538
+ }
539
+ /**
540
+ * Derives the per-operation security metadata from the OpenAPI document. The operation's own
541
+ * `security` overrides the global `security` (an explicit empty array disables auth), and every
542
+ * referenced scheme is resolved from `components.securitySchemes` into a flat, de-duplicated list of
543
+ * `Auth` objects the runtime walks in order.
544
+ *
545
+ * @example
546
+ * `getOperationSecurity({ document, method: 'POST', path: '/pet' })`
547
+ * `// [{ type: 'http', scheme: 'bearer' }]`
548
+ */
549
+ function getOperationSecurity({ document, method, path }) {
550
+ if (!document) return void 0;
551
+ const requirements = (document.paths?.[path]?.[method.toLowerCase()])?.security ?? document.security;
552
+ if (!requirements?.length) return void 0;
553
+ const definitions = document.components?.securitySchemes ?? {};
554
+ const security = [];
555
+ const seen = /* @__PURE__ */ new Set();
556
+ for (const requirement of requirements) for (const schemeName of Object.keys(requirement)) {
557
+ if (seen.has(schemeName)) continue;
558
+ seen.add(schemeName);
559
+ const auth = resolveSecurityScheme(definitions[schemeName]);
560
+ if (auth) security.push(auth);
561
+ }
562
+ return security.length ? security : void 0;
563
+ }
564
+ /**
565
+ * Serializes the per-operation security into the literal emitted on each generated call's `security`
566
+ * field. The runtime `resolveAuth` helper walks it, calling the configured `auth` resolver per entry.
567
+ *
568
+ * @example
569
+ * `buildSecurityMetadata({ security: [{ type: 'http', scheme: 'bearer' }] }) // "[{ type: 'http', scheme: 'bearer' }]"`
570
+ */
571
+ function buildSecurityMetadata({ security }) {
572
+ if (!security?.length) return null;
573
+ return `[${security.map(serializeAuth).join(", ")}]`;
574
+ }
457
575
  //#endregion
458
576
  //#region ../../internals/client/src/builders/contentType.ts
459
577
  /**
@@ -547,74 +665,6 @@ function buildReturnStatement({ node, types, callConfig, returnType, throwOnErro
547
665
  })}>>)`;
548
666
  }
549
667
  //#endregion
550
- //#region ../../internals/client/src/builders/security.ts
551
- function serializeAuth(auth) {
552
- const parts = [`type: '${auth.type}'`];
553
- if (auth.scheme) parts.push(`scheme: '${auth.scheme}'`);
554
- if (auth.name) parts.push(`name: '${auth.name}'`);
555
- if (auth.in) parts.push(`in: '${auth.in}'`);
556
- return `{ ${parts.join(", ")} }`;
557
- }
558
- /**
559
- * Maps an OpenAPI security scheme to the inline `Auth` object, or `null` when the runtime cannot
560
- * place it (an unresolved `$ref`, or an `apiKey` without a name or outside `header` / `query` /
561
- * `cookie`). `http` schemes other than `basic` are treated as bearer.
562
- */
563
- function resolveSecurityScheme(scheme) {
564
- if (!scheme || "$ref" in scheme) return null;
565
- if (scheme.type === "apiKey") {
566
- if (!scheme.name || scheme.in !== "header" && scheme.in !== "query" && scheme.in !== "cookie") return null;
567
- return {
568
- type: "apiKey",
569
- name: scheme.name,
570
- in: scheme.in
571
- };
572
- }
573
- if (scheme.type === "http") return {
574
- type: "http",
575
- scheme: scheme.scheme?.toLowerCase() === "basic" ? "basic" : "bearer"
576
- };
577
- if (scheme.type === "oauth2") return { type: "oauth2" };
578
- if (scheme.type === "openIdConnect") return { type: "openIdConnect" };
579
- return null;
580
- }
581
- /**
582
- * Derives the per-operation security metadata from the OpenAPI document. The operation's own
583
- * `security` overrides the global `security` (an explicit empty array disables auth), and every
584
- * referenced scheme is resolved from `components.securitySchemes` into a flat, de-duplicated list of
585
- * `Auth` objects the runtime walks in order.
586
- *
587
- * @example
588
- * `getOperationSecurity({ document, method: 'POST', path: '/pet' })`
589
- * `// [{ type: 'http', scheme: 'bearer' }]`
590
- */
591
- function getOperationSecurity({ document, method, path }) {
592
- if (!document) return void 0;
593
- const requirements = (document.paths?.[path]?.[method.toLowerCase()])?.security ?? document.security;
594
- if (!requirements?.length) return void 0;
595
- const definitions = document.components?.securitySchemes ?? {};
596
- const security = [];
597
- const seen = /* @__PURE__ */ new Set();
598
- for (const requirement of requirements) for (const schemeName of Object.keys(requirement)) {
599
- if (seen.has(schemeName)) continue;
600
- seen.add(schemeName);
601
- const auth = resolveSecurityScheme(definitions[schemeName]);
602
- if (auth) security.push(auth);
603
- }
604
- return security.length ? security : void 0;
605
- }
606
- /**
607
- * Serializes the per-operation security into the literal emitted on each generated call's `security`
608
- * field. The runtime `resolveAuth` helper walks it, calling the configured `auth` resolver per entry.
609
- *
610
- * @example
611
- * `buildSecurityMetadata({ security: [{ type: 'http', scheme: 'bearer' }] }) // "[{ type: 'http', scheme: 'bearer' }]"`
612
- */
613
- function buildSecurityMetadata({ security }) {
614
- if (!security?.length) return null;
615
- return `[${security.map(serializeAuth).join(", ")}]`;
616
- }
617
- //#endregion
618
668
  //#region ../../internals/client/src/builders/signature.ts
619
669
  const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
620
670
  /**
@@ -644,76 +694,6 @@ function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDef
644
694
  };
645
695
  }
646
696
  //#endregion
647
- //#region ../../internals/client/src/builders/validatorOptions.ts
648
- /**
649
- * Returns `'zod'` when request body parsing is enabled, `null` otherwise. The string shorthand
650
- * `'zod'` validates the response only, so it does not enable request parsing.
651
- */
652
- function resolveRequestValidator(validator) {
653
- if (!validator || validator === "zod") return null;
654
- return validator.request ?? null;
655
- }
656
- /**
657
- * Returns `'zod'` when response parsing is enabled, `null` otherwise. The string shorthand `'zod'`
658
- * maps to response parsing.
659
- */
660
- function resolveResponseValidator(validator) {
661
- if (!validator) return null;
662
- if (validator === "zod") return "zod";
663
- return validator.response ?? null;
664
- }
665
- /**
666
- * Resolves the zod expression a generated client validates a success response with. Only success
667
- * (2xx) bodies reach the parse under the throw-on-error contract, so the success-only
668
- * `<operation>ResponseSchema` is used; error bodies are never zod-parsed.
669
- */
670
- function buildZodResponseParse(node, zodResolver) {
671
- const name = zodResolver.response.response(node);
672
- return name ? {
673
- expression: name,
674
- importNames: [name]
675
- } : null;
676
- }
677
- /**
678
- * Resolves the zod expression a generated client validates an error body with on the non-throw path.
679
- * Uses the error-only `<operation>ErrorSchema` (the union of non-2xx statuses); returns `null` when the
680
- * operation documents no error responses with a schema.
681
- */
682
- function buildZodErrorParse(node, zodResolver) {
683
- if (!node.responses.some((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema))) return null;
684
- const name = zodResolver.response.error?.(node);
685
- return name ? {
686
- expression: name,
687
- importNames: [name]
688
- } : null;
689
- }
690
- //#endregion
691
- //#region ../../internals/client/src/builders/validator.ts
692
- /**
693
- * Builds the validator-hook references for one operation. Request validation runs before the send;
694
- * response validation runs on the success body only. Returns `null` references when the matching
695
- * direction is disabled or the schema is absent.
696
- */
697
- function buildValidatorHooks({ node, validator, zodResolver }) {
698
- const importedZodNames = [];
699
- const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
700
- const zodRequestName = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body(node) : null;
701
- const request = zodRequestName ?? null;
702
- if (zodRequestName) importedZodNames.push(zodRequestName);
703
- const responseParse = zodResolver && resolveResponseValidator(validator) === "zod" ? buildZodResponseParse(node, zodResolver) : null;
704
- const response = responseParse ? responseParse.expression : null;
705
- if (responseParse) importedZodNames.push(...responseParse.importNames);
706
- const errorParse = zodResolver && resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver) : null;
707
- const error = errorParse ? errorParse.expression : null;
708
- if (errorParse) importedZodNames.push(...errorParse.importNames);
709
- return {
710
- request,
711
- response,
712
- error,
713
- importedZodNames
714
- };
715
- }
716
- //#endregion
717
697
  //#region ../../internals/client/src/builders/styles.ts
718
698
  /**
719
699
  * Renders a parameter name as an object-literal key, quoted when it is not a bare identifier.
@@ -774,7 +754,7 @@ function buildStyles({ node }) {
774
754
  * type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
775
755
  * the source.
776
756
  */
777
- function Operation({ name, node, types, zodResolver, validator, returnType, throwOnErrorDefault, security, isExportable = true, isIndexable = true }) {
757
+ function Operation({ name, node, types, zodResolver, validator, returnType, throwOnErrorDefault, security }) {
778
758
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
779
759
  const signature = buildGroupedOptionsSignature({
780
760
  node,
@@ -821,11 +801,11 @@ function Operation({ name, node, types, zodResolver, validator, returnType, thro
821
801
  });
822
802
  return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
823
803
  name,
824
- isExportable,
825
- isIndexable,
804
+ isExportable: true,
805
+ isIndexable: true,
826
806
  children: /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.Function, {
827
807
  name,
828
- export: isExportable,
808
+ export: true,
829
809
  generics: signature.generics,
830
810
  params: signature.paramsSignature,
831
811
  returnType: functionReturnType,
@@ -845,27 +825,22 @@ function Operation({ name, node, types, zodResolver, validator, returnType, thro
845
825
  //#endregion
846
826
  //#region ../../internals/client/src/resolveOperationTypes.ts
847
827
  /**
848
- * Warning a client generator reports when neither plugin can supply the operation types, so the
849
- * pipeline says why nothing was generated instead of silently emitting no files.
850
- */
851
- const MISSING_OPERATION_TYPES_WARNING = "Skipped: no operation types available. Add `pluginTs()`, or `pluginZod({ inferred: true })` to type the client from its zod schemas.";
852
- /**
853
828
  * Picks the plugin a generated client takes its `Options` and `Responses` types from.
854
829
  *
855
830
  * `plugin-ts` wins whenever it is in the pipeline, so existing configs keep the types they generate
856
831
  * today. Without it, `pluginZod({ inferred: true })` takes over: its inferred options and responses
857
832
  * types are built from the same shared schemas as the `plugin-ts` ones, so a client typed off
858
833
  * `z.infer` carries what the codecs decode to (a `Temporal` instance, a `Date`) rather than the raw
859
- * wire type. Returns `null` when neither is available, leaving the caller nothing to type against.
834
+ * wire type. Throws `KUBB_MISSING_DEPENDENCY` for `pluginName` when neither is available, since the
835
+ * client has nothing to type against.
860
836
  *
861
837
  * @example
862
838
  * ```ts
863
- * const types = resolveOperationTypes(ctx.driver)
864
- * if (!types) return null
839
+ * const types = resolveOperationTypes(ctx.driver, ctx.plugin.name)
865
840
  * types.response.options(node) // 'GetPetByIdOptions', or 'GetPetByIdOptionsSchemaType' from zod
866
841
  * ```
867
842
  */
868
- function resolveOperationTypes(driver) {
843
+ function resolveOperationTypes(driver, pluginName) {
869
844
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
870
845
  if (pluginTs) {
871
846
  const resolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
@@ -877,7 +852,14 @@ function resolveOperationTypes(driver) {
877
852
  };
878
853
  }
879
854
  const pluginZod = driver.getPlugin(_kubb_plugin_zod.pluginZodName);
880
- if (!pluginZod?.options?.inferred) return null;
855
+ if (!pluginZod?.options?.inferred) throw new kubb_kit.Diagnostics.Error({
856
+ code: kubb_kit.Diagnostics.code.missingDependency,
857
+ severity: "error",
858
+ message: `Plugin "${pluginName}" has no source for operation types.`,
859
+ help: `Add \`pluginTs()\` before "${pluginName}", or add \`pluginZod({ inferred: true })\`.`,
860
+ location: { kind: "config" },
861
+ plugin: pluginName
862
+ });
881
863
  const resolver = driver.getResolver(_kubb_plugin_zod.pluginZodName);
882
864
  return {
883
865
  response: {
@@ -907,20 +889,15 @@ function createClientGenerator(name, resolveClientPath) {
907
889
  if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
908
890
  const { config, driver, resolver, root } = ctx;
909
891
  const { output, validator, returnType, throwOnErrorDefault, group } = ctx.options;
910
- const types = resolveOperationTypes(driver);
911
- if (!types) {
912
- ctx.warn(MISSING_OPERATION_TYPES_WARNING);
913
- return null;
914
- }
892
+ const types = resolveOperationTypes(driver, ctx.plugin.name);
915
893
  const pluginZod = resolveResponseValidator(validator) === "zod" || resolveRequestValidator(validator) === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
916
894
  const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
917
- const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
918
895
  const importedTypeNames = [types.response.options(node), types.response.responses(node)];
919
- const importedZodNames = zodResolver ? [
920
- resolveResponseValidator(validator) === "zod" ? zodResolver.response.response?.(node) : null,
921
- resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,
922
- resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.response.body?.(node) : null
923
- ].filter((name) => Boolean(name)) : [];
896
+ const importedZodNames = buildValidatorHooks({
897
+ node,
898
+ validator,
899
+ zodResolver
900
+ }).importedZodNames;
924
901
  const meta = {
925
902
  name: resolver.name(node.operationId),
926
903
  file: resolver.file({
@@ -1096,7 +1073,7 @@ const pluginClient = (0, kubb_kit.defineExtension)({
1096
1073
  },
1097
1074
  dependencies: (options) => ({
1098
1075
  [_kubb_plugin_ts.pluginTsName]: { optional: true },
1099
- [_kubb_plugin_zod.pluginZodName]: { optional: !options.validator }
1076
+ [_kubb_plugin_zod.pluginZodName]: { optional: !isValidatorEnabled(options.validator) }
1100
1077
  }),
1101
1078
  setup(options) {
1102
1079
  const { output = {