@kubb/plugin-fetch 5.13.0-canary.20261105T162357 → 5.13.1

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
@@ -30,6 +30,7 @@ let _kubb_plugin_zod = require("@kubb/plugin-zod");
30
30
  let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
31
31
  let _kubb_plugin_ts = require("@kubb/plugin-ts");
32
32
  let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
33
+ let node_module = require("node:module");
33
34
  let node_url = require("node:url");
34
35
  // ../../internals/shared/src/operation.ts
35
36
  /**
@@ -111,30 +112,86 @@ function getPrimarySuccessContentType(node) {
111
112
  return contentTypes[0].split(";")[0].trim().toLowerCase();
112
113
  }
113
114
  /**
114
- * Whether an operation streams its primary success response as Server-Sent Events
115
- * (`text/event-stream`). The client generator uses this to return a typed event stream instead of a
116
- * one-shot `RequestResult`.
115
+ * The `text/event-stream` content of the primary success response, when it is the only content type. A response that also
116
+ * declares another type stays a normal call with its query hooks.
117
+ */
118
+ function getEventStreamContent(node) {
119
+ return getPrimarySuccessContentType(node) === "text/event-stream" ? getPrimarySuccessResponse(node)?.content?.[0] : void 0;
120
+ }
121
+ /**
122
+ * Whether an operation streams its primary success response as Server-Sent Events (`text/event-stream`). The client
123
+ * generator uses this to return a typed event stream instead of a one-shot `RequestResult`.
117
124
  */
118
125
  function isEventStream(node) {
119
- return getPrimarySuccessContentType(node) === "text/event-stream";
126
+ return getEventStreamContent(node) !== void 0;
127
+ }
128
+ /**
129
+ * How the document types a `text/event-stream` response, as the adapter read it from `itemSchema`: `event` when each item is
130
+ * the parsed event with its own `event` and `data`, `data` when it is only the payload. Returns `null` without an event stream.
131
+ */
132
+ function getEventStream(node) {
133
+ const content = getEventStreamContent(node);
134
+ return content ? content.stream?.shape ?? "data" : null;
135
+ }
136
+ function baseContentType(contentType) {
137
+ return contentType.split(";")[0].trim().toLowerCase();
138
+ }
139
+ function isJsonContentType(baseType) {
140
+ return baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json";
120
141
  }
121
142
  /**
122
- * Derives the default `responseType` for an operation from its primary success response.
143
+ * Parser for one response content entry.
123
144
  *
124
- * Returns a value only when that response declares a single non-JSON content type. `text/event-stream`
125
- * and other binary types (`application/octet-stream`, `application/pdf`, `image/*`, `audio/*`,
126
- * `video/*`) map to a stream or `'blob'`, and other `text/*` maps to `'text'`. Otherwise `undefined`,
127
- * leaving the runtime client's `Content-Type` auto-detection in charge.
145
+ * JSON stays unset so the runtime detects it. `text/event-stream` is a stream. A binary schema
146
+ * (`format: binary` / `contentMediaType`) is a blob even when the media type is not a well-known
147
+ * binary type. Other `text/*` is text, and `application/octet-stream`, `application/pdf`,
148
+ * `image/*`, `audio/*`, and `video/*` are blobs.
128
149
  */
