@kubb/ast 5.0.0-beta.3 → 5.0.0-beta.31
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 +1 -1
- package/dist/index.cjs +694 -331
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1433 -1007
- package/dist/index.js +682 -332
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/dedupe.ts +202 -0
- package/src/dialect.ts +64 -0
- package/src/dispatch.ts +53 -0
- package/src/factory.ts +127 -11
- package/src/guards.ts +18 -3
- package/src/index.ts +11 -3
- package/src/infer.ts +16 -5
- package/src/nodes/base.ts +2 -0
- package/src/nodes/code.ts +21 -21
- package/src/nodes/content.ts +37 -0
- package/src/nodes/file.ts +16 -14
- package/src/nodes/index.ts +7 -3
- package/src/nodes/operation.ts +98 -62
- package/src/nodes/response.ts +21 -14
- package/src/nodes/root.ts +72 -10
- package/src/nodes/schema.ts +9 -3
- package/src/printer.ts +34 -28
- package/src/refs.ts +4 -2
- package/src/resolvers.ts +4 -4
- package/src/signature.ts +135 -0
- package/src/transformers.ts +20 -15
- package/src/types.ts +8 -0
- package/src/utils.ts +109 -68
- package/src/visitor.ts +229 -275
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.
|
|
@@ -415,11 +455,11 @@ function trimExtName(text) {
|
|
|
415
455
|
* @example
|
|
416
456
|
* ```ts
|
|
417
457
|
* const schema = createSchema({ type: 'string' })
|
|
418
|
-
* const stringNode = narrowSchema(schema, 'string') // StringSchemaNode |
|
|
458
|
+
* const stringNode = narrowSchema(schema, 'string') // StringSchemaNode | null
|
|
419
459
|
* ```
|
|
420
460
|
*/
|
|
421
461
|
function narrowSchema(node, type) {
|
|
422
|
-
return node?.type === type ? node :
|
|
462
|
+
return node?.type === type ? node : null;
|
|
423
463
|
}
|
|
424
464
|
function isKind(kind) {
|
|
425
465
|
return (node) => node.kind === kind;
|
|
@@ -458,6 +498,19 @@ const isOutputNode = isKind("Output");
|
|
|
458
498
|
*/
|
|
459
499
|
const isOperationNode = isKind("Operation");
|
|
460
500
|
/**
|
|
501
|
+
* Narrows an `OperationNode` to an `HttpOperationNode`, guaranteeing `method` and `path`.
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* ```ts
|
|
505
|
+
* if (isHttpOperationNode(node)) {
|
|
506
|
+
* console.log(node.method, node.path)
|
|
507
|
+
* }
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
function isHttpOperationNode(node) {
|
|
511
|
+
return node.protocol === "http" || node.method !== void 0 && node.path !== void 0;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
461
514
|
* Returns `true` when the input is a `SchemaNode`.
|
|
462
515
|
*
|
|
463
516
|
* @example
|
|
@@ -468,12 +521,6 @@ const isOperationNode = isKind("Operation");
|
|
|
468
521
|
* ```
|
|
469
522
|
*/
|
|
470
523
|
const isSchemaNode = isKind("Schema");
|
|
471
|
-
isKind("Property");
|
|
472
|
-
isKind("Parameter");
|
|
473
|
-
isKind("Response");
|
|
474
|
-
isKind("FunctionParameter");
|
|
475
|
-
isKind("ParameterGroup");
|
|
476
|
-
isKind("FunctionParameters");
|
|
477
524
|
//#endregion
|
|
478
525
|
//#region src/refs.ts
|
|
479
526
|
/**
|
|
@@ -526,53 +573,92 @@ function createLimit(concurrency) {
|
|
|
526
573
|
});
|
|
527
574
|
};
|
|
528
575
|
}
|
|
576
|
+
const visitorKeysByKind = {
|
|
577
|
+
Input: ["schemas", "operations"],
|
|
578
|
+
Operation: [
|
|
579
|
+
"parameters",
|
|
580
|
+
"requestBody",
|
|
581
|
+
"responses"
|
|
582
|
+
],
|
|
583
|
+
RequestBody: ["content"],
|
|
584
|
+
Content: ["schema"],
|
|
585
|
+
Response: ["content"],
|
|
586
|
+
Schema: [
|
|
587
|
+
"properties",
|
|
588
|
+
"items",
|
|
589
|
+
"members",
|
|
590
|
+
"additionalProperties"
|
|
591
|
+
],
|
|
592
|
+
Property: ["schema"],
|
|
593
|
+
Parameter: ["schema"]
|
|
594
|
+
};
|
|
595
|
+
/**
|
|
596
|
+
* Returns `true` when `value` is an AST node (an object carrying a `kind`).
|
|
597
|
+
*/
|
|
598
|
+
function isNode(value) {
|
|
599
|
+
return typeof value === "object" && value !== null && "kind" in value;
|
|
600
|
+
}
|
|
529
601
|
/**
|
|
530
|
-
* Returns the immediate traversable children of `node
|
|
602
|
+
* Returns the immediate traversable children of `node` based on {@link VISITOR_KEYS}.
|
|
531
603
|
*
|
|
532
|
-
*
|
|
533
|
-
* `additionalProperties`) are only included
|
|
534
|
-
* when `recurse` is `true`; shallow mode skips them.
|
|
604
|
+
* `Schema` children are only included when `recurse` is `true`; shallow mode skips them.
|
|
535
605
|
*
|
|
536
606
|
* @example
|
|
537
607
|
* ```ts
|
|
538
608
|
* const children = getChildren(operationNode, true)
|
|
539
|
-
* // returns parameters,
|
|
540
|
-
* ```
|
|
541
|
-
*/
|
|
542
|
-
function getChildren(node, recurse) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const children = [];
|
|
553
|
-
if (!recurse) return [];
|
|
554
|
-
if ("properties" in node && node.properties.length > 0) children.push(...node.properties);
|
|
555
|
-
if ("items" in node && node.items) children.push(...node.items);
|
|
556
|
-
if ("members" in node && node.members) children.push(...node.members);
|
|
557
|
-
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
558
|
-
return children;
|
|
559
|
-
}
|
|
560
|
-
case "Property": return [node.schema];
|
|
561
|
-
case "Parameter": return [node.schema];
|
|
562
|
-
case "Response": return node.schema ? [node.schema] : [];
|
|
563
|
-
case "FunctionParameter":
|
|
564
|
-
case "ParameterGroup":
|
|
565
|
-
case "FunctionParameters":
|
|
566
|
-
case "Type": return [];
|
|
567
|
-
default: return [];
|
|
609
|
+
* // returns parameters, the request body, and responses
|
|
610
|
+
* ```
|
|
611
|
+
*/
|
|
612
|
+
function* getChildren(node, recurse) {
|
|
613
|
+
if (node.kind === "Schema" && !recurse) return;
|
|
614
|
+
const keys = visitorKeysByKind[node.kind];
|
|
615
|
+
if (!keys) return;
|
|
616
|
+
const record = node;
|
|
617
|
+
for (const key of keys) {
|
|
618
|
+
const value = record[key];
|
|
619
|
+
if (Array.isArray(value)) {
|
|
620
|
+
for (const item of value) if (isNode(item)) yield item;
|
|
621
|
+
} else if (isNode(value)) yield value;
|
|
568
622
|
}
|
|
569
623
|
}
|
|
570
624
|
/**
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
625
|
+
* Maps a node `kind` to the matching visitor callback name. Only the seven
|
|
626
|
+
* traversable node kinds have an entry; every other kind resolves to
|
|
627
|
+
* `undefined` and is skipped.
|
|
628
|
+
*/
|
|
629
|
+
const VISITOR_KEY_BY_KIND = {
|
|
630
|
+
Input: "input",
|
|
631
|
+
Output: "output",
|
|
632
|
+
Operation: "operation",
|
|
633
|
+
Schema: "schema",
|
|
634
|
+
Property: "property",
|
|
635
|
+
Parameter: "parameter",
|
|
636
|
+
Response: "response"
|
|
637
|
+
};
|
|
638
|
+
/**
|
|
639
|
+
* Invokes the visitor callback that matches `node.kind`, passing the traversal
|
|
640
|
+
* context. Returns the callback's result (a replacement node, a collected
|
|
641
|
+
* value, or `undefined` when no callback is registered for the kind).
|
|
574
642
|
*
|
|
575
|
-
*
|
|
643
|
+
* Shared by `walk`, `transform`, and `collectLazy` so node-kind dispatch lives
|
|
644
|
+
* in one place. `TResult` is the caller's expected return: the same node type
|
|
645
|
+
* for `transform`, the collected value type for `collectLazy`, ignored for `walk`.
|
|
646
|
+
*/
|
|
647
|
+
function applyVisitor(node, visitor, parent) {
|
|
648
|
+
const key = VISITOR_KEY_BY_KIND[node.kind];
|
|
649
|
+
if (!key) return void 0;
|
|
650
|
+
const fn = visitor[key];
|
|
651
|
+
return fn?.(node, { parent });
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Async depth-first traversal for side effects. Visitor return values are
|
|
655
|
+
* ignored. Use `transform` when you want to rewrite nodes.
|
|
656
|
+
*
|
|
657
|
+
* Sibling nodes at each depth run concurrently up to `options.concurrency`
|
|
658
|
+
* (defaults to `WALK_CONCURRENCY`). Higher values overlap I/O-bound visitor
|
|
659
|
+
* work; lower values reduce memory pressure.
|
|
660
|
+
*
|
|
661
|
+
* @example Log every operation
|
|
576
662
|
* ```ts
|
|
577
663
|
* await walk(root, {
|
|
578
664
|
* operation(node) {
|
|
@@ -581,213 +667,114 @@ function getChildren(node, recurse) {
|
|
|
581
667
|
* })
|
|
582
668
|
* ```
|
|
583
669
|
*
|
|
584
|
-
* @example
|
|
670
|
+
* @example Only visit the root node
|
|
585
671
|
* ```ts
|
|
586
|
-
*
|
|
587
|
-
* await walk(root, { depth: 'shallow', root: () => {} })
|
|
672
|
+
* await walk(root, { depth: 'shallow', input: () => {} })
|
|
588
673
|
* ```
|
|
589
674
|
*/
|
|
590
675
|
async function walk(node, options) {
|
|
591
676
|
return _walk(node, options, (options.depth ?? visitorDepths.deep) === visitorDepths.deep, createLimit(options.concurrency ?? 30), void 0);
|
|
592
677
|
}
|
|
593
678
|
async function _walk(node, visitor, recurse, limit, parent) {
|
|
594
|
-
|
|
595
|
-
case "Input":
|
|
596
|
-
await limit(() => visitor.input?.(node, { parent }));
|
|
597
|
-
break;
|
|
598
|
-
case "Output":
|
|
599
|
-
await limit(() => visitor.output?.(node, { parent }));
|
|
600
|
-
break;
|
|
601
|
-
case "Operation":
|
|
602
|
-
await limit(() => visitor.operation?.(node, { parent }));
|
|
603
|
-
break;
|
|
604
|
-
case "Schema":
|
|
605
|
-
await limit(() => visitor.schema?.(node, { parent }));
|
|
606
|
-
break;
|
|
607
|
-
case "Property":
|
|
608
|
-
await limit(() => visitor.property?.(node, { parent }));
|
|
609
|
-
break;
|
|
610
|
-
case "Parameter":
|
|
611
|
-
await limit(() => visitor.parameter?.(node, { parent }));
|
|
612
|
-
break;
|
|
613
|
-
case "Response":
|
|
614
|
-
await limit(() => visitor.response?.(node, { parent }));
|
|
615
|
-
break;
|
|
616
|
-
case "FunctionParameter":
|
|
617
|
-
case "ParameterGroup":
|
|
618
|
-
case "FunctionParameters": break;
|
|
619
|
-
}
|
|
679
|
+
await limit(() => applyVisitor(node, visitor, parent));
|
|
620
680
|
const children = getChildren(node, recurse);
|
|
621
681
|
for (const child of children) await _walk(child, visitor, recurse, limit, node);
|
|
622
682
|
}
|
|
623
683
|
function transform(node, options) {
|
|
624
684
|
const { depth, parent, ...visitor } = options;
|
|
625
685
|
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
parent: op
|
|
666
|
-
}) : void 0
|
|
667
|
-
}))
|
|
668
|
-
} : void 0,
|
|
669
|
-
responses: op.responses.map((r) => transform(r, {
|
|
670
|
-
...options,
|
|
671
|
-
parent: op
|
|
672
|
-
}))
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
case "Schema": {
|
|
676
|
-
let schema = node;
|
|
677
|
-
const replaced = visitor.schema?.(schema, { parent });
|
|
678
|
-
if (replaced) schema = replaced;
|
|
679
|
-
const childOptions = {
|
|
680
|
-
...options,
|
|
681
|
-
parent: schema
|
|
682
|
-
};
|
|
683
|
-
return {
|
|
684
|
-
...schema,
|
|
685
|
-
..."properties" in schema && recurse ? { properties: schema.properties.map((p) => transform(p, childOptions)) } : {},
|
|
686
|
-
..."items" in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {},
|
|
687
|
-
..."members" in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {},
|
|
688
|
-
..."additionalProperties" in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true ? { additionalProperties: transform(schema.additionalProperties, childOptions) } : {}
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
case "Property": {
|
|
692
|
-
let prop = node;
|
|
693
|
-
const replaced = visitor.property?.(prop, { parent });
|
|
694
|
-
if (replaced) prop = replaced;
|
|
695
|
-
return createProperty({
|
|
696
|
-
...prop,
|
|
697
|
-
schema: transform(prop.schema, {
|
|
698
|
-
...options,
|
|
699
|
-
parent: prop
|
|
700
|
-
})
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
case "Parameter": {
|
|
704
|
-
let param = node;
|
|
705
|
-
const replaced = visitor.parameter?.(param, { parent });
|
|
706
|
-
if (replaced) param = replaced;
|
|
707
|
-
return createParameter({
|
|
708
|
-
...param,
|
|
709
|
-
schema: transform(param.schema, {
|
|
710
|
-
...options,
|
|
711
|
-
parent: param
|
|
712
|
-
})
|
|
686
|
+
const rebuilt = transformChildren(applyVisitor(node, visitor, parent) ?? node, options, recurse);
|
|
687
|
+
if (rebuilt === node) return node;
|
|
688
|
+
const finalize = nodeFinalizers[rebuilt.kind];
|
|
689
|
+
return finalize ? finalize(rebuilt) : rebuilt;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Per-kind builders rerun after children are rebuilt. `Property`/`Parameter`
|
|
693
|
+
* resync schema optionality against their `required` flag once the schema may
|
|
694
|
+
* have changed.
|
|
695
|
+
*/
|
|
696
|
+
const nodeFinalizers = {
|
|
697
|
+
Property: (node) => createProperty(node),
|
|
698
|
+
Parameter: (node) => createParameter(node)
|
|
699
|
+
};
|
|
700
|
+
/**
|
|
701
|
+
* Immutably rebuilds a node's children using {@link VISITOR_KEYS}, transforming
|
|
702
|
+
* each child node and leaving non-node values (e.g. `additionalProperties: true`) intact.
|
|
703
|
+
* `Schema` children are skipped in shallow mode.
|
|
704
|
+
*/
|
|
705
|
+
function transformChildren(node, options, recurse) {
|
|
706
|
+
if (node.kind === "Schema" && !recurse) return node;
|
|
707
|
+
const keys = visitorKeysByKind[node.kind];
|
|
708
|
+
if (!keys) return node;
|
|
709
|
+
const record = node;
|
|
710
|
+
const childOptions = {
|
|
711
|
+
...options,
|
|
712
|
+
parent: node
|
|
713
|
+
};
|
|
714
|
+
let updates;
|
|
715
|
+
for (const key of keys) {
|
|
716
|
+
if (!(key in record)) continue;
|
|
717
|
+
const value = record[key];
|
|
718
|
+
if (Array.isArray(value)) {
|
|
719
|
+
let changed = false;
|
|
720
|
+
const mapped = value.map((item) => {
|
|
721
|
+
if (!isNode(item)) return item;
|
|
722
|
+
const next = transform(item, childOptions);
|
|
723
|
+
if (next !== item) changed = true;
|
|
724
|
+
return next;
|
|
713
725
|
});
|
|
726
|
+
if (changed) (updates ??= {})[key] = mapped;
|
|
727
|
+
} else if (isNode(value)) {
|
|
728
|
+
const next = transform(value, childOptions);
|
|
729
|
+
if (next !== value) (updates ??= {})[key] = next;
|
|
714
730
|
}
|
|
715
|
-
case "Response": {
|
|
716
|
-
let response = node;
|
|
717
|
-
const replaced = visitor.response?.(response, { parent });
|
|
718
|
-
if (replaced) response = replaced;
|
|
719
|
-
return {
|
|
720
|
-
...response,
|
|
721
|
-
schema: transform(response.schema, {
|
|
722
|
-
...options,
|
|
723
|
-
parent: response
|
|
724
|
-
})
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
case "FunctionParameter":
|
|
728
|
-
case "ParameterGroup":
|
|
729
|
-
case "FunctionParameters":
|
|
730
|
-
case "Type": return node;
|
|
731
|
-
default: return node;
|
|
732
731
|
}
|
|
732
|
+
return updates ? {
|
|
733
|
+
...node,
|
|
734
|
+
...updates
|
|
735
|
+
} : node;
|
|
733
736
|
}
|
|
734
737
|
/**
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
* Non-`undefined` values returned by visitor callbacks are appended to the result.
|
|
738
|
+
* Lazy depth-first collection pass. Yields every non-null value returned by
|
|
739
|
+
* the visitor callbacks. Use `collect` for the eager array form.
|
|
738
740
|
*
|
|
739
|
-
* @example
|
|
741
|
+
* @example Collect every operationId
|
|
740
742
|
* ```ts
|
|
741
|
-
* const ids =
|
|
743
|
+
* const ids: string[] = []
|
|
744
|
+
* for (const id of collectLazy<string>(root, {
|
|
742
745
|
* operation(node) {
|
|
743
746
|
* return node.operationId
|
|
744
747
|
* },
|
|
745
|
-
* })
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
* @example
|
|
749
|
-
* ```ts
|
|
750
|
-
* // Collect from only the current node
|
|
751
|
-
* const values = collect(root, { depth: 'shallow', root: () => 'root' })
|
|
748
|
+
* })) {
|
|
749
|
+
* ids.push(id)
|
|
750
|
+
* }
|
|
752
751
|
* ```
|
|
753
752
|
*/
|
|
754
|
-
function
|
|
753
|
+
function* collectLazy(node, options) {
|
|
755
754
|
const { depth, parent, ...visitor } = options;
|
|
756
755
|
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
|
|
757
|
-
const
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
case "Input":
|
|
761
|
-
v = visitor.input?.(node, { parent });
|
|
762
|
-
break;
|
|
763
|
-
case "Output":
|
|
764
|
-
v = visitor.output?.(node, { parent });
|
|
765
|
-
break;
|
|
766
|
-
case "Operation":
|
|
767
|
-
v = visitor.operation?.(node, { parent });
|
|
768
|
-
break;
|
|
769
|
-
case "Schema":
|
|
770
|
-
v = visitor.schema?.(node, { parent });
|
|
771
|
-
break;
|
|
772
|
-
case "Property":
|
|
773
|
-
v = visitor.property?.(node, { parent });
|
|
774
|
-
break;
|
|
775
|
-
case "Parameter":
|
|
776
|
-
v = visitor.parameter?.(node, { parent });
|
|
777
|
-
break;
|
|
778
|
-
case "Response":
|
|
779
|
-
v = visitor.response?.(node, { parent });
|
|
780
|
-
break;
|
|
781
|
-
case "FunctionParameter":
|
|
782
|
-
case "ParameterGroup":
|
|
783
|
-
case "FunctionParameters": break;
|
|
784
|
-
}
|
|
785
|
-
if (v !== void 0) results.push(v);
|
|
786
|
-
for (const child of getChildren(node, recurse)) for (const item of collect(child, {
|
|
756
|
+
const v = applyVisitor(node, visitor, parent);
|
|
757
|
+
if (v != null) yield v;
|
|
758
|
+
for (const child of getChildren(node, recurse)) yield* collectLazy(child, {
|
|
787
759
|
...options,
|
|
788
760
|
parent: node
|
|
789
|
-
})
|
|
790
|
-
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Eager depth-first collection pass. Returns an array of every non-null value
|
|
765
|
+
* the visitor callbacks return.
|
|
766
|
+
*
|
|
767
|
+
* @example Collect every operationId
|
|
768
|
+
* ```ts
|
|
769
|
+
* const ids = collect<string>(root, {
|
|
770
|
+
* operation(node) {
|
|
771
|
+
* return node.operationId
|
|
772
|
+
* },
|
|
773
|
+
* })
|
|
774
|
+
* ```
|
|
775
|
+
*/
|
|
776
|
+
function collect(node, options) {
|
|
777
|
+
return Array.from(collectLazy(node, options));
|
|
791
778
|
}
|
|
792
779
|
//#endregion
|
|
793
780
|
//#region src/utils.ts
|
|
@@ -841,15 +828,16 @@ function isStringType(node) {
|
|
|
841
828
|
* the desired casing while preserving `OperationNode.parameters` for other consumers.
|
|
842
829
|
* The input array is not mutated. When `casing` is not set, the original array is returned unchanged.
|
|
843
830
|
*/
|
|
831
|
+
const caseParamsMemo = memoize(/* @__PURE__ */ new WeakMap(), (params) => memoize(/* @__PURE__ */ new Map(), (casing) => params.map((param) => {
|
|
832
|
+
const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
|
|
833
|
+
return {
|
|
834
|
+
...param,
|
|
835
|
+
name: transformed
|
|
836
|
+
};
|
|
837
|
+
})));
|
|
844
838
|
function caseParams(params, casing) {
|
|
845
839
|
if (!casing) return params;
|
|
846
|
-
return params
|
|
847
|
-
const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
|
|
848
|
-
return {
|
|
849
|
-
...param,
|
|
850
|
-
name: transformed
|
|
851
|
-
};
|
|
852
|
-
});
|
|
840
|
+
return caseParamsMemo(params)(casing);
|
|
853
841
|
}
|
|
854
842
|
/**
|
|
855
843
|
* Creates a single-property object schema used as a discriminator literal.
|
|
@@ -978,7 +966,7 @@ function createOperationParams(node, options) {
|
|
|
978
966
|
}));
|
|
979
967
|
} else {
|
|
980
968
|
if (pathParams.length) if (pathParamsType === "inlineSpread") {
|
|
981
|
-
const spreadType = resolver?.resolvePathParamsName(node, pathParams[0])
|
|
969
|
+
const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]);
|
|
982
970
|
params.push(createFunctionParameter({
|
|
983
971
|
name: pathName,
|
|
984
972
|
type: spreadType ? wrapType(spreadType) : void 0,
|
|
@@ -1054,13 +1042,13 @@ function buildGroupParam({ name, node, params, groupType, resolver, wrapType })
|
|
|
1054
1042
|
}
|
|
1055
1043
|
/**
|
|
1056
1044
|
* Derives a {@link ParamGroupType} from the resolver's group method.
|
|
1057
|
-
* Returns `
|
|
1045
|
+
* Returns `null` when the group name equals the individual param name (no real group).
|
|
1058
1046
|
*/
|
|
1059
1047
|
function resolveGroupType({ node, params, groupMethod, resolver }) {
|
|
1060
|
-
if (!params.length) return;
|
|
1048
|
+
if (!params.length) return null;
|
|
1061
1049
|
const firstParam = params[0];
|
|
1062
1050
|
const groupName = groupMethod.call(resolver, node, firstParam);
|
|
1063
|
-
if (groupName === resolver.resolveParamName(node, firstParam)) return;
|
|
1051
|
+
if (groupName === resolver.resolveParamName(node, firstParam)) return null;
|
|
1064
1052
|
const allOptional = params.every((p) => !p.required);
|
|
1065
1053
|
return {
|
|
1066
1054
|
type: createParamsType({
|
|
@@ -1127,6 +1115,16 @@ function combineSources(sources) {
|
|
|
1127
1115
|
return [...seen.values()];
|
|
1128
1116
|
}
|
|
1129
1117
|
/**
|
|
1118
|
+
* Merges `incoming` names into `existing`, preserving order and dropping duplicates.
|
|
1119
|
+
*
|
|
1120
|
+
* Shared by `combineExports` and `combineImports` for the same-path name-merge case.
|
|
1121
|
+
*/
|
|
1122
|
+
function mergeNameArrays(existing, incoming) {
|
|
1123
|
+
const merged = new Set(existing);
|
|
1124
|
+
for (const name of incoming) merged.add(name);
|
|
1125
|
+
return [...merged];
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1130
1128
|
* Deduplicates and merges `ExportNode` objects by path and type.
|
|
1131
1129
|
*
|
|
1132
1130
|
* Named exports with the same path and `isTypeOnly` flag have their names merged into a single export.
|
|
@@ -1147,11 +1145,8 @@ function combineExports(exports) {
|
|
|
1147
1145
|
if (!name.length) continue;
|
|
1148
1146
|
const key = pathTypeKey(path, isTypeOnly);
|
|
1149
1147
|
const existing = namedByPath.get(key);
|
|
1150
|
-
if (existing && Array.isArray(existing.name))
|
|
1151
|
-
|
|
1152
|
-
for (const n of name) merged.add(n);
|
|
1153
|
-
existing.name = [...merged];
|
|
1154
|
-
} else {
|
|
1148
|
+
if (existing && Array.isArray(existing.name)) existing.name = mergeNameArrays(existing.name, name);
|
|
1149
|
+
else {
|
|
1155
1150
|
const newItem = {
|
|
1156
1151
|
...curr,
|
|
1157
1152
|
name: [...new Set(name)]
|
|
@@ -1187,6 +1182,11 @@ function combineImports(imports, exports, source) {
|
|
|
1187
1182
|
if (!importNameMemo.has(key)) importNameMemo.set(key, n);
|
|
1188
1183
|
return importNameMemo.get(key);
|
|
1189
1184
|
};
|
|
1185
|
+
const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
|
|
1186
|
+
for (const node of imports) {
|
|
1187
|
+
if (!Array.isArray(node.name)) continue;
|
|
1188
|
+
if (node.name.some((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName))) pathsWithUsedNamedImport.add(node.path);
|
|
1189
|
+
}
|
|
1190
1190
|
const result = [];
|
|
1191
1191
|
const namedByPath = /* @__PURE__ */ new Map();
|
|
1192
1192
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1204,11 +1204,8 @@ function combineImports(imports, exports, source) {
|
|
|
1204
1204
|
if (!name.length) continue;
|
|
1205
1205
|
const key = pathTypeKey(path, isTypeOnly);
|
|
1206
1206
|
const existing = namedByPath.get(key);
|
|
1207
|
-
if (existing && Array.isArray(existing.name))
|
|
1208
|
-
|
|
1209
|
-
for (const n of name) merged.add(n);
|
|
1210
|
-
existing.name = [...merged];
|
|
1211
|
-
} else {
|
|
1207
|
+
if (existing && Array.isArray(existing.name)) existing.name = mergeNameArrays(existing.name, name);
|
|
1208
|
+
else {
|
|
1212
1209
|
const newItem = {
|
|
1213
1210
|
...curr,
|
|
1214
1211
|
name
|
|
@@ -1217,7 +1214,7 @@ function combineImports(imports, exports, source) {
|
|
|
1217
1214
|
namedByPath.set(key, newItem);
|
|
1218
1215
|
}
|
|
1219
1216
|
} else {
|
|
1220
|
-
if (name && !isUsed(name)) continue;
|
|
1217
|
+
if (name && !isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
|
|
1221
1218
|
const key = importKey(path, name, isTypeOnly);
|
|
1222
1219
|
if (!seen.has(key)) {
|
|
1223
1220
|
result.push(curr);
|
|
@@ -1253,7 +1250,7 @@ function extractStringsFromNodes(nodes) {
|
|
|
1253
1250
|
/**
|
|
1254
1251
|
* Resolves the schema name of a ref node, falling back through `ref` → `name` → nested `schema.name`.
|
|
1255
1252
|
*
|
|
1256
|
-
* Returns `
|
|
1253
|
+
* Returns `null` for non-ref nodes or when no name can be resolved. Use this to get a schema's
|
|
1257
1254
|
* identifier for type definitions or error messages.
|
|
1258
1255
|
*
|
|
1259
1256
|
* @example
|
|
@@ -1263,9 +1260,9 @@ function extractStringsFromNodes(nodes) {
|
|
|
1263
1260
|
* ```
|
|
1264
1261
|
*/
|
|
1265
1262
|
function resolveRefName(node) {
|
|
1266
|
-
if (!node || node.type !== "ref") return
|
|
1267
|
-
if (node.ref) return extractRefName(node.ref) ?? node.name ?? node.schema?.name ??
|
|
1268
|
-
return node.name ?? node.schema?.name ??
|
|
1263
|
+
if (!node || node.type !== "ref") return null;
|
|
1264
|
+
if (node.ref) return extractRefName(node.ref) ?? node.name ?? node.schema?.name ?? null;
|
|
1265
|
+
return node.name ?? node.schema?.name ?? null;
|
|
1269
1266
|
}
|
|
1270
1267
|
/**
|
|
1271
1268
|
* Collects every named schema referenced (transitively) from a node via ref edges.
|
|
@@ -1287,14 +1284,19 @@ function resolveRefName(node) {
|
|
|
1287
1284
|
* }
|
|
1288
1285
|
* ```
|
|
1289
1286
|
*/
|
|
1290
|
-
|
|
1291
|
-
|
|
1287
|
+
const collectSchemaRefs = memoize(/* @__PURE__ */ new WeakMap(), (node) => {
|
|
1288
|
+
const refs = /* @__PURE__ */ new Set();
|
|
1292
1289
|
collect(node, { schema(child) {
|
|
1293
1290
|
if (child.type === "ref") {
|
|
1294
1291
|
const name = resolveRefName(child);
|
|
1295
|
-
if (name)
|
|
1292
|
+
if (name) refs.add(name);
|
|
1296
1293
|
}
|
|
1297
1294
|
} });
|
|
1295
|
+
return refs;
|
|
1296
|
+
});
|
|
1297
|
+
function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
1298
|
+
if (!node) return out;
|
|
1299
|
+
for (const name of collectSchemaRefs(node)) out.add(name);
|
|
1298
1300
|
return out;
|
|
1299
1301
|
}
|
|
1300
1302
|
/**
|
|
@@ -1310,10 +1312,10 @@ function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
|
1310
1312
|
*
|
|
1311
1313
|
* @example Only generate schemas referenced by included operations
|
|
1312
1314
|
* ```ts
|
|
1313
|
-
* const includedOps =
|
|
1314
|
-
* const allowed = collectUsedSchemaNames(includedOps,
|
|
1315
|
+
* const includedOps = operations.filter(op => resolver.resolveOptions(op, { options, include }) !== null)
|
|
1316
|
+
* const allowed = collectUsedSchemaNames(includedOps, schemas)
|
|
1315
1317
|
*
|
|
1316
|
-
* for (const schema of
|
|
1318
|
+
* for (const schema of schemas) {
|
|
1317
1319
|
* if (schema.name && !allowed.has(schema.name)) continue
|
|
1318
1320
|
* // … generate schema
|
|
1319
1321
|
* }
|
|
@@ -1321,11 +1323,12 @@ function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
|
1321
1323
|
*
|
|
1322
1324
|
* @example Check whether a specific schema is needed
|
|
1323
1325
|
* ```ts
|
|
1324
|
-
* const allowed = collectUsedSchemaNames(includedOps,
|
|
1326
|
+
* const allowed = collectUsedSchemaNames(includedOps, schemas)
|
|
1325
1327
|
* allowed.has('OrderStatus') // false when no included operation references OrderStatus
|
|
1326
1328
|
* ```
|
|
1327
1329
|
*/
|
|
1328
|
-
|
|
1330
|
+
const collectUsedSchemaNamesMemo = memoize(/* @__PURE__ */ new WeakMap(), (ops) => memoize(/* @__PURE__ */ new WeakMap(), (schemas) => computeUsedSchemaNames(ops, schemas)));
|
|
1331
|
+
function computeUsedSchemaNames(operations, schemas) {
|
|
1329
1332
|
const schemaMap = /* @__PURE__ */ new Map();
|
|
1330
1333
|
for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
|
|
1331
1334
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -1337,22 +1340,17 @@ function collectUsedSchemaNames(operations, schemas) {
|
|
|
1337
1340
|
if (namedSchema) visitSchema(namedSchema);
|
|
1338
1341
|
}
|
|
1339
1342
|
}
|
|
1340
|
-
for (const op of operations) for (const schema of
|
|
1343
|
+
for (const op of operations) for (const schema of collectLazy(op, {
|
|
1341
1344
|
depth: "shallow",
|
|
1342
1345
|
schema: (node) => node
|
|
1343
1346
|
})) visitSchema(schema);
|
|
1344
1347
|
return result;
|
|
1345
1348
|
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
* Refs are followed by name only, keeping the algorithm linear in the schema graph size.
|
|
1352
|
-
*
|
|
1353
|
-
* @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
|
|
1354
|
-
*/
|
|
1355
|
-
function findCircularSchemas(schemas) {
|
|
1349
|
+
function collectUsedSchemaNames(operations, schemas) {
|
|
1350
|
+
return collectUsedSchemaNamesMemo(operations)(schemas);
|
|
1351
|
+
}
|
|
1352
|
+
const EMPTY_CIRCULAR_SET = /* @__PURE__ */ new Set();
|
|
1353
|
+
const findCircularSchemasMemo = memoize(/* @__PURE__ */ new WeakMap(), (schemas) => {
|
|
1356
1354
|
const graph = /* @__PURE__ */ new Map();
|
|
1357
1355
|
for (const schema of schemas) {
|
|
1358
1356
|
if (!schema.name) continue;
|
|
@@ -1375,6 +1373,19 @@ function findCircularSchemas(schemas) {
|
|
|
1375
1373
|
}
|
|
1376
1374
|
}
|
|
1377
1375
|
return circular;
|
|
1376
|
+
});
|
|
1377
|
+
/**
|
|
1378
|
+
* Identifies all schemas that participate in circular dependency chains, including direct self-loops.
|
|
1379
|
+
*
|
|
1380
|
+
* Returns a Set of schema names with circular dependencies. Use this to wrap recursive schema positions
|
|
1381
|
+
* in deferred constructs (lazy getter, `z.lazy(() => …)`) to prevent infinite recursion when generated code runs.
|
|
1382
|
+
* Refs are followed by name only, keeping the algorithm linear in the schema graph size.
|
|
1383
|
+
*
|
|
1384
|
+
* @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
|
|
1385
|
+
*/
|
|
1386
|
+
function findCircularSchemas(schemas) {
|
|
1387
|
+
if (schemas.length === 0) return EMPTY_CIRCULAR_SET;
|
|
1388
|
+
return findCircularSchemasMemo(schemas);
|
|
1378
1389
|
}
|
|
1379
1390
|
/**
|
|
1380
1391
|
* Type guard returning `true` when a schema or anything nested within it contains a ref to a circular schema.
|
|
@@ -1386,19 +1397,23 @@ function findCircularSchemas(schemas) {
|
|
|
1386
1397
|
*/
|
|
1387
1398
|
function containsCircularRef(node, { circularSchemas, excludeName }) {
|
|
1388
1399
|
if (!node || circularSchemas.size === 0) return false;
|
|
1389
|
-
|
|
1390
|
-
if (child.type !== "ref") return
|
|
1400
|
+
for (const _ of collectLazy(node, { schema(child) {
|
|
1401
|
+
if (child.type !== "ref") return null;
|
|
1391
1402
|
const name = resolveRefName(child);
|
|
1392
|
-
return name && name !== excludeName && circularSchemas.has(name) ? true :
|
|
1393
|
-
} })
|
|
1403
|
+
return name && name !== excludeName && circularSchemas.has(name) ? true : null;
|
|
1404
|
+
} })) return true;
|
|
1405
|
+
return false;
|
|
1394
1406
|
}
|
|
1395
1407
|
//#endregion
|
|
1396
1408
|
//#region src/factory.ts
|
|
1397
1409
|
/**
|
|
1398
|
-
*
|
|
1410
|
+
* Updates a schema's `optional` and `nullish` flags from a parent's `required`
|
|
1411
|
+
* value and the schema's own `nullable`. Mirrors how OpenAPI parameters and
|
|
1412
|
+
* object properties combine "required" and "nullable" into a single AST.
|
|
1399
1413
|
*
|
|
1400
|
-
* -
|
|
1401
|
-
* -
|
|
1414
|
+
* - Non-required + non-nullable → `optional: true`.
|
|
1415
|
+
* - Non-required + nullable → `nullish: true`.
|
|
1416
|
+
* - Required → both flags cleared.
|
|
1402
1417
|
*/
|
|
1403
1418
|
function syncOptionality(schema, required) {
|
|
1404
1419
|
const nullable = schema.nullable ?? false;
|
|
@@ -1409,6 +1424,29 @@ function syncOptionality(schema, required) {
|
|
|
1409
1424
|
};
|
|
1410
1425
|
}
|
|
1411
1426
|
/**
|
|
1427
|
+
* Identity-preserving node update: returns `node` unchanged when every field in
|
|
1428
|
+
* `changes` already equals (by reference) the current value, otherwise a new node
|
|
1429
|
+
* with the changes applied.
|
|
1430
|
+
*
|
|
1431
|
+
* Mirrors the TypeScript compiler's `factory.updateX` contract — pair it with the
|
|
1432
|
+
* structural sharing in {@link transform} so a no-op rewrite doesn't allocate and
|
|
1433
|
+
* downstream passes can detect "nothing changed" by identity. Comparison is
|
|
1434
|
+
* shallow: a structurally-equal but newly-allocated array/object counts as a change.
|
|
1435
|
+
*
|
|
1436
|
+
* @example
|
|
1437
|
+
* ```ts
|
|
1438
|
+
* update(node, { name: node.name }) // -> same `node` reference
|
|
1439
|
+
* update(node, { name: 'renamed' }) // -> new node, `name` replaced
|
|
1440
|
+
* ```
|
|
1441
|
+
*/
|
|
1442
|
+
function update(node, changes) {
|
|
1443
|
+
for (const key in changes) if (changes[key] !== node[key]) return {
|
|
1444
|
+
...node,
|
|
1445
|
+
...changes
|
|
1446
|
+
};
|
|
1447
|
+
return node;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1412
1450
|
* Creates an `InputNode` with stable defaults for `schemas` and `operations`.
|
|
1413
1451
|
*
|
|
1414
1452
|
* @example
|
|
@@ -1427,11 +1465,31 @@ function createInput(overrides = {}) {
|
|
|
1427
1465
|
return {
|
|
1428
1466
|
schemas: [],
|
|
1429
1467
|
operations: [],
|
|
1468
|
+
meta: {
|
|
1469
|
+
circularNames: [],
|
|
1470
|
+
enumNames: []
|
|
1471
|
+
},
|
|
1430
1472
|
...overrides,
|
|
1431
1473
|
kind: "Input"
|
|
1432
1474
|
};
|
|
1433
1475
|
}
|
|
1434
1476
|
/**
|
|
1477
|
+
* Creates an `InputStreamNode` from pre-built `AsyncIterable` sources.
|
|
1478
|
+
*
|
|
1479
|
+
* @example
|
|
1480
|
+
* ```ts
|
|
1481
|
+
* const node = createStreamInput(schemasIterable, operationsIterable, { title: 'My API' })
|
|
1482
|
+
* ```
|
|
1483
|
+
*/
|
|
1484
|
+
function createStreamInput(schemas, operations, meta) {
|
|
1485
|
+
return {
|
|
1486
|
+
kind: "Input",
|
|
1487
|
+
schemas,
|
|
1488
|
+
operations,
|
|
1489
|
+
meta
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1435
1493
|
* Creates an `OutputNode` with a stable default for `files`.
|
|
1436
1494
|
*
|
|
1437
1495
|
* @example
|
|
@@ -1453,35 +1511,35 @@ function createOutput(overrides = {}) {
|
|
|
1453
1511
|
};
|
|
1454
1512
|
}
|
|
1455
1513
|
/**
|
|
1456
|
-
* Creates
|
|
1457
|
-
*
|
|
1458
|
-
* @example
|
|
1459
|
-
* ```ts
|
|
1460
|
-
* const operation = createOperation({
|
|
1461
|
-
* operationId: 'getPetById',
|
|
1462
|
-
* method: 'GET',
|
|
1463
|
-
* path: '/pet/{petId}',
|
|
1464
|
-
* })
|
|
1465
|
-
* // tags, parameters, and responses are []
|
|
1466
|
-
* ```
|
|
1467
|
-
*
|
|
1468
|
-
* @example
|
|
1469
|
-
* ```ts
|
|
1470
|
-
* const operation = createOperation({
|
|
1471
|
-
* operationId: 'findPets',
|
|
1472
|
-
* method: 'GET',
|
|
1473
|
-
* path: '/pet/findByStatus',
|
|
1474
|
-
* tags: ['pet'],
|
|
1475
|
-
* })
|
|
1476
|
-
* ```
|
|
1514
|
+
* Creates a `ContentNode` for a single request-body or response content type.
|
|
1477
1515
|
*/
|
|
1516
|
+
function createContent(props) {
|
|
1517
|
+
return {
|
|
1518
|
+
...props,
|
|
1519
|
+
kind: "Content"
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Creates a `RequestBodyNode`, normalizing each content entry into a `ContentNode`.
|
|
1524
|
+
*/
|
|
1525
|
+
function createRequestBody(props) {
|
|
1526
|
+
return {
|
|
1527
|
+
...props,
|
|
1528
|
+
kind: "RequestBody",
|
|
1529
|
+
content: props.content?.map(createContent)
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1478
1532
|
function createOperation(props) {
|
|
1533
|
+
const { requestBody, ...rest } = props;
|
|
1534
|
+
const isHttp = rest.method !== void 0 && rest.path !== void 0;
|
|
1479
1535
|
return {
|
|
1480
1536
|
tags: [],
|
|
1481
1537
|
parameters: [],
|
|
1482
1538
|
responses: [],
|
|
1483
|
-
...
|
|
1484
|
-
|
|
1539
|
+
...rest,
|
|
1540
|
+
...isHttp ? { protocol: "http" } : {},
|
|
1541
|
+
kind: "Operation",
|
|
1542
|
+
requestBody: requestBody ? createRequestBody(requestBody) : void 0
|
|
1485
1543
|
};
|
|
1486
1544
|
}
|
|
1487
1545
|
/**
|
|
@@ -1595,19 +1653,29 @@ function createParameter(props) {
|
|
|
1595
1653
|
/**
|
|
1596
1654
|
* Creates a `ResponseNode`.
|
|
1597
1655
|
*
|
|
1656
|
+
* Response body schemas live inside `content`. For convenience a single legacy `schema`
|
|
1657
|
+
* (with optional `mediaType`/`keysToOmit`) is normalized into one `content` entry, so the same
|
|
1658
|
+
* schema is never stored both at the node root and inside `content`.
|
|
1659
|
+
*
|
|
1598
1660
|
* @example
|
|
1599
1661
|
* ```ts
|
|
1600
1662
|
* const response = createResponse({
|
|
1601
1663
|
* statusCode: '200',
|
|
1602
|
-
*
|
|
1603
|
-
* schema: createSchema({ type: 'object', properties: [] }),
|
|
1664
|
+
* content: [{ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) }],
|
|
1604
1665
|
* })
|
|
1605
1666
|
* ```
|
|
1606
1667
|
*/
|
|
1607
1668
|
function createResponse(props) {
|
|
1669
|
+
const { schema, mediaType, keysToOmit, content, ...rest } = props;
|
|
1670
|
+
const entries = content ?? (schema ? [{
|
|
1671
|
+
contentType: mediaType ?? "application/json",
|
|
1672
|
+
schema,
|
|
1673
|
+
keysToOmit: keysToOmit ?? null
|
|
1674
|
+
}] : void 0);
|
|
1608
1675
|
return {
|
|
1609
|
-
...
|
|
1610
|
-
kind: "Response"
|
|
1676
|
+
...rest,
|
|
1677
|
+
kind: "Response",
|
|
1678
|
+
content: entries?.map(createContent)
|
|
1611
1679
|
};
|
|
1612
1680
|
}
|
|
1613
1681
|
/**
|
|
@@ -2016,24 +2084,300 @@ function createJsx(value) {
|
|
|
2016
2084
|
};
|
|
2017
2085
|
}
|
|
2018
2086
|
//#endregion
|
|
2019
|
-
//#region src/
|
|
2087
|
+
//#region src/signature.ts
|
|
2088
|
+
/**
|
|
2089
|
+
* The shape-affecting flags shared by every node kind: base primitive, format, and `nullable`.
|
|
2090
|
+
* Documentation and usage-slot flags (`optional`/`nullish`/`readOnly`/`writeOnly`) are
|
|
2091
|
+
* intentionally excluded — they describe the property slot, not the type.
|
|
2092
|
+
*/
|
|
2093
|
+
function flagsDescriptor(node) {
|
|
2094
|
+
return `${node.primitive ?? ""};${node.format ?? ""};${node.nullable ? 1 : 0}`;
|
|
2095
|
+
}
|
|
2096
|
+
function refTargetName(node) {
|
|
2097
|
+
if (node.ref) return extractRefName(node.ref);
|
|
2098
|
+
return node.name ?? "";
|
|
2099
|
+
}
|
|
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 ?? ""}`;
|
|
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}`;
|
|
2121
|
+
}
|
|
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}]`;
|
|
2125
|
+
}
|
|
2126
|
+
case "intersection": return `intersection|${flags}|m[${(node.members ?? []).map((member) => signatureOf(member, signatures)).join(",")}]`;
|
|
2127
|
+
case "enum": {
|
|
2128
|
+
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}]`;
|
|
2132
|
+
}
|
|
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}`;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
/**
|
|
2148
|
+
* Hash-consing: each node's signature is a fixed-length digest of its local shape plus its
|
|
2149
|
+
* children's digests (a Merkle hash). Children contribute their 64-char hash instead of their
|
|
2150
|
+
* full nested descriptor, so a signature stays bounded regardless of subtree depth, and the
|
|
2151
|
+
* digest is identical across calls because it depends only on content — never on traversal
|
|
2152
|
+
* 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.
|
|
2154
|
+
*/
|
|
2155
|
+
function signatureOf(node, signatures) {
|
|
2156
|
+
const cached = signatures.get(node);
|
|
2157
|
+
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);
|
|
2160
|
+
return signature;
|
|
2161
|
+
}
|
|
2162
|
+
/**
|
|
2163
|
+
* Computes a deterministic, shape-only signature (a fixed-length content hash) for a schema node.
|
|
2164
|
+
*
|
|
2165
|
+
* Two schemas share a signature when they are structurally identical, ignoring
|
|
2166
|
+
* documentation (`name`, `title`, `description`, `example`, `default`, `deprecated`)
|
|
2167
|
+
* and usage-slot flags (`optional`, `nullish`, `readOnly`, `writeOnly`). `nullable`
|
|
2168
|
+
* is kept because it changes the produced type. `ref` nodes compare by target name,
|
|
2169
|
+
* which also keeps the algorithm terminating on circular shapes.
|
|
2170
|
+
*
|
|
2171
|
+
* @example Two enums with different descriptions share a signature
|
|
2172
|
+
* ```ts
|
|
2173
|
+
* schemaSignature(createSchema({ type: 'enum', primitive: 'string', enumValues: ['a', 'b'], description: 'x' })) ===
|
|
2174
|
+
* schemaSignature(createSchema({ type: 'enum', primitive: 'string', enumValues: ['a', 'b'] }))
|
|
2175
|
+
* ```
|
|
2176
|
+
*/
|
|
2177
|
+
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);
|
|
2190
|
+
}
|
|
2191
|
+
//#endregion
|
|
2192
|
+
//#region src/dedupe.ts
|
|
2193
|
+
/**
|
|
2194
|
+
* Builds the shared `ref` replacement for a duplicate occurrence, carrying the
|
|
2195
|
+
* usage-slot and documentation fields that are not part of the canonical type.
|
|
2196
|
+
*/
|
|
2197
|
+
function createRefNode(node, canonical) {
|
|
2198
|
+
return createSchema({
|
|
2199
|
+
type: "ref",
|
|
2200
|
+
name: canonical.name,
|
|
2201
|
+
ref: canonical.ref,
|
|
2202
|
+
optional: node.optional,
|
|
2203
|
+
nullish: node.nullish,
|
|
2204
|
+
readOnly: node.readOnly,
|
|
2205
|
+
writeOnly: node.writeOnly,
|
|
2206
|
+
deprecated: node.deprecated,
|
|
2207
|
+
description: node.description,
|
|
2208
|
+
default: node.default,
|
|
2209
|
+
example: node.example
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
function applyDedupe(node, canonicalBySignature, skipRootMatch = false) {
|
|
2213
|
+
if (canonicalBySignature.size === 0) return node;
|
|
2214
|
+
const signatures = /* @__PURE__ */ new Map();
|
|
2215
|
+
const root = node;
|
|
2216
|
+
return transform(node, { schema(schemaNode) {
|
|
2217
|
+
const signature = signatureOf(schemaNode, signatures);
|
|
2218
|
+
if (skipRootMatch && schemaNode === root) return void 0;
|
|
2219
|
+
const canonical = canonicalBySignature.get(signature);
|
|
2220
|
+
if (!canonical) return void 0;
|
|
2221
|
+
return createRefNode(schemaNode, canonical);
|
|
2222
|
+
} });
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* Strips usage-slot flags from a hoisted definition and applies its canonical name.
|
|
2226
|
+
* A standalone definition is never optional, so `optional`/`nullish` are cleared.
|
|
2227
|
+
*/
|
|
2228
|
+
function cleanDefinition(node, name) {
|
|
2229
|
+
return {
|
|
2230
|
+
...node,
|
|
2231
|
+
name,
|
|
2232
|
+
optional: void 0,
|
|
2233
|
+
nullish: void 0
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2020
2236
|
/**
|
|
2021
|
-
*
|
|
2237
|
+
* Scans a forest of schema and operation nodes and produces a {@link DedupePlan}.
|
|
2022
2238
|
*
|
|
2023
|
-
*
|
|
2239
|
+
* A shape that occurs at least `minOccurrences` times is deduplicated: if any occurrence
|
|
2240
|
+
* is a named top-level schema, that name becomes the canonical (so other top-level duplicates
|
|
2241
|
+
* and inline copies turn into references to it); otherwise a new definition is hoisted using
|
|
2242
|
+
* `nameFor`. The plan is then applied per node with {@link applyDedupe}.
|
|
2243
|
+
*
|
|
2244
|
+
* @example
|
|
2245
|
+
* ```ts
|
|
2246
|
+
* const plan = buildDedupePlan([...schemaNodes, ...operationNodes], {
|
|
2247
|
+
* isCandidate: (node) => node.type === 'enum' || node.type === 'object',
|
|
2248
|
+
* nameFor: (node) => node.name ?? null,
|
|
2249
|
+
* refFor: (name) => `#/components/schemas/${name}`,
|
|
2250
|
+
* })
|
|
2251
|
+
* ```
|
|
2252
|
+
*/
|
|
2253
|
+
function buildDedupePlan(roots, options) {
|
|
2254
|
+
const { isCandidate, nameFor, refFor, minOccurrences = 2 } = options;
|
|
2255
|
+
const signatures = /* @__PURE__ */ new Map();
|
|
2256
|
+
const topLevelNodes = /* @__PURE__ */ new Set();
|
|
2257
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2258
|
+
function record(schemaNode) {
|
|
2259
|
+
const signature = signatureOf(schemaNode, signatures);
|
|
2260
|
+
if (!isCandidate(schemaNode)) return;
|
|
2261
|
+
const isTopLevel = topLevelNodes.has(schemaNode) && !!schemaNode.name;
|
|
2262
|
+
const group = groups.get(signature);
|
|
2263
|
+
if (group) {
|
|
2264
|
+
group.count++;
|
|
2265
|
+
if (isTopLevel && !group.topLevelName) group.topLevelName = schemaNode.name;
|
|
2266
|
+
} else groups.set(signature, {
|
|
2267
|
+
count: 1,
|
|
2268
|
+
representative: schemaNode,
|
|
2269
|
+
topLevelName: isTopLevel ? schemaNode.name : void 0
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
for (const root of roots) {
|
|
2273
|
+
if (root.kind === "Schema") topLevelNodes.add(root);
|
|
2274
|
+
for (const schemaNode of collectLazy(root, { schema: (node) => node })) record(schemaNode);
|
|
2275
|
+
}
|
|
2276
|
+
const canonicalBySignature = /* @__PURE__ */ new Map();
|
|
2277
|
+
const pendingHoists = [];
|
|
2278
|
+
for (const [signature, group] of groups) {
|
|
2279
|
+
if (group.count < minOccurrences) continue;
|
|
2280
|
+
if (group.topLevelName) {
|
|
2281
|
+
canonicalBySignature.set(signature, {
|
|
2282
|
+
name: group.topLevelName,
|
|
2283
|
+
ref: refFor(group.topLevelName)
|
|
2284
|
+
});
|
|
2285
|
+
continue;
|
|
2286
|
+
}
|
|
2287
|
+
const name = nameFor(group.representative, signature);
|
|
2288
|
+
if (!name) continue;
|
|
2289
|
+
canonicalBySignature.set(signature, {
|
|
2290
|
+
name,
|
|
2291
|
+
ref: refFor(name)
|
|
2292
|
+
});
|
|
2293
|
+
pendingHoists.push({
|
|
2294
|
+
name,
|
|
2295
|
+
representative: group.representative
|
|
2296
|
+
});
|
|
2297
|
+
}
|
|
2298
|
+
return {
|
|
2299
|
+
canonicalBySignature,
|
|
2300
|
+
hoisted: pendingHoists.map(({ name, representative }) => cleanDefinition(applyDedupe(representative, canonicalBySignature, true), name))
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
//#endregion
|
|
2304
|
+
//#region src/dialect.ts
|
|
2305
|
+
/**
|
|
2306
|
+
* Identity helper that types a {@link SchemaDialect} for an adapter. Like
|
|
2307
|
+
* `defineParser`, it adds no runtime behavior — it pins the dialect's type for
|
|
2308
|
+
* inference and gives adapter authors a discoverable anchor.
|
|
2309
|
+
*
|
|
2310
|
+
* @example
|
|
2311
|
+
* ```ts
|
|
2312
|
+
* export const oasDialect = defineSchemaDialect({
|
|
2313
|
+
* name: 'oas',
|
|
2314
|
+
* isNullable,
|
|
2315
|
+
* isReference,
|
|
2316
|
+
* isDiscriminator,
|
|
2317
|
+
* isBinary: (schema) => schema.type === 'string' && schema.contentMediaType === 'application/octet-stream',
|
|
2318
|
+
* resolveRef,
|
|
2319
|
+
* })
|
|
2320
|
+
* ```
|
|
2321
|
+
*/
|
|
2322
|
+
function defineSchemaDialect(dialect) {
|
|
2323
|
+
return dialect;
|
|
2324
|
+
}
|
|
2325
|
+
//#endregion
|
|
2326
|
+
//#region src/dispatch.ts
|
|
2327
|
+
/**
|
|
2328
|
+
* Walks an ordered list of {@link DispatchRule}s and returns the first node produced.
|
|
2329
|
+
*
|
|
2330
|
+
* This is the shared backbone for spec adapters (OpenAPI today, AsyncAPI and others later).
|
|
2331
|
+
* The contract an adapter follows is intentionally minimal:
|
|
2332
|
+
*
|
|
2333
|
+
* context → [rule.match → rule.convert] → node
|
|
2334
|
+
*
|
|
2335
|
+
* An adapter derives a context from a source spec node, then declares an ordered table of
|
|
2336
|
+
* rules mapping spec shapes onto Kubb AST nodes. To add support for a new spec, write a new
|
|
2337
|
+
* context type and a new rules table — the traversal here is reused unchanged.
|
|
2338
|
+
*
|
|
2339
|
+
* Order is significant: earlier rules win, so list higher-precedence or more specific shapes
|
|
2340
|
+
* first (e.g. composition keywords before plain `type`). A rule whose `match` returns `true`
|
|
2341
|
+
* may still `convert` to `null` to defer to later rules. When no rule produces a node this
|
|
2342
|
+
* returns `null`, leaving the caller to apply its own fallback.
|
|
2343
|
+
*
|
|
2344
|
+
* @example
|
|
2345
|
+
* ```ts
|
|
2346
|
+
* const node = dispatch(schemaRules, schemaContext) ?? createSchema({ type: fallbackType })
|
|
2347
|
+
* ```
|
|
2348
|
+
*/
|
|
2349
|
+
function dispatch(rules, context) {
|
|
2350
|
+
for (const rule of rules) {
|
|
2351
|
+
if (!rule.match(context)) continue;
|
|
2352
|
+
const node = rule.convert(context);
|
|
2353
|
+
if (node !== null && node !== void 0) return node;
|
|
2354
|
+
}
|
|
2355
|
+
return null;
|
|
2356
|
+
}
|
|
2357
|
+
//#endregion
|
|
2358
|
+
//#region src/printer.ts
|
|
2359
|
+
/**
|
|
2360
|
+
* Defines a schema printer: a function that takes a `SchemaNode` and emits
|
|
2361
|
+
* code in your target language. Each plugin that produces code from schemas
|
|
2362
|
+
* (TypeScript types, Zod schemas, Faker factories) ships a printer built
|
|
2363
|
+
* with this helper.
|
|
2024
2364
|
*
|
|
2025
2365
|
* The builder receives resolved options and returns:
|
|
2026
|
-
* - `name` — a unique identifier for the printer
|
|
2027
|
-
* - `options` — options stored on the returned printer instance
|
|
2028
|
-
* - `nodes` — a map of `SchemaType` → handler functions that convert a `SchemaNode` to `TOutput`
|
|
2029
|
-
* - `print` _(optional)_ — top-level override exposed as `printer.print`
|
|
2030
|
-
* - Inside this function, use `this.transform(node)` to dispatch to the `nodes` map
|
|
2031
|
-
* - This keeps recursion safe and avoids self-calls
|
|
2032
2366
|
*
|
|
2033
|
-
*
|
|
2367
|
+
* - `name` — unique identifier for the printer.
|
|
2368
|
+
* - `options` — stored on the returned printer instance.
|
|
2369
|
+
* - `nodes` — map of `SchemaType` → handler. Handlers return the rendered
|
|
2370
|
+
* output (a string, a TypeScript AST node, ...) for that schema type.
|
|
2371
|
+
* - `print` (optional) — top-level override exposed as `printer.print`.
|
|
2372
|
+
* Use `this.transform(node)` inside it to dispatch to `nodes` recursively.
|
|
2034
2373
|
*
|
|
2035
|
-
*
|
|
2374
|
+
* Without a `print` override, `printer.print` falls back to `printer.transform`
|
|
2375
|
+
* (the node-level dispatcher).
|
|
2376
|
+
*
|
|
2377
|
+
* @example Tiny Zod printer
|
|
2036
2378
|
* ```ts
|
|
2379
|
+
* import { definePrinter, type PrinterFactoryOptions } from '@kubb/ast'
|
|
2380
|
+
*
|
|
2037
2381
|
* type PrinterZod = PrinterFactoryOptions<'zod', { strict?: boolean }, string>
|
|
2038
2382
|
*
|
|
2039
2383
|
* export const zodPrinter = definePrinter<PrinterZod>((options) => ({
|
|
@@ -2042,7 +2386,9 @@ function createJsx(value) {
|
|
|
2042
2386
|
* nodes: {
|
|
2043
2387
|
* string: () => 'z.string()',
|
|
2044
2388
|
* object(node) {
|
|
2045
|
-
* const props = node.properties
|
|
2389
|
+
* const props = node.properties
|
|
2390
|
+
* .map((p) => `${p.name}: ${this.transform(p.schema)}`)
|
|
2391
|
+
* .join(', ')
|
|
2046
2392
|
* return `z.object({ ${props} })`
|
|
2047
2393
|
* },
|
|
2048
2394
|
* },
|
|
@@ -2070,7 +2416,7 @@ function createPrinterFactory(getKey) {
|
|
|
2070
2416
|
options: resolvedOptions,
|
|
2071
2417
|
transform: (node) => {
|
|
2072
2418
|
const key = getKey(node);
|
|
2073
|
-
if (key ===
|
|
2419
|
+
if (key === null) return null;
|
|
2074
2420
|
const handler = nodes[key];
|
|
2075
2421
|
if (!handler) return null;
|
|
2076
2422
|
return handler.call(context, node);
|
|
@@ -2107,10 +2453,10 @@ function enumPropName(parentName, propName, enumSuffix) {
|
|
|
2107
2453
|
function collectImports({ node, nameMapping, resolve }) {
|
|
2108
2454
|
return collect(node, { schema(schemaNode) {
|
|
2109
2455
|
const schemaRef = narrowSchema(schemaNode, "ref");
|
|
2110
|
-
if (!schemaRef?.ref) return;
|
|
2456
|
+
if (!schemaRef?.ref) return null;
|
|
2111
2457
|
const rawName = extractRefName(schemaRef.ref);
|
|
2112
2458
|
const result = resolve(nameMapping.get(rawName) ?? rawName);
|
|
2113
|
-
if (!result) return;
|
|
2459
|
+
if (!result) return null;
|
|
2114
2460
|
return result;
|
|
2115
2461
|
} });
|
|
2116
2462
|
}
|
|
@@ -2164,23 +2510,27 @@ function setDiscriminatorEnum({ node, propertyName, values, enumName }) {
|
|
|
2164
2510
|
* ])
|
|
2165
2511
|
* ```
|
|
2166
2512
|
*/
|
|
2167
|
-
function
|
|
2168
|
-
|
|
2513
|
+
function* mergeAdjacentObjectsLazy(members) {
|
|
2514
|
+
let acc;
|
|
2515
|
+
for (const member of members) {
|
|
2169
2516
|
const objectMember = narrowSchema(member, "object");
|
|
2170
|
-
if (objectMember && !objectMember.name) {
|
|
2171
|
-
const
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
...
|
|
2176
|
-
properties: [...previousObject.properties ?? [], ...objectMember.properties ?? []]
|
|
2517
|
+
if (objectMember && !objectMember.name && acc !== void 0) {
|
|
2518
|
+
const accObject = narrowSchema(acc, "object");
|
|
2519
|
+
if (accObject && !accObject.name) {
|
|
2520
|
+
acc = createSchema({
|
|
2521
|
+
...accObject,
|
|
2522
|
+
properties: [...accObject.properties ?? [], ...objectMember.properties ?? []]
|
|
2177
2523
|
});
|
|
2178
|
-
|
|
2524
|
+
continue;
|
|
2179
2525
|
}
|
|
2180
2526
|
}
|
|
2181
|
-
acc
|
|
2182
|
-
|
|
2183
|
-
}
|
|
2527
|
+
if (acc !== void 0) yield acc;
|
|
2528
|
+
acc = member;
|
|
2529
|
+
}
|
|
2530
|
+
if (acc !== void 0) yield acc;
|
|
2531
|
+
}
|
|
2532
|
+
function mergeAdjacentObjects(members) {
|
|
2533
|
+
return [...mergeAdjacentObjectsLazy(members)];
|
|
2184
2534
|
}
|
|
2185
2535
|
/**
|
|
2186
2536
|
* Removes enum members that are covered by broader scalar primitives in the same union.
|
|
@@ -2212,7 +2562,7 @@ function setEnumName(propNode, parentName, propName, enumSuffix) {
|
|
|
2212
2562
|
const enumNode = narrowSchema(propNode, "enum");
|
|
2213
2563
|
if (enumNode?.primitive === "boolean") return {
|
|
2214
2564
|
...propNode,
|
|
2215
|
-
name:
|
|
2565
|
+
name: null
|
|
2216
2566
|
};
|
|
2217
2567
|
if (enumNode) return {
|
|
2218
2568
|
...propNode,
|
|
@@ -2221,16 +2571,20 @@ function setEnumName(propNode, parentName, propName, enumSuffix) {
|
|
|
2221
2571
|
return propNode;
|
|
2222
2572
|
}
|
|
2223
2573
|
//#endregion
|
|
2574
|
+
exports.applyDedupe = applyDedupe;
|
|
2575
|
+
exports.buildDedupePlan = buildDedupePlan;
|
|
2224
2576
|
exports.caseParams = caseParams;
|
|
2225
2577
|
exports.childName = childName;
|
|
2226
2578
|
exports.collect = collect;
|
|
2227
2579
|
exports.collectImports = collectImports;
|
|
2580
|
+
exports.collectLazy = collectLazy;
|
|
2228
2581
|
exports.collectReferencedSchemaNames = collectReferencedSchemaNames;
|
|
2229
2582
|
exports.collectUsedSchemaNames = collectUsedSchemaNames;
|
|
2230
2583
|
exports.containsCircularRef = containsCircularRef;
|
|
2231
2584
|
exports.createArrowFunction = createArrowFunction;
|
|
2232
2585
|
exports.createBreak = createBreak;
|
|
2233
2586
|
exports.createConst = createConst;
|
|
2587
|
+
exports.createContent = createContent;
|
|
2234
2588
|
exports.createDiscriminantNode = createDiscriminantNode;
|
|
2235
2589
|
exports.createExport = createExport;
|
|
2236
2590
|
exports.createFile = createFile;
|
|
@@ -2248,29 +2602,37 @@ exports.createParameterGroup = createParameterGroup;
|
|
|
2248
2602
|
exports.createParamsType = createParamsType;
|
|
2249
2603
|
exports.createPrinterFactory = createPrinterFactory;
|
|
2250
2604
|
exports.createProperty = createProperty;
|
|
2605
|
+
exports.createRequestBody = createRequestBody;
|
|
2251
2606
|
exports.createResponse = createResponse;
|
|
2252
2607
|
exports.createSchema = createSchema;
|
|
2253
2608
|
exports.createSource = createSource;
|
|
2609
|
+
exports.createStreamInput = createStreamInput;
|
|
2254
2610
|
exports.createText = createText;
|
|
2255
2611
|
exports.createType = createType;
|
|
2256
2612
|
exports.definePrinter = definePrinter;
|
|
2613
|
+
exports.defineSchemaDialect = defineSchemaDialect;
|
|
2614
|
+
exports.dispatch = dispatch;
|
|
2257
2615
|
exports.enumPropName = enumPropName;
|
|
2258
2616
|
exports.extractRefName = extractRefName;
|
|
2259
2617
|
exports.extractStringsFromNodes = extractStringsFromNodes;
|
|
2260
2618
|
exports.findCircularSchemas = findCircularSchemas;
|
|
2261
2619
|
exports.findDiscriminator = findDiscriminator;
|
|
2262
2620
|
exports.httpMethods = httpMethods;
|
|
2621
|
+
exports.isHttpOperationNode = isHttpOperationNode;
|
|
2263
2622
|
exports.isInputNode = isInputNode;
|
|
2264
2623
|
exports.isOperationNode = isOperationNode;
|
|
2265
2624
|
exports.isOutputNode = isOutputNode;
|
|
2266
2625
|
exports.isScalarPrimitive = isScalarPrimitive;
|
|
2626
|
+
exports.isSchemaEqual = isSchemaEqual;
|
|
2267
2627
|
exports.isSchemaNode = isSchemaNode;
|
|
2268
2628
|
exports.isStringType = isStringType;
|
|
2269
2629
|
exports.mediaTypes = mediaTypes;
|
|
2270
2630
|
exports.mergeAdjacentObjects = mergeAdjacentObjects;
|
|
2631
|
+
exports.mergeAdjacentObjectsLazy = mergeAdjacentObjectsLazy;
|
|
2271
2632
|
exports.narrowSchema = narrowSchema;
|
|
2272
2633
|
exports.nodeKinds = nodeKinds;
|
|
2273
2634
|
exports.resolveRefName = resolveRefName;
|
|
2635
|
+
exports.schemaSignature = schemaSignature;
|
|
2274
2636
|
exports.schemaTypes = schemaTypes;
|
|
2275
2637
|
exports.setDiscriminatorEnum = setDiscriminatorEnum;
|
|
2276
2638
|
exports.setEnumName = setEnumName;
|
|
@@ -2278,6 +2640,7 @@ exports.simplifyUnion = simplifyUnion;
|
|
|
2278
2640
|
exports.syncOptionality = syncOptionality;
|
|
2279
2641
|
exports.syncSchemaRef = syncSchemaRef;
|
|
2280
2642
|
exports.transform = transform;
|
|
2643
|
+
exports.update = update;
|
|
2281
2644
|
exports.walk = walk;
|
|
2282
2645
|
|
|
2283
2646
|
//# sourceMappingURL=index.cjs.map
|