@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/fs.js CHANGED
@@ -15,16 +15,12 @@ function hasMysqlGlobalCatalogVisibility(params) {
15
15
  }
16
16
 
17
17
  // packages/migrate/src/module-ownership.ts
18
- import { VibeError as VibeError18, sqlObjectKey as sqlObjectKey7, sqlObjectNamespace as sqlObjectNamespace3 } from "@vibeorm/schema";
18
+ import { VibeError as VibeError19, sqlObjectKey as sqlObjectKey7, sqlObjectNamespace as sqlObjectNamespace3 } from "@vibeorm/schema";
19
19
  import { getSqlDialect as getSqlDialect4, renderModuleDependencyQuery } from "@vibeorm/sql";
20
20
 
21
21
  // packages/migrate/src/desired-state.ts
22
22
  import { VibeError, assertValidSchemaIR, isSqlObjectDefinition, isSqlObjectIdentity, providerToDialect, sqlObjectKey } from "@vibeorm/schema";
23
23
  var UNFINALIZED_DESIRED_STATE = Object.freeze({ version: 1, status: "unfinalized" });
24
- function isUnfinalizedDesiredState(params) {
25
- const value = JSON.parse(params.text);
26
- return record(value) && value.version === 1 && value.status === "unfinalized" && Object.keys(value).length === 2;
27
- }
28
24
  var DESIRED_STATE_METADATA = Object.freeze({ filename: "desired-state.json", version: 1 });
29
25
  function createDesiredState(params) {
30
26
  assertValidSchemaIR({ schema: params.schema });
@@ -107,6 +103,15 @@ function parseDesiredStateSidecar(params) {
107
103
  return checked.value;
108
104
  }, failure: (cause) => invalid({ migration: params.migration, detail: "malformed metadata", cause }) });
109
105
  }
106
+ function loadDesiredStateSidecar(params) {
107
+ return parseMigrationMetadata({ text: params.text, parse: (value) => {
108
+ if (record(value) && value.version === 1 && value.status === "unfinalized" && Object.keys(value).length === 2)
109
+ return { desiredStatePending: true };
110
+ const checked = { value, migration: params.migration };
111
+ assertDesiredState(checked);
112
+ return { desiredState: checked.value };
113
+ }, failure: (cause) => invalid({ migration: params.migration, detail: "malformed metadata", cause }) });
114
+ }
110
115
  function parseMigrationMetadata(params) {
111
116
  try {
112
117
  return params.parse(JSON.parse(params.text));
@@ -781,6 +786,32 @@ function parseMysqlEnumColumnType(params) {
781
786
  }
782
787
 
783
788
  // packages/migrate/src/normalize.ts
789
+ function firstByName(params) {
790
+ const result = new Map;
791
+ for (const entry of params.entries)
792
+ if (!result.has(entry.name))
793
+ result.set(entry.name, entry);
794
+ return result;
795
+ }
796
+ function normalizationLookup(params) {
797
+ const enums = new Map;
798
+ const scopedEnums = new Map;
799
+ for (const entry of params.schema.enums) {
800
+ const namespace = qualifiedSqlName3({ name: entry.name, namespace: entry.namespace }).namespace;
801
+ for (const name of new Set([entry.name, entry.dbName ?? entry.name])) {
802
+ if (!enums.has(name))
803
+ enums.set(name, entry);
804
+ const key = JSON.stringify([namespace, name]);
805
+ if (!scopedEnums.has(key))
806
+ scopedEnums.set(key, entry);
807
+ }
808
+ }
809
+ return { models: firstByName({ entries: params.schema.models }), fields: new Map(params.schema.models.map((model) => [model, firstByName({ entries: model.fields })])), enums, scopedEnums };
810
+ }
811
+ function lookupFieldName(params) {
812
+ const field = params.lookup.fields.get(params.model)?.get(params.name);
813
+ return field?.dbName ?? field?.name ?? params.name;
814
+ }
784
815
  function normalizeSchema(params) {
785
816
  const { schema } = params;
786
817
  const dialect = params.dialect ?? "postgres";
@@ -793,6 +824,7 @@ function normalizeSchema(params) {
793
824
  continue;
794
825
  enums.set(physicalNameKey({ name: enumDef.dbName ?? enumDef.name, namespace: enumDef.namespace, dialect }), enumDef);
795
826
  }
827
+ const lookup = normalizationLookup({ schema });
796
828
  const tables = new Map;
797
829
  const views = new Map;
798
830
  for (const model of schema.models) {
@@ -802,25 +834,25 @@ function normalizeSchema(params) {
802
834
  views.set(physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect }), model);
803
835
  continue;
804
836
  }
805
- const table = normalizeModel({ model, schema, dialect, enums });
837
+ const table = normalizeModel({ model, lookup, dialect, enums });
806
838
  tables.set(physicalNameKey({ name: table.table, namespace: table.namespace, dialect }), table);
807
839
  }
808
840
  const joinTables = new Map;
809
841
  for (const pair of detectImplicitM2MPairs({ schema })) {
810
- const join = normalizeJoinTable({ pair, schema, dialect });
842
+ const join = normalizeJoinTable({ pair, lookup, dialect });
811
843
  joinTables.set(physicalNameKey({ name: join.table, namespace: join.namespace, dialect }), join);
812
844
  }
813
845
  return { tables, joinTables, enums, views };
814
846
  }