129
- function getResponseType(node) {
130
- const baseType = getPrimarySuccessContentType(node);
150
+ function responseTypeForContent(contentType, schema) {
151
+ const baseType = baseContentType(contentType);
131
152
  if (!baseType) return void 0;
132
- if (baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json") return void 0;
153
+ if (isJsonContentType(baseType)) return void 0;
133
154
  if (baseType === "text/event-stream") return "stream";
155
+ if (schema?.type === "blob") return "blob";
134
156
  if (baseType.startsWith("text/")) return "text";
135
157
  if (baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType)) return "blob";
136
158
  }
137
159
  /**
160
+ * Derives the default `responseType` string for an operation from its primary success response.
161
+ *
162
+ * One content type uses {@link responseTypeForContent}. Several content types that all use the same
163
+ * parser collapse to that string, so a call can keep `responseType: 'blob'`. Mixed parsers return
164
+ * `undefined`; {@link getResponseTypeMap} carries the per-type choice.
165
+ */
166
+ function getResponseType(node) {
167
+ const entries = getPrimarySuccessResponse(node)?.content ?? [];
168
+ if (entries.length === 0) return void 0;
169
+ const responseTypes = entries.map((entry) => responseTypeForContent(entry.contentType, entry.schema));
170
+ const first = responseTypes[0];
171
+ if (!first) return void 0;
172
+ if (responseTypes.every((responseType) => responseType === first)) return first;
173
+ }
174
+ /**
175
+ * Maps each base content type of a primary success response to its parser when those parsers differ.
176
+ *
177
+ * Keys are lowercased and stripped of parameters. JSON entries are kept as `'json'` so a catch-all
178
+ * entry cannot claim a JSON response. Returns `undefined` when {@link getResponseType} already
179
+ * covers the response with one string, or when no content type needs a parser other than JSON.
180
+ * The runtime matches the response `Content-Type` against this map instead of guessing from the media type.
181
+ */
182
+ function getResponseTypeMap(node) {
183
+ if (getResponseType(node)) return void 0;
184
+ const entries = getPrimarySuccessResponse(node)?.content ?? [];
185
+ if (entries.length <= 1) return void 0;
186
+ const map = {};
187
+ for (const entry of entries) {
188
+ const baseType = baseContentType(entry.contentType);
189
+ const responseType = responseTypeForContent(entry.contentType, entry.schema) ?? (isJsonContentType(baseType) ? "json" : void 0);
190
+ if (baseType && responseType) map[baseType] = responseType;
191
+ }
192
+ return Object.values(map).some((responseType) => responseType !== "json") ? map : void 0;
193
+ }
194
+ /**
138
195
  * Which of the grouped request options an operation carries.
139
196
  */
140
197
  function getRequestGroups(node) {
@@ -184,6 +241,22 @@ function buildOperationComments(node, options = {}) {
184
241
  }
185
242
  const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
186
243
  /**
244
+ * An OpenAPI 3.2 `querystring` parameter is the whole query string, so a form-encoded object becomes one query parameter
245
+ * per property, typed and serialized like any other.
246
+ * Another content type, such as JSON, stays one parameter named after it, and the runtime sends its value as the whole query string.
247
+ */
248
+ function spreadQuerystring(param) {
249
+ const schema = param.schema.type === "ref" ? param.schema.schema : param.schema;
250
+ const formEncoded = !param.contentType || param.contentType.split(";")[0].trim().toLowerCase() === "application/x-www-form-urlencoded";
251
+ if (schema?.type !== "object" || !formEncoded) return [param];
252
+ return schema.properties.map((property) => _kubb_kit.ast.factory.createParameter({
253
+ name: property.name,
254
+ in: "query",
255
+ schema: property.schema,
256
+ required: param.required && property.required
257
+ }));
258
+ }
259
+ /**
187
260
  * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
188
261
  * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
189
262
  * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
@@ -192,17 +265,18 @@ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
192
265
  function getOperationParameters(node) {
193
266
  const cached = operationParameterGroupsByNode.get(node);
194
267
  if (cached) return cached;
195
- const group = (location) => {
268
+ const dedupe = (params) => {
196
269
  const seen = /* @__PURE__ */ new Set();
197
- return node.parameters.filter((param) => {
198
- if (param.in !== location || seen.has(param.name)) return false;
270
+ return params.filter((param) => {
271
+ if (seen.has(param.name)) return false;
199
272
  seen.add(param.name);
200
273
  return true;
201
274
  });
202
275
  };
276
+ const group = (location) => dedupe(node.parameters.filter((param) => param.in === location));
203
277
  const groups = {
204
278
  path: group("path"),
205
- query: group("query"),
279
+ query: dedupe([...group("query"), ...group("querystring").flatMap(spreadQuerystring)]),
206
280
  header: group("header"),
207
281
  cookie: group("cookie")
208
282
  };
@@ -489,6 +563,114 @@ function buildZodErrorParse(node, zodResolver) {
489
563
  if (!node.responses.some((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema))) return null;
490
564
  return zodResolver.response.error?.(node) || null;
491
565
  }
566
+ // ../../internals/client/src/builders/generics.ts
567
+ /**
568
+ * Builds the `RequestResult` generic arguments for one operation: the per-status responses record
569
+ * plus the per-call `ThrowOnError` flag. `SuccessOf` / `ErrorOf` split the record inside the
570
+ * runtime, so this only names the record and threads `ThrowOnError`.
571
+ *
572
+ * @example
573
+ * `buildRequestResultGenerics({ node, types }) // 'AddPetResponses, ThrowOnError'`
574
+ */
575
+ function buildRequestResultGenerics({ node, types }) {
576
+ return `${types.response.responses(node)}, ThrowOnError`;
577
+ }
578
+ /**
579
+ * Builds the full return type an operation's function signature uses: `Unwrappable<RequestResult>`
580
+ * for the default `returnType: 'full'`, already a promise so no further wrapping is needed, a plain
581
+ * `Promise<RequestResult>` for the internal `'plain'` shape, or a
582
+ * `Promise` of the runtime's `UnwrappedResult` when `returnType: 'data'` narrows a resolved call
583
+ * down to the bare success body.
584
+ *
585
+ * @example
586
+ * `buildResultType({ node, types, returnType: 'data' }) // 'Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>'`
587
+ */
588
+ function buildResultType({ node, types, returnType }) {
589
+ const generics = buildRequestResultGenerics({
590
+ node,
591
+ types
592
+ });
593
+ if (returnType === "data") return `Promise<UnwrappedResult<${generics}>>`;
594
+ if (returnType === "plain") return `Promise<RequestResult<${generics}>>`;
595
+ return `Unwrappable<RequestResult<${generics}>>`;
596
+ }
597
+ // ../../internals/client/src/builders/returnStatement.ts
598
+ /**
599
+ * Builds the return statement of a generated operation function. With the default
600
+ * `returnType: 'full'` the runtime call already resolves to `{ data, error, request, response }`.
601
+ * The generated code casts that result to the operation's `RequestResult`, then wraps it with
602
+ * `withUnwrap`, so the caller can `await` it directly or call `.unwrap()` for the bare success
603
+ * body. With `returnType: 'data'` it instead routes the call through the runtime's `unwrapResult`,
604
+ * which narrows the resolved value down to the bare success body the same way `RequestResult`
605
+ * already does, using the plugin default when the call leaves `throwOnError` unset.
606
+ * With `returnType: 'plain'` it returns the runtime call as is, cast to `RequestResult`, with no helper.
607
+ *
608
+ * Cast first, wrap second, for the `'full'` path. That order keeps `withUnwrap`'s generic inferred
609
+ * as `RequestResult` instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
610
+ * straight to `Unwrappable<B>` does not work: the `.then` overload stays pinned to `A`, and `as`
611
+ * rejects that two-generic swap even where `A` and `B` on their own would satisfy it.
612
+ *
613
+ * @example
614
+ * `return withUnwrap(request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>)`
615
+ * @example
616
+ * `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError ?? false) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
617
+ * @example
618
+ * `return request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>`
619
+ */
620
+ function buildReturnStatement({ node, types, callConfig, returnType, throwOnErrorDefault }) {
621
+ const request = returnType !== "plain" && hasRequestContentTypeChoice(node) ? `request<${types.response.options(node)}['body']>` : "request";
622
+ if (returnType === "data") return `return unwrapResult(${request}(${callConfig}), config.throwOnError ?? ${throwOnErrorDefault}) as ${buildResultType({
623
+ node,
624
+ types,
625
+ returnType
626
+ })}`;
627
+ if (returnType === "plain") return `return ${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
628
+ node,
629
+ types
630
+ })}>>`;
631
+ return `return withUnwrap(${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
632
+ node,
633
+ types
634
+ })}>>)`;
635
+ }
636
+ /**
637
+ * Builds the return type and statement of a `text/event-stream` operation. The item type is `TypedEvent<...>` when each item
638
+ * declares its own `event` and `data`, and otherwise the payload type, which `EventStreamResult` wraps in `ServerSentEvent`. The call's
639
+ * promise goes through `toEventStream` with the `client` for its defaults, the call's `config`, the shape and the per-event
640
+ * validator. A `returnType: 'plain'` client receives only the promise,
641
+ * `toEventStream(request(config))`, and keeps its response validator in the call config.
642
+ */
643
+ function buildEventStreamReturn({ node, types, callConfig, returnType, validatorHooks, throwOnErrorDefault }) {
644
+ const shape = getEventStream(node) ?? "data";
645
+ const responses = types.response.responses(node);
646
+ const success = `SuccessOf<${responses}>`;
647
+ const eventType = shape === "event" ? `TypedEvent<${success}>` : success;
648
+ if (returnType === "plain") return {
649
+ returnType: `Promise<EventStreamResult<${eventType}>>`,
650
+ statement: `return toEventStream<${eventType}>(request(${callConfig}))`
651
+ };
652
+ const generics = `${responses}, ThrowOnError${shape === "event" ? `, ${eventType}` : ""}`;
653
+ const call = `toEventStream<${eventType}>(request(${callConfig}), { client: request, config, ${[`shape: '${shape}'`, validatorHooks?.response ? `validator: ${validatorHooks.response}` : null].filter(Boolean).join(", ")} })`;
654
+ if (returnType === "data") {
655
+ const result = `Promise<UnwrappedStreamResult<${generics}>>`;
656
+ return {
657
+ returnType: result,
658
+ statement: `return unwrapResult(${call}, config.throwOnError ?? ${throwOnErrorDefault}) as ${result}`
659
+ };
660
+ }
661
+ return {
662
+ returnType: `Unwrappable<StreamResult<${generics}>>`,
663
+ statement: `return withUnwrap(${call} as Promise<StreamResult<${generics}>>)`
664
+ };
665
+ }
666
+ /** The stream types an operation file imports: `StreamResult` and `StreamOptions`, `EventStreamResult` for a plain client, and `TypedEvent` for named events. */
667
+ function buildEventStreamImports({ returnType, typed }) {
668
+ return [...{
669
+ plain: ["EventStreamResult"],
670
+ full: ["StreamResult", "StreamOptions"],
671
+ data: ["UnwrappedStreamResult", "StreamOptions"]
672
+ }[returnType], ...typed ? ["TypedEvent"] : []];
673
+ }
492
674
  // ../../internals/client/src/builders/security.ts
493
675
  function serializeAuth(auth) {
494
676
  const parts = [`type: '${auth.type}'`];
@@ -644,6 +826,7 @@ function serializeParameter(parameter) {
644
826
  if (parameter.explode !== void 0) parts.push(`explode: ${parameter.explode}`);
645
827
  if (parameter.in === "query" && parameter.allowReserved !== void 0) parts.push(`allowReserved: ${parameter.allowReserved}`);
646
828
  if (parameter.in !== "path" && parameter.contentType) parts.push(`contentType: '${parameter.contentType}'`);
829
+ if (parameter.in === "querystring") parts.push("querystring: true");
647
830
  return parts.length > 0 ? `{ ${parts.join(", ")} }` : null;
648
831
  }
649
832
  /**
@@ -686,26 +869,44 @@ function buildStyles({ node }) {
686
869
  header: [],
687
870
  cookie: []
688
871
  };
689
- for (const parameter of node.parameters) {
872
+ for (const [location, parameters] of Object.entries(getOperationParameters(node))) for (const parameter of parameters) {
690
873
  const literal = serializeParameter(parameter);
691
- if (!literal) continue;
692
- groups[parameter.in].push(`${toKey(parameter.name)}: ${literal}`);
874
+ if (literal) groups[location].push(`${toKey(parameter.name)}: ${literal}`);
693
875
  }
694
876
  const entries = [...Object.keys(groups).filter((location) => groups[location].length > 0).map((location) => `${location}: { ${groups[location].join(", ")} }`), serializeBodyEncoding(node)].filter(Boolean);
695
877
  return entries.length > 0 ? `{ ${entries.join(", ")} }` : null;
696
878
  }
697
879
  // ../../internals/client/src/builders/callConfig.ts
698
880
  /**
881
+ * `responseType` baked into a call. One parser is a string. Several parsers become a map from content
882
+ * type to parser so the runtime can choose from the response `Content-Type`.
883
+ */
884
+ function buildResponseTypeLiteral(node) {
885
+ const responseType = getResponseType(node);
886
+ if (responseType) return `responseType: '${responseType}'`;
887
+ const responseTypes = getResponseTypeMap(node);
888
+ if (!responseTypes) return null;
889
+ return `responseType: { ${Object.entries(responseTypes).map(([contentType, type]) => `${JSON.stringify(contentType)}: '${type}'`).join(", ")} }`;
890
+ }
891
+ /**
699
892
  * Builds the call config literal a generated operation forwards to the client, shared by the standalone
700
893
  * functions and the SDK methods. The `throwOnError` fallback is applied after the `...config` spread.
701
894
  *
702
895
  * `mergeContentType` is `true` when the caller must destructure `contentType` from `options`, and
703
- * `eventStream` is `true` when the operation returns `text/event-stream`.
896
+ * `eventStream` is `true` when the operation returns `text/event-stream`. Unless `returnType` is `'plain'`, a
897
+ * stream's literal leaves the `response` validator to `toEventStream`, which runs it per event.
704
898
  */
705
- function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault }) {
899
+ function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault, returnType }) {
900
+ const eventStream = isEventStream(node);
706
901
  const securityLiteral = buildSecurityMetadata({ security });
707
902
  const stylesLiteral = buildStyles({ node });
708
- const validatorLiteral = buildValidatorLiteral(validatorHooks, [
903
+ const validatorLiteral = buildValidatorLiteral(validatorHooks, eventStream && returnType !== "plain" ? [
904
+ "request",
905
+ "path",
906
+ "query",
907
+ "headers",
908
+ "error"
909
+ ] : [
709
910
  "request",
710
911
  "path",
711
912
  "query",
@@ -714,7 +915,6 @@ function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault }
714
915
  "error"
715
916
  ]);
716
917
  const { literal: contentTypeLiteral, merge: mergeContentType } = buildContentType({ node });
717
- const responseType = getResponseType(node);
718
918
  return {
719
919
  callConfig: `{ ${[
720
920
  `method: '${node.method.toUpperCase()}'`,
@@ -723,93 +923,12 @@ function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault }
723
923
  stylesLiteral ? `styles: ${stylesLiteral}` : null,
724
924
  validatorLiteral,
725
925
  contentTypeLiteral,
726
- responseType ? `responseType: '${responseType}'` : null,
926
+ buildResponseTypeLiteral(node),
727
927
  "...config",
728
928
  `throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
729
929
  ].filter(Boolean).join(", ")} }`,
730
930
  mergeContentType,
731
- eventStream: isEventStream(node)
732
- };
733
- }
734
- // ../../internals/client/src/builders/generics.ts
735
- /**
736
- * Builds the `RequestResult` generic arguments for one operation: the per-status responses record
737
- * plus the per-call `ThrowOnError` flag. `SuccessOf` / `ErrorOf` split the record inside the
738
- * runtime, so this only names the record and threads `ThrowOnError`.
739
- *
740
- * @example
741
- * `buildRequestResultGenerics({ node, types }) // 'AddPetResponses, ThrowOnError'`
742
- */
743
- function buildRequestResultGenerics({ node, types }) {
744
- return `${types.response.responses(node)}, ThrowOnError`;
745
- }
746
- /**
747
- * Builds the full return type an operation's function signature uses: `Unwrappable<RequestResult>`
748
- * for the default `returnType: 'full'`, already a promise so no further wrapping is needed, a plain
749
- * `Promise<RequestResult>` for the internal `'plain'` shape, or a
750
- * `Promise` of the runtime's `UnwrappedResult` when `returnType: 'data'` narrows a resolved call
751
- * down to the bare success body.
752
- *
753
- * @example
754
- * `buildResultType({ node, types, returnType: 'data' }) // 'Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>'`
755
- */
756
- function buildResultType({ node, types, returnType }) {
757
- const generics = buildRequestResultGenerics({
758
- node,
759
- types
760
- });
761
- if (returnType === "data") return `Promise<UnwrappedResult<${generics}>>`;
762
- if (returnType === "plain") return `Promise<RequestResult<${generics}>>`;
763
- return `Unwrappable<RequestResult<${generics}>>`;
764
- }
765
- // ../../internals/client/src/builders/returnStatement.ts
766
- /**
767
- * Builds the return statement of a generated operation function. With the default
768
- * `returnType: 'full'` the runtime call already resolves to `{ data, error, request, response }`.
769
- * The generated code casts that result to the operation's `RequestResult`, then wraps it with
770
- * `withUnwrap`, so the caller can `await` it directly or call `.unwrap()` for the bare success
771
- * body. With `returnType: 'data'` it instead routes the call through the runtime's `unwrapResult`,
772
- * which narrows the resolved value down to the bare success body the same way `RequestResult`
773
- * already does, using the plugin default when the call leaves `throwOnError` unset.
774
- * With `returnType: 'plain'` it returns the runtime call as is, cast to `RequestResult`, with no helper.
775
- *
776
- * Cast first, wrap second, for the `'full'` path. That order keeps `withUnwrap`'s generic inferred
777
- * as `RequestResult` instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
778
- * straight to `Unwrappable<B>` does not work: the `.then` overload stays pinned to `A`, and `as`
779
- * rejects that two-generic swap even where `A` and `B` on their own would satisfy it.
780
- *
781
- * @example
782
- * `return withUnwrap(request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>)`
783
- * @example
784
- * `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError ?? false) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
785
- * @example
786
- * `return request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>`
787
- */
788
- function buildReturnStatement({ node, types, callConfig, returnType, throwOnErrorDefault }) {
789
- const request = returnType !== "plain" && hasRequestContentTypeChoice(node) ? `request<${types.response.options(node)}['body']>` : "request";
790
- if (returnType === "data") return `return unwrapResult(${request}(${callConfig}), config.throwOnError ?? ${throwOnErrorDefault}) as ${buildResultType({
791
- node,
792
- types,
793
- returnType
794
- })}`;
795
- if (returnType === "plain") return `return ${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
796
- node,
797
- types
798
- })}>>`;
799
- return `return withUnwrap(${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
800
- node,
801
- types
802
- })}>>)`;
803
- }
804
- /**
805
- * Builds the return type and statement of an operation that returns `text/event-stream`: the call
806
- * is wrapped in `toEventStream`, so the caller iterates the typed success events.
807
- */
808
- function buildEventStreamReturn({ node, types, callConfig }) {
809
- const eventType = `SuccessOf<${types.response.responses(node)}>`;
810
- return {
811
- returnType: `Promise<EventStreamResult<${eventType}>>`,
812
- statement: `return toEventStream<${eventType}>(request(${callConfig}))`
931
+ eventStream
813
932
  };
814
933
  }
815
934
  // ../../internals/client/src/builders/signature.ts
@@ -823,13 +942,14 @@ const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declara
823
942
  * per-operation input type has to be emitted. Both names come from `types`, which is `plugin-ts` or
824
943
  * `plugin-zod`'s inferred types (see `resolveOperationTypes`).
825
944
  */
826
- function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDefault }) {
945
+ function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDefault, eventStream = false }) {
827
946
  const optionsName = types.response.options(node);
947
+ const optionsType = `${eventStream && returnType !== "plain" ? "StreamOptions" : "Options"}<${optionsName}, ThrowOnError>`;
828
948
  const { isOptional } = getRequestGroupOptionality(node);
829
949
  return {
830
950
  paramsSignature: declarationPrinter.print((0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
831
951
  name: "options",
832
- type: `Options<${optionsName}, ThrowOnError>`,
952
+ type: optionsType,
833
953
  ...isOptional ? { default: "{}" } : {}
834
954
  })] })) ?? "",
