@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/compiler.cjs
CHANGED
|
@@ -266,6 +266,48 @@ var policyExpressionSchema = zod.z.union([
|
|
|
266
266
|
variants: zod.z.array(policyVariantSchema).max(10).default([])
|
|
267
267
|
})
|
|
268
268
|
]);
|
|
269
|
+
|
|
270
|
+
// src/canonical/resolved-rows.ts
|
|
271
|
+
function logicalKey(table) {
|
|
272
|
+
return (table.primaryKey ?? []).map((columnName) => table.columns.find((c) => c.columnName === columnName)?.name ?? columnName);
|
|
273
|
+
}
|
|
274
|
+
function sameKey(key, expected) {
|
|
275
|
+
return key.length === expected.length && key.every((name2) => expected.includes(name2));
|
|
276
|
+
}
|
|
277
|
+
function validateRowIdentity(input) {
|
|
278
|
+
validateExistsIn(input);
|
|
279
|
+
if (input.keys.length > 1) validateCompositeKey(input);
|
|
280
|
+
}
|
|
281
|
+
function validateExistsIn({ resolved, sourceByName, fail: fail4 }) {
|
|
282
|
+
const name2 = resolved.existsIn?.[0];
|
|
283
|
+
if (name2 === void 0) return;
|
|
284
|
+
if (sourceByName.get(name2)?.kind !== "etl") fail4(`existsIn '${name2}' must be an etl source`);
|
|
285
|
+
if (resolved.policies.insert !== void 0 || resolved.policies.write !== void 0) {
|
|
286
|
+
fail4("existsIn allows only an update policy: rows come from the etl source");
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function validateCompositeKey({ resolved, keys, tableByName, fail: fail4 }) {
|
|
290
|
+
const keyList = `(${keys.join(", ")})`;
|
|
291
|
+
if (resolved.existsIn === void 0) {
|
|
292
|
+
fail4(`requires exactly one primary-key column: composite key ${keyList} requires existsIn`);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const writes = ["insert", "update", "write"].filter((kind) => resolved.policies[kind] !== void 0);
|
|
296
|
+
if (writes.length > 0) fail4(`composite key ${keyList} requires a read-only table: remove the ${writes.join("/")} policy`);
|
|
297
|
+
if (resolved.materialized) fail4(`composite key ${keyList} cannot be materialized`);
|
|
298
|
+
for (const source of resolved.sources) {
|
|
299
|
+
if (source.kind === "prefetch") {
|
|
300
|
+
fail4(`composite key ${keyList} cannot have prefetch source '${source.name}'`);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const table = tableByName.get(source.table);
|
|
304
|
+
if (table && !sameKey(logicalKey(table), keys)) {
|
|
305
|
+
fail4(`composite key ${keyList} requires ${source.kind} source '${source.name}' to be keyed by it; table '${source.table}' is keyed by (${logicalKey(table).join(", ")})`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/canonical/resolved.ts
|
|
269
311
|
function parseDurationMs(input) {
|
|
270
312
|
if (typeof input === "number") return Number.isInteger(input) && input > 0 ? input : void 0;
|
|
271
313
|
const match = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(input);
|
|
@@ -342,6 +384,13 @@ var canonicalResolvedSchema = zod.z.object({
|
|
|
342
384
|
resolve: zod.z.array(canonicalResolveRuleSchema).max(500),
|
|
343
385
|
/** Sources whose tombstone hides the resolved row (v1: the manual source only). */
|
|
344
386
|
deletedBy: zod.z.array(identifier).max(1).default([]),
|
|
387
|
+
/**
|
|
388
|
+
* `rows.existsIn`: the one etl source whose rows are the table's rows (read
|
|
389
|
+
* directly as the row source). Only an update policy may write. It is also
|
|
390
|
+
* what admits a composite primary key: a read-only table keyed exactly like
|
|
391
|
+
* that source.
|
|
392
|
+
*/
|
|
393
|
+
existsIn: zod.z.array(identifier).length(1).optional(),
|
|
345
394
|
/**
|
|
346
395
|
* Materialized: the view reads an indexed internal table (`table`) that the
|
|
347
396
|
* data plane keeps in step with `compute`, the internal view holding the
|
|
@@ -360,8 +409,8 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
360
409
|
if (!resolved) return [];
|
|
361
410
|
const errors = [];
|
|
362
411
|
const fail4 = (message) => errors.push(`resolved table '${view.view}': ${message}`);
|
|
363
|
-
const
|
|
364
|
-
if (
|
|
412
|
+
const keyColumns = (view.primaryKey ?? []).map((columnName) => view.columns.find((c) => c.columnName === columnName)).filter((column) => column !== void 0);
|
|
413
|
+
if (keyColumns.length === 0 || keyColumns.length !== view.primaryKey?.length) {
|
|
365
414
|
fail4("requires exactly one primary-key column");
|
|
366
415
|
return errors;
|
|
367
416
|
}
|
|
@@ -369,7 +418,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
369
418
|
const scope = {
|
|
370
419
|
view,
|
|
371
420
|
resolved,
|
|
372
|
-
|
|
421
|
+
keys: new Map(keyColumns.map((column) => [column.name, column])),
|
|
373
422
|
dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
|
|
374
423
|
sourceByName: /* @__PURE__ */ new Map(),
|
|
375
424
|
fail: fail4
|
|
@@ -383,6 +432,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
383
432
|
if (!table) fail4(`source '${source.name}' references unknown table '${source.table}'`);
|
|
384
433
|
else validateSource({ scope, source, table });
|
|
385
434
|
}
|
|
435
|
+
validateRowIdentity({ resolved, keys: [...scope.keys.keys()], sourceByName: scope.sourceByName, tableByName, fail: fail4 });
|
|
386
436
|
validateRules(scope);
|
|
387
437
|
validateViewCells(scope);
|
|
388
438
|
if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
|
|
@@ -412,7 +462,7 @@ function validateSource({ scope, source, table }) {
|
|
|
412
462
|
validatePrefetchFields({ scope, source });
|
|
413
463
|
}
|
|
414
464
|
function validatePrefetchFields({ scope, source }) {
|
|
415
|
-
const { fail: fail4,
|
|
465
|
+
const { fail: fail4, keys } = scope;
|
|
416
466
|
if (source.kind !== "prefetch") {
|
|
417
467
|
if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
|
|
418
468
|
fail4(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
|
|
@@ -421,7 +471,7 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
421
471
|
}
|
|
422
472
|
if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
|
|
423
473
|
fail4(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
|
|
424
|
-
} else if (source.key
|
|
474
|
+
} else if (!keys.has(source.key) && !source.columns.includes(source.key)) {
|
|
425
475
|
fail4(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
|
|
426
476
|
}
|
|
427
477
|
for (const column of source.writeback?.columns ?? []) {
|
|
@@ -429,19 +479,19 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
429
479
|
}
|
|
430
480
|
}
|
|
431
481
|
function validateSourceColumns({ scope, source, byName }) {
|
|
432
|
-
const { fail: fail4,
|
|
433
|
-
for (const column of [
|
|
434
|
-
const own = column
|
|
482
|
+
const { fail: fail4, keys, dataColumns } = scope;
|
|
483
|
+
for (const column of [...keys.keys(), ...source.columns]) {
|
|
484
|
+
const own = keys.get(column) ?? dataColumns.get(column);
|
|
435
485
|
const theirs = byName.get(column);
|
|
436
486
|
if (!own) fail4(`source '${source.name}' supplies unknown column '${column}'`);
|
|
437
487
|
else if (!theirs) fail4(`source '${source.name}' table '${source.table}' has no column '${column}'`);
|
|
438
488
|
else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail4(`column '${column}' has a different type on source '${source.name}'`);
|
|
439
489
|
}
|
|
440
490
|
}
|
|
441
|
-
function validateRules({ resolved,
|
|
491
|
+
function validateRules({ resolved, keys, dataColumns, sourceByName, fail: fail4 }) {
|
|
442
492
|
const ruled = /* @__PURE__ */ new Set();
|
|
443
493
|
for (const rule of resolved.resolve) {
|
|
444
|
-
if (!dataColumns.has(rule.column) || rule.column
|
|
494
|
+
if (!dataColumns.has(rule.column) || keys.has(rule.column)) {
|
|
445
495
|
fail4(`resolve rule targets unknown column '${rule.column}'`);
|
|
446
496
|
continue;
|
|
447
497
|
}
|
|
@@ -458,7 +508,7 @@ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fa
|
|
|
458
508
|
}
|
|
459
509
|
}
|
|
460
510
|
for (const column of dataColumns.keys()) {
|
|
461
|
-
if (column
|
|
511
|
+
if (!keys.has(column) && !ruled.has(column)) fail4(`column '${column}' has no resolve rule`);
|
|
462
512
|
}
|
|
463
513
|
}
|
|
464
514
|
var VIEW_CELL_TYPES = {
|
|
@@ -467,10 +517,10 @@ var VIEW_CELL_TYPES = {
|
|
|
467
517
|
by: "text",
|
|
468
518
|
conflicts: "jsonb"
|
|
469
519
|
};
|
|
470
|
-
function validateViewCells({ view, dataColumns,
|
|
520
|
+
function validateViewCells({ view, dataColumns, keys, fail: fail4 }) {
|
|
471
521
|
const expected = /* @__PURE__ */ new Map();
|
|
472
522
|
for (const column of dataColumns.values()) {
|
|
473
|
-
if (column.name
|
|
523
|
+
if (keys.has(column.name)) continue;
|
|
474
524
|
for (const part of RESOLVED_VIEW_CELL_PARTS) {
|
|
475
525
|
expected.set(cellColumnName(column.name, part), { part, columnName: cellColumnName(column.columnName, part) });
|
|
476
526
|
}
|
|
@@ -838,7 +888,7 @@ var canonicalTableSchema = zod.z.object({
|
|
|
838
888
|
table: identifier2,
|
|
839
889
|
displayName: zod.z.string().trim().min(1).max(120).optional(),
|
|
840
890
|
description: zod.z.string().max(2e3).optional(),
|
|
841
|
-
/** Table-level rename hint
|
|
891
|
+
/** Table-level rename hint. */
|
|
842
892
|
renamedFrom: identifier2.optional(),
|
|
843
893
|
/** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
|
|
844
894
|
source: zod.z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
|
|
@@ -1696,8 +1746,13 @@ var resolvedSourceIrSchema = zod.z.object({
|
|
|
1696
1746
|
}).optional()
|
|
1697
1747
|
});
|
|
1698
1748
|
var resolvedMetaIrSchema = zod.z.object({
|
|
1699
|
-
/**
|
|
1700
|
-
|
|
1749
|
+
/**
|
|
1750
|
+
* Logical pk column of the resolved view. Absent for a composite key, which
|
|
1751
|
+
* only a read-only `existsIn` table may declare: it has no writes, prefetch
|
|
1752
|
+
* or materialization (the paths that address one key column), and its reads
|
|
1753
|
+
* use the view's `primaryKey` like any keyed view.
|
|
1754
|
+
*/
|
|
1755
|
+
pk: zod.z.string().optional(),
|
|
1701
1756
|
/** Source name of the manual source. */
|
|
1702
1757
|
manual: zod.z.string(),
|
|
1703
1758
|
sources: zod.z.array(resolvedSourceIrSchema),
|
|
@@ -3153,7 +3208,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
3153
3208
|
appendable: readOnly ? [] : active.filter((c) => c.type === "jsonb").map((c) => c.name),
|
|
3154
3209
|
...touchOnUpdate.length > 0 ? { touchOnUpdate } : {},
|
|
3155
3210
|
...cells ? { cells } : {},
|
|
3156
|
-
...resolved ? { resolved: resolvedMeta({ resolved,
|
|
3211
|
+
...resolved ? { resolved: resolvedMeta({ resolved, primaryKey }) } : {},
|
|
3157
3212
|
constraints: isView ? [] : constraints,
|
|
3158
3213
|
dynamicFilterMaxDepth: 6,
|
|
3159
3214
|
dynamicFilterMaxNodes: 64
|
|
@@ -3435,10 +3490,10 @@ function resolvedWritable(entries) {
|
|
|
3435
3490
|
const system = new Set(Object.values(RESOLVED_VIEW_SYSTEM_COLUMNS).map((column) => column.name));
|
|
3436
3491
|
return entries.filter((entry) => !entry.deprecated && !system.has(entry.name)).map((entry) => entry.name);
|
|
3437
3492
|
}
|
|
3438
|
-
function resolvedMeta({ resolved,
|
|
3493
|
+
function resolvedMeta({ resolved, primaryKey }) {
|
|
3439
3494
|
const manual = resolved.sources.find((source) => source.kind === "manual");
|
|
3440
3495
|
return {
|
|
3441
|
-
pk,
|
|
3496
|
+
...primaryKey.length === 1 ? { pk: primaryKey[0] } : {},
|
|
3442
3497
|
manual: manual?.name ?? "",
|
|
3443
3498
|
deletedBy: [...resolved.deletedBy],
|
|
3444
3499
|
...resolved.materialized ? { materialized: { ...resolved.materialized } } : {},
|
|
@@ -3564,7 +3619,9 @@ function applyPrefetchDirectives({ node, root, meta }) {
|
|
|
3564
3619
|
if (directive.maxStaleness !== void 0 && maxStalenessMs === void 0) {
|
|
3565
3620
|
fail(`@prefetch(maxStaleness: "${directive.maxStaleness}") must look like '500ms', '30s' or '5m'`, "invalid-source-directive");
|
|
3566
3621
|
}
|
|
3567
|
-
const
|
|
3622
|
+
const pk = meta.resolved?.pk;
|
|
3623
|
+
if (pk === void 0) return fail(`@prefetch on '${root.alias}' needs a single-column primary key`, "invalid-source-directive");
|
|
3624
|
+
const key = root.kind === "byPk" ? { one: root.pk[0].value } : selectKey({ where: root.where, pk, source, alias: root.alias });
|
|
3568
3625
|
return { source: source.name, ...maxStalenessMs !== void 0 ? { maxStalenessMs } : {}, key };
|
|
3569
3626
|
});
|
|
3570
3627
|
return { ...root, prefetch: plans };
|