@kubb/ast 5.18.7 → 5.19.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.d.cts CHANGED
@@ -2414,9 +2414,15 @@ type CollectOptions<T> = VisitorOf<T> & TraversalOptions;
2414
2414
  * })
2415
2415
  * ```
2416
2416
  */
2417
- declare function findSchema$1(node: Node, { predicate, followRefs }: {
2417
+ declare function findSchema$1(node: Node, { predicate, followRefs, cache }: {
2418
2418
  predicate: (schema: SchemaNode) => boolean;
2419
2419
  followRefs?: boolean;
2420
+ /**
2421
+ * Remembers the match below each schema (`null` for none), so later calls skip the parts of the graph they share.
2422
+ * Keep one cache per `predicate` and `followRefs` value, for example one per build.
2423
+ * In a cyclic graph, a cached call can return a different match than the first one in depth-first order.
2424
+ */
2425
+ cache?: WeakMap<SchemaNode, SchemaNode | null>;
2420
2426
  }): SchemaNode | undefined;
2421
2427
  /**
2422
2428
  * Synchronous depth-first transform. Each visitor callback can return a
@@ -2502,7 +2508,8 @@ declare function resolveSchemaProperties$1({ node, propertyName }: {
2502
2508
  * // ['cat', 'dog']
2503
2509
  * ```
2504
2510
  */
2505
- declare function getSchemaLiteralValues$1(node: SchemaNode): ReadonlyArray<string | number | boolean | null>;
2511
+ declare function getSchemaLiteralValues$1(node: SchemaNode): ReadonlyArray<LiteralValue>;
2512
+ type LiteralValue = string | number | boolean | null;
2506
2513
  /**
2507
2514
  * Infers an implicit discriminator: a property every member exposes with a distinct single
2508
2515
  * literal value, the same shape a declared OpenAPI `discriminator` already narrows on.
package/dist/index.d.ts CHANGED
@@ -2414,9 +2414,15 @@ type CollectOptions<T> = VisitorOf<T> & TraversalOptions;
2414
2414
  * })
2415
2415
  * ```
2416
2416
  */
