@kubb/ast 5.0.0-beta.31 → 5.0.0-beta.32
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 +227 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +228 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/dedupe.ts +2 -4
- package/src/guards.ts +1 -46
- package/src/refs.ts +0 -49
- package/src/signature.ts +161 -64
- /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -2097,66 +2097,245 @@ function refTargetName(node) {
|
|
|
2097
2097
|
if (node.ref) return extractRefName(node.ref);
|
|
2098
2098
|
return node.name ?? "";
|
|
2099
2099
|
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2100
|
+
const arrayTupleFields = [
|
|
2101
|
+
{
|
|
2102
|
+
kind: "children",
|
|
2103
|
+
key: "items",
|
|
2104
|
+
prefix: "i"
|
|
2105
|
+
},
|
|
2106
|
+
{
|
|
2107
|
+
kind: "child",
|
|
2108
|
+
key: "rest",
|
|
2109
|
+
prefix: "r"
|
|
2110
|
+
},
|
|
2111
|
+
{
|
|
2112
|
+
kind: "scalar",
|
|
2113
|
+
key: "min",
|
|
2114
|
+
prefix: "mn"
|
|
2115
|
+
},
|
|
2116
|
+
{
|
|
2117
|
+
kind: "scalar",
|
|
2118
|
+
key: "max",
|
|
2119
|
+
prefix: "mx"
|
|
2120
|
+
},
|
|
2121
|
+
{
|
|
2122
|
+
kind: "bool",
|
|
2123
|
+
key: "unique",
|
|
2124
|
+
prefix: "u"
|
|
2125
|
+
}
|
|
2126
|
+
];
|
|
2127
|
+
const numericFields = [
|
|
2128
|
+
{
|
|
2129
|
+
kind: "scalar",
|
|
2130
|
+
key: "min",
|
|
2131
|
+
prefix: "mn"
|
|
2132
|
+
},
|
|
2133
|
+
{
|
|
2134
|
+
kind: "scalar",
|
|
2135
|
+
key: "max",
|
|
2136
|
+
prefix: "mx"
|
|
2137
|
+
},
|
|
2138
|
+
{
|
|
2139
|
+
kind: "scalar",
|
|
2140
|
+
key: "exclusiveMinimum",
|
|
2141
|
+
prefix: "emn"
|
|
2142
|
+
},
|
|
2143
|
+
{
|
|
2144
|
+
kind: "scalar",
|
|
2145
|
+
key: "exclusiveMaximum",
|
|
2146
|
+
prefix: "emx"
|
|
2147
|
+
},
|
|
2148
|
+
{
|
|
2149
|
+
kind: "scalar",
|
|
2150
|
+
key: "multipleOf",
|
|
2151
|
+
prefix: "mo"
|
|
2152
|
+
}
|
|
2153
|
+
];
|
|
2154
|
+
const rangeFields = [{
|
|
2155
|
+
kind: "scalar",
|
|
2156
|
+
key: "min",
|
|
2157
|
+
prefix: "mn"
|
|
2158
|
+
}, {
|
|
2159
|
+
kind: "scalar",
|
|
2160
|
+
key: "max",
|
|
2161
|
+
prefix: "mx"
|
|
2162
|
+
}];
|
|
2163
|
+
/**
|
|
2164
|
+
* Maps each schema node `type` to the ordered list of shape-contributing fields.
|
|
2165
|
+
* Node types absent from this map (scalar types like boolean, null, any, etc.) fall
|
|
2166
|
+
* back to `${type}|${flags}` with no additional fields.
|
|
2167
|
+
*/
|
|
2168
|
+
const SHAPE_KEYS = {
|
|
2169
|
+
object: [
|
|
2170
|
+
{ kind: "objectProps" },
|
|
2171
|
+
{ kind: "additionalProps" },
|
|
2172
|
+
{ kind: "patternProps" },
|
|
2173
|
+
{
|
|
2174
|
+
kind: "scalar",
|
|
2175
|
+
key: "minProperties",
|
|
2176
|
+
prefix: "mn"
|
|
2177
|
+
},
|
|
2178
|
+
{
|
|
2179
|
+
kind: "scalar",
|
|
2180
|
+
key: "maxProperties",
|
|
2181
|
+
prefix: "mx"
|
|
2182
|
+
}
|
|
2183
|
+
],
|
|
2184
|
+
array: arrayTupleFields,
|
|
2185
|
+
tuple: arrayTupleFields,
|
|
2186
|
+
union: [
|
|
2187
|
+
{
|
|
2188
|
+
kind: "scalar",
|
|
2189
|
+
key: "strategy",
|
|
2190
|
+
prefix: "s"
|
|
2191
|
+
},
|
|
2192
|
+
{
|
|
2193
|
+
kind: "scalar",
|
|
2194
|
+
key: "discriminatorPropertyName",
|
|
2195
|
+
prefix: "d"
|
|
2196
|
+
},
|
|
2197
|
+
{
|
|
2198
|
+
kind: "children",
|
|
2199
|
+
key: "members",
|
|
2200
|
+
prefix: "m"
|
|
2201
|
+
}
|
|
2202
|
+
],
|
|
2203
|
+
intersection: [{
|
|
2204
|
+
kind: "children",
|
|
2205
|
+
key: "members",
|
|
2206
|
+
prefix: "m"
|
|
2207
|
+
}],
|
|
2208
|
+
enum: [{ kind: "enumValues" }],
|
|
2209
|
+
ref: [{ kind: "refTarget" }],
|
|
2210
|
+
string: [
|
|
2211
|
+
{
|
|
2212
|
+
kind: "scalar",
|
|
2213
|
+
key: "min",
|
|
2214
|
+
prefix: "mn"
|
|
2215
|
+
},
|
|
2216
|
+
{
|
|
2217
|
+
kind: "scalar",
|
|
2218
|
+
key: "max",
|
|
2219
|
+
prefix: "mx"
|
|
2220
|
+
},
|
|
2221
|
+
{
|
|
2222
|
+
kind: "scalar",
|
|
2223
|
+
key: "pattern",
|
|
2224
|
+
prefix: "pt"
|
|
2225
|
+
}
|
|
2226
|
+
],
|
|
2227
|
+
number: numericFields,
|
|
2228
|
+
integer: numericFields,
|
|
2229
|
+
bigint: numericFields,
|
|
2230
|
+
url: [
|
|
2231
|
+
{
|
|
2232
|
+
kind: "scalar",
|
|
2233
|
+
key: "path",
|
|
2234
|
+
prefix: "path"
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
kind: "scalar",
|
|
2238
|
+
key: "min",
|
|
2239
|
+
prefix: "mn"
|
|
2240
|
+
},
|
|
2241
|
+
{
|
|
2242
|
+
kind: "scalar",
|
|
2243
|
+
key: "max",
|
|
2244
|
+
prefix: "mx"
|
|
2115
2245
|
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2246
|
+
],
|
|
2247
|
+
uuid: rangeFields,
|
|
2248
|
+
email: rangeFields,
|
|
2249
|
+
datetime: [{
|
|
2250
|
+
kind: "bool",
|
|
2251
|
+
key: "offset",
|
|
2252
|
+
prefix: "o"
|
|
2253
|
+
}, {
|
|
2254
|
+
kind: "bool",
|
|
2255
|
+
key: "local",
|
|
2256
|
+
prefix: "l"
|
|
2257
|
+
}],
|
|
2258
|
+
date: [{
|
|
2259
|
+
kind: "scalar",
|
|
2260
|
+
key: "representation",
|
|
2261
|
+
prefix: "rep"
|
|
2262
|
+
}],
|
|
2263
|
+
time: [{
|
|
2264
|
+
kind: "scalar",
|
|
2265
|
+
key: "representation",
|
|
2266
|
+
prefix: "rep"
|
|
2267
|
+
}]
|
|
2268
|
+
};
|
|
2269
|
+
function serializeShapeField(field, node, record) {
|
|
2270
|
+
switch (field.kind) {
|
|
2271
|
+
case "scalar": return `${field.prefix}:${record[field.key] ?? ""}`;
|
|
2272
|
+
case "bool": return `${field.prefix}:${record[field.key] ? 1 : 0}`;
|
|
2273
|
+
case "child": {
|
|
2274
|
+
const child = record[field.key];
|
|
2275
|
+
return `${field.prefix}:${child ? signatureOf(child) : ""}`;
|
|
2121
2276
|
}
|
|
2122
|
-
case "
|
|
2123
|
-
const
|
|
2124
|
-
return
|
|
2277
|
+
case "children": {
|
|
2278
|
+
const children = record[field.key] ?? [];
|
|
2279
|
+
return `${field.prefix}[${children.map((c) => signatureOf(c)).join(",")}]`;
|
|
2125
2280
|
}
|
|
2126
|
-
case "
|
|
2127
|
-
case "
|
|
2281
|
+
case "objectProps": return `p[${(node.properties ?? []).map((prop) => `${prop.name}${prop.required ? "!" : "?"}${signatureOf(prop.schema)}`).join(",")}]`;
|
|
2282
|
+
case "additionalProps": {
|
|
2283
|
+
const obj = node;
|
|
2284
|
+
if (typeof obj.additionalProperties === "boolean") return `ab:${obj.additionalProperties}`;
|
|
2285
|
+
if (obj.additionalProperties) return `as:${signatureOf(obj.additionalProperties)}`;
|
|
2286
|
+
return "";
|
|
2287
|
+
}
|
|
2288
|
+
case "patternProps": {
|
|
2289
|
+
const obj = node;
|
|
2290
|
+
return `pp[${obj.patternProperties ? Object.keys(obj.patternProperties).sort().map((key) => `${key}=${signatureOf(obj.patternProperties[key])}`).join(",") : ""}]`;
|
|
2291
|
+
}
|
|
2292
|
+
case "enumValues": {
|
|
2293
|
+
const en = node;
|
|
2128
2294
|
let values = "";
|
|
2129
|
-
if (
|
|
2130
|
-
else if (
|
|
2131
|
-
return `
|
|
2295
|
+
if (en.namedEnumValues?.length) values = en.namedEnumValues.map((entry) => `${entry.name}=${entry.primitive}:${String(entry.value)}`).join(",");
|
|
2296
|
+
else if (en.enumValues?.length) values = en.enumValues.map((value) => `${value === null ? "null" : typeof value}:${String(value)}`).join(",");
|
|
2297
|
+
return `v[${values}]`;
|
|
2132
2298
|
}
|
|
2133
|
-
case "
|
|
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}`;
|
|
2299
|
+
case "refTarget": return `->${refTargetName(node)}`;
|
|
2145
2300
|
}
|
|
2146
2301
|
}
|
|
2147
2302
|
/**
|
|
2303
|
+
* Builds the local, shape-only descriptor for a node: its kind, flags, constraints, and its
|
|
2304
|
+
* children's signatures. {@link signatureOf} hashes this string; children contribute their
|
|
2305
|
+
* fixed-length signature rather than their own full descriptor, which keeps the result bounded.
|
|
2306
|
+
*/
|
|
2307
|
+
function describeShape(node) {
|
|
2308
|
+
const flags = flagsDescriptor(node);
|
|
2309
|
+
const fields = SHAPE_KEYS[node.type];
|
|
2310
|
+
if (!fields) return `${node.type}|${flags}`;
|
|
2311
|
+
const record = node;
|
|
2312
|
+
const parts = [`${node.type}|${flags}`];
|
|
2313
|
+
for (const field of fields) parts.push(serializeShapeField(field, node, record));
|
|
2314
|
+
return parts.join("|");
|
|
2315
|
+
}
|
|
2316
|
+
/**
|
|
2317
|
+
* Persistent hash-consing cache: `SchemaNode` → signature digest, keyed by node identity.
|
|
2318
|
+
*
|
|
2319
|
+
* A `WeakMap` so entries are released once the node is garbage-collected, and so a node hashed
|
|
2320
|
+
* during dedupe planning is not re-hashed when the same tree is rewritten during streaming
|
|
2321
|
+
* (where `schemaSignature` and `applyDedupe` would otherwise each walk it from scratch). Reuse
|
|
2322
|
+
* across calls is sound because a signature depends only on a node's content, and schema nodes
|
|
2323
|
+
* are immutable once created — transforms allocate new objects rather than mutating in place.
|
|
2324
|
+
*/
|
|
2325
|
+
const signatureCache = /* @__PURE__ */ new WeakMap();
|
|
2326
|
+
/**
|
|
2148
2327
|
* Hash-consing: each node's signature is a fixed-length digest of its local shape plus its
|
|
2149
2328
|
* children's digests (a Merkle hash). Children contribute their 64-char hash instead of their
|
|
2150
2329
|
* full nested descriptor, so a signature stays bounded regardless of subtree depth, and the
|
|
2151
2330
|
* digest is identical across calls because it depends only on content — never on traversal
|
|
2152
2331
|
* order. This keeps the keys built during planning consistent with the ones recomputed later
|
|
2153
|
-
* during streaming.
|
|
2332
|
+
* during streaming. {@link signatureCache} memoizes node → digest across every computation.
|
|
2154
2333
|
*/
|
|
2155
|
-
function signatureOf(node
|
|
2156
|
-
const cached =
|
|
2334
|
+
function signatureOf(node) {
|
|
2335
|
+
const cached = signatureCache.get(node);
|
|
2157
2336
|
if (cached !== void 0) return cached;
|
|
2158
|
-
const signature = (0, node_crypto.createHash)("sha256").update(describeShape(node
|
|
2159
|
-
|
|
2337
|
+
const signature = (0, node_crypto.createHash)("sha256").update(describeShape(node)).digest("hex");
|
|
2338
|
+
signatureCache.set(node, signature);
|
|
2160
2339
|
return signature;
|
|
2161
2340
|
}
|
|
2162
2341
|
/**
|
|
@@ -2175,7 +2354,7 @@ function signatureOf(node, signatures) {
|
|
|
2175
2354
|
* ```
|
|
2176
2355
|
*/
|
|
2177
2356
|
function schemaSignature(node) {
|
|
2178
|
-
return signatureOf(node
|
|
2357
|
+
return signatureOf(node);
|
|
2179
2358
|
}
|
|
2180
2359
|
/**
|
|
2181
2360
|
* Returns `true` when two schema nodes are structurally identical under shape-only equality.
|
|
@@ -2211,10 +2390,9 @@ function createRefNode(node, canonical) {
|
|
|
2211
2390
|
}
|
|
2212
2391
|
function applyDedupe(node, canonicalBySignature, skipRootMatch = false) {
|
|
2213
2392
|
if (canonicalBySignature.size === 0) return node;
|
|
2214
|
-
const signatures = /* @__PURE__ */ new Map();
|
|
2215
2393
|
const root = node;
|
|
2216
2394
|
return transform(node, { schema(schemaNode) {
|
|
2217
|
-
const signature = signatureOf(schemaNode
|
|
2395
|
+
const signature = signatureOf(schemaNode);
|
|
2218
2396
|
if (skipRootMatch && schemaNode === root) return void 0;
|
|
2219
2397
|
const canonical = canonicalBySignature.get(signature);
|
|
2220
2398
|
if (!canonical) return void 0;
|
|
@@ -2252,11 +2430,10 @@ function cleanDefinition(node, name) {
|
|
|
2252
2430
|
*/
|
|
2253
2431
|
function buildDedupePlan(roots, options) {
|
|
2254
2432
|
const { isCandidate, nameFor, refFor, minOccurrences = 2 } = options;
|
|
2255
|
-
const signatures = /* @__PURE__ */ new Map();
|
|
2256
2433
|
const topLevelNodes = /* @__PURE__ */ new Set();
|
|
2257
2434
|
const groups = /* @__PURE__ */ new Map();
|
|
2258
2435
|
function record(schemaNode) {
|
|
2259
|
-
const signature = signatureOf(schemaNode
|
|
2436
|
+
const signature = signatureOf(schemaNode);
|
|
2260
2437
|
if (!isCandidate(schemaNode)) return;
|
|
2261
2438
|
const isTopLevel = topLevelNodes.has(schemaNode) && !!schemaNode.name;
|
|
2262
2439
|
const group = groups.get(signature);
|