835
955
  returnType: buildResultType({
@@ -840,37 +960,63 @@ function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDef
840
960
  generics: [`ThrowOnError extends boolean = ${throwOnErrorDefault}`]
841
961
  };
842
962
  }
843
- // ../../internals/client/src/components/Operation.tsx
963
+ // ../../internals/client/src/builders/operationBody.ts
844
964
  /**
845
- * Renders one client operation: the grouped `<Name>Request` type and the function that forwards a
846
- * single `options` object to the resolved client and returns the `Unwrappable<RequestResult>`. The
847
- * type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
848
- * the source.
965
+ * The pieces of a generated operation: its generics, `options` parameter, return type, return statement, and whether the
966
+ * body destructures `contentType` from `options`. Shared by the standalone functions and the SDK methods, so a
967
+ * `text/event-stream` operation is wired to `toEventStream` in one place.
849
968
  */
850
- function Operation({ name, node, types, validatorHooks, returnType, throwOnErrorDefault, security }) {
851
- if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
969
+ function buildOperationBody({ node, types, validatorHooks, security, returnType, throwOnErrorDefault }) {
970
+ const { callConfig, mergeContentType, eventStream } = buildCallConfig({
971
+ node,
972
+ validatorHooks,
973
+ security,
974
+ throwOnErrorDefault,
975
+ returnType
976
+ });
852
977
  const signature = buildGroupedOptionsSignature({
853
978
  node,
854
979
  types,
855
980
  returnType,
856
- throwOnErrorDefault
981
+ throwOnErrorDefault,
982
+ eventStream
857
983
  });
858
- const { callConfig, mergeContentType, eventStream } = buildCallConfig({
984
+ const stream = eventStream ? buildEventStreamReturn({
859
985
  node,
986
+ types,
987
+ callConfig,
988
+ returnType,
860
989
  validatorHooks,
861
- security,
862
990
  throwOnErrorDefault
863
- });
864
- const eventStreamReturn = eventStream ? buildEventStreamReturn({
865
- node,
866
- types,
867
- callConfig
868
991
  }) : null;
869
- const functionReturnType = eventStreamReturn?.returnType ?? signature.returnType;
870
- const returnStatement = eventStreamReturn?.statement ?? buildReturnStatement({
992
+ return {
993
+ generics: signature.generics,
994
+ paramsSignature: signature.paramsSignature,
995
+ returnType: stream?.returnType ?? signature.returnType,
996
+ statement: stream?.statement ?? buildReturnStatement({
997
+ node,
998
+ types,
999
+ callConfig,
1000
+ returnType,
1001
+ throwOnErrorDefault
1002
+ }),
1003
+ mergeContentType
1004
+ };
1005
+ }
1006
+ // ../../internals/client/src/components/Operation.tsx
1007
+ /**
1008
+ * Renders one client operation: the grouped `<Name>Request` type and the function that forwards a
1009
+ * single `options` object to the resolved client and returns the `Unwrappable<RequestResult>`. The
1010
+ * type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
1011
+ * the source.
1012
+ */
1013
+ function Operation({ name, node, types, validatorHooks, returnType, throwOnErrorDefault, security }) {
1014
+ if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
1015
+ const body = buildOperationBody({
871
1016
  node,
872
1017
  types,
873
- callConfig,
1018
+ validatorHooks,
1019
+ security,
874
1020
  returnType,
875
1021
  throwOnErrorDefault
876
1022
  });
@@ -881,22 +1027,53 @@ function Operation({ name, node, types, validatorHooks, returnType, throwOnError
881
1027
  children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.Function, {
882
1028
  name,
883
1029
  export: true,
884
- generics: signature.generics,
885
- params: signature.paramsSignature,
886
- returnType: functionReturnType,
1030
+ generics: body.generics,
1031
+ params: body.paramsSignature,
1032
+ returnType: body.returnType,
887
1033
  JSDoc: { comments: buildOperationComments(node, {
888
1034
  link: "urlPath",
889
1035
  linkPosition: "beforeDeprecated",
890
1036
  splitLines: true
891
1037
  }) },
892
1038
  children: [
893
- mergeContentType ? "const { client: request = client, contentType, ...config } = options" : "const { client: request = client, ...config } = options",
1039
+ body.mergeContentType ? "const { client: request = client, contentType, ...config } = options" : "const { client: request = client, ...config } = options",
894
1040
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
895
- returnStatement
1041
+ body.statement
896
1042
  ]
897
1043
  })
898
1044
  });
899
1045
  }
1046
+ // ../../internals/client/src/components/StreamRuntime.tsx
1047
+ /**
1048
+ * The `footer` of `.kubb/client.ts`: the `baseURL` set at generation time, and the `sse.ts` re-export when the document
1049
+ * streams events, so `ServerSentEvent` and `toEventStream` stay importable from the client as in earlier releases.
1050
+ */
1051
+ function buildClientFooter({ baseURL, sse }) {
1052
+ const baseURLExpression = baseURL ? baseURL.includes("${") ? `\`${baseURL.replaceAll("`", "\\`")}\`` : JSON.stringify(baseURL) : void 0;
1053
+ return [baseURLExpression ? `client.setConfig({ baseURL: ${baseURLExpression} })` : null, sse ? `export * from './sse.ts'` : null].filter(Boolean).join("\n") || void 0;
1054
+ }
1055
+ /**
1056
+ * Writes the footer of the `.kubb/client.ts` the plugin injected, the one place it is built, and copies `sse.ts` into
1057
+ * `.kubb/` only for a document with a `text/event-stream` operation, so a document without streams ships no stream runtime.
1058
+ * `sse` is set by the plugins that ship the runtime, so a custom client from `@kubb/plugin-client` gets neither file.
1059
+ */
1060
+ function StreamRuntime({ nodes, root, sse, baseURL }) {
1061
+ if (!sse) return null;
1062
+ const streams = nodes.some(isEventStream);
1063
+ const footer = buildClientFooter({
1064
+ baseURL,
1065
+ sse: streams
1066
+ });
1067
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [streams && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File, {
1068
+ baseName: "sse.ts",
1069
+ path: node_path.default.resolve(root, ".kubb/sse.ts"),
1070
+ copy: sse
1071
+ }), footer && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File, {
1072
+ baseName: "client.ts",
1073
+ path: node_path.default.resolve(root, ".kubb/client.ts"),
1074
+ footer
1075
+ })] });
1076
+ }
900
1077
  // ../../internals/client/src/resolveOperationTypes.ts