2417
- declare function findSchema$1(node: Node, { predicate, followRefs }: {
2417
+ declare function findSchema$1(node: Node, { predicate, followRefs, cache }: {
2418
2418
  predicate: (schema: SchemaNode) => boolean;
2419
2419
  followRefs?: boolean;
2420
+ /**
2421
+ * Remembers the match below each schema (`null` for none), so later calls skip the parts of the graph they share.
2422
+ * Keep one cache per `predicate` and `followRefs` value, for example one per build.
2423
+ * In a cyclic graph, a cached call can return a different match than the first one in depth-first order.
2424
+ */
2425
+ cache?: WeakMap<SchemaNode, SchemaNode | null>;
2420
2426
  }): SchemaNode | undefined;
2421
2427
  /**
2422
2428
  * Synchronous depth-first transform. Each visitor callback can return a
@@ -2502,7 +2508,8 @@ declare function resolveSchemaProperties$1({ node, propertyName }: {
2502
2508
  * // ['cat', 'dog']
2503
2509
  * ```
2504
2510
  */
2505
- declare function getSchemaLiteralValues$1(node: SchemaNode): ReadonlyArray<string | number | boolean | null>;
2511
+ declare function getSchemaLiteralValues$1(node: SchemaNode): ReadonlyArray<LiteralValue>;
2512
+ type LiteralValue = string | number | boolean | null;
2506
2513
  /**
2507
2514
  * Infers an implicit discriminator: a property every member exposes with a distinct single
2508
2515
  * literal value, the same shape a declared OpenAPI `discriminator` already narrows on.
package/dist/index.js CHANGED
@@ -25,11 +25,37 @@ var __exportAll = (all, flags) => {
25
25
  * `const refs = memo(refCache, node, () => collectRefs(node))`
26
26
  */
27
27
  function memo(store, key, compute) {
28
- if (store.has(key)) return store.get(key);
28
+ const cached = store.get(key);
29
+ if (cached !== void 0 || store.has(key)) return cached;
29
30
  const value = compute();
30
31
  store.set(key, value);
31
32
  return value;
32
33
  }
34
+ function createStore({ weak, max } = {}) {
35
+ if (weak) return /* @__PURE__ */ new WeakMap();
36
+ const entry = /* @__PURE__ */ new Map();
37
+ if (max === void 0) return entry;
38
+ const set = (key, value) => {
39
+ if (entry.size >= max) entry.clear();
40
+ entry.set(key, value);
41
+ };
42
+ if (max !== 1) return {
43
+ has: (key) => entry.has(key),
44
+ get: (key) => entry.get(key),
45
+ set
46
+ };
47
+ const touch = (key) => {
48
+ if (!entry.has(key)) return false;
49
+ set(key, entry.get(key));
50
+ return true;
51
+ };
52
+ return {
53
+ has: touch,
54
+ get: (key) => touch(key) ? entry.get(key) : void 0,
55
+ set
56
+ };
57
+ }
58
+ memo.create = createStore;
33
59
  // src/constants.ts
34
60
  /**
35
61
  * Node kind discriminators used by every AST node (for example `node.kind === nodeKinds.text`).
@@ -300,14 +326,17 @@ function walkSchema(node, through, visit) {
300
326
  * ```
301
327
  */
302
328
  function getSchemaLiteralValues$1(node) {
303
- const values = /* @__PURE__ */ new Set();
304
- walkSchema(node, "union", (current) => {
305
- if (current.type !== "enum") return;
306
- for (const { value } of current.namedEnumValues ?? current.enumValues?.map((value) => ({ value })) ?? []) values.add(value);
329
+ return memo(literalValues, node, () => {
330
+ const values = /* @__PURE__ */ new Set();
331
+ walkSchema(node, "union", (current) => {
332
+ if (current.type !== "enum") return;
333
+ for (const { value } of current.namedEnumValues ?? current.enumValues?.map((value) => ({ value })) ?? []) values.add(value);
334
+ });
335
+ return Object.freeze([...values]);
307
336
  });
308
- return [...values];
309
337
  }
310
338
  __name(getSchemaLiteralValues$1, "getSchemaLiteralValues");
339
+ const literalValues = memo.create({ weak: true });
311
340
  function collectPropertyNames(node) {
312
341
  const names = /* @__PURE__ */ new Set();
313
342
  walkSchema(node, "intersection", (current) => {
@@ -456,24 +485,6 @@ function isNode(value) {
456
485
  function isNodeRecord(value) {
457
486
  return typeof value === "object" && value !== null && !Array.isArray(value);
458
487
  }
459
- /**
460
- * Returns the immediate traversable children of `node`.
461
- *
462
- * @example
463
- * ```ts
464
- * const children = getChildren(operationNode, true)
465
- * // returns parameters, the request body, and responses
466
- * ```
467
- */
468
- function getChildren(node, recurse) {
469
- if (node.kind === nodeKinds$1.schema && !recurse) return [];
470
- const children = [];
471
- visitChildNodes(node, (child) => {
472
- children.push(child);
473
- return false;
474
- });
475
- return children;
476
- }
477
488
  function isNeverPrinted({ node, key }) {
478
489
  if (node.kind !== nodeKinds$1.schema) return false;
479
490
  if (key === "contains" && (node.type === "array" || node.type === "tuple")) return node.contains?.min === 0 && node.contains.max === void 0;
@@ -481,41 +492,63 @@ function isNeverPrinted({ node, key }) {
481
492
  return false;
482
493
  }
483
494
  /**
495
+ * Child keys of a schema walked with `checks: true`: the shape keys, then {@link schemaCheckKeys}.
496
+ */
497
+ const SCHEMA_CHECK_VISITOR_KEYS = [...VISITOR_KEYS.Schema, ...schemaCheckKeys];
498
+ function visitOnce(child, seen, visit) {
499
+ if (seen.first === void 0) {
500
+ seen.first = child;
501
+ return visit(child);
502
+ }
503
+ if (seen.first === child || seen.rest?.has(child)) return false;
504
+ (seen.rest ??= /* @__PURE__ */ new Set()).add(child);
505
+ return visit(child);
506
+ }
507
+ function visitRecord(value, seen, visit) {
508
+ for (const name in value) {
509
+ if (!Object.hasOwn(value, name)) continue;
510
+ const item = value[name];
511
+ if (isNode(item) && visitOnce(item, seen, visit)) return true;
512
+ }
513
+ return false;
514
+ }
515
+ /**
484
516
  * Calls `visit` for each direct child node and stops at the first call that returns `true`.
485
517
  * With `checks: true`, a schema's {@link schemaCheckKeys} fields are walked too, except checks that cannot fail.
486
518
  */
487
519
  function visitChildNodes(node, visit, { checks = false } = {}) {
488
- const keys = checks && node.kind === nodeKinds$1.schema ? [...VISITOR_KEYS[node.kind] ?? [], ...schemaCheckKeys] : VISITOR_KEYS[node.kind];
520
+ const withChecks = checks && node.kind === nodeKinds$1.schema;
521
+ const keys = withChecks ? SCHEMA_CHECK_VISITOR_KEYS : VISITOR_KEYS[node.kind];
489
522
  if (!keys) return false;
490
523
  const record = node;
491
- const seen = /* @__PURE__ */ new Set();
492
- const visitOnce = (child) => !seen.has(child) && (seen.add(child), visit(child));
493
- const visitRecord = (value) => {
494
- for (const name in value) {
495
- if (!Object.hasOwn(value, name)) continue;
496
- const item = value[name];
497
- if (isNode(item) && visitOnce(item)) return true;
498
- }
499
- return false;
500
- };
524
+ let seen;
501
525
  for (const key of keys) {
502
- if (checks && isNeverPrinted({
526
+ const value = record[key];
527
+ if (value === void 0 || value === null) continue;
528
+ if (withChecks && isNeverPrinted({
503
529
  node,
504
530
  key
505
531
  })) continue;
506
- const value = record[key];
532
+ seen ??= {
533
+ first: void 0,
534
+ rest: void 0
535
+ };
507
536
  if (Array.isArray(value)) {
508
- for (const item of value) if (isNode(item) ? visitOnce(item) : isNodeRecord(item) && visitRecord(item)) return true;
537
+ for (const item of value) if (isNode(item) ? visitOnce(item, seen, visit) : isNodeRecord(item) && visitRecord(item, seen, visit)) return true;
509
538
  continue;
510
539
  }
511
540
  if (isNode(value)) {
512
- if (visitOnce(value)) return true;
541
+ if (visitOnce(value, seen, visit)) return true;
513
542
  continue;
514
543
  }
515
- if (isNodeRecord(value) && visitRecord(value)) return true;
544
+ if (isNodeRecord(value) && visitRecord(value, seen, visit)) return true;
516
545
  }
517
546
  return false;
518
547
  }
548
+ const notFound = {
549
+ found: void 0,
550
+ low: Number.POSITIVE_INFINITY
551
+ };
519
552
  /**
520
553
  * Finds the first schema that matches `predicate` in depth-first order. Set `followRefs` to include resolved ref targets.
521
554
  *
@@ -526,28 +559,68 @@ function visitChildNodes(node, visit, { checks = false } = {}) {
526
559
  * })
527
560
  * ```
528
561
  */
529
- function findSchema$1(node, { predicate, followRefs = false }) {
530
- const seen = followRefs ? /* @__PURE__ */ new WeakSet() : void 0;
531
- const walk = (current) => {
532
- if (current.kind === nodeKinds$1.schema) {
533
- if (predicate(current)) return current;
534
- if (seen) {
535
- if (seen.has(current)) return void 0;
536
- seen.add(current);
537
- if (current.type === "ref" && current.schema) {
538
- const found = walk(current.schema);
539
- if (found) return found;
540
- }
541
- }
542
- }
543
- let found;
562
+ function findSchema$1(node, { predicate, followRefs = false, cache }) {
563
+ const state = followRefs ? /* @__PURE__ */ new Map() : void 0;
564
+ const pending = [];
565
+ let order = 0;
566
+ const walkChildren = (current, initial) => {
567
+ let result = initial;
544
568
  visitChildNodes(current, (child) => {
545
- found = walk(child);
546
- return found !== void 0;
569
+ const next = walk(child);
570
+ result = next.found || next.low < result.low ? next : result;
571
+ return next.found !== void 0;
547
572
  });
548
- if (found) return found;
573
+ return result;
574
+ };
575
+ const walkSchema = (current) => {
576
+ const target = state && current.type === "ref" && current.schema ? walk(current.schema) : notFound;
577
+ if (target.found) return target;
578
+ return walkChildren(current, target);
549
579
  };
550
- return walk(node);
580
+ const walk = (current) => {
581
+ if (current.kind !== nodeKinds$1.schema) return walkChildren(current, notFound);
582
+ const cached = cache?.get(current);
583
+ if (cached === null) return notFound;
584
+ if (cached) return {
585
+ found: cached,
586
+ low: Number.POSITIVE_INFINITY
587
+ };
588
+ if (predicate(current)) {
589
+ cache?.set(current, current);
590
+ return {
591
+ found: current,
592
+ low: Number.POSITIVE_INFINITY
593
+ };
594
+ }
595
+ const known = state?.get(current);
596
+ if (known !== void 0) return {
597
+ found: void 0,
598
+ low: known
599
+ };
600
+ const index = order++;
601
+ const start = pending.length;
602
+ state?.set(current, index);
603
+ const result = walkSchema(current);
604
+ if (result.found) {
605
+ cache?.set(current, result.found);
606
+ return result;
607
+ }
608
+ if (!state) {
609
+ cache?.set(current, null);
610
+ return notFound;
611
+ }
612
+ if (result.low < index) {
613
+ state.set(current, result.low);
614
+ pending.push(current);
615
+ return result;
616
+ }
617
+ for (const done of [...pending.splice(start), current]) {
618
+ state.set(done, Number.POSITIVE_INFINITY);
619
+ cache?.set(done, null);
620
+ }
621
+ return notFound;
622
+ };
623
+ return walk(node).found;
551
624
  }
552
625
  __name(findSchema$1, "findSchema");
553
626
  /**
@@ -611,15 +684,16 @@ function transformChildren(node, visitor, recurse) {
611
684
  if (!keys) return node;
612
685
  const record = node;
613
686
  let updates;
614
- const done = /* @__PURE__ */ new Map();
687
+ let done;
615
688
  const transformOnce = (child) => {
689
+ done ??= /* @__PURE__ */ new Map();
616
690
  const next = done.get(child) ?? transformNode(child, visitor, recurse, node);
617
691
  done.set(child, next);
618
692
  return next;
619
693
  };
620
694
  for (const key of keys) {
621
- if (!(key in record)) continue;
622
695
  const value = record[key];
696
+ if (value === void 0 || value === null) continue;
623
697
  if (Array.isArray(value)) {
624
698
  let mapped;
625
699
  for (const [i, item] of value.entries()) {
@@ -676,7 +750,11 @@ __name(collect$1, "collect");
676
750
  function collectInto(node, visitor, recurse, parent, result) {
677
751
  const v = applyVisitor(node, visitor, parent);
678
752
  if (v != null) result.push(v);
679
- for (const child of getChildren(node, recurse)) collectInto(child, visitor, recurse, node, result);
753
+ if (node.kind === nodeKinds$1.schema && !recurse) return;
754
+ visitChildNodes(node, (child) => {
755
+ collectInto(child, visitor, recurse, node, result);
756
+ return false;
757
+ });
680
758
  }
681
759
  // src/utils/refs.ts
682
760
  /**
@@ -685,21 +763,17 @@ function collectInto(node, visitor, recurse, parent, result) {
685
763
  * (RFC 6901), so `a~1b~0c` names the component `a/b~c`.
686
764
  */
687
765
  function refName$1(ref) {
688
- const cached = refNames.get(ref);
689
- if (cached !== void 0) return cached;
690
- const segment = ref.split("/").at(-1) ?? ref;
691
- let decoded = segment;
692
- try {
693
- decoded = decodeURIComponent(segment);
694
- } catch {}
695
- const name = decoded.replaceAll("~1", "/").replaceAll("~0", "~");
696
- if (refNames.size >= memoLimit) refNames.clear();
697
- refNames.set(ref, name);
698
- return name;
766
+ return memo(refNames, ref, () => {
767
+ const segment = ref.split("/").at(-1) ?? ref;
768
+ let decoded = segment;
769
+ try {
770
+ decoded = decodeURIComponent(segment);
771
+ } catch {}
772
+ return decoded.replaceAll("~1", "/").replaceAll("~0", "~");
773
+ });
699
774
  }
700
775
  __name(refName$1, "refName");
701
- const refNames = /* @__PURE__ */ new Map();
702
- const memoLimit = 1e5;
776
+ const refNames = memo.create({ max: 1e5 });
703
777
  /**
704
778
  * Resolves the emitted name of the schema a ref node points at. Prefers `targetName` (set when
705
779
  * the referenced schema was renamed, e.g. to break a collision), then the last segment of `ref`,
@@ -785,11 +859,11 @@ function isBareRef$1(node, keysToOmit) {
785
859
  }
786
860
  __name(isBareRef$1, "isBareRef");
787
861
  // src/utils/schemaGraph.ts
788
- const refEntries = /* @__PURE__ */ new WeakMap();
789
- const checkRefEntries = /* @__PURE__ */ new WeakMap();
790
- const schemaRefs = /* @__PURE__ */ new WeakMap();
791
- const importedRefNames = /* @__PURE__ */ new WeakMap();
792
- const importedCheckRefNames = /* @__PURE__ */ new WeakMap();
862
+ const refEntries = memo.create({ weak: true });
863
+ const checkRefEntries = memo.create({ weak: true });
864
+ const schemaRefs = memo.create({ weak: true });
865
+ const importedRefNames = memo.create({ weak: true });
866
+ const importedCheckRefNames = memo.create({ weak: true });
793
867
  /**
794
868
  * Walks a node's subtree once and lists every ref it holds, in traversal order, as the resolved
795
869
  * name plus whether the ref carries a `$ref` pointer. Memoized per node and per `checks` mode, so
@@ -1011,27 +1085,25 @@ function getBaseURL(server) {
1011
1085
  if (!server || server.variables || server.url.includes("{") || !server.url.includes("://")) return void 0;
1012
1086
  return URL.canParse(server.url) ? server.url : void 0;
1013
1087
  }
1014
- const parametersByNode = /* @__PURE__ */ new WeakMap();
1088
+ const parametersByNode = memo.create({ weak: true });
1015
1089
  /** The operation's parameters by location, with a `querystring` parameter in `query`. A repeated name keeps its first parameter. */
1016
1090
  function groupParameters(node) {
1017
- const cached = parametersByNode.get(node);
1018
- if (cached) return cached;
1019
- const group = (...locations) => {
1020
- const seen = /* @__PURE__ */ new Set();
1021
- return locations.flatMap((location) => node.parameters.filter((param) => {
1022
- if (param.in !== location || seen.has(param.name)) return false;
1023
- seen.add(param.name);
1024
- return true;
1025
- }));
1026
- };
1027
- const groups = {
1028
- path: group("path"),
1029
- query: group("query", "querystring"),
1030
- header: group("header"),
1031
- cookie: group("cookie")
1032
- };
1033
- parametersByNode.set(node, groups);
1034
- return groups;
1091
+ return memo(parametersByNode, node, () => {
1092
+ const group = (...locations) => {
1093
+ const seen = /* @__PURE__ */ new Set();
1094
+ return locations.flatMap((location) => node.parameters.filter((param) => {
1095
+ if (param.in !== location || seen.has(param.name)) return false;
1096
+ seen.add(param.name);
1097
+ return true;
1098
+ }));
1099
+ };
1100
+ return {
1101
+ path: group("path"),
1102
+ query: group("query", "querystring"),
1103
+ header: group("header"),
1104
+ cookie: group("cookie")
1105
+ };
1106
+ });
1035
1107
  }
1036
1108
  // src/nodes/schema.ts
1037
1109
  /**
@@ -1437,12 +1509,11 @@ function combineImports(imports, exports, source) {
1437
1509
  const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
1438
1510
  const identifiers = source && imports.length > INDEX_ABOVE_IMPORTS ? new Set(source.match(IDENTIFIER_RUN)) : null;
1439
1511
  const isUsed = (importName) => !source || identifiers?.has(importName) || source.includes(importName) || exportedNames.has(importName);
1440
- const importNameMemo = /* @__PURE__ */ new Map();
1512
+ const importNameMemo = memo.create();
1441
1513
  const canonicalizeName = (n) => {
1442
1514
  if (typeof n === "string") return n;
1443
1515
  const key = `${n.propertyName}:${n.name ?? ""}`;
1444
- if (!importNameMemo.has(key)) importNameMemo.set(key, n);
1445
- return importNameMemo.get(key);
1516
+ return memo(importNameMemo, key, () => n);
1446
1517
  };
1447
1518
  const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
1448
1519
  for (const node of imports) if (Array.isArray(node.name) && node.name.some((item) => isUsed(importLocalName(item)))) pathsWithUsedNamedImport.add(node.path);