@sequenceholdings/orm 0.1.3 → 2.1.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 +108 -38
- package/dist/authoring.cjs +128 -16
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +21 -4
- package/dist/authoring.d.ts +21 -4
- package/dist/authoring.js +128 -16
- package/dist/authoring.js.map +1 -1
- package/dist/canonical.cjs +83 -9
- package/dist/canonical.cjs.map +1 -1
- package/dist/canonical.d.cts +47 -1
- package/dist/canonical.d.ts +47 -1
- package/dist/canonical.js +83 -10
- package/dist/canonical.js.map +1 -1
- package/dist/cli.cjs +816 -134
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +818 -136
- package/dist/cli.js.map +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/compiler.cjs +488 -96
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.d.cts +34 -2
- package/dist/compiler.d.ts +34 -2
- package/dist/compiler.js +489 -97
- package/dist/compiler.js.map +1 -1
- package/dist/index.cjs +420 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +266 -9
- package/dist/index.d.ts +266 -9
- package/dist/index.js +410 -73
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +146 -12
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +161 -5
- package/dist/ir.d.ts +161 -5
- package/dist/ir.js +144 -13
- package/dist/ir.js.map +1 -1
- package/dist/{naming-CTvWSRTf.d.ts → naming-DCn94LJ4.d.ts} +15 -1
- package/dist/{naming-DRyinU4u.d.cts → naming-DPglwzkB.d.cts} +15 -1
- package/dist/predicate-v2.cjs +136 -11
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.d.cts +3 -0
- package/dist/predicate-v2.d.ts +3 -0
- package/dist/predicate-v2.js +136 -11
- package/dist/predicate-v2.js.map +1 -1
- package/dist/sdl.cjs +460 -13
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.d.cts +1 -1
- package/dist/sdl.d.ts +1 -1
- package/dist/sdl.js +459 -14
- package/dist/sdl.js.map +1 -1
- package/package.json +6 -4
- package/src/codegen-gql/preset.cjs +64 -0
- package/src/codegen-gql/server-fns.cjs +11 -6
- package/templates/orm-namespace/codegen.preset.cjs +17 -0
- package/templates/orm-namespace/codegen.ts +1 -1
- package/templates/orm-namespace/package.json +3 -0
- package/templates/orm-namespace/schema/tables.ts +1 -1
- package/templates/orm-namespace/sequence.config.ts +1 -1
package/dist/index.cjs
CHANGED
|
@@ -36,6 +36,7 @@ var ColumnBuilder = class {
|
|
|
36
36
|
hasDefault: false,
|
|
37
37
|
defaultValue: void 0,
|
|
38
38
|
defaultFn: void 0,
|
|
39
|
+
onUpdateFn: void 0,
|
|
39
40
|
description: void 0,
|
|
40
41
|
renamedFrom: void 0,
|
|
41
42
|
cast: void 0,
|
|
@@ -71,6 +72,15 @@ var ColumnBuilder = class {
|
|
|
71
72
|
this.state.defaultFn = void 0;
|
|
72
73
|
return this;
|
|
73
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Stamp this column with Postgres `now()` on every UPDATE. Only valid on
|
|
77
|
+
* timestamptz/date; the compiler drops it from SetInput so callers cannot
|
|
78
|
+
* supply a value.
|
|
79
|
+
*/
|
|
80
|
+
onUpdate(value) {
|
|
81
|
+
this.state.onUpdateFn = value;
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
74
84
|
description(text4) {
|
|
75
85
|
this.state.description = text4;
|
|
76
86
|
return this;
|
|
@@ -114,6 +124,7 @@ var ColumnBuilder = class {
|
|
|
114
124
|
};
|
|
115
125
|
if (this.state.hasDefault) column.default = this.state.defaultValue ?? null;
|
|
116
126
|
if (this.state.defaultFn !== void 0) column.defaultFn = this.state.defaultFn;
|
|
127
|
+
if (this.state.onUpdateFn !== void 0) column.onUpdateFn = this.state.onUpdateFn;
|
|
117
128
|
if (this.state.description !== void 0) column.description = this.state.description;
|
|
118
129
|
if (this.state.renamedFrom !== void 0) column.renamedFrom = this.state.renamedFrom;
|
|
119
130
|
if (this.state.cast !== void 0) column.cast = this.state.cast;
|
|
@@ -141,6 +152,7 @@ var t = {
|
|
|
141
152
|
// src/constants.ts
|
|
142
153
|
var NAMESPACE_PATTERN = /^[a-z][a-z0-9_]{0,40}$/;
|
|
143
154
|
var IDENTIFIER_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
155
|
+
var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
|
|
144
156
|
var MAX_IDENTIFIER_LENGTH = 63;
|
|
145
157
|
var ORM_DATA_TOKEN_HEADER = "x-sequence-data-token";
|
|
146
158
|
var ORM_SERVICE_DATA_TOKEN_HEADER = "x-sequence-data-token-service";
|
|
@@ -156,6 +168,8 @@ var DEFINITION_FILE_EXTENSIONS = [".yml", ".yaml"];
|
|
|
156
168
|
var NAMESPACE_CONFIG_FILENAMES = /* @__PURE__ */ new Set(["namespace.yaml", "namespace.yml"]);
|
|
157
169
|
var HASH_DOMAIN = "sequence-orm-definitions\0";
|
|
158
170
|
var MAX_DEFINITION_FILE_BYTES = 1024 * 1024;
|
|
171
|
+
var MAX_GENERATED_SDL_BYTES = 16 * 1024 * 1024;
|
|
172
|
+
var ORM_REGISTRATION_MAX_BYTES = 64 * 1024 * 1024;
|
|
159
173
|
var RESERVED_TABLE_NAMES = /* @__PURE__ */ new Set(["actions", "query"]);
|
|
160
174
|
var RESERVED_COLUMN_NAMES = /* @__PURE__ */ new Set(["select", "order", "limit", "offset"]);
|
|
161
175
|
|
|
@@ -264,10 +278,10 @@ function isSeqLink(value) {
|
|
|
264
278
|
function isSeqTable(value) {
|
|
265
279
|
return typeof value === "object" && value !== null && value.kind === TABLE_BRAND;
|
|
266
280
|
}
|
|
267
|
-
function assertIdentifier({ value, what, file }) {
|
|
268
|
-
if (!
|
|
281
|
+
function assertIdentifier({ value, what, file, pattern = IDENTIFIER_PATTERN }) {
|
|
282
|
+
if (!pattern.test(value) || value.length > MAX_IDENTIFIER_LENGTH) {
|
|
269
283
|
throw new OrmDefinitionError(
|
|
270
|
-
`${what} "${value}" must match ${
|
|
284
|
+
`${what} "${value}" must match ${pattern} and be at most ${MAX_IDENTIFIER_LENGTH} characters`,
|
|
271
285
|
file
|
|
272
286
|
);
|
|
273
287
|
}
|
|
@@ -288,7 +302,7 @@ function defineTable(name, columns, config = {}) {
|
|
|
288
302
|
);
|
|
289
303
|
}
|
|
290
304
|
const columnName = builder.columnNameFor(propertyName);
|
|
291
|
-
assertIdentifier({ value: columnName, what: `table "${name}": column "${propertyName}" column name`, file });
|
|
305
|
+
assertIdentifier({ value: columnName, what: `table "${name}": column "${propertyName}" column name`, file, pattern: COLUMN_IDENTIFIER_PATTERN });
|
|
292
306
|
const existing = seenColumnNames.get(columnName);
|
|
293
307
|
if (existing !== void 0) {
|
|
294
308
|
throw new OrmDefinitionError(
|
|
@@ -328,7 +342,8 @@ function defineSyncedTable(name, columnsOrConfig, maybeConfig) {
|
|
|
328
342
|
assertIdentifier({
|
|
329
343
|
value: columnName,
|
|
330
344
|
what: `synced table "${name}": column "${propertyName}" column name`,
|
|
331
|
-
file
|
|
345
|
+
file,
|
|
346
|
+
pattern: COLUMN_IDENTIFIER_PATTERN
|
|
332
347
|
});
|
|
333
348
|
const existing = seenColumnNames.get(columnName);
|
|
334
349
|
if (existing !== void 0) {
|
|
@@ -417,7 +432,7 @@ function defineView(name, columns, config) {
|
|
|
417
432
|
);
|
|
418
433
|
}
|
|
419
434
|
const columnName = builder.columnNameFor(propertyName);
|
|
420
|
-
if (!
|
|
435
|
+
if (!COLUMN_IDENTIFIER_PATTERN.test(columnName) || columnName.length > MAX_IDENTIFIER_LENGTH) {
|
|
421
436
|
throw new OrmDefinitionError(`view "${name}": column "${propertyName}" has invalid column name "${columnName}"`, file);
|
|
422
437
|
}
|
|
423
438
|
const existing = seenColumnNames.get(columnName);
|
|
@@ -866,6 +881,7 @@ function scalarColumnType(table, property, helper) {
|
|
|
866
881
|
var CANONICAL_FORMAT = 2;
|
|
867
882
|
var IDENTIFIER_RE = /^[a-z][a-z0-9_]*$/;
|
|
868
883
|
var identifier = zod.z.string().min(1).max(63).regex(IDENTIFIER_RE);
|
|
884
|
+
var columnIdentifier = zod.z.string().min(1).max(63).regex(COLUMN_IDENTIFIER_PATTERN);
|
|
869
885
|
var COLUMN_TYPES = [
|
|
870
886
|
"uuid",
|
|
871
887
|
"text",
|
|
@@ -886,18 +902,33 @@ var canonicalEnumSchema = zod.z.object({
|
|
|
886
902
|
var canonicalColumnSchema = zod.z.object({
|
|
887
903
|
/** Property name in TS (camelCase); columnName is the Postgres name. */
|
|
888
904
|
name: zod.z.string().min(1).max(63),
|
|
889
|
-
columnName:
|
|
905
|
+
columnName: columnIdentifier,
|
|
890
906
|
type: zod.z.union([columnTypeSchema, zod.z.object({ enum: identifier })]),
|
|
891
907
|
nullable: zod.z.boolean().default(true),
|
|
892
908
|
unique: zod.z.boolean().default(false),
|
|
893
909
|
/** Literal default, or one of the allowlisted SQL functions. */
|
|
894
910
|
default: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()]).optional(),
|
|
895
911
|
defaultFn: zod.z.enum(["gen_random_uuid()", "now()"]).optional(),
|
|
912
|
+
/**
|
|
913
|
+
* Stamp this column from the database clock on UPDATE (`now()`). The
|
|
914
|
+
* column stays insertable (defaults fill gaps) but is omitted from
|
|
915
|
+
* SetInput / settable so callers cannot backdate it.
|
|
916
|
+
*/
|
|
917
|
+
onUpdateFn: zod.z.enum(["now()"]).optional(),
|
|
896
918
|
description: zod.z.string().max(500).optional(),
|
|
897
919
|
/** Migration hints — same semantics as v1. */
|
|
898
|
-
renamedFrom:
|
|
920
|
+
renamedFrom: columnIdentifier.optional(),
|
|
899
921
|
cast: zod.z.string().max(200).optional(),
|
|
900
922
|
deprecated: zod.z.boolean().default(false)
|
|
923
|
+
}).superRefine((column, ctx) => {
|
|
924
|
+
if (column.onUpdateFn === void 0) return;
|
|
925
|
+
if (typeof column.type !== "string" || column.type !== "timestamptz" && column.type !== "date") {
|
|
926
|
+
ctx.addIssue({
|
|
927
|
+
code: "custom",
|
|
928
|
+
path: ["onUpdateFn"],
|
|
929
|
+
message: `onUpdateFn '${column.onUpdateFn}' is only valid on timestamptz/date columns`
|
|
930
|
+
});
|
|
931
|
+
}
|
|
901
932
|
});
|
|
902
933
|
var canonicalLinkSchema = zod.z.object({
|
|
903
934
|
name: zod.z.string().min(1).max(63),
|
|
@@ -905,15 +936,15 @@ var canonicalLinkSchema = zod.z.object({
|
|
|
905
936
|
/** Target table name (namespace-local). Absent for toPlatformUser. */
|
|
906
937
|
target: identifier.optional(),
|
|
907
938
|
/** FK column on the owning side (toOne/toPlatformUser: on this table; toMany: on the target). */
|
|
908
|
-
on:
|
|
939
|
+
on: columnIdentifier,
|
|
909
940
|
/** Referenced column on the target (default: target's PK / platform user `sub`). */
|
|
910
|
-
references:
|
|
941
|
+
references: columnIdentifier.optional(),
|
|
911
942
|
/** enforce: true emits a real FK constraint (same-namespace toOne only). */
|
|
912
943
|
enforce: zod.z.boolean().default(false),
|
|
913
944
|
description: zod.z.string().max(500).optional()
|
|
914
945
|
});
|
|
915
946
|
var canonicalIndexSchema = zod.z.object({
|
|
916
|
-
columns: zod.z.array(
|
|
947
|
+
columns: zod.z.array(columnIdentifier).min(1),
|
|
917
948
|
unique: zod.z.boolean().default(false),
|
|
918
949
|
name: identifier.optional()
|
|
919
950
|
});
|
|
@@ -1161,6 +1192,13 @@ var canonicalCowMutationSchema = zod.z.object({
|
|
|
1161
1192
|
}
|
|
1162
1193
|
}
|
|
1163
1194
|
});
|
|
1195
|
+
var canonicalForeignKeySchema = zod.z.object({
|
|
1196
|
+
name: identifier,
|
|
1197
|
+
columns: zod.z.array(columnIdentifier).min(1).max(8),
|
|
1198
|
+
target: identifier,
|
|
1199
|
+
references: zod.z.array(columnIdentifier).min(1).max(8),
|
|
1200
|
+
deferrable: zod.z.enum(["initially-immediate", "initially-deferred"]).optional()
|
|
1201
|
+
}).strict();
|
|
1164
1202
|
var canonicalTableSchema = zod.z.object({
|
|
1165
1203
|
table: identifier,
|
|
1166
1204
|
description: zod.z.string().max(2e3).optional(),
|
|
@@ -1169,7 +1207,7 @@ var canonicalTableSchema = zod.z.object({
|
|
|
1169
1207
|
/** Synced/reflected read-only tables (`source: gold.*`) — v1 semantics. */
|
|
1170
1208
|
source: zod.z.string().regex(/^gold\.[a-z][a-z0-9_]*$/).optional(),
|
|
1171
1209
|
columns: zod.z.array(canonicalColumnSchema).default([]),
|
|
1172
|
-
primaryKey: zod.z.array(
|
|
1210
|
+
primaryKey: zod.z.array(columnIdentifier).min(1).max(8).optional(),
|
|
1173
1211
|
links: zod.z.array(canonicalLinkSchema).default([]),
|
|
1174
1212
|
indexes: zod.z.array(canonicalIndexSchema).default([]),
|
|
1175
1213
|
policies: canonicalPoliciesSchema.default({}),
|
|
@@ -1180,14 +1218,16 @@ var canonicalTableSchema = zod.z.object({
|
|
|
1180
1218
|
*/
|
|
1181
1219
|
actions: zod.z.array(canonicalActionSchema).default([]),
|
|
1182
1220
|
/** Optional (not defaulted) so pre-CoW canonical documents keep byte-identical hashes. */
|
|
1183
|
-
cowMutations: zod.z.array(canonicalCowMutationSchema).optional()
|
|
1221
|
+
cowMutations: zod.z.array(canonicalCowMutationSchema).optional(),
|
|
1222
|
+
/** Omitted by old definitions, preserving their existing content hashes. */
|
|
1223
|
+
foreignKeys: zod.z.array(canonicalForeignKeySchema).max(100).optional()
|
|
1184
1224
|
});
|
|
1185
1225
|
var canonicalViewSchema = zod.z.object({
|
|
1186
1226
|
view: identifier,
|
|
1187
1227
|
description: zod.z.string().max(2e3).optional(),
|
|
1188
1228
|
columns: zod.z.array(canonicalColumnSchema).min(1),
|
|
1189
1229
|
/** Logical identity for GraphQL only; PostgreSQL views have no PK constraint. */
|
|
1190
|
-
primaryKey: zod.z.array(
|
|
1230
|
+
primaryKey: zod.z.array(columnIdentifier).min(1).max(8).optional(),
|
|
1191
1231
|
links: zod.z.array(canonicalLinkSchema).default([]),
|
|
1192
1232
|
policies: zod.z.object({ read: policyExpressionSchema }).strict(),
|
|
1193
1233
|
query: zod.z.string().min(1).max(1e5).refine(isSingleViewQuerySql, {
|
|
@@ -1200,6 +1240,9 @@ var canonicalViewSchema = zod.z.object({
|
|
|
1200
1240
|
if (column.default !== void 0 || column.defaultFn !== void 0) {
|
|
1201
1241
|
ctx.addIssue({ code: "custom", path: ["columns", index3], message: `view column '${column.name}' cannot declare a default` });
|
|
1202
1242
|
}
|
|
1243
|
+
if (column.onUpdateFn !== void 0) {
|
|
1244
|
+
ctx.addIssue({ code: "custom", path: ["columns", index3], message: `view column '${column.name}' cannot declare onUpdateFn` });
|
|
1245
|
+
}
|
|
1203
1246
|
if (column.unique) {
|
|
1204
1247
|
ctx.addIssue({ code: "custom", path: ["columns", index3], message: `view column '${column.name}' cannot declare unique` });
|
|
1205
1248
|
}
|
|
@@ -1233,6 +1276,45 @@ var canonicalDefinitionSchema = zod.z.object({
|
|
|
1233
1276
|
ctx.addIssue({ code: "custom", path: ["tables", index3, "table"], message: `duplicate relation '${table.table}'` });
|
|
1234
1277
|
}
|
|
1235
1278
|
seen.add(table.table);
|
|
1279
|
+
if (table.source !== void 0) {
|
|
1280
|
+
for (const [columnIndex, column] of table.columns.entries()) {
|
|
1281
|
+
if (column.onUpdateFn !== void 0) {
|
|
1282
|
+
ctx.addIssue({
|
|
1283
|
+
code: "custom",
|
|
1284
|
+
path: ["tables", index3, "columns", columnIndex],
|
|
1285
|
+
message: `synced table '${table.table}' column '${column.name}' cannot declare onUpdateFn`
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
const fkTables = new Map(definition.tables.map((table) => [table.table, table]));
|
|
1292
|
+
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
1293
|
+
const names = /* @__PURE__ */ new Set([
|
|
1294
|
+
...table.primaryKey?.length ? [`${table.table}_pkey`] : [],
|
|
1295
|
+
...table.columns.filter((column) => column.unique && !(table.primaryKey?.length === 1 && table.primaryKey[0] === column.columnName)).map((column) => `${table.table}_${column.columnName}_unique`),
|
|
1296
|
+
...table.links.filter((link) => link.enforce).map((link) => `fk_${table.table}_${link.name}`)
|
|
1297
|
+
]);
|
|
1298
|
+
for (const [index3, fk] of (table.foreignKeys ?? []).entries()) {
|
|
1299
|
+
const fail = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "foreignKeys", index3], message });
|
|
1300
|
+
if (names.has(fk.name)) fail(`duplicate foreign key '${fk.name}'`);
|
|
1301
|
+
names.add(fk.name);
|
|
1302
|
+
const target = fkTables.get(fk.target);
|
|
1303
|
+
if (table.source || !target || target.source) {
|
|
1304
|
+
fail("foreign keys must connect owned tables in the same namespace");
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1307
|
+
if (fk.columns.length !== fk.references.length) fail("foreign key columns and references must have the same number of columns");
|
|
1308
|
+
if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail("foreign key columns must not repeat");
|
|
1309
|
+
for (const [position, name] of fk.columns.entries()) {
|
|
1310
|
+
const sourceColumn = table.columns.find((column) => column.columnName === name);
|
|
1311
|
+
const targetColumn = target.columns.find((column) => column.columnName === fk.references[position]);
|
|
1312
|
+
if (!sourceColumn || !targetColumn) fail("foreign key references an unknown column");
|
|
1313
|
+
else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail("foreign key column types must match");
|
|
1314
|
+
}
|
|
1315
|
+
const uniqueKeys = [target.primaryKey ?? [], ...target.indexes.filter((index4) => index4.unique).map((index4) => index4.columns), ...target.columns.filter((column) => column.unique).map((column) => [column.columnName])];
|
|
1316
|
+
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");
|
|
1317
|
+
}
|
|
1236
1318
|
}
|
|
1237
1319
|
const tableByName = new Map(definition.tables.map((table) => [table.table, table]));
|
|
1238
1320
|
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
@@ -1368,12 +1450,13 @@ function normalizeCanonical(definition) {
|
|
|
1368
1450
|
...definition,
|
|
1369
1451
|
enums: sortByName(definition.enums),
|
|
1370
1452
|
tables: [...definition.tables].sort((a, b) => a.table.localeCompare(b.table)).map((t2) => {
|
|
1371
|
-
const { cowMutations, ...rest } = t2;
|
|
1453
|
+
const { cowMutations, foreignKeys, ...rest } = t2;
|
|
1372
1454
|
return {
|
|
1373
1455
|
...rest,
|
|
1374
1456
|
links: sortByName(t2.links),
|
|
1375
1457
|
indexes: [...t2.indexes].sort((a, b) => (a.name ?? a.columns.join("_")).localeCompare(b.name ?? b.columns.join("_"))),
|
|
1376
1458
|
actions: sortByName(t2.actions),
|
|
1459
|
+
...foreignKeys?.length ? { foreignKeys: sortByName(foreignKeys) } : {},
|
|
1377
1460
|
// Omitted entirely when absent/empty so pre-CoW documents hash unchanged.
|
|
1378
1461
|
...cowMutations !== void 0 && cowMutations.length > 0 ? { cowMutations: sortByName(cowMutations) } : {}
|
|
1379
1462
|
};
|
|
@@ -1647,6 +1730,22 @@ function buildCanonicalTable({ table, relationsByName, enumsByName }) {
|
|
|
1647
1730
|
} else if (columnLevelPk.length > 0) {
|
|
1648
1731
|
candidate.primaryKey = columnLevelPk;
|
|
1649
1732
|
}
|
|
1733
|
+
if (table.config.foreignKeys?.length) {
|
|
1734
|
+
if (table.source) throw new OrmDefinitionError(`synced table "${table.tableName}" cannot enforce foreign keys`, table.file);
|
|
1735
|
+
candidate.foreignKeys = table.config.foreignKeys.map((fk) => {
|
|
1736
|
+
const target = resolveRelationRef({ ref: fk.target, fail: (message) => {
|
|
1737
|
+
throw new OrmDefinitionError(message, table.file);
|
|
1738
|
+
} });
|
|
1739
|
+
if (!isSeqTable(target) || !relationsByName.has(target.tableName)) throw new OrmDefinitionError("foreign key target must be a table in this namespace", table.file);
|
|
1740
|
+
return {
|
|
1741
|
+
name: fk.name,
|
|
1742
|
+
columns: fk.columns.map((ref) => resolveColumnRef({ relation: table, ref, role: `foreign key "${fk.name}" column` })),
|
|
1743
|
+
target: target.tableName,
|
|
1744
|
+
references: fk.references.map((ref) => resolveColumnRef({ relation: target, ref, role: `foreign key "${fk.name}" reference` })),
|
|
1745
|
+
...fk.deferrable ? { deferrable: fk.deferrable } : {}
|
|
1746
|
+
};
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1650
1749
|
for (const index3 of table.config.indexes ?? []) {
|
|
1651
1750
|
const entry = {
|
|
1652
1751
|
columns: index3.columns.map((ref) => resolveColumnRef({ relation: table, ref, role: "index" }))
|
|
@@ -1699,6 +1798,12 @@ function buildCanonicalView({
|
|
|
1699
1798
|
if (column.default !== void 0 || column.defaultFn !== void 0) {
|
|
1700
1799
|
throw new OrmDefinitionError(`view "${view.viewName}": column "${propertyName}" cannot declare a default`, view.file);
|
|
1701
1800
|
}
|
|
1801
|
+
if (column.onUpdateFn !== void 0) {
|
|
1802
|
+
throw new OrmDefinitionError(
|
|
1803
|
+
`view "${view.viewName}": column "${propertyName}" cannot declare onUpdateFn`,
|
|
1804
|
+
view.file
|
|
1805
|
+
);
|
|
1806
|
+
}
|
|
1702
1807
|
if (column.unique) {
|
|
1703
1808
|
throw new OrmDefinitionError(`view "${view.viewName}": column "${propertyName}" cannot declare unique`, view.file);
|
|
1704
1809
|
}
|
|
@@ -1957,7 +2062,16 @@ async function loadNamespaceFromDir(dir, options = {}) {
|
|
|
1957
2062
|
const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)), {
|
|
1958
2063
|
interopDefault: true,
|
|
1959
2064
|
fsCache: false,
|
|
1960
|
-
...options.ormModulePath !== void 0 ? {
|
|
2065
|
+
...options.ormModulePath !== void 0 ? {
|
|
2066
|
+
alias: {
|
|
2067
|
+
// The public /drizzle barrel re-exports this pinned dependency.
|
|
2068
|
+
// Explicitly map it before the root alias: otherwise jiti appends
|
|
2069
|
+
// "/drizzle" to the overridden module's filename.
|
|
2070
|
+
"@sequenceholdings/orm/drizzle": module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))).resolve("drizzle-orm"),
|
|
2071
|
+
"@sequenceholdings/orm": options.ormModulePath,
|
|
2072
|
+
"@sequenceholdings/orm/authoring": options.ormModulePath
|
|
2073
|
+
}
|
|
2074
|
+
} : {}
|
|
1961
2075
|
});
|
|
1962
2076
|
const context = beginAuthoringContext();
|
|
1963
2077
|
try {
|
|
@@ -2516,8 +2630,8 @@ var actionSchema = zod.z.strictObject({
|
|
|
2516
2630
|
}
|
|
2517
2631
|
}
|
|
2518
2632
|
});
|
|
2519
|
-
function extractNamedParams(
|
|
2520
|
-
const withoutStrings =
|
|
2633
|
+
function extractNamedParams(sql5) {
|
|
2634
|
+
const withoutStrings = sql5.replace(QUOTED_STRING_PATTERN, "''");
|
|
2521
2635
|
const names = [...withoutStrings.matchAll(NAMED_PARAM_PATTERN)].map((match) => match[1]);
|
|
2522
2636
|
return [...new Set(names)];
|
|
2523
2637
|
}
|
|
@@ -2674,8 +2788,8 @@ var actionParamArg = (name) => `p_${name}`;
|
|
|
2674
2788
|
function compileActionStatements(action, renderParam = actionParamArg) {
|
|
2675
2789
|
return actionStatementSources(action.executes).map((statement) => compileStatement(statement, renderParam));
|
|
2676
2790
|
}
|
|
2677
|
-
function compileStatement(
|
|
2678
|
-
const compiled =
|
|
2791
|
+
function compileStatement(sql5, renderParam) {
|
|
2792
|
+
const compiled = sql5.replace(TOKEN_PATTERN, (token, claimKey, capabilityKey, paramName) => {
|
|
2679
2793
|
if (claimKey !== void 0) {
|
|
2680
2794
|
if (!CLAIM_KEY_PATTERN.test(claimKey)) throw new OrmDefinitionError(`invalid claim key in executes: ${token}`);
|
|
2681
2795
|
return claimExpression(claimKey);
|
|
@@ -2840,8 +2954,8 @@ function actionPrepareChecks(definition) {
|
|
|
2840
2954
|
const order = actionParamOrder(action);
|
|
2841
2955
|
const signature = order.length > 0 ? ` (${order.map((param) => sqlTypeName(action.params[param].type)).join(", ")})` : "";
|
|
2842
2956
|
const compiled = compileActionStatements(action, (param) => `$${order.indexOf(param) + 1}`);
|
|
2843
|
-
compiled.forEach((
|
|
2844
|
-
const checked = action.returns && index3 === compiled.length - 1 ? returnProjection(action.returns,
|
|
2957
|
+
compiled.forEach((sql5, index3) => {
|
|
2958
|
+
const checked = action.returns && index3 === compiled.length - 1 ? returnProjection(action.returns, sql5) : sql5;
|
|
2845
2959
|
statements.push(`PREPARE ${quoteIdentifier(`orm_check_${name}_${index3}`)}${signature} AS ${checked}`);
|
|
2846
2960
|
});
|
|
2847
2961
|
}
|
|
@@ -3295,7 +3409,8 @@ function syncedViewStatements({
|
|
|
3295
3409
|
readPolicyIrByTable,
|
|
3296
3410
|
columnNamesByTable,
|
|
3297
3411
|
columnTypesByTable,
|
|
3298
|
-
executionOwnerRole = SYNCED_VIEW_EXECUTION_OWNER_ROLE
|
|
3412
|
+
executionOwnerRole = SYNCED_VIEW_EXECUTION_OWNER_ROLE,
|
|
3413
|
+
backingPrivileges = "grant"
|
|
3299
3414
|
}) {
|
|
3300
3415
|
const schema = quoteIdentifier(ormSchemaName(definition.namespace));
|
|
3301
3416
|
const role = quoteIdentifier(consumerRoleName(definition.namespace));
|
|
@@ -3329,8 +3444,10 @@ function syncedViewStatements({
|
|
|
3329
3444
|
columnTypes,
|
|
3330
3445
|
columnNames: columnNamesByTable?.get(table.table)
|
|
3331
3446
|
})}` : readPolicy ? ` WHERE ${compilePredicate(readPolicy.ast, narrowToV1ColumnTypes(columnTypes))}` : " WHERE false";
|
|
3332
|
-
|
|
3333
|
-
|
|
3447
|
+
if (backingPrivileges === "grant") {
|
|
3448
|
+
statements.push(`GRANT USAGE ON SCHEMA ${quoteIdentifier(physical.schema)} TO ${owner}`);
|
|
3449
|
+
statements.push(`GRANT SELECT ON ${physicalTarget} TO ${owner}`);
|
|
3450
|
+
}
|
|
3334
3451
|
statements.push(
|
|
3335
3452
|
`CREATE OR REPLACE VIEW ${viewName} AS SELECT * FROM ${physicalTarget}${whereClause}`
|
|
3336
3453
|
);
|
|
@@ -3619,7 +3736,7 @@ var FACTORIES2 = {
|
|
|
3619
3736
|
var SCHEMA_IMPORT_KEY2 = "$schema";
|
|
3620
3737
|
var enumImportKey = (name) => `$enum_${name}`;
|
|
3621
3738
|
var columnTablesV2 = (definition) => definition.tables.filter((table) => !table.source);
|
|
3622
|
-
function buildDrizzleEntitiesV2(definition) {
|
|
3739
|
+
function buildDrizzleEntitiesV2(definition, options = {}) {
|
|
3623
3740
|
const schemaName = ormSchemaName(definition.namespace);
|
|
3624
3741
|
const schema = pgCore.pgSchema(schemaName);
|
|
3625
3742
|
const imports = { [SCHEMA_IMPORT_KEY2]: schema };
|
|
@@ -3634,11 +3751,19 @@ function buildDrizzleEntitiesV2(definition) {
|
|
|
3634
3751
|
}
|
|
3635
3752
|
const byName = new Map(definition.tables.map((table) => [table.table, table]));
|
|
3636
3753
|
for (const table of sortByEnforcedLinks2(columnTablesV2(definition))) {
|
|
3637
|
-
const drizzleTable = buildTable2(schema, table, built, enums, byName);
|
|
3754
|
+
const drizzleTable = buildTable2(schema, table, built, enums, byName, options.introspection ?? false);
|
|
3638
3755
|
built.set(table.table, drizzleTable);
|
|
3639
3756
|
imports[table.table] = drizzleTable;
|
|
3640
3757
|
tables[table.table] = drizzleTable;
|
|
3641
3758
|
}
|
|
3759
|
+
if (options.introspection) {
|
|
3760
|
+
for (const table of definition.tables.filter((table2) => table2.source)) {
|
|
3761
|
+
const columns = Object.fromEntries(
|
|
3762
|
+
table.columns.map((column) => [column.columnName, buildViewColumnV2({ view: table.table, column, enums })])
|
|
3763
|
+
);
|
|
3764
|
+
imports[table.table] = schema.view(table.table, columns).existing();
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3642
3767
|
for (const view of definition.views ?? []) {
|
|
3643
3768
|
const columns = Object.fromEntries(
|
|
3644
3769
|
view.columns.map((column) => [column.columnName, buildViewColumnV2({ view: view.view, column, enums })])
|
|
@@ -3706,18 +3831,18 @@ function rewriteSyncedSourceRelationsInViewQuery({
|
|
|
3706
3831
|
return rewritten;
|
|
3707
3832
|
});
|
|
3708
3833
|
}
|
|
3709
|
-
function transformSqlOutsideLiterals(
|
|
3834
|
+
function transformSqlOutsideLiterals(sql5, transform) {
|
|
3710
3835
|
const out = [];
|
|
3711
3836
|
let state = { kind: "normal" };
|
|
3712
3837
|
let i = 0;
|
|
3713
3838
|
let codeStart = 0;
|
|
3714
3839
|
const emitCode = (end) => {
|
|
3715
|
-
if (end > codeStart) out.push(transform(
|
|
3840
|
+
if (end > codeStart) out.push(transform(sql5.slice(codeStart, end)));
|
|
3716
3841
|
codeStart = end;
|
|
3717
3842
|
};
|
|
3718
|
-
while (i <
|
|
3719
|
-
const c =
|
|
3720
|
-
const next = i + 1 <
|
|
3843
|
+
while (i < sql5.length) {
|
|
3844
|
+
const c = sql5[i];
|
|
3845
|
+
const next = i + 1 < sql5.length ? sql5[i + 1] : "";
|
|
3721
3846
|
switch (state.kind) {
|
|
3722
3847
|
case "normal": {
|
|
3723
3848
|
if (c === "-" && next === "-") {
|
|
@@ -3737,7 +3862,7 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3737
3862
|
break;
|
|
3738
3863
|
}
|
|
3739
3864
|
if ((c === "E" || c === "e") && next === "'") {
|
|
3740
|
-
const prev = i > 0 ?
|
|
3865
|
+
const prev = i > 0 ? sql5[i - 1] : "";
|
|
3741
3866
|
if (!/[A-Za-z0-9_]/.test(prev)) {
|
|
3742
3867
|
emitCode(i);
|
|
3743
3868
|
state = { kind: "e-single" };
|
|
@@ -3756,9 +3881,9 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3756
3881
|
break;
|
|
3757
3882
|
}
|
|
3758
3883
|
if (c === "$") {
|
|
3759
|
-
const prev = i > 0 ?
|
|
3884
|
+
const prev = i > 0 ? sql5[i - 1] : "";
|
|
3760
3885
|
if (!/[A-Za-z0-9_$]/.test(prev)) {
|
|
3761
|
-
const tagMatch = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(
|
|
3886
|
+
const tagMatch = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(sql5.slice(i));
|
|
3762
3887
|
if (tagMatch) {
|
|
3763
3888
|
emitCode(i);
|
|
3764
3889
|
state = { kind: "dollar", tag: tagMatch[1] ?? "" };
|
|
@@ -3790,8 +3915,8 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3790
3915
|
break;
|
|
3791
3916
|
}
|
|
3792
3917
|
case "e-single": {
|
|
3793
|
-
if (c === "\\" && i + 1 <
|
|
3794
|
-
out.push(c,
|
|
3918
|
+
if (c === "\\" && i + 1 < sql5.length) {
|
|
3919
|
+
out.push(c, sql5[i + 1]);
|
|
3795
3920
|
i += 2;
|
|
3796
3921
|
break;
|
|
3797
3922
|
}
|
|
@@ -3813,7 +3938,7 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3813
3938
|
}
|
|
3814
3939
|
case "dollar": {
|
|
3815
3940
|
const closer = `$${state.tag}$`;
|
|
3816
|
-
if (
|
|
3941
|
+
if (sql5.startsWith(closer, i)) {
|
|
3817
3942
|
out.push(closer);
|
|
3818
3943
|
state = { kind: "normal" };
|
|
3819
3944
|
i += closer.length;
|
|
@@ -3857,15 +3982,15 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3857
3982
|
}
|
|
3858
3983
|
}
|
|
3859
3984
|
}
|
|
3860
|
-
emitCode(
|
|
3985
|
+
emitCode(sql5.length);
|
|
3861
3986
|
return out.join("");
|
|
3862
3987
|
}
|
|
3863
3988
|
function escapeRegExp(value) {
|
|
3864
3989
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3865
3990
|
}
|
|
3866
|
-
function sqlReferencesQualifiedRelation(
|
|
3991
|
+
function sqlReferencesQualifiedRelation(sql5, sourceSchema, sourceName) {
|
|
3867
3992
|
let referenced = false;
|
|
3868
|
-
transformSqlOutsideLiterals(
|
|
3993
|
+
transformSqlOutsideLiterals(sql5, (segment) => {
|
|
3869
3994
|
if (qualifiedRelationPattern(sourceSchema, sourceName).test(segment)) referenced = true;
|
|
3870
3995
|
return segment;
|
|
3871
3996
|
});
|
|
@@ -3876,15 +4001,16 @@ function qualifiedRelationPattern(sourceSchema, sourceName) {
|
|
|
3876
4001
|
const namePattern = `(?:"${escapeRegExp(sourceName)}"|${escapeRegExp(sourceName)})`;
|
|
3877
4002
|
return new RegExp(`(?<![\\w."])${schemaPattern}\\.${namePattern}(?![\\w"])`, "g");
|
|
3878
4003
|
}
|
|
3879
|
-
function replaceQualifiedRelation(
|
|
3880
|
-
return
|
|
4004
|
+
function replaceQualifiedRelation(sql5, sourceSchema, sourceName, boundSchema, boundTable) {
|
|
4005
|
+
return sql5.replace(qualifiedRelationPattern(sourceSchema, sourceName), `"${boundSchema}"."${boundTable}"`);
|
|
3881
4006
|
}
|
|
3882
4007
|
function generateSnapshotV2(definition, previousId3) {
|
|
3883
4008
|
const { imports, schemaName } = buildDrizzleEntitiesV2(definition);
|
|
3884
4009
|
const snapshot = api.generateDrizzleJson(imports, previousId3, [schemaName]);
|
|
3885
|
-
if ((definition.views?.length ?? 0) > 0) {
|
|
4010
|
+
if ((definition.views?.length ?? 0) > 0 || definition.tables.some((table) => table.foreignKeys?.length)) {
|
|
3886
4011
|
const extended = snapshot;
|
|
3887
4012
|
extended._sequenceOrm = {
|
|
4013
|
+
...definition.tables.some((table) => table.foreignKeys?.length) ? { foreignKeys: Object.fromEntries(definition.tables.flatMap((table) => (table.foreignKeys ?? []).map((fk) => [`${table.table}.${fk.name}`, structuredClone(fk)]))) } : {},
|
|
3888
4014
|
viewDependencies: Object.fromEntries(
|
|
3889
4015
|
(definition.views ?? []).map((view) => [view.view, [...view.dependencies].sort()])
|
|
3890
4016
|
)
|
|
@@ -3902,7 +4028,7 @@ function buildViewColumnV2({
|
|
|
3902
4028
|
if (!column.nullable) builder = builder.notNull();
|
|
3903
4029
|
return builder;
|
|
3904
4030
|
}
|
|
3905
|
-
function buildTable2(schema, table, built, enums, byName) {
|
|
4031
|
+
function buildTable2(schema, table, built, enums, byName, introspection) {
|
|
3906
4032
|
const primaryKeyColumns = table.primaryKey ?? [];
|
|
3907
4033
|
if (primaryKeyColumns.length === 0) {
|
|
3908
4034
|
throw new OrmDefinitionError(`table '${table.table}' declares no primaryKey`);
|
|
@@ -3918,13 +4044,22 @@ function buildTable2(schema, table, built, enums, byName) {
|
|
|
3918
4044
|
const columns = Object.fromEntries(
|
|
3919
4045
|
table.columns.map((column) => [
|
|
3920
4046
|
column.columnName,
|
|
3921
|
-
buildColumnV2({ table: table.table, column, singlePrimaryKey, compositeMembers, enums })
|
|
4047
|
+
buildColumnV2({ table: table.table, column, singlePrimaryKey, compositeMembers, enums, introspection })
|
|
3922
4048
|
])
|
|
3923
4049
|
);
|
|
3924
4050
|
const drizzleTable = schema.table(table.table, columns, (self) => [
|
|
3925
4051
|
...buildCompositePrimaryKey(table, primaryKeyColumns, self),
|
|
3926
4052
|
...buildIndexes2(table, self),
|
|
3927
|
-
...buildForeignKeys2(table, self, built, byName)
|
|
4053
|
+
...buildForeignKeys2(table, self, built, byName),
|
|
4054
|
+
...(table.foreignKeys ?? []).map((fk) => {
|
|
4055
|
+
const target = fk.target === table.table ? self : built.get(fk.target);
|
|
4056
|
+
if (!target) throw new OrmDefinitionError(`foreign key '${fk.name}' has no target '${fk.target}'`);
|
|
4057
|
+
return pgCore.foreignKey({
|
|
4058
|
+
name: fk.name,
|
|
4059
|
+
columns: fk.columns.map((column) => self[column]),
|
|
4060
|
+
foreignColumns: fk.references.map((column) => target[column])
|
|
4061
|
+
});
|
|
4062
|
+
})
|
|
3928
4063
|
]);
|
|
3929
4064
|
return drizzleTable.enableRLS();
|
|
3930
4065
|
}
|
|
@@ -3941,7 +4076,8 @@ function buildColumnV2({
|
|
|
3941
4076
|
column,
|
|
3942
4077
|
singlePrimaryKey,
|
|
3943
4078
|
compositeMembers,
|
|
3944
|
-
enums
|
|
4079
|
+
enums,
|
|
4080
|
+
introspection
|
|
3945
4081
|
}) {
|
|
3946
4082
|
const factory = resolveColumnFactory(table, column, enums);
|
|
3947
4083
|
let builder = factory(column.columnName);
|
|
@@ -3962,7 +4098,7 @@ function buildColumnV2({
|
|
|
3962
4098
|
}
|
|
3963
4099
|
builder = builder.default(drizzleOrm.sql.raw(column.defaultFn));
|
|
3964
4100
|
} else if (column.default !== void 0 && column.default !== null) {
|
|
3965
|
-
builder = builder.default(column.default);
|
|
4101
|
+
builder = introspection && column.type === "jsonb" && typeof column.default === "string" ? builder.default(drizzleOrm.sql.raw(`${quoteLiteral(column.default)}::jsonb`)) : builder.default(column.default);
|
|
3966
4102
|
}
|
|
3967
4103
|
return builder;
|
|
3968
4104
|
}
|
|
@@ -4147,7 +4283,8 @@ async function planMigrationV2({
|
|
|
4147
4283
|
assertValidViewDependencies(canonical);
|
|
4148
4284
|
const hints = applyMigrationHintsV2(previous, canonical);
|
|
4149
4285
|
const snapshot = generateSnapshotV2(canonical, previousId(previous));
|
|
4150
|
-
const
|
|
4286
|
+
const generatedBaseDdl = await diffStatements(withoutViews(hints.adjusted), withoutViews(snapshot));
|
|
4287
|
+
const baseDdl = orderForeignKeyAdditions(canonical, generatedBaseDdl);
|
|
4151
4288
|
const previousViews = snapshotViews(hints.adjusted);
|
|
4152
4289
|
const currentViews = snapshotViews(snapshot);
|
|
4153
4290
|
const previousDependencies = snapshotViewDependencies(hints.adjusted);
|
|
@@ -4184,6 +4321,7 @@ async function planMigrationV2({
|
|
|
4184
4321
|
...dropOrder.map((name) => ({ kind: "ddl", sql: requiredViewStatement(dropDdl, name, schema, "DROP") })),
|
|
4185
4322
|
...hints.statements.map((statement) => ({ kind: "hint", sql: statement })),
|
|
4186
4323
|
...baseDdl.map((statement) => ({ kind: "ddl", sql: statement.replace(/;\s*$/, "") })),
|
|
4324
|
+
...foreignKeyTimingStatements(canonical, previous, baseDdl).map((sql5) => ({ kind: "ddl", sql: sql5 })),
|
|
4187
4325
|
...createOrder.map((name) => ({ kind: "ddl", sql: requiredViewStatement(createDdl, name, schema, "CREATE") })),
|
|
4188
4326
|
...createOrder.map((name) => {
|
|
4189
4327
|
const view = currentViewDefinitions.get(name);
|
|
@@ -4193,6 +4331,29 @@ async function planMigrationV2({
|
|
|
4193
4331
|
];
|
|
4194
4332
|
return { namespace: canonical.namespace, schema, statements, snapshot };
|
|
4195
4333
|
}
|
|
4334
|
+
function orderForeignKeyAdditions(canonical, statements) {
|
|
4335
|
+
if (!canonical.tables.some((table) => table.foreignKeys?.length)) return [...statements];
|
|
4336
|
+
const addsForeignKey = (statement) => /^ALTER TABLE[\s\S]* ADD CONSTRAINT[\s\S]* FOREIGN KEY\b/.test(statement);
|
|
4337
|
+
return [...statements.filter((statement) => !addsForeignKey(statement)), ...statements.filter(addsForeignKey)];
|
|
4338
|
+
}
|
|
4339
|
+
function foreignKeyTimingStatements(canonical, previous, baseDdl) {
|
|
4340
|
+
const prior = previous._sequenceOrm?.foreignKeys ?? {};
|
|
4341
|
+
const result = [];
|
|
4342
|
+
for (const table of canonical.tables) {
|
|
4343
|
+
for (const fk of table.foreignKeys ?? []) {
|
|
4344
|
+
const before = prior[`${table.table}.${fk.name}`];
|
|
4345
|
+
const recreated = baseDdl.some((statement) => statement.includes(`ALTER TABLE ${quoteIdentifier(ormSchemaName(canonical.namespace))}.${quoteIdentifier(table.table)} ADD CONSTRAINT ${quoteIdentifier(fk.name)}`));
|
|
4346
|
+
if (fk.deferrable ? !before || before.deferrable !== fk.deferrable || recreated : before?.deferrable && !recreated) {
|
|
4347
|
+
result.push(foreignKeyTimingSql(ormSchemaName(canonical.namespace), table.table, fk.name, fk.deferrable));
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
return result;
|
|
4352
|
+
}
|
|
4353
|
+
function foreignKeyTimingSql(schema, table, name, timing) {
|
|
4354
|
+
const clause = timing === void 0 ? "NOT DEFERRABLE" : timing === "initially-deferred" ? "DEFERRABLE INITIALLY DEFERRED" : "DEFERRABLE INITIALLY IMMEDIATE";
|
|
4355
|
+
return `ALTER TABLE ${quoteIdentifier(schema)}.${quoteIdentifier(table)} ALTER CONSTRAINT ${quoteIdentifier(name)} ${clause}`;
|
|
4356
|
+
}
|
|
4196
4357
|
function validateViewCatalogStatement({
|
|
4197
4358
|
canonical,
|
|
4198
4359
|
view,
|
|
@@ -4447,7 +4608,63 @@ async function pendingKitStatementsV2({
|
|
|
4447
4608
|
canonical,
|
|
4448
4609
|
db
|
|
4449
4610
|
}) {
|
|
4450
|
-
|
|
4611
|
+
const { desired, defaultDrift } = await normalizeJsonDefaultsForIntrospection(canonical, db);
|
|
4612
|
+
const entities = buildDrizzleEntitiesV2(desired, { introspection: true });
|
|
4613
|
+
const pending = await pendingStatements({ entities, db, owned: V2_OWNED_STATEMENT });
|
|
4614
|
+
return [...orderForeignKeyAdditions(canonical, pending), ...defaultDrift, ...await pendingForeignKeyTimingStatements(canonical, db)];
|
|
4615
|
+
}
|
|
4616
|
+
async function normalizeJsonDefaultsForIntrospection(canonical, db) {
|
|
4617
|
+
const defaults = canonical.tables.flatMap((table, tableIndex) => table.source ? [] : table.columns.flatMap((column, columnIndex) => column.type === "jsonb" && typeof column.default === "string" ? [{ tableIndex, columnIndex, table: table.table, column: column.columnName, value: column.default }] : []));
|
|
4618
|
+
if (defaults.length === 0) return { desired: canonical, defaultDrift: [] };
|
|
4619
|
+
const array = (values) => drizzleOrm.sql`ARRAY[${drizzleOrm.sql.join(values.map((value) => drizzleOrm.sql`${value}`), drizzleOrm.sql`, `)}]::text[]`;
|
|
4620
|
+
const schema = ormSchemaName(canonical.namespace);
|
|
4621
|
+
const result = await shimExecute(db).execute(drizzleOrm.sql`
|
|
4622
|
+
SELECT desired.ordinal, desired.value::jsonb::text AS normalized,
|
|
4623
|
+
attribute.attnum IS NOT NULL AS column_exists,
|
|
4624
|
+
coalesce(pg_get_expr(ad.adbin, ad.adrelid) = quote_literal(desired.value::jsonb::text) || '::jsonb', false) AS matches
|
|
4625
|
+
FROM unnest(${array(defaults.map((item) => item.table))}, ${array(defaults.map((item) => item.column))}, ${array(defaults.map((item) => item.value))})
|
|
4626
|
+
WITH ORDINALITY AS desired(table_name, column_name, value, ordinal)
|
|
4627
|
+
LEFT JOIN pg_catalog.pg_namespace namespace ON namespace.nspname = ${schema}
|
|
4628
|
+
LEFT JOIN pg_catalog.pg_class relation ON relation.relnamespace = namespace.oid AND relation.relname = desired.table_name
|
|
4629
|
+
LEFT JOIN pg_catalog.pg_attribute attribute ON attribute.attrelid = relation.oid AND attribute.attname = desired.column_name AND NOT attribute.attisdropped
|
|
4630
|
+
LEFT JOIN pg_catalog.pg_attrdef ad ON ad.adrelid = relation.oid AND ad.adnum = attribute.attnum
|
|
4631
|
+
ORDER BY desired.ordinal
|
|
4632
|
+
`);
|
|
4633
|
+
const rows = result.rows;
|
|
4634
|
+
if (!Array.isArray(rows) || rows.length !== defaults.length || rows.some((row, index3) => Number(row.ordinal) !== index3 + 1 || typeof row.normalized !== "string" || typeof row.column_exists !== "boolean" || typeof row.matches !== "boolean")) {
|
|
4635
|
+
throw new Error("invalid JSON default introspection result");
|
|
4636
|
+
}
|
|
4637
|
+
const desired = structuredClone(canonical);
|
|
4638
|
+
const defaultDrift = [];
|
|
4639
|
+
defaults.forEach((item, index3) => {
|
|
4640
|
+
const row = rows[index3];
|
|
4641
|
+
desired.tables[item.tableIndex].columns[item.columnIndex].default = JSON.stringify(JSON.parse(row.normalized));
|
|
4642
|
+
if (row.column_exists && !row.matches) defaultDrift.push(
|
|
4643
|
+
`ALTER TABLE ${quoteIdentifier(schema)}.${quoteIdentifier(item.table)} ALTER COLUMN ${quoteIdentifier(item.column)} SET DEFAULT ${quoteLiteral(item.value)}::jsonb;`
|
|
4644
|
+
);
|
|
4645
|
+
});
|
|
4646
|
+
return { desired, defaultDrift };
|
|
4647
|
+
}
|
|
4648
|
+
async function pendingForeignKeyTimingStatements(canonical, db) {
|
|
4649
|
+
const keys = canonical.tables.flatMap((table) => (table.foreignKeys ?? []).map((fk) => ({ table: table.table, fk })));
|
|
4650
|
+
if (keys.length === 0) return [];
|
|
4651
|
+
const schema = ormSchemaName(canonical.namespace);
|
|
4652
|
+
const result = await shimExecute(db).execute(drizzleOrm.sql`
|
|
4653
|
+
SELECT relation.relname AS table_name, constraint_row.conname AS name,
|
|
4654
|
+
constraint_row.condeferrable AS deferrable, constraint_row.condeferred AS deferred
|
|
4655
|
+
FROM pg_catalog.pg_constraint AS constraint_row
|
|
4656
|
+
JOIN pg_catalog.pg_class AS relation ON relation.oid = constraint_row.conrelid
|
|
4657
|
+
JOIN pg_catalog.pg_namespace AS namespace ON namespace.oid = relation.relnamespace
|
|
4658
|
+
WHERE namespace.nspname = ${schema} AND constraint_row.contype = 'f'
|
|
4659
|
+
`);
|
|
4660
|
+
const rows = result.rows;
|
|
4661
|
+
return keys.flatMap(({ table, fk }) => {
|
|
4662
|
+
const actual = rows.find((row) => row.table_name === table && row.name === fk.name);
|
|
4663
|
+
const deferrable = fk.deferrable !== void 0;
|
|
4664
|
+
const deferred = fk.deferrable === "initially-deferred";
|
|
4665
|
+
if (actual ? actual.deferrable === deferrable && actual.deferred === deferred : !deferrable) return [];
|
|
4666
|
+
return [foreignKeyTimingSql(schema, table, fk.name, fk.deferrable)];
|
|
4667
|
+
});
|
|
4451
4668
|
}
|
|
4452
4669
|
async function pendingStatements({
|
|
4453
4670
|
entities,
|
|
@@ -4460,11 +4677,31 @@ async function pendingStatements({
|
|
|
4460
4677
|
);
|
|
4461
4678
|
return statementsToExecute.filter((statement) => !owned.test(statement));
|
|
4462
4679
|
}
|
|
4680
|
+
function correctIntrospectionQuery(query) {
|
|
4681
|
+
if (!drizzleOrm.is(query, drizzleOrm.SQL)) return query;
|
|
4682
|
+
const compiled = new pgCore.PgDialect().sqlToQuery(query);
|
|
4683
|
+
let text4 = compiled.sql;
|
|
4684
|
+
const indexJoin = "pg_constraint con ON con.conindid = idx.indexrelid";
|
|
4685
|
+
if (text4.includes("AS generated_by_constraint") && text4.includes(indexJoin)) {
|
|
4686
|
+
text4 = text4.replace(indexJoin, `${indexJoin} AND con.contype IN ('p', 'u', 'x')`);
|
|
4687
|
+
}
|
|
4688
|
+
const sourceJoin = "LEFT JOIN pg_catalog.pg_attribute att ON att.attnum = ANY (con.conkey)";
|
|
4689
|
+
const targetJoin = "LEFT JOIN pg_catalog.pg_attribute fatt ON fatt.attnum = ANY (con.confkey)";
|
|
4690
|
+
if (text4.includes(sourceJoin) && text4.includes(targetJoin) && text4.includes("AND con.contype IN ('f')")) {
|
|
4691
|
+
text4 = text4.replace(
|
|
4692
|
+
sourceJoin,
|
|
4693
|
+
"JOIN LATERAL unnest(con.conkey, con.confkey) WITH ORDINALITY AS fk_key(source_num, target_num, ordinal) ON true LEFT JOIN pg_catalog.pg_attribute att ON att.attnum = fk_key.source_num"
|
|
4694
|
+
).replace(targetJoin, "LEFT JOIN pg_catalog.pg_attribute fatt ON fatt.attnum = fk_key.target_num").replace(/;\s*$/, "") + " ORDER BY con.conname, fk_key.ordinal";
|
|
4695
|
+
}
|
|
4696
|
+
if (text4 === compiled.sql) return query;
|
|
4697
|
+
if (compiled.params.length !== 0) throw new Error("parameterized Drizzle catalog query cannot be normalized");
|
|
4698
|
+
return drizzleOrm.sql.raw(text4);
|
|
4699
|
+
}
|
|
4463
4700
|
function shimExecute(db) {
|
|
4464
4701
|
const execute = db.execute.bind(db);
|
|
4465
4702
|
return {
|
|
4466
4703
|
execute: async (query) => {
|
|
4467
|
-
const result = await execute(query);
|
|
4704
|
+
const result = await execute(correctIntrospectionQuery(query));
|
|
4468
4705
|
if (result && typeof result === "object" && "rows" in result) return result;
|
|
4469
4706
|
return { rows: result };
|
|
4470
4707
|
}
|
|
@@ -4504,21 +4741,73 @@ async function planMigration({
|
|
|
4504
4741
|
const ddl = await diffStatements(hints.adjusted, snapshot);
|
|
4505
4742
|
const policyDrops = policyDropStatements(schema, snapshotTableNames(previous, schema));
|
|
4506
4743
|
const statements = [
|
|
4507
|
-
...policyDrops.map((
|
|
4508
|
-
...hints.statements.map((
|
|
4509
|
-
...ddl.map((
|
|
4510
|
-
...securityStatements(definition).map((
|
|
4744
|
+
...policyDrops.map((sql5) => ({ kind: "security", sql: sql5 })),
|
|
4745
|
+
...hints.statements.map((sql5) => ({ kind: "hint", sql: sql5 })),
|
|
4746
|
+
...ddl.map((sql5) => ({ kind: "ddl", sql: sql5.replace(/;\s*$/, "") })),
|
|
4747
|
+
...securityStatements(definition).map((sql5) => ({ kind: "security", sql: sql5 })),
|
|
4511
4748
|
// PREPARE each action's executes first (plpgsql bodies don't check columns
|
|
4512
4749
|
// at CREATE), then create the functions. Both reference the tables the DDL
|
|
4513
4750
|
// above creates, so they close the plan.
|
|
4514
|
-
...actionPrepareChecks(definition).map((
|
|
4515
|
-
...actionFunctionStatements(definition).map((
|
|
4751
|
+
...actionPrepareChecks(definition).map((sql5) => ({ kind: "function", sql: sql5 })),
|
|
4752
|
+
...actionFunctionStatements(definition).map((sql5) => ({ kind: "function", sql: sql5 }))
|
|
4516
4753
|
];
|
|
4517
4754
|
return { namespace: definition.namespace, schema, statements, snapshot };
|
|
4518
4755
|
}
|
|
4519
4756
|
var compileNamespace = (definition) => planMigration({ definition });
|
|
4520
4757
|
var previousId2 = (snapshot) => snapshot.id;
|
|
4521
|
-
var
|
|
4758
|
+
var ORM_MIGRATION_EXPORT_FILE = "orm.migration.json";
|
|
4759
|
+
var ORM_MIGRATION_EXPORT_MAX_BYTES = ORM_REGISTRATION_MAX_BYTES;
|
|
4760
|
+
var hash = zod.z.string().regex(/^sha256:[0-9a-f]{64}$/);
|
|
4761
|
+
var ormMigrationPayloadSchema = zod.z.object({
|
|
4762
|
+
canonical_definition: canonicalDefinitionSchema,
|
|
4763
|
+
documents: zod.z.array(zod.z.object({ path: zod.z.string().min(1).max(2048), contents: zod.z.string().max(1e6) }).strict()).max(5e3),
|
|
4764
|
+
client_manifest: zod.z.object({ ops_hash: hash, operations: zod.z.record(zod.z.string(), hash) }).strict(),
|
|
4765
|
+
sdl: zod.z.string().refine((value) => Buffer.byteLength(value, "utf8") <= MAX_GENERATED_SDL_BYTES, {
|
|
4766
|
+
message: `sdl exceeds ${MAX_GENERATED_SDL_BYTES} bytes`
|
|
4767
|
+
}),
|
|
4768
|
+
schema_snapshot: zod.z.json(),
|
|
4769
|
+
// The existing ORM committed-migration validator and replay engine remain
|
|
4770
|
+
// authoritative. This envelope hashes the provenance, never authorizes SQL.
|
|
4771
|
+
migration: zod.z.json().optional()
|
|
4772
|
+
}).strict();
|
|
4773
|
+
var ormMigrationExportSchema = zod.z.object({
|
|
4774
|
+
schema_version: zod.z.literal(1),
|
|
4775
|
+
schema_hash: hash,
|
|
4776
|
+
payload: ormMigrationPayloadSchema,
|
|
4777
|
+
checksum: zod.z.string().regex(/^[0-9a-f]{64}$/)
|
|
4778
|
+
}).strict();
|
|
4779
|
+
function checksum(value) {
|
|
4780
|
+
return crypto.createHash("sha256").update(stableJson(value)).digest("hex");
|
|
4781
|
+
}
|
|
4782
|
+
function createOrmMigrationExport({ schemaHash, payload }) {
|
|
4783
|
+
const contents = { schema_version: 1, schema_hash: hash.parse(schemaHash), payload: ormMigrationPayloadSchema.parse(payload) };
|
|
4784
|
+
return { ...contents, checksum: checksum(contents) };
|
|
4785
|
+
}
|
|
4786
|
+
function parseOrmMigrationExport(text4) {
|
|
4787
|
+
if (Buffer.byteLength(text4, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
|
|
4788
|
+
const exported = ormMigrationExportSchema.parse(JSON.parse(text4));
|
|
4789
|
+
const { checksum: expected, ...contents } = exported;
|
|
4790
|
+
if (checksum(contents) !== expected) throw new Error("ORM migration export checksum mismatch; regenerate and commit it");
|
|
4791
|
+
return exported;
|
|
4792
|
+
}
|
|
4793
|
+
function renderOrmMigrationExport(exported) {
|
|
4794
|
+
const text4 = `${JSON.stringify(exported, null, 2)}
|
|
4795
|
+
`;
|
|
4796
|
+
if (Buffer.byteLength(text4, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
|
|
4797
|
+
return text4;
|
|
4798
|
+
}
|
|
4799
|
+
var IR_VERSION = 2;
|
|
4800
|
+
var LEGACY_IR_VERSION = 1;
|
|
4801
|
+
var irVersionSchema = zod.z.union([zod.z.literal(LEGACY_IR_VERSION), zod.z.literal(IR_VERSION)]);
|
|
4802
|
+
var operationVersionSchema = zod.z.object({ irVersion: irVersionSchema });
|
|
4803
|
+
function requiredArtifactIrVersion(operations) {
|
|
4804
|
+
let required = LEGACY_IR_VERSION;
|
|
4805
|
+
for (const operation of operations) {
|
|
4806
|
+
const { irVersion } = operationVersionSchema.parse(operation);
|
|
4807
|
+
if (irVersion > required) required = irVersion;
|
|
4808
|
+
}
|
|
4809
|
+
return required;
|
|
4810
|
+
}
|
|
4522
4811
|
var CLAIMS_CONTRACT = {
|
|
4523
4812
|
sub: "string",
|
|
4524
4813
|
org: "string?",
|
|
@@ -4645,6 +4934,33 @@ var aggregateRootSchema = zod.z.object({
|
|
|
4645
4934
|
nodes: selectionIrSchema.optional(),
|
|
4646
4935
|
includeIf: includeIfSchema.optional()
|
|
4647
4936
|
});
|
|
4937
|
+
var DATE_BUCKETS = ["year", "quarter", "month", "week", "day", "hour"];
|
|
4938
|
+
var groupByKeyIrSchema = zod.z.object({
|
|
4939
|
+
column: zod.z.string(),
|
|
4940
|
+
bucket: zod.z.enum(DATE_BUCKETS).optional()
|
|
4941
|
+
});
|
|
4942
|
+
var aggregateGroupsRootSchema = zod.z.object({
|
|
4943
|
+
kind: zod.z.literal("aggregateGroups"),
|
|
4944
|
+
alias: zod.z.string(),
|
|
4945
|
+
table: zod.z.string(),
|
|
4946
|
+
/** Composed at compile time: AND(policy, documentWhere?, dynamicFilter?). */
|
|
4947
|
+
where: predIrSchema,
|
|
4948
|
+
groupBy: zod.z.array(groupByKeyIrSchema).min(1),
|
|
4949
|
+
/**
|
|
4950
|
+
* The `key { … }` projection: which group columns appear in each entry's key
|
|
4951
|
+
* object, in selection order. A subset of `groupBy` (the compiler rejects a
|
|
4952
|
+
* key field that is not a group key — it would be indeterminate within the
|
|
4953
|
+
* group). Empty when the document selects only `aggregate`.
|
|
4954
|
+
*/
|
|
4955
|
+
key: zod.z.array(zod.z.object({ alias: zod.z.string(), column: zod.z.string() })).default([]),
|
|
4956
|
+
count: zod.z.object({ columns: zod.z.array(zod.z.string()).optional(), distinct: zod.z.boolean().optional() }).optional(),
|
|
4957
|
+
sum: zod.z.array(zod.z.string()).optional(),
|
|
4958
|
+
avg: zod.z.array(zod.z.string()).optional(),
|
|
4959
|
+
/** Bounds the GROUP count, not the scanned rows — see MAX_GROUPS. */
|
|
4960
|
+
limit: limitIrSchema,
|
|
4961
|
+
offset: zod.z.object({ variable: zod.z.string().optional(), value: zod.z.number().int().optional() }).optional(),
|
|
4962
|
+
includeIf: includeIfSchema.optional()
|
|
4963
|
+
});
|
|
4648
4964
|
var writeValuesSchema = zod.z.union([
|
|
4649
4965
|
zod.z.object({ slot: zod.z.string() }),
|
|
4650
4966
|
zod.z.object({ entries: zod.z.array(zod.z.object({ column: zod.z.string(), value: valueIrSchema })) })
|
|
@@ -4690,12 +5006,15 @@ var mutationRootSchema = zod.z.object({
|
|
|
4690
5006
|
insertCheck: predIrSchema.optional(),
|
|
4691
5007
|
returning: selectionIrSchema.nullable(),
|
|
4692
5008
|
affectedRows: zod.z.boolean().default(false),
|
|
5009
|
+
/** v2: abort the transaction when the actual write count differs, regardless of selection. */
|
|
5010
|
+
assertAffectedRows: zod.z.number().int().min(0).max(2147483647).optional(),
|
|
4693
5011
|
includeIf: includeIfSchema.optional()
|
|
4694
5012
|
});
|
|
4695
5013
|
var rootIrSchema = zod.z.discriminatedUnion("kind", [
|
|
4696
5014
|
selectRootSchema,
|
|
4697
5015
|
byPkRootSchema,
|
|
4698
5016
|
aggregateRootSchema,
|
|
5017
|
+
aggregateGroupsRootSchema,
|
|
4699
5018
|
mutationRootSchema
|
|
4700
5019
|
]);
|
|
4701
5020
|
var INPUT_REF_KINDS = [
|
|
@@ -4725,7 +5044,7 @@ var variableIrSchema = zod.z.object({
|
|
|
4725
5044
|
defaultValue: variableDefaultIrSchema.optional()
|
|
4726
5045
|
});
|
|
4727
5046
|
var compiledOperationSchema = zod.z.object({
|
|
4728
|
-
irVersion:
|
|
5047
|
+
irVersion: irVersionSchema,
|
|
4729
5048
|
name: zod.z.string(),
|
|
4730
5049
|
opHash: zod.z.string().regex(/^sha256:[0-9a-f]{64}$/),
|
|
4731
5050
|
kind: zod.z.enum(["query", "mutation"]),
|
|
@@ -4737,6 +5056,13 @@ var compiledOperationSchema = zod.z.object({
|
|
|
4737
5056
|
/** Every table the op touches — data-token scoping + audit. */
|
|
4738
5057
|
tableSet: zod.z.array(zod.z.string()).min(1),
|
|
4739
5058
|
roots: zod.z.array(rootIrSchema).min(1)
|
|
5059
|
+
}).superRefine((operation, ctx) => {
|
|
5060
|
+
for (const [index3, root] of operation.roots.entries()) {
|
|
5061
|
+
if (root.kind !== "mutation" || root.assertAffectedRows === void 0) continue;
|
|
5062
|
+
if (operation.irVersion < 2 || operation.kind !== "mutation" || root.includeIf !== void 0) {
|
|
5063
|
+
ctx.addIssue({ code: "custom", path: ["roots", index3], message: "affected-row assertions require unconditional mutation roots and IR v2" });
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
4740
5066
|
});
|
|
4741
5067
|
var tableMetaIrSchema = zod.z.object({
|
|
4742
5068
|
table: zod.z.string(),
|
|
@@ -4773,18 +5099,29 @@ var tableMetaIrSchema = zod.z.object({
|
|
|
4773
5099
|
insertable: zod.z.array(zod.z.string()),
|
|
4774
5100
|
settable: zod.z.array(zod.z.string()),
|
|
4775
5101
|
appendable: zod.z.array(zod.z.string()),
|
|
5102
|
+
/**
|
|
5103
|
+
* Logical columns stamped with Postgres `now()` on UPDATE (canonical
|
|
5104
|
+
* `onUpdateFn`). Omitted when empty so pre-onUpdate artifacts still parse.
|
|
5105
|
+
*/
|
|
5106
|
+
touchOnUpdate: zod.z.array(zod.z.string()).optional(),
|
|
4776
5107
|
constraints: zod.z.array(zod.z.object({ name: zod.z.string(), columns: zod.z.array(zod.z.string()) })),
|
|
4777
5108
|
/** Depth/node caps for dynamic filter trees on this table. */
|
|
4778
5109
|
dynamicFilterMaxDepth: zod.z.number().int().positive().default(6),
|
|
4779
5110
|
dynamicFilterMaxNodes: zod.z.number().int().positive().default(64)
|
|
4780
5111
|
});
|
|
4781
5112
|
var compiledArtifactSchema = zod.z.object({
|
|
4782
|
-
irVersion:
|
|
5113
|
+
irVersion: irVersionSchema,
|
|
4783
5114
|
namespace: zod.z.string(),
|
|
4784
5115
|
schemaHash: zod.z.string(),
|
|
4785
5116
|
opsHash: zod.z.string(),
|
|
4786
5117
|
tables: zod.z.record(zod.z.string(), tableMetaIrSchema),
|
|
4787
5118
|
operations: zod.z.record(zod.z.string(), compiledOperationSchema)
|
|
5119
|
+
}).superRefine((artifact, ctx) => {
|
|
5120
|
+
for (const [name, operation] of Object.entries(artifact.operations)) {
|
|
5121
|
+
if (operation.irVersion > artifact.irVersion) {
|
|
5122
|
+
ctx.addIssue({ code: "custom", path: ["operations", name, "irVersion"], message: "operation requires a newer IR version than its artifact" });
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
4788
5125
|
});
|
|
4789
5126
|
var IrVersionError = class extends Error {
|
|
4790
5127
|
constructor(found) {
|
|
@@ -4795,7 +5132,7 @@ var IrVersionError = class extends Error {
|
|
|
4795
5132
|
function normalizeIr(raw) {
|
|
4796
5133
|
if (!raw || typeof raw !== "object") throw new IrVersionError(void 0);
|
|
4797
5134
|
const version = raw.irVersion;
|
|
4798
|
-
if (version !== IR_VERSION) throw new IrVersionError(version);
|
|
5135
|
+
if (version !== LEGACY_IR_VERSION && version !== IR_VERSION) throw new IrVersionError(version);
|
|
4799
5136
|
return compiledArtifactSchema.parse(raw);
|
|
4800
5137
|
}
|
|
4801
5138
|
function indexByHash(artifact) {
|
|
@@ -5111,12 +5448,12 @@ async function writeMigration({
|
|
|
5111
5448
|
const tip = workspace.journal.entries.at(-1);
|
|
5112
5449
|
const ordinal = (tip?.ordinal ?? 0) + 1;
|
|
5113
5450
|
const name = migrationName(ordinal, contentHash);
|
|
5114
|
-
const
|
|
5451
|
+
const sql5 = renderMigrationSql({ name, namespace: workspace.journal.namespace, contentHash, plan });
|
|
5115
5452
|
const metaDir = path.join(dir, MIGRATIONS_DIRNAME, META_DIRNAME);
|
|
5116
5453
|
await promises.mkdir(metaDir, { recursive: true });
|
|
5117
5454
|
const snapshotText = `${JSON.stringify(plan.snapshot, null, 2)}
|
|
5118
5455
|
`;
|
|
5119
|
-
await promises.writeFile(path.join(dir, MIGRATIONS_DIRNAME, `${name}.sql`),
|
|
5456
|
+
await promises.writeFile(path.join(dir, MIGRATIONS_DIRNAME, `${name}.sql`), sql5);
|
|
5120
5457
|
await promises.writeFile(path.join(metaDir, `${pad(ordinal)}.snapshot.json`), snapshotText);
|
|
5121
5458
|
const journal = {
|
|
5122
5459
|
...workspace.journal,
|
|
@@ -5127,7 +5464,7 @@ async function writeMigration({
|
|
|
5127
5464
|
name,
|
|
5128
5465
|
content_hash: contentHash,
|
|
5129
5466
|
previous_content_hash: tip?.content_hash ?? null,
|
|
5130
|
-
checksum: checksumOf(
|
|
5467
|
+
checksum: checksumOf(sql5),
|
|
5131
5468
|
snapshot_checksum: checksumOf(snapshotText),
|
|
5132
5469
|
destructive
|
|
5133
5470
|
}
|
|
@@ -5242,8 +5579,8 @@ async function checkWorkspace({
|
|
|
5242
5579
|
let previousId3;
|
|
5243
5580
|
let previousHash = null;
|
|
5244
5581
|
for (const entry of workspace.journal.entries) {
|
|
5245
|
-
const
|
|
5246
|
-
if (checksumOf(
|
|
5582
|
+
const sql5 = await readMigrationSql(dir, entry);
|
|
5583
|
+
if (checksumOf(sql5) !== entry.checksum) {
|
|
5247
5584
|
log(`${MIGRATIONS_DIRNAME}/${entry.name}.sql does not match its journal checksum \u2014 committed migrations must not be edited`);
|
|
5248
5585
|
return 1;
|
|
5249
5586
|
}
|
|
@@ -5283,7 +5620,7 @@ async function checkWorkspace({
|
|
|
5283
5620
|
log(`migrations in sync \u2014 ${workspace.journal.entries.length} committed, tip ${tip.name}`);
|
|
5284
5621
|
return 0;
|
|
5285
5622
|
}
|
|
5286
|
-
var firstLine = (
|
|
5623
|
+
var firstLine = (sql5) => sql5.split("\n")[0] ?? sql5;
|
|
5287
5624
|
|
|
5288
5625
|
// src/migrations/from-yaml.ts
|
|
5289
5626
|
function convertV1DefinitionToCanonical(definition) {
|
|
@@ -5389,7 +5726,7 @@ function buildRebaseJournalEntry({ namespace, previousHash, newHash, ordinal, sn
|
|
|
5389
5726
|
throw new OrmDefinitionError(`rebase ordinal must be an integer \u2265 2 (a rebase sits on an existing journal tip); got ${ordinal}`);
|
|
5390
5727
|
}
|
|
5391
5728
|
const name = migrationName(ordinal, newHash);
|
|
5392
|
-
const
|
|
5729
|
+
const sql5 = renderRebaseMigrationSql({ name, namespace, previousHash, newHash });
|
|
5393
5730
|
const snapshotText = `${JSON.stringify(snapshot, null, 2)}
|
|
5394
5731
|
`;
|
|
5395
5732
|
const entry = {
|
|
@@ -5397,11 +5734,11 @@ function buildRebaseJournalEntry({ namespace, previousHash, newHash, ordinal, sn
|
|
|
5397
5734
|
name,
|
|
5398
5735
|
content_hash: newHash,
|
|
5399
5736
|
previous_content_hash: previousHash,
|
|
5400
|
-
checksum: checksumOf(
|
|
5737
|
+
checksum: checksumOf(sql5),
|
|
5401
5738
|
snapshot_checksum: checksumOf(snapshotText),
|
|
5402
5739
|
destructive: false
|
|
5403
5740
|
};
|
|
5404
|
-
return { name, entry, sql:
|
|
5741
|
+
return { name, entry, sql: sql5, snapshotText };
|
|
5405
5742
|
}
|
|
5406
5743
|
function renderRebaseMigrationSql({
|
|
5407
5744
|
name,
|
|
@@ -5531,10 +5868,12 @@ exports.CLAIM_KEY_PATTERN = CLAIM_KEY_PATTERN;
|
|
|
5531
5868
|
exports.CMP_OPS = CMP_OPS;
|
|
5532
5869
|
exports.COLUMN_BRAND = COLUMN_BRAND;
|
|
5533
5870
|
exports.COLUMN_DEFAULT_FNS = COLUMN_DEFAULT_FNS;
|
|
5871
|
+
exports.COLUMN_IDENTIFIER_PATTERN = COLUMN_IDENTIFIER_PATTERN;
|
|
5534
5872
|
exports.COLUMN_TYPES = COLUMN_TYPES2;
|
|
5535
5873
|
exports.COMPARE_OPS = COMPARE_OPS;
|
|
5536
5874
|
exports.CONSUMER_ROLE_SUFFIX = CONSUMER_ROLE_SUFFIX;
|
|
5537
5875
|
exports.ColumnBuilder = ColumnBuilder;
|
|
5876
|
+
exports.DATE_BUCKETS = DATE_BUCKETS;
|
|
5538
5877
|
exports.DEFINITION_FILE_EXTENSIONS = DEFINITION_FILE_EXTENSIONS;
|
|
5539
5878
|
exports.ENUM_BRAND = ENUM_BRAND;
|
|
5540
5879
|
exports.HASH_DOMAIN = HASH_DOMAIN;
|
|
@@ -5543,8 +5882,10 @@ exports.IDENTIFIER_RE = IDENTIFIER_RE;
|
|
|
5543
5882
|
exports.INPUT_REF_KINDS = INPUT_REF_KINDS;
|
|
5544
5883
|
exports.IR_VERSION = IR_VERSION;
|
|
5545
5884
|
exports.IrVersionError = IrVersionError;
|
|
5885
|
+
exports.LEGACY_IR_VERSION = LEGACY_IR_VERSION;
|
|
5546
5886
|
exports.LINK_BRAND = LINK_BRAND;
|
|
5547
5887
|
exports.MAX_DEFINITION_FILE_BYTES = MAX_DEFINITION_FILE_BYTES;
|
|
5888
|
+
exports.MAX_GENERATED_SDL_BYTES = MAX_GENERATED_SDL_BYTES;
|
|
5548
5889
|
exports.MAX_IDENTIFIER_LENGTH = MAX_IDENTIFIER_LENGTH;
|
|
5549
5890
|
exports.MIGRATIONS_DIRNAME = MIGRATIONS_DIRNAME;
|
|
5550
5891
|
exports.NAMESPACE_CONFIG_BRAND = NAMESPACE_CONFIG_BRAND;
|
|
@@ -5552,6 +5893,9 @@ exports.NAMESPACE_CONFIG_FILE = NAMESPACE_CONFIG_FILE;
|
|
|
5552
5893
|
exports.NAMESPACE_CONFIG_FILENAMES = NAMESPACE_CONFIG_FILENAMES;
|
|
5553
5894
|
exports.NAMESPACE_PATTERN = NAMESPACE_PATTERN;
|
|
5554
5895
|
exports.ORM_DATA_TOKEN_HEADER = ORM_DATA_TOKEN_HEADER;
|
|
5896
|
+
exports.ORM_MIGRATION_EXPORT_FILE = ORM_MIGRATION_EXPORT_FILE;
|
|
5897
|
+
exports.ORM_MIGRATION_EXPORT_MAX_BYTES = ORM_MIGRATION_EXPORT_MAX_BYTES;
|
|
5898
|
+
exports.ORM_REGISTRATION_MAX_BYTES = ORM_REGISTRATION_MAX_BYTES;
|
|
5555
5899
|
exports.ORM_SCHEMA_PREFIX = ORM_SCHEMA_PREFIX;
|
|
5556
5900
|
exports.ORM_SERVICE_DATA_TOKEN_HEADER = ORM_SERVICE_DATA_TOKEN_HEADER;
|
|
5557
5901
|
exports.ORM_TO_PIPELINE_TYPE = ORM_TO_PIPELINE_TYPE;
|
|
@@ -5616,6 +5960,7 @@ exports.computeDefinitionHash = computeDefinitionHash;
|
|
|
5616
5960
|
exports.computeSyncedContractHash = computeSyncedContractHash;
|
|
5617
5961
|
exports.consumerRoleName = consumerRoleName;
|
|
5618
5962
|
exports.convertV1DefinitionToCanonical = convertV1DefinitionToCanonical;
|
|
5963
|
+
exports.createOrmMigrationExport = createOrmMigrationExport;
|
|
5619
5964
|
exports.declaredCapabilityKeys = declaredCapabilityKeys;
|
|
5620
5965
|
exports.defineEnum = defineEnum;
|
|
5621
5966
|
exports.defineNamespace = defineNamespace;
|
|
@@ -5657,11 +6002,13 @@ exports.normalizeCanonical = normalizeCanonical;
|
|
|
5657
6002
|
exports.normalizeIr = normalizeIr;
|
|
5658
6003
|
exports.normalizeSnapshot = normalizeSnapshot;
|
|
5659
6004
|
exports.orderIrSchema = orderIrSchema;
|
|
6005
|
+
exports.ormMigrationExportSchema = ormMigrationExportSchema;
|
|
5660
6006
|
exports.ormSchemaName = ormSchemaName;
|
|
5661
6007
|
exports.overlayWinsUnion = overlayWinsUnion;
|
|
5662
6008
|
exports.overrideRowMutation = overrideRowMutation;
|
|
5663
6009
|
exports.parseCanonicalDefinition = parseCanonicalDefinition;
|
|
5664
6010
|
exports.parseDefinitionSources = parseDefinitionSources;
|
|
6011
|
+
exports.parseOrmMigrationExport = parseOrmMigrationExport;
|
|
5665
6012
|
exports.parsePredicate = parsePredicate;
|
|
5666
6013
|
exports.parseTableFile = parseTableFile;
|
|
5667
6014
|
exports.parsedPredicateSchema = parsedPredicateSchema;
|
|
@@ -5682,6 +6029,8 @@ exports.rebaseViolations = rebaseViolations;
|
|
|
5682
6029
|
exports.referencedCapabilities = referencedCapabilities;
|
|
5683
6030
|
exports.referencedColumns = referencedColumns;
|
|
5684
6031
|
exports.renderMigrationSql = renderMigrationSql;
|
|
6032
|
+
exports.renderOrmMigrationExport = renderOrmMigrationExport;
|
|
6033
|
+
exports.requiredArtifactIrVersion = requiredArtifactIrVersion;
|
|
5685
6034
|
exports.revertOverlayMutation = revertOverlayMutation;
|
|
5686
6035
|
exports.rewriteSyncedSourceRelationsInViewQuery = rewriteSyncedSourceRelationsInViewQuery;
|
|
5687
6036
|
exports.roleExpression = roleExpression;
|