@kubb/plugin-zod 5.0.0-beta.74 → 5.0.0-beta.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -11,8 +11,8 @@ var __name = (target, value) => __defProp(target, "name", {
11
11
  //#endregion
12
12
  let _kubb_core = require("@kubb/core");
13
13
  let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
14
- let _kubb_ast_utils = require("@kubb/ast/utils");
15
14
  let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
15
+ let _kubb_ast_utils = require("@kubb/ast/utils");
16
16
  //#region ../../internals/utils/src/casing.ts
17
17
  /**
18
18
  * Shared implementation for camelCase and PascalCase conversion.
@@ -313,80 +313,6 @@ function createGroupConfig(group) {
313
313
  };
314
314
  }
315
315
  //#endregion
316
- //#region src/components/Operations.tsx
317
- function Operations({ name, operations }) {
318
- const operationsJSON = operations.reduce((prev, acc) => {
319
- prev[`"${acc.node.operationId}"`] = acc.data;
320
- return prev;
321
- }, {});
322
- const pathsJSON = operations.reduce((prev, acc) => {
323
- if (!_kubb_core.ast.isHttpOperationNode(acc.node)) return prev;
324
- prev[`"${acc.node.path}"`] = {
325
- ...prev[`"${acc.node.path}"`] ?? {},
326
- [acc.node.method]: `operations["${acc.node.operationId}"]`
327
- };
328
- return prev;
329
- }, {});
330
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [
331
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
332
- name: "OperationSchema",
333
- isExportable: true,
334
- isIndexable: true,
335
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Type, {
336
- name: "OperationSchema",
337
- export: true,
338
- children: `{
339
- readonly request: z.ZodTypeAny | undefined;
340
- readonly parameters: {
341
- readonly path: z.ZodTypeAny | undefined;
342
- readonly query: z.ZodTypeAny | undefined;
343
- readonly header: z.ZodTypeAny | undefined;
344
- };
345
- readonly responses: {
346
- readonly [status: number]: z.ZodTypeAny;
347
- readonly default: z.ZodTypeAny;
348
- };
349
- readonly errors: {
350
- readonly [status: number]: z.ZodTypeAny;
351
- };
352
- }`
353
- })
354
- }),
355
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
356
- name: "OperationsMap",
357
- isExportable: true,
358
- isIndexable: true,
359
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Type, {
360
- name: "OperationsMap",
361
- export: true,
362
- children: "Record<string, OperationSchema>"
363
- })
364
- }),
365
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
366
- name,
367
- isExportable: true,
368
- isIndexable: true,
369
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
370
- export: true,
371
- name,
372
- asConst: true,
373
- children: `{${(0, _kubb_ast_utils.stringifyObject)(operationsJSON)}}`
374
- })
375
- }),
376
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
377
- name: "paths",
378
- isExportable: true,
379
- isIndexable: true,
380
- children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
381
- export: true,
382
- name: "paths",
383
- asConst: true,
384
- children: `{${(0, _kubb_ast_utils.stringifyObject)(pathsJSON)}}`
385
- })
386
- })
387
- ] });
388
- }
389
- //#endregion
390
316
  //#region src/components/Zod.tsx
391
317
  function Zod({ name, node, printer, inferTypeName }) {
392
318
  const output = printer.print(node);
@@ -492,36 +418,6 @@ function collectCodecRefNames(node) {
492
418
  return _kubb_core.ast.collect(node, { schema: (n) => n.type === "ref" && n.ref && containsCodec(n) ? (0, _kubb_ast_utils.extractRefName)(n.ref) ?? void 0 : void 0 });
493
419
  }
494
420
  /**
495
- * Collects all resolved schema names for an operation's parameters and responses
496
- * into a single lookup object, useful for building imports and type references.
497
- */
498
- function buildSchemaNames(node, { params, resolver }) {
499
- const pathParam = params.find((p) => p.in === "path");
500
- const queryParam = params.find((p) => p.in === "query");
501
- const headerParam = params.find((p) => p.in === "header");
502
- const responses = {};
503
- const errors = {};
504
- for (const res of node.responses) {
505
- const name = resolver.resolveResponseStatusName(node, res.statusCode);
506
- const statusNum = Number(res.statusCode);
507
- if (!Number.isNaN(statusNum)) {
508
- responses[statusNum] = name;
509
- if (statusNum >= 400) errors[statusNum] = name;
510
- }
511
- }
512
- responses["default"] = resolver.resolveResponseName(node);
513
- return {
514
- request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : null,
515
- parameters: {
516
- path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : null,
517
- query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : null,
518
- header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : null
519
- },
520
- responses,
521
- errors
522
- };
523
- }
524
- /**
525
421
  * Format a default value as a code-level literal.
526
422
  * Objects become `{}`, primitives become their string representation, strings are quoted.
527
423
  */
@@ -546,6 +442,28 @@ function regexFunc(regexType) {
546
442
  return regexType === "constructor" ? "RegExp" : null;
547
443
  }
548
444
  /**
445
+ * Builds a Zod record key schema that enforces the `patternProperties` regexes a plain
446
+ * `.catchall()` would drop. Several patterns combine into one alternation (`(^a)|(^b)`).
447
+ */
448
+ function patternKeySchema({ patterns, regexType }) {
449
+ return `z.string().regex(${patternKeySource({
450
+ patterns,
451
+ regexType
452
+ })})`;
453
+ }
454
+ /**
455
+ * `zod/mini` variant of {@link patternKeySchema}, wrapping the regex in `.check(z.regex(...))`.
456
+ */
457
+ function patternKeySchemaMini({ patterns, regexType }) {
458
+ return `z.string().check(z.regex(${patternKeySource({
459
+ patterns,
460
+ regexType
461
+ })}))`;
462
+ }
463
+ function patternKeySource({ patterns, regexType }) {
464
+ return (0, _kubb_ast_utils.toRegExpString)(patterns.length === 1 ? patterns[0] : patterns.map((pattern) => `(${pattern})`).join("|"), regexFunc(regexType));
465
+ }
466
+ /**
549
467
  * Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
550
468
  * using the standard chainable Zod v4 API.
551
469
  */
@@ -735,7 +653,7 @@ const printerZod = _kubb_core.ast.createPrinter((options) => {
735
653
  },
736
654
  object(node) {
737
655
  const isCyclic = (schema) => this.options.cyclicSchemas != null && (0, _kubb_ast_utils.containsCircularRef)(schema, { circularSchemas: this.options.cyclicSchemas });
738
- const objectBase = `z.object(${(0, _kubb_ast_utils.buildObject)((0, _kubb_ast_utils.mapSchemaProperties)(node, (schema) => {
656
+ const entries = (0, _kubb_ast_utils.mapSchemaProperties)(node, (schema) => {
739
657
  const hasSelfRef = isCyclic(schema);
740
658
  const savedCyclicSchemas = this.options.cyclicSchemas;
741
659
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
@@ -763,14 +681,29 @@ const printerZod = _kubb_core.ast.createPrinter((options) => {
763
681
  name: propName,
764
682
  body: value
765
683
  }) : `${(0, _kubb_ast_utils.objectKey)(propName)}: ${value}`;
766
- }))})`;
684
+ });
685
+ const objectBase = `z.object(${(0, _kubb_ast_utils.buildObject)(entries)})`;
767
686
  return (() => {
687
+ const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
768
688
  if (node.additionalProperties && node.additionalProperties !== true) {
769
689
  const catchallType = this.transform(node.additionalProperties);
770
690
  return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
771
691
  }
772
692
  if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }))})`;
773
- if (node.additionalProperties === false) return `${objectBase}.strict()`;
693
+ if (node.additionalProperties === false && patterns.length === 0) return `${objectBase}.strict()`;
694
+ if (patterns.length > 0) {
695
+ const values = patterns.map(([, valueSchema]) => {
696
+ const valueType = this.transform(valueSchema) ?? this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }));
697
+ return valueSchema.nullable ? `${valueType}.nullable()` : valueType;
698
+ });
699
+ const distinct = [...new Set(values)];
700
+ const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
701
+ if (entries.length > 0) return `${objectBase}.catchall(${value})`;
702
+ return `z.record(${patternKeySchema({
703
+ patterns: patterns.map(([pattern]) => pattern),
704
+ regexType: this.options.regexType
705
+ })}, ${value})`;
706
+ }
774
707
  return objectBase;
775
708
  })();
776
709
  },
@@ -938,7 +871,7 @@ const printerZodMini = _kubb_core.ast.createPrinter((options) => {
938
871
  },
939
872
  object(node) {
940
873
  const isCyclic = (schema) => this.options.cyclicSchemas != null && (0, _kubb_ast_utils.containsCircularRef)(schema, { circularSchemas: this.options.cyclicSchemas });
941
- return `z.object(${(0, _kubb_ast_utils.buildObject)((0, _kubb_ast_utils.mapSchemaProperties)(node, (schema) => {
874
+ const entries = (0, _kubb_ast_utils.mapSchemaProperties)(node, (schema) => {
942
875
  const hasSelfRef = isCyclic(schema);
943
876
  const savedCyclicSchemas = this.options.cyclicSchemas;
944
877
  if (hasSelfRef) this.options.cyclicSchemas = void 0;
@@ -962,7 +895,29 @@ const printerZodMini = _kubb_core.ast.createPrinter((options) => {
962
895
  name: propName,
963
896
  body: value
964
897
  }) : `${(0, _kubb_ast_utils.objectKey)(propName)}: ${value}`;
965
- }))})`;
898
+ });
899
+ const objectBase = `z.object(${(0, _kubb_ast_utils.buildObject)(entries)})`;
900
+ const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
901
+ if (node.additionalProperties && node.additionalProperties !== true) {
902
+ const catchallType = this.transform(node.additionalProperties);
903
+ return catchallType ? `z.catchall(${objectBase}, ${catchallType})` : objectBase;
904
+ }
905
+ if (node.additionalProperties === true) return `z.catchall(${objectBase}, ${this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }))})`;
906
+ if (node.additionalProperties === false && patterns.length === 0) return objectBase.replace(/^z\.object\(/, "z.strictObject(");
907
+ if (patterns.length > 0) {
908
+ const values = patterns.map(([, valueSchema]) => {
909
+ const valueType = this.transform(valueSchema) ?? this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }));
910
+ return valueSchema.nullable ? `z.nullable(${valueType})` : valueType;
911
+ });
912
+ const distinct = [...new Set(values)];
913
+ const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
914
+ if (entries.length > 0) return `z.catchall(${objectBase}, ${value})`;
915
+ return `z.record(${patternKeySchemaMini({
916
+ patterns: patterns.map(([pattern]) => pattern),
917
+ regexType: this.options.regexType
918
+ })}, ${value})`;
919
+ }
920
+ return objectBase;
966
921
  },
967
922
  array(node) {
968
923
  const base = `z.array(${(0, _kubb_ast_utils.mapSchemaItems)(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean).join(", ") || this.transform(_kubb_core.ast.factory.createSchema({ type: "unknown" }))})${lengthChecksMini({
@@ -1341,6 +1296,25 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1341
1296
  name: responseUnionName
1342
1297
  });
1343
1298
  })() : null;
