@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.js
CHANGED
|
@@ -412,6 +412,48 @@ var policyExpressionSchema = z.union([
|
|
|
412
412
|
variants: z.array(policyVariantSchema).max(10).default([])
|
|
413
413
|
})
|
|
414
414
|
]);
|
|
415
|
+
|
|
416
|
+
// src/canonical/resolved-rows.ts
|
|
417
|
+
function logicalKey(table) {
|
|
418
|
+
return (table.primaryKey ?? []).map((columnName) => table.columns.find((c) => c.columnName === columnName)?.name ?? columnName);
|
|
419
|
+
}
|
|
420
|
+
function sameKey(key, expected) {
|
|
421
|
+
return key.length === expected.length && key.every((name2) => expected.includes(name2));
|
|
422
|
+
}
|
|
423
|
+
function validateRowIdentity(input) {
|
|
424
|
+
validateExistsIn(input);
|
|
425
|
+
if (input.keys.length > 1) validateCompositeKey(input);
|
|
426
|
+
}
|
|
427
|
+
function validateExistsIn({ resolved, sourceByName, fail: fail4 }) {
|
|
428
|
+
const name2 = resolved.existsIn?.[0];
|
|
429
|
+
if (name2 === void 0) return;
|
|
430
|
+
if (sourceByName.get(name2)?.kind !== "etl") fail4(`existsIn '${name2}' must be an etl source`);
|
|
431
|
+
if (resolved.policies.insert !== void 0 || resolved.policies.write !== void 0) {
|
|
432
|
+
fail4("existsIn allows only an update policy: rows come from the etl source");
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function validateCompositeKey({ resolved, keys, tableByName, fail: fail4 }) {
|
|
436
|
+
const keyList = `(${keys.join(", ")})`;
|
|
437
|
+
if (resolved.existsIn === void 0) {
|
|
438
|
+
fail4(`requires exactly one primary-key column: composite key ${keyList} requires existsIn`);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
const writes = ["insert", "update", "write"].filter((kind) => resolved.policies[kind] !== void 0);
|
|
442
|
+
if (writes.length > 0) fail4(`composite key ${keyList} requires a read-only table: remove the ${writes.join("/")} policy`);
|
|
443
|
+
if (resolved.materialized) fail4(`composite key ${keyList} cannot be materialized`);
|
|
444
|
+
for (const source of resolved.sources) {
|
|
445
|
+
if (source.kind === "prefetch") {
|
|
446
|
+
fail4(`composite key ${keyList} cannot have prefetch source '${source.name}'`);
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const table = tableByName.get(source.table);
|
|
450
|
+
if (table && !sameKey(logicalKey(table), keys)) {
|
|
451
|
+
fail4(`composite key ${keyList} requires ${source.kind} source '${source.name}' to be keyed by it; table '${source.table}' is keyed by (${logicalKey(table).join(", ")})`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// src/canonical/resolved.ts
|
|
415
457
|
function parseDurationMs(input) {
|
|
416
458
|
if (typeof input === "number") return Number.isInteger(input) && input > 0 ? input : void 0;
|
|
417
459
|
const match = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(input);
|
|
@@ -488,6 +530,13 @@ var canonicalResolvedSchema = z.object({
|
|
|
488
530
|
resolve: z.array(canonicalResolveRuleSchema).max(500),
|
|
489
531
|
/** Sources whose tombstone hides the resolved row (v1: the manual source only). */
|
|
490
532
|
deletedBy: z.array(identifier).max(1).default([]),
|
|
533
|
+
/**
|
|
534
|
+
* `rows.existsIn`: the one etl source whose rows are the table's rows (read
|
|
535
|
+
* directly as the row source). Only an update policy may write. It is also
|
|
536
|
+
* what admits a composite primary key: a read-only table keyed exactly like
|
|
537
|
+
* that source.
|
|
538
|
+
*/
|
|
539
|
+
existsIn: z.array(identifier).length(1).optional(),
|
|
491
540
|
/**
|
|
492
541
|
* Materialized: the view reads an indexed internal table (`table`) that the
|
|
493
542
|
* data plane keeps in step with `compute`, the internal view holding the
|
|
@@ -506,8 +555,8 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
506
555
|
if (!resolved) return [];
|
|
507
556
|
const errors = [];
|
|
508
557
|
const fail4 = (message) => errors.push(`resolved table '${view.view}': ${message}`);
|
|
509
|
-
const
|
|
510
|
-
if (
|
|
558
|
+
const keyColumns = (view.primaryKey ?? []).map((columnName) => view.columns.find((c) => c.columnName === columnName)).filter((column) => column !== void 0);
|
|
559
|
+
if (keyColumns.length === 0 || keyColumns.length !== view.primaryKey?.length) {
|
|
511
560
|
fail4("requires exactly one primary-key column");
|
|
512
561
|
return errors;
|
|
513
562
|
}
|
|
@@ -515,7 +564,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
515
564
|
const scope = {
|
|
516
565
|
view,
|
|
517
566
|
resolved,
|
|
518
|
-
|
|
567
|
+
keys: new Map(keyColumns.map((column) => [column.name, column])),
|
|
519
568
|
dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
|
|
520
569
|
sourceByName: /* @__PURE__ */ new Map(),
|
|
521
570
|
fail: fail4
|
|
@@ -529,6 +578,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
529
578
|
if (!table) fail4(`source '${source.name}' references unknown table '${source.table}'`);
|
|
530
579
|
else validateSource({ scope, source, table });
|
|
531
580
|
}
|
|
581
|
+
validateRowIdentity({ resolved, keys: [...scope.keys.keys()], sourceByName: scope.sourceByName, tableByName, fail: fail4 });
|
|
532
582
|
validateRules(scope);
|
|
533
583
|
validateViewCells(scope);
|
|
534
584
|
if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
|
|
@@ -558,7 +608,7 @@ function validateSource({ scope, source, table }) {
|
|
|
558
608
|
validatePrefetchFields({ scope, source });
|
|
559
609
|
}
|
|
560
610
|
function validatePrefetchFields({ scope, source }) {
|
|
561
|
-
const { fail: fail4,
|
|
611
|
+
const { fail: fail4, keys } = scope;
|
|
562
612
|
if (source.kind !== "prefetch") {
|
|
563
613
|
if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
|
|
564
614
|
fail4(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
|
|
@@ -567,7 +617,7 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
567
617
|
}
|
|
568
618
|
if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
|
|
569
619
|
fail4(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
|
|
570
|
-
} else if (source.key
|
|
620
|
+
} else if (!keys.has(source.key) && !source.columns.includes(source.key)) {
|
|
571
621
|
fail4(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
|
|
572
622
|
}
|
|
573
623
|
for (const column of source.writeback?.columns ?? []) {
|
|
@@ -575,19 +625,19 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
575
625
|
}
|
|
576
626
|
}
|
|
577
627
|
function validateSourceColumns({ scope, source, byName }) {
|
|
578
|
-
const { fail: fail4,
|
|
579
|
-
for (const column of [
|
|
580
|
-
const own = column
|
|
628
|
+
const { fail: fail4, keys, dataColumns } = scope;
|
|
629
|
+
for (const column of [...keys.keys(), ...source.columns]) {
|
|
630
|
+
const own = keys.get(column) ?? dataColumns.get(column);
|
|
581
631
|
const theirs = byName.get(column);
|
|
582
632
|
if (!own) fail4(`source '${source.name}' supplies unknown column '${column}'`);
|
|
583
633
|
else if (!theirs) fail4(`source '${source.name}' table '${source.table}' has no column '${column}'`);
|
|
584
634
|
else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail4(`column '${column}' has a different type on source '${source.name}'`);
|
|
585
635
|
}
|
|
586
636
|
}
|
|
587
|
-
function validateRules({ resolved,
|
|
637
|
+
function validateRules({ resolved, keys, dataColumns, sourceByName, fail: fail4 }) {
|
|
588
638
|
const ruled = /* @__PURE__ */ new Set();
|
|
589
639
|
for (const rule of resolved.resolve) {
|
|
590
|
-
if (!dataColumns.has(rule.column) || rule.column
|
|
640
|
+
if (!dataColumns.has(rule.column) || keys.has(rule.column)) {
|
|
591
641
|
fail4(`resolve rule targets unknown column '${rule.column}'`);
|
|
592
642
|
continue;
|
|
593
643
|
}
|
|
@@ -604,7 +654,7 @@ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fa
|
|
|
604
654
|
}
|
|
605
655
|
}
|
|
606
656
|
for (const column of dataColumns.keys()) {
|
|
607
|
-
if (column
|
|
657
|
+
if (!keys.has(column) && !ruled.has(column)) fail4(`column '${column}' has no resolve rule`);
|
|
608
658
|
}
|
|
609
659
|
}
|
|
610
660
|
var VIEW_CELL_TYPES = {
|
|
@@ -613,10 +663,10 @@ var VIEW_CELL_TYPES = {
|
|
|
613
663
|
by: "text",
|
|
614
664
|
conflicts: "jsonb"
|
|
615
665
|
};
|
|
616
|
-
function validateViewCells({ view, dataColumns,
|
|
666
|
+
function validateViewCells({ view, dataColumns, keys, fail: fail4 }) {
|
|
617
667
|
const expected = /* @__PURE__ */ new Map();
|
|
618
668
|
for (const column of dataColumns.values()) {
|
|
619
|
-
if (column.name
|
|
669
|
+
if (keys.has(column.name)) continue;
|
|
620
670
|
for (const part of RESOLVED_VIEW_CELL_PARTS) {
|
|
621
671
|
expected.set(cellColumnName(column.name, part), { part, columnName: cellColumnName(column.columnName, part) });
|
|
622
672
|
}
|
|
@@ -984,7 +1034,7 @@ var canonicalTableSchema = z.object({
|
|
|
984
1034
|
table: identifier2,
|
|
985
1035
|
displayName: z.string().trim().min(1).max(120).optional(),
|
|
986
1036
|
description: z.string().max(2e3).optional(),
|
|
987
|
-
/** Table-level rename hint
|
|
1037
|
+
/** Table-level rename hint. */
|
|
988
1038
|
renamedFrom: identifier2.optional(),
|
|
989
1039
|
/** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
|
|
990
1040
|
source: z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
|
|
@@ -1842,8 +1892,13 @@ var resolvedSourceIrSchema = z.object({
|
|
|
1842
1892
|
}).optional()
|
|
1843
1893
|
});
|
|
1844
1894
|
var resolvedMetaIrSchema = z.object({
|
|
1845
|
-
/**
|
|
1846
|
-
|
|
1895
|
+
/**
|
|
1896
|
+
* Logical pk column of the resolved view. Absent for a composite key, which
|
|
1897
|
+
* only a read-only `existsIn` table may declare: it has no writes, prefetch
|
|
1898
|
+
* or materialization (the paths that address one key column), and its reads
|
|
1899
|
+
* use the view's `primaryKey` like any keyed view.
|
|
1900
|
+
*/
|
|
1901
|
+
pk: z.string().optional(),
|
|
1847
1902
|
/** Source name of the manual source. */
|
|
1848
1903
|
manual: z.string(),
|
|
1849
1904
|
sources: z.array(resolvedSourceIrSchema),
|
|
@@ -3268,7 +3323,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
3268
3323
|
appendable: readOnly ? [] : active.filter((c) => c.type === "jsonb").map((c) => c.name),
|
|
3269
3324
|
...touchOnUpdate.length > 0 ? { touchOnUpdate } : {},
|
|
3270
3325
|
...cells ? { cells } : {},
|
|
3271
|
-
...resolved ? { resolved: resolvedMeta({ resolved,
|
|
3326
|
+
...resolved ? { resolved: resolvedMeta({ resolved, primaryKey: primaryKey2 }) } : {},
|
|
3272
3327
|
constraints: isView ? [] : constraints,
|
|
3273
3328
|
dynamicFilterMaxDepth: 6,
|
|
3274
3329
|
dynamicFilterMaxNodes: 64
|
|
@@ -3550,10 +3605,10 @@ function resolvedWritable(entries) {
|
|
|
3550
3605
|
const system = new Set(Object.values(RESOLVED_VIEW_SYSTEM_COLUMNS).map((column) => column.name));
|
|
3551
3606
|
return entries.filter((entry) => !entry.deprecated && !system.has(entry.name)).map((entry) => entry.name);
|
|
3552
3607
|
}
|
|
3553
|
-
function resolvedMeta({ resolved,
|
|
3608
|
+
function resolvedMeta({ resolved, primaryKey: primaryKey2 }) {
|
|
3554
3609
|
const manual = resolved.sources.find((source) => source.kind === "manual");
|
|
3555
3610
|
return {
|
|
3556
|
-
pk,
|
|
3611
|
+
...primaryKey2.length === 1 ? { pk: primaryKey2[0] } : {},
|
|
3557
3612
|
manual: manual?.name ?? "",
|
|
3558
3613
|
deletedBy: [...resolved.deletedBy],
|
|
3559
3614
|
...resolved.materialized ? { materialized: { ...resolved.materialized } } : {},
|
|
@@ -3679,7 +3734,9 @@ function applyPrefetchDirectives({ node, root, meta }) {
|
|
|
3679
3734
|
if (directive.maxStaleness !== void 0 && maxStalenessMs === void 0) {
|
|
3680
3735
|
fail(`@prefetch(maxStaleness: "${directive.maxStaleness}") must look like '500ms', '30s' or '5m'`, "invalid-source-directive");
|
|
3681
3736
|
}
|
|
3682
|
-
const
|
|
3737
|
+
const pk = meta.resolved?.pk;
|
|
3738
|
+
if (pk === void 0) return fail(`@prefetch on '${root.alias}' needs a single-column primary key`, "invalid-source-directive");
|
|
3739
|
+
const key = root.kind === "byPk" ? { one: root.pk[0].value } : selectKey({ where: root.where, pk, source, alias: root.alias });
|
|
3683
3740
|
return { source: source.name, ...maxStalenessMs !== void 0 ? { maxStalenessMs } : {}, key };
|
|
3684
3741
|
});
|
|
3685
3742
|
return { ...root, prefetch: plans };
|
|
@@ -6575,6 +6632,7 @@ function buildCanonicalResolved(resolved) {
|
|
|
6575
6632
|
})),
|
|
6576
6633
|
resolve: resolved.rules.map((rule) => ({ ...rule, candidates: [...rule.candidates] })),
|
|
6577
6634
|
deletedBy: [...resolved.deletedBy],
|
|
6635
|
+
...resolved.existsIn !== void 0 ? { existsIn: [...resolved.existsIn] } : {},
|
|
6578
6636
|
...resolved.materialized ? { materialized: { ...resolved.materialized } } : {},
|
|
6579
6637
|
policies
|
|
6580
6638
|
};
|