@vibeorm/migrate 2.7.0 → 3.0.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/dist/desired-state.d.ts +9 -0
  3. package/dist/differ.d.ts +7 -0
  4. package/dist/fs.js +395 -257
  5. package/dist/fs.js.map +14 -13
  6. package/dist/index.js +766 -455
  7. package/dist/index.js.map +16 -14
  8. package/dist/inspect-objects.d.ts +8 -0
  9. package/dist/introspect/group.d.ts +6 -0
  10. package/dist/introspect/mysql.d.ts +4 -3
  11. package/dist/object-ownership.d.ts +8 -0
  12. package/dist/sqlite-fts-upgrade.d.ts +64 -0
  13. package/package.json +6 -4
  14. package/dist/adoption.d.ts.map +0 -1
  15. package/dist/artifact-probe.d.ts.map +0 -1
  16. package/dist/baseline-record.d.ts.map +0 -1
  17. package/dist/baseline.d.ts.map +0 -1
  18. package/dist/compare-reference-rls.d.ts.map +0 -1
  19. package/dist/compare-sqlite-primary-key.d.ts.map +0 -1
  20. package/dist/compile-desired-state.d.ts.map +0 -1
  21. package/dist/ddl/drop-order.d.ts.map +0 -1
  22. package/dist/ddl/mysql.d.ts.map +0 -1
  23. package/dist/ddl/postgres.d.ts.map +0 -1
  24. package/dist/ddl/render.d.ts.map +0 -1
  25. package/dist/ddl/rls.d.ts.map +0 -1
  26. package/dist/ddl/sqlite.d.ts.map +0 -1
  27. package/dist/desired-state.d.ts.map +0 -1
  28. package/dist/differ.d.ts.map +0 -1
  29. package/dist/down.d.ts.map +0 -1
  30. package/dist/enum-values.d.ts.map +0 -1
  31. package/dist/exclusions.d.ts.map +0 -1
  32. package/dist/execute.d.ts.map +0 -1
  33. package/dist/execution.d.ts.map +0 -1
  34. package/dist/extensions.d.ts.map +0 -1
  35. package/dist/external-dependencies.d.ts.map +0 -1
  36. package/dist/external-objects.d.ts.map +0 -1
  37. package/dist/fs.d.ts.map +0 -1
  38. package/dist/history.d.ts.map +0 -1
  39. package/dist/index-predicates.d.ts.map +0 -1
  40. package/dist/index.d.ts.map +0 -1
  41. package/dist/inspect-objects.d.ts.map +0 -1
  42. package/dist/introspect/mysql.d.ts.map +0 -1
  43. package/dist/introspect/postgres-rls.d.ts.map +0 -1
  44. package/dist/introspect/postgres.d.ts.map +0 -1
  45. package/dist/introspect/shared.d.ts.map +0 -1
  46. package/dist/introspect/sqlite.d.ts.map +0 -1
  47. package/dist/inventory.d.ts.map +0 -1
  48. package/dist/journal-read.d.ts.map +0 -1
  49. package/dist/journal-table.d.ts.map +0 -1
  50. package/dist/migration-checksum.d.ts.map +0 -1
  51. package/dist/module-adoption-record.d.ts.map +0 -1
  52. package/dist/module-adoption-reference.d.ts.map +0 -1
  53. package/dist/module-adoption.d.ts.map +0 -1
  54. package/dist/module-bundle.d.ts.map +0 -1
  55. package/dist/module-export.d.ts.map +0 -1
  56. package/dist/module-history.d.ts.map +0 -1
  57. package/dist/module-journal.d.ts.map +0 -1
  58. package/dist/module-origin.d.ts.map +0 -1
  59. package/dist/module-ownership.d.ts.map +0 -1
  60. package/dist/module-pending.d.ts.map +0 -1
  61. package/dist/module-readiness.d.ts.map +0 -1
  62. package/dist/mysql-catalog-visibility.d.ts.map +0 -1
  63. package/dist/mysql-types.d.ts.map +0 -1
  64. package/dist/normalize.d.ts.map +0 -1
  65. package/dist/object-ownership.d.ts.map +0 -1
  66. package/dist/operator-classes.d.ts.map +0 -1
  67. package/dist/planner.d.ts.map +0 -1
  68. package/dist/postgres-default-equivalence.d.ts.map +0 -1
  69. package/dist/postgres-type-identity.d.ts.map +0 -1
  70. package/dist/postgres-types.d.ts.map +0 -1
  71. package/dist/push.d.ts.map +0 -1
  72. package/dist/recovery.d.ts.map +0 -1
  73. package/dist/reference-state.d.ts.map +0 -1
  74. package/dist/relations.d.ts.map +0 -1
  75. package/dist/renames.d.ts.map +0 -1
  76. package/dist/replay-objects.d.ts.map +0 -1
  77. package/dist/replay-plan.d.ts.map +0 -1
  78. package/dist/replay-reference.d.ts.map +0 -1
  79. package/dist/runner.d.ts.map +0 -1
  80. package/dist/shadow-dedicated.d.ts.map +0 -1
  81. package/dist/shadow-guard.d.ts.map +0 -1
  82. package/dist/shadow-types.d.ts.map +0 -1
  83. package/dist/shadow.d.ts.map +0 -1
  84. package/dist/sql-statements.d.ts.map +0 -1
  85. package/dist/sqlite-types.d.ts.map +0 -1
  86. package/dist/types.d.ts.map +0 -1
  87. package/dist/verify-history.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // @bun
2
2
  // packages/migrate/src/external-objects.ts
