@kurotako/ir 0.1.0 → 0.2.0
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/CHANGELOG.md +17 -0
- package/dist/index.cjs +452 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -54
- package/dist/index.d.ts +104 -54
- package/dist/index.js +445 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -55,11 +55,29 @@ var IndexTypeSchema = v.picklist([
|
|
|
55
55
|
"brin",
|
|
56
56
|
"spgist"
|
|
57
57
|
]);
|
|
58
|
-
var FieldTypeSchema = v.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
var FieldTypeSchema = v.lazy(
|
|
59
|
+
() => v.variant("kind", [
|
|
60
|
+
v.object({ kind: v.literal("scalar"), scalar: ScalarTypeSchema }),
|
|
61
|
+
v.object({ kind: v.literal("enum"), ref: v.string() }),
|
|
62
|
+
v.object({ kind: v.literal("unknown"), hint: v.optional(v.string()) }),
|
|
63
|
+
v.object({ kind: v.literal("ref"), ref: v.string() }),
|
|
64
|
+
v.object({
|
|
65
|
+
kind: v.literal("union"),
|
|
66
|
+
variants: v.array(FieldTypeSchema),
|
|
67
|
+
discriminator: v.optional(
|
|
68
|
+
v.object({
|
|
69
|
+
propertyName: v.string(),
|
|
70
|
+
mapping: v.optional(v.record(v.string(), v.string()))
|
|
71
|
+
})
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
])
|
|
75
|
+
);
|
|
76
|
+
var TypeAliasSchema = v.object({
|
|
77
|
+
name: v.string(),
|
|
78
|
+
type: FieldTypeSchema,
|
|
79
|
+
doc: v.optional(v.string())
|
|
80
|
+
});
|
|
63
81
|
var ConstraintsSchema = v.object({
|
|
64
82
|
min: v.optional(v.number()),
|
|
65
83
|
max: v.optional(v.number()),
|
|
@@ -140,7 +158,8 @@ var SourceIrSchema = v.object({
|
|
|
140
158
|
parser: v.string(),
|
|
141
159
|
parserVersion: v.optional(v.string()),
|
|
142
160
|
entities: v.record(v.string(), EntitySchema),
|
|
143
|
-
enums: v.record(v.string(), EnumDefSchema)
|
|
161
|
+
enums: v.record(v.string(), EnumDefSchema),
|
|
162
|
+
typeAliases: v.optional(v.record(v.string(), TypeAliasSchema))
|
|
144
163
|
});
|
|
145
164
|
var IrSchema = v.object({
|
|
146
165
|
irVersion: v.string(),
|
|
@@ -151,7 +170,7 @@ var IrSchema = v.object({
|
|
|
151
170
|
import * as v2 from "valibot";
|
|
152
171
|
|
|
153
172
|
// src/version.ts
|
|
154
|
-
var IR_VERSION = "
|
|
173
|
+
var IR_VERSION = "2";
|
|
155
174
|
function isCompatible(irVersion) {
|
|
156
175
|
return irVersion === IR_VERSION;
|
|
157
176
|
}
|
|
@@ -220,7 +239,134 @@ function checkEnumValues(issues, path, values) {
|
|
|
220
239
|
seen.add(value.name);
|
|
221
240
|
}
|
|
222
241
|
}
|
|
223
|
-
function
|
|
242
|
+
function walkFieldType(type, path, entity, source, issues, info) {
|
|
243
|
+
switch (type.kind) {
|
|
244
|
+
case "scalar":
|
|
245
|
+
case "unknown":
|
|
246
|
+
return;
|
|
247
|
+
case "enum": {
|
|
248
|
+
const resolved = entity?.enums?.[type.ref] ?? source.enums[type.ref];
|
|
249
|
+
if (resolved === void 0) {
|
|
250
|
+
pushIssue(
|
|
251
|
+
issues,
|
|
252
|
+
path,
|
|
253
|
+
"unresolved_enum_ref",
|
|
254
|
+
`field type references unknown enum '${type.ref}'`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
case "ref": {
|
|
260
|
+
const known = source.entities[type.ref] !== void 0 || source.typeAliases?.[type.ref] !== void 0;
|
|
261
|
+
if (!known) {
|
|
262
|
+
pushIssue(
|
|
263
|
+
issues,
|
|
264
|
+
path,
|
|
265
|
+
"unresolved_ref",
|
|
266
|
+
`ref '${type.ref}' resolves to no entity and no type alias`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
case "union": {
|
|
272
|
+
if (type.variants.length < 2) {
|
|
273
|
+
pushIssue(
|
|
274
|
+
info,
|
|
275
|
+
path,
|
|
276
|
+
"degenerate_union",
|
|
277
|
+
`union has ${type.variants.length} variant(s); expected at least 2`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
type.variants.forEach((variant2, i) => {
|
|
281
|
+
walkFieldType(
|
|
282
|
+
variant2,
|
|
283
|
+
`${path}.variants.${i}`,
|
|
284
|
+
entity,
|
|
285
|
+
source,
|
|
286
|
+
issues,
|
|
287
|
+
info
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
const mapping = type.discriminator?.mapping;
|
|
291
|
+
if (mapping !== void 0) {
|
|
292
|
+
const refVariants = new Set(
|
|
293
|
+
type.variants.flatMap((vv) => vv.kind === "ref" ? [vv.ref] : [])
|
|
294
|
+
);
|
|
295
|
+
for (const [key, target] of Object.entries(mapping)) {
|
|
296
|
+
if (!refVariants.has(target)) {
|
|
297
|
+
pushIssue(
|
|
298
|
+
issues,
|
|
299
|
+
`${path}.discriminator.mapping.${key}`,
|
|
300
|
+
"unresolved_type_alias",
|
|
301
|
+
`discriminator mapping '${key}' -> '${target}' names no ref variant of the union`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function collectRefs(type, out) {
|
|
311
|
+
if (type.kind === "ref") {
|
|
312
|
+
out.add(type.ref);
|
|
313
|
+
} else if (type.kind === "union") {
|
|
314
|
+
for (const variant2 of type.variants) {
|
|
315
|
+
collectRefs(variant2, out);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function checkRefCycles(namespace, source, info) {
|
|
320
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
321
|
+
for (const [key, alias] of Object.entries(source.typeAliases ?? {})) {
|
|
322
|
+
const refs = /* @__PURE__ */ new Set();
|
|
323
|
+
collectRefs(alias.type, refs);
|
|
324
|
+
adjacency.set(key, refs);
|
|
325
|
+
}
|
|
326
|
+
for (const [key, entity] of Object.entries(source.entities)) {
|
|
327
|
+
const refs = adjacency.get(key) ?? /* @__PURE__ */ new Set();
|
|
328
|
+
for (const field of entity.fields) {
|
|
329
|
+
collectRefs(field.type, refs);
|
|
330
|
+
}
|
|
331
|
+
adjacency.set(key, refs);
|
|
332
|
+
}
|
|
333
|
+
const state = /* @__PURE__ */ new Map();
|
|
334
|
+
const stack = [];
|
|
335
|
+
const reported = /* @__PURE__ */ new Set();
|
|
336
|
+
const visit = (node) => {
|
|
337
|
+
state.set(node, "gray");
|
|
338
|
+
stack.push(node);
|
|
339
|
+
for (const next of adjacency.get(node) ?? []) {
|
|
340
|
+
if (!adjacency.has(next)) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const seen = state.get(next);
|
|
344
|
+
if (seen === "gray") {
|
|
345
|
+
const cycle = stack.slice(stack.indexOf(next));
|
|
346
|
+
const signature = [...cycle].sort().join("|");
|
|
347
|
+
if (!reported.has(signature)) {
|
|
348
|
+
reported.add(signature);
|
|
349
|
+
pushIssue(
|
|
350
|
+
info,
|
|
351
|
+
namespace,
|
|
352
|
+
"union_cycle",
|
|
353
|
+
`reference cycle: ${[...cycle, next].join(" -> ")}`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
} else if (seen === void 0) {
|
|
357
|
+
visit(next);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
stack.pop();
|
|
361
|
+
state.set(node, "black");
|
|
362
|
+
};
|
|
363
|
+
for (const node of adjacency.keys()) {
|
|
364
|
+
if (state.get(node) === void 0) {
|
|
365
|
+
visit(node);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function checkSource(namespace, source, lookupEntity, isNamespacePresent, issues, info) {
|
|
224
370
|
for (const [key, def] of Object.entries(source.enums)) {
|
|
225
371
|
if (def.name !== key) {
|
|
226
372
|
pushIssue(
|
|
@@ -255,17 +401,7 @@ function checkSource(namespace, source, lookupEntity, isNamespacePresent, issues
|
|
|
255
401
|
}
|
|
256
402
|
fieldNames.add(field.name);
|
|
257
403
|
checkConstraints(issues, `${fPath}.constraints`, field.constraints);
|
|
258
|
-
|
|
259
|
-
const resolved = entity.enums?.[field.type.ref] ?? source.enums[field.type.ref];
|
|
260
|
-
if (resolved === void 0) {
|
|
261
|
-
pushIssue(
|
|
262
|
-
issues,
|
|
263
|
-
fPath,
|
|
264
|
-
"unresolved_enum_ref",
|
|
265
|
-
`field '${field.name}' references unknown enum '${field.type.ref}'`
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
404
|
+
walkFieldType(field.type, fPath, entity, source, issues, info);
|
|
269
405
|
}
|
|
270
406
|
for (const [localKey, def] of Object.entries(entity.enums ?? {})) {
|
|
271
407
|
if (def.name !== localKey) {
|
|
@@ -363,6 +499,19 @@ function checkSource(namespace, source, lookupEntity, isNamespacePresent, issues
|
|
|
363
499
|
}
|
|
364
500
|
});
|
|
365
501
|
}
|
|
502
|
+
for (const [key, alias] of Object.entries(source.typeAliases ?? {})) {
|
|
503
|
+
const aPath = `${namespace}.typeAliases.${key}`;
|
|
504
|
+
if (alias.name !== key) {
|
|
505
|
+
pushIssue(
|
|
506
|
+
issues,
|
|
507
|
+
aPath,
|
|
508
|
+
"type_alias_key_mismatch",
|
|
509
|
+
`type alias key '${key}' does not match name '${alias.name}'`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
walkFieldType(alias.type, `${aPath}.type`, void 0, source, issues, info);
|
|
513
|
+
}
|
|
514
|
+
checkRefCycles(namespace, source, info);
|
|
366
515
|
}
|
|
367
516
|
function validateSourceIR(value) {
|
|
368
517
|
const parsed = v2.safeParse(SourceIrSchema, value);
|
|
@@ -371,10 +520,14 @@ function validateSourceIR(value) {
|
|
|
371
520
|
}
|
|
372
521
|
const source = parsed.output;
|
|
373
522
|
const issues = [];
|
|
523
|
+
const info = [];
|
|
374
524
|
const lookup = (ns, name) => ns === source.namespace ? source.entities[name] : void 0;
|
|
375
525
|
const isPresent = (ns) => ns === source.namespace;
|
|
376
|
-
checkSource(source.namespace, source, lookup, isPresent, issues);
|
|
377
|
-
|
|
526
|
+
checkSource(source.namespace, source, lookup, isPresent, issues, info);
|
|
527
|
+
if (issues.length > 0) {
|
|
528
|
+
return { ok: false, issues };
|
|
529
|
+
}
|
|
530
|
+
return info.length > 0 ? { ok: true, value: source, info } : { ok: true, value: source };
|
|
378
531
|
}
|
|
379
532
|
function validateIR(value) {
|
|
380
533
|
const parsed = v2.safeParse(IrSchema, value);
|
|
@@ -383,6 +536,7 @@ function validateIR(value) {
|
|
|
383
536
|
}
|
|
384
537
|
const ir = parsed.output;
|
|
385
538
|
const issues = [];
|
|
539
|
+
const info = [];
|
|
386
540
|
if (!isCompatible(ir.irVersion)) {
|
|
387
541
|
pushIssue(
|
|
388
542
|
issues,
|
|
@@ -402,9 +556,12 @@ function validateIR(value) {
|
|
|
402
556
|
`source key '${key}' does not match namespace '${source.namespace}'`
|
|
403
557
|
);
|
|
404
558
|
}
|
|
405
|
-
checkSource(source.namespace, source, lookup, isPresent, issues);
|
|
559
|
+
checkSource(source.namespace, source, lookup, isPresent, issues, info);
|
|
560
|
+
}
|
|
561
|
+
if (issues.length > 0) {
|
|
562
|
+
return { ok: false, issues };
|
|
406
563
|
}
|
|
407
|
-
return
|
|
564
|
+
return info.length > 0 ? { ok: true, value: ir, info } : { ok: true, value: ir };
|
|
408
565
|
}
|
|
409
566
|
function assertIR(value) {
|
|
410
567
|
const result = validateIR(value);
|
|
@@ -463,6 +620,123 @@ var EnumBuilderImpl = class {
|
|
|
463
620
|
return this.#def;
|
|
464
621
|
}
|
|
465
622
|
};
|
|
623
|
+
function checkedScalar(path, t) {
|
|
624
|
+
if (!v3.is(ScalarTypeSchema, t)) {
|
|
625
|
+
throw new IrBuildError(path, `unknown scalar type '${t}'`);
|
|
626
|
+
}
|
|
627
|
+
return { kind: "scalar", scalar: t };
|
|
628
|
+
}
|
|
629
|
+
var UnionBuilderImpl = class _UnionBuilderImpl {
|
|
630
|
+
#path;
|
|
631
|
+
#variants = [];
|
|
632
|
+
#discriminator;
|
|
633
|
+
constructor(path) {
|
|
634
|
+
this.#path = path;
|
|
635
|
+
}
|
|
636
|
+
scalar(t) {
|
|
637
|
+
this.#variants.push(checkedScalar(this.#path, t));
|
|
638
|
+
return this;
|
|
639
|
+
}
|
|
640
|
+
enum(ref) {
|
|
641
|
+
this.#variants.push({ kind: "enum", ref });
|
|
642
|
+
return this;
|
|
643
|
+
}
|
|
644
|
+
ref(name) {
|
|
645
|
+
this.#variants.push({ kind: "ref", ref: name });
|
|
646
|
+
return this;
|
|
647
|
+
}
|
|
648
|
+
union(build) {
|
|
649
|
+
const nested = new _UnionBuilderImpl(this.#path);
|
|
650
|
+
build(nested);
|
|
651
|
+
for (const variant2 of nested.#variants) {
|
|
652
|
+
this.#variants.push(variant2);
|
|
653
|
+
}
|
|
654
|
+
return this;
|
|
655
|
+
}
|
|
656
|
+
unknown(hint) {
|
|
657
|
+
this.#variants.push(
|
|
658
|
+
hint === void 0 ? { kind: "unknown" } : { kind: "unknown", hint }
|
|
659
|
+
);
|
|
660
|
+
return this;
|
|
661
|
+
}
|
|
662
|
+
discriminator(propertyName, mapping) {
|
|
663
|
+
this.#discriminator = mapping === void 0 ? { propertyName } : { propertyName, mapping };
|
|
664
|
+
return this;
|
|
665
|
+
}
|
|
666
|
+
build() {
|
|
667
|
+
if (this.#variants.length < 2) {
|
|
668
|
+
throw new IrBuildError(
|
|
669
|
+
this.#path,
|
|
670
|
+
`union() needs at least 2 variants, got ${this.#variants.length}`
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
const mapping = this.#discriminator?.mapping;
|
|
674
|
+
if (mapping !== void 0) {
|
|
675
|
+
const refVariants = new Set(
|
|
676
|
+
this.#variants.flatMap((vv) => vv.kind === "ref" ? [vv.ref] : [])
|
|
677
|
+
);
|
|
678
|
+
for (const [key, target] of Object.entries(mapping)) {
|
|
679
|
+
if (!refVariants.has(target)) {
|
|
680
|
+
throw new IrBuildError(
|
|
681
|
+
this.#path,
|
|
682
|
+
`discriminator mapping '${key}' -> '${target}' names no ref variant`
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
const type = {
|
|
688
|
+
kind: "union",
|
|
689
|
+
variants: this.#variants
|
|
690
|
+
};
|
|
691
|
+
if (this.#discriminator !== void 0) {
|
|
692
|
+
type.discriminator = this.#discriminator;
|
|
693
|
+
}
|
|
694
|
+
return type;
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
var TypeAliasBuilderImpl = class {
|
|
698
|
+
#path;
|
|
699
|
+
#name;
|
|
700
|
+
#type = { kind: "unknown" };
|
|
701
|
+
#doc;
|
|
702
|
+
constructor(path, name) {
|
|
703
|
+
this.#path = path;
|
|
704
|
+
this.#name = name;
|
|
705
|
+
}
|
|
706
|
+
scalar(t) {
|
|
707
|
+
this.#type = checkedScalar(this.#path, t);
|
|
708
|
+
return this;
|
|
709
|
+
}
|
|
710
|
+
enum(ref) {
|
|
711
|
+
this.#type = { kind: "enum", ref };
|
|
712
|
+
return this;
|
|
713
|
+
}
|
|
714
|
+
ref(name) {
|
|
715
|
+
this.#type = { kind: "ref", ref: name };
|
|
716
|
+
return this;
|
|
717
|
+
}
|
|
718
|
+
union(build) {
|
|
719
|
+
const nested = new UnionBuilderImpl(this.#path);
|
|
720
|
+
build(nested);
|
|
721
|
+
this.#type = nested.build();
|
|
722
|
+
return this;
|
|
723
|
+
}
|
|
724
|
+
unknown(hint) {
|
|
725
|
+
this.#type = hint === void 0 ? { kind: "unknown" } : { kind: "unknown", hint };
|
|
726
|
+
return this;
|
|
727
|
+
}
|
|
728
|
+
doc(text) {
|
|
729
|
+
this.#doc = text;
|
|
730
|
+
return this;
|
|
731
|
+
}
|
|
732
|
+
build() {
|
|
733
|
+
const alias = { name: this.#name, type: this.#type };
|
|
734
|
+
if (this.#doc !== void 0) {
|
|
735
|
+
alias.doc = this.#doc;
|
|
736
|
+
}
|
|
737
|
+
return alias;
|
|
738
|
+
}
|
|
739
|
+
};
|
|
466
740
|
var FieldBuilderImpl = class {
|
|
467
741
|
#path;
|
|
468
742
|
#field;
|
|
@@ -490,6 +764,16 @@ var FieldBuilderImpl = class {
|
|
|
490
764
|
this.#field.type = { kind: "enum", ref };
|
|
491
765
|
return this;
|
|
492
766
|
}
|
|
767
|
+
ref(name) {
|
|
768
|
+
this.#field.type = { kind: "ref", ref: name };
|
|
769
|
+
return this;
|
|
770
|
+
}
|
|
771
|
+
union(build) {
|
|
772
|
+
const builder = new UnionBuilderImpl(this.#path);
|
|
773
|
+
build(builder);
|
|
774
|
+
this.#field.type = builder.build();
|
|
775
|
+
return this;
|
|
776
|
+
}
|
|
493
777
|
unknown(hint) {
|
|
494
778
|
this.#field.type = hint === void 0 ? { kind: "unknown" } : { kind: "unknown", hint };
|
|
495
779
|
return this;
|
|
@@ -734,6 +1018,7 @@ var SourceIrBuilderImpl = class {
|
|
|
734
1018
|
#entities = [];
|
|
735
1019
|
#entityNames = /* @__PURE__ */ new Set();
|
|
736
1020
|
#enums = {};
|
|
1021
|
+
#typeAliases = {};
|
|
737
1022
|
constructor(init) {
|
|
738
1023
|
this.#namespace = init.namespace;
|
|
739
1024
|
this.#parser = init.parser;
|
|
@@ -758,6 +1043,21 @@ var SourceIrBuilderImpl = class {
|
|
|
758
1043
|
this.#entities.push(builder);
|
|
759
1044
|
return this;
|
|
760
1045
|
}
|
|
1046
|
+
addTypeAlias(name, def) {
|
|
1047
|
+
if (name in this.#typeAliases) {
|
|
1048
|
+
throw new IrBuildError(
|
|
1049
|
+
`${this.#namespace}.typeAliases.${name}`,
|
|
1050
|
+
`duplicate type alias '${name}'`
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
const builder = new TypeAliasBuilderImpl(
|
|
1054
|
+
`${this.#namespace}.typeAliases.${name}`,
|
|
1055
|
+
name
|
|
1056
|
+
);
|
|
1057
|
+
def(builder);
|
|
1058
|
+
this.#typeAliases[name] = builder.build();
|
|
1059
|
+
return this;
|
|
1060
|
+
}
|
|
761
1061
|
build() {
|
|
762
1062
|
const source = {
|
|
763
1063
|
namespace: this.#namespace,
|
|
@@ -773,6 +1073,9 @@ var SourceIrBuilderImpl = class {
|
|
|
773
1073
|
if (this.#parserVersion !== void 0) {
|
|
774
1074
|
source.parserVersion = this.#parserVersion;
|
|
775
1075
|
}
|
|
1076
|
+
if (Object.keys(this.#typeAliases).length > 0) {
|
|
1077
|
+
source.typeAliases = this.#typeAliases;
|
|
1078
|
+
}
|
|
776
1079
|
try {
|
|
777
1080
|
assertSourceIR(source);
|
|
778
1081
|
} catch (err) {
|
|
@@ -803,6 +1106,112 @@ function resolveEntity(ir, namespace, name) {
|
|
|
803
1106
|
function resolveEnum(source, entity, ref) {
|
|
804
1107
|
return entity?.enums?.[ref] ?? source.enums[ref];
|
|
805
1108
|
}
|
|
1109
|
+
function resolveRef(source, ref) {
|
|
1110
|
+
return source.entities[ref] ?? source.typeAliases?.[ref];
|
|
1111
|
+
}
|
|
1112
|
+
function resolveTypeAlias(source, name) {
|
|
1113
|
+
return source.typeAliases?.[name];
|
|
1114
|
+
}
|
|
1115
|
+
function* iterTypeAliases(ir) {
|
|
1116
|
+
for (const [namespace, source] of Object.entries(ir.sources)) {
|
|
1117
|
+
for (const alias of Object.values(source.typeAliases ?? {})) {
|
|
1118
|
+
yield { namespace, alias };
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
function collectRefNames(type, into = /* @__PURE__ */ new Set()) {
|
|
1123
|
+
if (type.kind === "ref") {
|
|
1124
|
+
into.add(type.ref);
|
|
1125
|
+
} else if (type.kind === "union") {
|
|
1126
|
+
for (const variant2 of type.variants) {
|
|
1127
|
+
collectRefNames(variant2, into);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
return into;
|
|
1131
|
+
}
|
|
1132
|
+
function refCycleMembers(source) {
|
|
1133
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
1134
|
+
const edgesFor = (key) => {
|
|
1135
|
+
let set = adjacency.get(key);
|
|
1136
|
+
if (set === void 0) {
|
|
1137
|
+
set = /* @__PURE__ */ new Set();
|
|
1138
|
+
adjacency.set(key, set);
|
|
1139
|
+
}
|
|
1140
|
+
return set;
|
|
1141
|
+
};
|
|
1142
|
+
for (const [key, alias] of Object.entries(source.typeAliases ?? {})) {
|
|
1143
|
+
collectRefNames(alias.type, edgesFor(key));
|
|
1144
|
+
}
|
|
1145
|
+
for (const [key, entity] of Object.entries(source.entities)) {
|
|
1146
|
+
const set = edgesFor(key);
|
|
1147
|
+
for (const field of entity.fields) {
|
|
1148
|
+
collectRefNames(field.type, set);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
const canReachSelf = (start) => {
|
|
1152
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1153
|
+
const stack = [...adjacency.get(start) ?? []];
|
|
1154
|
+
while (stack.length > 0) {
|
|
1155
|
+
const node = stack.pop();
|
|
1156
|
+
if (node === void 0) {
|
|
1157
|
+
break;
|
|
1158
|
+
}
|
|
1159
|
+
if (node === start) {
|
|
1160
|
+
return true;
|
|
1161
|
+
}
|
|
1162
|
+
if (seen.has(node) || !adjacency.has(node)) {
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
seen.add(node);
|
|
1166
|
+
for (const next of adjacency.get(node) ?? []) {
|
|
1167
|
+
stack.push(next);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return false;
|
|
1171
|
+
};
|
|
1172
|
+
const members = /* @__PURE__ */ new Set();
|
|
1173
|
+
for (const node of adjacency.keys()) {
|
|
1174
|
+
if (canReachSelf(node)) {
|
|
1175
|
+
members.add(node);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return members;
|
|
1179
|
+
}
|
|
1180
|
+
function fieldTypeKey(type) {
|
|
1181
|
+
switch (type.kind) {
|
|
1182
|
+
case "scalar":
|
|
1183
|
+
return `scalar:${type.scalar}`;
|
|
1184
|
+
case "enum":
|
|
1185
|
+
return `enum:${type.ref}`;
|
|
1186
|
+
case "ref":
|
|
1187
|
+
return `ref:${type.ref}`;
|
|
1188
|
+
case "unknown":
|
|
1189
|
+
return `unknown:${type.hint ?? ""}`;
|
|
1190
|
+
case "union":
|
|
1191
|
+
return `union:${flattenUnion(type).map(fieldTypeKey).join(",")}`;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function flattenUnion(type) {
|
|
1195
|
+
const out = [];
|
|
1196
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1197
|
+
const push = (t) => {
|
|
1198
|
+
if (t.kind === "union") {
|
|
1199
|
+
for (const variant2 of t.variants) {
|
|
1200
|
+
push(variant2);
|
|
1201
|
+
}
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
const key = fieldTypeKey(t);
|
|
1205
|
+
if (!seen.has(key)) {
|
|
1206
|
+
seen.add(key);
|
|
1207
|
+
out.push(t);
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
for (const variant2 of type.variants) {
|
|
1211
|
+
push(variant2);
|
|
1212
|
+
}
|
|
1213
|
+
return out;
|
|
1214
|
+
}
|
|
806
1215
|
function isCrossSource(fromNamespace, rel) {
|
|
807
1216
|
return rel.target.namespace !== fromNamespace;
|
|
808
1217
|
}
|
|
@@ -855,10 +1264,16 @@ function scalarTsType(type) {
|
|
|
855
1264
|
switch (type.kind) {
|
|
856
1265
|
case "enum":
|
|
857
1266
|
return type.ref;
|
|
1267
|
+
case "ref":
|
|
1268
|
+
return type.ref;
|
|
858
1269
|
case "unknown":
|
|
859
1270
|
return "unknown";
|
|
860
1271
|
case "scalar":
|
|
861
1272
|
return mapScalar(type.scalar);
|
|
1273
|
+
case "union":
|
|
1274
|
+
return flattenUnion(type).map(
|
|
1275
|
+
(variant2) => variant2.kind === "union" ? `(${scalarTsType(variant2)})` : scalarTsType(variant2)
|
|
1276
|
+
).join(" | ");
|
|
862
1277
|
}
|
|
863
1278
|
}
|
|
864
1279
|
function mapScalar(scalar) {
|
|
@@ -905,10 +1320,13 @@ export {
|
|
|
905
1320
|
ScalarTypeSchema,
|
|
906
1321
|
SourceIrSchema,
|
|
907
1322
|
StringFormatSchema,
|
|
1323
|
+
TypeAliasSchema,
|
|
908
1324
|
assertIR,
|
|
909
1325
|
assertSourceIR,
|
|
1326
|
+
collectRefNames,
|
|
910
1327
|
createFields,
|
|
911
1328
|
createSourceIR,
|
|
1329
|
+
flattenUnion,
|
|
912
1330
|
getSource,
|
|
913
1331
|
isCompatible,
|
|
914
1332
|
isCreateOptional,
|
|
@@ -916,11 +1334,15 @@ export {
|
|
|
916
1334
|
isDbAssigned,
|
|
917
1335
|
iterEntities,
|
|
918
1336
|
iterFields,
|
|
1337
|
+
iterTypeAliases,
|
|
919
1338
|
parseIR,
|
|
920
1339
|
primaryKeyFields,
|
|
1340
|
+
refCycleMembers,
|
|
921
1341
|
resolveEntity,
|
|
922
1342
|
resolveEnum,
|
|
1343
|
+
resolveRef,
|
|
923
1344
|
resolveRelationTarget,
|
|
1345
|
+
resolveTypeAlias,
|
|
924
1346
|
scalarTsType,
|
|
925
1347
|
updateFields,
|
|
926
1348
|
validateIR,
|