@vibeorm/migrate 2.3.0 → 2.5.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/dist/artifact-probe.d.ts +41 -0
- package/dist/artifact-probe.d.ts.map +1 -0
- package/dist/ddl/sqlite.d.ts.map +1 -1
- package/dist/down.d.ts +21 -3
- package/dist/down.d.ts.map +1 -1
- package/dist/extensions.d.ts +52 -0
- package/dist/extensions.d.ts.map +1 -1
- package/dist/fs.js +152 -70
- package/dist/fs.js.map +12 -12
- package/dist/index-predicates.d.ts +35 -0
- package/dist/index-predicates.d.ts.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +352 -149
- package/dist/index.js.map +19 -17
- package/dist/introspect/postgres.d.ts.map +1 -1
- package/dist/normalize.d.ts.map +1 -1
- package/dist/object-ownership.d.ts.map +1 -1
- package/dist/planner.d.ts +28 -0
- package/dist/planner.d.ts.map +1 -1
- package/dist/postgres-types.d.ts +16 -0
- package/dist/postgres-types.d.ts.map +1 -1
- package/dist/push.d.ts +11 -0
- package/dist/push.d.ts.map +1 -1
- package/dist/replay-plan.d.ts.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -307,10 +307,10 @@ import { VibeError as VibeError18, sqlObjectKey as sqlObjectKey6 } from "@vibeor
|
|
|
307
307
|
import { getSqlDialect as getSqlDialect3, renderModuleDependencyQuery } from "@vibeorm/sql";
|
|
308
308
|
|
|
309
309
|
// packages/migrate/src/object-ownership.ts
|
|
310
|
-
import { VibeError as VibeError7,
|
|
310
|
+
import { VibeError as VibeError7, defaultPrimaryKeyName, enumStorageOf as enumStorageOf2, implicitJoinTableObjectNames, postgresGeneratedObjectName, sqlObjectKey as sqlObjectKey2 } from "@vibeorm/schema";
|
|
311
311
|
|
|
312
312
|
// packages/migrate/src/normalize.ts
|
|
313
|
-
import { VibeError as VibeError6, enumStorageOf, isEnumType as isEnumType5, unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
|
|
313
|
+
import { VibeError as VibeError6, defaultForeignKeyName, defaultIndexName, enumStorageOf, isEnumType as isEnumType5, unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
|
|
314
314
|
|
|
315
315
|
// packages/migrate/src/relations.ts
|
|
316
316
|
import { implicitJoinTable, isImplicitManyToManyRelation } from "@vibeorm/schema";
|
|
@@ -360,6 +360,49 @@ var SCALAR_TO_PG = {
|
|
|
360
360
|
var CANONICAL_EQUIVALENTS = {
|
|
361
361
|
DateTime: ["timestamptz(3)", "timestamptz(6)"]
|
|
362
362
|
};
|
|
363
|
+
var CANONICAL_UDT = {
|
|
364
|
+
text: "String",
|
|
365
|
+
bool: "Boolean",
|
|
366
|
+
int4: "Int",
|
|
367
|
+
int8: "BigInt",
|
|
368
|
+
float8: "Float",
|
|
369
|
+
numeric: "Decimal",
|
|
370
|
+
timestamptz: "DateTime",
|
|
371
|
+
jsonb: "Json",
|
|
372
|
+
bytea: "Bytes"
|
|
373
|
+
};
|
|
374
|
+
var NON_CANONICAL_UDT = {
|
|
375
|
+
varchar: "String",
|
|
376
|
+
bpchar: "String",
|
|
377
|
+
uuid: "String",
|
|
378
|
+
bit: "String",
|
|
379
|
+
varbit: "String",
|
|
380
|
+
xml: "String",
|
|
381
|
+
inet: "String",
|
|
382
|
+
citext: "String",
|
|
383
|
+
int2: "Int",
|
|
384
|
+
oid: "Int",
|
|
385
|
+
float4: "Float",
|
|
386
|
+
money: "Decimal",
|
|
387
|
+
timestamp: "DateTime",
|
|
388
|
+
date: "DateTime",
|
|
389
|
+
time: "DateTime",
|
|
390
|
+
timetz: "DateTime",
|
|
391
|
+
json: "Json"
|
|
392
|
+
};
|
|
393
|
+
var SPELLING_TO_UDT = {
|
|
394
|
+
integer: "int4",
|
|
395
|
+
bigint: "int8",
|
|
396
|
+
smallint: "int2",
|
|
397
|
+
boolean: "bool",
|
|
398
|
+
doubleprecision: "float8",
|
|
399
|
+
real: "float4",
|
|
400
|
+
char: "bpchar"
|
|
401
|
+
};
|
|
402
|
+
function scalarForBuiltinType(params) {
|
|
403
|
+
const udt = SPELLING_TO_UDT[params.name] ?? params.name;
|
|
404
|
+
return CANONICAL_UDT[udt] ?? NON_CANONICAL_UDT[udt];
|
|
405
|
+
}
|
|
363
406
|
var NATIVE_ALIASES = {
|
|
364
407
|
charactervarying: "varchar",
|
|
365
408
|
character: "char",
|
|
@@ -379,7 +422,11 @@ var NATIVE_ALIASES = {
|
|
|
379
422
|
timestampwithtimezone: "timestamptz",
|
|
380
423
|
timestampwithouttimezone: "timestamp",
|
|
381
424
|
timewithtimezone: "timetz",
|
|
382
|
-
timewithouttimezone: "time"
|
|
425
|
+
timewithouttimezone: "time",
|
|
426
|
+
bitvarying: "varbit"
|
|
427
|
+
};
|
|
428
|
+
var IMPLICIT_MODIFIERS = {
|
|
429
|
+
bit: "(1)"
|
|
383
430
|
};
|
|
384
431
|
function normalizeNativeType(params) {
|
|
385
432
|
const compact = params.raw.toLowerCase().replace(/\s+/g, "");
|
|
@@ -387,7 +434,7 @@ function normalizeNativeType(params) {
|
|
|
387
434
|
if (match === null)
|
|
388
435
|
return compact;
|
|
389
436
|
const base = NATIVE_ALIASES[match[1]] ?? match[1];
|
|
390
|
-
return `${base}${match[2] ?? ""}${match[3] ?? ""}`;
|
|
437
|
+
return `${base}${match[2] ?? IMPLICIT_MODIFIERS[base] ?? ""}${match[3] ?? ""}`;
|
|
391
438
|
}
|
|
392
439
|
function parseUnsupportedPostgresType(params) {
|
|
393
440
|
const temporal = /^\s*(time(?:stamp)?)\s*(\(\s*\d+\s*\))?\s+(with|without)\s+time\s+zone\s*((?:\[\s*\]\s*)*)$/i.exec(params.raw);
|
|
@@ -408,6 +455,11 @@ function columnTypeKey(params) {
|
|
|
408
455
|
return `u:${field.nativeType ?? ""}`;
|
|
409
456
|
const evidence = field.nativeTypeIdentity;
|
|
410
457
|
const namespace = evidence !== undefined && evidence.source === field.nativeType ? evidence.namespace : parsed.namespace;
|
|
458
|
+
const scalar = namespace === undefined || namespace === "pg_catalog" ? scalarForBuiltinType({ name: parsed.name }) : undefined;
|
|
459
|
+
if (scalar !== undefined) {
|
|
460
|
+
const { isUnsupported: _unsupported, nativeTypeIdentity: _identity, ...rest } = field;
|
|
461
|
+
return columnTypeKey({ field: { ...rest, type: scalar, nativeType: `${parsed.name}${parsed.modifier ?? ""}`, isList: parsed.array || field.isList } });
|
|
462
|
+
}
|
|
411
463
|
return `u:${JSON.stringify([namespace ?? null, parsed.name, parsed.modifier, parsed.array || field.isList])}`;
|
|
412
464
|
}
|
|
413
465
|
if (isEnumType(field.type)) {
|
|
@@ -496,8 +548,12 @@ function defaultFingerprint(params) {
|
|
|
496
548
|
}
|
|
497
549
|
case "enumValue":
|
|
498
550
|
return `enum:${def.value}`;
|
|
499
|
-
case "dbgenerated":
|
|
500
|
-
|
|
551
|
+
case "dbgenerated": {
|
|
552
|
+
const sql = def.sql.trim();
|
|
553
|
+
if (/^now\(\)$/i.test(sql) || /^current_timestamp(\(\d\))?$/i.test(sql))
|
|
554
|
+
return "now";
|
|
555
|
+
return `db:${sql}`;
|
|
556
|
+
}
|
|
501
557
|
}
|
|
502
558
|
}
|
|
503
559
|
function defaultSql(params) {
|
|
@@ -988,8 +1044,8 @@ function normalizeModel(params) {
|
|
|
988
1044
|
});
|
|
989
1045
|
}
|
|
990
1046
|
const pk = normalizePk({ model });
|
|
991
|
-
const indexes = normalizeIndexes({ model, table });
|
|
992
|
-
const fks = normalizeFks({ model, table, schema });
|
|
1047
|
+
const indexes = normalizeIndexes({ model, table, dialect });
|
|
1048
|
+
const fks = normalizeFks({ model, table, schema, dialect });
|
|
993
1049
|
const checks = new Map;
|
|
994
1050
|
for (const check of model.checks ?? [])
|
|
995
1051
|
checks.set(check.name, check.expression);
|
|
@@ -1003,7 +1059,7 @@ function normalizePk(params) {
|
|
|
1003
1059
|
return model.fields.filter((field) => field.isId).map((field) => field.dbName ?? field.name);
|
|
1004
1060
|
}
|
|
1005
1061
|
function normalizeIndexes(params) {
|
|
1006
|
-
const { model, table } = params;
|
|
1062
|
+
const { model, table, dialect } = params;
|
|
1007
1063
|
const result = new Map;
|
|
1008
1064
|
const add = (index) => {
|
|
1009
1065
|
const unique = index.kind === "unique";
|
|
@@ -1022,7 +1078,7 @@ function normalizeIndexes(params) {
|
|
|
1022
1078
|
const key = indexComparisonKey({ unique, kind, columns, where, nullsNotDistinct });
|
|
1023
1079
|
if (result.has(key))
|
|
1024
1080
|
return;
|
|
1025
|
-
const name = index.name ??
|
|
1081
|
+
const name = index.name ?? defaultIndexName({ table, columns: columns.map((column) => column.name), unique, dialect });
|
|
1026
1082
|
result.set(key, {
|
|
1027
1083
|
key,
|
|
1028
1084
|
name,
|
|
@@ -1228,7 +1284,7 @@ function normalizeFks(params) {
|
|
|
1228
1284
|
continue;
|
|
1229
1285
|
result.set(key, {
|
|
1230
1286
|
key,
|
|
1231
|
-
constraintName: relation.constraintName ??
|
|
1287
|
+
constraintName: relation.constraintName ?? defaultForeignKeyName({ table, columns, dialect: params.dialect }),
|
|
1232
1288
|
columns,
|
|
1233
1289
|
refTable,
|
|
1234
1290
|
refColumns,
|
|
@@ -1371,7 +1427,7 @@ function removalIdentity(params) {
|
|
|
1371
1427
|
case "dropForeignKey":
|
|
1372
1428
|
return { kind: "constraint", constraintType: "foreignKey", table, name: step.constraintName };
|
|
1373
1429
|
case "dropPrimaryKey":
|
|
1374
|
-
return { kind: "constraint", constraintType: "primaryKey", table, name: step.constraintName ??
|
|
1430
|
+
return { kind: "constraint", constraintType: "primaryKey", table, name: step.constraintName ?? defaultPrimaryKeyName({ table: step.table, dialect: "postgres" }) };
|
|
1375
1431
|
default:
|
|
1376
1432
|
return;
|
|
1377
1433
|
}
|
|
@@ -1390,7 +1446,7 @@ function schemaObjectIdentities(params) {
|
|
|
1390
1446
|
if (definition.defaultKey !== null)
|
|
1391
1447
|
result.push({ kind: "columnDefault", table, column });
|
|
1392
1448
|
if (params.dialect === "postgres" && definition.field.default?.kind === "autoincrement")
|
|
1393
|
-
result.push({ kind: "sequence", name: { namespace: params.namespace, name:
|
|
1449
|
+
result.push({ kind: "sequence", name: { namespace: params.namespace, name: postgresGeneratedObjectName({ name1: name, name2: column, label: "seq" }) } });
|
|
1394
1450
|
}
|
|
1395
1451
|
for (const exclusion of model.model.exclusions ?? [])
|
|
1396
1452
|
result.push({ kind: "constraint", constraintType: "exclusion", table, name: exclusion.name });
|
|
@@ -1404,15 +1460,15 @@ function schemaObjectIdentities(params) {
|
|
|
1404
1460
|
for (const fk of model.fks.values())
|
|
1405
1461
|
result.push({ kind: "constraint", constraintType: "foreignKey", table, name: fk.constraintName });
|
|
1406
1462
|
if (model.pk.length > 0)
|
|
1407
|
-
result.push({ kind: "constraint", constraintType: "primaryKey", table, name: model.model.primaryKeyConstraintName ?? (params.dialect === "mysql" ? "PRIMARY" :
|
|
1463
|
+
result.push({ kind: "constraint", constraintType: "primaryKey", table, name: model.model.primaryKeyConstraintName ?? (params.dialect === "mysql" ? "PRIMARY" : defaultPrimaryKeyName({ table: name, dialect: params.dialect })) });
|
|
1408
1464
|
}
|
|
1409
1465
|
for (const name of normalized.joinTables.keys()) {
|
|
1410
1466
|
const table = { namespace: params.namespace, name };
|
|
1411
1467
|
result.push({ kind: "table", name: table });
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1468
|
+
const derived = implicitJoinTableObjectNames({ table: name });
|
|
1469
|
+
result.push({ kind: "column", table, column: "A" }, { kind: "constraint", constraintType: "foreignKey", table, name: derived.foreignKeyA });
|
|
1470
|
+
result.push({ kind: "column", table, column: "B" }, { kind: "constraint", constraintType: "foreignKey", table, name: derived.foreignKeyB });
|
|
1471
|
+
result.push({ kind: "index", table, name: derived.uniqueIndex }, { kind: "index", table, name: derived.indexB });
|
|
1416
1472
|
}
|
|
1417
1473
|
for (const name of normalized.views.keys())
|
|
1418
1474
|
result.push({ kind: "view", name: { namespace: params.namespace, name } });
|
|
@@ -1487,7 +1543,7 @@ function assertRemovalAuthorization(params) {
|
|
|
1487
1543
|
}
|
|
1488
1544
|
|
|
1489
1545
|
// packages/migrate/src/inventory.ts
|
|
1490
|
-
import { VibeError as VibeError17, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey5 } from "@vibeorm/schema";
|
|
1546
|
+
import { VibeError as VibeError17, defaultForeignKeyName as defaultForeignKeyName2, defaultPrimaryKeyName as defaultPrimaryKeyName2, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey5 } from "@vibeorm/schema";
|
|
1491
1547
|
import { getSqlDialect as getSqlDialect2, renderMigrationInventory, renderSqliteColumnInventory as renderSqliteColumnInventory2, renderMysqlObjectMembers as renderMysqlObjectMembers2, sqliteTableConstraints as sqliteTableConstraints3, renderSqliteForeignKeyInventory } from "@vibeorm/sql";
|
|
1492
1548
|
|
|
1493
1549
|
// packages/migrate/src/exclusions.ts
|
|
@@ -2233,18 +2289,17 @@ async function introspectPostgres(params) {
|
|
|
2233
2289
|
if (params.inTransaction !== true)
|
|
2234
2290
|
await executor({ text: "BEGIN" });
|
|
2235
2291
|
try {
|
|
2236
|
-
const savedPath =
|
|
2237
|
-
const searchPath = savedPath
|
|
2238
|
-
if (
|
|
2292
|
+
const savedPath = await executor(renderRlsReadinessQuery({ dialect: postgresDialect6, query: "readSearchPath" }));
|
|
2293
|
+
const searchPath = savedPath[0]?.searchPath;
|
|
2294
|
+
if (typeof searchPath !== "string")
|
|
2239
2295
|
throw new VibeError13({ code: "VIBE_MIGRATION", message: "Cannot read the migration search_path" });
|
|
2240
2296
|
const matched = await verifyPostgresRls({ executor, ...params.rlsSchema === undefined ? {} : { schema: params.rlsSchema }, ...params.requireInstalledRls === undefined ? {} : { requireInstalled: params.requireInstalledRls } }).then((verified) => verified, (error) => {
|
|
2241
2297
|
if (error instanceof VibeError13)
|
|
2242
2298
|
throw error;
|
|
2243
2299
|
throw new VibeError13({ code: "VIBE_MIGRATION", message: "Native RLS verification could not complete. Check migration privileges and supply the deployed schema as previousSchema/rlsSchema before changing referenced columns.", meta: { reason: "rls-verification-unavailable" }, cause: error });
|
|
2244
2300
|
});
|
|
2301
|
+
await executor(renderRlsReadinessQuery({ dialect: postgresDialect6, query: "setSearchPath", searchPath }));
|
|
2245
2302
|
const physical = await introspectPhysicalPostgres({ executor });
|
|
2246
|
-
if (typeof searchPath === "string")
|
|
2247
|
-
await executor(renderRlsReadinessQuery({ dialect: postgresDialect6, query: "setSearchPath", searchPath }));
|
|
2248
2303
|
return projectPostgresRls({ physical, ...params.rlsSchema === undefined ? {} : { declared: params.rlsSchema }, matched });
|
|
2249
2304
|
} finally {
|
|
2250
2305
|
if (params.inTransaction !== true)
|
|
@@ -2521,27 +2576,6 @@ function buildField(params) {
|
|
|
2521
2576
|
...mapped.isUnsupported === true ? { isUnsupported: true } : {}
|
|
2522
2577
|
};
|
|
2523
2578
|
}
|
|
2524
|
-
var CANONICAL_UDT = {
|
|
2525
|
-
text: "String",
|
|
2526
|
-
bool: "Boolean",
|
|
2527
|
-
int4: "Int",
|
|
2528
|
-
int8: "BigInt",
|
|
2529
|
-
float8: "Float",
|
|
2530
|
-
numeric: "Decimal",
|
|
2531
|
-
timestamptz: "DateTime",
|
|
2532
|
-
jsonb: "Json",
|
|
2533
|
-
bytea: "Bytes"
|
|
2534
|
-
};
|
|
2535
|
-
var NON_CANONICAL_UDT = {
|
|
2536
|
-
varchar: "String",
|
|
2537
|
-
bpchar: "String",
|
|
2538
|
-
uuid: "String",
|
|
2539
|
-
int2: "Int",
|
|
2540
|
-
float4: "Float",
|
|
2541
|
-
timestamp: "DateTime",
|
|
2542
|
-
date: "DateTime",
|
|
2543
|
-
json: "Json"
|
|
2544
|
-
};
|
|
2545
2579
|
function reverseMapType(params) {
|
|
2546
2580
|
const { column, enumNames } = params;
|
|
2547
2581
|
const isList = column.dataType === "ARRAY";
|
|
@@ -2597,8 +2631,10 @@ function withModifier(params) {
|
|
|
2597
2631
|
}
|
|
2598
2632
|
if (udt === "bpchar")
|
|
2599
2633
|
return "char";
|
|
2600
|
-
if (udt === "
|
|
2601
|
-
return
|
|
2634
|
+
if ((udt === "bit" || udt === "varbit") && column.charMaxLength !== null)
|
|
2635
|
+
return `${udt}(${column.charMaxLength})`;
|
|
2636
|
+
if ((udt === "timestamp" || udt === "time" || udt === "timetz") && column.datetimePrecision !== null && column.datetimePrecision !== 6) {
|
|
2637
|
+
return `${udt}(${column.datetimePrecision})`;
|
|
2602
2638
|
}
|
|
2603
2639
|
return udt;
|
|
2604
2640
|
}
|
|
@@ -4348,7 +4384,7 @@ async function inspectMigrationInventory(params) {
|
|
|
4348
4384
|
for (const constraint of clauses) {
|
|
4349
4385
|
if (constraint.type === "check")
|
|
4350
4386
|
continue;
|
|
4351
|
-
const name = constraint.named ? constraint.name : constraint.type === "primaryKey" ?
|
|
4387
|
+
const name = constraint.named ? constraint.name : constraint.type === "primaryKey" ? defaultPrimaryKeyName2({ table: qualified.name, dialect: "sqlite" }) : constraint.type === "foreignKey" ? defaultForeignKeyName2({ table: qualified.name, columns: constraint.columns, dialect: "sqlite" }) : constraint.name;
|
|
4352
4388
|
objects.push({ identity: { kind: "constraint", constraintType: constraint.type, table: qualified, name }, ...constraint.type === "unique" && !constraint.named ? { inspectionUnavailable: "sqlite-anonymous-unique-constraint" } : {} });
|
|
4353
4389
|
}
|
|
4354
4390
|
const checks = parseSqliteNamedChecks({ tableSql: row.definition });
|
|
@@ -4359,7 +4395,7 @@ async function inspectMigrationInventory(params) {
|
|
|
4359
4395
|
const query = renderSqliteColumnInventory2({ dialect: getSqlDialect2({ dialect: params.dialect }), namespace: params.namespace, table: name });
|
|
4360
4396
|
const columns = await params.executor({ text: query.text, values: [...query.values] });
|
|
4361
4397
|
if (columns.some((column) => Number(column.pk) > 0) && !clauses.some((clause) => clause.type === "primaryKey"))
|
|
4362
|
-
objects.push({ identity: { kind: "constraint", constraintType: "primaryKey", table: qualified, name:
|
|
4398
|
+
objects.push({ identity: { kind: "constraint", constraintType: "primaryKey", table: qualified, name: defaultPrimaryKeyName2({ table: qualified.name, dialect: "sqlite" }) } });
|
|
4363
4399
|
const fkQuery = renderSqliteForeignKeyInventory({ dialect: getSqlDialect2({ dialect: params.dialect }), namespace: params.namespace, table: name });
|
|
4364
4400
|
const foreignKeys = await params.executor({ text: fkQuery.text, values: [...fkQuery.values] });
|
|
4365
4401
|
const groups = new Map;
|
|
@@ -4368,7 +4404,7 @@ async function inspectMigrationInventory(params) {
|
|
|
4368
4404
|
for (const fields of groups.values()) {
|
|
4369
4405
|
if (clauses.some((clause) => clause.type === "foreignKey" && JSON.stringify(clause.columns) === JSON.stringify(fields)))
|
|
4370
4406
|
continue;
|
|
4371
|
-
objects.push({ identity: { kind: "constraint", constraintType: "foreignKey", table: qualified, name:
|
|
4407
|
+
objects.push({ identity: { kind: "constraint", constraintType: "foreignKey", table: qualified, name: defaultForeignKeyName2({ table: qualified.name, columns: fields, dialect: "sqlite" }) }, inspectionUnavailable: "sqlite-inline-foreign-key-definition-unavailable" });
|
|
4372
4408
|
}
|
|
4373
4409
|
for (const column of columns) {
|
|
4374
4410
|
objects.push({ identity: { kind: "column", table: qualified, column: String(column.name) } });
|
|
@@ -5641,7 +5677,7 @@ import { VibeError as VibeError31, sqlObjectKey as sqlObjectKey9 } from "@vibeor
|
|
|
5641
5677
|
|
|
5642
5678
|
// packages/migrate/src/ddl/postgres.ts
|
|
5643
5679
|
import { renderExclusionSetup } from "@vibeorm/sql";
|
|
5644
|
-
import { VibeError as VibeError28, isEnumType as isEnumType7 } from "@vibeorm/schema";
|
|
5680
|
+
import { VibeError as VibeError28, defaultPrimaryKeyName as defaultPrimaryKeyName3, implicitJoinTableObjectNames as implicitJoinTableObjectNames2, isEnumType as isEnumType7 } from "@vibeorm/schema";
|
|
5645
5681
|
import { postgresDialect as postgresDialect8, renderRlsPolicyPath } from "@vibeorm/sql";
|
|
5646
5682
|
function renderPostgresSteps(params) {
|
|
5647
5683
|
refuseSameTransactionEnumUse({ steps: params.steps });
|
|
@@ -5713,7 +5749,7 @@ function renderStep(params) {
|
|
|
5713
5749
|
const lines = step.columns.map((column) => ` ${columnDefSql({ field: column })}`);
|
|
5714
5750
|
if (step.primaryKey.length > 0) {
|
|
5715
5751
|
const pkCols = step.primaryKey.map((column) => q2(column)).join(", ");
|
|
5716
|
-
lines.push(` CONSTRAINT ${q2(step.primaryKeyConstraintName ??
|
|
5752
|
+
lines.push(` CONSTRAINT ${q2(step.primaryKeyConstraintName ?? defaultPrimaryKeyName3({ table: step.table, dialect: "postgres" }))} PRIMARY KEY (${pkCols})`);
|
|
5717
5753
|
}
|
|
5718
5754
|
for (const check of step.checks ?? []) {
|
|
5719
5755
|
lines.push(` CONSTRAINT ${q2(check.name)} CHECK (${check.expression})`);
|
|
@@ -5786,25 +5822,26 @@ ${lines.join(`,
|
|
|
5786
5822
|
case "createJoinTable": {
|
|
5787
5823
|
const aType = columnTypeSql({ field: { ...step.a.field, isList: false } });
|
|
5788
5824
|
const bType = columnTypeSql({ field: { ...step.b.field, isList: false } });
|
|
5825
|
+
const names = implicitJoinTableObjectNames2({ table: step.table });
|
|
5789
5826
|
return [
|
|
5790
5827
|
`CREATE TABLE ${publicName(step.table)} (
|
|
5791
5828
|
"A" ${aType} NOT NULL,
|
|
5792
5829
|
"B" ${bType} NOT NULL
|
|
5793
5830
|
);`,
|
|
5794
|
-
`CREATE UNIQUE INDEX ${q2(
|
|
5795
|
-
`CREATE INDEX ${q2(
|
|
5796
|
-
`ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(
|
|
5797
|
-
`ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(
|
|
5831
|
+
`CREATE UNIQUE INDEX ${q2(names.uniqueIndex)} ON ${publicName(step.table)} ("A", "B");`,
|
|
5832
|
+
`CREATE INDEX ${q2(names.indexB)} ON ${publicName(step.table)} ("B");`,
|
|
5833
|
+
`ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(names.foreignKeyA)} FOREIGN KEY ("A") REFERENCES ${publicName(step.a.table)} (${q2(step.a.column)}) ON DELETE CASCADE ON UPDATE CASCADE;`,
|
|
5834
|
+
`ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(names.foreignKeyB)} FOREIGN KEY ("B") REFERENCES ${publicName(step.b.table)} (${q2(step.b.column)}) ON DELETE CASCADE ON UPDATE CASCADE;`
|
|
5798
5835
|
];
|
|
5799
5836
|
}
|
|
5800
5837
|
case "addPrimaryKey": {
|
|
5801
5838
|
const cols = step.columns.map((column) => q2(column)).join(", ");
|
|
5802
5839
|
return [
|
|
5803
|
-
`ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(step.constraintName ??
|
|
5840
|
+
`ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(step.constraintName ?? defaultPrimaryKeyName3({ table: step.table, dialect: "postgres" }))} PRIMARY KEY (${cols});`
|
|
5804
5841
|
];
|
|
5805
5842
|
}
|
|
5806
5843
|
case "dropPrimaryKey":
|
|
5807
|
-
return [`ALTER TABLE ${publicName(step.table)} DROP CONSTRAINT ${q2(step.constraintName ??
|
|
5844
|
+
return [`ALTER TABLE ${publicName(step.table)} DROP CONSTRAINT ${q2(step.constraintName ?? defaultPrimaryKeyName3({ table: step.table, dialect: "postgres" }))};`];
|
|
5808
5845
|
}
|
|
5809
5846
|
}
|
|
5810
5847
|
function columnDefSql(params) {
|
|
@@ -5866,7 +5903,7 @@ function q2(name) {
|
|
|
5866
5903
|
}
|
|
5867
5904
|
|
|
5868
5905
|
// packages/migrate/src/ddl/sqlite.ts
|
|
5869
|
-
import { VibeError as VibeError29, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
|
|
5906
|
+
import { VibeError as VibeError29, implicitJoinTableObjectNames as implicitJoinTableObjectNames3, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
|
|
5870
5907
|
import { sqliteDialect as sqliteDialect3 } from "@vibeorm/sql";
|
|
5871
5908
|
|
|
5872
5909
|
// packages/migrate/src/ddl/drop-order.ts
|
|
@@ -6064,12 +6101,13 @@ function renderSqliteSteps(params) {
|
|
|
6064
6101
|
case "createJoinTable": {
|
|
6065
6102
|
const aType = sqliteStorageType({ field: { ...step.a.field, isList: false } });
|
|
6066
6103
|
const bType = sqliteStorageType({ field: { ...step.b.field, isList: false } });
|
|
6104
|
+
const names = implicitJoinTableObjectNames3({ table: step.table });
|
|
6067
6105
|
statements.push(`CREATE TABLE ${q3(step.table)} (
|
|
6068
6106
|
"A" ${aType} NOT NULL,
|
|
6069
6107
|
"B" ${bType} NOT NULL,
|
|
6070
|
-
CONSTRAINT ${q3(
|
|
6071
|
-
CONSTRAINT ${q3(
|
|
6072
|
-
);`, `CREATE UNIQUE INDEX ${q3(
|
|
6108
|
+
CONSTRAINT ${q3(names.foreignKeyA)} FOREIGN KEY ("A") REFERENCES ${q3(step.a.table)} (${q3(step.a.column)}) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
6109
|
+
CONSTRAINT ${q3(names.foreignKeyB)} FOREIGN KEY ("B") REFERENCES ${q3(step.b.table)} (${q3(step.b.column)}) ON DELETE CASCADE ON UPDATE CASCADE
|
|
6110
|
+
);`, `CREATE UNIQUE INDEX ${q3(names.uniqueIndex)} ON ${q3(step.table)} ("A", "B");`, `CREATE INDEX ${q3(names.indexB)} ON ${q3(step.table)} ("B");`);
|
|
6073
6111
|
break;
|
|
6074
6112
|
}
|
|
6075
6113
|
case "addColumn":
|
|
@@ -6435,7 +6473,7 @@ function q3(name) {
|
|
|
6435
6473
|
}
|
|
6436
6474
|
|
|
6437
6475
|
// packages/migrate/src/ddl/mysql.ts
|
|
6438
|
-
import { VibeError as VibeError30, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
|
|
6476
|
+
import { VibeError as VibeError30, implicitJoinTableObjectNames as implicitJoinTableObjectNames4, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
|
|
6439
6477
|
import { mysqlDialect as mysqlDialect3 } from "@vibeorm/sql";
|
|
6440
6478
|
function renderMysqlSteps(params) {
|
|
6441
6479
|
for (const step of params.steps) {
|
|
@@ -6614,15 +6652,16 @@ ${lines.join(`,
|
|
|
6614
6652
|
case "createJoinTable": {
|
|
6615
6653
|
const aType = mysqlColumnTypeSql({ field: { ...step.a.field, isList: false }, enums });
|
|
6616
6654
|
const bType = mysqlColumnTypeSql({ field: { ...step.b.field, isList: false }, enums });
|
|
6655
|
+
const names = implicitJoinTableObjectNames4({ table: step.table });
|
|
6617
6656
|
return [
|
|
6618
6657
|
`CREATE TABLE ${q4(step.table)} (
|
|
6619
6658
|
\`A\` ${aType} NOT NULL,
|
|
6620
6659
|
\`B\` ${bType} NOT NULL
|
|
6621
6660
|
);`,
|
|
6622
|
-
`CREATE UNIQUE INDEX ${q4(
|
|
6623
|
-
`CREATE INDEX ${q4(
|
|
6624
|
-
`ALTER TABLE ${q4(step.table)} ADD CONSTRAINT ${q4(
|
|
6625
|
-
`ALTER TABLE ${q4(step.table)} ADD CONSTRAINT ${q4(
|
|
6661
|
+
`CREATE UNIQUE INDEX ${q4(names.uniqueIndex)} ON ${q4(step.table)} (\`A\`, \`B\`);`,
|
|
6662
|
+
`CREATE INDEX ${q4(names.indexB)} ON ${q4(step.table)} (\`B\`);`,
|
|
6663
|
+
`ALTER TABLE ${q4(step.table)} ADD CONSTRAINT ${q4(names.foreignKeyA)} FOREIGN KEY (\`A\`) REFERENCES ${q4(step.a.table)} (${q4(step.a.column)}) ON DELETE CASCADE ON UPDATE CASCADE;`,
|
|
6664
|
+
`ALTER TABLE ${q4(step.table)} ADD CONSTRAINT ${q4(names.foreignKeyB)} FOREIGN KEY (\`B\`) REFERENCES ${q4(step.b.table)} (${q4(step.b.column)}) ON DELETE CASCADE ON UPDATE CASCADE;`
|
|
6626
6665
|
];
|
|
6627
6666
|
}
|
|
6628
6667
|
case "addPrimaryKey":
|
|
@@ -6759,6 +6798,7 @@ function collectExtensionArtifacts(params) {
|
|
|
6759
6798
|
const { schema, dialect, extensions } = params;
|
|
6760
6799
|
const setup = [];
|
|
6761
6800
|
const objects = [];
|
|
6801
|
+
const groups = [];
|
|
6762
6802
|
for (const extension of extensions) {
|
|
6763
6803
|
const artifactsHook = extension.migrate?.artifacts;
|
|
6764
6804
|
if (artifactsHook === undefined)
|
|
@@ -6766,6 +6806,7 @@ function collectExtensionArtifacts(params) {
|
|
|
6766
6806
|
const declarations = extensionDeclarations({ schema, name: extension.name });
|
|
6767
6807
|
if (Object.keys(declarations).length === 0 && extension.schema?.configOnly !== true)
|
|
6768
6808
|
continue;
|
|
6809
|
+
const contributed = [];
|
|
6769
6810
|
for (const artifact of artifactsHook({
|
|
6770
6811
|
schema,
|
|
6771
6812
|
dialect,
|
|
@@ -6773,10 +6814,19 @@ function collectExtensionArtifacts(params) {
|
|
|
6773
6814
|
options: extension.options,
|
|
6774
6815
|
declarations
|
|
6775
6816
|
})) {
|
|
6776
|
-
(artifact.owns.kind === "postgresExtension"
|
|
6817
|
+
if (artifact.owns.kind === "postgresExtension")
|
|
6818
|
+
setup.push(artifact);
|
|
6819
|
+
else {
|
|
6820
|
+
objects.push(artifact);
|
|
6821
|
+
contributed.push(artifact);
|
|
6822
|
+
}
|
|
6777
6823
|
}
|
|
6824
|
+
groups.push({ extension: extension.name, objects: contributed });
|
|
6778
6825
|
}
|
|
6779
|
-
return { setup, objects };
|
|
6826
|
+
return { setup, objects, groups };
|
|
6827
|
+
}
|
|
6828
|
+
function lastDeclaredSchema(params) {
|
|
6829
|
+
return [...params.migrations].reverse().find((migration) => migration.desiredState !== undefined)?.desiredState?.to.schema;
|
|
6780
6830
|
}
|
|
6781
6831
|
function ownedTableNames(params) {
|
|
6782
6832
|
const exact = new Set;
|
|
@@ -6878,6 +6928,36 @@ async function probeInstalledExtensionState(params) {
|
|
|
6878
6928
|
function artifactCreateSql(params) {
|
|
6879
6929
|
return params.artifacts.flatMap((artifact) => [...artifact.create]);
|
|
6880
6930
|
}
|
|
6931
|
+
function planChangedArtifacts(params) {
|
|
6932
|
+
const previousById = new Map((params.previous?.objects ?? []).map((artifact) => [artifact.id, artifact]));
|
|
6933
|
+
const probedIds = new Set((params.probed ?? []).map((artifact) => artifact.id));
|
|
6934
|
+
const missingIds = new Set(params.missing.map((artifact) => artifact.id));
|
|
6935
|
+
const rebuilt = new Set;
|
|
6936
|
+
for (const group of params.current.groups) {
|
|
6937
|
+
const changed = group.objects.filter((artifact) => {
|
|
6938
|
+
if (missingIds.has(artifact.id))
|
|
6939
|
+
return false;
|
|
6940
|
+
if (probedIds.has(artifact.id))
|
|
6941
|
+
return true;
|
|
6942
|
+
const before = previousById.get(artifact.id);
|
|
6943
|
+
return before !== undefined && JSON.stringify(before.owns) === JSON.stringify(artifact.owns) && JSON.stringify(before.create) !== JSON.stringify(artifact.create);
|
|
6944
|
+
});
|
|
6945
|
+
if (changed.length === 0)
|
|
6946
|
+
continue;
|
|
6947
|
+
const rebuiltColumnTables = new Set(changed.flatMap((artifact) => artifact.owns.kind === "column" ? [artifact.owns.table] : []));
|
|
6948
|
+
for (const artifact of group.objects) {
|
|
6949
|
+
if (missingIds.has(artifact.id))
|
|
6950
|
+
continue;
|
|
6951
|
+
const dependent = (artifact.owns.kind === "index" || artifact.owns.kind === "trigger") && rebuiltColumnTables.has(artifact.owns.table);
|
|
6952
|
+
if (changed.includes(artifact) || dependent)
|
|
6953
|
+
rebuilt.add(artifact.id);
|
|
6954
|
+
}
|
|
6955
|
+
}
|
|
6956
|
+
return params.current.objects.filter((artifact) => rebuilt.has(artifact.id));
|
|
6957
|
+
}
|
|
6958
|
+
function artifactDropSql(params) {
|
|
6959
|
+
return [...params.artifacts].reverse().flatMap((artifact) => [...artifact.drop]);
|
|
6960
|
+
}
|
|
6881
6961
|
// packages/migrate/src/runner.ts
|
|
6882
6962
|
function migrationRlsSecurityChanges(params) {
|
|
6883
6963
|
const source = params.migration.rls;
|
|
@@ -7028,6 +7108,7 @@ function generateMigration(params) {
|
|
|
7028
7108
|
extensions: params.extensions ?? []
|
|
7029
7109
|
});
|
|
7030
7110
|
const allArtifacts = [...artifacts.setup, ...artifacts.objects];
|
|
7111
|
+
const rebuilt = planChangedArtifacts({ previous: collectExtensionArtifacts({ schema: from, dialect, extensions: params.extensions ?? [] }), current: artifacts, missing: [] });
|
|
7031
7112
|
assertRetainedMigrationObjects({ from: from.migrationObjects ?? [], to: to.migrationObjects ?? [] });
|
|
7032
7113
|
const strippedFrom = preserveMigrationObjects({ from: stripExtensionArtifacts({ schema: from, artifacts: allArtifacts }), to, objects: [...from.migrationObjects ?? [], ...to.migrationObjects ?? []] });
|
|
7033
7114
|
const steps = diffSchemas({ from: strippedFrom, to, dialect, ...params.renames === undefined ? {} : { renames: params.renames } });
|
|
@@ -7038,6 +7119,7 @@ function generateMigration(params) {
|
|
|
7038
7119
|
artifacts: missing.filter((artifact) => artifact.owns.kind === "postgresExtension")
|
|
7039
7120
|
}),
|
|
7040
7121
|
...coreSql,
|
|
7122
|
+
...artifactDropSql({ artifacts: rebuilt }),
|
|
7041
7123
|
...artifactCreateSql({
|
|
7042
7124
|
artifacts: missing.filter((artifact) => artifact.owns.kind !== "postgresExtension")
|
|
7043
7125
|
}),
|
|
@@ -8395,11 +8477,94 @@ async function compareReplayMigrationObjects(params) {
|
|
|
8395
8477
|
return { ...params.schema, migrationObjects: objects.map((object) => ({ ...object, expected: definitions.get(sqlObjectKey11({ identity: object.identity })) })) };
|
|
8396
8478
|
}
|
|
8397
8479
|
// packages/migrate/src/replay-plan.ts
|
|
8398
|
-
import { VibeError as
|
|
8480
|
+
import { VibeError as VibeError40, sqlObjectKey as sqlObjectKey13 } from "@vibeorm/schema";
|
|
8399
8481
|
|
|
8400
8482
|
// packages/migrate/src/planner.ts
|
|
8401
|
-
import { VibeError as
|
|
8483
|
+
import { VibeError as VibeError39, sqlObjectKey as sqlObjectKey12 } from "@vibeorm/schema";
|
|
8402
8484
|
import { renderSqliteRebuildObjects, sqliteDialect as sqliteDialect4 } from "@vibeorm/sql";
|
|
8485
|
+
|
|
8486
|
+
// packages/migrate/src/artifact-probe.ts
|
|
8487
|
+
import { VibeError as VibeError37 } from "@vibeorm/schema";
|
|
8488
|
+
var SAVEPOINT = "vibeorm_artifact_probe";
|
|
8489
|
+
async function probeChangedArtifacts(params) {
|
|
8490
|
+
const { executor, namespace } = params;
|
|
8491
|
+
const changed = [];
|
|
8492
|
+
for (const artifact of params.artifacts) {
|
|
8493
|
+
if (artifact.owns.kind === "postgresExtension" || artifact.drop.length === 0)
|
|
8494
|
+
continue;
|
|
8495
|
+
const identity = legacyMigrationObjects({ artifacts: [artifact], dialect: "postgres", namespace })[0].identity;
|
|
8496
|
+
const before = await inspectSqlObject({ executor, dialect: "postgres", identity });
|
|
8497
|
+
if (before.state !== "present")
|
|
8498
|
+
continue;
|
|
8499
|
+
await executor({ text: `SAVEPOINT ${SAVEPOINT}` });
|
|
8500
|
+
let after;
|
|
8501
|
+
try {
|
|
8502
|
+
for (const statement of [...artifact.drop, ...artifact.create])
|
|
8503
|
+
await executor({ text: statement });
|
|
8504
|
+
after = await inspectSqlObject({ executor, dialect: "postgres", identity });
|
|
8505
|
+
} finally {
|
|
8506
|
+
await executor({ text: `ROLLBACK TO SAVEPOINT ${SAVEPOINT}` });
|
|
8507
|
+
await executor({ text: `RELEASE SAVEPOINT ${SAVEPOINT}` });
|
|
8508
|
+
}
|
|
8509
|
+
if (after.state !== "present")
|
|
8510
|
+
throw new VibeError37({ code: "VIBE_MIGRATION", message: `Extension artifact ${artifact.id} did not produce its object when probed; its create SQL cannot be verified against the replayed history`, meta: { reason: "artifact-probe-unavailable", id: artifact.id, identity } });
|
|
8511
|
+
if (canonicalMigrationJson({ value: before.definition.facts }) === canonicalMigrationJson({ value: after.definition.facts }))
|
|
8512
|
+
continue;
|
|
8513
|
+
const definition = before.definition.facts.definition;
|
|
8514
|
+
changed.push({ artifact, ...identity.kind === "index" && typeof definition === "string" ? { restore: [`${definition};`] } : {} });
|
|
8515
|
+
}
|
|
8516
|
+
return changed;
|
|
8517
|
+
}
|
|
8518
|
+
|
|
8519
|
+
// packages/migrate/src/index-predicates.ts
|
|
8520
|
+
import { VibeError as VibeError38 } from "@vibeorm/schema";
|
|
8521
|
+
import { defaultIndexName as defaultIndexName2 } from "@vibeorm/schema";
|
|
8522
|
+
function physicalIndexName(params) {
|
|
8523
|
+
return params.index.name ?? defaultIndexName2({ table: params.table, columns: params.columns, unique: params.index.kind === "unique", dialect: "postgres" });
|
|
8524
|
+
}
|
|
8525
|
+
async function prepareIndexPredicateDiff(params) {
|
|
8526
|
+
if (params.probe !== "rollback")
|
|
8527
|
+
return params.from;
|
|
8528
|
+
const models = [];
|
|
8529
|
+
for (const live of params.from.models) {
|
|
8530
|
+
const table = live.dbName ?? live.name;
|
|
8531
|
+
const authored = params.to.models.find((entry) => (entry.dbName ?? entry.name) === table);
|
|
8532
|
+
if (authored === undefined || !live.indexes.some((index) => index.where !== undefined)) {
|
|
8533
|
+
models.push(live);
|
|
8534
|
+
continue;
|
|
8535
|
+
}
|
|
8536
|
+
const authoredByName = new Map;
|
|
8537
|
+
for (const index of authored.indexes) {
|
|
8538
|
+
const columns = index.fields.map((entry) => authored.fields.find((candidate) => candidate.name === entry.name)?.dbName ?? entry.name);
|
|
8539
|
+
authoredByName.set(physicalIndexName({ index, table, columns }), index);
|
|
8540
|
+
}
|
|
8541
|
+
const indexes = [];
|
|
8542
|
+
for (const index of live.indexes) {
|
|
8543
|
+
const next = index.name === undefined ? undefined : authoredByName.get(index.name);
|
|
8544
|
+
if (index.where === undefined || next?.where === undefined || next.where === index.where) {
|
|
8545
|
+
indexes.push(index);
|
|
8546
|
+
continue;
|
|
8547
|
+
}
|
|
8548
|
+
const deparsed = await deparseIndexPredicate({ executor: params.executor, table: { namespace: params.namespace, name: table }, where: next.where });
|
|
8549
|
+
indexes.push(deparsed === index.where ? { ...index, where: next.where } : index);
|
|
8550
|
+
}
|
|
8551
|
+
models.push({ ...live, indexes });
|
|
8552
|
+
}
|
|
8553
|
+
return { ...params.from, models };
|
|
8554
|
+
}
|
|
8555
|
+
async function deparseIndexPredicate(params) {
|
|
8556
|
+
try {
|
|
8557
|
+
const definition = await deparsePostgresCheck({ executor: params.executor, table: params.table, expression: params.where, probe: "rollback" });
|
|
8558
|
+
const match = /^CHECK \((.*)\)$/s.exec(definition);
|
|
8559
|
+
return match === null ? undefined : match[1];
|
|
8560
|
+
} catch (error) {
|
|
8561
|
+
if (error instanceof VibeError38 && error.code === "VIBE_MIGRATION")
|
|
8562
|
+
throw error;
|
|
8563
|
+
return;
|
|
8564
|
+
}
|
|
8565
|
+
}
|
|
8566
|
+
|
|
8567
|
+
// packages/migrate/src/planner.ts
|
|
8403
8568
|
async function planSchemaMigration(params) {
|
|
8404
8569
|
const { executor, dialect } = params;
|
|
8405
8570
|
const resolved = dialect === "postgres" ? await resolvePostgresTypeIdentities({ executor, schema: params.schema, ...params.previous === undefined ? {} : { previous: params.previous.schema }, mode: "verify" }) : { schema: params.schema, unavailable: [] };
|
|
@@ -8412,7 +8577,7 @@ async function planSchemaMigration(params) {
|
|
|
8412
8577
|
assertRetainedMigrationObjects({ from: params.previous?.schema.migrationObjects ?? [], to: objects });
|
|
8413
8578
|
for (const object of objects)
|
|
8414
8579
|
if (object.dialect !== dialect)
|
|
8415
|
-
throw new
|
|
8580
|
+
throw new VibeError39({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}`, meta: { feature: "migrationObject", dialect } });
|
|
8416
8581
|
const physical = params.physical ?? (dialect === "postgres" ? await introspectPhysicalPostgres({ executor }) : dialect === "mysql" ? await introspectMysql({ executor }) : await introspectSqlite({ executor }));
|
|
8417
8582
|
if (params.previous !== undefined)
|
|
8418
8583
|
assertMigrationState({ report: await verifyMigrationState({ executor, expected: params.previous, dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
|
|
@@ -8421,7 +8586,7 @@ async function planSchemaMigration(params) {
|
|
|
8421
8586
|
const identity = object.identity;
|
|
8422
8587
|
if (identity.kind !== "table" || physical.models.some((model) => (model.dbName ?? model.name) === identity.name.name) || normalizeSchema({ schema: physical, dialect }).joinTables.has(identity.name.name))
|
|
8423
8588
|
continue;
|
|
8424
|
-
throw new
|
|
8589
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Catalog table is not visible to schema introspection; automatic planning cannot prove the complete physical state", meta: { reason: "inventory-unavailable", identity: object.identity } });
|
|
8425
8590
|
}
|
|
8426
8591
|
const states = resolved.unavailable.map((identity) => {
|
|
8427
8592
|
const exists = identity.kind === "column" && [physical, params.previous?.schema].some((source) => source?.models.some((model) => (model.dbName ?? model.name) === identity.table.name && model.fields.some((field) => (field.dbName ?? field.name) === identity.column)));
|
|
@@ -8452,7 +8617,7 @@ async function planSchemaMigration(params) {
|
|
|
8452
8617
|
}
|
|
8453
8618
|
for (const object of legacyObjects) {
|
|
8454
8619
|
if (objects.some((entry) => sqlObjectKey12({ identity: entry.identity }) === sqlObjectKey12({ identity: object.identity })))
|
|
8455
|
-
throw new
|
|
8620
|
+
throw new VibeError39({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate combined and migration-only ownership", meta: { identity: object.identity } });
|
|
8456
8621
|
const expected = object.expected ?? params.previous?.definitions.find((entry) => sqlObjectKey12({ identity: entry.identity }) === sqlObjectKey12({ identity: object.identity }));
|
|
8457
8622
|
if (expected === undefined)
|
|
8458
8623
|
states.push({ identity: object.identity, ownership: "migration", state: "unavailable", reason: "legacy-definition-required", differences: [] });
|
|
@@ -8467,7 +8632,8 @@ async function planSchemaMigration(params) {
|
|
|
8467
8632
|
const protectedObjects = [...legacyObjects, ...objects, ...params.previous?.schema.migrationObjects ?? []];
|
|
8468
8633
|
const stripped = preserveMigrationObjects({ from: stripExtensionArtifacts({ schema: physical, artifacts }), to: schema, objects: protectedObjects, namespace });
|
|
8469
8634
|
const checked = await prepareCheckDiff({ executor, from: stripped, to: schema, dialect, ...params.probe === undefined ? {} : { probe: params.probe }, ...params.previous === undefined ? {} : { previous: params.previous }, ...params.replaceChecks === undefined ? {} : { replaceChecks: params.replaceChecks } });
|
|
8470
|
-
const
|
|
8635
|
+
const exclusionsPrepared = await prepareExclusionDiff({ ...params, from: checked, to: schema });
|
|
8636
|
+
const from = dialect === "postgres" ? await prepareIndexPredicateDiff({ executor, from: exclusionsPrepared, to: schema, namespace, ...params.probe === undefined ? {} : { probe: params.probe } }) : exclusionsPrepared;
|
|
8471
8637
|
const steps = diffSchemas({ from, to: schema, dialect });
|
|
8472
8638
|
const known = new Set(schemaObjectIdentities({ schema: params.previous?.schema ?? schema, dialect, namespace }).map((identity) => sqlObjectKey12({ identity })));
|
|
8473
8639
|
const declaredIndexes = normalizeSchema({ schema: params.previous?.schema ?? schema, dialect });
|
|
@@ -8494,7 +8660,7 @@ async function planSchemaMigration(params) {
|
|
|
8494
8660
|
if (object.inspectionUnavailable !== undefined)
|
|
8495
8661
|
states.push({ identity: object.identity, ownership, state: "unavailable", reason: object.inspectionUnavailable, differences: [] });
|
|
8496
8662
|
if (object.inspectionUnavailable !== undefined && invalid.rebuilt.has("table" in object.identity ? object.identity.table.name : object.identity.name.name))
|
|
8497
|
-
throw new
|
|
8663
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "SQLite rebuild cannot discard an unrepresented constraint; declare its independent definition or use a reviewed migration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
|
|
8498
8664
|
if (ownership === "unknown")
|
|
8499
8665
|
states.push({ identity: object.identity, ownership, state: "unavailable", reason: "unmanaged", differences: [] });
|
|
8500
8666
|
let collateral = "table" in object.identity && object.identity.table.namespace === namespace && droppedTables.has(object.identity.table.name);
|
|
@@ -8513,7 +8679,7 @@ async function planSchemaMigration(params) {
|
|
|
8513
8679
|
for (const address of addresses) {
|
|
8514
8680
|
const value = JSON.parse(String(address));
|
|
8515
8681
|
if (!Array.isArray(value) || !Array.isArray(value[2]))
|
|
8516
|
-
throw new
|
|
8682
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency address" });
|
|
8517
8683
|
const names = value[2];
|
|
8518
8684
|
if (names[0] !== namespace || typeof names[1] !== "string")
|
|
8519
8685
|
continue;
|
|
@@ -8523,10 +8689,10 @@ async function planSchemaMigration(params) {
|
|
|
8523
8689
|
}
|
|
8524
8690
|
}
|
|
8525
8691
|
if (dialect === "sqlite" && ownership === "unknown" && object.identity.kind === "generatedColumn" && invalid.rebuilt.has(object.identity.table.name))
|
|
8526
|
-
throw new
|
|
8692
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "SQLite generated-column preservation requires an independent ownership declaration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
|
|
8527
8693
|
const removed = collateral || removals.some((identity) => sqlObjectKey12({ identity }) === key);
|
|
8528
8694
|
if (removed && ownership === "migration")
|
|
8529
|
-
throw new
|
|
8695
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Automatic DDL would remove a migration-owned object", meta: { reason: "migration-owned-removal", identity: object.identity } });
|
|
8530
8696
|
if (removed && ownership === "unknown" && object.backingConstraint === undefined)
|
|
8531
8697
|
unmanagedRemovals.push(object.identity);
|
|
8532
8698
|
}
|
|
@@ -8537,6 +8703,19 @@ async function planSchemaMigration(params) {
|
|
|
8537
8703
|
if (artifact.owns.kind === "trigger" && invalidated.has(artifact.owns.table))
|
|
8538
8704
|
triggers?.delete(artifact.owns.name);
|
|
8539
8705
|
const missing = planMissingArtifacts({ introspected: physical, artifacts, installed: { ...installed, ...triggers === undefined ? {} : { triggers } } });
|
|
8706
|
+
const previousDeclarations = params.previousDeclarations ?? params.previous?.schema;
|
|
8707
|
+
const previousArtifacts = previousDeclarations === undefined ? undefined : collectExtensionArtifacts({ schema: previousDeclarations, dialect, extensions: params.extensions ?? [] });
|
|
8708
|
+
const missingIds = new Set(missing.map((artifact) => artifact.id));
|
|
8709
|
+
const probed = dialect === "postgres" && params.probe === "rollback" && params.probeArtifacts === true ? await probeChangedArtifacts({ executor, namespace, artifacts: collected.objects.filter((artifact) => !missingIds.has(artifact.id)) }) : [];
|
|
8710
|
+
const rebuilt = params.rebuildArtifacts === true ? collected.objects.filter((artifact) => !missingIds.has(artifact.id)) : planChangedArtifacts({ ...previousArtifacts === undefined ? {} : { previous: previousArtifacts }, current: collected, missing, probed: probed.map((entry) => entry.artifact) });
|
|
8711
|
+
const probedById = new Map(probed.map((entry) => [entry.artifact.id, entry]));
|
|
8712
|
+
const rebuiltArtifacts = rebuilt.map((artifact) => {
|
|
8713
|
+
const probe = probedById.get(artifact.id);
|
|
8714
|
+
if (probe !== undefined)
|
|
8715
|
+
return probe;
|
|
8716
|
+
const declared = previousArtifacts?.objects.find((entry) => entry.id === artifact.id);
|
|
8717
|
+
return { artifact, restore: [...(declared ?? artifact).create] };
|
|
8718
|
+
});
|
|
8540
8719
|
const restore = new Map;
|
|
8541
8720
|
const preservedColumns = new Map;
|
|
8542
8721
|
const preservedDefaults = new Map;
|
|
@@ -8560,11 +8739,11 @@ async function planSchemaMigration(params) {
|
|
|
8560
8739
|
continue;
|
|
8561
8740
|
const live = await inspectSqlObject({ executor, dialect, identity });
|
|
8562
8741
|
if (live.state !== "present" || identity.kind !== "columnDefault" && typeof live.definition.facts.columnDefinition !== "string")
|
|
8563
|
-
throw new
|
|
8742
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Cannot read the verified SQLite column definition for preservation", meta: { reason: "sqlite-preservation-unavailable", identity } });
|
|
8564
8743
|
if (identity.kind === "columnDefault") {
|
|
8565
8744
|
const definition = live.definition.facts.definition;
|
|
8566
8745
|
if (definition !== null && typeof definition !== "string")
|
|
8567
|
-
throw new
|
|
8746
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Cannot preserve SQLite default", meta: { identity } });
|
|
8568
8747
|
const defaults = preservedDefaults.get(identity.table.name) ?? new Map;
|
|
8569
8748
|
defaults.set(identity.column, definition);
|
|
8570
8749
|
preservedDefaults.set(identity.table.name, defaults);
|
|
@@ -8586,10 +8765,12 @@ async function planSchemaMigration(params) {
|
|
|
8586
8765
|
return { ...rest, ...typeof expression === "string" ? { default: { kind: "dbgenerated", sql: expression } } : {} };
|
|
8587
8766
|
}) })) };
|
|
8588
8767
|
const core = renderSteps({ steps, dialect, from: dialect === "sqlite" ? physical : from, to: dialect === "mysql" ? { ...target, migrationObjects: protectedObjects } : target, sqliteRestore: restore, sqliteColumns: preservedColumns });
|
|
8768
|
+
const planned = new Set([...missing, ...rebuilt].map((artifact) => artifact.id));
|
|
8589
8769
|
const sql = [
|
|
8590
8770
|
...artifactCreateSql({ artifacts: missing.filter((artifact) => artifact.owns.kind === "postgresExtension") }),
|
|
8591
8771
|
...core,
|
|
8592
|
-
...
|
|
8772
|
+
...artifactDropSql({ artifacts: rebuilt }),
|
|
8773
|
+
...artifactCreateSql({ artifacts: collected.objects.filter((artifact) => planned.has(artifact.id) && !(artifact.owns.kind === "trigger" && invalid.rebuilt.has(artifact.owns.table))) }),
|
|
8593
8774
|
...creates
|
|
8594
8775
|
];
|
|
8595
8776
|
if (sql.length === 0) {
|
|
@@ -8601,7 +8782,7 @@ async function planSchemaMigration(params) {
|
|
|
8601
8782
|
states.push(...report.objects);
|
|
8602
8783
|
}
|
|
8603
8784
|
const blocked = states.some((state) => state.reason === "dependency-inspection-unavailable" || state.ownership !== "unknown" && state.reason !== "legacy-definition-required" && (state.state === "unavailable" || state.state === "changed" || state.state === "missing" && sql.length === 0));
|
|
8604
|
-
return { physical, from, to: schema, namespace, steps, objects: states, unmanagedRemovals, blocked, sql: blocked ? [] : sql };
|
|
8785
|
+
return { physical, from, to: schema, namespace, steps, objects: states, unmanagedRemovals, blocked, sql: blocked ? [] : sql, rebuiltArtifacts };
|
|
8605
8786
|
}
|
|
8606
8787
|
|
|
8607
8788
|
// packages/migrate/src/replay-plan.ts
|
|
@@ -8628,7 +8809,8 @@ async function planReplayMigration(params) {
|
|
|
8628
8809
|
const probe = reference.dialect === "postgres" ? "rollback" : reference.dialect === "mysql" ? "temporary" : "none";
|
|
8629
8810
|
if (probe === "rollback")
|
|
8630
8811
|
await executor({ text: "BEGIN" });
|
|
8631
|
-
const
|
|
8812
|
+
const previousDeclarations = lastDeclaredSchema({ migrations: params.migrations });
|
|
8813
|
+
const plan = await Promise.resolve().then(() => planSchemaMigration({ executor, schema, physical: reference.state.schema, physicalInventory: reference.inventory, dialect: reference.dialect, namespace: reference.namespace, extensions: params.extensions, replaceChecks: params.replaceChecks, probe, probeArtifacts: probe === "rollback", ...previousDeclarations === undefined ? {} : { previousDeclarations } })).finally(async () => {
|
|
8632
8814
|
if (probe === "rollback")
|
|
8633
8815
|
await executor({ text: "ROLLBACK" });
|
|
8634
8816
|
});
|
|
@@ -8647,24 +8829,24 @@ async function planReplayMigration(params) {
|
|
|
8647
8829
|
const retainedObjects = reference.inventory.filter((object) => !declared.has(sqlObjectKey13({ identity: object.identity })) && !removals.has(sqlObjectKey13({ identity: object.identity })) && (object.identity.kind === "function" || object.identity.kind === "trigger" || historicalObjects.has(sqlObjectKey13({ identity: object.identity })))).map((object) => object.identity);
|
|
8648
8830
|
const removedRegistrations = [...historicalObjects].filter(([key]) => present.has(key) && !declared.has(key)).map(([, identity]) => identity);
|
|
8649
8831
|
if (removedRegistrations.length > 0)
|
|
8650
|
-
throw new
|
|
8832
|
+
throw new VibeError40({ code: "VIBE_MIGRATION", message: "Custom registrations removed from declarations still exist in replayed history. Restore the registration or supply reviewed SQL removing the exact named objects before generating again.", meta: { reason: "migration-object-removal-review-required", shadowReusable: true, objects: removedRegistrations } });
|
|
8651
8833
|
return { ...plan, requiredRemovals: [...removals.values()], retainedObjects, securityChanges: securityChangesOf({ steps: plan.steps }), replayRemovals: params.migrations.flatMap((migration) => migration.desiredState?.unmanagedRemovals ?? []) };
|
|
8652
8834
|
}
|
|
8653
8835
|
function assertReplayRemovals(params) {
|
|
8654
8836
|
const wanted = new Set([...params.plan.requiredRemovals, ...params.plan.replayRemovals].map((identity) => sqlObjectKey13({ identity })));
|
|
8655
8837
|
const keys = params.approved.map((identity) => sqlObjectKey13({ identity }));
|
|
8656
8838
|
if (new Set(keys).size !== keys.length)
|
|
8657
|
-
throw new
|
|
8839
|
+
throw new VibeError40({ code: "VIBE_MIGRATION", message: "Removal decisions contain duplicate identities", meta: { reason: "removal-decision-invalid", shadowReusable: true } });
|
|
8658
8840
|
const unused = params.approved.filter((identity) => !wanted.has(sqlObjectKey13({ identity })));
|
|
8659
8841
|
if (unused.length > 0)
|
|
8660
|
-
throw new
|
|
8842
|
+
throw new VibeError40({ code: "VIBE_MIGRATION", message: "Removal decisions do not identify proposed removals. Custom objects without a generated removal require a reviewed hand-authored SQL migration.", meta: { reason: "removal-decision-unused", shadowReusable: true, objects: unused } });
|
|
8661
8843
|
assertNamedRemovals({ removals: params.plan.requiredRemovals, approved: params.approved, acceptIntegrityLoss: params.acceptIntegrityLoss });
|
|
8662
8844
|
}
|
|
8663
8845
|
// packages/migrate/src/baseline.ts
|
|
8664
|
-
import { VibeError as
|
|
8846
|
+
import { VibeError as VibeError45, sqlObjectKey as sqlObjectKey16 } from "@vibeorm/schema";
|
|
8665
8847
|
|
|
8666
8848
|
// packages/migrate/src/reference-state.ts
|
|
8667
|
-
import { VibeError as
|
|
8849
|
+
import { VibeError as VibeError41, sqlObjectKey as sqlObjectKey14 } from "@vibeorm/schema";
|
|
8668
8850
|
async function captureMigrationReference(params) {
|
|
8669
8851
|
const namespace = await migrationNamespace({ executor: params.referenceExecutor, dialect: params.dialect });
|
|
8670
8852
|
const identities = [...schemaObjectIdentities({ schema: params.schema, dialect: params.dialect, namespace }), ...(params.schema.migrationObjects ?? []).map((object) => object.identity)];
|
|
@@ -8674,14 +8856,14 @@ async function captureMigrationReference(params) {
|
|
|
8674
8856
|
continue;
|
|
8675
8857
|
const result = await inspectSqlObject({ executor: params.referenceExecutor, dialect: params.dialect, identity });
|
|
8676
8858
|
if (result.state !== "present")
|
|
8677
|
-
throw new
|
|
8859
|
+
throw new VibeError41({ code: "VIBE_MIGRATION", message: "Independent reference object is missing or unavailable", meta: { reason: "reference-unavailable", identity, state: result.state } });
|
|
8678
8860
|
definitions.push(result.definition);
|
|
8679
8861
|
}
|
|
8680
8862
|
let schema = params.schema;
|
|
8681
8863
|
if (params.dialect === "postgres" && schema.models.some((model) => model.fields.some((field) => field.isUnsupported === true))) {
|
|
8682
8864
|
const resolved = await resolvePostgresTypeIdentities({ executor: params.referenceExecutor, schema, mode: "reference" });
|
|
8683
8865
|
if (resolved.unavailable.length > 0)
|
|
8684
|
-
throw new
|
|
8866
|
+
throw new VibeError41({ code: "VIBE_MIGRATION", message: "Resolve unsupported types in the independent reference before capture", meta: { reason: "type-reference-required", columns: resolved.unavailable } });
|
|
8685
8867
|
const physical = await introspectPhysicalPostgres({ executor: params.referenceExecutor });
|
|
8686
8868
|
for (const model of resolved.schema.models)
|
|
8687
8869
|
for (const field of model.fields) {
|
|
@@ -8689,7 +8871,7 @@ async function captureMigrationReference(params) {
|
|
|
8689
8871
|
continue;
|
|
8690
8872
|
const actual = physical.models.find((entry) => entry.name === (model.dbName ?? model.name))?.fields.find((entry) => entry.name === (field.dbName ?? field.name));
|
|
8691
8873
|
if (actual === undefined || columnTypeKey({ field }) !== columnTypeKey({ field: actual }))
|
|
8692
|
-
throw new
|
|
8874
|
+
throw new VibeError41({ code: "VIBE_MIGRATION", message: "Independent reference column does not match its declared PostgreSQL type", meta: { reason: "reference-type-mismatch", table: model.dbName ?? model.name, column: field.dbName ?? field.name } });
|
|
8693
8875
|
}
|
|
8694
8876
|
schema = resolved.schema;
|
|
8695
8877
|
}
|
|
@@ -8697,13 +8879,13 @@ async function captureMigrationReference(params) {
|
|
|
8697
8879
|
}
|
|
8698
8880
|
|
|
8699
8881
|
// packages/migrate/src/compile-desired-state.ts
|
|
8700
|
-
import { VibeError as
|
|
8882
|
+
import { VibeError as VibeError42 } from "@vibeorm/schema";
|
|
8701
8883
|
import { renderExclusionSetup as renderExclusionSetup2, getSqlDialect as getSqlDialect12, renderMigrationServerVersion as renderMigrationServerVersion2 } from "@vibeorm/sql";
|
|
8702
8884
|
async function compileDesiredState(params) {
|
|
8703
8885
|
if (params.dialect !== "postgres")
|
|
8704
|
-
throw new
|
|
8886
|
+
throw new VibeError42({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Rollback declaration compilation requires PostgreSQL", meta: { feature: "desiredStateCompilation", dialect: params.dialect } });
|
|
8705
8887
|
if (params.steps.some((step) => step.kind === "alterColumn" || step.kind === "addColumn" || step.kind === "addEnumValue"))
|
|
8706
|
-
throw new
|
|
8888
|
+
throw new VibeError42({ code: "VIBE_MIGRATION", message: "CHECK compilation after column or enum changes requires an independently built reference with the new type context", meta: { reason: "reference-required" } });
|
|
8707
8889
|
const definitions = (params.schema.migrationObjects ?? []).flatMap((object) => object.expected === undefined ? [] : [object.expected]);
|
|
8708
8890
|
await params.executor({ text: "SAVEPOINT vibeorm_desired_state" });
|
|
8709
8891
|
try {
|
|
@@ -8726,7 +8908,7 @@ async function compileDesiredState(params) {
|
|
|
8726
8908
|
const query = renderMigrationServerVersion2({ dialect: getSqlDialect12({ dialect: params.dialect }) });
|
|
8727
8909
|
const version = (await params.executor({ text: query.text, values: [] }))[0]?.version;
|
|
8728
8910
|
if (typeof version !== "string")
|
|
8729
|
-
throw new
|
|
8911
|
+
throw new VibeError42({ code: "VIBE_MIGRATION", message: "Cannot determine definition normalization version" });
|
|
8730
8912
|
return { ...createDesiredState({ schema: { ...params.schema, models }, dialect: params.dialect, definitions }), normalization: { format: "catalog-v1", dialect: params.dialect, serverVersion: version } };
|
|
8731
8913
|
} finally {
|
|
8732
8914
|
await params.executor({ text: "ROLLBACK TO SAVEPOINT vibeorm_desired_state" });
|
|
@@ -8735,7 +8917,7 @@ async function compileDesiredState(params) {
|
|
|
8735
8917
|
}
|
|
8736
8918
|
|
|
8737
8919
|
// packages/migrate/src/verify-history.ts
|
|
8738
|
-
import { VibeError as
|
|
8920
|
+
import { VibeError as VibeError44, sqlObjectKey as sqlObjectKey15 } from "@vibeorm/schema";
|
|
8739
8921
|
import { getSqlDialect as getSqlDialect13, renderReplayCoverageGaps as renderReplayCoverageGaps2 } from "@vibeorm/sql";
|
|
8740
8922
|
|
|
8741
8923
|
// packages/migrate/src/compare-sqlite-primary-key.ts
|
|
@@ -8808,7 +8990,7 @@ function compareSqlitePrimaryKeyRepresentations(params) {
|
|
|
8808
8990
|
}
|
|
8809
8991
|
|
|
8810
8992
|
// packages/migrate/src/compare-reference-rls.ts
|
|
8811
|
-
import { VibeError as
|
|
8993
|
+
import { VibeError as VibeError43 } from "@vibeorm/schema";
|
|
8812
8994
|
function securityFacts(params) {
|
|
8813
8995
|
return new Map(params.tables.filter((table) => table.enabled || table.force || table.policies.length > 0).map((table) => [table.table, {
|
|
8814
8996
|
table: table.table,
|
|
@@ -8819,7 +9001,7 @@ function securityFacts(params) {
|
|
|
8819
9001
|
}
|
|
8820
9002
|
async function comparePostgresReferenceRls(params) {
|
|
8821
9003
|
if (params.schema.rls !== undefined && params.referenceExecutor === undefined)
|
|
8822
|
-
throw new
|
|
9004
|
+
throw new VibeError43({ code: "VIBE_MIGRATION", message: "Native RLS verification requires the independently replayed reference connection; target policy probes are not permitted.", meta: { reason: "verification-rls-reference-required" } });
|
|
8823
9005
|
const expected = securityFacts({ tables: params.schema.rls === undefined ? [] : await readPostgresRls({ executor: params.referenceExecutor }) });
|
|
8824
9006
|
const actual = securityFacts({ tables: await readPostgresRls({ executor: params.executor }) });
|
|
8825
9007
|
const objects = [...new Set([...expected.keys(), ...actual.keys()])].sort().map((table) => {
|
|
@@ -8843,14 +9025,14 @@ function selectMigrationVerificationPrefix(params) {
|
|
|
8843
9025
|
for (const [index, file] of params.migrations.entries()) {
|
|
8844
9026
|
const previous = params.migrations[index - 1];
|
|
8845
9027
|
if (seen.has(file.name) || previous !== undefined && previous.name.localeCompare(file.name) > 0) {
|
|
8846
|
-
throw new
|
|
9028
|
+
throw new VibeError44({ code: "VIBE_MIGRATION", message: "Verification requires a complete local migration chain in unique name order", meta: { reason: "verification-history-order", migration: file.name } });
|
|
8847
9029
|
}
|
|
8848
9030
|
seen.add(file.name);
|
|
8849
9031
|
}
|
|
8850
9032
|
for (const [index, row] of params.recorded.entries()) {
|
|
8851
9033
|
const file = params.migrations[index];
|
|
8852
9034
|
if (row.state !== "applied" || row.statementIndex < 0 || row.checksum === "" || file?.name !== row.name || file.desiredStatePending === true || migrationChecksum(file) !== row.checksum) {
|
|
8853
|
-
throw new
|
|
9035
|
+
throw new VibeError44({ code: "VIBE_MIGRATION", message: "Verification requires an exact, complete, checksum-verified applied prefix; inspect migrate status and resolve the history discrepancy first", meta: { reason: "verification-history-unverified", migration: row.name, state: row.state } });
|
|
8854
9036
|
}
|
|
8855
9037
|
}
|
|
8856
9038
|
return params.migrations.slice(0, params.recorded.length);
|
|
@@ -8867,7 +9049,7 @@ async function verifyMigrationReference(params) {
|
|
|
8867
9049
|
const coverage = renderReplayCoverageGaps2({ dialect: getSqlDialect13({ dialect }), namespace });
|
|
8868
9050
|
const gaps = await executor({ text: coverage.text, values: [...coverage.values] });
|
|
8869
9051
|
if (gaps.length > 0)
|
|
8870
|
-
throw new
|
|
9052
|
+
throw new VibeError44({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Target contains objects whose full definitions are outside migration inspection coverage; verification is unavailable. No target changes were made.", meta: { feature: "targetInventoryCoverage", dialect, namespace, objects: gaps } });
|
|
8871
9053
|
const security = dialect === "postgres" ? await comparePostgresReferenceRls({ executor, referenceExecutor: params.referenceExecutor, schema: reference.state.schema }) : { inSync: true, objects: [] };
|
|
8872
9054
|
const inspected = await verifyMigrationState({ executor, dialect, expected: reference.state, probe: "none", batch: true });
|
|
8873
9055
|
const structural = dialect === "sqlite" ? compareSqlitePrimaryKeyRepresentations({ report: inspected }) : inspected;
|
|
@@ -9014,7 +9196,7 @@ async function mysqlComparisonFacts(params) {
|
|
|
9014
9196
|
return { ...facts, members };
|
|
9015
9197
|
}
|
|
9016
9198
|
function referenceContextError() {
|
|
9017
|
-
return new
|
|
9199
|
+
return new VibeError44({ code: "VIBE_MIGRATION", message: "Replay and target dialect/namespace differ; safe reference translation is unavailable. Use a supported same-namespace reference setup", meta: { reason: "verification-reference-context" } });
|
|
9018
9200
|
}
|
|
9019
9201
|
|
|
9020
9202
|
// packages/migrate/src/baseline.ts
|
|
@@ -9022,19 +9204,19 @@ function generateBaselineMigration(params) {
|
|
|
9022
9204
|
const objects = orderedMigrationObjects({ objects: params.schema.migrationObjects ?? [] });
|
|
9023
9205
|
for (const object of objects)
|
|
9024
9206
|
if (object.dialect !== params.dialect)
|
|
9025
|
-
throw new
|
|
9207
|
+
throw new VibeError45({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Baseline object ${object.id} requires ${object.dialect}`, meta: { identity: object.identity, dialect: params.dialect } });
|
|
9026
9208
|
const prerequisites = objects.filter((object) => object.identity.kind === "postgresExtension" || object.identity.kind === "enum" || object.identity.kind === "sequence");
|
|
9027
9209
|
const prerequisiteKeys = new Set(prerequisites.map((object) => sqlObjectKey16({ identity: object.identity })));
|
|
9028
9210
|
for (const object of prerequisites) {
|
|
9029
9211
|
const unresolved = object.dependsOn.filter((identity) => !prerequisiteKeys.has(sqlObjectKey16({ identity })));
|
|
9030
9212
|
if (unresolved.length > 0)
|
|
9031
|
-
throw new
|
|
9213
|
+
throw new VibeError45({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Baseline prerequisite depends on objects created later. Supply reviewed initial SQL with an explicit dependency order.", meta: { reason: "baseline-prerequisite-order", identity: object.identity, dependencies: unresolved } });
|
|
9032
9214
|
}
|
|
9033
9215
|
const schema = { ...params.schema, migrationObjects: objects.filter((object) => !prerequisiteKeys.has(sqlObjectKey16({ identity: object.identity }))) };
|
|
9034
9216
|
const generated = generateMigration({ from: { datasource: schema.datasource, models: [], enums: [], extensions: [] }, to: schema, name: params.name, dialect: params.dialect, extensions: params.extensions ?? [] });
|
|
9035
9217
|
const sql = splitSqlStatements({ sql: joinSqlStatements({ sql: [...prerequisites.flatMap((object) => object.create), ...generated.sql] }), dialect: params.dialect });
|
|
9036
9218
|
if (sql.length === 0)
|
|
9037
|
-
throw new
|
|
9219
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "A baseline must create declared database objects", meta: { reason: "baseline-empty" } });
|
|
9038
9220
|
return {
|
|
9039
9221
|
name: generated.name,
|
|
9040
9222
|
sql,
|
|
@@ -9063,17 +9245,17 @@ async function verifyBaselineDeclarations(params) {
|
|
|
9063
9245
|
async function verifyBaselineTarget(params) {
|
|
9064
9246
|
const { reference } = params;
|
|
9065
9247
|
if (reference.unavailable.length > 0 || reference.inventory.some((object) => object.inspectionUnavailable !== undefined))
|
|
9066
|
-
throw new
|
|
9248
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "Baseline reference contains objects that cannot be inspected. Supply supported reviewed declarations before adoption.", meta: { reason: "baseline-reference-unavailable", objects: reference.unavailable, inventory: reference.inventory.filter((object) => object.inspectionUnavailable !== undefined) } });
|
|
9067
9249
|
const comparison = await verifyMigrationReference({ executor: params.executor, dialect: reference.dialect, reference, referenceExecutor: params.referenceExecutor }).catch((cause) => {
|
|
9068
|
-
if (cause instanceof
|
|
9069
|
-
throw new
|
|
9250
|
+
if (cause instanceof VibeError45 && cause.meta.reason === "verification-rls-reference-required")
|
|
9251
|
+
throw new VibeError45({ code: cause.code, message: cause.message, meta: { reason: "baseline-rls-reference-required" }, cause });
|
|
9070
9252
|
throw cause;
|
|
9071
9253
|
});
|
|
9072
9254
|
const security = comparison.objects.filter((object) => object.reason === "native-rls-mismatch");
|
|
9073
9255
|
if (security.length > 0)
|
|
9074
|
-
throw new
|
|
9256
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "Target native RLS policies or flags differ from the independent baseline. Restore the reviewed security declarations before recording; no target policies were changed.", meta: { reason: "baseline-rls-mismatch", shadowReusable: true, expected: security.flatMap((object) => object.differences.flatMap((difference) => difference.expected === undefined ? [] : [difference.expected])), actual: security.flatMap((object) => object.differences.flatMap((difference) => difference.actual === undefined ? [] : [difference.actual])) } });
|
|
9075
9257
|
if (comparison.unavailable.length > 0)
|
|
9076
|
-
throw new
|
|
9258
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "Baseline comparison contains objects whose definitions cannot be verified in the target context; use a supported reference before adoption.", meta: { reason: "baseline-target-unavailable", shadowReusable: true, objects: comparison.unavailable } });
|
|
9077
9259
|
const report = { inSync: comparison.objects.every((object) => object.state === "matching"), objects: comparison.objects };
|
|
9078
9260
|
assertMigrationState({ report });
|
|
9079
9261
|
const unmanaged = comparison.unmanaged.map((object) => object.identity);
|
|
@@ -9081,14 +9263,14 @@ async function verifyBaselineTarget(params) {
|
|
|
9081
9263
|
const exclusions = params.excludeUnmanaged ?? [];
|
|
9082
9264
|
const excluded = new Set(exclusions.map((identity) => sqlObjectKey16({ identity })));
|
|
9083
9265
|
if (excluded.size !== exclusions.length || exclusions.some((identity) => !extras.has(sqlObjectKey16({ identity }))))
|
|
9084
|
-
throw new
|
|
9266
|
+
throw new VibeError45({ code: "VIBE_CONFIG", message: "Baseline exclusions must uniquely name actual unmanaged target objects, never declared objects", meta: { reason: "baseline-exclusion-invalid", shadowReusable: true, exclusions } });
|
|
9085
9267
|
const unreviewed = unmanaged.filter((identity) => !excluded.has(sqlObjectKey16({ identity })));
|
|
9086
9268
|
if (unreviewed.length > 0)
|
|
9087
|
-
throw new
|
|
9269
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "Target has unmanaged objects. Register their SQL or explicitly exclude each identity with --exclude-unmanaged; no target objects were removed.", meta: { reason: "baseline-unmanaged-objects", shadowReusable: true, objects: unreviewed } });
|
|
9088
9270
|
return { ...report, unmanaged };
|
|
9089
9271
|
}
|
|
9090
9272
|
// packages/migrate/src/baseline-record.ts
|
|
9091
|
-
import { VibeError as
|
|
9273
|
+
import { VibeError as VibeError46 } from "@vibeorm/schema";
|
|
9092
9274
|
import { getSqlDialect as getSqlDialect15, renderStatement } from "@vibeorm/sql";
|
|
9093
9275
|
|
|
9094
9276
|
// packages/migrate/src/history.ts
|
|
@@ -9104,20 +9286,20 @@ async function readMigrationHistory(params) {
|
|
|
9104
9286
|
|
|
9105
9287
|
// packages/migrate/src/baseline-record.ts
|
|
9106
9288
|
function transactionFailure(params) {
|
|
9107
|
-
if (params.cause instanceof
|
|
9289
|
+
if (params.cause instanceof VibeError46)
|
|
9108
9290
|
return params.cause;
|
|
9109
|
-
return new
|
|
9291
|
+
return new VibeError46({ code: "VIBE_MIGRATION", message: "SQLite baseline recording failed; inspect the journal and transaction outcome before retrying.", meta: { reason: "baseline-record-transaction-failed" }, cause: params.cause });
|
|
9110
9292
|
}
|
|
9111
9293
|
async function recordBaselineMigration(params) {
|
|
9112
9294
|
if (params.migration.moduleOrigin !== undefined)
|
|
9113
|
-
throw new
|
|
9295
|
+
throw new VibeError46({ code: "VIBE_MIGRATION", message: "Package module migrations must use the immutable module installation workflow, not baseline adoption", meta: { reason: "baseline-module-unsupported" } });
|
|
9114
9296
|
validateMigrationReplay({ dialect: params.dialect, migrations: [params.migration], acceptSecurityChanges: params.acceptSecurityChanges });
|
|
9115
9297
|
await withMigrationLock({ executor: params.executor, dialect: params.dialect, run: async (executor) => {
|
|
9116
9298
|
const record = async () => {
|
|
9117
9299
|
const assertEmpty = async () => {
|
|
9118
9300
|
const history = await readMigrationHistory({ executor, migrations: [params.migration], dialect: params.dialect });
|
|
9119
9301
|
if (history.recorded.length > 0)
|
|
9120
|
-
throw new
|
|
9302
|
+
throw new VibeError46({ code: "VIBE_MIGRATION", message: "Baseline recording requires an absent or empty migration journal. Use normal apply or explicit recovery for existing history.", meta: { reason: "baseline-journal-not-empty", shadowReusable: true, recorded: history.recorded.map((row) => ({ name: row.name, state: row.state })) } });
|
|
9121
9303
|
};
|
|
9122
9304
|
await assertEmpty();
|
|
9123
9305
|
await params.verify({ executor });
|
|
@@ -9142,14 +9324,14 @@ async function recordBaselineMigration(params) {
|
|
|
9142
9324
|
await executor({ text: "COMMIT" });
|
|
9143
9325
|
}).then(undefined, async (cause) => {
|
|
9144
9326
|
await executor({ text: "ROLLBACK" }).then(undefined, (rollback) => {
|
|
9145
|
-
throw new
|
|
9327
|
+
throw new VibeError46({ code: "VIBE_MIGRATION", message: "Baseline recording failed and its SQLite transaction could not be rolled back. Inspect the journal and connection state before retrying; the outcome is uncertain.", meta: { reason: "baseline-record-rollback-failed" }, cause: new AggregateError([cause, rollback], "Baseline recording and rollback failed") });
|
|
9146
9328
|
});
|
|
9147
9329
|
throw transactionFailure({ cause });
|
|
9148
9330
|
});
|
|
9149
9331
|
} });
|
|
9150
9332
|
}
|
|
9151
9333
|
// packages/migrate/src/down.ts
|
|
9152
|
-
import { VibeError as
|
|
9334
|
+
import { VibeError as VibeError47, sqlObjectKey as sqlObjectKey17 } from "@vibeorm/schema";
|
|
9153
9335
|
function buildRollbackTarget(params) {
|
|
9154
9336
|
const { from, to, dialect } = params;
|
|
9155
9337
|
const skipped = [];
|
|
@@ -9202,6 +9384,7 @@ function generateDownMigration(params) {
|
|
|
9202
9384
|
extensions: params.extensions ?? []
|
|
9203
9385
|
});
|
|
9204
9386
|
const allArtifacts = [...artifacts.setup, ...artifacts.objects];
|
|
9387
|
+
const previous = collectExtensionArtifacts({ schema: params.previousDeclarations ?? from, dialect, extensions: params.extensions ?? [] });
|
|
9205
9388
|
const owned = [...from.migrationObjects ?? [], ...to.migrationObjects ?? []];
|
|
9206
9389
|
const strippedFrom = preserveMigrationObjects({ from: stripExtensionArtifacts({ schema: from, artifacts: allArtifacts }), to: from, objects: owned });
|
|
9207
9390
|
const { target, skipped } = buildRollbackTarget({ from: strippedFrom, to, dialect });
|
|
@@ -9225,14 +9408,27 @@ function generateDownMigration(params) {
|
|
|
9225
9408
|
return added?.kind === "addColumn" ? { ...field, isOptional: added.column.isOptional } : field;
|
|
9226
9409
|
}) })) };
|
|
9227
9410
|
const coreSql = renderSteps({ steps, dialect, from: to, to: dialect === "sqlite" ? sqlitePreservedSchema({ from, to: restored, objects: from.migrationObjects ?? [] }) : restored });
|
|
9228
|
-
const
|
|
9229
|
-
const
|
|
9411
|
+
const declaredBefore = new Set([...previous.setup, ...previous.objects].map((artifact) => JSON.stringify([artifact.id, artifact.owns])));
|
|
9412
|
+
const created = params.physical === undefined ? allArtifacts.filter((artifact) => !declaredBefore.has(JSON.stringify([artifact.id, artifact.owns]))) : planMissingArtifacts({ introspected: params.physical, artifacts: allArtifacts, ...params.installed === undefined ? {} : { installed: params.installed } });
|
|
9413
|
+
const createdDropSql = [];
|
|
9230
9414
|
for (const artifact of [...created].reverse()) {
|
|
9231
9415
|
if (artifact.owns.kind === "postgresExtension") {
|
|
9232
9416
|
skipped.push(`extension "${artifact.owns.name}" stays installed \u2014 other objects may depend on it (drop it manually if intended)`);
|
|
9233
9417
|
continue;
|
|
9234
9418
|
}
|
|
9235
|
-
|
|
9419
|
+
createdDropSql.push(...artifact.drop);
|
|
9420
|
+
}
|
|
9421
|
+
const rebuilt = params.rebuilt ?? planChangedArtifacts({ previous, current: artifacts, missing: created }).map((artifact) => {
|
|
9422
|
+
const declared = previous.objects.find((entry) => entry.id === artifact.id);
|
|
9423
|
+
return { artifact, ...declared === undefined ? {} : { restore: artifactCreateSql({ artifacts: [declared] }) } };
|
|
9424
|
+
});
|
|
9425
|
+
const restoreSql = [];
|
|
9426
|
+
for (const entry of rebuilt) {
|
|
9427
|
+
if (entry.restore === undefined) {
|
|
9428
|
+
skipped.push(`rebuilt ${entry.artifact.id} cannot be restored automatically \u2014 its previous definition is not spelled by the catalog; write it into a reviewed down migration`);
|
|
9429
|
+
continue;
|
|
9430
|
+
}
|
|
9431
|
+
restoreSql.push(...entry.restore);
|
|
9236
9432
|
}
|
|
9237
9433
|
const previousKeys = new Set((from.migrationObjects ?? []).map((object) => sqlObjectKey17({ identity: object.identity })));
|
|
9238
9434
|
const ownedDropSql = [];
|
|
@@ -9244,30 +9440,32 @@ function generateDownMigration(params) {
|
|
|
9244
9440
|
continue;
|
|
9245
9441
|
}
|
|
9246
9442
|
if (object.drop.length === 0)
|
|
9247
|
-
throw new
|
|
9443
|
+
throw new VibeError47({ code: "VIBE_MIGRATION", message: `Migration object ${object.id} needs reviewed reverse SQL`, meta: { reason: "migration-object-reverse-required", id: object.id } });
|
|
9248
9444
|
ownedDropSql.push(...object.drop);
|
|
9249
9445
|
}
|
|
9250
9446
|
return {
|
|
9251
9447
|
name,
|
|
9252
9448
|
target: restored,
|
|
9253
|
-
sql: [...ownedDropSql, ...artifactDropSql, ...coreSql],
|
|
9449
|
+
sql: [...ownedDropSql, ...createdDropSql, ...artifactDropSql({ artifacts: rebuilt.map((entry) => entry.artifact) }), ...coreSql, ...restoreSql],
|
|
9254
9450
|
destructive: steps.some((step) => step.destructive),
|
|
9255
9451
|
...securityChangesOf({ steps }).length === 0 ? {} : { securityChanges: securityChangesOf({ steps }) },
|
|
9256
9452
|
skipped
|
|
9257
9453
|
};
|
|
9258
9454
|
}
|
|
9259
9455
|
// packages/migrate/src/push.ts
|
|
9260
|
-
import { VibeError as
|
|
9456
|
+
import { VibeError as VibeError48, assertValidSchemaIR as assertValidSchemaIR4, providerToDialect as providerToDialect4, sqlObjectKey as sqlObjectKey18, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
|
|
9261
9457
|
import { DIALECT_CAPABILITIES as DIALECT_CAPABILITIES4 } from "@vibeorm/sql";
|
|
9262
9458
|
async function push(params) {
|
|
9263
9459
|
assertValidSchemaIR4({ schema: params.schema });
|
|
9264
9460
|
const dialect = providerToDialect4({ provider: params.schema.datasource.provider });
|
|
9461
|
+
if (params.rebuildArtifacts === true && dialect !== "postgres")
|
|
9462
|
+
throw unsupportedCapability9({ dialect, feature: "rebuilding extension artifacts on push (rebuildArtifacts)", hint: "drop the extension's objects by hand and push again; the transactional rebuild exists on PostgreSQL only" });
|
|
9265
9463
|
if (dialect !== "postgres")
|
|
9266
9464
|
return pushChanges({ ...params, dialect });
|
|
9267
9465
|
await params.executor({ text: "BEGIN" });
|
|
9268
9466
|
let committed = false;
|
|
9269
9467
|
try {
|
|
9270
|
-
const result = await pushChanges({ ...params, dialect });
|
|
9468
|
+
const result = await pushChanges({ ...params, dialect, probe: params.probe ?? "rollback" });
|
|
9271
9469
|
if (params.dryRun !== true) {
|
|
9272
9470
|
await params.executor({ text: "COMMIT" });
|
|
9273
9471
|
committed = true;
|
|
@@ -9289,6 +9487,7 @@ async function pushChanges(params) {
|
|
|
9289
9487
|
...params.previousState === undefined ? {} : { previous: params.previousState },
|
|
9290
9488
|
...params.extensions === undefined ? {} : { extensions: params.extensions },
|
|
9291
9489
|
...params.probe === undefined ? {} : { probe: params.probe },
|
|
9490
|
+
...params.rebuildArtifacts === undefined ? {} : { rebuildArtifacts: params.rebuildArtifacts },
|
|
9292
9491
|
...params.replaceChecks === undefined ? {} : { replaceChecks: params.replaceChecks }
|
|
9293
9492
|
});
|
|
9294
9493
|
const { steps, sql } = plan;
|
|
@@ -9296,11 +9495,11 @@ async function pushChanges(params) {
|
|
|
9296
9495
|
if (params.dryRun === true)
|
|
9297
9496
|
return { steps, applied: [], planned: sql, securityChanges, objects: plan.objects, blocked: plan.blocked, unmanagedRemovals: plan.unmanagedRemovals };
|
|
9298
9497
|
if (plan.blocked)
|
|
9299
|
-
throw new
|
|
9498
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: "Schema push is blocked by unverified object definitions", meta: { reason: "migration-state-unverified", objects: plan.objects } });
|
|
9300
9499
|
assertSecurityAcknowledgement({ securityChanges, ...params.acceptSecurityChanges === undefined ? {} : { acceptSecurityChanges: params.acceptSecurityChanges } });
|
|
9301
9500
|
const destructive = steps.filter((step) => step.destructive);
|
|
9302
9501
|
if (destructive.length > 0 && params.acceptDataLoss !== true)
|
|
9303
|
-
throw new
|
|
9502
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: `push refused: ${destructive.length} destructive step(s) would lose data; pass acceptDataLoss: true: ${destructive.map((step) => describeStep({ step })).join("; ")}`, meta: { destructive: destructive.map((step) => describeStep({ step })) } });
|
|
9304
9503
|
assertRemovalAuthorization({
|
|
9305
9504
|
steps,
|
|
9306
9505
|
dialect,
|
|
@@ -9312,7 +9511,7 @@ async function pushChanges(params) {
|
|
|
9312
9511
|
const approved = new Set((params.removeUnmanaged ?? []).map((identity) => sqlObjectKey18({ identity })));
|
|
9313
9512
|
const unapproved = plan.unmanagedRemovals.filter((identity) => !approved.has(sqlObjectKey18({ identity })));
|
|
9314
9513
|
if (unapproved.length > 0)
|
|
9315
|
-
throw new
|
|
9514
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: "DDL would remove unmanaged dependent objects; approve their exact identities", meta: { reason: "unmanaged-removal-required", objects: unapproved } });
|
|
9316
9515
|
const wrap = dialect !== "postgres" && DIALECT_CAPABILITIES4[dialect].transactionalDdl && !usesSelfManagedTransactions({ sql });
|
|
9317
9516
|
if (wrap)
|
|
9318
9517
|
await executor({ text: "BEGIN" });
|
|
@@ -9330,7 +9529,7 @@ async function pushChanges(params) {
|
|
|
9330
9529
|
} catch {}
|
|
9331
9530
|
} else if (dialect === "sqlite")
|
|
9332
9531
|
await cleanupSelfManagedFailure({ executor });
|
|
9333
|
-
throw new
|
|
9532
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: `push failed while applying schema changes: ${error instanceof Error ? error.message : String(error)}`, meta: { statements: sql }, cause: error });
|
|
9334
9533
|
}
|
|
9335
9534
|
return { steps, applied: sql, securityChanges, objects: plan.objects, desiredState: createDesiredState({ schema: plan.to, dialect }) };
|
|
9336
9535
|
}
|
|
@@ -9345,23 +9544,23 @@ async function pull(params) {
|
|
|
9345
9544
|
}
|
|
9346
9545
|
}
|
|
9347
9546
|
// packages/migrate/src/module-export.ts
|
|
9348
|
-
import { VibeError as
|
|
9547
|
+
import { VibeError as VibeError50 } from "@vibeorm/schema";
|
|
9349
9548
|
|
|
9350
9549
|
// packages/migrate/src/module-bundle.ts
|
|
9351
|
-
import { VibeError as
|
|
9550
|
+
import { VibeError as VibeError49 } from "@vibeorm/schema";
|
|
9352
9551
|
function defineModuleMigrations(params) {
|
|
9353
9552
|
if (!Array.isArray(params.steps) || params.steps.length === 0)
|
|
9354
|
-
throw new
|
|
9553
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "A module upgrade bundle must contain its initial revision", meta: { reason: "module-chain-invalid", moduleId: params.moduleId } });
|
|
9355
9554
|
const migrations = [];
|
|
9356
9555
|
const revisions = new Set;
|
|
9357
9556
|
let previous = null;
|
|
9358
9557
|
for (const step of params.steps) {
|
|
9359
9558
|
if (step.previousRevision !== previous || revisions.has(step.revision))
|
|
9360
|
-
throw new
|
|
9559
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: `Module ${params.moduleId} has a duplicate revision or broken predecessor chain`, meta: { reason: "module-chain-invalid", moduleId: params.moduleId, revision: step.revision } });
|
|
9361
9560
|
const origin = createModuleOrigin({ moduleId: params.moduleId, dialect: params.dialect, revision: step.revision, previousRevision: step.previousRevision, contracts: step.contracts, owned: step.owned, source: step.migration });
|
|
9362
9561
|
const predecessor = migrations.at(-1)?.moduleOrigin;
|
|
9363
9562
|
if (predecessor !== undefined && canonicalMigrationJson({ value: predecessor.source.desiredState.to }) !== canonicalMigrationJson({ value: origin.source.desiredState.from }))
|
|
9364
|
-
throw new
|
|
9563
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "Module source state differs from its predecessor", meta: { reason: "module-predecessor-state", moduleId: params.moduleId } });
|
|
9365
9564
|
validateMigrationReplay({ migrations: [origin.source], dialect: params.dialect, acceptSecurityChanges: true, acceptIntegrityLoss: true, removeUnmanaged: origin.source.desiredState?.unmanagedRemovals ?? [] });
|
|
9366
9565
|
migrations.push(Object.freeze({ ...origin.source, moduleOrigin: origin }));
|
|
9367
9566
|
revisions.add(step.revision);
|
|
@@ -9371,13 +9570,13 @@ function defineModuleMigrations(params) {
|
|
|
9371
9570
|
}
|
|
9372
9571
|
function moduleMigrationFiles(params) {
|
|
9373
9572
|
if (params.bundle === null || typeof params.bundle !== "object" || !Array.isArray(params.bundle.migrations))
|
|
9374
|
-
throw new
|
|
9573
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "Module bundle must contain an immutable migration chain", meta: { reason: "module-chain-invalid" } });
|
|
9375
9574
|
if (!/^[A-Za-z0-9_-]+$/.test(params.prefix))
|
|
9376
|
-
throw new
|
|
9575
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "Module migration prefix must be a safe local directory name", meta: { reason: "module-prefix-invalid" } });
|
|
9377
9576
|
const bundle = defineModuleMigrations({ moduleId: params.bundle.moduleId, dialect: params.bundle.dialect, steps: params.bundle.migrations.map((migration) => {
|
|
9378
9577
|
const origin = migration?.moduleOrigin;
|
|
9379
9578
|
if (origin === undefined || origin.moduleId !== params.bundle.moduleId || origin.dialect !== params.bundle.dialect)
|
|
9380
|
-
throw new
|
|
9579
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "Module bundle lacks matching immutable origin metadata", meta: { reason: "module-origin-invalid" } });
|
|
9381
9580
|
assertModuleOrigin({ value: origin });
|
|
9382
9581
|
restoreModuleMigration({ migration, origin });
|
|
9383
9582
|
return { revision: origin.revision, previousRevision: origin.previousRevision, contracts: origin.contracts, owned: origin.owned, migration: origin.source };
|
|
@@ -9390,7 +9589,7 @@ function emitModuleMigrationExport(params) {
|
|
|
9390
9589
|
moduleMigrationFiles({ bundle: params.bundle, prefix: "export" });
|
|
9391
9590
|
const origin = params.bundle.migrations.at(-1).moduleOrigin;
|
|
9392
9591
|
if (!origin.contracts.includes(params.contract))
|
|
9393
|
-
throw new
|
|
9592
|
+
throw new VibeError50({ code: "VIBE_CONFIG", message: "The package runtime contract is not supported by its migration head", meta: { reason: "module-contract-mismatch", moduleId: origin.moduleId } });
|
|
9394
9593
|
const expectation = { moduleId: origin.moduleId, revision: origin.revision, artifactChecksum: origin.artifactChecksum, contract: params.contract };
|
|
9395
9594
|
return [
|
|
9396
9595
|
{ name: "migrations.js", content: `// Generated immutable migration data; no driver, filesystem or runner import.
|
|
@@ -9407,9 +9606,9 @@ export const moduleReadiness = Object.freeze(${JSON.stringify(expectation)});
|
|
|
9407
9606
|
];
|
|
9408
9607
|
}
|
|
9409
9608
|
// packages/migrate/src/module-pending.ts
|
|
9410
|
-
import { VibeError as
|
|
9609
|
+
import { VibeError as VibeError51, sqlObjectKey as sqlObjectKey19 } from "@vibeorm/schema";
|
|
9411
9610
|
function refusal3(params) {
|
|
9412
|
-
return new
|
|
9611
|
+
return new VibeError51({ code: "VIBE_MIGRATION", message: "Module registration or immutable upgrade history conflicts with the application's declarations; review the registration and deployment sequence", meta: params });
|
|
9413
9612
|
}
|
|
9414
9613
|
function compose(params) {
|
|
9415
9614
|
const { current, from, to } = params;
|
|
@@ -9517,16 +9716,16 @@ function pendingModuleMigrations(params) {
|
|
|
9517
9716
|
return Object.freeze(result);
|
|
9518
9717
|
}
|
|
9519
9718
|
// packages/migrate/src/module-readiness.ts
|
|
9520
|
-
import { VibeError as
|
|
9719
|
+
import { VibeError as VibeError52 } from "@vibeorm/schema";
|
|
9521
9720
|
async function inspectModuleReadiness(params) {
|
|
9522
9721
|
const ids = new Set;
|
|
9523
9722
|
for (const expected of params.modules) {
|
|
9524
9723
|
if (ids.has(expected.moduleId) || !expected.moduleId || !expected.revision || !expected.contract || !/^[a-f0-9]{64}$/.test(expected.artifactChecksum))
|
|
9525
|
-
throw new
|
|
9724
|
+
throw new VibeError52({ code: "VIBE_CONFIG", message: "Module readiness needs a unique module id, revision, contract and immutable artifact checksum", meta: { reason: "module-readiness-expectation", moduleId: expected.moduleId } });
|
|
9526
9725
|
ids.add(expected.moduleId);
|
|
9527
9726
|
}
|
|
9528
9727
|
if (params.compatibility !== undefined && params.compatibility !== "exact" && params.compatibility !== "contract")
|
|
9529
|
-
throw new
|
|
9728
|
+
throw new VibeError52({ code: "VIBE_CONFIG", message: "Unknown module compatibility mode", meta: { reason: "module-readiness-expectation" } });
|
|
9530
9729
|
const records = await readModuleJournal(params);
|
|
9531
9730
|
const heads = moduleHistoryHeads({ records });
|
|
9532
9731
|
const unresolved = [...records.values()].some(({ row, entry }) => row.error !== undefined || row.statementIndex < 0 || entry !== undefined && (!entry.complete || row.statementIndex !== entry.migration.sql.length));
|
|
@@ -9560,7 +9759,7 @@ async function inspectModuleReadiness(params) {
|
|
|
9560
9759
|
return { ready: !unresolved && modules.every((module) => (module.revision === "matching" || module.revision === "compatible") && module.structural.status !== "drifted"), modules };
|
|
9561
9760
|
}
|
|
9562
9761
|
// packages/migrate/src/adoption.ts
|
|
9563
|
-
import { VibeError as
|
|
9762
|
+
import { VibeError as VibeError53 } from "@vibeorm/schema";
|
|
9564
9763
|
async function adoptMigration(params) {
|
|
9565
9764
|
const report = await verifyMigrationState({ executor: params.executor, expected: params.reference, dialect: params.dialect });
|
|
9566
9765
|
assertMigrationState({ report });
|
|
@@ -9576,11 +9775,11 @@ function deployedDesiredState(params) {
|
|
|
9576
9775
|
for (const [index, row] of params.recorded.entries()) {
|
|
9577
9776
|
const file = params.migrations[index];
|
|
9578
9777
|
if (row.state !== "applied" || row.checksum === "" || file?.name !== row.name || migrationChecksum(file) !== row.checksum)
|
|
9579
|
-
throw new
|
|
9778
|
+
throw new VibeError53({ code: "VIBE_MIGRATION", message: "Desired state requires complete, checksum-verified migration history", meta: { reason: "desired-state-history-unverified", migration: row.name, state: row.state } });
|
|
9580
9779
|
}
|
|
9581
9780
|
const applied = new Set(params.recorded.map((row) => row.name));
|
|
9582
9781
|
if (params.migrations.some((migration) => applied.has(migration.name) && migration.desiredStatePending === true))
|
|
9583
|
-
throw new
|
|
9782
|
+
throw new VibeError53({ code: "VIBE_MIGRATION", message: "Applied unfinalized metadata cannot establish desired state", meta: { reason: "desired-state-history-unverified" } });
|
|
9584
9783
|
const last = [...params.migrations].reverse().find((migration) => applied.has(migration.name));
|
|
9585
9784
|
return last?.desiredState?.to;
|
|
9586
9785
|
}
|
|
@@ -9594,6 +9793,7 @@ export {
|
|
|
9594
9793
|
adoptMigration,
|
|
9595
9794
|
applyMigrations,
|
|
9596
9795
|
artifactCreateSql,
|
|
9796
|
+
artifactDropSql,
|
|
9597
9797
|
assertMigrationState,
|
|
9598
9798
|
assertProbeMode,
|
|
9599
9799
|
assertRemovalAuthorization,
|
|
@@ -9630,6 +9830,7 @@ export {
|
|
|
9630
9830
|
introspectPostgres,
|
|
9631
9831
|
introspectSqlite,
|
|
9632
9832
|
isDefaultExecution,
|
|
9833
|
+
lastDeclaredSchema,
|
|
9633
9834
|
legacyMigrationObjects,
|
|
9634
9835
|
migrationChecksum,
|
|
9635
9836
|
migrationNamespace,
|
|
@@ -9641,11 +9842,13 @@ export {
|
|
|
9641
9842
|
parseExecutionSidecar,
|
|
9642
9843
|
parseModuleOrigin,
|
|
9643
9844
|
pendingModuleMigrations,
|
|
9845
|
+
planChangedArtifacts,
|
|
9644
9846
|
planMissingArtifacts,
|
|
9645
9847
|
planRenames,
|
|
9646
9848
|
planReplayMigration,
|
|
9647
9849
|
planSchemaMigration,
|
|
9648
9850
|
prepareCheckDiff,
|
|
9851
|
+
probeChangedArtifacts,
|
|
9649
9852
|
probeInstalledExtensionState,
|
|
9650
9853
|
pull,
|
|
9651
9854
|
push,
|
|
@@ -9672,4 +9875,4 @@ export {
|
|
|
9672
9875
|
withMigrationReference
|
|
9673
9876
|
};
|
|
9674
9877
|
|
|
9675
|
-
//# debugId=
|
|
9878
|
+
//# debugId=9F525A8FE96388CC64756E2164756E21
|