@kubb/plugin-client 5.10.0-canary.20261105T162357 → 5.10.0
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/README.md +1 -1
- package/dist/client-CiHb_Cfg.d.ts +345 -0
- package/dist/index.cjs +288 -140
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +290 -142
- package/dist/index.js.map +1 -1
- package/dist/serializers-Ds8zZwOO.d.ts +190 -0
- package/dist/standardSchema-BShJWz2i.d.ts +104 -0
- package/dist/templates/client.d.ts +2 -0
- package/dist/templates/core.d.ts +3 -0
- package/dist/templates/serializers.d.ts +2 -0
- package/dist/templates/sse.d.ts +124 -0
- package/dist/templates/standardSchema.d.ts +2 -0
- package/docs/guides/write-your-client.md +1 -1
- package/docs/overview.md +1 -1
- package/kubb-extension.json +1 -1
- package/package.json +17 -8
- package/templates/client.ts +5 -0
- package/templates/core.ts +694 -0
- package/templates/serializers.ts +493 -0
- package/templates/sse.ts +274 -0
- package/templates/standardSchema.ts +162 -0
package/dist/index.cjs
CHANGED
|
@@ -110,20 +110,33 @@ function getPrimarySuccessContentType(node) {
|
|
|
110
110
|
return contentTypes[0].split(";")[0].trim().toLowerCase();
|
|
111
111
|
}
|
|
112
112
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
|
|
113
|
+
* The `text/event-stream` content of the primary success response, when it is the only content type. A response that also
|
|
114
|
+
* declares another type stays a normal call with its query hooks.
|
|
115
|
+
*/
|
|
116
|
+
function getEventStreamContent(node) {
|
|
117
|
+
return getPrimarySuccessContentType(node) === "text/event-stream" ? getPrimarySuccessResponse(node)?.content?.[0] : void 0;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Whether an operation streams its primary success response as Server-Sent Events (`text/event-stream`). The client
|
|
121
|
+
* generator uses this to return a typed event stream instead of a one-shot `RequestResult`.
|
|
116
122
|
*/
|
|
117
123
|
function isEventStream(node) {
|
|
118
|
-
return
|
|
124
|
+
return getEventStreamContent(node) !== void 0;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* How the document types a `text/event-stream` response, as the adapter read it from `itemSchema`: `event` when each item is
|
|
128
|
+
* the parsed event with its own `event` and `data`, `data` when it is only the payload. Returns `null` without an event stream.
|
|
129
|
+
*/
|
|
130
|
+
function getEventStream(node) {
|
|
131
|
+
const content = getEventStreamContent(node);
|
|
132
|
+
return content ? content.stream?.shape ?? "data" : null;
|
|
119
133
|
}
|
|
120
134
|
/**
|
|
121
135
|
* Derives the default `responseType` for an operation from its primary success response.
|
|
122
136
|
*
|
|
123
|
-
* Returns a value only when that response declares a single non-JSON content type. `text/event-stream`
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* leaving the runtime client's `Content-Type` auto-detection in charge.
|
|
137
|
+
* Returns a value only when that response declares a single non-JSON content type. `text/event-stream` maps to
|
|
138
|
+
* `'stream'`, `application/octet-stream`, `application/pdf`, `image/*`, `audio/*` and `video/*` map to `'blob'`, and
|
|
139
|
+
* other `text/*` to `'text'`. Otherwise `undefined`, leaving the runtime client's `Content-Type` auto-detection in charge.
|
|
127
140
|
*/
|
|
128
141
|
function getResponseType(node) {
|
|
129
142
|
const baseType = getPrimarySuccessContentType(node);
|
|
@@ -183,6 +196,22 @@ function buildOperationComments(node, options = {}) {
|
|
|
183
196
|
}
|
|
184
197
|
const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
185
198
|
/**
|
|
199
|
+
* An OpenAPI 3.2 `querystring` parameter is the whole query string, so a form-encoded object becomes one query parameter
|
|
200
|
+
* per property, typed and serialized like any other.
|
|
201
|
+
* Another content type, such as JSON, stays one parameter named after it, and the runtime sends its value as the whole query string.
|
|
202
|
+
*/
|
|
203
|
+
function spreadQuerystring(param) {
|
|
204
|
+
const schema = param.schema.type === "ref" ? param.schema.schema : param.schema;
|
|
205
|
+
const formEncoded = !param.contentType || param.contentType.split(";")[0].trim().toLowerCase() === "application/x-www-form-urlencoded";
|
|
206
|
+
if (schema?.type !== "object" || !formEncoded) return [param];
|
|
207
|
+
return schema.properties.map((property) => _kubb_kit.ast.factory.createParameter({
|
|
208
|
+
name: property.name,
|
|
209
|
+
in: "query",
|
|
210
|
+
schema: property.schema,
|
|
211
|
+
required: param.required && property.required
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
186
215
|
* Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
|
|
187
216
|
* group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
|
|
188
217
|
* (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
|
|
@@ -191,17 +220,18 @@ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
|
191
220
|
function getOperationParameters(node) {
|
|
192
221
|
const cached = operationParameterGroupsByNode.get(node);
|
|
193
222
|
if (cached) return cached;
|
|
194
|
-
const
|
|
223
|
+
const dedupe = (params) => {
|
|
195
224
|
const seen = /* @__PURE__ */ new Set();
|
|
196
|
-
return
|
|
197
|
-
if (
|
|
225
|
+
return params.filter((param) => {
|
|
226
|
+
if (seen.has(param.name)) return false;
|
|
198
227
|
seen.add(param.name);
|
|
199
228
|
return true;
|
|
200
229
|
});
|
|
201
230
|
};
|
|
231
|
+
const group = (location) => dedupe(node.parameters.filter((param) => param.in === location));
|
|
202
232
|
const groups = {
|
|
203
233
|
path: group("path"),
|
|
204
|
-
query: group("query"),
|
|
234
|
+
query: dedupe([...group("query"), ...group("querystring").flatMap(spreadQuerystring)]),
|
|
205
235
|
header: group("header"),
|
|
206
236
|
cookie: group("cookie")
|
|
207
237
|
};
|
|
@@ -431,11 +461,11 @@ function createGroupConfig(group) {
|
|
|
431
461
|
}
|
|
432
462
|
// kubb-extension.json
|
|
433
463
|
var compatibility = {
|
|
434
|
-
"kubb": ">=5.
|
|
464
|
+
"kubb": ">=5.16.0",
|
|
435
465
|
"node": ">=22.22"
|
|
436
466
|
};
|
|
437
467
|
// package.json
|
|
438
|
-
var version = "5.10.0
|
|
468
|
+
var version = "5.10.0";
|
|
439
469
|
// ../../internals/client/src/builders/validatorOptions.ts
|
|
440
470
|
/**
|
|
441
471
|
* Returns `true` when any direction of the validator uses zod (used for dependency checks).
|
|
@@ -479,6 +509,114 @@ function buildZodErrorParse(node, zodResolver) {
|
|
|
479
509
|
if (!node.responses.some((res) => !isSuccessStatusCode(res.statusCode) && res.content?.some((entry) => entry.schema))) return null;
|
|
480
510
|
return zodResolver.response.error?.(node) || null;
|
|
481
511
|
}
|
|
512
|
+
// ../../internals/client/src/builders/generics.ts
|
|
513
|
+
/**
|
|
514
|
+
* Builds the `RequestResult` generic arguments for one operation: the per-status responses record
|
|
515
|
+
* plus the per-call `ThrowOnError` flag. `SuccessOf` / `ErrorOf` split the record inside the
|
|
516
|
+
* runtime, so this only names the record and threads `ThrowOnError`.
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* `buildRequestResultGenerics({ node, types }) // 'AddPetResponses, ThrowOnError'`
|
|
520
|
+
*/
|
|
521
|
+
function buildRequestResultGenerics({ node, types }) {
|
|
522
|
+
return `${types.response.responses(node)}, ThrowOnError`;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Builds the full return type an operation's function signature uses: `Unwrappable<RequestResult>`
|
|
526
|
+
* for the default `returnType: 'full'`, already a promise so no further wrapping is needed, a plain
|
|
527
|
+
* `Promise<RequestResult>` for the internal `'plain'` shape, or a
|
|
528
|
+
* `Promise` of the runtime's `UnwrappedResult` when `returnType: 'data'` narrows a resolved call
|
|
529
|
+
* down to the bare success body.
|
|
530
|
+
*
|
|
531
|
+
* @example
|
|
532
|
+
* `buildResultType({ node, types, returnType: 'data' }) // 'Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>'`
|
|
533
|
+
*/
|
|
534
|
+
function buildResultType({ node, types, returnType }) {
|
|
535
|
+
const generics = buildRequestResultGenerics({
|
|
536
|
+
node,
|
|
537
|
+
types
|
|
538
|
+
});
|
|
539
|
+
if (returnType === "data") return `Promise<UnwrappedResult<${generics}>>`;
|
|
540
|
+
if (returnType === "plain") return `Promise<RequestResult<${generics}>>`;
|
|
541
|
+
return `Unwrappable<RequestResult<${generics}>>`;
|
|
542
|
+
}
|
|
543
|
+
// ../../internals/client/src/builders/returnStatement.ts
|
|
544
|
+
/**
|
|
545
|
+
* Builds the return statement of a generated operation function. With the default
|
|
546
|
+
* `returnType: 'full'` the runtime call already resolves to `{ data, error, request, response }`.
|
|
547
|
+
* The generated code casts that result to the operation's `RequestResult`, then wraps it with
|
|
548
|
+
* `withUnwrap`, so the caller can `await` it directly or call `.unwrap()` for the bare success
|
|
549
|
+
* body. With `returnType: 'data'` it instead routes the call through the runtime's `unwrapResult`,
|
|
550
|
+
* which narrows the resolved value down to the bare success body the same way `RequestResult`
|
|
551
|
+
* already does, using the plugin default when the call leaves `throwOnError` unset.
|
|
552
|
+
* With `returnType: 'plain'` it returns the runtime call as is, cast to `RequestResult`, with no helper.
|
|
553
|
+
*
|
|
554
|
+
* Cast first, wrap second, for the `'full'` path. That order keeps `withUnwrap`'s generic inferred
|
|
555
|
+
* as `RequestResult` instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
|
|
556
|
+
* straight to `Unwrappable<B>` does not work: the `.then` overload stays pinned to `A`, and `as`
|
|
557
|
+
* rejects that two-generic swap even where `A` and `B` on their own would satisfy it.
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* `return withUnwrap(request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>)`
|
|
561
|
+
* @example
|
|
562
|
+
* `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError ?? false) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
|
|
563
|
+
* @example
|
|
564
|
+
* `return request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>`
|
|
565
|
+
*/
|
|
566
|
+
function buildReturnStatement({ node, types, callConfig, returnType, throwOnErrorDefault }) {
|
|
567
|
+
const request = returnType !== "plain" && hasRequestContentTypeChoice(node) ? `request<${types.response.options(node)}['body']>` : "request";
|
|
568
|
+
if (returnType === "data") return `return unwrapResult(${request}(${callConfig}), config.throwOnError ?? ${throwOnErrorDefault}) as ${buildResultType({
|
|
569
|
+
node,
|
|
570
|
+
types,
|
|
571
|
+
returnType
|
|
572
|
+
})}`;
|
|
573
|
+
if (returnType === "plain") return `return ${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
|
|
574
|
+
node,
|
|
575
|
+
types
|
|
576
|
+
})}>>`;
|
|
577
|
+
return `return withUnwrap(${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
|
|
578
|
+
node,
|
|
579
|
+
types
|
|
580
|
+
})}>>)`;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Builds the return type and statement of a `text/event-stream` operation. The item type is `TypedEvent<...>` when each item
|
|
584
|
+
* declares its own `event` and `data`, and otherwise the payload type, which `EventStreamResult` wraps in `ServerSentEvent`. The call's
|
|
585
|
+
* promise goes through `toEventStream` with the `client` for its defaults, the call's `config`, the shape and the per-event
|
|
586
|
+
* validator. A `returnType: 'plain'` client receives only the promise,
|
|
587
|
+
* `toEventStream(request(config))`, and keeps its response validator in the call config.
|
|
588
|
+
*/
|
|
589
|
+
function buildEventStreamReturn({ node, types, callConfig, returnType, validatorHooks, throwOnErrorDefault }) {
|
|
590
|
+
const shape = getEventStream(node) ?? "data";
|
|
591
|
+
const responses = types.response.responses(node);
|
|
592
|
+
const success = `SuccessOf<${responses}>`;
|
|
593
|
+
const eventType = shape === "event" ? `TypedEvent<${success}>` : success;
|
|
594
|
+
if (returnType === "plain") return {
|
|
595
|
+
returnType: `Promise<EventStreamResult<${eventType}>>`,
|
|
596
|
+
statement: `return toEventStream<${eventType}>(request(${callConfig}))`
|
|
597
|
+
};
|
|
598
|
+
const generics = `${responses}, ThrowOnError${shape === "event" ? `, ${eventType}` : ""}`;
|
|
599
|
+
const call = `toEventStream<${eventType}>(request(${callConfig}), { client: request, config, ${[`shape: '${shape}'`, validatorHooks?.response ? `validator: ${validatorHooks.response}` : null].filter(Boolean).join(", ")} })`;
|
|
600
|
+
if (returnType === "data") {
|
|
601
|
+
const result = `Promise<UnwrappedStreamResult<${generics}>>`;
|
|
602
|
+
return {
|
|
603
|
+
returnType: result,
|
|
604
|
+
statement: `return unwrapResult(${call}, config.throwOnError ?? ${throwOnErrorDefault}) as ${result}`
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
return {
|
|
608
|
+
returnType: `Unwrappable<StreamResult<${generics}>>`,
|
|
609
|
+
statement: `return withUnwrap(${call} as Promise<StreamResult<${generics}>>)`
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
/** The stream types an operation file imports: `StreamResult` and `StreamOptions`, `EventStreamResult` for a plain client, and `TypedEvent` for named events. */
|
|
613
|
+
function buildEventStreamImports({ returnType, typed }) {
|
|
614
|
+
return [...{
|
|
615
|
+
plain: ["EventStreamResult"],
|
|
616
|
+
full: ["StreamResult", "StreamOptions"],
|
|
617
|
+
data: ["UnwrappedStreamResult", "StreamOptions"]
|
|
618
|
+
}[returnType], ...typed ? ["TypedEvent"] : []];
|
|
619
|
+
}
|
|
482
620
|
// ../../internals/client/src/builders/security.ts
|
|
483
621
|
function serializeAuth(auth) {
|
|
484
622
|
const parts = [`type: '${auth.type}'`];
|
|
@@ -634,6 +772,7 @@ function serializeParameter(parameter) {
|
|
|
634
772
|
if (parameter.explode !== void 0) parts.push(`explode: ${parameter.explode}`);
|
|
635
773
|
if (parameter.in === "query" && parameter.allowReserved !== void 0) parts.push(`allowReserved: ${parameter.allowReserved}`);
|
|
636
774
|
if (parameter.in !== "path" && parameter.contentType) parts.push(`contentType: '${parameter.contentType}'`);
|
|
775
|
+
if (parameter.in === "querystring") parts.push("querystring: true");
|
|
637
776
|
return parts.length > 0 ? `{ ${parts.join(", ")} }` : null;
|
|
638
777
|
}
|
|
639
778
|
/**
|
|
@@ -676,10 +815,9 @@ function buildStyles({ node }) {
|
|
|
676
815
|
header: [],
|
|
677
816
|
cookie: []
|
|
678
817
|
};
|
|
679
|
-
for (const parameter of
|
|
818
|
+
for (const [location, parameters] of Object.entries(getOperationParameters(node))) for (const parameter of parameters) {
|
|
680
819
|
const literal = serializeParameter(parameter);
|
|
681
|
-
if (
|
|
682
|
-
groups[parameter.in].push(`${toKey(parameter.name)}: ${literal}`);
|
|
820
|
+
if (literal) groups[location].push(`${toKey(parameter.name)}: ${literal}`);
|
|
683
821
|
}
|
|
684
822
|
const entries = [...Object.keys(groups).filter((location) => groups[location].length > 0).map((location) => `${location}: { ${groups[location].join(", ")} }`), serializeBodyEncoding(node)].filter(Boolean);
|
|
685
823
|
return entries.length > 0 ? `{ ${entries.join(", ")} }` : null;
|
|
@@ -690,12 +828,20 @@ function buildStyles({ node }) {
|
|
|
690
828
|
* functions and the SDK methods. The `throwOnError` fallback is applied after the `...config` spread.
|
|
691
829
|
*
|
|
692
830
|
* `mergeContentType` is `true` when the caller must destructure `contentType` from `options`, and
|
|
693
|
-
* `eventStream` is `true` when the operation returns `text/event-stream`.
|
|
831
|
+
* `eventStream` is `true` when the operation returns `text/event-stream`. Unless `returnType` is `'plain'`, a
|
|
832
|
+
* stream's literal leaves the `response` validator to `toEventStream`, which runs it per event.
|
|
694
833
|
*/
|
|
695
|
-
function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault }) {
|
|
834
|
+
function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault, returnType }) {
|
|
835
|
+
const eventStream = isEventStream(node);
|
|
696
836
|
const securityLiteral = buildSecurityMetadata({ security });
|
|
697
837
|
const stylesLiteral = buildStyles({ node });
|
|
698
|
-
const validatorLiteral = buildValidatorLiteral(validatorHooks, [
|
|
838
|
+
const validatorLiteral = buildValidatorLiteral(validatorHooks, eventStream && returnType !== "plain" ? [
|
|
839
|
+
"request",
|
|
840
|
+
"path",
|
|
841
|
+
"query",
|
|
842
|
+
"headers",
|
|
843
|
+
"error"
|
|
844
|
+
] : [
|
|
699
845
|
"request",
|
|
700
846
|
"path",
|
|
701
847
|
"query",
|
|
@@ -718,88 +864,7 @@ function buildCallConfig({ node, validatorHooks, security, throwOnErrorDefault }
|
|
|
718
864
|
`throwOnError: config.throwOnError ?? ${throwOnErrorDefault}`
|
|
719
865
|
].filter(Boolean).join(", ")} }`,
|
|
720
866
|
mergeContentType,
|
|
721
|
-
eventStream
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
// ../../internals/client/src/builders/generics.ts
|
|
725
|
-
/**
|
|
726
|
-
* Builds the `RequestResult` generic arguments for one operation: the per-status responses record
|
|
727
|
-
* plus the per-call `ThrowOnError` flag. `SuccessOf` / `ErrorOf` split the record inside the
|
|
728
|
-
* runtime, so this only names the record and threads `ThrowOnError`.
|
|
729
|
-
*
|
|
730
|
-
* @example
|
|
731
|
-
* `buildRequestResultGenerics({ node, types }) // 'AddPetResponses, ThrowOnError'`
|
|
732
|
-
*/
|
|
733
|
-
function buildRequestResultGenerics({ node, types }) {
|
|
734
|
-
return `${types.response.responses(node)}, ThrowOnError`;
|
|
735
|
-
}
|
|
736
|
-
/**
|
|
737
|
-
* Builds the full return type an operation's function signature uses: `Unwrappable<RequestResult>`
|
|
738
|
-
* for the default `returnType: 'full'`, already a promise so no further wrapping is needed, a plain
|
|
739
|
-
* `Promise<RequestResult>` for the internal `'plain'` shape, or a
|
|
740
|
-
* `Promise` of the runtime's `UnwrappedResult` when `returnType: 'data'` narrows a resolved call
|
|
741
|
-
* down to the bare success body.
|
|
742
|
-
*
|
|
743
|
-
* @example
|
|
744
|
-
* `buildResultType({ node, types, returnType: 'data' }) // 'Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>'`
|
|
745
|
-
*/
|
|
746
|
-
function buildResultType({ node, types, returnType }) {
|
|
747
|
-
const generics = buildRequestResultGenerics({
|
|
748
|
-
node,
|
|
749
|
-
types
|
|
750
|
-
});
|
|
751
|
-
if (returnType === "data") return `Promise<UnwrappedResult<${generics}>>`;
|
|
752
|
-
if (returnType === "plain") return `Promise<RequestResult<${generics}>>`;
|
|
753
|
-
return `Unwrappable<RequestResult<${generics}>>`;
|
|
754
|
-
}
|
|
755
|
-
// ../../internals/client/src/builders/returnStatement.ts
|
|
756
|
-
/**
|
|
757
|
-
* Builds the return statement of a generated operation function. With the default
|
|
758
|
-
* `returnType: 'full'` the runtime call already resolves to `{ data, error, request, response }`.
|
|
759
|
-
* The generated code casts that result to the operation's `RequestResult`, then wraps it with
|
|
760
|
-
* `withUnwrap`, so the caller can `await` it directly or call `.unwrap()` for the bare success
|
|
761
|
-
* body. With `returnType: 'data'` it instead routes the call through the runtime's `unwrapResult`,
|
|
762
|
-
* which narrows the resolved value down to the bare success body the same way `RequestResult`
|
|
763
|
-
* already does, using the plugin default when the call leaves `throwOnError` unset.
|
|
764
|
-
* With `returnType: 'plain'` it returns the runtime call as is, cast to `RequestResult`, with no helper.
|
|
765
|
-
*
|
|
766
|
-
* Cast first, wrap second, for the `'full'` path. That order keeps `withUnwrap`'s generic inferred
|
|
767
|
-
* as `RequestResult` instead of the runtime's own internal result type. Casting an `Unwrappable<A>`
|
|
768
|
-
* straight to `Unwrappable<B>` does not work: the `.then` overload stays pinned to `A`, and `as`
|
|
769
|
-
* rejects that two-generic swap even where `A` and `B` on their own would satisfy it.
|
|
770
|
-
*
|
|
771
|
-
* @example
|
|
772
|
-
* `return withUnwrap(request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>)`
|
|
773
|
-
* @example
|
|
774
|
-
* `return unwrapResult(request({ method: 'POST', url: '/pet', ...config }), config.throwOnError ?? false) as Promise<UnwrappedResult<AddPetResponses, ThrowOnError>>`
|
|
775
|
-
* @example
|
|
776
|
-
* `return request({ method: 'POST', url: '/pet', ...config }) as Promise<RequestResult<AddPetResponses, ThrowOnError>>`
|
|
777
|
-
*/
|
|
778
|
-
function buildReturnStatement({ node, types, callConfig, returnType, throwOnErrorDefault }) {
|
|
779
|
-
const request = returnType !== "plain" && hasRequestContentTypeChoice(node) ? `request<${types.response.options(node)}['body']>` : "request";
|
|
780
|
-
if (returnType === "data") return `return unwrapResult(${request}(${callConfig}), config.throwOnError ?? ${throwOnErrorDefault}) as ${buildResultType({
|
|
781
|
-
node,
|
|
782
|
-
types,
|
|
783
|
-
returnType
|
|
784
|
-
})}`;
|
|
785
|
-
if (returnType === "plain") return `return ${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
|
|
786
|
-
node,
|
|
787
|
-
types
|
|
788
|
-
})}>>`;
|
|
789
|
-
return `return withUnwrap(${request}(${callConfig}) as Promise<RequestResult<${buildRequestResultGenerics({
|
|
790
|
-
node,
|
|
791
|
-
types
|
|
792
|
-
})}>>)`;
|
|
793
|
-
}
|
|
794
|
-
/**
|
|
795
|
-
* Builds the return type and statement of an operation that returns `text/event-stream`: the call
|
|
796
|
-
* is wrapped in `toEventStream`, so the caller iterates the typed success events.
|
|
797
|
-
*/
|
|
798
|
-
function buildEventStreamReturn({ node, types, callConfig }) {
|
|
799
|
-
const eventType = `SuccessOf<${types.response.responses(node)}>`;
|
|
800
|
-
return {
|
|
801
|
-
returnType: `Promise<EventStreamResult<${eventType}>>`,
|
|
802
|
-
statement: `return toEventStream<${eventType}>(request(${callConfig}))`
|
|
867
|
+
eventStream
|
|
803
868
|
};
|
|
804
869
|
}
|
|
805
870
|
// ../../internals/client/src/builders/signature.ts
|
|
@@ -813,13 +878,14 @@ const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declara
|
|
|
813
878
|
* per-operation input type has to be emitted. Both names come from `types`, which is `plugin-ts` or
|
|
814
879
|
* `plugin-zod`'s inferred types (see `resolveOperationTypes`).
|
|
815
880
|
*/
|
|
816
|
-
function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDefault }) {
|
|
881
|
+
function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDefault, eventStream = false }) {
|
|
817
882
|
const optionsName = types.response.options(node);
|
|
883
|
+
const optionsType = `${eventStream && returnType !== "plain" ? "StreamOptions" : "Options"}<${optionsName}, ThrowOnError>`;
|
|
818
884
|
const { isOptional } = getRequestGroupOptionality(node);
|
|
819
885
|
return {
|
|
820
886
|
paramsSignature: declarationPrinter.print((0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
|
|
821
887
|
name: "options",
|
|
822
|
-
type:
|
|
888
|
+
type: optionsType,
|
|
823
889
|
...isOptional ? { default: "{}" } : {}
|
|
824
890
|
})] })) ?? "",
|
|
825
891
|
returnType: buildResultType({
|
|
@@ -830,37 +896,63 @@ function buildGroupedOptionsSignature({ node, types, returnType, throwOnErrorDef
|
|
|
830
896
|
generics: [`ThrowOnError extends boolean = ${throwOnErrorDefault}`]
|
|
831
897
|
};
|
|
832
898
|
}
|
|
833
|
-
// ../../internals/client/src/
|
|
899
|
+
// ../../internals/client/src/builders/operationBody.ts
|
|
834
900
|
/**
|
|
835
|
-
*
|
|
836
|
-
*
|
|
837
|
-
*
|
|
838
|
-
* the source.
|
|
901
|
+
* The pieces of a generated operation: its generics, `options` parameter, return type, return statement, and whether the
|
|
902
|
+
* body destructures `contentType` from `options`. Shared by the standalone functions and the SDK methods, so a
|
|
903
|
+
* `text/event-stream` operation is wired to `toEventStream` in one place.
|
|
839
904
|
*/
|
|
840
|
-
function
|
|
841
|
-
|
|
905
|
+
function buildOperationBody({ node, types, validatorHooks, security, returnType, throwOnErrorDefault }) {
|
|
906
|
+
const { callConfig, mergeContentType, eventStream } = buildCallConfig({
|
|
907
|
+
node,
|
|
908
|
+
validatorHooks,
|
|
909
|
+
security,
|
|
910
|
+
throwOnErrorDefault,
|
|
911
|
+
returnType
|
|
912
|
+
});
|
|
842
913
|
const signature = buildGroupedOptionsSignature({
|
|
843
914
|
node,
|
|
844
915
|
types,
|
|
845
916
|
returnType,
|
|
846
|
-
throwOnErrorDefault
|
|
917
|
+
throwOnErrorDefault,
|
|
918
|
+
eventStream
|
|
847
919
|
});
|
|
848
|
-
const
|
|
920
|
+
const stream = eventStream ? buildEventStreamReturn({
|
|
849
921
|
node,
|
|
922
|
+
types,
|
|
923
|
+
callConfig,
|
|
924
|
+
returnType,
|
|
850
925
|
validatorHooks,
|
|
851
|
-
security,
|
|
852
926
|
throwOnErrorDefault
|
|
853
|
-
});
|
|
854
|
-
const eventStreamReturn = eventStream ? buildEventStreamReturn({
|
|
855
|
-
node,
|
|
856
|
-
types,
|
|
857
|
-
callConfig
|
|
858
927
|
}) : null;
|
|
859
|
-
|
|
860
|
-
|
|
928
|
+
return {
|
|
929
|
+
generics: signature.generics,
|
|
930
|
+
paramsSignature: signature.paramsSignature,
|
|
931
|
+
returnType: stream?.returnType ?? signature.returnType,
|
|
932
|
+
statement: stream?.statement ?? buildReturnStatement({
|
|
933
|
+
node,
|
|
934
|
+
types,
|
|
935
|
+
callConfig,
|
|
936
|
+
returnType,
|
|
937
|
+
throwOnErrorDefault
|
|
938
|
+
}),
|
|
939
|
+
mergeContentType
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
// ../../internals/client/src/components/Operation.tsx
|
|
943
|
+
/**
|
|
944
|
+
* Renders one client operation: the grouped `<Name>Request` type and the function that forwards a
|
|
945
|
+
* single `options` object to the resolved client and returns the `Unwrappable<RequestResult>`. The
|
|
946
|
+
* type, signature, and call config are built with the AST factory, and only the jsx-renderer emits
|
|
947
|
+
* the source.
|
|
948
|
+
*/
|
|
949
|
+
function Operation({ name, node, types, validatorHooks, returnType, throwOnErrorDefault, security }) {
|
|
950
|
+
if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
|
|
951
|
+
const body = buildOperationBody({
|
|
861
952
|
node,
|
|
862
953
|
types,
|
|
863
|
-
|
|
954
|
+
validatorHooks,
|
|
955
|
+
security,
|
|
864
956
|
returnType,
|
|
865
957
|
throwOnErrorDefault
|
|
866
958
|
});
|
|
@@ -871,22 +963,53 @@ function Operation({ name, node, types, validatorHooks, returnType, throwOnError
|
|
|
871
963
|
children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.Function, {
|
|
872
964
|
name,
|
|
873
965
|
export: true,
|
|
874
|
-
generics:
|
|
875
|
-
params:
|
|
876
|
-
returnType:
|
|
966
|
+
generics: body.generics,
|
|
967
|
+
params: body.paramsSignature,
|
|
968
|
+
returnType: body.returnType,
|
|
877
969
|
JSDoc: { comments: buildOperationComments(node, {
|
|
878
970
|
link: "urlPath",
|
|
879
971
|
linkPosition: "beforeDeprecated",
|
|
880
972
|
splitLines: true
|
|
881
973
|
}) },
|
|
882
974
|
children: [
|
|
883
|
-
mergeContentType ? "const { client: request = client, contentType, ...config } = options" : "const { client: request = client, ...config } = options",
|
|
975
|
+
body.mergeContentType ? "const { client: request = client, contentType, ...config } = options" : "const { client: request = client, ...config } = options",
|
|
884
976
|
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)("br", {}),
|
|
885
|
-
|
|
977
|
+
body.statement
|
|
886
978
|
]
|
|
887
979
|
})
|
|
888
980
|
});
|
|
889
981
|
}
|
|
982
|
+
// ../../internals/client/src/components/StreamRuntime.tsx
|
|
983
|
+
/**
|
|
984
|
+
* The `footer` of `.kubb/client.ts`: the `baseURL` set at generation time, and the `sse.ts` re-export when the document
|
|
985
|
+
* streams events, so `ServerSentEvent` and `toEventStream` stay importable from the client as in earlier releases.
|
|
986
|
+
*/
|
|
987
|
+
function buildClientFooter({ baseURL, sse }) {
|
|
988
|
+
const baseURLExpression = baseURL ? baseURL.includes("${") ? `\`${baseURL.replaceAll("`", "\\`")}\`` : JSON.stringify(baseURL) : void 0;
|
|
989
|
+
return [baseURLExpression ? `client.setConfig({ baseURL: ${baseURLExpression} })` : null, sse ? `export * from './sse.ts'` : null].filter(Boolean).join("\n") || void 0;
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Writes the footer of the `.kubb/client.ts` the plugin injected, the one place it is built, and copies `sse.ts` into
|
|
993
|
+
* `.kubb/` only for a document with a `text/event-stream` operation, so a document without streams ships no stream runtime.
|
|
994
|
+
* `sse` is set by the plugins that ship the runtime, so a custom client from `@kubb/plugin-client` gets neither file.
|
|
995
|
+
*/
|
|
996
|
+
function StreamRuntime({ nodes, root, sse, baseURL }) {
|
|
997
|
+
if (!sse) return null;
|
|
998
|
+
const streams = nodes.some(isEventStream);
|
|
999
|
+
const footer = buildClientFooter({
|
|
1000
|
+
baseURL,
|
|
1001
|
+
sse: streams
|
|
1002
|
+
});
|
|
1003
|
+
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, {
|
|
1004
|
+
baseName: "sse.ts",
|
|
1005
|
+
path: node_path.default.resolve(root, ".kubb/sse.ts"),
|
|
1006
|
+
copy: sse
|
|
1007
|
+
}), footer && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File, {
|
|
1008
|
+
baseName: "client.ts",
|
|
1009
|
+
path: node_path.default.resolve(root, ".kubb/client.ts"),
|
|
1010
|
+
footer
|
|
1011
|
+
})] });
|
|
1012
|
+
}
|
|
890
1013
|
// ../../internals/client/src/resolveOperationTypes.ts
|
|
891
1014
|
/**
|
|
892
1015
|
* A `plugin-ts` shaped resolver whose operation type names point at the types `pluginZod({ inferred })`
|
|
@@ -962,13 +1085,24 @@ function resolveOperationTypes(driver, pluginName) {
|
|
|
962
1085
|
* `@kubb/plugin-axios`). Emits one async function per OpenAPI operation using the shared
|
|
963
1086
|
* `Operation` component: a grouped `<Name>Request` type and a function that forwards a single
|
|
964
1087
|
* `options` object to the bundled `client` and returns the `Unwrappable<RequestResult>`. Only the
|
|
965
|
-
* generator `name`
|
|
966
|
-
* identical.
|
|
1088
|
+
* generator `name` and the runtime templates differ between plugins. Every other resolution, import,
|
|
1089
|
+
* and rendering step is identical.
|
|
1090
|
+
*
|
|
1091
|
+
* `sse` is the Server-Sent Events runtime template, copied into `.kubb/sse.ts` only for a document with a
|
|
1092
|
+
* `text/event-stream` operation.
|
|
967
1093
|
*/
|
|
968
|
-
function createClientGenerator(name) {
|
|
1094
|
+
function createClientGenerator(name, { sse } = {}) {
|
|
969
1095
|
return (0, _kubb_kit.defineGenerator)({
|
|
970
1096
|
name,
|
|
971
1097
|
renderer: _kubb_renderer_jsx.jsxRenderer,
|
|
1098
|
+
operations(nodes, ctx) {
|
|
1099
|
+
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(StreamRuntime, {
|
|
1100
|
+
nodes,
|
|
1101
|
+
root: ctx.root,
|
|
1102
|
+
sse,
|
|
1103
|
+
baseURL: ctx.options.baseURL
|
|
1104
|
+
});
|
|
1105
|
+
},
|
|
972
1106
|
operation(node, ctx) {
|
|
973
1107
|
if (!_kubb_kit.ast.isHttpOperationNode(node)) return null;
|
|
974
1108
|
const { config, driver, resolver, root } = ctx;
|
|
@@ -1018,19 +1152,19 @@ function createClientGenerator(name) {
|
|
|
1018
1152
|
const clientPath = importPath ?? node_path.default.resolve(root, ".kubb/client.ts");
|
|
1019
1153
|
const clientRoot = importPath === void 0 ? meta.file.path : void 0;
|
|
1020
1154
|
const standardSchemaPath = importPath ?? node_path.default.resolve(root, ".kubb/standardSchema.ts");
|
|
1021
|
-
const
|
|
1155
|
+
const ssePath = importPath ?? node_path.default.resolve(root, ".kubb/sse.ts");
|
|
1156
|
+
const stream = getEventStream(node);
|
|
1022
1157
|
const clientValueImports = (() => {
|
|
1023
|
-
if (eventStream) return ["client", "toEventStream"];
|
|
1024
1158
|
if (returnType === "plain") return ["client"];
|
|
1025
1159
|
if (returnType === "data") return ["client", "unwrapResult"];
|
|
1026
1160
|
return ["client", "withUnwrap"];
|
|
1027
1161
|
})();
|
|
1028
1162
|
const clientTypeImports = (() => {
|
|
1029
|
-
if (
|
|
1030
|
-
"Options",
|
|
1031
|
-
"
|
|
1032
|
-
"SuccessOf"
|
|
1033
|
-
];
|
|
1163
|
+
if (stream) return {
|
|
1164
|
+
plain: ["Options", "SuccessOf"],
|
|
1165
|
+
data: ["SuccessOf"],
|
|
1166
|
+
full: ["SuccessOf", "Unwrappable"]
|
|
1167
|
+
}[returnType];
|
|
1034
1168
|
if (returnType === "plain") return ["Options", "RequestResult"];
|
|
1035
1169
|
if (returnType === "data") return ["Options", "UnwrappedResult"];
|
|
1036
1170
|
return [
|
|
@@ -1071,6 +1205,20 @@ function createClientGenerator(name) {
|
|
|
1071
1205
|
path: clientPath,
|
|
1072
1206
|
isTypeOnly: true
|
|
1073
1207
|
}),
|
|
1208
|
+
stream && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1209
|
+
name: ["toEventStream"],
|
|
1210
|
+
root: clientRoot,
|
|
1211
|
+
path: ssePath
|
|
1212
|
+
}),
|
|
1213
|
+
stream && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1214
|
+
name: buildEventStreamImports({
|
|
1215
|
+
returnType,
|
|
1216
|
+
typed: stream === "event"
|
|
1217
|
+
}),
|
|
1218
|
+
root: clientRoot,
|
|
1219
|
+
path: ssePath,
|
|
1220
|
+
isTypeOnly: true
|
|
1221
|
+
}),
|
|
1074
1222
|
meta.fileTypes && importedTypeNames.length > 0 && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
1075
1223
|
name: Array.from(new Set(importedTypeNames)),
|
|
1076
1224
|
root: meta.file.path,
|
|
@@ -1103,11 +1251,11 @@ function createClientGenerator(name) {
|
|
|
1103
1251
|
}
|
|
1104
1252
|
// ../../internals/client/src/macros.ts
|
|
1105
1253
|
/**
|
|
1106
|
-
* Macros the client plugins apply by default, ahead of any user macros. `
|
|
1107
|
-
* drops union members
|
|
1254
|
+
* Macros the client plugins apply by default, ahead of any user macros. `macroDedupeUnion`
|
|
1255
|
+
* drops repeated union members, keeping the generated response and error
|
|
1108
1256
|
* unions tidy. A plugin wires them with `ctx.setMacros([...defaultMacros, ...userMacros])`.
|
|
1109
1257
|
*/
|
|
1110
|
-
const defaultMacros = [_kubb_kit.
|
|
1258
|
+
const defaultMacros = [_kubb_kit.macroDedupeUnion];
|
|
1111
1259
|
// ../../internals/client/src/resolver.ts
|
|
1112
1260
|
/**
|
|
1113
1261
|
* Default resolver shared by the client plugins. Functions use safe camelCase identifiers and
|