@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/src/utils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { camelCase, isValidVarName } from '@internals/utils'
|
|
1
|
+
import { camelCase, isValidVarName, memoize } from '@internals/utils'
|
|
2
2
|
|
|
3
3
|
import { createFunctionParameter, createFunctionParameters, createParameterGroup, createParamsType, createProperty, createSchema } from './factory.ts'
|
|
4
4
|
import { narrowSchema } from './guards.ts'
|
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
} from './nodes/index.ts'
|
|
18
18
|
import type { SchemaType } from './nodes/schema.ts'
|
|
19
19
|
import { extractRefName } from './refs.ts'
|
|
20
|
-
import { collect } from './visitor.ts'
|
|
20
|
+
import { collect, collectLazy } from './visitor.ts'
|
|
21
21
|
|
|
22
22
|
const plainStringTypes = new Set<SchemaType>(['string', 'uuid', 'email', 'url', 'datetime'] as const)
|
|
23
23
|
|
|
@@ -74,16 +74,18 @@ export function isStringType(node: SchemaNode): boolean {
|
|
|
74
74
|
* the desired casing while preserving `OperationNode.parameters` for other consumers.
|
|
75
75
|
* The input array is not mutated. When `casing` is not set, the original array is returned unchanged.
|
|
76
76
|
*/
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
const caseParamsMemo = memoize(new WeakMap<Array<ParameterNode>, (casing: string) => Array<ParameterNode>>(), (params) =>
|
|
78
|
+
memoize(new Map<string, Array<ParameterNode>>(), (casing: string) =>
|
|
79
|
+
params.map((param) => {
|
|
80
|
+
const transformed = casing === 'camelcase' || !isValidVarName(param.name) ? camelCase(param.name) : param.name
|
|
81
|
+
return { ...param, name: transformed }
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
)
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return { ...param, name: transformed }
|
|
86
|
-
})
|
|
86
|
+
export function caseParams(params: Array<ParameterNode>, casing: 'camelcase' | undefined): Array<ParameterNode> {
|
|
87
|
+
if (!casing) return params
|
|
88
|
+
return caseParamsMemo(params)(casing)
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
/**
|
|
@@ -392,7 +394,7 @@ export function createOperationParams(node: OperationNode, options: CreateOperat
|
|
|
392
394
|
} else {
|
|
393
395
|
if (pathParams.length) {
|
|
394
396
|
if (pathParamsType === 'inlineSpread') {
|
|
395
|
-
const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]!)
|
|
397
|
+
const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]!)
|
|
396
398
|
params.push(
|
|
397
399
|
createFunctionParameter({
|
|
398
400
|
name: pathName,
|
|
@@ -474,7 +476,7 @@ function buildGroupParam({
|
|
|
474
476
|
name: string
|
|
475
477
|
node: OperationNode
|
|
476
478
|
params: Array<ParameterNode>
|
|
477
|
-
groupType: ParamGroupType | undefined
|
|
479
|
+
groupType: ParamGroupType | null | undefined
|
|
478
480
|
resolver: OperationParamsResolver | undefined
|
|
479
481
|
wrapType: (type: string) => ParamsTypeNode
|
|
480
482
|
}): Array<FunctionParameterNode> {
|
|
@@ -496,7 +498,7 @@ function buildGroupParam({
|
|
|
496
498
|
|
|
497
499
|
/**
|
|
498
500
|
* Derives a {@link ParamGroupType} from the resolver's group method.
|
|
499
|
-
* Returns `
|
|
501
|
+
* Returns `null` when the group name equals the individual param name (no real group).
|
|
500
502
|
*/
|
|
501
503
|
function resolveGroupType({
|
|
502
504
|
node,
|
|
@@ -508,14 +510,14 @@ function resolveGroupType({
|
|
|
508
510
|
params: Array<ParameterNode>
|
|
509
511
|
groupMethod: (_node: OperationNode, _param: ParameterNode) => string
|
|
510
512
|
resolver: OperationParamsResolver
|
|
511
|
-
}): ParamGroupType |
|
|
513
|
+
}): ParamGroupType | null {
|
|
512
514
|
if (!params.length) {
|
|
513
|
-
return
|
|
515
|
+
return null
|
|
514
516
|
}
|
|
515
517
|
const firstParam = params[0]!
|
|
516
518
|
const groupName = groupMethod.call(resolver, node, firstParam)
|
|
517
519
|
if (groupName === resolver.resolveParamName(node, firstParam)) {
|
|
518
|
-
return
|
|
520
|
+
return null
|
|
519
521
|
}
|
|
520
522
|
const allOptional = params.every((p) => !p.required)
|
|
521
523
|
return {
|
|
@@ -554,15 +556,15 @@ function sourceKey(source: SourceNode): string {
|
|
|
554
556
|
return `${nameKey}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`
|
|
555
557
|
}
|
|
556
558
|
|
|
557
|
-
function pathTypeKey(path: string, isTypeOnly: boolean | undefined): string {
|
|
559
|
+
function pathTypeKey(path: string, isTypeOnly: boolean | null | undefined): string {
|
|
558
560
|
return `${path}:${isTypeOnly ?? false}`
|
|
559
561
|
}
|
|
560
562
|
|
|
561
|
-
function exportKey(path: string, name: string | undefined, isTypeOnly: boolean | undefined, asAlias: boolean | undefined): string {
|
|
563
|
+
function exportKey(path: string, name: string | null | undefined, isTypeOnly: boolean | null | undefined, asAlias: boolean | null | undefined): string {
|
|
562
564
|
return `${path}:${name ?? ''}:${isTypeOnly ?? false}:${asAlias ?? ''}`
|
|
563
565
|
}
|
|
564
566
|
|
|
565
|
-
function importKey(path: string, name: string | undefined, isTypeOnly: boolean | undefined): string {
|
|
567
|
+
function importKey(path: string, name: string | null | undefined, isTypeOnly: boolean | null | undefined): string {
|
|
566
568
|
return `${path}:${name ?? ''}:${isTypeOnly ?? false}`
|
|
567
569
|
}
|
|
568
570
|
|
|
@@ -570,7 +572,7 @@ function importKey(path: string, name: string | undefined, isTypeOnly: boolean |
|
|
|
570
572
|
* Computes a multi-level sort key for exports and imports:
|
|
571
573
|
* non-array names first (wildcards/namespace aliases); type-only before value; alphabetical path; unnamed before named.
|
|
572
574
|
*/
|
|
573
|
-
function sortKey(node: { name?: string | Array<unknown
|
|
575
|
+
function sortKey(node: { name?: string | Array<unknown> | null; isTypeOnly?: boolean | null; path: string }): string {
|
|
574
576
|
const isArray = Array.isArray(node.name) ? '1' : '0'
|
|
575
577
|
const typeOnly = node.isTypeOnly ? '0' : '1'
|
|
576
578
|
const hasName = node.name != null ? '1' : '0'
|
|
@@ -592,6 +594,17 @@ export function combineSources(sources: Array<SourceNode>): Array<SourceNode> {
|
|
|
592
594
|
return [...seen.values()]
|
|
593
595
|
}
|
|
594
596
|
|
|
597
|
+
/**
|
|
598
|
+
* Merges `incoming` names into `existing`, preserving order and dropping duplicates.
|
|
599
|
+
*
|
|
600
|
+
* Shared by `combineExports` and `combineImports` for the same-path name-merge case.
|
|
601
|
+
*/
|
|
602
|
+
function mergeNameArrays<TName>(existing: Array<TName>, incoming: Array<TName>): Array<TName> {
|
|
603
|
+
const merged = new Set(existing)
|
|
604
|
+
for (const name of incoming) merged.add(name)
|
|
605
|
+
return [...merged]
|
|
606
|
+
}
|
|
607
|
+
|
|
595
608
|
/**
|
|
596
609
|
* Deduplicates and merges `ExportNode` objects by path and type.
|
|
597
610
|
*
|
|
@@ -619,9 +632,7 @@ export function combineExports(exports: Array<ExportNode>): Array<ExportNode> {
|
|
|
619
632
|
const existing = namedByPath.get(key)
|
|
620
633
|
|
|
621
634
|
if (existing && Array.isArray(existing.name)) {
|
|
622
|
-
|
|
623
|
-
for (const n of name) merged.add(n)
|
|
624
|
-
existing.name = [...merged]
|
|
635
|
+
existing.name = mergeNameArrays(existing.name, name)
|
|
625
636
|
} else {
|
|
626
637
|
const newItem: ExportNode = { ...curr, name: [...new Set(name)] }
|
|
627
638
|
result.push(newItem)
|
|
@@ -662,6 +673,17 @@ export function combineImports(imports: Array<ImportNode>, exports: Array<Export
|
|
|
662
673
|
return importNameMemo.get(key)!
|
|
663
674
|
}
|
|
664
675
|
|
|
676
|
+
// Paths that keep at least one used named import. A default import from such a path is retained
|
|
677
|
+
// even when its binding can't be found in `source` — e.g. a generated `client` default import
|
|
678
|
+
// alongside `import type { Client } from <same path>`, where merged grouped output omits the body.
|
|
679
|
+
const pathsWithUsedNamedImport = new Set<string>()
|
|
680
|
+
for (const node of imports) {
|
|
681
|
+
if (!Array.isArray(node.name)) continue
|
|
682
|
+
if (node.name.some((item) => (typeof item === 'string' ? isUsed(item) : isUsed(item.name ?? item.propertyName)))) {
|
|
683
|
+
pathsWithUsedNamedImport.add(node.path)
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
665
687
|
const result: Array<ImportNode> = []
|
|
666
688
|
// Accumulates array-named imports keyed by `path:isTypeOnly` for name-merging
|
|
667
689
|
const namedByPath = new Map<string, ImportNode>()
|
|
@@ -686,16 +708,14 @@ export function combineImports(imports: Array<ImportNode>, exports: Array<Export
|
|
|
686
708
|
const existing = namedByPath.get(key)
|
|
687
709
|
|
|
688
710
|
if (existing && Array.isArray(existing.name)) {
|
|
689
|
-
|
|
690
|
-
for (const n of name) merged.add(n)
|
|
691
|
-
existing.name = [...merged]
|
|
711
|
+
existing.name = mergeNameArrays(existing.name, name)
|
|
692
712
|
} else {
|
|
693
713
|
const newItem: ImportNode = { ...curr, name }
|
|
694
714
|
result.push(newItem)
|
|
695
715
|
namedByPath.set(key, newItem)
|
|
696
716
|
}
|
|
697
717
|
} else {
|
|
698
|
-
if (name && !isUsed(name)) continue
|
|
718
|
+
if (name && !isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue
|
|
699
719
|
|
|
700
720
|
const key = importKey(path, name, isTypeOnly)
|
|
701
721
|
if (!seen.has(key)) {
|
|
@@ -723,13 +743,18 @@ export function extractStringsFromNodes(nodes: Array<CodeNode> | undefined): str
|
|
|
723
743
|
if (node.kind === 'Text') return node.value
|
|
724
744
|
if (node.kind === 'Break') return ''
|
|
725
745
|
if (node.kind === 'Jsx') return node.value
|
|
726
|
-
|
|
746
|
+
|
|
747
|
+
const parts: Array<string> = []
|
|
748
|
+
|
|
727
749
|
if ('params' in node && node.params) parts.push(node.params)
|
|
728
750
|
if ('generics' in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(', ') : node.generics)
|
|
729
751
|
if ('returnType' in node && node.returnType) parts.push(node.returnType)
|
|
730
752
|
if ('type' in node && typeof node.type === 'string') parts.push(node.type)
|
|
753
|
+
|
|
731
754
|
const nested = extractStringsFromNodes(node.nodes)
|
|
755
|
+
|
|
732
756
|
if (nested) parts.push(nested)
|
|
757
|
+
|
|
733
758
|
return parts.join('\n')
|
|
734
759
|
})
|
|
735
760
|
.filter(Boolean)
|
|
@@ -739,7 +764,7 @@ export function extractStringsFromNodes(nodes: Array<CodeNode> | undefined): str
|
|
|
739
764
|
/**
|
|
740
765
|
* Resolves the schema name of a ref node, falling back through `ref` → `name` → nested `schema.name`.
|
|
741
766
|
*
|
|
742
|
-
* Returns `
|
|
767
|
+
* Returns `null` for non-ref nodes or when no name can be resolved. Use this to get a schema's
|
|
743
768
|
* identifier for type definitions or error messages.
|
|
744
769
|
*
|
|
745
770
|
* @example
|
|
@@ -748,11 +773,11 @@ export function extractStringsFromNodes(nodes: Array<CodeNode> | undefined): str
|
|
|
748
773
|
* // => 'Pet'
|
|
749
774
|
* ```
|
|
750
775
|
*/
|
|
751
|
-
export function resolveRefName(node: SchemaNode | undefined): string |
|
|
752
|
-
if (!node || node.type !== 'ref') return
|
|
753
|
-
if (node.ref) return extractRefName(node.ref) ?? node.name ?? node.schema?.name ??
|
|
776
|
+
export function resolveRefName(node: SchemaNode | undefined): string | null {
|
|
777
|
+
if (!node || node.type !== 'ref') return null
|
|
778
|
+
if (node.ref) return extractRefName(node.ref) ?? node.name ?? node.schema?.name ?? null
|
|
754
779
|
|
|
755
|
-
return node.name ?? node.schema?.name ??
|
|
780
|
+
return node.name ?? node.schema?.name ?? null
|
|
756
781
|
}
|
|
757
782
|
|
|
758
783
|
/**
|
|
@@ -775,18 +800,22 @@ export function resolveRefName(node: SchemaNode | undefined): string | undefined
|
|
|
775
800
|
* }
|
|
776
801
|
* ```
|
|
777
802
|
*/
|
|
778
|
-
|
|
779
|
-
|
|
803
|
+
const collectSchemaRefs = memoize(new WeakMap<SchemaNode, ReadonlySet<string>>(), (node: SchemaNode): ReadonlySet<string> => {
|
|
804
|
+
const refs = new Set<string>()
|
|
780
805
|
collect<void>(node, {
|
|
781
806
|
schema(child) {
|
|
782
807
|
if (child.type === 'ref') {
|
|
783
808
|
const name = resolveRefName(child)
|
|
784
|
-
|
|
785
|
-
if (name) out.add(name)
|
|
809
|
+
if (name) refs.add(name)
|
|
786
810
|
}
|
|
787
|
-
return undefined
|
|
788
811
|
},
|
|
789
812
|
})
|
|
813
|
+
return refs
|
|
814
|
+
})
|
|
815
|
+
|
|
816
|
+
export function collectReferencedSchemaNames(node: SchemaNode | undefined, out: Set<string> = new Set()): Set<string> {
|
|
817
|
+
if (!node) return out
|
|
818
|
+
for (const name of collectSchemaRefs(node)) out.add(name)
|
|
790
819
|
return out
|
|
791
820
|
}
|
|
792
821
|
|
|
@@ -803,10 +832,10 @@ export function collectReferencedSchemaNames(node: SchemaNode | undefined, out:
|
|
|
803
832
|
*
|
|
804
833
|
* @example Only generate schemas referenced by included operations
|
|
805
834
|
* ```ts
|
|
806
|
-
* const includedOps =
|
|
807
|
-
* const allowed = collectUsedSchemaNames(includedOps,
|
|
835
|
+
* const includedOps = operations.filter(op => resolver.resolveOptions(op, { options, include }) !== null)
|
|
836
|
+
* const allowed = collectUsedSchemaNames(includedOps, schemas)
|
|
808
837
|
*
|
|
809
|
-
* for (const schema of
|
|
838
|
+
* for (const schema of schemas) {
|
|
810
839
|
* if (schema.name && !allowed.has(schema.name)) continue
|
|
811
840
|
* // … generate schema
|
|
812
841
|
* }
|
|
@@ -814,16 +843,18 @@ export function collectReferencedSchemaNames(node: SchemaNode | undefined, out:
|
|
|
814
843
|
*
|
|
815
844
|
* @example Check whether a specific schema is needed
|
|
816
845
|
* ```ts
|
|
817
|
-
* const allowed = collectUsedSchemaNames(includedOps,
|
|
846
|
+
* const allowed = collectUsedSchemaNames(includedOps, schemas)
|
|
818
847
|
* allowed.has('OrderStatus') // false when no included operation references OrderStatus
|
|
819
848
|
* ```
|
|
820
849
|
*/
|
|
821
|
-
|
|
850
|
+
const collectUsedSchemaNamesMemo = memoize(new WeakMap<ReadonlyArray<OperationNode>, (schemas: ReadonlyArray<SchemaNode>) => Set<string>>(), (ops) =>
|
|
851
|
+
memoize(new WeakMap<ReadonlyArray<SchemaNode>, Set<string>>(), (schemas) => computeUsedSchemaNames(ops, schemas)),
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
function computeUsedSchemaNames(operations: ReadonlyArray<OperationNode>, schemas: ReadonlyArray<SchemaNode>): Set<string> {
|
|
822
855
|
const schemaMap = new Map<string, SchemaNode>()
|
|
823
856
|
for (const schema of schemas) {
|
|
824
|
-
if (schema.name)
|
|
825
|
-
schemaMap.set(schema.name, schema)
|
|
826
|
-
}
|
|
857
|
+
if (schema.name) schemaMap.set(schema.name, schema)
|
|
827
858
|
}
|
|
828
859
|
|
|
829
860
|
const result = new Set<string>()
|
|
@@ -834,15 +865,13 @@ export function collectUsedSchemaNames(operations: ReadonlyArray<OperationNode>,
|
|
|
834
865
|
if (!result.has(name)) {
|
|
835
866
|
result.add(name)
|
|
836
867
|
const namedSchema = schemaMap.get(name)
|
|
837
|
-
if (namedSchema)
|
|
838
|
-
visitSchema(namedSchema)
|
|
839
|
-
}
|
|
868
|
+
if (namedSchema) visitSchema(namedSchema)
|
|
840
869
|
}
|
|
841
870
|
}
|
|
842
871
|
}
|
|
843
872
|
|
|
844
873
|
for (const op of operations) {
|
|
845
|
-
for (const schema of
|
|
874
|
+
for (const schema of collectLazy<SchemaNode>(op, { depth: 'shallow', schema: (node) => node })) {
|
|
846
875
|
visitSchema(schema)
|
|
847
876
|
}
|
|
848
877
|
}
|
|
@@ -850,16 +879,13 @@ export function collectUsedSchemaNames(operations: ReadonlyArray<OperationNode>,
|
|
|
850
879
|
return result
|
|
851
880
|
}
|
|
852
881
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
* @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
|
|
861
|
-
*/
|
|
862
|
-
export function findCircularSchemas(schemas: ReadonlyArray<SchemaNode>): Set<string> {
|
|
882
|
+
export function collectUsedSchemaNames(operations: ReadonlyArray<OperationNode>, schemas: ReadonlyArray<SchemaNode>): Set<string> {
|
|
883
|
+
return collectUsedSchemaNamesMemo(operations)(schemas)
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const EMPTY_CIRCULAR_SET = new Set<string>()
|
|
887
|
+
|
|
888
|
+
const findCircularSchemasMemo = memoize(new WeakMap<ReadonlyArray<SchemaNode>, Set<string>>(), (schemas: ReadonlyArray<SchemaNode>): Set<string> => {
|
|
863
889
|
const graph = new Map<string, Set<string>>()
|
|
864
890
|
|
|
865
891
|
for (const schema of schemas) {
|
|
@@ -870,7 +896,7 @@ export function findCircularSchemas(schemas: ReadonlyArray<SchemaNode>): Set<str
|
|
|
870
896
|
const circular = new Set<string>()
|
|
871
897
|
for (const start of graph.keys()) {
|
|
872
898
|
const visited = new Set<string>()
|
|
873
|
-
const stack: string
|
|
899
|
+
const stack: Array<string> = [...(graph.get(start) ?? [])]
|
|
874
900
|
while (stack.length > 0) {
|
|
875
901
|
const node = stack.pop()!
|
|
876
902
|
if (node === start) {
|
|
@@ -886,6 +912,20 @@ export function findCircularSchemas(schemas: ReadonlyArray<SchemaNode>): Set<str
|
|
|
886
912
|
}
|
|
887
913
|
|
|
888
914
|
return circular
|
|
915
|
+
})
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Identifies all schemas that participate in circular dependency chains, including direct self-loops.
|
|
919
|
+
*
|
|
920
|
+
* Returns a Set of schema names with circular dependencies. Use this to wrap recursive schema positions
|
|
921
|
+
* in deferred constructs (lazy getter, `z.lazy(() => …)`) to prevent infinite recursion when generated code runs.
|
|
922
|
+
* Refs are followed by name only, keeping the algorithm linear in the schema graph size.
|
|
923
|
+
*
|
|
924
|
+
* @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
|
|
925
|
+
*/
|
|
926
|
+
export function findCircularSchemas(schemas: ReadonlyArray<SchemaNode>): Set<string> {
|
|
927
|
+
if (schemas.length === 0) return EMPTY_CIRCULAR_SET
|
|
928
|
+
return findCircularSchemasMemo(schemas)
|
|
889
929
|
}
|
|
890
930
|
|
|
891
931
|
/**
|
|
@@ -902,14 +942,15 @@ export function containsCircularRef(
|
|
|
902
942
|
): boolean {
|
|
903
943
|
if (!node || circularSchemas.size === 0) return false
|
|
904
944
|
|
|
905
|
-
const
|
|
945
|
+
for (const _ of collectLazy<true>(node, {
|
|
906
946
|
schema(child) {
|
|
907
|
-
if (child.type !== 'ref') return
|
|
947
|
+
if (child.type !== 'ref') return null
|
|
908
948
|
const name = resolveRefName(child)
|
|
909
|
-
|
|
910
|
-
return name && name !== excludeName && circularSchemas.has(name) ? true : undefined
|
|
949
|
+
return name && name !== excludeName && circularSchemas.has(name) ? true : null
|
|
911
950
|
},
|
|
912
|
-
})
|
|
951
|
+
})) {
|
|
952
|
+
return true
|
|
953
|
+
}
|
|
913
954
|
|
|
914
|
-
return
|
|
955
|
+
return false
|
|
915
956
|
}
|