3
- import { IDENTIFIER_LIMITS, VibeError as VibeError17, identifierLength, sqlNamespaceIssue, sqlObjectKey as sqlObjectKey6, sqlObjectNamespace as sqlObjectNamespace2, qualifiedSqlName as qualifiedSqlName8, enumStorageOf as enumStorageOf3, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
3
+ import { IDENTIFIER_LIMITS, VibeError as VibeError18, identifierLength, sqlNamespaceIssue, sqlObjectKey as sqlObjectKey6, sqlObjectNamespace as sqlObjectNamespace2, qualifiedSqlName as qualifiedSqlName8, enumStorageOf as enumStorageOf3, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
4
4
  import { getSqlDialect as getSqlDialect3, renderExternalOwnershipCatalog, renderReplayCoverageGaps } from "@vibeorm/sql";
5
5
 
6
6
  // packages/migrate/src/inventory.ts
7
- import { VibeError as VibeError16, defaultForeignKeyName as defaultForeignKeyName2, defaultPrimaryKeyName as defaultPrimaryKeyName2, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey5 } from "@vibeorm/schema";
7
+ import { VibeError as VibeError17, defaultForeignKeyName as defaultForeignKeyName2, defaultPrimaryKeyName as defaultPrimaryKeyName2, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey5 } from "@vibeorm/schema";
8
8
  import { getSqlDialect as getSqlDialect2, renderMigrationInventory, renderSqliteColumnInventory as renderSqliteColumnInventory2, renderMysqlObjectMembers as renderMysqlObjectMembers2, sqliteTableConstraints as sqliteTableConstraints3, renderSqliteForeignKeyInventory } from "@vibeorm/sql";
9
9
 
10
10
  // packages/migrate/src/exclusions.ts
@@ -85,16 +85,12 @@ async function verifyExclusions(params) {
85
85
 
86
86
  // packages/migrate/src/inspect-objects.ts
87
87
  import { randomUUID as randomUUID2 } from "crypto";
88
- import { VibeError as VibeError15, sqlObjectKey as sqlObjectKey4, sqlObjectNamespace, DIALECT_CAPABILITIES as DIALECT_CAPABILITIES2, qualifiedSqlName as qualifiedSqlName7 } from "@vibeorm/schema";
88
+ import { VibeError as VibeError16, sqlObjectKey as sqlObjectKey4, sqlObjectNamespace, DIALECT_CAPABILITIES as DIALECT_CAPABILITIES2, qualifiedSqlName as qualifiedSqlName7 } from "@vibeorm/schema";
89
89
  import { getSqlDialect, renderMigrationObjectBatch, renderMigrationObjectQuery as renderMigrationObjectQuery2, renderCheckProbe, renderMigrationNamespace, renderMigrationDependencies, renderMysqlObjectMembers, renderMysqlCheckProbe, sqliteColumnClauses as sqliteColumnClauses2, renderMigrationVisibility, sqliteTableConstraints as sqliteTableConstraints2, renderSqliteColumnInventory } from "@vibeorm/sql";
90
90
 
91
91
  // packages/migrate/src/desired-state.ts
92
92
  import { VibeError as VibeError2, assertValidSchemaIR, isSqlObjectDefinition, isSqlObjectIdentity, providerToDialect, sqlObjectKey as sqlObjectKey2 } from "@vibeorm/schema";
93
93
  var UNFINALIZED_DESIRED_STATE = Object.freeze({ version: 1, status: "unfinalized" });
94
- function isUnfinalizedDesiredState(params) {
95
- const value = JSON.parse(params.text);
96
- return record(value) && value.version === 1 && value.status === "unfinalized" && Object.keys(value).length === 2;
97
- }
98
94
  var DESIRED_STATE_METADATA = Object.freeze({ filename: "desired-state.json", version: 1 });
99
95
  function createDesiredState(params) {
100
96
  assertValidSchemaIR({ schema: params.schema });
@@ -177,6 +173,15 @@ function parseDesiredStateSidecar(params) {
177
173
  return checked.value;
178
174
  }, failure: (cause) => invalid({ migration: params.migration, detail: "malformed metadata", cause }) });
179
175
  }
176
+ function loadDesiredStateSidecar(params) {
177
+ return parseMigrationMetadata({ text: params.text, parse: (value) => {
178
+ if (record(value) && value.version === 1 && value.status === "unfinalized" && Object.keys(value).length === 2)
179
+ return { desiredStatePending: true };
180
+ const checked = { value, migration: params.migration };
181
+ assertDesiredState(checked);
182
+ return { desiredState: checked.value };
183
+ }, failure: (cause) => invalid({ migration: params.migration, detail: "malformed metadata", cause }) });
184
+ }
180
185
  function parseMigrationMetadata(params) {
181
186
  try {
182
187
  return params.parse(JSON.parse(params.text));
@@ -1046,6 +1051,32 @@ function parseMysqlEnumColumnType(params) {
1046
1051
  }
1047
1052
 
1048
1053
  // packages/migrate/src/normalize.ts
1054
+ function firstByName(params) {
1055
+ const result = new Map;
1056
+ for (const entry of params.entries)
1057
+ if (!result.has(entry.name))
1058
+ result.set(entry.name, entry);
1059
+ return result;
1060
+ }
1061
+ function normalizationLookup(params) {
1062
+ const enums = new Map;
1063
+ const scopedEnums = new Map;
1064
+ for (const entry of params.schema.enums) {
1065
+ const namespace = qualifiedSqlName3({ name: entry.name, namespace: entry.namespace }).namespace;
1066
+ for (const name of new Set([entry.name, entry.dbName ?? entry.name])) {
1067
+ if (!enums.has(name))
1068
+ enums.set(name, entry);
1069
+ const key = JSON.stringify([namespace, name]);
1070
+ if (!scopedEnums.has(key))
1071
+ scopedEnums.set(key, entry);
1072
+ }
1073
+ }
1074
+ return { models: firstByName({ entries: params.schema.models }), fields: new Map(params.schema.models.map((model) => [model, firstByName({ entries: model.fields })])), enums, scopedEnums };
1075
+ }
1076
+ function lookupFieldName(params) {
1077
+ const field = params.lookup.fields.get(params.model)?.get(params.name);
1078
+ return field?.dbName ?? field?.name ?? params.name;
1079
+ }
1049
1080
  function normalizeSchema(params) {
1050
1081
  const { schema } = params;
1051
1082
  const dialect = params.dialect ?? "postgres";
@@ -1058,6 +1089,7 @@ function normalizeSchema(params) {
1058
1089
  continue;
1059
1090
  enums.set(physicalNameKey({ name: enumDef.dbName ?? enumDef.name, namespace: enumDef.namespace, dialect }), enumDef);
1060
1091
  }
1092
+ const lookup = normalizationLookup({ schema });
1061
1093
  const tables = new Map;
1062
1094
  const views = new Map;
1063
1095
  for (const model of schema.models) {
@@ -1067,25 +1099,25 @@ function normalizeSchema(params) {
1067
1099
  views.set(physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect }), model);
1068
1100
  continue;
1069
1101
  }
1070
- const table = normalizeModel({ model, schema, dialect, enums });
1102
+ const table = normalizeModel({ model, lookup, dialect, enums });
1071
1103
  tables.set(physicalNameKey({ name: table.table, namespace: table.namespace, dialect }), table);
1072
1104
  }
1073
1105
  const joinTables = new Map;
1074
1106
  for (const pair of detectImplicitM2MPairs({ schema })) {
1075
- const join = normalizeJoinTable({ pair, schema, dialect });
1107
+ const join = normalizeJoinTable({ pair, lookup, dialect });
1076
1108
  joinTables.set(physicalNameKey({ name: join.table, namespace: join.namespace, dialect }), join);
1077
1109
  }
1078
1110
  return { tables, joinTables, enums, views };
1079
1111
  }
1080
1112
  function physicalEnumField(params) {
1081
- const { field, schema, dialect, table } = params;
1113
+ const { field, lookup, dialect, table } = params;
1082
1114
  if (!isEnumType5(field.type))
1083
1115
  return field;
1084
1116
  const type = field.type;
1085
1117
  if (type.namespace !== undefined)
1086
1118
  assertSqlNamespace({ namespace: type.namespace, dialect });
1087
1119
  const name = type.enum;
1088
- const enumDef = schema.enums.find((candidate) => (candidate.name === name || candidate.dbName === name) && (type.namespace === undefined || qualifiedSqlName3({ name, namespace: candidate.namespace }).namespace === type.namespace));
1120
+ const enumDef = type.namespace === undefined ? lookup.enums.get(name) : lookup.scopedEnums.get(JSON.stringify([type.namespace, name]));
1089
1121
  if (enumDef === undefined)
1090
1122
  return field;
1091
1123
  if (enumStorageOf({ enumDef }) !== "string") {
@@ -1150,48 +1182,49 @@ function defaultFingerprintFor(params) {
1150
1182
  }
1151
1183
  }
1152
1184
  function normalizeModel(params) {
1153
- const { model, schema, dialect, enums } = params;
1185
+ const { model, lookup, dialect, enums } = params;
1154
1186
  const table = model.dbName ?? model.name;
1155
1187
  const columns = new Map;
1156
1188
  for (const logicalField of model.fields) {
1157
- const field = physicalEnumField({ field: logicalField, schema, dialect, table });
1189
+ const field = physicalEnumField({ field: logicalField, lookup, dialect, table });
1158
1190
  const name = field.dbName ?? field.name;
1191
+ const typeKey = columnTypeKeyFor({ field, dialect, enums });
1159
1192
  columns.set(name, {
1160
1193
  name,
1161
1194
  field,
1162
- typeKey: columnTypeKeyFor({ field, dialect, enums }),
1163
- exactTypeKey: columnTypeKeyFor({ field, dialect, enums, exact: true }),
1195
+ typeKey,
1196
+ exactTypeKey: dialect === "postgres" ? columnTypeKeyFor({ field, dialect, enums, exact: true }) : typeKey,
1164
1197
  pinsExactType: dialect === "postgres" && declaresExactColumnType({ field }),
1165
1198
  nullable: field.isOptional,
1166
1199
  defaultKey: defaultFingerprintFor({ field, dialect })
1167
1200
  });
1168
1201
  }
1169
- const pk = normalizePk({ model });
1170
- const indexes = normalizeIndexes({ model, table, dialect });
1171
- const fks = normalizeFks({ model, table, schema, dialect });
1202
+ const pk = normalizePk({ model, lookup });
1203
+ const indexes = normalizeIndexes({ model, table, dialect, lookup });
1204
+ const fks = normalizeFks({ model, table, lookup, dialect });
1172
1205
  const checks = new Map;
1173
1206
  for (const check of model.checks ?? [])
1174
1207
  checks.set(check.name, check.expression);
1175
1208
  return { model, table, ...model.namespace === undefined ? {} : { namespace: model.namespace }, columns, pk, indexes, fks, checks };
1176
1209
  }
1177
1210
  function normalizePk(params) {
1178
- const { model } = params;
1211
+ const { model, lookup } = params;
1179
1212
  if (model.primaryKey !== undefined && model.primaryKey.fields.length > 0) {
1180
- return model.primaryKey.fields.map((name) => fieldDbName({ model, name }));
1213
+ return model.primaryKey.fields.map((name) => lookupFieldName({ lookup, model, name }));
1181
1214
  }
1182
1215
  return model.fields.filter((field) => field.isId).map((field) => field.dbName ?? field.name);
1183
1216
  }
1184
1217
  function normalizeIndexes(params) {
1185
- const { model, table, dialect } = params;
1218
+ const { model, table, dialect, lookup } = params;
1186
1219
  const result = new Map;
1187
1220
  const add = (index) => {
1188
1221
  const unique = index.kind === "unique";
1189
1222
  const kind = unique ? "btree" : index.kind;
1190
1223
  const columns = index.fields.map((field) => {
1191
- const fieldIR = model.fields.find((candidate) => candidate.name === field.name);
1224
+ const fieldIR = lookup.fields.get(model)?.get(field.name);
1192
1225
  const decimal = fieldIR !== undefined && fieldIR.type === "Decimal" && fieldIR.isList !== true;
1193
1226
  return {
1194
- name: fieldDbName({ model, name: field.name }),
1227
+ name: fieldIR?.dbName ?? fieldIR?.name ?? field.name,
1195
1228
  desc: field.sort === "desc",
1196
1229
  ...decimal ? { decimal: true } : {},
1197
1230
  ...field.operatorClass === undefined ? {} : { operatorClass: field.operatorClass }
@@ -1225,8 +1258,7 @@ function normalizeIndexes(params) {
1225
1258
  }
1226
1259
  function indexComparisonKey(params) {
1227
1260
  const { unique, kind, columns, where, nullsNotDistinct } = params;
1228
- const cols = columns.map((column) => `${column.name}${column.desc ? " desc" : ""}${column.operatorClass === undefined ? "" : ` ops:${JSON.stringify([column.operatorClass.namespace, column.operatorClass.name])}`}`).join(",");
1229
- return `${unique ? "u" : "i"}${nullsNotDistinct === true ? "n" : ""}:${kind}:${cols}:${normalizeWhere({ where })}`;
1261
+ return JSON.stringify([unique, nullsNotDistinct === true, kind, columns.map((column) => [column.name, column.desc === true, column.operatorClass === undefined ? null : [column.operatorClass.namespace, column.operatorClass.name]]), normalizeWhere({ where })]);
1230
1262
  }
1231
1263
  function normalizeWhere(params) {
1232
1264
  const { where } = params;
@@ -1388,22 +1420,22 @@ function parsePrimaryExpression(params) {
1388
1420
  return { text: parts.join(" "), at, kind: "atom" };
1389
1421
  }
1390
1422
  function normalizeFks(params) {
1391
- const { model, table, schema } = params;
1423
+ const { model, table, lookup } = params;
1392
1424
  const result = new Map;
1393
1425
  for (const relation of model.relations) {
1394
1426
  if (relation.fields === undefined || relation.fields.length === 0)
1395
1427
  continue;
1396
- const target = schema.models.find((candidate) => candidate.name === relation.targetModel);
1428
+ const target = lookup.models.get(relation.targetModel);
1397
1429
  if (target !== undefined && target.isView)
1398
1430
  continue;
1399
1431
  const refTable = target === undefined ? relation.targetModel : target.dbName ?? target.name;
1400
- const columns = relation.fields.map((name) => fieldDbName({ model, name }));
1401
- const refColumns = (relation.references ?? []).map((name) => target === undefined ? name : fieldDbName({ model: target, name }));
1432
+ const columns = relation.fields.map((name) => lookupFieldName({ lookup, model, name }));
1433
+ const refColumns = (relation.references ?? []).map((name) => target === undefined ? name : lookupFieldName({ lookup, model: target, name }));
1402
1434
  const required = relation.fields.every((name) => {
1403
- const field = model.fields.find((candidate) => candidate.name === name);
1435
+ const field = lookup.fields.get(model)?.get(name);
1404
1436
  return field === undefined ? true : !field.isOptional;
1405
1437
  });
1406
- const key = `${columns.join(",")}\u2192${physicalNameKey({ name: refTable, namespace: target?.namespace, dialect: params.dialect })}.${refColumns.join(",")}`;
1438
+ const key = JSON.stringify([columns, physicalNameKey({ name: refTable, namespace: target?.namespace, dialect: params.dialect }), refColumns]);
1407
1439
  if (result.has(key))
1408
1440
  continue;
1409
1441
  result.set(key, {
@@ -1420,17 +1452,17 @@ function normalizeFks(params) {
1420
1452
  return result;
1421
1453
  }
1422
1454
  function normalizeJoinTable(params) {
1423
- const { pair, schema, dialect } = params;
1455
+ const { pair, lookup, dialect } = params;
1424
1456
  return {
1425
1457
  table: pair.tableName,
1426
1458
  ...pair.namespace === undefined ? {} : { namespace: pair.namespace },
1427
- a: joinTableEnd({ modelName: pair.modelA, schema, dialect, joinTable: pair.tableName }),
1428
- b: joinTableEnd({ modelName: pair.modelB, schema, dialect, joinTable: pair.tableName })
1459
+ a: joinTableEnd({ modelName: pair.modelA, lookup, dialect, joinTable: pair.tableName }),
1460
+ b: joinTableEnd({ modelName: pair.modelB, lookup, dialect, joinTable: pair.tableName })
1429
1461
  };
1430
1462
  }
1431
1463
  function joinTableEnd(params) {
1432
- const { modelName, schema, joinTable } = params;
1433
- const model = schema.models.find((candidate) => candidate.name === modelName);
1464
+ const { modelName, lookup, joinTable } = params;
1465
+ const model = lookup.models.get(modelName);
1434
1466
  if (model === undefined) {
1435
1467
  throw new VibeError7({
1436
1468
  code: "VIBE_MIGRATION",
@@ -1438,7 +1470,7 @@ function joinTableEnd(params) {
1438
1470
  meta: { joinTable, model: modelName }
1439
1471
  });
1440
1472
  }
1441
- const pkFields = model.primaryKey !== undefined && model.primaryKey.fields.length > 0 ? model.primaryKey.fields.map((name) => model.fields.find((field) => field.name === name)) : model.fields.filter((field) => field.isId);
1473
+ const pkFields = model.primaryKey !== undefined && model.primaryKey.fields.length > 0 ? model.primaryKey.fields.map((name) => lookup.fields.get(model)?.get(name)) : model.fields.filter((field) => field.isId);
1442
1474
  const pkField = pkFields[0];
1443
1475
  if (pkFields.length !== 1 || pkField === undefined) {
1444
1476
  throw new VibeError7({
@@ -1451,7 +1483,7 @@ function joinTableEnd(params) {
1451
1483
  table: model.dbName ?? model.name,
1452
1484
  ...model.namespace === undefined ? {} : { namespace: model.namespace },
1453
1485
  column: pkField.dbName ?? pkField.name,
1454
- field: physicalEnumField({ field: pkField, schema, dialect: params.dialect, table: model.dbName ?? model.name })
1486
+ field: physicalEnumField({ field: pkField, lookup, dialect: params.dialect, table: model.dbName ?? model.name })
1455
1487
  };
1456
1488
  }
1457
1489
  function physicalNameKey(params) {
@@ -1467,10 +1499,19 @@ function schemaNamespaces(params) {
1467
1499
  ...(params.schema.migrationObjects ?? []).flatMap((object) => object.identity.kind === "namespace" ? [object.identity.name] : [])
1468
1500
  ])];
1469
1501
  }
1470
- function fieldDbName(params) {
1471
- const { model, name } = params;
1472
- const field = model.fields.find((candidate) => candidate.name === name);
1473
- return field === undefined ? name : field.dbName ?? field.name;
1502
+
1503
+ // packages/migrate/src/introspect/group.ts
1504
+ function groupCatalogRows(params) {
1505
+ const groups = new Map;
1506
+ for (const row of params.rows) {
1507
+ const key = params.key(row);
1508
+ const group = groups.get(key);
1509
+ if (group === undefined)
1510
+ groups.set(key, [row]);
1511
+ else
1512
+ group.push(row);
1513
+ }
1514
+ return groups;
1474
1515
  }
1475
1516
 
1476
1517
  // packages/migrate/src/postgres-type-identity.ts
@@ -1874,10 +1915,15 @@ async function introspectPhysicalPostgres(params) {
1874
1915
  read("checks"),
1875
1916
  Promise.all(namespaces.map(async (namespace) => (await executor({ text: renderExclusionCatalog2({ dialect: postgresDialect6, namespace }) })).map((row) => ({ ...row, table_name: physicalNameKey({ namespace, name: String(row.table_name) }) })))).then((rows) => rows.flat())
1876
1917
  ]);
1877
- const keysQuery = renderPostgresIndexKeys({ dialect: postgresDialect6 });
1918
+ const keysQuery = renderPostgresIndexKeys({ dialect: postgresDialect6, namespaces });
1878
1919
  const indexKeys = await executor({ text: keysQuery.text, values: [...keysQuery.values] });
1879
1920
  if (indexKeys.some((row) => typeof row.table_namespace !== "string" || typeof row.table_name !== "string" || typeof row.index_name !== "string" || typeof row.method !== "string" || typeof row.unique !== "boolean" || typeof row.descending !== "boolean" || typeof row.exact_default !== "boolean" && row.column_name !== null || typeof row.operator_namespace !== "string" || typeof row.operator_name !== "string" || !Number.isSafeInteger(Number(row.ordinal)) || Number(row.ordinal) < 1))
1880
1921
  throw new VibeError11({ code: "VIBE_MIGRATION", message: "PostgreSQL index-key identity is incomplete", meta: { reason: "index-inspection-unavailable" } });
1922
+ const keysByIndex = groupCatalogRows({ rows: indexKeys, key: (row) => JSON.stringify([physicalNameKey({ namespace: String(row.table_namespace), name: String(row.table_name) }), row.index_name]) });
1923
+ const catalogColumns = new Map(columnRows.map((row) => [JSON.stringify([row.table_name, row.column_name]), row]));
1924
+ const foreignKeysByTable = groupCatalogRows({ rows: fkRows, key: (row) => String(row.table_name) });
1925
+ const catalogIndexesByTable = groupCatalogRows({ rows: indexRows, key: (row) => String(row.table_name) });
1926
+ const exclusionsByTable = groupCatalogRows({ rows: exclusionRows, key: (row) => String(row.table_name) });
1881
1927
  const tables = tableRows.map((row) => row.table_name);
1882
1928
  const views = viewRows.map((row) => row.table_name);
1883
1929
  const columns = columnRows.map((row) => ({
@@ -1895,6 +1941,8 @@ async function introspectPhysicalPostgres(params) {
1895
1941
  numericScale: toNumberOrNull(row.numeric_scale),
1896
1942
  fullType: row.full_type ?? null
1897
1943
  }));
1944
+ const columnsByTable = groupCatalogRows({ rows: columns, key: (column) => column.table });
1945
+ const columnsByIdentity = new Map(columns.map((column) => [JSON.stringify([column.table, column.name]), column]));
1898
1946
  const enums = [];
1899
1947
  {
1900
1948
  const valuesByName = new Map;
@@ -1935,6 +1983,7 @@ async function introspectPhysicalPostgres(params) {
1935
1983
  entry.columns.push(columnName);
1936
1984
  }
1937
1985
  }
1986
+ const uniquesByTable = groupCatalogRows({ rows: uniquesByConstraint.values(), key: (unique) => unique.table });
1938
1987
  const checksByTable = new Map;
1939
1988
  for (const row of checkRows) {
1940
1989
  const table = row.table_name;
@@ -1969,7 +2018,7 @@ async function introspectPhysicalPostgres(params) {
1969
2018
  const fkGroups = [...fkGroupsByName.values()].map((group) => ({
1970
2019
  ...group,
1971
2020
  required: group.columns.every((columnName) => {
1972
- const column = columns.find((c) => c.table === group.table && c.name === columnName);
2021
+ const column = columnsByIdentity.get(JSON.stringify([group.table, columnName]));
1973
2022
  return column === undefined ? true : !column.nullable;
1974
2023
  })
1975
2024
  }));
@@ -1977,7 +2026,7 @@ async function introspectPhysicalPostgres(params) {
1977
2026
  for (const row of indexRows) {
1978
2027
  if (row.backing_constraint_type === "x")
1979
2028
  continue;
1980
- const operands = indexKeys.filter((key) => physicalNameKey({ namespace: String(key.table_namespace), name: String(key.table_name) }) === row.table_name && key.index_name === row.index_name);
2029
+ const operands = keysByIndex.get(JSON.stringify([row.table_name, row.index_name])) ?? [];
1981
2030
  if (row.plain_columns === true && operands.length !== Number(row.key_count))
1982
2031
  throw new VibeError11({ code: "VIBE_MIGRATION", message: "Plain index keys could not be read completely; refusing name-only operator-class introspection", meta: { reason: "index-inspection-unavailable", table: row.table_name, index: row.index_name } });
1983
2032
  if (operands.length > 0) {
@@ -2002,22 +2051,24 @@ async function introspectPhysicalPostgres(params) {
2002
2051
  indexes.push(parsed);
2003
2052
  }
2004
2053
  }
2054
+ const indexesByTable = groupCatalogRows({ rows: indexes, key: (index) => index.table });
2055
+ const fkGroupsByTable = groupCatalogRows({ rows: fkGroups, key: (group) => group.table });
2005
2056
  const joinTables = new Map;
2006
2057
  for (const table of tables) {
2007
2058
  const physicalTable = physicalNames.get(table);
2008
2059
  if (!physicalTable.name.startsWith("_"))
2009
2060
  continue;
2010
- const tableFks = fkRows.filter((row) => row.table_name === table);
2011
- const tableIndexes = indexRows.filter((row) => row.table_name === table);
2012
- if (pkByTable.has(table) || (checksByTable.get(table)?.length ?? 0) > 0 || exclusionRows.some((row) => row.table_name === table))
2061
+ const tableFks = foreignKeysByTable.get(table) ?? [];
2062
+ const tableIndexes = catalogIndexesByTable.get(table) ?? [];
2063
+ if (pkByTable.has(table) || (checksByTable.get(table)?.length ?? 0) > 0 || exclusionsByTable.has(table))
2013
2064
  continue;
2014
2065
  if (!tableFks.every((row) => row.validated === true && row.deferrable === false && row.triggers_enabled === true))
2015
2066
  continue;
2016
2067
  if (tableIndexes.length !== 2 || !tableIndexes.every((row) => row.valid === true && row.ready === true && row.immediate === true && row.plain_columns === true && typeof row.key_count === "number" && row.key_count === row.attribute_count))
2017
2068
  continue;
2018
2069
  if (tableFks.some((row) => {
2019
- const source = columnRows.find((column) => column.table_name === table && column.column_name === row.column_name);
2020
- const target = columnRows.find((column) => column.table_name === row.foreign_table_name && column.column_name === row.foreign_column_name);
2070
+ const source = catalogColumns.get(JSON.stringify([table, row.column_name]));
2071
+ const target = catalogColumns.get(JSON.stringify([row.foreign_table_name, row.foreign_column_name]));
2021
2072
  return source === undefined || target === undefined || typeof source.full_type !== "string" || source.full_type.length === 0 || source.full_type !== target.full_type || source.column_default !== null || source.is_identity !== "NO" || source.is_generated !== "NEVER" || source.is_nullable !== "NO" || source.collation_name === undefined || source.collation_name !== target.collation_name;
2022
2073
  }))
2023
2074
  continue;
@@ -2025,9 +2076,9 @@ async function introspectPhysicalPostgres(params) {
2025
2076
  continue;
2026
2077
  const detected = detectJoinTable({
2027
2078
  table: physicalTable.name,
2028
- columnNames: columns.filter((column) => column.table === table).map((column) => column.name),
2029
- fkGroups: fkGroups.filter((group) => group.table === table).map((group) => ({ ...group, table: physicalTable.name, refTable: physicalNames.get(group.refTable)?.name ?? group.refTable })),
2030
- indexes: indexes.filter((index) => index.table === table).map((index) => ({ ...index, table: physicalTable.name }))
2079
+ columnNames: (columnsByTable.get(table) ?? []).map((column) => column.name),
2080
+ fkGroups: (fkGroupsByTable.get(table) ?? []).map((group) => ({ ...group, table: physicalTable.name, refTable: physicalNames.get(group.refTable)?.name ?? group.refTable })),
2081
+ indexes: (indexesByTable.get(table) ?? []).map((index) => ({ ...index, table: physicalTable.name }))
2031
2082
  });
2032
2083
  if (detected !== null)
2033
2084
  joinTables.set(table, { ...detected, ...physicalTable.namespace === "public" && namespaces.length === 1 ? {} : { relationName: detected.relationName ?? physicalTable.name.slice(1) }, tableA: physicalNameKey({ namespace: physicalTable.namespace, name: detected.tableA }), tableB: physicalNameKey({ namespace: physicalTable.namespace, name: detected.tableB }) });
@@ -2040,14 +2091,14 @@ async function introspectPhysicalPostgres(params) {
2040
2091
  table,
2041
2092
  physicalTable: physicalNames.get(table).name,
2042
2093
  isView: false,
2043
- columns,
2094
+ columns: columnsByTable.get(table) ?? [],
2044
2095
  pkByTable,
2045
2096
  pkNames,
2046
- uniquesByConstraint,
2097
+ uniques: uniquesByTable.get(table) ?? [],
2047
2098
  constraintNames,
2048
- indexes,
2099
+ indexes: indexesByTable.get(table) ?? [],
2049
2100
  enumNames,
2050
- exclusions: exclusionRows.filter((row) => row.table_name === table).map((row) => ({ name: String(row.constraint_name), definition: String(row.definition), requiredExtensions: [], catalogDefinition: true })),
2101
+ exclusions: (exclusionsByTable.get(table) ?? []).map((row) => ({ name: String(row.constraint_name), definition: String(row.definition), requiredExtensions: [], catalogDefinition: true })),
2051
2102
  checks: checksByTable.get(table) ?? []
2052
2103
  }));
2053
2104
  }
@@ -2056,10 +2107,10 @@ async function introspectPhysicalPostgres(params) {
2056
2107
  table: view,
2057
2108
  physicalTable: physicalNames.get(view).name,
2058
2109
  isView: true,
2059
- columns,
2110
+ columns: columnsByTable.get(view) ?? [],
2060
2111
  pkByTable,
2061
2112
  pkNames,
2062
- uniquesByConstraint,
2113
+ uniques: uniquesByTable.get(view) ?? [],
2063
2114
  constraintNames,
2064
2115
  indexes: [],
2065
2116
  enumNames,
@@ -2079,14 +2130,16 @@ async function introspectPhysicalPostgres(params) {
2079
2130
  logicalNames.add(name);
2080
2131
  return name;
2081
2132
  };
2133
+ const modelBasenames = groupCatalogRows({ rows: physicalNames.values(), key: (entry) => entry.name });
2134
+ const enumBasenames = groupCatalogRows({ rows: enumIdentities.values(), key: (entry) => entry.name });
2082
2135
  const modelNames = new Map(models.map((model) => {
2083
2136
  const identity = physicalNames.get(model.name);
2084
- const duplicates = [...physicalNames.values()].filter((entry) => entry.name === identity.name).length > 1;
2137
+ const duplicates = (modelBasenames.get(identity.name)?.length ?? 0) > 1;
2085
2138
  return [model.name, logicalName({ key: model.name, name: identity.name, duplicates })];
2086
2139
  }));
2087
2140
  const enumLogicalNames = new Map(enums.map((entry) => {
2088
2141
  const identity = enumIdentities.get(entry.name);
2089
- const duplicates = [...enumIdentities.values()].filter((candidate) => candidate.name === identity.name).length > 1;
2142
+ const duplicates = (enumBasenames.get(identity.name)?.length ?? 0) > 1;
2090
2143
  return [entry.name, logicalName({ key: entry.name, name: identity.name, duplicates })];
2091
2144
  }));
2092
2145
  const qualifiedModels = models.map((model) => {
@@ -2124,20 +2177,18 @@ async function introspectPhysicalPostgres(params) {
2124
2177
  return resolved.schema;
2125
2178
  }
2126
2179
  function buildModel(params) {
2127
- const { table, isView, columns, pkByTable, uniquesByConstraint, constraintNames, indexes, enumNames } = params;
2128
- const tableColumns = columns.filter((column) => column.table === table);
2180
+ const { table, isView, columns, pkByTable, uniques, constraintNames, indexes, enumNames } = params;
2181
+ const tableColumns = columns;
2129
2182
  const pk = pkByTable.get(table) ?? [];
2130
2183
  const uniqueSingles = new Set;
2131
2184
  const multiUniques = [];
2132
- for (const unique of uniquesByConstraint.values()) {
2185
+ for (const unique of uniques) {
2133
2186
  const name = unique.name;
2134
- if (unique.table !== table)
2135
- continue;
2136
2187
  if (unique.columns.length === 1)
2137
2188
  uniqueSingles.add(unique.columns[0]);
2138
2189
  multiUniques.push({ name, columns: unique.columns });
2139
2190
  }
2140
- const tableIndexes = indexes.filter((index) => index.table === table && !constraintNames.has(JSON.stringify([table, index.name])));
2191
+ const tableIndexes = indexes.filter((index) => !constraintNames.has(JSON.stringify([table, index.name])));
2141
2192
  for (const index of tableIndexes) {
2142
2193
  if (!index.unique)
2143
2194
  continue;
@@ -2422,7 +2473,7 @@ function toNumberOrNull(value) {
2422
2473
  }
2423
2474
 
2424
2475
  // packages/migrate/src/introspect/mysql.ts
2425
- import { exactIntegerLiteral as exactIntegerLiteral2 } from "@vibeorm/schema";
2476
+ import { VibeError as VibeError12, exactIntegerLiteral as exactIntegerLiteral2 } from "@vibeorm/schema";
2426
2477
  import { mysqlDialect as mysqlDialect2 } from "@vibeorm/sql";
2427
2478
 
2428
2479
  // packages/migrate/src/journal-table.ts
@@ -2551,6 +2602,12 @@ async function introspectMysql(params) {
2551
2602
  collation: catalogField({ row, name: "collation_name" }) ?? null,
2552
2603
  extra: (catalogField({ row, name: "extra" }) ?? "").toLowerCase()
2553
2604
  }));
2605
+ const columnsByTable = groupCatalogRows({ rows: columns, key: (column) => column.table });
2606
+ const columnsByIdentity = new Map(columns.map((column) => [JSON.stringify([column.table, column.name]), column]));
2607
+ const catalogColumns = new Map(columnRows.map((row) => [JSON.stringify([catalogField({ row, name: "table_name" }), catalogField({ row, name: "column_name" })]), row]));
2608
+ const statisticsByTable = groupCatalogRows({ rows: statisticRows, key: (row) => String(catalogField({ row, name: "table_name" })) });
2609
+ const foreignKeysByTable = groupCatalogRows({ rows: fkRows, key: (row) => String(catalogField({ row, name: "table_name" })) });
2610
+ const enginesByTable = new Map(tableRows.map((row) => [catalogField({ row, name: "table_name" }), catalogField({ row, name: "engine" })]));
2554
2611
  const rulesByConstraint = new Map;
2555
2612
  for (const row of refRows) {
2556
2613
  rulesByConstraint.set(catalogField({ row, name: "constraint_name" }), {
@@ -2562,6 +2619,15 @@ async function introspectMysql(params) {
2562
2619
  for (const row of fkRows) {
2563
2620
  const constraintName = catalogField({ row, name: "constraint_name" });
2564
2621
  const table = catalogField({ row, name: "table_name" });
2622
+ const tableSchema = catalogField({ row, name: "table_schema" });
2623
+ const referencedSchema = catalogField({ row, name: "referenced_table_schema" });
2624
+ if (typeof tableSchema === "string" && typeof referencedSchema === "string" && tableSchema !== referencedSchema) {
2625
+ throw new VibeError12({
2626
+ code: "VIBE_UNSUPPORTED_CAPABILITY",
2627
+ message: `MySQL cross-database foreign key "${constraintName}" from "${tableSchema}.${table}" to "${referencedSchema}" cannot be introspected without losing its database identity`,
2628
+ meta: { dialect: "mysql", feature: "cross-database foreign keys", constraint: constraintName, table, tableSchema, referencedSchema }
2629
+ });
2630
+ }
2565
2631
  const key = `${table}\x00${constraintName}`;
2566
2632
  const existing = groupsByKey.get(key);
2567
2633
  if (existing === undefined) {
@@ -2584,17 +2650,11 @@ async function introspectMysql(params) {
2584
2650
  onDelete: rules?.onDelete ?? "NoAction",
2585
2651
  onUpdate: rules?.onUpdate ?? "NoAction",
2586
2652
  required: group.columns.every((columnName) => {
2587
- const column = columns.find((candidate) => candidate.table === group.table && candidate.name === columnName);
2653
+ const column = columnsByIdentity.get(JSON.stringify([group.table, columnName]));
2588
2654
  return column === undefined ? true : !column.nullable;
2589
2655
  })
2590
2656
  };
2591
2657
  });
2592
- const fkNamesByTable = new Map;
2593
- for (const group of fkGroups) {
2594
- const set = fkNamesByTable.get(group.table) ?? new Set;
2595
- set.add(group.constraintName);
2596
- fkNamesByTable.set(group.table, set);
2597
- }
2598
2658
  const pkByTable = new Map;
2599
2659
  const indexes = [];
2600
2660
  {
@@ -2603,6 +2663,12 @@ async function introspectMysql(params) {
2603
2663
  const table = catalogField({ row, name: "table_name" });
2604
2664
  const name = catalogField({ row, name: "index_name" });
2605
2665
  const columnName = catalogField({ row, name: "column_name" });
2666
+ const type = catalogField({ row, name: "index_type" });
2667
+ const unique = Number(catalogField({ row, name: "non_unique" })) === 0;
2668
+ const kind = typeof type === "string" ? type.toLowerCase() : "btree";
2669
+ if (catalogField({ row, name: "sub_part" }) != null || catalogField({ row, name: "expression" }) != null || catalogField({ row, name: "is_visible" }) === "NO" || typeof columnName !== "string" || kind !== "btree" && (kind !== "fulltext" || unique)) {
2670
+ throw new VibeError12({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `MySQL index "${table}.${name}" cannot be introspected without losing its physical shape`, meta: { dialect: "mysql", feature: "index introspection", table, index: name } });
2671
+ }
2606
2672
  if (name === "PRIMARY") {
2607
2673
  const list = pkByTable.get(table);
2608
2674
  if (list === undefined)
@@ -2611,8 +2677,6 @@ async function introspectMysql(params) {
2611
2677
  list.push(columnName);
2612
2678
  continue;
2613
2679
  }
2614
- if (fkNamesByTable.get(table)?.has(name) === true)
2615
- continue;
2616
2680
  const key = `${table}\x00${name}`;
2617
2681
  const entry = byKey.get(key);
2618
2682
  const column = {
@@ -2623,7 +2687,8 @@ async function introspectMysql(params) {
2623
2687
  byKey.set(key, {
2624
2688
  table,
2625
2689
  name,
2626
- unique: Number(catalogField({ row, name: "non_unique" })) === 0,
2690
+ unique,
2691
+ kind,
2627
2692
  columns: [column]
2628
2693
  });
2629
2694
  } else {
@@ -2631,34 +2696,39 @@ async function introspectMysql(params) {
2631
2696
  }
2632
2697
  }
2633
2698
  for (const entry of byKey.values()) {
2634
- indexes.push({ ...entry, kind: "btree" });
2699
+ const support = groupsByKey.get(`${entry.table}\x00${entry.name}`);
2700
+ if (!entry.unique && entry.kind === "btree" && support !== undefined && entry.columns.length === support.columns.length && entry.columns.every((column, index) => column.name === support.columns[index] && !column.desc))
2701
+ continue;
2702
+ indexes.push(entry);
2635
2703
  }
2636
2704
  }
2705
+ const indexesByTable = groupCatalogRows({ rows: indexes, key: (index) => index.table });
2706
+ const fkGroupsByTable = groupCatalogRows({ rows: fkGroups, key: (group) => group.table });
2637
2707
  const enums = [];
2638
2708
  const joinTables = new Map;
2639
2709
  let foreignKeysEnabled;
2640
2710
  for (const table of tables) {
2641
2711
  if (!table.startsWith("_") || pkByTable.has(table) || (checksByTable.get(table)?.length ?? 0) > 0)
2642
2712
  continue;
2643
- const tableIndexes = statisticRows.filter((row) => catalogField({ row, name: "table_name" }) === table);
2713
+ const tableIndexes = statisticsByTable.get(table) ?? [];
2644
2714
  if (new Set(tableIndexes.map((row) => catalogField({ row, name: "index_name" }))).size !== 2 || !tableIndexes.every((row) => catalogField({ row, name: "index_type" }) === "BTREE" && catalogField({ row, name: "is_visible" }) === "YES" && catalogField({ row, name: "sub_part" }) === null && catalogField({ row, name: "expression" }) === null && catalogField({ row, name: "collation" }) === "A" && Number.isInteger(Number(catalogField({ row, name: "seq_in_index" }))) && Number(catalogField({ row, name: "seq_in_index" })) > 0))
2645
2715
  continue;
2646
- const tableFks = fkRows.filter((row) => catalogField({ row, name: "table_name" }) === table);
2716
+ const tableFks = foreignKeysByTable.get(table) ?? [];
2647
2717
  if (tableFks.some((row) => {
2648
2718
  const targetTable = catalogField({ row, name: "referenced_table_name" });
2649
- const source = columnRows.find((column) => catalogField({ row: column, name: "table_name" }) === table && catalogField({ row: column, name: "column_name" }) === catalogField({ row, name: "column_name" }));
2650
- const target = columnRows.find((column) => catalogField({ row: column, name: "table_name" }) === targetTable && catalogField({ row: column, name: "column_name" }) === catalogField({ row, name: "referenced_column_name" }));
2719
+ const source = catalogColumns.get(JSON.stringify([table, catalogField({ row, name: "column_name" })]));
2720
+ const target = catalogColumns.get(JSON.stringify([targetTable, catalogField({ row, name: "referenced_column_name" })]));
2651
2721
  if (source === undefined || target === undefined)
2652
2722
  return true;
2653
2723
  const type = catalogField({ row: source, name: "column_type" });
2654
- return typeof type !== "string" || type.length === 0 || type !== catalogField({ row: target, name: "column_type" }) || catalogField({ row: source, name: "column_default" }) !== null || catalogField({ row: source, name: "extra" }) !== "" || catalogField({ row: source, name: "generation_expression" }) !== "" || catalogField({ row: source, name: "is_nullable" }) !== "NO" || catalogField({ row: source, name: "collation_name" }) === undefined || catalogField({ row: source, name: "collation_name" }) !== catalogField({ row: target, name: "collation_name" }) || catalogField({ row, name: "table_schema" }) === undefined || catalogField({ row, name: "table_schema" }) !== catalogField({ row, name: "referenced_table_schema" }) || ![table, targetTable].every((name) => tableRows.some((entry) => catalogField({ row: entry, name: "table_name" }) === name && catalogField({ row: entry, name: "engine" }) === "InnoDB"));
2724
+ return typeof type !== "string" || type.length === 0 || type !== catalogField({ row: target, name: "column_type" }) || catalogField({ row: source, name: "column_default" }) !== null || catalogField({ row: source, name: "extra" }) !== "" || catalogField({ row: source, name: "generation_expression" }) !== "" || catalogField({ row: source, name: "is_nullable" }) !== "NO" || catalogField({ row: source, name: "collation_name" }) === undefined || catalogField({ row: source, name: "collation_name" }) !== catalogField({ row: target, name: "collation_name" }) || catalogField({ row, name: "table_schema" }) === undefined || catalogField({ row, name: "table_schema" }) !== catalogField({ row, name: "referenced_table_schema" }) || ![table, targetTable].every((name) => enginesByTable.get(name) === "InnoDB");
2655
2725
  }))
2656
2726
  continue;
2657
2727
  const detected = detectJoinTable({
2658
2728
  table,
2659
- columnNames: columns.filter((column) => column.table === table).map((column) => column.name),
2660
- fkGroups,
2661
- indexes
2729
+ columnNames: (columnsByTable.get(table) ?? []).map((column) => column.name),
2730
+ fkGroups: fkGroupsByTable.get(table) ?? [],
2731
+ indexes: indexesByTable.get(table) ?? []
2662
2732
  });
2663
2733
  if (detected === null)
2664
2734
  continue;
@@ -2670,7 +2740,7 @@ async function introspectMysql(params) {
2670
2740
  continue;
2671
2741
  const q = (name) => mysqlDialect2.quoteIdent({ name });
2672
2742
  let valid = true;
2673
- for (const fk of fkGroups.filter((group) => group.table === table)) {
2743
+ for (const fk of fkGroupsByTable.get(table) ?? []) {
2674
2744
  const rows = await executor({ text: `SELECT 1 AS invalid FROM ${q(table)} child LEFT JOIN ${q(fk.refTable)} parent ON child.${q(fk.columns[0])} = parent.${q(fk.refColumns[0])} WHERE parent.${q(fk.refColumns[0])} IS NULL LIMIT 1`, values: [] });
2675
2745
  if (rows.length > 0) {
2676
2746
  valid = false;
@@ -2687,9 +2757,9 @@ async function introspectMysql(params) {
2687
2757
  models.push(buildModel2({
2688
2758
  table,
2689
2759
  isView: false,
2690
- columns,
2760
+ columns: columnsByTable.get(table) ?? [],
2691
2761
  pkByTable,
2692
- indexes,
2762
+ indexes: indexesByTable.get(table) ?? [],
2693
2763
  enums,
2694
2764
  checks: checksByTable.get(table) ?? []
2695
2765
  }));
@@ -2698,7 +2768,7 @@ async function introspectMysql(params) {
2698
2768
  models.push(buildModel2({
2699
2769
  table: view,
2700
2770
  isView: true,
2701
- columns,
2771
+ columns: columnsByTable.get(view) ?? [],
2702
2772
  pkByTable: new Map,
2703
2773
  indexes: [],
2704
2774
  enums,
@@ -2716,9 +2786,9 @@ async function introspectMysql(params) {
2716
2786
  }
2717
2787
  function buildModel2(params) {
2718
2788
  const { table, isView, columns, pkByTable, indexes, enums } = params;
2719
- const tableColumns = columns.filter((column) => column.table === table);
2789
+ const tableColumns = columns;
2720
2790
  const pk = pkByTable.get(table) ?? [];
2721
- const tableIndexes = indexes.filter((index) => index.table === table);
2791
+ const tableIndexes = indexes;
2722
2792
  const uniqueSingles = new Set;
2723
2793
  for (const index of tableIndexes) {
2724
2794
  if (index.unique && index.columns.length === 1) {
@@ -2749,7 +2819,7 @@ function buildModel2(params) {
2749
2819
  continue;
2750
2820
  modelIndexes.push({
2751
2821
  name: index.name,
2752
- kind: index.unique ? "unique" : "btree",
2822
+ kind: index.unique ? "unique" : index.kind,
2753
2823
  fields: index.columns.map((column) => ({
2754
2824
  name: column.name,
2755
2825
  ...column.desc ? { sort: "desc" } : {}
@@ -3281,7 +3351,7 @@ function q(name) {
3281
3351
  }
3282
3352
 
3283
3353
  // packages/migrate/src/execute.ts
3284
- import { VibeError as VibeError12 } from "@vibeorm/schema";
3354
+ import { VibeError as VibeError13 } from "@vibeorm/schema";
3285
3355
  var SELF_MANAGED_MARKER = "PRAGMA foreign_keys=OFF";
3286
3356
  var FK_CHECK_PREFIX = "PRAGMA foreign_key_check";
3287
3357
  function usesSelfManagedTransactions(params) {
@@ -3322,7 +3392,7 @@ function assertSelfManagedResumeSafe(params) {
3322
3392
  const completedIndex = params.direction === "up" ? params.start + 1 : -(params.start + 2);
3323
3393
  const rewind = selfManagedResumeStart({ sql: params.sql, start: params.start });
3324
3394
  const retryIndex = params.direction === "up" ? rewind : -(rewind + 1);
3325
- throw new VibeError12({
3395
+ throw new VibeError13({
3326
3396
  code: "VIBE_MIGRATION",
3327
3397
  message: `migration "${params.migration}" has a legacy sqlite checkpoint at ${params.direction} COMMIT (statement ${params.start + 1}). The block may already have committed; replay is refused. Inspect the block's effects. If it committed, set its journal statement_index to ${completedIndex} and error to NULL to resume the remaining statements. Only if the entire block did not commit, set statement_index to ${retryIndex} and error to NULL to retry the block. Do not mark the whole migration applied unless all remaining statements also ran.`,
3328
3398
  meta: { reason: "uncertain-statement", migration: params.migration, statementIndex: params.start, direction: params.direction, dialect: "sqlite" }
@@ -3351,7 +3421,7 @@ function unsupportedScript(params) {
3351
3421
  const { migration, direction, index, problem, found } = params;
3352
3422
  const subject = direction === "down" ? `the down script of migration "${migration}"` : `migration "${migration}"`;
3353
3423
  const position = `${direction === "down" ? "down statement" : "statement"} ${index + 1} of ${params.statements}`;
3354
- return new VibeError12({
3424
+ return new VibeError13({
3355
3425
  code: "VIBE_MIGRATION",
3356
3426
  message: `${subject} manages its own sqlite transactions, and ${position} breaks the shape recovery depends on: ${found}. ` + `A table-rebuild block must be written exactly as \`${SUPPORTED_BLOCK_SHAPE}\`. ` + "Inside that block a crash rolls everything back, so a resume can replay it safely. " + "A statement that sits in a foreign-keys-off region with no transaction around it commits on its own, " + "and the resume would run it a second time with no error raised either time. " + "Nothing has been executed. Move the statement after `PRAGMA foreign_keys=ON`, where it commits together " + "with its own journal checkpoint, or give it its own `BEGIN;` and `COMMIT;`. " + 'See "Self-managed sqlite scripts" in docs/migrations.md.',
3357
3427
  meta: { reason: "unsupported-self-managed-script", problem, migration, statementIndex: index, direction }
@@ -3407,7 +3477,7 @@ async function executeStatement(params) {
3407
3477
  const rows = await executor({ text: statement, values: [] });
3408
3478
  if (rows.length > 0) {
3409
3479
  const sample = rows.slice(0, 5).map((row) => `${String(row.table)} rowid ${String(row.rowid)} \u2192 ${String(row.parent)}`).join("; ");
3410
- throw new VibeError12({
3480
+ throw new VibeError13({
3411
3481
  code: "VIBE_MIGRATION",
3412
3482
  message: `foreign key check failed after table rebuild: ${rows.length} violation(s) \u2014 ${sample}`,
3413
3483
  meta: { violations: rows }
@@ -3462,7 +3532,7 @@ async function prepareOperatorClassDiff(params) {
3462
3532
  }
3463
3533
 
3464
3534
  // packages/migrate/src/postgres-default-equivalence.ts
3465
- import { VibeError as VibeError13 } from "@vibeorm/schema";
3535
+ import { VibeError as VibeError14 } from "@vibeorm/schema";
3466
3536
  import { postgresDialect as postgresDialect8, renderPostgresDefaultFunctionIdentity } from "@vibeorm/sql";
3467
3537
  var IDENTIFIER = '(?:"(?:[^"\\u0000]|"")+"|[A-Za-z_][A-Za-z_0-9$]*)';
3468
3538
  var CALL = new RegExp(`^\\s*(${IDENTIFIER})(?:\\s*\\.\\s*(${IDENTIFIER}))?\\s*\\(\\s*\\)\\s*$`);
@@ -3481,7 +3551,7 @@ async function equivalentPostgresDefaultFunction(params) {
3481
3551
  return false;
3482
3552
  const query = renderPostgresDefaultFunctionIdentity({ dialect: postgresDialect8, table: params.table, column: params.column });
3483
3553
  const rows = await params.executor({ text: query.text, values: [...query.values] }).catch((cause) => {
3484
- throw new VibeError13({ code: "VIBE_MIGRATION", message: "Cannot inspect the stored default function identity", meta: { reason: "default-inspection-unavailable", table: params.table, column: params.column }, cause });
3554
+ throw new VibeError14({ code: "VIBE_MIGRATION", message: "Cannot inspect the stored default function identity", meta: { reason: "default-inspection-unavailable", table: params.table, column: params.column }, cause });
3485
3555
  });
3486
3556
  if (rows.length !== 1)
3487
3557
  return false;
@@ -3495,7 +3565,7 @@ async function equivalentPostgresDefaultFunction(params) {
3495
3565
  }
3496
3566
 
3497
3567
  // packages/migrate/src/object-ownership.ts
3498
- import { VibeError as VibeError14, defaultPrimaryKeyName, enumStorageOf as enumStorageOf2, implicitJoinTableObjectNames, postgresGeneratedObjectName, qualifiedSqlName as qualifiedSqlName6, sqlObjectKey as sqlObjectKey3 } from "@vibeorm/schema";
3568
+ import { VibeError as VibeError15, defaultPrimaryKeyName, enumStorageOf as enumStorageOf2, implicitJoinTableObjectNames, postgresGeneratedObjectName, qualifiedSqlName as qualifiedSqlName6, sqlObjectKey as sqlObjectKey3 } from "@vibeorm/schema";
3499
3569
  function legacyMigrationObjects(params) {
3500
3570
  return params.artifacts.map((artifact) => {
3501
3571
  const owner = artifact.owns;
@@ -3597,7 +3667,10 @@ function removalIdentity(params) {
3597
3667
  }
3598
3668
  }
3599
3669
  function schemaObjectIdentities(params) {
3600
- const normalized = normalizeSchema({ schema: params.schema, dialect: params.dialect });
3670
+ return schemaObjectIdentitiesFromNormalized({ ...params, normalized: normalizeSchema({ schema: params.schema, dialect: params.dialect }) });
3671
+ }
3672
+ function schemaObjectIdentitiesFromNormalized(params) {
3673
+ const { normalized } = params;
3601
3674
  const result = [];
3602
3675
  if (params.dialect === "postgres") {
3603
3676
  for (const namespace of schemaNamespaces({ schema: params.schema }))
@@ -3647,7 +3720,7 @@ function assertRetainedMigrationObjects(params) {
3647
3720
  const keys = new Set(params.to.map((object) => sqlObjectKey3({ identity: object.identity })));
3648
3721
  const removed = params.from.filter((object) => !keys.has(sqlObjectKey3({ identity: object.identity })));
3649
3722
  if (removed.length > 0)
3650
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Removing migration ownership requires a reviewed hand-authored migration and new desired state", meta: { reason: "migration-object-removal-review-required", shadowReusable: true, objects: removed.map((object) => object.identity) } });
3723
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Removing migration ownership requires a reviewed hand-authored migration and new desired state", meta: { reason: "migration-object-removal-review-required", shadowReusable: true, objects: removed.map((object) => object.identity) } });
3651
3724
  }
3652
3725
  function orderedMigrationObjects(params) {
3653
3726
  const byKey = new Map(params.objects.map((object) => [sqlObjectKey3({ identity: object.identity }), object]));
@@ -3659,7 +3732,7 @@ function orderedMigrationObjects(params) {
3659
3732
  if (visited.has(key))
3660
3733
  return;
3661
3734
  if (visiting.has(key))
3662
- throw new VibeError14({ code: "VIBE_SCHEMA_INVALID", message: "Migration object dependency cycle", meta: { id: object.id } });
3735
+ throw new VibeError15({ code: "VIBE_SCHEMA_INVALID", message: "Migration object dependency cycle", meta: { id: object.id } });
3663
3736
  visiting.add(key);
3664
3737
  for (const dependency of object.dependsOn) {
3665
3738
  const target = byKey.get(sqlObjectKey3({ identity: dependency }));
@@ -3678,10 +3751,10 @@ function assertNamedRemovals(params) {
3678
3751
  const approved = new Set((params.approved ?? []).map((identity) => sqlObjectKey3({ identity })));
3679
3752
  const missing = params.removals.filter((identity) => !approved.has(sqlObjectKey3({ identity })));
3680
3753
  if (missing.length > 0)
3681
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Unmanaged removals require individually named approvals", meta: { reason: "unmanaged-removal-required", shadowReusable: true, objects: missing } });
3754
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Unmanaged removals require individually named approvals", meta: { reason: "unmanaged-removal-required", shadowReusable: true, objects: missing } });
3682
3755
  const integrity = params.removals.filter((identity) => identity.kind === "constraint" || identity.kind === "index");
3683
3756
  if (integrity.length > 0 && params.acceptIntegrityLoss !== true)
3684
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Named constraint or index removals also require acceptIntegrityLoss", meta: { reason: "integrity-acknowledgement-required", shadowReusable: true, objects: integrity } });
3757
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Named constraint or index removals also require acceptIntegrityLoss", meta: { reason: "integrity-acknowledgement-required", shadowReusable: true, objects: integrity } });
3685
3758
  }
3686
3759
  function assertRemovalAuthorization(params) {
3687
3760
  const declared = params.previous === undefined ? [] : schemaObjectIdentities({ schema: params.previous.schema, dialect: params.dialect, namespace: params.namespace });
@@ -3706,9 +3779,9 @@ function assertRemovalAuthorization(params) {
3706
3779
  }
3707
3780
  }
3708
3781
  if (unknown.length > 0)
3709
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Unknown object removals require individually named removeUnmanaged decisions", meta: { reason: "unmanaged-removal-required", shadowReusable: true, objects: unknown } });
3782
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Unknown object removals require individually named removeUnmanaged decisions", meta: { reason: "unmanaged-removal-required", shadowReusable: true, objects: unknown } });
3710
3783
  if (integrity.length > 0 && params.acceptIntegrityLoss !== true)
3711
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Constraint or index removal requires acceptIntegrityLoss; data-loss acknowledgement does not authorize it", meta: { reason: "integrity-acknowledgement-required", shadowReusable: true, objects: integrity } });
3784
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Constraint or index removal requires acceptIntegrityLoss; data-loss acknowledgement does not authorize it", meta: { reason: "integrity-acknowledgement-required", shadowReusable: true, objects: integrity } });
3712
3785
  }
3713
3786
 
3714
3787
  // packages/migrate/src/inspect-objects.ts
@@ -3793,7 +3866,7 @@ async function inspectSqlObject(params) {
3793
3866
  }
3794
3867
  }
3795
3868
  return { state: "present", definition: { identity: params.identity, facts, dependencies: [], format: "catalog-v2" } };
3796
- }).catch((error) => ({ state: "unavailable", reason: error instanceof VibeError15 ? String(error.meta.feature ?? error.code) : "catalog-query-failed" }));
3869
+ }).catch((error) => ({ state: "unavailable", reason: error instanceof VibeError16 ? String(error.meta.feature ?? error.code) : "catalog-query-failed" }));
3797
3870
  }
3798
3871
  function postgresInspection(params) {
3799
3872
  if (!params.addresses.every((address) => typeof address === "string"))
@@ -3808,7 +3881,7 @@ function postgresInspection(params) {
3808
3881
  return { state: "present", definition: { identity: params.identity, facts, dependencies, format: "catalog-v2" } };
3809
3882
  }
3810
3883
  function unavailableReason(params) {
3811
- return params.error instanceof VibeError15 ? String(params.error.meta.feature ?? params.error.meta.reason ?? params.error.code) : "catalog-query-failed";
3884
+ return params.error instanceof VibeError16 ? String(params.error.meta.feature ?? params.error.meta.reason ?? params.error.code) : "catalog-query-failed";
3812
3885
  }
3813
3886
  async function readBatch(params) {
3814
3887
  const positions = new Set(params.batch.positions);
@@ -3816,7 +3889,7 @@ async function readBatch(params) {
3816
3889
  const rows = new Map;
3817
3890
  for (const row of await params.executor({ text: params.batch.text, values: [...params.batch.values] })) {
3818
3891
  if (typeof row.position !== "number" || !Number.isInteger(row.position) || !positions.has(row.position))
3819
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Invalid batched catalog identity", meta: { reason: "catalog-query-failed" } });
3892
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Invalid batched catalog identity", meta: { reason: "catalog-query-failed" } });
3820
3893
  const matches = rows.get(row.position) ?? [];
3821
3894
  matches.push(row);
3822
3895
  rows.set(row.position, matches);
@@ -3861,7 +3934,7 @@ async function assertCatalogVisibility(params) {
3861
3934
  const grants = new Set(rows.map((row) => row.privilege));
3862
3935
  const missing = ["SELECT", "TRIGGER", "SHOW VIEW", "EXECUTE"].filter((privilege) => !grants.has(privilege));
3863
3936
  if (missing.length > 0)
3864
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "MySQL catalog visibility requires direct database-wide grants; role-only or partial grants cannot prove object absence", meta: { reason: "inventory-unavailable", missing } });
3937
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "MySQL catalog visibility requires direct database-wide grants; role-only or partial grants cannot prove object absence", meta: { reason: "inventory-unavailable", missing } });
3865
3938
  }
3866
3939
  var CATALOG_V1_OMITTED_FACTS = Object.freeze({
3867
3940
  function: ["definerOwner"],
@@ -3885,9 +3958,20 @@ function compare(params) {
3885
3958
  differences.push({ attribute: "dependencies", expected, actual });
3886
3959
  return differences;
3887
3960
  }
3961
+ function compareInspectedSqlObject(params) {
3962
+ const { identity, ownership, expected, live } = params;
3963
+ if (live.state !== "present")
3964
+ return { identity, ownership, state: live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} };
3965
+ if (expected === undefined)
3966
+ return identity.kind === "namespace" ? { identity, ownership, state: "matching", differences: [] } : { identity, ownership, state: "unavailable", reason: "independent-definition-required", differences: [] };
3967
+ if (params.dialect === "postgres" && identity.kind === "table" && (expected.facts.persistence === undefined || expected.facts.options === undefined))
3968
+ return { identity, ownership, state: "unavailable", reason: "table-physical-reference-required", differences: compare({ expected, live: live.definition }) };
3969
+ const differences = compare({ expected, live: live.definition });
3970
+ return { identity, ownership, state: differences.length === 0 ? "matching" : "changed", differences };
3971
+ }
3888
3972
  function assertProbeMode(params) {
3889
3973
  if (params.probe !== undefined && params.probe !== "none" && DIALECT_CAPABILITIES2[params.dialect].checkNormalization !== params.probe)
3890
- throw new VibeError15({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `${params.probe} CHECK probes are unavailable on ${params.dialect}`, meta: { feature: "checkProbe", dialect: params.dialect } });
3974
+ throw new VibeError16({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `${params.probe} CHECK probes are unavailable on ${params.dialect}`, meta: { feature: "checkProbe", dialect: params.dialect } });
3891
3975
  }
3892
3976
  async function verifyMigrationState(params) {
3893
3977
  return verifyState(params);
@@ -3910,18 +3994,7 @@ async function verifyState(params) {
3910
3994
  const expected = definitions.get(key);
3911
3995
  const live = inspections === undefined ? await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity }) : inspections[position];
3912
3996
  position++;
3913
- if (live.state !== "present") {
3914
- objects.push({ identity, ownership, state: live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} });
3915
- } else if (expected === undefined && identity.kind === "namespace") {
3916
- objects.push({ identity, ownership, state: "matching", differences: [] });
3917
- } else if (expected === undefined) {
3918
- objects.push({ identity, ownership, state: "unavailable", reason: "independent-definition-required", differences: [] });
3919
- } else if (params.dialect === "postgres" && identity.kind === "table" && (expected.facts.persistence === undefined || expected.facts.options === undefined)) {
3920
- objects.push({ identity, ownership, state: "unavailable", reason: "table-physical-reference-required", differences: compare({ expected, live: live.definition }) });
3921
- } else {
3922
- const differences = compare({ expected, live: live.definition });
3923
- objects.push({ identity, ownership, state: differences.length === 0 ? "matching" : "changed", differences });
3924
- }
3997
+ objects.push(compareInspectedSqlObject({ identity, ownership, expected, live, dialect: params.dialect }));
3925
3998
  }
3926
3999
  if (params.dialect === "postgres" && schemaNamespaces({ schema: params.expected.schema }).length > 0 || params.expected.schema.models.length > 0 || params.expected.schema.enums.length > 0)
3927
4000
  objects.push(...await verifySchemaObjects(params));
@@ -4090,7 +4163,7 @@ async function verifyMigrationTransition(params) {
4090
4163
  }
4091
4164
  async function verifySqliteTransitionBeforeRestore(params) {
4092
4165
  if (params.dialect !== "sqlite")
4093
- throw new VibeError15({ code: "VIBE_INTERNAL", message: "SQLite rebuild verification requires the SQLite dialect" });
4166
+ throw new VibeError16({ code: "VIBE_INTERNAL", message: "SQLite rebuild verification requires the SQLite dialect" });
4094
4167
  await executeStatement({ executor: params.executor, dialect: "sqlite", statement: "PRAGMA foreign_key_check;" });
4095
4168
  return verifyTransition({ ...params, sqliteRestorePending: true });
4096
4169
  }
@@ -4100,7 +4173,7 @@ async function verifyTransition(params) {
4100
4173
  for (const identity of params.removals ?? []) {
4101
4174
  const owner = externalObjectOwner({ identity, external });
4102
4175
  if (owner !== undefined)
4103
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Transition removals cannot target externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
4176
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Transition removals cannot target externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
4104
4177
  }
4105
4178
  params = { ...params, from: projectExternalState({ state: params.from, external }), to: projectExternalState({ state: params.to, external }) };
4106
4179
  }
@@ -4125,12 +4198,12 @@ async function migrationNamespace(params) {
4125
4198
  const rows = await params.executor({ text: query.text, values: [] });
4126
4199
  const namespace = rows[0]?.namespace;
4127
4200
  if (typeof namespace !== "string" || namespace.length === 0)
4128
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Cannot determine the migration namespace", meta: { reason: "inspection-unavailable" } });
4201
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Cannot determine the migration namespace", meta: { reason: "inspection-unavailable" } });
4129
4202
  return namespace;
4130
4203
  }
4131
4204
  function assertMigrationState(params) {
4132
4205
  if (!params.report.inSync)
4133
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Migration state could not be verified", meta: { reason: "migration-state-unverified", shadowReusable: true, objects: params.report.objects.filter((object) => object.state !== "matching") } });
4206
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Migration state could not be verified", meta: { reason: "migration-state-unverified", shadowReusable: true, objects: params.report.objects.filter((object) => object.state !== "matching") } });
4134
4207
  }
4135
4208
  async function deparsePostgresCheck(params) {
4136
4209
  const definition = await deparsePostgresCheckDefinition({ ...params, constraint: "vibeorm_check_probe" });
@@ -4144,7 +4217,7 @@ async function deparsePostgresCheckDefinition(params) {
4144
4217
  await params.executor({ text: query.text });
4145
4218
  const result = await inspectSqlObject({ executor: params.executor, dialect: "postgres", identity: { kind: "constraint", constraintType: "check", table: params.table, name } });
4146
4219
  if (result.state !== "present" || typeof result.definition.facts.definition !== "string")
4147
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "CHECK normalization probe unavailable", meta: { reason: "check-inspection-unavailable" } });
4220
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "CHECK normalization probe unavailable", meta: { reason: "check-inspection-unavailable" } });
4148
4221
  return { ...result.definition, identity: { kind: "constraint", constraintType: "check", table: params.table, name: params.constraint }, facts: { ...result.definition.facts, definition: result.definition.facts.definition.replace(/ NOT VALID$/, ""), validated: true } };
4149
4222
  } finally {
4150
4223
  await params.executor({ text: "ROLLBACK TO SAVEPOINT vibeorm_check_probe" });
@@ -4162,7 +4235,7 @@ async function deparseMysqlCheck(params) {
4162
4235
  const source = await params.executor({ text: read.text, values: [] });
4163
4236
  const definition = source[0]?.["Create Table"];
4164
4237
  if (typeof definition !== "string")
4165
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Cannot read temporary clone definition" });
4238
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Cannot read temporary clone definition" });
4166
4239
  const drop = renderMysqlCheckProbe({ dialect, probe, operation: "drop" });
4167
4240
  await params.executor({ text: drop.text });
4168
4241
  created = false;
@@ -4173,7 +4246,7 @@ async function deparseMysqlCheck(params) {
4173
4246
  const normalizedDefinition = rows[0]?.["Create Table"];
4174
4247
  const check = typeof normalizedDefinition === "string" ? parseSqliteNamedChecks({ tableSql: normalizedDefinition }).find((entry) => entry.name === probe) : undefined;
4175
4248
  if (check === undefined)
4176
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "MySQL temporary CHECK deparse unavailable", meta: { reason: "check-inspection-unavailable" } });
4249
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "MySQL temporary CHECK deparse unavailable", meta: { reason: "check-inspection-unavailable" } });
4177
4250
  return check.expression;
4178
4251
  } finally {
4179
4252
  if (created) {
@@ -4200,7 +4273,7 @@ async function prepareCheckDiff(params) {
4200
4273
  const desired = await deparsePostgresCheck({ executor: params.executor, table: identity.table, expression: next.expression, probe: "rollback" });
4201
4274
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
4202
4275
  if (live.state !== "present")
4203
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4276
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4204
4277
  if (live.definition.facts.definition === desired) {
4205
4278
  checks.push(next);
4206
4279
  continue;
@@ -4212,7 +4285,7 @@ async function prepareCheckDiff(params) {
4212
4285
  const desired = await deparseMysqlCheck({ executor: params.executor, table: identity.table, expression: next.expression, probe: "temporary" });
4213
4286
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
4214
4287
  if (live.state !== "present")
4215
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4288
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4216
4289
  checks.push(desired === live.definition.facts.definition ? next : { name: check.name, expression: check.expression });
4217
4290
  continue;
4218
4291
  }
@@ -4225,7 +4298,7 @@ async function prepareCheckDiff(params) {
4225
4298
  checks.push({ name: check.name, expression: check.expression });
4226
4299
  continue;
4227
4300
  }
4228
- throw new VibeError15({ code: "VIBE_MIGRATION", message: `Cannot establish CHECK equivalence for ${table}.${check.name}; request a rollback probe or an explicit replacement`, meta: { reason: "explicit-replacement-required", identity } });
4301
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: `Cannot establish CHECK equivalence for ${table}.${check.name}; request a rollback probe or an explicit replacement`, meta: { reason: "explicit-replacement-required", identity } });
4229
4302
  }
4230
4303
  models.push({ ...model, checks });
4231
4304
  }
@@ -4252,7 +4325,7 @@ async function inspectMigrationInventory(params) {
4252
4325
  for (const row of rows) {
4253
4326
  const name = row.name;
4254
4327
  if (typeof name !== "string" || typeof row.kind !== "string")
4255
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Malformed catalog identity", meta: { reason: "inventory-unavailable" } });
4328
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Malformed catalog identity", meta: { reason: "inventory-unavailable" } });
4256
4329
  const qualified = { namespace: params.namespace, name };
4257
4330
  const table = { namespace: params.namespace, name: String(row.parent ?? "") };
4258
4331
  let identity;
@@ -4278,7 +4351,7 @@ async function inspectMigrationInventory(params) {
4278
4351
  case "constraint": {
4279
4352
  const constraintType = kinds[String(row.subtype)];
4280
4353
  if (constraintType === undefined)
4281
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Unknown constraint catalog kind", meta: { reason: "inventory-unavailable" } });
4354
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Unknown constraint catalog kind", meta: { reason: "inventory-unavailable" } });
4282
4355
  identity = { kind: "constraint", table, name, constraintType };
4283
4356
  break;
4284
4357
  }
@@ -4288,17 +4361,17 @@ async function inspectMigrationInventory(params) {
4288
4361
  const query = renderMysqlObjectMembers2({ dialect: getSqlDialect2({ dialect: params.dialect }), identity: { kind: "function", name: qualified, argumentTypes: [] } });
4289
4362
  const members = await params.executor({ text: query.text, values: [...query.values] });
4290
4363
  if (members.length === 0)
4291
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "MySQL function signature unavailable", meta: { reason: "inventory-unavailable", name } });
4364
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "MySQL function signature unavailable", meta: { reason: "inventory-unavailable", name } });
4292
4365
  argumentTypes = members.filter((member) => member.mode !== null).map((member) => ({ namespace: "mysql", name: String(member.type) }));
4293
4366
  }
4294
4367
  const candidate = { kind: "function", name: qualified, argumentTypes };
4295
4368
  if (!isSqlObjectIdentity2(candidate))
4296
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Invalid function signature in catalog", meta: { reason: "inventory-unavailable" } });
4369
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Invalid function signature in catalog", meta: { reason: "inventory-unavailable" } });
4297
4370
  identity = candidate;
4298
4371
  break;
4299
4372
  }
4300
4373
  default:
4301
- throw new VibeError16({ code: "VIBE_MIGRATION", message: `Unknown catalog object kind ${row.kind}`, meta: { reason: "inventory-unavailable" } });
4374
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: `Unknown catalog object kind ${row.kind}`, meta: { reason: "inventory-unavailable" } });
4302
4375
  }
4303
4376
  objects.push({ identity, ...typeof row.backing === "string" ? { backingConstraint: row.backing } : {} });
4304
4377
  if (params.dialect === "sqlite" && row.kind === "table" && typeof row.definition === "string" && !/^\s*CREATE\s+VIRTUAL\s+TABLE\b/i.test(row.definition)) {
@@ -4343,7 +4416,7 @@ async function inspectMigrationInventory(params) {
4343
4416
  // packages/migrate/src/external-objects.ts
4344
4417
  var partitionPatterns = new WeakMap;
4345
4418
  function invalid2(params) {
4346
- return new VibeError17({ code: "VIBE_CONFIG", message: `Invalid external ownership declaration: ${params.detail}`, meta: { reason: "external-declaration-invalid", ...params.owner === undefined ? {} : { owner: params.owner } }, ...params.cause === undefined ? {} : { cause: params.cause } });
4419
+ return new VibeError18({ code: "VIBE_CONFIG", message: `Invalid external ownership declaration: ${params.detail}`, meta: { reason: "external-declaration-invalid", ...params.owner === undefined ? {} : { owner: params.owner } }, ...params.cause === undefined ? {} : { cause: params.cause } });
4347
4420
  }
4348
4421
  function record2(value) {
4349
4422
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -4431,7 +4504,7 @@ async function parseExternalObjects(params) {
4431
4504
  }
4432
4505
  return Object.freeze(result);
4433
4506
  }).catch((cause) => {
4434
- if (cause instanceof VibeError17)
4507
+ if (cause instanceof VibeError18)
4435
4508
  throw cause;
4436
4509
  throw invalid2({ detail: "regular-expression pattern cannot be compiled", cause });
4437
4510
  });
@@ -4455,7 +4528,7 @@ function declaredTableOwner(params) {
4455
4528
  return patterns?.some(({ pattern }) => pattern.exec(params.name.name)?.[0] === params.name.name) === true;
4456
4529
  });
4457
4530
  if (matches.length > 1)
4458
- throw new VibeError17({ code: "VIBE_CONFIG", message: "A table is claimed by more than one external owner", meta: { reason: "external-owner-collision", identity: { kind: "table", name: params.name }, owners: matches.map((entry) => entry.owner) } });
4531
+ throw new VibeError18({ code: "VIBE_CONFIG", message: "A table is claimed by more than one external owner", meta: { reason: "external-owner-collision", identity: { kind: "table", name: params.name }, owners: matches.map((entry) => entry.owner) } });
4459
4532
  return matches[0]?.owner;
4460
4533
  }
4461
4534
  function externalObjectOwner(params) {
@@ -4477,13 +4550,13 @@ function resolveExternalObjectOwnership(params) {
4477
4550
  if (params.external.length > 0 && params.dialect !== "postgres")
4478
4551
  throw unsupportedCapability7({ dialect: params.dialect, feature: "externalObjects" });
4479
4552
  if (params.catalog.inventory.some((object) => object.inspectionUnavailable !== undefined))
4480
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "External ownership requires complete catalog visibility", meta: { reason: "external-inventory-unavailable", objects: params.catalog.inventory.filter((object) => object.inspectionUnavailable !== undefined) } });
4553
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "External ownership requires complete catalog visibility", meta: { reason: "external-inventory-unavailable", objects: params.catalog.inventory.filter((object) => object.inspectionUnavailable !== undefined) } });
4481
4554
  const tables = new Map;
4482
4555
  const assign = (entry) => {
4483
4556
  const key = nameKey(entry.name);
4484
4557
  const prior = tables.get(key)?.owner ?? declaredTableOwner({ external: params.external, name: entry.name });
4485
4558
  if (prior !== undefined && prior !== entry.owner)
4486
- throw new VibeError17({ code: "VIBE_CONFIG", message: "An inherited table has conflicting external owners", meta: { reason: "external-owner-collision", identity: { kind: "table", name: entry.name }, owners: [prior, entry.owner] } });
4559
+ throw new VibeError18({ code: "VIBE_CONFIG", message: "An inherited table has conflicting external owners", meta: { reason: "external-owner-collision", identity: { kind: "table", name: entry.name }, owners: [prior, entry.owner] } });
4487
4560
  if (tables.has(key))
4488
4561
  return false;
4489
4562
  tables.set(key, entry);
@@ -4514,7 +4587,7 @@ function resolveExternalObjectOwnership(params) {
4514
4587
  return [];
4515
4588
  const whole = schemaOwner({ external: params.external, namespace: edge.sequence.namespace });
4516
4589
  if (whole !== undefined && whole !== owner)
4517
- throw new VibeError17({ code: "VIBE_CONFIG", message: "An owned sequence has conflicting external owners", meta: { reason: "external-owner-collision", identity: { kind: "sequence", name: edge.sequence }, owners: [whole, owner] } });
4590
+ throw new VibeError18({ code: "VIBE_CONFIG", message: "An owned sequence has conflicting external owners", meta: { reason: "external-owner-collision", identity: { kind: "sequence", name: edge.sequence }, owners: [whole, owner] } });
4518
4591
  return [{ owner, name: edge.sequence }];
4519
4592
  });
4520
4593
  const closure = { declarations: params.external, catalog: params.catalog, inventory: params.catalog.inventory, tables: [...tables.values()], sequences, owners: [], identities: [] };
@@ -4522,7 +4595,7 @@ function resolveExternalObjectOwnership(params) {
4522
4595
  const owner = externalObjectOwner({ external: closure, identity: { kind: "table", name: edge.table } });
4523
4596
  const targetOwner = externalObjectOwner({ external: closure, identity: { kind: "table", name: edge.target } });
4524
4597
  if (owner !== targetOwner && (owner !== undefined || targetOwner !== undefined))
4525
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "Foreign keys cannot cross external ownership boundaries", meta: { reason: "external-cross-owner-foreign-key", name: edge.name, table: edge.table, target: edge.target, owner: owner ?? "application", targetOwner: targetOwner ?? "application" } });
4598
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "Foreign keys cannot cross external ownership boundaries", meta: { reason: "external-cross-owner-foreign-key", name: edge.name, table: edge.table, target: edge.target, owner: owner ?? "application", targetOwner: targetOwner ?? "application" } });
4526
4599
  }
4527
4600
  const observed = new Map;
4528
4601
  for (const namespace of params.catalog.namespaces) {
@@ -4550,7 +4623,7 @@ function catalogName(params) {
4550
4623
  const namespace = params.row[params.namespace];
4551
4624
  const name = params.row[params.name];
4552
4625
  if (!identifier(namespace) || !identifier(name))
4553
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "Malformed external ownership catalog endpoint", meta: { reason: "external-inventory-unavailable" } });
4626
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "Malformed external ownership catalog endpoint", meta: { reason: "external-inventory-unavailable" } });
4554
4627
  return { namespace, name };
4555
4628
  }
4556
4629
  async function inspectExternalObjects(params) {
@@ -4563,7 +4636,7 @@ async function inspectExternalObjects(params) {
4563
4636
  };
4564
4637
  const namespaceRows = await read("namespaces");
4565
4638
  if (namespaceRows.some((row) => !identifier(row.namespace) || row.visible !== true))
4566
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "External ownership cannot prove visibility of every inspected namespace", meta: { reason: "external-inventory-unavailable" } });
4639
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "External ownership cannot prove visibility of every inspected namespace", meta: { reason: "external-inventory-unavailable" } });
4567
4640
  const namespaces = namespaceRows.map((row) => String(row.namespace));
4568
4641
  const [inherited, owned, foreignKeys] = await Promise.all([read("inheritance"), read("ownedSequences"), read("foreignKeys")]);
4569
4642
  const inventory = params.inventory ?? (await Promise.all(namespaces.map((namespace) => inspectMigrationInventory({ executor: params.executor, dialect: params.dialect, namespace })))).flat();
@@ -4584,7 +4657,7 @@ function assertExternalOwnershipClaims(params) {
4584
4657
  for (const identity of params.identities) {
4585
4658
  const owner = externalObjectOwner({ identity, external: params.external }) ?? (identity.kind === "namespace" ? [...params.external.tables, ...params.external.sequences].find((entry) => entry.name.namespace === identity.name)?.owner : undefined);
4586
4659
  if (owner !== undefined)
4587
- throw new VibeError17({ code: "VIBE_CONFIG", message: "Application or module declarations collide with external ownership", meta: { reason: "external-owned-object-collision", owner, identity } });
4660
+ throw new VibeError18({ code: "VIBE_CONFIG", message: "Application or module declarations collide with external ownership", meta: { reason: "external-owned-object-collision", owner, identity } });
4588
4661
  }
4589
4662
  }
4590
4663
  function projectExternalSchema(params) {
@@ -4594,7 +4667,7 @@ function projectExternalSchema(params) {
4594
4667
  for (const model of models)
4595
4668
  for (const relation of model.relations)
4596
4669
  if (removed.has(relation.targetModel))
4597
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "Application relations cannot depend on externally owned tables", meta: { reason: "external-cross-owner-foreign-key", model: model.name, relation: relation.name } });
4670
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "Application relations cannot depend on externally owned tables", meta: { reason: "external-cross-owner-foreign-key", model: model.name, relation: relation.name } });
4598
4671
  return {
4599
4672
  ...schema,
4600
4673
  models,
@@ -4627,7 +4700,7 @@ function migrationCoverageGaps(params) {
4627
4700
  return [...new Map(kept.map((gap) => [JSON.stringify(gap), gap])).values()];
4628
4701
  }
4629
4702
  // packages/migrate/src/module-adoption.ts
4630
- import { VibeError as VibeError21 } from "@vibeorm/schema";
4703
+ import { VibeError as VibeError22 } from "@vibeorm/schema";
4631
4704
 
4632
4705
  // packages/migrate/src/mysql-catalog-visibility.ts
4633
4706
  function hasMysqlGlobalCatalogVisibility(params) {
@@ -4645,10 +4718,10 @@ function hasMysqlGlobalCatalogVisibility(params) {
4645
4718
  }
4646
4719
 
4647
4720
  // packages/migrate/src/module-ownership.ts
4648
- import { VibeError as VibeError18, sqlObjectKey as sqlObjectKey7, sqlObjectNamespace as sqlObjectNamespace3 } from "@vibeorm/schema";
4721
+ import { VibeError as VibeError19, sqlObjectKey as sqlObjectKey7, sqlObjectNamespace as sqlObjectNamespace3 } from "@vibeorm/schema";
4649
4722
  import { getSqlDialect as getSqlDialect4, renderModuleDependencyQuery } from "@vibeorm/sql";
4650
4723
  function refusal(params) {
4651
- return new VibeError18({ code: "VIBE_MIGRATION", message: "Module ownership or dependencies cannot be reconciled with the declared and live schema; no package SQL was authorized", meta: params });
4724
+ return new VibeError19({ code: "VIBE_MIGRATION", message: "Module ownership or dependencies cannot be reconciled with the declared and live schema; no package SQL was authorized", meta: params });
4652
4725
  }
4653
4726
  function identities(params) {
4654
4727
  return [...schemaObjectIdentities({ schema: params.state.schema, dialect: params.dialect, namespace: params.namespace }), ...(params.state.schema.migrationObjects ?? []).map((object) => object.identity), ...params.state.definitions.map((definition) => definition.identity)];
@@ -4709,7 +4782,7 @@ function assertModuleOwnership(params) {
4709
4782
  throw refusal({ reason: "module-mutates-application-state", moduleId: origin.moduleId });
4710
4783
  }
4711
4784
  function dependencyUnavailable(params) {
4712
- return new VibeError18({ code: "VIBE_MIGRATION", message: "Cannot establish module foreign-key dependencies across namespaces. MySQL requires direct global SHOW VIEW (or SELECT) without partial revokes; SHOW VIEW exposes metadata without granting external row reads. Restore catalog visibility before retrying", meta: { reason: "module-dependency-unavailable", moduleId: params.origin.moduleId }, ...params.cause === undefined ? {} : { cause: params.cause } });
4785
+ return new VibeError19({ code: "VIBE_MIGRATION", message: "Cannot establish module foreign-key dependencies across namespaces. MySQL requires direct global SHOW VIEW (or SELECT) without partial revokes; SHOW VIEW exposes metadata without granting external row reads. Restore catalog visibility before retrying", meta: { reason: "module-dependency-unavailable", moduleId: params.origin.moduleId }, ...params.cause === undefined ? {} : { cause: params.cause } });
4713
4786
  }
4714
4787
  async function assertCrossNamespaceModuleDependencies(params) {
4715
4788
  const { executor, origin } = params;
@@ -4771,12 +4844,12 @@ async function assertApplicationModuleState(params) {
4771
4844
  const options = { executor: params.executor, dialect: origin.dialect, from: state.from, to: state.to, removals: state.unmanagedRemovals, ...params.probe === undefined ? {} : { probe: params.probe } };
4772
4845
  const report = params.sqliteRestorePending === true ? await verifySqliteTransitionBeforeRestore(options) : await verifyMigrationTransition(options);
4773
4846
  if (!report.inSync)
4774
- throw new VibeError18({ code: "VIBE_MIGRATION", message: `Application SQL cannot change objects owned by module ${origin.moduleId}; use its reviewed module upgrade`, meta: { reason: "module-application-mutation", moduleId: origin.moduleId, report } });
4847
+ throw new VibeError19({ code: "VIBE_MIGRATION", message: `Application SQL cannot change objects owned by module ${origin.moduleId}; use its reviewed module upgrade`, meta: { reason: "module-application-mutation", moduleId: origin.moduleId, report } });
4775
4848
  }
4776
4849
  }
4777
4850
 
4778
4851
  // packages/migrate/src/sql-statements.ts
4779
- import { VibeError as VibeError19 } from "@vibeorm/schema";
4852
+ import { VibeError as VibeError20 } from "@vibeorm/schema";
4780
4853
  var COMPOUND_BODY_HEADER = /^(?:\s|--[^\n]*\n|\/\*[\s\S]*?\*\/)*CREATE\s+(?:OR\s+REPLACE\s+)?(?:(?:TEMP|TEMPORARY)\s+)?(?:DEFINER\s*=\s*\S+\s+)?(?:TRIGGER|PROCEDURE|FUNCTION|EVENT)\b/i;
4781
4854
  var BLOCK_OPENERS = new Set(["BEGIN", "CASE", "IF", "LOOP", "WHILE", "REPEAT"]);
4782
4855
  function splitSqlStatements(params) {
@@ -4852,7 +4925,7 @@ function splitSqlStatements(params) {
4852
4925
  continue;
4853
4926
  }
4854
4927
  if (dialect === "mysql" || dialect === undefined) {
4855
- throw new VibeError19({ code: "VIBE_MIGRATION", message: "SQL string backslash escaping depends on the session SQL mode; use mode-independent quoting and pass the migration dialect", meta: { reason: "sql-mode-ambiguous", dialect } });
4928
+ throw new VibeError20({ code: "VIBE_MIGRATION", message: "SQL string backslash escaping depends on the session SQL mode; use mode-independent quoting and pass the migration dialect", meta: { reason: "sql-mode-ambiguous", dialect } });
4856
4929
  }
4857
4930
  }
4858
4931
  if (value === endQuote) {
@@ -4926,10 +4999,10 @@ function joinSqlStatements(params) {
4926
4999
 
4927
5000
  // packages/migrate/src/module-origin.ts
4928
5001
  import { createHash } from "crypto";
4929
- import { VibeError as VibeError20, isSqlObjectIdentity as isSqlObjectIdentity3, sqlObjectKey as sqlObjectKey8 } from "@vibeorm/schema";
5002
+ import { VibeError as VibeError21, isSqlObjectIdentity as isSqlObjectIdentity3, sqlObjectKey as sqlObjectKey8 } from "@vibeorm/schema";
4930
5003
  var MODULE_ORIGIN_METADATA = Object.freeze({ filename: "module-origin.json", version: 1 });
4931
5004
  function invalid3() {
4932
- return new VibeError20({ code: "VIBE_MIGRATION", message: "Module migration origin is malformed or differs from its immutable package artifact; restore the reviewed artifact", meta: { reason: "module-origin-invalid" } });
5005
+ return new VibeError21({ code: "VIBE_MIGRATION", message: "Module migration origin is malformed or differs from its immutable package artifact; restore the reviewed artifact", meta: { reason: "module-origin-invalid" } });
4933
5006
  }
4934
5007
  function record3(value) {
4935
5008
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -4993,7 +5066,7 @@ function assertMaterializedModuleMigration(params) {
4993
5066
  return;
4994
5067
  const restored = restoreModuleMigration({ migration, origin: migration.moduleOrigin });
4995
5068
  if (migration.desiredState === undefined || canonicalMigrationJson({ value: restored }) !== canonicalMigrationJson({ value: migration }))
4996
- throw new VibeError20({ code: "VIBE_MIGRATION", message: "Materialized module metadata differs from its immutable source", meta: { reason: "module-source-state-mismatch", moduleId: migration.moduleOrigin.moduleId } });
5069
+ throw new VibeError21({ code: "VIBE_MIGRATION", message: "Materialized module metadata differs from its immutable source", meta: { reason: "module-source-state-mismatch", moduleId: migration.moduleOrigin.moduleId } });
4997
5070
  }
4998
5071
  function restoreModuleMigration(params) {
4999
5072
  const { migration, origin } = params;
@@ -5018,7 +5091,7 @@ function restoreModuleMigration(params) {
5018
5091
  // packages/migrate/src/module-adoption.ts
5019
5092
  var MODULE_ADOPTION_METADATA = Object.freeze({ filename: "module-adoption.json", version: 1 });
5020
5093
  function invalid4() {
5021
- return new VibeError21({ code: "VIBE_MIGRATION", message: "Local module adoption proof is malformed or lacks immutable source provenance; restore the reviewed local history", meta: { reason: "module-adoption-invalid" } });
5094
+ return new VibeError22({ code: "VIBE_MIGRATION", message: "Local module adoption proof is malformed or lacks immutable source provenance; restore the reviewed local history", meta: { reason: "module-adoption-invalid" } });
5022
5095
  }
5023
5096
  function assertMarker(params) {
5024
5097
  const value = params.value;
@@ -5053,22 +5126,22 @@ function serializeModuleAdoption(params) {
5053
5126
  `;
5054
5127
  }
5055
5128
  // packages/migrate/src/module-adoption-record.ts
5056
- import { VibeError as VibeError42 } from "@vibeorm/schema";
5129
+ import { VibeError as VibeError43 } from "@vibeorm/schema";
5057
5130
 
5058
5131
  // packages/migrate/src/module-adoption-reference.ts
5059
- import { VibeError as VibeError41, isEnumType as isEnumType9, sqlObjectKey as sqlObjectKey14 } from "@vibeorm/schema";
5132
+ import { VibeError as VibeError42, isEnumType as isEnumType9, sqlObjectKey as sqlObjectKey14 } from "@vibeorm/schema";
5060
5133
 
5061
5134
  // packages/migrate/src/module-pending.ts
5062
- import { VibeError as VibeError37, sqlObjectKey as sqlObjectKey11, qualifiedSqlName as qualifiedSqlName10 } from "@vibeorm/schema";
5135
+ import { VibeError as VibeError38, sqlObjectKey as sqlObjectKey11, qualifiedSqlName as qualifiedSqlName11 } from "@vibeorm/schema";
5063
5136
 
5064
5137
  // packages/migrate/src/module-bundle.ts
5065
- import { VibeError as VibeError36 } from "@vibeorm/schema";
5138
+ import { VibeError as VibeError37 } from "@vibeorm/schema";
5066
5139
 
5067
5140
  // packages/migrate/src/migration-checksum.ts
5068
5141
  import { createHash as createHash2 } from "crypto";
5069
5142
 
5070
5143
  // packages/migrate/src/execution.ts
5071
- import { VibeError as VibeError22 } from "@vibeorm/schema";
5144
+ import { VibeError as VibeError23 } from "@vibeorm/schema";
5072
5145
  var EXECUTION_SIDECAR_FILENAME = "execution.json";
5073
5146
  var EXECUTION_SIDECAR_VERSION = 1;
5074
5147
  function isDefaultExecution(params) {
@@ -5107,7 +5180,7 @@ ${lines.join(`
5107
5180
  `)}`;
5108
5181
  }
5109
5182
  function invalid5(params) {
5110
- return new VibeError22({
5183
+ return new VibeError23({
5111
5184
  code: "VIBE_MIGRATION",
5112
5185
  message: `invalid execution metadata for migration "${params.migration}": ${params.detail}`,
5113
5186
  meta: { reason: "execution-metadata-invalid", migration: params.migration }
@@ -5158,7 +5231,7 @@ function parseExecutionSidecar(params) {
5158
5231
  try {
5159
5232
  parsed = JSON.parse(params.text);
5160
5233
  } catch (error) {
5161
- throw new VibeError22({
5234
+ throw new VibeError23({
5162
5235
  code: "VIBE_MIGRATION",
5163
5236
  message: `invalid execution metadata for migration "${migration}": not valid JSON`,
5164
5237
  meta: { reason: "execution-metadata-invalid", migration },
@@ -5257,7 +5330,7 @@ function migrationChecksum(params) {
5257
5330
  }
5258
5331
 
5259
5332
  // packages/migrate/src/shadow-guard.ts
5260
- import { VibeError as VibeError23 } from "@vibeorm/schema";
5333
+ import { VibeError as VibeError24 } from "@vibeorm/schema";
5261
5334
  import { getSqlDialect as getSqlDialect5, renderShadowOperation } from "@vibeorm/sql";
5262
5335
  var replayExecutors = new WeakSet;
5263
5336
  function isShadowReplayExecutor(params) {
@@ -5269,7 +5342,7 @@ async function assertDatabaseNotShadow(params) {
5269
5342
  const [query] = renderShadowOperation({ dialect: getSqlDialect5({ dialect: params.dialect }), operation: { kind: "markerExists" } });
5270
5343
  const rows = await params.executor({ text: query.text, values: [...query.values] });
5271
5344
  if (rows.length !== 0)
5272
- throw new VibeError23({ code: "VIBE_MIGRATION", message: "This database is claimed as a disposable migration shadow. Application migrations must use the deployment target.", meta: { reason: "shadow-database-is-not-target" } });
5345
+ throw new VibeError24({ code: "VIBE_MIGRATION", message: "This database is claimed as a disposable migration shadow. Application migrations must use the deployment target.", meta: { reason: "shadow-database-is-not-target" } });
5273
5346
  }
5274
5347
  function runShadowReplay(params) {
5275
5348
  replayExecutors.add(params.executor);
@@ -5279,7 +5352,7 @@ function runShadowReplay(params) {
5279
5352
  }
5280
5353
 
5281
5354
  // packages/migrate/src/external-dependencies.ts
5282
- import { VibeError as VibeError24, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
5355
+ import { VibeError as VibeError25, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
5283
5356
  import { getSqlDialect as getSqlDialect6, renderExternalDependents } from "@vibeorm/sql";
5284
5357
  function externalDependencyTargets(params) {
5285
5358
  return params.steps.flatMap((step) => {
@@ -5302,7 +5375,7 @@ function externalDependencyTargets(params) {
5302
5375
  });
5303
5376
  }
5304
5377
  function unavailable() {
5305
- return new VibeError24({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency row", meta: { reason: "external-inventory-unavailable" } });
5378
+ return new VibeError25({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency row", meta: { reason: "external-inventory-unavailable" } });
5306
5379
  }
5307
5380
  function text(params) {
5308
5381
  const value = params.row[params.key];
@@ -5343,7 +5416,7 @@ function assertNoExternalDependents(params) {
5343
5416
  const dependent = dependentOwner({ row, external: params.external });
5344
5417
  if (dependent.owner === undefined)
5345
5418
  continue;
5346
- throw new VibeError24({
5419
+ throw new VibeError25({
5347
5420
  code: "VIBE_MIGRATION",
5348
5421
  message: "An externally owned object depends on an application object this change drops or retypes; automatic DDL is refused",
5349
5422
  meta: { reason: "external-owned-dependency", owner: dependent.owner, ...dependent.identity === undefined ? {} : { identity: dependent.identity }, dependent: { type, identity }, dependency: target.dependency, referenced }
@@ -5364,9 +5437,9 @@ async function refuseExternalDependents(params) {
5364
5437
  }
5365
5438
 
5366
5439
  // packages/migrate/src/module-history.ts
5367
- import { VibeError as VibeError25, sqlObjectKey as sqlObjectKey9, providerToDialect as providerToDialect3 } from "@vibeorm/schema";
5440
+ import { VibeError as VibeError26, sqlObjectKey as sqlObjectKey9, providerToDialect as providerToDialect3 } from "@vibeorm/schema";
5368
5441
  function refusal2(params) {
5369
- return new VibeError25({ code: "VIBE_MIGRATION", message: `Module ${params.moduleId} history, ownership or predecessor state is inconsistent; restore or complete the reviewed upgrade first`, meta: params });
5442
+ return new VibeError26({ code: "VIBE_MIGRATION", message: `Module ${params.moduleId} history, ownership or predecessor state is inconsistent; restore or complete the reviewed upgrade first`, meta: params });
5370
5443
  }
5371
5444
  function moduleHistoryHeads(params) {
5372
5445
  const heads = new Map;
@@ -5473,11 +5546,11 @@ function assertModuleUpgradeHistory(params) {
5473
5546
  }
5474
5547
 
5475
5548
  // packages/migrate/src/module-journal.ts
5476
- import { VibeError as VibeError27 } from "@vibeorm/schema";
5549
+ import { VibeError as VibeError28 } from "@vibeorm/schema";
5477
5550
  import { getSqlDialect as getSqlDialect7, MODULE_JOURNAL_COLUMN, renderModuleJournal } from "@vibeorm/sql";
5478
5551
 
5479
5552
  // packages/migrate/src/journal-read.ts
5480
- import { VibeError as VibeError26 } from "@vibeorm/schema";
5553
+ import { VibeError as VibeError27 } from "@vibeorm/schema";
5481
5554
  var MISSING_TABLE_IDENTIFIERS = new Set(["42P01", "42S02", "ER_NO_SUCH_TABLE"]);
5482
5555
  function isMissingTable(params) {
5483
5556
  let current = params.cause;
@@ -5501,7 +5574,7 @@ async function readJournalRows(params) {
5501
5574
  if (presence.state === "missing")
5502
5575
  return;
5503
5576
  }
5504
- throw new VibeError26({ code: "VIBE_MIGRATION", message: "Cannot inspect migration history; check SELECT privileges", meta: { reason: "history-unavailable" }, cause });
5577
+ throw new VibeError27({ code: "VIBE_MIGRATION", message: "Cannot inspect migration history; check SELECT privileges", meta: { reason: "history-unavailable" }, cause });
5505
5578
  });
5506
5579
  if (rows === undefined)
5507
5580
  return;
@@ -5509,7 +5582,7 @@ async function readJournalRows(params) {
5509
5582
  for (const row of rows) {
5510
5583
  const index = row.statement_index;
5511
5584
  if (typeof row.name !== "string" || typeof row.checksum !== "string" || typeof index !== "number" && typeof index !== "string" || typeof index === "string" && !/^-?\d+$/.test(index) || !Number.isSafeInteger(Number(index)) || row.module_origin !== null && row.module_origin !== undefined && typeof row.module_origin !== "string" || row.error !== null && row.error !== undefined && typeof row.error !== "string" || result.has(row.name)) {
5512
- throw new VibeError26({ code: "VIBE_MIGRATION", message: "Malformed migration journal row", meta: { reason: "history-unavailable" } });
5585
+ throw new VibeError27({ code: "VIBE_MIGRATION", message: "Malformed migration journal row", meta: { reason: "history-unavailable" } });
5513
5586
  }
5514
5587
  result.set(row.name, { checksum: row.checksum, statementIndex: Number(index), ...typeof row.error === "string" ? { error: row.error } : {}, ...typeof row.module_origin === "string" ? { moduleEntry: row.module_origin } : {} });
5515
5588
  }
@@ -5518,7 +5591,7 @@ async function readJournalRows(params) {
5518
5591
 
5519
5592
  // packages/migrate/src/module-journal.ts
5520
5593
  function invalid6() {
5521
- return new VibeError27({ code: "VIBE_MIGRATION", message: "Module migration history is missing, altered or inconsistent; restore the reviewed history before using this module", meta: { reason: "module-history-invalid" } });
5594
+ return new VibeError28({ code: "VIBE_MIGRATION", message: "Module migration history is missing, altered or inconsistent; restore the reviewed history before using this module", meta: { reason: "module-history-invalid" } });
5522
5595
  }
5523
5596
  function parseModuleJournalEntry(params) {
5524
5597
  return parseMigrationMetadata({ text: params.text, failure: invalid6, parse: (value) => {
@@ -5577,7 +5650,7 @@ function moduleRecordComplete(params) {
5577
5650
  }
5578
5651
 
5579
5652
  // packages/migrate/src/runner.ts
5580
- import { VibeError as VibeError35 } from "@vibeorm/schema";
5653
+ import { VibeError as VibeError36 } from "@vibeorm/schema";
5581
5654
  import {
5582
5655
  DIALECT_CAPABILITIES as DIALECT_CAPABILITIES3,
5583
5656
  getSqlDialect as getSqlDialect9,
@@ -5587,7 +5660,7 @@ import {
5587
5660
  } from "@vibeorm/sql";
5588
5661
 
5589
5662
  // packages/migrate/src/recovery.ts
5590
- import { VibeError as VibeError28 } from "@vibeorm/schema";
5663
+ import { VibeError as VibeError29 } from "@vibeorm/schema";
5591
5664
  import {
5592
5665
  getSqlDialect as getSqlDialect8,
5593
5666
  renderColumnStateQuery,
@@ -5603,7 +5676,7 @@ function constraintUnverifiable(params) {
5603
5676
  });
5604
5677
  }
5605
5678
  function conflict2(params) {
5606
- return new VibeError28({
5679
+ return new VibeError29({
5607
5680
  code: "VIBE_MIGRATION",
5608
5681
  message: params.message,
5609
5682
  meta: { reason: params.reason, ...params.meta }
@@ -5708,10 +5781,10 @@ async function establishOperationState(params) {
5708
5781
 
5709
5782
  // packages/migrate/src/differ.ts
5710
5783
  import { assertIndexOperatorClasses as assertIndexOperatorClasses2, isEnumType as isEnumType6, qualifiedSqlName as qualifiedSqlName9, unsupportedCapability as unsupportedCapability10 } from "@vibeorm/schema";
5711
- import { VibeError as VibeError30 } from "@vibeorm/schema";
5784
+ import { VibeError as VibeError31 } from "@vibeorm/schema";
5712
5785
 
5713
5786
  // packages/migrate/src/renames.ts
5714
- import { VibeError as VibeError29, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
5787
+ import { VibeError as VibeError30, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
5715
5788
  function tableOf(model) {
5716
5789
  return model.dbName ?? model.name;
5717
5790
  }
@@ -5722,7 +5795,7 @@ function hasColumn(params) {
5722
5795
  return params.model.fields.some((field) => (field.dbName ?? field.name) === params.column);
5723
5796
  }
5724
5797
  function ambiguous(params) {
5725
- return new VibeError29({
5798
+ return new VibeError30({
5726
5799
  code: "VIBE_MIGRATION",
5727
5800
  message: `rename refused: ${params.detail}`,
5728
5801
  meta: { reason: "rename-ambiguous", ...params.meta }
@@ -5824,17 +5897,34 @@ function planRenames(params) {
5824
5897
 
5825
5898
  // packages/migrate/src/differ.ts
5826
5899
  function diffSchemas(params) {
5900
+ return diffSchemasWithNormalization(params).steps;
5901
+ }
5902
+ function diffSchemasWithNormalization(params) {
5827
5903
  const dialect = params.dialect ?? "postgres";
5828
5904
  assertIndexOperatorClasses2({ schema: params.to, dialect });
5829
- for (const kind of ["models", "enums"])
5905
+ for (const kind of ["models", "enums"]) {
5906
+ const sourceByName = new Map;
5907
+ const sourcePhysical = new Set;
5908
+ const targetPhysical = new Set(params.to[kind].map((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect })));
5909
+ const removedByBasename = new Map;
5910
+ for (const entry of params.from[kind]) {
5911
+ if (!sourceByName.has(entry.name))
5912
+ sourceByName.set(entry.name, entry);
5913
+ const basename = entry.dbName ?? entry.name;
5914
+ const key = physicalNameKey({ name: basename, namespace: entry.namespace, dialect });
5915
+ sourcePhysical.add(key);
5916
+ if (!targetPhysical.has(key) && !removedByBasename.has(basename))
5917
+ removedByBasename.set(basename, entry);
5918
+ }
5830
5919
  for (const entry of params.to[kind]) {
5831
5920
  const key = physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect });
5832
- const before = params.from[kind].find((candidate) => candidate.name === entry.name) ?? params.from[kind].find((candidate) => (candidate.dbName ?? candidate.name) === (entry.dbName ?? entry.name) && !params.to[kind].some((target) => physicalNameKey({ name: target.dbName ?? target.name, namespace: target.namespace, dialect }) === physicalNameKey({ name: candidate.dbName ?? candidate.name, namespace: candidate.namespace, dialect })));
5833
- if (before !== undefined && qualifiedSqlName9({ name: entry.name, namespace: before.namespace }).namespace !== qualifiedSqlName9({ name: entry.name, namespace: entry.namespace }).namespace && !params.from[kind].some((candidate) => physicalNameKey({ name: candidate.dbName ?? candidate.name, namespace: candidate.namespace, dialect }) === key))
5921
+ const before = sourceByName.get(entry.name) ?? removedByBasename.get(entry.dbName ?? entry.name);
5922
+ if (before !== undefined && qualifiedSqlName9({ name: entry.name, namespace: before.namespace }).namespace !== qualifiedSqlName9({ name: entry.name, namespace: entry.namespace }).namespace && !sourcePhysical.has(key))
5834
5923
  throw unsupportedCapability10({ dialect, feature: "automaticNamespaceMove", hint: "Moving populated objects to a fixed namespace requires an explicit reviewed migration" });
5835
5924
  }
5836
- for (const schema of [params.from, params.to])
5837
- normalizeSchema({ schema, dialect });
5925
+ }
5926
+ let from = normalizeSchema({ schema: params.from, dialect });
5927
+ const to = normalizeSchema({ schema: params.to, dialect });
5838
5928
  for (const schema of [params.from, params.to])
5839
5929
  if (dialect !== "postgres" && schema.models.some((model) => (model.exclusions?.length ?? 0) > 0))
5840
5930
  throw unsupportedCapability10({ dialect, feature: "exclusionConstraints" });
@@ -5843,11 +5933,12 @@ function diffSchemas(params) {
5843
5933
  if (dialect !== "postgres")
5844
5934
  refuseScalarListColumns({ schema: params.to, dialect });
5845
5935
  const renamed = planRenames({ from: params.from, to: params.to, renames: params.renames ?? [], dialect });
5936
+ if (renamed.from !== params.from)
5937
+ from = normalizeSchema({ schema: renamed.from, dialect });
5846
5938
  params = { ...params, from: renamed.from };
5847
- const from = normalizeSchema({ schema: params.from, dialect });
5848
- const to = normalizeSchema({ schema: params.to, dialect });
5939
+ const sourceNamespaces = new Set(dialect === "postgres" ? schemaNamespaces({ schema: params.from }) : []);
5849
5940
  const buckets = {
5850
- createNamespace: dialect === "postgres" ? schemaNamespaces({ schema: params.to }).filter((namespace) => !schemaNamespaces({ schema: params.from }).includes(namespace)).map((namespace) => ({ kind: "createNamespace", namespace, destructive: false })) : [],
5941
+ createNamespace: dialect === "postgres" ? schemaNamespaces({ schema: params.to }).filter((namespace) => !sourceNamespaces.has(namespace)).map((namespace) => ({ kind: "createNamespace", namespace, destructive: false })) : [],
5851
5942
  dropExclusion: [],
5852
5943
  addExclusion: [],
5853
5944
  dropView: [],
@@ -5875,16 +5966,22 @@ function diffSchemas(params) {
5875
5966
  diffViews({ from, to, buckets });
5876
5967
  diffJoinTables({ from, to, buckets });
5877
5968
  diffTables({ from, to, buckets, dialect });
5969
+ const sourceModels = new Map;
5970
+ for (const model of params.from.models) {
5971
+ const key = physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect });
5972
+ if (!sourceModels.has(key))
5973
+ sourceModels.set(key, model);
5974
+ }
5878
5975
  for (const model of params.to.models) {
5879
5976
  const table = model.dbName ?? model.name;
5880
5977
  const placement = model.namespace === undefined ? {} : { namespace: model.namespace };
5881
- const previous = params.from.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect }) === physicalNameKey({ name: table, namespace: model.namespace, dialect }));
5978
+ const previous = sourceModels.get(physicalNameKey({ name: table, namespace: model.namespace, dialect }));
5882
5979
  for (const exclusion of model.exclusions ?? []) {
5883
5980
  const before = previous?.exclusions?.find((entry) => entry.name === exclusion.name);
5884
5981
  if (before?.definition === exclusion.definition)
5885
5982
  continue;
5886
5983
  if (before?.catalogDefinition)
5887
- throw new VibeError30({ code: "VIBE_MIGRATION", message: "Exclusion comparison requires normalized evidence", meta: { reason: "exclusion-inspection-unavailable", table, name: exclusion.name } });
5984
+ throw new VibeError31({ code: "VIBE_MIGRATION", message: "Exclusion comparison requires normalized evidence", meta: { reason: "exclusion-inspection-unavailable", table, name: exclusion.name } });
5888
5985
  if (before !== undefined)
5889
5986
  buckets.dropExclusion.push({ kind: "dropExclusion", table, ...placement, exclusion: before, destructive: false });
5890
5987
  buckets.addExclusion.push({ kind: "addExclusion", table, ...placement, exclusion, destructive: false });
@@ -5894,7 +5991,8 @@ function diffSchemas(params) {
5894
5991
  buckets.dropExclusion.push({ kind: "dropExclusion", table, exclusion, destructive: false });
5895
5992
  }
5896
5993
  refuseDependentEnumStorageChanges({ from, to, buckets, dialect });
5897
- return withRlsSteps({ from: params.from, to: params.to, steps: [
5994
+ refuseMysqlForeignKeyColumnRewrites({ from, to, buckets, dialect });
5995
+ return { from, to, steps: withRlsSteps({ from: params.from, to: params.to, steps: [
5898
5996
  ...buckets.createNamespace,
5899
5997
  ...renamed.steps,
5900
5998
  ...buckets.dropView,
@@ -5918,7 +6016,7 @@ function diffSchemas(params) {
5918
6016
  ...buckets.addForeignKey,
5919
6017
  ...buckets.createJoinTable,
5920
6018
  ...buckets.dropEnum
5921
- ] });
6019
+ ] }) };
5922
6020
  }
5923
6021
  function refuseScalarListColumns(params) {
5924
6022
  const { schema, dialect } = params;
@@ -5985,6 +6083,40 @@ function refuseDependentEnumStorageChanges(params) {
5985
6083
  }
5986
6084
  }
5987
6085
  }
6086
+ function refuseMysqlForeignKeyColumnRewrites(params) {
6087
+ const { from, to, buckets, dialect } = params;
6088
+ if (dialect !== "mysql")
6089
+ return;
6090
+ const droppedFks = new Set(buckets.dropForeignKey.map((step) => step.kind === "dropForeignKey" ? `${step.table}\x00${step.constraintName}` : ""));
6091
+ for (const step of buckets.alterColumn) {
6092
+ if (step.kind !== "alterColumn")
6093
+ continue;
6094
+ const collation = step.changes.includes("collation");
6095
+ const type = step.changes.includes("type") && !isMysqlVarcharWidening({ from: step.from, to: step.to });
6096
+ if (!collation && !type)
6097
+ continue;
6098
+ const joined = [...from.joinTables].some(([key, join]) => to.joinTables.has(key) && [join.a, join.b].some((end) => end.table === step.table && end.column === step.column));
6099
+ const foreign = [...from.tables].some(([tableKey, table]) => [...table.fks].some(([fkKey, fk]) => to.tables.get(tableKey)?.fks.has(fkKey) === true && !droppedFks.has(`${table.table}\x00${fk.constraintName}`) && (table.table === step.table && fk.columns.includes(step.column) || fk.refTable === step.table && fk.refColumns.includes(step.column))));
6100
+ if (!joined && !foreign)
6101
+ continue;
6102
+ throw unsupportedCapability10({
6103
+ dialect,
6104
+ feature: `${collation ? "collation" : "type"} change of "${step.table}"."${step.column}" ${joined ? "referenced by an implicit many-to-many join table" : "in a foreign key"}`,
6105
+ hint: `drop the foreign key, change both columns' ${collation ? "collation" : "type"} and re-add it in a reviewed manual migration`
6106
+ });
6107
+ }
6108
+ }
6109
+ function isMysqlVarcharWidening(params) {
6110
+ const length = (field) => {
6111
+ if (isEnumType6(field.type) || field.isList || field.isUnsupported === true)
6112
+ return null;
6113
+ const match = /^varchar\((\d+)\)$/.exec(normalizeMysqlType({ raw: mysqlColumnTypeSql({ field, enums: new Map }) }));
6114
+ return match === null ? null : Number(match[1]);
6115
+ };
6116
+ const before = length(params.from);
6117
+ const after = length(params.to);
6118
+ return before !== null && after !== null && after >= before;
6119
+ }
5988
6120
  function diffViews(params) {
5989
6121
  const { from, to, buckets } = params;
5990
6122
  for (const [name, view] of from.views) {
@@ -6140,7 +6272,7 @@ function diffOneTable(params) {
6140
6272
  from: before.field,
6141
6273
  to: column.field,
6142
6274
  changes,
6143
- destructive: narrowingType || collationChanged
6275
+ destructive: narrowingType || collationChanged || dialect === "mysql" && before.nullable && !column.nullable
6144
6276
  });
6145
6277
  }
6146
6278
  for (const [name] of previous.columns) {
@@ -6148,9 +6280,7 @@ function diffOneTable(params) {
6148
6280
  buckets.dropColumn.push({ kind: "dropColumn", table, ...placement, column: name, destructive: true });
6149
6281
  }
6150
6282
  }
6151
- const pkBefore = previous.pk.join(",");
6152
- const pkAfter = current.pk.join(",");
6153
- if (pkBefore !== pkAfter) {
6283
+ if (previous.pk.length !== current.pk.length || previous.pk.some((column, index) => column !== current.pk[index])) {
6154
6284
  if (previous.pk.length > 0) {
6155
6285
  buckets.dropPrimaryKey.push({ kind: "dropPrimaryKey", table, ...placement, destructive: false, ...previous.model.primaryKeyConstraintName === undefined ? {} : { constraintName: previous.model.primaryKeyConstraintName } });
6156
6286
  }
@@ -6170,7 +6300,7 @@ function diffOneTable(params) {
6170
6300
  if (before === expression)
6171
6301
  continue;
6172
6302
  if (before !== undefined && previous.model.checks?.some((check) => check.name === name && check.catalogExpression === true))
6173
- throw new VibeError30({ code: "VIBE_MIGRATION", message: "Unequal catalog CHECK text requires normalized evidence or explicit replacement through the live planner", meta: { reason: "check-inspection-unavailable", table, name } });
6303
+ throw new VibeError31({ code: "VIBE_MIGRATION", message: "Unequal catalog CHECK text requires normalized evidence or explicit replacement through the live planner", meta: { reason: "check-inspection-unavailable", table, name } });
6174
6304
  if (before !== undefined)
6175
6305
  buckets.dropCheck.push({ kind: "dropCheck", table, ...placement, name, destructive: false });
6176
6306
  buckets.addCheck.push({ kind: "addCheck", table, ...placement, name, expression, destructive: false });
@@ -6256,11 +6386,11 @@ function diffOneTable(params) {
6256
6386
  }
6257
6387
 
6258
6388
  // packages/migrate/src/ddl/render.ts
6259
- import { VibeError as VibeError34, sqlObjectKey as sqlObjectKey10 } from "@vibeorm/schema";
6389
+ import { VibeError as VibeError35, sqlObjectKey as sqlObjectKey10 } from "@vibeorm/schema";
6260
6390
 
6261
6391
  // packages/migrate/src/ddl/postgres.ts
6262
6392
  import { renderExclusionSetup } from "@vibeorm/sql";
6263
- import { VibeError as VibeError31, assertSqlNamespace as assertSqlNamespace3, defaultPrimaryKeyName as defaultPrimaryKeyName3, implicitJoinTableObjectNames as implicitJoinTableObjectNames2, isEnumType as isEnumType7 } from "@vibeorm/schema";
6393
+ import { VibeError as VibeError32, assertSqlNamespace as assertSqlNamespace3, defaultPrimaryKeyName as defaultPrimaryKeyName3, implicitJoinTableObjectNames as implicitJoinTableObjectNames2, isEnumType as isEnumType7 } from "@vibeorm/schema";
6264
6394
  import { postgresDialect as postgresDialect9, renderRlsPolicyPath, renderIndexColumns } from "@vibeorm/sql";
6265
6395
  function renderPostgresSteps(params) {
6266
6396
  refuseSameTransactionEnumUse({ steps: params.steps });
@@ -6297,7 +6427,7 @@ function refuseSameTransactionEnumUse(params) {
6297
6427
  const found = use(field);
6298
6428
  if (found === undefined)
6299
6429
  continue;
6300
- throw new VibeError31({
6430
+ throw new VibeError32({
6301
6431
  code: "VIBE_MIGRATION",
6302
6432
  message: `enum value "${found.value}" is added to "${found.enumName}" and used as a default on "${step.table}"."${field.dbName ?? field.name}" in the same migration \u2014 PostgreSQL cannot use an enum member before the transaction that added it commits. Split this into two migrations: the first containing only the ALTER TYPE ... ADD VALUE, the second the column change`,
6303
6433
  meta: { enum: found.enumName, value: found.value, table: step.table, column: field.dbName ?? field.name, rule: "enum-value-used-in-adding-transaction" }
@@ -6371,7 +6501,7 @@ ${lines.join(`,
6371
6501
  case "dropIndex":
6372
6502
  if (step.constraintName !== undefined) {
6373
6503
  if (step.table === undefined)
6374
- throw new VibeError31({ code: "VIBE_MIGRATION", message: "Constraint-backed index removal requires its owning table" });
6504
+ throw new VibeError32({ code: "VIBE_MIGRATION", message: "Constraint-backed index removal requires its owning table" });
6375
6505
  return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} DROP CONSTRAINT ${q2(step.constraintName)};`];
6376
6506
  }
6377
6507
  return [`DROP INDEX ${qualifiedName(step.name, step.namespace)};`];
@@ -6436,7 +6566,7 @@ function columnDefSql(params) {
6436
6566
  const { field } = params;
6437
6567
  const name = q2(field.dbName ?? field.name);
6438
6568
  if (field.default?.kind === "autoincrement" && !isIdentityColumn({ field })) {
6439
- throw new VibeError31({
6569
+ throw new VibeError32({
6440
6570
  code: "VIBE_MIGRATION",
6441
6571
  message: `autoincrement default on "${field.name}" requires a non-list Int or BigInt column`,
6442
6572
  meta: { field: field.name }
@@ -6472,7 +6602,7 @@ function renderAlterColumn(params) {
6472
6602
  }
6473
6603
  if (step.changes.includes("default") || droppedDefault) {
6474
6604
  if (step.to.default?.kind === "autoincrement") {
6475
- throw new VibeError31({
6605
+ throw new VibeError32({
6476
6606
  code: "VIBE_MIGRATION",
6477
6607
  message: `cannot add autoincrement to existing column "${step.table}"."${step.column}" \u2014 drop and recreate the column instead`,
6478
6608
  meta: { table: step.table, column: step.column }
@@ -6492,7 +6622,7 @@ function q2(name) {
6492
6622
  }
6493
6623
 
6494
6624
  // packages/migrate/src/ddl/sqlite.ts
6495
- import { VibeError as VibeError32, implicitJoinTableObjectNames as implicitJoinTableObjectNames3, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
6625
+ import { VibeError as VibeError33, implicitJoinTableObjectNames as implicitJoinTableObjectNames3, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
6496
6626
  import { sqliteDialect as sqliteDialect3 } from "@vibeorm/sql";
6497
6627
 
6498
6628
  // packages/migrate/src/ddl/drop-order.ts
@@ -6640,7 +6770,7 @@ function renderSqliteSteps(params) {
6640
6770
  continue;
6641
6771
  const awaitsNewParent = [...toNorm?.tables.get(table)?.fks.values() ?? []].some((fk) => createdTables.has(fk.refTable));
6642
6772
  if (awaitsNewParent) {
6643
- throw new VibeError32({
6773
+ throw new VibeError33({
6644
6774
  code: "VIBE_MIGRATION",
6645
6775
  message: `cannot order this sqlite migration safely: rebuilding "${table}" must happen BEFORE the dropped table(s) it references (${[
6646
6776
  ...droppedTables
@@ -6669,7 +6799,7 @@ function renderSqliteSteps(params) {
6669
6799
  switch (step.kind) {
6670
6800
  case "addExclusion":
6671
6801
  case "dropExclusion":
6672
- throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6802
+ throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6673
6803
  case "createEnum":
6674
6804
  case "dropEnum":
6675
6805
  case "addEnumValue":
@@ -6755,7 +6885,7 @@ function renderSqliteSteps(params) {
6755
6885
  case "renameColumn": {
6756
6886
  const renamed = step.kind === "renameTable" ? step.from : step.table;
6757
6887
  if (rebuildSet.has(renamed) || step.kind === "renameTable" && rebuildSet.has(step.to)) {
6758
- throw new VibeError32({
6888
+ throw new VibeError33({
6759
6889
  code: "VIBE_MIGRATION",
6760
6890
  message: `sqlite cannot rename "${renamed}" in the same migration that rebuilds it \u2014 the table-rebuild recipe is written against a single table name. Split the rename and the column/constraint change into two migrations`,
6761
6891
  meta: { reason: "rename-ambiguous", table: renamed, dialect: "sqlite" }
@@ -6809,7 +6939,7 @@ function classifyRebuilds(params) {
6809
6939
  switch (step.kind) {
6810
6940
  case "addExclusion":
6811
6941
  case "dropExclusion":
6812
- throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6942
+ throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6813
6943
  case "alterColumn":
6814
6944
  case "dropForeignKey":
6815
6945
  case "addPrimaryKey":
@@ -6870,7 +7000,7 @@ function renderTableRebuild(params) {
6870
7000
  const source = from.tables.get(table);
6871
7001
  const target = to.tables.get(table);
6872
7002
  if (source === undefined || target === undefined) {
6873
- throw new VibeError32({
7003
+ throw new VibeError33({
6874
7004
  code: "VIBE_MIGRATION",
6875
7005
  message: `cannot rebuild "${table}" \u2014 the table is missing from the ${source === undefined ? "from" : "to"} schema`,
6876
7006
  meta: { table }
@@ -7005,7 +7135,7 @@ function columnDefSql2(params) {
7005
7135
  const { field, constraintTable, enums, inlinePkAutoincrement } = params;
7006
7136
  const name = field.dbName ?? field.name;
7007
7137
  if (field.default?.kind === "autoincrement" && !inlinePkAutoincrement) {
7008
- throw new VibeError32({
7138
+ throw new VibeError33({
7009
7139
  code: "VIBE_MIGRATION",
7010
7140
  message: `autoincrement on sqlite requires "${field.name}" to be the single-column Int or BigInt primary key`,
7011
7141
  meta: { field: field.name }
@@ -7024,7 +7154,7 @@ function columnDefSql2(params) {
7024
7154
  const enumName = field.type.enum;
7025
7155
  const values = resolveEnumValues({ name: enumName, enums });
7026
7156
  if (values === undefined) {
7027
- throw new VibeError32({
7157
+ throw new VibeError33({
7028
7158
  code: "VIBE_MIGRATION",
7029
7159
  message: `enum "${enumName}" on column "${field.name}" is not declared in the schema \u2014 sqlite rendering needs the target schema's enums for the CHECK constraint`,
7030
7160
  meta: { field: field.name, enum: enumName }
@@ -7059,7 +7189,7 @@ function requireSchemas(params) {
7059
7189
  const { step, fromNorm, toNorm } = params;
7060
7190
  if (fromNorm !== undefined && toNorm !== undefined)
7061
7191
  return;
7062
- throw new VibeError32({
7192
+ throw new VibeError33({
7063
7193
  code: "VIBE_MIGRATION",
7064
7194
  message: `sqlite step "${describeStep({ step })}" requires a table rebuild \u2014 renderSteps needs the { from, to } schemas to construct it`,
7065
7195
  meta: { step: step.kind }
@@ -7070,7 +7200,7 @@ function q3(name) {
7070
7200
  }
7071
7201
 
7072
7202
  // packages/migrate/src/ddl/mysql.ts
7073
- import { VibeError as VibeError33, implicitJoinTableObjectNames as implicitJoinTableObjectNames4, unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
7203
+ import { VibeError as VibeError34, implicitJoinTableObjectNames as implicitJoinTableObjectNames4, unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
7074
7204
  import { mysqlDialect as mysqlDialect3 } from "@vibeorm/sql";
7075
7205
  function renderMysqlSteps(params) {
7076
7206
  for (const step of params.steps) {
@@ -7218,12 +7348,12 @@ ${lines.join(`,
7218
7348
  ];
7219
7349
  case "addExclusion":
7220
7350
  case "dropExclusion":
7221
- throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "mysql", feature: "exclusionConstraints" } });
7351
+ throw new VibeError34({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "mysql", feature: "exclusionConstraints" } });
7222
7352
  case "dropCheck":
7223
7353
  return [`ALTER TABLE ${q4(step.table)} DROP CHECK ${q4(step.name)};`];
7224
7354
  case "dropIndex": {
7225
7355
  if (step.table === undefined) {
7226
- throw new VibeError33({
7356
+ throw new VibeError34({
7227
7357
  code: "VIBE_MIGRATION",
7228
7358
  message: `dropIndex "${step.name}" carries no table \u2014 mysql's DROP INDEX requires one`,
7229
7359
  meta: { index: step.name }
@@ -7253,8 +7383,8 @@ ${lines.join(`,
7253
7383
  case "dropForeignKey":
7254
7384
  return [`ALTER TABLE ${q4(step.table)} DROP FOREIGN KEY ${q4(step.constraintName)};`];
7255
7385
  case "createJoinTable": {
7256
- const aType = mysqlColumnTypeSql({ field: { ...step.a.field, isList: false }, enums });
7257
- const bType = mysqlColumnTypeSql({ field: { ...step.b.field, isList: false }, enums });
7386
+ const aType = joinColumnTypeSql({ field: step.a.field, enums });
7387
+ const bType = joinColumnTypeSql({ field: step.b.field, enums });
7258
7388
  const names = implicitJoinTableObjectNames4({ table: step.table });
7259
7389
  return [
7260
7390
  `CREATE TABLE ${q4(step.table)} (
@@ -7275,12 +7405,17 @@ ${lines.join(`,
7275
7405
  return [`ALTER TABLE ${q4(step.table)} DROP PRIMARY KEY;`];
7276
7406
  }
7277
7407
  }
7408
+ function joinColumnTypeSql(params) {
7409
+ const { field, enums } = params;
7410
+ const type = mysqlColumnTypeSql({ field: { ...field, isList: false }, enums });
7411
+ return field.collation === undefined ? type : `${type} COLLATE ${q4(field.collation)}`;
7412
+ }
7278
7413
  function columnDefSql3(params) {
7279
7414
  const { field, enums } = params;
7280
7415
  const name = q4(field.dbName ?? field.name);
7281
7416
  const autoincrement = field.default?.kind === "autoincrement";
7282
7417
  if (autoincrement && field.type !== "Int" && field.type !== "BigInt") {
7283
- throw new VibeError33({
7418
+ throw new VibeError34({
7284
7419
  code: "VIBE_MIGRATION",
7285
7420
  message: `autoincrement default on "${field.name}" requires a non-list Int or BigInt column`,
7286
7421
  meta: { field: field.name }
@@ -7302,7 +7437,7 @@ function renderAlterColumn2(params) {
7302
7437
  const { step, enums } = params;
7303
7438
  assertEnumStringTransition({ from: step.from, to: step.to, dialect: "mysql", table: step.table, column: step.column });
7304
7439
  if (step.to.default?.kind === "autoincrement" && step.from.default?.kind !== "autoincrement") {
7305
- throw new VibeError33({
7440
+ throw new VibeError34({
7306
7441
  code: "VIBE_MIGRATION",
7307
7442
  message: `cannot add autoincrement to existing column "${step.table}"."${step.column}" \u2014 drop and recreate the column instead`,
7308
7443
  meta: { table: step.table, column: step.column }
@@ -7342,24 +7477,24 @@ function declaredSqlitePreservation(params) {
7342
7477
  continue;
7343
7478
  const facts = object.expected?.facts;
7344
7479
  if (facts === undefined)
7345
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "SQLite rebuild requires independently declared preservation definitions", meta: { reason: "sqlite-preservation-unavailable", identity } });
7480
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "SQLite rebuild requires independently declared preservation definitions", meta: { reason: "sqlite-preservation-unavailable", identity } });
7346
7481
  if (identity.kind === "trigger" || identity.kind === "index") {
7347
7482
  if (identity.kind === "index" && to.models.some((model) => (model.dbName ?? model.name) === identity.table.name && model.indexes.some((index) => index.name === identity.name)))
7348
7483
  continue;
7349
7484
  if (typeof facts.definition !== "string")
7350
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite restoration SQL", meta: { reason: "sqlite-preservation-unavailable", identity } });
7485
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Missing SQLite restoration SQL", meta: { reason: "sqlite-preservation-unavailable", identity } });
7351
7486
  const statements = restore.get(identity.table.name) ?? [];
7352
7487
  statements.push(facts.definition);
7353
7488
  restore.set(identity.table.name, statements);
7354
7489
  } else if (identity.kind === "column" || identity.kind === "generatedColumn") {
7355
7490
  if (typeof facts.columnDefinition !== "string")
7356
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite column clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7491
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Missing SQLite column clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7357
7492
  const fields = columns.get(identity.table.name) ?? new Map;
7358
7493
  fields.set(identity.column, { sql: facts.columnDefinition, generated: Number(facts.generated) > 1 });
7359
7494
  columns.set(identity.table.name, fields);
7360
7495
  } else if (identity.kind === "columnDefault") {
7361
7496
  if (facts.definition !== null && typeof facts.definition !== "string")
7362
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite default clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7497
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Missing SQLite default clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7363
7498
  const expression = facts.definition;
7364
7499
  to = { ...to, models: to.models.map((model) => (model.dbName ?? model.name) !== identity.table.name ? model : { ...model, fields: model.fields.map((field) => {
7365
7500
  if ((field.dbName ?? field.name) !== identity.column)
@@ -7396,7 +7531,7 @@ function renderSteps(params) {
7396
7531
  }
7397
7532
 
7398
7533
  // packages/migrate/src/extensions.ts
7399
- import { extensionDeclarations } from "@vibeorm/schema";
7534
+ import { extensionDeclarations, providerToDialect as providerToDialect4, qualifiedSqlName as qualifiedSqlName10, SQL_NAMESPACES } from "@vibeorm/schema";
7400
7535
  function collectExtensionArtifacts(params) {
7401
7536
  const { schema, dialect, extensions } = params;
7402
7537
  const setup = [];
@@ -7463,6 +7598,10 @@ function stripExtensionArtifacts(params) {
7463
7598
  const models = [];
7464
7599
  for (const model of schema.models) {
7465
7600
  const tableName = model.dbName ?? model.name;
7601
+ if (providerToDialect4({ provider: schema.datasource.provider }) === "postgres" && qualifiedSqlName10({ name: tableName, namespace: model.namespace }).namespace !== SQL_NAMESPACES.postgres) {
7602
+ models.push(model);
7603
+ continue;
7604
+ }
7466
7605
  if (tables.exact.has(tableName) || tables.prefixes.some((prefix) => tableName.startsWith(prefix))) {
7467
7606
  continue;
7468
7607
  }
@@ -7486,6 +7625,8 @@ function planMissingArtifacts(params) {
7486
7625
  return [];
7487
7626
  const modelByTable = new Map;
7488
7627
  for (const model of introspected.models) {
7628
+ if (providerToDialect4({ provider: introspected.datasource.provider }) === "postgres" && qualifiedSqlName10({ name: model.dbName ?? model.name, namespace: model.namespace }).namespace !== SQL_NAMESPACES.postgres)
7629
+ continue;
7489
7630
  modelByTable.set(model.dbName ?? model.name, model);
7490
7631
  }
7491
7632
  return artifacts.filter((artifact) => {
@@ -7644,10 +7785,10 @@ function assertNativeMigrationMetadata(params) {
7644
7785
  if (!native)
7645
7786
  return;
7646
7787
  if (!DIALECT_CAPABILITIES3[params.dialect].nativeRowLevelSecurity) {
7647
- throw new VibeError35({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Native RLS migrations are unsupported on ${params.dialect}`, meta: { feature: "nativeRowLevelSecurity", dialect: params.dialect } });
7788
+ throw new VibeError36({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Native RLS migrations are unsupported on ${params.dialect}`, meta: { feature: "nativeRowLevelSecurity", dialect: params.dialect } });
7648
7789
  }
7649
7790
  if (params.migration.rls === undefined) {
7650
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Native RLS migration files require trusted rls.from/rls.to schema metadata. SQL alone cannot prove policy ownership or drift; regenerate with preserved security metadata.", meta: { reason: "rls-migration-metadata-required", migration: params.migration.name } });
7791
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Native RLS migration files require trusted rls.from/rls.to schema metadata. SQL alone cannot prove policy ownership or drift; regenerate with preserved security metadata.", meta: { reason: "rls-migration-metadata-required", migration: params.migration.name } });
7651
7792
  }
7652
7793
  }
7653
7794
  var PG_ADVISORY_LOCK_KEY = 1447645253;
@@ -7728,12 +7869,12 @@ function generateMigration(params) {
7728
7869
  }),
7729
7870
  ...orderedMigrationObjects({ objects: to.migrationObjects ?? [] }).flatMap((object) => {
7730
7871
  if (object.dialect !== dialect)
7731
- throw new VibeError35({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}` });
7872
+ throw new VibeError36({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}` });
7732
7873
  const previous = (params.desiredState?.from.schema.migrationObjects ?? from.migrationObjects)?.find((entry) => entry.id === object.id);
7733
7874
  if (previous === undefined)
7734
7875
  return [...object.create];
7735
7876
  if (JSON.stringify(previous) !== JSON.stringify(object))
7736
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration-owned object ${object.id} changed; supply a reviewed forward SQL migration`, meta: { reason: "migration-object-replacement-required", shadowReusable: true, id: object.id } });
7877
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration-owned object ${object.id} changed; supply a reviewed forward SQL migration`, meta: { reason: "migration-object-replacement-required", shadowReusable: true, id: object.id } });
7737
7878
  return [];
7738
7879
  })
7739
7880
  ];
@@ -7759,8 +7900,8 @@ async function withMigrationLock(params) {
7759
7900
  }, (cause) => releaseLock({ executor, dialect: params.dialect }).then(() => {
7760
7901
  throw cause;
7761
7902
  }, (cleanupCause) => {
7762
- if (cause instanceof VibeError35)
7763
- throw new VibeError35({ code: cause.code, message: cause.message, meta: { ...cause.meta, cleanupCause }, cause });
7903
+ if (cause instanceof VibeError36)
7904
+ throw new VibeError36({ code: cause.code, message: cause.message, meta: { ...cause.meta, cleanupCause }, cause });
7764
7905
  throw cause;
7765
7906
  }));
7766
7907
  };
@@ -7790,11 +7931,11 @@ function assertMigrationMetadata(params) {
7790
7931
  assertModuleOwnership({ origin: migration.moduleOrigin, desiredState: migration.desiredState });
7791
7932
  }
7792
7933
  if (migration.desiredStatePending === true)
7793
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration ${migration.name} has unfinalized desired state; finalize its reviewed before/after declarations before applying`, meta: { reason: "desired-state-unfinalized", migration: migration.name } });
7934
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration ${migration.name} has unfinalized desired state; finalize its reviewed before/after declarations before applying`, meta: { reason: "desired-state-unfinalized", migration: migration.name } });
7794
7935
  if (migration.desiredState !== undefined) {
7795
7936
  assertDesiredState({ value: migration.desiredState, migration: migration.name });
7796
7937
  if (migration.desiredState.dialect !== dialect)
7797
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch", migration: migration.name } });
7938
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch", migration: migration.name } });
7798
7939
  }
7799
7940
  assertNativeMigrationMetadata({ migration, sql: migration.sql, dialect });
7800
7941
  assertExecutable({ migration, execution: migration.execution, sql: migration.sql, dialect });
@@ -7804,7 +7945,7 @@ async function applyMigrations(params) {
7804
7945
  const dialect = params.dialect ?? "postgres";
7805
7946
  const trustedReplay = isShadowReplayExecutor({ executor });
7806
7947
  if (params.history !== undefined && params.history !== "complete" && params.history !== "subset")
7807
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Unknown migration history validation mode", meta: { reason: "history-mode-invalid" } });
7948
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Unknown migration history validation mode", meta: { reason: "history-mode-invalid" } });
7808
7949
  if (params.history === "complete")
7809
7950
  assertDistinctMigrationNames({ migrations });
7810
7951
  for (const migration of migrations)
@@ -7820,7 +7961,7 @@ async function applyMigrations(params) {
7820
7961
  assertModuleUpgradeHistory({ migrations, records: moduleRecords });
7821
7962
  const namespace = await migrationNamespace({ executor, dialect });
7822
7963
  if (dialect !== "postgres" && migrations.some((migration) => migration.moduleOrigin !== undefined && moduleNamespace({ origin: migration.moduleOrigin }) !== namespace))
7823
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Module placement differs from the active database namespace", meta: { reason: "module-placement-mismatch" } });
7964
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module placement differs from the active database namespace", meta: { reason: "module-placement-mismatch" } });
7824
7965
  }
7825
7966
  if (params.history === "complete")
7826
7967
  assertCompleteHistory({ migrations, recorded, dialect });
@@ -7831,7 +7972,7 @@ async function applyMigrations(params) {
7831
7972
  });
7832
7973
  const adopted = pending.find((migration) => migration.moduleAdoption !== undefined);
7833
7974
  if (adopted !== undefined && !trustedReplay)
7834
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "An adopted source must be recorded by the verified adoption workflow, never executed by ordinary apply", meta: { reason: "module-adoption-recording-required", migration: adopted.name } });
7975
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "An adopted source must be recorded by the verified adoption workflow, never executed by ordinary apply", meta: { reason: "module-adoption-recording-required", migration: adopted.name } });
7835
7976
  if (external !== undefined && !trustedReplay)
7836
7977
  for (const migration of pending)
7837
7978
  await assertExternalMigrationMutation({ executor, migration, dialect, namespace: await migrationNamespace({ executor, dialect }), external });
@@ -7847,7 +7988,7 @@ async function applyMigrations(params) {
7847
7988
  await assertLiveModuleOwnership({ executor, origin: migration.moduleOrigin, resume: stored !== undefined });
7848
7989
  if (stored !== undefined) {
7849
7990
  if (stored.statementIndex < 0) {
7850
- throw new VibeError35({
7991
+ throw new VibeError36({
7851
7992
  code: "VIBE_MIGRATION",
7852
7993
  message: `migration "${migration.name}" is mid-rollback (${-stored.statementIndex - 1} down statement(s) done) \u2014 finish it with \`migrate down\` or repair with resolveMigration before applying`,
7853
7994
  meta: { migration: migration.name, statementIndex: stored.statementIndex }
@@ -8005,7 +8146,7 @@ async function repairMigration(params) {
8005
8146
  const { executor, name, dialect } = params;
8006
8147
  const moduleRecord = (await readModuleJournal({ executor, dialect })).get(name);
8007
8148
  if (moduleRecord?.entry !== undefined)
8008
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Module history cannot be marked applied or deleted without verified module recovery", meta: { reason: "module-resolution-required", moduleId: moduleRecord.entry.migration.moduleOrigin.moduleId } });
8149
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module history cannot be marked applied or deleted without verified module recovery", meta: { reason: "module-resolution-required", moduleId: moduleRecord.entry.migration.moduleOrigin.moduleId } });
8009
8150
  await ensureMigrationsTable({ executor, dialect });
8010
8151
  if (params.as === "applied") {
8011
8152
  const insert = `INSERT INTO ${q5(dialect, MIGRATIONS_TABLE_NAME)} (${q5(dialect, "name")}, ${q5(dialect, "checksum")}, ${q5(dialect, "statement_index")}) VALUES (${ph(dialect, 1)}, ${ph(dialect, 2)}, ${RESOLVED_STATEMENT_INDEX})`;
@@ -8027,7 +8168,7 @@ async function rollbackMigration(params) {
8027
8168
  const stored = assertRollbackRecord({ migration, stored: recorded.get(migration.name), dialect });
8028
8169
  const newest = [...recorded.keys()].at(-1);
8029
8170
  if (newest !== migration.name)
8030
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${migration.name}" is not the newest recorded migration; select rollback again`, meta: { reason: "rollback-not-latest", migration: migration.name, newest } });
8171
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration "${migration.name}" is not the newest recorded migration; select rollback again`, meta: { reason: "rollback-not-latest", migration: migration.name, newest } });
8031
8172
  await validateRollbackRecovery({ executor, migration, downSql, stored, dialect });
8032
8173
  await executeRollback({ ...params, executor, dialect, downSql, stored });
8033
8174
  } });
@@ -8045,7 +8186,7 @@ async function rollbackMigrations(params) {
8045
8186
  const target = journal.findIndex((row) => row.name === params.to);
8046
8187
  const row = journal[target];
8047
8188
  if (row?.state !== "applied")
8048
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Rollback target "${params.to}" is not a known applied migration`, meta: { reason: "rollback-target-invalid", to: params.to, state: row?.state } });
8189
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Rollback target "${params.to}" is not a known applied migration`, meta: { reason: "rollback-target-invalid", to: params.to, state: row?.state } });
8049
8190
  assertMigrationMetadata({ migration: byName.get(row.name), dialect });
8050
8191
  start = target + 1;
8051
8192
  }
@@ -8053,7 +8194,7 @@ async function rollbackMigrations(params) {
8053
8194
  for (const row of journal.slice(start).reverse()) {
8054
8195
  const migration = byName.get(row.name);
8055
8196
  if (migration === undefined)
8056
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Cannot roll back "${row.name}" without its migration file`, meta: { reason: "rollback-file-missing", migration: row.name } });
8197
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Cannot roll back "${row.name}" without its migration file`, meta: { reason: "rollback-file-missing", migration: row.name } });
8057
8198
  const stored = assertRollbackRecord({ migration, stored: recorded.get(row.name), dialect });
8058
8199
  const downSql = validateRollbackFile({ ...params, migration, dialect });
8059
8200
  plan.push({ migration, downSql, stored });
@@ -8072,21 +8213,21 @@ function validateRollbackFile(params) {
8072
8213
  const { migration, dialect } = params;
8073
8214
  assertModuleAdoption({ migration });
8074
8215
  if (migration.moduleAdoption !== undefined)
8075
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Reversal across verified module adoption is refused; it would execute destructive source down SQL against pre-existing data", meta: { reason: "module-adoption-reverse-refused", migration: migration.name } });
8216
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Reversal across verified module adoption is refused; it would execute destructive source down SQL against pre-existing data", meta: { reason: "module-adoption-reverse-refused", migration: migration.name } });
8076
8217
  if (migration.desiredStatePending === true)
8077
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Unfinalized desired state cannot be rolled back", meta: { reason: "desired-state-unfinalized", migration: migration.name } });
8218
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Unfinalized desired state cannot be rolled back", meta: { reason: "desired-state-unfinalized", migration: migration.name } });
8078
8219
  if (migration.moduleOrigin !== undefined) {
8079
8220
  assertMaterializedModuleMigration({ migration });
8080
8221
  assertModuleOwnership({ origin: migration.moduleOrigin, desiredState: migration.desiredState });
8081
8222
  }
8082
8223
  const downSql = migration.downSql;
8083
8224
  if (downSql === undefined)
8084
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" has no down.sql; roll it back manually, then repair with resolveMigration (as: "rolled-back")`, meta: { reason: "rollback-down-missing", migration: migration.name } });
8225
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" has no down.sql; roll it back manually, then repair with resolveMigration (as: "rolled-back")`, meta: { reason: "rollback-down-missing", migration: migration.name } });
8085
8226
  assertProbeMode({ dialect, probe: params.probe });
8086
8227
  if (migration.desiredState !== undefined) {
8087
8228
  assertDesiredState({ value: migration.desiredState, migration: migration.name });
8088
8229
  if (migration.desiredState.dialect !== dialect)
8089
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch" } });
8230
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch" } });
8090
8231
  assertRemovalAuthorization({ steps: diffSchemas({ from: migration.desiredState.to.schema, to: (migration.desiredState.down ?? migration.desiredState.from).schema, dialect }), previous: migration.desiredState.to, dialect, namespace: dialect === "sqlite" ? "main" : "public", ...params.acceptIntegrityLoss === undefined ? {} : { acceptIntegrityLoss: params.acceptIntegrityLoss } });
8091
8232
  }
8092
8233
  assertNativeMigrationMetadata({ migration, sql: downSql, dialect });
@@ -8099,10 +8240,10 @@ function validateRollbackFile(params) {
8099
8240
  function assertRollbackRecord(params) {
8100
8241
  const { migration, stored, dialect } = params;
8101
8242
  if (stored === undefined)
8102
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is not recorded as applied; nothing to roll back`, meta: { migration: migration.name } });
8243
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is not recorded as applied; nothing to roll back`, meta: { migration: migration.name } });
8103
8244
  assertChecksumMatches({ migration, stored: stored.checksum });
8104
8245
  if (stored.statementIndex >= 0 && (!isCompleteRecord({ dialect, migration, stored }) || stored.error !== undefined))
8105
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is only partially applied or has an unresolved error; its down mirrors the FULL up. Finish or repair with resolveMigration`, meta: { reason: "rollback-partial-up", migration: migration.name, statementIndex: stored.statementIndex } });
8246
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is only partially applied or has an unresolved error; its down mirrors the FULL up. Finish or repair with resolveMigration`, meta: { reason: "rollback-partial-up", migration: migration.name, statementIndex: stored.statementIndex } });
8106
8247
  return stored;
8107
8248
  }
8108
8249
  async function validateRollbackRecovery(params) {
@@ -8111,7 +8252,7 @@ async function validateRollbackRecovery(params) {
8111
8252
  return;
8112
8253
  const start = -stored.statementIndex - 1;
8113
8254
  if (start > downSql.length)
8114
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Rollback progress exceeds its statement count", meta: { reason: "rollback-progress-invalid", migration: migration.name } });
8255
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Rollback progress exceeds its statement count", meta: { reason: "rollback-progress-invalid", migration: migration.name } });
8115
8256
  if (dialect === "sqlite" && usesSelfManagedTransactions({ sql: downSql }))
8116
8257
  assertSelfManagedResumeSafe({ sql: downSql, start, migration: migration.name, direction: "down" });
8117
8258
  if (!marksInflight({ dialect, execution: migration.downExecution }) || start >= downSql.length)
@@ -8177,7 +8318,7 @@ async function runTransactionalDown(params) {
8177
8318
  } else {
8178
8319
  await cleanupSelfManagedFailure({ executor });
8179
8320
  }
8180
- throw new VibeError35({
8321
+ throw new VibeError36({
8181
8322
  code: "VIBE_MIGRATION",
8182
8323
  message: `rollback of "${migration.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
8183
8324
  meta: { migration: migration.name, ...timeoutDiagnostic({ error }) },
@@ -8260,7 +8401,7 @@ async function runNonTransactionalDown(params) {
8260
8401
  values: [marks ? `${INFLIGHT_PREFIX}${index}` : error instanceof Error ? error.message : String(error), migration.name]
8261
8402
  });
8262
8403
  } catch {}
8263
- throw new VibeError35({
8404
+ throw new VibeError36({
8264
8405
  code: "VIBE_MIGRATION",
8265
8406
  message: `rollback of "${migration.name}" failed at down statement ${index + 1} of ${downSql.length}: ${error instanceof Error ? error.message : String(error)}. ${marks ? "The statement outcome is unknown; inspect its effects before resolving or retrying." : "Progress is recorded; re-running the rollback resumes from this statement."}`,
8266
8407
  meta: { migration: migration.name, statementIndex: index, ...marks ? { outcome: "unknown" } : {}, ...timeoutDiagnostic({ error }) },
@@ -8322,7 +8463,7 @@ async function runTransactional(params) {
8322
8463
  } else {
8323
8464
  await cleanupSelfManagedFailure({ executor });
8324
8465
  }
8325
- throw new VibeError35({
8466
+ throw new VibeError36({
8326
8467
  code: "VIBE_MIGRATION",
8327
8468
  message: `migration "${migration.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
8328
8469
  meta: { migration: migration.name },
@@ -8418,7 +8559,7 @@ async function runNonTransactional(params) {
8418
8559
  } catch {}
8419
8560
  const sqlState = sqlStateOf(error);
8420
8561
  const timeout = sqlState === "55P03" ? "lock-timeout" : sqlState === "57014" ? "statement-timeout" : undefined;
8421
- throw new VibeError35({
8562
+ throw new VibeError36({
8422
8563
  code: "VIBE_MIGRATION",
8423
8564
  message: timeout === "lock-timeout" ? `migration "${migration.name}" gave up waiting for a lock at statement ${index + 1} of ${migration.sql.length} (lock_timeout ${String(execution?.lockTimeoutMs ?? 0)}ms, SQLSTATE 55P03). Another session holds a conflicting lock; inspect the declared operation before retrying.` : timeout === "statement-timeout" ? `migration "${migration.name}" exceeded its statement_timeout at statement ${index + 1} of ${migration.sql.length} (${String(execution?.statementTimeoutMs ?? 0)}ms, SQLSTATE 57014). Inspect the declared operation before retrying.` : `migration "${migration.name}" failed at statement ${index + 1} of ${migration.sql.length}: ${error instanceof Error ? error.message : String(error)}. ${marks ? "The statement outcome is unknown; inspect its effects before resolving or retrying." : "Progress is recorded; re-running applyMigrations resumes from this statement."}`,
8424
8565
  meta: { migration: migration.name, statementIndex: index, ...marks ? { outcome: "unknown" } : {}, ...timeout === undefined ? {} : { reason: timeout, sqlState } },
@@ -8446,7 +8587,7 @@ async function acquireLock(params) {
8446
8587
  });
8447
8588
  const acquired = Number(rows[0]?.vibe_lock) === 1;
8448
8589
  if (!acquired) {
8449
- throw new VibeError35({
8590
+ throw new VibeError36({
8450
8591
  code: "VIBE_MIGRATION",
8451
8592
  message: `could not acquire the migration lock ('${MYSQL_LOCK_NAME}') within ${MYSQL_LOCK_TIMEOUT_SECONDS}s \u2014 another migration run is in progress`,
8452
8593
  meta: { lock: MYSQL_LOCK_NAME }
@@ -8481,7 +8622,7 @@ function uncertainStatement(params) {
8481
8622
  const subject = direction === "down" ? `rollback of "${migration}"` : `migration "${migration}"`;
8482
8623
  const position = `${direction === "down" ? "down statement" : "statement"} ${index + 1} of ${statements.length}`;
8483
8624
  const why = dialect === "mysql" ? "MySQL commits every statement on its own, so nothing outside the statement records whether it finished." : "That statement declares no operation whose effect recovery can read back from the catalog.";
8484
- return new VibeError35({
8625
+ return new VibeError36({
8485
8626
  code: "VIBE_MIGRATION",
8486
8627
  message: `${subject} was interrupted while ${position} was running. ${why} ` + "It may or may not have committed, and replaying it could apply it twice, so VibeORM stops here instead of guessing. " + 'Inspect what that statement did to the database, then follow "Recovering from a stopped migration" in docs/migrations.md: ' + `clear the \`error\` column of this migration's journal row if the statement did NOT land, set \`statement_index\` to ${direction === "down" ? -(index + 2) : index + 1} if it DID, ` + "or finish the whole migration by hand and record that with `vibeorm migrate resolve --applied`. " + `The statement was: ${statements[index].slice(0, 200)}`,
8487
8628
  meta: {
@@ -8499,7 +8640,7 @@ function assertExecutable(params) {
8499
8640
  if (execution === undefined || isDefaultExecution({ execution }))
8500
8641
  return;
8501
8642
  if (dialect !== "postgres") {
8502
- throw new VibeError35({
8643
+ throw new VibeError36({
8503
8644
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8504
8645
  message: `migration "${migration.name}" declares explicit execution semantics (transaction: "${execution.transaction}"), which only the postgres runner implements \u2014 ${dialect} has no separate nontransactional regime to opt into`,
8505
8646
  meta: { reason: "nontransactional-unsupported", dialect, migration: migration.name, feature: "nontransactionalMigrations" }
@@ -8575,7 +8716,7 @@ function assertChecksumMatches(params) {
8575
8716
  const actual = checksumOf({ migration });
8576
8717
  if (stored === actual)
8577
8718
  return;
8578
- throw new VibeError35({
8719
+ throw new VibeError36({
8579
8720
  code: "VIBE_MIGRATION",
8580
8721
  message: `checksum mismatch for migration "${migration.name}" \u2014 its SQL changed after it was applied (recorded ${stored.slice(0, 12)}\u2026, current ${actual.slice(0, 12)}\u2026)`,
8581
8722
  meta: { migration: migration.name, recorded: stored, current: actual }
@@ -8586,7 +8727,7 @@ function assertResumable(params) {
8586
8727
  const actual = checksumOf({ migration });
8587
8728
  if (stored.checksum === actual)
8588
8729
  return;
8589
- throw new VibeError35({
8730
+ throw new VibeError36({
8590
8731
  code: "VIBE_MIGRATION",
8591
8732
  message: `migration "${migration.name}" is partially applied (${stored.statementIndex} statement(s) done) and its SQL has CHANGED since \u2014 refusing to resume; restore the original SQL or repair with resolveMigration`,
8592
8733
  meta: {
@@ -8606,7 +8747,7 @@ function assertDistinctMigrationNames(params) {
8606
8747
  const names = new Set;
8607
8748
  for (const migration of params.migrations) {
8608
8749
  if (names.has(migration.name))
8609
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Duplicate migration file identity "${migration.name}"`, meta: { reason: "history-duplicate-file", migration: migration.name } });
8750
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Duplicate migration file identity "${migration.name}"`, meta: { reason: "history-duplicate-file", migration: migration.name } });
8610
8751
  names.add(migration.name);
8611
8752
  }
8612
8753
  }
@@ -8614,15 +8755,15 @@ function assertCompleteHistory(params) {
8614
8755
  const byName = new Map(params.migrations.map((migration) => [migration.name, migration]));
8615
8756
  for (const name of params.recorded.keys()) {
8616
8757
  if (!byName.has(name))
8617
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Recorded migration "${name}" has no file in the complete chain`, meta: { reason: "history-unknown-file", migration: name } });
8758
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Recorded migration "${name}" has no file in the complete chain`, meta: { reason: "history-unknown-file", migration: name } });
8618
8759
  }
8619
8760
  let index = 0;
8620
8761
  for (const [name, stored] of params.recorded) {
8621
8762
  const migration = params.migrations[index];
8622
8763
  if (migration?.name !== name)
8623
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Recorded migration order is not a prefix of the supplied complete chain", meta: { reason: "history-order-conflict", migration: name, expected: migration?.name, position: index } });
8764
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Recorded migration order is not a prefix of the supplied complete chain", meta: { reason: "history-order-conflict", migration: name, expected: migration?.name, position: index } });
8624
8765
  if (stored.statementIndex >= 0 && !isCompleteRecord({ dialect: params.dialect, migration, stored }) && index !== params.recorded.size - 1)
8625
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Partial migration "${name}" precedes later recorded work`, meta: { reason: "history-partial-not-last", migration: name } });
8766
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Partial migration "${name}" precedes later recorded work`, meta: { reason: "history-partial-not-last", migration: name } });
8626
8767
  index++;
8627
8768
  }
8628
8769
  }
@@ -8631,16 +8772,16 @@ async function assertApplyHistory(params) {
8631
8772
  const byName = new Map(migrations.map((migration) => [migration.name, migration]));
8632
8773
  for (const [name, stored] of recorded) {
8633
8774
  if (stored.statementIndex < 0) {
8634
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${name}" is mid-rollback; finish or resolve it before applying`, meta: { reason: "history-rolling-back", migration: name, statementIndex: stored.statementIndex } });
8775
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration "${name}" is mid-rollback; finish or resolve it before applying`, meta: { reason: "history-rolling-back", migration: name, statementIndex: stored.statementIndex } });
8635
8776
  }
8636
8777
  const migration = byName.get(name);
8637
8778
  if (migration === undefined) {
8638
8779
  if (stored.error !== undefined)
8639
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error but no supplied file`, meta: { reason: "history-unknown-dirty", migration: name } });
8780
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error but no supplied file`, meta: { reason: "history-unknown-dirty", migration: name } });
8640
8781
  continue;
8641
8782
  }
8642
8783
  if (isCompleteRecord({ dialect, migration, stored }) && stored.error !== undefined)
8643
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error`, meta: { reason: "history-dirty", migration: name } });
8784
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error`, meta: { reason: "history-dirty", migration: name } });
8644
8785
  if (isCompleteRecord({ dialect, migration, stored }))
8645
8786
  assertChecksumMatches({ migration, stored: stored.checksum });
8646
8787
  else
@@ -8674,17 +8815,17 @@ function ph(dialect, index) {
8674
8815
  // packages/migrate/src/module-bundle.ts
8675
8816
  function defineModuleMigrations(params) {
8676
8817
  if (!Array.isArray(params.steps) || params.steps.length === 0)
8677
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "A module upgrade bundle must contain its initial revision", meta: { reason: "module-chain-invalid", moduleId: params.moduleId } });
8818
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "A module upgrade bundle must contain its initial revision", meta: { reason: "module-chain-invalid", moduleId: params.moduleId } });
8678
8819
  const migrations = [];
8679
8820
  const revisions = new Set;
8680
8821
  let previous = null;
8681
8822
  for (const step of params.steps) {
8682
8823
  if (step.previousRevision !== previous || revisions.has(step.revision))
8683
- throw new VibeError36({ 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 } });
8824
+ throw new VibeError37({ 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 } });
8684
8825
  const origin = createModuleOrigin({ moduleId: params.moduleId, dialect: params.dialect, revision: step.revision, previousRevision: step.previousRevision, contracts: step.contracts, owned: step.owned, source: step.migration });
8685
8826
  const predecessor = migrations.at(-1)?.moduleOrigin;
8686
8827
  if (predecessor !== undefined && canonicalMigrationJson({ value: predecessor.source.desiredState.to }) !== canonicalMigrationJson({ value: origin.source.desiredState.from }))
8687
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module source state differs from its predecessor", meta: { reason: "module-predecessor-state", moduleId: params.moduleId } });
8828
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module source state differs from its predecessor", meta: { reason: "module-predecessor-state", moduleId: params.moduleId } });
8688
8829
  validateMigrationReplay({ migrations: [origin.source], dialect: params.dialect, acceptSecurityChanges: true, acceptIntegrityLoss: true, removeUnmanaged: origin.source.desiredState?.unmanagedRemovals ?? [] });
8689
8830
  migrations.push(Object.freeze({ ...origin.source, moduleOrigin: origin }));
8690
8831
  revisions.add(step.revision);
@@ -8694,21 +8835,21 @@ function defineModuleMigrations(params) {
8694
8835
  }
8695
8836
  function moduleMigrationFiles(params) {
8696
8837
  if (params.bundle === null || typeof params.bundle !== "object" || !Array.isArray(params.bundle.migrations))
8697
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module bundle must contain an immutable migration chain", meta: { reason: "module-chain-invalid" } });
8838
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module bundle must contain an immutable migration chain", meta: { reason: "module-chain-invalid" } });
8698
8839
  if (!/^[A-Za-z0-9_-]+$/.test(params.prefix))
8699
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module migration prefix must be a safe local directory name", meta: { reason: "module-prefix-invalid" } });
8840
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module migration prefix must be a safe local directory name", meta: { reason: "module-prefix-invalid" } });
8700
8841
  const bundle = defineModuleMigrations({ moduleId: params.bundle.moduleId, dialect: params.bundle.dialect, steps: params.bundle.migrations.map((migration) => {
8701
8842
  const origin = migration?.moduleOrigin;
8702
8843
  if (migration.moduleAdoption !== undefined)
8703
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Local adoption provenance cannot be exported as immutable package source", meta: { reason: "module-adoption-author-source" } });
8844
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Local adoption provenance cannot be exported as immutable package source", meta: { reason: "module-adoption-author-source" } });
8704
8845
  if (origin === undefined || origin.moduleId !== params.bundle.moduleId || origin.dialect !== params.bundle.dialect)
8705
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module bundle lacks matching immutable origin metadata", meta: { reason: "module-origin-invalid" } });
8846
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module bundle lacks matching immutable origin metadata", meta: { reason: "module-origin-invalid" } });
8706
8847
  assertModuleOrigin({ value: origin });
8707
8848
  restoreModuleMigration({ migration, origin });
8708
8849
  const { name: localName, sql: localSql, downSql: localDown, moduleOrigin: localOrigin, ...actual } = migration;
8709
8850
  const { name: sourceName, sql: sourceSql, downSql: sourceDown, ...expected } = origin.source;
8710
8851
  if (localSql === undefined || localDown === undefined !== (sourceDown === undefined) || canonicalMigrationJson({ value: actual }) !== canonicalMigrationJson({ value: expected }))
8711
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module bundle metadata differs from its sealed author source", meta: { reason: "module-origin-invalid", moduleId: origin.moduleId } });
8852
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module bundle metadata differs from its sealed author source", meta: { reason: "module-origin-invalid", moduleId: origin.moduleId } });
8712
8853
  return { revision: origin.revision, previousRevision: origin.previousRevision, contracts: origin.contracts, owned: origin.owned, migration: origin.source };
8713
8854
  }) });
8714
8855
  return Object.freeze(bundle.migrations.map((migration, index) => Object.freeze({ ...migration, name: `${params.prefix}_${String(index + 1).padStart(4, "0")}` })));
@@ -8716,10 +8857,10 @@ function moduleMigrationFiles(params) {
8716
8857
 
8717
8858
  // packages/migrate/src/module-pending.ts
8718
8859
  function refusal3(params) {
8719
- return new VibeError37({ code: "VIBE_MIGRATION", message: "Module registration or immutable upgrade history conflicts with the application's declarations; review the registration and deployment sequence", meta: params });
8860
+ return new VibeError38({ code: "VIBE_MIGRATION", message: "Module registration or immutable upgrade history conflicts with the application's declarations; review the registration and deployment sequence", meta: params });
8720
8861
  }
8721
8862
  function physicalKey(params) {
8722
- return sqlObjectKey11({ identity: { kind: "table", name: qualifiedSqlName10({ name: params.entry.dbName ?? params.entry.name, namespace: params.entry.namespace }) } });
8863
+ return sqlObjectKey11({ identity: { kind: "table", name: qualifiedSqlName11({ name: params.entry.dbName ?? params.entry.name, namespace: params.entry.namespace }) } });
8723
8864
  }
8724
8865
  function composeModuleState(params) {
8725
8866
  const { current, from, to } = params;
@@ -8830,7 +8971,7 @@ function pendingModuleMigrations(params) {
8830
8971
 
8831
8972
  // packages/migrate/src/replay-reference.ts
8832
8973
  import { createHash as createHash3 } from "crypto";
8833
- import { VibeError as VibeError38, sqlObjectKey as sqlObjectKey12, sqlObjectNamespace as sqlObjectNamespace4 } from "@vibeorm/schema";
8974
+ import { VibeError as VibeError39, sqlObjectKey as sqlObjectKey12, sqlObjectNamespace as sqlObjectNamespace4 } from "@vibeorm/schema";
8834
8975
  import { getSqlDialect as getSqlDialect10, renderMigrationServerVersion, renderReplayCoverageGaps as renderReplayCoverageGaps2, renderShadowOperation as renderShadowOperation2, SHADOW_DATABASE } from "@vibeorm/sql";
8835
8976
  function migrationReferenceNamespaces(params) {
8836
8977
  return [...new Set([params.namespace, ...params.migrations.flatMap((migration) => [
@@ -8846,7 +8987,7 @@ async function captureReplayReference(params) {
8846
8987
  for (const origin of params.namespaceOrigins ?? []) {
8847
8988
  assertModuleOrigin({ value: origin });
8848
8989
  if (origin.dialect !== dialect)
8849
- throw new VibeError38({ code: "VIBE_MIGRATION", message: "Namespace context origin uses a different dialect", meta: { reason: "module-dialect-mismatch", moduleId: origin.moduleId } });
8990
+ throw new VibeError39({ code: "VIBE_MIGRATION", message: "Namespace context origin uses a different dialect", meta: { reason: "module-dialect-mismatch", moduleId: origin.moduleId } });
8850
8991
  }
8851
8992
  const namespace = await migrationNamespace({ executor, dialect });
8852
8993
  await assertCatalogVisibility({ executor, dialect, namespace });
@@ -8855,17 +8996,17 @@ async function captureReplayReference(params) {
8855
8996
  const [query] = renderShadowOperation2({ dialect: sqlDialect, operation: { kind: "markerColumns" } });
8856
8997
  const columns = await executor({ text: query.text, values: [...query.values] });
8857
8998
  if (columns.length !== 1 || columns[0]?.name !== "payload" || columns[0]?.type !== "longtext" || columns[0]?.nullable !== "NO")
8858
- throw new VibeError38({ code: "VIBE_MIGRATION", message: "Replay changed the private shadow marker; its objects cannot be excluded from reference inspection", meta: { reason: "shadow-ownership-changed" } });
8999
+ throw new VibeError39({ code: "VIBE_MIGRATION", message: "Replay changed the private shadow marker; its objects cannot be excluded from reference inspection", meta: { reason: "shadow-ownership-changed" } });
8859
9000
  }
8860
9001
  if (params.privateNamespace !== undefined) {
8861
9002
  if (dialect !== "postgres" || params.privateNamespace !== SHADOW_DATABASE.schema)
8862
- throw new VibeError38({ code: "VIBE_MIGRATION", message: "Unknown private shadow namespace", meta: { reason: "shadow-ownership-invalid" } });
9003
+ throw new VibeError39({ code: "VIBE_MIGRATION", message: "Unknown private shadow namespace", meta: { reason: "shadow-ownership-invalid" } });
8863
9004
  const [inventoryQuery] = renderShadowOperation2({ dialect: sqlDialect, operation: { kind: "inventory" } });
8864
9005
  const privateObjects = (await executor({ text: inventoryQuery.text, values: [...inventoryQuery.values] })).filter((row) => row.namespace === params.privateNamespace);
8865
9006
  const [columnsQuery] = renderShadowOperation2({ dialect: sqlDialect, operation: { kind: "markerColumns" } });
8866
9007
  const columns = await executor({ text: columnsQuery.text, values: [...columnsQuery.values] });
8867
9008
  if (columns.length !== 1 || columns[0]?.name !== "payload" || columns[0]?.type !== "text" || columns[0]?.nullable !== "NO" || privateObjects.some((row) => !(row.kind === "namespace" && row.name === SHADOW_DATABASE.schema || row.kind === "relation" && row.name === SHADOW_DATABASE.table)))
8868
- throw new VibeError38({ code: "VIBE_MIGRATION", message: "Replay changed the private shadow namespace; its objects cannot be excluded from reference inspection", meta: { reason: "shadow-ownership-changed", objects: privateObjects } });
9009
+ throw new VibeError39({ code: "VIBE_MIGRATION", message: "Replay changed the private shadow namespace; its objects cannot be excluded from reference inspection", meta: { reason: "shadow-ownership-changed", objects: privateObjects } });
8869
9010
  }
8870
9011
  const namespaces = dialect === "postgres" ? [...new Set([...migrationReferenceNamespaces({ migrations: params.migrations, namespace }), ...(params.namespaceOrigins ?? []).map((origin) => moduleNamespace({ origin }))])] : [namespace];
8871
9012
  const external = (params.external?.length ?? 0) === 0 ? undefined : await inspectExternalObjects({ executor, dialect, external: params.external });
@@ -8876,7 +9017,7 @@ async function captureReplayReference(params) {
8876
9017
  }))).flat();
8877
9018
  const gaps = migrationCoverageGaps({ gaps: coverageRows, namespace, namespaces, external });
8878
9019
  if (gaps.length > 0)
8879
- throw new VibeError38({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Replayed history contains objects whose full definitions are not represented by migration inspection. Use reviewed SQL for changes; automatic generation and verification require supported reference coverage.", meta: { feature: "replayInventoryCoverage", dialect, namespace, objects: gaps } });
9020
+ throw new VibeError39({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Replayed history contains objects whose full definitions are not represented by migration inspection. Use reviewed SQL for changes; automatic generation and verification require supported reference coverage.", meta: { feature: "replayInventoryCoverage", dialect, namespace, objects: gaps } });
8880
9021
  const rls = [...params.migrations].reverse().find((migration) => migration.rls !== undefined)?.rls?.to;
8881
9022
  const physical = dialect === "postgres" ? await introspectPostgres({ executor, namespaces: inspectedNamespaces, ...rls === undefined ? {} : { rlsSchema: rls, requireInstalledRls: true } }) : dialect === "mysql" ? await introspectMysql({ executor }) : await introspectSqlite({ executor });
8882
9023
  const privateTable = params.privateTable !== undefined && physical.models.some((model) => !model.isView && (model.dbName ?? model.name) === params.privateTable) ? params.privateTable : undefined;
@@ -8924,7 +9065,7 @@ async function captureReplayReference(params) {
8924
9065
  }
8925
9066
 
8926
9067
  // packages/migrate/src/verify-history.ts
8927
- import { VibeError as VibeError40, sqlObjectKey as sqlObjectKey13 } from "@vibeorm/schema";
9068
+ import { VibeError as VibeError41, sqlObjectKey as sqlObjectKey13 } from "@vibeorm/schema";
8928
9069
  import { getSqlDialect as getSqlDialect11, renderReplayCoverageGaps as renderReplayCoverageGaps3 } from "@vibeorm/sql";
8929
9070
 
8930
9071
  // packages/migrate/src/compare-sqlite-primary-key.ts
@@ -8995,7 +9136,7 @@ function compareSqlitePrimaryKeyRepresentations(params) {
8995
9136
  }
8996
9137
 
8997
9138
  // packages/migrate/src/compare-reference-rls.ts
8998
- import { VibeError as VibeError39 } from "@vibeorm/schema";
9139
+ import { VibeError as VibeError40 } from "@vibeorm/schema";
8999
9140
  function securityFacts(params) {
9000
9141
  return new Map(params.tables.filter((table) => table.enabled || table.force || table.policies.length > 0).map((table) => [table.table, {
9001
9142
  table: table.table,
@@ -9006,7 +9147,7 @@ function securityFacts(params) {
9006
9147
  }
9007
9148
  async function comparePostgresReferenceRls(params) {
9008
9149
  if (params.schema.rls !== undefined && params.referenceExecutor === undefined)
9009
- throw new VibeError39({ 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" } });
9150
+ throw new VibeError40({ 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" } });
9010
9151
  const expected = securityFacts({ tables: params.schema.rls === undefined ? [] : await readPostgresRls({ executor: params.referenceExecutor }) });
9011
9152
  const actual = securityFacts({ tables: await readPostgresRls({ executor: params.executor }) });
9012
9153
  const objects = [...new Set([...expected.keys(), ...actual.keys()])].sort().map((table) => {
@@ -9030,14 +9171,14 @@ function selectMigrationVerificationPrefix(params) {
9030
9171
  for (const [index, file] of params.migrations.entries()) {
9031
9172
  const previous = params.migrations[index - 1];
9032
9173
  if (seen.has(file.name) || previous !== undefined && previous.name.localeCompare(file.name) > 0) {
9033
- throw new VibeError40({ code: "VIBE_MIGRATION", message: "Verification requires a complete local migration chain in unique name order", meta: { reason: "verification-history-order", migration: file.name } });
9174
+ throw new VibeError41({ code: "VIBE_MIGRATION", message: "Verification requires a complete local migration chain in unique name order", meta: { reason: "verification-history-order", migration: file.name } });
9034
9175
  }
9035
9176
  seen.add(file.name);
9036
9177
  }
9037
9178
  for (const [index, row] of params.recorded.entries()) {
9038
9179
  const file = params.migrations[index];
9039
9180
  if (row.state !== "applied" || row.statementIndex < 0 || row.checksum === "" || file?.name !== row.name || file.desiredStatePending === true || migrationChecksum(file) !== row.checksum) {
9040
- throw new VibeError40({ 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 } });
9181
+ throw new VibeError41({ 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 } });
9041
9182
  }
9042
9183
  }
9043
9184
  return params.migrations.slice(0, params.recorded.length);
@@ -9068,7 +9209,7 @@ async function verifyMigrationReference(params) {
9068
9209
  }))).flat();
9069
9210
  const gaps = migrationCoverageGaps({ gaps: coverageRows, namespace, namespaces, external });
9070
9211
  if (gaps.length > 0)
9071
- throw new VibeError40({ 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 } });
9212
+ throw new VibeError41({ 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 } });
9072
9213
  const security = dialect === "postgres" ? await comparePostgresReferenceRls({ executor, referenceExecutor: params.referenceExecutor, schema: reference.state.schema }) : { inSync: true, objects: [] };
9073
9214
  const inspected = await verifyMigrationState({ executor, dialect, expected: reference.state, external, probe: "none", batch: true });
9074
9215
  const structural = dialect === "sqlite" ? compareSqlitePrimaryKeyRepresentations({ report: inspected }) : inspected;
@@ -9221,12 +9362,12 @@ async function mysqlComparisonFacts(params) {
9221
9362
  return { ...facts, members };
9222
9363
  }
9223
9364
  function referenceContextError() {
9224
- return new VibeError40({ 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" } });
9365
+ return new VibeError41({ 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" } });
9225
9366
  }
9226
9367
 
9227
9368
  // packages/migrate/src/module-adoption-reference.ts
9228
9369
  function refusal4(params) {
9229
- return new VibeError41({ code: "VIBE_MIGRATION", message: "Module adoption needs complete independent source and application references with compatible ownership; no target SQL or metadata was authorized", meta: params });
9370
+ return new VibeError42({ code: "VIBE_MIGRATION", message: "Module adoption needs complete independent source and application references with compatible ownership; no target SQL or metadata was authorized", meta: params });
9230
9371
  }
9231
9372
  function moduleAdoptionSource(params) {
9232
9373
  if (params.migrations.length === 0)
@@ -9336,7 +9477,7 @@ async function verifyModuleAdoptionTarget(params) {
9336
9477
  await assertLiveModuleOwnership({ executor: params.executor, origin, resume: true });
9337
9478
  const report = await verifyMigrationReference({ executor: params.executor, dialect: origin.dialect, reference: params.reference, referenceExecutor: params.referenceExecutor });
9338
9479
  if (!report.inSync)
9339
- throw new VibeError41({ code: "VIBE_MIGRATION", message: "Populated target differs from independently replayed package/application structure; no package SQL or metadata was applied", meta: { reason: "module-adoption-target-mismatch", report } });
9480
+ throw new VibeError42({ code: "VIBE_MIGRATION", message: "Populated target differs from independently replayed package/application structure; no package SQL or metadata was applied", meta: { reason: "module-adoption-target-mismatch", report } });
9340
9481
  }
9341
9482
 
9342
9483
  // packages/migrate/src/history.ts
@@ -9352,10 +9493,10 @@ async function readMigrationHistory(params) {
9352
9493
 
9353
9494
  // packages/migrate/src/module-adoption-record.ts
9354
9495
  function refusal5(params) {
9355
- return new VibeError42({ code: "VIBE_MIGRATION", message: "Module adoption cannot record an incomplete, concurrent or altered history; no package SQL was executed", meta: params });
9496
+ return new VibeError43({ code: "VIBE_MIGRATION", message: "Module adoption cannot record an incomplete, concurrent or altered history; no package SQL was executed", meta: params });
9356
9497
  }
9357
9498
  function transactionFailure(params) {
9358
- return params.cause instanceof VibeError42 ? params.cause : new VibeError42({ code: "VIBE_MIGRATION", message: "Module adoption recording failed; inspect the local provenance and journal before retrying", meta: { reason: "module-adoption-transaction-failed" }, cause: params.cause });
9499
+ return params.cause instanceof VibeError43 ? params.cause : new VibeError43({ code: "VIBE_MIGRATION", message: "Module adoption recording failed; inspect the local provenance and journal before retrying", meta: { reason: "module-adoption-transaction-failed" }, cause: params.cause });
9359
9500
  }
9360
9501
  async function recordModuleAdoption(params) {
9361
9502
  moduleAdoptionSource(params);
@@ -9418,9 +9559,9 @@ async function recordModuleAdoption(params) {
9418
9559
  await record();
9419
9560
  }).then(undefined, async (cause) => {
9420
9561
  if (committed)
9421
- throw new VibeError42({ code: "VIBE_MIGRATION", message: "All adopted revisions committed, but filesystem lease cleanup failed; inspect the remaining publication lease before retrying", meta: { reason: "module-adoption-publication-cleanup-failed", recorded: true }, cause });
9562
+ throw new VibeError43({ code: "VIBE_MIGRATION", message: "All adopted revisions committed, but filesystem lease cleanup failed; inspect the remaining publication lease before retrying", meta: { reason: "module-adoption-publication-cleanup-failed", recorded: true }, cause });
9422
9563
  await executor({ text: "ROLLBACK" }).then(undefined, (rollback) => {
9423
- throw new VibeError42({ code: "VIBE_MIGRATION", message: "Adoption recording failed and rollback also failed; the journal outcome is uncertain and must be inspected before recovery", meta: { reason: "module-adoption-rollback-failed" }, cause: new AggregateError([cause, rollback], "Adoption recording and rollback failed") });
9564
+ throw new VibeError43({ code: "VIBE_MIGRATION", message: "Adoption recording failed and rollback also failed; the journal outcome is uncertain and must be inspected before recovery", meta: { reason: "module-adoption-rollback-failed" }, cause: new AggregateError([cause, rollback], "Adoption recording and rollback failed") });
9424
9565
  });
9425
9566
  throw transactionFailure({ cause });
9426
9567
  });
@@ -9431,14 +9572,14 @@ import { randomUUID as randomUUID3 } from "crypto";
9431
9572
  import { mkdtemp, rm } from "fs/promises";
9432
9573
  import { tmpdir } from "os";
9433
9574
  import { join } from "path";
9434
- import { VibeError as VibeError44, providerToDialect as providerToDialect5, sqlObjectKey as sqlObjectKey15, unsupportedCapability as unsupportedCapability13 } from "@vibeorm/schema";
9575
+ import { VibeError as VibeError45, providerToDialect as providerToDialect6, sqlObjectKey as sqlObjectKey15, unsupportedCapability as unsupportedCapability13 } from "@vibeorm/schema";
9435
9576
  import { getSqlDialect as getSqlDialect14, renderShadowOperation as renderShadowOperation4, SHADOW_DATABASE as SHADOW_DATABASE3 } from "@vibeorm/sql";
9436
9577
 
9437
9578
  // packages/migrate/src/shadow-dedicated.ts
9438
- import { SQL_NAMESPACES, VibeError as VibeError43, providerToDialect as providerToDialect4, sqlNamespaceIssue as sqlNamespaceIssue2 } from "@vibeorm/schema";
9579
+ import { SQL_NAMESPACES as SQL_NAMESPACES2, VibeError as VibeError44, providerToDialect as providerToDialect5, sqlNamespaceIssue as sqlNamespaceIssue2 } from "@vibeorm/schema";
9439
9580
  import { getSqlDialect as getSqlDialect13, renderShadowOperation as renderShadowOperation3, SHADOW_DATABASE as SHADOW_DATABASE2 } from "@vibeorm/sql";
9440
9581
  function refuse(params) {
9441
- throw new VibeError43({ code: "VIBE_MIGRATION", message: params.message, meta: { reason: params.reason, ...params.objects === undefined ? {} : { objects: params.objects } } });
9582
+ throw new VibeError44({ code: "VIBE_MIGRATION", message: params.message, meta: { reason: params.reason, ...params.objects === undefined ? {} : { objects: params.objects } } });
9442
9583
  }
9443
9584
  function readIdentity(params) {
9444
9585
  const row = params.rows[0];
@@ -9506,7 +9647,7 @@ async function readMarker(params) {
9506
9647
  if (params.rows.length !== 1 || typeof payload !== "string")
9507
9648
  return refuse({ reason: "shadow-ownership-invalid", message: "Shadow ownership marker is missing or malformed; automatic claim/reset is refused." });
9508
9649
  const value = await Promise.resolve().then(() => JSON.parse(payload)).then((result) => result, (cause) => {
9509
- throw new VibeError43({ code: "VIBE_MIGRATION", message: "Shadow marker is not valid JSON", meta: { reason: "shadow-ownership-invalid" }, cause });
9650
+ throw new VibeError44({ code: "VIBE_MIGRATION", message: "Shadow marker is not valid JSON", meta: { reason: "shadow-ownership-invalid" }, cause });
9510
9651
  });
9511
9652
  if (value === null || typeof value !== "object" || !("version" in value) || value.version !== 1 || !("ownerKey" in value) || typeof value.ownerKey !== "string" || !("identity" in value) || typeof value.identity !== "string" || !("status" in value) || value.status !== "READY" && value.status !== "BUILDING" || !("journal" in value) || !Array.isArray(value.journal) || !value.journal.every((row) => row !== null && typeof row === "object" && ("name" in row) && typeof row.name === "string" && ("checksum" in row) && typeof row.checksum === "string"))
9512
9653
  return refuse({ reason: "shadow-ownership-invalid", message: "Shadow marker has an unsupported ownership or replay-journal shape." });
@@ -9520,8 +9661,8 @@ async function withDedicatedShadow(params) {
9520
9661
  const intendedJournal = completedJournal({ rows: params.migrations.map((migration) => ({ name: migration.name, checksum: migrationChecksum(migration), statement_index: migration.sql.length, error: null })), reason: "shadow-replay-journal-changed" });
9521
9662
  if (shadow.ownerKey.length === 0 || shadow.targetUrl.length === 0)
9522
9663
  return refuse({ reason: "shadow-identity-unavailable", message: "Reusable shadow mode requires a project owner and a resolvable deployment target for an independent identity check." });
9523
- const dialect = providerToDialect4({ provider: shadow.provider });
9524
- const declaredNamespaces = dialect === "postgres" ? migrationReferenceNamespaces({ migrations: params.migrations, namespace: SQL_NAMESPACES.postgres }).filter((namespace) => namespace !== SQL_NAMESPACES.postgres) : [];
9664
+ const dialect = providerToDialect5({ provider: shadow.provider });
9665
+ const declaredNamespaces = dialect === "postgres" ? migrationReferenceNamespaces({ migrations: params.migrations, namespace: SQL_NAMESPACES2.postgres }).filter((namespace) => namespace !== SQL_NAMESPACES2.postgres) : [];
9525
9666
  const sqlDialect = getSqlDialect13({ dialect });
9526
9667
  const execute = async (executor, operation) => {
9527
9668
  let rows = [];
@@ -9550,13 +9691,13 @@ async function withDedicatedShadow(params) {
9550
9691
  return refuse({ reason: "shadow-identity-changed", message: "Shadow identity changed after lease acquisition; reset is refused." });
9551
9692
  if (dialect === "mysql") {
9552
9693
  const grants = await execute(executor, { kind: "grants" }).then(undefined, (cause) => {
9553
- throw new VibeError43({ code: "VIBE_MIGRATION", message: "Cannot establish dedicated MySQL catalog visibility from direct grants", meta: { reason: "shadow-inventory-unavailable" }, cause });
9694
+ throw new VibeError44({ code: "VIBE_MIGRATION", message: "Cannot establish dedicated MySQL catalog visibility from direct grants", meta: { reason: "shadow-inventory-unavailable" }, cause });
9554
9695
  });
9555
9696
  assertMysqlVisibility({ rows: grants, database: identity.database });
9556
9697
  }
9557
9698
  const existing = (await execute(executor, { kind: "markerExists" })).length > 0;
9558
9699
  const inventory = await execute(executor, { kind: "inventory" }).then(undefined, (cause) => {
9559
- throw new VibeError43({ code: "VIBE_MIGRATION", message: "Cannot inspect the complete dedicated shadow inventory; no reset was attempted", meta: { reason: "shadow-inventory-unavailable" }, cause });
9700
+ throw new VibeError44({ code: "VIBE_MIGRATION", message: "Cannot inspect the complete dedicated shadow inventory; no reset was attempted", meta: { reason: "shadow-inventory-unavailable" }, cause });
9560
9701
  });
9561
9702
  const externalReferences = inventory.filter((row) => row.kind === "EXTERNAL FOREIGN KEY");
9562
9703
  if (externalReferences.length !== 0)
@@ -9609,7 +9750,7 @@ async function withDedicatedShadow(params) {
9609
9750
  if (remaining.length !== 0)
9610
9751
  return refuse({ reason: "shadow-reset-incomplete", message: "Shadow reset left objects behind. Replay was not started.", objects: remaining });
9611
9752
  const result = await params.runReference(executor, dialect === "mysql" ? SHADOW_DATABASE2.table : undefined).then((value) => ({ ok: true, value }), (error) => {
9612
- if (!(error instanceof VibeError43) || error.meta.shadowInvalid === true || error.meta.shadowReusable !== true)
9753
+ if (!(error instanceof VibeError44) || error.meta.shadowInvalid === true || error.meta.shadowReusable !== true)
9613
9754
  throw error;
9614
9755
  return { ok: false, error };
9615
9756
  });
@@ -9633,13 +9774,13 @@ async function withDedicatedShadow(params) {
9633
9774
  function finishShadowWork(params) {
9634
9775
  return Promise.resolve().then(params.run).then(async (value) => {
9635
9776
  await Promise.resolve().then(params.cleanup).then(undefined, (cause) => {
9636
- throw new VibeError44({ code: "VIBE_MIGRATION", message: "Migration reference completed but its owned resource could not be cleaned. Inspect the named artifact before explicit cleanup.", meta: { cleanupReason: "shadow-cleanup-failed", artifact: params.artifact, cleanupFailure: cause instanceof VibeError44 ? cause.code : "unavailable" }, cause });
9777
+ throw new VibeError45({ code: "VIBE_MIGRATION", message: "Migration reference completed but its owned resource could not be cleaned. Inspect the named artifact before explicit cleanup.", meta: { cleanupReason: "shadow-cleanup-failed", artifact: params.artifact, cleanupFailure: cause instanceof VibeError45 ? cause.code : "unavailable" }, cause });
9637
9778
  });
9638
9779
  return value;
9639
9780
  }, async (primary) => {
9640
9781
  const [cleanup] = await Promise.allSettled([Promise.resolve().then(params.cleanup)]);
9641
9782
  if (cleanup.status === "rejected")
9642
- throw new VibeError44({ code: primary instanceof VibeError44 ? primary.code : "VIBE_MIGRATION", message: "Migration reference failed and its owned resource could not be cleaned. Inspect the named artifact before explicit cleanup.", meta: { cleanupReason: "shadow-cleanup-failed", artifact: params.artifact, cleanupFailure: cleanup.reason instanceof VibeError44 ? cleanup.reason.code : "unavailable" }, cause: { primary, cleanup: cleanup.reason } });
9783
+ throw new VibeError45({ code: primary instanceof VibeError45 ? primary.code : "VIBE_MIGRATION", message: "Migration reference failed and its owned resource could not be cleaned. Inspect the named artifact before explicit cleanup.", meta: { cleanupReason: "shadow-cleanup-failed", artifact: params.artifact, cleanupFailure: cleanup.reason instanceof VibeError45 ? cleanup.reason.code : "unavailable" }, cause: { primary, cleanup: cleanup.reason } });
9643
9784
  throw primary;
9644
9785
  });
9645
9786
  }
@@ -9651,21 +9792,21 @@ function withShadowConnection(params) {
9651
9792
  return finishShadowWork({ artifact: params.artifact, cleanup: () => params.connection.close(), run: () => params.connection.executor.withSession === undefined ? runPinned(params.connection.executor) : params.connection.executor.withSession({ run: runPinned }) });
9652
9793
  }
9653
9794
  async function withMigrationReference(params) {
9654
- const dialect = providerToDialect5({ provider: params.shadow.provider });
9795
+ const dialect = providerToDialect6({ provider: params.shadow.provider });
9655
9796
  const external = params.external ?? params.replay?.external;
9656
9797
  const replay = { ...params.replay, external, migrations: params.migrations, dialect };
9657
9798
  validateMigrationReplay(replay);
9658
9799
  for (const origin of params.namespaceOrigins ?? []) {
9659
9800
  assertModuleOrigin({ value: origin });
9660
9801
  if (origin.dialect !== dialect)
9661
- throw new VibeError44({ code: "VIBE_MIGRATION", message: "Namespace context origin uses a different dialect", meta: { reason: "module-dialect-mismatch", moduleId: origin.moduleId } });
9802
+ throw new VibeError45({ code: "VIBE_MIGRATION", message: "Namespace context origin uses a different dialect", meta: { reason: "module-dialect-mismatch", moduleId: origin.moduleId } });
9662
9803
  }
9663
9804
  const hasAdoption = params.migrations.some((migration) => migration.moduleAdoption !== undefined);
9664
9805
  if (hasAdoption && params.shadow.provider === "mysql")
9665
9806
  throw unsupportedCapability13({ dialect: "mysql", feature: "moduleAdoptionIndependentMysqlNamespace", hint: "Independent source and application databases require a proven metadata rebase before grouped adoption replay is supported" });
9666
9807
  const adoptionShadow = params.shadow.mode === "automatic" ? params.shadow : undefined;
9667
9808
  if (hasAdoption && adoptionShadow === undefined)
9668
- throw new VibeError44({ code: "VIBE_CONFIG", message: "Adopted history requires an independent automatic package source reference; a single dedicated shadow cannot prove both histories. Configure shadowAdminUrl.", meta: { reason: "module-adoption-automatic-shadow-required" } });
9809
+ throw new VibeError45({ code: "VIBE_CONFIG", message: "Adopted history requires an independent automatic package source reference; a single dedicated shadow cannot prove both histories. Configure shadowAdminUrl.", meta: { reason: "module-adoption-automatic-shadow-required" } });
9669
9810
  const inspect = async (executor, privateTable) => {
9670
9811
  if ((external?.length ?? 0) > 0) {
9671
9812
  const ownership = await inspectExternalObjects({ executor, dialect, external });
@@ -9692,7 +9833,7 @@ async function withMigrationReference(params) {
9692
9833
  await withMigrationReference({ shadow: adoptionShadow, openConnection: params.openConnection, migrations: moduleAdoptionSource({ migrations: group }), replay: sourceReplay, run: async ({ executor: sourceExecutor, reference: source }) => {
9693
9834
  const fingerprint = moduleAdoptionReferenceFingerprint({ migrations: group, reference: source });
9694
9835
  if (group.some((migration) => migration.moduleAdoption?.referenceFingerprint !== fingerprint))
9695
- throw new VibeError44({ code: "VIBE_MIGRATION", message: "Local adoption proof does not match independently replayed immutable package source", meta: { reason: "module-adoption-reference-unverified", moduleId: first.moduleOrigin?.moduleId } });
9836
+ throw new VibeError45({ code: "VIBE_MIGRATION", message: "Local adoption proof does not match independently replayed immutable package source", meta: { reason: "module-adoption-reference-unverified", moduleId: first.moduleOrigin?.moduleId } });
9696
9837
  const application = await captureReplayReference({
9697
9838
  executor,
9698
9839
  dialect,
@@ -9739,14 +9880,14 @@ async function withMigrationReference(params) {
9739
9880
  } });
9740
9881
  }
9741
9882
  if (!("adminUrl" in params.shadow))
9742
- throw new VibeError44({ code: "VIBE_CONFIG", message: "Automatic server shadows require an explicit admin URL" });
9883
+ throw new VibeError45({ code: "VIBE_CONFIG", message: "Automatic server shadows require an explicit admin URL" });
9743
9884
  const { provider, adminUrl } = params.shadow;
9744
9885
  const url = await Promise.resolve().then(() => new URL(adminUrl)).then((value) => value, (cause) => {
9745
- throw new VibeError44({ code: "VIBE_CONFIG", message: "shadowAdminUrl must be an explicit server database URL", cause });
9886
+ throw new VibeError45({ code: "VIBE_CONFIG", message: "shadowAdminUrl must be an explicit server database URL", cause });
9746
9887
  });
9747
9888
  const expected = provider === "postgresql" ? ["postgres:", "postgresql:"] : ["mysql:"];
9748
9889
  if (!expected.includes(url.protocol) || url.hostname.length === 0 || url.pathname.length <= 1 || [...url.searchParams.keys()].some((key) => ["database", "dbname", "host", "hostaddr", "port", "user", "socketpath", "connectionstring", "uri", "url", "service"].includes(key.toLowerCase())))
9749
- throw new VibeError44({ code: "VIBE_CONFIG", message: "Shadow admin URL has an unsupported protocol or conflicting connection selector", meta: { reason: "shadow-url-ambiguous" } });
9890
+ throw new VibeError45({ code: "VIBE_CONFIG", message: "Shadow admin URL has an unsupported protocol or conflicting connection selector", meta: { reason: "shadow-url-ambiguous" } });
9750
9891
  const prefix = /^\/vibeorm_test(?:_|$)/.test(url.pathname) ? "vibeorm_test_shadow" : "vibeorm_shadow";
9751
9892
  const name = `${prefix}_${randomUUID3().replaceAll("-", "")}`;
9752
9893
  const connection = await params.openConnection({ provider, url: adminUrl });
@@ -9754,7 +9895,7 @@ async function withMigrationReference(params) {
9754
9895
  const sqlDialect = getSqlDialect14({ dialect });
9755
9896
  for (const query of renderShadowOperation4({ dialect: sqlDialect, operation: { kind: "createDatabase", name } })) {
9756
9897
  await admin({ text: query.text, values: [...query.values] }).then(undefined, (cause) => {
9757
- throw new VibeError44({ code: "VIBE_MIGRATION", message: "Shadow CREATE did not report success. No deletion was attempted; inspect the named artifact on the configured admin server before explicit cleanup.", meta: { reason: "shadow-create-outcome-unknown", artifact: name }, cause });
9898
+ throw new VibeError45({ code: "VIBE_MIGRATION", message: "Shadow CREATE did not report success. No deletion was attempted; inspect the named artifact on the configured admin server before explicit cleanup.", meta: { reason: "shadow-create-outcome-unknown", artifact: name }, cause });
9758
9899
  });
9759
9900
  }
9760
9901
  return finishShadowWork({ artifact: name, cleanup: async () => {
@@ -9769,10 +9910,10 @@ async function withMigrationReference(params) {
9769
9910
  }
9770
9911
  // packages/migrate/src/replay-objects.ts
9771
9912
  import { randomUUID as randomUUID4 } from "crypto";
9772
- import { VibeError as VibeError45, sqlObjectKey as sqlObjectKey16 } from "@vibeorm/schema";
9913
+ import { VibeError as VibeError46, sqlObjectKey as sqlObjectKey16 } from "@vibeorm/schema";
9773
9914
  import { getSqlDialect as getSqlDialect15 } from "@vibeorm/sql";
9774
9915
  function comparisonFailure(params) {
9775
- return params.cause instanceof VibeError45 ? params.cause : new VibeError45({ code: "VIBE_MIGRATION", message: "Authored custom SQL could not be safely compiled on the independent scratch reference. Review dependencies and provide an explicit SQL migration when replacement is not supported.", meta: { reason: "migration-object-comparison-unavailable" }, cause: params.cause });
9916
+ return params.cause instanceof VibeError46 ? params.cause : new VibeError46({ code: "VIBE_MIGRATION", message: "Authored custom SQL could not be safely compiled on the independent scratch reference. Review dependencies and provide an explicit SQL migration when replacement is not supported.", meta: { reason: "migration-object-comparison-unavailable" }, cause: params.cause });
9776
9917
  }
9777
9918
  function leadingCommand(params) {
9778
9919
  const { sql } = params;
@@ -9814,11 +9955,11 @@ async function compareReplayMigrationObjects(params) {
9814
9955
  if (objects.length === 0)
9815
9956
  return params.schema;
9816
9957
  if (params.reference.dialect !== "postgres")
9817
- throw new VibeError45({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Independent authored custom-object comparison requires transactional scratch DDL. Use reviewed SQL for this dialect's custom objects.", meta: { feature: "authoredCustomObjectComparison", dialect: params.reference.dialect, objects: objects.map((object) => object.identity) } });
9958
+ throw new VibeError46({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Independent authored custom-object comparison requires transactional scratch DDL. Use reviewed SQL for this dialect's custom objects.", meta: { feature: "authoredCustomObjectComparison", dialect: params.reference.dialect, objects: objects.map((object) => object.identity) } });
9818
9959
  const before = new Map(params.reference.state.definitions.map((definition) => [sqlObjectKey16({ identity: definition.identity }), definition]));
9819
9960
  for (const object of objects) {
9820
9961
  if (object.dialect !== params.reference.dialect || object.create.length === 0 || before.has(sqlObjectKey16({ identity: object.identity })) && object.drop.length === 0 || [...object.create, ...object.drop].some((sql) => /^(?:BEGIN|START|COMMIT|END|ABORT|ROLLBACK|PREPARE|VACUUM|CREATE\s+DATABASE|DROP\s+DATABASE)\b/i.test(leadingCommand({ sql }))))
9821
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Custom object cannot be safely dropped/recreated in a rollback-only shadow transaction. Supply a reviewed SQL migration.", meta: { reason: "migration-object-comparison-unavailable", identity: object.identity } });
9962
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Custom object cannot be safely dropped/recreated in a rollback-only shadow transaction. Supply a reviewed SQL migration.", meta: { reason: "migration-object-comparison-unavailable", identity: object.identity } });
9822
9963
  }
9823
9964
  const definitions = new Map;
9824
9965
  const sentinel = getSqlDialect15({ dialect: "postgres" }).quoteIdent({ name: `vibe_probe_${randomUUID4().replaceAll("-", "")}` });
@@ -9826,7 +9967,7 @@ async function compareReplayMigrationObjects(params) {
9826
9967
  await Promise.resolve().then(() => params.executor({ text: `RELEASE SAVEPOINT ${sentinel}` })).then(() => {
9827
9968
  return;
9828
9969
  }, (cause) => {
9829
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Authored SQL destroyed the scratch transaction or its savepoint. Discard this scratch reference; no comparison result can be trusted.", meta: { reason: "migration-object-transaction-lost", shadowInvalid: true }, cause });
9970
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Authored SQL destroyed the scratch transaction or its savepoint. Discard this scratch reference; no comparison result can be trusted.", meta: { reason: "migration-object-transaction-lost", shadowInvalid: true }, cause });
9830
9971
  });
9831
9972
  await params.executor({ text: `SAVEPOINT ${sentinel}` });
9832
9973
  };
@@ -9854,7 +9995,7 @@ async function compareReplayMigrationObjects(params) {
9854
9995
  for (const object of objects) {
9855
9996
  const inspected = await inspectSqlObject({ executor: params.executor, dialect: "postgres", identity: object.identity });
9856
9997
  if (inspected.state !== "present")
9857
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Authored custom SQL did not produce an inspectable object in the independent scratch probe.", meta: { reason: "migration-object-comparison-unavailable", identity: object.identity, state: inspected.state } });
9998
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Authored custom SQL did not produce an inspectable object in the independent scratch probe.", meta: { reason: "migration-object-comparison-unavailable", identity: object.identity, state: inspected.state } });
9858
9999
  definitions.set(sqlObjectKey16({ identity: object.identity }), inspected.definition);
9859
10000
  }
9860
10001
  for (const [key, expected] of before) {
@@ -9862,32 +10003,173 @@ async function compareReplayMigrationObjects(params) {
9862
10003
  continue;
9863
10004
  const inspected = await inspectSqlObject({ executor: params.executor, dialect: "postgres", identity: expected.identity });
9864
10005
  if (inspected.state !== "present" || canonicalMigrationJson({ value: inspected.definition }) !== canonicalMigrationJson({ value: expected }))
9865
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Custom scratch replacement changed a dependent object outside the declared replacement set. Supply reviewed SQL.", meta: { reason: "migration-object-dependency-unavailable", identity: expected.identity } });
10006
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Custom scratch replacement changed a dependent object outside the declared replacement set. Supply reviewed SQL.", meta: { reason: "migration-object-dependency-unavailable", identity: expected.identity } });
9866
10007
  }
9867
10008
  await checkSentinel();
9868
10009
  }).then(() => ({ ok: true }), (cause) => ({ ok: false, error: comparisonFailure({ cause }) }));
9869
- const rollback = await Promise.resolve().then(() => params.executor({ text: "ROLLBACK" })).then(() => ({ ok: true }), (cause) => ({ ok: false, error: cause instanceof VibeError45 ? cause : new VibeError45({ code: "VIBE_MIGRATION", message: "Scratch rollback failed", meta: { reason: "migration-object-rollback-failed" }, cause }) }));
10010
+ const rollback = await Promise.resolve().then(() => params.executor({ text: "ROLLBACK" })).then(() => ({ ok: true }), (cause) => ({ ok: false, error: cause instanceof VibeError46 ? cause : new VibeError46({ code: "VIBE_MIGRATION", message: "Scratch rollback failed", meta: { reason: "migration-object-rollback-failed" }, cause }) }));
9870
10011
  if (!rollback.ok)
9871
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Custom comparison scratch could not be rolled back. Discard the scratch reference and inspect the preserved execution and cleanup errors.", meta: { reason: "migration-object-rollback-failed", shadowInvalid: true, cleanupFailure: rollback.error }, cause: outcome.ok ? rollback.error : outcome.error });
10012
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Custom comparison scratch could not be rolled back. Discard the scratch reference and inspect the preserved execution and cleanup errors.", meta: { reason: "migration-object-rollback-failed", shadowInvalid: true, cleanupFailure: rollback.error }, cause: outcome.ok ? rollback.error : outcome.error });
9872
10013
  if (!outcome.ok)
9873
10014
  throw outcome.error;
9874
10015
  for (const object of objects) {
9875
10016
  const key = sqlObjectKey16({ identity: object.identity });
9876
10017
  const previous = before.get(key);
9877
10018
  if (previous !== undefined && canonicalMigrationJson({ value: previous }) !== canonicalMigrationJson({ value: definitions.get(key) }))
9878
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Authored custom object differs from replayed history. Supply a reviewed forward SQL migration; generation will not silently replace its body.", meta: { reason: "migration-object-replacement-required", shadowReusable: true, identity: object.identity, id: object.id } });
10019
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Authored custom object differs from replayed history. Supply a reviewed forward SQL migration; generation will not silently replace its body.", meta: { reason: "migration-object-replacement-required", shadowReusable: true, identity: object.identity, id: object.id } });
9879
10020
  }
9880
10021
  return { ...params.schema, migrationObjects: objects.map((object) => ({ ...object, expected: definitions.get(sqlObjectKey16({ identity: object.identity })) })) };
9881
10022
  }
9882
10023
  // packages/migrate/src/replay-plan.ts
9883
- import { VibeError as VibeError49, sqlObjectKey as sqlObjectKey18 } from "@vibeorm/schema";
10024
+ import { VibeError as VibeError51, sqlObjectKey as sqlObjectKey19 } from "@vibeorm/schema";
10025
+
10026
+ // packages/migrate/src/sqlite-fts-upgrade.ts
10027
+ import { VibeError as VibeError47, sqlObjectKey as sqlObjectKey17 } from "@vibeorm/schema";
10028
+ import { sqliteFtsUpdateUpgrade } from "@vibeorm/sql";
10029
+ var actionReceipts = new WeakMap;
10030
+ function specs(params) {
10031
+ const result = [];
10032
+ for (const group of params.collected.groups)
10033
+ for (const artifact of group.objects) {
10034
+ const owner = artifact.owns;
10035
+ const expected = artifact.expected;
10036
+ if (owner.kind !== "trigger" || artifact.create.length !== 0 || artifact.drop.length !== 0 || expected?.identity.kind !== "trigger" || expected.identity.table.namespace !== "main" || expected.format !== "catalog-v2" || expected.facts.type !== "trigger" || typeof expected.facts.definition !== "string" || expected.dependencies.length !== 0)
10037
+ continue;
10038
+ const identity = { kind: "trigger", table: { namespace: "main", name: owner.table }, name: owner.name };
10039
+ if (sqlObjectKey17({ identity }) !== sqlObjectKey17({ identity: expected.identity }))
10040
+ continue;
10041
+ const matches = [];
10042
+ for (const bundle of group.objects) {
10043
+ if (bundle.owns.kind !== "trigger" || bundle.owns.table !== owner.table || bundle.create.length !== 4 || bundle.drop.length !== 3)
10044
+ continue;
10045
+ const create = bundle.create[2];
10046
+ const parsed = sqliteFtsUpdateUpgrade({ create, installed: expected.facts.definition, table: owner.table, name: owner.name });
10047
+ if (parsed?.kind !== "tableRequired" || bundle.owns.name !== `${parsed.ftsTable}_ai`)
10048
+ continue;
10049
+ const tableArtifact = group.objects.find((entry) => entry.owns.kind === "table" && entry.owns.name === parsed.ftsTable && entry.create.length === 1);
10050
+ if (tableArtifact === undefined)
10051
+ continue;
10052
+ const declared = sqliteFtsUpdateUpgrade({ create, installed: expected.facts.definition, table: owner.table, name: owner.name, tableDefinition: tableArtifact.create[0] });
10053
+ if (declared?.kind !== "recognized" || declared.installed !== "current")
10054
+ continue;
10055
+ matches.push({ artifact, bundle, tableArtifact, identity, create, ftsTable: declared.ftsTable, currentDefinition: declared.currentDefinition });
10056
+ }
10057
+ if (matches.length === 1)
10058
+ result.push(matches[0]);
10059
+ }
10060
+ return result;
10061
+ }
10062
+ function tableIdentity(params) {
10063
+ return { kind: "table", name: { namespace: "main", name: params.spec.ftsTable } };
10064
+ }
10065
+ function unavailable2(params) {
10066
+ return { identity: params.identity, ownership: "migration", state: "unavailable", reason: params.reason, differences: [] };
10067
+ }
10068
+ async function planSqliteFtsUpdates(params) {
10069
+ const states = new Map;
10070
+ const forcedArtifacts = new Set;
10071
+ const before = [];
10072
+ const after = [];
10073
+ const checks = [];
10074
+ for (const spec of specs(params)) {
10075
+ const key = sqlObjectKey17({ identity: spec.identity });
10076
+ const live = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: spec.identity });
10077
+ const state = compareInspectedSqlObject({ identity: spec.identity, ownership: "migration", expected: spec.artifact.expected, live, dialect: "sqlite" });
10078
+ states.set(key, state);
10079
+ if (live.state === "unavailable")
10080
+ continue;
10081
+ const beforeTrigger = live.state === "present" && typeof live.definition.facts.definition === "string" ? live.definition.facts.definition : undefined;
10082
+ if (live.state === "present" && beforeTrigger === undefined)
10083
+ continue;
10084
+ const candidate = sqliteFtsUpdateUpgrade({ create: spec.create, installed: beforeTrigger ?? spec.currentDefinition, table: spec.identity.table.name, name: spec.identity.name });
10085
+ if (candidate?.kind !== "tableRequired")
10086
+ continue;
10087
+ const table = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: tableIdentity({ spec }) });
10088
+ if (table.state === "unavailable") {
10089
+ states.set(key, unavailable2({ identity: spec.identity, reason: table.reason }));
10090
+ continue;
10091
+ }
10092
+ const beforeTable = table.state === "present" && typeof table.definition.facts.definition === "string" ? table.definition.facts.definition : undefined;
10093
+ const recognized = sqliteFtsUpdateUpgrade({
10094
+ create: spec.create,
10095
+ installed: beforeTrigger ?? spec.currentDefinition,
10096
+ table: spec.identity.table.name,
10097
+ name: spec.identity.name,
10098
+ tableDefinition: table.state === "missing" ? spec.tableArtifact.create[0] : beforeTable ?? ""
10099
+ });
10100
+ if (recognized?.kind !== "recognized") {
10101
+ states.set(key, unavailable2({ identity: spec.identity, reason: "sqlite-fts-table-unverified" }));
10102
+ continue;
10103
+ }
10104
+ const installBundle = table.state === "missing" || !params.installed.triggers?.has(`${spec.ftsTable}_ai`) || !params.installed.triggers?.has(`${spec.ftsTable}_ad`);
10105
+ if (table.state === "missing")
10106
+ forcedArtifacts.add(spec.tableArtifact.id);
10107
+ if (installBundle)
10108
+ forcedArtifacts.add(spec.bundle.id);
10109
+ else if (live.state === "missing")
10110
+ after.push(spec.create, spec.bundle.create[3]);
10111
+ const legacy = live.state === "present" && recognized.installed === "legacy";
10112
+ if (legacy) {
10113
+ before.push(recognized.drop);
10114
+ if (!installBundle)
10115
+ after.push(spec.create);
10116
+ states.set(key, { ...state, state: "changed", reason: "sqlite-fts-update-upgrade" });
10117
+ }
10118
+ checks.push({ spec, beforeTrigger, beforeTable, changes: installBundle || live.state === "missing" || legacy });
10119
+ }
10120
+ return { states, forcedArtifacts, before, after, checks };
10121
+ }
10122
+ function retainSqliteFtsPlan(params) {
10123
+ if (params.fts.checks.length > 0)
10124
+ actionReceipts.set(params.plan, { fts: params.fts, ...params.transaction === undefined ? {} : { transaction: params.transaction } });
10125
+ }
10126
+ function sqliteFtsTransaction(params) {
10127
+ return actionReceipts.get(params.plan)?.transaction;
10128
+ }
10129
+ function stateChanged(params) {
10130
+ return new VibeError47({ code: "VIBE_MIGRATION", message: "SQLite FTS state changed after planning; re-plan before replacing synchronization", meta: { reason: "sqlite-fts-state-changed", identity: params.identity } });
10131
+ }
10132
+ async function assertSqliteFtsApplyState(params) {
10133
+ const receipt = actionReceipts.get(params.plan)?.fts;
10134
+ if (receipt === undefined)
10135
+ return;
10136
+ for (const check of receipt.checks) {
10137
+ if (!check.changes)
10138
+ continue;
10139
+ for (const observation of [{ identity: check.spec.identity, definition: check.beforeTrigger }, { identity: tableIdentity({ spec: check.spec }), definition: check.beforeTable }]) {
10140
+ const live = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: observation.identity });
10141
+ if (observation.definition === undefined ? live.state !== "missing" : live.state !== "present" || live.definition.facts.definition !== observation.definition)
10142
+ throw stateChanged({ identity: observation.identity });
10143
+ }
10144
+ }
10145
+ }
10146
+ async function verifySqliteFtsApplyState(params) {
10147
+ const receipt = actionReceipts.get(params.plan)?.fts;
10148
+ if (receipt === undefined)
10149
+ return;
10150
+ for (const { spec } of receipt.checks) {
10151
+ const live = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: spec.identity });
10152
+ const expected = { identity: spec.identity, format: "catalog-v2", facts: { definition: spec.currentDefinition, type: "trigger" }, dependencies: [] };
10153
+ const compared = compareInspectedSqlObject({ identity: spec.identity, ownership: "migration", expected, live, dialect: "sqlite" });
10154
+ if (compared.state !== "matching")
10155
+ throw new VibeError47({ code: "VIBE_MIGRATION", message: "SQLite FTS UPDATE synchronization did not match its independent definition after apply", meta: { reason: "migration-state-unverified", objects: [compared] } });
10156
+ const table = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: tableIdentity({ spec }) });
10157
+ if (table.state !== "present" || typeof table.definition.facts.definition !== "string" || sqliteFtsUpdateUpgrade({ create: spec.create, installed: spec.currentDefinition, table: spec.identity.table.name, name: spec.identity.name, tableDefinition: table.definition.facts.definition })?.kind !== "recognized")
10158
+ throw stateChanged({ identity: tableIdentity({ spec }) });
10159
+ for (const suffix of ["ai", "ad"]) {
10160
+ const identity = { kind: "trigger", table: spec.identity.table, name: `${spec.ftsTable}_${suffix}` };
10161
+ if ((await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity })).state !== "present")
10162
+ throw stateChanged({ identity });
10163
+ }
10164
+ }
10165
+ }
9884
10166
 
9885
10167
  // packages/migrate/src/planner.ts
9886
- import { VibeError as VibeError48, sqlObjectKey as sqlObjectKey17, qualifiedSqlName as qualifiedSqlName12 } from "@vibeorm/schema";
10168
+ import { VibeError as VibeError50, sqlObjectKey as sqlObjectKey18, qualifiedSqlName as qualifiedSqlName13 } from "@vibeorm/schema";
9887
10169
  import { renderSqliteRebuildObjects, sqliteDialect as sqliteDialect4 } from "@vibeorm/sql";
9888
10170
 
9889
10171
  // packages/migrate/src/artifact-probe.ts
9890
- import { VibeError as VibeError46 } from "@vibeorm/schema";
10172
+ import { VibeError as VibeError48 } from "@vibeorm/schema";
9891
10173
  var SAVEPOINT = "vibeorm_artifact_probe";
9892
10174
  async function probeChangedArtifacts(params) {
9893
10175
  const { executor, namespace } = params;
@@ -9910,7 +10192,7 @@ async function probeChangedArtifacts(params) {
9910
10192
  await executor({ text: `RELEASE SAVEPOINT ${SAVEPOINT}` });
9911
10193
  }
9912
10194
  if (after.state !== "present")
9913
- throw new VibeError46({ 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 } });
10195
+ throw new VibeError48({ 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 } });
9914
10196
  if (canonicalMigrationJson({ value: before.definition.facts }) === canonicalMigrationJson({ value: after.definition.facts }))
9915
10197
  continue;
9916
10198
  const definition = before.definition.facts.definition;
@@ -9920,8 +10202,8 @@ async function probeChangedArtifacts(params) {
9920
10202
  }
9921
10203
 
9922
10204
  // packages/migrate/src/index-predicates.ts
9923
- import { VibeError as VibeError47 } from "@vibeorm/schema";
9924
- import { defaultIndexName as defaultIndexName3, qualifiedSqlName as qualifiedSqlName11 } from "@vibeorm/schema";
10205
+ import { VibeError as VibeError49 } from "@vibeorm/schema";
10206
+ import { defaultIndexName as defaultIndexName3, qualifiedSqlName as qualifiedSqlName12 } from "@vibeorm/schema";
9925
10207
  function physicalIndexName(params) {
9926
10208
  return params.index.name ?? defaultIndexName3({ table: params.table, columns: params.columns, unique: params.index.kind === "unique", dialect: "postgres" });
9927
10209
  }
@@ -9948,7 +10230,7 @@ async function prepareIndexPredicateDiff(params) {
9948
10230
  indexes.push(index);
9949
10231
  continue;
9950
10232
  }
9951
- const deparsed = await deparseIndexPredicate({ executor: params.executor, table: qualifiedSqlName11({ name: table, namespace: live.namespace, defaultNamespace: params.namespace }), where: next.where });
10233
+ const deparsed = await deparseIndexPredicate({ executor: params.executor, table: qualifiedSqlName12({ name: table, namespace: live.namespace, defaultNamespace: params.namespace }), where: next.where });
9952
10234
  indexes.push(deparsed === index.where ? { ...index, where: next.where } : index);
9953
10235
  }
9954
10236
  models.push({ ...live, indexes });
@@ -9961,7 +10243,7 @@ async function deparseIndexPredicate(params) {
9961
10243
  const match = /^CHECK \((.*)\)$/s.exec(definition);
9962
10244
  return match === null ? undefined : match[1];
9963
10245
  } catch (error) {
9964
- if (error instanceof VibeError47 && error.code === "VIBE_MIGRATION")
10246
+ if (error instanceof VibeError49 && error.code === "VIBE_MIGRATION")
9965
10247
  throw error;
9966
10248
  return;
9967
10249
  }
@@ -9985,19 +10267,23 @@ async function planSchemaMigration(params) {
9985
10267
  assertRetainedMigrationObjects({ from: (previous?.schema.migrationObjects ?? []).filter((object) => object.identity.kind !== "namespace" || !schemaNamespaces({ schema }).includes(object.identity.name)), to: objects });
9986
10268
  for (const object of objects)
9987
10269
  if (object.dialect !== dialect)
9988
- throw new VibeError48({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}`, meta: { feature: "migrationObject", dialect } });
10270
+ throw new VibeError50({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}`, meta: { feature: "migrationObject", dialect } });
9989
10271
  const physical = params.physical ?? (dialect === "postgres" ? await introspectPhysicalPostgres({ executor, namespaces }) : dialect === "mysql" ? await introspectMysql({ executor }) : await introspectSqlite({ executor }));
9990
10272
  const comparablePhysical = external === undefined ? physical : projectExternalSchema({ schema: physical, external });
9991
10273
  if (previous !== undefined)
9992
10274
  assertMigrationState({ report: await verifyMigrationState({ executor, expected: previous, dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
9993
10275
  const inventory = params.physicalInventory ?? external?.inventory ?? (await Promise.all((dialect === "postgres" ? namespaces : [namespace]).map((scope) => inspectMigrationInventory({ executor, dialect, namespace: scope })))).flat();
9994
10276
  const comparisonInventory = external === undefined ? inventory : projectExternalInventory({ inventory, external });
9995
- const visible = normalizeSchema({ schema: physical, dialect });
10277
+ const visibleModels = new Set(physical.models.map((model) => physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect })));
10278
+ let visible;
9996
10279
  for (const object of inventory) {
9997
10280
  const identity = object.identity;
9998
- if (identity.kind !== "table" || physical.models.some((model) => physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect }) === physicalNameKey({ ...identity.name, dialect })) || visible.joinTables.has(physicalNameKey({ ...identity.name, dialect })))
10281
+ if (identity.kind !== "table" || visibleModels.has(physicalNameKey({ ...identity.name, dialect })))
10282
+ continue;
10283
+ visible ??= normalizeSchema({ schema: physical, dialect });
10284
+ if (visible.joinTables.has(physicalNameKey({ ...identity.name, dialect })))
9999
10285
  continue;
10000
- throw new VibeError48({ 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 } });
10286
+ throw new VibeError50({ 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 } });
10001
10287
  }
10002
10288
  const states = resolved.unavailable.map((identity) => {
10003
10289
  const exists = identity.kind === "column" && [physical, previous?.schema].some((source) => source?.models.some((model) => physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect }) === physicalNameKey({ ...identity.table, dialect }) && model.fields.some((field) => (field.dbName ?? field.name) === identity.column)));
@@ -10008,13 +10294,13 @@ async function planSchemaMigration(params) {
10008
10294
  const creates = [];
10009
10295
  for (const object of objects) {
10010
10296
  for (const dependency of object.dependsOn) {
10011
- if ([...objects.map((entry) => entry.identity), ...schemaObjectIdentities({ schema, dialect, namespace })].some((identity) => sqlObjectKey17({ identity }) === sqlObjectKey17({ identity: dependency })))
10297
+ if ([...objects.map((entry) => entry.identity), ...schemaObjectIdentities({ schema, dialect, namespace })].some((identity) => sqlObjectKey18({ identity }) === sqlObjectKey18({ identity: dependency })))
10012
10298
  continue;
10013
10299
  const state = await inspectSqlObject({ executor, dialect, identity: dependency });
10014
10300
  if (state.state !== "present")
10015
10301
  states.push({ identity: dependency, ownership: "migration", state: "unavailable", reason: "dependency-unavailable", differences: [] });
10016
10302
  }
10017
- const expected = object.expected ?? previous?.definitions.find((entry) => sqlObjectKey17({ identity: entry.identity }) === sqlObjectKey17({ identity: object.identity }));
10303
+ const expected = object.expected ?? previous?.definitions.find((entry) => sqlObjectKey18({ identity: entry.identity }) === sqlObjectKey18({ identity: object.identity }));
10018
10304
  const live = await inspectSqlObject({ executor, dialect, identity: object.identity });
10019
10305
  if (object.identity.kind === "namespace") {
10020
10306
  states.push({ identity: object.identity, ownership: "schema", state: live.state === "present" ? "matching" : live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} });
@@ -10026,14 +10312,21 @@ async function planSchemaMigration(params) {
10026
10312
  states.push({ identity: object.identity, ownership: "migration", state: "missing", differences: [] });
10027
10313
  creates.push(...object.create);
10028
10314
  } else {
10029
- const report = await verifyMigrationState({ executor, dialect, expected: { schema: { ...schema, models: [], migrationObjects: [object] }, definitions: [expected] } });
10030
- states.push(...report.objects);
10315
+ states.push(compareInspectedSqlObject({ identity: object.identity, ownership: "migration", expected, live, dialect }));
10031
10316
  }
10032
10317
  }
10318
+ for (const object of legacyObjects)
10319
+ if (objects.some((entry) => sqlObjectKey18({ identity: entry.identity }) === sqlObjectKey18({ identity: object.identity })))
10320
+ throw new VibeError50({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate combined and migration-only ownership", meta: { identity: object.identity } });
10321
+ const installed = await probeInstalledExtensionState({ executor, dialect, artifacts });
10322
+ const sqliteFts = dialect === "sqlite" && collected.objects.some((artifact) => artifact.owns.kind === "trigger" && artifact.create.length === 0 && artifact.drop.length === 0 && artifact.expected !== undefined) ? await planSqliteFtsUpdates({ executor, collected, installed }) : undefined;
10033
10323
  for (const object of legacyObjects) {
10034
- if (objects.some((entry) => sqlObjectKey17({ identity: entry.identity }) === sqlObjectKey17({ identity: object.identity })))
10035
- throw new VibeError48({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate combined and migration-only ownership", meta: { identity: object.identity } });
10036
- const expected = object.expected ?? previous?.definitions.find((entry) => sqlObjectKey17({ identity: entry.identity }) === sqlObjectKey17({ identity: object.identity }));
10324
+ const qualifiedFts = sqliteFts?.states.get(sqlObjectKey18({ identity: object.identity }));
10325
+ if (qualifiedFts !== undefined) {
10326
+ states.push(qualifiedFts);
10327
+ continue;
10328
+ }
10329
+ const expected = object.expected ?? previous?.definitions.find((entry) => sqlObjectKey18({ identity: entry.identity }) === sqlObjectKey18({ identity: object.identity }));
10037
10330
  if (expected === undefined)
10038
10331
  states.push({ identity: object.identity, ownership: "migration", state: "unavailable", reason: "legacy-definition-required", differences: [] });
10039
10332
  else {
@@ -10041,7 +10334,7 @@ async function planSchemaMigration(params) {
10041
10334
  if (live.state === "missing")
10042
10335
  states.push({ identity: object.identity, ownership: "migration", state: "missing", differences: [] });
10043
10336
  else
10044
- states.push(...(await verifyMigrationState({ executor, dialect, expected: { schema: { ...schema, models: [], migrationObjects: [object] }, definitions: [expected] } })).objects);
10337
+ states.push(compareInspectedSqlObject({ identity: object.identity, ownership: "migration", expected, live, dialect }));
10045
10338
  }
10046
10339
  }
10047
10340
  const protectedObjects = [...legacyObjects, ...objects, ...previous?.schema.migrationObjects ?? []];
@@ -10049,16 +10342,17 @@ async function planSchemaMigration(params) {
10049
10342
  const checked = await prepareCheckDiff({ executor, from: stripped, to: schema, dialect, ...params.probe === undefined ? {} : { probe: params.probe }, ...previous === undefined ? {} : { previous }, ...params.replaceChecks === undefined ? {} : { replaceChecks: params.replaceChecks } });
10050
10343
  const exclusionsPrepared = await prepareExclusionDiff({ ...params, ...previous === undefined ? {} : { previous }, from: checked, to: schema });
10051
10344
  const from = dialect === "postgres" ? await prepareOperatorClassDiff({ executor, from: await prepareIndexPredicateDiff({ executor, from: exclusionsPrepared, to: schema, namespace, ...params.probe === undefined ? {} : { probe: params.probe } }), to: schema }) : exclusionsPrepared;
10052
- const steps = diffSchemas({ from, to: schema, dialect });
10053
- const known = new Set(schemaObjectIdentities({ schema: previous?.schema ?? schema, dialect, namespace }).map((identity) => sqlObjectKey17({ identity })));
10054
- const declaredIndexes = normalizeSchema({ schema: previous?.schema ?? schema, dialect });
10345
+ const comparison = diffSchemasWithNormalization({ from, to: schema, dialect });
10346
+ const steps = comparison.steps;
10347
+ const declaredIndexes = previous === undefined ? comparison.to : normalizeSchema({ schema: previous.schema, dialect });
10348
+ const known = new Set(schemaObjectIdentitiesFromNormalized({ schema: previous?.schema ?? schema, dialect, namespace, normalized: declaredIndexes }).map((identity) => sqlObjectKey18({ identity })));
10055
10349
  const physicalIndexes = normalizeSchema({ schema: physical, dialect });
10056
10350
  for (const [table, model] of physicalIndexes.tables)
10057
10351
  for (const [key, index] of model.indexes) {
10058
10352
  if (index.constraintName !== undefined && declaredIndexes.tables.get(table)?.indexes.has(key))
10059
- known.add(sqlObjectKey17({ identity: { kind: "constraint", constraintType: "unique", table: qualifiedSqlName12({ name: model.table, namespace: model.namespace, defaultNamespace: namespace }), name: index.constraintName } }));
10353
+ known.add(sqlObjectKey18({ identity: { kind: "constraint", constraintType: "unique", table: qualifiedSqlName13({ name: model.table, namespace: model.namespace, defaultNamespace: namespace }), name: index.constraintName } }));
10060
10354
  }
10061
- const migrationOwned = new Set(protectedObjects.map((object) => sqlObjectKey17({ identity: object.identity })));
10355
+ const migrationOwned = new Set(protectedObjects.map((object) => sqlObjectKey18({ identity: object.identity })));
10062
10356
  const removals = steps.flatMap((step) => {
10063
10357
  const identity = removalIdentity({ step, namespace });
10064
10358
  return identity === undefined ? [] : [identity];
@@ -10067,30 +10361,30 @@ async function planSchemaMigration(params) {
10067
10361
  for (const identity of removals) {
10068
10362
  const owner = externalObjectOwner({ external, identity });
10069
10363
  if (owner !== undefined)
10070
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Automatic DDL cannot remove externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
10364
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Automatic DDL cannot remove externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
10071
10365
  }
10072
10366
  const enumRemovals = new Set(removals.flatMap((identity) => identity.kind === "enum" ? [physicalNameKey({ ...identity.name, dialect })] : []));
10073
10367
  if (external !== undefined)
10074
10368
  await refuseExternalDependents({ executor, dialect, external, steps, namespace });
10075
10369
  const droppedTables = new Set(steps.flatMap((step) => step.kind === "dropTable" || step.kind === "dropJoinTable" ? [physicalNameKey({ name: step.table, namespace: step.namespace, dialect })] : []));
10076
10370
  const unmanagedRemovals = [];
10077
- const invalid = dialect === "sqlite" ? sqliteInvalidatedTables({ steps, from: physical }) : { rebuilt: new Set, dropped: new Set };
10371
+ const invalid = dialect === "sqlite" && steps.length > 0 ? sqliteInvalidatedTables({ steps, from: physical }) : { rebuilt: new Set, dropped: new Set };
10078
10372
  if (dialect === "sqlite")
10079
10373
  await assertSqliteRebuildTableOptions({ executor, tables: invalid.rebuilt });
10080
10374
  for (const object of inventory) {
10081
- const key = sqlObjectKey17({ identity: object.identity });
10375
+ const key = sqlObjectKey18({ identity: object.identity });
10082
10376
  const normalizedOwner = object.backingConstraint === undefined ? undefined : inventory.find((entry) => entry.identity.kind === "constraint" && entry.identity.name === object.backingConstraint && ("table" in object.identity) && entry.identity.table.name === object.identity.table.name && entry.identity.table.namespace === object.identity.table.namespace);
10083
- const ownership = migrationOwned.has(key) ? "migration" : known.has(key) || normalizedOwner !== undefined && known.has(sqlObjectKey17({ identity: normalizedOwner.identity })) ? "schema" : "unknown";
10377
+ const ownership = migrationOwned.has(key) ? "migration" : known.has(key) || normalizedOwner !== undefined && known.has(sqlObjectKey18({ identity: normalizedOwner.identity })) ? "schema" : "unknown";
10084
10378
  const externalOwner = external === undefined ? undefined : externalObjectOwner({ identity: object.identity, external });
10085
10379
  if (object.inspectionUnavailable !== undefined)
10086
10380
  states.push({ identity: object.identity, ownership, state: "unavailable", reason: object.inspectionUnavailable, differences: [] });
10087
10381
  if (object.inspectionUnavailable !== undefined && invalid.rebuilt.has("table" in object.identity ? object.identity.table.name : typeof object.identity.name === "object" ? object.identity.name.name : object.identity.name))
10088
- throw new VibeError48({ 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 } });
10382
+ throw new VibeError50({ 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 } });
10089
10383
  if (ownership === "unknown" && comparisonInventory.includes(object))
10090
10384
  states.push({ identity: object.identity, ownership, state: "unavailable", reason: "unmanaged", differences: [] });
10091
10385
  let collateral = "table" in object.identity && droppedTables.has(physicalNameKey({ ...object.identity.table, dialect }));
10092
- const declared = protectedObjects.find((entry) => sqlObjectKey17({ identity: entry.identity }) === key);
10093
- if (declared?.dependsOn.some((dependency) => removals.some((identity) => sqlObjectKey17({ identity }) === sqlObjectKey17({ identity: dependency })) || ("table" in dependency) && droppedTables.has(physicalNameKey({ ...dependency.table, dialect }))))
10386
+ const declared = protectedObjects.find((entry) => sqlObjectKey18({ identity: entry.identity }) === key);
10387
+ if (declared?.dependsOn.some((dependency) => removals.some((identity) => sqlObjectKey18({ identity }) === sqlObjectKey18({ identity: dependency })) || ("table" in dependency) && droppedTables.has(physicalNameKey({ ...dependency.table, dialect }))))
10094
10388
  collateral = true;
10095
10389
  if (ownership === "unknown" && object.identity.kind === "function" && steps.some((step) => step.kind === "dropTable" || step.kind === "dropColumn" || step.kind === "dropJoinTable" || step.kind === "dropEnum"))
10096
10390
  states.push({ identity: object.identity, ownership, state: "unavailable", reason: "dependency-inspection-unavailable", differences: [] });
@@ -10104,7 +10398,7 @@ async function planSchemaMigration(params) {
10104
10398
  for (const address of addresses) {
10105
10399
  const value = JSON.parse(String(address));
10106
10400
  if (!Array.isArray(value) || !Array.isArray(value[2]))
10107
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency address" });
10401
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency address" });
10108
10402
  const names = value[2];
10109
10403
  if (typeof names[0] !== "string" || typeof names[1] !== "string")
10110
10404
  continue;
@@ -10116,22 +10410,23 @@ async function planSchemaMigration(params) {
10116
10410
  }
10117
10411
  }
10118
10412
  if (dialect === "sqlite" && ownership === "unknown" && object.identity.kind === "generatedColumn" && invalid.rebuilt.has(object.identity.table.name))
10119
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "SQLite generated-column preservation requires an independent ownership declaration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
10120
- const removed = collateral || removals.some((identity) => sqlObjectKey17({ identity }) === key);
10413
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "SQLite generated-column preservation requires an independent ownership declaration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
10414
+ const removed = collateral || removals.some((identity) => sqlObjectKey18({ identity }) === key);
10121
10415
  if (removed && externalOwner !== undefined)
10122
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Automatic or collateral DDL cannot remove externally owned objects", meta: { reason: "external-owned-removal", owner: externalOwner, identity: object.identity } });
10416
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Automatic or collateral DDL cannot remove externally owned objects", meta: { reason: "external-owned-removal", owner: externalOwner, identity: object.identity } });
10123
10417
  if (removed && ownership === "migration")
10124
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Automatic DDL would remove a migration-owned object", meta: { reason: "migration-owned-removal", identity: object.identity } });
10418
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Automatic DDL would remove a migration-owned object", meta: { reason: "migration-owned-removal", identity: object.identity } });
10125
10419
  if (removed && ownership === "unknown" && comparisonInventory.includes(object) && object.backingConstraint === undefined)
10126
10420
  unmanagedRemovals.push(object.identity);
10127
10421
  }
10128
10422
  const invalidated = new Set([...invalid.rebuilt, ...invalid.dropped]);
10129
- const installed = await probeInstalledExtensionState({ executor, dialect, artifacts });
10130
10423
  const triggers = installed.triggers === undefined ? undefined : new Set(installed.triggers);
10131
10424
  for (const artifact of artifacts)
10132
10425
  if (artifact.owns.kind === "trigger" && invalidated.has(artifact.owns.table))
10133
10426
  triggers?.delete(artifact.owns.name);
10134
- const missing = planMissingArtifacts({ introspected: physical, artifacts, installed: { ...installed, ...triggers === undefined ? {} : { triggers } } });
10427
+ const absentArtifacts = planMissingArtifacts({ introspected: physical, artifacts, installed: { ...installed, ...triggers === undefined ? {} : { triggers } } });
10428
+ const absentIds = new Set(absentArtifacts.map((artifact) => artifact.id));
10429
+ const missing = artifacts.filter((artifact) => absentIds.has(artifact.id) || sqliteFts?.forcedArtifacts.has(artifact.id));
10135
10430
  const declaredPrevious = params.previousDeclarations ?? previous?.schema;
10136
10431
  const previousDeclarations = declaredPrevious === undefined || external === undefined ? declaredPrevious : projectExternalSchema({ schema: declaredPrevious, external });
10137
10432
  const previousArtifacts = previousDeclarations === undefined ? undefined : collectExtensionArtifacts({ schema: previousDeclarations, dialect, extensions: params.extensions ?? [] });
@@ -10169,11 +10464,11 @@ async function planSchemaMigration(params) {
10169
10464
  continue;
10170
10465
  const live = await inspectSqlObject({ executor, dialect, identity });
10171
10466
  if (live.state !== "present" || identity.kind !== "columnDefault" && typeof live.definition.facts.columnDefinition !== "string")
10172
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Cannot read the verified SQLite column definition for preservation", meta: { reason: "sqlite-preservation-unavailable", identity } });
10467
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Cannot read the verified SQLite column definition for preservation", meta: { reason: "sqlite-preservation-unavailable", identity } });
10173
10468
  if (identity.kind === "columnDefault") {
10174
10469
  const definition = live.definition.facts.definition;
10175
10470
  if (definition !== null && typeof definition !== "string")
10176
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Cannot preserve SQLite default", meta: { identity } });
10471
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Cannot preserve SQLite default", meta: { identity } });
10177
10472
  const defaults = preservedDefaults.get(identity.table.name) ?? new Map;
10178
10473
  defaults.set(identity.column, definition);
10179
10474
  preservedDefaults.set(identity.table.name, defaults);
@@ -10194,25 +10489,31 @@ async function planSchemaMigration(params) {
10194
10489
  const expression = defaults.get(column);
10195
10490
  return { ...rest, ...typeof expression === "string" ? { default: { kind: "dbgenerated", sql: expression } } : {} };
10196
10491
  }) })) };
10197
- const core = renderSteps({ steps, dialect, from: dialect === "sqlite" ? physical : from, to: dialect === "mysql" ? { ...target, migrationObjects: protectedObjects } : target, sqliteRestore: restore, sqliteColumns: preservedColumns });
10492
+ const core = steps.length === 0 ? [] : renderSteps({ steps, dialect, from: dialect === "sqlite" ? physical : from, to: dialect === "mysql" ? { ...target, migrationObjects: protectedObjects } : target, sqliteRestore: restore, sqliteColumns: preservedColumns });
10198
10493
  const planned = new Set([...missing, ...rebuilt].map((artifact) => artifact.id));
10199
- const sql = [
10200
- ...artifactCreateSql({ artifacts: missing.filter((artifact) => artifact.owns.kind === "postgresExtension") }),
10201
- ...core,
10494
+ const extensionSql = [
10495
+ ...sqliteFts?.before ?? [],
10202
10496
  ...artifactDropSql({ artifacts: rebuilt }),
10203
- ...artifactCreateSql({ artifacts: collected.objects.filter((artifact) => planned.has(artifact.id) && !(artifact.owns.kind === "trigger" && invalid.rebuilt.has(artifact.owns.table))) }),
10497
+ ...artifactCreateSql({ artifacts: collected.objects.filter((artifact) => planned.has(artifact.id) && (!(artifact.owns.kind === "trigger" && invalid.rebuilt.has(artifact.owns.table)) || sqliteFts?.forcedArtifacts.has(artifact.id))) }),
10498
+ ...sqliteFts?.after ?? [],
10204
10499
  ...creates
10205
10500
  ];
10501
+ const atomicFts = dialect === "sqlite" && sqliteFts?.checks.some((check) => check.changes) === true && usesSelfManagedTransactions({ sql: core });
10502
+ const setupSql = artifactCreateSql({ artifacts: missing.filter((artifact) => artifact.owns.kind === "postgresExtension") });
10503
+ const sql = [...setupSql, ...core, ...atomicFts ? ["BEGIN;"] : [], ...extensionSql, ...atomicFts ? ["COMMIT;"] : []];
10206
10504
  if (sql.length === 0) {
10207
10505
  const definitions = [...previous?.definitions ?? []];
10208
10506
  for (const object of objects)
10209
- if (object.expected !== undefined && !definitions.some((definition) => sqlObjectKey17({ identity: definition.identity }) === sqlObjectKey17({ identity: object.identity })))
10507
+ if (object.expected !== undefined && !definitions.some((definition) => sqlObjectKey18({ identity: definition.identity }) === sqlObjectKey18({ identity: object.identity })))
10210
10508
  definitions.push(object.expected);
10211
10509
  const report = await verifyMigrationState({ executor, dialect, expected: { schema, definitions }, ...params.probe === undefined ? {} : { probe: params.probe } });
10212
10510
  states.push(...report.objects);
10213
10511
  }
10214
- 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));
10215
- return { physical, from, to: schema, namespace, steps, objects: states, unmanagedRemovals, blocked, sql: blocked ? [] : sql, rebuiltArtifacts };
10512
+ const blocked = states.some((state) => state.reason === "dependency-inspection-unavailable" || state.ownership !== "unknown" && state.reason !== "legacy-definition-required" && state.reason !== "sqlite-fts-update-upgrade" && (state.state === "unavailable" || state.state === "changed" || state.state === "missing" && sql.length === 0));
10513
+ const plan = { physical, from, to: schema, namespace, steps, objects: states, unmanagedRemovals, blocked, sql: blocked ? [] : sql, rebuiltArtifacts };
10514
+ if (sqliteFts !== undefined)
10515
+ retainSqliteFtsPlan({ plan, fts: sqliteFts, ...atomicFts ? { transaction: { begin: setupSql.length + core.length, commit: sql.length - 1 } } : {} });
10516
+ return plan;
10216
10517
  }
10217
10518
 
10218
10519
  // packages/migrate/src/replay-plan.ts
@@ -10238,13 +10539,13 @@ async function planReplayMigration(params) {
10238
10539
  moduleHeads.set(migration.moduleOrigin.moduleId, migration);
10239
10540
  const immutableObjects = new Map([...moduleHeads.values()].flatMap((migration) => {
10240
10541
  const state = migration.moduleOrigin.source.desiredState.to;
10241
- return (state.schema.migrationObjects ?? []).map((object) => [sqlObjectKey18({ identity: object.identity }), object.expected ?? state.definitions.find((definition) => sqlObjectKey18({ identity: definition.identity }) === sqlObjectKey18({ identity: object.identity }))]);
10542
+ return (state.schema.migrationObjects ?? []).map((object) => [sqlObjectKey19({ identity: object.identity }), object.expected ?? state.definitions.find((definition) => sqlObjectKey19({ identity: definition.identity }) === sqlObjectKey19({ identity: object.identity }))]);
10242
10543
  }));
10243
10544
  const authored = reference.dialect === "postgres" ? (await resolvePostgresTypeIdentities({ executor, schema: params.schema, previous: reference.state.schema, mode: "verify" })).schema : params.schema;
10244
- const compared = await compareReplayMigrationObjects({ executor, reference, schema: { ...authored, migrationObjects: (authored.migrationObjects ?? []).filter((object) => !immutableObjects.has(sqlObjectKey18({ identity: object.identity }))) } });
10245
- const comparedObjects = new Map((compared.migrationObjects ?? []).map((object) => [sqlObjectKey18({ identity: object.identity }), object]));
10545
+ const compared = await compareReplayMigrationObjects({ executor, reference, schema: { ...authored, migrationObjects: (authored.migrationObjects ?? []).filter((object) => !immutableObjects.has(sqlObjectKey19({ identity: object.identity }))) } });
10546
+ const comparedObjects = new Map((compared.migrationObjects ?? []).map((object) => [sqlObjectKey19({ identity: object.identity }), object]));
10246
10547
  const schema = { ...compared, migrationObjects: (params.schema.migrationObjects ?? []).map((object) => {
10247
- const key = sqlObjectKey18({ identity: object.identity });
10548
+ const key = sqlObjectKey19({ identity: object.identity });
10248
10549
  if (!immutableObjects.has(key))
10249
10550
  return comparedObjects.get(key);
10250
10551
  const expected = immutableObjects.get(key);
@@ -10263,57 +10564,57 @@ async function planReplayMigration(params) {
10263
10564
  const identity = removalIdentity({ step, namespace: plan.namespace });
10264
10565
  return identity === undefined ? [] : [identity];
10265
10566
  })])
10266
- removals.set(sqlObjectKey18({ identity }), identity);
10267
- const declared = new Set((schema.migrationObjects ?? []).map((object) => sqlObjectKey18({ identity: object.identity })));
10567
+ removals.set(sqlObjectKey19({ identity }), identity);
10568
+ const declared = new Set((schema.migrationObjects ?? []).map((object) => sqlObjectKey19({ identity: object.identity })));
10268
10569
  const historicalObjects = new Map;
10269
10570
  for (const migration of params.migrations)
10270
10571
  for (const object of migration.desiredState?.to.schema.migrationObjects ?? []) {
10271
10572
  if (external === undefined || externalObjectOwner({ identity: object.identity, external }) === undefined)
10272
- historicalObjects.set(sqlObjectKey18({ identity: object.identity }), object.identity);
10573
+ historicalObjects.set(sqlObjectKey19({ identity: object.identity }), object.identity);
10273
10574
  }
10274
- const present = new Set(reference.inventory.map((object) => sqlObjectKey18({ identity: object.identity })));
10275
- const retainedObjects = reference.inventory.filter((object) => !declared.has(sqlObjectKey18({ identity: object.identity })) && !removals.has(sqlObjectKey18({ identity: object.identity })) && (object.identity.kind === "function" || object.identity.kind === "trigger" || historicalObjects.has(sqlObjectKey18({ identity: object.identity })))).map((object) => object.identity);
10575
+ const present = new Set(reference.inventory.map((object) => sqlObjectKey19({ identity: object.identity })));
10576
+ const retainedObjects = reference.inventory.filter((object) => !declared.has(sqlObjectKey19({ identity: object.identity })) && !removals.has(sqlObjectKey19({ identity: object.identity })) && (object.identity.kind === "function" || object.identity.kind === "trigger" || historicalObjects.has(sqlObjectKey19({ identity: object.identity })))).map((object) => object.identity);
10276
10577
  const removedRegistrations = [...historicalObjects].filter(([key]) => present.has(key) && !declared.has(key)).map(([, identity]) => identity);
10277
10578
  if (removedRegistrations.length > 0)
10278
- throw new VibeError49({ 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 } });
10579
+ throw new VibeError51({ 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 } });
10279
10580
  if (external !== undefined)
10280
10581
  assertExternalOwnershipClaims({ external, identities: [...removals.values()] });
10281
10582
  return { ...plan, requiredRemovals: [...removals.values()], retainedObjects, securityChanges: securityChangesOf({ steps: plan.steps }), replayRemovals: params.migrations.flatMap((migration) => migration.desiredState?.unmanagedRemovals ?? []).filter((identity) => external === undefined || externalObjectOwner({ identity, external }) === undefined) };
10282
10583
  }
10283
10584
  function assertReplayRemovals(params) {
10284
- const wanted = new Set([...params.plan.requiredRemovals, ...params.plan.replayRemovals].map((identity) => sqlObjectKey18({ identity })));
10285
- const keys = params.approved.map((identity) => sqlObjectKey18({ identity }));
10585
+ const wanted = new Set([...params.plan.requiredRemovals, ...params.plan.replayRemovals].map((identity) => sqlObjectKey19({ identity })));
10586
+ const keys = params.approved.map((identity) => sqlObjectKey19({ identity }));
10286
10587
  if (new Set(keys).size !== keys.length)
10287
- throw new VibeError49({ code: "VIBE_MIGRATION", message: "Removal decisions contain duplicate identities", meta: { reason: "removal-decision-invalid", shadowReusable: true } });
10288
- const unused = params.approved.filter((identity) => !wanted.has(sqlObjectKey18({ identity })));
10588
+ throw new VibeError51({ code: "VIBE_MIGRATION", message: "Removal decisions contain duplicate identities", meta: { reason: "removal-decision-invalid", shadowReusable: true } });
10589
+ const unused = params.approved.filter((identity) => !wanted.has(sqlObjectKey19({ identity })));
10289
10590
  if (unused.length > 0)
10290
- throw new VibeError49({ 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 } });
10591
+ throw new VibeError51({ 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 } });
10291
10592
  assertNamedRemovals({ removals: params.plan.requiredRemovals, approved: params.approved, acceptIntegrityLoss: params.acceptIntegrityLoss });
10292
10593
  }
10293
10594
  // packages/migrate/src/baseline.ts
10294
- import { VibeError as VibeError52, sqlObjectKey as sqlObjectKey20 } from "@vibeorm/schema";
10595
+ import { VibeError as VibeError54, sqlObjectKey as sqlObjectKey21 } from "@vibeorm/schema";
10295
10596
 
10296
10597
  // packages/migrate/src/reference-state.ts
10297
- import { VibeError as VibeError50, sqlObjectKey as sqlObjectKey19 } from "@vibeorm/schema";
10598
+ import { VibeError as VibeError52, sqlObjectKey as sqlObjectKey20 } from "@vibeorm/schema";
10298
10599
  async function captureMigrationReference(params) {
10299
10600
  const namespace = await migrationNamespace({ executor: params.referenceExecutor, dialect: params.dialect });
10300
10601
  const identities = [...schemaObjectIdentities({ schema: params.schema, dialect: params.dialect, namespace }), ...(params.schema.migrationObjects ?? []).map((object) => object.identity)];
10301
10602
  const definitions = [];
10302
- for (const identity of new Map(identities.map((identity) => [sqlObjectKey19({ identity }), identity])).values()) {
10603
+ for (const identity of new Map(identities.map((identity) => [sqlObjectKey20({ identity }), identity])).values()) {
10303
10604
  if (identity.kind === "namespace")
10304
10605
  continue;
10305
- if (!params.schema.migrationObjects?.some((object) => sqlObjectKey19({ identity: object.identity }) === sqlObjectKey19({ identity })) && !(identity.kind === "constraint" && (identity.constraintType === "check" || identity.constraintType === "exclusion")))
10606
+ if (!params.schema.migrationObjects?.some((object) => sqlObjectKey20({ identity: object.identity }) === sqlObjectKey20({ identity })) && !(identity.kind === "constraint" && (identity.constraintType === "check" || identity.constraintType === "exclusion")))
10306
10607
  continue;
10307
10608
  const result = await inspectSqlObject({ executor: params.referenceExecutor, dialect: params.dialect, identity });
10308
10609
  if (result.state !== "present")
10309
- throw new VibeError50({ code: "VIBE_MIGRATION", message: "Independent reference object is missing or unavailable", meta: { reason: "reference-unavailable", identity, state: result.state } });
10610
+ throw new VibeError52({ code: "VIBE_MIGRATION", message: "Independent reference object is missing or unavailable", meta: { reason: "reference-unavailable", identity, state: result.state } });
10310
10611
  definitions.push(result.definition);
10311
10612
  }
10312
10613
  let schema = params.schema;
10313
10614
  if (params.dialect === "postgres" && schema.models.some((model) => model.fields.some((field) => field.isUnsupported === true))) {
10314
10615
  const resolved = await resolvePostgresTypeIdentities({ executor: params.referenceExecutor, schema, mode: "reference" });
10315
10616
  if (resolved.unavailable.length > 0)
10316
- throw new VibeError50({ code: "VIBE_MIGRATION", message: "Resolve unsupported types in the independent reference before capture", meta: { reason: "type-reference-required", columns: resolved.unavailable } });
10617
+ throw new VibeError52({ code: "VIBE_MIGRATION", message: "Resolve unsupported types in the independent reference before capture", meta: { reason: "type-reference-required", columns: resolved.unavailable } });
10317
10618
  const physical = await introspectPhysicalPostgres({ executor: params.referenceExecutor, namespaces: ["public", ...schemaNamespaces({ schema })] });
10318
10619
  for (const model of resolved.schema.models)
10319
10620
  for (const field of model.fields) {
@@ -10321,7 +10622,7 @@ async function captureMigrationReference(params) {
10321
10622
  continue;
10322
10623
  const actual = physical.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace }) === physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace }))?.fields.find((entry) => entry.name === (field.dbName ?? field.name));
10323
10624
  if (actual === undefined || columnTypeKey({ field }) !== columnTypeKey({ field: actual }))
10324
- throw new VibeError50({ 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 } });
10625
+ throw new VibeError52({ 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 } });
10325
10626
  }
10326
10627
  schema = resolved.schema;
10327
10628
  }
@@ -10329,13 +10630,13 @@ async function captureMigrationReference(params) {
10329
10630
  }
10330
10631
 
10331
10632
  // packages/migrate/src/compile-desired-state.ts
10332
- import { VibeError as VibeError51, qualifiedSqlName as qualifiedSqlName13 } from "@vibeorm/schema";
10633
+ import { VibeError as VibeError53, qualifiedSqlName as qualifiedSqlName14 } from "@vibeorm/schema";
10333
10634
  import { renderExclusionSetup as renderExclusionSetup2, getSqlDialect as getSqlDialect16, renderMigrationServerVersion as renderMigrationServerVersion2 } from "@vibeorm/sql";
10334
10635
  async function compileDesiredState(params) {
10335
10636
  if (params.dialect !== "postgres")
10336
- throw new VibeError51({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Rollback declaration compilation requires PostgreSQL", meta: { feature: "desiredStateCompilation", dialect: params.dialect } });
10637
+ throw new VibeError53({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Rollback declaration compilation requires PostgreSQL", meta: { feature: "desiredStateCompilation", dialect: params.dialect } });
10337
10638
  if (params.steps.some((step) => step.kind === "alterColumn" || step.kind === "addColumn" || step.kind === "addEnumValue"))
10338
- throw new VibeError51({ 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" } });
10639
+ throw new VibeError53({ 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" } });
10339
10640
  const definitions = (params.schema.migrationObjects ?? []).flatMap((object) => object.expected === undefined ? [] : [object.expected]);
10340
10641
  await params.executor({ text: "SAVEPOINT vibeorm_desired_state" });
10341
10642
  try {
@@ -10344,7 +10645,7 @@ async function compileDesiredState(params) {
10344
10645
  await params.executor({ text });
10345
10646
  const models = [];
10346
10647
  for (const model of params.schema.models) {
10347
- const table = qualifiedSqlName13({ name: model.dbName ?? model.name, namespace: model.namespace });
10648
+ const table = qualifiedSqlName14({ name: model.dbName ?? model.name, namespace: model.namespace });
10348
10649
  const exclusions = [];
10349
10650
  for (const exclusion of model.exclusions ?? []) {
10350
10651
  for (const text of renderExclusionSetup2({ dialect: getSqlDialect16({ dialect: "postgres" }), exclusion }))
@@ -10358,7 +10659,7 @@ async function compileDesiredState(params) {
10358
10659
  const query = renderMigrationServerVersion2({ dialect: getSqlDialect16({ dialect: params.dialect }) });
10359
10660
  const version = (await params.executor({ text: query.text, values: [] }))[0]?.version;
10360
10661
  if (typeof version !== "string")
10361
- throw new VibeError51({ code: "VIBE_MIGRATION", message: "Cannot determine definition normalization version" });
10662
+ throw new VibeError53({ code: "VIBE_MIGRATION", message: "Cannot determine definition normalization version" });
10362
10663
  return { ...createDesiredState({ schema: { ...params.schema, models }, dialect: params.dialect, definitions }), normalization: { format: "catalog-v1", dialect: params.dialect, serverVersion: version } };
10363
10664
  } finally {
10364
10665
  await params.executor({ text: "ROLLBACK TO SAVEPOINT vibeorm_desired_state" });
@@ -10371,19 +10672,19 @@ function generateBaselineMigration(params) {
10371
10672
  const objects = orderedMigrationObjects({ objects: params.schema.migrationObjects ?? [] });
10372
10673
  for (const object of objects)
10373
10674
  if (object.dialect !== params.dialect)
10374
- throw new VibeError52({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Baseline object ${object.id} requires ${object.dialect}`, meta: { identity: object.identity, dialect: params.dialect } });
10675
+ throw new VibeError54({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Baseline object ${object.id} requires ${object.dialect}`, meta: { identity: object.identity, dialect: params.dialect } });
10375
10676
  const prerequisites = objects.filter((object) => object.identity.kind === "postgresExtension" || object.identity.kind === "enum" || object.identity.kind === "sequence");
10376
- const prerequisiteKeys = new Set(prerequisites.map((object) => sqlObjectKey20({ identity: object.identity })));
10677
+ const prerequisiteKeys = new Set(prerequisites.map((object) => sqlObjectKey21({ identity: object.identity })));
10377
10678
  for (const object of prerequisites) {
10378
- const unresolved = object.dependsOn.filter((identity) => !prerequisiteKeys.has(sqlObjectKey20({ identity })));
10679
+ const unresolved = object.dependsOn.filter((identity) => !prerequisiteKeys.has(sqlObjectKey21({ identity })));
10379
10680
  if (unresolved.length > 0)
10380
- throw new VibeError52({ 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 } });
10681
+ throw new VibeError54({ 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 } });
10381
10682
  }
10382
- const schema = { ...params.schema, migrationObjects: objects.filter((object) => !prerequisiteKeys.has(sqlObjectKey20({ identity: object.identity }))) };
10683
+ const schema = { ...params.schema, migrationObjects: objects.filter((object) => !prerequisiteKeys.has(sqlObjectKey21({ identity: object.identity }))) };
10383
10684
  const generated = generateMigration({ from: { datasource: schema.datasource, models: [], enums: [], extensions: [] }, to: schema, name: params.name, dialect: params.dialect, extensions: params.extensions ?? [] });
10384
10685
  const sql = splitSqlStatements({ sql: joinSqlStatements({ sql: [...prerequisites.flatMap((object) => object.create), ...generated.sql] }), dialect: params.dialect });
10385
10686
  if (sql.length === 0)
10386
- throw new VibeError52({ code: "VIBE_MIGRATION", message: "A baseline must create declared database objects", meta: { reason: "baseline-empty" } });
10687
+ throw new VibeError54({ code: "VIBE_MIGRATION", message: "A baseline must create declared database objects", meta: { reason: "baseline-empty" } });
10387
10688
  return {
10388
10689
  name: generated.name,
10389
10690
  sql,
@@ -10395,8 +10696,8 @@ function generateBaselineMigration(params) {
10395
10696
  async function verifyBaselineDeclarations(params) {
10396
10697
  const captured = await captureMigrationReference({ referenceExecutor: params.executor, schema: params.schema, dialect: params.dialect });
10397
10698
  const authored = (params.schema.migrationObjects ?? []).flatMap((object) => object.expected === undefined ? [] : [object.expected]);
10398
- const customKeys = new Set((params.schema.migrationObjects ?? []).map((object) => sqlObjectKey20({ identity: object.identity })));
10399
- const customDefinitions = captured.definitions.filter((definition) => customKeys.has(sqlObjectKey20({ identity: definition.identity })) && !authored.some((entry) => sqlObjectKey20({ identity: entry.identity }) === sqlObjectKey20({ identity: definition.identity })));
10699
+ const customKeys = new Set((params.schema.migrationObjects ?? []).map((object) => sqlObjectKey21({ identity: object.identity })));
10700
+ const customDefinitions = captured.definitions.filter((definition) => customKeys.has(sqlObjectKey21({ identity: definition.identity })) && !authored.some((entry) => sqlObjectKey21({ identity: entry.identity }) === sqlObjectKey21({ identity: definition.identity })));
10400
10701
  const verify = async () => {
10401
10702
  const compiled = params.dialect === "postgres" ? await compileDesiredState({ ...params, schema: captured.schema, steps: [], probe: "rollback" }) : { schema: captured.schema, definitions: authored };
10402
10703
  const expected = { ...compiled, definitions: [...compiled.definitions, ...customDefinitions] };
@@ -10412,48 +10713,48 @@ async function verifyBaselineDeclarations(params) {
10412
10713
  async function verifyBaselineTarget(params) {
10413
10714
  const { reference } = params;
10414
10715
  if (reference.unavailable.length > 0 || reference.inventory.some((object) => object.inspectionUnavailable !== undefined))
10415
- throw new VibeError52({ 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) } });
10716
+ throw new VibeError54({ 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) } });
10416
10717
  const comparison = await verifyMigrationReference({ executor: params.executor, dialect: reference.dialect, reference, referenceExecutor: params.referenceExecutor }).catch((cause) => {
10417
- if (cause instanceof VibeError52 && cause.meta.reason === "verification-rls-reference-required")
10418
- throw new VibeError52({ code: cause.code, message: cause.message, meta: { reason: "baseline-rls-reference-required" }, cause });
10718
+ if (cause instanceof VibeError54 && cause.meta.reason === "verification-rls-reference-required")
10719
+ throw new VibeError54({ code: cause.code, message: cause.message, meta: { reason: "baseline-rls-reference-required" }, cause });
10419
10720
  throw cause;
10420
10721
  });
10421
10722
  const security = comparison.objects.filter((object) => object.reason === "native-rls-mismatch");
10422
10723
  if (security.length > 0)
10423
- throw new VibeError52({ 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])) } });
10724
+ throw new VibeError54({ 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])) } });
10424
10725
  if (comparison.unavailable.length > 0)
10425
- throw new VibeError52({ 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 } });
10726
+ throw new VibeError54({ 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 } });
10426
10727
  const report = { inSync: comparison.objects.every((object) => object.state === "matching"), objects: comparison.objects };
10427
10728
  assertMigrationState({ report });
10428
10729
  const unmanaged = comparison.unmanaged.map((object) => object.identity);
10429
- const extras = new Set(unmanaged.map((identity) => sqlObjectKey20({ identity })));
10730
+ const extras = new Set(unmanaged.map((identity) => sqlObjectKey21({ identity })));
10430
10731
  const exclusions = params.excludeUnmanaged ?? [];
10431
- const excluded = new Set(exclusions.map((identity) => sqlObjectKey20({ identity })));
10432
- if (excluded.size !== exclusions.length || exclusions.some((identity) => !extras.has(sqlObjectKey20({ identity }))))
10433
- throw new VibeError52({ code: "VIBE_CONFIG", message: "Baseline exclusions must uniquely name actual unmanaged target objects, never declared objects", meta: { reason: "baseline-exclusion-invalid", shadowReusable: true, exclusions } });
10434
- const unreviewed = unmanaged.filter((identity) => !excluded.has(sqlObjectKey20({ identity })));
10732
+ const excluded = new Set(exclusions.map((identity) => sqlObjectKey21({ identity })));
10733
+ if (excluded.size !== exclusions.length || exclusions.some((identity) => !extras.has(sqlObjectKey21({ identity }))))
10734
+ throw new VibeError54({ code: "VIBE_CONFIG", message: "Baseline exclusions must uniquely name actual unmanaged target objects, never declared objects", meta: { reason: "baseline-exclusion-invalid", shadowReusable: true, exclusions } });
10735
+ const unreviewed = unmanaged.filter((identity) => !excluded.has(sqlObjectKey21({ identity })));
10435
10736
  if (unreviewed.length > 0)
10436
- throw new VibeError52({ 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 } });
10737
+ throw new VibeError54({ 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 } });
10437
10738
  return { ...report, unmanaged };
10438
10739
  }
10439
10740
  // packages/migrate/src/baseline-record.ts
10440
- import { VibeError as VibeError53 } from "@vibeorm/schema";
10741
+ import { VibeError as VibeError55 } from "@vibeorm/schema";
10441
10742
  import { getSqlDialect as getSqlDialect17, renderStatement } from "@vibeorm/sql";
10442
10743
  function transactionFailure2(params) {
10443
- if (params.cause instanceof VibeError53)
10744
+ if (params.cause instanceof VibeError55)
10444
10745
  return params.cause;
10445
- return new VibeError53({ 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 });
10746
+ return new VibeError55({ 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 });
10446
10747
  }
10447
10748
  async function recordBaselineMigration(params) {
10448
10749
  if (params.migration.moduleOrigin !== undefined)
10449
- throw new VibeError53({ code: "VIBE_MIGRATION", message: "Package module migrations must use the immutable module installation workflow, not baseline adoption", meta: { reason: "baseline-module-unsupported" } });
10750
+ throw new VibeError55({ code: "VIBE_MIGRATION", message: "Package module migrations must use the immutable module installation workflow, not baseline adoption", meta: { reason: "baseline-module-unsupported" } });
10450
10751
  validateMigrationReplay({ dialect: params.dialect, migrations: [params.migration], acceptSecurityChanges: params.acceptSecurityChanges });
10451
10752
  await withMigrationLock({ executor: params.executor, dialect: params.dialect, run: async (executor) => {
10452
10753
  const record = async () => {
10453
10754
  const assertEmpty = async () => {
10454
10755
  const history = await readMigrationHistory({ executor, migrations: [params.migration], dialect: params.dialect });
10455
10756
  if (history.recorded.length > 0)
10456
- throw new VibeError53({ 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 })) } });
10757
+ throw new VibeError55({ 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 })) } });
10457
10758
  };
10458
10759
  await assertEmpty();
10459
10760
  await params.verify({ executor });
@@ -10478,14 +10779,14 @@ async function recordBaselineMigration(params) {
10478
10779
  await executor({ text: "COMMIT" });
10479
10780
  }).then(undefined, async (cause) => {
10480
10781
  await executor({ text: "ROLLBACK" }).then(undefined, (rollback) => {
10481
- throw new VibeError53({ 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") });
10782
+ throw new VibeError55({ 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") });
10482
10783
  });
10483
10784
  throw transactionFailure2({ cause });
10484
10785
  });
10485
10786
  } });
10486
10787
  }
10487
10788
  // packages/migrate/src/down.ts
10488
- import { VibeError as VibeError54, sqlObjectKey as sqlObjectKey21 } from "@vibeorm/schema";
10789
+ import { VibeError as VibeError56, sqlObjectKey as sqlObjectKey22 } from "@vibeorm/schema";
10489
10790
  function buildRollbackTarget(params) {
10490
10791
  const { from, to, dialect } = params;
10491
10792
  const skipped = [];
@@ -10589,10 +10890,10 @@ function generateDownMigration(params) {
10589
10890
  }
10590
10891
  restoreSql.push(...entry.restore);
10591
10892
  }
10592
- const previousKeys = new Set((from.migrationObjects ?? []).map((object) => sqlObjectKey21({ identity: object.identity })));
10893
+ const previousKeys = new Set((from.migrationObjects ?? []).map((object) => sqlObjectKey22({ identity: object.identity })));
10593
10894
  const ownedDropSql = [];
10594
10895
  for (const object of orderedMigrationObjects({ objects: to.migrationObjects ?? [] }).reverse()) {
10595
- if (previousKeys.has(sqlObjectKey21({ identity: object.identity })))
10896
+ if (previousKeys.has(sqlObjectKey22({ identity: object.identity })))
10596
10897
  continue;
10597
10898
  if (object.identity.kind === "namespace") {
10598
10899
  skipped.push(`namespace ${object.identity.name} remains installed`);
@@ -10603,7 +10904,7 @@ function generateDownMigration(params) {
10603
10904
  continue;
10604
10905
  }
10605
10906
  if (object.drop.length === 0)
10606
- throw new VibeError54({ code: "VIBE_MIGRATION", message: `Migration object ${object.id} needs reviewed reverse SQL`, meta: { reason: "migration-object-reverse-required", id: object.id } });
10907
+ throw new VibeError56({ code: "VIBE_MIGRATION", message: `Migration object ${object.id} needs reviewed reverse SQL`, meta: { reason: "migration-object-reverse-required", id: object.id } });
10607
10908
  ownedDropSql.push(...object.drop);
10608
10909
  }
10609
10910
  return {
@@ -10616,11 +10917,11 @@ function generateDownMigration(params) {
10616
10917
  };
10617
10918
  }
10618
10919
  // packages/migrate/src/push.ts
10619
- import { VibeError as VibeError55, assertValidSchemaIR as assertValidSchemaIR4, providerToDialect as providerToDialect6, sqlObjectKey as sqlObjectKey22, unsupportedCapability as unsupportedCapability14 } from "@vibeorm/schema";
10920
+ import { VibeError as VibeError57, assertValidSchemaIR as assertValidSchemaIR4, providerToDialect as providerToDialect7, sqlObjectKey as sqlObjectKey23, unsupportedCapability as unsupportedCapability14 } from "@vibeorm/schema";
10620
10921
  import { DIALECT_CAPABILITIES as DIALECT_CAPABILITIES4 } from "@vibeorm/sql";
10621
10922
  async function push(params) {
10622
10923
  assertValidSchemaIR4({ schema: params.schema });
10623
- const dialect = providerToDialect6({ provider: params.schema.datasource.provider });
10924
+ const dialect = providerToDialect7({ provider: params.schema.datasource.provider });
10624
10925
  if (params.rebuildArtifacts === true && dialect !== "postgres")
10625
10926
  throw unsupportedCapability14({ 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" });
10626
10927
  if (dialect !== "postgres")
@@ -10660,11 +10961,11 @@ async function pushChanges(params) {
10660
10961
  if (params.dryRun === true)
10661
10962
  return { steps, applied: [], planned: sql, securityChanges, objects: plan.objects, blocked: plan.blocked, unmanagedRemovals: plan.unmanagedRemovals };
10662
10963
  if (plan.blocked)
10663
- throw new VibeError55({ code: "VIBE_MIGRATION", message: "Schema push is blocked by unverified object definitions", meta: { reason: "migration-state-unverified", objects: plan.objects } });
10964
+ throw new VibeError57({ code: "VIBE_MIGRATION", message: "Schema push is blocked by unverified object definitions", meta: { reason: "migration-state-unverified", objects: plan.objects } });
10664
10965
  assertSecurityAcknowledgement({ securityChanges, ...params.acceptSecurityChanges === undefined ? {} : { acceptSecurityChanges: params.acceptSecurityChanges } });
10665
10966
  const destructive = steps.filter((step) => step.destructive);
10666
10967
  if (destructive.length > 0 && params.acceptDataLoss !== true)
10667
- throw new VibeError55({ 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 })) } });
10968
+ throw new VibeError57({ 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 })) } });
10668
10969
  assertRemovalAuthorization({
10669
10970
  steps,
10670
10971
  dialect,
@@ -10673,16 +10974,26 @@ async function pushChanges(params) {
10673
10974
  ...params.acceptIntegrityLoss === undefined ? {} : { acceptIntegrityLoss: params.acceptIntegrityLoss },
10674
10975
  ...params.removeUnmanaged === undefined ? {} : { removeUnmanaged: params.removeUnmanaged }
10675
10976
  });
10676
- const approved = new Set((params.removeUnmanaged ?? []).map((identity) => sqlObjectKey22({ identity })));
10677
- const unapproved = plan.unmanagedRemovals.filter((identity) => !approved.has(sqlObjectKey22({ identity })));
10977
+ const approved = new Set((params.removeUnmanaged ?? []).map((identity) => sqlObjectKey23({ identity })));
10978
+ const unapproved = plan.unmanagedRemovals.filter((identity) => !approved.has(sqlObjectKey23({ identity })));
10678
10979
  if (unapproved.length > 0)
10679
- throw new VibeError55({ code: "VIBE_MIGRATION", message: "DDL would remove unmanaged dependent objects; approve their exact identities", meta: { reason: "unmanaged-removal-required", objects: unapproved } });
10980
+ throw new VibeError57({ code: "VIBE_MIGRATION", message: "DDL would remove unmanaged dependent objects; approve their exact identities", meta: { reason: "unmanaged-removal-required", objects: unapproved } });
10680
10981
  const wrap = dialect !== "postgres" && DIALECT_CAPABILITIES4[dialect].transactionalDdl && !usesSelfManagedTransactions({ sql });
10982
+ const ftsTransaction = dialect === "sqlite" ? sqliteFtsTransaction({ plan }) : undefined;
10681
10983
  if (wrap)
10682
10984
  await executor({ text: "BEGIN" });
10683
10985
  try {
10684
- for (const statement of sql)
10986
+ if (dialect === "sqlite" && ftsTransaction === undefined)
10987
+ await assertSqliteFtsApplyState({ plan, executor });
10988
+ for (const [index, statement] of sql.entries()) {
10989
+ if (ftsTransaction?.commit === index)
10990
+ await verifySqliteFtsApplyState({ plan, executor });
10685
10991
  await executeStatement({ executor, statement, dialect });
10992
+ if (ftsTransaction?.begin === index)
10993
+ await assertSqliteFtsApplyState({ plan, executor });
10994
+ }
10995
+ if (dialect === "sqlite" && ftsTransaction === undefined)
10996
+ await verifySqliteFtsApplyState({ plan, executor });
10686
10997
  if ((schema.migrationObjects?.length ?? 0) > 0)
10687
10998
  assertMigrationState({ report: await verifyMigrationState({ executor, dialect, expected: createDesiredState({ schema: { ...schema, models: [] }, dialect }), ...params.external === undefined ? {} : { external: params.external } }) });
10688
10999
  if (wrap)
@@ -10694,7 +11005,7 @@ async function pushChanges(params) {
10694
11005
  } catch {}
10695
11006
  } else if (dialect === "sqlite")
10696
11007
  await cleanupSelfManagedFailure({ executor });
10697
- throw new VibeError55({ code: "VIBE_MIGRATION", message: `push failed while applying schema changes: ${error instanceof Error ? error.message : String(error)}`, meta: { statements: sql }, cause: error });
11008
+ throw new VibeError57({ code: "VIBE_MIGRATION", message: `push failed while applying schema changes: ${error instanceof Error ? error.message : String(error)}`, meta: { statements: sql }, cause: error });
10698
11009
  }
10699
11010
  return { steps, applied: sql, securityChanges, objects: plan.objects, desiredState: createDesiredState({ schema: plan.to, dialect }) };
10700
11011
  }
@@ -10709,11 +11020,11 @@ async function pull(params) {
10709
11020
  }
10710
11021
  }
10711
11022
  // packages/migrate/src/module-export.ts
10712
- import { VibeError as VibeError56, providerToDialect as providerToDialect7 } from "@vibeorm/schema";
11023
+ import { VibeError as VibeError58, providerToDialect as providerToDialect8 } from "@vibeorm/schema";
10713
11024
  function runtimeSchema(params) {
10714
11025
  const byName = (left, right) => left.name.localeCompare(right.name);
10715
11026
  return {
10716
- datasource: { provider: providerToDialect7({ provider: params.schema.datasource.provider }) },
11027
+ datasource: { provider: providerToDialect8({ provider: params.schema.datasource.provider }) },
10717
11028
  models: [...params.schema.models].sort(byName),
10718
11029
  enums: [...params.schema.enums].sort(byName),
10719
11030
  extensions: [...params.schema.extensions].sort(byName),
@@ -10724,13 +11035,13 @@ function emitModuleMigrationExport(params) {
10724
11035
  moduleMigrationFiles({ bundle: params.bundle, prefix: "export" });
10725
11036
  const origin = params.bundle.migrations.at(-1).moduleOrigin;
10726
11037
  if (!origin.contracts.includes(params.contract))
10727
- throw new VibeError56({ code: "VIBE_CONFIG", message: "The package runtime contract is not supported by its migration head", meta: { reason: "module-contract-mismatch", moduleId: origin.moduleId } });
11038
+ throw new VibeError58({ code: "VIBE_CONFIG", message: "The package runtime contract is not supported by its migration head", meta: { reason: "module-contract-mismatch", moduleId: origin.moduleId } });
10728
11039
  if (params.client !== undefined) {
10729
11040
  const { module, schema } = params.client;
10730
- if (module.id !== origin.moduleId || module.revision !== origin.revision || module.contract !== params.contract || providerToDialect7({ provider: schema.datasource.provider }) !== origin.dialect)
10731
- throw new VibeError56({ code: "VIBE_CONFIG", message: "The generated module identity, revision or contract differs from its migration head", meta: { reason: "module-client-head-mismatch", moduleId: origin.moduleId } });
11041
+ if (module.id !== origin.moduleId || module.revision !== origin.revision || module.contract !== params.contract || providerToDialect8({ provider: schema.datasource.provider }) !== origin.dialect)
11042
+ throw new VibeError58({ code: "VIBE_CONFIG", message: "The generated module identity, revision or contract differs from its migration head", meta: { reason: "module-client-head-mismatch", moduleId: origin.moduleId } });
10732
11043
  if (canonicalMigrationJson({ value: runtimeSchema({ schema }) }) !== canonicalMigrationJson({ value: runtimeSchema({ schema: origin.source.desiredState.to.schema }) }))
10733
- throw new VibeError56({ code: "VIBE_CONFIG", message: "The generated module schema differs from its immutable migration head", meta: { reason: "module-client-schema-mismatch", moduleId: origin.moduleId } });
11044
+ throw new VibeError58({ code: "VIBE_CONFIG", message: "The generated module schema differs from its immutable migration head", meta: { reason: "module-client-schema-mismatch", moduleId: origin.moduleId } });
10734
11045
  }
10735
11046
  const expectation = { moduleId: origin.moduleId, revision: origin.revision, artifactChecksum: origin.artifactChecksum, contract: params.contract };
10736
11047
  return [
@@ -10749,16 +11060,16 @@ export const moduleReadiness = Object.freeze(${JSON.stringify(expectation)});
10749
11060
  ];
10750
11061
  }
10751
11062
  // packages/migrate/src/module-readiness.ts
10752
- import { VibeError as VibeError57 } from "@vibeorm/schema";
11063
+ import { VibeError as VibeError59 } from "@vibeorm/schema";
10753
11064
  async function inspectModuleReadiness(params) {
10754
11065
  const ids = new Set;
10755
11066
  for (const expected of params.modules) {
10756
11067
  if (ids.has(expected.moduleId) || !expected.moduleId || !expected.revision || !expected.contract || !/^[a-f0-9]{64}$/.test(expected.artifactChecksum))
10757
- throw new VibeError57({ 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 } });
11068
+ throw new VibeError59({ 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 } });
10758
11069
  ids.add(expected.moduleId);
10759
11070
  }
10760
11071
  if (params.compatibility !== undefined && params.compatibility !== "exact" && params.compatibility !== "contract")
10761
- throw new VibeError57({ code: "VIBE_CONFIG", message: "Unknown module compatibility mode", meta: { reason: "module-readiness-expectation" } });
11072
+ throw new VibeError59({ code: "VIBE_CONFIG", message: "Unknown module compatibility mode", meta: { reason: "module-readiness-expectation" } });
10762
11073
  const records = await readModuleJournal(params);
10763
11074
  const heads = moduleHistoryHeads({ records });
10764
11075
  const unresolved = [...records.values()].some(({ row, entry }) => row.error !== undefined || row.statementIndex < 0 || entry !== undefined && (!entry.complete || row.statementIndex !== entry.migration.sql.length));
@@ -10793,7 +11104,7 @@ async function inspectModuleReadiness(params) {
10793
11104
  return { ready: !unresolved && modules.every((module) => (module.revision === "matching" || module.revision === "compatible") && module.structural.status !== "drifted"), modules };
10794
11105
  }
10795
11106
  // packages/migrate/src/adoption.ts
10796
- import { VibeError as VibeError58 } from "@vibeorm/schema";
11107
+ import { VibeError as VibeError60 } from "@vibeorm/schema";
10797
11108
  async function adoptMigration(params) {
10798
11109
  const report = await verifyMigrationState({ executor: params.executor, expected: params.reference, dialect: params.dialect });
10799
11110
  assertMigrationState({ report });
@@ -10809,11 +11120,11 @@ function deployedDesiredState(params) {
10809
11120
  for (const [index, row] of params.recorded.entries()) {
10810
11121
  const file = params.migrations[index];
10811
11122
  if (row.state !== "applied" || row.checksum === "" || file?.name !== row.name || migrationChecksum(file) !== row.checksum)
10812
- throw new VibeError58({ code: "VIBE_MIGRATION", message: "Desired state requires complete, checksum-verified migration history", meta: { reason: "desired-state-history-unverified", migration: row.name, state: row.state } });
11123
+ throw new VibeError60({ code: "VIBE_MIGRATION", message: "Desired state requires complete, checksum-verified migration history", meta: { reason: "desired-state-history-unverified", migration: row.name, state: row.state } });
10813
11124
  }
10814
11125
  const applied = new Set(params.recorded.map((row) => row.name));
10815
11126
  if (params.migrations.some((migration) => applied.has(migration.name) && migration.desiredStatePending === true))
10816
- throw new VibeError58({ code: "VIBE_MIGRATION", message: "Applied unfinalized metadata cannot establish desired state", meta: { reason: "desired-state-history-unverified" } });
11127
+ throw new VibeError60({ code: "VIBE_MIGRATION", message: "Applied unfinalized metadata cannot establish desired state", meta: { reason: "desired-state-history-unverified" } });
10817
11128
  const last = [...params.migrations].reverse().find((migration) => applied.has(migration.name));
10818
11129
  return last?.desiredState?.to;
10819
11130
  }
@@ -10928,4 +11239,4 @@ export {
10928
11239
  withMigrationReference
10929
11240
  };
10930
11241
 
10931
- //# debugId=CC107738F16DF7A964756E2164756E21
11242
+ //# debugId=F91BB3120BEADA3064756E2164756E21