@sequenceholdings/orm 2.0.0 → 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 +57 -2
- package/dist/authoring.cjs +122 -15
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +20 -3
- package/dist/authoring.d.ts +20 -3
- package/dist/authoring.js +122 -15
- 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 +721 -132
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +723 -134
- 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 +482 -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 +483 -97
- package/dist/compiler.js.map +1 -1
- package/dist/index.cjs +414 -70
- 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 +404 -72
- 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 +130 -11
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.js +130 -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 +3 -3
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sql, eq, notExists } from 'drizzle-orm';
|
|
1
|
+
import { sql, eq, notExists, is, SQL } from 'drizzle-orm';
|
|
2
2
|
import { pgSchema, uniqueIndex, index, foreignKey, primaryKey, PgDialect, QueryBuilder, getViewConfig, extractUsedTable, jsonb, date, timestamp, boolean, doublePrecision, numeric, bigint, integer, text, uuid } from 'drizzle-orm/pg-core';
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
import { z } from 'zod';
|
|
@@ -34,6 +34,7 @@ var ColumnBuilder = class {
|
|
|
34
34
|
hasDefault: false,
|
|
35
35
|
defaultValue: void 0,
|
|
36
36
|
defaultFn: void 0,
|
|
37
|
+
onUpdateFn: void 0,
|
|
37
38
|
description: void 0,
|
|
38
39
|
renamedFrom: void 0,
|
|
39
40
|
cast: void 0,
|
|
@@ -69,6 +70,15 @@ var ColumnBuilder = class {
|
|
|
69
70
|
this.state.defaultFn = void 0;
|
|
70
71
|
return this;
|
|
71
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Stamp this column with Postgres `now()` on every UPDATE. Only valid on
|
|
75
|
+
* timestamptz/date; the compiler drops it from SetInput so callers cannot
|
|
76
|
+
* supply a value.
|
|
77
|
+
*/
|
|
78
|
+
onUpdate(value) {
|
|
79
|
+
this.state.onUpdateFn = value;
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
72
82
|
description(text4) {
|
|
73
83
|
this.state.description = text4;
|
|
74
84
|
return this;
|
|
@@ -112,6 +122,7 @@ var ColumnBuilder = class {
|
|
|
112
122
|
};
|
|
113
123
|
if (this.state.hasDefault) column.default = this.state.defaultValue ?? null;
|
|
114
124
|
if (this.state.defaultFn !== void 0) column.defaultFn = this.state.defaultFn;
|
|
125
|
+
if (this.state.onUpdateFn !== void 0) column.onUpdateFn = this.state.onUpdateFn;
|
|
115
126
|
if (this.state.description !== void 0) column.description = this.state.description;
|
|
116
127
|
if (this.state.renamedFrom !== void 0) column.renamedFrom = this.state.renamedFrom;
|
|
117
128
|
if (this.state.cast !== void 0) column.cast = this.state.cast;
|
|
@@ -139,6 +150,7 @@ var t = {
|
|
|
139
150
|
// src/constants.ts
|
|
140
151
|
var NAMESPACE_PATTERN = /^[a-z][a-z0-9_]{0,40}$/;
|
|
141
152
|
var IDENTIFIER_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
153
|
+
var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
|
|
142
154
|
var MAX_IDENTIFIER_LENGTH = 63;
|
|
143
155
|
var ORM_DATA_TOKEN_HEADER = "x-sequence-data-token";
|
|
144
156
|
var ORM_SERVICE_DATA_TOKEN_HEADER = "x-sequence-data-token-service";
|
|
@@ -154,6 +166,8 @@ var DEFINITION_FILE_EXTENSIONS = [".yml", ".yaml"];
|
|
|
154
166
|
var NAMESPACE_CONFIG_FILENAMES = /* @__PURE__ */ new Set(["namespace.yaml", "namespace.yml"]);
|
|
155
167
|
var HASH_DOMAIN = "sequence-orm-definitions\0";
|
|
156
168
|
var MAX_DEFINITION_FILE_BYTES = 1024 * 1024;
|
|
169
|
+
var MAX_GENERATED_SDL_BYTES = 16 * 1024 * 1024;
|
|
170
|
+
var ORM_REGISTRATION_MAX_BYTES = 64 * 1024 * 1024;
|
|
157
171
|
var RESERVED_TABLE_NAMES = /* @__PURE__ */ new Set(["actions", "query"]);
|
|
158
172
|
var RESERVED_COLUMN_NAMES = /* @__PURE__ */ new Set(["select", "order", "limit", "offset"]);
|
|
159
173
|
|
|
@@ -262,10 +276,10 @@ function isSeqLink(value) {
|
|
|
262
276
|
function isSeqTable(value) {
|
|
263
277
|
return typeof value === "object" && value !== null && value.kind === TABLE_BRAND;
|
|
264
278
|
}
|
|
265
|
-
function assertIdentifier({ value, what, file }) {
|
|
266
|
-
if (!
|
|
279
|
+
function assertIdentifier({ value, what, file, pattern = IDENTIFIER_PATTERN }) {
|
|
280
|
+
if (!pattern.test(value) || value.length > MAX_IDENTIFIER_LENGTH) {
|
|
267
281
|
throw new OrmDefinitionError(
|
|
268
|
-
`${what} "${value}" must match ${
|
|
282
|
+
`${what} "${value}" must match ${pattern} and be at most ${MAX_IDENTIFIER_LENGTH} characters`,
|
|
269
283
|
file
|
|
270
284
|
);
|
|
271
285
|
}
|
|
@@ -286,7 +300,7 @@ function defineTable(name, columns, config = {}) {
|
|
|
286
300
|
);
|
|
287
301
|
}
|
|
288
302
|
const columnName = builder.columnNameFor(propertyName);
|
|
289
|
-
assertIdentifier({ value: columnName, what: `table "${name}": column "${propertyName}" column name`, file });
|
|
303
|
+
assertIdentifier({ value: columnName, what: `table "${name}": column "${propertyName}" column name`, file, pattern: COLUMN_IDENTIFIER_PATTERN });
|
|
290
304
|
const existing = seenColumnNames.get(columnName);
|
|
291
305
|
if (existing !== void 0) {
|
|
292
306
|
throw new OrmDefinitionError(
|
|
@@ -326,7 +340,8 @@ function defineSyncedTable(name, columnsOrConfig, maybeConfig) {
|
|
|
326
340
|
assertIdentifier({
|
|
327
341
|
value: columnName,
|
|
328
342
|
what: `synced table "${name}": column "${propertyName}" column name`,
|
|
329
|
-
file
|
|
343
|
+
file,
|
|
344
|
+
pattern: COLUMN_IDENTIFIER_PATTERN
|
|
330
345
|
});
|
|
331
346
|
const existing = seenColumnNames.get(columnName);
|
|
332
347
|
if (existing !== void 0) {
|
|
@@ -415,7 +430,7 @@ function defineView(name, columns, config) {
|
|
|
415
430
|
);
|
|
416
431
|
}
|
|
417
432
|
const columnName = builder.columnNameFor(propertyName);
|
|
418
|
-
if (!
|
|
433
|
+
if (!COLUMN_IDENTIFIER_PATTERN.test(columnName) || columnName.length > MAX_IDENTIFIER_LENGTH) {
|
|
419
434
|
throw new OrmDefinitionError(`view "${name}": column "${propertyName}" has invalid column name "${columnName}"`, file);
|
|
420
435
|
}
|
|
421
436
|
const existing = seenColumnNames.get(columnName);
|
|
@@ -864,6 +879,7 @@ function scalarColumnType(table, property, helper) {
|
|
|
864
879
|
var CANONICAL_FORMAT = 2;
|
|
865
880
|
var IDENTIFIER_RE = /^[a-z][a-z0-9_]*$/;
|
|
866
881
|
var identifier = z.string().min(1).max(63).regex(IDENTIFIER_RE);
|
|
882
|
+
var columnIdentifier = z.string().min(1).max(63).regex(COLUMN_IDENTIFIER_PATTERN);
|
|
867
883
|
var COLUMN_TYPES = [
|
|
868
884
|
"uuid",
|
|
869
885
|
"text",
|
|
@@ -884,18 +900,33 @@ var canonicalEnumSchema = z.object({
|
|
|
884
900
|
var canonicalColumnSchema = z.object({
|
|
885
901
|
/** Property name in TS (camelCase); columnName is the Postgres name. */
|
|
886
902
|
name: z.string().min(1).max(63),
|
|
887
|
-
columnName:
|
|
903
|
+
columnName: columnIdentifier,
|
|
888
904
|
type: z.union([columnTypeSchema, z.object({ enum: identifier })]),
|
|
889
905
|
nullable: z.boolean().default(true),
|
|
890
906
|
unique: z.boolean().default(false),
|
|
891
907
|
/** Literal default, or one of the allowlisted SQL functions. */
|
|
892
908
|
default: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),
|
|
893
909
|
defaultFn: z.enum(["gen_random_uuid()", "now()"]).optional(),
|
|
910
|
+
/**
|
|
911
|
+
* Stamp this column from the database clock on UPDATE (`now()`). The
|
|
912
|
+
* column stays insertable (defaults fill gaps) but is omitted from
|
|
913
|
+
* SetInput / settable so callers cannot backdate it.
|
|
914
|
+
*/
|
|
915
|
+
onUpdateFn: z.enum(["now()"]).optional(),
|
|
894
916
|
description: z.string().max(500).optional(),
|
|
895
917
|
/** Migration hints — same semantics as v1. */
|
|
896
|
-
renamedFrom:
|
|
918
|
+
renamedFrom: columnIdentifier.optional(),
|
|
897
919
|
cast: z.string().max(200).optional(),
|
|
898
920
|
deprecated: z.boolean().default(false)
|
|
921
|
+
}).superRefine((column, ctx) => {
|
|
922
|
+
if (column.onUpdateFn === void 0) return;
|
|
923
|
+
if (typeof column.type !== "string" || column.type !== "timestamptz" && column.type !== "date") {
|
|
924
|
+
ctx.addIssue({
|
|
925
|
+
code: "custom",
|
|
926
|
+
path: ["onUpdateFn"],
|
|
927
|
+
message: `onUpdateFn '${column.onUpdateFn}' is only valid on timestamptz/date columns`
|
|
928
|
+
});
|
|
929
|
+
}
|
|
899
930
|
});
|
|
900
931
|
var canonicalLinkSchema = z.object({
|
|
901
932
|
name: z.string().min(1).max(63),
|
|
@@ -903,15 +934,15 @@ var canonicalLinkSchema = z.object({
|
|
|
903
934
|
/** Target table name (namespace-local). Absent for toPlatformUser. */
|
|
904
935
|
target: identifier.optional(),
|
|
905
936
|
/** FK column on the owning side (toOne/toPlatformUser: on this table; toMany: on the target). */
|
|
906
|
-
on:
|
|
937
|
+
on: columnIdentifier,
|
|
907
938
|
/** Referenced column on the target (default: target's PK / platform user `sub`). */
|
|
908
|
-
references:
|
|
939
|
+
references: columnIdentifier.optional(),
|
|
909
940
|
/** enforce: true emits a real FK constraint (same-namespace toOne only). */
|
|
910
941
|
enforce: z.boolean().default(false),
|
|
911
942
|
description: z.string().max(500).optional()
|
|
912
943
|
});
|
|
913
944
|
var canonicalIndexSchema = z.object({
|
|
914
|
-
columns: z.array(
|
|
945
|
+
columns: z.array(columnIdentifier).min(1),
|
|
915
946
|
unique: z.boolean().default(false),
|
|
916
947
|
name: identifier.optional()
|
|
917
948
|
});
|
|
@@ -1159,6 +1190,13 @@ var canonicalCowMutationSchema = z.object({
|
|
|
1159
1190
|
}
|
|
1160
1191
|
}
|
|
1161
1192
|
});
|
|
1193
|
+
var canonicalForeignKeySchema = z.object({
|
|
1194
|
+
name: identifier,
|
|
1195
|
+
columns: z.array(columnIdentifier).min(1).max(8),
|
|
1196
|
+
target: identifier,
|
|
1197
|
+
references: z.array(columnIdentifier).min(1).max(8),
|
|
1198
|
+
deferrable: z.enum(["initially-immediate", "initially-deferred"]).optional()
|
|
1199
|
+
}).strict();
|
|
1162
1200
|
var canonicalTableSchema = z.object({
|
|
1163
1201
|
table: identifier,
|
|
1164
1202
|
description: z.string().max(2e3).optional(),
|
|
@@ -1167,7 +1205,7 @@ var canonicalTableSchema = z.object({
|
|
|
1167
1205
|
/** Synced/reflected read-only tables (`source: gold.*`) — v1 semantics. */
|
|
1168
1206
|
source: z.string().regex(/^gold\.[a-z][a-z0-9_]*$/).optional(),
|
|
1169
1207
|
columns: z.array(canonicalColumnSchema).default([]),
|
|
1170
|
-
primaryKey: z.array(
|
|
1208
|
+
primaryKey: z.array(columnIdentifier).min(1).max(8).optional(),
|
|
1171
1209
|
links: z.array(canonicalLinkSchema).default([]),
|
|
1172
1210
|
indexes: z.array(canonicalIndexSchema).default([]),
|
|
1173
1211
|
policies: canonicalPoliciesSchema.default({}),
|
|
@@ -1178,14 +1216,16 @@ var canonicalTableSchema = z.object({
|
|
|
1178
1216
|
*/
|
|
1179
1217
|
actions: z.array(canonicalActionSchema).default([]),
|
|
1180
1218
|
/** Optional (not defaulted) so pre-CoW canonical documents keep byte-identical hashes. */
|
|
1181
|
-
cowMutations: z.array(canonicalCowMutationSchema).optional()
|
|
1219
|
+
cowMutations: z.array(canonicalCowMutationSchema).optional(),
|
|
1220
|
+
/** Omitted by old definitions, preserving their existing content hashes. */
|
|
1221
|
+
foreignKeys: z.array(canonicalForeignKeySchema).max(100).optional()
|
|
1182
1222
|
});
|
|
1183
1223
|
var canonicalViewSchema = z.object({
|
|
1184
1224
|
view: identifier,
|
|
1185
1225
|
description: z.string().max(2e3).optional(),
|
|
1186
1226
|
columns: z.array(canonicalColumnSchema).min(1),
|
|
1187
1227
|
/** Logical identity for GraphQL only; PostgreSQL views have no PK constraint. */
|
|
1188
|
-
primaryKey: z.array(
|
|
1228
|
+
primaryKey: z.array(columnIdentifier).min(1).max(8).optional(),
|
|
1189
1229
|
links: z.array(canonicalLinkSchema).default([]),
|
|
1190
1230
|
policies: z.object({ read: policyExpressionSchema }).strict(),
|
|
1191
1231
|
query: z.string().min(1).max(1e5).refine(isSingleViewQuerySql, {
|
|
@@ -1198,6 +1238,9 @@ var canonicalViewSchema = z.object({
|
|
|
1198
1238
|
if (column.default !== void 0 || column.defaultFn !== void 0) {
|
|
1199
1239
|
ctx.addIssue({ code: "custom", path: ["columns", index3], message: `view column '${column.name}' cannot declare a default` });
|
|
1200
1240
|
}
|
|
1241
|
+
if (column.onUpdateFn !== void 0) {
|
|
1242
|
+
ctx.addIssue({ code: "custom", path: ["columns", index3], message: `view column '${column.name}' cannot declare onUpdateFn` });
|
|
1243
|
+
}
|
|
1201
1244
|
if (column.unique) {
|
|
1202
1245
|
ctx.addIssue({ code: "custom", path: ["columns", index3], message: `view column '${column.name}' cannot declare unique` });
|
|
1203
1246
|
}
|
|
@@ -1231,6 +1274,45 @@ var canonicalDefinitionSchema = z.object({
|
|
|
1231
1274
|
ctx.addIssue({ code: "custom", path: ["tables", index3, "table"], message: `duplicate relation '${table.table}'` });
|
|
1232
1275
|
}
|
|
1233
1276
|
seen.add(table.table);
|
|
1277
|
+
if (table.source !== void 0) {
|
|
1278
|
+
for (const [columnIndex, column] of table.columns.entries()) {
|
|
1279
|
+
if (column.onUpdateFn !== void 0) {
|
|
1280
|
+
ctx.addIssue({
|
|
1281
|
+
code: "custom",
|
|
1282
|
+
path: ["tables", index3, "columns", columnIndex],
|
|
1283
|
+
message: `synced table '${table.table}' column '${column.name}' cannot declare onUpdateFn`
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
const fkTables = new Map(definition.tables.map((table) => [table.table, table]));
|
|
1290
|
+
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
1291
|
+
const names = /* @__PURE__ */ new Set([
|
|
1292
|
+
...table.primaryKey?.length ? [`${table.table}_pkey`] : [],
|
|
1293
|
+
...table.columns.filter((column) => column.unique && !(table.primaryKey?.length === 1 && table.primaryKey[0] === column.columnName)).map((column) => `${table.table}_${column.columnName}_unique`),
|
|
1294
|
+
...table.links.filter((link) => link.enforce).map((link) => `fk_${table.table}_${link.name}`)
|
|
1295
|
+
]);
|
|
1296
|
+
for (const [index3, fk] of (table.foreignKeys ?? []).entries()) {
|
|
1297
|
+
const fail = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "foreignKeys", index3], message });
|
|
1298
|
+
if (names.has(fk.name)) fail(`duplicate foreign key '${fk.name}'`);
|
|
1299
|
+
names.add(fk.name);
|
|
1300
|
+
const target = fkTables.get(fk.target);
|
|
1301
|
+
if (table.source || !target || target.source) {
|
|
1302
|
+
fail("foreign keys must connect owned tables in the same namespace");
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
if (fk.columns.length !== fk.references.length) fail("foreign key columns and references must have the same number of columns");
|
|
1306
|
+
if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail("foreign key columns must not repeat");
|
|
1307
|
+
for (const [position, name] of fk.columns.entries()) {
|
|
1308
|
+
const sourceColumn = table.columns.find((column) => column.columnName === name);
|
|
1309
|
+
const targetColumn = target.columns.find((column) => column.columnName === fk.references[position]);
|
|
1310
|
+
if (!sourceColumn || !targetColumn) fail("foreign key references an unknown column");
|
|
1311
|
+
else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail("foreign key column types must match");
|
|
1312
|
+
}
|
|
1313
|
+
const uniqueKeys = [target.primaryKey ?? [], ...target.indexes.filter((index4) => index4.unique).map((index4) => index4.columns), ...target.columns.filter((column) => column.unique).map((column) => [column.columnName])];
|
|
1314
|
+
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");
|
|
1315
|
+
}
|
|
1234
1316
|
}
|
|
1235
1317
|
const tableByName = new Map(definition.tables.map((table) => [table.table, table]));
|
|
1236
1318
|
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
@@ -1366,12 +1448,13 @@ function normalizeCanonical(definition) {
|
|
|
1366
1448
|
...definition,
|
|
1367
1449
|
enums: sortByName(definition.enums),
|
|
1368
1450
|
tables: [...definition.tables].sort((a, b) => a.table.localeCompare(b.table)).map((t2) => {
|
|
1369
|
-
const { cowMutations, ...rest } = t2;
|
|
1451
|
+
const { cowMutations, foreignKeys, ...rest } = t2;
|
|
1370
1452
|
return {
|
|
1371
1453
|
...rest,
|
|
1372
1454
|
links: sortByName(t2.links),
|
|
1373
1455
|
indexes: [...t2.indexes].sort((a, b) => (a.name ?? a.columns.join("_")).localeCompare(b.name ?? b.columns.join("_"))),
|
|
1374
1456
|
actions: sortByName(t2.actions),
|
|
1457
|
+
...foreignKeys?.length ? { foreignKeys: sortByName(foreignKeys) } : {},
|
|
1375
1458
|
// Omitted entirely when absent/empty so pre-CoW documents hash unchanged.
|
|
1376
1459
|
...cowMutations !== void 0 && cowMutations.length > 0 ? { cowMutations: sortByName(cowMutations) } : {}
|
|
1377
1460
|
};
|
|
@@ -1645,6 +1728,22 @@ function buildCanonicalTable({ table, relationsByName, enumsByName }) {
|
|
|
1645
1728
|
} else if (columnLevelPk.length > 0) {
|
|
1646
1729
|
candidate.primaryKey = columnLevelPk;
|
|
1647
1730
|
}
|
|
1731
|
+
if (table.config.foreignKeys?.length) {
|
|
1732
|
+
if (table.source) throw new OrmDefinitionError(`synced table "${table.tableName}" cannot enforce foreign keys`, table.file);
|
|
1733
|
+
candidate.foreignKeys = table.config.foreignKeys.map((fk) => {
|
|
1734
|
+
const target = resolveRelationRef({ ref: fk.target, fail: (message) => {
|
|
1735
|
+
throw new OrmDefinitionError(message, table.file);
|
|
1736
|
+
} });
|
|
1737
|
+
if (!isSeqTable(target) || !relationsByName.has(target.tableName)) throw new OrmDefinitionError("foreign key target must be a table in this namespace", table.file);
|
|
1738
|
+
return {
|
|
1739
|
+
name: fk.name,
|
|
1740
|
+
columns: fk.columns.map((ref) => resolveColumnRef({ relation: table, ref, role: `foreign key "${fk.name}" column` })),
|
|
1741
|
+
target: target.tableName,
|
|
1742
|
+
references: fk.references.map((ref) => resolveColumnRef({ relation: target, ref, role: `foreign key "${fk.name}" reference` })),
|
|
1743
|
+
...fk.deferrable ? { deferrable: fk.deferrable } : {}
|
|
1744
|
+
};
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1648
1747
|
for (const index3 of table.config.indexes ?? []) {
|
|
1649
1748
|
const entry = {
|
|
1650
1749
|
columns: index3.columns.map((ref) => resolveColumnRef({ relation: table, ref, role: "index" }))
|
|
@@ -1697,6 +1796,12 @@ function buildCanonicalView({
|
|
|
1697
1796
|
if (column.default !== void 0 || column.defaultFn !== void 0) {
|
|
1698
1797
|
throw new OrmDefinitionError(`view "${view.viewName}": column "${propertyName}" cannot declare a default`, view.file);
|
|
1699
1798
|
}
|
|
1799
|
+
if (column.onUpdateFn !== void 0) {
|
|
1800
|
+
throw new OrmDefinitionError(
|
|
1801
|
+
`view "${view.viewName}": column "${propertyName}" cannot declare onUpdateFn`,
|
|
1802
|
+
view.file
|
|
1803
|
+
);
|
|
1804
|
+
}
|
|
1700
1805
|
if (column.unique) {
|
|
1701
1806
|
throw new OrmDefinitionError(`view "${view.viewName}": column "${propertyName}" cannot declare unique`, view.file);
|
|
1702
1807
|
}
|
|
@@ -1957,6 +2062,10 @@ async function loadNamespaceFromDir(dir, options = {}) {
|
|
|
1957
2062
|
fsCache: false,
|
|
1958
2063
|
...options.ormModulePath !== void 0 ? {
|
|
1959
2064
|
alias: {
|
|
2065
|
+
// The public /drizzle barrel re-exports this pinned dependency.
|
|
2066
|
+
// Explicitly map it before the root alias: otherwise jiti appends
|
|
2067
|
+
// "/drizzle" to the overridden module's filename.
|
|
2068
|
+
"@sequenceholdings/orm/drizzle": createRequire(import.meta.url).resolve("drizzle-orm"),
|
|
1960
2069
|
"@sequenceholdings/orm": options.ormModulePath,
|
|
1961
2070
|
"@sequenceholdings/orm/authoring": options.ormModulePath
|
|
1962
2071
|
}
|
|
@@ -2519,8 +2628,8 @@ var actionSchema = z.strictObject({
|
|
|
2519
2628
|
}
|
|
2520
2629
|
}
|
|
2521
2630
|
});
|
|
2522
|
-
function extractNamedParams(
|
|
2523
|
-
const withoutStrings =
|
|
2631
|
+
function extractNamedParams(sql5) {
|
|
2632
|
+
const withoutStrings = sql5.replace(QUOTED_STRING_PATTERN, "''");
|
|
2524
2633
|
const names = [...withoutStrings.matchAll(NAMED_PARAM_PATTERN)].map((match) => match[1]);
|
|
2525
2634
|
return [...new Set(names)];
|
|
2526
2635
|
}
|
|
@@ -2677,8 +2786,8 @@ var actionParamArg = (name) => `p_${name}`;
|
|
|
2677
2786
|
function compileActionStatements(action, renderParam = actionParamArg) {
|
|
2678
2787
|
return actionStatementSources(action.executes).map((statement) => compileStatement(statement, renderParam));
|
|
2679
2788
|
}
|
|
2680
|
-
function compileStatement(
|
|
2681
|
-
const compiled =
|
|
2789
|
+
function compileStatement(sql5, renderParam) {
|
|
2790
|
+
const compiled = sql5.replace(TOKEN_PATTERN, (token, claimKey, capabilityKey, paramName) => {
|
|
2682
2791
|
if (claimKey !== void 0) {
|
|
2683
2792
|
if (!CLAIM_KEY_PATTERN.test(claimKey)) throw new OrmDefinitionError(`invalid claim key in executes: ${token}`);
|
|
2684
2793
|
return claimExpression(claimKey);
|
|
@@ -2843,8 +2952,8 @@ function actionPrepareChecks(definition) {
|
|
|
2843
2952
|
const order = actionParamOrder(action);
|
|
2844
2953
|
const signature = order.length > 0 ? ` (${order.map((param) => sqlTypeName(action.params[param].type)).join(", ")})` : "";
|
|
2845
2954
|
const compiled = compileActionStatements(action, (param) => `$${order.indexOf(param) + 1}`);
|
|
2846
|
-
compiled.forEach((
|
|
2847
|
-
const checked = action.returns && index3 === compiled.length - 1 ? returnProjection(action.returns,
|
|
2955
|
+
compiled.forEach((sql5, index3) => {
|
|
2956
|
+
const checked = action.returns && index3 === compiled.length - 1 ? returnProjection(action.returns, sql5) : sql5;
|
|
2848
2957
|
statements.push(`PREPARE ${quoteIdentifier(`orm_check_${name}_${index3}`)}${signature} AS ${checked}`);
|
|
2849
2958
|
});
|
|
2850
2959
|
}
|
|
@@ -3298,7 +3407,8 @@ function syncedViewStatements({
|
|
|
3298
3407
|
readPolicyIrByTable,
|
|
3299
3408
|
columnNamesByTable,
|
|
3300
3409
|
columnTypesByTable,
|
|
3301
|
-
executionOwnerRole = SYNCED_VIEW_EXECUTION_OWNER_ROLE
|
|
3410
|
+
executionOwnerRole = SYNCED_VIEW_EXECUTION_OWNER_ROLE,
|
|
3411
|
+
backingPrivileges = "grant"
|
|
3302
3412
|
}) {
|
|
3303
3413
|
const schema = quoteIdentifier(ormSchemaName(definition.namespace));
|
|
3304
3414
|
const role = quoteIdentifier(consumerRoleName(definition.namespace));
|
|
@@ -3332,8 +3442,10 @@ function syncedViewStatements({
|
|
|
3332
3442
|
columnTypes,
|
|
3333
3443
|
columnNames: columnNamesByTable?.get(table.table)
|
|
3334
3444
|
})}` : readPolicy ? ` WHERE ${compilePredicate(readPolicy.ast, narrowToV1ColumnTypes(columnTypes))}` : " WHERE false";
|
|
3335
|
-
|
|
3336
|
-
|
|
3445
|
+
if (backingPrivileges === "grant") {
|
|
3446
|
+
statements.push(`GRANT USAGE ON SCHEMA ${quoteIdentifier(physical.schema)} TO ${owner}`);
|
|
3447
|
+
statements.push(`GRANT SELECT ON ${physicalTarget} TO ${owner}`);
|
|
3448
|
+
}
|
|
3337
3449
|
statements.push(
|
|
3338
3450
|
`CREATE OR REPLACE VIEW ${viewName} AS SELECT * FROM ${physicalTarget}${whereClause}`
|
|
3339
3451
|
);
|
|
@@ -3622,7 +3734,7 @@ var FACTORIES2 = {
|
|
|
3622
3734
|
var SCHEMA_IMPORT_KEY2 = "$schema";
|
|
3623
3735
|
var enumImportKey = (name) => `$enum_${name}`;
|
|
3624
3736
|
var columnTablesV2 = (definition) => definition.tables.filter((table) => !table.source);
|
|
3625
|
-
function buildDrizzleEntitiesV2(definition) {
|
|
3737
|
+
function buildDrizzleEntitiesV2(definition, options = {}) {
|
|
3626
3738
|
const schemaName = ormSchemaName(definition.namespace);
|
|
3627
3739
|
const schema = pgSchema(schemaName);
|
|
3628
3740
|
const imports = { [SCHEMA_IMPORT_KEY2]: schema };
|
|
@@ -3637,11 +3749,19 @@ function buildDrizzleEntitiesV2(definition) {
|
|
|
3637
3749
|
}
|
|
3638
3750
|
const byName = new Map(definition.tables.map((table) => [table.table, table]));
|
|
3639
3751
|
for (const table of sortByEnforcedLinks2(columnTablesV2(definition))) {
|
|
3640
|
-
const drizzleTable = buildTable2(schema, table, built, enums, byName);
|
|
3752
|
+
const drizzleTable = buildTable2(schema, table, built, enums, byName, options.introspection ?? false);
|
|
3641
3753
|
built.set(table.table, drizzleTable);
|
|
3642
3754
|
imports[table.table] = drizzleTable;
|
|
3643
3755
|
tables[table.table] = drizzleTable;
|
|
3644
3756
|
}
|
|
3757
|
+
if (options.introspection) {
|
|
3758
|
+
for (const table of definition.tables.filter((table2) => table2.source)) {
|
|
3759
|
+
const columns = Object.fromEntries(
|
|
3760
|
+
table.columns.map((column) => [column.columnName, buildViewColumnV2({ view: table.table, column, enums })])
|
|
3761
|
+
);
|
|
3762
|
+
imports[table.table] = schema.view(table.table, columns).existing();
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3645
3765
|
for (const view of definition.views ?? []) {
|
|
3646
3766
|
const columns = Object.fromEntries(
|
|
3647
3767
|
view.columns.map((column) => [column.columnName, buildViewColumnV2({ view: view.view, column, enums })])
|
|
@@ -3709,18 +3829,18 @@ function rewriteSyncedSourceRelationsInViewQuery({
|
|
|
3709
3829
|
return rewritten;
|
|
3710
3830
|
});
|
|
3711
3831
|
}
|
|
3712
|
-
function transformSqlOutsideLiterals(
|
|
3832
|
+
function transformSqlOutsideLiterals(sql5, transform) {
|
|
3713
3833
|
const out = [];
|
|
3714
3834
|
let state = { kind: "normal" };
|
|
3715
3835
|
let i = 0;
|
|
3716
3836
|
let codeStart = 0;
|
|
3717
3837
|
const emitCode = (end) => {
|
|
3718
|
-
if (end > codeStart) out.push(transform(
|
|
3838
|
+
if (end > codeStart) out.push(transform(sql5.slice(codeStart, end)));
|
|
3719
3839
|
codeStart = end;
|
|
3720
3840
|
};
|
|
3721
|
-
while (i <
|
|
3722
|
-
const c =
|
|
3723
|
-
const next = i + 1 <
|
|
3841
|
+
while (i < sql5.length) {
|
|
3842
|
+
const c = sql5[i];
|
|
3843
|
+
const next = i + 1 < sql5.length ? sql5[i + 1] : "";
|
|
3724
3844
|
switch (state.kind) {
|
|
3725
3845
|
case "normal": {
|
|
3726
3846
|
if (c === "-" && next === "-") {
|
|
@@ -3740,7 +3860,7 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3740
3860
|
break;
|
|
3741
3861
|
}
|
|
3742
3862
|
if ((c === "E" || c === "e") && next === "'") {
|
|
3743
|
-
const prev = i > 0 ?
|
|
3863
|
+
const prev = i > 0 ? sql5[i - 1] : "";
|
|
3744
3864
|
if (!/[A-Za-z0-9_]/.test(prev)) {
|
|
3745
3865
|
emitCode(i);
|
|
3746
3866
|
state = { kind: "e-single" };
|
|
@@ -3759,9 +3879,9 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3759
3879
|
break;
|
|
3760
3880
|
}
|
|
3761
3881
|
if (c === "$") {
|
|
3762
|
-
const prev = i > 0 ?
|
|
3882
|
+
const prev = i > 0 ? sql5[i - 1] : "";
|
|
3763
3883
|
if (!/[A-Za-z0-9_$]/.test(prev)) {
|
|
3764
|
-
const tagMatch = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(
|
|
3884
|
+
const tagMatch = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(sql5.slice(i));
|
|
3765
3885
|
if (tagMatch) {
|
|
3766
3886
|
emitCode(i);
|
|
3767
3887
|
state = { kind: "dollar", tag: tagMatch[1] ?? "" };
|
|
@@ -3793,8 +3913,8 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3793
3913
|
break;
|
|
3794
3914
|
}
|
|
3795
3915
|
case "e-single": {
|
|
3796
|
-
if (c === "\\" && i + 1 <
|
|
3797
|
-
out.push(c,
|
|
3916
|
+
if (c === "\\" && i + 1 < sql5.length) {
|
|
3917
|
+
out.push(c, sql5[i + 1]);
|
|
3798
3918
|
i += 2;
|
|
3799
3919
|
break;
|
|
3800
3920
|
}
|
|
@@ -3816,7 +3936,7 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3816
3936
|
}
|
|
3817
3937
|
case "dollar": {
|
|
3818
3938
|
const closer = `$${state.tag}$`;
|
|
3819
|
-
if (
|
|
3939
|
+
if (sql5.startsWith(closer, i)) {
|
|
3820
3940
|
out.push(closer);
|
|
3821
3941
|
state = { kind: "normal" };
|
|
3822
3942
|
i += closer.length;
|
|
@@ -3860,15 +3980,15 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3860
3980
|
}
|
|
3861
3981
|
}
|
|
3862
3982
|
}
|
|
3863
|
-
emitCode(
|
|
3983
|
+
emitCode(sql5.length);
|
|
3864
3984
|
return out.join("");
|
|
3865
3985
|
}
|
|
3866
3986
|
function escapeRegExp(value) {
|
|
3867
3987
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3868
3988
|
}
|
|
3869
|
-
function sqlReferencesQualifiedRelation(
|
|
3989
|
+
function sqlReferencesQualifiedRelation(sql5, sourceSchema, sourceName) {
|
|
3870
3990
|
let referenced = false;
|
|
3871
|
-
transformSqlOutsideLiterals(
|
|
3991
|
+
transformSqlOutsideLiterals(sql5, (segment) => {
|
|
3872
3992
|
if (qualifiedRelationPattern(sourceSchema, sourceName).test(segment)) referenced = true;
|
|
3873
3993
|
return segment;
|
|
3874
3994
|
});
|
|
@@ -3879,15 +3999,16 @@ function qualifiedRelationPattern(sourceSchema, sourceName) {
|
|
|
3879
3999
|
const namePattern = `(?:"${escapeRegExp(sourceName)}"|${escapeRegExp(sourceName)})`;
|
|
3880
4000
|
return new RegExp(`(?<![\\w."])${schemaPattern}\\.${namePattern}(?![\\w"])`, "g");
|
|
3881
4001
|
}
|
|
3882
|
-
function replaceQualifiedRelation(
|
|
3883
|
-
return
|
|
4002
|
+
function replaceQualifiedRelation(sql5, sourceSchema, sourceName, boundSchema, boundTable) {
|
|
4003
|
+
return sql5.replace(qualifiedRelationPattern(sourceSchema, sourceName), `"${boundSchema}"."${boundTable}"`);
|
|
3884
4004
|
}
|
|
3885
4005
|
function generateSnapshotV2(definition, previousId3) {
|
|
3886
4006
|
const { imports, schemaName } = buildDrizzleEntitiesV2(definition);
|
|
3887
4007
|
const snapshot = generateDrizzleJson(imports, previousId3, [schemaName]);
|
|
3888
|
-
if ((definition.views?.length ?? 0) > 0) {
|
|
4008
|
+
if ((definition.views?.length ?? 0) > 0 || definition.tables.some((table) => table.foreignKeys?.length)) {
|
|
3889
4009
|
const extended = snapshot;
|
|
3890
4010
|
extended._sequenceOrm = {
|
|
4011
|
+
...definition.tables.some((table) => table.foreignKeys?.length) ? { foreignKeys: Object.fromEntries(definition.tables.flatMap((table) => (table.foreignKeys ?? []).map((fk) => [`${table.table}.${fk.name}`, structuredClone(fk)]))) } : {},
|
|
3891
4012
|
viewDependencies: Object.fromEntries(
|
|
3892
4013
|
(definition.views ?? []).map((view) => [view.view, [...view.dependencies].sort()])
|
|
3893
4014
|
)
|
|
@@ -3905,7 +4026,7 @@ function buildViewColumnV2({
|
|
|
3905
4026
|
if (!column.nullable) builder = builder.notNull();
|
|
3906
4027
|
return builder;
|
|
3907
4028
|
}
|
|
3908
|
-
function buildTable2(schema, table, built, enums, byName) {
|
|
4029
|
+
function buildTable2(schema, table, built, enums, byName, introspection) {
|
|
3909
4030
|
const primaryKeyColumns = table.primaryKey ?? [];
|
|
3910
4031
|
if (primaryKeyColumns.length === 0) {
|
|
3911
4032
|
throw new OrmDefinitionError(`table '${table.table}' declares no primaryKey`);
|
|
@@ -3921,13 +4042,22 @@ function buildTable2(schema, table, built, enums, byName) {
|
|
|
3921
4042
|
const columns = Object.fromEntries(
|
|
3922
4043
|
table.columns.map((column) => [
|
|
3923
4044
|
column.columnName,
|
|
3924
|
-
buildColumnV2({ table: table.table, column, singlePrimaryKey, compositeMembers, enums })
|
|
4045
|
+
buildColumnV2({ table: table.table, column, singlePrimaryKey, compositeMembers, enums, introspection })
|
|
3925
4046
|
])
|
|
3926
4047
|
);
|
|
3927
4048
|
const drizzleTable = schema.table(table.table, columns, (self) => [
|
|
3928
4049
|
...buildCompositePrimaryKey(table, primaryKeyColumns, self),
|
|
3929
4050
|
...buildIndexes2(table, self),
|
|
3930
|
-
...buildForeignKeys2(table, self, built, byName)
|
|
4051
|
+
...buildForeignKeys2(table, self, built, byName),
|
|
4052
|
+
...(table.foreignKeys ?? []).map((fk) => {
|
|
4053
|
+
const target = fk.target === table.table ? self : built.get(fk.target);
|
|
4054
|
+
if (!target) throw new OrmDefinitionError(`foreign key '${fk.name}' has no target '${fk.target}'`);
|
|
4055
|
+
return foreignKey({
|
|
4056
|
+
name: fk.name,
|
|
4057
|
+
columns: fk.columns.map((column) => self[column]),
|
|
4058
|
+
foreignColumns: fk.references.map((column) => target[column])
|
|
4059
|
+
});
|
|
4060
|
+
})
|
|
3931
4061
|
]);
|
|
3932
4062
|
return drizzleTable.enableRLS();
|
|
3933
4063
|
}
|
|
@@ -3944,7 +4074,8 @@ function buildColumnV2({
|
|
|
3944
4074
|
column,
|
|
3945
4075
|
singlePrimaryKey,
|
|
3946
4076
|
compositeMembers,
|
|
3947
|
-
enums
|
|
4077
|
+
enums,
|
|
4078
|
+
introspection
|
|
3948
4079
|
}) {
|
|
3949
4080
|
const factory = resolveColumnFactory(table, column, enums);
|
|
3950
4081
|
let builder = factory(column.columnName);
|
|
@@ -3965,7 +4096,7 @@ function buildColumnV2({
|
|
|
3965
4096
|
}
|
|
3966
4097
|
builder = builder.default(sql.raw(column.defaultFn));
|
|
3967
4098
|
} else if (column.default !== void 0 && column.default !== null) {
|
|
3968
|
-
builder = builder.default(column.default);
|
|
4099
|
+
builder = introspection && column.type === "jsonb" && typeof column.default === "string" ? builder.default(sql.raw(`${quoteLiteral(column.default)}::jsonb`)) : builder.default(column.default);
|
|
3969
4100
|
}
|
|
3970
4101
|
return builder;
|
|
3971
4102
|
}
|
|
@@ -4150,7 +4281,8 @@ async function planMigrationV2({
|
|
|
4150
4281
|
assertValidViewDependencies(canonical);
|
|
4151
4282
|
const hints = applyMigrationHintsV2(previous, canonical);
|
|
4152
4283
|
const snapshot = generateSnapshotV2(canonical, previousId(previous));
|
|
4153
|
-
const
|
|
4284
|
+
const generatedBaseDdl = await diffStatements(withoutViews(hints.adjusted), withoutViews(snapshot));
|
|
4285
|
+
const baseDdl = orderForeignKeyAdditions(canonical, generatedBaseDdl);
|
|
4154
4286
|
const previousViews = snapshotViews(hints.adjusted);
|
|
4155
4287
|
const currentViews = snapshotViews(snapshot);
|
|
4156
4288
|
const previousDependencies = snapshotViewDependencies(hints.adjusted);
|
|
@@ -4187,6 +4319,7 @@ async function planMigrationV2({
|
|
|
4187
4319
|
...dropOrder.map((name) => ({ kind: "ddl", sql: requiredViewStatement(dropDdl, name, schema, "DROP") })),
|
|
4188
4320
|
...hints.statements.map((statement) => ({ kind: "hint", sql: statement })),
|
|
4189
4321
|
...baseDdl.map((statement) => ({ kind: "ddl", sql: statement.replace(/;\s*$/, "") })),
|
|
4322
|
+
...foreignKeyTimingStatements(canonical, previous, baseDdl).map((sql5) => ({ kind: "ddl", sql: sql5 })),
|
|
4190
4323
|
...createOrder.map((name) => ({ kind: "ddl", sql: requiredViewStatement(createDdl, name, schema, "CREATE") })),
|
|
4191
4324
|
...createOrder.map((name) => {
|
|
4192
4325
|
const view = currentViewDefinitions.get(name);
|
|
@@ -4196,6 +4329,29 @@ async function planMigrationV2({
|
|
|
4196
4329
|
];
|
|
4197
4330
|
return { namespace: canonical.namespace, schema, statements, snapshot };
|
|
4198
4331
|
}
|
|
4332
|
+
function orderForeignKeyAdditions(canonical, statements) {
|
|
4333
|
+
if (!canonical.tables.some((table) => table.foreignKeys?.length)) return [...statements];
|
|
4334
|
+
const addsForeignKey = (statement) => /^ALTER TABLE[\s\S]* ADD CONSTRAINT[\s\S]* FOREIGN KEY\b/.test(statement);
|
|
4335
|
+
return [...statements.filter((statement) => !addsForeignKey(statement)), ...statements.filter(addsForeignKey)];
|
|
4336
|
+
}
|
|
4337
|
+
function foreignKeyTimingStatements(canonical, previous, baseDdl) {
|
|
4338
|
+
const prior = previous._sequenceOrm?.foreignKeys ?? {};
|
|
4339
|
+
const result = [];
|
|
4340
|
+
for (const table of canonical.tables) {
|
|
4341
|
+
for (const fk of table.foreignKeys ?? []) {
|
|
4342
|
+
const before = prior[`${table.table}.${fk.name}`];
|
|
4343
|
+
const recreated = baseDdl.some((statement) => statement.includes(`ALTER TABLE ${quoteIdentifier(ormSchemaName(canonical.namespace))}.${quoteIdentifier(table.table)} ADD CONSTRAINT ${quoteIdentifier(fk.name)}`));
|
|
4344
|
+
if (fk.deferrable ? !before || before.deferrable !== fk.deferrable || recreated : before?.deferrable && !recreated) {
|
|
4345
|
+
result.push(foreignKeyTimingSql(ormSchemaName(canonical.namespace), table.table, fk.name, fk.deferrable));
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
return result;
|
|
4350
|
+
}
|
|
4351
|
+
function foreignKeyTimingSql(schema, table, name, timing) {
|
|
4352
|
+
const clause = timing === void 0 ? "NOT DEFERRABLE" : timing === "initially-deferred" ? "DEFERRABLE INITIALLY DEFERRED" : "DEFERRABLE INITIALLY IMMEDIATE";
|
|
4353
|
+
return `ALTER TABLE ${quoteIdentifier(schema)}.${quoteIdentifier(table)} ALTER CONSTRAINT ${quoteIdentifier(name)} ${clause}`;
|
|
4354
|
+
}
|
|
4199
4355
|
function validateViewCatalogStatement({
|
|
4200
4356
|
canonical,
|
|
4201
4357
|
view,
|
|
@@ -4450,7 +4606,63 @@ async function pendingKitStatementsV2({
|
|
|
4450
4606
|
canonical,
|
|
4451
4607
|
db
|
|
4452
4608
|
}) {
|
|
4453
|
-
|
|
4609
|
+
const { desired, defaultDrift } = await normalizeJsonDefaultsForIntrospection(canonical, db);
|
|
4610
|
+
const entities = buildDrizzleEntitiesV2(desired, { introspection: true });
|
|
4611
|
+
const pending = await pendingStatements({ entities, db, owned: V2_OWNED_STATEMENT });
|
|
4612
|
+
return [...orderForeignKeyAdditions(canonical, pending), ...defaultDrift, ...await pendingForeignKeyTimingStatements(canonical, db)];
|
|
4613
|
+
}
|
|
4614
|
+
async function normalizeJsonDefaultsForIntrospection(canonical, db) {
|
|
4615
|
+
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 }] : []));
|
|
4616
|
+
if (defaults.length === 0) return { desired: canonical, defaultDrift: [] };
|
|
4617
|
+
const array = (values) => sql`ARRAY[${sql.join(values.map((value) => sql`${value}`), sql`, `)}]::text[]`;
|
|
4618
|
+
const schema = ormSchemaName(canonical.namespace);
|
|
4619
|
+
const result = await shimExecute(db).execute(sql`
|
|
4620
|
+
SELECT desired.ordinal, desired.value::jsonb::text AS normalized,
|
|
4621
|
+
attribute.attnum IS NOT NULL AS column_exists,
|
|
4622
|
+
coalesce(pg_get_expr(ad.adbin, ad.adrelid) = quote_literal(desired.value::jsonb::text) || '::jsonb', false) AS matches
|
|
4623
|
+
FROM unnest(${array(defaults.map((item) => item.table))}, ${array(defaults.map((item) => item.column))}, ${array(defaults.map((item) => item.value))})
|
|
4624
|
+
WITH ORDINALITY AS desired(table_name, column_name, value, ordinal)
|
|
4625
|
+
LEFT JOIN pg_catalog.pg_namespace namespace ON namespace.nspname = ${schema}
|
|
4626
|
+
LEFT JOIN pg_catalog.pg_class relation ON relation.relnamespace = namespace.oid AND relation.relname = desired.table_name
|
|
4627
|
+
LEFT JOIN pg_catalog.pg_attribute attribute ON attribute.attrelid = relation.oid AND attribute.attname = desired.column_name AND NOT attribute.attisdropped
|
|
4628
|
+
LEFT JOIN pg_catalog.pg_attrdef ad ON ad.adrelid = relation.oid AND ad.adnum = attribute.attnum
|
|
4629
|
+
ORDER BY desired.ordinal
|
|
4630
|
+
`);
|
|
4631
|
+
const rows = result.rows;
|
|
4632
|
+
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")) {
|
|
4633
|
+
throw new Error("invalid JSON default introspection result");
|
|
4634
|
+
}
|
|
4635
|
+
const desired = structuredClone(canonical);
|
|
4636
|
+
const defaultDrift = [];
|
|
4637
|
+
defaults.forEach((item, index3) => {
|
|
4638
|
+
const row = rows[index3];
|
|
4639
|
+
desired.tables[item.tableIndex].columns[item.columnIndex].default = JSON.stringify(JSON.parse(row.normalized));
|
|
4640
|
+
if (row.column_exists && !row.matches) defaultDrift.push(
|
|
4641
|
+
`ALTER TABLE ${quoteIdentifier(schema)}.${quoteIdentifier(item.table)} ALTER COLUMN ${quoteIdentifier(item.column)} SET DEFAULT ${quoteLiteral(item.value)}::jsonb;`
|
|
4642
|
+
);
|
|
4643
|
+
});
|
|
4644
|
+
return { desired, defaultDrift };
|
|
4645
|
+
}
|
|
4646
|
+
async function pendingForeignKeyTimingStatements(canonical, db) {
|
|
4647
|
+
const keys = canonical.tables.flatMap((table) => (table.foreignKeys ?? []).map((fk) => ({ table: table.table, fk })));
|
|
4648
|
+
if (keys.length === 0) return [];
|
|
4649
|
+
const schema = ormSchemaName(canonical.namespace);
|
|
4650
|
+
const result = await shimExecute(db).execute(sql`
|
|
4651
|
+
SELECT relation.relname AS table_name, constraint_row.conname AS name,
|
|
4652
|
+
constraint_row.condeferrable AS deferrable, constraint_row.condeferred AS deferred
|
|
4653
|
+
FROM pg_catalog.pg_constraint AS constraint_row
|
|
4654
|
+
JOIN pg_catalog.pg_class AS relation ON relation.oid = constraint_row.conrelid
|
|
4655
|
+
JOIN pg_catalog.pg_namespace AS namespace ON namespace.oid = relation.relnamespace
|
|
4656
|
+
WHERE namespace.nspname = ${schema} AND constraint_row.contype = 'f'
|
|
4657
|
+
`);
|
|
4658
|
+
const rows = result.rows;
|
|
4659
|
+
return keys.flatMap(({ table, fk }) => {
|
|
4660
|
+
const actual = rows.find((row) => row.table_name === table && row.name === fk.name);
|
|
4661
|
+
const deferrable = fk.deferrable !== void 0;
|
|
4662
|
+
const deferred = fk.deferrable === "initially-deferred";
|
|
4663
|
+
if (actual ? actual.deferrable === deferrable && actual.deferred === deferred : !deferrable) return [];
|
|
4664
|
+
return [foreignKeyTimingSql(schema, table, fk.name, fk.deferrable)];
|
|
4665
|
+
});
|
|
4454
4666
|
}
|
|
4455
4667
|
async function pendingStatements({
|
|
4456
4668
|
entities,
|
|
@@ -4463,11 +4675,31 @@ async function pendingStatements({
|
|
|
4463
4675
|
);
|
|
4464
4676
|
return statementsToExecute.filter((statement) => !owned.test(statement));
|
|
4465
4677
|
}
|
|
4678
|
+
function correctIntrospectionQuery(query) {
|
|
4679
|
+
if (!is(query, SQL)) return query;
|
|
4680
|
+
const compiled = new PgDialect().sqlToQuery(query);
|
|
4681
|
+
let text4 = compiled.sql;
|
|
4682
|
+
const indexJoin = "pg_constraint con ON con.conindid = idx.indexrelid";
|
|
4683
|
+
if (text4.includes("AS generated_by_constraint") && text4.includes(indexJoin)) {
|
|
4684
|
+
text4 = text4.replace(indexJoin, `${indexJoin} AND con.contype IN ('p', 'u', 'x')`);
|
|
4685
|
+
}
|
|
4686
|
+
const sourceJoin = "LEFT JOIN pg_catalog.pg_attribute att ON att.attnum = ANY (con.conkey)";
|
|
4687
|
+
const targetJoin = "LEFT JOIN pg_catalog.pg_attribute fatt ON fatt.attnum = ANY (con.confkey)";
|
|
4688
|
+
if (text4.includes(sourceJoin) && text4.includes(targetJoin) && text4.includes("AND con.contype IN ('f')")) {
|
|
4689
|
+
text4 = text4.replace(
|
|
4690
|
+
sourceJoin,
|
|
4691
|
+
"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"
|
|
4692
|
+
).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";
|
|
4693
|
+
}
|
|
4694
|
+
if (text4 === compiled.sql) return query;
|
|
4695
|
+
if (compiled.params.length !== 0) throw new Error("parameterized Drizzle catalog query cannot be normalized");
|
|
4696
|
+
return sql.raw(text4);
|
|
4697
|
+
}
|
|
4466
4698
|
function shimExecute(db) {
|
|
4467
4699
|
const execute = db.execute.bind(db);
|
|
4468
4700
|
return {
|
|
4469
4701
|
execute: async (query) => {
|
|
4470
|
-
const result = await execute(query);
|
|
4702
|
+
const result = await execute(correctIntrospectionQuery(query));
|
|
4471
4703
|
if (result && typeof result === "object" && "rows" in result) return result;
|
|
4472
4704
|
return { rows: result };
|
|
4473
4705
|
}
|
|
@@ -4507,21 +4739,73 @@ async function planMigration({
|
|
|
4507
4739
|
const ddl = await diffStatements(hints.adjusted, snapshot);
|
|
4508
4740
|
const policyDrops = policyDropStatements(schema, snapshotTableNames(previous, schema));
|
|
4509
4741
|
const statements = [
|
|
4510
|
-
...policyDrops.map((
|
|
4511
|
-
...hints.statements.map((
|
|
4512
|
-
...ddl.map((
|
|
4513
|
-
...securityStatements(definition).map((
|
|
4742
|
+
...policyDrops.map((sql5) => ({ kind: "security", sql: sql5 })),
|
|
4743
|
+
...hints.statements.map((sql5) => ({ kind: "hint", sql: sql5 })),
|
|
4744
|
+
...ddl.map((sql5) => ({ kind: "ddl", sql: sql5.replace(/;\s*$/, "") })),
|
|
4745
|
+
...securityStatements(definition).map((sql5) => ({ kind: "security", sql: sql5 })),
|
|
4514
4746
|
// PREPARE each action's executes first (plpgsql bodies don't check columns
|
|
4515
4747
|
// at CREATE), then create the functions. Both reference the tables the DDL
|
|
4516
4748
|
// above creates, so they close the plan.
|
|
4517
|
-
...actionPrepareChecks(definition).map((
|
|
4518
|
-
...actionFunctionStatements(definition).map((
|
|
4749
|
+
...actionPrepareChecks(definition).map((sql5) => ({ kind: "function", sql: sql5 })),
|
|
4750
|
+
...actionFunctionStatements(definition).map((sql5) => ({ kind: "function", sql: sql5 }))
|
|
4519
4751
|
];
|
|
4520
4752
|
return { namespace: definition.namespace, schema, statements, snapshot };
|
|
4521
4753
|
}
|
|
4522
4754
|
var compileNamespace = (definition) => planMigration({ definition });
|
|
4523
4755
|
var previousId2 = (snapshot) => snapshot.id;
|
|
4524
|
-
var
|
|
4756
|
+
var ORM_MIGRATION_EXPORT_FILE = "orm.migration.json";
|
|
4757
|
+
var ORM_MIGRATION_EXPORT_MAX_BYTES = ORM_REGISTRATION_MAX_BYTES;
|
|
4758
|
+
var hash = z.string().regex(/^sha256:[0-9a-f]{64}$/);
|
|
4759
|
+
var ormMigrationPayloadSchema = z.object({
|
|
4760
|
+
canonical_definition: canonicalDefinitionSchema,
|
|
4761
|
+
documents: z.array(z.object({ path: z.string().min(1).max(2048), contents: z.string().max(1e6) }).strict()).max(5e3),
|
|
4762
|
+
client_manifest: z.object({ ops_hash: hash, operations: z.record(z.string(), hash) }).strict(),
|
|
4763
|
+
sdl: z.string().refine((value) => Buffer.byteLength(value, "utf8") <= MAX_GENERATED_SDL_BYTES, {
|
|
4764
|
+
message: `sdl exceeds ${MAX_GENERATED_SDL_BYTES} bytes`
|
|
4765
|
+
}),
|
|
4766
|
+
schema_snapshot: z.json(),
|
|
4767
|
+
// The existing ORM committed-migration validator and replay engine remain
|
|
4768
|
+
// authoritative. This envelope hashes the provenance, never authorizes SQL.
|
|
4769
|
+
migration: z.json().optional()
|
|
4770
|
+
}).strict();
|
|
4771
|
+
var ormMigrationExportSchema = z.object({
|
|
4772
|
+
schema_version: z.literal(1),
|
|
4773
|
+
schema_hash: hash,
|
|
4774
|
+
payload: ormMigrationPayloadSchema,
|
|
4775
|
+
checksum: z.string().regex(/^[0-9a-f]{64}$/)
|
|
4776
|
+
}).strict();
|
|
4777
|
+
function checksum(value) {
|
|
4778
|
+
return createHash("sha256").update(stableJson(value)).digest("hex");
|
|
4779
|
+
}
|
|
4780
|
+
function createOrmMigrationExport({ schemaHash, payload }) {
|
|
4781
|
+
const contents = { schema_version: 1, schema_hash: hash.parse(schemaHash), payload: ormMigrationPayloadSchema.parse(payload) };
|
|
4782
|
+
return { ...contents, checksum: checksum(contents) };
|
|
4783
|
+
}
|
|
4784
|
+
function parseOrmMigrationExport(text4) {
|
|
4785
|
+
if (Buffer.byteLength(text4, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
|
|
4786
|
+
const exported = ormMigrationExportSchema.parse(JSON.parse(text4));
|
|
4787
|
+
const { checksum: expected, ...contents } = exported;
|
|
4788
|
+
if (checksum(contents) !== expected) throw new Error("ORM migration export checksum mismatch; regenerate and commit it");
|
|
4789
|
+
return exported;
|
|
4790
|
+
}
|
|
4791
|
+
function renderOrmMigrationExport(exported) {
|
|
4792
|
+
const text4 = `${JSON.stringify(exported, null, 2)}
|
|
4793
|
+
`;
|
|
4794
|
+
if (Buffer.byteLength(text4, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
|
|
4795
|
+
return text4;
|
|
4796
|
+
}
|
|
4797
|
+
var IR_VERSION = 2;
|
|
4798
|
+
var LEGACY_IR_VERSION = 1;
|
|
4799
|
+
var irVersionSchema = z.union([z.literal(LEGACY_IR_VERSION), z.literal(IR_VERSION)]);
|
|
4800
|
+
var operationVersionSchema = z.object({ irVersion: irVersionSchema });
|
|
4801
|
+
function requiredArtifactIrVersion(operations) {
|
|
4802
|
+
let required = LEGACY_IR_VERSION;
|
|
4803
|
+
for (const operation of operations) {
|
|
4804
|
+
const { irVersion } = operationVersionSchema.parse(operation);
|
|
4805
|
+
if (irVersion > required) required = irVersion;
|
|
4806
|
+
}
|
|
4807
|
+
return required;
|
|
4808
|
+
}
|
|
4525
4809
|
var CLAIMS_CONTRACT = {
|
|
4526
4810
|
sub: "string",
|
|
4527
4811
|
org: "string?",
|
|
@@ -4648,6 +4932,33 @@ var aggregateRootSchema = z.object({
|
|
|
4648
4932
|
nodes: selectionIrSchema.optional(),
|
|
4649
4933
|
includeIf: includeIfSchema.optional()
|
|
4650
4934
|
});
|
|
4935
|
+
var DATE_BUCKETS = ["year", "quarter", "month", "week", "day", "hour"];
|
|
4936
|
+
var groupByKeyIrSchema = z.object({
|
|
4937
|
+
column: z.string(),
|
|
4938
|
+
bucket: z.enum(DATE_BUCKETS).optional()
|
|
4939
|
+
});
|
|
4940
|
+
var aggregateGroupsRootSchema = z.object({
|
|
4941
|
+
kind: z.literal("aggregateGroups"),
|
|
4942
|
+
alias: z.string(),
|
|
4943
|
+
table: z.string(),
|
|
4944
|
+
/** Composed at compile time: AND(policy, documentWhere?, dynamicFilter?). */
|
|
4945
|
+
where: predIrSchema,
|
|
4946
|
+
groupBy: z.array(groupByKeyIrSchema).min(1),
|
|
4947
|
+
/**
|
|
4948
|
+
* The `key { … }` projection: which group columns appear in each entry's key
|
|
4949
|
+
* object, in selection order. A subset of `groupBy` (the compiler rejects a
|
|
4950
|
+
* key field that is not a group key — it would be indeterminate within the
|
|
4951
|
+
* group). Empty when the document selects only `aggregate`.
|
|
4952
|
+
*/
|
|
4953
|
+
key: z.array(z.object({ alias: z.string(), column: z.string() })).default([]),
|
|
4954
|
+
count: z.object({ columns: z.array(z.string()).optional(), distinct: z.boolean().optional() }).optional(),
|
|
4955
|
+
sum: z.array(z.string()).optional(),
|
|
4956
|
+
avg: z.array(z.string()).optional(),
|
|
4957
|
+
/** Bounds the GROUP count, not the scanned rows — see MAX_GROUPS. */
|
|
4958
|
+
limit: limitIrSchema,
|
|
4959
|
+
offset: z.object({ variable: z.string().optional(), value: z.number().int().optional() }).optional(),
|
|
4960
|
+
includeIf: includeIfSchema.optional()
|
|
4961
|
+
});
|
|
4651
4962
|
var writeValuesSchema = z.union([
|
|
4652
4963
|
z.object({ slot: z.string() }),
|
|
4653
4964
|
z.object({ entries: z.array(z.object({ column: z.string(), value: valueIrSchema })) })
|
|
@@ -4693,12 +5004,15 @@ var mutationRootSchema = z.object({
|
|
|
4693
5004
|
insertCheck: predIrSchema.optional(),
|
|
4694
5005
|
returning: selectionIrSchema.nullable(),
|
|
4695
5006
|
affectedRows: z.boolean().default(false),
|
|
5007
|
+
/** v2: abort the transaction when the actual write count differs, regardless of selection. */
|
|
5008
|
+
assertAffectedRows: z.number().int().min(0).max(2147483647).optional(),
|
|
4696
5009
|
includeIf: includeIfSchema.optional()
|
|
4697
5010
|
});
|
|
4698
5011
|
var rootIrSchema = z.discriminatedUnion("kind", [
|
|
4699
5012
|
selectRootSchema,
|
|
4700
5013
|
byPkRootSchema,
|
|
4701
5014
|
aggregateRootSchema,
|
|
5015
|
+
aggregateGroupsRootSchema,
|
|
4702
5016
|
mutationRootSchema
|
|
4703
5017
|
]);
|
|
4704
5018
|
var INPUT_REF_KINDS = [
|
|
@@ -4728,7 +5042,7 @@ var variableIrSchema = z.object({
|
|
|
4728
5042
|
defaultValue: variableDefaultIrSchema.optional()
|
|
4729
5043
|
});
|
|
4730
5044
|
var compiledOperationSchema = z.object({
|
|
4731
|
-
irVersion:
|
|
5045
|
+
irVersion: irVersionSchema,
|
|
4732
5046
|
name: z.string(),
|
|
4733
5047
|
opHash: z.string().regex(/^sha256:[0-9a-f]{64}$/),
|
|
4734
5048
|
kind: z.enum(["query", "mutation"]),
|
|
@@ -4740,6 +5054,13 @@ var compiledOperationSchema = z.object({
|
|
|
4740
5054
|
/** Every table the op touches — data-token scoping + audit. */
|
|
4741
5055
|
tableSet: z.array(z.string()).min(1),
|
|
4742
5056
|
roots: z.array(rootIrSchema).min(1)
|
|
5057
|
+
}).superRefine((operation, ctx) => {
|
|
5058
|
+
for (const [index3, root] of operation.roots.entries()) {
|
|
5059
|
+
if (root.kind !== "mutation" || root.assertAffectedRows === void 0) continue;
|
|
5060
|
+
if (operation.irVersion < 2 || operation.kind !== "mutation" || root.includeIf !== void 0) {
|
|
5061
|
+
ctx.addIssue({ code: "custom", path: ["roots", index3], message: "affected-row assertions require unconditional mutation roots and IR v2" });
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
4743
5064
|
});
|
|
4744
5065
|
var tableMetaIrSchema = z.object({
|
|
4745
5066
|
table: z.string(),
|
|
@@ -4776,18 +5097,29 @@ var tableMetaIrSchema = z.object({
|
|
|
4776
5097
|
insertable: z.array(z.string()),
|
|
4777
5098
|
settable: z.array(z.string()),
|
|
4778
5099
|
appendable: z.array(z.string()),
|
|
5100
|
+
/**
|
|
5101
|
+
* Logical columns stamped with Postgres `now()` on UPDATE (canonical
|
|
5102
|
+
* `onUpdateFn`). Omitted when empty so pre-onUpdate artifacts still parse.
|
|
5103
|
+
*/
|
|
5104
|
+
touchOnUpdate: z.array(z.string()).optional(),
|
|
4779
5105
|
constraints: z.array(z.object({ name: z.string(), columns: z.array(z.string()) })),
|
|
4780
5106
|
/** Depth/node caps for dynamic filter trees on this table. */
|
|
4781
5107
|
dynamicFilterMaxDepth: z.number().int().positive().default(6),
|
|
4782
5108
|
dynamicFilterMaxNodes: z.number().int().positive().default(64)
|
|
4783
5109
|
});
|
|
4784
5110
|
var compiledArtifactSchema = z.object({
|
|
4785
|
-
irVersion:
|
|
5111
|
+
irVersion: irVersionSchema,
|
|
4786
5112
|
namespace: z.string(),
|
|
4787
5113
|
schemaHash: z.string(),
|
|
4788
5114
|
opsHash: z.string(),
|
|
4789
5115
|
tables: z.record(z.string(), tableMetaIrSchema),
|
|
4790
5116
|
operations: z.record(z.string(), compiledOperationSchema)
|
|
5117
|
+
}).superRefine((artifact, ctx) => {
|
|
5118
|
+
for (const [name, operation] of Object.entries(artifact.operations)) {
|
|
5119
|
+
if (operation.irVersion > artifact.irVersion) {
|
|
5120
|
+
ctx.addIssue({ code: "custom", path: ["operations", name, "irVersion"], message: "operation requires a newer IR version than its artifact" });
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
4791
5123
|
});
|
|
4792
5124
|
var IrVersionError = class extends Error {
|
|
4793
5125
|
constructor(found) {
|
|
@@ -4798,7 +5130,7 @@ var IrVersionError = class extends Error {
|
|
|
4798
5130
|
function normalizeIr(raw) {
|
|
4799
5131
|
if (!raw || typeof raw !== "object") throw new IrVersionError(void 0);
|
|
4800
5132
|
const version = raw.irVersion;
|
|
4801
|
-
if (version !== IR_VERSION) throw new IrVersionError(version);
|
|
5133
|
+
if (version !== LEGACY_IR_VERSION && version !== IR_VERSION) throw new IrVersionError(version);
|
|
4802
5134
|
return compiledArtifactSchema.parse(raw);
|
|
4803
5135
|
}
|
|
4804
5136
|
function indexByHash(artifact) {
|
|
@@ -5114,12 +5446,12 @@ async function writeMigration({
|
|
|
5114
5446
|
const tip = workspace.journal.entries.at(-1);
|
|
5115
5447
|
const ordinal = (tip?.ordinal ?? 0) + 1;
|
|
5116
5448
|
const name = migrationName(ordinal, contentHash);
|
|
5117
|
-
const
|
|
5449
|
+
const sql5 = renderMigrationSql({ name, namespace: workspace.journal.namespace, contentHash, plan });
|
|
5118
5450
|
const metaDir = join(dir, MIGRATIONS_DIRNAME, META_DIRNAME);
|
|
5119
5451
|
await mkdir(metaDir, { recursive: true });
|
|
5120
5452
|
const snapshotText = `${JSON.stringify(plan.snapshot, null, 2)}
|
|
5121
5453
|
`;
|
|
5122
|
-
await writeFile(join(dir, MIGRATIONS_DIRNAME, `${name}.sql`),
|
|
5454
|
+
await writeFile(join(dir, MIGRATIONS_DIRNAME, `${name}.sql`), sql5);
|
|
5123
5455
|
await writeFile(join(metaDir, `${pad(ordinal)}.snapshot.json`), snapshotText);
|
|
5124
5456
|
const journal = {
|
|
5125
5457
|
...workspace.journal,
|
|
@@ -5130,7 +5462,7 @@ async function writeMigration({
|
|
|
5130
5462
|
name,
|
|
5131
5463
|
content_hash: contentHash,
|
|
5132
5464
|
previous_content_hash: tip?.content_hash ?? null,
|
|
5133
|
-
checksum: checksumOf(
|
|
5465
|
+
checksum: checksumOf(sql5),
|
|
5134
5466
|
snapshot_checksum: checksumOf(snapshotText),
|
|
5135
5467
|
destructive
|
|
5136
5468
|
}
|
|
@@ -5245,8 +5577,8 @@ async function checkWorkspace({
|
|
|
5245
5577
|
let previousId3;
|
|
5246
5578
|
let previousHash = null;
|
|
5247
5579
|
for (const entry of workspace.journal.entries) {
|
|
5248
|
-
const
|
|
5249
|
-
if (checksumOf(
|
|
5580
|
+
const sql5 = await readMigrationSql(dir, entry);
|
|
5581
|
+
if (checksumOf(sql5) !== entry.checksum) {
|
|
5250
5582
|
log(`${MIGRATIONS_DIRNAME}/${entry.name}.sql does not match its journal checksum \u2014 committed migrations must not be edited`);
|
|
5251
5583
|
return 1;
|
|
5252
5584
|
}
|
|
@@ -5286,7 +5618,7 @@ async function checkWorkspace({
|
|
|
5286
5618
|
log(`migrations in sync \u2014 ${workspace.journal.entries.length} committed, tip ${tip.name}`);
|
|
5287
5619
|
return 0;
|
|
5288
5620
|
}
|
|
5289
|
-
var firstLine = (
|
|
5621
|
+
var firstLine = (sql5) => sql5.split("\n")[0] ?? sql5;
|
|
5290
5622
|
|
|
5291
5623
|
// src/migrations/from-yaml.ts
|
|
5292
5624
|
function convertV1DefinitionToCanonical(definition) {
|
|
@@ -5392,7 +5724,7 @@ function buildRebaseJournalEntry({ namespace, previousHash, newHash, ordinal, sn
|
|
|
5392
5724
|
throw new OrmDefinitionError(`rebase ordinal must be an integer \u2265 2 (a rebase sits on an existing journal tip); got ${ordinal}`);
|
|
5393
5725
|
}
|
|
5394
5726
|
const name = migrationName(ordinal, newHash);
|
|
5395
|
-
const
|
|
5727
|
+
const sql5 = renderRebaseMigrationSql({ name, namespace, previousHash, newHash });
|
|
5396
5728
|
const snapshotText = `${JSON.stringify(snapshot, null, 2)}
|
|
5397
5729
|
`;
|
|
5398
5730
|
const entry = {
|
|
@@ -5400,11 +5732,11 @@ function buildRebaseJournalEntry({ namespace, previousHash, newHash, ordinal, sn
|
|
|
5400
5732
|
name,
|
|
5401
5733
|
content_hash: newHash,
|
|
5402
5734
|
previous_content_hash: previousHash,
|
|
5403
|
-
checksum: checksumOf(
|
|
5735
|
+
checksum: checksumOf(sql5),
|
|
5404
5736
|
snapshot_checksum: checksumOf(snapshotText),
|
|
5405
5737
|
destructive: false
|
|
5406
5738
|
};
|
|
5407
|
-
return { name, entry, sql:
|
|
5739
|
+
return { name, entry, sql: sql5, snapshotText };
|
|
5408
5740
|
}
|
|
5409
5741
|
function renderRebaseMigrationSql({
|
|
5410
5742
|
name,
|
|
@@ -5502,6 +5834,6 @@ function canonicalActions(value) {
|
|
|
5502
5834
|
);
|
|
5503
5835
|
}
|
|
5504
5836
|
|
|
5505
|
-
export { ACTION_REQUIRES_DENIED_SQLSTATE, ACTION_VALIDATES_FAILED_SQLSTATE, AUDIT_ACTIONS, CANONICAL_FORMAT, CAPABILITIES_CLAIM_KEY, CAPABILITY_KEY_PATTERN, CLAIMS_CONTRACT, CLAIMS_SETTING, CLAIM_KEY_PATTERN, CMP_OPS, COLUMN_BRAND, COLUMN_DEFAULT_FNS, COLUMN_TYPES2 as COLUMN_TYPES, COMPARE_OPS, CONSUMER_ROLE_SUFFIX, ColumnBuilder, DEFINITION_FILE_EXTENSIONS, ENUM_BRAND, HASH_DOMAIN, IDENTIFIER_PATTERN, IDENTIFIER_RE, INPUT_REF_KINDS, IR_VERSION, IrVersionError, LINK_BRAND, MAX_DEFINITION_FILE_BYTES, MAX_IDENTIFIER_LENGTH, MIGRATIONS_DIRNAME, NAMESPACE_CONFIG_BRAND, NAMESPACE_CONFIG_FILE, NAMESPACE_CONFIG_FILENAMES, NAMESPACE_PATTERN, ORM_DATA_TOKEN_HEADER, 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, checksumOf, claimExpression, classifyDestructive, columnSchema, columnTypeSchema2 as columnTypeSchema, compileActionRequires, compileActionStatements, compileNamespace, compilePredIrToSql, compilePredicate, compiledArtifactSchema, compiledOperationSchema, computeCanonicalHash, computeDefinitionHash, computeSyncedContractHash, consumerRoleName, convertV1DefinitionToCanonical, declaredCapabilityKeys, defineEnum, defineNamespace, defineSyncedTable, defineTable, defineView, deriveColumnName, diffCommand, diffStatements, emptyJournal, emptySnapshot, endAuthoringContext, extractNamedParams, extractOrmSyncedTableContract, generateNamespaceTypes, generateSnapshot, generateSnapshotV2, identifierSchema, indexByHash, isColumnBuilder, isEnumRef, isFunctionDefault, isOwnedStatement, isOwnedStatementV2, isQualifiedReference, isSeqLink, isSeqNamespaceConfig, isSeqTable, isSeqView, limitIrSchema, loadCommittedMigrations, loadCommittedTipMigration, loadNamespaceDefinition, loadNamespaceFromDir, migrationName, namespaceConfigSchema, namespaceSchema, normalizeCanonical, normalizeIr, normalizeSnapshot, orderIrSchema, ormSchemaName, overlayWinsUnion, overrideRowMutation, parseCanonicalDefinition, parseDefinitionSources, parsePredicate, parseTableFile, parsedPredicateSchema, pendingKitStatements, pendingKitStatementsV2, planMigration, planMigrationV2, policyExpressionSchema, policyVariantSchema, predIrSchema, predicateOperands, quoteIdentifier, quoteLiteral, readMigrationSql, readMigrationWorkspace, readSnapshot, rebaseViolations, referencedCapabilities, referencedColumns, renderMigrationSql, 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 };
|
|
5837
|
+
export { ACTION_REQUIRES_DENIED_SQLSTATE, ACTION_VALIDATES_FAILED_SQLSTATE, AUDIT_ACTIONS, CANONICAL_FORMAT, CAPABILITIES_CLAIM_KEY, CAPABILITY_KEY_PATTERN, 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, MAX_DEFINITION_FILE_BYTES, MAX_GENERATED_SDL_BYTES, MAX_IDENTIFIER_LENGTH, 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, checksumOf, claimExpression, classifyDestructive, columnSchema, columnTypeSchema2 as columnTypeSchema, compileActionRequires, compileActionStatements, compileNamespace, compilePredIrToSql, compilePredicate, compiledArtifactSchema, compiledOperationSchema, computeCanonicalHash, computeDefinitionHash, computeSyncedContractHash, consumerRoleName, convertV1DefinitionToCanonical, createOrmMigrationExport, declaredCapabilityKeys, defineEnum, defineNamespace, defineSyncedTable, defineTable, defineView, deriveColumnName, diffCommand, diffStatements, emptyJournal, emptySnapshot, endAuthoringContext, extractNamedParams, extractOrmSyncedTableContract, generateNamespaceTypes, generateSnapshot, generateSnapshotV2, identifierSchema, indexByHash, isColumnBuilder, isEnumRef, isFunctionDefault, isOwnedStatement, isOwnedStatementV2, isQualifiedReference, isSeqLink, isSeqNamespaceConfig, isSeqTable, isSeqView, limitIrSchema, loadCommittedMigrations, loadCommittedTipMigration, loadNamespaceDefinition, loadNamespaceFromDir, migrationName, namespaceConfigSchema, namespaceSchema, normalizeCanonical, normalizeIr, normalizeSnapshot, orderIrSchema, ormMigrationExportSchema, ormSchemaName, overlayWinsUnion, overrideRowMutation, parseCanonicalDefinition, parseDefinitionSources, parseOrmMigrationExport, parsePredicate, parseTableFile, parsedPredicateSchema, pendingKitStatements, pendingKitStatementsV2, planMigration, planMigrationV2, policyExpressionSchema, policyVariantSchema, predIrSchema, predicateOperands, quoteIdentifier, quoteLiteral, readMigrationSql, readMigrationWorkspace, readSnapshot, rebaseViolations, referencedCapabilities, referencedColumns, renderMigrationSql, renderOrmMigrationExport, requiredArtifactIrVersion, 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 };
|
|
5506
5838
|
//# sourceMappingURL=index.js.map
|
|
5507
5839
|
//# sourceMappingURL=index.js.map
|