1299
+ const errorResponsesWithSchema = responsesWithSchema.filter((res) => !isSuccessStatusCode(res.statusCode));
1300
+ const errorUnionSchema = errorResponsesWithSchema.length > 0 ? (() => {
1301
+ const errorUnionName = resolver.resolveErrorName(node);
1302
+ if (new Set(errorResponsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? adapter.getImports(entry.schema, (schemaName) => ({
1303
+ name: resolver.resolveSchemaName(schemaName),
1304
+ path: ""
1305
+ })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : []))).has(errorUnionName)) return null;
1306
+ const members = errorResponsesWithSchema.map((res) => _kubb_core.ast.factory.createSchema({
1307
+ type: "ref",
1308
+ name: resolver.resolveResponseStatusName(node, res.statusCode)
1309
+ }));
1310
+ return renderSchemaEntry({
1311
+ schema: members.length === 1 ? members[0] : _kubb_core.ast.factory.createSchema({
1312
+ type: "union",
1313
+ members
1314
+ }),
1315
+ name: errorUnionName
1316
+ });
1317
+ })() : null;
1344
1318
  const requestBodyContent = node.requestBody?.content ?? [];
1345
1319
  const requestSchema = (() => {
1346
1320
  if (requestBodyContent.length === 0) return null;
@@ -1391,88 +1365,10 @@ const zodGenerator = (0, _kubb_core.defineGenerator)({
1391
1365
  paramSchemas,
1392
1366
  responseSchemas,
1393
1367
  responseUnionSchema,
1368
+ errorUnionSchema,
1394
1369
  requestSchema
1395
1370
  ]
1396
1371
  });
1397
- },
1398
- operations(nodes, ctx) {
1399
- const { config, resolver, root } = ctx;
1400
- const { output, importPath, group, operations } = ctx.options;
1401
- if (!operations) return;
1402
- const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
1403
- const meta = { file: resolver.resolveFile({
1404
- name: "operations",
1405
- extname: ".ts"
1406
- }, {
1407
- root,
1408
- output,
1409
- group: group ?? void 0
1410
- }) };
1411
- const transformedOperations = nodes.filter(_kubb_core.ast.isHttpOperationNode).map((node) => {
1412
- return {
1413
- node,
1414
- data: buildSchemaNames(node, {
1415
- params: caseParams(node.parameters, "camelcase"),
1416
- resolver
1417
- })
1418
- };
1419
- });
1420
- const imports = transformedOperations.flatMap(({ node, data }) => {
1421
- const names = [
1422
- data.request,
1423
- ...Object.values(data.responses),
1424
- ...Object.values(data.parameters)
1425
- ].filter(Boolean);
1426
- const opFile = resolver.resolveFile({
1427
- name: node.operationId,
1428
- extname: ".ts",
1429
- tag: node.tags[0] ?? "default",
1430
- path: node.path
1431
- }, {
1432
- root,
1433
- output,
1434
- group: group ?? void 0
1435
- });
1436
- return names.map((name) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1437
- name: [name],
1438
- root: meta.file.path,
1439
- path: opFile.path
1440
- }, [name, opFile.path].join("-")));
1441
- });
1442
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
1443
- baseName: meta.file.baseName,
1444
- path: meta.file.path,
1445
- meta: meta.file.meta,
1446
- banner: resolver.resolveBanner(ctx.meta, {
1447
- output,
1448
- config,
1449
- file: {
1450
- path: meta.file.path,
1451
- baseName: meta.file.baseName
1452
- }
1453
- }),
1454
- footer: resolver.resolveFooter(ctx.meta, {
1455
- output,
1456
- config,
1457
- file: {
1458
- path: meta.file.path,
1459
- baseName: meta.file.baseName
1460
- }
1461
- }),
1462
- children: [
1463
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1464
- isTypeOnly: true,
1465
- name: isZodImport ? "z" : ["z"],
1466
- path: importPath,
1467
- isNameSpace: isZodImport
1468
- }),
1469
- imports,
1470
- /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Operations, {
1471
- name: "operations",
1472
- operations: transformedOperations
1473
- })
1474
- ]
1475
- });
1476
1372
  }
