@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/client-Dn80AjdR.d.ts +39 -0
- package/dist/core-D6FAGTsL.d.ts +2 -0
- package/dist/index.cjs +443 -232
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +444 -233
- package/dist/index.js.map +1 -1
- package/dist/templates/client.d.ts +3 -0
- package/dist/templates/core.d.ts +2 -0
- package/dist/templates/fetch.d.ts +5 -405
- package/dist/templates/serializers.d.ts +1 -179
- package/dist/templates/standardSchema.d.ts +1 -63
- package/docs/guides/serialization.md +6 -0
- package/docs/guides/server-sent-events.md +281 -45
- package/kubb-extension.json +1 -1
- package/package.json +8 -7
- package/src/generators/clientGenerator.tsx +2 -1
- package/src/plugin.ts +9 -5
- package/src/templates.ts +10 -7
- package/templates/client.ts +111 -0
- package/templates/core.ts +2 -0
- package/templates/fetch.ts +2 -919
- package/templates/serializers.ts +2 -481
- package/templates/standardSchema.ts +2 -109
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
|
-
*
|
|
115
|
-
*
|
|
116
|
-
|
|
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
|
|
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
|
-
*
|
|
143
|
+
* Parser for one response content entry.
|
|
123
144
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
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
|
|
130
|
-
const baseType =
|
|
150
|
+
function responseTypeForContent(contentType, schema) {
|
|
151
|
+
const baseType = baseContentType(contentType);
|
|
131
152
|
if (!baseType) return void 0;
|
|
132
|
-
if (
|
|
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
|
|
268
|
+
const dedupe = (params) => {
|
|
196
269
|
const seen = /* @__PURE__ */ new Set();
|
|
197
|
-
return
|
|
198
|
-
if (
|
|
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
|
|
872
|
+
for (const [location, parameters] of Object.entries(getOperationParameters(node))) for (const parameter of parameters) {
|
|
690
873
|
const literal = serializeParameter(parameter);
|
|
691
|
-
if (
|
|
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
|
-
|
|
926
|
+
buildResponseTypeLiteral(node),
|
|
727
927
|
"...config",
|
|
728
928
|
`throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
|
|
729
929
|
].filter(Boolean).join(", ")} }`,
|
|
730
930
|
mergeContentType,
|
|
731
|
-
eventStream
|
|
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:
|
|
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/
|
|
963
|
+
// ../../internals/client/src/builders/operationBody.ts
|
|
844
964
|
/**
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
*
|
|
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
|
|
851
|
-
|
|
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
|
|
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
|
-
|
|
870
|
-
|
|
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
|
-
|
|
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:
|
|
885
|
-
params:
|
|
886
|
-
returnType:
|
|
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
|
-
|
|
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`
|
|
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
|
|
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 (
|
|
1040
|
-
"Options",
|
|
1041
|
-
"
|
|
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
|
|
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 =
|
|
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
|
-
|
|
1339
|
+
body.statement
|
|
1155
1340
|
].map((line) => line ? ` ${line}` : "").join("\n");
|
|
1156
|
-
return `${jsdoc} public ${name}${generics}(${
|
|
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
|
|
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
|
-
...
|
|
1386
|
-
...
|
|
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
|
-
|
|
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
|
-
|
|
1430
|
-
|
|
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
|
-
|
|
1436
|
-
|
|
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. `
|
|
1473
|
-
* drops union members
|
|
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.
|
|
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.
|
|
1728
|
+
"kubb": ">=5.16.0",
|
|
1511
1729
|
"node": ">=22.22"
|
|
1512
1730
|
};
|
|
1513
1731
|
// package.json
|
|
1514
|
-
var version = "5.13.
|
|
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:
|
|
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",
|