@kubb/ast 5.0.0-beta.19 → 5.0.0-beta.20

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
@@ -288,6 +288,46 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
288
288
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
289
289
  }
290
290
  //#endregion
291
+ //#region ../../internals/utils/src/promise.ts
292
+ /**
293
+ * Wraps `factory` with a keyed cache backed by the provided store.
294
+ *
295
+ * Pass a `WeakMap` for object keys (results are GC-eligible when the key is
296
+ * collected) or a `Map` for primitive keys. For multi-argument functions,
297
+ * nest two `memoize` calls — the outer keyed by the first argument, the
298
+ * inner (created once per outer miss) keyed by the second.
299
+ *
300
+ * Because the cache is owned by the caller, it can be shared, inspected, or
301
+ * cleared independently of the memoized function.
302
+ *
303
+ * @example Single WeakMap key
304
+ * ```ts
305
+ * const cache = new WeakMap<SchemaNode, Set<string>>()
306
+ * const getRefs = memoize(cache, (node) => collectRefs(node))
307
+ * ```
308
+ *
309
+ * @example Single Map key (primitive)
310
+ * ```ts
311
+ * const cache = new Map<string, Resolver>()
312
+ * const getResolver = memoize(cache, (name) => buildResolver(name))
313
+ * ```
314
+ *
315
+ * @example Two-level (object + primitive)
316
+ * ```ts
317
+ * const outer = new WeakMap<Params[], Map<string, Params[]>>()
318
+ * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))
319
+ * fn(params)('camelcase')
320
+ * ```
321
+ */
322
+ function memoize(store, factory) {
323
+ return (key) => {
324
+ if (store.has(key)) return store.get(key);
325
+ const value = factory(key);
326
+ store.set(key, value);
327
+ return value;
328
+ };
329
+ }
330
+ //#endregion
291
331
  //#region ../../internals/utils/src/reserved.ts
292
332
  /**
293
333
  * JavaScript and Java reserved words.
@@ -534,35 +574,39 @@ function createLimit(concurrency) {
534
574
  * ```
535
575
  */
536
576
  function* getChildren(node, recurse) {
537
- switch (node.kind) {
538
- case "Input":
539
- yield* node.schemas;
540
- yield* node.operations;
541
- break;
542
- case "Output": break;
543
- case "Operation":
544
- yield* node.parameters;
545
- if (node.requestBody?.content) {
546
- for (const c of node.requestBody.content) if (c.schema) yield c.schema;
547
- }
548
- yield* node.responses;
549
- break;
550
- case "Schema":
551
- if (!recurse) break;
552
- if ("properties" in node && node.properties.length > 0) yield* node.properties;
553
- if ("items" in node && node.items) yield* node.items;
554
- if ("members" in node && node.members) yield* node.members;
555
- if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) yield node.additionalProperties;
556
- break;
557
- case "Property":
558
- yield node.schema;
559
- break;
560
- case "Parameter":
561
- yield node.schema;
562
- break;
563
- case "Response":
564
- if (node.schema) yield node.schema;
565
- break;
577
+ if (node.kind === "Input") {
578
+ yield* node.schemas;
579
+ yield* node.operations;
580
+ return;
581
+ }
582
+ if (node.kind === "Output") return;
583
+ if (node.kind === "Operation") {
584
+ yield* node.parameters;
585
+ if (node.requestBody?.content) {
586
+ for (const c of node.requestBody.content) if (c.schema) yield c.schema;
587
+ }
588
+ yield* node.responses;
589
+ return;
590
+ }
591
+ if (node.kind === "Schema") {
592
+ if (!recurse) return;
593
+ if ("properties" in node && node.properties.length > 0) yield* node.properties;
594
+ if ("items" in node && node.items) yield* node.items;
595
+ if ("members" in node && node.members) yield* node.members;
596
+ if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) yield node.additionalProperties;
597
+ return;
598
+ }
599
+ if (node.kind === "Property") {
600
+ yield node.schema;
601
+ return;
602
+ }
603
+ if (node.kind === "Parameter") {
604
+ yield node.schema;
605
+ return;
606
+ }
607
+ if (node.kind === "Response") {
608
+ if (node.schema) yield node.schema;
609
+ return;
566
610
  }
567
611
  }
