@vibeorm/migrate 2.3.0 → 2.4.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/ddl/sqlite.d.ts.map +1 -1
- package/dist/fs.js +107 -68
- package/dist/fs.js.map +10 -10
- package/dist/index-predicates.d.ts +35 -0
- package/dist/index-predicates.d.ts.map +1 -0
- package/dist/index.js +230 -140
- package/dist/index.js.map +14 -13
- 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.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.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":
|
|
@@ -8395,11 +8434,61 @@ async function compareReplayMigrationObjects(params) {
|
|
|
8395
8434
|
return { ...params.schema, migrationObjects: objects.map((object) => ({ ...object, expected: definitions.get(sqlObjectKey11({ identity: object.identity })) })) };
|
|
8396
8435
|
}
|
|
8397
8436
|
// packages/migrate/src/replay-plan.ts
|
|
8398
|
-
import { VibeError as
|
|
8437
|
+
import { VibeError as VibeError39, sqlObjectKey as sqlObjectKey13 } from "@vibeorm/schema";
|
|
8399
8438
|
|
|
8400
8439
|
// packages/migrate/src/planner.ts
|
|
8401
|
-
import { VibeError as
|
|
8440
|
+
import { VibeError as VibeError38, sqlObjectKey as sqlObjectKey12 } from "@vibeorm/schema";
|
|
8402
8441
|
import { renderSqliteRebuildObjects, sqliteDialect as sqliteDialect4 } from "@vibeorm/sql";
|
|
8442
|
+
|
|
8443
|
+
// packages/migrate/src/index-predicates.ts
|
|
8444
|
+
import { VibeError as VibeError37 } from "@vibeorm/schema";
|
|
8445
|
+
import { defaultIndexName as defaultIndexName2 } from "@vibeorm/schema";
|
|
8446
|
+
function physicalIndexName(params) {
|
|
8447
|
+
return params.index.name ?? defaultIndexName2({ table: params.table, columns: params.columns, unique: params.index.kind === "unique", dialect: "postgres" });
|
|
8448
|
+
}
|
|
8449
|
+
async function prepareIndexPredicateDiff(params) {
|
|
8450
|
+
if (params.probe !== "rollback")
|
|
8451
|
+
return params.from;
|
|
8452
|
+
const models = [];
|
|
8453
|
+
for (const live of params.from.models) {
|
|
8454
|
+
const table = live.dbName ?? live.name;
|
|
8455
|
+
const authored = params.to.models.find((entry) => (entry.dbName ?? entry.name) === table);
|
|
8456
|
+
if (authored === undefined || !live.indexes.some((index) => index.where !== undefined)) {
|
|
8457
|
+
models.push(live);
|
|
8458
|
+
continue;
|
|
8459
|
+
}
|
|
8460
|
+
const authoredByName = new Map;
|
|
8461
|
+
for (const index of authored.indexes) {
|
|
8462
|
+
const columns = index.fields.map((entry) => authored.fields.find((candidate) => candidate.name === entry.name)?.dbName ?? entry.name);
|
|
8463
|
+
authoredByName.set(physicalIndexName({ index, table, columns }), index);
|
|
8464
|
+
}
|
|
8465
|
+
const indexes = [];
|
|
8466
|
+
for (const index of live.indexes) {
|
|
8467
|
+
const next = index.name === undefined ? undefined : authoredByName.get(index.name);
|
|
8468
|
+
if (index.where === undefined || next?.where === undefined || next.where === index.where) {
|
|
8469
|
+
indexes.push(index);
|
|
8470
|
+
continue;
|
|
8471
|
+
}
|
|
8472
|
+
const deparsed = await deparseIndexPredicate({ executor: params.executor, table: { namespace: params.namespace, name: table }, where: next.where });
|
|
8473
|
+
indexes.push(deparsed === index.where ? { ...index, where: next.where } : index);
|
|
8474
|
+
}
|
|
8475
|
+
models.push({ ...live, indexes });
|
|
8476
|
+
}
|
|
8477
|
+
return { ...params.from, models };
|
|
8478
|
+
}
|
|
8479
|
+
async function deparseIndexPredicate(params) {
|
|
8480
|
+
try {
|
|
8481
|
+
const definition = await deparsePostgresCheck({ executor: params.executor, table: params.table, expression: params.where, probe: "rollback" });
|
|
8482
|
+
const match = /^CHECK \((.*)\)$/s.exec(definition);
|
|
8483
|
+
return match === null ? undefined : match[1];
|
|
8484
|
+
} catch (error) {
|
|
8485
|
+
if (error instanceof VibeError37 && error.code === "VIBE_MIGRATION")
|
|
8486
|
+
throw error;
|
|
8487
|
+
return;
|
|
8488
|
+
}
|
|
8489
|
+
}
|
|
8490
|
+
|
|
8491
|
+
// packages/migrate/src/planner.ts
|
|
8403
8492
|
async function planSchemaMigration(params) {
|
|
8404
8493
|
const { executor, dialect } = params;
|
|
8405
8494
|
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 +8501,7 @@ async function planSchemaMigration(params) {
|
|
|
8412
8501
|
assertRetainedMigrationObjects({ from: params.previous?.schema.migrationObjects ?? [], to: objects });
|
|
8413
8502
|
for (const object of objects)
|
|
8414
8503
|
if (object.dialect !== dialect)
|
|
8415
|
-
throw new
|
|
8504
|
+
throw new VibeError38({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}`, meta: { feature: "migrationObject", dialect } });
|
|
8416
8505
|
const physical = params.physical ?? (dialect === "postgres" ? await introspectPhysicalPostgres({ executor }) : dialect === "mysql" ? await introspectMysql({ executor }) : await introspectSqlite({ executor }));
|
|
8417
8506
|
if (params.previous !== undefined)
|
|
8418
8507
|
assertMigrationState({ report: await verifyMigrationState({ executor, expected: params.previous, dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
|
|
@@ -8421,7 +8510,7 @@ async function planSchemaMigration(params) {
|
|
|
8421
8510
|
const identity = object.identity;
|
|
8422
8511
|
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
8512
|
continue;
|
|
8424
|
-
throw new
|
|
8513
|
+
throw new VibeError38({ 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
8514
|
}
|
|
8426
8515
|
const states = resolved.unavailable.map((identity) => {
|
|
8427
8516
|
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 +8541,7 @@ async function planSchemaMigration(params) {
|
|
|
8452
8541
|
}
|
|
8453
8542
|
for (const object of legacyObjects) {
|
|
8454
8543
|
if (objects.some((entry) => sqlObjectKey12({ identity: entry.identity }) === sqlObjectKey12({ identity: object.identity })))
|
|
8455
|
-
throw new
|
|
8544
|
+
throw new VibeError38({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate combined and migration-only ownership", meta: { identity: object.identity } });
|
|
8456
8545
|
const expected = object.expected ?? params.previous?.definitions.find((entry) => sqlObjectKey12({ identity: entry.identity }) === sqlObjectKey12({ identity: object.identity }));
|
|
8457
8546
|
if (expected === undefined)
|
|
8458
8547
|
states.push({ identity: object.identity, ownership: "migration", state: "unavailable", reason: "legacy-definition-required", differences: [] });
|
|
@@ -8467,7 +8556,8 @@ async function planSchemaMigration(params) {
|
|
|
8467
8556
|
const protectedObjects = [...legacyObjects, ...objects, ...params.previous?.schema.migrationObjects ?? []];
|
|
8468
8557
|
const stripped = preserveMigrationObjects({ from: stripExtensionArtifacts({ schema: physical, artifacts }), to: schema, objects: protectedObjects, namespace });
|
|
8469
8558
|
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
|
|
8559
|
+
const exclusionsPrepared = await prepareExclusionDiff({ ...params, from: checked, to: schema });
|
|
8560
|
+
const from = dialect === "postgres" ? await prepareIndexPredicateDiff({ executor, from: exclusionsPrepared, to: schema, namespace, ...params.probe === undefined ? {} : { probe: params.probe } }) : exclusionsPrepared;
|
|
8471
8561
|
const steps = diffSchemas({ from, to: schema, dialect });
|
|
8472
8562
|
const known = new Set(schemaObjectIdentities({ schema: params.previous?.schema ?? schema, dialect, namespace }).map((identity) => sqlObjectKey12({ identity })));
|
|
8473
8563
|
const declaredIndexes = normalizeSchema({ schema: params.previous?.schema ?? schema, dialect });
|
|
@@ -8494,7 +8584,7 @@ async function planSchemaMigration(params) {
|
|
|
8494
8584
|
if (object.inspectionUnavailable !== undefined)
|
|
8495
8585
|
states.push({ identity: object.identity, ownership, state: "unavailable", reason: object.inspectionUnavailable, differences: [] });
|
|
8496
8586
|
if (object.inspectionUnavailable !== undefined && invalid.rebuilt.has("table" in object.identity ? object.identity.table.name : object.identity.name.name))
|
|
8497
|
-
throw new
|
|
8587
|
+
throw new VibeError38({ 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
8588
|
if (ownership === "unknown")
|
|
8499
8589
|
states.push({ identity: object.identity, ownership, state: "unavailable", reason: "unmanaged", differences: [] });
|
|
8500
8590
|
let collateral = "table" in object.identity && object.identity.table.namespace === namespace && droppedTables.has(object.identity.table.name);
|
|
@@ -8513,7 +8603,7 @@ async function planSchemaMigration(params) {
|
|
|
8513
8603
|
for (const address of addresses) {
|
|
8514
8604
|
const value = JSON.parse(String(address));
|
|
8515
8605
|
if (!Array.isArray(value) || !Array.isArray(value[2]))
|
|
8516
|
-
throw new
|
|
8606
|
+
throw new VibeError38({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency address" });
|
|
8517
8607
|
const names = value[2];
|
|
8518
8608
|
if (names[0] !== namespace || typeof names[1] !== "string")
|
|
8519
8609
|
continue;
|
|
@@ -8523,10 +8613,10 @@ async function planSchemaMigration(params) {
|
|
|
8523
8613
|
}
|
|
8524
8614
|
}
|
|
8525
8615
|
if (dialect === "sqlite" && ownership === "unknown" && object.identity.kind === "generatedColumn" && invalid.rebuilt.has(object.identity.table.name))
|
|
8526
|
-
throw new
|
|
8616
|
+
throw new VibeError38({ code: "VIBE_MIGRATION", message: "SQLite generated-column preservation requires an independent ownership declaration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
|
|
8527
8617
|
const removed = collateral || removals.some((identity) => sqlObjectKey12({ identity }) === key);
|
|
8528
8618
|
if (removed && ownership === "migration")
|
|
8529
|
-
throw new
|
|
8619
|
+
throw new VibeError38({ code: "VIBE_MIGRATION", message: "Automatic DDL would remove a migration-owned object", meta: { reason: "migration-owned-removal", identity: object.identity } });
|
|
8530
8620
|
if (removed && ownership === "unknown" && object.backingConstraint === undefined)
|
|
8531
8621
|
unmanagedRemovals.push(object.identity);
|
|
8532
8622
|
}
|
|
@@ -8560,11 +8650,11 @@ async function planSchemaMigration(params) {
|
|
|
8560
8650
|
continue;
|
|
8561
8651
|
const live = await inspectSqlObject({ executor, dialect, identity });
|
|
8562
8652
|
if (live.state !== "present" || identity.kind !== "columnDefault" && typeof live.definition.facts.columnDefinition !== "string")
|
|
8563
|
-
throw new
|
|
8653
|
+
throw new VibeError38({ code: "VIBE_MIGRATION", message: "Cannot read the verified SQLite column definition for preservation", meta: { reason: "sqlite-preservation-unavailable", identity } });
|
|
8564
8654
|
if (identity.kind === "columnDefault") {
|
|
8565
8655
|
const definition = live.definition.facts.definition;
|
|
8566
8656
|
if (definition !== null && typeof definition !== "string")
|
|
8567
|
-
throw new
|
|
8657
|
+
throw new VibeError38({ code: "VIBE_MIGRATION", message: "Cannot preserve SQLite default", meta: { identity } });
|
|
8568
8658
|
const defaults = preservedDefaults.get(identity.table.name) ?? new Map;
|
|
8569
8659
|
defaults.set(identity.column, definition);
|
|
8570
8660
|
preservedDefaults.set(identity.table.name, defaults);
|
|
@@ -8647,24 +8737,24 @@ async function planReplayMigration(params) {
|
|
|
8647
8737
|
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
8738
|
const removedRegistrations = [...historicalObjects].filter(([key]) => present.has(key) && !declared.has(key)).map(([, identity]) => identity);
|
|
8649
8739
|
if (removedRegistrations.length > 0)
|
|
8650
|
-
throw new
|
|
8740
|
+
throw new VibeError39({ 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
8741
|
return { ...plan, requiredRemovals: [...removals.values()], retainedObjects, securityChanges: securityChangesOf({ steps: plan.steps }), replayRemovals: params.migrations.flatMap((migration) => migration.desiredState?.unmanagedRemovals ?? []) };
|
|
8652
8742
|
}
|
|
8653
8743
|
function assertReplayRemovals(params) {
|
|
8654
8744
|
const wanted = new Set([...params.plan.requiredRemovals, ...params.plan.replayRemovals].map((identity) => sqlObjectKey13({ identity })));
|
|
8655
8745
|
const keys = params.approved.map((identity) => sqlObjectKey13({ identity }));
|
|
8656
8746
|
if (new Set(keys).size !== keys.length)
|
|
8657
|
-
throw new
|
|
8747
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Removal decisions contain duplicate identities", meta: { reason: "removal-decision-invalid", shadowReusable: true } });
|
|
8658
8748
|
const unused = params.approved.filter((identity) => !wanted.has(sqlObjectKey13({ identity })));
|
|
8659
8749
|
if (unused.length > 0)
|
|
8660
|
-
throw new
|
|
8750
|
+
throw new VibeError39({ 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
8751
|
assertNamedRemovals({ removals: params.plan.requiredRemovals, approved: params.approved, acceptIntegrityLoss: params.acceptIntegrityLoss });
|
|
8662
8752
|
}
|
|
8663
8753
|
// packages/migrate/src/baseline.ts
|
|
8664
|
-
import { VibeError as
|
|
8754
|
+
import { VibeError as VibeError44, sqlObjectKey as sqlObjectKey16 } from "@vibeorm/schema";
|
|
8665
8755
|
|
|
8666
8756
|
// packages/migrate/src/reference-state.ts
|
|
8667
|
-
import { VibeError as
|
|
8757
|
+
import { VibeError as VibeError40, sqlObjectKey as sqlObjectKey14 } from "@vibeorm/schema";
|
|
8668
8758
|
async function captureMigrationReference(params) {
|
|
8669
8759
|
const namespace = await migrationNamespace({ executor: params.referenceExecutor, dialect: params.dialect });
|
|
8670
8760
|
const identities = [...schemaObjectIdentities({ schema: params.schema, dialect: params.dialect, namespace }), ...(params.schema.migrationObjects ?? []).map((object) => object.identity)];
|
|
@@ -8674,14 +8764,14 @@ async function captureMigrationReference(params) {
|
|
|
8674
8764
|
continue;
|
|
8675
8765
|
const result = await inspectSqlObject({ executor: params.referenceExecutor, dialect: params.dialect, identity });
|
|
8676
8766
|
if (result.state !== "present")
|
|
8677
|
-
throw new
|
|
8767
|
+
throw new VibeError40({ code: "VIBE_MIGRATION", message: "Independent reference object is missing or unavailable", meta: { reason: "reference-unavailable", identity, state: result.state } });
|
|
8678
8768
|
definitions.push(result.definition);
|
|
8679
8769
|
}
|
|
8680
8770
|
let schema = params.schema;
|
|
8681
8771
|
if (params.dialect === "postgres" && schema.models.some((model) => model.fields.some((field) => field.isUnsupported === true))) {
|
|
8682
8772
|
const resolved = await resolvePostgresTypeIdentities({ executor: params.referenceExecutor, schema, mode: "reference" });
|
|
8683
8773
|
if (resolved.unavailable.length > 0)
|
|
8684
|
-
throw new
|
|
8774
|
+
throw new VibeError40({ code: "VIBE_MIGRATION", message: "Resolve unsupported types in the independent reference before capture", meta: { reason: "type-reference-required", columns: resolved.unavailable } });
|
|
8685
8775
|
const physical = await introspectPhysicalPostgres({ executor: params.referenceExecutor });
|
|
8686
8776
|
for (const model of resolved.schema.models)
|
|
8687
8777
|
for (const field of model.fields) {
|
|
@@ -8689,7 +8779,7 @@ async function captureMigrationReference(params) {
|
|
|
8689
8779
|
continue;
|
|
8690
8780
|
const actual = physical.models.find((entry) => entry.name === (model.dbName ?? model.name))?.fields.find((entry) => entry.name === (field.dbName ?? field.name));
|
|
8691
8781
|
if (actual === undefined || columnTypeKey({ field }) !== columnTypeKey({ field: actual }))
|
|
8692
|
-
throw new
|
|
8782
|
+
throw new VibeError40({ 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
8783
|
}
|
|
8694
8784
|
schema = resolved.schema;
|
|
8695
8785
|
}
|
|
@@ -8697,13 +8787,13 @@ async function captureMigrationReference(params) {
|
|
|
8697
8787
|
}
|
|
8698
8788
|
|
|
8699
8789
|
// packages/migrate/src/compile-desired-state.ts
|
|
8700
|
-
import { VibeError as
|
|
8790
|
+
import { VibeError as VibeError41 } from "@vibeorm/schema";
|
|
8701
8791
|
import { renderExclusionSetup as renderExclusionSetup2, getSqlDialect as getSqlDialect12, renderMigrationServerVersion as renderMigrationServerVersion2 } from "@vibeorm/sql";
|
|
8702
8792
|
async function compileDesiredState(params) {
|
|
8703
8793
|
if (params.dialect !== "postgres")
|
|
8704
|
-
throw new
|
|
8794
|
+
throw new VibeError41({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Rollback declaration compilation requires PostgreSQL", meta: { feature: "desiredStateCompilation", dialect: params.dialect } });
|
|
8705
8795
|
if (params.steps.some((step) => step.kind === "alterColumn" || step.kind === "addColumn" || step.kind === "addEnumValue"))
|
|
8706
|
-
throw new
|
|
8796
|
+
throw new VibeError41({ 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
8797
|
const definitions = (params.schema.migrationObjects ?? []).flatMap((object) => object.expected === undefined ? [] : [object.expected]);
|
|
8708
8798
|
await params.executor({ text: "SAVEPOINT vibeorm_desired_state" });
|
|
8709
8799
|
try {
|
|
@@ -8726,7 +8816,7 @@ async function compileDesiredState(params) {
|
|
|
8726
8816
|
const query = renderMigrationServerVersion2({ dialect: getSqlDialect12({ dialect: params.dialect }) });
|
|
8727
8817
|
const version = (await params.executor({ text: query.text, values: [] }))[0]?.version;
|
|
8728
8818
|
if (typeof version !== "string")
|
|
8729
|
-
throw new
|
|
8819
|
+
throw new VibeError41({ code: "VIBE_MIGRATION", message: "Cannot determine definition normalization version" });
|
|
8730
8820
|
return { ...createDesiredState({ schema: { ...params.schema, models }, dialect: params.dialect, definitions }), normalization: { format: "catalog-v1", dialect: params.dialect, serverVersion: version } };
|
|
8731
8821
|
} finally {
|
|
8732
8822
|
await params.executor({ text: "ROLLBACK TO SAVEPOINT vibeorm_desired_state" });
|
|
@@ -8735,7 +8825,7 @@ async function compileDesiredState(params) {
|
|
|
8735
8825
|
}
|
|
8736
8826
|
|
|
8737
8827
|
// packages/migrate/src/verify-history.ts
|
|
8738
|
-
import { VibeError as
|
|
8828
|
+
import { VibeError as VibeError43, sqlObjectKey as sqlObjectKey15 } from "@vibeorm/schema";
|
|
8739
8829
|
import { getSqlDialect as getSqlDialect13, renderReplayCoverageGaps as renderReplayCoverageGaps2 } from "@vibeorm/sql";
|
|
8740
8830
|
|
|
8741
8831
|
// packages/migrate/src/compare-sqlite-primary-key.ts
|
|
@@ -8808,7 +8898,7 @@ function compareSqlitePrimaryKeyRepresentations(params) {
|
|
|
8808
8898
|
}
|
|
8809
8899
|
|
|
8810
8900
|
// packages/migrate/src/compare-reference-rls.ts
|
|
8811
|
-
import { VibeError as
|
|
8901
|
+
import { VibeError as VibeError42 } from "@vibeorm/schema";
|
|
8812
8902
|
function securityFacts(params) {
|
|
8813
8903
|
return new Map(params.tables.filter((table) => table.enabled || table.force || table.policies.length > 0).map((table) => [table.table, {
|
|
8814
8904
|
table: table.table,
|
|
@@ -8819,7 +8909,7 @@ function securityFacts(params) {
|
|
|
8819
8909
|
}
|
|
8820
8910
|
async function comparePostgresReferenceRls(params) {
|
|
8821
8911
|
if (params.schema.rls !== undefined && params.referenceExecutor === undefined)
|
|
8822
|
-
throw new
|
|
8912
|
+
throw new VibeError42({ 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
8913
|
const expected = securityFacts({ tables: params.schema.rls === undefined ? [] : await readPostgresRls({ executor: params.referenceExecutor }) });
|
|
8824
8914
|
const actual = securityFacts({ tables: await readPostgresRls({ executor: params.executor }) });
|
|
8825
8915
|
const objects = [...new Set([...expected.keys(), ...actual.keys()])].sort().map((table) => {
|
|
@@ -8843,14 +8933,14 @@ function selectMigrationVerificationPrefix(params) {
|
|
|
8843
8933
|
for (const [index, file] of params.migrations.entries()) {
|
|
8844
8934
|
const previous = params.migrations[index - 1];
|
|
8845
8935
|
if (seen.has(file.name) || previous !== undefined && previous.name.localeCompare(file.name) > 0) {
|
|
8846
|
-
throw new
|
|
8936
|
+
throw new VibeError43({ code: "VIBE_MIGRATION", message: "Verification requires a complete local migration chain in unique name order", meta: { reason: "verification-history-order", migration: file.name } });
|
|
8847
8937
|
}
|
|
8848
8938
|
seen.add(file.name);
|
|
8849
8939
|
}
|
|
8850
8940
|
for (const [index, row] of params.recorded.entries()) {
|
|
8851
8941
|
const file = params.migrations[index];
|
|
8852
8942
|
if (row.state !== "applied" || row.statementIndex < 0 || row.checksum === "" || file?.name !== row.name || file.desiredStatePending === true || migrationChecksum(file) !== row.checksum) {
|
|
8853
|
-
throw new
|
|
8943
|
+
throw new VibeError43({ 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
8944
|
}
|
|
8855
8945
|
}
|
|
8856
8946
|
return params.migrations.slice(0, params.recorded.length);
|
|
@@ -8867,7 +8957,7 @@ async function verifyMigrationReference(params) {
|
|
|
8867
8957
|
const coverage = renderReplayCoverageGaps2({ dialect: getSqlDialect13({ dialect }), namespace });
|
|
8868
8958
|
const gaps = await executor({ text: coverage.text, values: [...coverage.values] });
|
|
8869
8959
|
if (gaps.length > 0)
|
|
8870
|
-
throw new
|
|
8960
|
+
throw new VibeError43({ 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
8961
|
const security = dialect === "postgres" ? await comparePostgresReferenceRls({ executor, referenceExecutor: params.referenceExecutor, schema: reference.state.schema }) : { inSync: true, objects: [] };
|
|
8872
8962
|
const inspected = await verifyMigrationState({ executor, dialect, expected: reference.state, probe: "none", batch: true });
|
|
8873
8963
|
const structural = dialect === "sqlite" ? compareSqlitePrimaryKeyRepresentations({ report: inspected }) : inspected;
|
|
@@ -9014,7 +9104,7 @@ async function mysqlComparisonFacts(params) {
|
|
|
9014
9104
|
return { ...facts, members };
|
|
9015
9105
|
}
|
|
9016
9106
|
function referenceContextError() {
|
|
9017
|
-
return new
|
|
9107
|
+
return new VibeError43({ 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
9108
|
}
|
|
9019
9109
|
|
|
9020
9110
|
// packages/migrate/src/baseline.ts
|
|
@@ -9022,19 +9112,19 @@ function generateBaselineMigration(params) {
|
|
|
9022
9112
|
const objects = orderedMigrationObjects({ objects: params.schema.migrationObjects ?? [] });
|
|
9023
9113
|
for (const object of objects)
|
|
9024
9114
|
if (object.dialect !== params.dialect)
|
|
9025
|
-
throw new
|
|
9115
|
+
throw new VibeError44({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Baseline object ${object.id} requires ${object.dialect}`, meta: { identity: object.identity, dialect: params.dialect } });
|
|
9026
9116
|
const prerequisites = objects.filter((object) => object.identity.kind === "postgresExtension" || object.identity.kind === "enum" || object.identity.kind === "sequence");
|
|
9027
9117
|
const prerequisiteKeys = new Set(prerequisites.map((object) => sqlObjectKey16({ identity: object.identity })));
|
|
9028
9118
|
for (const object of prerequisites) {
|
|
9029
9119
|
const unresolved = object.dependsOn.filter((identity) => !prerequisiteKeys.has(sqlObjectKey16({ identity })));
|
|
9030
9120
|
if (unresolved.length > 0)
|
|
9031
|
-
throw new
|
|
9121
|
+
throw new VibeError44({ 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
9122
|
}
|
|
9033
9123
|
const schema = { ...params.schema, migrationObjects: objects.filter((object) => !prerequisiteKeys.has(sqlObjectKey16({ identity: object.identity }))) };
|
|
9034
9124
|
const generated = generateMigration({ from: { datasource: schema.datasource, models: [], enums: [], extensions: [] }, to: schema, name: params.name, dialect: params.dialect, extensions: params.extensions ?? [] });
|
|
9035
9125
|
const sql = splitSqlStatements({ sql: joinSqlStatements({ sql: [...prerequisites.flatMap((object) => object.create), ...generated.sql] }), dialect: params.dialect });
|
|
9036
9126
|
if (sql.length === 0)
|
|
9037
|
-
throw new
|
|
9127
|
+
throw new VibeError44({ code: "VIBE_MIGRATION", message: "A baseline must create declared database objects", meta: { reason: "baseline-empty" } });
|
|
9038
9128
|
return {
|
|
9039
9129
|
name: generated.name,
|
|
9040
9130
|
sql,
|
|
@@ -9063,17 +9153,17 @@ async function verifyBaselineDeclarations(params) {
|
|
|
9063
9153
|
async function verifyBaselineTarget(params) {
|
|
9064
9154
|
const { reference } = params;
|
|
9065
9155
|
if (reference.unavailable.length > 0 || reference.inventory.some((object) => object.inspectionUnavailable !== undefined))
|
|
9066
|
-
throw new
|
|
9156
|
+
throw new VibeError44({ 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
9157
|
const comparison = await verifyMigrationReference({ executor: params.executor, dialect: reference.dialect, reference, referenceExecutor: params.referenceExecutor }).catch((cause) => {
|
|
9068
|
-
if (cause instanceof
|
|
9069
|
-
throw new
|
|
9158
|
+
if (cause instanceof VibeError44 && cause.meta.reason === "verification-rls-reference-required")
|
|
9159
|
+
throw new VibeError44({ code: cause.code, message: cause.message, meta: { reason: "baseline-rls-reference-required" }, cause });
|
|
9070
9160
|
throw cause;
|
|
9071
9161
|
});
|
|
9072
9162
|
const security = comparison.objects.filter((object) => object.reason === "native-rls-mismatch");
|
|
9073
9163
|
if (security.length > 0)
|
|
9074
|
-
throw new
|
|
9164
|
+
throw new VibeError44({ 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
9165
|
if (comparison.unavailable.length > 0)
|
|
9076
|
-
throw new
|
|
9166
|
+
throw new VibeError44({ 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
9167
|
const report = { inSync: comparison.objects.every((object) => object.state === "matching"), objects: comparison.objects };
|
|
9078
9168
|
assertMigrationState({ report });
|
|
9079
9169
|
const unmanaged = comparison.unmanaged.map((object) => object.identity);
|
|
@@ -9081,14 +9171,14 @@ async function verifyBaselineTarget(params) {
|
|
|
9081
9171
|
const exclusions = params.excludeUnmanaged ?? [];
|
|
9082
9172
|
const excluded = new Set(exclusions.map((identity) => sqlObjectKey16({ identity })));
|
|
9083
9173
|
if (excluded.size !== exclusions.length || exclusions.some((identity) => !extras.has(sqlObjectKey16({ identity }))))
|
|
9084
|
-
throw new
|
|
9174
|
+
throw new VibeError44({ 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
9175
|
const unreviewed = unmanaged.filter((identity) => !excluded.has(sqlObjectKey16({ identity })));
|
|
9086
9176
|
if (unreviewed.length > 0)
|
|
9087
|
-
throw new
|
|
9177
|
+
throw new VibeError44({ 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
9178
|
return { ...report, unmanaged };
|
|
9089
9179
|
}
|
|
9090
9180
|
// packages/migrate/src/baseline-record.ts
|
|
9091
|
-
import { VibeError as
|
|
9181
|
+
import { VibeError as VibeError45 } from "@vibeorm/schema";
|
|
9092
9182
|
import { getSqlDialect as getSqlDialect15, renderStatement } from "@vibeorm/sql";
|
|
9093
9183
|
|
|
9094
9184
|
// packages/migrate/src/history.ts
|
|
@@ -9104,20 +9194,20 @@ async function readMigrationHistory(params) {
|
|
|
9104
9194
|
|
|
9105
9195
|
// packages/migrate/src/baseline-record.ts
|
|
9106
9196
|
function transactionFailure(params) {
|
|
9107
|
-
if (params.cause instanceof
|
|
9197
|
+
if (params.cause instanceof VibeError45)
|
|
9108
9198
|
return params.cause;
|
|
9109
|
-
return new
|
|
9199
|
+
return new VibeError45({ 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
9200
|
}
|
|
9111
9201
|
async function recordBaselineMigration(params) {
|
|
9112
9202
|
if (params.migration.moduleOrigin !== undefined)
|
|
9113
|
-
throw new
|
|
9203
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "Package module migrations must use the immutable module installation workflow, not baseline adoption", meta: { reason: "baseline-module-unsupported" } });
|
|
9114
9204
|
validateMigrationReplay({ dialect: params.dialect, migrations: [params.migration], acceptSecurityChanges: params.acceptSecurityChanges });
|
|
9115
9205
|
await withMigrationLock({ executor: params.executor, dialect: params.dialect, run: async (executor) => {
|
|
9116
9206
|
const record = async () => {
|
|
9117
9207
|
const assertEmpty = async () => {
|
|
9118
9208
|
const history = await readMigrationHistory({ executor, migrations: [params.migration], dialect: params.dialect });
|
|
9119
9209
|
if (history.recorded.length > 0)
|
|
9120
|
-
throw new
|
|
9210
|
+
throw new VibeError45({ 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
9211
|
};
|
|
9122
9212
|
await assertEmpty();
|
|
9123
9213
|
await params.verify({ executor });
|
|
@@ -9142,14 +9232,14 @@ async function recordBaselineMigration(params) {
|
|
|
9142
9232
|
await executor({ text: "COMMIT" });
|
|
9143
9233
|
}).then(undefined, async (cause) => {
|
|
9144
9234
|
await executor({ text: "ROLLBACK" }).then(undefined, (rollback) => {
|
|
9145
|
-
throw new
|
|
9235
|
+
throw new VibeError45({ 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
9236
|
});
|
|
9147
9237
|
throw transactionFailure({ cause });
|
|
9148
9238
|
});
|
|
9149
9239
|
} });
|
|
9150
9240
|
}
|
|
9151
9241
|
// packages/migrate/src/down.ts
|
|
9152
|
-
import { VibeError as
|
|
9242
|
+
import { VibeError as VibeError46, sqlObjectKey as sqlObjectKey17 } from "@vibeorm/schema";
|
|
9153
9243
|
function buildRollbackTarget(params) {
|
|
9154
9244
|
const { from, to, dialect } = params;
|
|
9155
9245
|
const skipped = [];
|
|
@@ -9244,7 +9334,7 @@ function generateDownMigration(params) {
|
|
|
9244
9334
|
continue;
|
|
9245
9335
|
}
|
|
9246
9336
|
if (object.drop.length === 0)
|
|
9247
|
-
throw new
|
|
9337
|
+
throw new VibeError46({ code: "VIBE_MIGRATION", message: `Migration object ${object.id} needs reviewed reverse SQL`, meta: { reason: "migration-object-reverse-required", id: object.id } });
|
|
9248
9338
|
ownedDropSql.push(...object.drop);
|
|
9249
9339
|
}
|
|
9250
9340
|
return {
|
|
@@ -9257,7 +9347,7 @@ function generateDownMigration(params) {
|
|
|
9257
9347
|
};
|
|
9258
9348
|
}
|
|
9259
9349
|
// packages/migrate/src/push.ts
|
|
9260
|
-
import { VibeError as
|
|
9350
|
+
import { VibeError as VibeError47, assertValidSchemaIR as assertValidSchemaIR4, providerToDialect as providerToDialect4, sqlObjectKey as sqlObjectKey18 } from "@vibeorm/schema";
|
|
9261
9351
|
import { DIALECT_CAPABILITIES as DIALECT_CAPABILITIES4 } from "@vibeorm/sql";
|
|
9262
9352
|
async function push(params) {
|
|
9263
9353
|
assertValidSchemaIR4({ schema: params.schema });
|
|
@@ -9267,7 +9357,7 @@ async function push(params) {
|
|
|
9267
9357
|
await params.executor({ text: "BEGIN" });
|
|
9268
9358
|
let committed = false;
|
|
9269
9359
|
try {
|
|
9270
|
-
const result = await pushChanges({ ...params, dialect });
|
|
9360
|
+
const result = await pushChanges({ ...params, dialect, probe: params.probe ?? "rollback" });
|
|
9271
9361
|
if (params.dryRun !== true) {
|
|
9272
9362
|
await params.executor({ text: "COMMIT" });
|
|
9273
9363
|
committed = true;
|
|
@@ -9296,11 +9386,11 @@ async function pushChanges(params) {
|
|
|
9296
9386
|
if (params.dryRun === true)
|
|
9297
9387
|
return { steps, applied: [], planned: sql, securityChanges, objects: plan.objects, blocked: plan.blocked, unmanagedRemovals: plan.unmanagedRemovals };
|
|
9298
9388
|
if (plan.blocked)
|
|
9299
|
-
throw new
|
|
9389
|
+
throw new VibeError47({ code: "VIBE_MIGRATION", message: "Schema push is blocked by unverified object definitions", meta: { reason: "migration-state-unverified", objects: plan.objects } });
|
|
9300
9390
|
assertSecurityAcknowledgement({ securityChanges, ...params.acceptSecurityChanges === undefined ? {} : { acceptSecurityChanges: params.acceptSecurityChanges } });
|
|
9301
9391
|
const destructive = steps.filter((step) => step.destructive);
|
|
9302
9392
|
if (destructive.length > 0 && params.acceptDataLoss !== true)
|
|
9303
|
-
throw new
|
|
9393
|
+
throw new VibeError47({ 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
9394
|
assertRemovalAuthorization({
|
|
9305
9395
|
steps,
|
|
9306
9396
|
dialect,
|
|
@@ -9312,7 +9402,7 @@ async function pushChanges(params) {
|
|
|
9312
9402
|
const approved = new Set((params.removeUnmanaged ?? []).map((identity) => sqlObjectKey18({ identity })));
|
|
9313
9403
|
const unapproved = plan.unmanagedRemovals.filter((identity) => !approved.has(sqlObjectKey18({ identity })));
|
|
9314
9404
|
if (unapproved.length > 0)
|
|
9315
|
-
throw new
|
|
9405
|
+
throw new VibeError47({ code: "VIBE_MIGRATION", message: "DDL would remove unmanaged dependent objects; approve their exact identities", meta: { reason: "unmanaged-removal-required", objects: unapproved } });
|
|
9316
9406
|
const wrap = dialect !== "postgres" && DIALECT_CAPABILITIES4[dialect].transactionalDdl && !usesSelfManagedTransactions({ sql });
|
|
9317
9407
|
if (wrap)
|
|
9318
9408
|
await executor({ text: "BEGIN" });
|
|
@@ -9330,7 +9420,7 @@ async function pushChanges(params) {
|
|
|
9330
9420
|
} catch {}
|
|
9331
9421
|
} else if (dialect === "sqlite")
|
|
9332
9422
|
await cleanupSelfManagedFailure({ executor });
|
|
9333
|
-
throw new
|
|
9423
|
+
throw new VibeError47({ code: "VIBE_MIGRATION", message: `push failed while applying schema changes: ${error instanceof Error ? error.message : String(error)}`, meta: { statements: sql }, cause: error });
|
|
9334
9424
|
}
|
|
9335
9425
|
return { steps, applied: sql, securityChanges, objects: plan.objects, desiredState: createDesiredState({ schema: plan.to, dialect }) };
|
|
9336
9426
|
}
|
|
@@ -9345,23 +9435,23 @@ async function pull(params) {
|
|
|
9345
9435
|
}
|
|
9346
9436
|
}
|
|
9347
9437
|
// packages/migrate/src/module-export.ts
|
|
9348
|
-
import { VibeError as
|
|
9438
|
+
import { VibeError as VibeError49 } from "@vibeorm/schema";
|
|
9349
9439
|
|
|
9350
9440
|
// packages/migrate/src/module-bundle.ts
|
|
9351
|
-
import { VibeError as
|
|
9441
|
+
import { VibeError as VibeError48 } from "@vibeorm/schema";
|
|
9352
9442
|
function defineModuleMigrations(params) {
|
|
9353
9443
|
if (!Array.isArray(params.steps) || params.steps.length === 0)
|
|
9354
|
-
throw new
|
|
9444
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: "A module upgrade bundle must contain its initial revision", meta: { reason: "module-chain-invalid", moduleId: params.moduleId } });
|
|
9355
9445
|
const migrations = [];
|
|
9356
9446
|
const revisions = new Set;
|
|
9357
9447
|
let previous = null;
|
|
9358
9448
|
for (const step of params.steps) {
|
|
9359
9449
|
if (step.previousRevision !== previous || revisions.has(step.revision))
|
|
9360
|
-
throw new
|
|
9450
|
+
throw new VibeError48({ 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
9451
|
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
9452
|
const predecessor = migrations.at(-1)?.moduleOrigin;
|
|
9363
9453
|
if (predecessor !== undefined && canonicalMigrationJson({ value: predecessor.source.desiredState.to }) !== canonicalMigrationJson({ value: origin.source.desiredState.from }))
|
|
9364
|
-
throw new
|
|
9454
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: "Module source state differs from its predecessor", meta: { reason: "module-predecessor-state", moduleId: params.moduleId } });
|
|
9365
9455
|
validateMigrationReplay({ migrations: [origin.source], dialect: params.dialect, acceptSecurityChanges: true, acceptIntegrityLoss: true, removeUnmanaged: origin.source.desiredState?.unmanagedRemovals ?? [] });
|
|
9366
9456
|
migrations.push(Object.freeze({ ...origin.source, moduleOrigin: origin }));
|
|
9367
9457
|
revisions.add(step.revision);
|
|
@@ -9371,13 +9461,13 @@ function defineModuleMigrations(params) {
|
|
|
9371
9461
|
}
|
|
9372
9462
|
function moduleMigrationFiles(params) {
|
|
9373
9463
|
if (params.bundle === null || typeof params.bundle !== "object" || !Array.isArray(params.bundle.migrations))
|
|
9374
|
-
throw new
|
|
9464
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: "Module bundle must contain an immutable migration chain", meta: { reason: "module-chain-invalid" } });
|
|
9375
9465
|
if (!/^[A-Za-z0-9_-]+$/.test(params.prefix))
|
|
9376
|
-
throw new
|
|
9466
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: "Module migration prefix must be a safe local directory name", meta: { reason: "module-prefix-invalid" } });
|
|
9377
9467
|
const bundle = defineModuleMigrations({ moduleId: params.bundle.moduleId, dialect: params.bundle.dialect, steps: params.bundle.migrations.map((migration) => {
|
|
9378
9468
|
const origin = migration?.moduleOrigin;
|
|
9379
9469
|
if (origin === undefined || origin.moduleId !== params.bundle.moduleId || origin.dialect !== params.bundle.dialect)
|
|
9380
|
-
throw new
|
|
9470
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: "Module bundle lacks matching immutable origin metadata", meta: { reason: "module-origin-invalid" } });
|
|
9381
9471
|
assertModuleOrigin({ value: origin });
|
|
9382
9472
|
restoreModuleMigration({ migration, origin });
|
|
9383
9473
|
return { revision: origin.revision, previousRevision: origin.previousRevision, contracts: origin.contracts, owned: origin.owned, migration: origin.source };
|
|
@@ -9390,7 +9480,7 @@ function emitModuleMigrationExport(params) {
|
|
|
9390
9480
|
moduleMigrationFiles({ bundle: params.bundle, prefix: "export" });
|
|
9391
9481
|
const origin = params.bundle.migrations.at(-1).moduleOrigin;
|
|
9392
9482
|
if (!origin.contracts.includes(params.contract))
|
|
9393
|
-
throw new
|
|
9483
|
+
throw new VibeError49({ code: "VIBE_CONFIG", message: "The package runtime contract is not supported by its migration head", meta: { reason: "module-contract-mismatch", moduleId: origin.moduleId } });
|
|
9394
9484
|
const expectation = { moduleId: origin.moduleId, revision: origin.revision, artifactChecksum: origin.artifactChecksum, contract: params.contract };
|
|
9395
9485
|
return [
|
|
9396
9486
|
{ name: "migrations.js", content: `// Generated immutable migration data; no driver, filesystem or runner import.
|
|
@@ -9407,9 +9497,9 @@ export const moduleReadiness = Object.freeze(${JSON.stringify(expectation)});
|
|
|
9407
9497
|
];
|
|
9408
9498
|
}
|
|
9409
9499
|
// packages/migrate/src/module-pending.ts
|
|
9410
|
-
import { VibeError as
|
|
9500
|
+
import { VibeError as VibeError50, sqlObjectKey as sqlObjectKey19 } from "@vibeorm/schema";
|
|
9411
9501
|
function refusal3(params) {
|
|
9412
|
-
return new
|
|
9502
|
+
return new VibeError50({ 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
9503
|
}
|
|
9414
9504
|
function compose(params) {
|
|
9415
9505
|
const { current, from, to } = params;
|
|
@@ -9517,16 +9607,16 @@ function pendingModuleMigrations(params) {
|
|
|
9517
9607
|
return Object.freeze(result);
|
|
9518
9608
|
}
|
|
9519
9609
|
// packages/migrate/src/module-readiness.ts
|
|
9520
|
-
import { VibeError as
|
|
9610
|
+
import { VibeError as VibeError51 } from "@vibeorm/schema";
|
|
9521
9611
|
async function inspectModuleReadiness(params) {
|
|
9522
9612
|
const ids = new Set;
|
|
9523
9613
|
for (const expected of params.modules) {
|
|
9524
9614
|
if (ids.has(expected.moduleId) || !expected.moduleId || !expected.revision || !expected.contract || !/^[a-f0-9]{64}$/.test(expected.artifactChecksum))
|
|
9525
|
-
throw new
|
|
9615
|
+
throw new VibeError51({ 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
9616
|
ids.add(expected.moduleId);
|
|
9527
9617
|
}
|
|
9528
9618
|
if (params.compatibility !== undefined && params.compatibility !== "exact" && params.compatibility !== "contract")
|
|
9529
|
-
throw new
|
|
9619
|
+
throw new VibeError51({ code: "VIBE_CONFIG", message: "Unknown module compatibility mode", meta: { reason: "module-readiness-expectation" } });
|
|
9530
9620
|
const records = await readModuleJournal(params);
|
|
9531
9621
|
const heads = moduleHistoryHeads({ records });
|
|
9532
9622
|
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 +9650,7 @@ async function inspectModuleReadiness(params) {
|
|
|
9560
9650
|
return { ready: !unresolved && modules.every((module) => (module.revision === "matching" || module.revision === "compatible") && module.structural.status !== "drifted"), modules };
|
|
9561
9651
|
}
|
|
9562
9652
|
// packages/migrate/src/adoption.ts
|
|
9563
|
-
import { VibeError as
|
|
9653
|
+
import { VibeError as VibeError52 } from "@vibeorm/schema";
|
|
9564
9654
|
async function adoptMigration(params) {
|
|
9565
9655
|
const report = await verifyMigrationState({ executor: params.executor, expected: params.reference, dialect: params.dialect });
|
|
9566
9656
|
assertMigrationState({ report });
|
|
@@ -9576,11 +9666,11 @@ function deployedDesiredState(params) {
|
|
|
9576
9666
|
for (const [index, row] of params.recorded.entries()) {
|
|
9577
9667
|
const file = params.migrations[index];
|
|
9578
9668
|
if (row.state !== "applied" || row.checksum === "" || file?.name !== row.name || migrationChecksum(file) !== row.checksum)
|
|
9579
|
-
throw new
|
|
9669
|
+
throw new VibeError52({ 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
9670
|
}
|
|
9581
9671
|
const applied = new Set(params.recorded.map((row) => row.name));
|
|
9582
9672
|
if (params.migrations.some((migration) => applied.has(migration.name) && migration.desiredStatePending === true))
|
|
9583
|
-
throw new
|
|
9673
|
+
throw new VibeError52({ code: "VIBE_MIGRATION", message: "Applied unfinalized metadata cannot establish desired state", meta: { reason: "desired-state-history-unverified" } });
|
|
9584
9674
|
const last = [...params.migrations].reverse().find((migration) => applied.has(migration.name));
|
|
9585
9675
|
return last?.desiredState?.to;
|
|
9586
9676
|
}
|
|
@@ -9672,4 +9762,4 @@ export {
|
|
|
9672
9762
|
withMigrationReference
|
|
9673
9763
|
};
|
|
9674
9764
|
|
|
9675
|
-
//# debugId=
|
|
9765
|
+
//# debugId=AA96882D3F595BC864756E2164756E21
|