@sequenceholdings/orm 2.8.4 → 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 +142 -56
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +5 -1
- package/dist/authoring.d.ts +5 -1
- package/dist/authoring.js +142 -56
- package/dist/authoring.js.map +1 -1
- package/dist/canonical.cjs +63 -13
- package/dist/canonical.cjs.map +1 -1
- package/dist/canonical.d.cts +11 -6
- package/dist/canonical.d.ts +11 -6
- package/dist/canonical.js +63 -13
- package/dist/canonical.js.map +1 -1
- package/dist/cli.cjs +77 -19
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +77 -19
- package/dist/cli.js.map +1 -1
- package/dist/compiler.cjs +76 -19
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.js +76 -19
- package/dist/compiler.js.map +1 -1
- package/dist/index.cjs +149 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +149 -58
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +70 -15
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +7 -7
- package/dist/ir.d.ts +7 -7
- package/dist/ir.js +70 -15
- package/dist/ir.js.map +1 -1
- package/dist/predicate-v2.cjs +70 -15
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.js +70 -15
- package/dist/predicate-v2.js.map +1 -1
- package/dist/sdl.cjs +70 -15
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.js +70 -15
- package/dist/sdl.js.map +1 -1
- package/package.json +1 -1
package/dist/predicate-v2.js
CHANGED
|
@@ -263,6 +263,48 @@ var policyExpressionSchema = z.union([
|
|
|
263
263
|
variants: z.array(policyVariantSchema).max(10).default([])
|
|
264
264
|
})
|
|
265
265
|
]);
|
|
266
|
+
|
|
267
|
+
// src/canonical/resolved-rows.ts
|
|
268
|
+
function logicalKey(table) {
|
|
269
|
+
return (table.primaryKey ?? []).map((columnName) => table.columns.find((c) => c.columnName === columnName)?.name ?? columnName);
|
|
270
|
+
}
|
|
271
|
+
function sameKey(key, expected) {
|
|
272
|
+
return key.length === expected.length && key.every((name) => expected.includes(name));
|
|
273
|
+
}
|
|
274
|
+
function validateRowIdentity(input) {
|
|
275
|
+
validateExistsIn(input);
|
|
276
|
+
if (input.keys.length > 1) validateCompositeKey(input);
|
|
277
|
+
}
|
|
278
|
+
function validateExistsIn({ resolved, sourceByName, fail }) {
|
|
279
|
+
const name = resolved.existsIn?.[0];
|
|
280
|
+
if (name === void 0) return;
|
|
281
|
+
if (sourceByName.get(name)?.kind !== "etl") fail(`existsIn '${name}' must be an etl source`);
|
|
282
|
+
if (resolved.policies.insert !== void 0 || resolved.policies.write !== void 0) {
|
|
283
|
+
fail("existsIn allows only an update policy: rows come from the etl source");
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function validateCompositeKey({ resolved, keys, tableByName, fail }) {
|
|
287
|
+
const keyList = `(${keys.join(", ")})`;
|
|
288
|
+
if (resolved.existsIn === void 0) {
|
|
289
|
+
fail(`requires exactly one primary-key column: composite key ${keyList} requires existsIn`);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const writes = ["insert", "update", "write"].filter((kind) => resolved.policies[kind] !== void 0);
|
|
293
|
+
if (writes.length > 0) fail(`composite key ${keyList} requires a read-only table: remove the ${writes.join("/")} policy`);
|
|
294
|
+
if (resolved.materialized) fail(`composite key ${keyList} cannot be materialized`);
|
|
295
|
+
for (const source of resolved.sources) {
|
|
296
|
+
if (source.kind === "prefetch") {
|
|
297
|
+
fail(`composite key ${keyList} cannot have prefetch source '${source.name}'`);
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const table = tableByName.get(source.table);
|
|
301
|
+
if (table && !sameKey(logicalKey(table), keys)) {
|
|
302
|
+
fail(`composite key ${keyList} requires ${source.kind} source '${source.name}' to be keyed by it; table '${source.table}' is keyed by (${logicalKey(table).join(", ")})`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/canonical/resolved.ts
|
|
266
308
|
var identifier = z.string().min(1).max(63).regex(/^[a-z][a-z0-9_]*$/);
|
|
267
309
|
var durationMs = z.number().int().positive().max(24 * 60 * 60 * 1e3);
|
|
268
310
|
var RESOLVED_VIEW_SYSTEM_COLUMNS = {
|
|
@@ -331,6 +373,13 @@ var canonicalResolvedSchema = z.object({
|
|
|
331
373
|
resolve: z.array(canonicalResolveRuleSchema).max(500),
|
|
332
374
|
/** Sources whose tombstone hides the resolved row (v1: the manual source only). */
|
|
333
375
|
deletedBy: z.array(identifier).max(1).default([]),
|
|
376
|
+
/**
|
|
377
|
+
* `rows.existsIn`: the one etl source whose rows are the table's rows (read
|
|
378
|
+
* directly as the row source). Only an update policy may write. It is also
|
|
379
|
+
* what admits a composite primary key: a read-only table keyed exactly like
|
|
380
|
+
* that source.
|
|
381
|
+
*/
|
|
382
|
+
existsIn: z.array(identifier).length(1).optional(),
|
|
334
383
|
/**
|
|
335
384
|
* Materialized: the view reads an indexed internal table (`table`) that the
|
|
336
385
|
* data plane keeps in step with `compute`, the internal view holding the
|
|
@@ -349,8 +398,8 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
349
398
|
if (!resolved) return [];
|
|
350
399
|
const errors = [];
|
|
351
400
|
const fail = (message) => errors.push(`resolved table '${view.view}': ${message}`);
|
|
352
|
-
const
|
|
353
|
-
if (
|
|
401
|
+
const keyColumns = (view.primaryKey ?? []).map((columnName) => view.columns.find((c) => c.columnName === columnName)).filter((column) => column !== void 0);
|
|
402
|
+
if (keyColumns.length === 0 || keyColumns.length !== view.primaryKey?.length) {
|
|
354
403
|
fail("requires exactly one primary-key column");
|
|
355
404
|
return errors;
|
|
356
405
|
}
|
|
@@ -358,7 +407,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
358
407
|
const scope = {
|
|
359
408
|
view,
|
|
360
409
|
resolved,
|
|
361
|
-
|
|
410
|
+
keys: new Map(keyColumns.map((column) => [column.name, column])),
|
|
362
411
|
dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
|
|
363
412
|
sourceByName: /* @__PURE__ */ new Map(),
|
|
364
413
|
fail
|
|
@@ -372,6 +421,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
372
421
|
if (!table) fail(`source '${source.name}' references unknown table '${source.table}'`);
|
|
373
422
|
else validateSource({ scope, source, table });
|
|
374
423
|
}
|
|
424
|
+
validateRowIdentity({ resolved, keys: [...scope.keys.keys()], sourceByName: scope.sourceByName, tableByName, fail });
|
|
375
425
|
validateRules(scope);
|
|
376
426
|
validateViewCells(scope);
|
|
377
427
|
if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
|
|
@@ -401,7 +451,7 @@ function validateSource({ scope, source, table }) {
|
|
|
401
451
|
validatePrefetchFields({ scope, source });
|
|
402
452
|
}
|
|
403
453
|
function validatePrefetchFields({ scope, source }) {
|
|
404
|
-
const { fail,
|
|
454
|
+
const { fail, keys } = scope;
|
|
405
455
|
if (source.kind !== "prefetch") {
|
|
406
456
|
if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
|
|
407
457
|
fail(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
|
|
@@ -410,7 +460,7 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
410
460
|
}
|
|
411
461
|
if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
|
|
412
462
|
fail(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
|
|
413
|
-
} else if (source.key
|
|
463
|
+
} else if (!keys.has(source.key) && !source.columns.includes(source.key)) {
|
|
414
464
|
fail(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
|
|
415
465
|
}
|
|
416
466
|
for (const column of source.writeback?.columns ?? []) {
|
|
@@ -418,19 +468,19 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
418
468
|
}
|
|
419
469
|
}
|
|
420
470
|
function validateSourceColumns({ scope, source, byName }) {
|
|
421
|
-
const { fail,
|
|
422
|
-
for (const column of [
|
|
423
|
-
const own = column
|
|
471
|
+
const { fail, keys, dataColumns } = scope;
|
|
472
|
+
for (const column of [...keys.keys(), ...source.columns]) {
|
|
473
|
+
const own = keys.get(column) ?? dataColumns.get(column);
|
|
424
474
|
const theirs = byName.get(column);
|
|
425
475
|
if (!own) fail(`source '${source.name}' supplies unknown column '${column}'`);
|
|
426
476
|
else if (!theirs) fail(`source '${source.name}' table '${source.table}' has no column '${column}'`);
|
|
427
477
|
else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail(`column '${column}' has a different type on source '${source.name}'`);
|
|
428
478
|
}
|
|
429
479
|
}
|
|
430
|
-
function validateRules({ resolved,
|
|
480
|
+
function validateRules({ resolved, keys, dataColumns, sourceByName, fail }) {
|
|
431
481
|
const ruled = /* @__PURE__ */ new Set();
|
|
432
482
|
for (const rule of resolved.resolve) {
|
|
433
|
-
if (!dataColumns.has(rule.column) || rule.column
|
|
483
|
+
if (!dataColumns.has(rule.column) || keys.has(rule.column)) {
|
|
434
484
|
fail(`resolve rule targets unknown column '${rule.column}'`);
|
|
435
485
|
continue;
|
|
436
486
|
}
|
|
@@ -447,7 +497,7 @@ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail })
|
|
|
447
497
|
}
|
|
448
498
|
}
|
|
449
499
|
for (const column of dataColumns.keys()) {
|
|
450
|
-
if (column
|
|
500
|
+
if (!keys.has(column) && !ruled.has(column)) fail(`column '${column}' has no resolve rule`);
|
|
451
501
|
}
|
|
452
502
|
}
|
|
453
503
|
var VIEW_CELL_TYPES = {
|
|
@@ -456,10 +506,10 @@ var VIEW_CELL_TYPES = {
|
|
|
456
506
|
by: "text",
|
|
457
507
|
conflicts: "jsonb"
|
|
458
508
|
};
|
|
459
|
-
function validateViewCells({ view, dataColumns,
|
|
509
|
+
function validateViewCells({ view, dataColumns, keys, fail }) {
|
|
460
510
|
const expected = /* @__PURE__ */ new Map();
|
|
461
511
|
for (const column of dataColumns.values()) {
|
|
462
|
-
if (column.name
|
|
512
|
+
if (keys.has(column.name)) continue;
|
|
463
513
|
for (const part of RESOLVED_VIEW_CELL_PARTS) {
|
|
464
514
|
expected.set(cellColumnName(column.name, part), { part, columnName: cellColumnName(column.columnName, part) });
|
|
465
515
|
}
|
|
@@ -1469,8 +1519,13 @@ var resolvedSourceIrSchema = z.object({
|
|
|
1469
1519
|
}).optional()
|
|
1470
1520
|
});
|
|
1471
1521
|
var resolvedMetaIrSchema = z.object({
|
|
1472
|
-
/**
|
|
1473
|
-
|
|
1522
|
+
/**
|
|
1523
|
+
* Logical pk column of the resolved view. Absent for a composite key, which
|
|
1524
|
+
* only a read-only `existsIn` table may declare: it has no writes, prefetch
|
|
1525
|
+
* or materialization (the paths that address one key column), and its reads
|
|
1526
|
+
* use the view's `primaryKey` like any keyed view.
|
|
1527
|
+
*/
|
|
1528
|
+
pk: z.string().optional(),
|
|
1474
1529
|
/** Source name of the manual source. */
|
|
1475
1530
|
manual: z.string(),
|
|
1476
1531
|
sources: z.array(resolvedSourceIrSchema),
|