568
612
  /**
@@ -611,9 +655,6 @@ async function _walk(node, visitor, recurse, limit, parent) {
611
655
  case "Response":
612
656
  await limit(() => visitor.response?.(node, { parent }));
613
657
  break;
614
- case "FunctionParameter":
615
- case "ParameterGroup":
616
- case "FunctionParameters": break;
617
658
  }
618
659
  const children = getChildren(node, recurse);
619
660
  for (const child of children) await _walk(child, visitor, recurse, limit, node);
@@ -621,96 +662,90 @@ async function _walk(node, visitor, recurse, limit, parent) {
621
662
  function transform(node, options) {
622
663
  const { depth, parent, ...visitor } = options;
623
664
  const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
624
- switch (node.kind) {
625
- case "Input": {
626
- const input = visitor.input?.(node, { parent }) ?? node;
627
- return {
628
- ...input,
629
- schemas: input.schemas.map((s) => transform(s, {
630
- ...options,
631
- parent: input
632
- })),
633
- operations: input.operations.map((op) => transform(op, {
634
- ...options,
635
- parent: input
636
- }))
637
- };
638
- }
639
- case "Output": return visitor.output?.(node, { parent }) ?? node;
640
- case "Operation": {
641
- const op = visitor.operation?.(node, { parent }) ?? node;
642
- return {
643
- ...op,
644
- parameters: op.parameters.map((p) => transform(p, {
645
- ...options,
646
- parent: op
647
- })),
648
- requestBody: op.requestBody ? {
649
- ...op.requestBody,
650
- content: op.requestBody.content?.map((c) => ({
651
- ...c,
652
- schema: c.schema ? transform(c.schema, {
653
- ...options,
654
- parent: op
655
- }) : void 0
656
- }))
657
- } : void 0,
658
- responses: op.responses.map((r) => transform(r, {
659
- ...options,
660
- parent: op
665
+ if (node.kind === "Input") {
666
+ const input = visitor.input?.(node, { parent }) ?? node;
667
+ return {
668
+ ...input,
669
+ schemas: input.schemas.map((s) => transform(s, {
670
+ ...options,
671
+ parent: input
672
+ })),
673
+ operations: input.operations.map((op) => transform(op, {
674
+ ...options,
675
+ parent: input
676
+ }))
677
+ };
678
+ }
679
+ if (node.kind === "Output") return visitor.output?.(node, { parent }) ?? node;
680
+ if (node.kind === "Operation") {
681
+ const op = visitor.operation?.(node, { parent }) ?? node;
682
+ return {
683
+ ...op,
684
+ parameters: op.parameters.map((p) => transform(p, {
685
+ ...options,
686
+ parent: op
687
+ })),
688
+ requestBody: op.requestBody ? {
689
+ ...op.requestBody,
690
+ content: op.requestBody.content?.map((c) => ({
691
+ ...c,
692
+ schema: c.schema ? transform(c.schema, {
693
+ ...options,
694
+ parent: op
695
+ }) : void 0
661
696
  }))
662
- };
663
- }
664
- case "Schema": {
665
- const schema = visitor.schema?.(node, { parent }) ?? node;
666
- const childOptions = {
697
+ } : void 0,
698
+ responses: op.responses.map((r) => transform(r, {
667
699
  ...options,
668
- parent: schema
669
- };
670
- return {
671
- ...schema,
672
- ..."properties" in schema && recurse ? { properties: schema.properties.map((p) => transform(p, childOptions)) } : {},
673
- ..."items" in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {},
674
- ..."members" in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {},
675
- ..."additionalProperties" in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true ? { additionalProperties: transform(schema.additionalProperties, childOptions) } : {}
676
- };
677
- }
678
- case "Property": {
679
- const prop = visitor.property?.(node, { parent }) ?? node;
680
- return createProperty({
681
- ...prop,
682
- schema: transform(prop.schema, {
683
- ...options,
684
- parent: prop
685
- })
686
- });
687
- }
688
- case "Parameter": {
689
- const param = visitor.parameter?.(node, { parent }) ?? node;
690
- return createParameter({
691
- ...param,
692
- schema: transform(param.schema, {
693
- ...options,
694
- parent: param
695
- })
696
- });
697
- }
698
- case "Response": {
699
- const response = visitor.response?.(node, { parent }) ?? node;
700
- return {
701
- ...response,
702
- schema: transform(response.schema, {
703
- ...options,
704
- parent: response
705
- })
706
- };
707
- }
708
- case "FunctionParameter":
709
- case "ParameterGroup":
710
- case "FunctionParameters":
711
- case "Type": return node;
712
- default: return node;
700
+ parent: op
701
+ }))
702
+ };
703
+ }
704
+ if (node.kind === "Schema") {
705
+ const schema = visitor.schema?.(node, { parent }) ?? node;
706
+ const childOptions = {
707
+ ...options,
708
+ parent: schema
709
+ };
710
+ return {
711
+ ...schema,
712
+ ..."properties" in schema && recurse ? { properties: schema.properties.map((p) => transform(p, childOptions)) } : {},
713
+ ..."items" in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {},
714
+ ..."members" in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {},
715
+ ..."additionalProperties" in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true ? { additionalProperties: transform(schema.additionalProperties, childOptions) } : {}
716
+ };
717
+ }
718
+ if (node.kind === "Property") {
719
+ const prop = visitor.property?.(node, { parent }) ?? node;
720
+ return createProperty({
721
+ ...prop,
722
+ schema: transform(prop.schema, {
723
+ ...options,
724
+ parent: prop
725
+ })
726
+ });
713
727
  }
728
+ if (node.kind === "Parameter") {
729
+ const param = visitor.parameter?.(node, { parent }) ?? node;
730
+ return createParameter({
731
+ ...param,
732
+ schema: transform(param.schema, {
733
+ ...options,
734
+ parent: param
735
+ })
736
+ });
737
+ }
738
+ if (node.kind === "Response") {
739
+ const response = visitor.response?.(node, { parent }) ?? node;
740
+ return {
741
+ ...response,
742
+ schema: transform(response.schema, {
743
+ ...options,
744
+ parent: response
745
+ })
746
+ };
747
+ }
748
+ return node;
714
749
  }
715
750
  /**
716
751
  * Runs a depth-first synchronous collection pass.
@@ -758,9 +793,6 @@ function* collectLazy(node, options) {
758
793
  case "Response":
759
794
  v = visitor.response?.(node, { parent });
760
795
  break;
761
- case "FunctionParameter":
762
- case "ParameterGroup":
763
- case "FunctionParameters": break;
764
796
  }
765
797
  if (v !== void 0) yield v;
766
798
  for (const child of getChildren(node, recurse)) yield* collectLazy(child, {
@@ -823,25 +855,16 @@ function isStringType(node) {
823
855
  * the desired casing while preserving `OperationNode.parameters` for other consumers.
824
856
  * The input array is not mutated. When `casing` is not set, the original array is returned unchanged.
825
857
  */