901
1078
  /**
902
1079
  * A `plugin-ts` shaped resolver whose operation type names point at the types `pluginZod({ inferred })`
@@ -972,13 +1149,24 @@ function resolveOperationTypes(driver, pluginName) {
972
1149
  * `@kubb/plugin-axios`). Emits one async function per OpenAPI operation using the shared
973
1150
  * `Operation` component: a grouped `<Name>Request` type and a function that forwards a single
974
1151
  * `options` object to the bundled `client` and returns the `Unwrappable<RequestResult>`. Only the
975
- * generator `name` differs between plugins. Every other resolution, import, and rendering step is
976
- * identical.
1152
+ * generator `name` and the runtime templates differ between plugins. Every other resolution, import,
1153
+ * and rendering step is identical.
1154
+ *
1155
+ * `sse` is the Server-Sent Events runtime template, copied into `.kubb/sse.ts` only for a document with a
1156
+ * `text/event-stream` operation.
977
1157
  */
978
- function createClientGenerator(name) {
1158
+ function createClientGenerator(name, { sse } = {}) {
979
1159
  return (0, _kubb_kit.defineGenerator)({
980
1160
  name,
981
1161
  renderer: _kubb_renderer_jsx.jsxRenderer,
1162
+ operations(nodes, ctx) {
1163
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(StreamRuntime, {
1164
+ nodes,
1165
+ root: ctx.root,
1166
+ sse,
1167
+ baseURL: ctx.options.baseURL
1168
+ });
1169
+ },
982
1170
  operation(node, ctx) {
983
1171
  if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
984
1172
  const { config, driver, resolver, root } = ctx;
@@ -1028,19 +1216,19 @@ function createClientGenerator(name) {
1028
1216
  const clientPath = importPath ?? node_path.default.resolve(root, ".kubb/client.ts");
1029
1217
  const clientRoot = importPath === void 0 ? meta.file.path : void 0;
1030
1218
  const standardSchemaPath = importPath ?? node_path.default.resolve(root, ".kubb/standardSchema.ts");
1031
- const eventStream = isEventStream(node);
1219
+ const ssePath = importPath ?? node_path.default.resolve(root, ".kubb/sse.ts");
1220
+ const stream = getEventStream(node);
1032
1221
  const clientValueImports = (() => {
1033
- if (eventStream) return ["client", "toEventStream"];
1034
1222
  if (returnType === "plain") return ["client"];
1035
1223
  if (returnType === "data") return ["client", "unwrapResult"];
1036
1224
  return ["client", "withUnwrap"];
1037
1225
  })();
1038
1226
  const clientTypeImports = (() => {
1039
- if (eventStream) return [
1040
- "Options",
1041
- "EventStreamResult",
1042
- "SuccessOf"
1043
- ];
1227
+ if (stream) return {
1228
+ plain: ["Options", "SuccessOf"],
1229
+ data: ["SuccessOf"],
1230
+ full: ["SuccessOf", "Unwrappable"]
1231
+ }[returnType];
1044
1232
  if (returnType === "plain") return ["Options", "RequestResult"];
1045
1233
  if (returnType === "data") return ["Options", "UnwrappedResult"];
1046
1234
  return [
@@ -1081,6 +1269,20 @@ function createClientGenerator(name) {
1081
1269
  path: clientPath,
1082
1270
  isTypeOnly: true
1083
1271
  }),
1272
+ stream && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1273
+ name: ["toEventStream"],
1274
+ root: clientRoot,
1275
+ path: ssePath
1276
+ }),
1277
+ stream && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1278
+ name: buildEventStreamImports({
1279
+ returnType,
1280
+ typed: stream === "event"
1281
+ }),
1282
+ root: clientRoot,
1283
+ path: ssePath,
1284
+ isTypeOnly: true
1285
+ }),
1084
1286
  meta.fileTypes && importedTypeNames.length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1085
1287
  name: Array.from(new Set(importedTypeNames)),
1086
1288
  root: meta.file.path,
@@ -1117,43 +1319,26 @@ function createClientGenerator(name) {
1117
1319
  */
1118
1320
  function buildSdkMethod({ node, name, types, validatorHooks, security, returnType, throwOnErrorDefault }) {
1119
1321
  if (!_kubb_kit.ast.isHttpOperationNode(node)) return "";
1120
- const signature = buildGroupedOptionsSignature({
1322
+ const body = buildOperationBody({
1121
1323
  node,
1122
1324
  types,
1123
- returnType,
1124
- throwOnErrorDefault
1125
- });
1126
- const { callConfig, mergeContentType, eventStream } = buildCallConfig({
1127
- node,
1128
1325
  validatorHooks,
1129
1326
  security,
1130
- throwOnErrorDefault
1131
- });
1132
- const eventStreamReturn = eventStream ? buildEventStreamReturn({
1133
- node,
1134
- types,
1135
- callConfig
1136
- }) : null;
1137
- const methodReturnType = eventStreamReturn?.returnType ?? signature.returnType;
1138
- const returnStatement = eventStreamReturn?.statement ?? buildReturnStatement({
1139
- node,
1140
- types,
1141
- callConfig,
1142
1327
  returnType,
1143
1328
  throwOnErrorDefault
1144
1329
  });
1145
- const generics = signature.generics.length ? `<${signature.generics.join(", ")}>` : "";
1330
+ const generics = body.generics.length ? `<${body.generics.join(", ")}>` : "";
1146
1331
  const jsdoc = buildJSDoc(buildOperationComments(node, {
1147
1332
  link: "urlPath",
1148
1333
  linkPosition: "beforeDeprecated",
1149
1334
  splitLines: true
1150
1335
  }));
1151
1336
  const methodBody = [
1152
- mergeContentType ? "const { client: request = this.client, contentType, ...config } = options" : "const { client: request = this.client, ...config } = options",
1337
+ body.mergeContentType ? "const { client: request = this.client, contentType, ...config } = options" : "const { client: request = this.client, ...config } = options",
1153
1338
  "",
1154
- returnStatement
1339
+ body.statement
1155
1340
  ].map((line) => line ? ` ${line}` : "").join("\n");
1156
- return `${jsdoc} public ${name}${generics}(${signature.paramsSignature}): ${methodReturnType} {\n${methodBody}\n }`;
1341
+ return `${jsdoc} public ${name}${generics}(${body.paramsSignature}): ${body.returnType} {\n${methodBody}\n }`;
1157
1342
  }
1158
1343
  /**
1159
1344
  * Renders one instance class per tag with one method per operation. The constructor takes a client
@@ -1309,8 +1494,11 @@ function collectImportsByFile(ops, pick) {
1309
1494
  * emits one class per tag and, when `sdk.name` is set, a composed root that instantiates every tag
1310
1495
  * client. With `sdk.mode: 'flat'` it emits one class named by `sdk.name`, with every operation as a
1311
1496
  * direct method.
1497
+ *
1498
+ * `sse` is the Server-Sent Events runtime template, copied into `.kubb/sse.ts` only for a document with a
1499
+ * `text/event-stream` operation.
1312
1500
  */
1313
- function createSdkGenerator() {
1501
+ function createSdkGenerator({ sse } = {}) {
1314
1502
  return (0, _kubb_kit.defineGenerator)({
1315
1503
  name: "sdk",
1316
1504
  renderer: _kubb_renderer_jsx.jsxRenderer,
@@ -1331,6 +1519,7 @@ function createSdkGenerator() {
1331
1519
  const controllers = buildControllers(nodes, ctx, types, validatorEnabled);
1332
1520
  const clientPath = node_path.default.resolve(root, ".kubb/client.ts");
1333
1521
  const standardSchemaPath = node_path.default.resolve(root, ".kubb/standardSchema.ts");
1522
+ const ssePath = node_path.default.resolve(root, ".kubb/sse.ts");
1334
1523
  const banner = (file) => resolver.default.banner(ctx.meta, {
1335
1524
  output,
1336
1525
  config,
@@ -1359,7 +1548,8 @@ function createSdkGenerator() {
1359
1548
  namesByPath: /* @__PURE__ */ new Map(),
1360
1549
  filesByPath: /* @__PURE__ */ new Map()
1361
1550
  };
1362
- const hasEventStream = ops.some((op) => isEventStream(op.node));
1551
+ const shapes = new Set(ops.map((op) => getEventStream(op.node)).filter(Boolean));
1552
+ const hasEventStream = shapes.size > 0;
1363
1553
  const hasResult = ops.some((op) => !isEventStream(op.node));
1364
1554
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1365
1555
  baseName: file.baseName,
@@ -1369,11 +1559,7 @@ function createSdkGenerator() {
1369
1559
  footer: footer(file),
1370
1560
  children: [
1371
1561
  /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1372
- name: [
1373
- "createClient",
1374
- ...hasResult ? [returnType === "data" ? "unwrapResult" : "withUnwrap"] : [],
1375
- ...hasEventStream ? ["toEventStream"] : []
1376
- ],
1562
+ name: ["createClient", returnType === "data" ? "unwrapResult" : "withUnwrap"],
1377
1563
  root: file.path,
1378
1564
  path: clientPath
1379
1565
  }),
@@ -1382,13 +1568,28 @@ function createSdkGenerator() {
1382
1568
  "ClientConfig",
1383
1569
  "ClientInstance",
1384
1570
  "Options",
1385
- ...hasResult ? returnType === "data" ? ["UnwrappedResult"] : ["Unwrappable", "RequestResult"] : [],
1386
- ...hasEventStream ? ["EventStreamResult", "SuccessOf"] : []
1571
+ ...returnType === "data" ? [] : ["Unwrappable"],
1572
+ ...hasResult ? [returnType === "data" ? "UnwrappedResult" : "RequestResult"] : [],
1573
+ ...hasEventStream ? ["SuccessOf"] : []
1387
1574
  ],
1388
1575
  root: file.path,
1389
1576
  path: clientPath,
1390
1577
  isTypeOnly: true
1391
1578
  }),
1579
+ hasEventStream && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1580
+ name: ["toEventStream"],
1581
+ root: file.path,
1582
+ path: ssePath
1583
+ }),
1584
+ hasEventStream && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1585
+ name: buildEventStreamImports({
1586
+ returnType,
1587
+ typed: shapes.has("event")
1588
+ }),
1589
+ root: file.path,
1590
+ path: ssePath,
1591
+ isTypeOnly: true
1592
+ }),
1392
1593
  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, {
1393
1594
  name: ["z"],
1394
1595
  path: "zod",
@@ -1419,61 +1620,66 @@ function createSdkGenerator() {
1419
1620
  ]
1420
1621
  }, file.path);
1421
1622
  };
1422
- if (sdk.mode === "flat") return renderClassFile(resolver.className(sdk.name ?? "sdk"), resolver.file({
1623
+ const renderFacadeFile = (name) => {
1624
+ const sdkFile = resolver.file({
1625
+ name,
1626
+ extname: ".ts",
1627
+ root,
1628
+ output,
1629
+ group: group ?? void 0
1630
+ });
1631
+ const members = controllers.map(({ name, tag }) => ({
1632
+ className: name,
1633
+ propName: resolver.propertyName(tag ?? name)
1634
+ }));
1635
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1636
+ baseName: sdkFile.baseName,
1637
+ path: sdkFile.path,
1638
+ meta: sdkFile.meta,
1639
+ banner: banner(sdkFile),
1640
+ footer: footer(sdkFile),
1641
+ children: [
1642
+ /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1643
+ name: ["ClientConfig"],
1644
+ root: sdkFile.path,
1645
+ path: clientPath,
1646
+ isTypeOnly: true
1647
+ }),
1648
+ controllers.map(({ name, file }) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1649
+ name: [name],
1650
+ root: sdkFile.path,
1651
+ path: file.path
1652
+ }, name)),
1653
+ /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(SdkFacade, {
1654
+ name: resolver.className(name),
1655
+ members
1656
+ })
1657
+ ]
1658
+ }, sdkFile.path);
1659
+ };
1660
+ const files = sdk.mode === "flat" ? renderClassFile(resolver.className(sdk.name ?? "sdk"), resolver.file({
1423
1661
  name: sdk.name ?? "sdk",
1424
1662
  extname: ".ts",
1425
1663
  root,
1426
1664
  output,
1427
1665
  group: group ?? void 0
1428
- }), controllers.flatMap((controller) => controller.operations));
1429
- const classFiles = controllers.map(({ name, file, operations: ops }) => renderClassFile(name, file, ops));
1430
- if (!sdk.name) return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: classFiles });
1431
- const sdkFile = resolver.file({
1432
- name: sdk.name,
1433
- extname: ".ts",
1666
+ }), controllers.flatMap((controller) => controller.operations)) : [...controllers.map(({ name, file, operations: ops }) => renderClassFile(name, file, ops)), sdk.name && renderFacadeFile(sdk.name)];
1667
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(StreamRuntime, {
1668
+ nodes,
1434
1669
  root,
1435
- output,
1436
- group: group ?? void 0
1437
- });
1438
- const facadeName = resolver.className(sdk.name);
1439
- const members = controllers.map(({ name, tag }) => ({
1440
- className: name,
1441
- propName: resolver.propertyName(tag ?? name)
1442
- }));
1443
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [classFiles, /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1444
- baseName: sdkFile.baseName,
1445
- path: sdkFile.path,
1446
- meta: sdkFile.meta,
1447
- banner: banner(sdkFile),
1448
- footer: footer(sdkFile),
1449
- children: [
1450
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1451
- name: ["ClientConfig"],
1452
- root: sdkFile.path,
1453
- path: clientPath,
1454
- isTypeOnly: true
1455
- }),
1456
- controllers.map(({ name, file }) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1457
- name: [name],
1458
- root: sdkFile.path,
1459
- path: file.path
1460
- }, name)),
1461
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(SdkFacade, {
1462
- name: facadeName,
1463
- members
1464
- })
1465
- ]
1466
- }, sdkFile.path)] });
1670
+ sse,
1671
+ baseURL: ctx.options.baseURL
1672
+ }), files] });
1467
1673
  }
