@kubb/plugin-fetch 5.0.0-beta.77 → 5.0.0-beta.79

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
@@ -103,7 +103,7 @@ function toFilePath(name, caseLast = camelCase) {
103
103
  * JavaScript and Java reserved words.
104
104
  * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
105
105
  */
106
- const reservedWords = new Set([
106
+ const reservedWords = /* @__PURE__ */ new Set([
107
107
  "abstract",
108
108
  "arguments",
109
109
  "boolean",
@@ -352,6 +352,23 @@ function caseParams(params, casing) {
352
352
  caseParamsCache.set(params, result);
353
353
  return result;
354
354
  }
355
+ /**
356
+ * Drops parameters that collapse to the same property identity once camelCased, keeping the first.
357
+ *
358
+ * Some specs declare the same parameter twice under different casings (for example AWS S3 lists both
359
+ * `max-uploads` and `MaxUploads`). Both resolve to one output property, so emitting both would yield
360
+ * an object type with a duplicate member, which TypeScript rejects. De-duplicate by the camelCased
361
+ * identity so the resulting group is collision-free regardless of the names each caller carries.
362
+ */
363
+ function dedupeByCasedName(params) {
364
+ const seen = /* @__PURE__ */ new Set();
365
+ return params.filter((param) => {
366
+ const key = camelCase(param.name);
367
+ if (seen.has(key)) return false;
368
+ seen.add(key);
369
+ return true;
370
+ });
371
+ }
355
372
  //#endregion
356
373
  //#region ../../internals/shared/src/operation.ts
357
374
  /**
@@ -390,6 +407,55 @@ function getContentTypeInfo(node) {
390
407
  hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
391
408
  };
392
409
  }
410
+ /**
411
+ * The request-body counterpart for the primary success response: the content types it documents and
412
+ * whether several are present, so the client can let a caller pick which one to accept.
413
+ */
414
+ function getResponseContentTypeInfo(node) {
415
+ const contentTypes = getPrimarySuccessResponse(node)?.content?.map((e) => e.contentType) ?? [];
416
+ const isMultipleContentTypes = contentTypes.length > 1;
417
+ return {
418
+ contentTypes,
419
+ isMultipleContentTypes,
420
+ contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
421
+ defaultContentType: contentTypes[0] ?? "application/json",
422
+ hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
423
+ };
424
+ }
425
+ /**
426
+ * Reads the single base content type of an operation's primary success response, lowercased and
427
+ * stripped of any `; charset=...` suffix. Returns `undefined` when the response declares zero or
428
+ * more than one content type, since neither case has a single type to act on.
429
+ */
430
+ function getPrimarySuccessContentType(node) {
431
+ const contentTypes = getPrimarySuccessResponse(node)?.content?.map((entry) => entry.contentType) ?? [];
432
+ if (contentTypes.length !== 1) return void 0;
433
+ return contentTypes[0].split(";")[0].trim().toLowerCase();
434
+ }
435
+ /**
436
+ * Whether an operation streams its primary success response as Server-Sent Events
437
+ * (`text/event-stream`). The client generator uses this to return a typed event stream instead of a
438
+ * one-shot `RequestResult`.
439
+ */
440
+ function isEventStream(node) {
441
+ return getPrimarySuccessContentType(node) === "text/event-stream";
442
+ }
443
+ /**
444
+ * Derives the default `responseType` for an operation from its primary success response.
445
+ *
446
+ * Returns a value only when that response declares a single non-JSON content type. `text/event-stream`
447
+ * and other binary types (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`,
448
+ * `video/*`) map to a stream or `'blob'`, and other `text/*` maps to `'text'`. Otherwise `undefined`,
449
+ * leaving the runtime client's `Content-Type` auto-detection in charge.
450
+ */
451
+ function getResponseType(node) {
452
+ const baseType = getPrimarySuccessContentType(node);
453
+ if (!baseType) return void 0;
454
+ if (baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json") return void 0;
455
+ if (baseType === "text/event-stream") return "stream";
456
+ if (baseType.startsWith("text/")) return "text";
457
+ if (baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType)) return "blob";
458
+ }
393
459
  function buildOperationComments(node, options = {}) {
394
460
  const { link = "pathTemplate", linkPosition = "afterDeprecated", splitLines = false } = options;
395
461
  const linkComment = getOperationLink(node, link);
@@ -410,10 +476,10 @@ function buildOperationComments(node, options = {}) {
410
476
  function getOperationParameters(node, options = {}) {
411
477
  const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
412
478
  return {
413
- path: params.filter((param) => param.in === "path"),
414
- query: params.filter((param) => param.in === "query"),
415
- header: params.filter((param) => param.in === "header"),
416
- cookie: params.filter((param) => param.in === "cookie")
479
+ path: dedupeByCasedName(params.filter((param) => param.in === "path")),
480
+ query: dedupeByCasedName(params.filter((param) => param.in === "query")),
481
+ header: dedupeByCasedName(params.filter((param) => param.in === "header")),
482
+ cookie: dedupeByCasedName(params.filter((param) => param.in === "cookie"))
417
483
  };
418
484
  }
419
485
  function getStatusCodeNumber(statusCode) {
@@ -424,6 +490,15 @@ function isSuccessStatusCode(statusCode) {
424
490
  const code = getStatusCodeNumber(statusCode);
425
491
  return code !== null && code >= 200 && code < 300;
426
492
  }
493
+ function getSuccessResponses(responses) {
494
+ return responses.filter((response) => isSuccessStatusCode(response.statusCode));
495
+ }
496
+ function getOperationSuccessResponses(node) {
497
+ return getSuccessResponses(node.responses);
498
+ }
499
+ function getPrimarySuccessResponse(node) {
500
+ return getOperationSuccessResponses(node)[0] ?? null;
501
+ }
427
502
  //#endregion
428
503
  //#region ../../internals/shared/src/group.ts
429
504
  /**
@@ -456,39 +531,39 @@ function createGroupConfig(group) {
456
531
  };
457
532
  }
458
533
  //#endregion
459
- //#region ../../internals/client/src/builders/parser.ts
534
+ //#region ../../internals/client/src/builders/validatorOptions.ts
460
535
  /**
461
- * Returns `true` when any direction of the parser uses zod (used for dependency checks).
536
+ * Returns `true` when any direction of the validator uses zod (used for dependency checks).
462
537
  */
463
- function isParserEnabled(parser) {
464
- if (!parser) return false;
465
- if (parser === "zod") return true;
466
- return Boolean(parser.request || parser.response);
538
+ function isValidatorEnabled(validator) {
539
+ if (!validator) return false;
540
+ if (validator === "zod") return true;
541
+ return Boolean(validator.request || validator.response);
467
542
  }
468
543
  /**
469
544
  * Returns `'zod'` when request body parsing is enabled, `null` otherwise. The string shorthand
470
545
  * `'zod'` validates the response only, so it does not enable request parsing.
471
546
  */
472
- function resolveRequestParser(parser) {
473
- if (!parser || parser === "zod") return null;
474
- return parser.request ?? null;
547
+ function resolveRequestValidator(validator) {
548
+ if (!validator || validator === "zod") return null;
549
+ return validator.request ?? null;
475
550
  }
476
551
  /**
477
552
  * Returns `'zod'` when query-parameters parsing is enabled, `null` otherwise. Only the object form
478
553
  * `{ request: 'zod' }` enables it.
479
554
  */
480
- function resolveQueryParamsParser(parser) {
481
- if (!parser || parser === "zod") return null;
482
- return parser.request ?? null;
555
+ function resolveQueryParamsValidator(validator) {
556
+ if (!validator || validator === "zod") return null;
557
+ return validator.request ?? null;
483
558
  }
484
559
  /**
485
560
  * Returns `'zod'` when response parsing is enabled, `null` otherwise. The string shorthand `'zod'`
486
561
  * maps to response parsing.
487
562
  */
488
- function resolveResponseParser(parser) {
489
- if (!parser) return null;
490
- if (parser === "zod") return "zod";
491
- return parser.response ?? null;
563
+ function resolveResponseValidator(validator) {
564
+ if (!validator) return null;
565
+ if (validator === "zod") return "zod";
566
+ return validator.response ?? null;
492
567
  }
493
568
  /**
494
569
  * Resolves the zod expression a generated client validates a success response with. Only success
@@ -642,23 +717,74 @@ function buildGroupedOptionsSignature({ node, tsResolver }) {
642
717
  };
643
718
  }
644
719
  //#endregion
720
+ //#region ../../internals/client/src/builders/styles.ts
721
+ /**
722
+ * Renders a parameter name as an object-literal key, quoting it when the camelCased name is not a
723
+ * bare identifier (for example a name that starts with a digit) so the emitted literal stays valid.
724
+ */
725
+ function toKey(name) {
726
+ const cased = camelCase(name);
727
+ return isValidVarName(cased) ? cased : JSON.stringify(cased);
728
+ }
729
+ /**
730
+ * Serializes one parameter's metadata into a `{ style, explode }` literal, or `null` when the
731
+ * parameter carries neither. Path and query carry the serialization `style`; header and cookie use a
732
+ * fixed style (`simple` and `form`), so only `explode` is emitted for them.
733
+ */
734
+ function serializeParameter(parameter) {
735
+ const parts = [];
736
+ if ((parameter.in === "path" || parameter.in === "query") && parameter.style) parts.push(`style: '${parameter.style}'`);
737
+ if (parameter.explode !== void 0) parts.push(`explode: ${parameter.explode}`);
738
+ return parts.length > 0 ? `{ ${parts.join(", ")} }` : null;
739
+ }
740
+ /**
741
+ * Builds the per-operation `styles` literal from the operation's parameters, grouped by location and
742
+ * keyed by the camelCased parameter name to match the generated `path` / `query` / `headers` keys.
743
+ * Only parameters whose source defines `style` or `explode` are emitted, so calls without
744
+ * serialization metadata keep the runtime defaults and existing output is unchanged. Returns `null`
745
+ * when no parameter carries metadata.
746
+ *
747
+ * @example
748
+ * ```ts
749
+ * // a path param with { style: 'matrix', explode: true } and a query param with { explode: false }
750
+ * buildStyles({ node }) // "{ path: { id: { style: 'matrix', explode: true } }, query: { tags: { explode: false } } }"
751
+ * ```
752
+ */
753
+ function buildStyles({ node }) {
754
+ if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
755
+ const groups = {
756
+ path: [],
757
+ query: [],
758
+ header: [],
759
+ cookie: []
760
+ };
761
+ for (const parameter of node.parameters) {
762
+ const literal = serializeParameter(parameter);
763
+ if (!literal) continue;
764
+ groups[parameter.in].push(`${toKey(parameter.name)}: ${literal}`);
765
+ }
766
+ const locations = Object.keys(groups).filter((location) => groups[location].length > 0);
767
+ if (locations.length === 0) return null;
768
+ return `{ ${locations.map((location) => `${location}: { ${groups[location].join(", ")} }`).join(", ")} }`;
769
+ }
770
+ //#endregion
645
771
  //#region ../../internals/client/src/builders/validator.ts
646
772
  /**
647
- * Builds the parser-hook expressions for one operation. Request parsing runs before the send;
648
- * response parsing runs on the success body only. Returns `null` expressions when the matching
649
- * parser direction is disabled or the schema is absent.
773
+ * Builds the validator-hook references for one operation. Request validation runs before the send;
774
+ * response validation runs on the success body only. Returns `null` references when the matching
775
+ * direction is disabled or the schema is absent.
650
776
  */
651
- function buildParserHooks({ node, parser, zodResolver }) {
777
+ function buildValidatorHooks({ node, validator, zodResolver }) {
652
778
  const importedZodNames = [];
653
779
  const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
654
- const zodRequestName = zodResolver && resolveRequestParser(parser) === "zod" && hasRequestBody ? zodResolver.resolveDataName?.(node) : null;
655
- const request = zodRequestName ? `(data: unknown) => ${zodRequestName}.parse(data)` : null;
780
+ const zodRequestName = zodResolver && resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.resolveDataName?.(node) : null;
781
+ const request = zodRequestName ?? null;
656
782
  if (zodRequestName) importedZodNames.push(zodRequestName);
657
- const responseParse = zodResolver && resolveResponseParser(parser) === "zod" ? buildZodResponseParse(node, zodResolver) : null;
658
- const response = responseParse ? `(data: unknown) => ${responseParse.expression}.parse(data)` : null;
783
+ const responseParse = zodResolver && resolveResponseValidator(validator) === "zod" ? buildZodResponseParse(node, zodResolver) : null;
784
+ const response = responseParse ? responseParse.expression : null;
659
785
  if (responseParse) importedZodNames.push(...responseParse.importNames);
660
- const errorParse = zodResolver && resolveResponseParser(parser) === "zod" ? buildZodErrorParse(node, zodResolver) : null;
661
- const error = errorParse ? `(data: unknown) => ${errorParse.expression}.parse(data)` : null;
786
+ const errorParse = zodResolver && resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver) : null;
787
+ const error = errorParse ? errorParse.expression : null;
662
788
  if (errorParse) importedZodNames.push(...errorParse.importNames);
663
789
  return {
664
790
  request,
@@ -674,34 +800,49 @@ function buildParserHooks({ node, parser, zodResolver }) {
674
800
  * single `options` object to the resolved client and returns the `RequestResult`. The type, signature,
675
801
  * and call config are built with the AST factory, and only the jsx-renderer emits the source.
676
802
  */
677
- function Operation({ name, node, tsResolver, zodResolver, parser, security, isExportable = true, isIndexable = true }) {
803
+ function Operation({ name, node, tsResolver, zodResolver, validator, security, isExportable = true, isIndexable = true }) {
678
804
  if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
679
805
  const signature = buildGroupedOptionsSignature({
680
806
  node,
681
807
  tsResolver
682
808
  });
683
- const parsers = buildParserHooks({
809
+ const validators = buildValidatorHooks({
684
810
  node,
685
- parser,
811
+ validator,
686
812
  zodResolver
687
813
  });
688
814
  const securityLiteral = buildSecurityMetadata({ security });
815
+ const stylesLiteral = buildStyles({ node });
689
816
  const { defaultContentType } = getContentTypeInfo(node);
690
- const contentTypeLiteral = Boolean(node.requestBody?.content?.[0]?.schema) && defaultContentType !== "application/json" ? `contentType: '${defaultContentType}'` : null;
691
- const parserEntries = [
692
- parsers.request ? `request: ${parsers.request}` : null,
693
- parsers.response ? `response: ${parsers.response}` : null,
694
- parsers.error ? `error: ${parsers.error}` : null
817
+ const bakedRequestContentType = Boolean(node.requestBody?.content?.[0]?.schema) && defaultContentType !== "application/json" ? defaultContentType : null;
818
+ const mergeContentType = Boolean(bakedRequestContentType) && getResponseContentTypeInfo(node).isMultipleContentTypes;
819
+ const contentTypeLiteral = !bakedRequestContentType ? null : mergeContentType ? `contentType: { request: '${bakedRequestContentType}', ...(typeof contentType === 'string' ? { request: contentType } : contentType) }` : `contentType: { request: '${bakedRequestContentType}' }`;
820
+ const eventStream = isEventStream(node);
821
+ const responseType = getResponseType(node);
822
+ const responseTypeLiteral = responseType ? `responseType: '${responseType}'` : null;
823
+ const validatorEntries = [
824
+ validators.request ? `request: ${validators.request}` : null,
825
+ validators.response ? `response: ${validators.response}` : null,
826
+ validators.error ? `error: ${validators.error}` : null
695
827
  ].filter(Boolean);
696
- const parserLiteral = parserEntries.length ? `parser: { ${parserEntries.join(", ")} }` : null;
828
+ const validatorLiteral = validatorEntries.length ? `validator: { ${validatorEntries.join(", ")} }` : null;
697
829
  const callConfig = `{ ${[
698
830
  `method: '${node.method.toUpperCase()}'`,
699
831
  `url: '${Url.toCasedTemplate(node.path, { casing: "camelcase" })}'`,
700
832
  securityLiteral ? `security: ${securityLiteral}` : null,
701
- parserLiteral,
833
+ stylesLiteral ? `styles: ${stylesLiteral}` : null,
834
+ validatorLiteral,
702
835
  contentTypeLiteral,
836
+ responseTypeLiteral,
703
837
  "...config"
704
838
  ].filter(Boolean).join(", ")} }`;
839
+ const eventType = `SuccessOf<${tsResolver.resolveResponsesName(node)}>`;
840
+ const returnType = eventStream ? `Promise<EventStreamResult<${eventType}>>` : signature.returnType;
841
+ const returnStatement = eventStream ? `return toEventStream<${eventType}>(request(${callConfig}))` : buildReturnStatement({
842
+ node,
843
+ tsResolver,
844
+ callConfig
845
+ });
705
846
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
706
847
  name,
707
848
  isExportable,
@@ -711,20 +852,16 @@ function Operation({ name, node, tsResolver, zodResolver, parser, security, isEx
711
852
  export: isExportable,
712
853
  generics: signature.generics,
713
854
  params: signature.paramsSignature,
714
- returnType: signature.returnType,
855
+ returnType,
715
856
  JSDoc: { comments: buildOperationComments(node, {
716
857
  link: "urlPath",
717
858
  linkPosition: "beforeDeprecated",
718
859
  splitLines: true
719
860
  }) },
720
861
  children: [
721
- "const { client: request = client, ...config } = options",
862
+ mergeContentType ? "const { client: request = client, contentType, ...config } = options" : "const { client: request = client, ...config } = options",
722
863
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
723
- buildReturnStatement({
724
- node,
725
- tsResolver,
726
- callConfig
727
- })
864
+ returnStatement
728
865
  ]
729
866
  })
730
867
  });
@@ -733,23 +870,23 @@ function Operation({ name, node, tsResolver, zodResolver, parser, security, isEx
733
870
  //#region ../../internals/client/src/builders/sdkMethod.ts
734
871
  /**
735
872
  * Builds the call config literal forwarded to the contract client, mirroring the shared `Operation`
736
- * component: `{ method, url, security?, parser?, ...config }`. The `...config` spread carries every
873
+ * component: `{ method, url, security?, validator?, ...config }`. The `...config` spread carries every
737
874
  * per-call field (including `throwOnError`), so the method stays a thin wrapper over the contract.
738
875
  */
739
- function buildCallConfig({ node, parser, zodResolver, security }) {
740
- const parsers = buildParserHooks({
876
+ function buildCallConfig({ node, validator, zodResolver, security }) {
877
+ const validators = buildValidatorHooks({
741
878
  node,
742
- parser,
879
+ validator,
743
880
  zodResolver
744
881
  });
745
- const parserEntries = [parsers.request ? `request: ${parsers.request}` : null, parsers.response ? `response: ${parsers.response}` : null].filter(Boolean);
746
- const parserLiteral = parserEntries.length ? `parser: { ${parserEntries.join(", ")} }` : null;
882
+ const validatorEntries = [validators.request ? `request: ${validators.request}` : null, validators.response ? `response: ${validators.response}` : null].filter(Boolean);
883
+ const validatorLiteral = validatorEntries.length ? `validator: { ${validatorEntries.join(", ")} }` : null;
747
884
  const securityLiteral = buildSecurityMetadata({ security });
748
885
  return `{ ${[
749
886
  `method: '${node.method.toUpperCase()}'`,
750
887
  `url: '${Url.toCasedTemplate(node.path, { casing: "camelcase" })}'`,
751
888
  securityLiteral ? `security: ${securityLiteral}` : null,
752
- parserLiteral,
889
+ validatorLiteral,
753
890
  "...config"
754
891
  ].filter(Boolean).join(", ")} }`;
755
892
  }
@@ -759,7 +896,7 @@ function buildCallConfig({ node, parser, zodResolver, security }) {
759
896
  * returns the `RequestResult`. A per-call `options.client` still overrides the instance client, so
760
897
  * one operation can be routed to a different environment without a new instance.
761
898
  */
762
- function buildSdkMethod({ node, name, tsResolver, zodResolver, parser, security }) {
899
+ function buildSdkMethod({ node, name, tsResolver, zodResolver, validator, security }) {
763
900
  if (!_kubb_core.ast.isHttpOperationNode(node)) return "";
764
901
  const signature = buildGroupedOptionsSignature({
765
902
  node,
@@ -770,7 +907,7 @@ function buildSdkMethod({ node, name, tsResolver, zodResolver, parser, security
770
907
  tsResolver,
771
908
  callConfig: buildCallConfig({
772
909
  node,
773
- parser,
910
+ validator,
774
911
  zodResolver,
775
912
  security
776
913
  })
@@ -796,13 +933,13 @@ function buildSdkMethod({ node, name, tsResolver, zodResolver, parser, security
796
933
  * instance: `const api = new PetClient({ baseURL }); api.getPetById(...)`. A per-call `client` option
797
934
  * still overrides the instance client for a one-off call.
798
935
  */
799
- function SdkClient({ name, isExportable = true, isIndexable = true, operations, parser, children }) {
936
+ function SdkClient({ name, isExportable = true, isIndexable = true, operations, validator, children }) {
800
937
  const methods = operations.map(({ node, name: methodName, tsResolver, zodResolver, security }) => buildSdkMethod({
801
938
  node,
802
939
  name: methodName,
803
940
  tsResolver,
804
941
  zodResolver,
805
- parser,
942
+ validator,
806
943
  security
807
944
  }));
808
945
  const classCode = `export class ${name} {\n${[
@@ -848,13 +985,13 @@ function SdkFacade({ name, isExportable = true, isIndexable = true, members, chi
848
985
  function resolveTypeImportNames(node, tsResolver) {
849
986
  return [tsResolver.resolveRequestConfigName(node), tsResolver.resolveResponsesName(node)];
850
987
  }
851
- function resolveZodImportNames(node, zodResolver, parser) {
988
+ function resolveZodImportNames(node, zodResolver, validator) {
852
989
  const { query: queryParams } = getOperationParameters(node, { paramsCasing: "original" });
853
990
  return [
854
- resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
855
- resolveResponseParser(parser) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,
856
- resolveRequestParser(parser) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
857
- resolveQueryParamsParser(parser) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
991
+ resolveResponseValidator(validator) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
992
+ resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,
993
+ resolveRequestValidator(validator) === "zod" && node.requestBody?.content?.[0]?.schema ? zodResolver.resolveDataName?.(node) : null,
994
+ resolveQueryParamsValidator(validator) === "zod" && queryParams.length > 0 ? zodResolver.resolveQueryParamsName?.(node, queryParams[0]) : null
858
995
  ].filter((n) => Boolean(n));
859
996
  }
860
997
  /**
@@ -863,11 +1000,11 @@ function resolveZodImportNames(node, zodResolver, parser) {
863
1000
  */
864
1001
  function buildControllers(nodes, ctx) {
865
1002
  const { driver, resolver, root } = ctx;
866
- const { output, group, parser } = ctx.options;
1003
+ const { output, group, validator } = ctx.options;
867
1004
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
868
1005
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
869
1006
  const tsPluginOptions = pluginTs.options;
870
- const pluginZod = isParserEnabled(parser) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1007
+ const pluginZod = isValidatorEnabled(validator) ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
871
1008
  const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
872
1009
  const document = ctx.adapter.document;
873
1010
  function buildOperationData(node) {
@@ -954,7 +1091,7 @@ function createSdkGenerator() {
954
1091
  renderer: _kubb_renderer_jsx.jsxRenderer,
955
1092
  operations(nodes, ctx) {
956
1093
  const { config, resolver, root } = ctx;
957
- const { output, group, parser, sdk } = ctx.options;
1094
+ const { output, group, validator, sdk } = ctx.options;
958
1095
  if (!ctx.driver.getPlugin(_kubb_plugin_ts.pluginTsName) || !sdk) return null;
959
1096
  const controllers = buildControllers(nodes, ctx);
960
1097
  const clientPath = node_path.default.resolve(root, ".kubb/client.ts");
@@ -979,9 +1116,9 @@ function createSdkGenerator() {
979
1116
  file: op.typeFile,
980
1117
  names: resolveTypeImportNames(op.node, op.tsResolver)
981
1118
  }));
982
- const { namesByPath: zodNamesByPath, filesByPath: zodFilesByPath } = isParserEnabled(parser) ? collectImportsByFile(ops, (op) => ({
1119
+ const { namesByPath: zodNamesByPath, filesByPath: zodFilesByPath } = isValidatorEnabled(validator) ? collectImportsByFile(ops, (op) => ({
983
1120
  file: op.zodFile,
984
- names: op.zodResolver ? resolveZodImportNames(op.node, op.zodResolver, parser) : []
1121
+ names: op.zodResolver ? resolveZodImportNames(op.node, op.zodResolver, validator) : []
985
1122
  })) : {
986
1123
  namesByPath: /* @__PURE__ */ new Map(),
987
1124
  filesByPath: /* @__PURE__ */ new Map()
@@ -1009,7 +1146,7 @@ function createSdkGenerator() {
1009
1146
  path: clientPath,
1010
1147
  isTypeOnly: true
1011
1148
  }),
1012
- parser === "zod" && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1149
+ validator === "zod" && ops.some((op) => op.node.requestBody?.content?.[0]?.schema != null) && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1013
1150
  name: ["z"],
1014
1151
  path: "zod",
1015
1152
  isTypeOnly: true
@@ -1020,7 +1157,7 @@ function createSdkGenerator() {
1020
1157
  path: typeFilesByPath.get(filePath).path,
1021
1158
  isTypeOnly: true
1022
1159
  }, filePath)),
1023
- isParserEnabled(parser) && Array.from(zodNamesByPath.entries()).map(([filePath, set]) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1160
+ isValidatorEnabled(validator) && Array.from(zodNamesByPath.entries()).map(([filePath, set]) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1024
1161
  name: Array.from(set),
1025
1162
  root: file.path,
1026
1163
  path: zodFilesByPath.get(filePath).path
@@ -1028,7 +1165,7 @@ function createSdkGenerator() {
1028
1165
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(SdkClient, {
1029
1166
  name: className,
1030
1167
  operations: ops,
1031
- parser
1168
+ validator
1032
1169
  })
1033
1170
  ]
1034
1171
  }, file.path);
@@ -1139,18 +1276,18 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1139
1276
  operation(node, ctx) {
1140
1277
  if (!_kubb_core.ast.isHttpOperationNode(node)) return null;
1141
1278
  const { config, driver, resolver, root } = ctx;
1142
- const { output, parser, group } = ctx.options;
1279
+ const { output, validator, group } = ctx.options;
1143
1280
  const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
1144
1281
  if (!pluginTs) return null;
1145
1282
  const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
1146
- const pluginZod = resolveResponseParser(parser) === "zod" || resolveRequestParser(parser) === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1283
+ const pluginZod = resolveResponseValidator(validator) === "zod" || resolveRequestValidator(validator) === "zod" ? driver.getPlugin(_kubb_plugin_zod.pluginZodName) : null;
1147
1284
  const zodResolver = pluginZod ? driver.getResolver(_kubb_plugin_zod.pluginZodName) : null;
1148
1285
  const hasRequestBody = Boolean(node.requestBody?.content?.[0]?.schema);
1149
1286
  const importedTypeNames = [tsResolver.resolveRequestConfigName(node), tsResolver.resolveResponsesName(node)];
1150
1287
  const importedZodNames = zodResolver ? [
1151
- resolveResponseParser(parser) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1152
- resolveResponseParser(parser) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,
1153
- resolveRequestParser(parser) === "zod" && hasRequestBody ? zodResolver.resolveDataName?.(node) : null
1288
+ resolveResponseValidator(validator) === "zod" ? zodResolver.resolveResponseName?.(node) : null,
1289
+ resolveResponseValidator(validator) === "zod" ? buildZodErrorParse(node, zodResolver)?.expression ?? null : null,
1290
+ resolveRequestValidator(validator) === "zod" && hasRequestBody ? zodResolver.resolveDataName?.(node) : null
1154
1291
  ].filter((name) => Boolean(name)) : [];
1155
1292
  const meta = {
1156
1293
  name: resolver.resolveName(node.operationId),
@@ -1176,6 +1313,7 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1176
1313
  path: node.path
1177
1314
  });
1178
1315
  const clientPath = node_path.default.resolve(root, ".kubb/client.ts");
1316
+ const eventStream = isEventStream(node);
1179
1317
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1180
1318
  baseName: meta.file.baseName,
1181
1319
  path: meta.file.path,
@@ -1198,12 +1336,16 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1198
1336
  }),
1199
1337
  children: [
1200
1338
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1201
- name: ["client"],
1339
+ name: eventStream ? ["client", "toEventStream"] : ["client"],
1202
1340
  root: meta.file.path,
1203
1341
  path: clientPath
1204
1342
  }),
1205
1343
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1206
- name: ["Options", "RequestResult"],
1344
+ name: eventStream ? [
1345
+ "Options",
1346
+ "EventStreamResult",
1347
+ "SuccessOf"
1348
+ ] : ["Options", "RequestResult"],
1207
1349
  root: meta.file.path,
1208
1350
  path: clientPath,
1209
1351
  isTypeOnly: true
@@ -1224,7 +1366,7 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1224
1366
  node,
1225
1367
  tsResolver,
1226
1368
  zodResolver,
1227
- parser,
1369
+ validator,
1228
1370
  security
1229
1371
  })
1230
1372
  ]
@@ -1233,12 +1375,15 @@ const clientGenerator = (0, _kubb_core.defineGenerator)({
1233
1375
  });
1234
1376
  //#endregion
1235
1377
  //#region src/templates.ts
1378
+ /** Absolute path to the fetch client template, copied into `.kubb/client.ts`. */
1379
+ const fetchClientTemplatePath = (0, node_url.fileURLToPath)(new URL("../templates/fetch.ts", require("url").pathToFileURL(__filename).href));
1380
+ /** Absolute path to the fetch serializers template, copied into `.kubb/serializers.ts`. */
1381
+ const fetchSerializersTemplatePath = (0, node_url.fileURLToPath)(new URL("../templates/serializers.ts", require("url").pathToFileURL(__filename).href));
1236
1382
  /**
1237
- * Absolute path to the fetch client runtime template, resolved relative to this package's own
1238
- * location so it stays correct no matter which package imports it. Pass it to a file node's `copy`
1239
- * field to emit the runtime into the generated `.kubb/client.ts` verbatim.
1383
+ * Absolute path to the Standard Schema runtime template. Pass it to a file node's `copy` field to
1384
+ * emit the helper into the generated `.kubb/standardSchema.ts` verbatim.
1240
1385
  */
1241
- const fetchClientTemplatePath = (0, node_url.fileURLToPath)(new URL("../templates/fetch.ts", require("url").pathToFileURL(__filename).href));
1386
+ const standardSchemaTemplatePath = (0, node_url.fileURLToPath)(new URL("../templates/standardSchema.ts", require("url").pathToFileURL(__filename).href));
1242
1387
  //#endregion
1243
1388
  //#region src/plugin.ts
1244
1389
  /**
@@ -1272,7 +1417,7 @@ const pluginFetch = (0, _kubb_core.definePlugin)((options) => {
1272
1417
  const { output = {
1273
1418
  path: "clients",
1274
1419
  barrel: { type: "named" }
1275
- }, exclude = [], include, override = [], baseURL, parser = false, group, sdk, resolver: userResolver } = options;
1420
+ }, exclude = [], include, override = [], baseURL, validator = false, group, sdk, resolver: userResolver } = options;
1276
1421
  const resolved = {
1277
1422
  output,
1278
1423
  exclude,
@@ -1280,7 +1425,7 @@ const pluginFetch = (0, _kubb_core.definePlugin)((options) => {
1280
1425
  override,
1281
1426
  group: createGroupConfig(group),
1282
1427
  baseURL,
1283
- parser,
1428
+ validator,
1284
1429
  sdk: sdk ? {
1285
1430
  mode: sdk.mode ?? "tag",
1286
1431
  name: sdk.name
@@ -1294,19 +1439,29 @@ const pluginFetch = (0, _kubb_core.definePlugin)((options) => {
1294
1439
  return {
1295
1440
  name: pluginFetchName,
1296
1441
  options,
1297
- dependencies: [_kubb_plugin_ts.pluginTsName, isParserEnabled(resolved.parser) ? _kubb_plugin_zod.pluginZodName : null].filter((dependency) => Boolean(dependency)),
1442
+ dependencies: [_kubb_plugin_ts.pluginTsName, isValidatorEnabled(resolved.validator) ? _kubb_plugin_zod.pluginZodName : null].filter((dependency) => Boolean(dependency)),
1298
1443
  hooks: { "kubb:plugin:setup"(ctx) {
1299
1444
  ctx.setOptions(resolved);
1300
1445
  ctx.setResolver(resolved.resolver);
1301
1446
  ctx.setMacros([...defaultMacros, ...options.macros ?? []]);
1302
1447
  ctx.addGenerator(...selectedGenerators);
1303
1448
  const root = node_path.default.resolve(ctx.config.root, ctx.config.output.path);
1449
+ ctx.injectFile({
1450
+ baseName: "serializers.ts",
1451
+ path: node_path.default.resolve(root, ".kubb/serializers.ts"),
1452
+ copy: fetchSerializersTemplatePath
1453
+ });
1304
1454
  ctx.injectFile({
1305
1455
  baseName: "client.ts",
1306
1456
  path: node_path.default.resolve(root, ".kubb/client.ts"),
1307
1457
  copy: fetchClientTemplatePath,
1308
1458
  footer: baseURL ? `client.setConfig({ baseURL: ${JSON.stringify(baseURL)} })` : void 0
1309
1459
  });
1460
+ ctx.injectFile({
1461
+ baseName: "standardSchema.ts",
1462
+ path: node_path.default.resolve(root, ".kubb/standardSchema.ts"),
1463
+ copy: standardSchemaTemplatePath
1464
+ });
1310
1465
  } }
1311
1466
  };
1312
1467
  });