@kubb/ast 5.0.0-beta.2 → 5.0.0-beta.21
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 +270 -206
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +118 -95
- package/dist/index.js +268 -207
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/factory.ts +15 -0
- package/src/guards.ts +3 -3
- package/src/index.ts +3 -2
- package/src/nodes/index.ts +1 -1
- package/src/nodes/operation.ts +1 -1
- package/src/nodes/response.ts +1 -1
- package/src/nodes/root.ts +72 -10
- package/src/nodes/schema.ts +9 -3
- package/src/printer.ts +15 -16
- package/src/refs.ts +4 -2
- package/src/resolvers.ts +4 -4
- package/src/transformers.ts +20 -15
- package/src/types.ts +1 -0
- package/src/utils.ts +89 -56
- package/src/visitor.ts +133 -181
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;
|
|
@@ -468,12 +508,6 @@ const isOperationNode = isKind("Operation");
|
|
|
468
508
|
* ```
|
|
469
509
|
*/
|
|
470
510
|
const isSchemaNode = isKind("Schema");
|
|
471
|
-
isKind("Property");
|
|
472
|
-
isKind("Parameter");
|
|
473
|
-
isKind("Response");
|
|
474
|
-
isKind("FunctionParameter");
|
|
475
|
-
isKind("ParameterGroup");
|
|
476
|
-
isKind("FunctionParameters");
|
|
477
511
|
//#endregion
|
|
478
512
|
//#region src/refs.ts
|
|
479
513
|
/**
|
|
@@ -539,32 +573,40 @@ function createLimit(concurrency) {
|
|
|
539
573
|
* // returns parameters, requestBody schema (if present), and responses
|
|
540
574
|
* ```
|
|
541
575
|
*/
|
|
542
|
-
function getChildren(node, recurse) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const
|
|
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;
|
|
576
|
+
function* getChildren(node, recurse) {
|
|
577
|
+
if (node.kind === "Input") {
|
|
578
|
+
yield* node.schemas;
|
|
579
|
+
yield* node.operations;
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
if (node.kind === "Output") return;
|
|
583
|
+
if (node.kind === "Operation") {
|
|
584
|
+
yield* node.parameters;
|
|
585
|
+
if (node.requestBody?.content) {
|
|
586
|
+
for (const c of node.requestBody.content) if (c.schema) yield c.schema;
|
|
559
587
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
588
|
+
yield* node.responses;
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (node.kind === "Schema") {
|
|
592
|
+
if (!recurse) return;
|
|
593
|
+
if ("properties" in node && node.properties.length > 0) yield* node.properties;
|
|
594
|
+
if ("items" in node && node.items) yield* node.items;
|
|
595
|
+
if ("members" in node && node.members) yield* node.members;
|
|
596
|
+
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) yield node.additionalProperties;
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (node.kind === "Property") {
|
|
600
|
+
yield node.schema;
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
if (node.kind === "Parameter") {
|
|
604
|
+
yield node.schema;
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (node.kind === "Response") {
|
|
608
|
+
if (node.schema) yield node.schema;
|
|
609
|
+
return;
|
|
568
610
|
}
|
|
569
611
|
}
|
|
570
612
|
/**
|
|
@@ -613,9 +655,6 @@ async function _walk(node, visitor, recurse, limit, parent) {
|
|
|
613
655
|
case "Response":
|
|
614
656
|
await limit(() => visitor.response?.(node, { parent }));
|
|
615
657
|
break;
|
|
616
|
-
case "FunctionParameter":
|
|
617
|
-
case "ParameterGroup":
|
|
618
|
-
case "FunctionParameters": break;
|
|
619
658
|
}
|
|
620
659
|
const children = getChildren(node, recurse);
|
|
621
660
|
for (const child of children) await _walk(child, visitor, recurse, limit, node);
|
|
@@ -623,118 +662,95 @@ async function _walk(node, visitor, recurse, limit, parent) {
|
|
|
623
662
|
function transform(node, options) {
|
|
624
663
|
const { depth, parent, ...visitor } = options;
|
|
625
664
|
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
|
-
parent: op
|
|
658
|
-
})),
|
|
659
|
-
requestBody: op.requestBody ? {
|
|
660
|
-
...op.requestBody,
|
|
661
|
-
content: op.requestBody.content?.map((c) => ({
|
|
662
|
-
...c,
|
|
663
|
-
schema: c.schema ? transform(c.schema, {
|
|
664
|
-
...options,
|
|
665
|
-
parent: op
|
|
666
|
-
}) : void 0
|
|
667
|
-
}))
|
|
668
|
-
} : void 0,
|
|
669
|
-
responses: op.responses.map((r) => transform(r, {
|
|
670
|
-
...options,
|
|
671
|
-
parent: op
|
|
665
|
+
if (node.kind === "Input") {
|
|
666
|
+
const input = visitor.input?.(node, { parent }) ?? node;
|
|
667
|
+
return {
|
|
668
|
+
...input,
|
|
669
|
+
schemas: input.schemas.map((s) => transform(s, {
|
|
670
|
+
...options,
|
|
671
|
+
parent: input
|
|
672
|
+
})),
|
|
673
|
+
operations: input.operations.map((op) => transform(op, {
|
|
674
|
+
...options,
|
|
675
|
+
parent: input
|
|
676
|
+
}))
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
if (node.kind === "Output") return visitor.output?.(node, { parent }) ?? node;
|
|
680
|
+
if (node.kind === "Operation") {
|
|
681
|
+
const op = visitor.operation?.(node, { parent }) ?? node;
|
|
682
|
+
return {
|
|
683
|
+
...op,
|
|
684
|
+
parameters: op.parameters.map((p) => transform(p, {
|
|
685
|
+
...options,
|
|
686
|
+
parent: op
|
|
687
|
+
})),
|
|
688
|
+
requestBody: op.requestBody ? {
|
|
689
|
+
...op.requestBody,
|
|
690
|
+
content: op.requestBody.content?.map((c) => ({
|
|
691
|
+
...c,
|
|
692
|
+
schema: c.schema ? transform(c.schema, {
|
|
693
|
+
...options,
|
|
694
|
+
parent: op
|
|
695
|
+
}) : void 0
|
|
672
696
|
}))
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
case "Schema": {
|
|
676
|
-
let schema = node;
|
|
677
|
-
const replaced = visitor.schema?.(schema, { parent });
|
|
678
|
-
if (replaced) schema = replaced;
|
|
679
|
-
const childOptions = {
|
|
697
|
+
} : void 0,
|
|
698
|
+
responses: op.responses.map((r) => transform(r, {
|
|
680
699
|
...options,
|
|
681
|
-
parent:
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
...param,
|
|
709
|
-
schema: transform(param.schema, {
|
|
710
|
-
...options,
|
|
711
|
-
parent: param
|
|
712
|
-
})
|
|
713
|
-
});
|
|
714
|
-
}
|
|
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;
|
|
700
|
+
parent: op
|
|
701
|
+
}))
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
if (node.kind === "Schema") {
|
|
705
|
+
const schema = visitor.schema?.(node, { parent }) ?? node;
|
|
706
|
+
const childOptions = {
|
|
707
|
+
...options,
|
|
708
|
+
parent: schema
|
|
709
|
+
};
|
|
710
|
+
return {
|
|
711
|
+
...schema,
|
|
712
|
+
..."properties" in schema && recurse ? { properties: schema.properties.map((p) => transform(p, childOptions)) } : {},
|
|
713
|
+
..."items" in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {},
|
|
714
|
+
..."members" in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {},
|
|
715
|
+
..."additionalProperties" in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true ? { additionalProperties: transform(schema.additionalProperties, childOptions) } : {}
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
if (node.kind === "Property") {
|
|
719
|
+
const prop = visitor.property?.(node, { parent }) ?? node;
|
|
720
|
+
return createProperty({
|
|
721
|
+
...prop,
|
|
722
|
+
schema: transform(prop.schema, {
|
|
723
|
+
...options,
|
|
724
|
+
parent: prop
|
|
725
|
+
})
|
|
726
|
+
});
|
|
732
727
|
}
|
|
728
|
+
if (node.kind === "Parameter") {
|
|
729
|
+
const param = visitor.parameter?.(node, { parent }) ?? node;
|
|
730
|
+
return createParameter({
|
|
731
|
+
...param,
|
|
732
|
+
schema: transform(param.schema, {
|
|
733
|
+
...options,
|
|
734
|
+
parent: param
|
|
735
|
+
})
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
if (node.kind === "Response") {
|
|
739
|
+
const response = visitor.response?.(node, { parent }) ?? node;
|
|
740
|
+
return {
|
|
741
|
+
...response,
|
|
742
|
+
schema: transform(response.schema, {
|
|
743
|
+
...options,
|
|
744
|
+
parent: response
|
|
745
|
+
})
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
return node;
|
|
733
749
|
}
|
|
734
750
|
/**
|
|
735
751
|
* Runs a depth-first synchronous collection pass.
|
|
736
752
|
*
|
|
737
|
-
* Non-`
|
|
753
|
+
* Non-`null` values returned by visitor callbacks are appended to the result.
|
|
738
754
|
*
|
|
739
755
|
* @example
|
|
740
756
|
* ```ts
|
|
@@ -751,10 +767,9 @@ function transform(node, options) {
|
|
|
751
767
|
* const values = collect(root, { depth: 'shallow', root: () => 'root' })
|
|
752
768
|
* ```
|
|
753
769
|
*/
|
|
754
|
-
function
|
|
770
|
+
function* collectLazy(node, options) {
|
|
755
771
|
const { depth, parent, ...visitor } = options;
|
|
756
772
|
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
|
|
757
|
-
const results = [];
|
|
758
773
|
let v;
|
|
759
774
|
switch (node.kind) {
|
|
760
775
|
case "Input":
|
|
@@ -778,16 +793,15 @@ function collect(node, options) {
|
|
|
778
793
|
case "Response":
|
|
779
794
|
v = visitor.response?.(node, { parent });
|
|
780
795
|
break;
|
|
781
|
-
case "FunctionParameter":
|
|
782
|
-
case "ParameterGroup":
|
|
783
|
-
case "FunctionParameters": break;
|
|
784
796
|
}
|
|
785
|
-
if (v
|
|
786
|
-
for (const child of getChildren(node, recurse))
|
|
797
|
+
if (v != null) yield v;
|
|
798
|
+
for (const child of getChildren(node, recurse)) yield* collectLazy(child, {
|
|
787
799
|
...options,
|
|
788
800
|
parent: node
|
|
789
|
-
})
|
|
790
|
-
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
function collect(node, options) {
|
|
804
|
+
return Array.from(collectLazy(node, options));
|
|
791
805
|
}
|
|
792
806
|
//#endregion
|
|
793
807
|
//#region src/utils.ts
|
|
@@ -841,15 +855,16 @@ function isStringType(node) {
|
|
|
841
855
|
* the desired casing while preserving `OperationNode.parameters` for other consumers.
|
|
842
856
|
* The input array is not mutated. When `casing` is not set, the original array is returned unchanged.
|
|
843
857
|
*/
|
|
858
|
+
const caseParamsMemo = memoize(/* @__PURE__ */ new WeakMap(), (params) => memoize(/* @__PURE__ */ new Map(), (casing) => params.map((param) => {
|
|
859
|
+
const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
|
|
860
|
+
return {
|
|
861
|
+
...param,
|
|
862
|
+
name: transformed
|
|
863
|
+
};
|
|
864
|
+
})));
|
|
844
865
|
function caseParams(params, casing) {
|
|
845
866
|
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
|
-
});
|
|
867
|
+
return caseParamsMemo(params)(casing);
|
|
853
868
|
}
|
|
854
869
|
/**
|
|
855
870
|
* Creates a single-property object schema used as a discriminator literal.
|
|
@@ -978,7 +993,7 @@ function createOperationParams(node, options) {
|
|
|
978
993
|
}));
|
|
979
994
|
} else {
|
|
980
995
|
if (pathParams.length) if (pathParamsType === "inlineSpread") {
|
|
981
|
-
const spreadType = resolver?.resolvePathParamsName(node, pathParams[0])
|
|
996
|
+
const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]);
|
|
982
997
|
params.push(createFunctionParameter({
|
|
983
998
|
name: pathName,
|
|
984
999
|
type: spreadType ? wrapType(spreadType) : void 0,
|
|
@@ -1054,13 +1069,13 @@ function buildGroupParam({ name, node, params, groupType, resolver, wrapType })
|
|
|
1054
1069
|
}
|
|
1055
1070
|
/**
|
|
1056
1071
|
* Derives a {@link ParamGroupType} from the resolver's group method.
|
|
1057
|
-
* Returns `
|
|
1072
|
+
* Returns `null` when the group name equals the individual param name (no real group).
|
|
1058
1073
|
*/
|
|
1059
1074
|
function resolveGroupType({ node, params, groupMethod, resolver }) {
|
|
1060
|
-
if (!params.length) return;
|
|
1075
|
+
if (!params.length) return null;
|
|
1061
1076
|
const firstParam = params[0];
|
|
1062
1077
|
const groupName = groupMethod.call(resolver, node, firstParam);
|
|
1063
|
-
if (groupName === resolver.resolveParamName(node, firstParam)) return;
|
|
1078
|
+
if (groupName === resolver.resolveParamName(node, firstParam)) return null;
|
|
1064
1079
|
const allOptional = params.every((p) => !p.required);
|
|
1065
1080
|
return {
|
|
1066
1081
|
type: createParamsType({
|
|
@@ -1180,6 +1195,13 @@ function combineExports(exports) {
|
|
|
1180
1195
|
function combineImports(imports, exports, source) {
|
|
1181
1196
|
const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
|
|
1182
1197
|
const isUsed = (importName) => !source || source.includes(importName) || exportedNames.has(importName);
|
|
1198
|
+
const importNameMemo = /* @__PURE__ */ new Map();
|
|
1199
|
+
const canonicalizeName = (n) => {
|
|
1200
|
+
if (typeof n === "string") return n;
|
|
1201
|
+
const key = `${n.propertyName}:${n.name ?? ""}`;
|
|
1202
|
+
if (!importNameMemo.has(key)) importNameMemo.set(key, n);
|
|
1203
|
+
return importNameMemo.get(key);
|
|
1204
|
+
};
|
|
1183
1205
|
const result = [];
|
|
1184
1206
|
const namedByPath = /* @__PURE__ */ new Map();
|
|
1185
1207
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1193,7 +1215,7 @@ function combineImports(imports, exports, source) {
|
|
|
1193
1215
|
const { path, isTypeOnly } = curr;
|
|
1194
1216
|
let { name } = curr;
|
|
1195
1217
|
if (Array.isArray(name)) {
|
|
1196
|
-
name = [...new Set(name)].filter((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName));
|
|
1218
|
+
name = [...new Set(name.map(canonicalizeName))].filter((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName));
|
|
1197
1219
|
if (!name.length) continue;
|
|
1198
1220
|
const key = pathTypeKey(path, isTypeOnly);
|
|
1199
1221
|
const existing = namedByPath.get(key);
|
|
@@ -1246,7 +1268,7 @@ function extractStringsFromNodes(nodes) {
|
|
|
1246
1268
|
/**
|
|
1247
1269
|
* Resolves the schema name of a ref node, falling back through `ref` → `name` → nested `schema.name`.
|
|
1248
1270
|
*
|
|
1249
|
-
* Returns `
|
|
1271
|
+
* Returns `null` for non-ref nodes or when no name can be resolved. Use this to get a schema's
|
|
1250
1272
|
* identifier for type definitions or error messages.
|
|
1251
1273
|
*
|
|
1252
1274
|
* @example
|
|
@@ -1256,9 +1278,9 @@ function extractStringsFromNodes(nodes) {
|
|
|
1256
1278
|
* ```
|
|
1257
1279
|
*/
|
|
1258
1280
|
function resolveRefName(node) {
|
|
1259
|
-
if (!node || node.type !== "ref") return
|
|
1260
|
-
if (node.ref) return extractRefName(node.ref) ?? node.name ?? node.schema?.name ??
|
|
1261
|
-
return node.name ?? node.schema?.name ??
|
|
1281
|
+
if (!node || node.type !== "ref") return null;
|
|
1282
|
+
if (node.ref) return extractRefName(node.ref) ?? node.name ?? node.schema?.name ?? null;
|
|
1283
|
+
return node.name ?? node.schema?.name ?? null;
|
|
1262
1284
|
}
|
|
1263
1285
|
/**
|
|
1264
1286
|
* Collects every named schema referenced (transitively) from a node via ref edges.
|
|
@@ -1280,14 +1302,19 @@ function resolveRefName(node) {
|
|
|
1280
1302
|
* }
|
|
1281
1303
|
* ```
|
|
1282
1304
|
*/
|
|
1283
|
-
|
|
1284
|
-
|
|
1305
|
+
const collectSchemaRefs = memoize(/* @__PURE__ */ new WeakMap(), (node) => {
|
|
1306
|
+
const refs = /* @__PURE__ */ new Set();
|
|
1285
1307
|
collect(node, { schema(child) {
|
|
1286
1308
|
if (child.type === "ref") {
|
|
1287
1309
|
const name = resolveRefName(child);
|
|
1288
|
-
if (name)
|
|
1310
|
+
if (name) refs.add(name);
|
|
1289
1311
|
}
|
|
1290
1312
|
} });
|
|
1313
|
+
return refs;
|
|
1314
|
+
});
|
|
1315
|
+
function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
1316
|
+
if (!node) return out;
|
|
1317
|
+
for (const name of collectSchemaRefs(node)) out.add(name);
|
|
1291
1318
|
return out;
|
|
1292
1319
|
}
|
|
1293
1320
|
/**
|
|
@@ -1303,10 +1330,10 @@ function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
|
1303
1330
|
*
|
|
1304
1331
|
* @example Only generate schemas referenced by included operations
|
|
1305
1332
|
* ```ts
|
|
1306
|
-
* const includedOps =
|
|
1307
|
-
* const allowed = collectUsedSchemaNames(includedOps,
|
|
1333
|
+
* const includedOps = operations.filter(op => resolver.resolveOptions(op, { options, include }) !== null)
|
|
1334
|
+
* const allowed = collectUsedSchemaNames(includedOps, schemas)
|
|
1308
1335
|
*
|
|
1309
|
-
* for (const schema of
|
|
1336
|
+
* for (const schema of schemas) {
|
|
1310
1337
|
* if (schema.name && !allowed.has(schema.name)) continue
|
|
1311
1338
|
* // … generate schema
|
|
1312
1339
|
* }
|
|
@@ -1314,11 +1341,12 @@ function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
|
|
|
1314
1341
|
*
|
|
1315
1342
|
* @example Check whether a specific schema is needed
|
|
1316
1343
|
* ```ts
|
|
1317
|
-
* const allowed = collectUsedSchemaNames(includedOps,
|
|
1344
|
+
* const allowed = collectUsedSchemaNames(includedOps, schemas)
|
|
1318
1345
|
* allowed.has('OrderStatus') // false when no included operation references OrderStatus
|
|
1319
1346
|
* ```
|
|
1320
1347
|
*/
|
|
1321
|
-
|
|
1348
|
+
const collectUsedSchemaNamesMemo = memoize(/* @__PURE__ */ new WeakMap(), (ops) => memoize(/* @__PURE__ */ new WeakMap(), (schemas) => computeUsedSchemaNames(ops, schemas)));
|
|
1349
|
+
function computeUsedSchemaNames(operations, schemas) {
|
|
1322
1350
|
const schemaMap = /* @__PURE__ */ new Map();
|
|
1323
1351
|
for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
|
|
1324
1352
|
const result = /* @__PURE__ */ new Set();
|
|
@@ -1330,22 +1358,17 @@ function collectUsedSchemaNames(operations, schemas) {
|
|
|
1330
1358
|
if (namedSchema) visitSchema(namedSchema);
|
|
1331
1359
|
}
|
|
1332
1360
|
}
|
|
1333
|
-
for (const op of operations) for (const schema of
|
|
1361
|
+
for (const op of operations) for (const schema of collectLazy(op, {
|
|
1334
1362
|
depth: "shallow",
|
|
1335
1363
|
schema: (node) => node
|
|
1336
1364
|
})) visitSchema(schema);
|
|
1337
1365
|
return result;
|
|
1338
1366
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
* Refs are followed by name only, keeping the algorithm linear in the schema graph size.
|
|
1345
|
-
*
|
|
1346
|
-
* @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
|
|
1347
|
-
*/
|
|
1348
|
-
function findCircularSchemas(schemas) {
|
|
1367
|
+
function collectUsedSchemaNames(operations, schemas) {
|
|
1368
|
+
return collectUsedSchemaNamesMemo(operations)(schemas);
|
|
1369
|
+
}
|
|
1370
|
+
const EMPTY_CIRCULAR_SET = /* @__PURE__ */ new Set();
|
|
1371
|
+
const findCircularSchemasMemo = memoize(/* @__PURE__ */ new WeakMap(), (schemas) => {
|
|
1349
1372
|
const graph = /* @__PURE__ */ new Map();
|
|
1350
1373
|
for (const schema of schemas) {
|
|
1351
1374
|
if (!schema.name) continue;
|
|
@@ -1368,6 +1391,19 @@ function findCircularSchemas(schemas) {
|
|
|
1368
1391
|
}
|
|
1369
1392
|
}
|
|
1370
1393
|
return circular;
|
|
1394
|
+
});
|
|
1395
|
+
/**
|
|
1396
|
+
* Identifies all schemas that participate in circular dependency chains, including direct self-loops.
|
|
1397
|
+
*
|
|
1398
|
+
* Returns a Set of schema names with circular dependencies. Use this to wrap recursive schema positions
|
|
1399
|
+
* in deferred constructs (lazy getter, `z.lazy(() => …)`) to prevent infinite recursion when generated code runs.
|
|
1400
|
+
* Refs are followed by name only, keeping the algorithm linear in the schema graph size.
|
|
1401
|
+
*
|
|
1402
|
+
* @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
|
|
1403
|
+
*/
|
|
1404
|
+
function findCircularSchemas(schemas) {
|
|
1405
|
+
if (schemas.length === 0) return EMPTY_CIRCULAR_SET;
|
|
1406
|
+
return findCircularSchemasMemo(schemas);
|
|
1371
1407
|
}
|
|
1372
1408
|
/**
|
|
1373
1409
|
* Type guard returning `true` when a schema or anything nested within it contains a ref to a circular schema.
|
|
@@ -1379,11 +1415,12 @@ function findCircularSchemas(schemas) {
|
|
|
1379
1415
|
*/
|
|
1380
1416
|
function containsCircularRef(node, { circularSchemas, excludeName }) {
|
|
1381
1417
|
if (!node || circularSchemas.size === 0) return false;
|
|
1382
|
-
|
|
1383
|
-
if (child.type !== "ref") return
|
|
1418
|
+
for (const _ of collectLazy(node, { schema(child) {
|
|
1419
|
+
if (child.type !== "ref") return null;
|
|
1384
1420
|
const name = resolveRefName(child);
|
|
1385
|
-
return name && name !== excludeName && circularSchemas.has(name) ? true :
|
|
1386
|
-
} })
|
|
1421
|
+
return name && name !== excludeName && circularSchemas.has(name) ? true : null;
|
|
1422
|
+
} })) return true;
|
|
1423
|
+
return false;
|
|
1387
1424
|
}
|
|
1388
1425
|
//#endregion
|
|
1389
1426
|
//#region src/factory.ts
|
|
@@ -1420,11 +1457,31 @@ function createInput(overrides = {}) {
|
|
|
1420
1457
|
return {
|
|
1421
1458
|
schemas: [],
|
|
1422
1459
|
operations: [],
|
|
1460
|
+
meta: {
|
|
1461
|
+
circularNames: [],
|
|
1462
|
+
enumNames: []
|
|
1463
|
+
},
|
|
1423
1464
|
...overrides,
|
|
1424
1465
|
kind: "Input"
|
|
1425
1466
|
};
|
|
1426
1467
|
}
|
|
1427
1468
|
/**
|
|
1469
|
+
* Creates an `InputStreamNode` from pre-built `AsyncIterable` sources.
|
|
1470
|
+
*
|
|
1471
|
+
* @example
|
|
1472
|
+
* ```ts
|
|
1473
|
+
* const node = createStreamInput(schemasIterable, operationsIterable, { title: 'My API' })
|
|
1474
|
+
* ```
|
|
1475
|
+
*/
|
|
1476
|
+
function createStreamInput(schemas, operations, meta) {
|
|
1477
|
+
return {
|
|
1478
|
+
kind: "Input",
|
|
1479
|
+
schemas,
|
|
1480
|
+
operations,
|
|
1481
|
+
meta
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1428
1485
|
* Creates an `OutputNode` with a stable default for `files`.
|
|
1429
1486
|
*
|
|
1430
1487
|
* @example
|
|
@@ -2063,7 +2120,7 @@ function createPrinterFactory(getKey) {
|
|
|
2063
2120
|
options: resolvedOptions,
|
|
2064
2121
|
transform: (node) => {
|
|
2065
2122
|
const key = getKey(node);
|
|
2066
|
-
if (key ===
|
|
2123
|
+
if (key === null) return null;
|
|
2067
2124
|
const handler = nodes[key];
|
|
2068
2125
|
if (!handler) return null;
|
|
2069
2126
|
return handler.call(context, node);
|
|
@@ -2100,10 +2157,10 @@ function enumPropName(parentName, propName, enumSuffix) {
|
|
|
2100
2157
|
function collectImports({ node, nameMapping, resolve }) {
|
|
2101
2158
|
return collect(node, { schema(schemaNode) {
|
|
2102
2159
|
const schemaRef = narrowSchema(schemaNode, "ref");
|
|
2103
|
-
if (!schemaRef?.ref) return;
|
|
2160
|
+
if (!schemaRef?.ref) return null;
|
|
2104
2161
|
const rawName = extractRefName(schemaRef.ref);
|
|
2105
2162
|
const result = resolve(nameMapping.get(rawName) ?? rawName);
|
|
2106
|
-
if (!result) return;
|
|
2163
|
+
if (!result) return null;
|
|
2107
2164
|
return result;
|
|
2108
2165
|
} });
|
|
2109
2166
|
}
|
|
@@ -2157,23 +2214,27 @@ function setDiscriminatorEnum({ node, propertyName, values, enumName }) {
|
|
|
2157
2214
|
* ])
|
|
2158
2215
|
* ```
|
|
2159
2216
|
*/
|
|
2160
|
-
function
|
|
2161
|
-
|
|
2217
|
+
function* mergeAdjacentObjectsLazy(members) {
|
|
2218
|
+
let acc;
|
|
2219
|
+
for (const member of members) {
|
|
2162
2220
|
const objectMember = narrowSchema(member, "object");
|
|
2163
|
-
if (objectMember && !objectMember.name) {
|
|
2164
|
-
const
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
...
|
|
2169
|
-
properties: [...previousObject.properties ?? [], ...objectMember.properties ?? []]
|
|
2221
|
+
if (objectMember && !objectMember.name && acc !== void 0) {
|
|
2222
|
+
const accObject = narrowSchema(acc, "object");
|
|
2223
|
+
if (accObject && !accObject.name) {
|
|
2224
|
+
acc = createSchema({
|
|
2225
|
+
...accObject,
|
|
2226
|
+
properties: [...accObject.properties ?? [], ...objectMember.properties ?? []]
|
|
2170
2227
|
});
|
|
2171
|
-
|
|
2228
|
+
continue;
|
|
2172
2229
|
}
|
|
2173
2230
|
}
|
|
2174
|
-
acc
|
|
2175
|
-
|
|
2176
|
-
}
|
|
2231
|
+
if (acc !== void 0) yield acc;
|
|
2232
|
+
acc = member;
|
|
2233
|
+
}
|
|
2234
|
+
if (acc !== void 0) yield acc;
|
|
2235
|
+
}
|
|
2236
|
+
function mergeAdjacentObjects(members) {
|
|
2237
|
+
return [...mergeAdjacentObjectsLazy(members)];
|
|
2177
2238
|
}
|
|
2178
2239
|
/**
|
|
2179
2240
|
* Removes enum members that are covered by broader scalar primitives in the same union.
|
|
@@ -2205,7 +2266,7 @@ function setEnumName(propNode, parentName, propName, enumSuffix) {
|
|
|
2205
2266
|
const enumNode = narrowSchema(propNode, "enum");
|
|
2206
2267
|
if (enumNode?.primitive === "boolean") return {
|
|
2207
2268
|
...propNode,
|
|
2208
|
-
name:
|
|
2269
|
+
name: null
|
|
2209
2270
|
};
|
|
2210
2271
|
if (enumNode) return {
|
|
2211
2272
|
...propNode,
|
|
@@ -2218,6 +2279,7 @@ exports.caseParams = caseParams;
|
|
|
2218
2279
|
exports.childName = childName;
|
|
2219
2280
|
exports.collect = collect;
|
|
2220
2281
|
exports.collectImports = collectImports;
|
|
2282
|
+
exports.collectLazy = collectLazy;
|
|
2221
2283
|
exports.collectReferencedSchemaNames = collectReferencedSchemaNames;
|
|
2222
2284
|
exports.collectUsedSchemaNames = collectUsedSchemaNames;
|
|
2223
2285
|
exports.containsCircularRef = containsCircularRef;
|
|
@@ -2244,6 +2306,7 @@ exports.createProperty = createProperty;
|
|
|
2244
2306
|
exports.createResponse = createResponse;
|
|
2245
2307
|
exports.createSchema = createSchema;
|
|
2246
2308
|
exports.createSource = createSource;
|
|
2309
|
+
exports.createStreamInput = createStreamInput;
|
|
2247
2310
|
exports.createText = createText;
|
|
2248
2311
|
exports.createType = createType;
|
|
2249
2312
|
exports.definePrinter = definePrinter;
|
|
@@ -2261,6 +2324,7 @@ exports.isSchemaNode = isSchemaNode;
|
|
|
2261
2324
|
exports.isStringType = isStringType;
|
|
2262
2325
|
exports.mediaTypes = mediaTypes;
|
|
2263
2326
|
exports.mergeAdjacentObjects = mergeAdjacentObjects;
|
|
2327
|
+
exports.mergeAdjacentObjectsLazy = mergeAdjacentObjectsLazy;
|
|
2264
2328
|
exports.narrowSchema = narrowSchema;
|
|
2265
2329
|
exports.nodeKinds = nodeKinds;
|
|
2266
2330
|
exports.resolveRefName = resolveRefName;
|