1468
1674
  });
1469
1675
  }
1470
1676
  // ../../internals/client/src/macros.ts
1471
1677
  /**
1472
- * Macros the client plugins apply by default, ahead of any user macros. `macroSimplifyUnion`
1473
- * drops union members a broader scalar already covers, keeping the generated response and error
1678
+ * Macros the client plugins apply by default, ahead of any user macros. `macroDedupeUnion`
1679
+ * drops repeated union members, keeping the generated response and error
1474
1680
  * unions tidy. A plugin wires them with `ctx.setMacros([...defaultMacros, ...userMacros])`.
1475
1681
  */
1476
- const defaultMacros = [_kubb_kit.macroSimplifyUnion];
1682
+ const defaultMacros = [_kubb_kit.macroDedupeUnion];
1477
1683
  // ../../internals/client/src/resolver.ts
1478
1684
  /**
1479
1685
  * Default resolver shared by the client plugins. Functions use safe camelCase identifiers and
@@ -1498,30 +1704,32 @@ const resolverClient = (0, _kubb_kit.createResolver)({
1498
1704
  return ensureValidVarName((0, _kubb_kit.camelCase)(name));
1499
1705
  }
1500
1706
  });
1707
+ // src/templates.ts
1708
+ const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
1709
+ /** Absolute path to the fetch client template, copied into `.kubb/client.ts`. */
1710
+ const fetchClientTemplatePath = (0, node_url.fileURLToPath)(new URL("../templates/client.ts", require("url").pathToFileURL(__filename).href));
1711
+ /**
1712
+ * Absolute paths to the runtime templates `@kubb/plugin-client` ships for every client plugin, copied into `.kubb/core.ts`,
1713
+ * `.kubb/serializers.ts`, `.kubb/standardSchema.ts`, and `.kubb/sse.ts`.
1714
+ */
1715
+ const coreTemplatePath = require$1.resolve("@kubb/plugin-client/templates/core.ts");
1716
+ const serializersTemplatePath = require$1.resolve("@kubb/plugin-client/templates/serializers.ts");
1717
+ const standardSchemaTemplatePath = require$1.resolve("@kubb/plugin-client/templates/standardSchema.ts");
1718
+ const sseTemplatePath = require$1.resolve("@kubb/plugin-client/templates/sse.ts");
1501
1719
  // src/generators/clientGenerator.tsx
