@sequenceholdings/orm 2.1.1 → 2.2.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/README.md +3 -1
- package/dist/authoring.cjs +284 -99
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +31 -8
- package/dist/authoring.d.ts +31 -8
- package/dist/authoring.js +282 -100
- package/dist/authoring.js.map +1 -1
- package/dist/canonical.cjs +51 -3
- package/dist/canonical.cjs.map +1 -1
- package/dist/canonical.d.cts +838 -12
- package/dist/canonical.d.ts +838 -12
- package/dist/canonical.js +47 -4
- package/dist/canonical.js.map +1 -1
- package/dist/cli.cjs +5838 -4197
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +5838 -4197
- package/dist/cli.js.map +1 -1
- package/dist/client.cjs +49 -32
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +49 -6
- package/dist/client.d.ts +49 -6
- package/dist/client.js +49 -32
- package/dist/client.js.map +1 -1
- package/dist/compiler.cjs +1237 -263
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.d.cts +57 -3
- package/dist/compiler.d.ts +57 -3
- package/dist/compiler.js +1230 -264
- package/dist/compiler.js.map +1 -1
- package/dist/{constants-YoFGjIBM.d.cts → constants-C6EG6Wq7.d.cts} +1 -1
- package/dist/{constants-YoFGjIBM.d.ts → constants-C6EG6Wq7.d.ts} +1 -1
- package/dist/definition-DjfC7Lll.d.cts +91 -0
- package/dist/definition-DjfC7Lll.d.ts +91 -0
- package/dist/dynamic-input.cjs +2 -1
- package/dist/dynamic-input.cjs.map +1 -1
- package/dist/dynamic-input.d.cts +9 -2
- package/dist/dynamic-input.d.ts +9 -2
- package/dist/dynamic-input.js +2 -1
- package/dist/dynamic-input.js.map +1 -1
- package/dist/index.cjs +1094 -321
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +479 -19
- package/dist/index.d.ts +479 -19
- package/dist/index.js +1083 -322
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +84 -10
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +138 -13
- package/dist/ir.d.ts +138 -13
- package/dist/ir.js +81 -11
- package/dist/ir.js.map +1 -1
- package/dist/predicate-v2.cjs +78 -9
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.d.cts +1 -1
- package/dist/predicate-v2.d.ts +1 -1
- package/dist/predicate-v2.js +78 -9
- package/dist/predicate-v2.js.map +1 -1
- package/dist/sdl.cjs +156 -21
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.d.cts +8 -1
- package/dist/sdl.d.ts +8 -1
- package/dist/sdl.js +156 -21
- package/dist/sdl.js.map +1 -1
- package/package.json +5 -4
- package/src/codegen-gql/server-fns.cjs +2 -2
- package/templates/orm-namespace/AGENTS.md +10 -0
- package/templates/orm-namespace/CLAUDE.md +1 -84
- package/templates/orm-namespace/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -163,8 +163,8 @@ var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
|
|
|
163
163
|
var MAX_IDENTIFIER_LENGTH = 63;
|
|
164
164
|
var CAPABILITY_KEY_PATTERN = /^(?:org\/|app\/[a-z][a-z0-9-]{0,63}\/)?[a-z0-9-]{1,64}$/;
|
|
165
165
|
var CLAIM_KEY_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
166
|
-
var QUALIFIED_TABLE_PATTERN = /^[a-z][a-z0-9_]
|
|
167
|
-
var SYNCED_SOURCE_PATTERN =
|
|
166
|
+
var QUALIFIED_TABLE_PATTERN = /^[a-z][a-z0-9_]{0,62}\.[a-z][a-z0-9_]{0,62}$/;
|
|
167
|
+
var SYNCED_SOURCE_PATTERN = QUALIFIED_TABLE_PATTERN;
|
|
168
168
|
var ORM_SCHEMA_PREFIX = "orm_";
|
|
169
169
|
var CONSUMER_ROLE_SUFFIX = "_consumer";
|
|
170
170
|
var ormSchemaName = (namespace) => `${ORM_SCHEMA_PREFIX}${namespace}`;
|
|
@@ -265,6 +265,9 @@ function registerNamespaceConfig(value) {
|
|
|
265
265
|
// src/authoring/table.ts
|
|
266
266
|
var TABLE_BRAND = "seq-table";
|
|
267
267
|
var LINK_BRAND = "seq-link";
|
|
268
|
+
function objectRef(namespace, table) {
|
|
269
|
+
return { kind: "seq-external-object", namespace, table };
|
|
270
|
+
}
|
|
268
271
|
function toOne(target, options) {
|
|
269
272
|
return {
|
|
270
273
|
kind: LINK_BRAND,
|
|
@@ -318,51 +321,51 @@ function assertIdentifier({ value, what, file, pattern = IDENTIFIER_PATTERN }) {
|
|
|
318
321
|
);
|
|
319
322
|
}
|
|
320
323
|
}
|
|
321
|
-
function defineTable(
|
|
324
|
+
function defineTable(name2, columns, config = {}) {
|
|
322
325
|
const file = activeAuthoringContext()?.currentFile;
|
|
323
|
-
assertIdentifier({ value:
|
|
326
|
+
assertIdentifier({ value: name2, what: "table name", file });
|
|
324
327
|
const entries = Object.entries(columns);
|
|
325
328
|
if (entries.length === 0) {
|
|
326
|
-
throw new OrmDefinitionError(`table "${
|
|
329
|
+
throw new OrmDefinitionError(`table "${name2}" declares no columns`, file);
|
|
327
330
|
}
|
|
328
331
|
const seenColumnNames = /* @__PURE__ */ new Map();
|
|
329
332
|
for (const [propertyName, builder] of entries) {
|
|
330
333
|
if (!isColumnBuilder(builder)) {
|
|
331
334
|
throw new OrmDefinitionError(
|
|
332
|
-
`table "${
|
|
335
|
+
`table "${name2}": column "${propertyName}" is not a column builder \u2014 use the t.* factories (t.uuid(), t.text(), \u2026)`,
|
|
333
336
|
file
|
|
334
337
|
);
|
|
335
338
|
}
|
|
336
339
|
const columnName = builder.columnNameFor(propertyName);
|
|
337
|
-
assertIdentifier({ value: columnName, what: `table "${
|
|
340
|
+
assertIdentifier({ value: columnName, what: `table "${name2}": column "${propertyName}" column name`, file, pattern: COLUMN_IDENTIFIER_PATTERN });
|
|
338
341
|
if (config.materializes === void 0 && (propertyName === CELLS_FIELD || Object.values(CELL_PART_SUFFIX).some((suffix) => columnName.endsWith(suffix) || propertyName.endsWith(suffix)))) {
|
|
339
342
|
throw new OrmDefinitionError(
|
|
340
|
-
`table "${
|
|
343
|
+
`table "${name2}": "${propertyName}" collides with the reserved cell-metadata names ('${CELLS_FIELD}', or a ${Object.values(CELL_PART_SUFFIX).join("/")} suffix)`,
|
|
341
344
|
file
|
|
342
345
|
);
|
|
343
346
|
}
|
|
344
347
|
const existing = seenColumnNames.get(columnName);
|
|
345
348
|
if (existing !== void 0) {
|
|
346
349
|
throw new OrmDefinitionError(
|
|
347
|
-
`table "${
|
|
350
|
+
`table "${name2}": columns "${existing}" and "${propertyName}" both map to Postgres column "${columnName}"`,
|
|
348
351
|
file
|
|
349
352
|
);
|
|
350
353
|
}
|
|
351
354
|
seenColumnNames.set(columnName, propertyName);
|
|
352
355
|
}
|
|
353
|
-
const table = { kind: TABLE_BRAND, tableName:
|
|
356
|
+
const table = { kind: TABLE_BRAND, tableName: name2, columns, config, source: void 0, file };
|
|
354
357
|
registerTable(table);
|
|
355
358
|
return table;
|
|
356
359
|
}
|
|
357
|
-
function defineSyncedTable(
|
|
360
|
+
function defineSyncedTable(name2, columnsOrConfig, maybeConfig) {
|
|
358
361
|
const file = activeAuthoringContext()?.currentFile;
|
|
359
|
-
assertIdentifier({ value:
|
|
362
|
+
assertIdentifier({ value: name2, what: "table name", file });
|
|
360
363
|
const isTyped = maybeConfig !== void 0;
|
|
361
364
|
const columns = isTyped ? columnsOrConfig : {};
|
|
362
365
|
const config = isTyped ? maybeConfig : columnsOrConfig;
|
|
363
366
|
if (!SYNCED_SOURCE_PATTERN.test(config.source)) {
|
|
364
367
|
throw new OrmDefinitionError(
|
|
365
|
-
`synced table "${
|
|
368
|
+
`synced table "${name2}": source "${config.source}" must be a qualified lowercase schema.table identifier`,
|
|
366
369
|
file
|
|
367
370
|
);
|
|
368
371
|
}
|
|
@@ -372,21 +375,21 @@ function defineSyncedTable(name, columnsOrConfig, maybeConfig) {
|
|
|
372
375
|
for (const [propertyName, builder] of entries) {
|
|
373
376
|
if (!isColumnBuilder(builder)) {
|
|
374
377
|
throw new OrmDefinitionError(
|
|
375
|
-
`synced table "${
|
|
378
|
+
`synced table "${name2}": column "${propertyName}" is not a column builder \u2014 use the t.* factories (t.uuid(), t.text(), \u2026)`,
|
|
376
379
|
file
|
|
377
380
|
);
|
|
378
381
|
}
|
|
379
382
|
const columnName = builder.columnNameFor(propertyName);
|
|
380
383
|
assertIdentifier({
|
|
381
384
|
value: columnName,
|
|
382
|
-
what: `synced table "${
|
|
385
|
+
what: `synced table "${name2}": column "${propertyName}" column name`,
|
|
383
386
|
file,
|
|
384
387
|
pattern: COLUMN_IDENTIFIER_PATTERN
|
|
385
388
|
});
|
|
386
389
|
const existing = seenColumnNames.get(columnName);
|
|
387
390
|
if (existing !== void 0) {
|
|
388
391
|
throw new OrmDefinitionError(
|
|
389
|
-
`synced table "${
|
|
392
|
+
`synced table "${name2}": columns "${existing}" and "${propertyName}" both map to Postgres column "${columnName}"`,
|
|
390
393
|
file
|
|
391
394
|
);
|
|
392
395
|
}
|
|
@@ -397,13 +400,13 @@ function defineSyncedTable(name, columnsOrConfig, maybeConfig) {
|
|
|
397
400
|
const hasConfigPk = config.primaryKey !== void 0 && config.primaryKey.length > 0;
|
|
398
401
|
if (columnLevelPk.length === 0 && !hasConfigPk) {
|
|
399
402
|
throw new OrmDefinitionError(
|
|
400
|
-
`synced table "${
|
|
403
|
+
`synced table "${name2}" declares columns but no primary key \u2014 use .primaryKey() or config.primaryKey`,
|
|
401
404
|
file
|
|
402
405
|
);
|
|
403
406
|
}
|
|
404
407
|
if (columnLevelPk.length > 0 && hasConfigPk) {
|
|
405
408
|
throw new OrmDefinitionError(
|
|
406
|
-
`synced table "${
|
|
409
|
+
`synced table "${name2}": declare the primary key either with .primaryKey() on columns or with config.primaryKey \u2014 not both`,
|
|
407
410
|
file
|
|
408
411
|
);
|
|
409
412
|
}
|
|
@@ -415,7 +418,7 @@ function defineSyncedTable(name, columnsOrConfig, maybeConfig) {
|
|
|
415
418
|
if (config.policies !== void 0) tableConfig.policies = { read: config.policies.read };
|
|
416
419
|
const table = {
|
|
417
420
|
kind: TABLE_BRAND,
|
|
418
|
-
tableName:
|
|
421
|
+
tableName: name2,
|
|
419
422
|
columns,
|
|
420
423
|
config: tableConfig,
|
|
421
424
|
source: config.source,
|
|
@@ -424,26 +427,26 @@ function defineSyncedTable(name, columnsOrConfig, maybeConfig) {
|
|
|
424
427
|
registerTable(table);
|
|
425
428
|
return table;
|
|
426
429
|
}
|
|
427
|
-
function defineEnum(
|
|
430
|
+
function defineEnum(name2, values) {
|
|
428
431
|
const file = activeAuthoringContext()?.currentFile;
|
|
429
|
-
assertIdentifier({ value:
|
|
432
|
+
assertIdentifier({ value: name2, what: "enum name", file });
|
|
430
433
|
if (values.length === 0 || values.length > 100) {
|
|
431
|
-
throw new OrmDefinitionError(`enum "${
|
|
434
|
+
throw new OrmDefinitionError(`enum "${name2}" must declare between 1 and 100 values`, file);
|
|
432
435
|
}
|
|
433
436
|
const seen = /* @__PURE__ */ new Set();
|
|
434
437
|
for (const value of values) {
|
|
435
438
|
if (value.length === 0 || value.length > MAX_IDENTIFIER_LENGTH) {
|
|
436
439
|
throw new OrmDefinitionError(
|
|
437
|
-
`enum "${
|
|
440
|
+
`enum "${name2}": value ${JSON.stringify(value)} must be 1-${MAX_IDENTIFIER_LENGTH} characters`,
|
|
438
441
|
file
|
|
439
442
|
);
|
|
440
443
|
}
|
|
441
444
|
if (seen.has(value)) {
|
|
442
|
-
throw new OrmDefinitionError(`enum "${
|
|
445
|
+
throw new OrmDefinitionError(`enum "${name2}" declares duplicate value "${value}"`, file);
|
|
443
446
|
}
|
|
444
447
|
seen.add(value);
|
|
445
448
|
}
|
|
446
|
-
const ref = { kind: ENUM_BRAND, name, values: [...values] };
|
|
449
|
+
const ref = { kind: ENUM_BRAND, name: name2, values: [...values] };
|
|
447
450
|
registerEnum(ref);
|
|
448
451
|
return ref;
|
|
449
452
|
}
|
|
@@ -451,52 +454,52 @@ var VIEW_BRAND = "seq-view";
|
|
|
451
454
|
function isSeqView(value) {
|
|
452
455
|
return typeof value === "object" && value !== null && value.kind === VIEW_BRAND;
|
|
453
456
|
}
|
|
454
|
-
function defineView(
|
|
457
|
+
function defineView(name2, columns, config) {
|
|
455
458
|
const file = activeAuthoringContext()?.currentFile;
|
|
456
|
-
if (!IDENTIFIER_PATTERN.test(
|
|
459
|
+
if (!IDENTIFIER_PATTERN.test(name2) || name2.length > MAX_IDENTIFIER_LENGTH) {
|
|
457
460
|
throw new OrmDefinitionError(
|
|
458
|
-
`view name "${
|
|
461
|
+
`view name "${name2}" must match ${IDENTIFIER_PATTERN} and be at most ${MAX_IDENTIFIER_LENGTH} characters`,
|
|
459
462
|
file
|
|
460
463
|
);
|
|
461
464
|
}
|
|
462
465
|
const entries = Object.entries(columns);
|
|
463
|
-
if (entries.length === 0) throw new OrmDefinitionError(`view "${
|
|
466
|
+
if (entries.length === 0) throw new OrmDefinitionError(`view "${name2}" declares no columns`, file);
|
|
464
467
|
const seenColumnNames = /* @__PURE__ */ new Map();
|
|
465
468
|
for (const [propertyName, builder] of entries) {
|
|
466
469
|
if (!isColumnBuilder(builder)) {
|
|
467
470
|
throw new OrmDefinitionError(
|
|
468
|
-
`view "${
|
|
471
|
+
`view "${name2}": column "${propertyName}" is not a column builder \u2014 use the t.* factories`,
|
|
469
472
|
file
|
|
470
473
|
);
|
|
471
474
|
}
|
|
472
475
|
const columnName = builder.columnNameFor(propertyName);
|
|
473
476
|
if (!COLUMN_IDENTIFIER_PATTERN.test(columnName) || columnName.length > MAX_IDENTIFIER_LENGTH) {
|
|
474
|
-
throw new OrmDefinitionError(`view "${
|
|
477
|
+
throw new OrmDefinitionError(`view "${name2}": column "${propertyName}" has invalid column name "${columnName}"`, file);
|
|
475
478
|
}
|
|
476
479
|
const existing = seenColumnNames.get(columnName);
|
|
477
480
|
if (existing !== void 0) {
|
|
478
481
|
throw new OrmDefinitionError(
|
|
479
|
-
`view "${
|
|
482
|
+
`view "${name2}": columns "${existing}" and "${propertyName}" both map to Postgres column "${columnName}"`,
|
|
480
483
|
file
|
|
481
484
|
);
|
|
482
485
|
}
|
|
483
486
|
seenColumnNames.set(columnName, propertyName);
|
|
484
487
|
}
|
|
485
|
-
const view = { kind: VIEW_BRAND, viewName:
|
|
488
|
+
const view = { kind: VIEW_BRAND, viewName: name2, columns, config, file };
|
|
486
489
|
registerView(view);
|
|
487
490
|
return view;
|
|
488
491
|
}
|
|
489
492
|
var QUERY_COLUMN_FACTORIES = {
|
|
490
|
-
uuid: (
|
|
491
|
-
text: (
|
|
492
|
-
integer: (
|
|
493
|
-
bigint: (
|
|
494
|
-
numeric: (
|
|
495
|
-
double: (
|
|
496
|
-
boolean: (
|
|
497
|
-
timestamptz: (
|
|
498
|
-
date: (
|
|
499
|
-
jsonb: (
|
|
493
|
+
uuid: (name2) => uuid(name2),
|
|
494
|
+
text: (name2) => text(name2),
|
|
495
|
+
integer: (name2) => integer(name2),
|
|
496
|
+
bigint: (name2) => bigint(name2, { mode: "bigint" }),
|
|
497
|
+
numeric: (name2) => numeric(name2),
|
|
498
|
+
double: (name2) => doublePrecision(name2),
|
|
499
|
+
boolean: (name2) => boolean(name2),
|
|
500
|
+
timestamptz: (name2) => timestamp(name2, { withTimezone: true }),
|
|
501
|
+
date: (name2) => date(name2),
|
|
502
|
+
jsonb: (name2) => jsonb(name2)
|
|
500
503
|
};
|
|
501
504
|
function compileViewQueries(input) {
|
|
502
505
|
const schemaName = `orm_${input.namespace}`;
|
|
@@ -552,9 +555,9 @@ function compileViewQueries(input) {
|
|
|
552
555
|
}
|
|
553
556
|
const handle = handles.get(resolved);
|
|
554
557
|
if (!handle) {
|
|
555
|
-
const
|
|
558
|
+
const name2 = isSeqTable(resolved) ? resolved.tableName : resolved.viewName;
|
|
556
559
|
throw new OrmDefinitionError(
|
|
557
|
-
`view "${authored.viewName}" references relation "${
|
|
560
|
+
`view "${authored.viewName}" references relation "${name2}" which is not part of this namespace`,
|
|
558
561
|
authored.file
|
|
559
562
|
);
|
|
560
563
|
}
|
|
@@ -745,8 +748,8 @@ function overlayWinsUnion(input) {
|
|
|
745
748
|
// src/authoring/cow-mutations.ts
|
|
746
749
|
function overrideRowMutation(input) {
|
|
747
750
|
const { overlay, synced, pk } = input;
|
|
748
|
-
const
|
|
749
|
-
assertMutationName(
|
|
751
|
+
const name2 = input.name ?? `override_${overlay.tableName}`;
|
|
752
|
+
assertMutationName(name2, "overrideRowMutation");
|
|
750
753
|
assertOverlayPairPrimaryKey({ overlay, synced, pk, helper: "overrideRowMutation" });
|
|
751
754
|
const sharedProperties = sharedNonPkProperties({ overlay, synced, pk });
|
|
752
755
|
const setProperties = resolveSetProperties({
|
|
@@ -765,7 +768,7 @@ function overrideRowMutation(input) {
|
|
|
765
768
|
});
|
|
766
769
|
return {
|
|
767
770
|
kind: "override",
|
|
768
|
-
name,
|
|
771
|
+
name: name2,
|
|
769
772
|
synced: synced.tableName,
|
|
770
773
|
pk,
|
|
771
774
|
copy: [pk, ...sharedProperties],
|
|
@@ -774,23 +777,23 @@ function overrideRowMutation(input) {
|
|
|
774
777
|
}
|
|
775
778
|
function revertOverlayMutation(input) {
|
|
776
779
|
const { overlay, synced, pk } = input;
|
|
777
|
-
const
|
|
778
|
-
assertMutationName(
|
|
780
|
+
const name2 = input.name ?? `revert_${overlay.tableName}`;
|
|
781
|
+
assertMutationName(name2, "revertOverlayMutation");
|
|
779
782
|
assertOverlayPairPrimaryKey({ overlay, synced, pk, helper: "revertOverlayMutation" });
|
|
780
783
|
assertMatchingColumns({ overlay, synced, properties: [pk], helper: "revertOverlayMutation" });
|
|
781
784
|
return {
|
|
782
785
|
kind: "revert",
|
|
783
|
-
name,
|
|
786
|
+
name: name2,
|
|
784
787
|
synced: synced.tableName,
|
|
785
788
|
pk,
|
|
786
789
|
copy: [],
|
|
787
790
|
set: []
|
|
788
791
|
};
|
|
789
792
|
}
|
|
790
|
-
function assertMutationName(
|
|
791
|
-
if (!IDENTIFIER_PATTERN.test(
|
|
793
|
+
function assertMutationName(name2, helper) {
|
|
794
|
+
if (!IDENTIFIER_PATTERN.test(name2) || name2.length > MAX_IDENTIFIER_LENGTH) {
|
|
792
795
|
throw new OrmDefinitionError(
|
|
793
|
-
`${helper}: mutation name "${
|
|
796
|
+
`${helper}: mutation name "${name2}" must match ${IDENTIFIER_PATTERN} and be at most ${MAX_IDENTIFIER_LENGTH} characters`
|
|
794
797
|
);
|
|
795
798
|
}
|
|
796
799
|
}
|
|
@@ -916,17 +919,35 @@ function scalarColumnType(table, property, helper) {
|
|
|
916
919
|
}
|
|
917
920
|
return type;
|
|
918
921
|
}
|
|
922
|
+
var objectRef2 = z.string().max(255).regex(/^[a-z][a-z0-9_]{0,40}\/[A-Za-z_][A-Za-z0-9_]*$/, "Expected an explicit namespace/object reference (no wildcards)");
|
|
923
|
+
var functionRef = z.string().max(255).regex(/^[a-z][a-z0-9-]*$/, "Expected a function slug");
|
|
924
|
+
var refs = (schema) => z.array(schema).max(256).refine(
|
|
925
|
+
(values) => new Set(values).size === values.length,
|
|
926
|
+
"Duplicate dependency reference"
|
|
927
|
+
).default([]);
|
|
928
|
+
var ontologyContractSchema = z.strictObject({
|
|
929
|
+
version: z.literal(1),
|
|
930
|
+
read: refs(objectRef2),
|
|
931
|
+
write: refs(objectRef2),
|
|
932
|
+
produce: refs(objectRef2),
|
|
933
|
+
invoke: refs(functionRef)
|
|
934
|
+
}).refine((contract) => JSON.stringify(contract).length <= 4096, "Ontology contract exceeds the 4 KiB signed-context budget");
|
|
935
|
+
function readOntologyContract(manifest) {
|
|
936
|
+
if (!manifest || typeof manifest !== "object" || !("ontology" in manifest) || manifest.ontology === void 0) return void 0;
|
|
937
|
+
return ontologyContractSchema.parse(manifest.ontology);
|
|
938
|
+
}
|
|
919
939
|
|
|
920
940
|
// src/compiler/sql.ts
|
|
921
941
|
var CLAIMS_SETTING = "request.jwt.claims";
|
|
922
942
|
var CAPABILITIES_CLAIM_KEY = "capabilities";
|
|
923
943
|
var ROLES_CLAIM_KEY = "roles";
|
|
924
|
-
var quoteIdentifier = (
|
|
944
|
+
var quoteIdentifier = (name2) => `"${name2.replaceAll('"', '""')}"`;
|
|
925
945
|
var quoteLiteral = (value) => `'${value.replaceAll("'", "''")}'`;
|
|
926
946
|
var CLAIMS_SOURCE = `nullif(current_setting('${CLAIMS_SETTING}', true), '')::jsonb`;
|
|
927
947
|
var claimExpression = (key) => `(${CLAIMS_SOURCE} ->> ${quoteLiteral(key)})`;
|
|
928
|
-
var
|
|
929
|
-
var
|
|
948
|
+
var scopedClaimsSource = (namespace) => namespace ? `(CASE WHEN ${CLAIMS_SOURCE} ? 'namespaces' THEN ${CLAIMS_SOURCE} -> 'namespaces' -> ${quoteLiteral(namespace)} ELSE ${CLAIMS_SOURCE} END)` : CLAIMS_SOURCE;
|
|
949
|
+
var capabilityExpression = (key, namespace) => `coalesce(${scopedClaimsSource(namespace)} -> '${CAPABILITIES_CLAIM_KEY}' ? ${quoteLiteral(key)}, false)`;
|
|
950
|
+
var roleExpression = (key, namespace) => `coalesce(${scopedClaimsSource(namespace)} -> '${ROLES_CLAIM_KEY}' ? ${quoteLiteral(key)}, false)`;
|
|
930
951
|
|
|
931
952
|
// src/checks/compile.ts
|
|
932
953
|
function tokenize(source) {
|
|
@@ -1259,8 +1280,8 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
1259
1280
|
validateRules(scope);
|
|
1260
1281
|
validateViewCells(scope);
|
|
1261
1282
|
if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
|
|
1262
|
-
for (const
|
|
1263
|
-
if (scope.sourceByName.get(
|
|
1283
|
+
for (const name2 of resolved.deletedBy) {
|
|
1284
|
+
if (scope.sourceByName.get(name2)?.kind !== "manual") fail(`deletedBy '${name2}' must be the manual source`);
|
|
1264
1285
|
}
|
|
1265
1286
|
return errors;
|
|
1266
1287
|
}
|
|
@@ -1357,7 +1378,7 @@ function validateViewCells({ view, dataColumns, pkColumn, fail }) {
|
|
|
1357
1378
|
if (column.type !== VIEW_CELL_TYPES[column.cell.part]) fail(`cell column '${column.name}' must be ${VIEW_CELL_TYPES[column.cell.part]}`);
|
|
1358
1379
|
expected.delete(column.name);
|
|
1359
1380
|
}
|
|
1360
|
-
for (const
|
|
1381
|
+
for (const name2 of expected.keys()) fail(`missing cell column '${name2}'`);
|
|
1361
1382
|
}
|
|
1362
1383
|
var columnSignature = (columns) => JSON.stringify(columns.map((column) => [column.name, column.columnName, column.type]));
|
|
1363
1384
|
function validateMaterialized({ scope, tableByName, views }) {
|
|
@@ -1448,6 +1469,8 @@ var canonicalLinkSchema = z.object({
|
|
|
1448
1469
|
kind: z.enum(["toOne", "toMany", "toPlatformUser"]),
|
|
1449
1470
|
/** Target table name (namespace-local). Absent for toPlatformUser. */
|
|
1450
1471
|
target: identifier2.optional(),
|
|
1472
|
+
/** Owning namespace of an imported target; omitted for local links. */
|
|
1473
|
+
namespace: identifier2.max(41).optional(),
|
|
1451
1474
|
/** FK column on the owning side (toOne/toPlatformUser: on this table; toMany: on the target). */
|
|
1452
1475
|
on: columnIdentifier,
|
|
1453
1476
|
/** Referenced column on the target (default: target's PK / platform user `sub`). */
|
|
@@ -1706,11 +1729,12 @@ var canonicalForeignKeySchema = z.object({
|
|
|
1706
1729
|
}).strict();
|
|
1707
1730
|
var canonicalTableSchema = z.object({
|
|
1708
1731
|
table: identifier2,
|
|
1732
|
+
displayName: z.string().trim().min(1).max(120).optional(),
|
|
1709
1733
|
description: z.string().max(2e3).optional(),
|
|
1710
1734
|
/** Table-level rename hint (new in v2). */
|
|
1711
1735
|
renamedFrom: identifier2.optional(),
|
|
1712
|
-
/** Synced/reflected read-only tables (`source:
|
|
1713
|
-
source: z.string().regex(
|
|
1736
|
+
/** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
|
|
1737
|
+
source: z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
|
|
1714
1738
|
columns: z.array(canonicalColumnSchema).default([]),
|
|
1715
1739
|
primaryKey: z.array(columnIdentifier).min(1).max(8).optional(),
|
|
1716
1740
|
links: z.array(canonicalLinkSchema).default([]),
|
|
@@ -1863,7 +1887,9 @@ function validateCellMetadata(table, index3, ctx) {
|
|
|
1863
1887
|
}
|
|
1864
1888
|
}
|
|
1865
1889
|
}
|
|
1866
|
-
var
|
|
1890
|
+
var exportGateSchema = z.union([z.literal(true), z.object({ capability: z.string().min(1) }).strict(), z.object({ role: z.string().min(1) }).strict()]);
|
|
1891
|
+
var relationExportSchema = z.object({ read: exportGateSchema, write: exportGateSchema.optional() }).strict();
|
|
1892
|
+
var localDefinitionSchema = z.object({
|
|
1867
1893
|
definition_format: z.literal(CANONICAL_FORMAT),
|
|
1868
1894
|
namespace: identifier2.max(41),
|
|
1869
1895
|
description: z.string().max(2e3).optional(),
|
|
@@ -1871,6 +1897,8 @@ var canonicalDefinitionSchema = z.object({
|
|
|
1871
1897
|
tables: z.array(canonicalTableSchema).min(0).max(500),
|
|
1872
1898
|
/** Optional to preserve byte-identical hashes for pre-view canonical v2 documents. */
|
|
1873
1899
|
views: z.array(canonicalViewSchema).max(500).optional(),
|
|
1900
|
+
/** Target-owned cross-namespace access contract, using existing namespace grants. */
|
|
1901
|
+
exports: z.record(identifier2, relationExportSchema).optional(),
|
|
1874
1902
|
/** Verbatim custom-roles grammar (@sequenceholdings/custom-roles-manifest). */
|
|
1875
1903
|
capabilities: z.object({
|
|
1876
1904
|
roles: z.unknown().optional(),
|
|
@@ -1897,7 +1925,16 @@ var canonicalDefinitionSchema = z.object({
|
|
|
1897
1925
|
}
|
|
1898
1926
|
}
|
|
1899
1927
|
const fkTables = new Map(definition.tables.map((table) => [table.table, table]));
|
|
1928
|
+
const relations = /* @__PURE__ */ new Set([...fkTables.keys(), ...(definition.views ?? []).map((view) => view.view)]);
|
|
1929
|
+
for (const name2 of Object.keys(definition.exports ?? {})) {
|
|
1930
|
+
if (!relations.has(name2)) ctx.addIssue({ code: "custom", path: ["exports", name2], message: `export '${name2}' is not a local object` });
|
|
1931
|
+
}
|
|
1900
1932
|
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
1933
|
+
for (const [linkIndex, link] of table.links.entries()) {
|
|
1934
|
+
if (link.enforce && link.namespace !== void 0 && link.namespace !== definition.namespace) {
|
|
1935
|
+
ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "links", linkIndex], message: "physical foreign keys require a local target; use an ORM link without enforce" });
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1901
1938
|
const names = /* @__PURE__ */ new Set([
|
|
1902
1939
|
...table.primaryKey?.length ? [`${table.table}_pkey`] : [],
|
|
1903
1940
|
...table.columns.filter((column) => column.unique && !(table.primaryKey?.length === 1 && table.primaryKey[0] === column.columnName)).map((column) => `${table.table}_${column.columnName}_unique`),
|
|
@@ -1934,14 +1971,14 @@ var canonicalDefinitionSchema = z.object({
|
|
|
1934
1971
|
}
|
|
1935
1972
|
if (fk.columns.length !== fk.references.length) fail("foreign key columns and references must have the same number of columns");
|
|
1936
1973
|
if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail("foreign key columns must not repeat");
|
|
1937
|
-
for (const [position,
|
|
1938
|
-
const sourceColumn = table.columns.find((column) => column.columnName ===
|
|
1974
|
+
for (const [position, name2] of fk.columns.entries()) {
|
|
1975
|
+
const sourceColumn = table.columns.find((column) => column.columnName === name2);
|
|
1939
1976
|
const targetColumn = target.columns.find((column) => column.columnName === fk.references[position]);
|
|
1940
1977
|
if (!sourceColumn || !targetColumn) fail("foreign key references an unknown column");
|
|
1941
1978
|
else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail("foreign key column types must match");
|
|
1942
1979
|
}
|
|
1943
1980
|
if (fk.onDelete === "set null" || fk.onUpdate === "set null") {
|
|
1944
|
-
if (fk.columns.some((
|
|
1981
|
+
if (fk.columns.some((name2) => table.columns.find((column) => column.columnName === name2)?.nullable === false || table.primaryKey?.includes(name2))) fail("SET NULL requires nullable source columns");
|
|
1945
1982
|
}
|
|
1946
1983
|
const uniqueKeys = [target.primaryKey ?? [], ...target.indexes.filter((index4) => index4.unique).map((index4) => index4.columns), ...target.columns.filter((column) => column.unique).map((column) => [column.columnName])];
|
|
1947
1984
|
if (!uniqueKeys.some((key) => key.length === fk.references.length && key.every((column) => fk.references.includes(column)))) fail("foreign key target columns must form a unique key");
|
|
@@ -2046,27 +2083,31 @@ var canonicalDefinitionSchema = z.object({
|
|
|
2046
2083
|
const visiting = /* @__PURE__ */ new Set();
|
|
2047
2084
|
const visited = /* @__PURE__ */ new Set();
|
|
2048
2085
|
let cycleReported = false;
|
|
2049
|
-
const visit = (
|
|
2050
|
-
if (visited.has(
|
|
2051
|
-
if (visiting.has(
|
|
2086
|
+
const visit = (name2, path) => {
|
|
2087
|
+
if (visited.has(name2) || cycleReported) return;
|
|
2088
|
+
if (visiting.has(name2)) {
|
|
2052
2089
|
cycleReported = true;
|
|
2053
2090
|
ctx.addIssue({
|
|
2054
2091
|
code: "custom",
|
|
2055
2092
|
path: ["views"],
|
|
2056
|
-
message: `view dependencies form a cycle: ${[...path,
|
|
2093
|
+
message: `view dependencies form a cycle: ${[...path, name2].join(" -> ")}`
|
|
2057
2094
|
});
|
|
2058
2095
|
return;
|
|
2059
2096
|
}
|
|
2060
|
-
visiting.add(
|
|
2061
|
-
const view = viewByName.get(
|
|
2097
|
+
visiting.add(name2);
|
|
2098
|
+
const view = viewByName.get(name2);
|
|
2062
2099
|
for (const dependency of view?.dependencies ?? []) {
|
|
2063
|
-
if (viewByName.has(dependency)) visit(dependency, [...path,
|
|
2100
|
+
if (viewByName.has(dependency)) visit(dependency, [...path, name2]);
|
|
2064
2101
|
}
|
|
2065
|
-
visiting.delete(
|
|
2066
|
-
visited.add(
|
|
2102
|
+
visiting.delete(name2);
|
|
2103
|
+
visited.add(name2);
|
|
2067
2104
|
};
|
|
2068
2105
|
for (const view of views) visit(view.view, []);
|
|
2069
2106
|
});
|
|
2107
|
+
var canonicalDefinitionSchema = localDefinitionSchema.safeExtend({
|
|
2108
|
+
/** Flattened, pinned snapshots. Imported relations never participate in this namespace's DDL. */
|
|
2109
|
+
imports: z.array(localDefinitionSchema).max(32).optional()
|
|
2110
|
+
});
|
|
2070
2111
|
function canonicalRelations(definition) {
|
|
2071
2112
|
const views = (definition.views ?? []).map((view) => ({
|
|
2072
2113
|
table: view.view,
|
|
@@ -2110,6 +2151,11 @@ function normalizeCanonical(definition) {
|
|
|
2110
2151
|
dependencies: [...view.dependencies].sort()
|
|
2111
2152
|
}));
|
|
2112
2153
|
}
|
|
2154
|
+
if (definition.imports?.length) {
|
|
2155
|
+
normalized.imports = definition.imports.map(normalizeCanonical).sort((a, b) => a.namespace.localeCompare(b.namespace));
|
|
2156
|
+
} else {
|
|
2157
|
+
delete normalized.imports;
|
|
2158
|
+
}
|
|
2113
2159
|
return normalized;
|
|
2114
2160
|
}
|
|
2115
2161
|
|
|
@@ -2234,8 +2280,8 @@ var literal = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
|
2234
2280
|
var physical = ({ source, property }) => source.table.columns[property].columnNameFor(property);
|
|
2235
2281
|
function candidateSql({ source, column, pk, pgType }) {
|
|
2236
2282
|
const a = ident(`s_${source.name}`);
|
|
2237
|
-
const
|
|
2238
|
-
const value = `CAST(${a}.${ident(
|
|
2283
|
+
const name2 = physical({ source, property: column });
|
|
2284
|
+
const value = `CAST(${a}.${ident(name2)} AS ${pgType})`;
|
|
2239
2285
|
if (source.kind === "etl") {
|
|
2240
2286
|
return {
|
|
2241
2287
|
source: source.name,
|
|
@@ -2245,9 +2291,9 @@ function candidateSql({ source, column, pk, pgType }) {
|
|
|
2245
2291
|
by: "CAST(NULL AS text)"
|
|
2246
2292
|
};
|
|
2247
2293
|
}
|
|
2248
|
-
const at = `${a}.${ident(cellColumnName(
|
|
2294
|
+
const at = `${a}.${ident(cellColumnName(name2, "at"))}`;
|
|
2249
2295
|
const tombstoned2 = source.kind === "prefetch" ? ` AND NOT ${a}.${ident(RESOLVED_SOURCE_SYSTEM_COLUMNS.tombstoned.columnName)}` : "";
|
|
2250
|
-
return { source: source.name, value, has: `(${at} IS NOT NULL${tombstoned2})`, at, by: `${a}.${ident(cellColumnName(
|
|
2296
|
+
return { source: source.name, value, has: `(${at} IS NOT NULL${tombstoned2})`, at, by: `${a}.${ident(cellColumnName(name2, "by"))}` };
|
|
2251
2297
|
}
|
|
2252
2298
|
function outranks({ i, j, key, direction, inclusive }) {
|
|
2253
2299
|
const ki = i[key];
|
|
@@ -2340,7 +2386,7 @@ function buildResolvedQuery({ context, plan }) {
|
|
|
2340
2386
|
plan.rules.forEach((rule, index3) => {
|
|
2341
2387
|
const canonical = plan.columns[rule.column].toCanonicalColumn(rule.column);
|
|
2342
2388
|
const pgType = typeof canonical.type === "string" ? PG_TYPE[canonical.type] : `${schema === void 0 ? "" : `${ident(schema)}.`}${ident(canonical.type.enum)}`;
|
|
2343
|
-
const candidates = rule.candidates.map((
|
|
2389
|
+
const candidates = rule.candidates.map((name2) => candidateSql({ source: sourceByName.get(name2), column: rule.column, pk: plan.pk, pgType }));
|
|
2344
2390
|
cells.set(rule.column, rule.comparator === "custom" ? customCell({ candidates, orderBy: rule.orderBy, index: index3 }) : builtinCell({ comparator: rule.comparator, candidates }));
|
|
2345
2391
|
});
|
|
2346
2392
|
const laterals = [...cells.values()].flatMap((cell) => cell.lateral ? [sql.raw(cell.lateral)] : []);
|
|
@@ -2361,7 +2407,7 @@ function buildResolvedQuery({ context, plan }) {
|
|
|
2361
2407
|
const system = RESOLVED_VIEW_SYSTEM_COLUMNS;
|
|
2362
2408
|
selected[system.writebackStatus.name] = sql.raw(`${alias(manual.name)}.${ident(RESOLVED_SOURCE_SYSTEM_COLUMNS.writebackStatus.columnName)}`).as(system.writebackStatus.columnName);
|
|
2363
2409
|
selected[system.writebackError.name] = sql.raw(`${alias(manual.name)}.${ident(RESOLVED_SOURCE_SYSTEM_COLUMNS.writebackError.columnName)}`).as(system.writebackError.columnName);
|
|
2364
|
-
const hidden = plan.deletedBy.map((
|
|
2410
|
+
const hidden = plan.deletedBy.map((name2) => `NOT COALESCE(${alias(name2)}.${ident(tombstoned2)}, false)`);
|
|
2365
2411
|
const query = qb.select(selected).from(from);
|
|
2366
2412
|
return hidden.length > 0 ? query.where(sql.raw(hidden.join(" AND "))) : query;
|
|
2367
2413
|
}
|
|
@@ -2387,12 +2433,12 @@ var SYSTEM_VIEW_COLUMNS = {
|
|
|
2387
2433
|
[RESOLVED_VIEW_SYSTEM_COLUMNS.writebackStatus.name]: () => t.text(RESOLVED_VIEW_SYSTEM_COLUMNS.writebackStatus.columnName),
|
|
2388
2434
|
[RESOLVED_VIEW_SYSTEM_COLUMNS.writebackError.name]: () => t.text(RESOLVED_VIEW_SYSTEM_COLUMNS.writebackError.columnName)
|
|
2389
2435
|
};
|
|
2390
|
-
function defineResolvedTable(
|
|
2436
|
+
function defineResolvedTable(name2, columns, config) {
|
|
2391
2437
|
const file = activeAuthoringContext()?.currentFile;
|
|
2392
2438
|
const fail = (message) => {
|
|
2393
|
-
throw new OrmDefinitionError(`resolved table "${
|
|
2439
|
+
throw new OrmDefinitionError(`resolved table "${name2}": ${message}`, file);
|
|
2394
2440
|
};
|
|
2395
|
-
if (!IDENTIFIER_PATTERN.test(
|
|
2441
|
+
if (!IDENTIFIER_PATTERN.test(name2)) fail(`name must match ${IDENTIFIER_PATTERN}`);
|
|
2396
2442
|
for (const property of Object.keys(columns)) {
|
|
2397
2443
|
if (RESERVED_RESOLVED_COLUMN_NAMES.has(property) || RESERVED_RESOLVED_COLUMN_NAMES.has(columns[property].columnNameFor(property))) {
|
|
2398
2444
|
fail(`column "${property}" collides with a reserved system column`);
|
|
@@ -2411,7 +2457,7 @@ function defineResolvedTable(name, columns, config) {
|
|
|
2411
2457
|
const sourceEntries = Object.entries(config.sources);
|
|
2412
2458
|
if (sourceEntries.filter(([, source]) => source.kind === "manual").length !== 1) fail("declare exactly one manualSource()");
|
|
2413
2459
|
const plans = sourceEntries.map(
|
|
2414
|
-
([sourceName, input]) => planSource({ viewName:
|
|
2460
|
+
([sourceName, input]) => planSource({ viewName: name2, sourceName, input, columns, pk, pkBuilder, dataColumns, fail })
|
|
2415
2461
|
);
|
|
2416
2462
|
const rules = planRules({ config, plans, dataColumns, fail });
|
|
2417
2463
|
const deletedBy = [...config.rows?.deletedBy ?? []];
|
|
@@ -2445,23 +2491,23 @@ function defineResolvedTable(name, columns, config) {
|
|
|
2445
2491
|
policies: { read }
|
|
2446
2492
|
};
|
|
2447
2493
|
if (!config.materialize) {
|
|
2448
|
-
return defineView(
|
|
2494
|
+
return defineView(name2, typedColumns, { ...surface, query: (context) => buildResolvedQuery({ context, plan: queryPlan }), resolved });
|
|
2449
2495
|
}
|
|
2450
2496
|
const materialize = config.materialize === true ? {} : config.materialize;
|
|
2451
|
-
const computeName = `${
|
|
2452
|
-
const tableName = `${
|
|
2497
|
+
const computeName = `${name2}__compute`;
|
|
2498
|
+
const tableName = `${name2}__materialized`;
|
|
2453
2499
|
const compute = defineView(computeName, typedColumns, {
|
|
2454
2500
|
policies: { read },
|
|
2455
2501
|
query: (context) => buildResolvedQuery({ context, plan: queryPlan }),
|
|
2456
2502
|
internal: true
|
|
2457
2503
|
});
|
|
2458
2504
|
const table = defineTable(tableName, viewColumns, {
|
|
2459
|
-
description: `Materialized rows of resolved table "${
|
|
2505
|
+
description: `Materialized rows of resolved table "${name2}". System-maintained from "${computeName}".`,
|
|
2460
2506
|
internal: true,
|
|
2461
|
-
materializes:
|
|
2507
|
+
materializes: name2,
|
|
2462
2508
|
...materialize.indexes !== void 0 ? { indexes: materialize.indexes } : {}
|
|
2463
2509
|
});
|
|
2464
|
-
return defineView(
|
|
2510
|
+
return defineView(name2, typedColumns, {
|
|
2465
2511
|
...surface,
|
|
2466
2512
|
query: (context) => {
|
|
2467
2513
|
const handle = context.relation(table);
|
|
@@ -2488,7 +2534,7 @@ function planRules({ config, plans, dataColumns, fail }) {
|
|
|
2488
2534
|
return dataColumns.map((column) => {
|
|
2489
2535
|
const explicit = config.resolve?.[column];
|
|
2490
2536
|
const comparator = explicit ?? fallback;
|
|
2491
|
-
const candidates = explicit ? [...explicit.candidates] : comparator.candidates.filter((
|
|
2537
|
+
const candidates = explicit ? [...explicit.candidates] : comparator.candidates.filter((name2) => plans.find((plan) => plan.name === name2)?.columns.includes(column));
|
|
2492
2538
|
for (const candidate of candidates) {
|
|
2493
2539
|
const plan = plans.find((entry) => entry.name === candidate);
|
|
2494
2540
|
if (!plan) fail(`column "${column}" names unknown source "${candidate}"`);
|
|
@@ -2590,8 +2636,8 @@ function canonicalTable(table) {
|
|
|
2590
2636
|
},
|
|
2591
2637
|
actions: emptyToNull(
|
|
2592
2638
|
Object.fromEntries(
|
|
2593
|
-
Object.entries(table.actions ?? {}).map(([
|
|
2594
|
-
|
|
2639
|
+
Object.entries(table.actions ?? {}).map(([name2, action]) => [
|
|
2640
|
+
name2,
|
|
2595
2641
|
{
|
|
2596
2642
|
description: action.description ?? null,
|
|
2597
2643
|
requires: action.requires?.source ?? null,
|
|
@@ -2645,6 +2691,72 @@ function computeDefinitionHash(definition) {
|
|
|
2645
2691
|
return createHash("sha256").update(HASH_DOMAIN).update(stableJson(canonicalizeDefinition(definition))).digest("hex");
|
|
2646
2692
|
}
|
|
2647
2693
|
|
|
2694
|
+
// src/canonical/composition.ts
|
|
2695
|
+
function localContract(definition) {
|
|
2696
|
+
return normalizeCanonical(localDefinitionSchema.parse(definition));
|
|
2697
|
+
}
|
|
2698
|
+
function flattenNamespaceImports(inputs) {
|
|
2699
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
2700
|
+
const visit = (value, depth) => {
|
|
2701
|
+
if (depth > 16 || definitions.size > 32) throw new OrmDefinitionError("namespace import graph is too large");
|
|
2702
|
+
const definition = localContract(localDefinitionSchema.parse(value));
|
|
2703
|
+
const previous = definitions.get(definition.namespace);
|
|
2704
|
+
if (previous) {
|
|
2705
|
+
if (stableJson(previous) !== stableJson(definition)) throw new OrmDefinitionError(`conflicting contracts for namespace '${definition.namespace}'`);
|
|
2706
|
+
}
|
|
2707
|
+
definitions.set(definition.namespace, definition);
|
|
2708
|
+
if (typeof value === "object" && value !== null && "imports" in value && Array.isArray(value.imports)) {
|
|
2709
|
+
for (const dependency of value.imports) visit(dependency, depth + 1);
|
|
2710
|
+
}
|
|
2711
|
+
};
|
|
2712
|
+
for (const input of inputs) visit(input, 0);
|
|
2713
|
+
return [...definitions.values()].sort((a, b) => a.namespace.localeCompare(b.namespace));
|
|
2714
|
+
}
|
|
2715
|
+
function composeNamespace(definition) {
|
|
2716
|
+
const all = [localContract(definition), ...definition.imports ?? []];
|
|
2717
|
+
const owners = /* @__PURE__ */ new Map();
|
|
2718
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
2719
|
+
const enums = /* @__PURE__ */ new Map();
|
|
2720
|
+
for (const owner of all) {
|
|
2721
|
+
if (namespaces.has(owner.namespace)) throw new OrmDefinitionError(`duplicate imported namespace '${owner.namespace}'`);
|
|
2722
|
+
namespaces.add(owner.namespace);
|
|
2723
|
+
for (const value of owner.enums) {
|
|
2724
|
+
const previous = enums.get(value.name);
|
|
2725
|
+
if (previous && stableJson(previous) !== stableJson(value)) {
|
|
2726
|
+
throw new OrmDefinitionError(`conflicting imported enum '${value.name}'`);
|
|
2727
|
+
}
|
|
2728
|
+
enums.set(value.name, value);
|
|
2729
|
+
}
|
|
2730
|
+
for (const relation of canonicalRelations(owner)) {
|
|
2731
|
+
if (owners.has(relation.table)) throw new OrmDefinitionError(`ambiguous imported relation '${relation.table}'`);
|
|
2732
|
+
owners.set(relation.table, owner);
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
for (const owner of definition.imports?.length ? all : []) {
|
|
2736
|
+
for (const relation of canonicalRelations(owner)) {
|
|
2737
|
+
for (const link of relation.links) {
|
|
2738
|
+
if (link.kind === "toPlatformUser") continue;
|
|
2739
|
+
const target = owners.get(link.target ?? "");
|
|
2740
|
+
if (!target || target.namespace !== (link.namespace ?? owner.namespace)) {
|
|
2741
|
+
throw new OrmDefinitionError(`link '${owner.namespace}/${relation.table}.${link.name}' has no matching imported target`);
|
|
2742
|
+
}
|
|
2743
|
+
if (target.namespace !== owner.namespace && !target.exports?.[link.target ?? ""]) {
|
|
2744
|
+
throw new OrmDefinitionError(`target '${target.namespace}/${link.target}' is not exported`);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
return {
|
|
2750
|
+
owners,
|
|
2751
|
+
definition: {
|
|
2752
|
+
...localContract(definition),
|
|
2753
|
+
tables: all.flatMap((item) => item.tables),
|
|
2754
|
+
views: all.flatMap((item) => item.views ?? []),
|
|
2755
|
+
enums: [...enums.values()]
|
|
2756
|
+
}
|
|
2757
|
+
};
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2648
2760
|
// src/authoring/namespace.ts
|
|
2649
2761
|
var NAMESPACE_CONFIG_BRAND = "seq-namespace-config";
|
|
2650
2762
|
function isSeqNamespaceConfig(value) {
|
|
@@ -2660,6 +2772,8 @@ function defineNamespace(input) {
|
|
|
2660
2772
|
namespace: input.namespace,
|
|
2661
2773
|
description: input.description,
|
|
2662
2774
|
capabilities: input.capabilities ?? {},
|
|
2775
|
+
imports: input.imports,
|
|
2776
|
+
exports: input.exports,
|
|
2663
2777
|
file
|
|
2664
2778
|
};
|
|
2665
2779
|
registerNamespaceConfig(config);
|
|
@@ -2704,15 +2818,15 @@ function serializeNamespace(input) {
|
|
|
2704
2818
|
}
|
|
2705
2819
|
const relationsByName = /* @__PURE__ */ new Map();
|
|
2706
2820
|
for (const relation of [...tables, ...views]) {
|
|
2707
|
-
const
|
|
2708
|
-
const existing = relationsByName.get(
|
|
2821
|
+
const name2 = relationName(relation);
|
|
2822
|
+
const existing = relationsByName.get(name2);
|
|
2709
2823
|
if (existing) {
|
|
2710
2824
|
throw new OrmDefinitionError(
|
|
2711
|
-
`duplicate relation "${
|
|
2825
|
+
`duplicate relation "${name2}" (defined as ${relationKind(existing)} in ${describeFile(existing)} and ${relationKind(relation)} in ${describeFile(relation)})`,
|
|
2712
2826
|
relation.file
|
|
2713
2827
|
);
|
|
2714
2828
|
}
|
|
2715
|
-
relationsByName.set(
|
|
2829
|
+
relationsByName.set(name2, relation);
|
|
2716
2830
|
}
|
|
2717
2831
|
const enumsByName = /* @__PURE__ */ new Map();
|
|
2718
2832
|
for (const ref of enums) {
|
|
@@ -2753,6 +2867,8 @@ function serializeNamespace(input) {
|
|
|
2753
2867
|
};
|
|
2754
2868
|
if (config.description !== void 0) candidate.description = config.description;
|
|
2755
2869
|
if (canonicalViews.length > 0) candidate.views = canonicalViews;
|
|
2870
|
+
if (config.imports?.length) candidate.imports = flattenNamespaceImports(config.imports);
|
|
2871
|
+
if (config.exports) candidate.exports = config.exports;
|
|
2756
2872
|
let parsed;
|
|
2757
2873
|
try {
|
|
2758
2874
|
parsed = canonicalDefinitionSchema.parse(candidate);
|
|
@@ -2762,7 +2878,9 @@ function serializeNamespace(input) {
|
|
|
2762
2878
|
}
|
|
2763
2879
|
throw error;
|
|
2764
2880
|
}
|
|
2765
|
-
|
|
2881
|
+
const normalized = normalizeCanonical(parsed);
|
|
2882
|
+
if (normalized.imports?.length) composeNamespace(normalized);
|
|
2883
|
+
return normalized;
|
|
2766
2884
|
}
|
|
2767
2885
|
function computeCanonicalHash(definition) {
|
|
2768
2886
|
return createHash("sha256").update(HASH_DOMAIN).update(stableJson(definition)).digest("hex");
|
|
@@ -2776,6 +2894,7 @@ function buildCanonicalTable({ table, relationsByName, enumsByName }) {
|
|
|
2776
2894
|
policies: {},
|
|
2777
2895
|
actions: []
|
|
2778
2896
|
};
|
|
2897
|
+
if (table.config.displayName !== void 0) candidate.displayName = table.config.displayName;
|
|
2779
2898
|
if (table.config.description !== void 0) candidate.description = table.config.description;
|
|
2780
2899
|
if (table.config.renamedFrom !== void 0) candidate.renamedFrom = table.config.renamedFrom;
|
|
2781
2900
|
if (table.source !== void 0) candidate.source = table.source;
|
|
@@ -2859,6 +2978,9 @@ function buildCanonicalTable({ table, relationsByName, enumsByName }) {
|
|
|
2859
2978
|
if (table.config.foreignKeys?.length) {
|
|
2860
2979
|
if (table.source) throw new OrmDefinitionError(`synced table "${table.tableName}" cannot enforce foreign keys`, table.file);
|
|
2861
2980
|
candidate.foreignKeys = table.config.foreignKeys.map((fk) => {
|
|
2981
|
+
if (typeof fk.target === "object" && fk.target.kind === "seq-external-object") {
|
|
2982
|
+
throw new OrmDefinitionError("physical foreign keys require a local target; use a cross-namespace ORM link", table.file);
|
|
2983
|
+
}
|
|
2862
2984
|
const target = resolveRelationRef({ ref: fk.target, fail: (message) => {
|
|
2863
2985
|
throw new OrmDefinitionError(message, table.file);
|
|
2864
2986
|
} });
|
|
@@ -3029,6 +3151,19 @@ function buildCanonicalLink({
|
|
|
3029
3151
|
...link.description !== void 0 ? { description: link.description } : {}
|
|
3030
3152
|
};
|
|
3031
3153
|
}
|
|
3154
|
+
if (typeof link.target === "object" && link.target?.kind === "seq-external-object") {
|
|
3155
|
+
if (link.enforce) return fail("cannot enforce a cross-namespace physical foreign key; use an ORM link without enforce");
|
|
3156
|
+
return {
|
|
3157
|
+
name: linkName,
|
|
3158
|
+
kind: link.linkKind,
|
|
3159
|
+
namespace: link.target.namespace,
|
|
3160
|
+
target: link.target.table,
|
|
3161
|
+
on: link.linkKind === "toOne" ? resolveColumnRef({ relation, ref: link.on, role: "link on" }) : link.on,
|
|
3162
|
+
...link.references !== void 0 ? { references: link.linkKind === "toMany" ? resolveColumnRef({ relation, ref: link.references, role: "link references" }) : link.references } : {},
|
|
3163
|
+
enforce: false,
|
|
3164
|
+
...link.description !== void 0 ? { description: link.description } : {}
|
|
3165
|
+
};
|
|
3166
|
+
}
|
|
3032
3167
|
const target = resolveRelationRef({ ref: link.target, fail });
|
|
3033
3168
|
const targetName = relationName(target);
|
|
3034
3169
|
if (!relationsByName.has(targetName)) {
|
|
@@ -3085,11 +3220,11 @@ function assertAcyclicViewDependencies(views) {
|
|
|
3085
3220
|
views.map((view) => [view.view, new Set(view.dependencies.filter((dependency) => viewNames.has(dependency)))])
|
|
3086
3221
|
);
|
|
3087
3222
|
while (remaining.size > 0) {
|
|
3088
|
-
const ready = [...remaining.entries()].filter(([, dependencies]) => [...dependencies].every((dependency) => !remaining.has(dependency))).map(([
|
|
3223
|
+
const ready = [...remaining.entries()].filter(([, dependencies]) => [...dependencies].every((dependency) => !remaining.has(dependency))).map(([name2]) => name2);
|
|
3089
3224
|
if (ready.length === 0) {
|
|
3090
3225
|
throw new OrmDefinitionError(`view dependencies form a cycle among: ${[...remaining.keys()].sort().join(", ")}`);
|
|
3091
3226
|
}
|
|
3092
|
-
for (const
|
|
3227
|
+
for (const name2 of ready) remaining.delete(name2);
|
|
3093
3228
|
}
|
|
3094
3229
|
}
|
|
3095
3230
|
function toCanonicalPolicy(policy) {
|
|
@@ -3135,8 +3270,8 @@ function syncedContractSurface({
|
|
|
3135
3270
|
namespace,
|
|
3136
3271
|
table,
|
|
3137
3272
|
columns: Object.fromEntries(
|
|
3138
|
-
Object.entries(columns).map(([
|
|
3139
|
-
|
|
3273
|
+
Object.entries(columns).map(([name2, column]) => [
|
|
3274
|
+
name2,
|
|
3140
3275
|
{ type: column.type, nullable: column.nullable, key: column.key }
|
|
3141
3276
|
])
|
|
3142
3277
|
),
|
|
@@ -3157,7 +3292,7 @@ function extractOrmSyncedTableContract({
|
|
|
3157
3292
|
}
|
|
3158
3293
|
if (table.source === void 0) {
|
|
3159
3294
|
throw new OrmDefinitionError(
|
|
3160
|
-
`table "${table.table}" is not a synced table \u2014 extractOrmSyncedTableContract requires source
|
|
3295
|
+
`table "${table.table}" is not a synced table \u2014 extractOrmSyncedTableContract requires a qualified source`
|
|
3161
3296
|
);
|
|
3162
3297
|
}
|
|
3163
3298
|
if (table.columns.length === 0) {
|
|
@@ -3304,6 +3439,53 @@ function purgeModuleCache(root) {
|
|
|
3304
3439
|
if (key.startsWith(prefix)) delete require2.cache[key];
|
|
3305
3440
|
}
|
|
3306
3441
|
}
|
|
3442
|
+
var sqlScalarSchema = z.enum(["text", "boolean", "integer", "bigint", "numeric", "double", "uuid", "date", "timestamptz"]);
|
|
3443
|
+
var name = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).max(63).refine((value) => !["__proto__", "prototype", "constructor"].includes(value), "reserved name");
|
|
3444
|
+
var base = z.object({
|
|
3445
|
+
name,
|
|
3446
|
+
namespace: name,
|
|
3447
|
+
language: z.literal("sql"),
|
|
3448
|
+
kind: z.literal("query"),
|
|
3449
|
+
requires: z.object({ capability: z.string().min(1).max(128) }).strict(),
|
|
3450
|
+
ontology: z.object({ read: z.array(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*\/[A-Za-z_][A-Za-z0-9_]*$/)).min(1).max(256) }).strict(),
|
|
3451
|
+
maxRows: z.number().int().min(1).max(1e4).default(1e3)
|
|
3452
|
+
});
|
|
3453
|
+
var fixed = base.extend({
|
|
3454
|
+
parameters: z.record(name, z.object({ type: sqlScalarSchema, required: z.boolean().default(true) }).strict()).default({}),
|
|
3455
|
+
sql: z.string().min(1).max(65536)
|
|
3456
|
+
}).strict();
|
|
3457
|
+
var runtime = base.extend({ mode: z.literal("runtime") }).strict();
|
|
3458
|
+
var sqlOperationDefinitionSchema = z.union([fixed, runtime]);
|
|
3459
|
+
function defineOperation(input) {
|
|
3460
|
+
return sqlOperationDefinitionSchema.parse(input);
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3463
|
+
// src/authoring/catalog.ts
|
|
3464
|
+
var SEGMENT = /^[^./\\\s][^/\\]*[^./\\\s]$|^[^./\\\s]$/;
|
|
3465
|
+
function validateNamespaceCatalog(catalog, file) {
|
|
3466
|
+
const paths = /* @__PURE__ */ new Set([""]);
|
|
3467
|
+
function visit(folders, parent) {
|
|
3468
|
+
for (const [name2, children] of Object.entries(folders)) {
|
|
3469
|
+
if (!SEGMENT.test(name2) || ["__proto__", "constructor", "prototype"].includes(name2)) {
|
|
3470
|
+
throw new OrmDefinitionError(`invalid catalog folder name "${name2}"`, file);
|
|
3471
|
+
}
|
|
3472
|
+
const path = parent ? `${parent}/${name2}` : name2;
|
|
3473
|
+
paths.add(path);
|
|
3474
|
+
if (children !== null) {
|
|
3475
|
+
if (typeof children !== "object" || Array.isArray(children)) {
|
|
3476
|
+
throw new OrmDefinitionError(`catalog folder "${path}" must contain folders or null`, file);
|
|
3477
|
+
}
|
|
3478
|
+
visit(children, path);
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
visit(catalog.folders, "");
|
|
3483
|
+
for (const [object, path] of Object.entries(catalog.objects)) {
|
|
3484
|
+
if (!object || !paths.has(path)) {
|
|
3485
|
+
throw new OrmDefinitionError(`catalog object "${object}" references missing folder "${path}"`, file);
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3307
3489
|
|
|
3308
3490
|
// src/codegen/types.ts
|
|
3309
3491
|
var WIRE_TYPES = {
|
|
@@ -3335,9 +3517,9 @@ function generateNamespaceTypes(definition) {
|
|
|
3335
3517
|
lines.push(` /** Synced from ${table.source} \u2014 read-only typed contract. */`);
|
|
3336
3518
|
}
|
|
3337
3519
|
lines.push(` ${table.table}: {`);
|
|
3338
|
-
for (const [
|
|
3520
|
+
for (const [name2, column] of columns) {
|
|
3339
3521
|
if (column.deprecated) continue;
|
|
3340
|
-
lines.push(` ${
|
|
3522
|
+
lines.push(` ${name2}: ${columnWireType(column)}`);
|
|
3341
3523
|
}
|
|
3342
3524
|
lines.push(" }");
|
|
3343
3525
|
}
|
|
@@ -3348,8 +3530,8 @@ function generateNamespaceTypes(definition) {
|
|
|
3348
3530
|
function actionTypeLines(definition) {
|
|
3349
3531
|
const actions = definition.tables.flatMap((table) => Object.entries(table.actions ?? {}));
|
|
3350
3532
|
const lines = [`export type ${actionsInterfaceName(definition.namespace)} = {`];
|
|
3351
|
-
for (const [
|
|
3352
|
-
lines.push(` ${
|
|
3533
|
+
for (const [name2, action] of actions.sort(([a], [b]) => a.localeCompare(b))) {
|
|
3534
|
+
lines.push(` ${name2}: { params: ${actionParamsType(action)}; result: ${actionResultType(action)} }`);
|
|
3353
3535
|
}
|
|
3354
3536
|
lines.push("}", "");
|
|
3355
3537
|
return lines;
|
|
@@ -3357,17 +3539,17 @@ function actionTypeLines(definition) {
|
|
|
3357
3539
|
function actionParamsType(action) {
|
|
3358
3540
|
const entries = Object.entries(action.params ?? {});
|
|
3359
3541
|
if (entries.length === 0) return "Record<never, never>";
|
|
3360
|
-
const fields = entries.map(([
|
|
3361
|
-
const
|
|
3362
|
-
return param.optional ? `${
|
|
3542
|
+
const fields = entries.map(([name2, param]) => {
|
|
3543
|
+
const base2 = WIRE_TYPES[param.type];
|
|
3544
|
+
return param.optional ? `${name2}?: ${base2} | null` : `${name2}: ${base2}`;
|
|
3363
3545
|
});
|
|
3364
3546
|
return `{ ${fields.join("; ")} }`;
|
|
3365
3547
|
}
|
|
3366
3548
|
function actionResultType(action) {
|
|
3367
3549
|
if (!action.returns) return "{ rowsAffected: number }";
|
|
3368
|
-
const columns = Object.entries(action.returns).map(([
|
|
3369
|
-
const
|
|
3370
|
-
return
|
|
3550
|
+
const columns = Object.entries(action.returns).map(([name2, type]) => {
|
|
3551
|
+
const base2 = WIRE_TYPES[type];
|
|
3552
|
+
return base2 === "unknown" ? `${name2}: ${base2}` : `${name2}: ${base2} | null`;
|
|
3371
3553
|
});
|
|
3372
3554
|
return `{ rows: Array<{ ${columns.join("; ")} }> }`;
|
|
3373
3555
|
}
|
|
@@ -3381,9 +3563,9 @@ function pascalCase(namespace) {
|
|
|
3381
3563
|
return namespace.split("_").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
3382
3564
|
}
|
|
3383
3565
|
function columnWireType(column) {
|
|
3384
|
-
const
|
|
3566
|
+
const base2 = WIRE_TYPES[column.type];
|
|
3385
3567
|
const notNull = column.primary_key === true || column.nullable === false;
|
|
3386
|
-
return notNull ||
|
|
3568
|
+
return notNull || base2 === "unknown" ? base2 : `${base2} | null`;
|
|
3387
3569
|
}
|
|
3388
3570
|
|
|
3389
3571
|
// src/predicate/ast.ts
|
|
@@ -3848,7 +4030,7 @@ function executesIssues(executes, declaredParams, hasReturns) {
|
|
|
3848
4030
|
if (hasReturns && last && !RETURNING_PATTERN.test(last.replace(QUOTED_STRING_PATTERN, "''"))) {
|
|
3849
4031
|
issues.push("the last executes statement must use RETURNING to populate the declared returns columns");
|
|
3850
4032
|
}
|
|
3851
|
-
const undeclared = extractNamedParams(statements.join("\n")).filter((
|
|
4033
|
+
const undeclared = extractNamedParams(statements.join("\n")).filter((name2) => !declaredParams.has(name2));
|
|
3852
4034
|
if (undeclared.length > 0) issues.push(`executes references undeclared params: ${undeclared.join(", ")}`);
|
|
3853
4035
|
return issues;
|
|
3854
4036
|
}
|
|
@@ -3923,19 +4105,19 @@ var PARAM_SCHEMAS = {
|
|
|
3923
4105
|
function buildActionParamsSchema(action) {
|
|
3924
4106
|
const shape = {};
|
|
3925
4107
|
const entries = Object.entries(action.params ?? {});
|
|
3926
|
-
for (const [
|
|
3927
|
-
shape[
|
|
4108
|
+
for (const [name2, param] of entries) {
|
|
4109
|
+
shape[name2] = param.optional ? PARAM_SCHEMAS[param.type].nullish() : PARAM_SCHEMAS[param.type];
|
|
3928
4110
|
}
|
|
3929
4111
|
return z.strictObject(shape).superRefine((obj, ctx) => {
|
|
3930
|
-
for (const [
|
|
3931
|
-
if (!param.optional && param.type === "jsonb" && obj[
|
|
3932
|
-
ctx.addIssue({ code: "custom", message: "Required", path: [
|
|
4112
|
+
for (const [name2, param] of entries) {
|
|
4113
|
+
if (!param.optional && param.type === "jsonb" && obj[name2] === void 0) {
|
|
4114
|
+
ctx.addIssue({ code: "custom", message: "Required", path: [name2] });
|
|
3933
4115
|
}
|
|
3934
4116
|
}
|
|
3935
4117
|
});
|
|
3936
4118
|
}
|
|
3937
4119
|
var actionParamOrder = (action) => Object.keys(action.params ?? {});
|
|
3938
|
-
var actionParamArg = (
|
|
4120
|
+
var actionParamArg = (name2) => `p_${name2}`;
|
|
3939
4121
|
function compileActionStatements(action, renderParam = actionParamArg) {
|
|
3940
4122
|
return actionStatementSources(action.executes).map((statement) => compileStatement(statement, renderParam));
|
|
3941
4123
|
}
|
|
@@ -3986,10 +4168,12 @@ function compilePredIrToSql(pred, options = {}) {
|
|
|
3986
4168
|
case "cmp":
|
|
3987
4169
|
return compileCmp(pred.op, pred.column, pred.value, options);
|
|
3988
4170
|
case "capability":
|
|
3989
|
-
return `(${capabilityExpression(pred.key)})`;
|
|
4171
|
+
return `(${capabilityExpression(pred.key, pred.namespace ?? options.namespace)})`;
|
|
3990
4172
|
case "role":
|
|
3991
|
-
return `(${roleExpression(pred.key)})`;
|
|
4173
|
+
return `(${roleExpression(pred.key, pred.namespace ?? options.namespace)})`;
|
|
3992
4174
|
case "exists":
|
|
4175
|
+
if (options.exists) return options.exists(pred);
|
|
4176
|
+
throw new OrmDefinitionError("synced view read policy cannot use 'exists' without a relationship resolver");
|
|
3993
4177
|
case "aggPredicate":
|
|
3994
4178
|
case "dynamicFilter":
|
|
3995
4179
|
throw new OrmDefinitionError(
|
|
@@ -4008,7 +4192,8 @@ function compileCmp(op, column, value, options) {
|
|
|
4008
4192
|
const physical2 = physicalColumn(column, options);
|
|
4009
4193
|
const declared = options.columnTypes?.[physical2] ?? options.columnTypes?.[column];
|
|
4010
4194
|
const isEnum = typeof declared === "object" && declared !== null;
|
|
4011
|
-
const
|
|
4195
|
+
const qualified = options.alias ? `${quoteIdentifier(options.alias)}.${quoteIdentifier(physical2)}` : quoteIdentifier(physical2);
|
|
4196
|
+
const col = isEnum ? `${qualified}::text` : qualified;
|
|
4012
4197
|
const columnType = isEnum ? "text" : declared;
|
|
4013
4198
|
switch (op) {
|
|
4014
4199
|
case "eq":
|
|
@@ -4092,22 +4277,22 @@ function returnProjection(returns, lastStatement) {
|
|
|
4092
4277
|
const projection = Object.keys(returns).map(quoteIdentifier).join(", ");
|
|
4093
4278
|
return `WITH orm_returning AS (${lastStatement}) SELECT ${projection} FROM orm_returning`;
|
|
4094
4279
|
}
|
|
4095
|
-
var allActions = (definition) => definition.tables.flatMap((table) => Object.entries(table.actions ?? {}).map(([
|
|
4280
|
+
var allActions = (definition) => definition.tables.flatMap((table) => Object.entries(table.actions ?? {}).map(([name2, action]) => ({ name: name2, action }))).sort((a, b) => a.name.localeCompare(b.name));
|
|
4096
4281
|
function actionFunctionStatements(definition) {
|
|
4097
4282
|
const schema = ormSchemaName(definition.namespace);
|
|
4098
|
-
return [dropAllActionFunctions(schema), ...allActions(definition).flatMap(({ name, action }) => createActionFunction(definition,
|
|
4283
|
+
return [dropAllActionFunctions(schema), ...allActions(definition).flatMap(({ name: name2, action }) => createActionFunction(definition, name2, action))];
|
|
4099
4284
|
}
|
|
4100
4285
|
function actionPrepareChecks(definition) {
|
|
4101
4286
|
const actions = allActions(definition);
|
|
4102
4287
|
if (actions.length === 0) return [];
|
|
4103
4288
|
const statements = [`SET LOCAL search_path TO ${quoteIdentifier(ormSchemaName(definition.namespace))}`];
|
|
4104
|
-
for (const { name, action } of actions) {
|
|
4289
|
+
for (const { name: name2, action } of actions) {
|
|
4105
4290
|
const order = actionParamOrder(action);
|
|
4106
4291
|
const signature = order.length > 0 ? ` (${order.map((param) => sqlTypeName(action.params[param].type)).join(", ")})` : "";
|
|
4107
4292
|
const compiled = compileActionStatements(action, (param) => `$${order.indexOf(param) + 1}`);
|
|
4108
4293
|
compiled.forEach((sql7, index3) => {
|
|
4109
4294
|
const checked = action.returns && index3 === compiled.length - 1 ? returnProjection(action.returns, sql7) : sql7;
|
|
4110
|
-
statements.push(`PREPARE ${quoteIdentifier(`orm_check_${
|
|
4295
|
+
statements.push(`PREPARE ${quoteIdentifier(`orm_check_${name2}_${index3}`)}${signature} AS ${checked}`);
|
|
4111
4296
|
});
|
|
4112
4297
|
}
|
|
4113
4298
|
return statements;
|
|
@@ -4128,9 +4313,9 @@ BEGIN
|
|
|
4128
4313
|
END
|
|
4129
4314
|
$orm_reconcile_actions$`;
|
|
4130
4315
|
}
|
|
4131
|
-
function createActionFunction(definition,
|
|
4316
|
+
function createActionFunction(definition, name2, action) {
|
|
4132
4317
|
const schema = quoteIdentifier(ormSchemaName(definition.namespace));
|
|
4133
|
-
const fn = `${schema}.${quoteIdentifier(actionFunctionName(
|
|
4318
|
+
const fn = `${schema}.${quoteIdentifier(actionFunctionName(name2))}`;
|
|
4134
4319
|
const order = actionParamOrder(action);
|
|
4135
4320
|
const signature = order.map((paramName) => `${quoteIdentifier(actionParamArg(paramName))} ${sqlTypeName(action.params[paramName].type)}`).join(", ");
|
|
4136
4321
|
const argTypes = order.map((paramName) => sqlTypeName(action.params[paramName].type)).join(", ");
|
|
@@ -4150,7 +4335,7 @@ AS $orm_action$
|
|
|
4150
4335
|
-- columns; resolve unqualified names to the column.
|
|
4151
4336
|
#variable_conflict use_column
|
|
4152
4337
|
BEGIN
|
|
4153
|
-
${[functionGuards(definition,
|
|
4338
|
+
${[functionGuards(definition, name2, action), ...leading, ` RETURN QUERY ${returnProjection(action.returns, last)};`].filter(Boolean).join("\n")}
|
|
4154
4339
|
END
|
|
4155
4340
|
$orm_action$`
|
|
4156
4341
|
) : (
|
|
@@ -4164,7 +4349,7 @@ AS $orm_action$
|
|
|
4164
4349
|
DECLARE
|
|
4165
4350
|
v_count bigint;
|
|
4166
4351
|
BEGIN
|
|
4167
|
-
${[functionGuards(definition,
|
|
4352
|
+
${[functionGuards(definition, name2, action), ...leading, ` ${last};`].filter(Boolean).join("\n")}
|
|
4168
4353
|
GET DIAGNOSTICS v_count = ROW_COUNT;
|
|
4169
4354
|
RETURN v_count;
|
|
4170
4355
|
END
|
|
@@ -4176,13 +4361,13 @@ $orm_action$`
|
|
|
4176
4361
|
`GRANT EXECUTE ON FUNCTION ${fn}(${argTypes}) TO ${role}`
|
|
4177
4362
|
];
|
|
4178
4363
|
}
|
|
4179
|
-
function functionGuards(definition,
|
|
4364
|
+
function functionGuards(definition, name2, action) {
|
|
4180
4365
|
const lines = [];
|
|
4181
4366
|
const requires = compileActionRequires(action);
|
|
4182
4367
|
if (requires) {
|
|
4183
4368
|
lines.push(
|
|
4184
4369
|
` IF NOT (${requires}) THEN`,
|
|
4185
|
-
` RAISE EXCEPTION 'action ${
|
|
4370
|
+
` RAISE EXCEPTION 'action ${name2} requires a grant the caller does not hold' USING ERRCODE = ${quoteLiteral(ACTION_REQUIRES_DENIED_SQLSTATE)};`,
|
|
4186
4371
|
` END IF;`
|
|
4187
4372
|
);
|
|
4188
4373
|
}
|
|
@@ -4234,7 +4419,7 @@ var tableFileSchema = z.strictObject({
|
|
|
4234
4419
|
table: identifierSchema,
|
|
4235
4420
|
description: z.string().optional(),
|
|
4236
4421
|
columns: z.record(identifierSchema, columnSchema).optional(),
|
|
4237
|
-
source: z.string().regex(SYNCED_SOURCE_PATTERN, "source must
|
|
4422
|
+
source: z.string().regex(SYNCED_SOURCE_PATTERN, "source must be a qualified lowercase schema.table identifier").optional(),
|
|
4238
4423
|
links: z.record(identifierSchema, linkSchema).optional(),
|
|
4239
4424
|
indexes: z.array(indexSchema).optional(),
|
|
4240
4425
|
policies: policiesSchema.optional(),
|
|
@@ -4243,12 +4428,12 @@ var tableFileSchema = z.strictObject({
|
|
|
4243
4428
|
if (RESERVED_TABLE_NAMES.has(table.table)) {
|
|
4244
4429
|
ctx.addIssue({ code: "custom", message: `'${table.table}' is a reserved table name`, path: ["table"] });
|
|
4245
4430
|
}
|
|
4246
|
-
for (const
|
|
4247
|
-
if (RESERVED_COLUMN_NAMES.has(
|
|
4431
|
+
for (const name2 of Object.keys(table.columns ?? {})) {
|
|
4432
|
+
if (RESERVED_COLUMN_NAMES.has(name2)) {
|
|
4248
4433
|
ctx.addIssue({
|
|
4249
4434
|
code: "custom",
|
|
4250
|
-
message: `column '${
|
|
4251
|
-
path: ["columns",
|
|
4435
|
+
message: `column '${name2}' collides with the Data API's reserved query parameters (select, order, limit, offset) \u2014 it could never be filtered`,
|
|
4436
|
+
path: ["columns", name2]
|
|
4252
4437
|
});
|
|
4253
4438
|
}
|
|
4254
4439
|
}
|
|
@@ -4281,41 +4466,41 @@ function validateSyncedTable(table, ctx) {
|
|
|
4281
4466
|
const columns = table.columns ?? {};
|
|
4282
4467
|
const names = Object.keys(columns);
|
|
4283
4468
|
if (names.length === 0) return;
|
|
4284
|
-
const primaryKeys = names.filter((
|
|
4469
|
+
const primaryKeys = names.filter((name2) => columns[name2]?.primary_key);
|
|
4285
4470
|
if (primaryKeys.length !== 1) {
|
|
4286
4471
|
ctx.addIssue({ code: "custom", message: "exactly one column must be primary_key", path: ["columns"] });
|
|
4287
4472
|
}
|
|
4288
4473
|
validateRenames(columns, names, ctx);
|
|
4289
|
-
for (const
|
|
4290
|
-
const column = columns[
|
|
4474
|
+
for (const name2 of names) {
|
|
4475
|
+
const column = columns[name2];
|
|
4291
4476
|
if (!column) continue;
|
|
4292
4477
|
const cast = column.cast ? castParts(column.cast) : null;
|
|
4293
|
-
if (column.cast && cast?.column !==
|
|
4478
|
+
if (column.cast && cast?.column !== name2) {
|
|
4294
4479
|
ctx.addIssue({
|
|
4295
4480
|
code: "custom",
|
|
4296
4481
|
message: "cast must reference the column it is declared on",
|
|
4297
|
-
path: ["columns",
|
|
4482
|
+
path: ["columns", name2, "cast"]
|
|
4298
4483
|
});
|
|
4299
4484
|
}
|
|
4300
4485
|
if (cast && cast.type !== column.type) {
|
|
4301
4486
|
ctx.addIssue({
|
|
4302
4487
|
code: "custom",
|
|
4303
4488
|
message: `cast target '${cast.type}' must match the declared type '${column.type}'`,
|
|
4304
|
-
path: ["columns",
|
|
4489
|
+
path: ["columns", name2, "cast"]
|
|
4305
4490
|
});
|
|
4306
4491
|
}
|
|
4307
4492
|
}
|
|
4308
4493
|
}
|
|
4309
4494
|
function validateRenames(columns, names, ctx) {
|
|
4310
4495
|
const renameSources = /* @__PURE__ */ new Map();
|
|
4311
|
-
for (const
|
|
4312
|
-
const renamedFrom = columns[
|
|
4496
|
+
for (const name2 of names) {
|
|
4497
|
+
const renamedFrom = columns[name2]?.renamed_from;
|
|
4313
4498
|
if (!renamedFrom) continue;
|
|
4314
|
-
if (renamedFrom ===
|
|
4499
|
+
if (renamedFrom === name2 || names.includes(renamedFrom)) {
|
|
4315
4500
|
ctx.addIssue({
|
|
4316
4501
|
code: "custom",
|
|
4317
4502
|
message: "renamed_from must name a column that no longer exists",
|
|
4318
|
-
path: ["columns",
|
|
4503
|
+
path: ["columns", name2, "renamed_from"]
|
|
4319
4504
|
});
|
|
4320
4505
|
}
|
|
4321
4506
|
const claimed = renameSources.get(renamedFrom);
|
|
@@ -4323,10 +4508,10 @@ function validateRenames(columns, names, ctx) {
|
|
|
4323
4508
|
ctx.addIssue({
|
|
4324
4509
|
code: "custom",
|
|
4325
4510
|
message: `renamed_from '${renamedFrom}' is already claimed by '${claimed}'`,
|
|
4326
|
-
path: ["columns",
|
|
4511
|
+
path: ["columns", name2, "renamed_from"]
|
|
4327
4512
|
});
|
|
4328
4513
|
}
|
|
4329
|
-
renameSources.set(renamedFrom,
|
|
4514
|
+
renameSources.set(renamedFrom, name2);
|
|
4330
4515
|
}
|
|
4331
4516
|
}
|
|
4332
4517
|
function validateColumnTable(table, ctx) {
|
|
@@ -4336,27 +4521,27 @@ function validateColumnTable(table, ctx) {
|
|
|
4336
4521
|
ctx.addIssue({ code: "custom", message: "a table must declare columns or a source", path: ["columns"] });
|
|
4337
4522
|
return;
|
|
4338
4523
|
}
|
|
4339
|
-
const primaryKeys = names.filter((
|
|
4524
|
+
const primaryKeys = names.filter((name2) => columns[name2]?.primary_key);
|
|
4340
4525
|
if (primaryKeys.length !== 1) {
|
|
4341
4526
|
ctx.addIssue({ code: "custom", message: "exactly one column must be primary_key", path: ["columns"] });
|
|
4342
4527
|
}
|
|
4343
4528
|
validateRenames(columns, names, ctx);
|
|
4344
|
-
for (const
|
|
4345
|
-
const column = columns[
|
|
4529
|
+
for (const name2 of names) {
|
|
4530
|
+
const column = columns[name2];
|
|
4346
4531
|
if (!column) continue;
|
|
4347
4532
|
const cast = column.cast ? castParts(column.cast) : null;
|
|
4348
|
-
if (column.cast && cast?.column !==
|
|
4533
|
+
if (column.cast && cast?.column !== name2) {
|
|
4349
4534
|
ctx.addIssue({
|
|
4350
4535
|
code: "custom",
|
|
4351
4536
|
message: "cast must reference the column it is declared on",
|
|
4352
|
-
path: ["columns",
|
|
4537
|
+
path: ["columns", name2, "cast"]
|
|
4353
4538
|
});
|
|
4354
4539
|
}
|
|
4355
4540
|
if (cast && cast.type !== column.type) {
|
|
4356
4541
|
ctx.addIssue({
|
|
4357
4542
|
code: "custom",
|
|
4358
4543
|
message: `cast target '${cast.type}' must match the declared type '${column.type}'`,
|
|
4359
|
-
path: ["columns",
|
|
4544
|
+
path: ["columns", name2, "cast"]
|
|
4360
4545
|
});
|
|
4361
4546
|
}
|
|
4362
4547
|
}
|
|
@@ -4381,22 +4566,22 @@ function validateColumnTable(table, ctx) {
|
|
|
4381
4566
|
}
|
|
4382
4567
|
function validateLinks(table, ctx) {
|
|
4383
4568
|
const columnNames = Object.keys(table.columns ?? {});
|
|
4384
|
-
for (const [
|
|
4569
|
+
for (const [name2, link] of Object.entries(table.links ?? {})) {
|
|
4385
4570
|
if (columnNames.length > 0 && !columnNames.includes(link.on)) {
|
|
4386
|
-
ctx.addIssue({ code: "custom", message: `link references unknown column '${link.on}'`, path: ["links",
|
|
4571
|
+
ctx.addIssue({ code: "custom", message: `link references unknown column '${link.on}'`, path: ["links", name2, "on"] });
|
|
4387
4572
|
}
|
|
4388
4573
|
if (link.enforce && link.to.includes(".")) {
|
|
4389
4574
|
ctx.addIssue({
|
|
4390
4575
|
code: "custom",
|
|
4391
4576
|
message: "enforce is only valid for links inside the namespace; cross-boundary links stay soft",
|
|
4392
|
-
path: ["links",
|
|
4577
|
+
path: ["links", name2, "enforce"]
|
|
4393
4578
|
});
|
|
4394
4579
|
}
|
|
4395
4580
|
if (link.enforce && table.source) {
|
|
4396
4581
|
ctx.addIssue({
|
|
4397
4582
|
code: "custom",
|
|
4398
4583
|
message: "a synced table cannot enforce foreign keys \u2014 its DDL is owned by the sync",
|
|
4399
|
-
path: ["links",
|
|
4584
|
+
path: ["links", name2, "enforce"]
|
|
4400
4585
|
});
|
|
4401
4586
|
}
|
|
4402
4587
|
}
|
|
@@ -4411,32 +4596,32 @@ function validateDerivedNameLengths(table, ctx) {
|
|
|
4411
4596
|
});
|
|
4412
4597
|
}
|
|
4413
4598
|
for (const [position, index3] of (table.indexes ?? []).entries()) {
|
|
4414
|
-
const
|
|
4415
|
-
if (
|
|
4599
|
+
const name2 = indexName(table.table, index3);
|
|
4600
|
+
if (name2.length > MAX_IDENTIFIER_LENGTH) {
|
|
4416
4601
|
ctx.addIssue({
|
|
4417
4602
|
code: "custom",
|
|
4418
|
-
message: `derived index name '${
|
|
4603
|
+
message: `derived index name '${name2}' exceeds Postgres's ${MAX_IDENTIFIER_LENGTH}-byte identifier limit \u2014 set a shorter explicit name`,
|
|
4419
4604
|
path: ["indexes", position]
|
|
4420
4605
|
});
|
|
4421
4606
|
}
|
|
4422
4607
|
}
|
|
4423
|
-
for (const [
|
|
4424
|
-
const constraint = foreignKeyName(table.table,
|
|
4608
|
+
for (const [name2, link] of Object.entries(table.links ?? {})) {
|
|
4609
|
+
const constraint = foreignKeyName(table.table, name2);
|
|
4425
4610
|
if (link.enforce && constraint.length > MAX_IDENTIFIER_LENGTH) {
|
|
4426
4611
|
ctx.addIssue({
|
|
4427
4612
|
code: "custom",
|
|
4428
4613
|
message: `derived foreign-key name '${constraint}' exceeds Postgres's ${MAX_IDENTIFIER_LENGTH}-byte identifier limit \u2014 shorten the link or table name`,
|
|
4429
|
-
path: ["links",
|
|
4614
|
+
path: ["links", name2]
|
|
4430
4615
|
});
|
|
4431
4616
|
}
|
|
4432
4617
|
}
|
|
4433
|
-
for (const
|
|
4434
|
-
const fnName = `action_${
|
|
4618
|
+
for (const name2 of Object.keys(table.actions ?? {})) {
|
|
4619
|
+
const fnName = `action_${name2}`;
|
|
4435
4620
|
if (fnName.length > MAX_IDENTIFIER_LENGTH) {
|
|
4436
4621
|
ctx.addIssue({
|
|
4437
4622
|
code: "custom",
|
|
4438
4623
|
message: `derived action function name '${fnName}' exceeds Postgres's ${MAX_IDENTIFIER_LENGTH}-byte identifier limit \u2014 shorten the action name`,
|
|
4439
|
-
path: ["actions",
|
|
4624
|
+
path: ["actions", name2]
|
|
4440
4625
|
});
|
|
4441
4626
|
}
|
|
4442
4627
|
}
|
|
@@ -4529,7 +4714,7 @@ function policyStatements(schema, table) {
|
|
|
4529
4714
|
const policyName2 = (suffix) => quoteIdentifier(policyName(table.table, suffix));
|
|
4530
4715
|
const statements = POLICY_SUFFIXES.map((suffix) => `DROP POLICY IF EXISTS ${policyName2(suffix)} ON ${qualified}`);
|
|
4531
4716
|
const columnTypes = Object.fromEntries(
|
|
4532
|
-
Object.entries(table.columns ?? {}).map(([
|
|
4717
|
+
Object.entries(table.columns ?? {}).map(([name2, column]) => [name2, column.type])
|
|
4533
4718
|
);
|
|
4534
4719
|
const read = table.policies?.read;
|
|
4535
4720
|
if (read) {
|
|
@@ -4592,12 +4777,19 @@ function syncedViewStatements({
|
|
|
4592
4777
|
const readPolicyIr = readPolicyIrByTable?.get(table.table);
|
|
4593
4778
|
const readPolicy = table.policies?.read;
|
|
4594
4779
|
const whereClause = readPolicyIr ? ` WHERE ${compilePredIrToSql(readPolicyIr, {
|
|
4780
|
+
namespace: definition.namespace,
|
|
4595
4781
|
columnTypes,
|
|
4596
4782
|
columnNames: columnNamesByTable?.get(table.table)
|
|
4597
4783
|
})}` : readPolicy ? ` WHERE ${compilePredicate(readPolicy.ast, narrowToV1ColumnTypes(columnTypes))}` : " WHERE false";
|
|
4598
4784
|
if (backingPrivileges === "grant") {
|
|
4599
4785
|
statements.push(`GRANT USAGE ON SCHEMA ${quoteIdentifier(physical2.schema)} TO ${owner}`);
|
|
4600
4786
|
statements.push(`GRANT SELECT ON ${physicalTarget} TO ${owner}`);
|
|
4787
|
+
statements.push(`DO ${quoteLiteral(`BEGIN
|
|
4788
|
+
IF NOT (pg_catalog.has_schema_privilege(${quoteLiteral(executionOwnerRole)}, ${quoteLiteral(physical2.schema)}, 'USAGE')
|
|
4789
|
+
AND pg_catalog.has_table_privilege(${quoteLiteral(executionOwnerRole)}, ${quoteLiteral(physicalTarget)}, 'SELECT')) THEN
|
|
4790
|
+
RAISE EXCEPTION 'Synced backing owner lacks effective USAGE/SELECT; repair managed serving reader access before ORM apply' USING ERRCODE = '42501';
|
|
4791
|
+
END IF;
|
|
4792
|
+
END`)}`);
|
|
4601
4793
|
}
|
|
4602
4794
|
statements.push(
|
|
4603
4795
|
`CREATE OR REPLACE VIEW ${viewName} AS SELECT * FROM ${physicalTarget}${whereClause}`
|
|
@@ -4613,8 +4805,8 @@ function mergeSyncedViewColumnTypes({
|
|
|
4613
4805
|
columnNames
|
|
4614
4806
|
}) {
|
|
4615
4807
|
const fromTable = {};
|
|
4616
|
-
for (const [
|
|
4617
|
-
if (def?.type) fromTable[
|
|
4808
|
+
for (const [name2, def] of Object.entries(table.columns ?? {})) {
|
|
4809
|
+
if (def?.type) fromTable[name2] = def.type;
|
|
4618
4810
|
}
|
|
4619
4811
|
if (columnNames) {
|
|
4620
4812
|
for (const [logical, physical2] of Object.entries(columnNames)) {
|
|
@@ -4668,21 +4860,21 @@ function syncedBackingRevokeStatements({
|
|
|
4668
4860
|
return statements;
|
|
4669
4861
|
}
|
|
4670
4862
|
var FACTORIES = {
|
|
4671
|
-
uuid: (
|
|
4672
|
-
text: (
|
|
4673
|
-
integer: (
|
|
4863
|
+
uuid: (name2) => uuid(name2),
|
|
4864
|
+
text: (name2) => text(name2),
|
|
4865
|
+
integer: (name2) => integer(name2),
|
|
4674
4866
|
// Mode 'bigint' keeps int8 lossless — mode 'number' silently rounds past
|
|
4675
4867
|
// 2^53. The read executor serializes BigInt to string for JSON.
|
|
4676
|
-
bigint: (
|
|
4677
|
-
numeric: (
|
|
4678
|
-
double: (
|
|
4679
|
-
boolean: (
|
|
4680
|
-
timestamptz: (
|
|
4681
|
-
date: (
|
|
4682
|
-
jsonb: (
|
|
4868
|
+
bigint: (name2) => bigint(name2, { mode: "bigint" }),
|
|
4869
|
+
numeric: (name2) => numeric(name2),
|
|
4870
|
+
double: (name2) => doublePrecision(name2),
|
|
4871
|
+
boolean: (name2) => boolean(name2),
|
|
4872
|
+
timestamptz: (name2) => timestamp(name2, { withTimezone: true }),
|
|
4873
|
+
date: (name2) => date(name2),
|
|
4874
|
+
jsonb: (name2) => jsonb(name2)
|
|
4683
4875
|
};
|
|
4684
|
-
function buildColumn(
|
|
4685
|
-
let column = FACTORIES[definition.type](
|
|
4876
|
+
function buildColumn(name2, definition) {
|
|
4877
|
+
let column = FACTORIES[definition.type](name2);
|
|
4686
4878
|
if (definition.primary_key) column = column.primaryKey();
|
|
4687
4879
|
if (definition.nullable === false && !definition.primary_key) column = column.notNull();
|
|
4688
4880
|
if (definition.unique && !definition.primary_key) column = column.unique();
|
|
@@ -4713,7 +4905,7 @@ function buildDrizzleEntities(definition) {
|
|
|
4713
4905
|
}
|
|
4714
4906
|
function buildTable(schema, table, built) {
|
|
4715
4907
|
const columns = Object.fromEntries(
|
|
4716
|
-
Object.entries(table.columns ?? {}).map(([
|
|
4908
|
+
Object.entries(table.columns ?? {}).map(([name2, column]) => [name2, buildColumn(name2, column)])
|
|
4717
4909
|
);
|
|
4718
4910
|
const drizzleTable = schema.table(table.table, columns, (self) => [
|
|
4719
4911
|
...buildIndexes(table, self),
|
|
@@ -4723,21 +4915,21 @@ function buildTable(schema, table, built) {
|
|
|
4723
4915
|
}
|
|
4724
4916
|
function buildIndexes(table, self) {
|
|
4725
4917
|
return (table.indexes ?? []).map((definition) => {
|
|
4726
|
-
const
|
|
4727
|
-
const factory = definition.unique ? uniqueIndex(
|
|
4918
|
+
const name2 = indexName(table.table, definition);
|
|
4919
|
+
const factory = definition.unique ? uniqueIndex(name2) : index(name2);
|
|
4728
4920
|
const columns = definition.columns.map((column) => self[column]);
|
|
4729
4921
|
return factory.on(...columns);
|
|
4730
4922
|
});
|
|
4731
4923
|
}
|
|
4732
4924
|
function buildForeignKeys(table, self, built) {
|
|
4733
|
-
return Object.entries(table.links ?? {}).filter(([, link]) => link.enforce).map(([
|
|
4925
|
+
return Object.entries(table.links ?? {}).filter(([, link]) => link.enforce).map(([name2, link]) => {
|
|
4734
4926
|
const target = link.to === table.table ? self : built.get(link.to);
|
|
4735
4927
|
const targetColumn = target && findPrimaryKeyColumn(target);
|
|
4736
4928
|
if (!target || !targetColumn) {
|
|
4737
|
-
throw new OrmDefinitionError(`enforced link '${
|
|
4929
|
+
throw new OrmDefinitionError(`enforced link '${name2}' on '${table.table}' has no built target '${link.to}'`);
|
|
4738
4930
|
}
|
|
4739
4931
|
return foreignKey({
|
|
4740
|
-
name: foreignKeyName(table.table,
|
|
4932
|
+
name: foreignKeyName(table.table, name2),
|
|
4741
4933
|
columns: [self[link.on]],
|
|
4742
4934
|
foreignColumns: [targetColumn]
|
|
4743
4935
|
});
|
|
@@ -4794,21 +4986,21 @@ function diffStatements(previous, current) {
|
|
|
4794
4986
|
function assertUnambiguousDiff(previous, current) {
|
|
4795
4987
|
const previousTables = tablesOf(previous);
|
|
4796
4988
|
const currentTables = tablesOf(current);
|
|
4797
|
-
const removedTables = [...previousTables.keys()].filter((
|
|
4798
|
-
const addedTables = [...currentTables.keys()].filter((
|
|
4989
|
+
const removedTables = [...previousTables.keys()].filter((name2) => !currentTables.has(name2));
|
|
4990
|
+
const addedTables = [...currentTables.keys()].filter((name2) => !previousTables.has(name2));
|
|
4799
4991
|
if (removedTables.length > 0 && addedTables.length > 0) {
|
|
4800
4992
|
throw new OrmDefinitionError(
|
|
4801
4993
|
`ambiguous diff: table(s) added (${addedTables.join(", ")}) and removed (${removedTables.join(", ")}) in the same change \u2014 ship the add and the drop as separate changes`
|
|
4802
4994
|
);
|
|
4803
4995
|
}
|
|
4804
|
-
for (const [
|
|
4805
|
-
const currentColumns = currentTables.get(
|
|
4996
|
+
for (const [name2, previousColumns] of previousTables) {
|
|
4997
|
+
const currentColumns = currentTables.get(name2);
|
|
4806
4998
|
if (!currentColumns) continue;
|
|
4807
4999
|
const removed = [...previousColumns].filter((column) => !currentColumns.has(column));
|
|
4808
5000
|
const added = [...currentColumns].filter((column) => !previousColumns.has(column));
|
|
4809
5001
|
if (removed.length > 0 && added.length > 0) {
|
|
4810
5002
|
throw new OrmDefinitionError(
|
|
4811
|
-
`ambiguous diff on '${
|
|
5003
|
+
`ambiguous diff on '${name2}': column(s) added (${added.join(", ")}) and removed (${removed.join(", ")}) in the same change \u2014 declare \`renamed_from\` for a rename, or ship the add and the drop as separate changes`
|
|
4812
5004
|
);
|
|
4813
5005
|
}
|
|
4814
5006
|
}
|
|
@@ -4827,33 +5019,33 @@ function applyMigrationHints(previous, definition) {
|
|
|
4827
5019
|
for (const table of columnTables(definition)) {
|
|
4828
5020
|
const snapshotTable = findTable(adjusted, schemaName, table.table);
|
|
4829
5021
|
if (!snapshotTable) continue;
|
|
4830
|
-
for (const [
|
|
5022
|
+
for (const [name2, column] of Object.entries(table.columns ?? {})) {
|
|
4831
5023
|
if (column.renamed_from) {
|
|
4832
5024
|
const previousColumn = snapshotTable.columns[column.renamed_from];
|
|
4833
|
-
if (previousColumn && snapshotTable.columns[
|
|
5025
|
+
if (previousColumn && snapshotTable.columns[name2]) {
|
|
4834
5026
|
throw new OrmDefinitionError(
|
|
4835
|
-
`'${table.table}.${
|
|
5027
|
+
`'${table.table}.${name2}' declares renamed_from '${column.renamed_from}', but both columns exist in the previous schema \u2014 a rename cannot overwrite a live column`
|
|
4836
5028
|
);
|
|
4837
5029
|
}
|
|
4838
5030
|
if (previousColumn) {
|
|
4839
5031
|
statements.push(
|
|
4840
|
-
`ALTER TABLE ${qualified(table.table)} RENAME COLUMN ${quoteIdentifier(column.renamed_from)} TO ${quoteIdentifier(
|
|
5032
|
+
`ALTER TABLE ${qualified(table.table)} RENAME COLUMN ${quoteIdentifier(column.renamed_from)} TO ${quoteIdentifier(name2)}`
|
|
4841
5033
|
);
|
|
4842
5034
|
delete snapshotTable.columns[column.renamed_from];
|
|
4843
|
-
snapshotTable.columns[
|
|
4844
|
-
} else if (!snapshotTable.columns[
|
|
5035
|
+
snapshotTable.columns[name2] = { ...previousColumn, name: name2 };
|
|
5036
|
+
} else if (!snapshotTable.columns[name2]) {
|
|
4845
5037
|
throw new OrmDefinitionError(
|
|
4846
|
-
`'${table.table}.${
|
|
5038
|
+
`'${table.table}.${name2}' declares renamed_from '${column.renamed_from}', but the previous schema has neither column \u2014 check the declared source name`
|
|
4847
5039
|
);
|
|
4848
5040
|
}
|
|
4849
5041
|
}
|
|
4850
5042
|
const cast = column.cast ? castParts(column.cast) : null;
|
|
4851
5043
|
if (cast) {
|
|
4852
|
-
const current = snapshotTable.columns[
|
|
5044
|
+
const current = snapshotTable.columns[name2];
|
|
4853
5045
|
const targetType = sqlTypeName(column.type);
|
|
4854
5046
|
if (current && current.type !== targetType) {
|
|
4855
5047
|
statements.push(
|
|
4856
|
-
`ALTER TABLE ${qualified(table.table)} ALTER COLUMN ${quoteIdentifier(
|
|
5048
|
+
`ALTER TABLE ${qualified(table.table)} ALTER COLUMN ${quoteIdentifier(name2)} TYPE ${targetType} USING ${quoteIdentifier(name2)}::${targetType}`
|
|
4857
5049
|
);
|
|
4858
5050
|
current.type = targetType;
|
|
4859
5051
|
}
|
|
@@ -4894,8 +5086,8 @@ function materializedRefreshFunctions(canonical) {
|
|
|
4894
5086
|
}
|
|
4895
5087
|
return functions;
|
|
4896
5088
|
}
|
|
4897
|
-
function refreshFunctionSql({ schema, name, table, compute, columns, pk }) {
|
|
4898
|
-
const fn = `${quoteIdentifier(schema)}.${quoteIdentifier(materializedRefreshFunctionName(
|
|
5089
|
+
function refreshFunctionSql({ schema, name: name2, table, compute, columns, pk }) {
|
|
5090
|
+
const fn = `${quoteIdentifier(schema)}.${quoteIdentifier(materializedRefreshFunctionName(name2))}`;
|
|
4899
5091
|
const target = `${quoteIdentifier(schema)}.${quoteIdentifier(table)}`;
|
|
4900
5092
|
const source = `${quoteIdentifier(schema)}.${quoteIdentifier(compute)}`;
|
|
4901
5093
|
const list = columns.map(quoteIdentifier).join(", ");
|
|
@@ -4904,7 +5096,7 @@ function refreshFunctionSql({ schema, name, table, compute, columns, pk }) {
|
|
|
4904
5096
|
const matches = pk.map((column) => `c.${quoteIdentifier(column)} = m.${quoteIdentifier(column)}`).join(" AND ");
|
|
4905
5097
|
const onConflict = others.length === 0 ? "DO NOTHING" : `DO UPDATE SET ${others.map((column) => `${quoteIdentifier(column)} = EXCLUDED.${quoteIdentifier(column)}`).join(", ")}
|
|
4906
5098
|
WHERE (${others.map((column) => `m.${quoteIdentifier(column)}`).join(", ")}) IS DISTINCT FROM (${others.map((column) => `EXCLUDED.${quoteIdentifier(column)}`).join(", ")})`;
|
|
4907
|
-
const lockKey = quoteLiteral(materializeLockKey(schema,
|
|
5099
|
+
const lockKey = quoteLiteral(materializeLockKey(schema, name2));
|
|
4908
5100
|
return `CREATE OR REPLACE FUNCTION ${fn}(wait boolean DEFAULT false)
|
|
4909
5101
|
RETURNS TABLE(upserted bigint, deleted bigint, locked boolean)
|
|
4910
5102
|
LANGUAGE plpgsql
|
|
@@ -4942,12 +5134,12 @@ $orm_refresh$`;
|
|
|
4942
5134
|
function refreshFunctionStatements({ namespace, previous, current }) {
|
|
4943
5135
|
const schema = quoteIdentifier(ormSchemaName(namespace));
|
|
4944
5136
|
const statements = [];
|
|
4945
|
-
for (const
|
|
4946
|
-
if (!current.has(
|
|
5137
|
+
for (const name2 of Object.keys(previous).sort()) {
|
|
5138
|
+
if (!current.has(name2)) statements.push(`DROP FUNCTION IF EXISTS ${schema}.${quoteIdentifier(materializedRefreshFunctionName(name2))}(boolean)`);
|
|
4947
5139
|
}
|
|
4948
|
-
for (const [
|
|
4949
|
-
if (previous[
|
|
4950
|
-
const fn = `${schema}.${quoteIdentifier(materializedRefreshFunctionName(
|
|
5140
|
+
for (const [name2, create] of [...current].sort(([a], [b]) => a.localeCompare(b))) {
|
|
5141
|
+
if (previous[name2] === create) continue;
|
|
5142
|
+
const fn = `${schema}.${quoteIdentifier(materializedRefreshFunctionName(name2))}(boolean)`;
|
|
4951
5143
|
statements.push(create, `REVOKE ALL ON FUNCTION ${fn} FROM PUBLIC`);
|
|
4952
5144
|
}
|
|
4953
5145
|
return statements;
|
|
@@ -4955,21 +5147,21 @@ function refreshFunctionStatements({ namespace, previous, current }) {
|
|
|
4955
5147
|
|
|
4956
5148
|
// src/engine/canonical2.ts
|
|
4957
5149
|
var FACTORIES2 = {
|
|
4958
|
-
uuid: (
|
|
4959
|
-
text: (
|
|
4960
|
-
integer: (
|
|
5150
|
+
uuid: (name2) => uuid(name2),
|
|
5151
|
+
text: (name2) => text(name2),
|
|
5152
|
+
integer: (name2) => integer(name2),
|
|
4961
5153
|
// Mode 'bigint' keeps int8 lossless — mode 'number' silently rounds past
|
|
4962
5154
|
// 2^53. The read executor serializes BigInt to string for JSON.
|
|
4963
|
-
bigint: (
|
|
4964
|
-
numeric: (
|
|
4965
|
-
double: (
|
|
4966
|
-
boolean: (
|
|
4967
|
-
timestamptz: (
|
|
4968
|
-
date: (
|
|
4969
|
-
jsonb: (
|
|
5155
|
+
bigint: (name2) => bigint(name2, { mode: "bigint" }),
|
|
5156
|
+
numeric: (name2) => numeric(name2),
|
|
5157
|
+
double: (name2) => doublePrecision(name2),
|
|
5158
|
+
boolean: (name2) => boolean(name2),
|
|
5159
|
+
timestamptz: (name2) => timestamp(name2, { withTimezone: true }),
|
|
5160
|
+
date: (name2) => date(name2),
|
|
5161
|
+
jsonb: (name2) => jsonb(name2)
|
|
4970
5162
|
};
|
|
4971
5163
|
var SCHEMA_IMPORT_KEY2 = "$schema";
|
|
4972
|
-
var enumImportKey = (
|
|
5164
|
+
var enumImportKey = (name2) => `$enum_${name2}`;
|
|
4973
5165
|
var columnTablesV2 = (definition) => definition.tables.filter((table) => !table.source);
|
|
4974
5166
|
function buildDrizzleEntitiesV2(definition, options = {}) {
|
|
4975
5167
|
const schemaName = ormSchemaName(definition.namespace);
|
|
@@ -5271,9 +5463,9 @@ function buildTable2(schema, table, built, enums, byName, introspection) {
|
|
|
5271
5463
|
throw new OrmDefinitionError(`table '${table.table}' declares no primaryKey`);
|
|
5272
5464
|
}
|
|
5273
5465
|
const columnNames = new Set(table.columns.map((column) => column.columnName));
|
|
5274
|
-
for (const
|
|
5275
|
-
if (!columnNames.has(
|
|
5276
|
-
throw new OrmDefinitionError(`primaryKey of '${table.table}' references unknown column '${
|
|
5466
|
+
for (const name2 of primaryKeyColumns) {
|
|
5467
|
+
if (!columnNames.has(name2)) {
|
|
5468
|
+
throw new OrmDefinitionError(`primaryKey of '${table.table}' references unknown column '${name2}'`);
|
|
5277
5469
|
}
|
|
5278
5470
|
}
|
|
5279
5471
|
const singlePrimaryKey = primaryKeyColumns.length === 1 ? primaryKeyColumns[0] : void 0;
|
|
@@ -5359,8 +5551,8 @@ function buildIndexes2(table, self) {
|
|
|
5359
5551
|
throw new OrmDefinitionError(`index on '${table.table}' references unknown column '${column}'`);
|
|
5360
5552
|
}
|
|
5361
5553
|
}
|
|
5362
|
-
const
|
|
5363
|
-
const factory = definition.unique ? uniqueIndex(
|
|
5554
|
+
const name2 = indexName(table.table, definition);
|
|
5555
|
+
const factory = definition.unique ? uniqueIndex(name2) : index(name2);
|
|
5364
5556
|
const columns = definition.columns.map((column) => self[column]);
|
|
5365
5557
|
return factory.on(...columns);
|
|
5366
5558
|
});
|
|
@@ -5439,33 +5631,33 @@ function applyMigrationHintsV2(previous, definition) {
|
|
|
5439
5631
|
const snapshotTable = findTable2(adjusted, schemaName, table.table);
|
|
5440
5632
|
if (!snapshotTable) continue;
|
|
5441
5633
|
for (const column of table.columns) {
|
|
5442
|
-
const
|
|
5634
|
+
const name2 = column.columnName;
|
|
5443
5635
|
if (column.renamedFrom) {
|
|
5444
5636
|
const previousColumn = snapshotTable.columns[column.renamedFrom];
|
|
5445
|
-
if (previousColumn && snapshotTable.columns[
|
|
5637
|
+
if (previousColumn && snapshotTable.columns[name2]) {
|
|
5446
5638
|
throw new OrmDefinitionError(
|
|
5447
|
-
`'${table.table}.${
|
|
5639
|
+
`'${table.table}.${name2}' declares renamedFrom '${column.renamedFrom}', but both columns exist in the previous schema \u2014 a rename cannot overwrite a live column`
|
|
5448
5640
|
);
|
|
5449
5641
|
}
|
|
5450
5642
|
if (previousColumn) {
|
|
5451
5643
|
statements.push(
|
|
5452
|
-
`ALTER TABLE ${qualified(table.table)} RENAME COLUMN ${quoteIdentifier(column.renamedFrom)} TO ${quoteIdentifier(
|
|
5644
|
+
`ALTER TABLE ${qualified(table.table)} RENAME COLUMN ${quoteIdentifier(column.renamedFrom)} TO ${quoteIdentifier(name2)}`
|
|
5453
5645
|
);
|
|
5454
5646
|
delete snapshotTable.columns[column.renamedFrom];
|
|
5455
|
-
snapshotTable.columns[
|
|
5456
|
-
} else if (!snapshotTable.columns[
|
|
5647
|
+
snapshotTable.columns[name2] = { ...previousColumn, name: name2 };
|
|
5648
|
+
} else if (!snapshotTable.columns[name2]) {
|
|
5457
5649
|
throw new OrmDefinitionError(
|
|
5458
|
-
`'${table.table}.${
|
|
5650
|
+
`'${table.table}.${name2}' declares renamedFrom '${column.renamedFrom}', but the previous schema has neither column \u2014 check the declared source name`
|
|
5459
5651
|
);
|
|
5460
5652
|
}
|
|
5461
5653
|
}
|
|
5462
5654
|
const cast = column.cast ? castParts(column.cast) : null;
|
|
5463
5655
|
if (cast) {
|
|
5464
|
-
const current = snapshotTable.columns[
|
|
5656
|
+
const current = snapshotTable.columns[name2];
|
|
5465
5657
|
const { snapshotType, sqlType } = castTargetTypes(column, enumsByName, schemaName, table.table);
|
|
5466
5658
|
if (current && current.type !== snapshotType) {
|
|
5467
5659
|
statements.push(
|
|
5468
|
-
`ALTER TABLE ${qualified(table.table)} ALTER COLUMN ${quoteIdentifier(
|
|
5660
|
+
`ALTER TABLE ${qualified(table.table)} ALTER COLUMN ${quoteIdentifier(name2)} TYPE ${sqlType} USING ${quoteIdentifier(name2)}::${sqlType}`
|
|
5469
5661
|
);
|
|
5470
5662
|
current.type = snapshotType;
|
|
5471
5663
|
}
|
|
@@ -5476,8 +5668,8 @@ function applyMigrationHintsV2(previous, definition) {
|
|
|
5476
5668
|
}
|
|
5477
5669
|
function castTargetTypes(column, enums, schemaName, table) {
|
|
5478
5670
|
if (typeof column.type === "string") {
|
|
5479
|
-
const
|
|
5480
|
-
return { snapshotType:
|
|
5671
|
+
const name2 = sqlTypeName(column.type);
|
|
5672
|
+
return { snapshotType: name2, sqlType: name2 };
|
|
5481
5673
|
}
|
|
5482
5674
|
const enumName = column.type.enum;
|
|
5483
5675
|
if (!enums.has(enumName)) {
|
|
@@ -5562,11 +5754,11 @@ async function planWithViewRecreation({
|
|
|
5562
5754
|
})
|
|
5563
5755
|
});
|
|
5564
5756
|
const dropOrder = topologicalViewNames(
|
|
5565
|
-
new Set([...affected].filter((
|
|
5757
|
+
new Set([...affected].filter((name2) => previousViews.has(name2))),
|
|
5566
5758
|
previousDependencies
|
|
5567
5759
|
).reverse();
|
|
5568
5760
|
const createOrder = topologicalViewNames(
|
|
5569
|
-
new Set([...affected].filter((
|
|
5761
|
+
new Set([...affected].filter((name2) => currentViews.has(name2))),
|
|
5570
5762
|
currentDependencies
|
|
5571
5763
|
);
|
|
5572
5764
|
const dropDdl = await generatedViewStatements({
|
|
@@ -5583,14 +5775,14 @@ async function planWithViewRecreation({
|
|
|
5583
5775
|
});
|
|
5584
5776
|
const currentViewDefinitions = new Map((canonical.views ?? []).map((view) => [view.view, view]));
|
|
5585
5777
|
const statements = [
|
|
5586
|
-
...dropOrder.map((
|
|
5778
|
+
...dropOrder.map((name2) => ({ kind: "ddl", sql: requiredViewStatement(dropDdl, name2, schema, "DROP") })),
|
|
5587
5779
|
...hints.statements.map((statement) => ({ kind: "hint", sql: statement })),
|
|
5588
5780
|
...baseDdl.map((statement) => ({ kind: "ddl", sql: statement.replace(/;\s*$/, "") })),
|
|
5589
5781
|
...foreignKeyTimingStatements(canonical, previous, baseDdl).map((sql7) => ({ kind: "ddl", sql: sql7 })),
|
|
5590
|
-
...createOrder.map((
|
|
5591
|
-
...createOrder.map((
|
|
5592
|
-
const view = currentViewDefinitions.get(
|
|
5593
|
-
if (!view) throw new OrmDefinitionError(`missing canonical definition for view '${
|
|
5782
|
+
...createOrder.map((name2) => ({ kind: "ddl", sql: requiredViewStatement(createDdl, name2, schema, "CREATE") })),
|
|
5783
|
+
...createOrder.map((name2) => {
|
|
5784
|
+
const view = currentViewDefinitions.get(name2);
|
|
5785
|
+
if (!view) throw new OrmDefinitionError(`missing canonical definition for view '${name2}'`);
|
|
5594
5786
|
return { kind: "ddl", sql: validateViewCatalogStatement({ canonical, view, schema }) };
|
|
5595
5787
|
}),
|
|
5596
5788
|
...refreshFunctionStatements({
|
|
@@ -5620,9 +5812,9 @@ function foreignKeyTimingStatements(canonical, previous, baseDdl) {
|
|
|
5620
5812
|
}
|
|
5621
5813
|
return result;
|
|
5622
5814
|
}
|
|
5623
|
-
function foreignKeyTimingSql(schema, table,
|
|
5815
|
+
function foreignKeyTimingSql(schema, table, name2, timing) {
|
|
5624
5816
|
const clause = timing === void 0 ? "NOT DEFERRABLE" : timing === "initially-deferred" ? "DEFERRABLE INITIALLY DEFERRED" : "DEFERRABLE INITIALLY IMMEDIATE";
|
|
5625
|
-
return `ALTER TABLE ${quoteIdentifier(schema)}.${quoteIdentifier(table)} ALTER CONSTRAINT ${quoteIdentifier(
|
|
5817
|
+
return `ALTER TABLE ${quoteIdentifier(schema)}.${quoteIdentifier(table)} ALTER CONSTRAINT ${quoteIdentifier(name2)} ${clause}`;
|
|
5626
5818
|
}
|
|
5627
5819
|
function validateViewCatalogStatement({
|
|
5628
5820
|
canonical,
|
|
@@ -5764,7 +5956,7 @@ function viewReach({
|
|
|
5764
5956
|
if (viewRecreation === "all-on-base-change") return "all";
|
|
5765
5957
|
if (baseDdl.some((statement) => DROPS_TYPE.test(statement))) return "all";
|
|
5766
5958
|
const dependencies = snapshotViewDependencies(adjusted);
|
|
5767
|
-
if ([...snapshotViews(adjusted).keys()].some((
|
|
5959
|
+
if ([...snapshotViews(adjusted).keys()].some((name2) => dependencies[name2] === void 0)) return "all";
|
|
5768
5960
|
return baseChangesReachingViews({ previous, adjusted, current });
|
|
5769
5961
|
}
|
|
5770
5962
|
var SNAPSHOT_BOOKKEEPING = /* @__PURE__ */ new Set(["id", "prevId", "version", "dialect", "_meta", "views", "_sequenceOrm", "enums"]);
|
|
@@ -5813,8 +6005,8 @@ function tableChangeReachesViews({ was, is: is2 }) {
|
|
|
5813
6005
|
if (field === "columns" || VIEW_SAFE_TABLE_FIELDS.has(field)) continue;
|
|
5814
6006
|
if (stableJson(was[field]) !== stableJson(is2[field])) return true;
|
|
5815
6007
|
}
|
|
5816
|
-
return Object.entries(was.columns).some(([
|
|
5817
|
-
const next = is2.columns[
|
|
6008
|
+
return Object.entries(was.columns).some(([name2, column]) => {
|
|
6009
|
+
const next = is2.columns[name2];
|
|
5818
6010
|
if (!next) return true;
|
|
5819
6011
|
return [.../* @__PURE__ */ new Set([...Object.keys(column), ...Object.keys(next)])].some(
|
|
5820
6012
|
(field) => !VIEW_SAFE_COLUMN_FIELDS.has(field) && stableJson(column[field]) !== stableJson(next[field])
|
|
@@ -5840,22 +6032,22 @@ function affectedViewNames({
|
|
|
5840
6032
|
}) {
|
|
5841
6033
|
const allNames = /* @__PURE__ */ new Set([...previousViews.keys(), ...currentViews.keys()]);
|
|
5842
6034
|
if (reach === "all") return allNames;
|
|
5843
|
-
const dependenciesOf = (
|
|
6035
|
+
const dependenciesOf = (name2) => /* @__PURE__ */ new Set([...previousDependencies[name2] ?? [], ...currentDependencies[name2] ?? []]);
|
|
5844
6036
|
const affected = new Set(
|
|
5845
|
-
[...allNames].filter((
|
|
5846
|
-
const before = previousViews.get(
|
|
5847
|
-
const after = currentViews.get(
|
|
6037
|
+
[...allNames].filter((name2) => {
|
|
6038
|
+
const before = previousViews.get(name2);
|
|
6039
|
+
const after = currentViews.get(name2);
|
|
5848
6040
|
if (before === void 0 || after === void 0 || stableJson(before) !== stableJson(after)) return true;
|
|
5849
|
-
return [...dependenciesOf(
|
|
6041
|
+
return [...dependenciesOf(name2)].some((dependency) => reach.has(dependency));
|
|
5850
6042
|
})
|
|
5851
6043
|
);
|
|
5852
6044
|
let grew = true;
|
|
5853
6045
|
while (grew) {
|
|
5854
6046
|
grew = false;
|
|
5855
|
-
for (const
|
|
5856
|
-
if (affected.has(
|
|
5857
|
-
if ([...dependenciesOf(
|
|
5858
|
-
affected.add(
|
|
6047
|
+
for (const name2 of allNames) {
|
|
6048
|
+
if (affected.has(name2)) continue;
|
|
6049
|
+
if ([...dependenciesOf(name2)].some((dependency) => affected.has(dependency))) {
|
|
6050
|
+
affected.add(name2);
|
|
5859
6051
|
grew = true;
|
|
5860
6052
|
}
|
|
5861
6053
|
}
|
|
@@ -5866,13 +6058,13 @@ function topologicalViewNames(names, dependencies) {
|
|
|
5866
6058
|
const remaining = new Set(names);
|
|
5867
6059
|
const ordered = [];
|
|
5868
6060
|
while (remaining.size > 0) {
|
|
5869
|
-
const ready = [...remaining].filter((
|
|
6061
|
+
const ready = [...remaining].filter((name2) => (dependencies[name2] ?? []).every((dependency) => !remaining.has(dependency))).sort();
|
|
5870
6062
|
if (ready.length === 0) {
|
|
5871
6063
|
throw new OrmDefinitionError(`view dependencies form a cycle among: ${[...remaining].sort().join(", ")}`);
|
|
5872
6064
|
}
|
|
5873
|
-
for (const
|
|
5874
|
-
ordered.push(
|
|
5875
|
-
remaining.delete(
|
|
6065
|
+
for (const name2 of ready) {
|
|
6066
|
+
ordered.push(name2);
|
|
6067
|
+
remaining.delete(name2);
|
|
5876
6068
|
}
|
|
5877
6069
|
}
|
|
5878
6070
|
return ordered;
|
|
@@ -5890,18 +6082,18 @@ async function generatedViewStatements({
|
|
|
5890
6082
|
holder.views = Object.fromEntries(Object.entries(views).filter(([, view]) => !names.has(view.name)));
|
|
5891
6083
|
const generated = mode === "drop" ? await diffStatements(snapshot, without) : await diffStatements(without, snapshot);
|
|
5892
6084
|
const statements = /* @__PURE__ */ new Map();
|
|
5893
|
-
for (const
|
|
5894
|
-
const qualified = `${quoteIdentifier(schema)}.${quoteIdentifier(
|
|
6085
|
+
for (const name2 of names) {
|
|
6086
|
+
const qualified = `${quoteIdentifier(schema)}.${quoteIdentifier(name2)}`;
|
|
5895
6087
|
const prefix = mode === "drop" ? "DROP VIEW" : "CREATE VIEW";
|
|
5896
6088
|
const statement = generated.find((candidate) => candidate.trimStart().startsWith(`${prefix} ${qualified}`));
|
|
5897
|
-
if (statement) statements.set(
|
|
6089
|
+
if (statement) statements.set(name2, statement.replace(/;\s*$/, ""));
|
|
5898
6090
|
}
|
|
5899
6091
|
return statements;
|
|
5900
6092
|
}
|
|
5901
|
-
function requiredViewStatement(statements,
|
|
5902
|
-
const statement = statements.get(
|
|
6093
|
+
function requiredViewStatement(statements, name2, schema, operation) {
|
|
6094
|
+
const statement = statements.get(name2);
|
|
5903
6095
|
if (!statement) {
|
|
5904
|
-
throw new OrmDefinitionError(`drizzle-kit did not generate ${operation} VIEW for '${schema}.${
|
|
6096
|
+
throw new OrmDefinitionError(`drizzle-kit did not generate ${operation} VIEW for '${schema}.${name2}'`);
|
|
5905
6097
|
}
|
|
5906
6098
|
return statement;
|
|
5907
6099
|
}
|
|
@@ -6147,9 +6339,13 @@ function renderOrmMigrationExport(exported) {
|
|
|
6147
6339
|
if (Buffer.byteLength(text4, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
|
|
6148
6340
|
return text4;
|
|
6149
6341
|
}
|
|
6150
|
-
var IR_VERSION =
|
|
6342
|
+
var IR_VERSION = 5;
|
|
6151
6343
|
var LEGACY_IR_VERSION = 1;
|
|
6152
|
-
var
|
|
6344
|
+
var GUARDED_IR_VERSION = 2;
|
|
6345
|
+
var COMPOSED_IR_VERSION = 3;
|
|
6346
|
+
var SQL_IR_VERSION = 4;
|
|
6347
|
+
var RUNTIME_SQL_IR_VERSION = 5;
|
|
6348
|
+
var irVersionSchema = z.union([z.literal(LEGACY_IR_VERSION), z.literal(GUARDED_IR_VERSION), z.literal(COMPOSED_IR_VERSION), z.literal(SQL_IR_VERSION), z.literal(RUNTIME_SQL_IR_VERSION)]);
|
|
6153
6349
|
var operationVersionSchema = z.object({ irVersion: irVersionSchema });
|
|
6154
6350
|
function requiredArtifactIrVersion(operations) {
|
|
6155
6351
|
let required = LEGACY_IR_VERSION;
|
|
@@ -6201,8 +6397,8 @@ var predIrSchema = z.lazy(
|
|
|
6201
6397
|
value: z.number(),
|
|
6202
6398
|
where: predIrSchema.optional()
|
|
6203
6399
|
}),
|
|
6204
|
-
z.object({ kind: z.literal("capability"), key: z.string() }),
|
|
6205
|
-
z.object({ kind: z.literal("role"), key: z.string() }),
|
|
6400
|
+
z.object({ kind: z.literal("capability"), key: z.string(), namespace: z.string().optional() }),
|
|
6401
|
+
z.object({ kind: z.literal("role"), key: z.string(), namespace: z.string().optional() }),
|
|
6206
6402
|
z.object({ kind: z.literal("dynamicFilter"), slot: z.string() }),
|
|
6207
6403
|
z.object({ kind: z.literal("const"), value: z.boolean() })
|
|
6208
6404
|
])
|
|
@@ -6429,7 +6625,31 @@ var variableIrSchema = z.object({
|
|
|
6429
6625
|
/** Absent = no default declared; see {@link variableDefaultIrSchema}. */
|
|
6430
6626
|
defaultValue: variableDefaultIrSchema.optional()
|
|
6431
6627
|
});
|
|
6628
|
+
var sqlColumnSchema = z.object({ name: z.string(), type: z.enum(["text", "boolean", "integer", "bigint", "numeric", "double", "uuid", "date", "timestamptz"]), nullable: z.boolean() });
|
|
6432
6629
|
var compiledOperationSchema = z.object({
|
|
6630
|
+
language: z.literal("sql").optional(),
|
|
6631
|
+
/** Server-compiled object catalog and permission ceiling, never caller SQL. */
|
|
6632
|
+
sqlQuery: z.object({
|
|
6633
|
+
catalog: z.object({
|
|
6634
|
+
identity: z.string(),
|
|
6635
|
+
objects: z.array(z.object({
|
|
6636
|
+
ref: z.string(),
|
|
6637
|
+
table: z.string(),
|
|
6638
|
+
columns: z.array(sqlColumnSchema.extend({ type: z.string() })).min(1),
|
|
6639
|
+
scan: z.string().min(1),
|
|
6640
|
+
read: z.array(z.string()).min(1)
|
|
6641
|
+
}).strict()).min(1).max(256)
|
|
6642
|
+
}).strict(),
|
|
6643
|
+
read: z.array(z.string()).min(1).max(256),
|
|
6644
|
+
maxRows: z.number().int().min(1).max(1e4)
|
|
6645
|
+
}).strict().optional(),
|
|
6646
|
+
sql: z.object({
|
|
6647
|
+
text: z.string().min(1).max(1048576),
|
|
6648
|
+
columns: z.array(sqlColumnSchema).min(1).max(256),
|
|
6649
|
+
parameters: z.array(z.string()).max(128),
|
|
6650
|
+
read: z.array(z.string()).min(1).max(256),
|
|
6651
|
+
maxRows: z.number().int().min(1).max(1e4)
|
|
6652
|
+
}).strict().optional(),
|
|
6433
6653
|
irVersion: irVersionSchema,
|
|
6434
6654
|
name: z.string(),
|
|
6435
6655
|
opHash: z.string().regex(/^sha256:[0-9a-f]{64}$/),
|
|
@@ -6441,8 +6661,16 @@ var compiledOperationSchema = z.object({
|
|
|
6441
6661
|
variables: z.array(variableIrSchema),
|
|
6442
6662
|
/** Every table the op touches — data-token scoping + audit. */
|
|
6443
6663
|
tableSet: z.array(z.string()).min(1),
|
|
6444
|
-
roots: z.array(rootIrSchema)
|
|
6664
|
+
roots: z.array(rootIrSchema)
|
|
6445
6665
|
}).superRefine((operation, ctx) => {
|
|
6666
|
+
if (operation.language === "sql") {
|
|
6667
|
+
const validPlan = operation.sqlQuery ? !operation.sql && operation.irVersion === RUNTIME_SQL_IR_VERSION : !!operation.sql && operation.irVersion === SQL_IR_VERSION;
|
|
6668
|
+
if (!validPlan || operation.kind !== "query" || operation.roots.length !== 0 || !operation.requires?.capability) {
|
|
6669
|
+
ctx.addIssue({ code: "custom", message: "SQL operations require a query-only v4 plan or v5 runtime contract, capability, and no GraphQL roots" });
|
|
6670
|
+
}
|
|
6671
|
+
} else if (operation.sql || operation.sqlQuery || operation.roots.length === 0) {
|
|
6672
|
+
ctx.addIssue({ code: "custom", message: "GraphQL operations require roots and cannot carry SQL" });
|
|
6673
|
+
}
|
|
6446
6674
|
for (const [index3, root] of operation.roots.entries()) {
|
|
6447
6675
|
if (root.kind !== "mutation" || root.assertAffectedRows === void 0) continue;
|
|
6448
6676
|
if (operation.irVersion < 2 || operation.kind !== "mutation" || root.includeIf !== void 0) {
|
|
@@ -6489,11 +6717,16 @@ var resolvedMetaIrSchema = z.object({
|
|
|
6489
6717
|
});
|
|
6490
6718
|
var tableMetaIrSchema = z.object({
|
|
6491
6719
|
table: z.string(),
|
|
6720
|
+
namespace: z.string().optional(),
|
|
6721
|
+
dependencies: z.array(z.string()).optional(),
|
|
6722
|
+
foreignReferences: z.array(z.object({ column: z.string(), target: z.string(), targetColumn: z.string() })).optional(),
|
|
6723
|
+
exportRead: z.union([z.literal(true), z.object({ capability: z.string() }), z.object({ role: z.string() })]).optional(),
|
|
6724
|
+
exportWrite: z.union([z.literal(true), z.object({ capability: z.string() }), z.object({ role: z.string() })]).optional(),
|
|
6492
6725
|
schema: z.string(),
|
|
6493
6726
|
typename: z.string(),
|
|
6494
6727
|
/**
|
|
6495
6728
|
* True when statements over this relation evaluate a claims-gated synced
|
|
6496
|
-
* view: synced twins (`source:
|
|
6729
|
+
* view: synced twins (`source: schema.table`) materialize as views whose baked
|
|
6497
6730
|
* policy reads `current_setting('request.jwt.claims')`, and namespace views
|
|
6498
6731
|
* inherit the gate transitively through their declared dependencies. The
|
|
6499
6732
|
* executor must stamp that GUC (transaction-scoped) before any statement
|
|
@@ -6552,13 +6785,21 @@ var compiledArtifactSchema = z.object({
|
|
|
6552
6785
|
irVersion: irVersionSchema,
|
|
6553
6786
|
namespace: z.string(),
|
|
6554
6787
|
schemaHash: z.string(),
|
|
6788
|
+
ownSchemaHash: z.string().optional(),
|
|
6789
|
+
dependencies: z.record(z.string(), z.string()).optional(),
|
|
6790
|
+
readPolicies: z.record(z.string(), predIrSchema).optional(),
|
|
6555
6791
|
opsHash: z.string(),
|
|
6792
|
+
/** Apply-observed synced objects without a serving backing; not part of the authored schema hash. */
|
|
6793
|
+
unbackedTables: z.array(z.string()).optional(),
|
|
6556
6794
|
tables: z.record(z.string(), tableMetaIrSchema),
|
|
6557
6795
|
operations: z.record(z.string(), compiledOperationSchema)
|
|
6558
6796
|
}).superRefine((artifact, ctx) => {
|
|
6559
|
-
|
|
6797
|
+
if (Object.keys(artifact.dependencies ?? {}).length > 0 && artifact.irVersion < COMPOSED_IR_VERSION) {
|
|
6798
|
+
ctx.addIssue({ code: "custom", path: ["irVersion"], message: "cross-namespace dependencies require IR v3" });
|
|
6799
|
+
}
|
|
6800
|
+
for (const [name2, operation] of Object.entries(artifact.operations)) {
|
|
6560
6801
|
if (operation.irVersion > artifact.irVersion) {
|
|
6561
|
-
ctx.addIssue({ code: "custom", path: ["operations",
|
|
6802
|
+
ctx.addIssue({ code: "custom", path: ["operations", name2, "irVersion"], message: "operation requires a newer IR version than its artifact" });
|
|
6562
6803
|
}
|
|
6563
6804
|
}
|
|
6564
6805
|
});
|
|
@@ -6571,7 +6812,7 @@ var IrVersionError = class extends Error {
|
|
|
6571
6812
|
function normalizeIr(raw) {
|
|
6572
6813
|
if (!raw || typeof raw !== "object") throw new IrVersionError(void 0);
|
|
6573
6814
|
const version = raw.irVersion;
|
|
6574
|
-
if (version
|
|
6815
|
+
if (!irVersionSchema.safeParse(version).success) throw new IrVersionError(version);
|
|
6575
6816
|
return compiledArtifactSchema.parse(raw);
|
|
6576
6817
|
}
|
|
6577
6818
|
function indexByHash(artifact) {
|
|
@@ -6604,10 +6845,10 @@ function buildNamespaceDefinition(files, config) {
|
|
|
6604
6845
|
}
|
|
6605
6846
|
const actionsSeen = /* @__PURE__ */ new Map();
|
|
6606
6847
|
for (const { file, table } of files) {
|
|
6607
|
-
for (const
|
|
6608
|
-
const existing = actionsSeen.get(
|
|
6609
|
-
if (existing) throw new OrmDefinitionError(`action '${
|
|
6610
|
-
actionsSeen.set(
|
|
6848
|
+
for (const name2 of Object.keys(table.actions ?? {})) {
|
|
6849
|
+
const existing = actionsSeen.get(name2);
|
|
6850
|
+
if (existing) throw new OrmDefinitionError(`action '${name2}' is already defined in ${existing}`, file);
|
|
6851
|
+
actionsSeen.set(name2, file);
|
|
6611
6852
|
}
|
|
6612
6853
|
}
|
|
6613
6854
|
const capabilities = config?.capabilities ? { roles: config.capabilities.roles ?? EMPTY_CAPABILITIES.roles, uses: config.capabilities.uses ?? [] } : EMPTY_CAPABILITIES;
|
|
@@ -6650,12 +6891,12 @@ function validateActionValidates(files, byName) {
|
|
|
6650
6891
|
}
|
|
6651
6892
|
function validateIndexNames(files) {
|
|
6652
6893
|
const seen = /* @__PURE__ */ new Map();
|
|
6653
|
-
const claim = (
|
|
6654
|
-
const existing = seen.get(
|
|
6894
|
+
const claim = (name2, label, file) => {
|
|
6895
|
+
const existing = seen.get(name2);
|
|
6655
6896
|
if (existing) {
|
|
6656
|
-
throw new OrmDefinitionError(`relation name '${
|
|
6897
|
+
throw new OrmDefinitionError(`relation name '${name2}' (${label}) collides with ${existing}`, file);
|
|
6657
6898
|
}
|
|
6658
|
-
seen.set(
|
|
6899
|
+
seen.set(name2, `${label} (${file})`);
|
|
6659
6900
|
};
|
|
6660
6901
|
for (const { file, table } of files) {
|
|
6661
6902
|
claim(table.table, `table '${table.table}'`, file);
|
|
@@ -6663,9 +6904,9 @@ function validateIndexNames(files) {
|
|
|
6663
6904
|
for (const { file, table } of files) {
|
|
6664
6905
|
if (table.source) continue;
|
|
6665
6906
|
claim(`${table.table}_pkey`, `implicit primary-key index of '${table.table}'`, file);
|
|
6666
|
-
for (const [
|
|
6907
|
+
for (const [name2, column] of Object.entries(table.columns ?? {})) {
|
|
6667
6908
|
if (column.unique && !column.primary_key) {
|
|
6668
|
-
claim(`${table.table}_${
|
|
6909
|
+
claim(`${table.table}_${name2}_unique`, `implicit unique constraint of '${table.table}.${name2}'`, file);
|
|
6669
6910
|
}
|
|
6670
6911
|
}
|
|
6671
6912
|
}
|
|
@@ -6677,20 +6918,20 @@ function validateIndexNames(files) {
|
|
|
6677
6918
|
}
|
|
6678
6919
|
function validateLocalLinks(files, byName) {
|
|
6679
6920
|
for (const { file, table } of files) {
|
|
6680
|
-
for (const [
|
|
6921
|
+
for (const [name2, link] of Object.entries(table.links ?? {})) {
|
|
6681
6922
|
if (isQualifiedReference(link.to)) continue;
|
|
6682
6923
|
const target = byName.get(link.to);
|
|
6683
6924
|
if (!target) {
|
|
6684
|
-
throw new OrmDefinitionError(`link '${
|
|
6925
|
+
throw new OrmDefinitionError(`link '${name2}' references unknown table '${link.to}'`, file);
|
|
6685
6926
|
}
|
|
6686
6927
|
if (link.enforce && target.source) {
|
|
6687
|
-
throw new OrmDefinitionError(`link '${
|
|
6928
|
+
throw new OrmDefinitionError(`link '${name2}' cannot enforce a foreign key onto a synced table`, file);
|
|
6688
6929
|
}
|
|
6689
6930
|
const sourceType = table.columns?.[link.on]?.type;
|
|
6690
6931
|
const targetPrimaryKey = Object.values(target.columns ?? {}).find((column) => column.primary_key);
|
|
6691
6932
|
if (sourceType && targetPrimaryKey && columnTypeGroup(sourceType) !== columnTypeGroup(targetPrimaryKey.type)) {
|
|
6692
6933
|
throw new OrmDefinitionError(
|
|
6693
|
-
`link '${
|
|
6934
|
+
`link '${name2}' can never join: '${link.on}' is ${sourceType} but '${link.to}' has a ${targetPrimaryKey.type} primary key`,
|
|
6694
6935
|
file
|
|
6695
6936
|
);
|
|
6696
6937
|
}
|
|
@@ -6770,7 +7011,7 @@ function parseYaml(file, contents) {
|
|
|
6770
7011
|
throw new OrmDefinitionError(error instanceof Error ? error.message : "invalid YAML", file);
|
|
6771
7012
|
}
|
|
6772
7013
|
}
|
|
6773
|
-
var isDefinitionFile = (
|
|
7014
|
+
var isDefinitionFile = (name2) => DEFINITION_FILE_EXTENSIONS.some((extension) => extname(name2) === extension);
|
|
6774
7015
|
async function listDefinitionFiles(dir) {
|
|
6775
7016
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
6776
7017
|
return entries.filter((entry) => entry.isFile() && isDefinitionFile(entry.name)).map((entry) => entry.name).sort();
|
|
@@ -6864,13 +7105,13 @@ async function readMigrationSql(dir, entry) {
|
|
|
6864
7105
|
var checksumOf = (text4) => createHash("sha256").update(text4).digest("hex");
|
|
6865
7106
|
var migrationName = (ordinal, contentHash) => `${pad(ordinal)}_${contentHash.slice(0, 12)}`;
|
|
6866
7107
|
function renderMigrationSql({
|
|
6867
|
-
name,
|
|
7108
|
+
name: name2,
|
|
6868
7109
|
namespace,
|
|
6869
7110
|
contentHash,
|
|
6870
7111
|
plan
|
|
6871
7112
|
}) {
|
|
6872
7113
|
const header = [
|
|
6873
|
-
`-- ${
|
|
7114
|
+
`-- ${name2}`,
|
|
6874
7115
|
`-- namespace: ${namespace}`,
|
|
6875
7116
|
`-- content hash: ${contentHash}`,
|
|
6876
7117
|
"-- generated by `seq-studio orm diff` \u2014 review and commit, do not edit"
|
|
@@ -6886,13 +7127,13 @@ async function writeMigration({
|
|
|
6886
7127
|
}) {
|
|
6887
7128
|
const tip = workspace.journal.entries.at(-1);
|
|
6888
7129
|
const ordinal = (tip?.ordinal ?? 0) + 1;
|
|
6889
|
-
const
|
|
6890
|
-
const sql7 = renderMigrationSql({ name, namespace: workspace.journal.namespace, contentHash, plan });
|
|
7130
|
+
const name2 = migrationName(ordinal, contentHash);
|
|
7131
|
+
const sql7 = renderMigrationSql({ name: name2, namespace: workspace.journal.namespace, contentHash, plan });
|
|
6891
7132
|
const metaDir = join(dir, MIGRATIONS_DIRNAME, META_DIRNAME);
|
|
6892
7133
|
await mkdir(metaDir, { recursive: true });
|
|
6893
7134
|
const snapshotText = `${JSON.stringify(plan.snapshot, null, 2)}
|
|
6894
7135
|
`;
|
|
6895
|
-
await writeFile(join(dir, MIGRATIONS_DIRNAME, `${
|
|
7136
|
+
await writeFile(join(dir, MIGRATIONS_DIRNAME, `${name2}.sql`), sql7);
|
|
6896
7137
|
await writeFile(join(metaDir, `${pad(ordinal)}.snapshot.json`), snapshotText);
|
|
6897
7138
|
const journal = {
|
|
6898
7139
|
...workspace.journal,
|
|
@@ -6900,7 +7141,7 @@ async function writeMigration({
|
|
|
6900
7141
|
...workspace.journal.entries,
|
|
6901
7142
|
{
|
|
6902
7143
|
ordinal,
|
|
6903
|
-
name,
|
|
7144
|
+
name: name2,
|
|
6904
7145
|
content_hash: contentHash,
|
|
6905
7146
|
previous_content_hash: tip?.content_hash ?? null,
|
|
6906
7147
|
checksum: checksumOf(sql7),
|
|
@@ -6911,7 +7152,7 @@ async function writeMigration({
|
|
|
6911
7152
|
};
|
|
6912
7153
|
await writeFile(join(metaDir, JOURNAL_FILENAME), `${JSON.stringify(journal, null, 2)}
|
|
6913
7154
|
`);
|
|
6914
|
-
return { name, ordinal, destructive };
|
|
7155
|
+
return { name: name2, ordinal, destructive };
|
|
6915
7156
|
}
|
|
6916
7157
|
var pad = (ordinal) => String(ordinal).padStart(4, "0");
|
|
6917
7158
|
function parseJson(path, raw) {
|
|
@@ -7085,15 +7326,15 @@ function convertV1DefinitionToCanonical(definition) {
|
|
|
7085
7326
|
}
|
|
7086
7327
|
function convertTable(table) {
|
|
7087
7328
|
const columns = Object.entries(table.columns ?? {});
|
|
7088
|
-
const primaryKey2 = columns.filter(([, column]) => column.primary_key).map(([
|
|
7329
|
+
const primaryKey2 = columns.filter(([, column]) => column.primary_key).map(([name2]) => name2);
|
|
7089
7330
|
return {
|
|
7090
7331
|
table: table.table,
|
|
7091
7332
|
description: table.description,
|
|
7092
7333
|
source: table.source,
|
|
7093
|
-
columns: columns.map(([
|
|
7334
|
+
columns: columns.map(([name2, column]) => convertColumn(name2, column)),
|
|
7094
7335
|
// Synced tables may declare a typed column/PK contract; untyped remain empty.
|
|
7095
7336
|
primaryKey: primaryKey2.length > 0 ? primaryKey2 : void 0,
|
|
7096
|
-
links: Object.entries(table.links ?? {}).map(([
|
|
7337
|
+
links: Object.entries(table.links ?? {}).map(([name2, link]) => convertLink(table.table, name2, link)),
|
|
7097
7338
|
indexes: (table.indexes ?? []).map((index3) => ({
|
|
7098
7339
|
columns: [...index3.columns],
|
|
7099
7340
|
unique: index3.unique ?? false,
|
|
@@ -7110,11 +7351,11 @@ function convertTable(table) {
|
|
|
7110
7351
|
actions: []
|
|
7111
7352
|
};
|
|
7112
7353
|
}
|
|
7113
|
-
function convertColumn(
|
|
7354
|
+
function convertColumn(name2, column) {
|
|
7114
7355
|
const isDefaultFn = column.default !== void 0 && isFunctionDefault(column.default, column.type);
|
|
7115
7356
|
return {
|
|
7116
|
-
name,
|
|
7117
|
-
columnName:
|
|
7357
|
+
name: name2,
|
|
7358
|
+
columnName: name2,
|
|
7118
7359
|
type: column.type,
|
|
7119
7360
|
nullable: column.primary_key ? false : column.nullable !== false,
|
|
7120
7361
|
unique: column.unique === true,
|
|
@@ -7128,14 +7369,14 @@ function convertColumn(name, column) {
|
|
|
7128
7369
|
deprecated: column.deprecated === true
|
|
7129
7370
|
};
|
|
7130
7371
|
}
|
|
7131
|
-
function convertLink(table,
|
|
7372
|
+
function convertLink(table, name2, link) {
|
|
7132
7373
|
if (link.to.includes(".")) {
|
|
7133
7374
|
throw new OrmDefinitionError(
|
|
7134
|
-
`link '${
|
|
7375
|
+
`link '${name2}' on '${table}' targets '${link.to}' \u2014 cross-namespace links have no canonical v2 representation (links are namespace-local in v2)`
|
|
7135
7376
|
);
|
|
7136
7377
|
}
|
|
7137
7378
|
return {
|
|
7138
|
-
name,
|
|
7379
|
+
name: name2,
|
|
7139
7380
|
kind: "toOne",
|
|
7140
7381
|
target: link.to,
|
|
7141
7382
|
on: link.on,
|
|
@@ -7164,29 +7405,29 @@ function buildRebaseJournalEntry({ namespace, previousHash, newHash, ordinal, sn
|
|
|
7164
7405
|
if (!Number.isInteger(ordinal) || ordinal < 2) {
|
|
7165
7406
|
throw new OrmDefinitionError(`rebase ordinal must be an integer \u2265 2 (a rebase sits on an existing journal tip); got ${ordinal}`);
|
|
7166
7407
|
}
|
|
7167
|
-
const
|
|
7168
|
-
const sql7 = renderRebaseMigrationSql({ name, namespace, previousHash, newHash });
|
|
7408
|
+
const name2 = migrationName(ordinal, newHash);
|
|
7409
|
+
const sql7 = renderRebaseMigrationSql({ name: name2, namespace, previousHash, newHash });
|
|
7169
7410
|
const snapshotText = `${JSON.stringify(snapshot, null, 2)}
|
|
7170
7411
|
`;
|
|
7171
7412
|
const entry = {
|
|
7172
7413
|
ordinal,
|
|
7173
|
-
name,
|
|
7414
|
+
name: name2,
|
|
7174
7415
|
content_hash: newHash,
|
|
7175
7416
|
previous_content_hash: previousHash,
|
|
7176
7417
|
checksum: checksumOf(sql7),
|
|
7177
7418
|
snapshot_checksum: checksumOf(snapshotText),
|
|
7178
7419
|
destructive: false
|
|
7179
7420
|
};
|
|
7180
|
-
return { name, entry, sql: sql7, snapshotText };
|
|
7421
|
+
return { name: name2, entry, sql: sql7, snapshotText };
|
|
7181
7422
|
}
|
|
7182
7423
|
function renderRebaseMigrationSql({
|
|
7183
|
-
name,
|
|
7424
|
+
name: name2,
|
|
7184
7425
|
namespace,
|
|
7185
7426
|
previousHash,
|
|
7186
7427
|
newHash
|
|
7187
7428
|
}) {
|
|
7188
7429
|
return [
|
|
7189
|
-
`-- ${
|
|
7430
|
+
`-- ${name2}`,
|
|
7190
7431
|
`-- namespace: ${namespace}`,
|
|
7191
7432
|
`-- content hash: ${newHash}`,
|
|
7192
7433
|
`-- rebase: v1 definitions (${previousHash}) converted to canonical v2 \u2014 verified no-op, zero statements`,
|
|
@@ -7260,8 +7501,8 @@ function canonicalActions(value) {
|
|
|
7260
7501
|
const entries = Object.entries(value);
|
|
7261
7502
|
if (entries.length === 0) return void 0;
|
|
7262
7503
|
return Object.fromEntries(
|
|
7263
|
-
entries.map(([
|
|
7264
|
-
|
|
7504
|
+
entries.map(([name2, action]) => [
|
|
7505
|
+
name2,
|
|
7265
7506
|
{
|
|
7266
7507
|
description: action["description"] ?? void 0,
|
|
7267
7508
|
requires: action["requires"] ?? void 0,
|
|
@@ -7274,7 +7515,527 @@ function canonicalActions(value) {
|
|
|
7274
7515
|
])
|
|
7275
7516
|
);
|
|
7276
7517
|
}
|
|
7518
|
+
var IMPORT_CONFLICT_MODES = ["error", "skip", "update"];
|
|
7519
|
+
var columnMapSchema = z.object({
|
|
7520
|
+
/** ORM property name or Postgres column name. */
|
|
7521
|
+
target: z.string().min(1).max(63),
|
|
7522
|
+
/** Column on the source relation. Identifiers only — never an expression. */
|
|
7523
|
+
source: z.string().min(1).max(63),
|
|
7524
|
+
/**
|
|
7525
|
+
* Used when the source value is NULL. Required columns without a database
|
|
7526
|
+
* default fail the load if the source contains nulls and this is omitted.
|
|
7527
|
+
*/
|
|
7528
|
+
nullDefault: z.union([z.string().max(1e4), z.number(), z.boolean()]).optional()
|
|
7529
|
+
}).strict();
|
|
7530
|
+
var importSpecSchema = z.object({
|
|
7531
|
+
table: z.string().min(1).max(63),
|
|
7532
|
+
from: z.string().regex(QUALIFIED_TABLE_PATTERN),
|
|
7533
|
+
columns: z.array(columnMapSchema).min(1).max(500),
|
|
7534
|
+
onConflict: z.enum(IMPORT_CONFLICT_MODES).default("skip"),
|
|
7535
|
+
batchSize: z.number().int().min(1).max(2e4).default(5e3)
|
|
7536
|
+
}).strict();
|
|
7537
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7538
|
+
var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
7539
|
+
var TIMESTAMPTZ_RE = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})$/;
|
|
7540
|
+
var INTEGER_RE = /^-?\d+$/;
|
|
7541
|
+
var NUMERIC_RE = /^-?\d+(\.\d+)?$/;
|
|
7542
|
+
function sourceSchemaAllowed(schema) {
|
|
7543
|
+
return schema !== "information_schema" && !schema.startsWith("pg_");
|
|
7544
|
+
}
|
|
7545
|
+
function isRelationIdentifier(value) {
|
|
7546
|
+
return value.length <= 63 && IDENTIFIER_PATTERN.test(value);
|
|
7547
|
+
}
|
|
7548
|
+
function isSourceColumn(value) {
|
|
7549
|
+
return value.length <= 63 && COLUMN_IDENTIFIER_PATTERN.test(value);
|
|
7550
|
+
}
|
|
7551
|
+
function splitQualifiedRelation(from) {
|
|
7552
|
+
const dot = from.indexOf(".");
|
|
7553
|
+
if (dot <= 0 || dot !== from.lastIndexOf(".")) return null;
|
|
7554
|
+
const schema = from.slice(0, dot);
|
|
7555
|
+
const table = from.slice(dot + 1);
|
|
7556
|
+
if (!isRelationIdentifier(schema) || !isRelationIdentifier(table)) return null;
|
|
7557
|
+
return { schema, table };
|
|
7558
|
+
}
|
|
7559
|
+
function postgresTypeName(type) {
|
|
7560
|
+
if (type === "double") return "double precision";
|
|
7561
|
+
if (type === "timestamptz") return "timestamp with time zone";
|
|
7562
|
+
return type;
|
|
7563
|
+
}
|
|
7564
|
+
function columnPostgresType(column, schemaName, enums) {
|
|
7565
|
+
if (typeof column.type === "string") return postgresTypeName(column.type);
|
|
7566
|
+
if (!enums.has(column.type.enum)) return null;
|
|
7567
|
+
return `${quoteIdentifier(schemaName)}.${quoteIdentifier(column.type.enum)}`;
|
|
7568
|
+
}
|
|
7569
|
+
function quoteQualified(schema, table) {
|
|
7570
|
+
return `${quoteIdentifier(schema)}.${quoteIdentifier(table)}`;
|
|
7571
|
+
}
|
|
7572
|
+
var INT4_MIN = -2147483648;
|
|
7573
|
+
var INT4_MAX = 2147483647;
|
|
7574
|
+
function resumeLiteralSql({
|
|
7575
|
+
column,
|
|
7576
|
+
after,
|
|
7577
|
+
schemaName,
|
|
7578
|
+
enums
|
|
7579
|
+
}) {
|
|
7580
|
+
const coerced = coerceResumeValue(column, after);
|
|
7581
|
+
if (!coerced.ok) return coerced;
|
|
7582
|
+
return literalSql({ column, value: coerced.value, schemaName, enums });
|
|
7583
|
+
}
|
|
7584
|
+
function coerceResumeValue(column, after) {
|
|
7585
|
+
const type = typeof column.type === "string" ? column.type : null;
|
|
7586
|
+
if (type === "integer") {
|
|
7587
|
+
if (!INTEGER_RE.test(after)) return { ok: false, error: `${column.name}: after must be an integer` };
|
|
7588
|
+
const parsed = Number(after);
|
|
7589
|
+
if (!Number.isSafeInteger(parsed) || parsed < INT4_MIN || parsed > INT4_MAX) {
|
|
7590
|
+
return { ok: false, error: `${column.name}: after must be an integer` };
|
|
7591
|
+
}
|
|
7592
|
+
return { ok: true, value: parsed };
|
|
7593
|
+
}
|
|
7594
|
+
if (type === "boolean") {
|
|
7595
|
+
if (after !== "true" && after !== "false") return { ok: false, error: `${column.name}: after must be true or false` };
|
|
7596
|
+
return { ok: true, value: after === "true" };
|
|
7597
|
+
}
|
|
7598
|
+
return { ok: true, value: after };
|
|
7599
|
+
}
|
|
7600
|
+
function literalSql({
|
|
7601
|
+
column,
|
|
7602
|
+
value,
|
|
7603
|
+
schemaName,
|
|
7604
|
+
enums
|
|
7605
|
+
}) {
|
|
7606
|
+
const label = column.name;
|
|
7607
|
+
if (typeof value === "string" && value.includes("\0")) {
|
|
7608
|
+
return { ok: false, error: `${label}: null default contains a NUL character` };
|
|
7609
|
+
}
|
|
7610
|
+
if (typeof column.type !== "string") {
|
|
7611
|
+
const enumDef = enums.get(column.type.enum);
|
|
7612
|
+
if (!enumDef) return { ok: false, error: `${label}: enum '${column.type.enum}' is not declared` };
|
|
7613
|
+
if (typeof value !== "string" || !enumDef.values.includes(value)) {
|
|
7614
|
+
return { ok: false, error: `${label}: null default must be one of ${enumDef.values.join(", ")}` };
|
|
7615
|
+
}
|
|
7616
|
+
const typeName = columnPostgresType(column, schemaName, enums);
|
|
7617
|
+
return { ok: true, sql: `${quoteLiteral(value)}::${typeName}` };
|
|
7618
|
+
}
|
|
7619
|
+
return scalarLiteral2({ column, type: column.type, value, label });
|
|
7620
|
+
}
|
|
7621
|
+
var SCALAR_LITERALS = {
|
|
7622
|
+
text: (value, label) => typeof value === "string" ? { ok: true, sql: quoteLiteral(value) } : { ok: false, error: `${label}: null default must be a string` },
|
|
7623
|
+
uuid: (value, label) => typeof value === "string" && UUID_RE.test(value) ? { ok: true, sql: `${quoteLiteral(value)}::uuid` } : { ok: false, error: `${label}: null default must be a UUID` },
|
|
7624
|
+
boolean: (value, label) => typeof value === "boolean" ? { ok: true, sql: value ? "TRUE" : "FALSE" } : { ok: false, error: `${label}: null default must be true or false` },
|
|
7625
|
+
integer: (value, label) => typeof value === "number" && Number.isSafeInteger(value) ? { ok: true, sql: String(value) } : { ok: false, error: `${label}: null default must be an integer` },
|
|
7626
|
+
bigint: (value, label) => typeof value === "string" && INTEGER_RE.test(value) ? { ok: true, sql: `${value}::bigint` } : { ok: false, error: `${label}: null default must be a base-10 integer string` },
|
|
7627
|
+
numeric: numericLiteral,
|
|
7628
|
+
double: numericLiteral,
|
|
7629
|
+
date: (value, label) => typeof value === "string" && DATE_RE.test(value) ? { ok: true, sql: `${quoteLiteral(value)}::date` } : { ok: false, error: `${label}: null default must be a YYYY-MM-DD date` },
|
|
7630
|
+
timestamptz: (value, label) => typeof value === "string" && TIMESTAMPTZ_RE.test(value) ? { ok: true, sql: `${quoteLiteral(value)}::${postgresTypeName("timestamptz")}` } : { ok: false, error: `${label}: null default must be an ISO-8601 timestamp` },
|
|
7631
|
+
jsonb: jsonbLiteral
|
|
7632
|
+
};
|
|
7633
|
+
function numericLiteral(value, label) {
|
|
7634
|
+
if (typeof value === "number" && Number.isFinite(value)) return { ok: true, sql: String(value) };
|
|
7635
|
+
if (typeof value === "string" && NUMERIC_RE.test(value)) return { ok: true, sql: value };
|
|
7636
|
+
return { ok: false, error: `${label}: null default must be a finite number` };
|
|
7637
|
+
}
|
|
7638
|
+
function scalarLiteral2({
|
|
7639
|
+
column,
|
|
7640
|
+
type,
|
|
7641
|
+
value,
|
|
7642
|
+
label
|
|
7643
|
+
}) {
|
|
7644
|
+
const encode = SCALAR_LITERALS[type];
|
|
7645
|
+
if (!encode) return { ok: false, error: `${column.name}: unsupported type '${type}'` };
|
|
7646
|
+
return encode(value, label);
|
|
7647
|
+
}
|
|
7648
|
+
function jsonbLiteral(value, label) {
|
|
7649
|
+
if (typeof value !== "string") return { ok: false, error: `${label}: null default must be a JSON string` };
|
|
7650
|
+
try {
|
|
7651
|
+
JSON.parse(value);
|
|
7652
|
+
} catch {
|
|
7653
|
+
return { ok: false, error: `${label}: null default must be valid JSON` };
|
|
7654
|
+
}
|
|
7655
|
+
return { ok: true, sql: `${quoteLiteral(value)}::jsonb` };
|
|
7656
|
+
}
|
|
7657
|
+
function dollarQuote(body) {
|
|
7658
|
+
const digest = createHash("sha256").update(body).digest("hex");
|
|
7659
|
+
const candidates = ["seq_orm_import", `seq_orm_${digest.slice(0, 16)}`, `seq_orm_${digest}`];
|
|
7660
|
+
const tag = candidates.find((candidate) => !body.includes(`$${candidate}$`)) ?? `seq_orm_${digest}_body`;
|
|
7661
|
+
return `$${tag}$${body}$${tag}$`;
|
|
7662
|
+
}
|
|
7663
|
+
|
|
7664
|
+
// src/import/script.ts
|
|
7665
|
+
function renderImportScript({
|
|
7666
|
+
namespace,
|
|
7667
|
+
schemaName,
|
|
7668
|
+
table,
|
|
7669
|
+
source,
|
|
7670
|
+
mapped,
|
|
7671
|
+
primaryKey: primaryKey2,
|
|
7672
|
+
onConflict,
|
|
7673
|
+
batchSize,
|
|
7674
|
+
omitted,
|
|
7675
|
+
resume
|
|
7676
|
+
}) {
|
|
7677
|
+
const sourceSql = `${quoteQualified(source.schema, source.table)} AS src`;
|
|
7678
|
+
const targetSql = quoteQualified(schemaName, table.table);
|
|
7679
|
+
const keys = primaryKey2.map((name2) => mapped.find((column) => column.column.columnName === name2)).filter((column) => column !== void 0);
|
|
7680
|
+
const probeSql = renderProbe({ sourceSql, keys, mapped });
|
|
7681
|
+
const procedure = renderProcedure({ targetSql, sourceSql, mapped, keys, onConflict, batchSize });
|
|
7682
|
+
const call = `CALL pg_temp.seq_orm_import(${resume.join(", ")});`;
|
|
7683
|
+
const omittedLine = omitted.length > 0 ? `-- Omitted columns keep their database default or NULL: ${omitted.join(", ")}` : "-- Every column is mapped.";
|
|
7684
|
+
const text4 = [
|
|
7685
|
+
"-- Sequence ORM data import",
|
|
7686
|
+
`-- namespace: ${namespace}`,
|
|
7687
|
+
`-- target: ${schemaName}.${table.table}`,
|
|
7688
|
+
`-- source: ${source.schema}.${source.table}`,
|
|
7689
|
+
`-- conflict: ${onConflict}`,
|
|
7690
|
+
`-- batch size: ${batchSize}`,
|
|
7691
|
+
"--",
|
|
7692
|
+
"-- Runs as the namespace owner. Does not evaluate insert or update policies,",
|
|
7693
|
+
"-- capability gates, or the operation journal. Sync the warehouse table into",
|
|
7694
|
+
"-- this Postgres relation first, review the probe, then run the load.",
|
|
7695
|
+
"-- skip and update are safe to re-run from the start after a failure. error stops on an existing key.",
|
|
7696
|
+
"-- Each batch commits. Do not resume from the maximum key in the target:",
|
|
7697
|
+
"-- a higher key that was already there would skip source rows this run never copied.",
|
|
7698
|
+
"-- --after is only the last key this procedure committed. This script does not print it.",
|
|
7699
|
+
omittedLine,
|
|
7700
|
+
"-- cast_* columns force every mapped value through the target type. An invalid value aborts before any insert.",
|
|
7701
|
+
"",
|
|
7702
|
+
probeSql,
|
|
7703
|
+
"",
|
|
7704
|
+
procedure,
|
|
7705
|
+
call,
|
|
7706
|
+
""
|
|
7707
|
+
].join("\n");
|
|
7708
|
+
return { probeSql, text: text4 };
|
|
7709
|
+
}
|
|
7710
|
+
function renderProbe({
|
|
7711
|
+
sourceSql,
|
|
7712
|
+
keys,
|
|
7713
|
+
mapped
|
|
7714
|
+
}) {
|
|
7715
|
+
const nullKey = keys.map((key) => `${key.expression} IS NULL`).join(" OR ");
|
|
7716
|
+
const distinct = keys.length === 1 ? keys[0].expression : `(${keys.map((key) => key.expression).join(", ")})`;
|
|
7717
|
+
const requiredNulls = nullChecked(mapped, keys).map((column) => `count(*) FILTER (WHERE src.${quoteIdentifier(column.source)} IS NULL) AS ${quoteIdentifier(`null_${column.column.columnName}`)}`);
|
|
7718
|
+
const selects = [
|
|
7719
|
+
"count(*) AS source_rows",
|
|
7720
|
+
`count(*) FILTER (WHERE ${nullKey}) AS null_primary_key`,
|
|
7721
|
+
`count(*) FILTER (WHERE NOT (${nullKey})) - count(DISTINCT ${distinct}) AS duplicate_primary_key`,
|
|
7722
|
+
...requiredNulls,
|
|
7723
|
+
...mapped.map((column) => `count(${column.expression}) AS ${quoteIdentifier(`cast_${column.column.columnName}`)}`)
|
|
7724
|
+
];
|
|
7725
|
+
return `SELECT
|
|
7726
|
+
${selects.join(",\n ")}
|
|
7727
|
+
FROM ${sourceSql};`;
|
|
7728
|
+
}
|
|
7729
|
+
function renderProcedure({
|
|
7730
|
+
targetSql,
|
|
7731
|
+
sourceSql,
|
|
7732
|
+
mapped,
|
|
7733
|
+
keys,
|
|
7734
|
+
onConflict,
|
|
7735
|
+
batchSize
|
|
7736
|
+
}) {
|
|
7737
|
+
const args = keys.map((key, index3) => `IN start_${index3} ${key.pgType} DEFAULT NULL`).join(", ");
|
|
7738
|
+
const declarations = [
|
|
7739
|
+
"DECLARE",
|
|
7740
|
+
" batch_scanned integer;",
|
|
7741
|
+
" batch_affected integer;",
|
|
7742
|
+
" total_rows bigint;",
|
|
7743
|
+
" null_keys bigint;",
|
|
7744
|
+
" distinct_keys bigint;",
|
|
7745
|
+
...keys.map((key, index3) => ` watermark_${index3} ${key.pgType};`),
|
|
7746
|
+
...requiredNullDeclarations(mapped, keys),
|
|
7747
|
+
...mapped.map((column) => ` cast_${column.column.columnName} bigint;`)
|
|
7748
|
+
];
|
|
7749
|
+
const body = [
|
|
7750
|
+
"BEGIN",
|
|
7751
|
+
...keys.map((_, index3) => ` watermark_${index3} := start_${index3};`),
|
|
7752
|
+
` ${renderGuard({ sourceSql, keys, mapped })}`,
|
|
7753
|
+
" LOOP",
|
|
7754
|
+
` ${renderPage({ targetSql, sourceSql, mapped, keys, onConflict, batchSize })}`,
|
|
7755
|
+
" EXIT WHEN batch_scanned = 0;",
|
|
7756
|
+
" RAISE NOTICE 'orm import scanned % affected %', batch_scanned, batch_affected;",
|
|
7757
|
+
" COMMIT;",
|
|
7758
|
+
" END LOOP;",
|
|
7759
|
+
"END"
|
|
7760
|
+
].join("\n");
|
|
7761
|
+
return [
|
|
7762
|
+
`CREATE OR REPLACE PROCEDURE pg_temp.seq_orm_import(${args})`,
|
|
7763
|
+
"LANGUAGE plpgsql",
|
|
7764
|
+
`AS ${dollarQuote(`
|
|
7765
|
+
${declarations.join("\n")}
|
|
7766
|
+
${body}
|
|
7767
|
+
`)}`,
|
|
7768
|
+
";",
|
|
7769
|
+
""
|
|
7770
|
+
].join("\n");
|
|
7771
|
+
}
|
|
7772
|
+
function nullChecked(mapped, keys) {
|
|
7773
|
+
return mapped.filter((column) => !column.fillsNull && column.column.nullable === false && !keys.includes(column));
|
|
7774
|
+
}
|
|
7775
|
+
function requiredNullDeclarations(mapped, keys) {
|
|
7776
|
+
return nullChecked(mapped, keys).map((column) => ` null_${column.column.columnName} bigint;`);
|
|
7777
|
+
}
|
|
7778
|
+
function renderGuard({
|
|
7779
|
+
sourceSql,
|
|
7780
|
+
keys,
|
|
7781
|
+
mapped
|
|
7782
|
+
}) {
|
|
7783
|
+
const nullKey = keys.map((key) => `${key.expression} IS NULL`).join(" OR ");
|
|
7784
|
+
const distinct = keys.length === 1 ? keys[0].expression : `(${keys.map((key) => key.expression).join(", ")})`;
|
|
7785
|
+
const nullColumns = nullChecked(mapped, keys);
|
|
7786
|
+
const into = [
|
|
7787
|
+
"total_rows",
|
|
7788
|
+
"null_keys",
|
|
7789
|
+
"distinct_keys",
|
|
7790
|
+
...nullColumns.map((column) => `null_${column.column.columnName}`),
|
|
7791
|
+
...mapped.map((column) => `cast_${column.column.columnName}`)
|
|
7792
|
+
];
|
|
7793
|
+
const selects = [
|
|
7794
|
+
"count(*)::bigint",
|
|
7795
|
+
`count(*) FILTER (WHERE ${nullKey})::bigint`,
|
|
7796
|
+
`count(DISTINCT ${distinct})::bigint`,
|
|
7797
|
+
...nullColumns.map((column) => `count(*) FILTER (WHERE src.${quoteIdentifier(column.source)} IS NULL)::bigint`),
|
|
7798
|
+
...mapped.map((column) => `count(${column.expression})::bigint`)
|
|
7799
|
+
];
|
|
7800
|
+
const checks = [
|
|
7801
|
+
"IF null_keys > 0 THEN RAISE EXCEPTION 'import refused: source primary key has % null values', null_keys; END IF;",
|
|
7802
|
+
"IF total_rows <> distinct_keys THEN RAISE EXCEPTION 'import refused: source primary key has % duplicate values', total_rows - distinct_keys; END IF;",
|
|
7803
|
+
...nullColumns.map(
|
|
7804
|
+
(column) => `IF null_${column.column.columnName} > 0 THEN RAISE EXCEPTION 'import refused: source column ${column.source} is null on % rows and ${column.column.columnName} is required', null_${column.column.columnName}; END IF;`
|
|
7805
|
+
)
|
|
7806
|
+
];
|
|
7807
|
+
return [`SELECT ${selects.join(", ")}`, ` INTO ${into.join(", ")}`, ` FROM ${sourceSql};`, ...checks.map((check) => ` ${check}`)].join("\n");
|
|
7808
|
+
}
|
|
7809
|
+
function renderPage({
|
|
7810
|
+
targetSql,
|
|
7811
|
+
sourceSql,
|
|
7812
|
+
mapped,
|
|
7813
|
+
keys,
|
|
7814
|
+
onConflict,
|
|
7815
|
+
batchSize
|
|
7816
|
+
}) {
|
|
7817
|
+
const keyExprs = keys.map((key) => key.expression);
|
|
7818
|
+
const where = `watermark_0 IS NULL OR (${keyExprs.join(", ")}) > (${keys.map((_, index3) => `watermark_${index3}`).join(", ")})`;
|
|
7819
|
+
const order = keyExprs.join(", ");
|
|
7820
|
+
const pageColumns = mapped.map((column) => `${column.expression} AS ${quoteIdentifier(column.column.columnName)}`);
|
|
7821
|
+
const insertColumns = mapped.map((column) => quoteIdentifier(column.column.columnName)).join(", ");
|
|
7822
|
+
const conflict = conflictClause(onConflict, keys, mapped);
|
|
7823
|
+
const keyOrder = keys.map((key) => `${quoteIdentifier(key.column.columnName)} DESC`).join(", ");
|
|
7824
|
+
const lastKeys = keys.map((key) => `(SELECT ${quoteIdentifier(key.column.columnName)} FROM page ORDER BY ${keyOrder} LIMIT 1)`).join(", ");
|
|
7825
|
+
const watermarkTargets = keys.map((_, index3) => `watermark_${index3}`).join(", ");
|
|
7826
|
+
return [
|
|
7827
|
+
"WITH page AS (",
|
|
7828
|
+
` SELECT ${pageColumns.join(", ")}`,
|
|
7829
|
+
` FROM ${sourceSql}`,
|
|
7830
|
+
` WHERE ${where}`,
|
|
7831
|
+
` ORDER BY ${order}`,
|
|
7832
|
+
` LIMIT ${batchSize}`,
|
|
7833
|
+
" ),",
|
|
7834
|
+
" ins AS (",
|
|
7835
|
+
` INSERT INTO ${targetSql} (${insertColumns})`,
|
|
7836
|
+
` SELECT ${insertColumns} FROM page`,
|
|
7837
|
+
` ${conflict}`,
|
|
7838
|
+
" RETURNING 1",
|
|
7839
|
+
" )",
|
|
7840
|
+
` SELECT (SELECT count(*)::int FROM page), (SELECT count(*)::int FROM ins), ${lastKeys}`,
|
|
7841
|
+
` INTO batch_scanned, batch_affected, ${watermarkTargets};`
|
|
7842
|
+
].join("\n");
|
|
7843
|
+
}
|
|
7844
|
+
function conflictClause(onConflict, keys, mapped) {
|
|
7845
|
+
if (onConflict === "error") return "";
|
|
7846
|
+
const target = keys.map((key) => quoteIdentifier(key.column.columnName)).join(", ");
|
|
7847
|
+
if (onConflict === "skip") return `ON CONFLICT (${target}) DO NOTHING`;
|
|
7848
|
+
const assignments = mapped.filter((column) => !keys.includes(column)).map((column) => `${quoteIdentifier(column.column.columnName)} = EXCLUDED.${quoteIdentifier(column.column.columnName)}`);
|
|
7849
|
+
return `ON CONFLICT (${target}) DO UPDATE SET ${assignments.join(", ")}`;
|
|
7850
|
+
}
|
|
7851
|
+
|
|
7852
|
+
// src/import/plan.ts
|
|
7853
|
+
function planTableImport({
|
|
7854
|
+
definition,
|
|
7855
|
+
spec,
|
|
7856
|
+
after
|
|
7857
|
+
}) {
|
|
7858
|
+
const parsedDefinition = canonicalDefinitionSchema.safeParse(definition);
|
|
7859
|
+
if (!parsedDefinition.success) return { ok: false, errors: ["namespace definition is not canonical ORM v2"] };
|
|
7860
|
+
const parsedSpec = importSpecSchema.safeParse(spec);
|
|
7861
|
+
if (!parsedSpec.success) {
|
|
7862
|
+
return { ok: false, errors: parsedSpec.error.issues.map((issue) => `${issue.path.join(".") || "spec"}: ${issue.message}`) };
|
|
7863
|
+
}
|
|
7864
|
+
return planParsed({ definition: parsedDefinition.data, spec: parsedSpec.data, after });
|
|
7865
|
+
}
|
|
7866
|
+
function planParsed({
|
|
7867
|
+
definition,
|
|
7868
|
+
spec,
|
|
7869
|
+
after
|
|
7870
|
+
}) {
|
|
7871
|
+
const source = splitQualifiedRelation(spec.from);
|
|
7872
|
+
const target = resolveTarget(definition, spec.table);
|
|
7873
|
+
const errors = [];
|
|
7874
|
+
if (!source) errors.push(`from '${spec.from}' is not a schema.table identifier`);
|
|
7875
|
+
else if (!sourceSchemaAllowed(source.schema)) errors.push(`from schema '${source.schema}' is not a warehouse relation`);
|
|
7876
|
+
if (!target.table) errors.push(...target.errors);
|
|
7877
|
+
if (!source || !target.table) return { ok: false, errors };
|
|
7878
|
+
const schemaName = ormSchemaName(definition.namespace);
|
|
7879
|
+
if (source.schema === schemaName && source.table === target.table.table) {
|
|
7880
|
+
errors.push(`from '${spec.from}' is the import target`);
|
|
7881
|
+
}
|
|
7882
|
+
const enums = new Map(definition.enums.map((entry) => [entry.name, entry]));
|
|
7883
|
+
const mapped = mapColumns({ table: target.table, spec, schemaName, enums, errors });
|
|
7884
|
+
const primaryKey2 = target.table.primaryKey ?? [];
|
|
7885
|
+
if (primaryKey2.length === 0) errors.push(`table '${target.table.table}' needs a primary key`);
|
|
7886
|
+
for (const key of primaryKey2) {
|
|
7887
|
+
if (!mapped.some((column) => column.column.columnName === key)) {
|
|
7888
|
+
errors.push(`primary key column '${key}' must be mapped so existing identities are preserved`);
|
|
7889
|
+
}
|
|
7890
|
+
}
|
|
7891
|
+
collectUnmapped({ table: target.table, mapped, errors });
|
|
7892
|
+
if (spec.onConflict === "update" && mapped.every((column) => primaryKey2.includes(column.column.columnName))) {
|
|
7893
|
+
errors.push('onConflict "update" needs a mapped column that is not part of the primary key');
|
|
7894
|
+
}
|
|
7895
|
+
const resume = resumeArguments({ mapped, primaryKey: primaryKey2, after, schemaName, enums, errors });
|
|
7896
|
+
if (errors.length > 0) return { ok: false, errors };
|
|
7897
|
+
const omitted = omittedColumns(target.table, mapped);
|
|
7898
|
+
const script = renderImportScript({
|
|
7899
|
+
namespace: definition.namespace,
|
|
7900
|
+
schemaName,
|
|
7901
|
+
table: target.table,
|
|
7902
|
+
source,
|
|
7903
|
+
mapped,
|
|
7904
|
+
primaryKey: primaryKey2,
|
|
7905
|
+
onConflict: spec.onConflict,
|
|
7906
|
+
batchSize: spec.batchSize,
|
|
7907
|
+
omitted,
|
|
7908
|
+
resume
|
|
7909
|
+
});
|
|
7910
|
+
const warnings = [
|
|
7911
|
+
"This load runs as the namespace owner. It does not evaluate insert or update policies.",
|
|
7912
|
+
...spec.onConflict === "error" ? ['onConflict "error" is not safe to re-run: the first existing key aborts the batch.'] : []
|
|
7913
|
+
];
|
|
7914
|
+
return {
|
|
7915
|
+
ok: true,
|
|
7916
|
+
plan: { probeSql: script.probeSql, script: script.text, batchSize: spec.batchSize, onConflict: spec.onConflict, warnings }
|
|
7917
|
+
};
|
|
7918
|
+
}
|
|
7919
|
+
function resolveTarget(definition, name2) {
|
|
7920
|
+
const table = definition.tables.find((candidate) => candidate.table === name2);
|
|
7921
|
+
if (!table) {
|
|
7922
|
+
const view = definition.views?.find((candidate) => candidate.view === name2);
|
|
7923
|
+
return { table: null, errors: [view ? `'${name2}' is a view, not an owned table` : `table '${name2}' is not in this namespace`] };
|
|
7924
|
+
}
|
|
7925
|
+
if (table.source !== void 0) return { table: null, errors: [`'${name2}' is a synced reflection and cannot be imported into`] };
|
|
7926
|
+
if (table.internal || table.materializes !== void 0) {
|
|
7927
|
+
return { table: null, errors: [`'${name2}' is an internal relation, not an import target`] };
|
|
7928
|
+
}
|
|
7929
|
+
return { table, errors: [] };
|
|
7930
|
+
}
|
|
7931
|
+
function mapColumns({
|
|
7932
|
+
table,
|
|
7933
|
+
spec,
|
|
7934
|
+
schemaName,
|
|
7935
|
+
enums,
|
|
7936
|
+
errors
|
|
7937
|
+
}) {
|
|
7938
|
+
const mapped = [];
|
|
7939
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7940
|
+
for (const entry of spec.columns) {
|
|
7941
|
+
const column = findColumn(table, entry.target);
|
|
7942
|
+
if (column === "ambiguous") {
|
|
7943
|
+
errors.push(`target '${entry.target}' matches more than one column`);
|
|
7944
|
+
continue;
|
|
7945
|
+
}
|
|
7946
|
+
if (!column) {
|
|
7947
|
+
errors.push(`target '${entry.target}' is not a column of '${table.table}'`);
|
|
7948
|
+
continue;
|
|
7949
|
+
}
|
|
7950
|
+
if (column.cell !== void 0) {
|
|
7951
|
+
errors.push(`target '${entry.target}' is cell metadata and cannot be imported`);
|
|
7952
|
+
continue;
|
|
7953
|
+
}
|
|
7954
|
+
if (seen.has(column.columnName)) {
|
|
7955
|
+
errors.push(`column '${column.columnName}' is mapped more than once`);
|
|
7956
|
+
continue;
|
|
7957
|
+
}
|
|
7958
|
+
if (!isSourceColumn(entry.source)) {
|
|
7959
|
+
errors.push(`source '${entry.source}' is not a column identifier`);
|
|
7960
|
+
continue;
|
|
7961
|
+
}
|
|
7962
|
+
seen.add(column.columnName);
|
|
7963
|
+
const pgType = columnPostgresType(column, schemaName, enums);
|
|
7964
|
+
if (!pgType) {
|
|
7965
|
+
errors.push(`column '${column.name}' references undeclared enum '${typeof column.type === "string" ? column.type : column.type.enum}'`);
|
|
7966
|
+
continue;
|
|
7967
|
+
}
|
|
7968
|
+
const expression = valueExpression({ column, source: entry.source, pgType, nullDefault: entry.nullDefault, schemaName, enums, errors });
|
|
7969
|
+
if (expression) mapped.push({ column, source: entry.source, expression, pgType, fillsNull: entry.nullDefault !== void 0 });
|
|
7970
|
+
}
|
|
7971
|
+
return mapped;
|
|
7972
|
+
}
|
|
7973
|
+
function findColumn(table, target) {
|
|
7974
|
+
const matches = table.columns.filter((column) => column.name === target || column.columnName === target);
|
|
7975
|
+
if (matches.length > 1) return "ambiguous";
|
|
7976
|
+
return matches[0];
|
|
7977
|
+
}
|
|
7978
|
+
function valueExpression({
|
|
7979
|
+
column,
|
|
7980
|
+
source,
|
|
7981
|
+
pgType,
|
|
7982
|
+
nullDefault,
|
|
7983
|
+
schemaName,
|
|
7984
|
+
enums,
|
|
7985
|
+
errors
|
|
7986
|
+
}) {
|
|
7987
|
+
const cast = `CAST(src.${quoteIdentifier(source)} AS ${pgType})`;
|
|
7988
|
+
if (nullDefault === void 0) return cast;
|
|
7989
|
+
const literal2 = literalSql({ column, value: nullDefault, schemaName, enums });
|
|
7990
|
+
if (!literal2.ok) {
|
|
7991
|
+
errors.push(literal2.error);
|
|
7992
|
+
return null;
|
|
7993
|
+
}
|
|
7994
|
+
return `COALESCE(${cast}, ${literal2.sql})`;
|
|
7995
|
+
}
|
|
7996
|
+
function collectUnmapped({
|
|
7997
|
+
table,
|
|
7998
|
+
mapped,
|
|
7999
|
+
errors
|
|
8000
|
+
}) {
|
|
8001
|
+
const mappedNames = new Set(mapped.map((column) => column.column.columnName));
|
|
8002
|
+
const primaryKey2 = new Set(table.primaryKey ?? []);
|
|
8003
|
+
for (const column of table.columns) {
|
|
8004
|
+
if (mappedNames.has(column.columnName) || column.cell !== void 0) continue;
|
|
8005
|
+
const hasDefault = column.default !== void 0 && column.default !== null || column.defaultFn !== void 0;
|
|
8006
|
+
const required = column.nullable === false && !hasDefault && !primaryKey2.has(column.columnName);
|
|
8007
|
+
if (required) {
|
|
8008
|
+
errors.push(`column '${column.name}' is required and has no default \u2014 map it or set a database default before importing`);
|
|
8009
|
+
}
|
|
8010
|
+
}
|
|
8011
|
+
}
|
|
8012
|
+
function omittedColumns(table, mapped) {
|
|
8013
|
+
const mappedNames = new Set(mapped.map((column) => column.column.columnName));
|
|
8014
|
+
return table.columns.filter((column) => column.cell === void 0 && !mappedNames.has(column.columnName)).map((column) => column.columnName);
|
|
8015
|
+
}
|
|
8016
|
+
function resumeArguments({
|
|
8017
|
+
mapped,
|
|
8018
|
+
primaryKey: primaryKey2,
|
|
8019
|
+
after,
|
|
8020
|
+
schemaName,
|
|
8021
|
+
enums,
|
|
8022
|
+
errors
|
|
8023
|
+
}) {
|
|
8024
|
+
if (after === void 0) return primaryKey2.map(() => "NULL");
|
|
8025
|
+
if (primaryKey2.length !== 1) {
|
|
8026
|
+
errors.push("after resumes a single-column primary key; re-run from the start for a composite key (skip and update are idempotent)");
|
|
8027
|
+
return [];
|
|
8028
|
+
}
|
|
8029
|
+
const key = mapped.find((column) => column.column.columnName === primaryKey2[0]);
|
|
8030
|
+
if (!key) return [];
|
|
8031
|
+
const literal2 = resumeLiteralSql({ column: key.column, after, schemaName, enums });
|
|
8032
|
+
if (!literal2.ok) {
|
|
8033
|
+
errors.push(`after: ${literal2.error}`);
|
|
8034
|
+
return [];
|
|
8035
|
+
}
|
|
8036
|
+
return [literal2.sql];
|
|
8037
|
+
}
|
|
7277
8038
|
|
|
7278
|
-
export { ACTION_REQUIRES_DENIED_SQLSTATE, ACTION_VALIDATES_FAILED_SQLSTATE, AUDIT_ACTIONS, CANONICAL_FORMAT, CAPABILITIES_CLAIM_KEY, CAPABILITY_KEY_PATTERN, CELLS_FIELD, CELL_PARTS, CELL_PART_SUFFIX, CLAIMS_CONTRACT, CLAIMS_SETTING, CLAIM_KEY_PATTERN, CMP_OPS, COLUMN_BRAND, COLUMN_DEFAULT_FNS, COLUMN_IDENTIFIER_PATTERN, COLUMN_TYPES2 as COLUMN_TYPES, COMPARE_OPS, CONSUMER_ROLE_SUFFIX, ColumnBuilder, DATE_BUCKETS, DEFINITION_FILE_EXTENSIONS, ENUM_BRAND, HASH_DOMAIN, IDENTIFIER_PATTERN, IDENTIFIER_RE, INPUT_REF_KINDS, IR_VERSION, IrVersionError, LEGACY_IR_VERSION, LINK_BRAND, MATERIALIZE_ACTOR, MATERIALIZE_CLAIMS_JSON, MAX_DEFINITION_FILE_BYTES, MAX_GENERATED_SDL_BYTES, MAX_IDENTIFIER_LENGTH, MAX_TRACKED_COLUMN_NAME, MIGRATIONS_DIRNAME, NAMESPACE_CONFIG_BRAND, NAMESPACE_CONFIG_FILE, NAMESPACE_CONFIG_FILENAMES, NAMESPACE_PATTERN, ORM_DATA_TOKEN_HEADER, ORM_MIGRATION_EXPORT_FILE, ORM_MIGRATION_EXPORT_MAX_BYTES, ORM_REGISTRATION_MAX_BYTES, ORM_SCHEMA_PREFIX, ORM_SERVICE_DATA_TOKEN_HEADER, ORM_TO_PIPELINE_TYPE, OrmDefinitionError, PredicateParseError, QUALIFIED_TABLE_PATTERN, RESERVED_COLUMN_NAMES, RESERVED_TABLE_NAMES, ROLES_CLAIM_KEY, SCHEMA_DIR, STATEMENT_KINDS, SYNCED_CONTRACT_HASH_DOMAIN, SYNCED_SOURCE_PATTERN, SYNCED_VIEW_EXECUTION_OWNER_ROLE, TABLE_BRAND, VIEW_BRAND, actionFunctionName, actionFunctionStatements, actionParamArg, actionParamOrder, actionParamSchema, actionPrepareChecks, actionSchema, actionWriteTargets, actionsInterfaceName, activeAuthoringContext, applyMigrationHints, applyMigrationHintsV2, beginAuthoringContext, buildActionParamsSchema, buildDrizzleEntities, buildDrizzleEntitiesV2, buildNamespaceDefinition, buildRebaseJournalEntry, canonicalActionSchema, canonicalColumnSchema, canonicalDefinitionSchema, canonicalEnumSchema, canonicalIndexSchema, canonicalLinkSchema, canonicalPoliciesSchema, canonicalRelations, canonicalTableSchema, canonicalViewSchema, canonicalizeDefinition, capabilityExpression, castParts, cellColumnName, checksumOf, claimExpression, classifyDestructive, columnSchema, columnTypeSchema2 as columnTypeSchema, compileActionRequires, compileActionStatements, compileNamespace, compilePredIrToSql, compilePredicate, compiledArtifactSchema, compiledOperationSchema, computeCanonicalHash, computeDefinitionHash, computeSyncedContractHash, consumerRoleName, convertV1DefinitionToCanonical, createOrmMigrationExport, custom, declaredCapabilityKeys, defineEnum, defineNamespace, defineResolvedTable, defineSyncedTable, defineTable, defineView, deriveColumnName, diffCommand, diffStatements, emptyJournal, emptySnapshot, endAuthoringContext, etlSource, extractNamedParams, extractOrmSyncedTableContract, generateNamespaceTypes, generateSnapshot, generateSnapshotV2, highest, identifierSchema, indexByHash, isColumnBuilder, isEnumRef, isFunctionDefault, isOwnedStatement, isOwnedStatementV2, isQualifiedReference, isSeqLink, isSeqNamespaceConfig, isSeqTable, isSeqView, latest, limitIrSchema, loadCommittedMigrations, loadCommittedTipMigration, loadNamespaceDefinition, loadNamespaceFromDir, lowest, managedFn, manualSource, materializeLockKey, materializedRefreshFunctionName, migrationName, namespaceConfigSchema, namespaceSchema, nonNullFirst, normalizeCanonical, normalizeIr, normalizeSnapshot, orderIrSchema, ormMigrationExportSchema, ormSchemaName, overlayWinsUnion, overrideRowMutation, parseCanonicalDefinition, parseDefinitionSources, parseOrmMigrationExport, parsePredicate, parseTableFile, parsedPredicateSchema, pendingKitStatements, pendingKitStatementsV2, planCommittedMigrationV2, planMigration, planMigrationV2, policyExpressionSchema, policyVariantSchema, predIrSchema, predicateOperands, prefetchPlanIrSchema, prefetchSource, priority, quoteIdentifier, quoteLiteral, readMigrationSql, readMigrationWorkspace, readSnapshot, rebaseViolations, referencedCapabilities, referencedColumns, renderMigrationSql, renderOrmMigrationExport, requiredArtifactIrVersion, resolvedMetaIrSchema, resolvedSourceIrSchema, revertOverlayMutation, rewriteSyncedSourceRelationsInViewQuery, roleExpression, rootIrSchema, securityStatements, selectionIrSchema, serializeNamespace, sqlTypeName, stableJson, syncedBackingRevokeStatements, syncedContractSurface, syncedTables, syncedViewDropStatements, syncedViewStatements, t, tableFileSchema, tableMetaIrSchema, tableReferenceSchema, toMany, toOne, toPlatformUser, typesInterfaceName, validateViewCatalogStatement, valueIrSchema, variableDefaultIrSchema, variableIrSchema, verifyRebaseIsNoop, walkPredicate, writeMigration };
|
|
8039
|
+
export { ACTION_REQUIRES_DENIED_SQLSTATE, ACTION_VALIDATES_FAILED_SQLSTATE, AUDIT_ACTIONS, CANONICAL_FORMAT, CAPABILITIES_CLAIM_KEY, CAPABILITY_KEY_PATTERN, CELLS_FIELD, CELL_PARTS, CELL_PART_SUFFIX, CLAIMS_CONTRACT, CLAIMS_SETTING, CLAIM_KEY_PATTERN, CMP_OPS, COLUMN_BRAND, COLUMN_DEFAULT_FNS, COLUMN_IDENTIFIER_PATTERN, COLUMN_TYPES2 as COLUMN_TYPES, COMPARE_OPS, COMPOSED_IR_VERSION, CONSUMER_ROLE_SUFFIX, ColumnBuilder, DATE_BUCKETS, DEFINITION_FILE_EXTENSIONS, ENUM_BRAND, GUARDED_IR_VERSION, HASH_DOMAIN, IDENTIFIER_PATTERN, IDENTIFIER_RE, IMPORT_CONFLICT_MODES, INPUT_REF_KINDS, IR_VERSION, IrVersionError, LEGACY_IR_VERSION, LINK_BRAND, MATERIALIZE_ACTOR, MATERIALIZE_CLAIMS_JSON, MAX_DEFINITION_FILE_BYTES, MAX_GENERATED_SDL_BYTES, MAX_IDENTIFIER_LENGTH, MAX_TRACKED_COLUMN_NAME, MIGRATIONS_DIRNAME, NAMESPACE_CONFIG_BRAND, NAMESPACE_CONFIG_FILE, NAMESPACE_CONFIG_FILENAMES, NAMESPACE_PATTERN, ORM_DATA_TOKEN_HEADER, ORM_MIGRATION_EXPORT_FILE, ORM_MIGRATION_EXPORT_MAX_BYTES, ORM_REGISTRATION_MAX_BYTES, ORM_SCHEMA_PREFIX, ORM_SERVICE_DATA_TOKEN_HEADER, ORM_TO_PIPELINE_TYPE, OrmDefinitionError, PredicateParseError, QUALIFIED_TABLE_PATTERN, RESERVED_COLUMN_NAMES, RESERVED_TABLE_NAMES, ROLES_CLAIM_KEY, RUNTIME_SQL_IR_VERSION, SCHEMA_DIR, SQL_IR_VERSION, STATEMENT_KINDS, SYNCED_CONTRACT_HASH_DOMAIN, SYNCED_SOURCE_PATTERN, SYNCED_VIEW_EXECUTION_OWNER_ROLE, TABLE_BRAND, VIEW_BRAND, actionFunctionName, actionFunctionStatements, actionParamArg, actionParamOrder, actionParamSchema, actionPrepareChecks, actionSchema, actionWriteTargets, actionsInterfaceName, activeAuthoringContext, applyMigrationHints, applyMigrationHintsV2, beginAuthoringContext, buildActionParamsSchema, buildDrizzleEntities, buildDrizzleEntitiesV2, buildNamespaceDefinition, buildRebaseJournalEntry, canonicalActionSchema, canonicalColumnSchema, canonicalDefinitionSchema, canonicalEnumSchema, canonicalIndexSchema, canonicalLinkSchema, canonicalPoliciesSchema, canonicalRelations, canonicalTableSchema, canonicalViewSchema, canonicalizeDefinition, capabilityExpression, castParts, cellColumnName, checksumOf, claimExpression, classifyDestructive, columnSchema, columnTypeSchema2 as columnTypeSchema, compileActionRequires, compileActionStatements, compileNamespace, compilePredIrToSql, compilePredicate, compiledArtifactSchema, compiledOperationSchema, computeCanonicalHash, computeDefinitionHash, computeSyncedContractHash, consumerRoleName, convertV1DefinitionToCanonical, createOrmMigrationExport, custom, declaredCapabilityKeys, defineEnum, defineNamespace, defineOperation, defineResolvedTable, defineSyncedTable, defineTable, defineView, deriveColumnName, diffCommand, diffStatements, emptyJournal, emptySnapshot, endAuthoringContext, etlSource, extractNamedParams, extractOrmSyncedTableContract, generateNamespaceTypes, generateSnapshot, generateSnapshotV2, highest, identifierSchema, importSpecSchema, indexByHash, isColumnBuilder, isEnumRef, isFunctionDefault, isOwnedStatement, isOwnedStatementV2, isQualifiedReference, isSeqLink, isSeqNamespaceConfig, isSeqTable, isSeqView, latest, limitIrSchema, loadCommittedMigrations, loadCommittedTipMigration, loadNamespaceDefinition, loadNamespaceFromDir, lowest, managedFn, manualSource, materializeLockKey, materializedRefreshFunctionName, migrationName, namespaceConfigSchema, namespaceSchema, nonNullFirst, normalizeCanonical, normalizeIr, normalizeSnapshot, objectRef, ontologyContractSchema, orderIrSchema, ormMigrationExportSchema, ormSchemaName, overlayWinsUnion, overrideRowMutation, parseCanonicalDefinition, parseDefinitionSources, parseOrmMigrationExport, parsePredicate, parseTableFile, parsedPredicateSchema, pendingKitStatements, pendingKitStatementsV2, planCommittedMigrationV2, planMigration, planMigrationV2, planTableImport, policyExpressionSchema, policyVariantSchema, predIrSchema, predicateOperands, prefetchPlanIrSchema, prefetchSource, priority, quoteIdentifier, quoteLiteral, readMigrationSql, readMigrationWorkspace, readOntologyContract, readSnapshot, rebaseViolations, referencedCapabilities, referencedColumns, renderMigrationSql, renderOrmMigrationExport, requiredArtifactIrVersion, resolvedMetaIrSchema, resolvedSourceIrSchema, revertOverlayMutation, rewriteSyncedSourceRelationsInViewQuery, roleExpression, rootIrSchema, securityStatements, selectionIrSchema, serializeNamespace, sqlTypeName, stableJson, syncedBackingRevokeStatements, syncedContractSurface, syncedTables, syncedViewDropStatements, syncedViewStatements, t, tableFileSchema, tableMetaIrSchema, tableReferenceSchema, toMany, toOne, toPlatformUser, typesInterfaceName, validateNamespaceCatalog, validateViewCatalogStatement, valueIrSchema, variableDefaultIrSchema, variableIrSchema, verifyRebaseIsNoop, walkPredicate, writeMigration };
|
|
7279
8040
|
//# sourceMappingURL=index.js.map
|
|
7280
8041
|
//# sourceMappingURL=index.js.map
|