@sequenceholdings/orm 2.8.3 → 2.8.5
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/dist/authoring.cjs +154 -45
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +12 -2
- package/dist/authoring.d.ts +12 -2
- package/dist/authoring.js +154 -45
- package/dist/authoring.js.map +1 -1
- package/dist/canonical.cjs +64 -14
- package/dist/canonical.cjs.map +1 -1
- package/dist/canonical.d.cts +12 -7
- package/dist/canonical.d.ts +12 -7
- package/dist/canonical.js +64 -14
- package/dist/canonical.js.map +1 -1
- package/dist/cli.cjs +78 -20
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +78 -20
- package/dist/cli.js.map +1 -1
- package/dist/compiler.cjs +77 -20
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.d.cts +4 -4
- package/dist/compiler.d.ts +4 -4
- package/dist/compiler.js +77 -20
- package/dist/compiler.js.map +1 -1
- package/dist/index.cjs +161 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -11
- package/dist/index.d.ts +11 -11
- package/dist/index.js +161 -47
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +71 -16
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +9 -9
- package/dist/ir.d.ts +9 -9
- package/dist/ir.js +71 -16
- package/dist/ir.js.map +1 -1
- package/dist/{naming-CU9ThP_E.d.cts → naming-BMZKFO0W.d.cts} +1 -2
- package/dist/{naming-CPfx7EfF.d.ts → naming-DUk7AcXs.d.ts} +1 -2
- package/dist/predicate-v2.cjs +71 -16
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.d.cts +3 -3
- package/dist/predicate-v2.d.ts +3 -3
- package/dist/predicate-v2.js +71 -16
- package/dist/predicate-v2.js.map +1 -1
- package/dist/sdl.cjs +71 -16
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.d.cts +2 -2
- package/dist/sdl.d.ts +2 -2
- package/dist/sdl.js +71 -16
- package/dist/sdl.js.map +1 -1
- package/package.json +1 -1
- package/src/codegen-gql/apollo-hooks.cjs +1 -1
- package/src/codegen-gql/server-fns.cjs +1 -1
- package/templates/orm-namespace/graphql/example/queries.ts +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -415,6 +415,48 @@ var policyExpressionSchema = zod.z.union([
|
|
|
415
415
|
variants: zod.z.array(policyVariantSchema).max(10).default([])
|
|
416
416
|
})
|
|
417
417
|
]);
|
|
418
|
+
|
|
419
|
+
// src/canonical/resolved-rows.ts
|
|
420
|
+
function logicalKey(table) {
|
|
421
|
+
return (table.primaryKey ?? []).map((columnName) => table.columns.find((c) => c.columnName === columnName)?.name ?? columnName);
|
|
422
|
+
}
|
|
423
|
+
function sameKey(key, expected) {
|
|
424
|
+
return key.length === expected.length && key.every((name2) => expected.includes(name2));
|
|
425
|
+
}
|
|
426
|
+
function validateRowIdentity(input) {
|
|
427
|
+
validateExistsIn(input);
|
|
428
|
+
if (input.keys.length > 1) validateCompositeKey(input);
|
|
429
|
+
}
|
|
430
|
+
function validateExistsIn({ resolved, sourceByName, fail: fail4 }) {
|
|
431
|
+
const name2 = resolved.existsIn?.[0];
|
|
432
|
+
if (name2 === void 0) return;
|
|
433
|
+
if (sourceByName.get(name2)?.kind !== "etl") fail4(`existsIn '${name2}' must be an etl source`);
|
|
434
|
+
if (resolved.policies.insert !== void 0 || resolved.policies.write !== void 0) {
|
|
435
|
+
fail4("existsIn allows only an update policy: rows come from the etl source");
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function validateCompositeKey({ resolved, keys, tableByName, fail: fail4 }) {
|
|
439
|
+
const keyList = `(${keys.join(", ")})`;
|
|
440
|
+
if (resolved.existsIn === void 0) {
|
|
441
|
+
fail4(`requires exactly one primary-key column: composite key ${keyList} requires existsIn`);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const writes = ["insert", "update", "write"].filter((kind) => resolved.policies[kind] !== void 0);
|
|
445
|
+
if (writes.length > 0) fail4(`composite key ${keyList} requires a read-only table: remove the ${writes.join("/")} policy`);
|
|
446
|
+
if (resolved.materialized) fail4(`composite key ${keyList} cannot be materialized`);
|
|
447
|
+
for (const source of resolved.sources) {
|
|
448
|
+
if (source.kind === "prefetch") {
|
|
449
|
+
fail4(`composite key ${keyList} cannot have prefetch source '${source.name}'`);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const table = tableByName.get(source.table);
|
|
453
|
+
if (table && !sameKey(logicalKey(table), keys)) {
|
|
454
|
+
fail4(`composite key ${keyList} requires ${source.kind} source '${source.name}' to be keyed by it; table '${source.table}' is keyed by (${logicalKey(table).join(", ")})`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/canonical/resolved.ts
|
|
418
460
|
function parseDurationMs(input) {
|
|
419
461
|
if (typeof input === "number") return Number.isInteger(input) && input > 0 ? input : void 0;
|
|
420
462
|
const match = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(input);
|
|
@@ -491,6 +533,13 @@ var canonicalResolvedSchema = zod.z.object({
|
|
|
491
533
|
resolve: zod.z.array(canonicalResolveRuleSchema).max(500),
|
|
492
534
|
/** Sources whose tombstone hides the resolved row (v1: the manual source only). */
|
|
493
535
|
deletedBy: zod.z.array(identifier).max(1).default([]),
|
|
536
|
+
/**
|
|
537
|
+
* `rows.existsIn`: the one etl source whose rows are the table's rows (read
|
|
538
|
+
* directly as the row source). Only an update policy may write. It is also
|
|
539
|
+
* what admits a composite primary key: a read-only table keyed exactly like
|
|
540
|
+
* that source.
|
|
541
|
+
*/
|
|
542
|
+
existsIn: zod.z.array(identifier).length(1).optional(),
|
|
494
543
|
/**
|
|
495
544
|
* Materialized: the view reads an indexed internal table (`table`) that the
|
|
496
545
|
* data plane keeps in step with `compute`, the internal view holding the
|
|
@@ -509,8 +558,8 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
509
558
|
if (!resolved) return [];
|
|
510
559
|
const errors = [];
|
|
511
560
|
const fail4 = (message) => errors.push(`resolved table '${view.view}': ${message}`);
|
|
512
|
-
const
|
|
513
|
-
if (
|
|
561
|
+
const keyColumns = (view.primaryKey ?? []).map((columnName) => view.columns.find((c) => c.columnName === columnName)).filter((column) => column !== void 0);
|
|
562
|
+
if (keyColumns.length === 0 || keyColumns.length !== view.primaryKey?.length) {
|
|
514
563
|
fail4("requires exactly one primary-key column");
|
|
515
564
|
return errors;
|
|
516
565
|
}
|
|
@@ -518,7 +567,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
518
567
|
const scope = {
|
|
519
568
|
view,
|
|
520
569
|
resolved,
|
|
521
|
-
|
|
570
|
+
keys: new Map(keyColumns.map((column) => [column.name, column])),
|
|
522
571
|
dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
|
|
523
572
|
sourceByName: /* @__PURE__ */ new Map(),
|
|
524
573
|
fail: fail4
|
|
@@ -532,6 +581,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
532
581
|
if (!table) fail4(`source '${source.name}' references unknown table '${source.table}'`);
|
|
533
582
|
else validateSource({ scope, source, table });
|
|
534
583
|
}
|
|
584
|
+
validateRowIdentity({ resolved, keys: [...scope.keys.keys()], sourceByName: scope.sourceByName, tableByName, fail: fail4 });
|
|
535
585
|
validateRules(scope);
|
|
536
586
|
validateViewCells(scope);
|
|
537
587
|
if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
|
|
@@ -561,7 +611,7 @@ function validateSource({ scope, source, table }) {
|
|
|
561
611
|
validatePrefetchFields({ scope, source });
|
|
562
612
|
}
|
|
563
613
|
function validatePrefetchFields({ scope, source }) {
|
|
564
|
-
const { fail: fail4,
|
|
614
|
+
const { fail: fail4, keys } = scope;
|
|
565
615
|
if (source.kind !== "prefetch") {
|
|
566
616
|
if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
|
|
567
617
|
fail4(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
|
|
@@ -570,7 +620,7 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
570
620
|
}
|
|
571
621
|
if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
|
|
572
622
|
fail4(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
|
|
573
|
-
} else if (source.key
|
|
623
|
+
} else if (!keys.has(source.key) && !source.columns.includes(source.key)) {
|
|
574
624
|
fail4(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
|
|
575
625
|
}
|
|
576
626
|
for (const column of source.writeback?.columns ?? []) {
|
|
@@ -578,19 +628,19 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
578
628
|
}
|
|
579
629
|
}
|
|
580
630
|
function validateSourceColumns({ scope, source, byName }) {
|
|
581
|
-
const { fail: fail4,
|
|
582
|
-
for (const column of [
|
|
583
|
-
const own = column
|
|
631
|
+
const { fail: fail4, keys, dataColumns } = scope;
|
|
632
|
+
for (const column of [...keys.keys(), ...source.columns]) {
|
|
633
|
+
const own = keys.get(column) ?? dataColumns.get(column);
|
|
584
634
|
const theirs = byName.get(column);
|
|
585
635
|
if (!own) fail4(`source '${source.name}' supplies unknown column '${column}'`);
|
|
586
636
|
else if (!theirs) fail4(`source '${source.name}' table '${source.table}' has no column '${column}'`);
|
|
587
637
|
else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail4(`column '${column}' has a different type on source '${source.name}'`);
|
|
588
638
|
}
|
|
589
639
|
}
|
|
590
|
-
function validateRules({ resolved,
|
|
640
|
+
function validateRules({ resolved, keys, dataColumns, sourceByName, fail: fail4 }) {
|
|
591
641
|
const ruled = /* @__PURE__ */ new Set();
|
|
592
642
|
for (const rule of resolved.resolve) {
|
|
593
|
-
if (!dataColumns.has(rule.column) || rule.column
|
|
643
|
+
if (!dataColumns.has(rule.column) || keys.has(rule.column)) {
|
|
594
644
|
fail4(`resolve rule targets unknown column '${rule.column}'`);
|
|
595
645
|
continue;
|
|
596
646
|
}
|
|
@@ -607,7 +657,7 @@ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fa
|
|
|
607
657
|
}
|
|
608
658
|
}
|
|
609
659
|
for (const column of dataColumns.keys()) {
|
|
610
|
-
if (column
|
|
660
|
+
if (!keys.has(column) && !ruled.has(column)) fail4(`column '${column}' has no resolve rule`);
|
|
611
661
|
}
|
|
612
662
|
}
|
|
613
663
|
var VIEW_CELL_TYPES = {
|
|
@@ -616,10 +666,10 @@ var VIEW_CELL_TYPES = {
|
|
|
616
666
|
by: "text",
|
|
617
667
|
conflicts: "jsonb"
|
|
618
668
|
};
|
|
619
|
-
function validateViewCells({ view, dataColumns,
|
|
669
|
+
function validateViewCells({ view, dataColumns, keys, fail: fail4 }) {
|
|
620
670
|
const expected = /* @__PURE__ */ new Map();
|
|
621
671
|
for (const column of dataColumns.values()) {
|
|
622
|
-
if (column.name
|
|
672
|
+
if (keys.has(column.name)) continue;
|
|
623
673
|
for (const part of RESOLVED_VIEW_CELL_PARTS) {
|
|
624
674
|
expected.set(cellColumnName(column.name, part), { part, columnName: cellColumnName(column.columnName, part) });
|
|
625
675
|
}
|
|
@@ -987,7 +1037,7 @@ var canonicalTableSchema = zod.z.object({
|
|
|
987
1037
|
table: identifier2,
|
|
988
1038
|
displayName: zod.z.string().trim().min(1).max(120).optional(),
|
|
989
1039
|
description: zod.z.string().max(2e3).optional(),
|
|
990
|
-
/** Table-level rename hint
|
|
1040
|
+
/** Table-level rename hint. */
|
|
991
1041
|
renamedFrom: identifier2.optional(),
|
|
992
1042
|
/** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
|
|
993
1043
|
source: zod.z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
|
|
@@ -1845,8 +1895,13 @@ var resolvedSourceIrSchema = zod.z.object({
|
|
|
1845
1895
|
}).optional()
|
|
1846
1896
|
});
|
|
1847
1897
|
var resolvedMetaIrSchema = zod.z.object({
|
|
1848
|
-
/**
|
|
1849
|
-
|
|
1898
|
+
/**
|
|
1899
|
+
* Logical pk column of the resolved view. Absent for a composite key, which
|
|
1900
|
+
* only a read-only `existsIn` table may declare: it has no writes, prefetch
|
|
1901
|
+
* or materialization (the paths that address one key column), and its reads
|
|
1902
|
+
* use the view's `primaryKey` like any keyed view.
|
|
1903
|
+
*/
|
|
1904
|
+
pk: zod.z.string().optional(),
|
|
1850
1905
|
/** Source name of the manual source. */
|
|
1851
1906
|
manual: zod.z.string(),
|
|
1852
1907
|
sources: zod.z.array(resolvedSourceIrSchema),
|
|
@@ -3271,7 +3326,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
3271
3326
|
appendable: readOnly ? [] : active.filter((c) => c.type === "jsonb").map((c) => c.name),
|
|
3272
3327
|
...touchOnUpdate.length > 0 ? { touchOnUpdate } : {},
|
|
3273
3328
|
...cells ? { cells } : {},
|
|
3274
|
-
...resolved ? { resolved: resolvedMeta({ resolved,
|
|
3329
|
+
...resolved ? { resolved: resolvedMeta({ resolved, primaryKey: primaryKey2 }) } : {},
|
|
3275
3330
|
constraints: isView ? [] : constraints,
|
|
3276
3331
|
dynamicFilterMaxDepth: 6,
|
|
3277
3332
|
dynamicFilterMaxNodes: 64
|
|
@@ -3553,10 +3608,10 @@ function resolvedWritable(entries) {
|
|
|
3553
3608
|
const system = new Set(Object.values(RESOLVED_VIEW_SYSTEM_COLUMNS).map((column) => column.name));
|
|
3554
3609
|
return entries.filter((entry) => !entry.deprecated && !system.has(entry.name)).map((entry) => entry.name);
|
|
3555
3610
|
}
|
|
3556
|
-
function resolvedMeta({ resolved,
|
|
3611
|
+
function resolvedMeta({ resolved, primaryKey: primaryKey2 }) {
|
|
3557
3612
|
const manual = resolved.sources.find((source) => source.kind === "manual");
|
|
3558
3613
|
return {
|
|
3559
|
-
pk,
|
|
3614
|
+
...primaryKey2.length === 1 ? { pk: primaryKey2[0] } : {},
|
|
3560
3615
|
manual: manual?.name ?? "",
|
|
3561
3616
|
deletedBy: [...resolved.deletedBy],
|
|
3562
3617
|
...resolved.materialized ? { materialized: { ...resolved.materialized } } : {},
|
|
@@ -3682,7 +3737,9 @@ function applyPrefetchDirectives({ node, root, meta }) {
|
|
|
3682
3737
|
if (directive.maxStaleness !== void 0 && maxStalenessMs === void 0) {
|
|
3683
3738
|
fail(`@prefetch(maxStaleness: "${directive.maxStaleness}") must look like '500ms', '30s' or '5m'`, "invalid-source-directive");
|
|
3684
3739
|
}
|
|
3685
|
-
const
|
|
3740
|
+
const pk = meta.resolved?.pk;
|
|
3741
|
+
if (pk === void 0) return fail(`@prefetch on '${root.alias}' needs a single-column primary key`, "invalid-source-directive");
|
|
3742
|
+
const key = root.kind === "byPk" ? { one: root.pk[0].value } : selectKey({ where: root.where, pk, source, alias: root.alias });
|
|
3686
3743
|
return { source: source.name, ...maxStalenessMs !== void 0 ? { maxStalenessMs } : {}, key };
|
|
3687
3744
|
});
|
|
3688
3745
|
return { ...root, prefetch: plans };
|
|
@@ -6578,6 +6635,7 @@ function buildCanonicalResolved(resolved) {
|
|
|
6578
6635
|
})),
|
|
6579
6636
|
resolve: resolved.rules.map((rule) => ({ ...rule, candidates: [...rule.candidates] })),
|
|
6580
6637
|
deletedBy: [...resolved.deletedBy],
|
|
6638
|
+
...resolved.existsIn !== void 0 ? { existsIn: [...resolved.existsIn] } : {},
|
|
6581
6639
|
...resolved.materialized ? { materialized: { ...resolved.materialized } } : {},
|
|
6582
6640
|
policies
|
|
6583
6641
|
};
|