826
- const caseParamsCache = /* @__PURE__ */ new WeakMap();
858
+ const caseParamsMemo = memoize(/* @__PURE__ */ new WeakMap(), (params) => memoize(/* @__PURE__ */ new Map(), (casing) => params.map((param) => {
859
+ const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
860
+ return {
861
+ ...param,
862
+ name: transformed
863
+ };
864
+ })));
827
865
  function caseParams(params, casing) {
828
866
  if (!casing) return params;
829
- let byParams = caseParamsCache.get(params);
830
- if (!byParams) {
831
- byParams = /* @__PURE__ */ new Map();
832
- caseParamsCache.set(params, byParams);
833
- }
834
- const cached = byParams.get(casing);
835
- if (cached) return cached;
836
- const result = params.map((param) => {
837
- const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
838
- return {
839
- ...param,
840
- name: transformed
841
- };
842
- });
843
- byParams.set(casing, result);
844
- return result;
867
+ return caseParamsMemo(params)(casing);
845
868
  }
846
869
  /**
847
870
  * Creates a single-property object schema used as a discriminator literal.
@@ -1279,10 +1302,7 @@ function resolveRefName(node) {
1279
1302
  * }
1280
1303
  * ```
1281
1304
  */
1282
- const schemaRefCache = /* @__PURE__ */ new WeakMap();
1283
- function collectSchemaRefs(node) {
1284
- const cached = schemaRefCache.get(node);
1285
- if (cached) return cached;
1305
+ const collectSchemaRefs = memoize(/* @__PURE__ */ new WeakMap(), (node) => {
1286
1306
  const refs = /* @__PURE__ */ new Set();
1287
1307
  collect(node, { schema(child) {
1288
1308
  if (child.type === "ref") {
@@ -1290,9 +1310,8 @@ function collectSchemaRefs(node) {
1290
1310
  if (name) refs.add(name);
1291
1311
  }
1292
1312
  } });
1293
- schemaRefCache.set(node, refs);
1294
1313
  return refs;
1295
- }
1314
+ });
1296
1315
  function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
1297
1316
  if (!node) return out;
1298
1317
  for (const name of collectSchemaRefs(node)) out.add(name);
@@ -1311,10 +1330,10 @@ function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
1311
1330
  *
1312
1331
  * @example Only generate schemas referenced by included operations
1313
1332
  * ```ts
1314
- * const includedOps = inputNode.operations.filter(op => resolver.resolveOptions(op, { options, include }) !== null)
1315
- * const allowed = collectUsedSchemaNames(includedOps, inputNode.schemas)
1333
+ * const includedOps = operations.filter(op => resolver.resolveOptions(op, { options, include }) !== null)
1334
+ * const allowed = collectUsedSchemaNames(includedOps, schemas)
1316
1335
  *
1317
- * for (const schema of inputNode.schemas) {
1336
+ * for (const schema of schemas) {
1318
1337
  * if (schema.name && !allowed.has(schema.name)) continue
1319
1338
  * // … generate schema
1320
1339
  * }
@@ -1322,19 +1341,12 @@ function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
1322
1341
  *
1323
1342
  * @example Check whether a specific schema is needed
1324
1343
  * ```ts
1325
- * const allowed = collectUsedSchemaNames(includedOps, inputNode.schemas)
1344
+ * const allowed = collectUsedSchemaNames(includedOps, schemas)
1326
1345
  * allowed.has('OrderStatus') // false when no included operation references OrderStatus
1327
1346
  * ```
1328
1347
  */
1329
- const usedSchemaNamesCache = /* @__PURE__ */ new WeakMap();
1330
- function collectUsedSchemaNames(operations, schemas) {
1331
- let byOps = usedSchemaNamesCache.get(operations);
1332
- if (!byOps) {
1333
- byOps = /* @__PURE__ */ new WeakMap();
1334
- usedSchemaNamesCache.set(operations, byOps);
1335
- }
1336
- const cached = byOps.get(schemas);
1337
- if (cached) return cached;
1348
+ const collectUsedSchemaNamesMemo = memoize(/* @__PURE__ */ new WeakMap(), (ops) => memoize(/* @__PURE__ */ new WeakMap(), (schemas) => computeUsedSchemaNames(ops, schemas)));
1349
+ function computeUsedSchemaNames(operations, schemas) {
1338
1350
  const schemaMap = /* @__PURE__ */ new Map();
1339
1351
  for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
1340
1352
  const result = /* @__PURE__ */ new Set();
@@ -1350,24 +1362,13 @@ function collectUsedSchemaNames(operations, schemas) {
1350
1362
  depth: "shallow",
1351
1363
  schema: (node) => node
1352
1364
  })) visitSchema(schema);
1353
- byOps.set(schemas, result);
1354
1365
  return result;
1355
1366
  }
1367
+ function collectUsedSchemaNames(operations, schemas) {
1368
+ return collectUsedSchemaNamesMemo(operations)(schemas);
1369
+ }
1356
1370
  const EMPTY_CIRCULAR_SET = /* @__PURE__ */ new Set();
1357
- const circularSchemaCache = /* @__PURE__ */ new WeakMap();
1358
- /**
1359
- * Identifies all schemas that participate in circular dependency chains, including direct self-loops.
1360
- *
1361
- * Returns a Set of schema names with circular dependencies. Use this to wrap recursive schema positions
1362
- * in deferred constructs (lazy getter, `z.lazy(() => …)`) to prevent infinite recursion when generated code runs.
1363
- * Refs are followed by name only, keeping the algorithm linear in the schema graph size.
1364
- *
1365
- * @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
1366
- */
1367
- function findCircularSchemas(schemas) {
1368
- if (schemas.length === 0) return EMPTY_CIRCULAR_SET;
1369
- const cached = circularSchemaCache.get(schemas);
1370
- if (cached) return cached;
1371
+ const findCircularSchemasMemo = memoize(/* @__PURE__ */ new WeakMap(), (schemas) => {
1371
1372
  const graph = /* @__PURE__ */ new Map();
1372
1373
  for (const schema of schemas) {
1373
1374
  if (!schema.name) continue;
@@ -1389,8 +1390,20 @@ function findCircularSchemas(schemas) {
1389
1390
  if (next) for (const r of next) stack.push(r);
1390
1391
  }
1391
1392
  }
1392
- circularSchemaCache.set(schemas, circular);
1393
1393
  return circular;
1394
+ });
1395
+ /**
1396
+ * Identifies all schemas that participate in circular dependency chains, including direct self-loops.
1397
+ *
1398
+ * Returns a Set of schema names with circular dependencies. Use this to wrap recursive schema positions
1399
+ * in deferred constructs (lazy getter, `z.lazy(() => …)`) to prevent infinite recursion when generated code runs.
1400
+ * Refs are followed by name only, keeping the algorithm linear in the schema graph size.
1401
+ *
1402
+ * @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
1403
+ */
1404
+ function findCircularSchemas(schemas) {
1405
+ if (schemas.length === 0) return EMPTY_CIRCULAR_SET;
1406
+ return findCircularSchemasMemo(schemas);
1394
1407
  }
1395
1408
  /**
1396
1409
  * Type guard returning `true` when a schema or anything nested within it contains a ref to a circular schema.
@@ -1444,6 +1457,10 @@ function createInput(overrides = {}) {
1444
1457
  return {
1445
1458
  schemas: [],
1446
1459
  operations: [],
1460
+ meta: {
1461
+ circularNames: [],
1462
+ enumNames: []
1463
+ },
1447
1464
  ...overrides,
1448
1465
  kind: "Input"
1449
1466
  };