@kubb/ast 5.0.0-beta.31 → 5.0.0-beta.33

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 CHANGED
@@ -120,10 +120,9 @@ function process(node: Node) {
120
120
  ### Refs
121
121
 
122
122
  ```ts
123
- import { buildRefMap, resolveRef } from '@kubb/ast'
123
+ import { extractRefName } from '@kubb/ast'
124
124
 
125
- const refMap = buildRefMap(root)
126
- const pet = resolveRef(refMap, 'Pet')
125
+ extractRefName('#/components/schemas/Pet') // 'Pet'
127
126
  ```
128
127
 
129
128
  ## Supporting Kubb
package/dist/index.cjs CHANGED
@@ -29,25 +29,6 @@ const visitorDepths = {
29
29
  shallow: "shallow",
30
30
  deep: "deep"
31
31
  };
32
- const nodeKinds = {
33
- input: "Input",
34
- output: "Output",
35
- operation: "Operation",
36
- schema: "Schema",
37
- property: "Property",
38
- parameter: "Parameter",
39
- response: "Response",
40
- functionParameter: "FunctionParameter",
41
- parameterGroup: "ParameterGroup",
42
- functionParameters: "FunctionParameters",
43
- type: "Type",
44
- file: "File",
45
- import: "Import",
46
- export: "Export",
47
- source: "Source",
48
- text: "Text",
49
- break: "Break"
50
- };
51
32
  /**
52
33
  * Schema type discriminators used by all AST schema nodes.
53
34
  *
@@ -196,33 +177,6 @@ const httpMethods = {
196
177
  options: "OPTIONS",
197
178
  trace: "TRACE"
198
179
  };
199
- /**
200
- * Common MIME types used in request/response content negotiation.
201
- *
202
- * Covers JSON, XML, form data, PDFs, images, audio, and video formats.
203
- * Use these as keys when serializing request/response bodies.
204
- */
205
- const mediaTypes = {
206
- applicationJson: "application/json",
207
- applicationXml: "application/xml",
208
- applicationFormUrlEncoded: "application/x-www-form-urlencoded",
209
- applicationOctetStream: "application/octet-stream",
210
- applicationPdf: "application/pdf",
211
- applicationZip: "application/zip",
212
- applicationGraphql: "application/graphql",
213
- multipartFormData: "multipart/form-data",
214
- textPlain: "text/plain",
215
- textHtml: "text/html",
216
- textCsv: "text/csv",
217
- textXml: "text/xml",
218
- imagePng: "image/png",
219
- imageJpeg: "image/jpeg",
220
- imageGif: "image/gif",
221
- imageWebp: "image/webp",
222
- imageSvgXml: "image/svg+xml",
223
- audioMpeg: "audio/mpeg",
224
- videoMp4: "video/mp4"
225
- };
226
180
  //#endregion
227
181
  //#region ../../internals/utils/src/casing.ts
228
182
  /**
@@ -465,28 +419,6 @@ function isKind(kind) {
465
419
  return (node) => node.kind === kind;
466
420
  }
467
421
  /**
468
- * Returns `true` when the input is an `InputNode`.
469
- *
470
- * @example
471
- * ```ts
472
- * if (isInputNode(node)) {
473
- * console.log(node.schemas.length)
474
- * }
475
- * ```
476
- */
477
- const isInputNode = isKind("Input");
478
- /**
479
- * Returns `true` when the input is an `OutputNode`.
480
- *
481
- * @example
482
- * ```ts
483
- * if (isOutputNode(node)) {
484
- * console.log(node.files.length)
485
- * }
486
- * ```
487
- */
488
- const isOutputNode = isKind("Output");
489
- /**
490
422
  * Returns `true` when the input is an `OperationNode`.
491
423
  *
492
424
  * @example
@@ -677,8 +609,9 @@ async function walk(node, options) {
677
609
  }
678
610
  async function _walk(node, visitor, recurse, limit, parent) {
679
611
  await limit(() => applyVisitor(node, visitor, parent));
680
- const children = getChildren(node, recurse);
681
- for (const child of children) await _walk(child, visitor, recurse, limit, node);
612
+ const children = Array.from(getChildren(node, recurse));
613
+ if (children.length === 0) return;
614
+ await Promise.all(children.map((child) => _walk(child, visitor, recurse, limit, node)));
682
615
  }
683
616
  function transform(node, options) {
684
617
  const { depth, parent, ...visitor } = options;
@@ -2097,66 +2030,245 @@ function refTargetName(node) {
2097
2030
  if (node.ref) return extractRefName(node.ref);
2098
2031
  return node.name ?? "";
2099
2032
  }
2100
- /**
2101
- * Builds the local, shape-only descriptor for a node: its kind, flags, constraints, and its
2102
- * children's signatures. {@link signatureOf} hashes this string; children contribute their
2103
- * fixed-length signature rather than their own full descriptor, which keeps the result bounded.
2104
- */
2105
- function describeShape(node, signatures) {
2106
- const flags = flagsDescriptor(node);
2107
- switch (node.type) {
2108
- case "object": {
2109
- const props = (node.properties ?? []).map((prop) => `${prop.name}${prop.required ? "!" : "?"}${signatureOf(prop.schema, signatures)}`).join(",");
2110
- let additional = "";
2111
- if (typeof node.additionalProperties === "boolean") additional = `ab:${node.additionalProperties}`;
2112
- else if (node.additionalProperties) additional = `as:${signatureOf(node.additionalProperties, signatures)}`;
2113
- const pattern = node.patternProperties ? Object.keys(node.patternProperties).sort().map((key) => `${key}=${signatureOf(node.patternProperties[key], signatures)}`).join(",") : "";
2114
- return `object|${flags}|p[${props}]|${additional}|pp[${pattern}]|mn:${node.minProperties ?? ""}|mx:${node.maxProperties ?? ""}`;
2033
+ const arrayTupleFields = [
2034
+ {
2035
+ kind: "children",
2036
+ key: "items",
2037
+ prefix: "i"
2038
+ },
2039
+ {
2040
+ kind: "child",
2041
+ key: "rest",
2042
+ prefix: "r"
2043
+ },
2044
+ {
2045
+ kind: "scalar",
2046
+ key: "min",
2047
+ prefix: "mn"
2048
+ },
2049
+ {
2050
+ kind: "scalar",
2051
+ key: "max",
2052
+ prefix: "mx"
2053
+ },
2054
+ {
2055
+ kind: "bool",
2056
+ key: "unique",
2057
+ prefix: "u"
2058
+ }
2059
+ ];
2060
+ const numericFields = [
2061
+ {
2062
+ kind: "scalar",
2063
+ key: "min",
2064
+ prefix: "mn"
2065
+ },
2066
+ {
2067
+ kind: "scalar",
2068
+ key: "max",
2069
+ prefix: "mx"
2070
+ },
2071
+ {
2072
+ kind: "scalar",
2073
+ key: "exclusiveMinimum",
2074
+ prefix: "emn"
2075
+ },
2076
+ {
2077
+ kind: "scalar",
2078
+ key: "exclusiveMaximum",
2079
+ prefix: "emx"
2080
+ },
2081
+ {
2082
+ kind: "scalar",
2083
+ key: "multipleOf",
2084
+ prefix: "mo"
2085
+ }
2086
+ ];
2087
+ const rangeFields = [{
2088
+ kind: "scalar",
2089
+ key: "min",
2090
+ prefix: "mn"
2091
+ }, {
2092
+ kind: "scalar",
2093
+ key: "max",
2094
+ prefix: "mx"
2095
+ }];
2096
+ /**
2097
+ * Maps each schema node `type` to the ordered list of shape-contributing fields.
2098
+ * Node types absent from this map (scalar types like boolean, null, any, etc.) fall
2099
+ * back to `${type}|${flags}` with no additional fields.
2100
+ */
2101
+ const SHAPE_KEYS = {
2102
+ object: [
2103
+ { kind: "objectProps" },
2104
+ { kind: "additionalProps" },
2105
+ { kind: "patternProps" },
2106
+ {
2107
+ kind: "scalar",
2108
+ key: "minProperties",
2109
+ prefix: "mn"
2110
+ },
2111
+ {
2112
+ kind: "scalar",
2113
+ key: "maxProperties",
2114
+ prefix: "mx"
2115
2115
  }
2116
- case "array":
2117
- case "tuple": {
2118
- const items = (node.items ?? []).map((item) => signatureOf(item, signatures)).join(",");
2119
- const rest = node.rest ? signatureOf(node.rest, signatures) : "";
2120
- return `${node.type}|${flags}|i[${items}]|r:${rest}|mn:${node.min ?? ""}|mx:${node.max ?? ""}|u:${node.unique ? 1 : 0}`;
2116
+ ],
2117
+ array: arrayTupleFields,
2118
+ tuple: arrayTupleFields,
2119
+ union: [
2120
+ {
2121
+ kind: "scalar",
2122
+ key: "strategy",
2123
+ prefix: "s"
2124
+ },
2125
+ {
2126
+ kind: "scalar",
2127
+ key: "discriminatorPropertyName",
2128
+ prefix: "d"
2129
+ },
2130
+ {
2131
+ kind: "children",
2132
+ key: "members",
2133
+ prefix: "m"
2121
2134
  }
2122
- case "union": {
2123
- const members = (node.members ?? []).map((member) => signatureOf(member, signatures)).join(",");
2124
- return `union|${flags}|s:${node.strategy ?? ""}|d:${node.discriminatorPropertyName ?? ""}|m[${members}]`;
2135
+ ],
2136
+ intersection: [{
2137
+ kind: "children",
2138
+ key: "members",
2139
+ prefix: "m"
2140
+ }],
2141
+ enum: [{ kind: "enumValues" }],
2142
+ ref: [{ kind: "refTarget" }],
2143
+ string: [
2144
+ {
2145
+ kind: "scalar",
2146
+ key: "min",
2147
+ prefix: "mn"
2148
+ },
2149
+ {
2150
+ kind: "scalar",
2151
+ key: "max",
2152
+ prefix: "mx"
2153
+ },
2154
+ {
2155
+ kind: "scalar",
2156
+ key: "pattern",
2157
+ prefix: "pt"
2158
+ }
2159
+ ],
2160
+ number: numericFields,
2161
+ integer: numericFields,
2162
+ bigint: numericFields,
2163
+ url: [
2164
+ {
2165
+ kind: "scalar",
2166
+ key: "path",
2167
+ prefix: "path"
2168
+ },
2169
+ {
2170
+ kind: "scalar",
2171
+ key: "min",
2172
+ prefix: "mn"
2173
+ },
2174
+ {
2175
+ kind: "scalar",
2176
+ key: "max",
2177
+ prefix: "mx"
2178
+ }
2179
+ ],
2180
+ uuid: rangeFields,
2181
+ email: rangeFields,
2182
+ datetime: [{
2183
+ kind: "bool",
2184
+ key: "offset",
2185
+ prefix: "o"
2186
+ }, {
2187
+ kind: "bool",
2188
+ key: "local",
2189
+ prefix: "l"
2190
+ }],
2191
+ date: [{
2192
+ kind: "scalar",
2193
+ key: "representation",
2194
+ prefix: "rep"
2195
+ }],
2196
+ time: [{
2197
+ kind: "scalar",
2198
+ key: "representation",
2199
+ prefix: "rep"
2200
+ }]
2201
+ };
2202
+ function serializeShapeField(field, node, record) {
2203
+ switch (field.kind) {
2204
+ case "scalar": return `${field.prefix}:${record[field.key] ?? ""}`;
2205
+ case "bool": return `${field.prefix}:${record[field.key] ? 1 : 0}`;
2206
+ case "child": {
2207
+ const child = record[field.key];
2208
+ return `${field.prefix}:${child ? signatureOf(child) : ""}`;
2125
2209
  }
2126
- case "intersection": return `intersection|${flags}|m[${(node.members ?? []).map((member) => signatureOf(member, signatures)).join(",")}]`;
2127
- case "enum": {
2210
+ case "children": {
2211
+ const children = record[field.key] ?? [];
2212
+ return `${field.prefix}[${children.map((c) => signatureOf(c)).join(",")}]`;
2213
+ }
2214
+ case "objectProps": return `p[${(node.properties ?? []).map((prop) => `${prop.name}${prop.required ? "!" : "?"}${signatureOf(prop.schema)}`).join(",")}]`;
2215
+ case "additionalProps": {
2216
+ const obj = node;
2217
+ if (typeof obj.additionalProperties === "boolean") return `ab:${obj.additionalProperties}`;
2218
+ if (obj.additionalProperties) return `as:${signatureOf(obj.additionalProperties)}`;
2219
+ return "";
2220
+ }
2221
+ case "patternProps": {
2222
+ const obj = node;
2223
+ return `pp[${obj.patternProperties ? Object.keys(obj.patternProperties).sort().map((key) => `${key}=${signatureOf(obj.patternProperties[key])}`).join(",") : ""}]`;
2224
+ }
2225
+ case "enumValues": {
2226
+ const en = node;
2128
2227
  let values = "";
2129
- if (node.namedEnumValues?.length) values = node.namedEnumValues.map((entry) => `${entry.name}=${entry.primitive}:${String(entry.value)}`).join(",");
2130
- else if (node.enumValues?.length) values = node.enumValues.map((value) => `${value === null ? "null" : typeof value}:${String(value)}`).join(",");
2131
- return `enum|${flags}|v[${values}]`;
2228
+ if (en.namedEnumValues?.length) values = en.namedEnumValues.map((entry) => `${entry.name}=${entry.primitive}:${String(entry.value)}`).join(",");
2229
+ else if (en.enumValues?.length) values = en.enumValues.map((value) => `${value === null ? "null" : typeof value}:${String(value)}`).join(",");
2230
+ return `v[${values}]`;
2132
2231
  }
2133
- case "ref": return `ref|${flags}|->${refTargetName(node)}`;
2134
- case "string": return `string|${flags}|mn:${node.min ?? ""}|mx:${node.max ?? ""}|pt:${node.pattern ?? ""}`;
2135
- case "number":
2136
- case "integer":
2137
- case "bigint": return `${node.type}|${flags}|mn:${node.min ?? ""}|mx:${node.max ?? ""}|emn:${node.exclusiveMinimum ?? ""}|emx:${node.exclusiveMaximum ?? ""}|mo:${node.multipleOf ?? ""}`;
2138
- case "url": return `url|${flags}|path:${node.path ?? ""}|mn:${node.min ?? ""}|mx:${node.max ?? ""}`;
2139
- case "uuid":
2140
- case "email": return `${node.type}|${flags}|mn:${node.min ?? ""}|mx:${node.max ?? ""}`;
2141
- case "datetime": return `datetime|${flags}|o:${node.offset ? 1 : 0}|l:${node.local ? 1 : 0}`;
2142
- case "date":
2143
- case "time": return `${node.type}|${flags}|rep:${node.representation}`;
2144
- default: return `${node.type}|${flags}`;
2232
+ case "refTarget": return `->${refTargetName(node)}`;
2145
2233
  }
2146
2234
  }
2147
2235
  /**
2236
+ * Builds the local, shape-only descriptor for a node: its kind, flags, constraints, and its
2237
+ * children's signatures. {@link signatureOf} hashes this string; children contribute their
2238
+ * fixed-length signature rather than their own full descriptor, which keeps the result bounded.
2239
+ */
2240
+ function describeShape(node) {
2241
+ const flags = flagsDescriptor(node);
2242
+ const fields = SHAPE_KEYS[node.type];
2243
+ if (!fields) return `${node.type}|${flags}`;
2244
+ const record = node;
2245
+ const parts = [`${node.type}|${flags}`];
2246
+ for (const field of fields) parts.push(serializeShapeField(field, node, record));
2247
+ return parts.join("|");
2248
+ }
2249
+ /**
2250
+ * Persistent hash-consing cache: `SchemaNode` → signature digest, keyed by node identity.
2251
+ *
2252
+ * A `WeakMap` so entries are released once the node is garbage-collected, and so a node hashed
2253
+ * during dedupe planning is not re-hashed when the same tree is rewritten during streaming
2254
+ * (where `schemaSignature` and `applyDedupe` would otherwise each walk it from scratch). Reuse
2255
+ * across calls is sound because a signature depends only on a node's content, and schema nodes
2256
+ * are immutable once created — transforms allocate new objects rather than mutating in place.
2257
+ */
2258
+ const signatureCache = /* @__PURE__ */ new WeakMap();
2259
+ /**
2148
2260
  * Hash-consing: each node's signature is a fixed-length digest of its local shape plus its
2149
2261
  * children's digests (a Merkle hash). Children contribute their 64-char hash instead of their
2150
2262
  * full nested descriptor, so a signature stays bounded regardless of subtree depth, and the
2151
2263
  * digest is identical across calls because it depends only on content — never on traversal
2152
2264
  * order. This keeps the keys built during planning consistent with the ones recomputed later
2153
- * during streaming. `signatures` memoizes node → digest within a single computation.
2265
+ * during streaming. {@link signatureCache} memoizes node → digest across every computation.
2154
2266
  */
2155
- function signatureOf(node, signatures) {
2156
- const cached = signatures.get(node);
2267
+ function signatureOf(node) {
2268
+ const cached = signatureCache.get(node);
2157
2269
  if (cached !== void 0) return cached;
2158
- const signature = (0, node_crypto.createHash)("sha256").update(describeShape(node, signatures)).digest("hex");
2159
- signatures.set(node, signature);
2270
+ const signature = (0, node_crypto.createHash)("sha256").update(describeShape(node)).digest("hex");
2271
+ signatureCache.set(node, signature);
2160
2272
  return signature;
2161
2273
  }
2162
2274
  /**
@@ -2175,18 +2287,7 @@ function signatureOf(node, signatures) {
2175
2287
  * ```
2176
2288
  */
2177
2289
  function schemaSignature(node) {
2178
- return signatureOf(node, /* @__PURE__ */ new Map());
2179
- }
2180
- /**
2181
- * Returns `true` when two schema nodes are structurally identical under shape-only equality.
2182
- *
2183
- * @example
2184
- * ```ts
2185
- * isSchemaEqual(a, b) // a and b produce the same TypeScript type
2186
- * ```
2187
- */
2188
- function isSchemaEqual(a, b) {
2189
- return schemaSignature(a) === schemaSignature(b);
2290
+ return signatureOf(node);
2190
2291
  }
2191
2292
  //#endregion
2192
2293
  //#region src/dedupe.ts
@@ -2211,10 +2312,9 @@ function createRefNode(node, canonical) {
2211
2312
  }
2212
2313
  function applyDedupe(node, canonicalBySignature, skipRootMatch = false) {
2213
2314
  if (canonicalBySignature.size === 0) return node;
2214
- const signatures = /* @__PURE__ */ new Map();
2215
2315
  const root = node;
2216
2316
  return transform(node, { schema(schemaNode) {
2217
- const signature = signatureOf(schemaNode, signatures);
2317
+ const signature = signatureOf(schemaNode);
2218
2318
  if (skipRootMatch && schemaNode === root) return void 0;
2219
2319
  const canonical = canonicalBySignature.get(signature);
2220
2320
  if (!canonical) return void 0;
@@ -2252,11 +2352,10 @@ function cleanDefinition(node, name) {
2252
2352
  */
2253
2353
  function buildDedupePlan(roots, options) {
2254
2354
  const { isCandidate, nameFor, refFor, minOccurrences = 2 } = options;
2255
- const signatures = /* @__PURE__ */ new Map();
2256
2355
  const topLevelNodes = /* @__PURE__ */ new Set();
2257
2356
  const groups = /* @__PURE__ */ new Map();
2258
2357
  function record(schemaNode) {
2259
- const signature = signatureOf(schemaNode, signatures);
2358
+ const signature = signatureOf(schemaNode);
2260
2359
  if (!isCandidate(schemaNode)) return;
2261
2360
  const isTopLevel = topLevelNodes.has(schemaNode) && !!schemaNode.name;
2262
2361
  const group = groups.get(signature);
@@ -2529,9 +2628,6 @@ function* mergeAdjacentObjectsLazy(members) {
2529
2628
  }
2530
2629
  if (acc !== void 0) yield acc;
2531
2630
  }
2532
- function mergeAdjacentObjects(members) {
2533
- return [...mergeAdjacentObjectsLazy(members)];
2534
- }
2535
2631
  /**
2536
2632
  * Removes enum members that are covered by broader scalar primitives in the same union.
2537
2633
  *
@@ -2577,14 +2673,11 @@ exports.caseParams = caseParams;
2577
2673
  exports.childName = childName;
2578
2674
  exports.collect = collect;
2579
2675
  exports.collectImports = collectImports;
2580
- exports.collectLazy = collectLazy;
2581
- exports.collectReferencedSchemaNames = collectReferencedSchemaNames;
2582
2676
  exports.collectUsedSchemaNames = collectUsedSchemaNames;
2583
2677
  exports.containsCircularRef = containsCircularRef;
2584
2678
  exports.createArrowFunction = createArrowFunction;
2585
2679
  exports.createBreak = createBreak;
2586
2680
  exports.createConst = createConst;
2587
- exports.createContent = createContent;
2588
2681
  exports.createDiscriminantNode = createDiscriminantNode;
2589
2682
  exports.createExport = createExport;
2590
2683
  exports.createFile = createFile;
@@ -2602,7 +2695,6 @@ exports.createParameterGroup = createParameterGroup;
2602
2695
  exports.createParamsType = createParamsType;
2603
2696
  exports.createPrinterFactory = createPrinterFactory;
2604
2697
  exports.createProperty = createProperty;
2605
- exports.createRequestBody = createRequestBody;
2606
2698
  exports.createResponse = createResponse;
2607
2699
  exports.createSchema = createSchema;
2608
2700
  exports.createSource = createSource;
@@ -2619,19 +2711,11 @@ exports.findCircularSchemas = findCircularSchemas;
2619
2711
  exports.findDiscriminator = findDiscriminator;
2620
2712
  exports.httpMethods = httpMethods;
2621
2713
  exports.isHttpOperationNode = isHttpOperationNode;
2622
- exports.isInputNode = isInputNode;
2623
2714
  exports.isOperationNode = isOperationNode;
2624
- exports.isOutputNode = isOutputNode;
2625
- exports.isScalarPrimitive = isScalarPrimitive;
2626
- exports.isSchemaEqual = isSchemaEqual;
2627
2715
  exports.isSchemaNode = isSchemaNode;
2628
2716
  exports.isStringType = isStringType;
2629
- exports.mediaTypes = mediaTypes;
2630
- exports.mergeAdjacentObjects = mergeAdjacentObjects;
2631
2717
  exports.mergeAdjacentObjectsLazy = mergeAdjacentObjectsLazy;
2632
2718
  exports.narrowSchema = narrowSchema;
2633
- exports.nodeKinds = nodeKinds;
2634
- exports.resolveRefName = resolveRefName;
2635
2719
  exports.schemaSignature = schemaSignature;
2636
2720
  exports.schemaTypes = schemaTypes;
2637
2721
  exports.setDiscriminatorEnum = setDiscriminatorEnum;