@kubb/plugin-zod 5.0.0-beta.98 → 5.0.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/dist/index.cjs +323 -136
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.js +324 -137
- package/dist/index.js.map +1 -1
- package/package.json +3 -6
package/dist/index.cjs
CHANGED
|
@@ -12,6 +12,179 @@ var __name = (target, value) => __defProp(target, "name", {
|
|
|
12
12
|
let kubb_kit = require("kubb/kit");
|
|
13
13
|
let kubb_jsx = require("kubb/jsx");
|
|
14
14
|
let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
|
|
15
|
+
//#region ../../internals/shared/src/params.ts
|
|
16
|
+
/**
|
|
17
|
+
* Drops parameters that share the same name, keeping the first.
|
|
18
|
+
*
|
|
19
|
+
* A malformed spec can declare the same parameter name twice within one `in` location. Both would
|
|
20
|
+
* resolve to the same output property, so emitting both would yield an object type with a duplicate
|
|
21
|
+
* member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
|
|
22
|
+
* parameter names flow through unchanged, so no two distinct names ever collide here anymore.
|
|
23
|
+
*/
|
|
24
|
+
function dedupeParams(params) {
|
|
25
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26
|
+
return params.filter((param) => {
|
|
27
|
+
if (seen.has(param.name)) return false;
|
|
28
|
+
seen.add(param.name);
|
|
29
|
+
return true;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region ../../internals/shared/src/operation.ts
|
|
34
|
+
/**
|
|
35
|
+
* Maps a content type to the PascalCase suffix used to name per-content-type variants
|
|
36
|
+
* (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
|
|
37
|
+
*/
|
|
38
|
+
function getContentTypeSuffix(contentType) {
|
|
39
|
+
const baseType = contentType.split(";")[0].trim();
|
|
40
|
+
if (baseType === "application/json") return "Json";
|
|
41
|
+
if (baseType === "multipart/form-data") return "FormData";
|
|
42
|
+
if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
|
|
43
|
+
const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
44
|
+
if (parts.length === 0) return "Unknown";
|
|
45
|
+
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
|
|
49
|
+
* (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
|
|
50
|
+
*/
|
|
51
|
+
function getPerContentTypeName(baseName, suffix) {
|
|
52
|
+
if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
|
|
53
|
+
return baseName + suffix;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolves per-content-type variant names for a set of content entries, deduplicating suffix
|
|
57
|
+
* collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
|
|
58
|
+
* the final (possibly counter-augmented) value, so callers can derive parallel names in another
|
|
59
|
+
* namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
|
|
60
|
+
*/
|
|
61
|
+
function resolveContentTypeVariants(entries, baseName) {
|
|
62
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
63
|
+
return entries.filter((entry) => entry.schema).map((entry) => {
|
|
64
|
+
const baseSuffix = getContentTypeSuffix(entry.contentType);
|
|
65
|
+
let suffix = baseSuffix;
|
|
66
|
+
let name = getPerContentTypeName(baseName, suffix);
|
|
67
|
+
let counter = 2;
|
|
68
|
+
while (usedNames.has(name)) {
|
|
69
|
+
suffix = `${baseSuffix}${counter++}`;
|
|
70
|
+
name = getPerContentTypeName(baseName, suffix);
|
|
71
|
+
}
|
|
72
|
+
usedNames.add(name);
|
|
73
|
+
return {
|
|
74
|
+
name,
|
|
75
|
+
suffix,
|
|
76
|
+
schema: entry.schema,
|
|
77
|
+
keysToOmit: entry.keysToOmit,
|
|
78
|
+
contentType: entry.contentType
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
83
|
+
/**
|
|
84
|
+
* Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
|
|
85
|
+
* group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
|
|
86
|
+
* (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
|
|
87
|
+
* same parameters once per plugin.
|
|
88
|
+
*/
|
|
89
|
+
function getOperationParameters(node) {
|
|
90
|
+
const cached = operationParameterGroupsByNode.get(node);
|
|
91
|
+
if (cached) return cached;
|
|
92
|
+
const groups = {
|
|
93
|
+
path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
|
|
94
|
+
query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
|
|
95
|
+
header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
|
|
96
|
+
cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
|
|
97
|
+
};
|
|
98
|
+
operationParameterGroupsByNode.set(node, groups);
|
|
99
|
+
return groups;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Builds the combined `{ body, path, query, headers }` options object schema for an operation,
|
|
103
|
+
* referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s
|
|
104
|
+
* `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same
|
|
105
|
+
* shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod
|
|
106
|
+
* printer to emit `z.object(…)` rather than a record.
|
|
107
|
+
*/
|
|
108
|
+
function buildOptionsSchema(node, resolver) {
|
|
109
|
+
const { path, query, header } = getOperationParameters(node);
|
|
110
|
+
const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
|
|
111
|
+
const createNever = () => kubb_kit.ast.factory.createSchema({
|
|
112
|
+
type: "never",
|
|
113
|
+
primitive: void 0,
|
|
114
|
+
optional: true
|
|
115
|
+
});
|
|
116
|
+
const groups = [
|
|
117
|
+
{
|
|
118
|
+
name: "path",
|
|
119
|
+
params: path,
|
|
120
|
+
resolve: resolver.param.path
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "query",
|
|
124
|
+
params: query,
|
|
125
|
+
resolve: resolver.param.query
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "headers",
|
|
129
|
+
params: header,
|
|
130
|
+
resolve: resolver.param.headers
|
|
131
|
+
}
|
|
132
|
+
];
|
|
133
|
+
return kubb_kit.ast.factory.createSchema({
|
|
134
|
+
type: "object",
|
|
135
|
+
primitive: "object",
|
|
136
|
+
deprecated: node.deprecated,
|
|
137
|
+
properties: [kubb_kit.ast.factory.createProperty({
|
|
138
|
+
name: "body",
|
|
139
|
+
required: hasBody,
|
|
140
|
+
schema: hasBody ? kubb_kit.ast.factory.createSchema({
|
|
141
|
+
type: "ref",
|
|
142
|
+
name: resolver.response.body(node)
|
|
143
|
+
}) : createNever()
|
|
144
|
+
}), ...groups.map(({ name, params, resolve }) => {
|
|
145
|
+
const required = params.some((param) => param.required);
|
|
146
|
+
return kubb_kit.ast.factory.createProperty({
|
|
147
|
+
name,
|
|
148
|
+
required,
|
|
149
|
+
schema: params.length > 0 ? kubb_kit.ast.factory.createSchema({
|
|
150
|
+
type: "ref",
|
|
151
|
+
name: resolve.call(resolver.param, node, params[0]),
|
|
152
|
+
optional: !required
|
|
153
|
+
}) : createNever()
|
|
154
|
+
});
|
|
155
|
+
})]
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function getStatusCodeNumber(statusCode) {
|
|
159
|
+
const code = Number(statusCode);
|
|
160
|
+
return Number.isNaN(code) ? null : code;
|
|
161
|
+
}
|
|
162
|
+
function isSuccessStatusCode(statusCode) {
|
|
163
|
+
const code = getStatusCodeNumber(statusCode);
|
|
164
|
+
return code !== null && code >= 200 && code < 300;
|
|
165
|
+
}
|
|
166
|
+
function getSuccessResponses(responses) {
|
|
167
|
+
return responses.filter((response) => isSuccessStatusCode(response.statusCode));
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region ../../internals/shared/src/adapter.ts
|
|
171
|
+
/**
|
|
172
|
+
* Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
|
|
173
|
+
* so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
|
|
174
|
+
*
|
|
175
|
+
* Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
|
|
176
|
+
* clear, actionable error at the point of use.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* const { dateType } = getOasAdapter(ctx.adapter).options
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
function getOasAdapter(adapter) {
|
|
184
|
+
if (adapter.name !== "oas") throw new Error(`Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}". Configure \`adapter: adapterOas()\` in your Kubb config.`);
|
|
185
|
+
return adapter;
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
15
188
|
//#region ../../internals/utils/src/casing.ts
|
|
16
189
|
/**
|
|
17
190
|
* Shared implementation for camelCase and PascalCase conversion.
|
|
@@ -356,106 +529,6 @@ function toFilePath(name, caseLast = camelCase) {
|
|
|
356
529
|
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
|
|
357
530
|
}
|
|
358
531
|
//#endregion
|
|
359
|
-
//#region ../../internals/shared/src/params.ts
|
|
360
|
-
const caseParamsCache = /* @__PURE__ */ new WeakMap();
|
|
361
|
-
/**
|
|
362
|
-
* Applies camelCase to parameter names and returns a new array without mutating the input.
|
|
363
|
-
*
|
|
364
|
-
* Run it before handing parameters to schema builders so output property keys get the right casing
|
|
365
|
-
* while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
|
|
366
|
-
* original array is returned unchanged. Results are cached per input array.
|
|
367
|
-
*/
|
|
368
|
-
function caseParams(params, casing) {
|
|
369
|
-
if (!casing) return params;
|
|
370
|
-
const cached = caseParamsCache.get(params);
|
|
371
|
-
if (cached) return cached;
|
|
372
|
-
const result = params.map((param) => ({
|
|
373
|
-
...param,
|
|
374
|
-
name: camelCase(param.name)
|
|
375
|
-
}));
|
|
376
|
-
caseParamsCache.set(params, result);
|
|
377
|
-
return result;
|
|
378
|
-
}
|
|
379
|
-
//#endregion
|
|
380
|
-
//#region ../../internals/shared/src/operation.ts
|
|
381
|
-
/**
|
|
382
|
-
* Maps a content type to the PascalCase suffix used to name per-content-type variants
|
|
383
|
-
* (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
|
|
384
|
-
*/
|
|
385
|
-
function getContentTypeSuffix(contentType) {
|
|
386
|
-
const baseType = contentType.split(";")[0].trim();
|
|
387
|
-
if (baseType === "application/json") return "Json";
|
|
388
|
-
if (baseType === "multipart/form-data") return "FormData";
|
|
389
|
-
if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
|
|
390
|
-
const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
391
|
-
if (parts.length === 0) return "Unknown";
|
|
392
|
-
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
|
|
396
|
-
* (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
|
|
397
|
-
*/
|
|
398
|
-
function getPerContentTypeName(baseName, suffix) {
|
|
399
|
-
if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
|
|
400
|
-
return baseName + suffix;
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Resolves per-content-type variant names for a set of content entries, deduplicating suffix
|
|
404
|
-
* collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
|
|
405
|
-
* the final (possibly counter-augmented) value, so callers can derive parallel names in another
|
|
406
|
-
* namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
|
|
407
|
-
*/
|
|
408
|
-
function resolveContentTypeVariants(entries, baseName) {
|
|
409
|
-
const usedNames = /* @__PURE__ */ new Set();
|
|
410
|
-
return entries.filter((entry) => entry.schema).map((entry) => {
|
|
411
|
-
const baseSuffix = getContentTypeSuffix(entry.contentType);
|
|
412
|
-
let suffix = baseSuffix;
|
|
413
|
-
let name = getPerContentTypeName(baseName, suffix);
|
|
414
|
-
let counter = 2;
|
|
415
|
-
while (usedNames.has(name)) {
|
|
416
|
-
suffix = `${baseSuffix}${counter++}`;
|
|
417
|
-
name = getPerContentTypeName(baseName, suffix);
|
|
418
|
-
}
|
|
419
|
-
usedNames.add(name);
|
|
420
|
-
return {
|
|
421
|
-
name,
|
|
422
|
-
suffix,
|
|
423
|
-
schema: entry.schema,
|
|
424
|
-
keysToOmit: entry.keysToOmit,
|
|
425
|
-
contentType: entry.contentType
|
|
426
|
-
};
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
function getStatusCodeNumber(statusCode) {
|
|
430
|
-
const code = Number(statusCode);
|
|
431
|
-
return Number.isNaN(code) ? null : code;
|
|
432
|
-
}
|
|
433
|
-
function isSuccessStatusCode(statusCode) {
|
|
434
|
-
const code = getStatusCodeNumber(statusCode);
|
|
435
|
-
return code !== null && code >= 200 && code < 300;
|
|
436
|
-
}
|
|
437
|
-
function getSuccessResponses(responses) {
|
|
438
|
-
return responses.filter((response) => isSuccessStatusCode(response.statusCode));
|
|
439
|
-
}
|
|
440
|
-
//#endregion
|
|
441
|
-
//#region ../../internals/shared/src/adapter.ts
|
|
442
|
-
/**
|
|
443
|
-
* Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
|
|
444
|
-
* so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
|
|
445
|
-
*
|
|
446
|
-
* Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
|
|
447
|
-
* clear, actionable error at the point of use.
|
|
448
|
-
*
|
|
449
|
-
* @example
|
|
450
|
-
* ```ts
|
|
451
|
-
* const { dateType } = getOasAdapter(ctx.adapter).options
|
|
452
|
-
* ```
|
|
453
|
-
*/
|
|
454
|
-
function getOasAdapter(adapter) {
|
|
455
|
-
if (adapter.name !== "oas") throw new Error(`Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}". Configure \`adapter: adapterOas()\` in your Kubb config.`);
|
|
456
|
-
return adapter;
|
|
457
|
-
}
|
|
458
|
-
//#endregion
|
|
459
532
|
//#region ../../internals/shared/src/resolver.ts
|
|
460
533
|
/**
|
|
461
534
|
* Resolves a single operation parameter name with the
|
|
@@ -468,6 +541,29 @@ function operationParamName(node, param) {
|
|
|
468
541
|
return this.name(`${node.operationId} ${param.in} ${param.name}`);
|
|
469
542
|
}
|
|
470
543
|
/**
|
|
544
|
+
* Builds the shared `param` namespace. Spread the result into `createResolver`
|
|
545
|
+
* and override individual methods next to it when a plugin deviates.
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```ts
|
|
549
|
+
* createResolver<PluginTs>({ param: createOperationParamResolver(), ... })
|
|
550
|
+
* ```
|
|
551
|
+
*/
|
|
552
|
+
function createOperationParamResolver() {
|
|
553
|
+
return {
|
|
554
|
+
name: operationParamName,
|
|
555
|
+
path(node) {
|
|
556
|
+
return this.name(`${node.operationId} Path`);
|
|
557
|
+
},
|
|
558
|
+
query(node) {
|
|
559
|
+
return this.name(`${node.operationId} Query`);
|
|
560
|
+
},
|
|
561
|
+
headers(node) {
|
|
562
|
+
return this.name(`${node.operationId} Headers`);
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
471
567
|
* Builds the shared `response` namespace. Spread the result into
|
|
472
568
|
* `createResolver` and add plugin-specific methods (`options`, `error`) next
|
|
473
569
|
* to it.
|
|
@@ -515,7 +611,7 @@ function createCasedFile(caseLast) {
|
|
|
515
611
|
* `resolver.imports` would resolve file paths that are then discarded.
|
|
516
612
|
*/
|
|
517
613
|
function collectRefNames(schema) {
|
|
518
|
-
return kubb_kit.ast.
|
|
614
|
+
return kubb_kit.ast.collectSync(schema, { schema: (node) => {
|
|
519
615
|
const refNode = kubb_kit.ast.narrowSchema(node, "ref");
|
|
520
616
|
if (!refNode?.ref) return null;
|
|
521
617
|
return kubb_kit.ast.resolveRefName(refNode);
|
|
@@ -553,6 +649,45 @@ function createGroupConfig(group) {
|
|
|
553
649
|
};
|
|
554
650
|
}
|
|
555
651
|
//#endregion
|
|
652
|
+
//#region ../../internals/shared/src/schemaTraversal.ts
|
|
653
|
+
/**
|
|
654
|
+
* Maps each property of an object schema to its transformed output. Pairs every result with the
|
|
655
|
+
* original property so the printer keeps full control over modifiers, getters, and key syntax.
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```ts
|
|
659
|
+
* const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
|
|
660
|
+
* // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
|
|
661
|
+
* ```
|
|
662
|
+
*/
|
|
663
|
+
function mapSchemaProperties(node, transform) {
|
|
664
|
+
return node.properties.map((property) => ({
|
|
665
|
+
name: property.name,
|
|
666
|
+
property,
|
|
667
|
+
output: transform(property.schema)
|
|
668
|
+
}));
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Maps each member of a union or intersection schema to its transformed output, pairing every
|
|
672
|
+
* result with the original member.
|
|
673
|
+
*/
|
|
674
|
+
function mapSchemaMembers(node, transform) {
|
|
675
|
+
return (node.members ?? []).map((schema) => ({
|
|
676
|
+
schema,
|
|
677
|
+
output: transform(schema)
|
|
678
|
+
}));
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Maps each item of an array or tuple schema to its transformed output, pairing every result with
|
|
682
|
+
* the original item.
|
|
683
|
+
*/
|
|
684
|
+
function mapSchemaItems(node, transform) {
|
|
685
|
+
return (node.items ?? []).map((schema) => ({
|
|
686
|
+
schema,
|
|
687
|
+
output: transform(schema)
|
|
688
|
+
}));
|
|
689
|
+
}
|
|
690
|
+
//#endregion
|
|
556
691
|
//#region src/components/Zod.tsx
|
|
557
692
|
function Zod({ name, node, printer, inferTypeName, cyclic }) {
|
|
558
693
|
const output = printer.print(node);
|
|
@@ -636,12 +771,12 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
|
|
|
636
771
|
if (hasCodec(node)) return true;
|
|
637
772
|
if (node.type === "ref") {
|
|
638
773
|
if (!node.ref) return false;
|
|
639
|
-
const refName = kubb_kit.
|
|
774
|
+
const refName = (0, kubb_kit.extractRefName)(node.ref);
|
|
640
775
|
if (refName) {
|
|
641
776
|
if (seen.has(refName)) return false;
|
|
642
777
|
seen.add(refName);
|
|
643
778
|
}
|
|
644
|
-
const resolved = kubb_kit.
|
|
779
|
+
const resolved = (0, kubb_kit.syncSchemaRef)(node);
|
|
645
780
|
if (resolved.type === "ref") return false;
|
|
646
781
|
return containsCodec(resolved, seen);
|
|
647
782
|
}
|
|
@@ -657,7 +792,7 @@ function containsCodec(node, seen = /* @__PURE__ */ new Set()) {
|
|
|
657
792
|
* them to their input (encode) variant.
|
|
658
793
|
*/
|
|
659
794
|
function collectCodecRefNames(node) {
|
|
660
|
-
return kubb_kit.ast.
|
|
795
|
+
return kubb_kit.ast.collectSync(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? kubb_kit.ast.resolveRefName(n) ?? void 0 : void 0 });
|
|
661
796
|
}
|
|
662
797
|
/**
|
|
663
798
|
* Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
|
|
@@ -680,7 +815,7 @@ function isObjectSchemaNode(node, cyclicSchemas) {
|
|
|
680
815
|
if (node.type === "ref") {
|
|
681
816
|
const refName = kubb_kit.ast.resolveRefName(node);
|
|
682
817
|
if (refName && cyclicSchemas?.has(refName)) return false;
|
|
683
|
-
const resolved = kubb_kit.
|
|
818
|
+
const resolved = (0, kubb_kit.syncSchemaRef)(node);
|
|
684
819
|
return resolved.type === "ref" || isObjectSchemaNode(resolved, cyclicSchemas);
|
|
685
820
|
}
|
|
686
821
|
if (node.type === "union") {
|
|
@@ -756,6 +891,20 @@ function buildEnum(values) {
|
|
|
756
891
|
return `z.union([${literals.join(", ")}])`;
|
|
757
892
|
}
|
|
758
893
|
/**
|
|
894
|
+
* Digit pattern for a `type: 'string'` schema that carries an integer `format`, the way ProtoJSON
|
|
895
|
+
* encodes 64-bit integers. Returns `undefined` for every other format, and a `pattern` from the
|
|
896
|
+
* spec takes precedence over this fallback.
|
|
897
|
+
*
|
|
898
|
+
* @example
|
|
899
|
+
* ```ts
|
|
900
|
+
* integerFormatPattern('uint64') // '^\\d+$'
|
|
901
|
+
* ```
|
|
902
|
+
*/
|
|
903
|
+
function integerFormatPattern(format) {
|
|
904
|
+
if (format === "int32" || format === "int64") return "^-?\\d+$";
|
|
905
|
+
if (format === "uint64") return "^\\d+$";
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
759
908
|
* Map a `regexType` to the `func` argument of `toRegExpString`: `'constructor'` emits
|
|
760
909
|
* `new RegExp(...)`, while `'literal'` (the default) emits a regex literal.
|
|
761
910
|
*/
|
|
@@ -880,6 +1029,22 @@ function applyMiniModifiers({ value, schema, nullable, optional, nullish, defaul
|
|
|
880
1029
|
const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
|
|
881
1030
|
return literal !== null ? `z._default(${withModifier}, ${literal})` : withModifier;
|
|
882
1031
|
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Builds an `object` schema node grouping the given parameter nodes.
|
|
1034
|
+
* The `primitive: 'object'` marker ensures the Zod printer emits `z.object(…)` rather than a record.
|
|
1035
|
+
*/
|
|
1036
|
+
function buildGroupedParamsSchema({ params, optional }) {
|
|
1037
|
+
return kubb_kit.ast.factory.createSchema({
|
|
1038
|
+
type: "object",
|
|
1039
|
+
optional,
|
|
1040
|
+
primitive: "object",
|
|
1041
|
+
properties: params.map((param) => kubb_kit.ast.factory.createProperty({
|
|
1042
|
+
name: param.name,
|
|
1043
|
+
required: param.required,
|
|
1044
|
+
schema: param.schema
|
|
1045
|
+
}))
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
883
1048
|
//#endregion
|
|
884
1049
|
//#region src/printers/printerZod.ts
|
|
885
1050
|
function strictOneOfMember$1(member, node, cyclicSchemas) {
|
|
@@ -888,7 +1053,7 @@ function strictOneOfMember$1(member, node, cyclicSchemas) {
|
|
|
888
1053
|
if (member.startsWith("z.lazy(")) return member;
|
|
889
1054
|
const refName = kubb_kit.ast.resolveRefName(node);
|
|
890
1055
|
if (refName && cyclicSchemas?.has(refName)) return member;
|
|
891
|
-
const schema = kubb_kit.
|
|
1056
|
+
const schema = (0, kubb_kit.syncSchemaRef)(node);
|
|
892
1057
|
if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
|
|
893
1058
|
if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
|
|
894
1059
|
}
|
|
@@ -913,8 +1078,8 @@ function getMemberConstraint({ member, regexType }) {
|
|
|
913
1078
|
function buildZodObjectShape(ctx, node) {
|
|
914
1079
|
const objectNode = kubb_kit.ast.narrowSchema(node, "object");
|
|
915
1080
|
if (!objectNode) return "{}";
|
|
916
|
-
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && kubb_kit.
|
|
917
|
-
return buildObject(
|
|
1081
|
+
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && (0, kubb_kit.containsCircularRef)(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
1082
|
+
return buildObject(mapSchemaProperties(objectNode, (schema) => {
|
|
918
1083
|
const hasSelfRef = isCyclic(schema);
|
|
919
1084
|
const savedCyclicSchemas = ctx.options.cyclicSchemas;
|
|
920
1085
|
if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
|
|
@@ -923,7 +1088,7 @@ function buildZodObjectShape(ctx, node) {
|
|
|
923
1088
|
return baseOutput;
|
|
924
1089
|
}).map(({ name: propName, property, output: baseOutput }) => {
|
|
925
1090
|
const { schema } = property;
|
|
926
|
-
const meta = kubb_kit.
|
|
1091
|
+
const meta = (0, kubb_kit.syncSchemaRef)(schema);
|
|
927
1092
|
const descriptionToApply = schema.type !== "ref" && meta.type === "ref" ? void 0 : meta.description;
|
|
928
1093
|
const value = applyModifiers({
|
|
929
1094
|
value: baseOutput,
|
|
@@ -966,8 +1131,11 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
|
|
|
966
1131
|
boolean: () => "z.boolean()",
|
|
967
1132
|
null: () => "z.null()",
|
|
968
1133
|
string(node) {
|
|
969
|
-
|
|
1134
|
+
const base = shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()";
|
|
1135
|
+
const pattern = node.pattern ?? integerFormatPattern(node.format);
|
|
1136
|
+
return `${base}${lengthConstraints({
|
|
970
1137
|
...node,
|
|
1138
|
+
pattern,
|
|
971
1139
|
regexType: this.options.regexType
|
|
972
1140
|
})}`;
|
|
973
1141
|
},
|
|
@@ -1066,18 +1234,18 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
|
|
|
1066
1234
|
})();
|
|
1067
1235
|
},
|
|
1068
1236
|
array(node) {
|
|
1069
|
-
const base = `z.array(${
|
|
1237
|
+
const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})${lengthConstraints({
|
|
1070
1238
|
...node,
|
|
1071
1239
|
regexType: this.options.regexType
|
|
1072
1240
|
})}`;
|
|
1073
1241
|
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
|
|
1074
1242
|
},
|
|
1075
1243
|
tuple(node) {
|
|
1076
|
-
return `z.tuple(${buildList(
|
|
1244
|
+
return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
|
|
1077
1245
|
},
|
|
1078
1246
|
union(node) {
|
|
1079
1247
|
const nodeMembers = node.members ?? [];
|
|
1080
|
-
const members =
|
|
1248
|
+
const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember$1(output, schema, cyclicSchemaNames) : output).filter(Boolean);
|
|
1081
1249
|
if (members.length === 0) return "";
|
|
1082
1250
|
if (members.length === 1) return members[0];
|
|
1083
1251
|
const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
@@ -1108,7 +1276,7 @@ const printerZod = kubb_kit.ast.createPrinter((options) => {
|
|
|
1108
1276
|
const { keysToOmit } = this.options;
|
|
1109
1277
|
const transformed = this.transform(node);
|
|
1110
1278
|
if (!transformed) return null;
|
|
1111
|
-
const meta = kubb_kit.
|
|
1279
|
+
const meta = (0, kubb_kit.syncSchemaRef)(node);
|
|
1112
1280
|
return applyModifiers({
|
|
1113
1281
|
value: (() => {
|
|
1114
1282
|
if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
|
|
@@ -1153,8 +1321,8 @@ function getMemberConstraintMini({ member, regexType }) {
|
|
|
1153
1321
|
function buildZodMiniObjectShape(ctx, node) {
|
|
1154
1322
|
const objectNode = kubb_kit.ast.narrowSchema(node, "object");
|
|
1155
1323
|
if (!objectNode) return "{}";
|
|
1156
|
-
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && kubb_kit.
|
|
1157
|
-
return buildObject(
|
|
1324
|
+
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && (0, kubb_kit.containsCircularRef)(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
1325
|
+
return buildObject(mapSchemaProperties(objectNode, (schema) => {
|
|
1158
1326
|
const hasSelfRef = isCyclic(schema);
|
|
1159
1327
|
const savedCyclicSchemas = ctx.options.cyclicSchemas;
|
|
1160
1328
|
if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
|
|
@@ -1163,7 +1331,7 @@ function buildZodMiniObjectShape(ctx, node) {
|
|
|
1163
1331
|
return baseOutput;
|
|
1164
1332
|
}).map(({ name: propName, property, output: baseOutput }) => {
|
|
1165
1333
|
const { schema } = property;
|
|
1166
|
-
const meta = kubb_kit.
|
|
1334
|
+
const meta = (0, kubb_kit.syncSchemaRef)(schema);
|
|
1167
1335
|
const value = applyMiniModifiers({
|
|
1168
1336
|
value: baseOutput,
|
|
1169
1337
|
schema,
|
|
@@ -1203,8 +1371,10 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
|
|
|
1203
1371
|
boolean: () => "z.boolean()",
|
|
1204
1372
|
null: () => "z.null()",
|
|
1205
1373
|
string(node) {
|
|
1374
|
+
const pattern = node.pattern ?? integerFormatPattern(node.format);
|
|
1206
1375
|
return `z.string()${lengthChecksMini({
|
|
1207
1376
|
...node,
|
|
1377
|
+
pattern,
|
|
1208
1378
|
regexType: this.options.regexType
|
|
1209
1379
|
})}`;
|
|
1210
1380
|
},
|
|
@@ -1292,18 +1462,18 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
|
|
|
1292
1462
|
return objectBase;
|
|
1293
1463
|
},
|
|
1294
1464
|
array(node) {
|
|
1295
|
-
const base = `z.array(${
|
|
1465
|
+
const base = `z.array(${mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(kubb_kit.ast.factory.createSchema({ type: "unknown" }))})${lengthChecksMini({
|
|
1296
1466
|
...node,
|
|
1297
1467
|
regexType: this.options.regexType
|
|
1298
1468
|
})}`;
|
|
1299
1469
|
return node.unique ? `${base}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })` : base;
|
|
1300
1470
|
},
|
|
1301
1471
|
tuple(node) {
|
|
1302
|
-
return `z.tuple(${buildList(
|
|
1472
|
+
return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
|
|
1303
1473
|
},
|
|
1304
1474
|
union(node) {
|
|
1305
1475
|
const nodeMembers = node.members ?? [];
|
|
1306
|
-
const members =
|
|
1476
|
+
const members = mapSchemaMembers(node, (memberNode) => this.transform(memberNode)).map(({ schema, output }) => output && node.strategy === "one" ? strictOneOfMember(output, schema) : output).filter(Boolean);
|
|
1307
1477
|
if (members.length === 0) return "";
|
|
1308
1478
|
if (members.length === 1) return members[0];
|
|
1309
1479
|
const allDiscriminable = nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemaNames));
|
|
@@ -1334,7 +1504,7 @@ const printerZodMini = kubb_kit.ast.createPrinter((options) => {
|
|
|
1334
1504
|
const { keysToOmit } = this.options;
|
|
1335
1505
|
const transformed = this.transform(node);
|
|
1336
1506
|
if (!transformed) return null;
|
|
1337
|
-
const meta = kubb_kit.
|
|
1507
|
+
const meta = (0, kubb_kit.syncSchemaRef)(node);
|
|
1338
1508
|
return applyMiniModifiers({
|
|
1339
1509
|
value: (() => {
|
|
1340
1510
|
if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
|
|
@@ -1531,7 +1701,6 @@ const zodGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1531
1701
|
const { output, coercion, guidType, regexType, mini, inferred, importPath, group, printer } = ctx.options;
|
|
1532
1702
|
const dateType = getOasAdapter(adapter).options.dateType;
|
|
1533
1703
|
const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
|
|
1534
|
-
const params = caseParams(node.parameters, "camelcase");
|
|
1535
1704
|
const meta = { file: resolver.file({
|
|
1536
1705
|
name: node.operationId,
|
|
1537
1706
|
extname: ".ts",
|
|
@@ -1637,7 +1806,7 @@ const zodGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1637
1806
|
name
|
|
1638
1807
|
});
|
|
1639
1808
|
}
|
|
1640
|
-
const paramSchemas =
|
|
1809
|
+
const paramSchemas = node.parameters.map((param) => renderSchemaEntry({
|
|
1641
1810
|
schema: param.schema,
|
|
1642
1811
|
name: resolver.param.name(node, param),
|
|
1643
1812
|
direction: "input"
|
|
@@ -1686,6 +1855,30 @@ const zodGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1686
1855
|
description: node.requestBody.description ?? schema.description
|
|
1687
1856
|
}), "input");
|
|
1688
1857
|
})();
|
|
1858
|
+
const { path, query, header } = getOperationParameters(node);
|
|
1859
|
+
const paramGroupSchemas = inferred ? [
|
|
1860
|
+
{
|
|
1861
|
+
kind: "path",
|
|
1862
|
+
params: path
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
kind: "query",
|
|
1866
|
+
params: query
|
|
1867
|
+
},
|
|
1868
|
+
{
|
|
1869
|
+
kind: "headers",
|
|
1870
|
+
params: header
|
|
1871
|
+
}
|
|
1872
|
+
].filter(({ params }) => params.length > 0).map(({ kind, params }) => renderSchemaEntry({
|
|
1873
|
+
schema: buildGroupedParamsSchema({ params }),
|
|
1874
|
+
name: resolver.param[kind](node, params[0]),
|
|
1875
|
+
direction: "input"
|
|
1876
|
+
})) : [];
|
|
1877
|
+
const optionsSchema = inferred ? renderSchemaEntry({
|
|
1878
|
+
schema: buildOptionsSchema(node, resolver),
|
|
1879
|
+
name: resolver.name(`${node.operationId} Options`),
|
|
1880
|
+
direction: "input"
|
|
1881
|
+
}) : null;
|
|
1689
1882
|
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
|
|
1690
1883
|
baseName: meta.file.baseName,
|
|
1691
1884
|
path: meta.file.path,
|
|
@@ -1716,7 +1909,9 @@ const zodGenerator = (0, kubb_kit.defineGenerator)({
|
|
|
1716
1909
|
responseSchemas,
|
|
1717
1910
|
responseUnionSchema,
|
|
1718
1911
|
errorUnionSchema,
|
|
1719
|
-
requestSchema
|
|
1912
|
+
requestSchema,
|
|
1913
|
+
paramGroupSchemas,
|
|
1914
|
+
optionsSchema
|
|
1720
1915
|
]
|
|
1721
1916
|
});
|
|
1722
1917
|
}
|
|
@@ -1758,22 +1953,14 @@ const resolverZod = (0, kubb_kit.createResolver)({
|
|
|
1758
1953
|
return this.schema.typeName(`${name} input`);
|
|
1759
1954
|
}
|
|
1760
1955
|
},
|
|
1761
|
-
param:
|
|
1762
|
-
name: operationParamName,
|
|
1763
|
-
path(node, param) {
|
|
1764
|
-
return this.param.name(node, param);
|
|
1765
|
-
},
|
|
1766
|
-
query(node, param) {
|
|
1767
|
-
return this.param.name(node, param);
|
|
1768
|
-
},
|
|
1769
|
-
headers(node, param) {
|
|
1770
|
-
return this.param.name(node, param);
|
|
1771
|
-
}
|
|
1772
|
-
},
|
|
1956
|
+
param: createOperationParamResolver(),
|
|
1773
1957
|
response: {
|
|
1774
1958
|
...createOperationResponseResolver(),
|
|
1775
1959
|
error(node) {
|
|
1776
1960
|
return this.name(`${node.operationId} Error`);
|
|
1961
|
+
},
|
|
1962
|
+
options(node) {
|
|
1963
|
+
return this.schema.type(this.name(`${node.operationId} Options`));
|
|
1777
1964
|
}
|
|
1778
1965
|
}
|
|
1779
1966
|
});
|