1477
1373
  });
1478
1374
  //#endregion
@@ -1533,6 +1429,9 @@ const resolverZod = (0, _kubb_core.defineResolver)(() => {
1533
1429
  resolveResponseName(node) {
1534
1430
  return this.resolveSchemaName(`${node.operationId} Response`);
1535
1431
  },
1432
+ resolveErrorName(node) {
1433
+ return this.resolveSchemaName(`${node.operationId} Error`);
1434
+ },
1536
1435
  resolvePathParamsName(node, param) {
1537
1436
  return this.resolveParamName(node, param);
1538
1437
  },
@@ -1581,7 +1480,7 @@ const pluginZod = (0, _kubb_core.definePlugin)((options) => {
1581
1480
  const { output = {
1582
1481
  path: "zod",
1583
1482
  barrel: { type: "named" }
1584
- }, group, exclude = [], include, override = [], typed = false, operations = false, mini = false, guidType = "uuid", regexType = "literal", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, wrapOutput = void 0, printer, resolver: userResolver, macros: userMacros } = options;
1483
+ }, group, exclude = [], include, override = [], typed = false, mini = false, guidType = "uuid", regexType = "literal", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, wrapOutput = void 0, printer, resolver: userResolver, macros: userMacros } = options;
1585
1484
  const groupConfig = createGroupConfig(group);
1586
1485
  return {
1587
1486
  name: pluginZodName,
@@ -1596,7 +1495,6 @@ const pluginZod = (0, _kubb_core.definePlugin)((options) => {
1596
1495
  typed,
1597
1496
  importPath,
1598
1497
  coercion,
1599
- operations,
1600
1498
  inferred,
1601
1499
  guidType,
1602
1500
  regexType,