1502
1720
  /**
1503
1721
  * Built-in operation generator for `@kubb/plugin-fetch`. Emits one async function per OpenAPI
1504
1722
  * operation using the shared `Operation` component: a grouped `<Name>Request` type and a function
1505
1723
  * that forwards a single `options` object to the bundled `client` and returns the `RequestResult`.
1506
1724
  */
1507
- const clientGenerator = createClientGenerator("fetch");
1725
+ const clientGenerator = createClientGenerator("fetch", { sse: sseTemplatePath });
1508
1726
  // kubb-extension.json
1509
1727
  var compatibility = {
1510
- "kubb": ">=5.14.6",
1728
+ "kubb": ">=5.16.0",
1511
1729
  "node": ">=22.22"
1512
1730
  };
1513
1731
  // package.json
1514
- var version = "5.13.0-canary.20261105T162357";
1515
- // src/templates.ts
1516
- /** Absolute path to the fetch client template, copied into `.kubb/client.ts`. */
1517
- const fetchClientTemplatePath = (0, node_url.fileURLToPath)(new URL("../templates/fetch.ts", require("url").pathToFileURL(__filename).href));
1518
- /** Absolute path to the fetch serializers template, copied into `.kubb/serializers.ts`. */
1519
- const fetchSerializersTemplatePath = (0, node_url.fileURLToPath)(new URL("../templates/serializers.ts", require("url").pathToFileURL(__filename).href));
1520
- /**
1521
- * Absolute path to the Standard Schema runtime template. Pass it to a file node's `copy` field to
1522
- * emit the helper into the generated `.kubb/standardSchema.ts` verbatim.
1523
- */
1524
- const standardSchemaTemplatePath = (0, node_url.fileURLToPath)(new URL("../templates/standardSchema.ts", require("url").pathToFileURL(__filename).href));
1732
+ var version = "5.13.1";
1525
1733
  // src/plugin.ts