815
847
  function physicalEnumField(params) {
816
- const { field, schema, dialect, table } = params;
848
+ const { field, lookup, dialect, table } = params;
817
849
  if (!isEnumType5(field.type))
818
850
  return field;
819
851
  const type = field.type;
820
852
  if (type.namespace !== undefined)
821
853
  assertSqlNamespace({ namespace: type.namespace, dialect });
822
854
  const name = type.enum;
823
- const enumDef = schema.enums.find((candidate) => (candidate.name === name || candidate.dbName === name) && (type.namespace === undefined || qualifiedSqlName3({ name, namespace: candidate.namespace }).namespace === type.namespace));
855
+ const enumDef = type.namespace === undefined ? lookup.enums.get(name) : lookup.scopedEnums.get(JSON.stringify([type.namespace, name]));
824
856
  if (enumDef === undefined)
825
857
  return field;
826
858
  if (enumStorageOf({ enumDef }) !== "string") {
@@ -885,48 +917,49 @@ function defaultFingerprintFor(params) {
885
917
  }
886
918
  }
887
919
  function normalizeModel(params) {
888
- const { model, schema, dialect, enums } = params;
920
+ const { model, lookup, dialect, enums } = params;
889
921
  const table = model.dbName ?? model.name;
890
922
  const columns = new Map;
891
923
  for (const logicalField of model.fields) {
892
- const field = physicalEnumField({ field: logicalField, schema, dialect, table });
924
+ const field = physicalEnumField({ field: logicalField, lookup, dialect, table });
893
925
  const name = field.dbName ?? field.name;
926
+ const typeKey = columnTypeKeyFor({ field, dialect, enums });
894
927
  columns.set(name, {
895
928
  name,
896
929
  field,
897
- typeKey: columnTypeKeyFor({ field, dialect, enums }),
898
- exactTypeKey: columnTypeKeyFor({ field, dialect, enums, exact: true }),
930
+ typeKey,
931
+ exactTypeKey: dialect === "postgres" ? columnTypeKeyFor({ field, dialect, enums, exact: true }) : typeKey,
899
932
  pinsExactType: dialect === "postgres" && declaresExactColumnType({ field }),
900
933
  nullable: field.isOptional,
901
934
  defaultKey: defaultFingerprintFor({ field, dialect })
902
935
  });
903
936
  }
904
- const pk = normalizePk({ model });
905
- const indexes = normalizeIndexes({ model, table, dialect });
906
- const fks = normalizeFks({ model, table, schema, dialect });
937
+ const pk = normalizePk({ model, lookup });
938
+ const indexes = normalizeIndexes({ model, table, dialect, lookup });
939
+ const fks = normalizeFks({ model, table, lookup, dialect });
907
940
  const checks = new Map;
908
941
  for (const check of model.checks ?? [])
909
942
  checks.set(check.name, check.expression);
910
943
  return { model, table, ...model.namespace === undefined ? {} : { namespace: model.namespace }, columns, pk, indexes, fks, checks };
911
944
  }
912
945
  function normalizePk(params) {
913
- const { model } = params;
946
+ const { model, lookup } = params;
914
947
  if (model.primaryKey !== undefined && model.primaryKey.fields.length > 0) {
915
- return model.primaryKey.fields.map((name) => fieldDbName({ model, name }));
948
+ return model.primaryKey.fields.map((name) => lookupFieldName({ lookup, model, name }));
916
949
  }
917
950
  return model.fields.filter((field) => field.isId).map((field) => field.dbName ?? field.name);
918
951
  }
919
952
  function normalizeIndexes(params) {
920
- const { model, table, dialect } = params;
953
+ const { model, table, dialect, lookup } = params;
921
954
  const result = new Map;
922
955
  const add = (index) => {
923
956
  const unique = index.kind === "unique";
924
957
  const kind = unique ? "btree" : index.kind;
925
958
  const columns = index.fields.map((field) => {
926
- const fieldIR = model.fields.find((candidate) => candidate.name === field.name);
959
+ const fieldIR = lookup.fields.get(model)?.get(field.name);
927
960
  const decimal = fieldIR !== undefined && fieldIR.type === "Decimal" && fieldIR.isList !== true;
928
961
  return {
929
- name: fieldDbName({ model, name: field.name }),
962
+ name: fieldIR?.dbName ?? fieldIR?.name ?? field.name,
930
963
  desc: field.sort === "desc",
931
964
  ...decimal ? { decimal: true } : {},
932
965
  ...field.operatorClass === undefined ? {} : { operatorClass: field.operatorClass }
@@ -960,8 +993,7 @@ function normalizeIndexes(params) {
960
993
  }
961
994
  function indexComparisonKey(params) {
962
995
  const { unique, kind, columns, where, nullsNotDistinct } = params;
963
- const cols = columns.map((column) => `${column.name}${column.desc ? " desc" : ""}${column.operatorClass === undefined ? "" : ` ops:${JSON.stringify([column.operatorClass.namespace, column.operatorClass.name])}`}`).join(",");
964
- return `${unique ? "u" : "i"}${nullsNotDistinct === true ? "n" : ""}:${kind}:${cols}:${normalizeWhere({ where })}`;
996
+ 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 })]);
965
997
  }
966
998
  function normalizeWhere(params) {
967
999
  const { where } = params;
@@ -1123,22 +1155,22 @@ function parsePrimaryExpression(params) {
1123
1155
  return { text: parts.join(" "), at, kind: "atom" };
1124
1156
  }
1125
1157
  function normalizeFks(params) {
1126
- const { model, table, schema } = params;
1158
+ const { model, table, lookup } = params;
1127
1159
  const result = new Map;
1128
1160
  for (const relation of model.relations) {
1129
1161
  if (relation.fields === undefined || relation.fields.length === 0)
1130
1162
  continue;
1131
- const target = schema.models.find((candidate) => candidate.name === relation.targetModel);
1163
+ const target = lookup.models.get(relation.targetModel);
1132
1164
  if (target !== undefined && target.isView)
1133
1165
  continue;
1134
1166
  const refTable = target === undefined ? relation.targetModel : target.dbName ?? target.name;
1135
- const columns = relation.fields.map((name) => fieldDbName({ model, name }));
1136
- const refColumns = (relation.references ?? []).map((name) => target === undefined ? name : fieldDbName({ model: target, name }));
1167
+ const columns = relation.fields.map((name) => lookupFieldName({ lookup, model, name }));
1168
+ const refColumns = (relation.references ?? []).map((name) => target === undefined ? name : lookupFieldName({ lookup, model: target, name }));
1137
1169
  const required = relation.fields.every((name) => {
1138
- const field = model.fields.find((candidate) => candidate.name === name);
1170
+ const field = lookup.fields.get(model)?.get(name);
1139
1171
  return field === undefined ? true : !field.isOptional;
1140
1172
  });
1141
- const key = `${columns.join(",")}\u2192${physicalNameKey({ name: refTable, namespace: target?.namespace, dialect: params.dialect })}.${refColumns.join(",")}`;
1173
+ const key = JSON.stringify([columns, physicalNameKey({ name: refTable, namespace: target?.namespace, dialect: params.dialect }), refColumns]);
1142
1174
  if (result.has(key))
1143
1175
  continue;
1144
1176
  result.set(key, {
@@ -1155,17 +1187,17 @@ function normalizeFks(params) {
1155
1187
  return result;
1156
1188
  }
1157
1189
  function normalizeJoinTable(params) {
1158
- const { pair, schema, dialect } = params;
1190
+ const { pair, lookup, dialect } = params;
1159
1191
  return {
1160
1192
  table: pair.tableName,
1161
1193
  ...pair.namespace === undefined ? {} : { namespace: pair.namespace },
1162
- a: joinTableEnd({ modelName: pair.modelA, schema, dialect, joinTable: pair.tableName }),
1163
- b: joinTableEnd({ modelName: pair.modelB, schema, dialect, joinTable: pair.tableName })
1194
+ a: joinTableEnd({ modelName: pair.modelA, lookup, dialect, joinTable: pair.tableName }),
1195
+ b: joinTableEnd({ modelName: pair.modelB, lookup, dialect, joinTable: pair.tableName })
1164
1196
  };
1165
1197
  }
1166
1198
  function joinTableEnd(params) {
1167
- const { modelName, schema, joinTable } = params;
1168
- const model = schema.models.find((candidate) => candidate.name === modelName);
1199
+ const { modelName, lookup, joinTable } = params;
1200
+ const model = lookup.models.get(modelName);
1169
1201
  if (model === undefined) {
1170
1202
  throw new VibeError5({
1171
1203
  code: "VIBE_MIGRATION",
@@ -1173,7 +1205,7 @@ function joinTableEnd(params) {
1173
1205
  meta: { joinTable, model: modelName }
1174
1206
  });
1175
1207
  }
1176
- 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);
1208
+ 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);
1177
1209
  const pkField = pkFields[0];
1178
1210
  if (pkFields.length !== 1 || pkField === undefined) {
1179
1211
  throw new VibeError5({
@@ -1186,7 +1218,7 @@ function joinTableEnd(params) {
1186
1218
  table: model.dbName ?? model.name,
1187
1219
  ...model.namespace === undefined ? {} : { namespace: model.namespace },
1188
1220
  column: pkField.dbName ?? pkField.name,
1189
- field: physicalEnumField({ field: pkField, schema, dialect: params.dialect, table: model.dbName ?? model.name })
1221
+ field: physicalEnumField({ field: pkField, lookup, dialect: params.dialect, table: model.dbName ?? model.name })
1190
1222
  };
1191
1223
  }
1192
1224
  function physicalNameKey(params) {
@@ -1202,11 +1234,6 @@ function schemaNamespaces(params) {
1202
1234
  ...(params.schema.migrationObjects ?? []).flatMap((object) => object.identity.kind === "namespace" ? [object.identity.name] : [])
1203
1235
  ])];
1204
1236
  }
1205
- function fieldDbName(params) {
1206
- const { model, name } = params;
1207
- const field = model.fields.find((candidate) => candidate.name === name);
1208
- return field === undefined ? name : field.dbName ?? field.name;
1209
- }
1210
1237
 
1211
1238
  // packages/migrate/src/object-ownership.ts
1212
1239
  import { VibeError as VibeError6, defaultPrimaryKeyName, enumStorageOf as enumStorageOf2, implicitJoinTableObjectNames, postgresGeneratedObjectName, qualifiedSqlName as qualifiedSqlName4, sqlObjectKey as sqlObjectKey2 } from "@vibeorm/schema";
@@ -1311,7 +1338,10 @@ function removalIdentity(params) {
1311
1338
  }
1312
1339
  }
1313
1340
  function schemaObjectIdentities(params) {
1314
- const normalized = normalizeSchema({ schema: params.schema, dialect: params.dialect });
1341
+ return schemaObjectIdentitiesFromNormalized({ ...params, normalized: normalizeSchema({ schema: params.schema, dialect: params.dialect }) });
1342
+ }
1343
+ function schemaObjectIdentitiesFromNormalized(params) {
1344
+ const { normalized } = params;
1315
1345
  const result = [];
1316
1346
  if (params.dialect === "postgres") {
1317
1347
  for (const namespace of schemaNamespaces({ schema: params.schema }))
@@ -1426,7 +1456,7 @@ function assertRemovalAuthorization(params) {
1426
1456
  }
1427
1457
 
1428
1458
  // packages/migrate/src/inventory.ts
1429
- import { VibeError as VibeError17, defaultForeignKeyName as defaultForeignKeyName2, defaultPrimaryKeyName as defaultPrimaryKeyName2, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey6 } from "@vibeorm/schema";
1459
+ import { VibeError as VibeError18, defaultForeignKeyName as defaultForeignKeyName2, defaultPrimaryKeyName as defaultPrimaryKeyName2, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey6 } from "@vibeorm/schema";
1430
1460
  import { getSqlDialect as getSqlDialect3, renderMigrationInventory, renderSqliteColumnInventory as renderSqliteColumnInventory2, renderMysqlObjectMembers as renderMysqlObjectMembers2, sqliteTableConstraints as sqliteTableConstraints3, renderSqliteForeignKeyInventory } from "@vibeorm/sql";
1431
1461
 
1432
1462
  // packages/migrate/src/external-objects.ts
@@ -1796,7 +1826,7 @@ async function verifyExclusions(params) {
1796
1826
 
1797
1827
  // packages/migrate/src/inspect-objects.ts
1798
1828
  import { randomUUID as randomUUID2 } from "crypto";
1799
- import { VibeError as VibeError16, sqlObjectKey as sqlObjectKey5, sqlObjectNamespace as sqlObjectNamespace2, DIALECT_CAPABILITIES as DIALECT_CAPABILITIES2, qualifiedSqlName as qualifiedSqlName8 } from "@vibeorm/schema";
1829
+ import { VibeError as VibeError17, sqlObjectKey as sqlObjectKey5, sqlObjectNamespace as sqlObjectNamespace2, DIALECT_CAPABILITIES as DIALECT_CAPABILITIES2, qualifiedSqlName as qualifiedSqlName8 } from "@vibeorm/schema";
1800
1830
  import { getSqlDialect as getSqlDialect2, renderMigrationObjectBatch, renderMigrationObjectQuery as renderMigrationObjectQuery2, renderCheckProbe, renderMigrationNamespace, renderMigrationDependencies, renderMysqlObjectMembers, renderMysqlCheckProbe, sqliteColumnClauses as sqliteColumnClauses2, renderMigrationVisibility, sqliteTableConstraints as sqliteTableConstraints2, renderSqliteColumnInventory } from "@vibeorm/sql";
1801
1831
 
1802
1832
  // packages/migrate/src/introspect/postgres.ts
@@ -1994,6 +2024,20 @@ function lowerFirst(value) {
1994
2024
  return value.charAt(0).toLowerCase() + value.slice(1);
1995
2025
  }
1996
2026
 
2027
+ // packages/migrate/src/introspect/group.ts
2028
+ function groupCatalogRows(params) {
2029
+ const groups = new Map;
2030
+ for (const row of params.rows) {
2031
+ const key = params.key(row);
2032
+ const group = groups.get(key);
2033
+ if (group === undefined)
2034
+ groups.set(key, [row]);
2035
+ else
2036
+ group.push(row);
2037
+ }
2038
+ return groups;
2039
+ }
2040
+
1997
2041
  // packages/migrate/src/postgres-type-identity.ts
1998
2042
  import { VibeError as VibeError10, qualifiedSqlName as qualifiedSqlName6 } from "@vibeorm/schema";
1999
2043
  import { postgresDialect as postgresDialect3, renderPostgresTypeIdentity } from "@vibeorm/sql";
@@ -2395,10 +2439,15 @@ async function introspectPhysicalPostgres(params) {
2395
2439
  read("checks"),
2396
2440
  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())
2397
2441
  ]);
2398
- const keysQuery = renderPostgresIndexKeys({ dialect: postgresDialect6 });
2442
+ const keysQuery = renderPostgresIndexKeys({ dialect: postgresDialect6, namespaces });
2399
2443
  const indexKeys = await executor({ text: keysQuery.text, values: [...keysQuery.values] });
2400
2444
  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))
2401
2445
  throw new VibeError13({ code: "VIBE_MIGRATION", message: "PostgreSQL index-key identity is incomplete", meta: { reason: "index-inspection-unavailable" } });
2446
+ const keysByIndex = groupCatalogRows({ rows: indexKeys, key: (row) => JSON.stringify([physicalNameKey({ namespace: String(row.table_namespace), name: String(row.table_name) }), row.index_name]) });
2447
+ const catalogColumns = new Map(columnRows.map((row) => [JSON.stringify([row.table_name, row.column_name]), row]));
2448
+ const foreignKeysByTable = groupCatalogRows({ rows: fkRows, key: (row) => String(row.table_name) });
2449
+ const catalogIndexesByTable = groupCatalogRows({ rows: indexRows, key: (row) => String(row.table_name) });
2450
+ const exclusionsByTable = groupCatalogRows({ rows: exclusionRows, key: (row) => String(row.table_name) });
2402
2451
  const tables = tableRows.map((row) => row.table_name);
2403
2452
  const views = viewRows.map((row) => row.table_name);
2404
2453
  const columns = columnRows.map((row) => ({
@@ -2416,6 +2465,8 @@ async function introspectPhysicalPostgres(params) {
2416
2465
  numericScale: toNumberOrNull(row.numeric_scale),
2417
2466
  fullType: row.full_type ?? null
2418
2467
  }));
2468
+ const columnsByTable = groupCatalogRows({ rows: columns, key: (column) => column.table });
2469
+ const columnsByIdentity = new Map(columns.map((column) => [JSON.stringify([column.table, column.name]), column]));
2419
2470
  const enums = [];
2420
2471
  {
2421
2472
  const valuesByName = new Map;
@@ -2456,6 +2507,7 @@ async function introspectPhysicalPostgres(params) {
2456
2507
  entry.columns.push(columnName);
2457
2508
  }
2458
2509
  }
2510
+ const uniquesByTable = groupCatalogRows({ rows: uniquesByConstraint.values(), key: (unique) => unique.table });
2459
2511
  const checksByTable = new Map;
2460
2512
  for (const row of checkRows) {
2461
2513
  const table = row.table_name;
@@ -2490,7 +2542,7 @@ async function introspectPhysicalPostgres(params) {
2490
2542
  const fkGroups = [...fkGroupsByName.values()].map((group) => ({
2491
2543
  ...group,
2492
2544
  required: group.columns.every((columnName) => {
2493
- const column = columns.find((c) => c.table === group.table && c.name === columnName);
2545
+ const column = columnsByIdentity.get(JSON.stringify([group.table, columnName]));
2494
2546
  return column === undefined ? true : !column.nullable;
2495
2547
  })
2496
2548
  }));
@@ -2498,7 +2550,7 @@ async function introspectPhysicalPostgres(params) {
2498
2550
  for (const row of indexRows) {
2499
2551
  if (row.backing_constraint_type === "x")
2500
2552
  continue;
2501
- 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);
2553
+ const operands = keysByIndex.get(JSON.stringify([row.table_name, row.index_name])) ?? [];
2502
2554
  if (row.plain_columns === true && operands.length !== Number(row.key_count))
2503
2555
  throw new VibeError13({ 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 } });
2504
2556
  if (operands.length > 0) {
@@ -2523,22 +2575,24 @@ async function introspectPhysicalPostgres(params) {
2523
2575
  indexes.push(parsed);
2524
2576
  }
2525
2577
  }
2578
+ const indexesByTable = groupCatalogRows({ rows: indexes, key: (index) => index.table });
2579
+ const fkGroupsByTable = groupCatalogRows({ rows: fkGroups, key: (group) => group.table });
2526
2580
  const joinTables = new Map;
2527
2581
  for (const table of tables) {
2528
2582
  const physicalTable = physicalNames.get(table);
2529
2583
  if (!physicalTable.name.startsWith("_"))
2530
2584
  continue;
2531
- const tableFks = fkRows.filter((row) => row.table_name === table);
2532
- const tableIndexes = indexRows.filter((row) => row.table_name === table);
2533
- if (pkByTable.has(table) || (checksByTable.get(table)?.length ?? 0) > 0 || exclusionRows.some((row) => row.table_name === table))
2585
+ const tableFks = foreignKeysByTable.get(table) ?? [];
2586
+ const tableIndexes = catalogIndexesByTable.get(table) ?? [];
2587
+ if (pkByTable.has(table) || (checksByTable.get(table)?.length ?? 0) > 0 || exclusionsByTable.has(table))
2534
2588
  continue;
2535
2589
  if (!tableFks.every((row) => row.validated === true && row.deferrable === false && row.triggers_enabled === true))
2536
2590
  continue;
2537
2591
  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))
2538
2592
  continue;
2539
2593
  if (tableFks.some((row) => {
2540
- const source = columnRows.find((column) => column.table_name === table && column.column_name === row.column_name);
2541
- const target = columnRows.find((column) => column.table_name === row.foreign_table_name && column.column_name === row.foreign_column_name);
2594
+ const source = catalogColumns.get(JSON.stringify([table, row.column_name]));
2595
+ const target = catalogColumns.get(JSON.stringify([row.foreign_table_name, row.foreign_column_name]));
2542
2596
  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;
2543
2597
  }))
2544
2598
  continue;
@@ -2546,9 +2600,9 @@ async function introspectPhysicalPostgres(params) {
2546
2600
  continue;
2547
2601
  const detected = detectJoinTable({
2548
2602
  table: physicalTable.name,
2549
- columnNames: columns.filter((column) => column.table === table).map((column) => column.name),
2550
- fkGroups: fkGroups.filter((group) => group.table === table).map((group) => ({ ...group, table: physicalTable.name, refTable: physicalNames.get(group.refTable)?.name ?? group.refTable })),
2551
- indexes: indexes.filter((index) => index.table === table).map((index) => ({ ...index, table: physicalTable.name }))
2603
+ columnNames: (columnsByTable.get(table) ?? []).map((column) => column.name),
2604
+ fkGroups: (fkGroupsByTable.get(table) ?? []).map((group) => ({ ...group, table: physicalTable.name, refTable: physicalNames.get(group.refTable)?.name ?? group.refTable })),
2605
+ indexes: (indexesByTable.get(table) ?? []).map((index) => ({ ...index, table: physicalTable.name }))
2552
2606
  });
2553
2607
  if (detected !== null)
2554
2608
  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 }) });
@@ -2561,14 +2615,14 @@ async function introspectPhysicalPostgres(params) {
2561
2615
  table,
2562
2616
  physicalTable: physicalNames.get(table).name,
2563
2617
  isView: false,
2564
- columns,
2618
+ columns: columnsByTable.get(table) ?? [],
2565
2619
  pkByTable,
2566
2620
  pkNames,
2567
- uniquesByConstraint,
2621
+ uniques: uniquesByTable.get(table) ?? [],
2568
2622
  constraintNames,
2569
- indexes,
2623
+ indexes: indexesByTable.get(table) ?? [],
2570
2624
  enumNames,
2571
- exclusions: exclusionRows.filter((row) => row.table_name === table).map((row) => ({ name: String(row.constraint_name), definition: String(row.definition), requiredExtensions: [], catalogDefinition: true })),
2625
+ exclusions: (exclusionsByTable.get(table) ?? []).map((row) => ({ name: String(row.constraint_name), definition: String(row.definition), requiredExtensions: [], catalogDefinition: true })),
2572
2626
  checks: checksByTable.get(table) ?? []
2573
2627
  }));
2574
2628
  }
@@ -2577,10 +2631,10 @@ async function introspectPhysicalPostgres(params) {
2577
2631
  table: view,
2578
2632
  physicalTable: physicalNames.get(view).name,
2579
2633
  isView: true,
2580
- columns,
2634
+ columns: columnsByTable.get(view) ?? [],
2581
2635
  pkByTable,
2582
2636
  pkNames,
2583
- uniquesByConstraint,
2637
+ uniques: uniquesByTable.get(view) ?? [],
2584
2638
  constraintNames,
2585
2639
  indexes: [],
2586
2640
  enumNames,
@@ -2600,14 +2654,16 @@ async function introspectPhysicalPostgres(params) {
2600
2654
  logicalNames.add(name);
2601
2655
  return name;
2602
2656
  };
2657
+ const modelBasenames = groupCatalogRows({ rows: physicalNames.values(), key: (entry) => entry.name });
2658
+ const enumBasenames = groupCatalogRows({ rows: enumIdentities.values(), key: (entry) => entry.name });
2603
2659
  const modelNames = new Map(models.map((model) => {
2604
2660
  const identity = physicalNames.get(model.name);
2605
- const duplicates = [...physicalNames.values()].filter((entry) => entry.name === identity.name).length > 1;
2661
+ const duplicates = (modelBasenames.get(identity.name)?.length ?? 0) > 1;
2606
2662
  return [model.name, logicalName({ key: model.name, name: identity.name, duplicates })];
2607
2663
  }));
2608
2664
  const enumLogicalNames = new Map(enums.map((entry) => {
2609
2665
  const identity = enumIdentities.get(entry.name);
2610
- const duplicates = [...enumIdentities.values()].filter((candidate) => candidate.name === identity.name).length > 1;
2666
+ const duplicates = (enumBasenames.get(identity.name)?.length ?? 0) > 1;
2611
2667
  return [entry.name, logicalName({ key: entry.name, name: identity.name, duplicates })];
2612
2668
  }));
2613
2669
  const qualifiedModels = models.map((model) => {
@@ -2645,20 +2701,18 @@ async function introspectPhysicalPostgres(params) {
2645
2701
  return resolved.schema;
2646
2702
  }
2647
2703
  function buildModel(params) {
2648
- const { table, isView, columns, pkByTable, uniquesByConstraint, constraintNames, indexes, enumNames } = params;
2649
- const tableColumns = columns.filter((column) => column.table === table);
2704
+ const { table, isView, columns, pkByTable, uniques, constraintNames, indexes, enumNames } = params;
2705
+ const tableColumns = columns;
2650
2706
  const pk = pkByTable.get(table) ?? [];
2651
2707
  const uniqueSingles = new Set;
2652
2708
  const multiUniques = [];
2653
- for (const unique of uniquesByConstraint.values()) {
2709
+ for (const unique of uniques) {
2654
2710
  const name = unique.name;
2655
- if (unique.table !== table)
2656
- continue;
2657
2711
  if (unique.columns.length === 1)
2658
2712
  uniqueSingles.add(unique.columns[0]);
2659
2713
  multiUniques.push({ name, columns: unique.columns });
2660
2714
  }
2661
- const tableIndexes = indexes.filter((index) => index.table === table && !constraintNames.has(JSON.stringify([table, index.name])));
2715
+ const tableIndexes = indexes.filter((index) => !constraintNames.has(JSON.stringify([table, index.name])));
2662
2716
  for (const index of tableIndexes) {
2663
2717
  if (!index.unique)
2664
2718
  continue;
@@ -2943,7 +2997,7 @@ function toNumberOrNull(value) {
2943
2997
  }
2944
2998
 
2945
2999
  // packages/migrate/src/introspect/mysql.ts
2946
- import { exactIntegerLiteral as exactIntegerLiteral2 } from "@vibeorm/schema";
3000
+ import { VibeError as VibeError14, exactIntegerLiteral as exactIntegerLiteral2 } from "@vibeorm/schema";
2947
3001
  import { mysqlDialect as mysqlDialect2 } from "@vibeorm/sql";
2948
3002
 
2949
3003
  // packages/migrate/src/journal-table.ts
@@ -3072,6 +3126,12 @@ async function introspectMysql(params) {
3072
3126
  collation: catalogField({ row, name: "collation_name" }) ?? null,
3073
3127
  extra: (catalogField({ row, name: "extra" }) ?? "").toLowerCase()
3074
3128
  }));
3129
+ const columnsByTable = groupCatalogRows({ rows: columns, key: (column) => column.table });
3130
+ const columnsByIdentity = new Map(columns.map((column) => [JSON.stringify([column.table, column.name]), column]));
3131
+ const catalogColumns = new Map(columnRows.map((row) => [JSON.stringify([catalogField({ row, name: "table_name" }), catalogField({ row, name: "column_name" })]), row]));
3132
+ const statisticsByTable = groupCatalogRows({ rows: statisticRows, key: (row) => String(catalogField({ row, name: "table_name" })) });
3133
+ const foreignKeysByTable = groupCatalogRows({ rows: fkRows, key: (row) => String(catalogField({ row, name: "table_name" })) });
3134
+ const enginesByTable = new Map(tableRows.map((row) => [catalogField({ row, name: "table_name" }), catalogField({ row, name: "engine" })]));
3075
3135
  const rulesByConstraint = new Map;
3076
3136
  for (const row of refRows) {
3077
3137
  rulesByConstraint.set(catalogField({ row, name: "constraint_name" }), {
@@ -3083,6 +3143,15 @@ async function introspectMysql(params) {
3083
3143
  for (const row of fkRows) {
3084
3144
  const constraintName = catalogField({ row, name: "constraint_name" });
3085
3145
  const table = catalogField({ row, name: "table_name" });
3146
+ const tableSchema = catalogField({ row, name: "table_schema" });
3147
+ const referencedSchema = catalogField({ row, name: "referenced_table_schema" });
3148
+ if (typeof tableSchema === "string" && typeof referencedSchema === "string" && tableSchema !== referencedSchema) {
3149
+ throw new VibeError14({
3150
+ code: "VIBE_UNSUPPORTED_CAPABILITY",
3151
+ message: `MySQL cross-database foreign key "${constraintName}" from "${tableSchema}.${table}" to "${referencedSchema}" cannot be introspected without losing its database identity`,
3152
+ meta: { dialect: "mysql", feature: "cross-database foreign keys", constraint: constraintName, table, tableSchema, referencedSchema }
3153
+ });
3154
+ }
3086
3155
  const key = `${table}\x00${constraintName}`;
3087
3156
  const existing = groupsByKey.get(key);
3088
3157
  if (existing === undefined) {
@@ -3105,17 +3174,11 @@ async function introspectMysql(params) {
3105
3174
  onDelete: rules?.onDelete ?? "NoAction",
3106
3175
  onUpdate: rules?.onUpdate ?? "NoAction",
3107
3176
  required: group.columns.every((columnName) => {
3108
- const column = columns.find((candidate) => candidate.table === group.table && candidate.name === columnName);
3177
+ const column = columnsByIdentity.get(JSON.stringify([group.table, columnName]));
3109
3178
  return column === undefined ? true : !column.nullable;
3110
3179
  })
3111
3180
  };
3112
3181
  });
3113
- const fkNamesByTable = new Map;
3114
- for (const group of fkGroups) {
3115
- const set = fkNamesByTable.get(group.table) ?? new Set;
3116
- set.add(group.constraintName);
3117
- fkNamesByTable.set(group.table, set);
3118
- }
3119
3182
  const pkByTable = new Map;
3120
3183
  const indexes = [];
3121
3184
  {
@@ -3124,6 +3187,12 @@ async function introspectMysql(params) {
3124
3187
  const table = catalogField({ row, name: "table_name" });
3125
3188
  const name = catalogField({ row, name: "index_name" });
3126
3189
  const columnName = catalogField({ row, name: "column_name" });
3190
+ const type = catalogField({ row, name: "index_type" });
3191
+ const unique = Number(catalogField({ row, name: "non_unique" })) === 0;
3192
+ const kind = typeof type === "string" ? type.toLowerCase() : "btree";
3193
+ 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)) {
3194
+ throw new VibeError14({ 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 } });
3195
+ }
3127
3196
  if (name === "PRIMARY") {
3128
3197
  const list = pkByTable.get(table);
3129
3198
  if (list === undefined)
@@ -3132,8 +3201,6 @@ async function introspectMysql(params) {
3132
3201
  list.push(columnName);
3133
3202
  continue;
3134
3203
  }
3135
- if (fkNamesByTable.get(table)?.has(name) === true)
3136
- continue;
3137
3204
  const key = `${table}\x00${name}`;
3138
3205
  const entry = byKey.get(key);
3139
3206
  const column = {
@@ -3144,7 +3211,8 @@ async function introspectMysql(params) {
3144
3211
  byKey.set(key, {
3145
3212
  table,
3146
3213
  name,
3147
- unique: Number(catalogField({ row, name: "non_unique" })) === 0,
3214
+ unique,
3215
+ kind,
3148
3216
  columns: [column]
3149
3217
  });
3150
3218
  } else {
@@ -3152,34 +3220,39 @@ async function introspectMysql(params) {
3152
3220
  }
3153
3221
  }
3154
3222
  for (const entry of byKey.values()) {
3155
- indexes.push({ ...entry, kind: "btree" });
3223
+ const support = groupsByKey.get(`${entry.table}\x00${entry.name}`);
3224
+ 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))
3225
+ continue;
3226
+ indexes.push(entry);
3156
3227
  }
3157
3228
  }
3229
+ const indexesByTable = groupCatalogRows({ rows: indexes, key: (index) => index.table });
3230
+ const fkGroupsByTable = groupCatalogRows({ rows: fkGroups, key: (group) => group.table });
3158
3231
  const enums = [];
3159
3232
  const joinTables = new Map;
3160
3233
  let foreignKeysEnabled;
3161
3234
  for (const table of tables) {
3162
3235
  if (!table.startsWith("_") || pkByTable.has(table) || (checksByTable.get(table)?.length ?? 0) > 0)
3163
3236
  continue;
3164
- const tableIndexes = statisticRows.filter((row) => catalogField({ row, name: "table_name" }) === table);
3237
+ const tableIndexes = statisticsByTable.get(table) ?? [];
3165
3238
  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))
3166
3239
  continue;
3167
- const tableFks = fkRows.filter((row) => catalogField({ row, name: "table_name" }) === table);
3240
+ const tableFks = foreignKeysByTable.get(table) ?? [];
3168
3241
  if (tableFks.some((row) => {
3169
3242
  const targetTable = catalogField({ row, name: "referenced_table_name" });
3170
- const source = columnRows.find((column) => catalogField({ row: column, name: "table_name" }) === table && catalogField({ row: column, name: "column_name" }) === catalogField({ row, name: "column_name" }));
3171
- const target = columnRows.find((column) => catalogField({ row: column, name: "table_name" }) === targetTable && catalogField({ row: column, name: "column_name" }) === catalogField({ row, name: "referenced_column_name" }));
3243
+ const source = catalogColumns.get(JSON.stringify([table, catalogField({ row, name: "column_name" })]));
3244
+ const target = catalogColumns.get(JSON.stringify([targetTable, catalogField({ row, name: "referenced_column_name" })]));
3172
3245
  if (source === undefined || target === undefined)
3173
3246
  return true;
3174
3247
  const type = catalogField({ row: source, name: "column_type" });
3175
- 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"));
3248
+ 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");
3176
3249
  }))
3177
3250
  continue;
3178
3251
  const detected = detectJoinTable({
3179
3252
  table,
3180
- columnNames: columns.filter((column) => column.table === table).map((column) => column.name),
3181
- fkGroups,
3182
- indexes
3253
+ columnNames: (columnsByTable.get(table) ?? []).map((column) => column.name),
3254
+ fkGroups: fkGroupsByTable.get(table) ?? [],
3255
+ indexes: indexesByTable.get(table) ?? []
3183
3256
  });
3184
3257
  if (detected === null)
3185
3258
  continue;
@@ -3191,7 +3264,7 @@ async function introspectMysql(params) {
3191
3264
  continue;
3192
3265
  const q = (name) => mysqlDialect2.quoteIdent({ name });
3193
3266
  let valid = true;
3194
- for (const fk of fkGroups.filter((group) => group.table === table)) {
3267
+ for (const fk of fkGroupsByTable.get(table) ?? []) {
3195
3268
  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: [] });
3196
3269
  if (rows.length > 0) {
3197
3270
  valid = false;
@@ -3208,9 +3281,9 @@ async function introspectMysql(params) {
3208
3281
  models.push(buildModel2({
3209
3282
  table,
3210
3283
  isView: false,
3211
- columns,
3284
+ columns: columnsByTable.get(table) ?? [],
3212
3285
  pkByTable,
3213
- indexes,
3286
+ indexes: indexesByTable.get(table) ?? [],
3214
3287
  enums,
3215
3288
  checks: checksByTable.get(table) ?? []
3216
3289
  }));
@@ -3219,7 +3292,7 @@ async function introspectMysql(params) {
3219
3292
  models.push(buildModel2({
3220
3293
  table: view,
3221
3294
  isView: true,
3222
- columns,
3295
+ columns: columnsByTable.get(view) ?? [],
3223
3296
  pkByTable: new Map,
3224
3297
  indexes: [],
3225
3298
  enums,
@@ -3237,9 +3310,9 @@ async function introspectMysql(params) {
3237
3310
  }
3238
3311
  function buildModel2(params) {
3239
3312
  const { table, isView, columns, pkByTable, indexes, enums } = params;
3240
- const tableColumns = columns.filter((column) => column.table === table);
3313
+ const tableColumns = columns;
3241
3314
  const pk = pkByTable.get(table) ?? [];
3242
- const tableIndexes = indexes.filter((index) => index.table === table);
3315
+ const tableIndexes = indexes;
3243
3316
  const uniqueSingles = new Set;
3244
3317
  for (const index of tableIndexes) {
3245
3318
  if (index.unique && index.columns.length === 1) {
@@ -3270,7 +3343,7 @@ function buildModel2(params) {
3270
3343
  continue;
3271
3344
  modelIndexes.push({
3272
3345
  name: index.name,
3273
- kind: index.unique ? "unique" : "btree",
3346
+ kind: index.unique ? "unique" : index.kind,
3274
3347
  fields: index.columns.map((column) => ({
3275
3348
  name: column.name,
3276
3349
  ...column.desc ? { sort: "desc" } : {}
@@ -3802,7 +3875,7 @@ function q(name) {
3802
3875
  }
3803
3876
 
3804
3877
  // packages/migrate/src/execute.ts
3805
- import { VibeError as VibeError14 } from "@vibeorm/schema";
3878
+ import { VibeError as VibeError15 } from "@vibeorm/schema";
3806
3879
  var SELF_MANAGED_MARKER = "PRAGMA foreign_keys=OFF";
3807
3880
  var FK_CHECK_PREFIX = "PRAGMA foreign_key_check";
3808
3881
  function usesSelfManagedTransactions(params) {
@@ -3843,7 +3916,7 @@ function assertSelfManagedResumeSafe(params) {
3843
3916
  const completedIndex = params.direction === "up" ? params.start + 1 : -(params.start + 2);
3844
3917
  const rewind = selfManagedResumeStart({ sql: params.sql, start: params.start });
3845
3918
  const retryIndex = params.direction === "up" ? rewind : -(rewind + 1);
3846
- throw new VibeError14({
3919
+ throw new VibeError15({
3847
3920
  code: "VIBE_MIGRATION",
3848
3921
  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.`,
3849
3922
  meta: { reason: "uncertain-statement", migration: params.migration, statementIndex: params.start, direction: params.direction, dialect: "sqlite" }
@@ -3872,7 +3945,7 @@ function unsupportedScript(params) {
3872
3945
  const { migration, direction, index, problem, found } = params;
3873
3946
  const subject = direction === "down" ? `the down script of migration "${migration}"` : `migration "${migration}"`;
3874
3947
  const position = `${direction === "down" ? "down statement" : "statement"} ${index + 1} of ${params.statements}`;
3875
- return new VibeError14({
3948
+ return new VibeError15({
3876
3949
  code: "VIBE_MIGRATION",
3877
3950
  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.',
3878
3951
  meta: { reason: "unsupported-self-managed-script", problem, migration, statementIndex: index, direction }
@@ -3928,7 +4001,7 @@ async function executeStatement(params) {
3928
4001
  const rows = await executor({ text: statement, values: [] });
3929
4002
  if (rows.length > 0) {
3930
4003
  const sample = rows.slice(0, 5).map((row) => `${String(row.table)} rowid ${String(row.rowid)} \u2192 ${String(row.parent)}`).join("; ");
3931
- throw new VibeError14({
4004
+ throw new VibeError15({
3932
4005
  code: "VIBE_MIGRATION",
3933
4006
  message: `foreign key check failed after table rebuild: ${rows.length} violation(s) \u2014 ${sample}`,
3934
4007
  meta: { violations: rows }
@@ -3983,7 +4056,7 @@ async function prepareOperatorClassDiff(params) {
3983
4056
  }
3984
4057
 
3985
4058
  // packages/migrate/src/postgres-default-equivalence.ts
3986
- import { VibeError as VibeError15 } from "@vibeorm/schema";
4059
+ import { VibeError as VibeError16 } from "@vibeorm/schema";
3987
4060
  import { postgresDialect as postgresDialect8, renderPostgresDefaultFunctionIdentity } from "@vibeorm/sql";
3988
4061
  var IDENTIFIER = '(?:"(?:[^"\\u0000]|"")+"|[A-Za-z_][A-Za-z_0-9$]*)';
3989
4062
  var CALL = new RegExp(`^\\s*(${IDENTIFIER})(?:\\s*\\.\\s*(${IDENTIFIER}))?\\s*\\(\\s*\\)\\s*$`);
@@ -4002,7 +4075,7 @@ async function equivalentPostgresDefaultFunction(params) {
4002
4075
  return false;
4003
4076
  const query = renderPostgresDefaultFunctionIdentity({ dialect: postgresDialect8, table: params.table, column: params.column });
4004
4077
  const rows = await params.executor({ text: query.text, values: [...query.values] }).catch((cause) => {
4005
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Cannot inspect the stored default function identity", meta: { reason: "default-inspection-unavailable", table: params.table, column: params.column }, cause });
4078
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Cannot inspect the stored default function identity", meta: { reason: "default-inspection-unavailable", table: params.table, column: params.column }, cause });
4006
4079
  });
4007
4080
  if (rows.length !== 1)
4008
4081
  return false;
@@ -4097,7 +4170,7 @@ async function inspectSqlObject(params) {
4097
4170
  }
4098
4171
  }
4099
4172
  return { state: "present", definition: { identity: params.identity, facts, dependencies: [], format: "catalog-v2" } };
4100
- }).catch((error) => ({ state: "unavailable", reason: error instanceof VibeError16 ? String(error.meta.feature ?? error.code) : "catalog-query-failed" }));
4173
+ }).catch((error) => ({ state: "unavailable", reason: error instanceof VibeError17 ? String(error.meta.feature ?? error.code) : "catalog-query-failed" }));
4101
4174
  }
4102
4175
  function postgresInspection(params) {
4103
4176
  if (!params.addresses.every((address) => typeof address === "string"))
@@ -4112,7 +4185,7 @@ function postgresInspection(params) {
4112
4185
  return { state: "present", definition: { identity: params.identity, facts, dependencies, format: "catalog-v2" } };
4113
4186
  }
4114
4187
  function unavailableReason(params) {
4115
- return params.error instanceof VibeError16 ? String(params.error.meta.feature ?? params.error.meta.reason ?? params.error.code) : "catalog-query-failed";
4188
+ return params.error instanceof VibeError17 ? String(params.error.meta.feature ?? params.error.meta.reason ?? params.error.code) : "catalog-query-failed";
4116
4189
  }
4117
4190
  async function readBatch(params) {
4118
4191
  const positions = new Set(params.batch.positions);
@@ -4120,7 +4193,7 @@ async function readBatch(params) {
4120
4193
  const rows = new Map;
4121
4194
  for (const row of await params.executor({ text: params.batch.text, values: [...params.batch.values] })) {
4122
4195
  if (typeof row.position !== "number" || !Number.isInteger(row.position) || !positions.has(row.position))
4123
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Invalid batched catalog identity", meta: { reason: "catalog-query-failed" } });
4196
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Invalid batched catalog identity", meta: { reason: "catalog-query-failed" } });
4124
4197
  const matches = rows.get(row.position) ?? [];
4125
4198
  matches.push(row);
4126
4199
  rows.set(row.position, matches);
@@ -4165,7 +4238,7 @@ async function assertCatalogVisibility(params) {
4165
4238
  const grants = new Set(rows.map((row) => row.privilege));
4166
4239
  const missing = ["SELECT", "TRIGGER", "SHOW VIEW", "EXECUTE"].filter((privilege) => !grants.has(privilege));
4167
4240
  if (missing.length > 0)
4168
- 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 } });
4241
+ throw new VibeError17({ 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 } });
4169
4242
  }
4170
4243
  var CATALOG_V1_OMITTED_FACTS = Object.freeze({
4171
4244
  function: ["definerOwner"],
@@ -4189,9 +4262,20 @@ function compare(params) {
4189
4262
  differences.push({ attribute: "dependencies", expected, actual });
4190
4263
  return differences;
4191
4264
  }
4265
+ function compareInspectedSqlObject(params) {
4266
+ const { identity, ownership, expected, live } = params;
4267
+ if (live.state !== "present")
4268
+ return { identity, ownership, state: live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} };
4269
+ if (expected === undefined)
4270
+ return identity.kind === "namespace" ? { identity, ownership, state: "matching", differences: [] } : { identity, ownership, state: "unavailable", reason: "independent-definition-required", differences: [] };
4271
+ if (params.dialect === "postgres" && identity.kind === "table" && (expected.facts.persistence === undefined || expected.facts.options === undefined))
4272
+ return { identity, ownership, state: "unavailable", reason: "table-physical-reference-required", differences: compare({ expected, live: live.definition }) };
4273
+ const differences = compare({ expected, live: live.definition });
4274
+ return { identity, ownership, state: differences.length === 0 ? "matching" : "changed", differences };
4275
+ }
4192
4276
  function assertProbeMode(params) {
4193
4277
  if (params.probe !== undefined && params.probe !== "none" && DIALECT_CAPABILITIES2[params.dialect].checkNormalization !== params.probe)
4194
- throw new VibeError16({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `${params.probe} CHECK probes are unavailable on ${params.dialect}`, meta: { feature: "checkProbe", dialect: params.dialect } });
4278
+ throw new VibeError17({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `${params.probe} CHECK probes are unavailable on ${params.dialect}`, meta: { feature: "checkProbe", dialect: params.dialect } });
4195
4279
  }
4196
4280
  async function verifyMigrationState(params) {
4197
4281
  return verifyState(params);
@@ -4214,18 +4298,7 @@ async function verifyState(params) {
4214
4298
  const expected = definitions.get(key);
4215
4299
  const live = inspections === undefined ? await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity }) : inspections[position];
4216
4300
  position++;
4217
- if (live.state !== "present") {
4218
- objects.push({ identity, ownership, state: live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} });
4219
- } else if (expected === undefined && identity.kind === "namespace") {
4220
- objects.push({ identity, ownership, state: "matching", differences: [] });
4221
- } else if (expected === undefined) {
4222
- objects.push({ identity, ownership, state: "unavailable", reason: "independent-definition-required", differences: [] });
4223
- } else if (params.dialect === "postgres" && identity.kind === "table" && (expected.facts.persistence === undefined || expected.facts.options === undefined)) {
4224
- objects.push({ identity, ownership, state: "unavailable", reason: "table-physical-reference-required", differences: compare({ expected, live: live.definition }) });
4225
- } else {
4226
- const differences = compare({ expected, live: live.definition });
4227
- objects.push({ identity, ownership, state: differences.length === 0 ? "matching" : "changed", differences });
4228
- }
4301
+ objects.push(compareInspectedSqlObject({ identity, ownership, expected, live, dialect: params.dialect }));
4229
4302
  }
4230
4303
  if (params.dialect === "postgres" && schemaNamespaces({ schema: params.expected.schema }).length > 0 || params.expected.schema.models.length > 0 || params.expected.schema.enums.length > 0)
4231
4304
  objects.push(...await verifySchemaObjects(params));
@@ -4394,7 +4467,7 @@ async function verifyMigrationTransition(params) {
4394
4467
  }
4395
4468
  async function verifySqliteTransitionBeforeRestore(params) {
4396
4469
  if (params.dialect !== "sqlite")
4397
- throw new VibeError16({ code: "VIBE_INTERNAL", message: "SQLite rebuild verification requires the SQLite dialect" });
4470
+ throw new VibeError17({ code: "VIBE_INTERNAL", message: "SQLite rebuild verification requires the SQLite dialect" });
4398
4471
  await executeStatement({ executor: params.executor, dialect: "sqlite", statement: "PRAGMA foreign_key_check;" });
4399
4472
  return verifyTransition({ ...params, sqliteRestorePending: true });
4400
4473
  }
@@ -4404,7 +4477,7 @@ async function verifyTransition(params) {
4404
4477
  for (const identity of params.removals ?? []) {
4405
4478
  const owner = externalObjectOwner({ identity, external });
4406
4479
  if (owner !== undefined)
4407
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Transition removals cannot target externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
4480
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Transition removals cannot target externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
4408
4481
  }
4409
4482
  params = { ...params, from: projectExternalState({ state: params.from, external }), to: projectExternalState({ state: params.to, external }) };
4410
4483
  }
@@ -4429,12 +4502,12 @@ async function migrationNamespace(params) {
4429
4502
  const rows = await params.executor({ text: query.text, values: [] });
4430
4503
  const namespace = rows[0]?.namespace;
4431
4504
  if (typeof namespace !== "string" || namespace.length === 0)
4432
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Cannot determine the migration namespace", meta: { reason: "inspection-unavailable" } });
4505
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Cannot determine the migration namespace", meta: { reason: "inspection-unavailable" } });
4433
4506
  return namespace;
4434
4507
  }
4435
4508
  function assertMigrationState(params) {
4436
4509
  if (!params.report.inSync)
4437
- 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") } });
4510
+ throw new VibeError17({ 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") } });
4438
4511
  }
4439
4512
  async function deparsePostgresCheck(params) {
4440
4513
  const definition = await deparsePostgresCheckDefinition({ ...params, constraint: "vibeorm_check_probe" });
@@ -4448,7 +4521,7 @@ async function deparsePostgresCheckDefinition(params) {
4448
4521
  await params.executor({ text: query.text });
4449
4522
  const result = await inspectSqlObject({ executor: params.executor, dialect: "postgres", identity: { kind: "constraint", constraintType: "check", table: params.table, name } });
4450
4523
  if (result.state !== "present" || typeof result.definition.facts.definition !== "string")
4451
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "CHECK normalization probe unavailable", meta: { reason: "check-inspection-unavailable" } });
4524
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "CHECK normalization probe unavailable", meta: { reason: "check-inspection-unavailable" } });
4452
4525
  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 } };
4453
4526
  } finally {
4454
4527
  await params.executor({ text: "ROLLBACK TO SAVEPOINT vibeorm_check_probe" });
@@ -4466,7 +4539,7 @@ async function deparseMysqlCheck(params) {
4466
4539
  const source = await params.executor({ text: read.text, values: [] });
4467
4540
  const definition = source[0]?.["Create Table"];
4468
4541
  if (typeof definition !== "string")
4469
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Cannot read temporary clone definition" });
4542
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Cannot read temporary clone definition" });
4470
4543
  const drop = renderMysqlCheckProbe({ dialect, probe, operation: "drop" });
4471
4544
  await params.executor({ text: drop.text });
4472
4545
  created = false;
@@ -4477,7 +4550,7 @@ async function deparseMysqlCheck(params) {
4477
4550
  const normalizedDefinition = rows[0]?.["Create Table"];
4478
4551
  const check = typeof normalizedDefinition === "string" ? parseSqliteNamedChecks({ tableSql: normalizedDefinition }).find((entry) => entry.name === probe) : undefined;
4479
4552
  if (check === undefined)
4480
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "MySQL temporary CHECK deparse unavailable", meta: { reason: "check-inspection-unavailable" } });
4553
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "MySQL temporary CHECK deparse unavailable", meta: { reason: "check-inspection-unavailable" } });
4481
4554
  return check.expression;
4482
4555
  } finally {
4483
4556
  if (created) {
@@ -4504,7 +4577,7 @@ async function prepareCheckDiff(params) {
4504
4577
  const desired = await deparsePostgresCheck({ executor: params.executor, table: identity.table, expression: next.expression, probe: "rollback" });
4505
4578
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
4506
4579
  if (live.state !== "present")
4507
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4580
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4508
4581
  if (live.definition.facts.definition === desired) {
4509
4582
  checks.push(next);
4510
4583
  continue;
@@ -4516,7 +4589,7 @@ async function prepareCheckDiff(params) {
4516
4589
  const desired = await deparseMysqlCheck({ executor: params.executor, table: identity.table, expression: next.expression, probe: "temporary" });
4517
4590
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
4518
4591
  if (live.state !== "present")
4519
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4592
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4520
4593
  checks.push(desired === live.definition.facts.definition ? next : { name: check.name, expression: check.expression });
4521
4594
  continue;
4522
4595
  }
@@ -4529,7 +4602,7 @@ async function prepareCheckDiff(params) {
4529
4602
  checks.push({ name: check.name, expression: check.expression });
4530
4603
  continue;
4531
4604
  }
4532
- 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 } });
4605
+ throw new VibeError17({ 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 } });
4533
4606
  }
4534
4607
  models.push({ ...model, checks });
4535
4608
  }
@@ -4556,7 +4629,7 @@ async function inspectMigrationInventory(params) {
4556
4629
  for (const row of rows) {
4557
4630
  const name = row.name;
4558
4631
  if (typeof name !== "string" || typeof row.kind !== "string")
4559
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "Malformed catalog identity", meta: { reason: "inventory-unavailable" } });
4632
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "Malformed catalog identity", meta: { reason: "inventory-unavailable" } });
4560
4633
  const qualified = { namespace: params.namespace, name };
4561
4634
  const table = { namespace: params.namespace, name: String(row.parent ?? "") };
4562
4635
  let identity;
@@ -4582,7 +4655,7 @@ async function inspectMigrationInventory(params) {
4582
4655
  case "constraint": {
4583
4656
  const constraintType = kinds[String(row.subtype)];
4584
4657
  if (constraintType === undefined)
4585
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "Unknown constraint catalog kind", meta: { reason: "inventory-unavailable" } });
4658
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "Unknown constraint catalog kind", meta: { reason: "inventory-unavailable" } });
4586
4659
  identity = { kind: "constraint", table, name, constraintType };
4587
4660
  break;
4588
4661
  }
@@ -4592,17 +4665,17 @@ async function inspectMigrationInventory(params) {
4592
4665
  const query = renderMysqlObjectMembers2({ dialect: getSqlDialect3({ dialect: params.dialect }), identity: { kind: "function", name: qualified, argumentTypes: [] } });
4593
4666
  const members = await params.executor({ text: query.text, values: [...query.values] });
4594
4667
  if (members.length === 0)
4595
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "MySQL function signature unavailable", meta: { reason: "inventory-unavailable", name } });
4668
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "MySQL function signature unavailable", meta: { reason: "inventory-unavailable", name } });
4596
4669
  argumentTypes = members.filter((member) => member.mode !== null).map((member) => ({ namespace: "mysql", name: String(member.type) }));
4597
4670
  }
4598
4671
  const candidate = { kind: "function", name: qualified, argumentTypes };
4599
4672
  if (!isSqlObjectIdentity2(candidate))
4600
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "Invalid function signature in catalog", meta: { reason: "inventory-unavailable" } });
4673
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "Invalid function signature in catalog", meta: { reason: "inventory-unavailable" } });
4601
4674
  identity = candidate;
4602
4675
  break;
4603
4676
  }
4604
4677
  default:
4605
- throw new VibeError17({ code: "VIBE_MIGRATION", message: `Unknown catalog object kind ${row.kind}`, meta: { reason: "inventory-unavailable" } });
4678
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: `Unknown catalog object kind ${row.kind}`, meta: { reason: "inventory-unavailable" } });
4606
4679
  }
4607
4680
  objects.push({ identity, ...typeof row.backing === "string" ? { backingConstraint: row.backing } : {} });
4608
4681
  if (params.dialect === "sqlite" && row.kind === "table" && typeof row.definition === "string" && !/^\s*CREATE\s+VIRTUAL\s+TABLE\b/i.test(row.definition)) {
@@ -4646,7 +4719,7 @@ async function inspectMigrationInventory(params) {
4646
4719
 
4647
4720
  // packages/migrate/src/module-ownership.ts
4648
4721
  function refusal(params) {
4649
- 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 });
4722
+ 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 });
4650
4723
  }
4651
4724
  function identities(params) {
4652
4725
  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)];
@@ -4707,7 +4780,7 @@ function assertModuleOwnership(params) {
4707
4780
  throw refusal({ reason: "module-mutates-application-state", moduleId: origin.moduleId });
4708
4781
  }
4709
4782
  function dependencyUnavailable(params) {
4710
- 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 } });
4783
+ 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 } });
4711
4784
  }
4712
4785
  async function assertCrossNamespaceModuleDependencies(params) {
4713
4786
  const { executor, origin } = params;
@@ -4769,15 +4842,15 @@ async function assertApplicationModuleState(params) {
4769
4842
  const options = { executor: params.executor, dialect: origin.dialect, from: state.from, to: state.to, removals: state.unmanagedRemovals, ...params.probe === undefined ? {} : { probe: params.probe } };
4770
4843
  const report = params.sqliteRestorePending === true ? await verifySqliteTransitionBeforeRestore(options) : await verifyMigrationTransition(options);
4771
4844
  if (!report.inSync)
4772
- 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 } });
4845
+ 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 } });
4773
4846
  }
4774
4847
  }
4775
4848
 
4776
4849
  // packages/migrate/src/module-adoption.ts
4777
- import { VibeError as VibeError19 } from "@vibeorm/schema";
4850
+ import { VibeError as VibeError20 } from "@vibeorm/schema";
4778
4851
  var MODULE_ADOPTION_METADATA = Object.freeze({ filename: "module-adoption.json", version: 1 });
4779
4852
  function invalid3() {
4780
- return new VibeError19({ 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" } });
4853
+ return new VibeError20({ 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" } });
4781
4854
  }
4782
4855
  function assertMarker(params) {
4783
4856
  const value = params.value;
@@ -4813,7 +4886,7 @@ function serializeModuleAdoption(params) {
4813
4886
  }
4814
4887
 
4815
4888
  // packages/migrate/src/sql-statements.ts
4816
- import { VibeError as VibeError20 } from "@vibeorm/schema";
4889
+ import { VibeError as VibeError21 } from "@vibeorm/schema";
4817
4890
  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;
4818
4891
  var BLOCK_OPENERS = new Set(["BEGIN", "CASE", "IF", "LOOP", "WHILE", "REPEAT"]);
4819
4892
  function splitSqlStatements(params) {
@@ -4889,7 +4962,7 @@ function splitSqlStatements(params) {
4889
4962
  continue;
4890
4963
  }
4891
4964
  if (dialect === "mysql" || dialect === undefined) {
4892
- 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 } });
4965
+ throw new VibeError21({ 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 } });
4893
4966
  }
4894
4967
  }
4895
4968
  if (value === endQuote) {
@@ -4963,10 +5036,10 @@ function joinSqlStatements(params) {
4963
5036
 
4964
5037
  // packages/migrate/src/module-origin.ts
4965
5038
  import { createHash } from "crypto";
4966
- import { VibeError as VibeError21, isSqlObjectIdentity as isSqlObjectIdentity3, sqlObjectKey as sqlObjectKey8 } from "@vibeorm/schema";
5039
+ import { VibeError as VibeError22, isSqlObjectIdentity as isSqlObjectIdentity3, sqlObjectKey as sqlObjectKey8 } from "@vibeorm/schema";
4967
5040
  var MODULE_ORIGIN_METADATA = Object.freeze({ filename: "module-origin.json", version: 1 });
4968
5041
  function invalid4() {
4969
- 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" } });
5042
+ return new VibeError22({ 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" } });
4970
5043
  }
4971
5044
  function record3(value) {
4972
5045
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -5030,7 +5103,7 @@ function assertMaterializedModuleMigration(params) {
5030
5103
  return;
5031
5104
  const restored = restoreModuleMigration({ migration, origin: migration.moduleOrigin });
5032
5105
  if (migration.desiredState === undefined || canonicalMigrationJson({ value: restored }) !== canonicalMigrationJson({ value: migration }))
5033
- 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 } });
5106
+ throw new VibeError22({ code: "VIBE_MIGRATION", message: "Materialized module metadata differs from its immutable source", meta: { reason: "module-source-state-mismatch", moduleId: migration.moduleOrigin.moduleId } });
5034
5107
  }
5035
5108
  function restoreModuleMigration(params) {
5036
5109
  const { migration, origin } = params;
@@ -5055,13 +5128,13 @@ function restoreModuleMigration(params) {
5055
5128
  // packages/migrate/src/fs.ts
5056
5129
  import { readFile, readdir } from "fs/promises";
5057
5130
  import { join } from "path";
5058
- import { VibeError as VibeError36, assertValidSchemaIR as assertValidSchemaIR4 } from "@vibeorm/schema";
5131
+ import { VibeError as VibeError37, assertValidSchemaIR as assertValidSchemaIR4 } from "@vibeorm/schema";
5059
5132
 
5060
5133
  // packages/migrate/src/migration-checksum.ts
5061
5134
  import { createHash as createHash2 } from "crypto";
5062
5135
 
5063
5136
  // packages/migrate/src/execution.ts
5064
- import { VibeError as VibeError22 } from "@vibeorm/schema";
5137
+ import { VibeError as VibeError23 } from "@vibeorm/schema";
5065
5138
  var EXECUTION_SIDECAR_FILENAME = "execution.json";
5066
5139
  var EXECUTION_SIDECAR_VERSION = 1;
5067
5140
  function isDefaultExecution(params) {
@@ -5100,7 +5173,7 @@ ${lines.join(`
5100
5173
  `)}`;
5101
5174
  }
5102
5175
  function invalid5(params) {
5103
- return new VibeError22({
5176
+ return new VibeError23({
5104
5177
  code: "VIBE_MIGRATION",
5105
5178
  message: `invalid execution metadata for migration "${params.migration}": ${params.detail}`,
5106
5179
  meta: { reason: "execution-metadata-invalid", migration: params.migration }
@@ -5151,7 +5224,7 @@ function parseExecutionSidecar(params) {
5151
5224
  try {
5152
5225
  parsed = JSON.parse(params.text);
5153
5226
  } catch (error) {
5154
- throw new VibeError22({
5227
+ throw new VibeError23({
5155
5228
  code: "VIBE_MIGRATION",
5156
5229
  message: `invalid execution metadata for migration "${migration}": not valid JSON`,
5157
5230
  meta: { reason: "execution-metadata-invalid", migration },
@@ -5250,7 +5323,7 @@ function migrationChecksum(params) {
5250
5323
  }
5251
5324
 
5252
5325
  // packages/migrate/src/shadow-guard.ts
5253
- import { VibeError as VibeError23 } from "@vibeorm/schema";
5326
+ import { VibeError as VibeError24 } from "@vibeorm/schema";
5254
5327
  import { getSqlDialect as getSqlDialect5, renderShadowOperation } from "@vibeorm/sql";
5255
5328
  var replayExecutors = new WeakSet;
5256
5329
  function isShadowReplayExecutor(params) {
@@ -5262,7 +5335,7 @@ async function assertDatabaseNotShadow(params) {
5262
5335
  const [query] = renderShadowOperation({ dialect: getSqlDialect5({ dialect: params.dialect }), operation: { kind: "markerExists" } });
5263
5336
  const rows = await params.executor({ text: query.text, values: [...query.values] });
5264
5337
  if (rows.length !== 0)
5265
- 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" } });
5338
+ 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" } });
5266
5339
  }
5267
5340
  function runShadowReplay(params) {
5268
5341
  replayExecutors.add(params.executor);
@@ -5272,7 +5345,7 @@ function runShadowReplay(params) {
5272
5345
  }
5273
5346
 
5274
5347
  // packages/migrate/src/external-dependencies.ts
5275
- import { VibeError as VibeError24, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
5348
+ import { VibeError as VibeError25, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
5276
5349
  import { getSqlDialect as getSqlDialect6, renderExternalDependents } from "@vibeorm/sql";
5277
5350
  function externalDependencyTargets(params) {
5278
5351
  return params.steps.flatMap((step) => {
@@ -5295,7 +5368,7 @@ function externalDependencyTargets(params) {
5295
5368
  });
5296
5369
  }
5297
5370
  function unavailable() {
5298
- return new VibeError24({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency row", meta: { reason: "external-inventory-unavailable" } });
5371
+ return new VibeError25({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency row", meta: { reason: "external-inventory-unavailable" } });
5299
5372
  }
5300
5373
  function text(params) {
5301
5374
  const value = params.row[params.key];
@@ -5336,7 +5409,7 @@ function assertNoExternalDependents(params) {
5336
5409
  const dependent = dependentOwner({ row, external: params.external });
5337
5410
  if (dependent.owner === undefined)
5338
5411
  continue;
5339
- throw new VibeError24({
5412
+ throw new VibeError25({
5340
5413
  code: "VIBE_MIGRATION",
5341
5414
  message: "An externally owned object depends on an application object this change drops or retypes; automatic DDL is refused",
5342
5415
  meta: { reason: "external-owned-dependency", owner: dependent.owner, ...dependent.identity === undefined ? {} : { identity: dependent.identity }, dependent: { type, identity }, dependency: target.dependency, referenced }
@@ -5357,9 +5430,9 @@ async function refuseExternalDependents(params) {
5357
5430
  }
5358
5431
 
5359
5432
  // packages/migrate/src/module-history.ts
5360
- import { VibeError as VibeError25, sqlObjectKey as sqlObjectKey9, providerToDialect as providerToDialect3 } from "@vibeorm/schema";
5433
+ import { VibeError as VibeError26, sqlObjectKey as sqlObjectKey9, providerToDialect as providerToDialect3 } from "@vibeorm/schema";
5361
5434
  function refusal2(params) {
5362
- 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 });
5435
+ 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 });
5363
5436
  }
5364
5437
  function moduleHistoryHeads(params) {
5365
5438
  const heads = new Map;
@@ -5466,11 +5539,11 @@ function assertModuleUpgradeHistory(params) {
5466
5539
  }
5467
5540
 
5468
5541
  // packages/migrate/src/module-journal.ts
5469
- import { VibeError as VibeError27 } from "@vibeorm/schema";
5542
+ import { VibeError as VibeError28 } from "@vibeorm/schema";
5470
5543
  import { getSqlDialect as getSqlDialect7, MODULE_JOURNAL_COLUMN, renderModuleJournal } from "@vibeorm/sql";
5471
5544
 
5472
5545
  // packages/migrate/src/journal-read.ts
5473
- import { VibeError as VibeError26 } from "@vibeorm/schema";
5546
+ import { VibeError as VibeError27 } from "@vibeorm/schema";
5474
5547
  var MISSING_TABLE_IDENTIFIERS = new Set(["42P01", "42S02", "ER_NO_SUCH_TABLE"]);
5475
5548
  function isMissingTable(params) {
5476
5549
  let current = params.cause;
@@ -5494,7 +5567,7 @@ async function readJournalRows(params) {
5494
5567
  if (presence.state === "missing")
5495
5568
  return;
5496
5569
  }
5497
- throw new VibeError26({ code: "VIBE_MIGRATION", message: "Cannot inspect migration history; check SELECT privileges", meta: { reason: "history-unavailable" }, cause });
5570
+ throw new VibeError27({ code: "VIBE_MIGRATION", message: "Cannot inspect migration history; check SELECT privileges", meta: { reason: "history-unavailable" }, cause });
5498
5571
  });
5499
5572
  if (rows === undefined)
5500
5573
  return;
@@ -5502,7 +5575,7 @@ async function readJournalRows(params) {
5502
5575
  for (const row of rows) {
5503
5576
  const index = row.statement_index;
5504
5577
  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)) {
5505
- throw new VibeError26({ code: "VIBE_MIGRATION", message: "Malformed migration journal row", meta: { reason: "history-unavailable" } });
5578
+ throw new VibeError27({ code: "VIBE_MIGRATION", message: "Malformed migration journal row", meta: { reason: "history-unavailable" } });
5506
5579
  }
5507
5580
  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 } : {} });
5508
5581
  }
@@ -5511,7 +5584,7 @@ async function readJournalRows(params) {
5511
5584
 
5512
5585
  // packages/migrate/src/module-journal.ts
5513
5586
  function invalid6() {
5514
- 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" } });
5587
+ 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" } });
5515
5588
  }
5516
5589
  function parseModuleJournalEntry(params) {
5517
5590
  return parseMigrationMetadata({ text: params.text, failure: invalid6, parse: (value) => {
@@ -5570,7 +5643,7 @@ function moduleRecordComplete(params) {
5570
5643
  }
5571
5644
 
5572
5645
  // packages/migrate/src/runner.ts
5573
- import { VibeError as VibeError35 } from "@vibeorm/schema";
5646
+ import { VibeError as VibeError36 } from "@vibeorm/schema";
5574
5647
  import {
5575
5648
  DIALECT_CAPABILITIES as DIALECT_CAPABILITIES3,
5576
5649
  getSqlDialect as getSqlDialect9,
@@ -5580,7 +5653,7 @@ import {
5580
5653
  } from "@vibeorm/sql";
5581
5654
 
5582
5655
  // packages/migrate/src/recovery.ts
5583
- import { VibeError as VibeError28 } from "@vibeorm/schema";
5656
+ import { VibeError as VibeError29 } from "@vibeorm/schema";
5584
5657
  import {
5585
5658
  getSqlDialect as getSqlDialect8,
5586
5659
  renderColumnStateQuery,
@@ -5596,7 +5669,7 @@ function constraintUnverifiable(params) {
5596
5669
  });
5597
5670
  }
5598
5671
  function conflict2(params) {
5599
- return new VibeError28({
5672
+ return new VibeError29({
5600
5673
  code: "VIBE_MIGRATION",
5601
5674
  message: params.message,
5602
5675
  meta: { reason: params.reason, ...params.meta }
@@ -5701,10 +5774,10 @@ async function establishOperationState(params) {
5701
5774
 
5702
5775
  // packages/migrate/src/differ.ts
5703
5776
  import { assertIndexOperatorClasses as assertIndexOperatorClasses2, isEnumType as isEnumType6, qualifiedSqlName as qualifiedSqlName9, unsupportedCapability as unsupportedCapability10 } from "@vibeorm/schema";
5704
- import { VibeError as VibeError30 } from "@vibeorm/schema";
5777
+ import { VibeError as VibeError31 } from "@vibeorm/schema";
5705
5778
 
5706
5779
  // packages/migrate/src/renames.ts
5707
- import { VibeError as VibeError29, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
5780
+ import { VibeError as VibeError30, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
5708
5781
  function tableOf(model) {
5709
5782
  return model.dbName ?? model.name;
5710
5783
  }
@@ -5715,7 +5788,7 @@ function hasColumn(params) {
5715
5788
  return params.model.fields.some((field) => (field.dbName ?? field.name) === params.column);
5716
5789
  }
5717
5790
  function ambiguous(params) {
5718
- return new VibeError29({
5791
+ return new VibeError30({
5719
5792
  code: "VIBE_MIGRATION",
5720
5793
  message: `rename refused: ${params.detail}`,
5721
5794
  meta: { reason: "rename-ambiguous", ...params.meta }
@@ -5817,17 +5890,34 @@ function planRenames(params) {
5817
5890
 
5818
5891
  // packages/migrate/src/differ.ts
5819
5892
  function diffSchemas(params) {
5893
+ return diffSchemasWithNormalization(params).steps;
5894
+ }
5895
+ function diffSchemasWithNormalization(params) {
5820
5896
  const dialect = params.dialect ?? "postgres";
5821
5897
  assertIndexOperatorClasses2({ schema: params.to, dialect });
5822
- for (const kind of ["models", "enums"])
5898
+ for (const kind of ["models", "enums"]) {
5899
+ const sourceByName = new Map;
5900
+ const sourcePhysical = new Set;
5901
+ const targetPhysical = new Set(params.to[kind].map((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect })));
5902
+ const removedByBasename = new Map;
5903
+ for (const entry of params.from[kind]) {
5904
+ if (!sourceByName.has(entry.name))
5905
+ sourceByName.set(entry.name, entry);
5906
+ const basename = entry.dbName ?? entry.name;
5907
+ const key = physicalNameKey({ name: basename, namespace: entry.namespace, dialect });
5908
+ sourcePhysical.add(key);
5909
+ if (!targetPhysical.has(key) && !removedByBasename.has(basename))
5910
+ removedByBasename.set(basename, entry);
5911
+ }
5823
5912
  for (const entry of params.to[kind]) {
5824
5913
  const key = physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect });
5825
- 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 })));
5826
- 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))
5914
+ const before = sourceByName.get(entry.name) ?? removedByBasename.get(entry.dbName ?? entry.name);
5915
+ if (before !== undefined && qualifiedSqlName9({ name: entry.name, namespace: before.namespace }).namespace !== qualifiedSqlName9({ name: entry.name, namespace: entry.namespace }).namespace && !sourcePhysical.has(key))
5827
5916
  throw unsupportedCapability10({ dialect, feature: "automaticNamespaceMove", hint: "Moving populated objects to a fixed namespace requires an explicit reviewed migration" });
5828
5917
  }
5829
- for (const schema of [params.from, params.to])
5830
- normalizeSchema({ schema, dialect });
5918
+ }
5919
+ let from = normalizeSchema({ schema: params.from, dialect });
5920
+ const to = normalizeSchema({ schema: params.to, dialect });
5831
5921
  for (const schema of [params.from, params.to])
5832
5922
  if (dialect !== "postgres" && schema.models.some((model) => (model.exclusions?.length ?? 0) > 0))
5833
5923
  throw unsupportedCapability10({ dialect, feature: "exclusionConstraints" });
@@ -5836,11 +5926,12 @@ function diffSchemas(params) {
5836
5926
  if (dialect !== "postgres")
5837
5927
  refuseScalarListColumns({ schema: params.to, dialect });
5838
5928
  const renamed = planRenames({ from: params.from, to: params.to, renames: params.renames ?? [], dialect });
5929
+ if (renamed.from !== params.from)
5930
+ from = normalizeSchema({ schema: renamed.from, dialect });
5839
5931
  params = { ...params, from: renamed.from };
5840
- const from = normalizeSchema({ schema: params.from, dialect });
5841
- const to = normalizeSchema({ schema: params.to, dialect });
5932
+ const sourceNamespaces = new Set(dialect === "postgres" ? schemaNamespaces({ schema: params.from }) : []);
5842
5933
  const buckets = {
5843
- createNamespace: dialect === "postgres" ? schemaNamespaces({ schema: params.to }).filter((namespace) => !schemaNamespaces({ schema: params.from }).includes(namespace)).map((namespace) => ({ kind: "createNamespace", namespace, destructive: false })) : [],
5934
+ createNamespace: dialect === "postgres" ? schemaNamespaces({ schema: params.to }).filter((namespace) => !sourceNamespaces.has(namespace)).map((namespace) => ({ kind: "createNamespace", namespace, destructive: false })) : [],
5844
5935
  dropExclusion: [],
5845
5936
  addExclusion: [],
5846
5937
  dropView: [],
@@ -5868,16 +5959,22 @@ function diffSchemas(params) {
5868
5959
  diffViews({ from, to, buckets });
5869
5960
  diffJoinTables({ from, to, buckets });
5870
5961
  diffTables({ from, to, buckets, dialect });
5962
+ const sourceModels = new Map;
5963
+ for (const model of params.from.models) {
5964
+ const key = physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect });
5965
+ if (!sourceModels.has(key))
5966
+ sourceModels.set(key, model);
5967
+ }
5871
5968
  for (const model of params.to.models) {
5872
5969
  const table = model.dbName ?? model.name;
5873
5970
  const placement = model.namespace === undefined ? {} : { namespace: model.namespace };
5874
- const previous = params.from.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect }) === physicalNameKey({ name: table, namespace: model.namespace, dialect }));
5971
+ const previous = sourceModels.get(physicalNameKey({ name: table, namespace: model.namespace, dialect }));
5875
5972
  for (const exclusion of model.exclusions ?? []) {
5876
5973
  const before = previous?.exclusions?.find((entry) => entry.name === exclusion.name);
5877
5974
  if (before?.definition === exclusion.definition)
5878
5975
  continue;
5879
5976
  if (before?.catalogDefinition)
5880
- throw new VibeError30({ code: "VIBE_MIGRATION", message: "Exclusion comparison requires normalized evidence", meta: { reason: "exclusion-inspection-unavailable", table, name: exclusion.name } });
5977
+ throw new VibeError31({ code: "VIBE_MIGRATION", message: "Exclusion comparison requires normalized evidence", meta: { reason: "exclusion-inspection-unavailable", table, name: exclusion.name } });
5881
5978
  if (before !== undefined)
5882
5979
  buckets.dropExclusion.push({ kind: "dropExclusion", table, ...placement, exclusion: before, destructive: false });
5883
5980
  buckets.addExclusion.push({ kind: "addExclusion", table, ...placement, exclusion, destructive: false });
@@ -5887,7 +5984,8 @@ function diffSchemas(params) {
5887
5984
  buckets.dropExclusion.push({ kind: "dropExclusion", table, exclusion, destructive: false });
5888
5985
  }
5889
5986
  refuseDependentEnumStorageChanges({ from, to, buckets, dialect });
5890
- return withRlsSteps({ from: params.from, to: params.to, steps: [
5987
+ refuseMysqlForeignKeyColumnRewrites({ from, to, buckets, dialect });
5988
+ return { from, to, steps: withRlsSteps({ from: params.from, to: params.to, steps: [
5891
5989
  ...buckets.createNamespace,
5892
5990
  ...renamed.steps,
5893
5991
  ...buckets.dropView,
@@ -5911,7 +6009,7 @@ function diffSchemas(params) {
5911
6009
  ...buckets.addForeignKey,
5912
6010
  ...buckets.createJoinTable,
5913
6011
  ...buckets.dropEnum
5914
- ] });
6012
+ ] }) };
5915
6013
  }
5916
6014
  function refuseScalarListColumns(params) {
5917
6015
  const { schema, dialect } = params;
@@ -5978,6 +6076,40 @@ function refuseDependentEnumStorageChanges(params) {
5978
6076
  }
5979
6077
  }
5980
6078
  }
6079
+ function refuseMysqlForeignKeyColumnRewrites(params) {
6080
+ const { from, to, buckets, dialect } = params;
6081
+ if (dialect !== "mysql")
6082
+ return;
6083
+ const droppedFks = new Set(buckets.dropForeignKey.map((step) => step.kind === "dropForeignKey" ? `${step.table}\x00${step.constraintName}` : ""));
6084
+ for (const step of buckets.alterColumn) {
6085
+ if (step.kind !== "alterColumn")
6086
+ continue;
6087
+ const collation = step.changes.includes("collation");
6088
+ const type = step.changes.includes("type") && !isMysqlVarcharWidening({ from: step.from, to: step.to });
6089
+ if (!collation && !type)
6090
+ continue;
6091
+ 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));
6092
+ 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))));
6093
+ if (!joined && !foreign)
6094
+ continue;
6095
+ throw unsupportedCapability10({
6096
+ dialect,
6097
+ feature: `${collation ? "collation" : "type"} change of "${step.table}"."${step.column}" ${joined ? "referenced by an implicit many-to-many join table" : "in a foreign key"}`,
6098
+ hint: `drop the foreign key, change both columns' ${collation ? "collation" : "type"} and re-add it in a reviewed manual migration`
6099
+ });
6100
+ }
6101
+ }
6102
+ function isMysqlVarcharWidening(params) {
6103
+ const length = (field) => {
6104
+ if (isEnumType6(field.type) || field.isList || field.isUnsupported === true)
6105
+ return null;
6106
+ const match = /^varchar\((\d+)\)$/.exec(normalizeMysqlType({ raw: mysqlColumnTypeSql({ field, enums: new Map }) }));
6107
+ return match === null ? null : Number(match[1]);
6108
+ };
6109
+ const before = length(params.from);
6110
+ const after = length(params.to);
6111
+ return before !== null && after !== null && after >= before;
6112
+ }
5981
6113
  function diffViews(params) {
5982
6114
  const { from, to, buckets } = params;
5983
6115
  for (const [name, view] of from.views) {
@@ -6133,7 +6265,7 @@ function diffOneTable(params) {
6133
6265
  from: before.field,
6134
6266
  to: column.field,
6135
6267
  changes,
6136
- destructive: narrowingType || collationChanged
6268
+ destructive: narrowingType || collationChanged || dialect === "mysql" && before.nullable && !column.nullable
6137
6269
  });
6138
6270
  }
6139
6271
  for (const [name] of previous.columns) {
@@ -6141,9 +6273,7 @@ function diffOneTable(params) {
6141
6273
  buckets.dropColumn.push({ kind: "dropColumn", table, ...placement, column: name, destructive: true });
6142
6274
  }
6143
6275
  }
6144
- const pkBefore = previous.pk.join(",");
6145
- const pkAfter = current.pk.join(",");
6146
- if (pkBefore !== pkAfter) {
6276
+ if (previous.pk.length !== current.pk.length || previous.pk.some((column, index) => column !== current.pk[index])) {
6147
6277
  if (previous.pk.length > 0) {
6148
6278
  buckets.dropPrimaryKey.push({ kind: "dropPrimaryKey", table, ...placement, destructive: false, ...previous.model.primaryKeyConstraintName === undefined ? {} : { constraintName: previous.model.primaryKeyConstraintName } });
6149
6279
  }
@@ -6163,7 +6293,7 @@ function diffOneTable(params) {
6163
6293
  if (before === expression)
6164
6294
  continue;
6165
6295
  if (before !== undefined && previous.model.checks?.some((check) => check.name === name && check.catalogExpression === true))
6166
- 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 } });
6296
+ 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 } });
6167
6297
  if (before !== undefined)
6168
6298
  buckets.dropCheck.push({ kind: "dropCheck", table, ...placement, name, destructive: false });
6169
6299
  buckets.addCheck.push({ kind: "addCheck", table, ...placement, name, expression, destructive: false });
@@ -6249,11 +6379,11 @@ function diffOneTable(params) {
6249
6379
  }
6250
6380
 
6251
6381
  // packages/migrate/src/ddl/render.ts
6252
- import { VibeError as VibeError34, sqlObjectKey as sqlObjectKey10 } from "@vibeorm/schema";
6382
+ import { VibeError as VibeError35, sqlObjectKey as sqlObjectKey10 } from "@vibeorm/schema";
6253
6383
 
6254
6384
  // packages/migrate/src/ddl/postgres.ts
6255
6385
  import { renderExclusionSetup } from "@vibeorm/sql";
6256
- import { VibeError as VibeError31, assertSqlNamespace as assertSqlNamespace3, defaultPrimaryKeyName as defaultPrimaryKeyName3, implicitJoinTableObjectNames as implicitJoinTableObjectNames2, isEnumType as isEnumType7 } from "@vibeorm/schema";
6386
+ import { VibeError as VibeError32, assertSqlNamespace as assertSqlNamespace3, defaultPrimaryKeyName as defaultPrimaryKeyName3, implicitJoinTableObjectNames as implicitJoinTableObjectNames2, isEnumType as isEnumType7 } from "@vibeorm/schema";
6257
6387
  import { postgresDialect as postgresDialect9, renderRlsPolicyPath, renderIndexColumns } from "@vibeorm/sql";
6258
6388
  function renderPostgresSteps(params) {
6259
6389
  refuseSameTransactionEnumUse({ steps: params.steps });
@@ -6290,7 +6420,7 @@ function refuseSameTransactionEnumUse(params) {
6290
6420
  const found = use(field);
6291
6421
  if (found === undefined)
6292
6422
  continue;
6293
- throw new VibeError31({
6423
+ throw new VibeError32({
6294
6424
  code: "VIBE_MIGRATION",
6295
6425
  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`,
6296
6426
  meta: { enum: found.enumName, value: found.value, table: step.table, column: field.dbName ?? field.name, rule: "enum-value-used-in-adding-transaction" }
@@ -6364,7 +6494,7 @@ ${lines.join(`,
6364
6494
  case "dropIndex":
6365
6495
  if (step.constraintName !== undefined) {
6366
6496
  if (step.table === undefined)
6367
- throw new VibeError31({ code: "VIBE_MIGRATION", message: "Constraint-backed index removal requires its owning table" });
6497
+ throw new VibeError32({ code: "VIBE_MIGRATION", message: "Constraint-backed index removal requires its owning table" });
6368
6498
  return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} DROP CONSTRAINT ${q2(step.constraintName)};`];
6369
6499
  }
6370
6500
  return [`DROP INDEX ${qualifiedName(step.name, step.namespace)};`];
@@ -6429,7 +6559,7 @@ function columnDefSql(params) {
6429
6559
  const { field } = params;
6430
6560
  const name = q2(field.dbName ?? field.name);
6431
6561
  if (field.default?.kind === "autoincrement" && !isIdentityColumn({ field })) {
6432
- throw new VibeError31({
6562
+ throw new VibeError32({
6433
6563
  code: "VIBE_MIGRATION",
6434
6564
  message: `autoincrement default on "${field.name}" requires a non-list Int or BigInt column`,
6435
6565
  meta: { field: field.name }
@@ -6465,7 +6595,7 @@ function renderAlterColumn(params) {
6465
6595
  }
6466
6596
  if (step.changes.includes("default") || droppedDefault) {
6467
6597
  if (step.to.default?.kind === "autoincrement") {
6468
- throw new VibeError31({
6598
+ throw new VibeError32({
6469
6599
  code: "VIBE_MIGRATION",
6470
6600
  message: `cannot add autoincrement to existing column "${step.table}"."${step.column}" \u2014 drop and recreate the column instead`,
6471
6601
  meta: { table: step.table, column: step.column }
@@ -6485,7 +6615,7 @@ function q2(name) {
6485
6615
  }
6486
6616
 
6487
6617
  // packages/migrate/src/ddl/sqlite.ts
6488
- import { VibeError as VibeError32, implicitJoinTableObjectNames as implicitJoinTableObjectNames3, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
6618
+ import { VibeError as VibeError33, implicitJoinTableObjectNames as implicitJoinTableObjectNames3, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
6489
6619
  import { sqliteDialect as sqliteDialect3 } from "@vibeorm/sql";
6490
6620
 
6491
6621
  // packages/migrate/src/ddl/drop-order.ts
@@ -6633,7 +6763,7 @@ function renderSqliteSteps(params) {
6633
6763
  continue;
6634
6764
  const awaitsNewParent = [...toNorm?.tables.get(table)?.fks.values() ?? []].some((fk) => createdTables.has(fk.refTable));
6635
6765
  if (awaitsNewParent) {
6636
- throw new VibeError32({
6766
+ throw new VibeError33({
6637
6767
  code: "VIBE_MIGRATION",
6638
6768
  message: `cannot order this sqlite migration safely: rebuilding "${table}" must happen BEFORE the dropped table(s) it references (${[
6639
6769
  ...droppedTables
@@ -6662,7 +6792,7 @@ function renderSqliteSteps(params) {
6662
6792
  switch (step.kind) {
6663
6793
  case "addExclusion":
6664
6794
  case "dropExclusion":
6665
- throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6795
+ throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6666
6796
  case "createEnum":
6667
6797
  case "dropEnum":
6668
6798
  case "addEnumValue":
@@ -6748,7 +6878,7 @@ function renderSqliteSteps(params) {
6748
6878
  case "renameColumn": {
6749
6879
  const renamed = step.kind === "renameTable" ? step.from : step.table;
6750
6880
  if (rebuildSet.has(renamed) || step.kind === "renameTable" && rebuildSet.has(step.to)) {
6751
- throw new VibeError32({
6881
+ throw new VibeError33({
6752
6882
  code: "VIBE_MIGRATION",
6753
6883
  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`,
6754
6884
  meta: { reason: "rename-ambiguous", table: renamed, dialect: "sqlite" }
@@ -6802,7 +6932,7 @@ function classifyRebuilds(params) {
6802
6932
  switch (step.kind) {
6803
6933
  case "addExclusion":
6804
6934
  case "dropExclusion":
6805
- throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6935
+ throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6806
6936
  case "alterColumn":
6807
6937
  case "dropForeignKey":
6808
6938
  case "addPrimaryKey":
@@ -6863,7 +6993,7 @@ function renderTableRebuild(params) {
6863
6993
  const source = from.tables.get(table);
6864
6994
  const target = to.tables.get(table);
6865
6995
  if (source === undefined || target === undefined) {
6866
- throw new VibeError32({
6996
+ throw new VibeError33({
6867
6997
  code: "VIBE_MIGRATION",
6868
6998
  message: `cannot rebuild "${table}" \u2014 the table is missing from the ${source === undefined ? "from" : "to"} schema`,
6869
6999
  meta: { table }
@@ -6998,7 +7128,7 @@ function columnDefSql2(params) {
6998
7128
  const { field, constraintTable, enums, inlinePkAutoincrement } = params;
6999
7129
  const name = field.dbName ?? field.name;
7000
7130
  if (field.default?.kind === "autoincrement" && !inlinePkAutoincrement) {
7001
- throw new VibeError32({
7131
+ throw new VibeError33({
7002
7132
  code: "VIBE_MIGRATION",
7003
7133
  message: `autoincrement on sqlite requires "${field.name}" to be the single-column Int or BigInt primary key`,
7004
7134
  meta: { field: field.name }
@@ -7017,7 +7147,7 @@ function columnDefSql2(params) {
7017
7147
  const enumName = field.type.enum;
7018
7148
  const values = resolveEnumValues({ name: enumName, enums });
7019
7149
  if (values === undefined) {
7020
- throw new VibeError32({
7150
+ throw new VibeError33({
7021
7151
  code: "VIBE_MIGRATION",
7022
7152
  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`,
7023
7153
  meta: { field: field.name, enum: enumName }
@@ -7052,7 +7182,7 @@ function requireSchemas(params) {
7052
7182
  const { step, fromNorm, toNorm } = params;
7053
7183
  if (fromNorm !== undefined && toNorm !== undefined)
7054
7184
  return;
7055
- throw new VibeError32({
7185
+ throw new VibeError33({
7056
7186
  code: "VIBE_MIGRATION",
7057
7187
  message: `sqlite step "${describeStep({ step })}" requires a table rebuild \u2014 renderSteps needs the { from, to } schemas to construct it`,
7058
7188
  meta: { step: step.kind }
@@ -7063,7 +7193,7 @@ function q3(name) {
7063
7193
  }
7064
7194
 
7065
7195
  // packages/migrate/src/ddl/mysql.ts
7066
- import { VibeError as VibeError33, implicitJoinTableObjectNames as implicitJoinTableObjectNames4, unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
7196
+ import { VibeError as VibeError34, implicitJoinTableObjectNames as implicitJoinTableObjectNames4, unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
7067
7197
  import { mysqlDialect as mysqlDialect3 } from "@vibeorm/sql";
7068
7198
  function renderMysqlSteps(params) {
7069
7199
  for (const step of params.steps) {
@@ -7211,12 +7341,12 @@ ${lines.join(`,
7211
7341
  ];
7212
7342
  case "addExclusion":
7213
7343
  case "dropExclusion":
7214
- throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "mysql", feature: "exclusionConstraints" } });
7344
+ throw new VibeError34({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "mysql", feature: "exclusionConstraints" } });
7215
7345
  case "dropCheck":
7216
7346
  return [`ALTER TABLE ${q4(step.table)} DROP CHECK ${q4(step.name)};`];
7217
7347
  case "dropIndex": {
7218
7348
  if (step.table === undefined) {
7219
- throw new VibeError33({
7349
+ throw new VibeError34({
7220
7350
  code: "VIBE_MIGRATION",
7221
7351
  message: `dropIndex "${step.name}" carries no table \u2014 mysql's DROP INDEX requires one`,
7222
7352
  meta: { index: step.name }
@@ -7246,8 +7376,8 @@ ${lines.join(`,
7246
7376
  case "dropForeignKey":
7247
7377
  return [`ALTER TABLE ${q4(step.table)} DROP FOREIGN KEY ${q4(step.constraintName)};`];
7248
7378
  case "createJoinTable": {
7249
- const aType = mysqlColumnTypeSql({ field: { ...step.a.field, isList: false }, enums });
7250
- const bType = mysqlColumnTypeSql({ field: { ...step.b.field, isList: false }, enums });
7379
+ const aType = joinColumnTypeSql({ field: step.a.field, enums });
7380
+ const bType = joinColumnTypeSql({ field: step.b.field, enums });
7251
7381
  const names = implicitJoinTableObjectNames4({ table: step.table });
7252
7382
  return [
7253
7383
  `CREATE TABLE ${q4(step.table)} (
@@ -7268,12 +7398,17 @@ ${lines.join(`,
7268
7398
  return [`ALTER TABLE ${q4(step.table)} DROP PRIMARY KEY;`];
7269
7399
  }
7270
7400
  }
7401
+ function joinColumnTypeSql(params) {
7402
+ const { field, enums } = params;
7403
+ const type = mysqlColumnTypeSql({ field: { ...field, isList: false }, enums });
7404
+ return field.collation === undefined ? type : `${type} COLLATE ${q4(field.collation)}`;
7405
+ }
7271
7406
  function columnDefSql3(params) {
7272
7407
  const { field, enums } = params;
7273
7408
  const name = q4(field.dbName ?? field.name);
7274
7409
  const autoincrement = field.default?.kind === "autoincrement";
7275
7410
  if (autoincrement && field.type !== "Int" && field.type !== "BigInt") {
7276
- throw new VibeError33({
7411
+ throw new VibeError34({
7277
7412
  code: "VIBE_MIGRATION",
7278
7413
  message: `autoincrement default on "${field.name}" requires a non-list Int or BigInt column`,
7279
7414
  meta: { field: field.name }
@@ -7295,7 +7430,7 @@ function renderAlterColumn2(params) {
7295
7430
  const { step, enums } = params;
7296
7431
  assertEnumStringTransition({ from: step.from, to: step.to, dialect: "mysql", table: step.table, column: step.column });
7297
7432
  if (step.to.default?.kind === "autoincrement" && step.from.default?.kind !== "autoincrement") {
7298
- throw new VibeError33({
7433
+ throw new VibeError34({
7299
7434
  code: "VIBE_MIGRATION",
7300
7435
  message: `cannot add autoincrement to existing column "${step.table}"."${step.column}" \u2014 drop and recreate the column instead`,
7301
7436
  meta: { table: step.table, column: step.column }
@@ -7335,24 +7470,24 @@ function declaredSqlitePreservation(params) {
7335
7470
  continue;
7336
7471
  const facts = object.expected?.facts;
7337
7472
  if (facts === undefined)
7338
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "SQLite rebuild requires independently declared preservation definitions", meta: { reason: "sqlite-preservation-unavailable", identity } });
7473
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "SQLite rebuild requires independently declared preservation definitions", meta: { reason: "sqlite-preservation-unavailable", identity } });
7339
7474
  if (identity.kind === "trigger" || identity.kind === "index") {
7340
7475
  if (identity.kind === "index" && to.models.some((model) => (model.dbName ?? model.name) === identity.table.name && model.indexes.some((index) => index.name === identity.name)))
7341
7476
  continue;
7342
7477
  if (typeof facts.definition !== "string")
7343
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite restoration SQL", meta: { reason: "sqlite-preservation-unavailable", identity } });
7478
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Missing SQLite restoration SQL", meta: { reason: "sqlite-preservation-unavailable", identity } });
7344
7479
  const statements = restore.get(identity.table.name) ?? [];
7345
7480
  statements.push(facts.definition);
7346
7481
  restore.set(identity.table.name, statements);
7347
7482
  } else if (identity.kind === "column" || identity.kind === "generatedColumn") {
7348
7483
  if (typeof facts.columnDefinition !== "string")
7349
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite column clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7484
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Missing SQLite column clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7350
7485
  const fields = columns.get(identity.table.name) ?? new Map;
7351
7486
  fields.set(identity.column, { sql: facts.columnDefinition, generated: Number(facts.generated) > 1 });
7352
7487
  columns.set(identity.table.name, fields);
7353
7488
  } else if (identity.kind === "columnDefault") {
7354
7489
  if (facts.definition !== null && typeof facts.definition !== "string")
7355
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite default clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7490
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Missing SQLite default clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7356
7491
  const expression = facts.definition;
7357
7492
  to = { ...to, models: to.models.map((model) => (model.dbName ?? model.name) !== identity.table.name ? model : { ...model, fields: model.fields.map((field) => {
7358
7493
  if ((field.dbName ?? field.name) !== identity.column)
@@ -7389,7 +7524,7 @@ function renderSteps(params) {
7389
7524
  }
7390
7525
 
7391
7526
  // packages/migrate/src/extensions.ts
7392
- import { extensionDeclarations } from "@vibeorm/schema";
7527
+ import { extensionDeclarations, providerToDialect as providerToDialect4, qualifiedSqlName as qualifiedSqlName10, SQL_NAMESPACES } from "@vibeorm/schema";
7393
7528
  function collectExtensionArtifacts(params) {
7394
7529
  const { schema, dialect, extensions } = params;
7395
7530
  const setup = [];
@@ -7456,6 +7591,10 @@ function stripExtensionArtifacts(params) {
7456
7591
  const models = [];
7457
7592
  for (const model of schema.models) {
7458
7593
  const tableName = model.dbName ?? model.name;
7594
+ if (providerToDialect4({ provider: schema.datasource.provider }) === "postgres" && qualifiedSqlName10({ name: tableName, namespace: model.namespace }).namespace !== SQL_NAMESPACES.postgres) {
7595
+ models.push(model);
7596
+ continue;
7597
+ }
7459
7598
  if (tables.exact.has(tableName) || tables.prefixes.some((prefix) => tableName.startsWith(prefix))) {
7460
7599
  continue;
7461
7600
  }
@@ -7479,6 +7618,8 @@ function planMissingArtifacts(params) {
7479
7618
  return [];
7480
7619
  const modelByTable = new Map;
7481
7620
  for (const model of introspected.models) {
7621
+ if (providerToDialect4({ provider: introspected.datasource.provider }) === "postgres" && qualifiedSqlName10({ name: model.dbName ?? model.name, namespace: model.namespace }).namespace !== SQL_NAMESPACES.postgres)
7622
+ continue;
7482
7623
  modelByTable.set(model.dbName ?? model.name, model);
7483
7624
  }
7484
7625
  return artifacts.filter((artifact) => {
@@ -7637,10 +7778,10 @@ function assertNativeMigrationMetadata(params) {
7637
7778
  if (!native)
7638
7779
  return;
7639
7780
  if (!DIALECT_CAPABILITIES3[params.dialect].nativeRowLevelSecurity) {
7640
- throw new VibeError35({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Native RLS migrations are unsupported on ${params.dialect}`, meta: { feature: "nativeRowLevelSecurity", dialect: params.dialect } });
7781
+ throw new VibeError36({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Native RLS migrations are unsupported on ${params.dialect}`, meta: { feature: "nativeRowLevelSecurity", dialect: params.dialect } });
7641
7782
  }
7642
7783
  if (params.migration.rls === undefined) {
7643
- 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 } });
7784
+ 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 } });
7644
7785
  }
7645
7786
  }
7646
7787
  var PG_ADVISORY_LOCK_KEY = 1447645253;
@@ -7721,12 +7862,12 @@ function generateMigration(params) {
7721
7862
  }),
7722
7863
  ...orderedMigrationObjects({ objects: to.migrationObjects ?? [] }).flatMap((object) => {
7723
7864
  if (object.dialect !== dialect)
7724
- throw new VibeError35({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}` });
7865
+ throw new VibeError36({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}` });
7725
7866
  const previous = (params.desiredState?.from.schema.migrationObjects ?? from.migrationObjects)?.find((entry) => entry.id === object.id);
7726
7867
  if (previous === undefined)
7727
7868
  return [...object.create];
7728
7869
  if (JSON.stringify(previous) !== JSON.stringify(object))
7729
- 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 } });
7870
+ 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 } });
7730
7871
  return [];
7731
7872
  })
7732
7873
  ];
@@ -7752,8 +7893,8 @@ async function withMigrationLock(params) {
7752
7893
  }, (cause) => releaseLock({ executor, dialect: params.dialect }).then(() => {
7753
7894
  throw cause;
7754
7895
  }, (cleanupCause) => {
7755
- if (cause instanceof VibeError35)
7756
- throw new VibeError35({ code: cause.code, message: cause.message, meta: { ...cause.meta, cleanupCause }, cause });
7896
+ if (cause instanceof VibeError36)
7897
+ throw new VibeError36({ code: cause.code, message: cause.message, meta: { ...cause.meta, cleanupCause }, cause });
7757
7898
  throw cause;
7758
7899
  }));
7759
7900
  };
@@ -7783,11 +7924,11 @@ function assertMigrationMetadata(params) {
7783
7924
  assertModuleOwnership({ origin: migration.moduleOrigin, desiredState: migration.desiredState });
7784
7925
  }
7785
7926
  if (migration.desiredStatePending === true)
7786
- 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 } });
7927
+ 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 } });
7787
7928
  if (migration.desiredState !== undefined) {
7788
7929
  assertDesiredState({ value: migration.desiredState, migration: migration.name });
7789
7930
  if (migration.desiredState.dialect !== dialect)
7790
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch", migration: migration.name } });
7931
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch", migration: migration.name } });
7791
7932
  }
7792
7933
  assertNativeMigrationMetadata({ migration, sql: migration.sql, dialect });
7793
7934
  assertExecutable({ migration, execution: migration.execution, sql: migration.sql, dialect });
@@ -7797,7 +7938,7 @@ async function applyMigrations(params) {
7797
7938
  const dialect = params.dialect ?? "postgres";
7798
7939
  const trustedReplay = isShadowReplayExecutor({ executor });
7799
7940
  if (params.history !== undefined && params.history !== "complete" && params.history !== "subset")
7800
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Unknown migration history validation mode", meta: { reason: "history-mode-invalid" } });
7941
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Unknown migration history validation mode", meta: { reason: "history-mode-invalid" } });
7801
7942
  if (params.history === "complete")
7802
7943
  assertDistinctMigrationNames({ migrations });
7803
7944
  for (const migration of migrations)
@@ -7813,7 +7954,7 @@ async function applyMigrations(params) {
7813
7954
  assertModuleUpgradeHistory({ migrations, records: moduleRecords });
7814
7955
  const namespace = await migrationNamespace({ executor, dialect });
7815
7956
  if (dialect !== "postgres" && migrations.some((migration) => migration.moduleOrigin !== undefined && moduleNamespace({ origin: migration.moduleOrigin }) !== namespace))
7816
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Module placement differs from the active database namespace", meta: { reason: "module-placement-mismatch" } });
7957
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module placement differs from the active database namespace", meta: { reason: "module-placement-mismatch" } });
7817
7958
  }
7818
7959
  if (params.history === "complete")
7819
7960
  assertCompleteHistory({ migrations, recorded, dialect });
@@ -7824,7 +7965,7 @@ async function applyMigrations(params) {
7824
7965
  });
7825
7966
  const adopted = pending.find((migration) => migration.moduleAdoption !== undefined);
7826
7967
  if (adopted !== undefined && !trustedReplay)
7827
- 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 } });
7968
+ 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 } });
7828
7969
  if (external !== undefined && !trustedReplay)
7829
7970
  for (const migration of pending)
7830
7971
  await assertExternalMigrationMutation({ executor, migration, dialect, namespace: await migrationNamespace({ executor, dialect }), external });
@@ -7840,7 +7981,7 @@ async function applyMigrations(params) {
7840
7981
  await assertLiveModuleOwnership({ executor, origin: migration.moduleOrigin, resume: stored !== undefined });
7841
7982
  if (stored !== undefined) {
7842
7983
  if (stored.statementIndex < 0) {
7843
- throw new VibeError35({
7984
+ throw new VibeError36({
7844
7985
  code: "VIBE_MIGRATION",
7845
7986
  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`,
7846
7987
  meta: { migration: migration.name, statementIndex: stored.statementIndex }
@@ -7998,7 +8139,7 @@ async function repairMigration(params) {
7998
8139
  const { executor, name, dialect } = params;
7999
8140
  const moduleRecord = (await readModuleJournal({ executor, dialect })).get(name);
8000
8141
  if (moduleRecord?.entry !== undefined)
8001
- 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 } });
8142
+ 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 } });
8002
8143
  await ensureMigrationsTable({ executor, dialect });
8003
8144
  if (params.as === "applied") {
8004
8145
  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})`;
@@ -8020,7 +8161,7 @@ async function rollbackMigration(params) {
8020
8161
  const stored = assertRollbackRecord({ migration, stored: recorded.get(migration.name), dialect });
8021
8162
  const newest = [...recorded.keys()].at(-1);
8022
8163
  if (newest !== migration.name)
8023
- 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 } });
8164
+ 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 } });
8024
8165
  await validateRollbackRecovery({ executor, migration, downSql, stored, dialect });
8025
8166
  await executeRollback({ ...params, executor, dialect, downSql, stored });
8026
8167
  } });
@@ -8038,7 +8179,7 @@ async function rollbackMigrations(params) {
8038
8179
  const target = journal.findIndex((row) => row.name === params.to);
8039
8180
  const row = journal[target];
8040
8181
  if (row?.state !== "applied")
8041
- 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 } });
8182
+ 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 } });
8042
8183
  assertMigrationMetadata({ migration: byName.get(row.name), dialect });
8043
8184
  start = target + 1;
8044
8185
  }
@@ -8046,7 +8187,7 @@ async function rollbackMigrations(params) {
8046
8187
  for (const row of journal.slice(start).reverse()) {
8047
8188
  const migration = byName.get(row.name);
8048
8189
  if (migration === undefined)
8049
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Cannot roll back "${row.name}" without its migration file`, meta: { reason: "rollback-file-missing", migration: row.name } });
8190
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Cannot roll back "${row.name}" without its migration file`, meta: { reason: "rollback-file-missing", migration: row.name } });
8050
8191
  const stored = assertRollbackRecord({ migration, stored: recorded.get(row.name), dialect });
8051
8192
  const downSql = validateRollbackFile({ ...params, migration, dialect });
8052
8193
  plan.push({ migration, downSql, stored });
@@ -8065,21 +8206,21 @@ function validateRollbackFile(params) {
8065
8206
  const { migration, dialect } = params;
8066
8207
  assertModuleAdoption({ migration });
8067
8208
  if (migration.moduleAdoption !== undefined)
8068
- 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 } });
8209
+ 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 } });
8069
8210
  if (migration.desiredStatePending === true)
8070
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Unfinalized desired state cannot be rolled back", meta: { reason: "desired-state-unfinalized", migration: migration.name } });
8211
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Unfinalized desired state cannot be rolled back", meta: { reason: "desired-state-unfinalized", migration: migration.name } });
8071
8212
  if (migration.moduleOrigin !== undefined) {
8072
8213
  assertMaterializedModuleMigration({ migration });
8073
8214
  assertModuleOwnership({ origin: migration.moduleOrigin, desiredState: migration.desiredState });
8074
8215
  }
8075
8216
  const downSql = migration.downSql;
8076
8217
  if (downSql === undefined)
8077
- 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 } });
8218
+ 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 } });
8078
8219
  assertProbeMode({ dialect, probe: params.probe });
8079
8220
  if (migration.desiredState !== undefined) {
8080
8221
  assertDesiredState({ value: migration.desiredState, migration: migration.name });
8081
8222
  if (migration.desiredState.dialect !== dialect)
8082
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch" } });
8223
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch" } });
8083
8224
  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 } });
8084
8225
  }
8085
8226
  assertNativeMigrationMetadata({ migration, sql: downSql, dialect });
@@ -8092,10 +8233,10 @@ function validateRollbackFile(params) {
8092
8233
  function assertRollbackRecord(params) {
8093
8234
  const { migration, stored, dialect } = params;
8094
8235
  if (stored === undefined)
8095
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is not recorded as applied; nothing to roll back`, meta: { migration: migration.name } });
8236
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is not recorded as applied; nothing to roll back`, meta: { migration: migration.name } });
8096
8237
  assertChecksumMatches({ migration, stored: stored.checksum });
8097
8238
  if (stored.statementIndex >= 0 && (!isCompleteRecord({ dialect, migration, stored }) || stored.error !== undefined))
8098
- 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 } });
8239
+ 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 } });
8099
8240
  return stored;
8100
8241
  }
8101
8242
  async function validateRollbackRecovery(params) {
@@ -8104,7 +8245,7 @@ async function validateRollbackRecovery(params) {
8104
8245
  return;
8105
8246
  const start = -stored.statementIndex - 1;
8106
8247
  if (start > downSql.length)
8107
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Rollback progress exceeds its statement count", meta: { reason: "rollback-progress-invalid", migration: migration.name } });
8248
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Rollback progress exceeds its statement count", meta: { reason: "rollback-progress-invalid", migration: migration.name } });
8108
8249
  if (dialect === "sqlite" && usesSelfManagedTransactions({ sql: downSql }))
8109
8250
  assertSelfManagedResumeSafe({ sql: downSql, start, migration: migration.name, direction: "down" });
8110
8251
  if (!marksInflight({ dialect, execution: migration.downExecution }) || start >= downSql.length)
@@ -8170,7 +8311,7 @@ async function runTransactionalDown(params) {
8170
8311
  } else {
8171
8312
  await cleanupSelfManagedFailure({ executor });
8172
8313
  }
8173
- throw new VibeError35({
8314
+ throw new VibeError36({
8174
8315
  code: "VIBE_MIGRATION",
8175
8316
  message: `rollback of "${migration.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
8176
8317
  meta: { migration: migration.name, ...timeoutDiagnostic({ error }) },
@@ -8253,7 +8394,7 @@ async function runNonTransactionalDown(params) {
8253
8394
  values: [marks ? `${INFLIGHT_PREFIX}${index}` : error instanceof Error ? error.message : String(error), migration.name]
8254
8395
  });
8255
8396
  } catch {}
8256
- throw new VibeError35({
8397
+ throw new VibeError36({
8257
8398
  code: "VIBE_MIGRATION",
8258
8399
  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."}`,
8259
8400
  meta: { migration: migration.name, statementIndex: index, ...marks ? { outcome: "unknown" } : {}, ...timeoutDiagnostic({ error }) },
@@ -8315,7 +8456,7 @@ async function runTransactional(params) {
8315
8456
  } else {
8316
8457
  await cleanupSelfManagedFailure({ executor });
8317
8458
  }
8318
- throw new VibeError35({
8459
+ throw new VibeError36({
8319
8460
  code: "VIBE_MIGRATION",
8320
8461
  message: `migration "${migration.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
8321
8462
  meta: { migration: migration.name },
@@ -8411,7 +8552,7 @@ async function runNonTransactional(params) {
8411
8552
  } catch {}
8412
8553
  const sqlState = sqlStateOf(error);
8413
8554
  const timeout = sqlState === "55P03" ? "lock-timeout" : sqlState === "57014" ? "statement-timeout" : undefined;
8414
- throw new VibeError35({
8555
+ throw new VibeError36({
8415
8556
  code: "VIBE_MIGRATION",
8416
8557
  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."}`,
8417
8558
  meta: { migration: migration.name, statementIndex: index, ...marks ? { outcome: "unknown" } : {}, ...timeout === undefined ? {} : { reason: timeout, sqlState } },
@@ -8439,7 +8580,7 @@ async function acquireLock(params) {
8439
8580
  });
8440
8581
  const acquired = Number(rows[0]?.vibe_lock) === 1;
8441
8582
  if (!acquired) {
8442
- throw new VibeError35({
8583
+ throw new VibeError36({
8443
8584
  code: "VIBE_MIGRATION",
8444
8585
  message: `could not acquire the migration lock ('${MYSQL_LOCK_NAME}') within ${MYSQL_LOCK_TIMEOUT_SECONDS}s \u2014 another migration run is in progress`,
8445
8586
  meta: { lock: MYSQL_LOCK_NAME }
@@ -8474,7 +8615,7 @@ function uncertainStatement(params) {
8474
8615
  const subject = direction === "down" ? `rollback of "${migration}"` : `migration "${migration}"`;
8475
8616
  const position = `${direction === "down" ? "down statement" : "statement"} ${index + 1} of ${statements.length}`;
8476
8617
  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.";
8477
- return new VibeError35({
8618
+ return new VibeError36({
8478
8619
  code: "VIBE_MIGRATION",
8479
8620
  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)}`,
8480
8621
  meta: {
@@ -8492,7 +8633,7 @@ function assertExecutable(params) {
8492
8633
  if (execution === undefined || isDefaultExecution({ execution }))
8493
8634
  return;
8494
8635
  if (dialect !== "postgres") {
8495
- throw new VibeError35({
8636
+ throw new VibeError36({
8496
8637
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8497
8638
  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`,
8498
8639
  meta: { reason: "nontransactional-unsupported", dialect, migration: migration.name, feature: "nontransactionalMigrations" }
@@ -8568,7 +8709,7 @@ function assertChecksumMatches(params) {
8568
8709
  const actual = checksumOf({ migration });
8569
8710
  if (stored === actual)
8570
8711
  return;
8571
- throw new VibeError35({
8712
+ throw new VibeError36({
8572
8713
  code: "VIBE_MIGRATION",
8573
8714
  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)`,
8574
8715
  meta: { migration: migration.name, recorded: stored, current: actual }
@@ -8579,7 +8720,7 @@ function assertResumable(params) {
8579
8720
  const actual = checksumOf({ migration });
8580
8721
  if (stored.checksum === actual)
8581
8722
  return;
8582
- throw new VibeError35({
8723
+ throw new VibeError36({
8583
8724
  code: "VIBE_MIGRATION",
8584
8725
  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`,
8585
8726
  meta: {
@@ -8599,7 +8740,7 @@ function assertDistinctMigrationNames(params) {
8599
8740
  const names = new Set;
8600
8741
  for (const migration of params.migrations) {
8601
8742
  if (names.has(migration.name))
8602
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Duplicate migration file identity "${migration.name}"`, meta: { reason: "history-duplicate-file", migration: migration.name } });
8743
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Duplicate migration file identity "${migration.name}"`, meta: { reason: "history-duplicate-file", migration: migration.name } });
8603
8744
  names.add(migration.name);
8604
8745
  }
8605
8746
  }
@@ -8607,15 +8748,15 @@ function assertCompleteHistory(params) {
8607
8748
  const byName = new Map(params.migrations.map((migration) => [migration.name, migration]));
8608
8749
  for (const name of params.recorded.keys()) {
8609
8750
  if (!byName.has(name))
8610
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Recorded migration "${name}" has no file in the complete chain`, meta: { reason: "history-unknown-file", migration: name } });
8751
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Recorded migration "${name}" has no file in the complete chain`, meta: { reason: "history-unknown-file", migration: name } });
8611
8752
  }
8612
8753
  let index = 0;
8613
8754
  for (const [name, stored] of params.recorded) {
8614
8755
  const migration = params.migrations[index];
8615
8756
  if (migration?.name !== name)
8616
- 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 } });
8757
+ 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 } });
8617
8758
  if (stored.statementIndex >= 0 && !isCompleteRecord({ dialect: params.dialect, migration, stored }) && index !== params.recorded.size - 1)
8618
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Partial migration "${name}" precedes later recorded work`, meta: { reason: "history-partial-not-last", migration: name } });
8759
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Partial migration "${name}" precedes later recorded work`, meta: { reason: "history-partial-not-last", migration: name } });
8619
8760
  index++;
8620
8761
  }
8621
8762
  }
@@ -8624,16 +8765,16 @@ async function assertApplyHistory(params) {
8624
8765
  const byName = new Map(migrations.map((migration) => [migration.name, migration]));
8625
8766
  for (const [name, stored] of recorded) {
8626
8767
  if (stored.statementIndex < 0) {
8627
- 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 } });
8768
+ 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 } });
8628
8769
  }
8629
8770
  const migration = byName.get(name);
8630
8771
  if (migration === undefined) {
8631
8772
  if (stored.error !== undefined)
8632
- 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 } });
8773
+ 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 } });
8633
8774
  continue;
8634
8775
  }
8635
8776
  if (isCompleteRecord({ dialect, migration, stored }) && stored.error !== undefined)
8636
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error`, meta: { reason: "history-dirty", migration: name } });
8777
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error`, meta: { reason: "history-dirty", migration: name } });
8637
8778
  if (isCompleteRecord({ dialect, migration, stored }))
8638
8779
  assertChecksumMatches({ migration, stored: stored.checksum });
8639
8780
  else
@@ -8669,7 +8810,7 @@ function absent(error) {
8669
8810
  return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
8670
8811
  }
8671
8812
  function failure(params) {
8672
- return new VibeError36({ code: "VIBE_MIGRATION", message: `Cannot read migration file ${params.path}`, meta: { reason: "migration-file-unreadable", path: params.path }, cause: params.cause });
8813
+ return new VibeError37({ code: "VIBE_MIGRATION", message: `Cannot read migration file ${params.path}`, meta: { reason: "migration-file-unreadable", path: params.path }, cause: params.cause });
8673
8814
  }
8674
8815
  async function optionalFile(params) {
8675
8816
  return readFile(params.path, "utf-8").catch((cause) => {
@@ -8683,13 +8824,13 @@ function rlsMetadata(params) {
8683
8824
  const metadata = JSON.parse(params.text);
8684
8825
  const strings = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
8685
8826
  if (metadata?.version !== 1 || typeof metadata.destructive !== "boolean" || metadata.rls === undefined || !strings(metadata.securityChanges) || !strings(metadata.downSecurityChanges) || metadata.sqlChecksum !== migrationChecksum({ sql: params.migration.sql }) || metadata.downChecksum !== migrationChecksum({ sql: params.migration.downSql ?? [] })) {
8686
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Invalid native RLS metadata" });
8827
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Invalid native RLS metadata" });
8687
8828
  }
8688
8829
  assertValidSchemaIR4({ schema: metadata.rls.from });
8689
8830
  assertValidSchemaIR4({ schema: metadata.rls.to });
8690
8831
  return { ...params.migration, rls: metadata.rls, destructive: metadata.destructive, securityChanges: metadata.securityChanges, downSecurityChanges: metadata.downSecurityChanges };
8691
8832
  }).catch((cause) => {
8692
- throw new VibeError36({ code: "VIBE_MIGRATION", message: `Invalid native RLS metadata for migration "${params.migration.name}"`, meta: { reason: "rls-migration-metadata-invalid", migration: params.migration.name }, cause });
8833
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: `Invalid native RLS metadata for migration "${params.migration.name}"`, meta: { reason: "rls-migration-metadata-invalid", migration: params.migration.name }, cause });
8693
8834
  });
8694
8835
  }
8695
8836
  async function loadMigrationFiles(params) {
@@ -8721,10 +8862,7 @@ async function loadMigrationFiles(params) {
8721
8862
  migration = { ...migration, ...parseExecutionSidecar({ text: execution, migration: name, statements: migration.sql.length, downStatements: migration.downSql?.length ?? 0 }) };
8722
8863
  const desired = await optionalFile({ path: join(directory, DESIRED_STATE_METADATA.filename) });
8723
8864
  if (desired !== undefined) {
8724
- const pending = await Promise.resolve().then(() => isUnfinalizedDesiredState({ text: desired })).catch((cause) => {
8725
- throw new VibeError36({ code: "VIBE_MIGRATION", message: `Malformed desired state for ${name}`, meta: { reason: "desired-state-invalid", migration: name }, cause });
8726
- });
8727
- migration = pending ? { ...migration, desiredStatePending: true } : { ...migration, desiredState: parseDesiredStateSidecar({ text: desired, migration: name }) };
8865
+ migration = { ...migration, ...loadDesiredStateSidecar({ text: desired, migration: name }) };
8728
8866
  }
8729
8867
  const origin = await optionalFile({ path: join(directory, MODULE_ORIGIN_METADATA.filename) });
8730
8868
  if (origin !== undefined)
@@ -8743,4 +8881,4 @@ export {
8743
8881
  splitSqlStatements
8744
8882
  };
8745
8883
 
8746
- //# debugId=000F8CFDEDF957F264756E2164756E21
8884
+ //# debugId=73DFB0227E02518164756E2164756E21