@kurotako/ir 0.1.0 → 0.3.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 +55 -0
- package/dist/index.cjs +548 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -54
- package/dist/index.d.ts +118 -54
- package/dist/index.js +541 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -51,10 +51,13 @@ __export(index_exports, {
|
|
|
51
51
|
ScalarTypeSchema: () => ScalarTypeSchema,
|
|
52
52
|
SourceIrSchema: () => SourceIrSchema,
|
|
53
53
|
StringFormatSchema: () => StringFormatSchema,
|
|
54
|
+
TypeAliasSchema: () => TypeAliasSchema,
|
|
54
55
|
assertIR: () => assertIR,
|
|
55
56
|
assertSourceIR: () => assertSourceIR,
|
|
57
|
+
collectRefNames: () => collectRefNames,
|
|
56
58
|
createFields: () => createFields,
|
|
57
59
|
createSourceIR: () => createSourceIR,
|
|
60
|
+
flattenUnion: () => flattenUnion,
|
|
58
61
|
getSource: () => getSource,
|
|
59
62
|
isCompatible: () => isCompatible,
|
|
60
63
|
isCreateOptional: () => isCreateOptional,
|
|
@@ -62,11 +65,15 @@ __export(index_exports, {
|
|
|
62
65
|
isDbAssigned: () => isDbAssigned,
|
|
63
66
|
iterEntities: () => iterEntities,
|
|
64
67
|
iterFields: () => iterFields,
|
|
68
|
+
iterTypeAliases: () => iterTypeAliases,
|
|
65
69
|
parseIR: () => parseIR,
|
|
66
70
|
primaryKeyFields: () => primaryKeyFields,
|
|
71
|
+
refCycleMembers: () => refCycleMembers,
|
|
67
72
|
resolveEntity: () => resolveEntity,
|
|
68
73
|
resolveEnum: () => resolveEnum,
|
|
74
|
+
resolveRef: () => resolveRef,
|
|
69
75
|
resolveRelationTarget: () => resolveRelationTarget,
|
|
76
|
+
resolveTypeAlias: () => resolveTypeAlias,
|
|
70
77
|
scalarTsType: () => scalarTsType,
|
|
71
78
|
updateFields: () => updateFields,
|
|
72
79
|
validateIR: () => validateIR,
|
|
@@ -131,11 +138,31 @@ var IndexTypeSchema = v.picklist([
|
|
|
131
138
|
"brin",
|
|
132
139
|
"spgist"
|
|
133
140
|
]);
|
|
134
|
-
var FieldTypeSchema = v.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
var FieldTypeSchema = v.lazy(
|
|
142
|
+
() => v.variant("kind", [
|
|
143
|
+
v.object({ kind: v.literal("scalar"), scalar: ScalarTypeSchema }),
|
|
144
|
+
v.object({ kind: v.literal("enum"), ref: v.string() }),
|
|
145
|
+
v.object({ kind: v.literal("unknown"), hint: v.optional(v.string()) }),
|
|
146
|
+
v.object({ kind: v.literal("ref"), ref: v.string() }),
|
|
147
|
+
v.object({ kind: v.literal("map"), value: FieldTypeSchema }),
|
|
148
|
+
v.object({ kind: v.literal("array"), element: FieldTypeSchema }),
|
|
149
|
+
v.object({
|
|
150
|
+
kind: v.literal("union"),
|
|
151
|
+
variants: v.array(FieldTypeSchema),
|
|
152
|
+
discriminator: v.optional(
|
|
153
|
+
v.object({
|
|
154
|
+
propertyName: v.string(),
|
|
155
|
+
mapping: v.optional(v.record(v.string(), v.string()))
|
|
156
|
+
})
|
|
157
|
+
)
|
|
158
|
+
})
|
|
159
|
+
])
|
|
160
|
+
);
|
|
161
|
+
var TypeAliasSchema = v.object({
|
|
162
|
+
name: v.string(),
|
|
163
|
+
type: FieldTypeSchema,
|
|
164
|
+
doc: v.optional(v.string())
|
|
165
|
+
});
|
|
139
166
|
var ConstraintsSchema = v.object({
|
|
140
167
|
min: v.optional(v.number()),
|
|
141
168
|
max: v.optional(v.number()),
|
|
@@ -203,6 +230,7 @@ var CompositeUniqueSchema = v.object({
|
|
|
203
230
|
var EntitySchema = v.object({
|
|
204
231
|
name: v.string(),
|
|
205
232
|
fields: v.array(FieldSchema),
|
|
233
|
+
additionalProperties: v.optional(FieldTypeSchema),
|
|
206
234
|
relations: v.array(RelationSchema),
|
|
207
235
|
enums: v.optional(v.record(v.string(), EnumDefSchema)),
|
|
208
236
|
primaryKey: v.optional(v.array(v.string())),
|
|
@@ -216,7 +244,8 @@ var SourceIrSchema = v.object({
|
|
|
216
244
|
parser: v.string(),
|
|
217
245
|
parserVersion: v.optional(v.string()),
|
|
218
246
|
entities: v.record(v.string(), EntitySchema),
|
|
219
|
-
enums: v.record(v.string(), EnumDefSchema)
|
|
247
|
+
enums: v.record(v.string(), EnumDefSchema),
|
|
248
|
+
typeAliases: v.optional(v.record(v.string(), TypeAliasSchema))
|
|
220
249
|
});
|
|
221
250
|
var IrSchema = v.object({
|
|
222
251
|
irVersion: v.string(),
|
|
@@ -227,7 +256,7 @@ var IrSchema = v.object({
|
|
|
227
256
|
var v2 = __toESM(require("valibot"), 1);
|
|
228
257
|
|
|
229
258
|
// src/version.ts
|
|
230
|
-
var IR_VERSION = "
|
|
259
|
+
var IR_VERSION = "4";
|
|
231
260
|
function isCompatible(irVersion) {
|
|
232
261
|
return irVersion === IR_VERSION;
|
|
233
262
|
}
|
|
@@ -296,7 +325,151 @@ function checkEnumValues(issues, path, values) {
|
|
|
296
325
|
seen.add(value.name);
|
|
297
326
|
}
|
|
298
327
|
}
|
|
299
|
-
function
|
|
328
|
+
function walkFieldType(type, path, entity, source, issues, info) {
|
|
329
|
+
switch (type.kind) {
|
|
330
|
+
case "scalar":
|
|
331
|
+
case "unknown":
|
|
332
|
+
return;
|
|
333
|
+
case "enum": {
|
|
334
|
+
const resolved = entity?.enums?.[type.ref] ?? source.enums[type.ref];
|
|
335
|
+
if (resolved === void 0) {
|
|
336
|
+
pushIssue(
|
|
337
|
+
issues,
|
|
338
|
+
path,
|
|
339
|
+
"unresolved_enum_ref",
|
|
340
|
+
`field type references unknown enum '${type.ref}'`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
case "ref": {
|
|
346
|
+
const known = source.entities[type.ref] !== void 0 || source.typeAliases?.[type.ref] !== void 0;
|
|
347
|
+
if (!known) {
|
|
348
|
+
pushIssue(
|
|
349
|
+
issues,
|
|
350
|
+
path,
|
|
351
|
+
"unresolved_ref",
|
|
352
|
+
`ref '${type.ref}' resolves to no entity and no type alias`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
case "map":
|
|
358
|
+
walkFieldType(type.value, `${path}.value`, entity, source, issues, info);
|
|
359
|
+
return;
|
|
360
|
+
case "array":
|
|
361
|
+
walkFieldType(
|
|
362
|
+
type.element,
|
|
363
|
+
`${path}.element`,
|
|
364
|
+
entity,
|
|
365
|
+
source,
|
|
366
|
+
issues,
|
|
367
|
+
info
|
|
368
|
+
);
|
|
369
|
+
return;
|
|
370
|
+
case "union": {
|
|
371
|
+
if (type.variants.length < 2) {
|
|
372
|
+
pushIssue(
|
|
373
|
+
info,
|
|
374
|
+
path,
|
|
375
|
+
"degenerate_union",
|
|
376
|
+
`union has ${type.variants.length} variant(s); expected at least 2`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
type.variants.forEach((variant2, i) => {
|
|
380
|
+
walkFieldType(
|
|
381
|
+
variant2,
|
|
382
|
+
`${path}.variants.${i}`,
|
|
383
|
+
entity,
|
|
384
|
+
source,
|
|
385
|
+
issues,
|
|
386
|
+
info
|
|
387
|
+
);
|
|
388
|
+
});
|
|
389
|
+
const mapping = type.discriminator?.mapping;
|
|
390
|
+
if (mapping !== void 0) {
|
|
391
|
+
const refVariants = new Set(
|
|
392
|
+
type.variants.flatMap((vv) => vv.kind === "ref" ? [vv.ref] : [])
|
|
393
|
+
);
|
|
394
|
+
for (const [key, target] of Object.entries(mapping)) {
|
|
395
|
+
if (!refVariants.has(target)) {
|
|
396
|
+
pushIssue(
|
|
397
|
+
issues,
|
|
398
|
+
`${path}.discriminator.mapping.${key}`,
|
|
399
|
+
"unresolved_type_alias",
|
|
400
|
+
`discriminator mapping '${key}' -> '${target}' names no ref variant of the union`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function collectRefs(type, out) {
|
|
410
|
+
if (type.kind === "ref") {
|
|
411
|
+
out.add(type.ref);
|
|
412
|
+
} else if (type.kind === "union") {
|
|
413
|
+
for (const variant2 of type.variants) {
|
|
414
|
+
collectRefs(variant2, out);
|
|
415
|
+
}
|
|
416
|
+
} else if (type.kind === "map") {
|
|
417
|
+
collectRefs(type.value, out);
|
|
418
|
+
} else if (type.kind === "array") {
|
|
419
|
+
collectRefs(type.element, out);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function checkRefCycles(namespace, source, info) {
|
|
423
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
424
|
+
for (const [key, alias] of Object.entries(source.typeAliases ?? {})) {
|
|
425
|
+
const refs = /* @__PURE__ */ new Set();
|
|
426
|
+
collectRefs(alias.type, refs);
|
|
427
|
+
adjacency.set(key, refs);
|
|
428
|
+
}
|
|
429
|
+
for (const [key, entity] of Object.entries(source.entities)) {
|
|
430
|
+
const refs = adjacency.get(key) ?? /* @__PURE__ */ new Set();
|
|
431
|
+
for (const field of entity.fields) {
|
|
432
|
+
collectRefs(field.type, refs);
|
|
433
|
+
}
|
|
434
|
+
adjacency.set(key, refs);
|
|
435
|
+
}
|
|
436
|
+
const state = /* @__PURE__ */ new Map();
|
|
437
|
+
const stack = [];
|
|
438
|
+
const reported = /* @__PURE__ */ new Set();
|
|
439
|
+
const visit = (node) => {
|
|
440
|
+
state.set(node, "gray");
|
|
441
|
+
stack.push(node);
|
|
442
|
+
for (const next of adjacency.get(node) ?? []) {
|
|
443
|
+
if (!adjacency.has(next)) {
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
const seen = state.get(next);
|
|
447
|
+
if (seen === "gray") {
|
|
448
|
+
const cycle = stack.slice(stack.indexOf(next));
|
|
449
|
+
const signature = [...cycle].sort().join("|");
|
|
450
|
+
if (!reported.has(signature)) {
|
|
451
|
+
reported.add(signature);
|
|
452
|
+
pushIssue(
|
|
453
|
+
info,
|
|
454
|
+
namespace,
|
|
455
|
+
"union_cycle",
|
|
456
|
+
`reference cycle: ${[...cycle, next].join(" -> ")}`
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
} else if (seen === void 0) {
|
|
460
|
+
visit(next);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
stack.pop();
|
|
464
|
+
state.set(node, "black");
|
|
465
|
+
};
|
|
466
|
+
for (const node of adjacency.keys()) {
|
|
467
|
+
if (state.get(node) === void 0) {
|
|
468
|
+
visit(node);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function checkSource(namespace, source, lookupEntity, isNamespacePresent, issues, info) {
|
|
300
473
|
for (const [key, def] of Object.entries(source.enums)) {
|
|
301
474
|
if (def.name !== key) {
|
|
302
475
|
pushIssue(
|
|
@@ -331,17 +504,17 @@ function checkSource(namespace, source, lookupEntity, isNamespacePresent, issues
|
|
|
331
504
|
}
|
|
332
505
|
fieldNames.add(field.name);
|
|
333
506
|
checkConstraints(issues, `${fPath}.constraints`, field.constraints);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
507
|
+
walkFieldType(field.type, fPath, entity, source, issues, info);
|
|
508
|
+
}
|
|
509
|
+
if (entity.additionalProperties !== void 0) {
|
|
510
|
+
walkFieldType(
|
|
511
|
+
entity.additionalProperties,
|
|
512
|
+
`${ePath}.additionalProperties`,
|
|
513
|
+
entity,
|
|
514
|
+
source,
|
|
515
|
+
issues,
|
|
516
|
+
info
|
|
517
|
+
);
|
|
345
518
|
}
|
|
346
519
|
for (const [localKey, def] of Object.entries(entity.enums ?? {})) {
|
|
347
520
|
if (def.name !== localKey) {
|
|
@@ -439,6 +612,19 @@ function checkSource(namespace, source, lookupEntity, isNamespacePresent, issues
|
|
|
439
612
|
}
|
|
440
613
|
});
|
|
441
614
|
}
|
|
615
|
+
for (const [key, alias] of Object.entries(source.typeAliases ?? {})) {
|
|
616
|
+
const aPath = `${namespace}.typeAliases.${key}`;
|
|
617
|
+
if (alias.name !== key) {
|
|
618
|
+
pushIssue(
|
|
619
|
+
issues,
|
|
620
|
+
aPath,
|
|
621
|
+
"type_alias_key_mismatch",
|
|
622
|
+
`type alias key '${key}' does not match name '${alias.name}'`
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
walkFieldType(alias.type, `${aPath}.type`, void 0, source, issues, info);
|
|
626
|
+
}
|
|
627
|
+
checkRefCycles(namespace, source, info);
|
|
442
628
|
}
|
|
443
629
|
function validateSourceIR(value) {
|
|
444
630
|
const parsed = v2.safeParse(SourceIrSchema, value);
|
|
@@ -447,10 +633,14 @@ function validateSourceIR(value) {
|
|
|
447
633
|
}
|
|
448
634
|
const source = parsed.output;
|
|
449
635
|
const issues = [];
|
|
636
|
+
const info = [];
|
|
450
637
|
const lookup = (ns, name) => ns === source.namespace ? source.entities[name] : void 0;
|
|
451
638
|
const isPresent = (ns) => ns === source.namespace;
|
|
452
|
-
checkSource(source.namespace, source, lookup, isPresent, issues);
|
|
453
|
-
|
|
639
|
+
checkSource(source.namespace, source, lookup, isPresent, issues, info);
|
|
640
|
+
if (issues.length > 0) {
|
|
641
|
+
return { ok: false, issues };
|
|
642
|
+
}
|
|
643
|
+
return info.length > 0 ? { ok: true, value: source, info } : { ok: true, value: source };
|
|
454
644
|
}
|
|
455
645
|
function validateIR(value) {
|
|
456
646
|
const parsed = v2.safeParse(IrSchema, value);
|
|
@@ -459,6 +649,7 @@ function validateIR(value) {
|
|
|
459
649
|
}
|
|
460
650
|
const ir = parsed.output;
|
|
461
651
|
const issues = [];
|
|
652
|
+
const info = [];
|
|
462
653
|
if (!isCompatible(ir.irVersion)) {
|
|
463
654
|
pushIssue(
|
|
464
655
|
issues,
|
|
@@ -478,9 +669,12 @@ function validateIR(value) {
|
|
|
478
669
|
`source key '${key}' does not match namespace '${source.namespace}'`
|
|
479
670
|
);
|
|
480
671
|
}
|
|
481
|
-
checkSource(source.namespace, source, lookup, isPresent, issues);
|
|
672
|
+
checkSource(source.namespace, source, lookup, isPresent, issues, info);
|
|
673
|
+
}
|
|
674
|
+
if (issues.length > 0) {
|
|
675
|
+
return { ok: false, issues };
|
|
482
676
|
}
|
|
483
|
-
return
|
|
677
|
+
return info.length > 0 ? { ok: true, value: ir, info } : { ok: true, value: ir };
|
|
484
678
|
}
|
|
485
679
|
function assertIR(value) {
|
|
486
680
|
const result = validateIR(value);
|
|
@@ -539,6 +733,147 @@ var EnumBuilderImpl = class {
|
|
|
539
733
|
return this.#def;
|
|
540
734
|
}
|
|
541
735
|
};
|
|
736
|
+
function checkedScalar(path, t) {
|
|
737
|
+
if (!v3.is(ScalarTypeSchema, t)) {
|
|
738
|
+
throw new IrBuildError(path, `unknown scalar type '${t}'`);
|
|
739
|
+
}
|
|
740
|
+
return { kind: "scalar", scalar: t };
|
|
741
|
+
}
|
|
742
|
+
var UnionBuilderImpl = class _UnionBuilderImpl {
|
|
743
|
+
#path;
|
|
744
|
+
#variants = [];
|
|
745
|
+
#discriminator;
|
|
746
|
+
constructor(path) {
|
|
747
|
+
this.#path = path;
|
|
748
|
+
}
|
|
749
|
+
scalar(t) {
|
|
750
|
+
this.#variants.push(checkedScalar(this.#path, t));
|
|
751
|
+
return this;
|
|
752
|
+
}
|
|
753
|
+
enum(ref) {
|
|
754
|
+
this.#variants.push({ kind: "enum", ref });
|
|
755
|
+
return this;
|
|
756
|
+
}
|
|
757
|
+
ref(name) {
|
|
758
|
+
this.#variants.push({ kind: "ref", ref: name });
|
|
759
|
+
return this;
|
|
760
|
+
}
|
|
761
|
+
union(build) {
|
|
762
|
+
const nested = new _UnionBuilderImpl(this.#path);
|
|
763
|
+
build(nested);
|
|
764
|
+
for (const variant2 of nested.#variants) {
|
|
765
|
+
this.#variants.push(variant2);
|
|
766
|
+
}
|
|
767
|
+
return this;
|
|
768
|
+
}
|
|
769
|
+
map(build) {
|
|
770
|
+
const value = new TypeAliasBuilderImpl(this.#path, "map-value");
|
|
771
|
+
build(value);
|
|
772
|
+
this.#variants.push({ kind: "map", value: value.build().type });
|
|
773
|
+
return this;
|
|
774
|
+
}
|
|
775
|
+
array(build) {
|
|
776
|
+
const element = new TypeAliasBuilderImpl(this.#path, "array-element");
|
|
777
|
+
build(element);
|
|
778
|
+
this.#variants.push({ kind: "array", element: element.build().type });
|
|
779
|
+
return this;
|
|
780
|
+
}
|
|
781
|
+
unknown(hint) {
|
|
782
|
+
this.#variants.push(
|
|
783
|
+
hint === void 0 ? { kind: "unknown" } : { kind: "unknown", hint }
|
|
784
|
+
);
|
|
785
|
+
return this;
|
|
786
|
+
}
|
|
787
|
+
discriminator(propertyName, mapping) {
|
|
788
|
+
this.#discriminator = mapping === void 0 ? { propertyName } : { propertyName, mapping };
|
|
789
|
+
return this;
|
|
790
|
+
}
|
|
791
|
+
build() {
|
|
792
|
+
if (this.#variants.length < 2) {
|
|
793
|
+
throw new IrBuildError(
|
|
794
|
+
this.#path,
|
|
795
|
+
`union() needs at least 2 variants, got ${this.#variants.length}`
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
const mapping = this.#discriminator?.mapping;
|
|
799
|
+
if (mapping !== void 0) {
|
|
800
|
+
const refVariants = new Set(
|
|
801
|
+
this.#variants.flatMap((vv) => vv.kind === "ref" ? [vv.ref] : [])
|
|
802
|
+
);
|
|
803
|
+
for (const [key, target] of Object.entries(mapping)) {
|
|
804
|
+
if (!refVariants.has(target)) {
|
|
805
|
+
throw new IrBuildError(
|
|
806
|
+
this.#path,
|
|
807
|
+
`discriminator mapping '${key}' -> '${target}' names no ref variant`
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
const type = {
|
|
813
|
+
kind: "union",
|
|
814
|
+
variants: this.#variants
|
|
815
|
+
};
|
|
816
|
+
if (this.#discriminator !== void 0) {
|
|
817
|
+
type.discriminator = this.#discriminator;
|
|
818
|
+
}
|
|
819
|
+
return type;
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
var TypeAliasBuilderImpl = class _TypeAliasBuilderImpl {
|
|
823
|
+
#path;
|
|
824
|
+
#name;
|
|
825
|
+
#type = { kind: "unknown" };
|
|
826
|
+
#doc;
|
|
827
|
+
constructor(path, name) {
|
|
828
|
+
this.#path = path;
|
|
829
|
+
this.#name = name;
|
|
830
|
+
}
|
|
831
|
+
scalar(t) {
|
|
832
|
+
this.#type = checkedScalar(this.#path, t);
|
|
833
|
+
return this;
|
|
834
|
+
}
|
|
835
|
+
enum(ref) {
|
|
836
|
+
this.#type = { kind: "enum", ref };
|
|
837
|
+
return this;
|
|
838
|
+
}
|
|
839
|
+
ref(name) {
|
|
840
|
+
this.#type = { kind: "ref", ref: name };
|
|
841
|
+
return this;
|
|
842
|
+
}
|
|
843
|
+
union(build) {
|
|
844
|
+
const nested = new UnionBuilderImpl(this.#path);
|
|
845
|
+
build(nested);
|
|
846
|
+
this.#type = nested.build();
|
|
847
|
+
return this;
|
|
848
|
+
}
|
|
849
|
+
map(build) {
|
|
850
|
+
const value = new _TypeAliasBuilderImpl(this.#path, "map-value");
|
|
851
|
+
build(value);
|
|
852
|
+
this.#type = { kind: "map", value: value.build().type };
|
|
853
|
+
return this;
|
|
854
|
+
}
|
|
855
|
+
array(build) {
|
|
856
|
+
const element = new _TypeAliasBuilderImpl(this.#path, "array-element");
|
|
857
|
+
build(element);
|
|
858
|
+
this.#type = { kind: "array", element: element.build().type };
|
|
859
|
+
return this;
|
|
860
|
+
}
|
|
861
|
+
unknown(hint) {
|
|
862
|
+
this.#type = hint === void 0 ? { kind: "unknown" } : { kind: "unknown", hint };
|
|
863
|
+
return this;
|
|
864
|
+
}
|
|
865
|
+
doc(text) {
|
|
866
|
+
this.#doc = text;
|
|
867
|
+
return this;
|
|
868
|
+
}
|
|
869
|
+
build() {
|
|
870
|
+
const alias = { name: this.#name, type: this.#type };
|
|
871
|
+
if (this.#doc !== void 0) {
|
|
872
|
+
alias.doc = this.#doc;
|
|
873
|
+
}
|
|
874
|
+
return alias;
|
|
875
|
+
}
|
|
876
|
+
};
|
|
542
877
|
var FieldBuilderImpl = class {
|
|
543
878
|
#path;
|
|
544
879
|
#field;
|
|
@@ -566,6 +901,28 @@ var FieldBuilderImpl = class {
|
|
|
566
901
|
this.#field.type = { kind: "enum", ref };
|
|
567
902
|
return this;
|
|
568
903
|
}
|
|
904
|
+
ref(name) {
|
|
905
|
+
this.#field.type = { kind: "ref", ref: name };
|
|
906
|
+
return this;
|
|
907
|
+
}
|
|
908
|
+
union(build) {
|
|
909
|
+
const builder = new UnionBuilderImpl(this.#path);
|
|
910
|
+
build(builder);
|
|
911
|
+
this.#field.type = builder.build();
|
|
912
|
+
return this;
|
|
913
|
+
}
|
|
914
|
+
map(build) {
|
|
915
|
+
const value = new TypeAliasBuilderImpl(this.#path, "map-value");
|
|
916
|
+
build(value);
|
|
917
|
+
this.#field.type = { kind: "map", value: value.build().type };
|
|
918
|
+
return this;
|
|
919
|
+
}
|
|
920
|
+
array(build) {
|
|
921
|
+
const element = new TypeAliasBuilderImpl(this.#path, "array-element");
|
|
922
|
+
build(element);
|
|
923
|
+
this.#field.type = { kind: "array", element: element.build().type };
|
|
924
|
+
return this;
|
|
925
|
+
}
|
|
569
926
|
unknown(hint) {
|
|
570
927
|
this.#field.type = hint === void 0 ? { kind: "unknown" } : { kind: "unknown", hint };
|
|
571
928
|
return this;
|
|
@@ -709,6 +1066,7 @@ var EntityBuilderImpl = class {
|
|
|
709
1066
|
#uniques = [];
|
|
710
1067
|
#doc;
|
|
711
1068
|
#dbName;
|
|
1069
|
+
#additionalProperties;
|
|
712
1070
|
constructor(namespace, name) {
|
|
713
1071
|
this.#namespace = namespace;
|
|
714
1072
|
this.#name = name;
|
|
@@ -735,6 +1093,15 @@ var EntityBuilderImpl = class {
|
|
|
735
1093
|
this.#fields.push(builder);
|
|
736
1094
|
return this;
|
|
737
1095
|
}
|
|
1096
|
+
additionalProperties(def) {
|
|
1097
|
+
const value = new TypeAliasBuilderImpl(
|
|
1098
|
+
`${this.#namespace}.${this.#name}.additionalProperties`,
|
|
1099
|
+
"additionalProperties"
|
|
1100
|
+
);
|
|
1101
|
+
def(value);
|
|
1102
|
+
this.#additionalProperties = value.build().type;
|
|
1103
|
+
return this;
|
|
1104
|
+
}
|
|
738
1105
|
relation(name, def) {
|
|
739
1106
|
const builder = new RelationBuilderImpl(name);
|
|
740
1107
|
def(builder);
|
|
@@ -800,6 +1167,9 @@ var EntityBuilderImpl = class {
|
|
|
800
1167
|
if (this.#dbName !== void 0) {
|
|
801
1168
|
entity.dbName = this.#dbName;
|
|
802
1169
|
}
|
|
1170
|
+
if (this.#additionalProperties !== void 0) {
|
|
1171
|
+
entity.additionalProperties = this.#additionalProperties;
|
|
1172
|
+
}
|
|
803
1173
|
return entity;
|
|
804
1174
|
}
|
|
805
1175
|
};
|
|
@@ -810,6 +1180,7 @@ var SourceIrBuilderImpl = class {
|
|
|
810
1180
|
#entities = [];
|
|
811
1181
|
#entityNames = /* @__PURE__ */ new Set();
|
|
812
1182
|
#enums = {};
|
|
1183
|
+
#typeAliases = {};
|
|
813
1184
|
constructor(init) {
|
|
814
1185
|
this.#namespace = init.namespace;
|
|
815
1186
|
this.#parser = init.parser;
|
|
@@ -834,6 +1205,21 @@ var SourceIrBuilderImpl = class {
|
|
|
834
1205
|
this.#entities.push(builder);
|
|
835
1206
|
return this;
|
|
836
1207
|
}
|
|
1208
|
+
addTypeAlias(name, def) {
|
|
1209
|
+
if (name in this.#typeAliases) {
|
|
1210
|
+
throw new IrBuildError(
|
|
1211
|
+
`${this.#namespace}.typeAliases.${name}`,
|
|
1212
|
+
`duplicate type alias '${name}'`
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
const builder = new TypeAliasBuilderImpl(
|
|
1216
|
+
`${this.#namespace}.typeAliases.${name}`,
|
|
1217
|
+
name
|
|
1218
|
+
);
|
|
1219
|
+
def(builder);
|
|
1220
|
+
this.#typeAliases[name] = builder.build();
|
|
1221
|
+
return this;
|
|
1222
|
+
}
|
|
837
1223
|
build() {
|
|
838
1224
|
const source = {
|
|
839
1225
|
namespace: this.#namespace,
|
|
@@ -849,6 +1235,9 @@ var SourceIrBuilderImpl = class {
|
|
|
849
1235
|
if (this.#parserVersion !== void 0) {
|
|
850
1236
|
source.parserVersion = this.#parserVersion;
|
|
851
1237
|
}
|
|
1238
|
+
if (Object.keys(this.#typeAliases).length > 0) {
|
|
1239
|
+
source.typeAliases = this.#typeAliases;
|
|
1240
|
+
}
|
|
852
1241
|
try {
|
|
853
1242
|
assertSourceIR(source);
|
|
854
1243
|
} catch (err) {
|
|
@@ -879,6 +1268,123 @@ function resolveEntity(ir, namespace, name) {
|
|
|
879
1268
|
function resolveEnum(source, entity, ref) {
|
|
880
1269
|
return entity?.enums?.[ref] ?? source.enums[ref];
|
|
881
1270
|
}
|
|
1271
|
+
function resolveRef(source, ref) {
|
|
1272
|
+
return source.entities[ref] ?? source.typeAliases?.[ref];
|
|
1273
|
+
}
|
|
1274
|
+
function resolveTypeAlias(source, name) {
|
|
1275
|
+
return source.typeAliases?.[name];
|
|
1276
|
+
}
|
|
1277
|
+
function* iterTypeAliases(ir) {
|
|
1278
|
+
for (const [namespace, source] of Object.entries(ir.sources)) {
|
|
1279
|
+
for (const alias of Object.values(source.typeAliases ?? {})) {
|
|
1280
|
+
yield { namespace, alias };
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function collectRefNames(type, into = /* @__PURE__ */ new Set()) {
|
|
1285
|
+
if (type.kind === "ref") {
|
|
1286
|
+
into.add(type.ref);
|
|
1287
|
+
} else if (type.kind === "union") {
|
|
1288
|
+
for (const variant2 of type.variants) {
|
|
1289
|
+
collectRefNames(variant2, into);
|
|
1290
|
+
}
|
|
1291
|
+
} else if (type.kind === "map") {
|
|
1292
|
+
collectRefNames(type.value, into);
|
|
1293
|
+
} else if (type.kind === "array") {
|
|
1294
|
+
collectRefNames(type.element, into);
|
|
1295
|
+
}
|
|
1296
|
+
return into;
|
|
1297
|
+
}
|
|
1298
|
+
function refCycleMembers(source) {
|
|
1299
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
1300
|
+
const edgesFor = (key) => {
|
|
1301
|
+
let set = adjacency.get(key);
|
|
1302
|
+
if (set === void 0) {
|
|
1303
|
+
set = /* @__PURE__ */ new Set();
|
|
1304
|
+
adjacency.set(key, set);
|
|
1305
|
+
}
|
|
1306
|
+
return set;
|
|
1307
|
+
};
|
|
1308
|
+
for (const [key, alias] of Object.entries(source.typeAliases ?? {})) {
|
|
1309
|
+
collectRefNames(alias.type, edgesFor(key));
|
|
1310
|
+
}
|
|
1311
|
+
for (const [key, entity] of Object.entries(source.entities)) {
|
|
1312
|
+
const set = edgesFor(key);
|
|
1313
|
+
for (const field of entity.fields) {
|
|
1314
|
+
collectRefNames(field.type, set);
|
|
1315
|
+
}
|
|
1316
|
+
if (entity.additionalProperties !== void 0) {
|
|
1317
|
+
collectRefNames(entity.additionalProperties, set);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
const canReachSelf = (start) => {
|
|
1321
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1322
|
+
const stack = [...adjacency.get(start) ?? []];
|
|
1323
|
+
while (stack.length > 0) {
|
|
1324
|
+
const node = stack.pop();
|
|
1325
|
+
if (node === void 0) {
|
|
1326
|
+
break;
|
|
1327
|
+
}
|
|
1328
|
+
if (node === start) {
|
|
1329
|
+
return true;
|
|
1330
|
+
}
|
|
1331
|
+
if (seen.has(node) || !adjacency.has(node)) {
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
seen.add(node);
|
|
1335
|
+
for (const next of adjacency.get(node) ?? []) {
|
|
1336
|
+
stack.push(next);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
return false;
|
|
1340
|
+
};
|
|
1341
|
+
const members = /* @__PURE__ */ new Set();
|
|
1342
|
+
for (const node of adjacency.keys()) {
|
|
1343
|
+
if (canReachSelf(node)) {
|
|
1344
|
+
members.add(node);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
return members;
|
|
1348
|
+
}
|
|
1349
|
+
function fieldTypeKey(type) {
|
|
1350
|
+
switch (type.kind) {
|
|
1351
|
+
case "scalar":
|
|
1352
|
+
return `scalar:${type.scalar}`;
|
|
1353
|
+
case "enum":
|
|
1354
|
+
return `enum:${type.ref}`;
|
|
1355
|
+
case "ref":
|
|
1356
|
+
return `ref:${type.ref}`;
|
|
1357
|
+
case "unknown":
|
|
1358
|
+
return `unknown:${type.hint ?? ""}`;
|
|
1359
|
+
case "map":
|
|
1360
|
+
return `map:${fieldTypeKey(type.value)}`;
|
|
1361
|
+
case "array":
|
|
1362
|
+
return `array:${fieldTypeKey(type.element)}`;
|
|
1363
|
+
case "union":
|
|
1364
|
+
return `union:${flattenUnion(type).map(fieldTypeKey).join(",")}`;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
function flattenUnion(type) {
|
|
1368
|
+
const out = [];
|
|
1369
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1370
|
+
const push = (t) => {
|
|
1371
|
+
if (t.kind === "union") {
|
|
1372
|
+
for (const variant2 of t.variants) {
|
|
1373
|
+
push(variant2);
|
|
1374
|
+
}
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
const key = fieldTypeKey(t);
|
|
1378
|
+
if (!seen.has(key)) {
|
|
1379
|
+
seen.add(key);
|
|
1380
|
+
out.push(t);
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
for (const variant2 of type.variants) {
|
|
1384
|
+
push(variant2);
|
|
1385
|
+
}
|
|
1386
|
+
return out;
|
|
1387
|
+
}
|
|
882
1388
|
function isCrossSource(fromNamespace, rel) {
|
|
883
1389
|
return rel.target.namespace !== fromNamespace;
|
|
884
1390
|
}
|
|
@@ -931,10 +1437,22 @@ function scalarTsType(type) {
|
|
|
931
1437
|
switch (type.kind) {
|
|
932
1438
|
case "enum":
|
|
933
1439
|
return type.ref;
|
|
1440
|
+
case "ref":
|
|
1441
|
+
return type.ref;
|
|
934
1442
|
case "unknown":
|
|
935
1443
|
return "unknown";
|
|
936
1444
|
case "scalar":
|
|
937
1445
|
return mapScalar(type.scalar);
|
|
1446
|
+
case "union":
|
|
1447
|
+
return flattenUnion(type).map(
|
|
1448
|
+
(variant2) => variant2.kind === "union" ? `(${scalarTsType(variant2)})` : scalarTsType(variant2)
|
|
1449
|
+
).join(" | ");
|
|
1450
|
+
case "map":
|
|
1451
|
+
return `Record<string, ${scalarTsType(type.value)}>`;
|
|
1452
|
+
case "array": {
|
|
1453
|
+
const element = scalarTsType(type.element);
|
|
1454
|
+
return type.element.kind === "union" ? `(${element})[]` : `${element}[]`;
|
|
1455
|
+
}
|
|
938
1456
|
}
|
|
939
1457
|
}
|
|
940
1458
|
function mapScalar(scalar) {
|
|
@@ -982,10 +1500,13 @@ function mapScalar(scalar) {
|
|
|
982
1500
|
ScalarTypeSchema,
|
|
983
1501
|
SourceIrSchema,
|
|
984
1502
|
StringFormatSchema,
|
|
1503
|
+
TypeAliasSchema,
|
|
985
1504
|
assertIR,
|
|
986
1505
|
assertSourceIR,
|
|
1506
|
+
collectRefNames,
|
|
987
1507
|
createFields,
|
|
988
1508
|
createSourceIR,
|
|
1509
|
+
flattenUnion,
|
|
989
1510
|
getSource,
|
|
990
1511
|
isCompatible,
|
|
991
1512
|
isCreateOptional,
|
|
@@ -993,11 +1514,15 @@ function mapScalar(scalar) {
|
|
|
993
1514
|
isDbAssigned,
|
|
994
1515
|
iterEntities,
|
|
995
1516
|
iterFields,
|
|
1517
|
+
iterTypeAliases,
|
|
996
1518
|
parseIR,
|
|
997
1519
|
primaryKeyFields,
|
|
1520
|
+
refCycleMembers,
|
|
998
1521
|
resolveEntity,
|
|
999
1522
|
resolveEnum,
|
|
1523
|
+
resolveRef,
|
|
1000
1524
|
resolveRelationTarget,
|
|
1525
|
+
resolveTypeAlias,
|
|
1001
1526
|
scalarTsType,
|
|
1002
1527
|
updateFields,
|
|
1003
1528
|
validateIR,
|