1526
1734
  /**
1527
1735
  * Canonical plugin name for `@kubb/plugin-fetch`. Used for driver lookups and cross-plugin
@@ -1585,19 +1793,22 @@ const pluginFetch = (0, _kubb_kit.defineExtension)({
1585
1793
  };
1586
1794
  (0, _kubb_kit.setResolver)(resolved.resolver);
1587
1795
  (0, _kubb_kit.setMacros)([...defaultMacros, ...options.macros ?? []]);
1588
- (0, _kubb_kit.addGenerator)(resolved.sdk ? createSdkGenerator() : clientGenerator);
1796
+ (0, _kubb_kit.addGenerator)(resolved.sdk ? createSdkGenerator({ sse: sseTemplatePath }) : clientGenerator);
1589
1797
  const root = node_path.default.resolve(config.root, config.output.path);
1590
- const baseURLExpression = baseURL ? baseURL.includes("${") ? `\`${baseURL.replaceAll("`", "\\`")}\`` : JSON.stringify(baseURL) : void 0;
1591
1798
  (0, _kubb_kit.injectFile)({
1592
1799
  baseName: "serializers.ts",
1593
1800
  path: node_path.default.resolve(root, ".kubb/serializers.ts"),
1594
- copy: fetchSerializersTemplatePath
1801
+ copy: serializersTemplatePath
1802
+ });
1803
+ (0, _kubb_kit.injectFile)({
1804
+ baseName: "core.ts",
1805
+ path: node_path.default.resolve(root, ".kubb/core.ts"),
1806
+ copy: coreTemplatePath
1595
1807
  });
1596
1808
  (0, _kubb_kit.injectFile)({
1597
1809
  baseName: "client.ts",
1598
1810
  path: node_path.default.resolve(root, ".kubb/client.ts"),
1599
- copy: fetchClientTemplatePath,
1600
- footer: baseURLExpression ? `client.setConfig({ baseURL: ${baseURLExpression} })` : void 0
1811
+ copy: fetchClientTemplatePath
1601
1812
  });
1602
1813
  (0, _kubb_kit.injectFile)({
1603
1814
  baseName: "standardSchema.ts",