@vibeorm/migrate 2.6.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 (89) 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 +449 -269
  5. package/dist/fs.js.map +15 -14
  6. package/dist/index.js +820 -467
  7. package/dist/index.js.map +17 -15
  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/normalize.d.ts +19 -1
  12. package/dist/object-ownership.d.ts +8 -0
  13. package/dist/postgres-types.d.ts +24 -0
  14. package/dist/sqlite-fts-upgrade.d.ts +64 -0
  15. package/package.json +6 -4
  16. package/dist/adoption.d.ts.map +0 -1
  17. package/dist/artifact-probe.d.ts.map +0 -1
  18. package/dist/baseline-record.d.ts.map +0 -1
  19. package/dist/baseline.d.ts.map +0 -1
  20. package/dist/compare-reference-rls.d.ts.map +0 -1
  21. package/dist/compare-sqlite-primary-key.d.ts.map +0 -1
  22. package/dist/compile-desired-state.d.ts.map +0 -1
  23. package/dist/ddl/drop-order.d.ts.map +0 -1
  24. package/dist/ddl/mysql.d.ts.map +0 -1
  25. package/dist/ddl/postgres.d.ts.map +0 -1
  26. package/dist/ddl/render.d.ts.map +0 -1
  27. package/dist/ddl/rls.d.ts.map +0 -1
  28. package/dist/ddl/sqlite.d.ts.map +0 -1
  29. package/dist/desired-state.d.ts.map +0 -1
  30. package/dist/differ.d.ts.map +0 -1
  31. package/dist/down.d.ts.map +0 -1
  32. package/dist/enum-values.d.ts.map +0 -1
  33. package/dist/exclusions.d.ts.map +0 -1
  34. package/dist/execute.d.ts.map +0 -1
  35. package/dist/execution.d.ts.map +0 -1
  36. package/dist/extensions.d.ts.map +0 -1
  37. package/dist/external-dependencies.d.ts.map +0 -1
  38. package/dist/external-objects.d.ts.map +0 -1
  39. package/dist/fs.d.ts.map +0 -1
  40. package/dist/history.d.ts.map +0 -1
  41. package/dist/index-predicates.d.ts.map +0 -1
  42. package/dist/index.d.ts.map +0 -1
  43. package/dist/inspect-objects.d.ts.map +0 -1
  44. package/dist/introspect/mysql.d.ts.map +0 -1
  45. package/dist/introspect/postgres-rls.d.ts.map +0 -1
  46. package/dist/introspect/postgres.d.ts.map +0 -1
  47. package/dist/introspect/shared.d.ts.map +0 -1
  48. package/dist/introspect/sqlite.d.ts.map +0 -1
  49. package/dist/inventory.d.ts.map +0 -1
  50. package/dist/journal-read.d.ts.map +0 -1
  51. package/dist/journal-table.d.ts.map +0 -1
  52. package/dist/migration-checksum.d.ts.map +0 -1
  53. package/dist/module-adoption-record.d.ts.map +0 -1
  54. package/dist/module-adoption-reference.d.ts.map +0 -1
  55. package/dist/module-adoption.d.ts.map +0 -1
  56. package/dist/module-bundle.d.ts.map +0 -1
  57. package/dist/module-export.d.ts.map +0 -1
  58. package/dist/module-history.d.ts.map +0 -1
  59. package/dist/module-journal.d.ts.map +0 -1
  60. package/dist/module-origin.d.ts.map +0 -1
  61. package/dist/module-ownership.d.ts.map +0 -1
  62. package/dist/module-pending.d.ts.map +0 -1
  63. package/dist/module-readiness.d.ts.map +0 -1
  64. package/dist/mysql-catalog-visibility.d.ts.map +0 -1
  65. package/dist/mysql-types.d.ts.map +0 -1
  66. package/dist/normalize.d.ts.map +0 -1
  67. package/dist/object-ownership.d.ts.map +0 -1
  68. package/dist/operator-classes.d.ts.map +0 -1
  69. package/dist/planner.d.ts.map +0 -1
  70. package/dist/postgres-default-equivalence.d.ts.map +0 -1
  71. package/dist/postgres-type-identity.d.ts.map +0 -1
  72. package/dist/postgres-types.d.ts.map +0 -1
  73. package/dist/push.d.ts.map +0 -1
  74. package/dist/recovery.d.ts.map +0 -1
  75. package/dist/reference-state.d.ts.map +0 -1
  76. package/dist/relations.d.ts.map +0 -1
  77. package/dist/renames.d.ts.map +0 -1
  78. package/dist/replay-objects.d.ts.map +0 -1
  79. package/dist/replay-plan.d.ts.map +0 -1
  80. package/dist/replay-reference.d.ts.map +0 -1
  81. package/dist/runner.d.ts.map +0 -1
  82. package/dist/shadow-dedicated.d.ts.map +0 -1
  83. package/dist/shadow-guard.d.ts.map +0 -1
  84. package/dist/shadow-types.d.ts.map +0 -1
  85. package/dist/shadow.d.ts.map +0 -1
  86. package/dist/sql-statements.d.ts.map +0 -1
  87. package/dist/sqlite-types.d.ts.map +0 -1
  88. package/dist/types.d.ts.map +0 -1
  89. package/dist/verify-history.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // @bun
2
2
  // packages/migrate/src/external-objects.ts
3
- import { IDENTIFIER_LIMITS, VibeError as VibeError17, identifierLength, sqlNamespaceIssue, sqlObjectKey as sqlObjectKey6, sqlObjectNamespace as sqlObjectNamespace2, qualifiedSqlName as qualifiedSqlName8, enumStorageOf as enumStorageOf3, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
3
+ import { IDENTIFIER_LIMITS, VibeError as VibeError18, identifierLength, sqlNamespaceIssue, sqlObjectKey as sqlObjectKey6, sqlObjectNamespace as sqlObjectNamespace2, qualifiedSqlName as qualifiedSqlName8, enumStorageOf as enumStorageOf3, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
4
4
  import { getSqlDialect as getSqlDialect3, renderExternalOwnershipCatalog, renderReplayCoverageGaps } from "@vibeorm/sql";
5
5
 
6
6
  // packages/migrate/src/inventory.ts
7
- import { VibeError as VibeError16, defaultForeignKeyName as defaultForeignKeyName2, defaultPrimaryKeyName as defaultPrimaryKeyName2, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey5 } from "@vibeorm/schema";
7
+ import { VibeError as VibeError17, defaultForeignKeyName as defaultForeignKeyName2, defaultPrimaryKeyName as defaultPrimaryKeyName2, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey5 } from "@vibeorm/schema";
8
8
  import { getSqlDialect as getSqlDialect2, renderMigrationInventory, renderSqliteColumnInventory as renderSqliteColumnInventory2, renderMysqlObjectMembers as renderMysqlObjectMembers2, sqliteTableConstraints as sqliteTableConstraints3, renderSqliteForeignKeyInventory } from "@vibeorm/sql";
9
9
 
10
10
  // packages/migrate/src/exclusions.ts
@@ -85,16 +85,12 @@ async function verifyExclusions(params) {
85
85
 
86
86
  // packages/migrate/src/inspect-objects.ts
87
87
  import { randomUUID as randomUUID2 } from "crypto";
88
- import { VibeError as VibeError15, sqlObjectKey as sqlObjectKey4, sqlObjectNamespace, DIALECT_CAPABILITIES as DIALECT_CAPABILITIES2, qualifiedSqlName as qualifiedSqlName7 } from "@vibeorm/schema";
88
+ import { VibeError as VibeError16, sqlObjectKey as sqlObjectKey4, sqlObjectNamespace, DIALECT_CAPABILITIES as DIALECT_CAPABILITIES2, qualifiedSqlName as qualifiedSqlName7 } from "@vibeorm/schema";
89
89
  import { getSqlDialect, renderMigrationObjectBatch, renderMigrationObjectQuery as renderMigrationObjectQuery2, renderCheckProbe, renderMigrationNamespace, renderMigrationDependencies, renderMysqlObjectMembers, renderMysqlCheckProbe, sqliteColumnClauses as sqliteColumnClauses2, renderMigrationVisibility, sqliteTableConstraints as sqliteTableConstraints2, renderSqliteColumnInventory } from "@vibeorm/sql";
90
90
 
91
91
  // packages/migrate/src/desired-state.ts
92
92
  import { VibeError as VibeError2, assertValidSchemaIR, isSqlObjectDefinition, isSqlObjectIdentity, providerToDialect, sqlObjectKey as sqlObjectKey2 } from "@vibeorm/schema";
93
93
  var UNFINALIZED_DESIRED_STATE = Object.freeze({ version: 1, status: "unfinalized" });
94
- function isUnfinalizedDesiredState(params) {
95
- const value = JSON.parse(params.text);
96
- return record(value) && value.version === 1 && value.status === "unfinalized" && Object.keys(value).length === 2;
97
- }
98
94
  var DESIRED_STATE_METADATA = Object.freeze({ filename: "desired-state.json", version: 1 });
99
95
  function createDesiredState(params) {
100
96
  assertValidSchemaIR({ schema: params.schema });
@@ -177,6 +173,15 @@ function parseDesiredStateSidecar(params) {
177
173
  return checked.value;
178
174
  }, failure: (cause) => invalid({ migration: params.migration, detail: "malformed metadata", cause }) });
179
175
  }
176
+ function loadDesiredStateSidecar(params) {
177
+ return parseMigrationMetadata({ text: params.text, parse: (value) => {
178
+ if (record(value) && value.version === 1 && value.status === "unfinalized" && Object.keys(value).length === 2)
179
+ return { desiredStatePending: true };
180
+ const checked = { value, migration: params.migration };
181
+ assertDesiredState(checked);
182
+ return { desiredState: checked.value };
183
+ }, failure: (cause) => invalid({ migration: params.migration, detail: "malformed metadata", cause }) });
184
+ }
180
185
  function parseMigrationMetadata(params) {
181
186
  try {
182
187
  return params.parse(JSON.parse(params.text));
@@ -233,6 +238,9 @@ var SCALAR_TO_PG = {
233
238
  var CANONICAL_EQUIVALENTS = {
234
239
  DateTime: ["timestamptz(3)", "timestamptz(6)"]
235
240
  };
241
+ var EXACT_CANONICAL_EQUIVALENTS = {
242
+ DateTime: ["timestamptz(6)"]
243
+ };
236
244
  var CANONICAL_UDT = {
237
245
  text: "String",
238
246
  bool: "Boolean",
@@ -301,13 +309,15 @@ var NATIVE_ALIASES = {
301
309
  var IMPLICIT_MODIFIERS = {
302
310
  bit: "(1)"
303
311
  };
312
+ var DEFAULT_PRECISION_SIX = new Set(["timestamp", "time", "timetz"]);
304
313
  function normalizeNativeType(params) {
305
314
  const compact = params.raw.toLowerCase().replace(/\s+/g, "");
306
315
  const match = compact.match(/^([a-z_0-9]+)(\(.*\))?(\[\])?$/);
307
316
  if (match === null)
308
317
  return compact;
309
318
  const base = NATIVE_ALIASES[match[1]] ?? match[1];
310
- return `${base}${match[2] ?? IMPLICIT_MODIFIERS[base] ?? ""}${match[3] ?? ""}`;
319
+ const modifier = DEFAULT_PRECISION_SIX.has(base) && match[2] === "(6)" ? "" : match[2] ?? IMPLICIT_MODIFIERS[base] ?? "";
320
+ return `${base}${modifier}${match[3] ?? ""}`;
311
321
  }
312
322
  function parseUnsupportedPostgresType(params) {
313
323
  const temporal = /^\s*(time(?:stamp)?)\s*(\(\s*\d+\s*\))?\s+(with|without)\s+time\s+zone\s*((?:\[\s*\]\s*)*)$/i.exec(params.raw);
@@ -320,7 +330,7 @@ function parseUnsupportedPostgresType(params) {
320
330
  return NATIVE_ALIASES[base] === undefined ? undefined : parsePostgresTypeName({ raw: normalizeNativeType(params) });
321
331
  }
322
332
  function columnTypeKey(params) {
323
- const { field } = params;
333
+ const { field, exact } = params;
324
334
  const listSuffix = field.isList ? "[]" : "";
325
335
  if (field.isUnsupported === true) {
326
336
  const parsed = parseUnsupportedPostgresType({ raw: field.nativeType ?? "" });
@@ -331,7 +341,7 @@ function columnTypeKey(params) {
331
341
  const scalar = namespace === undefined || namespace === "pg_catalog" ? scalarForBuiltinType({ name: parsed.name }) : undefined;
332
342
  if (scalar !== undefined) {
333
343
  const { isUnsupported: _unsupported, nativeTypeIdentity: _identity, ...rest } = field;
334
- return columnTypeKey({ field: { ...rest, type: scalar, nativeType: `${parsed.name}${parsed.modifier ?? ""}`, isList: parsed.array || field.isList } });
344
+ return columnTypeKey({ field: { ...rest, type: scalar, nativeType: `${parsed.name}${parsed.modifier ?? ""}`, isList: parsed.array || field.isList }, ...exact === undefined ? {} : { exact } });
335
345
  }
336
346
  return `u:${JSON.stringify([namespace ?? null, parsed.name, parsed.modifier, parsed.array || field.isList])}`;
337
347
  }
@@ -342,12 +352,37 @@ function columnTypeKey(params) {
342
352
  if (field.nativeType !== undefined) {
343
353
  const normalized = normalizeNativeType({ raw: field.nativeType });
344
354
  const canonical = normalizeNativeType({ raw: SCALAR_TO_PG[field.type] });
345
- const isCanonical = normalized === canonical || (CANONICAL_EQUIVALENTS[field.type]?.includes(normalized) ?? false);
355
+ const equivalents = exact === true ? EXACT_CANONICAL_EQUIVALENTS : CANONICAL_EQUIVALENTS;
356
+ const isCanonical = normalized === canonical || (equivalents[field.type]?.includes(normalized) ?? false);
346
357
  if (!isCanonical)
347
358
  return `n:${normalized}${listSuffix}`;
348
359
  }
349
360
  return `s:${field.type}${listSuffix}`;
350
361
  }
362
+ function declaresExactColumnType(params) {
363
+ const { field } = params;
364
+ if (field.isUnsupported === true || field.nativeType === undefined || isEnumType2(field.type))
365
+ return false;
366
+ return CANONICAL_EQUIVALENTS[field.type]?.includes(normalizeNativeType({ raw: field.nativeType })) ?? false;
367
+ }
368
+ function timestamptzPrecision(params) {
369
+ const { field } = params;
370
+ if (field.type !== "DateTime" || field.isUnsupported === true)
371
+ return;
372
+ const normalized = field.nativeType === undefined ? "timestamptz" : normalizeNativeType({ raw: field.nativeType });
373
+ if (normalized === "timestamptz")
374
+ return 6;
375
+ const match = /^timestamptz\((\d)\)$/.exec(normalized);
376
+ return match === null ? undefined : Number(match[1]);
377
+ }
378
+ function isTimestamptzPrecisionWidening(params) {
379
+ const { from, to } = params;
380
+ if (from.isList !== to.isList)
381
+ return false;
382
+ const before = timestamptzPrecision({ field: from });
383
+ const after = timestamptzPrecision({ field: to });
384
+ return before !== undefined && after !== undefined && after >= before;
385
+ }
351
386
  function isScalarToArrayChange(params) {
352
387
  const { from, to } = params;
353
388
  if (from.isList || !to.isList)
@@ -1016,6 +1051,32 @@ function parseMysqlEnumColumnType(params) {
1016
1051
  }
1017
1052
 
1018
1053
  // packages/migrate/src/normalize.ts
1054
+ function firstByName(params) {
1055
+ const result = new Map;
1056
+ for (const entry of params.entries)
1057
+ if (!result.has(entry.name))
1058
+ result.set(entry.name, entry);
1059
+ return result;
1060
+ }
1061
+ function normalizationLookup(params) {
1062
+ const enums = new Map;
1063
+ const scopedEnums = new Map;
1064
+ for (const entry of params.schema.enums) {
1065
+ const namespace = qualifiedSqlName3({ name: entry.name, namespace: entry.namespace }).namespace;
1066
+ for (const name of new Set([entry.name, entry.dbName ?? entry.name])) {
1067
+ if (!enums.has(name))
1068
+ enums.set(name, entry);
1069
+ const key = JSON.stringify([namespace, name]);
1070
+ if (!scopedEnums.has(key))
1071
+ scopedEnums.set(key, entry);
1072
+ }
1073
+ }
1074
+ return { models: firstByName({ entries: params.schema.models }), fields: new Map(params.schema.models.map((model) => [model, firstByName({ entries: model.fields })])), enums, scopedEnums };
1075
+ }
1076
+ function lookupFieldName(params) {
1077
+ const field = params.lookup.fields.get(params.model)?.get(params.name);
1078
+ return field?.dbName ?? field?.name ?? params.name;
1079
+ }
1019
1080
  function normalizeSchema(params) {
1020
1081
  const { schema } = params;
1021
1082
  const dialect = params.dialect ?? "postgres";
@@ -1028,6 +1089,7 @@ function normalizeSchema(params) {
1028
1089
  continue;
1029
1090
  enums.set(physicalNameKey({ name: enumDef.dbName ?? enumDef.name, namespace: enumDef.namespace, dialect }), enumDef);
1030
1091
  }
1092
+ const lookup = normalizationLookup({ schema });
1031
1093
  const tables = new Map;
1032
1094
  const views = new Map;
1033
1095
  for (const model of schema.models) {
@@ -1037,25 +1099,25 @@ function normalizeSchema(params) {
1037
1099
  views.set(physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect }), model);
1038
1100
  continue;
1039
1101
  }
1040
- const table = normalizeModel({ model, schema, dialect, enums });
1102
+ const table = normalizeModel({ model, lookup, dialect, enums });
1041
1103
  tables.set(physicalNameKey({ name: table.table, namespace: table.namespace, dialect }), table);
1042
1104
  }
1043
1105
  const joinTables = new Map;
1044
1106
  for (const pair of detectImplicitM2MPairs({ schema })) {
1045
- const join = normalizeJoinTable({ pair, schema, dialect });
1107
+ const join = normalizeJoinTable({ pair, lookup, dialect });
1046
1108
  joinTables.set(physicalNameKey({ name: join.table, namespace: join.namespace, dialect }), join);
1047
1109
  }
1048
1110
  return { tables, joinTables, enums, views };
1049
1111
  }
1050
1112
  function physicalEnumField(params) {
1051
- const { field, schema, dialect, table } = params;
1113
+ const { field, lookup, dialect, table } = params;
1052
1114
  if (!isEnumType5(field.type))
1053
1115
  return field;
1054
1116
  const type = field.type;
1055
1117
  if (type.namespace !== undefined)
1056
1118
  assertSqlNamespace({ namespace: type.namespace, dialect });
1057
1119
  const name = type.enum;
1058
- const enumDef = schema.enums.find((candidate) => (candidate.name === name || candidate.dbName === name) && (type.namespace === undefined || qualifiedSqlName3({ name, namespace: candidate.namespace }).namespace === type.namespace));
1120
+ const enumDef = type.namespace === undefined ? lookup.enums.get(name) : lookup.scopedEnums.get(JSON.stringify([type.namespace, name]));
1059
1121
  if (enumDef === undefined)
1060
1122
  return field;
1061
1123
  if (enumStorageOf({ enumDef }) !== "string") {
@@ -1094,16 +1156,20 @@ function assertStringEnumLabelsFit(params) {
1094
1156
  }
1095
1157
  }
1096
1158
  function columnTypeKeyFor(params) {
1097
- const { field, dialect, enums } = params;
1159
+ const { field, dialect, enums, exact } = params;
1098
1160
  switch (dialect) {
1099
1161
  case "postgres":
1100
- return columnTypeKey({ field });
1162
+ return columnTypeKey({ field, ...exact === undefined ? {} : { exact } });
1101
1163
  case "sqlite":
1102
1164
  return sqliteColumnTypeKey({ field, enums });
1103
1165
  case "mysql":
1104
1166
  return mysqlColumnTypeKey({ field, enums });
1105
1167
  }
1106
1168
  }
1169
+ function columnTypeChanged(params) {
1170
+ const { declared, actual } = params;
1171
+ return declared.pinsExactType ? declared.exactTypeKey !== actual.exactTypeKey : declared.typeKey !== actual.typeKey;
1172
+ }
1107
1173
  function defaultFingerprintFor(params) {
1108
1174
  const { field, dialect } = params;
1109
1175
  switch (dialect) {
@@ -1116,46 +1182,49 @@ function defaultFingerprintFor(params) {
1116
1182
  }
1117
1183
  }
1118
1184
  function normalizeModel(params) {
1119
- const { model, schema, dialect, enums } = params;
1185
+ const { model, lookup, dialect, enums } = params;
1120
1186
  const table = model.dbName ?? model.name;
1121
1187
  const columns = new Map;
1122
1188
  for (const logicalField of model.fields) {
1123
- const field = physicalEnumField({ field: logicalField, schema, dialect, table });
1189
+ const field = physicalEnumField({ field: logicalField, lookup, dialect, table });
1124
1190
  const name = field.dbName ?? field.name;
1191
+ const typeKey = columnTypeKeyFor({ field, dialect, enums });
1125
1192
  columns.set(name, {
1126
1193
  name,
1127
1194
  field,
1128
- typeKey: columnTypeKeyFor({ field, dialect, enums }),
1195
+ typeKey,
1196
+ exactTypeKey: dialect === "postgres" ? columnTypeKeyFor({ field, dialect, enums, exact: true }) : typeKey,
1197
+ pinsExactType: dialect === "postgres" && declaresExactColumnType({ field }),
1129
1198
  nullable: field.isOptional,
1130
1199
  defaultKey: defaultFingerprintFor({ field, dialect })
1131
1200
  });
1132
1201
  }
1133
- const pk = normalizePk({ model });
1134
- const indexes = normalizeIndexes({ model, table, dialect });
1135
- const fks = normalizeFks({ model, table, schema, dialect });
1202
+ const pk = normalizePk({ model, lookup });
1203
+ const indexes = normalizeIndexes({ model, table, dialect, lookup });
1204
+ const fks = normalizeFks({ model, table, lookup, dialect });
1136
1205
  const checks = new Map;
1137
1206
  for (const check of model.checks ?? [])
1138
1207
  checks.set(check.name, check.expression);
1139
1208
  return { model, table, ...model.namespace === undefined ? {} : { namespace: model.namespace }, columns, pk, indexes, fks, checks };
1140
1209
  }
1141
1210
  function normalizePk(params) {
1142
- const { model } = params;
1211
+ const { model, lookup } = params;
1143
1212
  if (model.primaryKey !== undefined && model.primaryKey.fields.length > 0) {
1144
- return model.primaryKey.fields.map((name) => fieldDbName({ model, name }));
1213
+ return model.primaryKey.fields.map((name) => lookupFieldName({ lookup, model, name }));
1145
1214
  }
1146
1215
  return model.fields.filter((field) => field.isId).map((field) => field.dbName ?? field.name);
1147
1216
  }
1148
1217
  function normalizeIndexes(params) {
1149
- const { model, table, dialect } = params;
1218
+ const { model, table, dialect, lookup } = params;
1150
1219
  const result = new Map;
1151
1220
  const add = (index) => {
1152
1221
  const unique = index.kind === "unique";
1153
1222
  const kind = unique ? "btree" : index.kind;
1154
1223
  const columns = index.fields.map((field) => {
1155
- const fieldIR = model.fields.find((candidate) => candidate.name === field.name);
1224
+ const fieldIR = lookup.fields.get(model)?.get(field.name);
1156
1225
  const decimal = fieldIR !== undefined && fieldIR.type === "Decimal" && fieldIR.isList !== true;
1157
1226
  return {
1158
- name: fieldDbName({ model, name: field.name }),
1227
+ name: fieldIR?.dbName ?? fieldIR?.name ?? field.name,
1159
1228
  desc: field.sort === "desc",
1160
1229
  ...decimal ? { decimal: true } : {},
1161
1230
  ...field.operatorClass === undefined ? {} : { operatorClass: field.operatorClass }
@@ -1189,8 +1258,7 @@ function normalizeIndexes(params) {
1189
1258
  }
1190
1259
  function indexComparisonKey(params) {
1191
1260
  const { unique, kind, columns, where, nullsNotDistinct } = params;
1192
- const cols = columns.map((column) => `${column.name}${column.desc ? " desc" : ""}${column.operatorClass === undefined ? "" : ` ops:${JSON.stringify([column.operatorClass.namespace, column.operatorClass.name])}`}`).join(",");
1193
- return `${unique ? "u" : "i"}${nullsNotDistinct === true ? "n" : ""}:${kind}:${cols}:${normalizeWhere({ where })}`;
1261
+ return JSON.stringify([unique, nullsNotDistinct === true, kind, columns.map((column) => [column.name, column.desc === true, column.operatorClass === undefined ? null : [column.operatorClass.namespace, column.operatorClass.name]]), normalizeWhere({ where })]);
1194
1262
  }
1195
1263
  function normalizeWhere(params) {
1196
1264
  const { where } = params;
@@ -1352,22 +1420,22 @@ function parsePrimaryExpression(params) {
1352
1420
  return { text: parts.join(" "), at, kind: "atom" };
1353
1421
  }
1354
1422
  function normalizeFks(params) {
1355
- const { model, table, schema } = params;
1423
+ const { model, table, lookup } = params;
1356
1424
  const result = new Map;
1357
1425
  for (const relation of model.relations) {
1358
1426
  if (relation.fields === undefined || relation.fields.length === 0)
1359
1427
  continue;
1360
- const target = schema.models.find((candidate) => candidate.name === relation.targetModel);
1428
+ const target = lookup.models.get(relation.targetModel);
1361
1429
  if (target !== undefined && target.isView)
1362
1430
  continue;
1363
1431
  const refTable = target === undefined ? relation.targetModel : target.dbName ?? target.name;
1364
- const columns = relation.fields.map((name) => fieldDbName({ model, name }));
1365
- const refColumns = (relation.references ?? []).map((name) => target === undefined ? name : fieldDbName({ model: target, name }));
1432
+ const columns = relation.fields.map((name) => lookupFieldName({ lookup, model, name }));
1433
+ const refColumns = (relation.references ?? []).map((name) => target === undefined ? name : lookupFieldName({ lookup, model: target, name }));
1366
1434
  const required = relation.fields.every((name) => {
1367
- const field = model.fields.find((candidate) => candidate.name === name);
1435
+ const field = lookup.fields.get(model)?.get(name);
1368
1436
  return field === undefined ? true : !field.isOptional;
1369
1437
  });
1370
- const key = `${columns.join(",")}\u2192${physicalNameKey({ name: refTable, namespace: target?.namespace, dialect: params.dialect })}.${refColumns.join(",")}`;
1438
+ const key = JSON.stringify([columns, physicalNameKey({ name: refTable, namespace: target?.namespace, dialect: params.dialect }), refColumns]);
1371
1439
  if (result.has(key))
1372
1440
  continue;
1373
1441
  result.set(key, {
@@ -1384,17 +1452,17 @@ function normalizeFks(params) {
1384
1452
  return result;
1385
1453
  }
1386
1454
  function normalizeJoinTable(params) {
1387
- const { pair, schema, dialect } = params;
1455
+ const { pair, lookup, dialect } = params;
1388
1456
  return {
1389
1457
  table: pair.tableName,
1390
1458
  ...pair.namespace === undefined ? {} : { namespace: pair.namespace },
1391
- a: joinTableEnd({ modelName: pair.modelA, schema, dialect, joinTable: pair.tableName }),
1392
- b: joinTableEnd({ modelName: pair.modelB, schema, dialect, joinTable: pair.tableName })
1459
+ a: joinTableEnd({ modelName: pair.modelA, lookup, dialect, joinTable: pair.tableName }),
1460
+ b: joinTableEnd({ modelName: pair.modelB, lookup, dialect, joinTable: pair.tableName })
1393
1461
  };
1394
1462
  }
1395
1463
  function joinTableEnd(params) {
1396
- const { modelName, schema, joinTable } = params;
1397
- const model = schema.models.find((candidate) => candidate.name === modelName);
1464
+ const { modelName, lookup, joinTable } = params;
1465
+ const model = lookup.models.get(modelName);
1398
1466
  if (model === undefined) {
1399
1467
  throw new VibeError7({
1400
1468
  code: "VIBE_MIGRATION",
@@ -1402,7 +1470,7 @@ function joinTableEnd(params) {
1402
1470
  meta: { joinTable, model: modelName }
1403
1471
  });
1404
1472
  }
1405
- const pkFields = model.primaryKey !== undefined && model.primaryKey.fields.length > 0 ? model.primaryKey.fields.map((name) => model.fields.find((field) => field.name === name)) : model.fields.filter((field) => field.isId);
1473
+ const pkFields = model.primaryKey !== undefined && model.primaryKey.fields.length > 0 ? model.primaryKey.fields.map((name) => lookup.fields.get(model)?.get(name)) : model.fields.filter((field) => field.isId);
1406
1474
  const pkField = pkFields[0];
1407
1475
  if (pkFields.length !== 1 || pkField === undefined) {
1408
1476
  throw new VibeError7({
@@ -1415,7 +1483,7 @@ function joinTableEnd(params) {
1415
1483
  table: model.dbName ?? model.name,
1416
1484
  ...model.namespace === undefined ? {} : { namespace: model.namespace },
1417
1485
  column: pkField.dbName ?? pkField.name,
1418
- field: physicalEnumField({ field: pkField, schema, dialect: params.dialect, table: model.dbName ?? model.name })
1486
+ field: physicalEnumField({ field: pkField, lookup, dialect: params.dialect, table: model.dbName ?? model.name })
1419
1487
  };
1420
1488
  }
1421
1489
  function physicalNameKey(params) {
@@ -1431,10 +1499,19 @@ function schemaNamespaces(params) {
1431
1499
  ...(params.schema.migrationObjects ?? []).flatMap((object) => object.identity.kind === "namespace" ? [object.identity.name] : [])
1432
1500
  ])];
1433
1501
  }
1434
- function fieldDbName(params) {
1435
- const { model, name } = params;
1436
- const field = model.fields.find((candidate) => candidate.name === name);
1437
- return field === undefined ? name : field.dbName ?? field.name;
1502
+
1503
+ // packages/migrate/src/introspect/group.ts
1504
+ function groupCatalogRows(params) {
1505
+ const groups = new Map;
1506
+ for (const row of params.rows) {
1507
+ const key = params.key(row);
1508
+ const group = groups.get(key);
1509
+ if (group === undefined)
1510
+ groups.set(key, [row]);
1511
+ else
1512
+ group.push(row);
1513
+ }
1514
+ return groups;
1438
1515
  }
1439
1516
 
1440
1517
  // packages/migrate/src/postgres-type-identity.ts
@@ -1838,10 +1915,15 @@ async function introspectPhysicalPostgres(params) {
1838
1915
  read("checks"),
1839
1916
  Promise.all(namespaces.map(async (namespace) => (await executor({ text: renderExclusionCatalog2({ dialect: postgresDialect6, namespace }) })).map((row) => ({ ...row, table_name: physicalNameKey({ namespace, name: String(row.table_name) }) })))).then((rows) => rows.flat())
1840
1917
  ]);
1841
- const keysQuery = renderPostgresIndexKeys({ dialect: postgresDialect6 });
1918
+ const keysQuery = renderPostgresIndexKeys({ dialect: postgresDialect6, namespaces });
1842
1919
  const indexKeys = await executor({ text: keysQuery.text, values: [...keysQuery.values] });
1843
1920
  if (indexKeys.some((row) => typeof row.table_namespace !== "string" || typeof row.table_name !== "string" || typeof row.index_name !== "string" || typeof row.method !== "string" || typeof row.unique !== "boolean" || typeof row.descending !== "boolean" || typeof row.exact_default !== "boolean" && row.column_name !== null || typeof row.operator_namespace !== "string" || typeof row.operator_name !== "string" || !Number.isSafeInteger(Number(row.ordinal)) || Number(row.ordinal) < 1))
1844
1921
  throw new VibeError11({ code: "VIBE_MIGRATION", message: "PostgreSQL index-key identity is incomplete", meta: { reason: "index-inspection-unavailable" } });
1922
+ const keysByIndex = groupCatalogRows({ rows: indexKeys, key: (row) => JSON.stringify([physicalNameKey({ namespace: String(row.table_namespace), name: String(row.table_name) }), row.index_name]) });
1923
+ const catalogColumns = new Map(columnRows.map((row) => [JSON.stringify([row.table_name, row.column_name]), row]));
1924
+ const foreignKeysByTable = groupCatalogRows({ rows: fkRows, key: (row) => String(row.table_name) });
1925
+ const catalogIndexesByTable = groupCatalogRows({ rows: indexRows, key: (row) => String(row.table_name) });
1926
+ const exclusionsByTable = groupCatalogRows({ rows: exclusionRows, key: (row) => String(row.table_name) });
1845
1927
  const tables = tableRows.map((row) => row.table_name);
1846
1928
  const views = viewRows.map((row) => row.table_name);
1847
1929
  const columns = columnRows.map((row) => ({
@@ -1859,6 +1941,8 @@ async function introspectPhysicalPostgres(params) {
1859
1941
  numericScale: toNumberOrNull(row.numeric_scale),
1860
1942
  fullType: row.full_type ?? null
1861
1943
  }));
1944
+ const columnsByTable = groupCatalogRows({ rows: columns, key: (column) => column.table });
1945
+ const columnsByIdentity = new Map(columns.map((column) => [JSON.stringify([column.table, column.name]), column]));
1862
1946
  const enums = [];
1863
1947
  {
1864
1948
  const valuesByName = new Map;
@@ -1899,6 +1983,7 @@ async function introspectPhysicalPostgres(params) {
1899
1983
  entry.columns.push(columnName);
1900
1984
  }
1901
1985
  }
1986
+ const uniquesByTable = groupCatalogRows({ rows: uniquesByConstraint.values(), key: (unique) => unique.table });
1902
1987
  const checksByTable = new Map;
1903
1988
  for (const row of checkRows) {
1904
1989
  const table = row.table_name;
@@ -1933,7 +2018,7 @@ async function introspectPhysicalPostgres(params) {
1933
2018
  const fkGroups = [...fkGroupsByName.values()].map((group) => ({
1934
2019
  ...group,
1935
2020
  required: group.columns.every((columnName) => {
1936
- const column = columns.find((c) => c.table === group.table && c.name === columnName);
2021
+ const column = columnsByIdentity.get(JSON.stringify([group.table, columnName]));
1937
2022
  return column === undefined ? true : !column.nullable;
1938
2023
  })
1939
2024
  }));
@@ -1941,7 +2026,7 @@ async function introspectPhysicalPostgres(params) {
1941
2026
  for (const row of indexRows) {
1942
2027
  if (row.backing_constraint_type === "x")
1943
2028
  continue;
1944
- const operands = indexKeys.filter((key) => physicalNameKey({ namespace: String(key.table_namespace), name: String(key.table_name) }) === row.table_name && key.index_name === row.index_name);
2029
+ const operands = keysByIndex.get(JSON.stringify([row.table_name, row.index_name])) ?? [];
1945
2030
  if (row.plain_columns === true && operands.length !== Number(row.key_count))
1946
2031
  throw new VibeError11({ code: "VIBE_MIGRATION", message: "Plain index keys could not be read completely; refusing name-only operator-class introspection", meta: { reason: "index-inspection-unavailable", table: row.table_name, index: row.index_name } });
1947
2032
  if (operands.length > 0) {
@@ -1966,22 +2051,24 @@ async function introspectPhysicalPostgres(params) {
1966
2051
  indexes.push(parsed);
1967
2052
  }
1968
2053
  }
2054
+ const indexesByTable = groupCatalogRows({ rows: indexes, key: (index) => index.table });
2055
+ const fkGroupsByTable = groupCatalogRows({ rows: fkGroups, key: (group) => group.table });
1969
2056
  const joinTables = new Map;
1970
2057
  for (const table of tables) {
1971
2058
  const physicalTable = physicalNames.get(table);
1972
2059
  if (!physicalTable.name.startsWith("_"))
1973
2060
  continue;
1974
- const tableFks = fkRows.filter((row) => row.table_name === table);
1975
- const tableIndexes = indexRows.filter((row) => row.table_name === table);
1976
- if (pkByTable.has(table) || (checksByTable.get(table)?.length ?? 0) > 0 || exclusionRows.some((row) => row.table_name === table))
2061
+ const tableFks = foreignKeysByTable.get(table) ?? [];
2062
+ const tableIndexes = catalogIndexesByTable.get(table) ?? [];
2063
+ if (pkByTable.has(table) || (checksByTable.get(table)?.length ?? 0) > 0 || exclusionsByTable.has(table))
1977
2064
  continue;
1978
2065
  if (!tableFks.every((row) => row.validated === true && row.deferrable === false && row.triggers_enabled === true))
1979
2066
  continue;
1980
2067
  if (tableIndexes.length !== 2 || !tableIndexes.every((row) => row.valid === true && row.ready === true && row.immediate === true && row.plain_columns === true && typeof row.key_count === "number" && row.key_count === row.attribute_count))
1981
2068
  continue;
1982
2069
  if (tableFks.some((row) => {
1983
- const source = columnRows.find((column) => column.table_name === table && column.column_name === row.column_name);
1984
- const target = columnRows.find((column) => column.table_name === row.foreign_table_name && column.column_name === row.foreign_column_name);
2070
+ const source = catalogColumns.get(JSON.stringify([table, row.column_name]));
2071
+ const target = catalogColumns.get(JSON.stringify([row.foreign_table_name, row.foreign_column_name]));
1985
2072
  return source === undefined || target === undefined || typeof source.full_type !== "string" || source.full_type.length === 0 || source.full_type !== target.full_type || source.column_default !== null || source.is_identity !== "NO" || source.is_generated !== "NEVER" || source.is_nullable !== "NO" || source.collation_name === undefined || source.collation_name !== target.collation_name;
1986
2073
  }))
1987
2074
  continue;
@@ -1989,9 +2076,9 @@ async function introspectPhysicalPostgres(params) {
1989
2076
  continue;
1990
2077
  const detected = detectJoinTable({
1991
2078
  table: physicalTable.name,
1992
- columnNames: columns.filter((column) => column.table === table).map((column) => column.name),
1993
- fkGroups: fkGroups.filter((group) => group.table === table).map((group) => ({ ...group, table: physicalTable.name, refTable: physicalNames.get(group.refTable)?.name ?? group.refTable })),
1994
- indexes: indexes.filter((index) => index.table === table).map((index) => ({ ...index, table: physicalTable.name }))
2079
+ columnNames: (columnsByTable.get(table) ?? []).map((column) => column.name),
2080
+ fkGroups: (fkGroupsByTable.get(table) ?? []).map((group) => ({ ...group, table: physicalTable.name, refTable: physicalNames.get(group.refTable)?.name ?? group.refTable })),
2081
+ indexes: (indexesByTable.get(table) ?? []).map((index) => ({ ...index, table: physicalTable.name }))
1995
2082
  });
1996
2083
  if (detected !== null)
1997
2084
  joinTables.set(table, { ...detected, ...physicalTable.namespace === "public" && namespaces.length === 1 ? {} : { relationName: detected.relationName ?? physicalTable.name.slice(1) }, tableA: physicalNameKey({ namespace: physicalTable.namespace, name: detected.tableA }), tableB: physicalNameKey({ namespace: physicalTable.namespace, name: detected.tableB }) });
@@ -2004,14 +2091,14 @@ async function introspectPhysicalPostgres(params) {
2004
2091
  table,
2005
2092
  physicalTable: physicalNames.get(table).name,
2006
2093
  isView: false,
2007
- columns,
2094
+ columns: columnsByTable.get(table) ?? [],
2008
2095
  pkByTable,
2009
2096
  pkNames,
2010
- uniquesByConstraint,
2097
+ uniques: uniquesByTable.get(table) ?? [],
2011
2098
  constraintNames,
2012
- indexes,
2099
+ indexes: indexesByTable.get(table) ?? [],
2013
2100
  enumNames,
2014
- exclusions: exclusionRows.filter((row) => row.table_name === table).map((row) => ({ name: String(row.constraint_name), definition: String(row.definition), requiredExtensions: [], catalogDefinition: true })),
2101
+ exclusions: (exclusionsByTable.get(table) ?? []).map((row) => ({ name: String(row.constraint_name), definition: String(row.definition), requiredExtensions: [], catalogDefinition: true })),
2015
2102
  checks: checksByTable.get(table) ?? []
2016
2103
  }));
2017
2104
  }
@@ -2020,10 +2107,10 @@ async function introspectPhysicalPostgres(params) {
2020
2107
  table: view,
2021
2108
  physicalTable: physicalNames.get(view).name,
2022
2109
  isView: true,
2023
- columns,
2110
+ columns: columnsByTable.get(view) ?? [],
2024
2111
  pkByTable,
2025
2112
  pkNames,
2026
- uniquesByConstraint,
2113
+ uniques: uniquesByTable.get(view) ?? [],
2027
2114
  constraintNames,
2028
2115
  indexes: [],
2029
2116
  enumNames,
@@ -2043,14 +2130,16 @@ async function introspectPhysicalPostgres(params) {
2043
2130
  logicalNames.add(name);
2044
2131
  return name;
2045
2132
  };
2133
+ const modelBasenames = groupCatalogRows({ rows: physicalNames.values(), key: (entry) => entry.name });
2134
+ const enumBasenames = groupCatalogRows({ rows: enumIdentities.values(), key: (entry) => entry.name });
2046
2135
  const modelNames = new Map(models.map((model) => {
2047
2136
  const identity = physicalNames.get(model.name);
2048
- const duplicates = [...physicalNames.values()].filter((entry) => entry.name === identity.name).length > 1;
2137
+ const duplicates = (modelBasenames.get(identity.name)?.length ?? 0) > 1;
2049
2138
  return [model.name, logicalName({ key: model.name, name: identity.name, duplicates })];
2050
2139
  }));
2051
2140
  const enumLogicalNames = new Map(enums.map((entry) => {
2052
2141
  const identity = enumIdentities.get(entry.name);
2053
- const duplicates = [...enumIdentities.values()].filter((candidate) => candidate.name === identity.name).length > 1;
2142
+ const duplicates = (enumBasenames.get(identity.name)?.length ?? 0) > 1;
2054
2143
  return [entry.name, logicalName({ key: entry.name, name: identity.name, duplicates })];
2055
2144
  }));
2056
2145
  const qualifiedModels = models.map((model) => {
@@ -2088,20 +2177,18 @@ async function introspectPhysicalPostgres(params) {
2088
2177
  return resolved.schema;
2089
2178
  }
2090
2179
  function buildModel(params) {
2091
- const { table, isView, columns, pkByTable, uniquesByConstraint, constraintNames, indexes, enumNames } = params;
2092
- const tableColumns = columns.filter((column) => column.table === table);
2180
+ const { table, isView, columns, pkByTable, uniques, constraintNames, indexes, enumNames } = params;
2181
+ const tableColumns = columns;
2093
2182
  const pk = pkByTable.get(table) ?? [];
2094
2183
  const uniqueSingles = new Set;
2095
2184
  const multiUniques = [];
2096
- for (const unique of uniquesByConstraint.values()) {
2185
+ for (const unique of uniques) {
2097
2186
  const name = unique.name;
2098
- if (unique.table !== table)
2099
- continue;
2100
2187
  if (unique.columns.length === 1)
2101
2188
  uniqueSingles.add(unique.columns[0]);
2102
2189
  multiUniques.push({ name, columns: unique.columns });
2103
2190
  }
2104
- const tableIndexes = indexes.filter((index) => index.table === table && !constraintNames.has(JSON.stringify([table, index.name])));
2191
+ const tableIndexes = indexes.filter((index) => !constraintNames.has(JSON.stringify([table, index.name])));
2105
2192
  for (const index of tableIndexes) {
2106
2193
  if (!index.unique)
2107
2194
  continue;
@@ -2199,8 +2286,9 @@ function reverseMapType(params) {
2199
2286
  function nonCanonicalModifier(params) {
2200
2287
  const { column, udt, scalar } = params;
2201
2288
  if (column.dataType === "ARRAY" && column.fullType !== null) {
2202
- const element = normalizeNativeType({ raw: column.fullType }).replace(/\[\]$/, "");
2203
- if (/^(numeric|timestamptz)\([\d,]+\)$/.test(element))
2289
+ const temporal = parseUnsupportedPostgresType({ raw: column.fullType });
2290
+ const element = temporal?.name === "timestamptz" ? `timestamptz${temporal.modifier}` : normalizeNativeType({ raw: column.fullType }).replace(/\[\]$/, "");
2291
+ if (element !== "timestamptz(6)" && /^(numeric|timestamptz)\([\d,]+\)$/.test(element))
2204
2292
  return element;
2205
2293
  }
2206
2294
  if (scalar === "DateTime" && udt === "timestamptz") {
@@ -2217,9 +2305,9 @@ function nonCanonicalModifier(params) {
2217
2305
  }
2218
2306
  function withModifier(params) {
2219
2307
  const { column, udt } = params;
2220
- if (udt === "varchar" && column.dataType === "ARRAY" && column.fullType !== null) {
2308
+ if ((udt === "varchar" || udt === "bpchar" || udt === "bit" || udt === "varbit") && column.dataType === "ARRAY" && column.fullType !== null) {
2221
2309
  const elementType = normalizeNativeType({ raw: column.fullType }).replace(/\[\]$/, "");
2222
- if (/^varchar\(\d+\)$/.test(elementType))
2310
+ if (/^(varchar|char|bit|varbit)\(\d+\)$/.test(elementType))
2223
2311
  return elementType;
2224
2312
  }
2225
2313
  if ((udt === "varchar" || udt === "bpchar") && column.charMaxLength !== null) {
@@ -2229,6 +2317,10 @@ function withModifier(params) {
2229
2317
  return "char";
2230
2318
  if ((udt === "bit" || udt === "varbit") && column.charMaxLength !== null)
2231
2319
  return `${udt}(${column.charMaxLength})`;
2320
+ if ((udt === "timestamp" || udt === "time" || udt === "timetz") && column.dataType === "ARRAY" && column.fullType !== null) {
2321
+ const element = parseUnsupportedPostgresType({ raw: column.fullType });
2322
+ return element === undefined || element.modifier === "" || element.modifier === "(6)" ? udt : `${udt}${element.modifier}`;
2323
+ }
2232
2324
  if ((udt === "timestamp" || udt === "time" || udt === "timetz") && column.datetimePrecision !== null && column.datetimePrecision !== 6) {
2233
2325
  return `${udt}(${column.datetimePrecision})`;
2234
2326
  }
@@ -2381,7 +2473,7 @@ function toNumberOrNull(value) {
2381
2473
  }
2382
2474
 
2383
2475
  // packages/migrate/src/introspect/mysql.ts
2384
- import { exactIntegerLiteral as exactIntegerLiteral2 } from "@vibeorm/schema";
2476
+ import { VibeError as VibeError12, exactIntegerLiteral as exactIntegerLiteral2 } from "@vibeorm/schema";
2385
2477
  import { mysqlDialect as mysqlDialect2 } from "@vibeorm/sql";
2386
2478
 
2387
2479
  // packages/migrate/src/journal-table.ts
@@ -2510,6 +2602,12 @@ async function introspectMysql(params) {
2510
2602
  collation: catalogField({ row, name: "collation_name" }) ?? null,
2511
2603
  extra: (catalogField({ row, name: "extra" }) ?? "").toLowerCase()
2512
2604
  }));
2605
+ const columnsByTable = groupCatalogRows({ rows: columns, key: (column) => column.table });
2606
+ const columnsByIdentity = new Map(columns.map((column) => [JSON.stringify([column.table, column.name]), column]));
2607
+ const catalogColumns = new Map(columnRows.map((row) => [JSON.stringify([catalogField({ row, name: "table_name" }), catalogField({ row, name: "column_name" })]), row]));
2608
+ const statisticsByTable = groupCatalogRows({ rows: statisticRows, key: (row) => String(catalogField({ row, name: "table_name" })) });
2609
+ const foreignKeysByTable = groupCatalogRows({ rows: fkRows, key: (row) => String(catalogField({ row, name: "table_name" })) });
2610
+ const enginesByTable = new Map(tableRows.map((row) => [catalogField({ row, name: "table_name" }), catalogField({ row, name: "engine" })]));
2513
2611
  const rulesByConstraint = new Map;
2514
2612
  for (const row of refRows) {
2515
2613
  rulesByConstraint.set(catalogField({ row, name: "constraint_name" }), {
@@ -2521,6 +2619,15 @@ async function introspectMysql(params) {
2521
2619
  for (const row of fkRows) {
2522
2620
  const constraintName = catalogField({ row, name: "constraint_name" });
2523
2621
  const table = catalogField({ row, name: "table_name" });
2622
+ const tableSchema = catalogField({ row, name: "table_schema" });
2623
+ const referencedSchema = catalogField({ row, name: "referenced_table_schema" });
2624
+ if (typeof tableSchema === "string" && typeof referencedSchema === "string" && tableSchema !== referencedSchema) {
2625
+ throw new VibeError12({
2626
+ code: "VIBE_UNSUPPORTED_CAPABILITY",
2627
+ message: `MySQL cross-database foreign key "${constraintName}" from "${tableSchema}.${table}" to "${referencedSchema}" cannot be introspected without losing its database identity`,
2628
+ meta: { dialect: "mysql", feature: "cross-database foreign keys", constraint: constraintName, table, tableSchema, referencedSchema }
2629
+ });
2630
+ }
2524
2631
  const key = `${table}\x00${constraintName}`;
2525
2632
  const existing = groupsByKey.get(key);
2526
2633
  if (existing === undefined) {
@@ -2543,17 +2650,11 @@ async function introspectMysql(params) {
2543
2650
  onDelete: rules?.onDelete ?? "NoAction",
2544
2651
  onUpdate: rules?.onUpdate ?? "NoAction",
2545
2652
  required: group.columns.every((columnName) => {
2546
- const column = columns.find((candidate) => candidate.table === group.table && candidate.name === columnName);
2653
+ const column = columnsByIdentity.get(JSON.stringify([group.table, columnName]));
2547
2654
  return column === undefined ? true : !column.nullable;
2548
2655
  })
2549
2656
  };
2550
2657
  });
2551
- const fkNamesByTable = new Map;
2552
- for (const group of fkGroups) {
2553
- const set = fkNamesByTable.get(group.table) ?? new Set;
2554
- set.add(group.constraintName);
2555
- fkNamesByTable.set(group.table, set);
2556
- }
2557
2658
  const pkByTable = new Map;
2558
2659
  const indexes = [];
2559
2660
  {
@@ -2562,6 +2663,12 @@ async function introspectMysql(params) {
2562
2663
  const table = catalogField({ row, name: "table_name" });
2563
2664
  const name = catalogField({ row, name: "index_name" });
2564
2665
  const columnName = catalogField({ row, name: "column_name" });
2666
+ const type = catalogField({ row, name: "index_type" });
2667
+ const unique = Number(catalogField({ row, name: "non_unique" })) === 0;
2668
+ const kind = typeof type === "string" ? type.toLowerCase() : "btree";
2669
+ if (catalogField({ row, name: "sub_part" }) != null || catalogField({ row, name: "expression" }) != null || catalogField({ row, name: "is_visible" }) === "NO" || typeof columnName !== "string" || kind !== "btree" && (kind !== "fulltext" || unique)) {
2670
+ throw new VibeError12({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `MySQL index "${table}.${name}" cannot be introspected without losing its physical shape`, meta: { dialect: "mysql", feature: "index introspection", table, index: name } });
2671
+ }
2565
2672
  if (name === "PRIMARY") {
2566
2673
  const list = pkByTable.get(table);
2567
2674
  if (list === undefined)
@@ -2570,8 +2677,6 @@ async function introspectMysql(params) {
2570
2677
  list.push(columnName);
2571
2678
  continue;
2572
2679
  }
2573
- if (fkNamesByTable.get(table)?.has(name) === true)
2574
- continue;
2575
2680
  const key = `${table}\x00${name}`;
2576
2681
  const entry = byKey.get(key);
2577
2682
  const column = {
@@ -2582,7 +2687,8 @@ async function introspectMysql(params) {
2582
2687
  byKey.set(key, {
2583
2688
  table,
2584
2689
  name,
2585
- unique: Number(catalogField({ row, name: "non_unique" })) === 0,
2690
+ unique,
2691
+ kind,
2586
2692
  columns: [column]
2587
2693
  });
2588
2694
  } else {
@@ -2590,34 +2696,39 @@ async function introspectMysql(params) {
2590
2696
  }
2591
2697
  }
2592
2698
  for (const entry of byKey.values()) {
2593
- indexes.push({ ...entry, kind: "btree" });
2699
+ const support = groupsByKey.get(`${entry.table}\x00${entry.name}`);
2700
+ if (!entry.unique && entry.kind === "btree" && support !== undefined && entry.columns.length === support.columns.length && entry.columns.every((column, index) => column.name === support.columns[index] && !column.desc))
2701
+ continue;
2702
+ indexes.push(entry);
2594
2703
  }
2595
2704
  }
2705
+ const indexesByTable = groupCatalogRows({ rows: indexes, key: (index) => index.table });
2706
+ const fkGroupsByTable = groupCatalogRows({ rows: fkGroups, key: (group) => group.table });
2596
2707
  const enums = [];
2597
2708
  const joinTables = new Map;
2598
2709
  let foreignKeysEnabled;
2599
2710
  for (const table of tables) {
2600
2711
  if (!table.startsWith("_") || pkByTable.has(table) || (checksByTable.get(table)?.length ?? 0) > 0)
2601
2712
  continue;
2602
- const tableIndexes = statisticRows.filter((row) => catalogField({ row, name: "table_name" }) === table);
2713
+ const tableIndexes = statisticsByTable.get(table) ?? [];
2603
2714
  if (new Set(tableIndexes.map((row) => catalogField({ row, name: "index_name" }))).size !== 2 || !tableIndexes.every((row) => catalogField({ row, name: "index_type" }) === "BTREE" && catalogField({ row, name: "is_visible" }) === "YES" && catalogField({ row, name: "sub_part" }) === null && catalogField({ row, name: "expression" }) === null && catalogField({ row, name: "collation" }) === "A" && Number.isInteger(Number(catalogField({ row, name: "seq_in_index" }))) && Number(catalogField({ row, name: "seq_in_index" })) > 0))
2604
2715
  continue;
2605
- const tableFks = fkRows.filter((row) => catalogField({ row, name: "table_name" }) === table);
2716
+ const tableFks = foreignKeysByTable.get(table) ?? [];
2606
2717
  if (tableFks.some((row) => {
2607
2718
  const targetTable = catalogField({ row, name: "referenced_table_name" });
2608
- const source = columnRows.find((column) => catalogField({ row: column, name: "table_name" }) === table && catalogField({ row: column, name: "column_name" }) === catalogField({ row, name: "column_name" }));
2609
- const target = columnRows.find((column) => catalogField({ row: column, name: "table_name" }) === targetTable && catalogField({ row: column, name: "column_name" }) === catalogField({ row, name: "referenced_column_name" }));
2719
+ const source = catalogColumns.get(JSON.stringify([table, catalogField({ row, name: "column_name" })]));
2720
+ const target = catalogColumns.get(JSON.stringify([targetTable, catalogField({ row, name: "referenced_column_name" })]));
2610
2721
  if (source === undefined || target === undefined)
2611
2722
  return true;
2612
2723
  const type = catalogField({ row: source, name: "column_type" });
2613
- return typeof type !== "string" || type.length === 0 || type !== catalogField({ row: target, name: "column_type" }) || catalogField({ row: source, name: "column_default" }) !== null || catalogField({ row: source, name: "extra" }) !== "" || catalogField({ row: source, name: "generation_expression" }) !== "" || catalogField({ row: source, name: "is_nullable" }) !== "NO" || catalogField({ row: source, name: "collation_name" }) === undefined || catalogField({ row: source, name: "collation_name" }) !== catalogField({ row: target, name: "collation_name" }) || catalogField({ row, name: "table_schema" }) === undefined || catalogField({ row, name: "table_schema" }) !== catalogField({ row, name: "referenced_table_schema" }) || ![table, targetTable].every((name) => tableRows.some((entry) => catalogField({ row: entry, name: "table_name" }) === name && catalogField({ row: entry, name: "engine" }) === "InnoDB"));
2724
+ return typeof type !== "string" || type.length === 0 || type !== catalogField({ row: target, name: "column_type" }) || catalogField({ row: source, name: "column_default" }) !== null || catalogField({ row: source, name: "extra" }) !== "" || catalogField({ row: source, name: "generation_expression" }) !== "" || catalogField({ row: source, name: "is_nullable" }) !== "NO" || catalogField({ row: source, name: "collation_name" }) === undefined || catalogField({ row: source, name: "collation_name" }) !== catalogField({ row: target, name: "collation_name" }) || catalogField({ row, name: "table_schema" }) === undefined || catalogField({ row, name: "table_schema" }) !== catalogField({ row, name: "referenced_table_schema" }) || ![table, targetTable].every((name) => enginesByTable.get(name) === "InnoDB");
2614
2725
  }))
2615
2726
  continue;
2616
2727
  const detected = detectJoinTable({
2617
2728
  table,
2618
- columnNames: columns.filter((column) => column.table === table).map((column) => column.name),
2619
- fkGroups,
2620
- indexes
2729
+ columnNames: (columnsByTable.get(table) ?? []).map((column) => column.name),
2730
+ fkGroups: fkGroupsByTable.get(table) ?? [],
2731
+ indexes: indexesByTable.get(table) ?? []
2621
2732
  });
2622
2733
  if (detected === null)
2623
2734
  continue;
@@ -2629,7 +2740,7 @@ async function introspectMysql(params) {
2629
2740
  continue;
2630
2741
  const q = (name) => mysqlDialect2.quoteIdent({ name });
2631
2742
  let valid = true;
2632
- for (const fk of fkGroups.filter((group) => group.table === table)) {
2743
+ for (const fk of fkGroupsByTable.get(table) ?? []) {
2633
2744
  const rows = await executor({ text: `SELECT 1 AS invalid FROM ${q(table)} child LEFT JOIN ${q(fk.refTable)} parent ON child.${q(fk.columns[0])} = parent.${q(fk.refColumns[0])} WHERE parent.${q(fk.refColumns[0])} IS NULL LIMIT 1`, values: [] });
2634
2745
  if (rows.length > 0) {
2635
2746
  valid = false;
@@ -2646,9 +2757,9 @@ async function introspectMysql(params) {
2646
2757
  models.push(buildModel2({
2647
2758
  table,
2648
2759
  isView: false,
2649
- columns,
2760
+ columns: columnsByTable.get(table) ?? [],
2650
2761
  pkByTable,
2651
- indexes,
2762
+ indexes: indexesByTable.get(table) ?? [],
2652
2763
  enums,
2653
2764
  checks: checksByTable.get(table) ?? []
2654
2765
  }));
@@ -2657,7 +2768,7 @@ async function introspectMysql(params) {
2657
2768
  models.push(buildModel2({
2658
2769
  table: view,
2659
2770
  isView: true,
2660
- columns,
2771
+ columns: columnsByTable.get(view) ?? [],
2661
2772
  pkByTable: new Map,
2662
2773
  indexes: [],
2663
2774
  enums,
@@ -2675,9 +2786,9 @@ async function introspectMysql(params) {
2675
2786
  }
2676
2787
  function buildModel2(params) {
2677
2788
  const { table, isView, columns, pkByTable, indexes, enums } = params;
2678
- const tableColumns = columns.filter((column) => column.table === table);
2789
+ const tableColumns = columns;
2679
2790
  const pk = pkByTable.get(table) ?? [];
2680
- const tableIndexes = indexes.filter((index) => index.table === table);
2791
+ const tableIndexes = indexes;
2681
2792
  const uniqueSingles = new Set;
2682
2793
  for (const index of tableIndexes) {
2683
2794
  if (index.unique && index.columns.length === 1) {
@@ -2708,7 +2819,7 @@ function buildModel2(params) {
2708
2819
  continue;
2709
2820
  modelIndexes.push({
2710
2821
  name: index.name,
2711
- kind: index.unique ? "unique" : "btree",
2822
+ kind: index.unique ? "unique" : index.kind,
2712
2823
  fields: index.columns.map((column) => ({
2713
2824
  name: column.name,
2714
2825
  ...column.desc ? { sort: "desc" } : {}
@@ -3240,7 +3351,7 @@ function q(name) {
3240
3351
  }
3241
3352
 
3242
3353
  // packages/migrate/src/execute.ts
3243
- import { VibeError as VibeError12 } from "@vibeorm/schema";
3354
+ import { VibeError as VibeError13 } from "@vibeorm/schema";
3244
3355
  var SELF_MANAGED_MARKER = "PRAGMA foreign_keys=OFF";
3245
3356
  var FK_CHECK_PREFIX = "PRAGMA foreign_key_check";
3246
3357
  function usesSelfManagedTransactions(params) {
@@ -3281,7 +3392,7 @@ function assertSelfManagedResumeSafe(params) {
3281
3392
  const completedIndex = params.direction === "up" ? params.start + 1 : -(params.start + 2);
3282
3393
  const rewind = selfManagedResumeStart({ sql: params.sql, start: params.start });
3283
3394
  const retryIndex = params.direction === "up" ? rewind : -(rewind + 1);
3284
- throw new VibeError12({
3395
+ throw new VibeError13({
3285
3396
  code: "VIBE_MIGRATION",
3286
3397
  message: `migration "${params.migration}" has a legacy sqlite checkpoint at ${params.direction} COMMIT (statement ${params.start + 1}). The block may already have committed; replay is refused. Inspect the block's effects. If it committed, set its journal statement_index to ${completedIndex} and error to NULL to resume the remaining statements. Only if the entire block did not commit, set statement_index to ${retryIndex} and error to NULL to retry the block. Do not mark the whole migration applied unless all remaining statements also ran.`,
3287
3398
  meta: { reason: "uncertain-statement", migration: params.migration, statementIndex: params.start, direction: params.direction, dialect: "sqlite" }
@@ -3310,7 +3421,7 @@ function unsupportedScript(params) {
3310
3421
  const { migration, direction, index, problem, found } = params;
3311
3422
  const subject = direction === "down" ? `the down script of migration "${migration}"` : `migration "${migration}"`;
3312
3423
  const position = `${direction === "down" ? "down statement" : "statement"} ${index + 1} of ${params.statements}`;
3313
- return new VibeError12({
3424
+ return new VibeError13({
3314
3425
  code: "VIBE_MIGRATION",
3315
3426
  message: `${subject} manages its own sqlite transactions, and ${position} breaks the shape recovery depends on: ${found}. ` + `A table-rebuild block must be written exactly as \`${SUPPORTED_BLOCK_SHAPE}\`. ` + "Inside that block a crash rolls everything back, so a resume can replay it safely. " + "A statement that sits in a foreign-keys-off region with no transaction around it commits on its own, " + "and the resume would run it a second time with no error raised either time. " + "Nothing has been executed. Move the statement after `PRAGMA foreign_keys=ON`, where it commits together " + "with its own journal checkpoint, or give it its own `BEGIN;` and `COMMIT;`. " + 'See "Self-managed sqlite scripts" in docs/migrations.md.',
3316
3427
  meta: { reason: "unsupported-self-managed-script", problem, migration, statementIndex: index, direction }
@@ -3366,7 +3477,7 @@ async function executeStatement(params) {
3366
3477
  const rows = await executor({ text: statement, values: [] });
3367
3478
  if (rows.length > 0) {
3368
3479
  const sample = rows.slice(0, 5).map((row) => `${String(row.table)} rowid ${String(row.rowid)} \u2192 ${String(row.parent)}`).join("; ");
3369
- throw new VibeError12({
3480
+ throw new VibeError13({
3370
3481
  code: "VIBE_MIGRATION",
3371
3482
  message: `foreign key check failed after table rebuild: ${rows.length} violation(s) \u2014 ${sample}`,
3372
3483
  meta: { violations: rows }
@@ -3421,7 +3532,7 @@ async function prepareOperatorClassDiff(params) {
3421
3532
  }
3422
3533
 
3423
3534
  // packages/migrate/src/postgres-default-equivalence.ts
3424
- import { VibeError as VibeError13 } from "@vibeorm/schema";
3535
+ import { VibeError as VibeError14 } from "@vibeorm/schema";
3425
3536
  import { postgresDialect as postgresDialect8, renderPostgresDefaultFunctionIdentity } from "@vibeorm/sql";
3426
3537
  var IDENTIFIER = '(?:"(?:[^"\\u0000]|"")+"|[A-Za-z_][A-Za-z_0-9$]*)';
3427
3538
  var CALL = new RegExp(`^\\s*(${IDENTIFIER})(?:\\s*\\.\\s*(${IDENTIFIER}))?\\s*\\(\\s*\\)\\s*$`);
@@ -3440,7 +3551,7 @@ async function equivalentPostgresDefaultFunction(params) {
3440
3551
  return false;
3441
3552
  const query = renderPostgresDefaultFunctionIdentity({ dialect: postgresDialect8, table: params.table, column: params.column });
3442
3553
  const rows = await params.executor({ text: query.text, values: [...query.values] }).catch((cause) => {
3443
- throw new VibeError13({ code: "VIBE_MIGRATION", message: "Cannot inspect the stored default function identity", meta: { reason: "default-inspection-unavailable", table: params.table, column: params.column }, cause });
3554
+ throw new VibeError14({ code: "VIBE_MIGRATION", message: "Cannot inspect the stored default function identity", meta: { reason: "default-inspection-unavailable", table: params.table, column: params.column }, cause });
3444
3555
  });
3445
3556
  if (rows.length !== 1)
3446
3557
  return false;
@@ -3454,7 +3565,7 @@ async function equivalentPostgresDefaultFunction(params) {
3454
3565
  }
3455
3566
 
3456
3567
  // packages/migrate/src/object-ownership.ts
3457
- import { VibeError as VibeError14, defaultPrimaryKeyName, enumStorageOf as enumStorageOf2, implicitJoinTableObjectNames, postgresGeneratedObjectName, qualifiedSqlName as qualifiedSqlName6, sqlObjectKey as sqlObjectKey3 } from "@vibeorm/schema";
3568
+ import { VibeError as VibeError15, defaultPrimaryKeyName, enumStorageOf as enumStorageOf2, implicitJoinTableObjectNames, postgresGeneratedObjectName, qualifiedSqlName as qualifiedSqlName6, sqlObjectKey as sqlObjectKey3 } from "@vibeorm/schema";
3458
3569
  function legacyMigrationObjects(params) {
3459
3570
  return params.artifacts.map((artifact) => {
3460
3571
  const owner = artifact.owns;
@@ -3556,7 +3667,10 @@ function removalIdentity(params) {
3556
3667
  }
3557
3668
  }
3558
3669
  function schemaObjectIdentities(params) {
3559
- const normalized = normalizeSchema({ schema: params.schema, dialect: params.dialect });
3670
+ return schemaObjectIdentitiesFromNormalized({ ...params, normalized: normalizeSchema({ schema: params.schema, dialect: params.dialect }) });
3671
+ }
3672
+ function schemaObjectIdentitiesFromNormalized(params) {
3673
+ const { normalized } = params;
3560
3674
  const result = [];
3561
3675
  if (params.dialect === "postgres") {
3562
3676
  for (const namespace of schemaNamespaces({ schema: params.schema }))
@@ -3606,7 +3720,7 @@ function assertRetainedMigrationObjects(params) {
3606
3720
  const keys = new Set(params.to.map((object) => sqlObjectKey3({ identity: object.identity })));
3607
3721
  const removed = params.from.filter((object) => !keys.has(sqlObjectKey3({ identity: object.identity })));
3608
3722
  if (removed.length > 0)
3609
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Removing migration ownership requires a reviewed hand-authored migration and new desired state", meta: { reason: "migration-object-removal-review-required", shadowReusable: true, objects: removed.map((object) => object.identity) } });
3723
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Removing migration ownership requires a reviewed hand-authored migration and new desired state", meta: { reason: "migration-object-removal-review-required", shadowReusable: true, objects: removed.map((object) => object.identity) } });
3610
3724
  }
3611
3725
  function orderedMigrationObjects(params) {
3612
3726
  const byKey = new Map(params.objects.map((object) => [sqlObjectKey3({ identity: object.identity }), object]));
@@ -3618,7 +3732,7 @@ function orderedMigrationObjects(params) {
3618
3732
  if (visited.has(key))
3619
3733
  return;
3620
3734
  if (visiting.has(key))
3621
- throw new VibeError14({ code: "VIBE_SCHEMA_INVALID", message: "Migration object dependency cycle", meta: { id: object.id } });
3735
+ throw new VibeError15({ code: "VIBE_SCHEMA_INVALID", message: "Migration object dependency cycle", meta: { id: object.id } });
3622
3736
  visiting.add(key);
3623
3737
  for (const dependency of object.dependsOn) {
3624
3738
  const target = byKey.get(sqlObjectKey3({ identity: dependency }));
@@ -3637,10 +3751,10 @@ function assertNamedRemovals(params) {
3637
3751
  const approved = new Set((params.approved ?? []).map((identity) => sqlObjectKey3({ identity })));
3638
3752
  const missing = params.removals.filter((identity) => !approved.has(sqlObjectKey3({ identity })));
3639
3753
  if (missing.length > 0)
3640
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Unmanaged removals require individually named approvals", meta: { reason: "unmanaged-removal-required", shadowReusable: true, objects: missing } });
3754
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Unmanaged removals require individually named approvals", meta: { reason: "unmanaged-removal-required", shadowReusable: true, objects: missing } });
3641
3755
  const integrity = params.removals.filter((identity) => identity.kind === "constraint" || identity.kind === "index");
3642
3756
  if (integrity.length > 0 && params.acceptIntegrityLoss !== true)
3643
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Named constraint or index removals also require acceptIntegrityLoss", meta: { reason: "integrity-acknowledgement-required", shadowReusable: true, objects: integrity } });
3757
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Named constraint or index removals also require acceptIntegrityLoss", meta: { reason: "integrity-acknowledgement-required", shadowReusable: true, objects: integrity } });
3644
3758
  }
3645
3759
  function assertRemovalAuthorization(params) {
3646
3760
  const declared = params.previous === undefined ? [] : schemaObjectIdentities({ schema: params.previous.schema, dialect: params.dialect, namespace: params.namespace });
@@ -3665,9 +3779,9 @@ function assertRemovalAuthorization(params) {
3665
3779
  }
3666
3780
  }
3667
3781
  if (unknown.length > 0)
3668
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Unknown object removals require individually named removeUnmanaged decisions", meta: { reason: "unmanaged-removal-required", shadowReusable: true, objects: unknown } });
3782
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Unknown object removals require individually named removeUnmanaged decisions", meta: { reason: "unmanaged-removal-required", shadowReusable: true, objects: unknown } });
3669
3783
  if (integrity.length > 0 && params.acceptIntegrityLoss !== true)
3670
- throw new VibeError14({ code: "VIBE_MIGRATION", message: "Constraint or index removal requires acceptIntegrityLoss; data-loss acknowledgement does not authorize it", meta: { reason: "integrity-acknowledgement-required", shadowReusable: true, objects: integrity } });
3784
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Constraint or index removal requires acceptIntegrityLoss; data-loss acknowledgement does not authorize it", meta: { reason: "integrity-acknowledgement-required", shadowReusable: true, objects: integrity } });
3671
3785
  }
3672
3786
 
3673
3787
  // packages/migrate/src/inspect-objects.ts
@@ -3752,7 +3866,7 @@ async function inspectSqlObject(params) {
3752
3866
  }
3753
3867
  }
3754
3868
  return { state: "present", definition: { identity: params.identity, facts, dependencies: [], format: "catalog-v2" } };
3755
- }).catch((error) => ({ state: "unavailable", reason: error instanceof VibeError15 ? String(error.meta.feature ?? error.code) : "catalog-query-failed" }));
3869
+ }).catch((error) => ({ state: "unavailable", reason: error instanceof VibeError16 ? String(error.meta.feature ?? error.code) : "catalog-query-failed" }));
3756
3870
  }
3757
3871
  function postgresInspection(params) {
3758
3872
  if (!params.addresses.every((address) => typeof address === "string"))
@@ -3767,7 +3881,7 @@ function postgresInspection(params) {
3767
3881
  return { state: "present", definition: { identity: params.identity, facts, dependencies, format: "catalog-v2" } };
3768
3882
  }
3769
3883
  function unavailableReason(params) {
3770
- return params.error instanceof VibeError15 ? String(params.error.meta.feature ?? params.error.meta.reason ?? params.error.code) : "catalog-query-failed";
3884
+ return params.error instanceof VibeError16 ? String(params.error.meta.feature ?? params.error.meta.reason ?? params.error.code) : "catalog-query-failed";
3771
3885
  }
3772
3886
  async function readBatch(params) {
3773
3887
  const positions = new Set(params.batch.positions);
@@ -3775,7 +3889,7 @@ async function readBatch(params) {
3775
3889
  const rows = new Map;
3776
3890
  for (const row of await params.executor({ text: params.batch.text, values: [...params.batch.values] })) {
3777
3891
  if (typeof row.position !== "number" || !Number.isInteger(row.position) || !positions.has(row.position))
3778
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Invalid batched catalog identity", meta: { reason: "catalog-query-failed" } });
3892
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Invalid batched catalog identity", meta: { reason: "catalog-query-failed" } });
3779
3893
  const matches = rows.get(row.position) ?? [];
3780
3894
  matches.push(row);
3781
3895
  rows.set(row.position, matches);
@@ -3820,7 +3934,7 @@ async function assertCatalogVisibility(params) {
3820
3934
  const grants = new Set(rows.map((row) => row.privilege));
3821
3935
  const missing = ["SELECT", "TRIGGER", "SHOW VIEW", "EXECUTE"].filter((privilege) => !grants.has(privilege));
3822
3936
  if (missing.length > 0)
3823
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "MySQL catalog visibility requires direct database-wide grants; role-only or partial grants cannot prove object absence", meta: { reason: "inventory-unavailable", missing } });
3937
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "MySQL catalog visibility requires direct database-wide grants; role-only or partial grants cannot prove object absence", meta: { reason: "inventory-unavailable", missing } });
3824
3938
  }
3825
3939
  var CATALOG_V1_OMITTED_FACTS = Object.freeze({
3826
3940
  function: ["definerOwner"],
@@ -3844,9 +3958,20 @@ function compare(params) {
3844
3958
  differences.push({ attribute: "dependencies", expected, actual });
3845
3959
  return differences;
3846
3960
  }
3961
+ function compareInspectedSqlObject(params) {
3962
+ const { identity, ownership, expected, live } = params;
3963
+ if (live.state !== "present")
3964
+ return { identity, ownership, state: live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} };
3965
+ if (expected === undefined)
3966
+ return identity.kind === "namespace" ? { identity, ownership, state: "matching", differences: [] } : { identity, ownership, state: "unavailable", reason: "independent-definition-required", differences: [] };
3967
+ if (params.dialect === "postgres" && identity.kind === "table" && (expected.facts.persistence === undefined || expected.facts.options === undefined))
3968
+ return { identity, ownership, state: "unavailable", reason: "table-physical-reference-required", differences: compare({ expected, live: live.definition }) };
3969
+ const differences = compare({ expected, live: live.definition });
3970
+ return { identity, ownership, state: differences.length === 0 ? "matching" : "changed", differences };
3971
+ }
3847
3972
  function assertProbeMode(params) {
3848
3973
  if (params.probe !== undefined && params.probe !== "none" && DIALECT_CAPABILITIES2[params.dialect].checkNormalization !== params.probe)
3849
- throw new VibeError15({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `${params.probe} CHECK probes are unavailable on ${params.dialect}`, meta: { feature: "checkProbe", dialect: params.dialect } });
3974
+ throw new VibeError16({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `${params.probe} CHECK probes are unavailable on ${params.dialect}`, meta: { feature: "checkProbe", dialect: params.dialect } });
3850
3975
  }
3851
3976
  async function verifyMigrationState(params) {
3852
3977
  return verifyState(params);
@@ -3869,18 +3994,7 @@ async function verifyState(params) {
3869
3994
  const expected = definitions.get(key);
3870
3995
  const live = inspections === undefined ? await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity }) : inspections[position];
3871
3996
  position++;
3872
- if (live.state !== "present") {
3873
- objects.push({ identity, ownership, state: live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} });
3874
- } else if (expected === undefined && identity.kind === "namespace") {
3875
- objects.push({ identity, ownership, state: "matching", differences: [] });
3876
- } else if (expected === undefined) {
3877
- objects.push({ identity, ownership, state: "unavailable", reason: "independent-definition-required", differences: [] });
3878
- } else if (params.dialect === "postgres" && identity.kind === "table" && (expected.facts.persistence === undefined || expected.facts.options === undefined)) {
3879
- objects.push({ identity, ownership, state: "unavailable", reason: "table-physical-reference-required", differences: compare({ expected, live: live.definition }) });
3880
- } else {
3881
- const differences = compare({ expected, live: live.definition });
3882
- objects.push({ identity, ownership, state: differences.length === 0 ? "matching" : "changed", differences });
3883
- }
3997
+ objects.push(compareInspectedSqlObject({ identity, ownership, expected, live, dialect: params.dialect }));
3884
3998
  }
3885
3999
  if (params.dialect === "postgres" && schemaNamespaces({ schema: params.expected.schema }).length > 0 || params.expected.schema.models.length > 0 || params.expected.schema.enums.length > 0)
3886
4000
  objects.push(...await verifySchemaObjects(params));
@@ -3944,7 +4058,8 @@ async function verifySchemaObjects(params) {
3944
4058
  pin("primaryKey", desired.pk, live.pk);
3945
4059
  for (const [column, field] of desired.columns) {
3946
4060
  const stored = live.columns.get(column);
3947
- pin(`columns.${column}.type`, field.typeKey, stored?.typeKey);
4061
+ const exactType = field.pinsExactType;
4062
+ pin(`columns.${column}.type`, exactType ? field.exactTypeKey : field.typeKey, exactType ? stored?.exactTypeKey : stored?.typeKey);
3948
4063
  pin(`columns.${column}.nullable`, field.nullable, stored?.nullable);
3949
4064
  if (params.dialect === "mysql" && field.field.collation !== undefined) {
3950
4065
  pin(`columns.${column}.collation`, field.field.collation.toLowerCase(), stored?.field.collation?.toLowerCase());
@@ -4048,7 +4163,7 @@ async function verifyMigrationTransition(params) {
4048
4163
  }
4049
4164
  async function verifySqliteTransitionBeforeRestore(params) {
4050
4165
  if (params.dialect !== "sqlite")
4051
- throw new VibeError15({ code: "VIBE_INTERNAL", message: "SQLite rebuild verification requires the SQLite dialect" });
4166
+ throw new VibeError16({ code: "VIBE_INTERNAL", message: "SQLite rebuild verification requires the SQLite dialect" });
4052
4167
  await executeStatement({ executor: params.executor, dialect: "sqlite", statement: "PRAGMA foreign_key_check;" });
4053
4168
  return verifyTransition({ ...params, sqliteRestorePending: true });
4054
4169
  }
@@ -4058,7 +4173,7 @@ async function verifyTransition(params) {
4058
4173
  for (const identity of params.removals ?? []) {
4059
4174
  const owner = externalObjectOwner({ identity, external });
4060
4175
  if (owner !== undefined)
4061
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Transition removals cannot target externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
4176
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Transition removals cannot target externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
4062
4177
  }
4063
4178
  params = { ...params, from: projectExternalState({ state: params.from, external }), to: projectExternalState({ state: params.to, external }) };
4064
4179
  }
@@ -4083,12 +4198,12 @@ async function migrationNamespace(params) {
4083
4198
  const rows = await params.executor({ text: query.text, values: [] });
4084
4199
  const namespace = rows[0]?.namespace;
4085
4200
  if (typeof namespace !== "string" || namespace.length === 0)
4086
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Cannot determine the migration namespace", meta: { reason: "inspection-unavailable" } });
4201
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Cannot determine the migration namespace", meta: { reason: "inspection-unavailable" } });
4087
4202
  return namespace;
4088
4203
  }
4089
4204
  function assertMigrationState(params) {
4090
4205
  if (!params.report.inSync)
4091
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Migration state could not be verified", meta: { reason: "migration-state-unverified", shadowReusable: true, objects: params.report.objects.filter((object) => object.state !== "matching") } });
4206
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Migration state could not be verified", meta: { reason: "migration-state-unverified", shadowReusable: true, objects: params.report.objects.filter((object) => object.state !== "matching") } });
4092
4207
  }
4093
4208
  async function deparsePostgresCheck(params) {
4094
4209
  const definition = await deparsePostgresCheckDefinition({ ...params, constraint: "vibeorm_check_probe" });
@@ -4102,7 +4217,7 @@ async function deparsePostgresCheckDefinition(params) {
4102
4217
  await params.executor({ text: query.text });
4103
4218
  const result = await inspectSqlObject({ executor: params.executor, dialect: "postgres", identity: { kind: "constraint", constraintType: "check", table: params.table, name } });
4104
4219
  if (result.state !== "present" || typeof result.definition.facts.definition !== "string")
4105
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "CHECK normalization probe unavailable", meta: { reason: "check-inspection-unavailable" } });
4220
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "CHECK normalization probe unavailable", meta: { reason: "check-inspection-unavailable" } });
4106
4221
  return { ...result.definition, identity: { kind: "constraint", constraintType: "check", table: params.table, name: params.constraint }, facts: { ...result.definition.facts, definition: result.definition.facts.definition.replace(/ NOT VALID$/, ""), validated: true } };
4107
4222
  } finally {
4108
4223
  await params.executor({ text: "ROLLBACK TO SAVEPOINT vibeorm_check_probe" });
@@ -4120,7 +4235,7 @@ async function deparseMysqlCheck(params) {
4120
4235
  const source = await params.executor({ text: read.text, values: [] });
4121
4236
  const definition = source[0]?.["Create Table"];
4122
4237
  if (typeof definition !== "string")
4123
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Cannot read temporary clone definition" });
4238
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Cannot read temporary clone definition" });
4124
4239
  const drop = renderMysqlCheckProbe({ dialect, probe, operation: "drop" });
4125
4240
  await params.executor({ text: drop.text });
4126
4241
  created = false;
@@ -4131,7 +4246,7 @@ async function deparseMysqlCheck(params) {
4131
4246
  const normalizedDefinition = rows[0]?.["Create Table"];
4132
4247
  const check = typeof normalizedDefinition === "string" ? parseSqliteNamedChecks({ tableSql: normalizedDefinition }).find((entry) => entry.name === probe) : undefined;
4133
4248
  if (check === undefined)
4134
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "MySQL temporary CHECK deparse unavailable", meta: { reason: "check-inspection-unavailable" } });
4249
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "MySQL temporary CHECK deparse unavailable", meta: { reason: "check-inspection-unavailable" } });
4135
4250
  return check.expression;
4136
4251
  } finally {
4137
4252
  if (created) {
@@ -4158,7 +4273,7 @@ async function prepareCheckDiff(params) {
4158
4273
  const desired = await deparsePostgresCheck({ executor: params.executor, table: identity.table, expression: next.expression, probe: "rollback" });
4159
4274
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
4160
4275
  if (live.state !== "present")
4161
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4276
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4162
4277
  if (live.definition.facts.definition === desired) {
4163
4278
  checks.push(next);
4164
4279
  continue;
@@ -4170,7 +4285,7 @@ async function prepareCheckDiff(params) {
4170
4285
  const desired = await deparseMysqlCheck({ executor: params.executor, table: identity.table, expression: next.expression, probe: "temporary" });
4171
4286
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
4172
4287
  if (live.state !== "present")
4173
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4288
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4174
4289
  checks.push(desired === live.definition.facts.definition ? next : { name: check.name, expression: check.expression });
4175
4290
  continue;
4176
4291
  }
@@ -4183,7 +4298,7 @@ async function prepareCheckDiff(params) {
4183
4298
  checks.push({ name: check.name, expression: check.expression });
4184
4299
  continue;
4185
4300
  }
4186
- throw new VibeError15({ code: "VIBE_MIGRATION", message: `Cannot establish CHECK equivalence for ${table}.${check.name}; request a rollback probe or an explicit replacement`, meta: { reason: "explicit-replacement-required", identity } });
4301
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: `Cannot establish CHECK equivalence for ${table}.${check.name}; request a rollback probe or an explicit replacement`, meta: { reason: "explicit-replacement-required", identity } });
4187
4302
  }
4188
4303
  models.push({ ...model, checks });
4189
4304
  }
@@ -4210,7 +4325,7 @@ async function inspectMigrationInventory(params) {
4210
4325
  for (const row of rows) {
4211
4326
  const name = row.name;
4212
4327
  if (typeof name !== "string" || typeof row.kind !== "string")
4213
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Malformed catalog identity", meta: { reason: "inventory-unavailable" } });
4328
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Malformed catalog identity", meta: { reason: "inventory-unavailable" } });
4214
4329
  const qualified = { namespace: params.namespace, name };
4215
4330
  const table = { namespace: params.namespace, name: String(row.parent ?? "") };
4216
4331
  let identity;
@@ -4236,7 +4351,7 @@ async function inspectMigrationInventory(params) {
4236
4351
  case "constraint": {
4237
4352
  const constraintType = kinds[String(row.subtype)];
4238
4353
  if (constraintType === undefined)
4239
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Unknown constraint catalog kind", meta: { reason: "inventory-unavailable" } });
4354
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Unknown constraint catalog kind", meta: { reason: "inventory-unavailable" } });
4240
4355
  identity = { kind: "constraint", table, name, constraintType };
4241
4356
  break;
4242
4357
  }
@@ -4246,17 +4361,17 @@ async function inspectMigrationInventory(params) {
4246
4361
  const query = renderMysqlObjectMembers2({ dialect: getSqlDialect2({ dialect: params.dialect }), identity: { kind: "function", name: qualified, argumentTypes: [] } });
4247
4362
  const members = await params.executor({ text: query.text, values: [...query.values] });
4248
4363
  if (members.length === 0)
4249
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "MySQL function signature unavailable", meta: { reason: "inventory-unavailable", name } });
4364
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "MySQL function signature unavailable", meta: { reason: "inventory-unavailable", name } });
4250
4365
  argumentTypes = members.filter((member) => member.mode !== null).map((member) => ({ namespace: "mysql", name: String(member.type) }));
4251
4366
  }
4252
4367
  const candidate = { kind: "function", name: qualified, argumentTypes };
4253
4368
  if (!isSqlObjectIdentity2(candidate))
4254
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Invalid function signature in catalog", meta: { reason: "inventory-unavailable" } });
4369
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Invalid function signature in catalog", meta: { reason: "inventory-unavailable" } });
4255
4370
  identity = candidate;
4256
4371
  break;
4257
4372
  }
4258
4373
  default:
4259
- throw new VibeError16({ code: "VIBE_MIGRATION", message: `Unknown catalog object kind ${row.kind}`, meta: { reason: "inventory-unavailable" } });
4374
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: `Unknown catalog object kind ${row.kind}`, meta: { reason: "inventory-unavailable" } });
4260
4375
  }
4261
4376
  objects.push({ identity, ...typeof row.backing === "string" ? { backingConstraint: row.backing } : {} });
4262
4377
  if (params.dialect === "sqlite" && row.kind === "table" && typeof row.definition === "string" && !/^\s*CREATE\s+VIRTUAL\s+TABLE\b/i.test(row.definition)) {
@@ -4301,7 +4416,7 @@ async function inspectMigrationInventory(params) {
4301
4416
  // packages/migrate/src/external-objects.ts
4302
4417
  var partitionPatterns = new WeakMap;
4303
4418
  function invalid2(params) {
4304
- return new VibeError17({ code: "VIBE_CONFIG", message: `Invalid external ownership declaration: ${params.detail}`, meta: { reason: "external-declaration-invalid", ...params.owner === undefined ? {} : { owner: params.owner } }, ...params.cause === undefined ? {} : { cause: params.cause } });
4419
+ return new VibeError18({ code: "VIBE_CONFIG", message: `Invalid external ownership declaration: ${params.detail}`, meta: { reason: "external-declaration-invalid", ...params.owner === undefined ? {} : { owner: params.owner } }, ...params.cause === undefined ? {} : { cause: params.cause } });
4305
4420
  }
4306
4421
  function record2(value) {
4307
4422
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -4389,7 +4504,7 @@ async function parseExternalObjects(params) {
4389
4504
  }
4390
4505
  return Object.freeze(result);
4391
4506
  }).catch((cause) => {
4392
- if (cause instanceof VibeError17)
4507
+ if (cause instanceof VibeError18)
4393
4508
  throw cause;
4394
4509
  throw invalid2({ detail: "regular-expression pattern cannot be compiled", cause });
4395
4510
  });
@@ -4413,7 +4528,7 @@ function declaredTableOwner(params) {
4413
4528
  return patterns?.some(({ pattern }) => pattern.exec(params.name.name)?.[0] === params.name.name) === true;
4414
4529
  });
4415
4530
  if (matches.length > 1)
4416
- throw new VibeError17({ code: "VIBE_CONFIG", message: "A table is claimed by more than one external owner", meta: { reason: "external-owner-collision", identity: { kind: "table", name: params.name }, owners: matches.map((entry) => entry.owner) } });
4531
+ throw new VibeError18({ code: "VIBE_CONFIG", message: "A table is claimed by more than one external owner", meta: { reason: "external-owner-collision", identity: { kind: "table", name: params.name }, owners: matches.map((entry) => entry.owner) } });
4417
4532
  return matches[0]?.owner;
4418
4533
  }
4419
4534
  function externalObjectOwner(params) {
@@ -4435,13 +4550,13 @@ function resolveExternalObjectOwnership(params) {
4435
4550
  if (params.external.length > 0 && params.dialect !== "postgres")
4436
4551
  throw unsupportedCapability7({ dialect: params.dialect, feature: "externalObjects" });
4437
4552
  if (params.catalog.inventory.some((object) => object.inspectionUnavailable !== undefined))
4438
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "External ownership requires complete catalog visibility", meta: { reason: "external-inventory-unavailable", objects: params.catalog.inventory.filter((object) => object.inspectionUnavailable !== undefined) } });
4553
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "External ownership requires complete catalog visibility", meta: { reason: "external-inventory-unavailable", objects: params.catalog.inventory.filter((object) => object.inspectionUnavailable !== undefined) } });
4439
4554
  const tables = new Map;
4440
4555
  const assign = (entry) => {
4441
4556
  const key = nameKey(entry.name);
4442
4557
  const prior = tables.get(key)?.owner ?? declaredTableOwner({ external: params.external, name: entry.name });
4443
4558
  if (prior !== undefined && prior !== entry.owner)
4444
- throw new VibeError17({ code: "VIBE_CONFIG", message: "An inherited table has conflicting external owners", meta: { reason: "external-owner-collision", identity: { kind: "table", name: entry.name }, owners: [prior, entry.owner] } });
4559
+ throw new VibeError18({ code: "VIBE_CONFIG", message: "An inherited table has conflicting external owners", meta: { reason: "external-owner-collision", identity: { kind: "table", name: entry.name }, owners: [prior, entry.owner] } });
4445
4560
  if (tables.has(key))
4446
4561
  return false;
4447
4562
  tables.set(key, entry);
@@ -4472,7 +4587,7 @@ function resolveExternalObjectOwnership(params) {
4472
4587
  return [];
4473
4588
  const whole = schemaOwner({ external: params.external, namespace: edge.sequence.namespace });
4474
4589
  if (whole !== undefined && whole !== owner)
4475
- throw new VibeError17({ code: "VIBE_CONFIG", message: "An owned sequence has conflicting external owners", meta: { reason: "external-owner-collision", identity: { kind: "sequence", name: edge.sequence }, owners: [whole, owner] } });
4590
+ throw new VibeError18({ code: "VIBE_CONFIG", message: "An owned sequence has conflicting external owners", meta: { reason: "external-owner-collision", identity: { kind: "sequence", name: edge.sequence }, owners: [whole, owner] } });
4476
4591
  return [{ owner, name: edge.sequence }];
4477
4592
  });
4478
4593
  const closure = { declarations: params.external, catalog: params.catalog, inventory: params.catalog.inventory, tables: [...tables.values()], sequences, owners: [], identities: [] };
@@ -4480,7 +4595,7 @@ function resolveExternalObjectOwnership(params) {
4480
4595
  const owner = externalObjectOwner({ external: closure, identity: { kind: "table", name: edge.table } });
4481
4596
  const targetOwner = externalObjectOwner({ external: closure, identity: { kind: "table", name: edge.target } });
4482
4597
  if (owner !== targetOwner && (owner !== undefined || targetOwner !== undefined))
4483
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "Foreign keys cannot cross external ownership boundaries", meta: { reason: "external-cross-owner-foreign-key", name: edge.name, table: edge.table, target: edge.target, owner: owner ?? "application", targetOwner: targetOwner ?? "application" } });
4598
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "Foreign keys cannot cross external ownership boundaries", meta: { reason: "external-cross-owner-foreign-key", name: edge.name, table: edge.table, target: edge.target, owner: owner ?? "application", targetOwner: targetOwner ?? "application" } });
4484
4599
  }
4485
4600
  const observed = new Map;
4486
4601
  for (const namespace of params.catalog.namespaces) {
@@ -4508,7 +4623,7 @@ function catalogName(params) {
4508
4623
  const namespace = params.row[params.namespace];
4509
4624
  const name = params.row[params.name];
4510
4625
  if (!identifier(namespace) || !identifier(name))
4511
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "Malformed external ownership catalog endpoint", meta: { reason: "external-inventory-unavailable" } });
4626
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "Malformed external ownership catalog endpoint", meta: { reason: "external-inventory-unavailable" } });
4512
4627
  return { namespace, name };
4513
4628
  }
4514
4629
  async function inspectExternalObjects(params) {
@@ -4521,7 +4636,7 @@ async function inspectExternalObjects(params) {
4521
4636
  };
4522
4637
  const namespaceRows = await read("namespaces");
4523
4638
  if (namespaceRows.some((row) => !identifier(row.namespace) || row.visible !== true))
4524
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "External ownership cannot prove visibility of every inspected namespace", meta: { reason: "external-inventory-unavailable" } });
4639
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "External ownership cannot prove visibility of every inspected namespace", meta: { reason: "external-inventory-unavailable" } });
4525
4640
  const namespaces = namespaceRows.map((row) => String(row.namespace));
4526
4641
  const [inherited, owned, foreignKeys] = await Promise.all([read("inheritance"), read("ownedSequences"), read("foreignKeys")]);
4527
4642
  const inventory = params.inventory ?? (await Promise.all(namespaces.map((namespace) => inspectMigrationInventory({ executor: params.executor, dialect: params.dialect, namespace })))).flat();
@@ -4542,7 +4657,7 @@ function assertExternalOwnershipClaims(params) {
4542
4657
  for (const identity of params.identities) {
4543
4658
  const owner = externalObjectOwner({ identity, external: params.external }) ?? (identity.kind === "namespace" ? [...params.external.tables, ...params.external.sequences].find((entry) => entry.name.namespace === identity.name)?.owner : undefined);
4544
4659
  if (owner !== undefined)
4545
- throw new VibeError17({ code: "VIBE_CONFIG", message: "Application or module declarations collide with external ownership", meta: { reason: "external-owned-object-collision", owner, identity } });
4660
+ throw new VibeError18({ code: "VIBE_CONFIG", message: "Application or module declarations collide with external ownership", meta: { reason: "external-owned-object-collision", owner, identity } });
4546
4661
  }
4547
4662
  }
4548
4663
  function projectExternalSchema(params) {
@@ -4552,7 +4667,7 @@ function projectExternalSchema(params) {
4552
4667
  for (const model of models)
4553
4668
  for (const relation of model.relations)
4554
4669
  if (removed.has(relation.targetModel))
4555
- throw new VibeError17({ code: "VIBE_MIGRATION", message: "Application relations cannot depend on externally owned tables", meta: { reason: "external-cross-owner-foreign-key", model: model.name, relation: relation.name } });
4670
+ throw new VibeError18({ code: "VIBE_MIGRATION", message: "Application relations cannot depend on externally owned tables", meta: { reason: "external-cross-owner-foreign-key", model: model.name, relation: relation.name } });
4556
4671
  return {
4557
4672
  ...schema,
4558
4673
  models,
@@ -4585,7 +4700,7 @@ function migrationCoverageGaps(params) {
4585
4700
  return [...new Map(kept.map((gap) => [JSON.stringify(gap), gap])).values()];
4586
4701
  }
4587
4702
  // packages/migrate/src/module-adoption.ts
4588
- import { VibeError as VibeError21 } from "@vibeorm/schema";
4703
+ import { VibeError as VibeError22 } from "@vibeorm/schema";
4589
4704
 
4590
4705
  // packages/migrate/src/mysql-catalog-visibility.ts
4591
4706
  function hasMysqlGlobalCatalogVisibility(params) {
@@ -4603,10 +4718,10 @@ function hasMysqlGlobalCatalogVisibility(params) {
4603
4718
  }
4604
4719
 
4605
4720
  // packages/migrate/src/module-ownership.ts
4606
- import { VibeError as VibeError18, sqlObjectKey as sqlObjectKey7, sqlObjectNamespace as sqlObjectNamespace3 } from "@vibeorm/schema";
4721
+ import { VibeError as VibeError19, sqlObjectKey as sqlObjectKey7, sqlObjectNamespace as sqlObjectNamespace3 } from "@vibeorm/schema";
4607
4722
  import { getSqlDialect as getSqlDialect4, renderModuleDependencyQuery } from "@vibeorm/sql";
4608
4723
  function refusal(params) {
4609
- return new VibeError18({ code: "VIBE_MIGRATION", message: "Module ownership or dependencies cannot be reconciled with the declared and live schema; no package SQL was authorized", meta: params });
4724
+ return new VibeError19({ code: "VIBE_MIGRATION", message: "Module ownership or dependencies cannot be reconciled with the declared and live schema; no package SQL was authorized", meta: params });
4610
4725
  }
4611
4726
  function identities(params) {
4612
4727
  return [...schemaObjectIdentities({ schema: params.state.schema, dialect: params.dialect, namespace: params.namespace }), ...(params.state.schema.migrationObjects ?? []).map((object) => object.identity), ...params.state.definitions.map((definition) => definition.identity)];
@@ -4667,7 +4782,7 @@ function assertModuleOwnership(params) {
4667
4782
  throw refusal({ reason: "module-mutates-application-state", moduleId: origin.moduleId });
4668
4783
  }
4669
4784
  function dependencyUnavailable(params) {
4670
- return new VibeError18({ code: "VIBE_MIGRATION", message: "Cannot establish module foreign-key dependencies across namespaces. MySQL requires direct global SHOW VIEW (or SELECT) without partial revokes; SHOW VIEW exposes metadata without granting external row reads. Restore catalog visibility before retrying", meta: { reason: "module-dependency-unavailable", moduleId: params.origin.moduleId }, ...params.cause === undefined ? {} : { cause: params.cause } });
4785
+ return new VibeError19({ code: "VIBE_MIGRATION", message: "Cannot establish module foreign-key dependencies across namespaces. MySQL requires direct global SHOW VIEW (or SELECT) without partial revokes; SHOW VIEW exposes metadata without granting external row reads. Restore catalog visibility before retrying", meta: { reason: "module-dependency-unavailable", moduleId: params.origin.moduleId }, ...params.cause === undefined ? {} : { cause: params.cause } });
4671
4786
  }
4672
4787
  async function assertCrossNamespaceModuleDependencies(params) {
4673
4788
  const { executor, origin } = params;
@@ -4729,12 +4844,12 @@ async function assertApplicationModuleState(params) {
4729
4844
  const options = { executor: params.executor, dialect: origin.dialect, from: state.from, to: state.to, removals: state.unmanagedRemovals, ...params.probe === undefined ? {} : { probe: params.probe } };
4730
4845
  const report = params.sqliteRestorePending === true ? await verifySqliteTransitionBeforeRestore(options) : await verifyMigrationTransition(options);
4731
4846
  if (!report.inSync)
4732
- throw new VibeError18({ code: "VIBE_MIGRATION", message: `Application SQL cannot change objects owned by module ${origin.moduleId}; use its reviewed module upgrade`, meta: { reason: "module-application-mutation", moduleId: origin.moduleId, report } });
4847
+ throw new VibeError19({ code: "VIBE_MIGRATION", message: `Application SQL cannot change objects owned by module ${origin.moduleId}; use its reviewed module upgrade`, meta: { reason: "module-application-mutation", moduleId: origin.moduleId, report } });
4733
4848
  }
4734
4849
  }
4735
4850
 
4736
4851
  // packages/migrate/src/sql-statements.ts
4737
- import { VibeError as VibeError19 } from "@vibeorm/schema";
4852
+ import { VibeError as VibeError20 } from "@vibeorm/schema";
4738
4853
  var COMPOUND_BODY_HEADER = /^(?:\s|--[^\n]*\n|\/\*[\s\S]*?\*\/)*CREATE\s+(?:OR\s+REPLACE\s+)?(?:(?:TEMP|TEMPORARY)\s+)?(?:DEFINER\s*=\s*\S+\s+)?(?:TRIGGER|PROCEDURE|FUNCTION|EVENT)\b/i;
4739
4854
  var BLOCK_OPENERS = new Set(["BEGIN", "CASE", "IF", "LOOP", "WHILE", "REPEAT"]);
4740
4855
  function splitSqlStatements(params) {
@@ -4810,7 +4925,7 @@ function splitSqlStatements(params) {
4810
4925
  continue;
4811
4926
  }
4812
4927
  if (dialect === "mysql" || dialect === undefined) {
4813
- throw new VibeError19({ code: "VIBE_MIGRATION", message: "SQL string backslash escaping depends on the session SQL mode; use mode-independent quoting and pass the migration dialect", meta: { reason: "sql-mode-ambiguous", dialect } });
4928
+ throw new VibeError20({ code: "VIBE_MIGRATION", message: "SQL string backslash escaping depends on the session SQL mode; use mode-independent quoting and pass the migration dialect", meta: { reason: "sql-mode-ambiguous", dialect } });
4814
4929
  }
4815
4930
  }
4816
4931
  if (value === endQuote) {
@@ -4884,10 +4999,10 @@ function joinSqlStatements(params) {
4884
4999
 
4885
5000
  // packages/migrate/src/module-origin.ts
4886
5001
  import { createHash } from "crypto";
4887
- import { VibeError as VibeError20, isSqlObjectIdentity as isSqlObjectIdentity3, sqlObjectKey as sqlObjectKey8 } from "@vibeorm/schema";
5002
+ import { VibeError as VibeError21, isSqlObjectIdentity as isSqlObjectIdentity3, sqlObjectKey as sqlObjectKey8 } from "@vibeorm/schema";
4888
5003
  var MODULE_ORIGIN_METADATA = Object.freeze({ filename: "module-origin.json", version: 1 });
4889
5004
  function invalid3() {
4890
- return new VibeError20({ code: "VIBE_MIGRATION", message: "Module migration origin is malformed or differs from its immutable package artifact; restore the reviewed artifact", meta: { reason: "module-origin-invalid" } });
5005
+ return new VibeError21({ code: "VIBE_MIGRATION", message: "Module migration origin is malformed or differs from its immutable package artifact; restore the reviewed artifact", meta: { reason: "module-origin-invalid" } });
4891
5006
  }
4892
5007
  function record3(value) {
4893
5008
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -4951,7 +5066,7 @@ function assertMaterializedModuleMigration(params) {
4951
5066
  return;
4952
5067
  const restored = restoreModuleMigration({ migration, origin: migration.moduleOrigin });
4953
5068
  if (migration.desiredState === undefined || canonicalMigrationJson({ value: restored }) !== canonicalMigrationJson({ value: migration }))
4954
- throw new VibeError20({ code: "VIBE_MIGRATION", message: "Materialized module metadata differs from its immutable source", meta: { reason: "module-source-state-mismatch", moduleId: migration.moduleOrigin.moduleId } });
5069
+ throw new VibeError21({ code: "VIBE_MIGRATION", message: "Materialized module metadata differs from its immutable source", meta: { reason: "module-source-state-mismatch", moduleId: migration.moduleOrigin.moduleId } });
4955
5070
  }
4956
5071
  function restoreModuleMigration(params) {
4957
5072
  const { migration, origin } = params;
@@ -4976,7 +5091,7 @@ function restoreModuleMigration(params) {
4976
5091
  // packages/migrate/src/module-adoption.ts
4977
5092
  var MODULE_ADOPTION_METADATA = Object.freeze({ filename: "module-adoption.json", version: 1 });
4978
5093
  function invalid4() {
4979
- return new VibeError21({ code: "VIBE_MIGRATION", message: "Local module adoption proof is malformed or lacks immutable source provenance; restore the reviewed local history", meta: { reason: "module-adoption-invalid" } });
5094
+ return new VibeError22({ code: "VIBE_MIGRATION", message: "Local module adoption proof is malformed or lacks immutable source provenance; restore the reviewed local history", meta: { reason: "module-adoption-invalid" } });
4980
5095
  }
4981
5096
  function assertMarker(params) {
4982
5097
  const value = params.value;
@@ -5011,22 +5126,22 @@ function serializeModuleAdoption(params) {
5011
5126
  `;
5012
5127
  }
5013
5128
  // packages/migrate/src/module-adoption-record.ts
5014
- import { VibeError as VibeError42 } from "@vibeorm/schema";
5129
+ import { VibeError as VibeError43 } from "@vibeorm/schema";
5015
5130
 
5016
5131
  // packages/migrate/src/module-adoption-reference.ts
5017
- import { VibeError as VibeError41, isEnumType as isEnumType9, sqlObjectKey as sqlObjectKey14 } from "@vibeorm/schema";
5132
+ import { VibeError as VibeError42, isEnumType as isEnumType9, sqlObjectKey as sqlObjectKey14 } from "@vibeorm/schema";
5018
5133
 
5019
5134
  // packages/migrate/src/module-pending.ts
5020
- import { VibeError as VibeError37, sqlObjectKey as sqlObjectKey11, qualifiedSqlName as qualifiedSqlName10 } from "@vibeorm/schema";
5135
+ import { VibeError as VibeError38, sqlObjectKey as sqlObjectKey11, qualifiedSqlName as qualifiedSqlName11 } from "@vibeorm/schema";
5021
5136
 
5022
5137
  // packages/migrate/src/module-bundle.ts
5023
- import { VibeError as VibeError36 } from "@vibeorm/schema";
5138
+ import { VibeError as VibeError37 } from "@vibeorm/schema";
5024
5139
 
5025
5140
  // packages/migrate/src/migration-checksum.ts
5026
5141
  import { createHash as createHash2 } from "crypto";
5027
5142
 
5028
5143
  // packages/migrate/src/execution.ts
5029
- import { VibeError as VibeError22 } from "@vibeorm/schema";
5144
+ import { VibeError as VibeError23 } from "@vibeorm/schema";
5030
5145
  var EXECUTION_SIDECAR_FILENAME = "execution.json";
5031
5146
  var EXECUTION_SIDECAR_VERSION = 1;
5032
5147
  function isDefaultExecution(params) {
@@ -5065,7 +5180,7 @@ ${lines.join(`
5065
5180
  `)}`;
5066
5181
  }
5067
5182
  function invalid5(params) {
5068
- return new VibeError22({
5183
+ return new VibeError23({
5069
5184
  code: "VIBE_MIGRATION",
5070
5185
  message: `invalid execution metadata for migration "${params.migration}": ${params.detail}`,
5071
5186
  meta: { reason: "execution-metadata-invalid", migration: params.migration }
@@ -5116,7 +5231,7 @@ function parseExecutionSidecar(params) {
5116
5231
  try {
5117
5232
  parsed = JSON.parse(params.text);
5118
5233
  } catch (error) {
5119
- throw new VibeError22({
5234
+ throw new VibeError23({
5120
5235
  code: "VIBE_MIGRATION",
5121
5236
  message: `invalid execution metadata for migration "${migration}": not valid JSON`,
5122
5237
  meta: { reason: "execution-metadata-invalid", migration },
@@ -5215,7 +5330,7 @@ function migrationChecksum(params) {
5215
5330
  }
5216
5331
 
5217
5332
  // packages/migrate/src/shadow-guard.ts
5218
- import { VibeError as VibeError23 } from "@vibeorm/schema";
5333
+ import { VibeError as VibeError24 } from "@vibeorm/schema";
5219
5334
  import { getSqlDialect as getSqlDialect5, renderShadowOperation } from "@vibeorm/sql";
5220
5335
  var replayExecutors = new WeakSet;
5221
5336
  function isShadowReplayExecutor(params) {
@@ -5227,7 +5342,7 @@ async function assertDatabaseNotShadow(params) {
5227
5342
  const [query] = renderShadowOperation({ dialect: getSqlDialect5({ dialect: params.dialect }), operation: { kind: "markerExists" } });
5228
5343
  const rows = await params.executor({ text: query.text, values: [...query.values] });
5229
5344
  if (rows.length !== 0)
5230
- throw new VibeError23({ code: "VIBE_MIGRATION", message: "This database is claimed as a disposable migration shadow. Application migrations must use the deployment target.", meta: { reason: "shadow-database-is-not-target" } });
5345
+ throw new VibeError24({ code: "VIBE_MIGRATION", message: "This database is claimed as a disposable migration shadow. Application migrations must use the deployment target.", meta: { reason: "shadow-database-is-not-target" } });
5231
5346
  }
5232
5347
  function runShadowReplay(params) {
5233
5348
  replayExecutors.add(params.executor);
@@ -5237,7 +5352,7 @@ function runShadowReplay(params) {
5237
5352
  }
5238
5353
 
5239
5354
  // packages/migrate/src/external-dependencies.ts
5240
- import { VibeError as VibeError24, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
5355
+ import { VibeError as VibeError25, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
5241
5356
  import { getSqlDialect as getSqlDialect6, renderExternalDependents } from "@vibeorm/sql";
5242
5357
  function externalDependencyTargets(params) {
5243
5358
  return params.steps.flatMap((step) => {
@@ -5260,7 +5375,7 @@ function externalDependencyTargets(params) {
5260
5375
  });
5261
5376
  }
5262
5377
  function unavailable() {
5263
- return new VibeError24({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency row", meta: { reason: "external-inventory-unavailable" } });
5378
+ return new VibeError25({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency row", meta: { reason: "external-inventory-unavailable" } });
5264
5379
  }
5265
5380
  function text(params) {
5266
5381
  const value = params.row[params.key];
@@ -5301,7 +5416,7 @@ function assertNoExternalDependents(params) {
5301
5416
  const dependent = dependentOwner({ row, external: params.external });
5302
5417
  if (dependent.owner === undefined)
5303
5418
  continue;
5304
- throw new VibeError24({
5419
+ throw new VibeError25({
5305
5420
  code: "VIBE_MIGRATION",
5306
5421
  message: "An externally owned object depends on an application object this change drops or retypes; automatic DDL is refused",
5307
5422
  meta: { reason: "external-owned-dependency", owner: dependent.owner, ...dependent.identity === undefined ? {} : { identity: dependent.identity }, dependent: { type, identity }, dependency: target.dependency, referenced }
@@ -5322,9 +5437,9 @@ async function refuseExternalDependents(params) {
5322
5437
  }
5323
5438
 
5324
5439
  // packages/migrate/src/module-history.ts
5325
- import { VibeError as VibeError25, sqlObjectKey as sqlObjectKey9, providerToDialect as providerToDialect3 } from "@vibeorm/schema";
5440
+ import { VibeError as VibeError26, sqlObjectKey as sqlObjectKey9, providerToDialect as providerToDialect3 } from "@vibeorm/schema";
5326
5441
  function refusal2(params) {
5327
- return new VibeError25({ code: "VIBE_MIGRATION", message: `Module ${params.moduleId} history, ownership or predecessor state is inconsistent; restore or complete the reviewed upgrade first`, meta: params });
5442
+ return new VibeError26({ code: "VIBE_MIGRATION", message: `Module ${params.moduleId} history, ownership or predecessor state is inconsistent; restore or complete the reviewed upgrade first`, meta: params });
5328
5443
  }
5329
5444
  function moduleHistoryHeads(params) {
5330
5445
  const heads = new Map;
@@ -5431,11 +5546,11 @@ function assertModuleUpgradeHistory(params) {
5431
5546
  }
5432
5547
 
5433
5548
  // packages/migrate/src/module-journal.ts
5434
- import { VibeError as VibeError27 } from "@vibeorm/schema";
5549
+ import { VibeError as VibeError28 } from "@vibeorm/schema";
5435
5550
  import { getSqlDialect as getSqlDialect7, MODULE_JOURNAL_COLUMN, renderModuleJournal } from "@vibeorm/sql";
5436
5551
 
5437
5552
  // packages/migrate/src/journal-read.ts
5438
- import { VibeError as VibeError26 } from "@vibeorm/schema";
5553
+ import { VibeError as VibeError27 } from "@vibeorm/schema";
5439
5554
  var MISSING_TABLE_IDENTIFIERS = new Set(["42P01", "42S02", "ER_NO_SUCH_TABLE"]);
5440
5555
  function isMissingTable(params) {
5441
5556
  let current = params.cause;
@@ -5459,7 +5574,7 @@ async function readJournalRows(params) {
5459
5574
  if (presence.state === "missing")
5460
5575
  return;
5461
5576
  }
5462
- throw new VibeError26({ code: "VIBE_MIGRATION", message: "Cannot inspect migration history; check SELECT privileges", meta: { reason: "history-unavailable" }, cause });
5577
+ throw new VibeError27({ code: "VIBE_MIGRATION", message: "Cannot inspect migration history; check SELECT privileges", meta: { reason: "history-unavailable" }, cause });
5463
5578
  });
5464
5579
  if (rows === undefined)
5465
5580
  return;
@@ -5467,7 +5582,7 @@ async function readJournalRows(params) {
5467
5582
  for (const row of rows) {
5468
5583
  const index = row.statement_index;
5469
5584
  if (typeof row.name !== "string" || typeof row.checksum !== "string" || typeof index !== "number" && typeof index !== "string" || typeof index === "string" && !/^-?\d+$/.test(index) || !Number.isSafeInteger(Number(index)) || row.module_origin !== null && row.module_origin !== undefined && typeof row.module_origin !== "string" || row.error !== null && row.error !== undefined && typeof row.error !== "string" || result.has(row.name)) {
5470
- throw new VibeError26({ code: "VIBE_MIGRATION", message: "Malformed migration journal row", meta: { reason: "history-unavailable" } });
5585
+ throw new VibeError27({ code: "VIBE_MIGRATION", message: "Malformed migration journal row", meta: { reason: "history-unavailable" } });
5471
5586
  }
5472
5587
  result.set(row.name, { checksum: row.checksum, statementIndex: Number(index), ...typeof row.error === "string" ? { error: row.error } : {}, ...typeof row.module_origin === "string" ? { moduleEntry: row.module_origin } : {} });
5473
5588
  }
@@ -5476,7 +5591,7 @@ async function readJournalRows(params) {
5476
5591
 
5477
5592
  // packages/migrate/src/module-journal.ts
5478
5593
  function invalid6() {
5479
- return new VibeError27({ code: "VIBE_MIGRATION", message: "Module migration history is missing, altered or inconsistent; restore the reviewed history before using this module", meta: { reason: "module-history-invalid" } });
5594
+ return new VibeError28({ code: "VIBE_MIGRATION", message: "Module migration history is missing, altered or inconsistent; restore the reviewed history before using this module", meta: { reason: "module-history-invalid" } });
5480
5595
  }
5481
5596
  function parseModuleJournalEntry(params) {
5482
5597
  return parseMigrationMetadata({ text: params.text, failure: invalid6, parse: (value) => {
@@ -5535,7 +5650,7 @@ function moduleRecordComplete(params) {
5535
5650
  }
5536
5651
 
5537
5652
  // packages/migrate/src/runner.ts
5538
- import { VibeError as VibeError35 } from "@vibeorm/schema";
5653
+ import { VibeError as VibeError36 } from "@vibeorm/schema";
5539
5654
  import {
5540
5655
  DIALECT_CAPABILITIES as DIALECT_CAPABILITIES3,
5541
5656
  getSqlDialect as getSqlDialect9,
@@ -5545,7 +5660,7 @@ import {
5545
5660
  } from "@vibeorm/sql";
5546
5661
 
5547
5662
  // packages/migrate/src/recovery.ts
5548
- import { VibeError as VibeError28 } from "@vibeorm/schema";
5663
+ import { VibeError as VibeError29 } from "@vibeorm/schema";
5549
5664
  import {
5550
5665
  getSqlDialect as getSqlDialect8,
5551
5666
  renderColumnStateQuery,
@@ -5561,7 +5676,7 @@ function constraintUnverifiable(params) {
5561
5676
  });
5562
5677
  }
5563
5678
  function conflict2(params) {
5564
- return new VibeError28({
5679
+ return new VibeError29({
5565
5680
  code: "VIBE_MIGRATION",
5566
5681
  message: params.message,
5567
5682
  meta: { reason: params.reason, ...params.meta }
@@ -5666,10 +5781,10 @@ async function establishOperationState(params) {
5666
5781
 
5667
5782
  // packages/migrate/src/differ.ts
5668
5783
  import { assertIndexOperatorClasses as assertIndexOperatorClasses2, isEnumType as isEnumType6, qualifiedSqlName as qualifiedSqlName9, unsupportedCapability as unsupportedCapability10 } from "@vibeorm/schema";
5669
- import { VibeError as VibeError30 } from "@vibeorm/schema";
5784
+ import { VibeError as VibeError31 } from "@vibeorm/schema";
5670
5785
 
5671
5786
  // packages/migrate/src/renames.ts
5672
- import { VibeError as VibeError29, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
5787
+ import { VibeError as VibeError30, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
5673
5788
  function tableOf(model) {
5674
5789
  return model.dbName ?? model.name;
5675
5790
  }
@@ -5680,7 +5795,7 @@ function hasColumn(params) {
5680
5795
  return params.model.fields.some((field) => (field.dbName ?? field.name) === params.column);
5681
5796
  }
5682
5797
  function ambiguous(params) {
5683
- return new VibeError29({
5798
+ return new VibeError30({
5684
5799
  code: "VIBE_MIGRATION",
5685
5800
  message: `rename refused: ${params.detail}`,
5686
5801
  meta: { reason: "rename-ambiguous", ...params.meta }
@@ -5782,17 +5897,34 @@ function planRenames(params) {
5782
5897
 
5783
5898
  // packages/migrate/src/differ.ts
5784
5899
  function diffSchemas(params) {
5900
+ return diffSchemasWithNormalization(params).steps;
5901
+ }
5902
+ function diffSchemasWithNormalization(params) {
5785
5903
  const dialect = params.dialect ?? "postgres";
5786
5904
  assertIndexOperatorClasses2({ schema: params.to, dialect });
5787
- for (const kind of ["models", "enums"])
5905
+ for (const kind of ["models", "enums"]) {
5906
+ const sourceByName = new Map;
5907
+ const sourcePhysical = new Set;
5908
+ const targetPhysical = new Set(params.to[kind].map((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect })));
5909
+ const removedByBasename = new Map;
5910
+ for (const entry of params.from[kind]) {
5911
+ if (!sourceByName.has(entry.name))
5912
+ sourceByName.set(entry.name, entry);
5913
+ const basename = entry.dbName ?? entry.name;
5914
+ const key = physicalNameKey({ name: basename, namespace: entry.namespace, dialect });
5915
+ sourcePhysical.add(key);
5916
+ if (!targetPhysical.has(key) && !removedByBasename.has(basename))
5917
+ removedByBasename.set(basename, entry);
5918
+ }
5788
5919
  for (const entry of params.to[kind]) {
5789
5920
  const key = physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect });
5790
- 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 })));
5791
- if (before !== undefined && qualifiedSqlName9({ name: entry.name, namespace: before.namespace }).namespace !== qualifiedSqlName9({ name: entry.name, namespace: entry.namespace }).namespace && !params.from[kind].some((candidate) => physicalNameKey({ name: candidate.dbName ?? candidate.name, namespace: candidate.namespace, dialect }) === key))
5921
+ const before = sourceByName.get(entry.name) ?? removedByBasename.get(entry.dbName ?? entry.name);
5922
+ if (before !== undefined && qualifiedSqlName9({ name: entry.name, namespace: before.namespace }).namespace !== qualifiedSqlName9({ name: entry.name, namespace: entry.namespace }).namespace && !sourcePhysical.has(key))
5792
5923
  throw unsupportedCapability10({ dialect, feature: "automaticNamespaceMove", hint: "Moving populated objects to a fixed namespace requires an explicit reviewed migration" });
5793
5924
  }
5794
- for (const schema of [params.from, params.to])
5795
- normalizeSchema({ schema, dialect });
5925
+ }
5926
+ let from = normalizeSchema({ schema: params.from, dialect });
5927
+ const to = normalizeSchema({ schema: params.to, dialect });
5796
5928
  for (const schema of [params.from, params.to])
5797
5929
  if (dialect !== "postgres" && schema.models.some((model) => (model.exclusions?.length ?? 0) > 0))
5798
5930
  throw unsupportedCapability10({ dialect, feature: "exclusionConstraints" });
@@ -5801,11 +5933,12 @@ function diffSchemas(params) {
5801
5933
  if (dialect !== "postgres")
5802
5934
  refuseScalarListColumns({ schema: params.to, dialect });
5803
5935
  const renamed = planRenames({ from: params.from, to: params.to, renames: params.renames ?? [], dialect });
5936
+ if (renamed.from !== params.from)
5937
+ from = normalizeSchema({ schema: renamed.from, dialect });
5804
5938
  params = { ...params, from: renamed.from };
5805
- const from = normalizeSchema({ schema: params.from, dialect });
5806
- const to = normalizeSchema({ schema: params.to, dialect });
5939
+ const sourceNamespaces = new Set(dialect === "postgres" ? schemaNamespaces({ schema: params.from }) : []);
5807
5940
  const buckets = {
5808
- createNamespace: dialect === "postgres" ? schemaNamespaces({ schema: params.to }).filter((namespace) => !schemaNamespaces({ schema: params.from }).includes(namespace)).map((namespace) => ({ kind: "createNamespace", namespace, destructive: false })) : [],
5941
+ createNamespace: dialect === "postgres" ? schemaNamespaces({ schema: params.to }).filter((namespace) => !sourceNamespaces.has(namespace)).map((namespace) => ({ kind: "createNamespace", namespace, destructive: false })) : [],
5809
5942
  dropExclusion: [],
5810
5943
  addExclusion: [],
5811
5944
  dropView: [],
@@ -5833,16 +5966,22 @@ function diffSchemas(params) {
5833
5966
  diffViews({ from, to, buckets });
5834
5967
  diffJoinTables({ from, to, buckets });
5835
5968
  diffTables({ from, to, buckets, dialect });
5969
+ const sourceModels = new Map;
5970
+ for (const model of params.from.models) {
5971
+ const key = physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect });
5972
+ if (!sourceModels.has(key))
5973
+ sourceModels.set(key, model);
5974
+ }
5836
5975
  for (const model of params.to.models) {
5837
5976
  const table = model.dbName ?? model.name;
5838
5977
  const placement = model.namespace === undefined ? {} : { namespace: model.namespace };
5839
- const previous = params.from.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect }) === physicalNameKey({ name: table, namespace: model.namespace, dialect }));
5978
+ const previous = sourceModels.get(physicalNameKey({ name: table, namespace: model.namespace, dialect }));
5840
5979
  for (const exclusion of model.exclusions ?? []) {
5841
5980
  const before = previous?.exclusions?.find((entry) => entry.name === exclusion.name);
5842
5981
  if (before?.definition === exclusion.definition)
5843
5982
  continue;
5844
5983
  if (before?.catalogDefinition)
5845
- throw new VibeError30({ code: "VIBE_MIGRATION", message: "Exclusion comparison requires normalized evidence", meta: { reason: "exclusion-inspection-unavailable", table, name: exclusion.name } });
5984
+ throw new VibeError31({ code: "VIBE_MIGRATION", message: "Exclusion comparison requires normalized evidence", meta: { reason: "exclusion-inspection-unavailable", table, name: exclusion.name } });
5846
5985
  if (before !== undefined)
5847
5986
  buckets.dropExclusion.push({ kind: "dropExclusion", table, ...placement, exclusion: before, destructive: false });
5848
5987
  buckets.addExclusion.push({ kind: "addExclusion", table, ...placement, exclusion, destructive: false });
@@ -5852,7 +5991,8 @@ function diffSchemas(params) {
5852
5991
  buckets.dropExclusion.push({ kind: "dropExclusion", table, exclusion, destructive: false });
5853
5992
  }
5854
5993
  refuseDependentEnumStorageChanges({ from, to, buckets, dialect });
5855
- return withRlsSteps({ from: params.from, to: params.to, steps: [
5994
+ refuseMysqlForeignKeyColumnRewrites({ from, to, buckets, dialect });
5995
+ return { from, to, steps: withRlsSteps({ from: params.from, to: params.to, steps: [
5856
5996
  ...buckets.createNamespace,
5857
5997
  ...renamed.steps,
5858
5998
  ...buckets.dropView,
@@ -5876,7 +6016,7 @@ function diffSchemas(params) {
5876
6016
  ...buckets.addForeignKey,
5877
6017
  ...buckets.createJoinTable,
5878
6018
  ...buckets.dropEnum
5879
- ] });
6019
+ ] }) };
5880
6020
  }
5881
6021
  function refuseScalarListColumns(params) {
5882
6022
  const { schema, dialect } = params;
@@ -5943,6 +6083,40 @@ function refuseDependentEnumStorageChanges(params) {
5943
6083
  }
5944
6084
  }
5945
6085
  }
6086
+ function refuseMysqlForeignKeyColumnRewrites(params) {
6087
+ const { from, to, buckets, dialect } = params;
6088
+ if (dialect !== "mysql")
6089
+ return;
6090
+ const droppedFks = new Set(buckets.dropForeignKey.map((step) => step.kind === "dropForeignKey" ? `${step.table}\x00${step.constraintName}` : ""));
6091
+ for (const step of buckets.alterColumn) {
6092
+ if (step.kind !== "alterColumn")
6093
+ continue;
6094
+ const collation = step.changes.includes("collation");
6095
+ const type = step.changes.includes("type") && !isMysqlVarcharWidening({ from: step.from, to: step.to });
6096
+ if (!collation && !type)
6097
+ continue;
6098
+ const joined = [...from.joinTables].some(([key, join]) => to.joinTables.has(key) && [join.a, join.b].some((end) => end.table === step.table && end.column === step.column));
6099
+ const foreign = [...from.tables].some(([tableKey, table]) => [...table.fks].some(([fkKey, fk]) => to.tables.get(tableKey)?.fks.has(fkKey) === true && !droppedFks.has(`${table.table}\x00${fk.constraintName}`) && (table.table === step.table && fk.columns.includes(step.column) || fk.refTable === step.table && fk.refColumns.includes(step.column))));
6100
+ if (!joined && !foreign)
6101
+ continue;
6102
+ throw unsupportedCapability10({
6103
+ dialect,
6104
+ feature: `${collation ? "collation" : "type"} change of "${step.table}"."${step.column}" ${joined ? "referenced by an implicit many-to-many join table" : "in a foreign key"}`,
6105
+ hint: `drop the foreign key, change both columns' ${collation ? "collation" : "type"} and re-add it in a reviewed manual migration`
6106
+ });
6107
+ }
6108
+ }
6109
+ function isMysqlVarcharWidening(params) {
6110
+ const length = (field) => {
6111
+ if (isEnumType6(field.type) || field.isList || field.isUnsupported === true)
6112
+ return null;
6113
+ const match = /^varchar\((\d+)\)$/.exec(normalizeMysqlType({ raw: mysqlColumnTypeSql({ field, enums: new Map }) }));
6114
+ return match === null ? null : Number(match[1]);
6115
+ };
6116
+ const before = length(params.from);
6117
+ const after = length(params.to);
6118
+ return before !== null && after !== null && after >= before;
6119
+ }
5946
6120
  function diffViews(params) {
5947
6121
  const { from, to, buckets } = params;
5948
6122
  for (const [name, view] of from.views) {
@@ -6051,7 +6225,7 @@ function diffOneTable(params) {
6051
6225
  continue;
6052
6226
  }
6053
6227
  const changes = [];
6054
- if (before.typeKey !== column.typeKey)
6228
+ if (columnTypeChanged({ declared: column, actual: before }))
6055
6229
  changes.push("type");
6056
6230
  if (before.nullable !== column.nullable)
6057
6231
  changes.push("nullability");
@@ -6074,7 +6248,7 @@ function diffOneTable(params) {
6074
6248
  assertStringEnumLabelsFit({ field: column.field, values, dialect, table });
6075
6249
  }
6076
6250
  }
6077
- let narrowingType = changes.includes("type") && !isScalarToArrayChange({ from: before.field, to: column.field }) && !(storageChange && column.field.type === "String");
6251
+ let narrowingType = changes.includes("type") && !isScalarToArrayChange({ from: before.field, to: column.field }) && !(dialect === "postgres" && isTimestamptzPrecisionWidening({ from: before.field, to: column.field })) && !(storageChange && column.field.type === "String");
6078
6252
  if (dialect !== "postgres" && changes.includes("type") && isEnumType6(before.field.type) && isEnumType6(column.field.type)) {
6079
6253
  const fromValues = resolveEnumValues({ name: before.field.type.enum, enums: fromEnums });
6080
6254
  const toValues = resolveEnumValues({ name: column.field.type.enum, enums: toEnums });
@@ -6098,7 +6272,7 @@ function diffOneTable(params) {
6098
6272
  from: before.field,
6099
6273
  to: column.field,
6100
6274
  changes,
6101
- destructive: narrowingType || collationChanged
6275
+ destructive: narrowingType || collationChanged || dialect === "mysql" && before.nullable && !column.nullable
6102
6276
  });
6103
6277
  }
6104
6278
  for (const [name] of previous.columns) {
@@ -6106,9 +6280,7 @@ function diffOneTable(params) {
6106
6280
  buckets.dropColumn.push({ kind: "dropColumn", table, ...placement, column: name, destructive: true });
6107
6281
  }
6108
6282
  }
6109
- const pkBefore = previous.pk.join(",");
6110
- const pkAfter = current.pk.join(",");
6111
- if (pkBefore !== pkAfter) {
6283
+ if (previous.pk.length !== current.pk.length || previous.pk.some((column, index) => column !== current.pk[index])) {
6112
6284
  if (previous.pk.length > 0) {
6113
6285
  buckets.dropPrimaryKey.push({ kind: "dropPrimaryKey", table, ...placement, destructive: false, ...previous.model.primaryKeyConstraintName === undefined ? {} : { constraintName: previous.model.primaryKeyConstraintName } });
6114
6286
  }
@@ -6128,7 +6300,7 @@ function diffOneTable(params) {
6128
6300
  if (before === expression)
6129
6301
  continue;
6130
6302
  if (before !== undefined && previous.model.checks?.some((check) => check.name === name && check.catalogExpression === true))
6131
- throw new VibeError30({ code: "VIBE_MIGRATION", message: "Unequal catalog CHECK text requires normalized evidence or explicit replacement through the live planner", meta: { reason: "check-inspection-unavailable", table, name } });
6303
+ throw new VibeError31({ code: "VIBE_MIGRATION", message: "Unequal catalog CHECK text requires normalized evidence or explicit replacement through the live planner", meta: { reason: "check-inspection-unavailable", table, name } });
6132
6304
  if (before !== undefined)
6133
6305
  buckets.dropCheck.push({ kind: "dropCheck", table, ...placement, name, destructive: false });
6134
6306
  buckets.addCheck.push({ kind: "addCheck", table, ...placement, name, expression, destructive: false });
@@ -6214,11 +6386,11 @@ function diffOneTable(params) {
6214
6386
  }
6215
6387
 
6216
6388
  // packages/migrate/src/ddl/render.ts
6217
- import { VibeError as VibeError34, sqlObjectKey as sqlObjectKey10 } from "@vibeorm/schema";
6389
+ import { VibeError as VibeError35, sqlObjectKey as sqlObjectKey10 } from "@vibeorm/schema";
6218
6390
 
6219
6391
  // packages/migrate/src/ddl/postgres.ts
6220
6392
  import { renderExclusionSetup } from "@vibeorm/sql";
6221
- import { VibeError as VibeError31, assertSqlNamespace as assertSqlNamespace3, defaultPrimaryKeyName as defaultPrimaryKeyName3, implicitJoinTableObjectNames as implicitJoinTableObjectNames2, isEnumType as isEnumType7 } from "@vibeorm/schema";
6393
+ import { VibeError as VibeError32, assertSqlNamespace as assertSqlNamespace3, defaultPrimaryKeyName as defaultPrimaryKeyName3, implicitJoinTableObjectNames as implicitJoinTableObjectNames2, isEnumType as isEnumType7 } from "@vibeorm/schema";
6222
6394
  import { postgresDialect as postgresDialect9, renderRlsPolicyPath, renderIndexColumns } from "@vibeorm/sql";
6223
6395
  function renderPostgresSteps(params) {
6224
6396
  refuseSameTransactionEnumUse({ steps: params.steps });
@@ -6255,7 +6427,7 @@ function refuseSameTransactionEnumUse(params) {
6255
6427
  const found = use(field);
6256
6428
  if (found === undefined)
6257
6429
  continue;
6258
- throw new VibeError31({
6430
+ throw new VibeError32({
6259
6431
  code: "VIBE_MIGRATION",
6260
6432
  message: `enum value "${found.value}" is added to "${found.enumName}" and used as a default on "${step.table}"."${field.dbName ?? field.name}" in the same migration \u2014 PostgreSQL cannot use an enum member before the transaction that added it commits. Split this into two migrations: the first containing only the ALTER TYPE ... ADD VALUE, the second the column change`,
6261
6433
  meta: { enum: found.enumName, value: found.value, table: step.table, column: field.dbName ?? field.name, rule: "enum-value-used-in-adding-transaction" }
@@ -6329,7 +6501,7 @@ ${lines.join(`,
6329
6501
  case "dropIndex":
6330
6502
  if (step.constraintName !== undefined) {
6331
6503
  if (step.table === undefined)
6332
- throw new VibeError31({ code: "VIBE_MIGRATION", message: "Constraint-backed index removal requires its owning table" });
6504
+ throw new VibeError32({ code: "VIBE_MIGRATION", message: "Constraint-backed index removal requires its owning table" });
6333
6505
  return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} DROP CONSTRAINT ${q2(step.constraintName)};`];
6334
6506
  }
6335
6507
  return [`DROP INDEX ${qualifiedName(step.name, step.namespace)};`];
@@ -6394,7 +6566,7 @@ function columnDefSql(params) {
6394
6566
  const { field } = params;
6395
6567
  const name = q2(field.dbName ?? field.name);
6396
6568
  if (field.default?.kind === "autoincrement" && !isIdentityColumn({ field })) {
6397
- throw new VibeError31({
6569
+ throw new VibeError32({
6398
6570
  code: "VIBE_MIGRATION",
6399
6571
  message: `autoincrement default on "${field.name}" requires a non-list Int or BigInt column`,
6400
6572
  meta: { field: field.name }
@@ -6430,7 +6602,7 @@ function renderAlterColumn(params) {
6430
6602
  }
6431
6603
  if (step.changes.includes("default") || droppedDefault) {
6432
6604
  if (step.to.default?.kind === "autoincrement") {
6433
- throw new VibeError31({
6605
+ throw new VibeError32({
6434
6606
  code: "VIBE_MIGRATION",
6435
6607
  message: `cannot add autoincrement to existing column "${step.table}"."${step.column}" \u2014 drop and recreate the column instead`,
6436
6608
  meta: { table: step.table, column: step.column }
@@ -6450,7 +6622,7 @@ function q2(name) {
6450
6622
  }
6451
6623
 
6452
6624
  // packages/migrate/src/ddl/sqlite.ts
6453
- import { VibeError as VibeError32, implicitJoinTableObjectNames as implicitJoinTableObjectNames3, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
6625
+ import { VibeError as VibeError33, implicitJoinTableObjectNames as implicitJoinTableObjectNames3, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
6454
6626
  import { sqliteDialect as sqliteDialect3 } from "@vibeorm/sql";
6455
6627
 
6456
6628
  // packages/migrate/src/ddl/drop-order.ts
@@ -6598,7 +6770,7 @@ function renderSqliteSteps(params) {
6598
6770
  continue;
6599
6771
  const awaitsNewParent = [...toNorm?.tables.get(table)?.fks.values() ?? []].some((fk) => createdTables.has(fk.refTable));
6600
6772
  if (awaitsNewParent) {
6601
- throw new VibeError32({
6773
+ throw new VibeError33({
6602
6774
  code: "VIBE_MIGRATION",
6603
6775
  message: `cannot order this sqlite migration safely: rebuilding "${table}" must happen BEFORE the dropped table(s) it references (${[
6604
6776
  ...droppedTables
@@ -6627,7 +6799,7 @@ function renderSqliteSteps(params) {
6627
6799
  switch (step.kind) {
6628
6800
  case "addExclusion":
6629
6801
  case "dropExclusion":
6630
- throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6802
+ throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6631
6803
  case "createEnum":
6632
6804
  case "dropEnum":
6633
6805
  case "addEnumValue":
@@ -6713,7 +6885,7 @@ function renderSqliteSteps(params) {
6713
6885
  case "renameColumn": {
6714
6886
  const renamed = step.kind === "renameTable" ? step.from : step.table;
6715
6887
  if (rebuildSet.has(renamed) || step.kind === "renameTable" && rebuildSet.has(step.to)) {
6716
- throw new VibeError32({
6888
+ throw new VibeError33({
6717
6889
  code: "VIBE_MIGRATION",
6718
6890
  message: `sqlite cannot rename "${renamed}" in the same migration that rebuilds it \u2014 the table-rebuild recipe is written against a single table name. Split the rename and the column/constraint change into two migrations`,
6719
6891
  meta: { reason: "rename-ambiguous", table: renamed, dialect: "sqlite" }
@@ -6767,7 +6939,7 @@ function classifyRebuilds(params) {
6767
6939
  switch (step.kind) {
6768
6940
  case "addExclusion":
6769
6941
  case "dropExclusion":
6770
- throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6942
+ throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6771
6943
  case "alterColumn":
6772
6944
  case "dropForeignKey":
6773
6945
  case "addPrimaryKey":
@@ -6828,7 +7000,7 @@ function renderTableRebuild(params) {
6828
7000
  const source = from.tables.get(table);
6829
7001
  const target = to.tables.get(table);
6830
7002
  if (source === undefined || target === undefined) {
6831
- throw new VibeError32({
7003
+ throw new VibeError33({
6832
7004
  code: "VIBE_MIGRATION",
6833
7005
  message: `cannot rebuild "${table}" \u2014 the table is missing from the ${source === undefined ? "from" : "to"} schema`,
6834
7006
  meta: { table }
@@ -6963,7 +7135,7 @@ function columnDefSql2(params) {
6963
7135
  const { field, constraintTable, enums, inlinePkAutoincrement } = params;
6964
7136
  const name = field.dbName ?? field.name;
6965
7137
  if (field.default?.kind === "autoincrement" && !inlinePkAutoincrement) {
6966
- throw new VibeError32({
7138
+ throw new VibeError33({
6967
7139
  code: "VIBE_MIGRATION",
6968
7140
  message: `autoincrement on sqlite requires "${field.name}" to be the single-column Int or BigInt primary key`,
6969
7141
  meta: { field: field.name }
@@ -6982,7 +7154,7 @@ function columnDefSql2(params) {
6982
7154
  const enumName = field.type.enum;
6983
7155
  const values = resolveEnumValues({ name: enumName, enums });
6984
7156
  if (values === undefined) {
6985
- throw new VibeError32({
7157
+ throw new VibeError33({
6986
7158
  code: "VIBE_MIGRATION",
6987
7159
  message: `enum "${enumName}" on column "${field.name}" is not declared in the schema \u2014 sqlite rendering needs the target schema's enums for the CHECK constraint`,
6988
7160
  meta: { field: field.name, enum: enumName }
@@ -7017,7 +7189,7 @@ function requireSchemas(params) {
7017
7189
  const { step, fromNorm, toNorm } = params;
7018
7190
  if (fromNorm !== undefined && toNorm !== undefined)
7019
7191
  return;
7020
- throw new VibeError32({
7192
+ throw new VibeError33({
7021
7193
  code: "VIBE_MIGRATION",
7022
7194
  message: `sqlite step "${describeStep({ step })}" requires a table rebuild \u2014 renderSteps needs the { from, to } schemas to construct it`,
7023
7195
  meta: { step: step.kind }
@@ -7028,7 +7200,7 @@ function q3(name) {
7028
7200
  }
7029
7201
 
7030
7202
  // packages/migrate/src/ddl/mysql.ts
7031
- import { VibeError as VibeError33, implicitJoinTableObjectNames as implicitJoinTableObjectNames4, unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
7203
+ import { VibeError as VibeError34, implicitJoinTableObjectNames as implicitJoinTableObjectNames4, unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
7032
7204
  import { mysqlDialect as mysqlDialect3 } from "@vibeorm/sql";
7033
7205
  function renderMysqlSteps(params) {
7034
7206
  for (const step of params.steps) {
@@ -7176,12 +7348,12 @@ ${lines.join(`,
7176
7348
  ];
7177
7349
  case "addExclusion":
7178
7350
  case "dropExclusion":
7179
- throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "mysql", feature: "exclusionConstraints" } });
7351
+ throw new VibeError34({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "mysql", feature: "exclusionConstraints" } });
7180
7352
  case "dropCheck":
7181
7353
  return [`ALTER TABLE ${q4(step.table)} DROP CHECK ${q4(step.name)};`];
7182
7354
  case "dropIndex": {
7183
7355
  if (step.table === undefined) {
7184
- throw new VibeError33({
7356
+ throw new VibeError34({
7185
7357
  code: "VIBE_MIGRATION",
7186
7358
  message: `dropIndex "${step.name}" carries no table \u2014 mysql's DROP INDEX requires one`,
7187
7359
  meta: { index: step.name }
@@ -7211,8 +7383,8 @@ ${lines.join(`,
7211
7383
  case "dropForeignKey":
7212
7384
  return [`ALTER TABLE ${q4(step.table)} DROP FOREIGN KEY ${q4(step.constraintName)};`];
7213
7385
  case "createJoinTable": {
7214
- const aType = mysqlColumnTypeSql({ field: { ...step.a.field, isList: false }, enums });
7215
- const bType = mysqlColumnTypeSql({ field: { ...step.b.field, isList: false }, enums });
7386
+ const aType = joinColumnTypeSql({ field: step.a.field, enums });
7387
+ const bType = joinColumnTypeSql({ field: step.b.field, enums });
7216
7388
  const names = implicitJoinTableObjectNames4({ table: step.table });
7217
7389
  return [
7218
7390
  `CREATE TABLE ${q4(step.table)} (
@@ -7233,12 +7405,17 @@ ${lines.join(`,
7233
7405
  return [`ALTER TABLE ${q4(step.table)} DROP PRIMARY KEY;`];
7234
7406
  }
7235
7407
  }
7408
+ function joinColumnTypeSql(params) {
7409
+ const { field, enums } = params;
7410
+ const type = mysqlColumnTypeSql({ field: { ...field, isList: false }, enums });
7411
+ return field.collation === undefined ? type : `${type} COLLATE ${q4(field.collation)}`;
7412
+ }
7236
7413
  function columnDefSql3(params) {
7237
7414
  const { field, enums } = params;
7238
7415
  const name = q4(field.dbName ?? field.name);
7239
7416
  const autoincrement = field.default?.kind === "autoincrement";
7240
7417
  if (autoincrement && field.type !== "Int" && field.type !== "BigInt") {
7241
- throw new VibeError33({
7418
+ throw new VibeError34({
7242
7419
  code: "VIBE_MIGRATION",
7243
7420
  message: `autoincrement default on "${field.name}" requires a non-list Int or BigInt column`,
7244
7421
  meta: { field: field.name }
@@ -7260,7 +7437,7 @@ function renderAlterColumn2(params) {
7260
7437
  const { step, enums } = params;
7261
7438
  assertEnumStringTransition({ from: step.from, to: step.to, dialect: "mysql", table: step.table, column: step.column });
7262
7439
  if (step.to.default?.kind === "autoincrement" && step.from.default?.kind !== "autoincrement") {
7263
- throw new VibeError33({
7440
+ throw new VibeError34({
7264
7441
  code: "VIBE_MIGRATION",
7265
7442
  message: `cannot add autoincrement to existing column "${step.table}"."${step.column}" \u2014 drop and recreate the column instead`,
7266
7443
  meta: { table: step.table, column: step.column }
@@ -7300,24 +7477,24 @@ function declaredSqlitePreservation(params) {
7300
7477
  continue;
7301
7478
  const facts = object.expected?.facts;
7302
7479
  if (facts === undefined)
7303
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "SQLite rebuild requires independently declared preservation definitions", meta: { reason: "sqlite-preservation-unavailable", identity } });
7480
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "SQLite rebuild requires independently declared preservation definitions", meta: { reason: "sqlite-preservation-unavailable", identity } });
7304
7481
  if (identity.kind === "trigger" || identity.kind === "index") {
7305
7482
  if (identity.kind === "index" && to.models.some((model) => (model.dbName ?? model.name) === identity.table.name && model.indexes.some((index) => index.name === identity.name)))
7306
7483
  continue;
7307
7484
  if (typeof facts.definition !== "string")
7308
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite restoration SQL", meta: { reason: "sqlite-preservation-unavailable", identity } });
7485
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Missing SQLite restoration SQL", meta: { reason: "sqlite-preservation-unavailable", identity } });
7309
7486
  const statements = restore.get(identity.table.name) ?? [];
7310
7487
  statements.push(facts.definition);
7311
7488
  restore.set(identity.table.name, statements);
7312
7489
  } else if (identity.kind === "column" || identity.kind === "generatedColumn") {
7313
7490
  if (typeof facts.columnDefinition !== "string")
7314
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite column clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7491
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Missing SQLite column clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7315
7492
  const fields = columns.get(identity.table.name) ?? new Map;
7316
7493
  fields.set(identity.column, { sql: facts.columnDefinition, generated: Number(facts.generated) > 1 });
7317
7494
  columns.set(identity.table.name, fields);
7318
7495
  } else if (identity.kind === "columnDefault") {
7319
7496
  if (facts.definition !== null && typeof facts.definition !== "string")
7320
- throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite default clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7497
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Missing SQLite default clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7321
7498
  const expression = facts.definition;
7322
7499
  to = { ...to, models: to.models.map((model) => (model.dbName ?? model.name) !== identity.table.name ? model : { ...model, fields: model.fields.map((field) => {
7323
7500
  if ((field.dbName ?? field.name) !== identity.column)
@@ -7354,7 +7531,7 @@ function renderSteps(params) {
7354
7531
  }
7355
7532
 
7356
7533
  // packages/migrate/src/extensions.ts
7357
- import { extensionDeclarations } from "@vibeorm/schema";
7534
+ import { extensionDeclarations, providerToDialect as providerToDialect4, qualifiedSqlName as qualifiedSqlName10, SQL_NAMESPACES } from "@vibeorm/schema";
7358
7535
  function collectExtensionArtifacts(params) {
7359
7536
  const { schema, dialect, extensions } = params;
7360
7537
  const setup = [];
@@ -7421,6 +7598,10 @@ function stripExtensionArtifacts(params) {
7421
7598
  const models = [];
7422
7599
  for (const model of schema.models) {
7423
7600
  const tableName = model.dbName ?? model.name;
7601
+ if (providerToDialect4({ provider: schema.datasource.provider }) === "postgres" && qualifiedSqlName10({ name: tableName, namespace: model.namespace }).namespace !== SQL_NAMESPACES.postgres) {
7602
+ models.push(model);
7603
+ continue;
7604
+ }
7424
7605
  if (tables.exact.has(tableName) || tables.prefixes.some((prefix) => tableName.startsWith(prefix))) {
7425
7606
  continue;
7426
7607
  }
@@ -7444,6 +7625,8 @@ function planMissingArtifacts(params) {
7444
7625
  return [];
7445
7626
  const modelByTable = new Map;
7446
7627
  for (const model of introspected.models) {
7628
+ if (providerToDialect4({ provider: introspected.datasource.provider }) === "postgres" && qualifiedSqlName10({ name: model.dbName ?? model.name, namespace: model.namespace }).namespace !== SQL_NAMESPACES.postgres)
7629
+ continue;
7447
7630
  modelByTable.set(model.dbName ?? model.name, model);
7448
7631
  }
7449
7632
  return artifacts.filter((artifact) => {
@@ -7602,10 +7785,10 @@ function assertNativeMigrationMetadata(params) {
7602
7785
  if (!native)
7603
7786
  return;
7604
7787
  if (!DIALECT_CAPABILITIES3[params.dialect].nativeRowLevelSecurity) {
7605
- throw new VibeError35({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Native RLS migrations are unsupported on ${params.dialect}`, meta: { feature: "nativeRowLevelSecurity", dialect: params.dialect } });
7788
+ throw new VibeError36({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Native RLS migrations are unsupported on ${params.dialect}`, meta: { feature: "nativeRowLevelSecurity", dialect: params.dialect } });
7606
7789
  }
7607
7790
  if (params.migration.rls === undefined) {
7608
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Native RLS migration files require trusted rls.from/rls.to schema metadata. SQL alone cannot prove policy ownership or drift; regenerate with preserved security metadata.", meta: { reason: "rls-migration-metadata-required", migration: params.migration.name } });
7791
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Native RLS migration files require trusted rls.from/rls.to schema metadata. SQL alone cannot prove policy ownership or drift; regenerate with preserved security metadata.", meta: { reason: "rls-migration-metadata-required", migration: params.migration.name } });
7609
7792
  }
7610
7793
  }
7611
7794
  var PG_ADVISORY_LOCK_KEY = 1447645253;
@@ -7686,12 +7869,12 @@ function generateMigration(params) {
7686
7869
  }),
7687
7870
  ...orderedMigrationObjects({ objects: to.migrationObjects ?? [] }).flatMap((object) => {
7688
7871
  if (object.dialect !== dialect)
7689
- throw new VibeError35({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}` });
7872
+ throw new VibeError36({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}` });
7690
7873
  const previous = (params.desiredState?.from.schema.migrationObjects ?? from.migrationObjects)?.find((entry) => entry.id === object.id);
7691
7874
  if (previous === undefined)
7692
7875
  return [...object.create];
7693
7876
  if (JSON.stringify(previous) !== JSON.stringify(object))
7694
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration-owned object ${object.id} changed; supply a reviewed forward SQL migration`, meta: { reason: "migration-object-replacement-required", shadowReusable: true, id: object.id } });
7877
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration-owned object ${object.id} changed; supply a reviewed forward SQL migration`, meta: { reason: "migration-object-replacement-required", shadowReusable: true, id: object.id } });
7695
7878
  return [];
7696
7879
  })
7697
7880
  ];
@@ -7717,8 +7900,8 @@ async function withMigrationLock(params) {
7717
7900
  }, (cause) => releaseLock({ executor, dialect: params.dialect }).then(() => {
7718
7901
  throw cause;
7719
7902
  }, (cleanupCause) => {
7720
- if (cause instanceof VibeError35)
7721
- throw new VibeError35({ code: cause.code, message: cause.message, meta: { ...cause.meta, cleanupCause }, cause });
7903
+ if (cause instanceof VibeError36)
7904
+ throw new VibeError36({ code: cause.code, message: cause.message, meta: { ...cause.meta, cleanupCause }, cause });
7722
7905
  throw cause;
7723
7906
  }));
7724
7907
  };
@@ -7748,11 +7931,11 @@ function assertMigrationMetadata(params) {
7748
7931
  assertModuleOwnership({ origin: migration.moduleOrigin, desiredState: migration.desiredState });
7749
7932
  }
7750
7933
  if (migration.desiredStatePending === true)
7751
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration ${migration.name} has unfinalized desired state; finalize its reviewed before/after declarations before applying`, meta: { reason: "desired-state-unfinalized", migration: migration.name } });
7934
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration ${migration.name} has unfinalized desired state; finalize its reviewed before/after declarations before applying`, meta: { reason: "desired-state-unfinalized", migration: migration.name } });
7752
7935
  if (migration.desiredState !== undefined) {
7753
7936
  assertDesiredState({ value: migration.desiredState, migration: migration.name });
7754
7937
  if (migration.desiredState.dialect !== dialect)
7755
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch", migration: migration.name } });
7938
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch", migration: migration.name } });
7756
7939
  }
7757
7940
  assertNativeMigrationMetadata({ migration, sql: migration.sql, dialect });
7758
7941
  assertExecutable({ migration, execution: migration.execution, sql: migration.sql, dialect });
@@ -7762,7 +7945,7 @@ async function applyMigrations(params) {
7762
7945
  const dialect = params.dialect ?? "postgres";
7763
7946
  const trustedReplay = isShadowReplayExecutor({ executor });
7764
7947
  if (params.history !== undefined && params.history !== "complete" && params.history !== "subset")
7765
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Unknown migration history validation mode", meta: { reason: "history-mode-invalid" } });
7948
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Unknown migration history validation mode", meta: { reason: "history-mode-invalid" } });
7766
7949
  if (params.history === "complete")
7767
7950
  assertDistinctMigrationNames({ migrations });
7768
7951
  for (const migration of migrations)
@@ -7778,7 +7961,7 @@ async function applyMigrations(params) {
7778
7961
  assertModuleUpgradeHistory({ migrations, records: moduleRecords });
7779
7962
  const namespace = await migrationNamespace({ executor, dialect });
7780
7963
  if (dialect !== "postgres" && migrations.some((migration) => migration.moduleOrigin !== undefined && moduleNamespace({ origin: migration.moduleOrigin }) !== namespace))
7781
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Module placement differs from the active database namespace", meta: { reason: "module-placement-mismatch" } });
7964
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module placement differs from the active database namespace", meta: { reason: "module-placement-mismatch" } });
7782
7965
  }
7783
7966
  if (params.history === "complete")
7784
7967
  assertCompleteHistory({ migrations, recorded, dialect });
@@ -7789,7 +7972,7 @@ async function applyMigrations(params) {
7789
7972
  });
7790
7973
  const adopted = pending.find((migration) => migration.moduleAdoption !== undefined);
7791
7974
  if (adopted !== undefined && !trustedReplay)
7792
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "An adopted source must be recorded by the verified adoption workflow, never executed by ordinary apply", meta: { reason: "module-adoption-recording-required", migration: adopted.name } });
7975
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "An adopted source must be recorded by the verified adoption workflow, never executed by ordinary apply", meta: { reason: "module-adoption-recording-required", migration: adopted.name } });
7793
7976
  if (external !== undefined && !trustedReplay)
7794
7977
  for (const migration of pending)
7795
7978
  await assertExternalMigrationMutation({ executor, migration, dialect, namespace: await migrationNamespace({ executor, dialect }), external });
@@ -7805,7 +7988,7 @@ async function applyMigrations(params) {
7805
7988
  await assertLiveModuleOwnership({ executor, origin: migration.moduleOrigin, resume: stored !== undefined });
7806
7989
  if (stored !== undefined) {
7807
7990
  if (stored.statementIndex < 0) {
7808
- throw new VibeError35({
7991
+ throw new VibeError36({
7809
7992
  code: "VIBE_MIGRATION",
7810
7993
  message: `migration "${migration.name}" is mid-rollback (${-stored.statementIndex - 1} down statement(s) done) \u2014 finish it with \`migrate down\` or repair with resolveMigration before applying`,
7811
7994
  meta: { migration: migration.name, statementIndex: stored.statementIndex }
@@ -7963,7 +8146,7 @@ async function repairMigration(params) {
7963
8146
  const { executor, name, dialect } = params;
7964
8147
  const moduleRecord = (await readModuleJournal({ executor, dialect })).get(name);
7965
8148
  if (moduleRecord?.entry !== undefined)
7966
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Module history cannot be marked applied or deleted without verified module recovery", meta: { reason: "module-resolution-required", moduleId: moduleRecord.entry.migration.moduleOrigin.moduleId } });
8149
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module history cannot be marked applied or deleted without verified module recovery", meta: { reason: "module-resolution-required", moduleId: moduleRecord.entry.migration.moduleOrigin.moduleId } });
7967
8150
  await ensureMigrationsTable({ executor, dialect });
7968
8151
  if (params.as === "applied") {
7969
8152
  const insert = `INSERT INTO ${q5(dialect, MIGRATIONS_TABLE_NAME)} (${q5(dialect, "name")}, ${q5(dialect, "checksum")}, ${q5(dialect, "statement_index")}) VALUES (${ph(dialect, 1)}, ${ph(dialect, 2)}, ${RESOLVED_STATEMENT_INDEX})`;
@@ -7985,7 +8168,7 @@ async function rollbackMigration(params) {
7985
8168
  const stored = assertRollbackRecord({ migration, stored: recorded.get(migration.name), dialect });
7986
8169
  const newest = [...recorded.keys()].at(-1);
7987
8170
  if (newest !== migration.name)
7988
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${migration.name}" is not the newest recorded migration; select rollback again`, meta: { reason: "rollback-not-latest", migration: migration.name, newest } });
8171
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration "${migration.name}" is not the newest recorded migration; select rollback again`, meta: { reason: "rollback-not-latest", migration: migration.name, newest } });
7989
8172
  await validateRollbackRecovery({ executor, migration, downSql, stored, dialect });
7990
8173
  await executeRollback({ ...params, executor, dialect, downSql, stored });
7991
8174
  } });
@@ -8003,7 +8186,7 @@ async function rollbackMigrations(params) {
8003
8186
  const target = journal.findIndex((row) => row.name === params.to);
8004
8187
  const row = journal[target];
8005
8188
  if (row?.state !== "applied")
8006
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Rollback target "${params.to}" is not a known applied migration`, meta: { reason: "rollback-target-invalid", to: params.to, state: row?.state } });
8189
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Rollback target "${params.to}" is not a known applied migration`, meta: { reason: "rollback-target-invalid", to: params.to, state: row?.state } });
8007
8190
  assertMigrationMetadata({ migration: byName.get(row.name), dialect });
8008
8191
  start = target + 1;
8009
8192
  }
@@ -8011,7 +8194,7 @@ async function rollbackMigrations(params) {
8011
8194
  for (const row of journal.slice(start).reverse()) {
8012
8195
  const migration = byName.get(row.name);
8013
8196
  if (migration === undefined)
8014
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Cannot roll back "${row.name}" without its migration file`, meta: { reason: "rollback-file-missing", migration: row.name } });
8197
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Cannot roll back "${row.name}" without its migration file`, meta: { reason: "rollback-file-missing", migration: row.name } });
8015
8198
  const stored = assertRollbackRecord({ migration, stored: recorded.get(row.name), dialect });
8016
8199
  const downSql = validateRollbackFile({ ...params, migration, dialect });
8017
8200
  plan.push({ migration, downSql, stored });
@@ -8030,21 +8213,21 @@ function validateRollbackFile(params) {
8030
8213
  const { migration, dialect } = params;
8031
8214
  assertModuleAdoption({ migration });
8032
8215
  if (migration.moduleAdoption !== undefined)
8033
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Reversal across verified module adoption is refused; it would execute destructive source down SQL against pre-existing data", meta: { reason: "module-adoption-reverse-refused", migration: migration.name } });
8216
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Reversal across verified module adoption is refused; it would execute destructive source down SQL against pre-existing data", meta: { reason: "module-adoption-reverse-refused", migration: migration.name } });
8034
8217
  if (migration.desiredStatePending === true)
8035
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Unfinalized desired state cannot be rolled back", meta: { reason: "desired-state-unfinalized", migration: migration.name } });
8218
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Unfinalized desired state cannot be rolled back", meta: { reason: "desired-state-unfinalized", migration: migration.name } });
8036
8219
  if (migration.moduleOrigin !== undefined) {
8037
8220
  assertMaterializedModuleMigration({ migration });
8038
8221
  assertModuleOwnership({ origin: migration.moduleOrigin, desiredState: migration.desiredState });
8039
8222
  }
8040
8223
  const downSql = migration.downSql;
8041
8224
  if (downSql === undefined)
8042
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" has no down.sql; roll it back manually, then repair with resolveMigration (as: "rolled-back")`, meta: { reason: "rollback-down-missing", migration: migration.name } });
8225
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" has no down.sql; roll it back manually, then repair with resolveMigration (as: "rolled-back")`, meta: { reason: "rollback-down-missing", migration: migration.name } });
8043
8226
  assertProbeMode({ dialect, probe: params.probe });
8044
8227
  if (migration.desiredState !== undefined) {
8045
8228
  assertDesiredState({ value: migration.desiredState, migration: migration.name });
8046
8229
  if (migration.desiredState.dialect !== dialect)
8047
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch" } });
8230
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch" } });
8048
8231
  assertRemovalAuthorization({ steps: diffSchemas({ from: migration.desiredState.to.schema, to: (migration.desiredState.down ?? migration.desiredState.from).schema, dialect }), previous: migration.desiredState.to, dialect, namespace: dialect === "sqlite" ? "main" : "public", ...params.acceptIntegrityLoss === undefined ? {} : { acceptIntegrityLoss: params.acceptIntegrityLoss } });
8049
8232
  }
8050
8233
  assertNativeMigrationMetadata({ migration, sql: downSql, dialect });
@@ -8057,10 +8240,10 @@ function validateRollbackFile(params) {
8057
8240
  function assertRollbackRecord(params) {
8058
8241
  const { migration, stored, dialect } = params;
8059
8242
  if (stored === undefined)
8060
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is not recorded as applied; nothing to roll back`, meta: { migration: migration.name } });
8243
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is not recorded as applied; nothing to roll back`, meta: { migration: migration.name } });
8061
8244
  assertChecksumMatches({ migration, stored: stored.checksum });
8062
8245
  if (stored.statementIndex >= 0 && (!isCompleteRecord({ dialect, migration, stored }) || stored.error !== undefined))
8063
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is only partially applied or has an unresolved error; its down mirrors the FULL up. Finish or repair with resolveMigration`, meta: { reason: "rollback-partial-up", migration: migration.name, statementIndex: stored.statementIndex } });
8246
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is only partially applied or has an unresolved error; its down mirrors the FULL up. Finish or repair with resolveMigration`, meta: { reason: "rollback-partial-up", migration: migration.name, statementIndex: stored.statementIndex } });
8064
8247
  return stored;
8065
8248
  }
8066
8249
  async function validateRollbackRecovery(params) {
@@ -8069,7 +8252,7 @@ async function validateRollbackRecovery(params) {
8069
8252
  return;
8070
8253
  const start = -stored.statementIndex - 1;
8071
8254
  if (start > downSql.length)
8072
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Rollback progress exceeds its statement count", meta: { reason: "rollback-progress-invalid", migration: migration.name } });
8255
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Rollback progress exceeds its statement count", meta: { reason: "rollback-progress-invalid", migration: migration.name } });
8073
8256
  if (dialect === "sqlite" && usesSelfManagedTransactions({ sql: downSql }))
8074
8257
  assertSelfManagedResumeSafe({ sql: downSql, start, migration: migration.name, direction: "down" });
8075
8258
  if (!marksInflight({ dialect, execution: migration.downExecution }) || start >= downSql.length)
@@ -8135,7 +8318,7 @@ async function runTransactionalDown(params) {
8135
8318
  } else {
8136
8319
  await cleanupSelfManagedFailure({ executor });
8137
8320
  }
8138
- throw new VibeError35({
8321
+ throw new VibeError36({
8139
8322
  code: "VIBE_MIGRATION",
8140
8323
  message: `rollback of "${migration.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
8141
8324
  meta: { migration: migration.name, ...timeoutDiagnostic({ error }) },
@@ -8218,7 +8401,7 @@ async function runNonTransactionalDown(params) {
8218
8401
  values: [marks ? `${INFLIGHT_PREFIX}${index}` : error instanceof Error ? error.message : String(error), migration.name]
8219
8402
  });
8220
8403
  } catch {}
8221
- throw new VibeError35({
8404
+ throw new VibeError36({
8222
8405
  code: "VIBE_MIGRATION",
8223
8406
  message: `rollback of "${migration.name}" failed at down statement ${index + 1} of ${downSql.length}: ${error instanceof Error ? error.message : String(error)}. ${marks ? "The statement outcome is unknown; inspect its effects before resolving or retrying." : "Progress is recorded; re-running the rollback resumes from this statement."}`,
8224
8407
  meta: { migration: migration.name, statementIndex: index, ...marks ? { outcome: "unknown" } : {}, ...timeoutDiagnostic({ error }) },
@@ -8280,7 +8463,7 @@ async function runTransactional(params) {
8280
8463
  } else {
8281
8464
  await cleanupSelfManagedFailure({ executor });
8282
8465
  }
8283
- throw new VibeError35({
8466
+ throw new VibeError36({
8284
8467
  code: "VIBE_MIGRATION",
8285
8468
  message: `migration "${migration.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
8286
8469
  meta: { migration: migration.name },
@@ -8376,7 +8559,7 @@ async function runNonTransactional(params) {
8376
8559
  } catch {}
8377
8560
  const sqlState = sqlStateOf(error);
8378
8561
  const timeout = sqlState === "55P03" ? "lock-timeout" : sqlState === "57014" ? "statement-timeout" : undefined;
8379
- throw new VibeError35({
8562
+ throw new VibeError36({
8380
8563
  code: "VIBE_MIGRATION",
8381
8564
  message: timeout === "lock-timeout" ? `migration "${migration.name}" gave up waiting for a lock at statement ${index + 1} of ${migration.sql.length} (lock_timeout ${String(execution?.lockTimeoutMs ?? 0)}ms, SQLSTATE 55P03). Another session holds a conflicting lock; inspect the declared operation before retrying.` : timeout === "statement-timeout" ? `migration "${migration.name}" exceeded its statement_timeout at statement ${index + 1} of ${migration.sql.length} (${String(execution?.statementTimeoutMs ?? 0)}ms, SQLSTATE 57014). Inspect the declared operation before retrying.` : `migration "${migration.name}" failed at statement ${index + 1} of ${migration.sql.length}: ${error instanceof Error ? error.message : String(error)}. ${marks ? "The statement outcome is unknown; inspect its effects before resolving or retrying." : "Progress is recorded; re-running applyMigrations resumes from this statement."}`,
8382
8565
  meta: { migration: migration.name, statementIndex: index, ...marks ? { outcome: "unknown" } : {}, ...timeout === undefined ? {} : { reason: timeout, sqlState } },
@@ -8404,7 +8587,7 @@ async function acquireLock(params) {
8404
8587
  });
8405
8588
  const acquired = Number(rows[0]?.vibe_lock) === 1;
8406
8589
  if (!acquired) {
8407
- throw new VibeError35({
8590
+ throw new VibeError36({
8408
8591
  code: "VIBE_MIGRATION",
8409
8592
  message: `could not acquire the migration lock ('${MYSQL_LOCK_NAME}') within ${MYSQL_LOCK_TIMEOUT_SECONDS}s \u2014 another migration run is in progress`,
8410
8593
  meta: { lock: MYSQL_LOCK_NAME }
@@ -8439,7 +8622,7 @@ function uncertainStatement(params) {
8439
8622
  const subject = direction === "down" ? `rollback of "${migration}"` : `migration "${migration}"`;
8440
8623
  const position = `${direction === "down" ? "down statement" : "statement"} ${index + 1} of ${statements.length}`;
8441
8624
  const why = dialect === "mysql" ? "MySQL commits every statement on its own, so nothing outside the statement records whether it finished." : "That statement declares no operation whose effect recovery can read back from the catalog.";
8442
- return new VibeError35({
8625
+ return new VibeError36({
8443
8626
  code: "VIBE_MIGRATION",
8444
8627
  message: `${subject} was interrupted while ${position} was running. ${why} ` + "It may or may not have committed, and replaying it could apply it twice, so VibeORM stops here instead of guessing. " + 'Inspect what that statement did to the database, then follow "Recovering from a stopped migration" in docs/migrations.md: ' + `clear the \`error\` column of this migration's journal row if the statement did NOT land, set \`statement_index\` to ${direction === "down" ? -(index + 2) : index + 1} if it DID, ` + "or finish the whole migration by hand and record that with `vibeorm migrate resolve --applied`. " + `The statement was: ${statements[index].slice(0, 200)}`,
8445
8628
  meta: {
@@ -8457,7 +8640,7 @@ function assertExecutable(params) {
8457
8640
  if (execution === undefined || isDefaultExecution({ execution }))
8458
8641
  return;
8459
8642
  if (dialect !== "postgres") {
8460
- throw new VibeError35({
8643
+ throw new VibeError36({
8461
8644
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8462
8645
  message: `migration "${migration.name}" declares explicit execution semantics (transaction: "${execution.transaction}"), which only the postgres runner implements \u2014 ${dialect} has no separate nontransactional regime to opt into`,
8463
8646
  meta: { reason: "nontransactional-unsupported", dialect, migration: migration.name, feature: "nontransactionalMigrations" }
@@ -8533,7 +8716,7 @@ function assertChecksumMatches(params) {
8533
8716
  const actual = checksumOf({ migration });
8534
8717
  if (stored === actual)
8535
8718
  return;
8536
- throw new VibeError35({
8719
+ throw new VibeError36({
8537
8720
  code: "VIBE_MIGRATION",
8538
8721
  message: `checksum mismatch for migration "${migration.name}" \u2014 its SQL changed after it was applied (recorded ${stored.slice(0, 12)}\u2026, current ${actual.slice(0, 12)}\u2026)`,
8539
8722
  meta: { migration: migration.name, recorded: stored, current: actual }
@@ -8544,7 +8727,7 @@ function assertResumable(params) {
8544
8727
  const actual = checksumOf({ migration });
8545
8728
  if (stored.checksum === actual)
8546
8729
  return;
8547
- throw new VibeError35({
8730
+ throw new VibeError36({
8548
8731
  code: "VIBE_MIGRATION",
8549
8732
  message: `migration "${migration.name}" is partially applied (${stored.statementIndex} statement(s) done) and its SQL has CHANGED since \u2014 refusing to resume; restore the original SQL or repair with resolveMigration`,
8550
8733
  meta: {
@@ -8564,7 +8747,7 @@ function assertDistinctMigrationNames(params) {
8564
8747
  const names = new Set;
8565
8748
  for (const migration of params.migrations) {
8566
8749
  if (names.has(migration.name))
8567
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Duplicate migration file identity "${migration.name}"`, meta: { reason: "history-duplicate-file", migration: migration.name } });
8750
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Duplicate migration file identity "${migration.name}"`, meta: { reason: "history-duplicate-file", migration: migration.name } });
8568
8751
  names.add(migration.name);
8569
8752
  }
8570
8753
  }
@@ -8572,15 +8755,15 @@ function assertCompleteHistory(params) {
8572
8755
  const byName = new Map(params.migrations.map((migration) => [migration.name, migration]));
8573
8756
  for (const name of params.recorded.keys()) {
8574
8757
  if (!byName.has(name))
8575
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Recorded migration "${name}" has no file in the complete chain`, meta: { reason: "history-unknown-file", migration: name } });
8758
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Recorded migration "${name}" has no file in the complete chain`, meta: { reason: "history-unknown-file", migration: name } });
8576
8759
  }
8577
8760
  let index = 0;
8578
8761
  for (const [name, stored] of params.recorded) {
8579
8762
  const migration = params.migrations[index];
8580
8763
  if (migration?.name !== name)
8581
- throw new VibeError35({ code: "VIBE_MIGRATION", message: "Recorded migration order is not a prefix of the supplied complete chain", meta: { reason: "history-order-conflict", migration: name, expected: migration?.name, position: index } });
8764
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Recorded migration order is not a prefix of the supplied complete chain", meta: { reason: "history-order-conflict", migration: name, expected: migration?.name, position: index } });
8582
8765
  if (stored.statementIndex >= 0 && !isCompleteRecord({ dialect: params.dialect, migration, stored }) && index !== params.recorded.size - 1)
8583
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Partial migration "${name}" precedes later recorded work`, meta: { reason: "history-partial-not-last", migration: name } });
8766
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Partial migration "${name}" precedes later recorded work`, meta: { reason: "history-partial-not-last", migration: name } });
8584
8767
  index++;
8585
8768
  }
8586
8769
  }
@@ -8589,16 +8772,16 @@ async function assertApplyHistory(params) {
8589
8772
  const byName = new Map(migrations.map((migration) => [migration.name, migration]));
8590
8773
  for (const [name, stored] of recorded) {
8591
8774
  if (stored.statementIndex < 0) {
8592
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${name}" is mid-rollback; finish or resolve it before applying`, meta: { reason: "history-rolling-back", migration: name, statementIndex: stored.statementIndex } });
8775
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration "${name}" is mid-rollback; finish or resolve it before applying`, meta: { reason: "history-rolling-back", migration: name, statementIndex: stored.statementIndex } });
8593
8776
  }
8594
8777
  const migration = byName.get(name);
8595
8778
  if (migration === undefined) {
8596
8779
  if (stored.error !== undefined)
8597
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error but no supplied file`, meta: { reason: "history-unknown-dirty", migration: name } });
8780
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error but no supplied file`, meta: { reason: "history-unknown-dirty", migration: name } });
8598
8781
  continue;
8599
8782
  }
8600
8783
  if (isCompleteRecord({ dialect, migration, stored }) && stored.error !== undefined)
8601
- throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error`, meta: { reason: "history-dirty", migration: name } });
8784
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error`, meta: { reason: "history-dirty", migration: name } });
8602
8785
  if (isCompleteRecord({ dialect, migration, stored }))
8603
8786
  assertChecksumMatches({ migration, stored: stored.checksum });
8604
8787
  else
@@ -8632,17 +8815,17 @@ function ph(dialect, index) {
8632
8815
  // packages/migrate/src/module-bundle.ts
8633
8816
  function defineModuleMigrations(params) {
8634
8817
  if (!Array.isArray(params.steps) || params.steps.length === 0)
8635
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "A module upgrade bundle must contain its initial revision", meta: { reason: "module-chain-invalid", moduleId: params.moduleId } });
8818
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "A module upgrade bundle must contain its initial revision", meta: { reason: "module-chain-invalid", moduleId: params.moduleId } });
8636
8819
  const migrations = [];
8637
8820
  const revisions = new Set;
8638
8821
  let previous = null;
8639
8822
  for (const step of params.steps) {
8640
8823
  if (step.previousRevision !== previous || revisions.has(step.revision))
8641
- throw new VibeError36({ code: "VIBE_MIGRATION", message: `Module ${params.moduleId} has a duplicate revision or broken predecessor chain`, meta: { reason: "module-chain-invalid", moduleId: params.moduleId, revision: step.revision } });
8824
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: `Module ${params.moduleId} has a duplicate revision or broken predecessor chain`, meta: { reason: "module-chain-invalid", moduleId: params.moduleId, revision: step.revision } });
8642
8825
  const origin = createModuleOrigin({ moduleId: params.moduleId, dialect: params.dialect, revision: step.revision, previousRevision: step.previousRevision, contracts: step.contracts, owned: step.owned, source: step.migration });
8643
8826
  const predecessor = migrations.at(-1)?.moduleOrigin;
8644
8827
  if (predecessor !== undefined && canonicalMigrationJson({ value: predecessor.source.desiredState.to }) !== canonicalMigrationJson({ value: origin.source.desiredState.from }))
8645
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module source state differs from its predecessor", meta: { reason: "module-predecessor-state", moduleId: params.moduleId } });
8828
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module source state differs from its predecessor", meta: { reason: "module-predecessor-state", moduleId: params.moduleId } });
8646
8829
  validateMigrationReplay({ migrations: [origin.source], dialect: params.dialect, acceptSecurityChanges: true, acceptIntegrityLoss: true, removeUnmanaged: origin.source.desiredState?.unmanagedRemovals ?? [] });
8647
8830
  migrations.push(Object.freeze({ ...origin.source, moduleOrigin: origin }));
8648
8831
  revisions.add(step.revision);
@@ -8652,21 +8835,21 @@ function defineModuleMigrations(params) {
8652
8835
  }
8653
8836
  function moduleMigrationFiles(params) {
8654
8837
  if (params.bundle === null || typeof params.bundle !== "object" || !Array.isArray(params.bundle.migrations))
8655
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module bundle must contain an immutable migration chain", meta: { reason: "module-chain-invalid" } });
8838
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module bundle must contain an immutable migration chain", meta: { reason: "module-chain-invalid" } });
8656
8839
  if (!/^[A-Za-z0-9_-]+$/.test(params.prefix))
8657
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module migration prefix must be a safe local directory name", meta: { reason: "module-prefix-invalid" } });
8840
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module migration prefix must be a safe local directory name", meta: { reason: "module-prefix-invalid" } });
8658
8841
  const bundle = defineModuleMigrations({ moduleId: params.bundle.moduleId, dialect: params.bundle.dialect, steps: params.bundle.migrations.map((migration) => {
8659
8842
  const origin = migration?.moduleOrigin;
8660
8843
  if (migration.moduleAdoption !== undefined)
8661
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Local adoption provenance cannot be exported as immutable package source", meta: { reason: "module-adoption-author-source" } });
8844
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Local adoption provenance cannot be exported as immutable package source", meta: { reason: "module-adoption-author-source" } });
8662
8845
  if (origin === undefined || origin.moduleId !== params.bundle.moduleId || origin.dialect !== params.bundle.dialect)
8663
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module bundle lacks matching immutable origin metadata", meta: { reason: "module-origin-invalid" } });
8846
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module bundle lacks matching immutable origin metadata", meta: { reason: "module-origin-invalid" } });
8664
8847
  assertModuleOrigin({ value: origin });
8665
8848
  restoreModuleMigration({ migration, origin });
8666
8849
  const { name: localName, sql: localSql, downSql: localDown, moduleOrigin: localOrigin, ...actual } = migration;
8667
8850
  const { name: sourceName, sql: sourceSql, downSql: sourceDown, ...expected } = origin.source;
8668
8851
  if (localSql === undefined || localDown === undefined !== (sourceDown === undefined) || canonicalMigrationJson({ value: actual }) !== canonicalMigrationJson({ value: expected }))
8669
- throw new VibeError36({ code: "VIBE_MIGRATION", message: "Module bundle metadata differs from its sealed author source", meta: { reason: "module-origin-invalid", moduleId: origin.moduleId } });
8852
+ throw new VibeError37({ code: "VIBE_MIGRATION", message: "Module bundle metadata differs from its sealed author source", meta: { reason: "module-origin-invalid", moduleId: origin.moduleId } });
8670
8853
  return { revision: origin.revision, previousRevision: origin.previousRevision, contracts: origin.contracts, owned: origin.owned, migration: origin.source };
8671
8854
  }) });
8672
8855
  return Object.freeze(bundle.migrations.map((migration, index) => Object.freeze({ ...migration, name: `${params.prefix}_${String(index + 1).padStart(4, "0")}` })));
@@ -8674,10 +8857,10 @@ function moduleMigrationFiles(params) {
8674
8857
 
8675
8858
  // packages/migrate/src/module-pending.ts
8676
8859
  function refusal3(params) {
8677
- return new VibeError37({ code: "VIBE_MIGRATION", message: "Module registration or immutable upgrade history conflicts with the application's declarations; review the registration and deployment sequence", meta: params });
8860
+ return new VibeError38({ code: "VIBE_MIGRATION", message: "Module registration or immutable upgrade history conflicts with the application's declarations; review the registration and deployment sequence", meta: params });
8678
8861
  }
8679
8862
  function physicalKey(params) {
8680
- return sqlObjectKey11({ identity: { kind: "table", name: qualifiedSqlName10({ name: params.entry.dbName ?? params.entry.name, namespace: params.entry.namespace }) } });
8863
+ return sqlObjectKey11({ identity: { kind: "table", name: qualifiedSqlName11({ name: params.entry.dbName ?? params.entry.name, namespace: params.entry.namespace }) } });
8681
8864
  }
8682
8865
  function composeModuleState(params) {
8683
8866
  const { current, from, to } = params;
@@ -8788,7 +8971,7 @@ function pendingModuleMigrations(params) {
8788
8971
 
8789
8972
  // packages/migrate/src/replay-reference.ts
8790
8973
  import { createHash as createHash3 } from "crypto";
8791
- import { VibeError as VibeError38, sqlObjectKey as sqlObjectKey12, sqlObjectNamespace as sqlObjectNamespace4 } from "@vibeorm/schema";
8974
+ import { VibeError as VibeError39, sqlObjectKey as sqlObjectKey12, sqlObjectNamespace as sqlObjectNamespace4 } from "@vibeorm/schema";
8792
8975
  import { getSqlDialect as getSqlDialect10, renderMigrationServerVersion, renderReplayCoverageGaps as renderReplayCoverageGaps2, renderShadowOperation as renderShadowOperation2, SHADOW_DATABASE } from "@vibeorm/sql";
8793
8976
  function migrationReferenceNamespaces(params) {
8794
8977
  return [...new Set([params.namespace, ...params.migrations.flatMap((migration) => [
@@ -8804,7 +8987,7 @@ async function captureReplayReference(params) {
8804
8987
  for (const origin of params.namespaceOrigins ?? []) {
8805
8988
  assertModuleOrigin({ value: origin });
8806
8989
  if (origin.dialect !== dialect)
8807
- throw new VibeError38({ code: "VIBE_MIGRATION", message: "Namespace context origin uses a different dialect", meta: { reason: "module-dialect-mismatch", moduleId: origin.moduleId } });
8990
+ throw new VibeError39({ code: "VIBE_MIGRATION", message: "Namespace context origin uses a different dialect", meta: { reason: "module-dialect-mismatch", moduleId: origin.moduleId } });
8808
8991
  }
8809
8992
  const namespace = await migrationNamespace({ executor, dialect });
8810
8993
  await assertCatalogVisibility({ executor, dialect, namespace });
@@ -8813,17 +8996,17 @@ async function captureReplayReference(params) {
8813
8996
  const [query] = renderShadowOperation2({ dialect: sqlDialect, operation: { kind: "markerColumns" } });
8814
8997
  const columns = await executor({ text: query.text, values: [...query.values] });
8815
8998
  if (columns.length !== 1 || columns[0]?.name !== "payload" || columns[0]?.type !== "longtext" || columns[0]?.nullable !== "NO")
8816
- throw new VibeError38({ code: "VIBE_MIGRATION", message: "Replay changed the private shadow marker; its objects cannot be excluded from reference inspection", meta: { reason: "shadow-ownership-changed" } });
8999
+ throw new VibeError39({ code: "VIBE_MIGRATION", message: "Replay changed the private shadow marker; its objects cannot be excluded from reference inspection", meta: { reason: "shadow-ownership-changed" } });
8817
9000
  }
8818
9001
  if (params.privateNamespace !== undefined) {
8819
9002
  if (dialect !== "postgres" || params.privateNamespace !== SHADOW_DATABASE.schema)
8820
- throw new VibeError38({ code: "VIBE_MIGRATION", message: "Unknown private shadow namespace", meta: { reason: "shadow-ownership-invalid" } });
9003
+ throw new VibeError39({ code: "VIBE_MIGRATION", message: "Unknown private shadow namespace", meta: { reason: "shadow-ownership-invalid" } });
8821
9004
  const [inventoryQuery] = renderShadowOperation2({ dialect: sqlDialect, operation: { kind: "inventory" } });
8822
9005
  const privateObjects = (await executor({ text: inventoryQuery.text, values: [...inventoryQuery.values] })).filter((row) => row.namespace === params.privateNamespace);
8823
9006
  const [columnsQuery] = renderShadowOperation2({ dialect: sqlDialect, operation: { kind: "markerColumns" } });
8824
9007
  const columns = await executor({ text: columnsQuery.text, values: [...columnsQuery.values] });
8825
9008
  if (columns.length !== 1 || columns[0]?.name !== "payload" || columns[0]?.type !== "text" || columns[0]?.nullable !== "NO" || privateObjects.some((row) => !(row.kind === "namespace" && row.name === SHADOW_DATABASE.schema || row.kind === "relation" && row.name === SHADOW_DATABASE.table)))
8826
- throw new VibeError38({ code: "VIBE_MIGRATION", message: "Replay changed the private shadow namespace; its objects cannot be excluded from reference inspection", meta: { reason: "shadow-ownership-changed", objects: privateObjects } });
9009
+ throw new VibeError39({ code: "VIBE_MIGRATION", message: "Replay changed the private shadow namespace; its objects cannot be excluded from reference inspection", meta: { reason: "shadow-ownership-changed", objects: privateObjects } });
8827
9010
  }
8828
9011
  const namespaces = dialect === "postgres" ? [...new Set([...migrationReferenceNamespaces({ migrations: params.migrations, namespace }), ...(params.namespaceOrigins ?? []).map((origin) => moduleNamespace({ origin }))])] : [namespace];
8829
9012
  const external = (params.external?.length ?? 0) === 0 ? undefined : await inspectExternalObjects({ executor, dialect, external: params.external });
@@ -8834,7 +9017,7 @@ async function captureReplayReference(params) {
8834
9017
  }))).flat();
8835
9018
  const gaps = migrationCoverageGaps({ gaps: coverageRows, namespace, namespaces, external });
8836
9019
  if (gaps.length > 0)
8837
- throw new VibeError38({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Replayed history contains objects whose full definitions are not represented by migration inspection. Use reviewed SQL for changes; automatic generation and verification require supported reference coverage.", meta: { feature: "replayInventoryCoverage", dialect, namespace, objects: gaps } });
9020
+ throw new VibeError39({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Replayed history contains objects whose full definitions are not represented by migration inspection. Use reviewed SQL for changes; automatic generation and verification require supported reference coverage.", meta: { feature: "replayInventoryCoverage", dialect, namespace, objects: gaps } });
8838
9021
  const rls = [...params.migrations].reverse().find((migration) => migration.rls !== undefined)?.rls?.to;
8839
9022
  const physical = dialect === "postgres" ? await introspectPostgres({ executor, namespaces: inspectedNamespaces, ...rls === undefined ? {} : { rlsSchema: rls, requireInstalledRls: true } }) : dialect === "mysql" ? await introspectMysql({ executor }) : await introspectSqlite({ executor });
8840
9023
  const privateTable = params.privateTable !== undefined && physical.models.some((model) => !model.isView && (model.dbName ?? model.name) === params.privateTable) ? params.privateTable : undefined;
@@ -8882,7 +9065,7 @@ async function captureReplayReference(params) {
8882
9065
  }
8883
9066
 
8884
9067
  // packages/migrate/src/verify-history.ts
8885
- import { VibeError as VibeError40, sqlObjectKey as sqlObjectKey13 } from "@vibeorm/schema";
9068
+ import { VibeError as VibeError41, sqlObjectKey as sqlObjectKey13 } from "@vibeorm/schema";
8886
9069
  import { getSqlDialect as getSqlDialect11, renderReplayCoverageGaps as renderReplayCoverageGaps3 } from "@vibeorm/sql";
8887
9070
 
8888
9071
  // packages/migrate/src/compare-sqlite-primary-key.ts
@@ -8953,7 +9136,7 @@ function compareSqlitePrimaryKeyRepresentations(params) {
8953
9136
  }
8954
9137
 
8955
9138
  // packages/migrate/src/compare-reference-rls.ts
8956
- import { VibeError as VibeError39 } from "@vibeorm/schema";
9139
+ import { VibeError as VibeError40 } from "@vibeorm/schema";
8957
9140
  function securityFacts(params) {
8958
9141
  return new Map(params.tables.filter((table) => table.enabled || table.force || table.policies.length > 0).map((table) => [table.table, {
8959
9142
  table: table.table,
@@ -8964,7 +9147,7 @@ function securityFacts(params) {
8964
9147
  }
8965
9148
  async function comparePostgresReferenceRls(params) {
8966
9149
  if (params.schema.rls !== undefined && params.referenceExecutor === undefined)
8967
- throw new VibeError39({ code: "VIBE_MIGRATION", message: "Native RLS verification requires the independently replayed reference connection; target policy probes are not permitted.", meta: { reason: "verification-rls-reference-required" } });
9150
+ throw new VibeError40({ code: "VIBE_MIGRATION", message: "Native RLS verification requires the independently replayed reference connection; target policy probes are not permitted.", meta: { reason: "verification-rls-reference-required" } });
8968
9151
  const expected = securityFacts({ tables: params.schema.rls === undefined ? [] : await readPostgresRls({ executor: params.referenceExecutor }) });
8969
9152
  const actual = securityFacts({ tables: await readPostgresRls({ executor: params.executor }) });
8970
9153
  const objects = [...new Set([...expected.keys(), ...actual.keys()])].sort().map((table) => {
@@ -8988,14 +9171,14 @@ function selectMigrationVerificationPrefix(params) {
8988
9171
  for (const [index, file] of params.migrations.entries()) {
8989
9172
  const previous = params.migrations[index - 1];
8990
9173
  if (seen.has(file.name) || previous !== undefined && previous.name.localeCompare(file.name) > 0) {
8991
- throw new VibeError40({ code: "VIBE_MIGRATION", message: "Verification requires a complete local migration chain in unique name order", meta: { reason: "verification-history-order", migration: file.name } });
9174
+ throw new VibeError41({ code: "VIBE_MIGRATION", message: "Verification requires a complete local migration chain in unique name order", meta: { reason: "verification-history-order", migration: file.name } });
8992
9175
  }
8993
9176
  seen.add(file.name);
8994
9177
  }
8995
9178
  for (const [index, row] of params.recorded.entries()) {
8996
9179
  const file = params.migrations[index];
8997
9180
  if (row.state !== "applied" || row.statementIndex < 0 || row.checksum === "" || file?.name !== row.name || file.desiredStatePending === true || migrationChecksum(file) !== row.checksum) {
8998
- throw new VibeError40({ code: "VIBE_MIGRATION", message: "Verification requires an exact, complete, checksum-verified applied prefix; inspect migrate status and resolve the history discrepancy first", meta: { reason: "verification-history-unverified", migration: row.name, state: row.state } });
9181
+ throw new VibeError41({ code: "VIBE_MIGRATION", message: "Verification requires an exact, complete, checksum-verified applied prefix; inspect migrate status and resolve the history discrepancy first", meta: { reason: "verification-history-unverified", migration: row.name, state: row.state } });
8999
9182
  }
9000
9183
  }
9001
9184
  return params.migrations.slice(0, params.recorded.length);
@@ -9026,7 +9209,7 @@ async function verifyMigrationReference(params) {
9026
9209
  }))).flat();
9027
9210
  const gaps = migrationCoverageGaps({ gaps: coverageRows, namespace, namespaces, external });
9028
9211
  if (gaps.length > 0)
9029
- throw new VibeError40({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Target contains objects whose full definitions are outside migration inspection coverage; verification is unavailable. No target changes were made.", meta: { feature: "targetInventoryCoverage", dialect, namespace, objects: gaps } });
9212
+ throw new VibeError41({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Target contains objects whose full definitions are outside migration inspection coverage; verification is unavailable. No target changes were made.", meta: { feature: "targetInventoryCoverage", dialect, namespace, objects: gaps } });
9030
9213
  const security = dialect === "postgres" ? await comparePostgresReferenceRls({ executor, referenceExecutor: params.referenceExecutor, schema: reference.state.schema }) : { inSync: true, objects: [] };
9031
9214
  const inspected = await verifyMigrationState({ executor, dialect, expected: reference.state, external, probe: "none", batch: true });
9032
9215
  const structural = dialect === "sqlite" ? compareSqlitePrimaryKeyRepresentations({ report: inspected }) : inspected;
@@ -9179,12 +9362,12 @@ async function mysqlComparisonFacts(params) {
9179
9362
  return { ...facts, members };
9180
9363
  }
9181
9364
  function referenceContextError() {
9182
- return new VibeError40({ code: "VIBE_MIGRATION", message: "Replay and target dialect/namespace differ; safe reference translation is unavailable. Use a supported same-namespace reference setup", meta: { reason: "verification-reference-context" } });
9365
+ return new VibeError41({ code: "VIBE_MIGRATION", message: "Replay and target dialect/namespace differ; safe reference translation is unavailable. Use a supported same-namespace reference setup", meta: { reason: "verification-reference-context" } });
9183
9366
  }
9184
9367
 
9185
9368
  // packages/migrate/src/module-adoption-reference.ts
9186
9369
  function refusal4(params) {
9187
- return new VibeError41({ code: "VIBE_MIGRATION", message: "Module adoption needs complete independent source and application references with compatible ownership; no target SQL or metadata was authorized", meta: params });
9370
+ return new VibeError42({ code: "VIBE_MIGRATION", message: "Module adoption needs complete independent source and application references with compatible ownership; no target SQL or metadata was authorized", meta: params });
9188
9371
  }
9189
9372
  function moduleAdoptionSource(params) {
9190
9373
  if (params.migrations.length === 0)
@@ -9294,7 +9477,7 @@ async function verifyModuleAdoptionTarget(params) {
9294
9477
  await assertLiveModuleOwnership({ executor: params.executor, origin, resume: true });
9295
9478
  const report = await verifyMigrationReference({ executor: params.executor, dialect: origin.dialect, reference: params.reference, referenceExecutor: params.referenceExecutor });
9296
9479
  if (!report.inSync)
9297
- throw new VibeError41({ code: "VIBE_MIGRATION", message: "Populated target differs from independently replayed package/application structure; no package SQL or metadata was applied", meta: { reason: "module-adoption-target-mismatch", report } });
9480
+ throw new VibeError42({ code: "VIBE_MIGRATION", message: "Populated target differs from independently replayed package/application structure; no package SQL or metadata was applied", meta: { reason: "module-adoption-target-mismatch", report } });
9298
9481
  }
9299
9482
 
9300
9483
  // packages/migrate/src/history.ts
@@ -9310,10 +9493,10 @@ async function readMigrationHistory(params) {
9310
9493
 
9311
9494
  // packages/migrate/src/module-adoption-record.ts
9312
9495
  function refusal5(params) {
9313
- return new VibeError42({ code: "VIBE_MIGRATION", message: "Module adoption cannot record an incomplete, concurrent or altered history; no package SQL was executed", meta: params });
9496
+ return new VibeError43({ code: "VIBE_MIGRATION", message: "Module adoption cannot record an incomplete, concurrent or altered history; no package SQL was executed", meta: params });
9314
9497
  }
9315
9498
  function transactionFailure(params) {
9316
- return params.cause instanceof VibeError42 ? params.cause : new VibeError42({ code: "VIBE_MIGRATION", message: "Module adoption recording failed; inspect the local provenance and journal before retrying", meta: { reason: "module-adoption-transaction-failed" }, cause: params.cause });
9499
+ return params.cause instanceof VibeError43 ? params.cause : new VibeError43({ code: "VIBE_MIGRATION", message: "Module adoption recording failed; inspect the local provenance and journal before retrying", meta: { reason: "module-adoption-transaction-failed" }, cause: params.cause });
9317
9500
  }
9318
9501
  async function recordModuleAdoption(params) {
9319
9502
  moduleAdoptionSource(params);
@@ -9376,9 +9559,9 @@ async function recordModuleAdoption(params) {
9376
9559
  await record();
9377
9560
  }).then(undefined, async (cause) => {
9378
9561
  if (committed)
9379
- throw new VibeError42({ code: "VIBE_MIGRATION", message: "All adopted revisions committed, but filesystem lease cleanup failed; inspect the remaining publication lease before retrying", meta: { reason: "module-adoption-publication-cleanup-failed", recorded: true }, cause });
9562
+ throw new VibeError43({ code: "VIBE_MIGRATION", message: "All adopted revisions committed, but filesystem lease cleanup failed; inspect the remaining publication lease before retrying", meta: { reason: "module-adoption-publication-cleanup-failed", recorded: true }, cause });
9380
9563
  await executor({ text: "ROLLBACK" }).then(undefined, (rollback) => {
9381
- throw new VibeError42({ code: "VIBE_MIGRATION", message: "Adoption recording failed and rollback also failed; the journal outcome is uncertain and must be inspected before recovery", meta: { reason: "module-adoption-rollback-failed" }, cause: new AggregateError([cause, rollback], "Adoption recording and rollback failed") });
9564
+ throw new VibeError43({ code: "VIBE_MIGRATION", message: "Adoption recording failed and rollback also failed; the journal outcome is uncertain and must be inspected before recovery", meta: { reason: "module-adoption-rollback-failed" }, cause: new AggregateError([cause, rollback], "Adoption recording and rollback failed") });
9382
9565
  });
9383
9566
  throw transactionFailure({ cause });
9384
9567
  });
@@ -9389,14 +9572,14 @@ import { randomUUID as randomUUID3 } from "crypto";
9389
9572
  import { mkdtemp, rm } from "fs/promises";
9390
9573
  import { tmpdir } from "os";
9391
9574
  import { join } from "path";
9392
- import { VibeError as VibeError44, providerToDialect as providerToDialect5, sqlObjectKey as sqlObjectKey15, unsupportedCapability as unsupportedCapability13 } from "@vibeorm/schema";
9575
+ import { VibeError as VibeError45, providerToDialect as providerToDialect6, sqlObjectKey as sqlObjectKey15, unsupportedCapability as unsupportedCapability13 } from "@vibeorm/schema";
9393
9576
  import { getSqlDialect as getSqlDialect14, renderShadowOperation as renderShadowOperation4, SHADOW_DATABASE as SHADOW_DATABASE3 } from "@vibeorm/sql";
9394
9577
 
9395
9578
  // packages/migrate/src/shadow-dedicated.ts
9396
- import { SQL_NAMESPACES, VibeError as VibeError43, providerToDialect as providerToDialect4, sqlNamespaceIssue as sqlNamespaceIssue2 } from "@vibeorm/schema";
9579
+ import { SQL_NAMESPACES as SQL_NAMESPACES2, VibeError as VibeError44, providerToDialect as providerToDialect5, sqlNamespaceIssue as sqlNamespaceIssue2 } from "@vibeorm/schema";
9397
9580
  import { getSqlDialect as getSqlDialect13, renderShadowOperation as renderShadowOperation3, SHADOW_DATABASE as SHADOW_DATABASE2 } from "@vibeorm/sql";
9398
9581
  function refuse(params) {
9399
- throw new VibeError43({ code: "VIBE_MIGRATION", message: params.message, meta: { reason: params.reason, ...params.objects === undefined ? {} : { objects: params.objects } } });
9582
+ throw new VibeError44({ code: "VIBE_MIGRATION", message: params.message, meta: { reason: params.reason, ...params.objects === undefined ? {} : { objects: params.objects } } });
9400
9583
  }
9401
9584
  function readIdentity(params) {
9402
9585
  const row = params.rows[0];
@@ -9464,7 +9647,7 @@ async function readMarker(params) {
9464
9647
  if (params.rows.length !== 1 || typeof payload !== "string")
9465
9648
  return refuse({ reason: "shadow-ownership-invalid", message: "Shadow ownership marker is missing or malformed; automatic claim/reset is refused." });
9466
9649
  const value = await Promise.resolve().then(() => JSON.parse(payload)).then((result) => result, (cause) => {
9467
- throw new VibeError43({ code: "VIBE_MIGRATION", message: "Shadow marker is not valid JSON", meta: { reason: "shadow-ownership-invalid" }, cause });
9650
+ throw new VibeError44({ code: "VIBE_MIGRATION", message: "Shadow marker is not valid JSON", meta: { reason: "shadow-ownership-invalid" }, cause });
9468
9651
  });
9469
9652
  if (value === null || typeof value !== "object" || !("version" in value) || value.version !== 1 || !("ownerKey" in value) || typeof value.ownerKey !== "string" || !("identity" in value) || typeof value.identity !== "string" || !("status" in value) || value.status !== "READY" && value.status !== "BUILDING" || !("journal" in value) || !Array.isArray(value.journal) || !value.journal.every((row) => row !== null && typeof row === "object" && ("name" in row) && typeof row.name === "string" && ("checksum" in row) && typeof row.checksum === "string"))
9470
9653
  return refuse({ reason: "shadow-ownership-invalid", message: "Shadow marker has an unsupported ownership or replay-journal shape." });
@@ -9478,8 +9661,8 @@ async function withDedicatedShadow(params) {
9478
9661
  const intendedJournal = completedJournal({ rows: params.migrations.map((migration) => ({ name: migration.name, checksum: migrationChecksum(migration), statement_index: migration.sql.length, error: null })), reason: "shadow-replay-journal-changed" });
9479
9662
  if (shadow.ownerKey.length === 0 || shadow.targetUrl.length === 0)
9480
9663
  return refuse({ reason: "shadow-identity-unavailable", message: "Reusable shadow mode requires a project owner and a resolvable deployment target for an independent identity check." });
9481
- const dialect = providerToDialect4({ provider: shadow.provider });
9482
- const declaredNamespaces = dialect === "postgres" ? migrationReferenceNamespaces({ migrations: params.migrations, namespace: SQL_NAMESPACES.postgres }).filter((namespace) => namespace !== SQL_NAMESPACES.postgres) : [];
9664
+ const dialect = providerToDialect5({ provider: shadow.provider });
9665
+ const declaredNamespaces = dialect === "postgres" ? migrationReferenceNamespaces({ migrations: params.migrations, namespace: SQL_NAMESPACES2.postgres }).filter((namespace) => namespace !== SQL_NAMESPACES2.postgres) : [];
9483
9666
  const sqlDialect = getSqlDialect13({ dialect });
9484
9667
  const execute = async (executor, operation) => {
9485
9668
  let rows = [];
@@ -9508,13 +9691,13 @@ async function withDedicatedShadow(params) {
9508
9691
  return refuse({ reason: "shadow-identity-changed", message: "Shadow identity changed after lease acquisition; reset is refused." });
9509
9692
  if (dialect === "mysql") {
9510
9693
  const grants = await execute(executor, { kind: "grants" }).then(undefined, (cause) => {
9511
- throw new VibeError43({ code: "VIBE_MIGRATION", message: "Cannot establish dedicated MySQL catalog visibility from direct grants", meta: { reason: "shadow-inventory-unavailable" }, cause });
9694
+ throw new VibeError44({ code: "VIBE_MIGRATION", message: "Cannot establish dedicated MySQL catalog visibility from direct grants", meta: { reason: "shadow-inventory-unavailable" }, cause });
9512
9695
  });
9513
9696
  assertMysqlVisibility({ rows: grants, database: identity.database });
9514
9697
  }
9515
9698
  const existing = (await execute(executor, { kind: "markerExists" })).length > 0;
9516
9699
  const inventory = await execute(executor, { kind: "inventory" }).then(undefined, (cause) => {
9517
- throw new VibeError43({ code: "VIBE_MIGRATION", message: "Cannot inspect the complete dedicated shadow inventory; no reset was attempted", meta: { reason: "shadow-inventory-unavailable" }, cause });
9700
+ throw new VibeError44({ code: "VIBE_MIGRATION", message: "Cannot inspect the complete dedicated shadow inventory; no reset was attempted", meta: { reason: "shadow-inventory-unavailable" }, cause });
9518
9701
  });
9519
9702
  const externalReferences = inventory.filter((row) => row.kind === "EXTERNAL FOREIGN KEY");
9520
9703
  if (externalReferences.length !== 0)
@@ -9567,7 +9750,7 @@ async function withDedicatedShadow(params) {
9567
9750
  if (remaining.length !== 0)
9568
9751
  return refuse({ reason: "shadow-reset-incomplete", message: "Shadow reset left objects behind. Replay was not started.", objects: remaining });
9569
9752
  const result = await params.runReference(executor, dialect === "mysql" ? SHADOW_DATABASE2.table : undefined).then((value) => ({ ok: true, value }), (error) => {
9570
- if (!(error instanceof VibeError43) || error.meta.shadowInvalid === true || error.meta.shadowReusable !== true)
9753
+ if (!(error instanceof VibeError44) || error.meta.shadowInvalid === true || error.meta.shadowReusable !== true)
9571
9754
  throw error;
9572
9755
  return { ok: false, error };
9573
9756
  });
@@ -9591,13 +9774,13 @@ async function withDedicatedShadow(params) {
9591
9774
  function finishShadowWork(params) {
9592
9775
  return Promise.resolve().then(params.run).then(async (value) => {
9593
9776
  await Promise.resolve().then(params.cleanup).then(undefined, (cause) => {
9594
- throw new VibeError44({ code: "VIBE_MIGRATION", message: "Migration reference completed but its owned resource could not be cleaned. Inspect the named artifact before explicit cleanup.", meta: { cleanupReason: "shadow-cleanup-failed", artifact: params.artifact, cleanupFailure: cause instanceof VibeError44 ? cause.code : "unavailable" }, cause });
9777
+ throw new VibeError45({ code: "VIBE_MIGRATION", message: "Migration reference completed but its owned resource could not be cleaned. Inspect the named artifact before explicit cleanup.", meta: { cleanupReason: "shadow-cleanup-failed", artifact: params.artifact, cleanupFailure: cause instanceof VibeError45 ? cause.code : "unavailable" }, cause });
9595
9778
  });
9596
9779
  return value;
9597
9780
  }, async (primary) => {
9598
9781
  const [cleanup] = await Promise.allSettled([Promise.resolve().then(params.cleanup)]);
9599
9782
  if (cleanup.status === "rejected")
9600
- throw new VibeError44({ code: primary instanceof VibeError44 ? primary.code : "VIBE_MIGRATION", message: "Migration reference failed and its owned resource could not be cleaned. Inspect the named artifact before explicit cleanup.", meta: { cleanupReason: "shadow-cleanup-failed", artifact: params.artifact, cleanupFailure: cleanup.reason instanceof VibeError44 ? cleanup.reason.code : "unavailable" }, cause: { primary, cleanup: cleanup.reason } });
9783
+ throw new VibeError45({ code: primary instanceof VibeError45 ? primary.code : "VIBE_MIGRATION", message: "Migration reference failed and its owned resource could not be cleaned. Inspect the named artifact before explicit cleanup.", meta: { cleanupReason: "shadow-cleanup-failed", artifact: params.artifact, cleanupFailure: cleanup.reason instanceof VibeError45 ? cleanup.reason.code : "unavailable" }, cause: { primary, cleanup: cleanup.reason } });
9601
9784
  throw primary;
9602
9785
  });
9603
9786
  }
@@ -9609,21 +9792,21 @@ function withShadowConnection(params) {
9609
9792
  return finishShadowWork({ artifact: params.artifact, cleanup: () => params.connection.close(), run: () => params.connection.executor.withSession === undefined ? runPinned(params.connection.executor) : params.connection.executor.withSession({ run: runPinned }) });
9610
9793
  }
9611
9794
  async function withMigrationReference(params) {
9612
- const dialect = providerToDialect5({ provider: params.shadow.provider });
9795
+ const dialect = providerToDialect6({ provider: params.shadow.provider });
9613
9796
  const external = params.external ?? params.replay?.external;
9614
9797
  const replay = { ...params.replay, external, migrations: params.migrations, dialect };
9615
9798
  validateMigrationReplay(replay);
9616
9799
  for (const origin of params.namespaceOrigins ?? []) {
9617
9800
  assertModuleOrigin({ value: origin });
9618
9801
  if (origin.dialect !== dialect)
9619
- throw new VibeError44({ code: "VIBE_MIGRATION", message: "Namespace context origin uses a different dialect", meta: { reason: "module-dialect-mismatch", moduleId: origin.moduleId } });
9802
+ throw new VibeError45({ code: "VIBE_MIGRATION", message: "Namespace context origin uses a different dialect", meta: { reason: "module-dialect-mismatch", moduleId: origin.moduleId } });
9620
9803
  }
9621
9804
  const hasAdoption = params.migrations.some((migration) => migration.moduleAdoption !== undefined);
9622
9805
  if (hasAdoption && params.shadow.provider === "mysql")
9623
9806
  throw unsupportedCapability13({ dialect: "mysql", feature: "moduleAdoptionIndependentMysqlNamespace", hint: "Independent source and application databases require a proven metadata rebase before grouped adoption replay is supported" });
9624
9807
  const adoptionShadow = params.shadow.mode === "automatic" ? params.shadow : undefined;
9625
9808
  if (hasAdoption && adoptionShadow === undefined)
9626
- throw new VibeError44({ code: "VIBE_CONFIG", message: "Adopted history requires an independent automatic package source reference; a single dedicated shadow cannot prove both histories. Configure shadowAdminUrl.", meta: { reason: "module-adoption-automatic-shadow-required" } });
9809
+ throw new VibeError45({ code: "VIBE_CONFIG", message: "Adopted history requires an independent automatic package source reference; a single dedicated shadow cannot prove both histories. Configure shadowAdminUrl.", meta: { reason: "module-adoption-automatic-shadow-required" } });
9627
9810
  const inspect = async (executor, privateTable) => {
9628
9811
  if ((external?.length ?? 0) > 0) {
9629
9812
  const ownership = await inspectExternalObjects({ executor, dialect, external });
@@ -9650,7 +9833,7 @@ async function withMigrationReference(params) {
9650
9833
  await withMigrationReference({ shadow: adoptionShadow, openConnection: params.openConnection, migrations: moduleAdoptionSource({ migrations: group }), replay: sourceReplay, run: async ({ executor: sourceExecutor, reference: source }) => {
9651
9834
  const fingerprint = moduleAdoptionReferenceFingerprint({ migrations: group, reference: source });
9652
9835
  if (group.some((migration) => migration.moduleAdoption?.referenceFingerprint !== fingerprint))
9653
- throw new VibeError44({ code: "VIBE_MIGRATION", message: "Local adoption proof does not match independently replayed immutable package source", meta: { reason: "module-adoption-reference-unverified", moduleId: first.moduleOrigin?.moduleId } });
9836
+ throw new VibeError45({ code: "VIBE_MIGRATION", message: "Local adoption proof does not match independently replayed immutable package source", meta: { reason: "module-adoption-reference-unverified", moduleId: first.moduleOrigin?.moduleId } });
9654
9837
  const application = await captureReplayReference({
9655
9838
  executor,
9656
9839
  dialect,
@@ -9697,14 +9880,14 @@ async function withMigrationReference(params) {
9697
9880
  } });
9698
9881
  }
9699
9882
  if (!("adminUrl" in params.shadow))
9700
- throw new VibeError44({ code: "VIBE_CONFIG", message: "Automatic server shadows require an explicit admin URL" });
9883
+ throw new VibeError45({ code: "VIBE_CONFIG", message: "Automatic server shadows require an explicit admin URL" });
9701
9884
  const { provider, adminUrl } = params.shadow;
9702
9885
  const url = await Promise.resolve().then(() => new URL(adminUrl)).then((value) => value, (cause) => {
9703
- throw new VibeError44({ code: "VIBE_CONFIG", message: "shadowAdminUrl must be an explicit server database URL", cause });
9886
+ throw new VibeError45({ code: "VIBE_CONFIG", message: "shadowAdminUrl must be an explicit server database URL", cause });
9704
9887
  });
9705
9888
  const expected = provider === "postgresql" ? ["postgres:", "postgresql:"] : ["mysql:"];
9706
9889
  if (!expected.includes(url.protocol) || url.hostname.length === 0 || url.pathname.length <= 1 || [...url.searchParams.keys()].some((key) => ["database", "dbname", "host", "hostaddr", "port", "user", "socketpath", "connectionstring", "uri", "url", "service"].includes(key.toLowerCase())))
9707
- throw new VibeError44({ code: "VIBE_CONFIG", message: "Shadow admin URL has an unsupported protocol or conflicting connection selector", meta: { reason: "shadow-url-ambiguous" } });
9890
+ throw new VibeError45({ code: "VIBE_CONFIG", message: "Shadow admin URL has an unsupported protocol or conflicting connection selector", meta: { reason: "shadow-url-ambiguous" } });
9708
9891
  const prefix = /^\/vibeorm_test(?:_|$)/.test(url.pathname) ? "vibeorm_test_shadow" : "vibeorm_shadow";
9709
9892
  const name = `${prefix}_${randomUUID3().replaceAll("-", "")}`;
9710
9893
  const connection = await params.openConnection({ provider, url: adminUrl });
@@ -9712,7 +9895,7 @@ async function withMigrationReference(params) {
9712
9895
  const sqlDialect = getSqlDialect14({ dialect });
9713
9896
  for (const query of renderShadowOperation4({ dialect: sqlDialect, operation: { kind: "createDatabase", name } })) {
9714
9897
  await admin({ text: query.text, values: [...query.values] }).then(undefined, (cause) => {
9715
- throw new VibeError44({ code: "VIBE_MIGRATION", message: "Shadow CREATE did not report success. No deletion was attempted; inspect the named artifact on the configured admin server before explicit cleanup.", meta: { reason: "shadow-create-outcome-unknown", artifact: name }, cause });
9898
+ throw new VibeError45({ code: "VIBE_MIGRATION", message: "Shadow CREATE did not report success. No deletion was attempted; inspect the named artifact on the configured admin server before explicit cleanup.", meta: { reason: "shadow-create-outcome-unknown", artifact: name }, cause });
9716
9899
  });
9717
9900
  }
9718
9901
  return finishShadowWork({ artifact: name, cleanup: async () => {
@@ -9727,10 +9910,10 @@ async function withMigrationReference(params) {
9727
9910
  }
9728
9911
  // packages/migrate/src/replay-objects.ts
9729
9912
  import { randomUUID as randomUUID4 } from "crypto";
9730
- import { VibeError as VibeError45, sqlObjectKey as sqlObjectKey16 } from "@vibeorm/schema";
9913
+ import { VibeError as VibeError46, sqlObjectKey as sqlObjectKey16 } from "@vibeorm/schema";
9731
9914
  import { getSqlDialect as getSqlDialect15 } from "@vibeorm/sql";
9732
9915
  function comparisonFailure(params) {
9733
- return params.cause instanceof VibeError45 ? params.cause : new VibeError45({ code: "VIBE_MIGRATION", message: "Authored custom SQL could not be safely compiled on the independent scratch reference. Review dependencies and provide an explicit SQL migration when replacement is not supported.", meta: { reason: "migration-object-comparison-unavailable" }, cause: params.cause });
9916
+ return params.cause instanceof VibeError46 ? params.cause : new VibeError46({ code: "VIBE_MIGRATION", message: "Authored custom SQL could not be safely compiled on the independent scratch reference. Review dependencies and provide an explicit SQL migration when replacement is not supported.", meta: { reason: "migration-object-comparison-unavailable" }, cause: params.cause });
9734
9917
  }
9735
9918
  function leadingCommand(params) {
9736
9919
  const { sql } = params;
@@ -9772,11 +9955,11 @@ async function compareReplayMigrationObjects(params) {
9772
9955
  if (objects.length === 0)
9773
9956
  return params.schema;
9774
9957
  if (params.reference.dialect !== "postgres")
9775
- throw new VibeError45({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Independent authored custom-object comparison requires transactional scratch DDL. Use reviewed SQL for this dialect's custom objects.", meta: { feature: "authoredCustomObjectComparison", dialect: params.reference.dialect, objects: objects.map((object) => object.identity) } });
9958
+ throw new VibeError46({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Independent authored custom-object comparison requires transactional scratch DDL. Use reviewed SQL for this dialect's custom objects.", meta: { feature: "authoredCustomObjectComparison", dialect: params.reference.dialect, objects: objects.map((object) => object.identity) } });
9776
9959
  const before = new Map(params.reference.state.definitions.map((definition) => [sqlObjectKey16({ identity: definition.identity }), definition]));
9777
9960
  for (const object of objects) {
9778
9961
  if (object.dialect !== params.reference.dialect || object.create.length === 0 || before.has(sqlObjectKey16({ identity: object.identity })) && object.drop.length === 0 || [...object.create, ...object.drop].some((sql) => /^(?:BEGIN|START|COMMIT|END|ABORT|ROLLBACK|PREPARE|VACUUM|CREATE\s+DATABASE|DROP\s+DATABASE)\b/i.test(leadingCommand({ sql }))))
9779
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Custom object cannot be safely dropped/recreated in a rollback-only shadow transaction. Supply a reviewed SQL migration.", meta: { reason: "migration-object-comparison-unavailable", identity: object.identity } });
9962
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Custom object cannot be safely dropped/recreated in a rollback-only shadow transaction. Supply a reviewed SQL migration.", meta: { reason: "migration-object-comparison-unavailable", identity: object.identity } });
9780
9963
  }
9781
9964
  const definitions = new Map;
9782
9965
  const sentinel = getSqlDialect15({ dialect: "postgres" }).quoteIdent({ name: `vibe_probe_${randomUUID4().replaceAll("-", "")}` });
@@ -9784,7 +9967,7 @@ async function compareReplayMigrationObjects(params) {
9784
9967
  await Promise.resolve().then(() => params.executor({ text: `RELEASE SAVEPOINT ${sentinel}` })).then(() => {
9785
9968
  return;
9786
9969
  }, (cause) => {
9787
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Authored SQL destroyed the scratch transaction or its savepoint. Discard this scratch reference; no comparison result can be trusted.", meta: { reason: "migration-object-transaction-lost", shadowInvalid: true }, cause });
9970
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Authored SQL destroyed the scratch transaction or its savepoint. Discard this scratch reference; no comparison result can be trusted.", meta: { reason: "migration-object-transaction-lost", shadowInvalid: true }, cause });
9788
9971
  });
9789
9972
  await params.executor({ text: `SAVEPOINT ${sentinel}` });
9790
9973
  };
@@ -9812,7 +9995,7 @@ async function compareReplayMigrationObjects(params) {
9812
9995
  for (const object of objects) {
9813
9996
  const inspected = await inspectSqlObject({ executor: params.executor, dialect: "postgres", identity: object.identity });
9814
9997
  if (inspected.state !== "present")
9815
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Authored custom SQL did not produce an inspectable object in the independent scratch probe.", meta: { reason: "migration-object-comparison-unavailable", identity: object.identity, state: inspected.state } });
9998
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Authored custom SQL did not produce an inspectable object in the independent scratch probe.", meta: { reason: "migration-object-comparison-unavailable", identity: object.identity, state: inspected.state } });
9816
9999
  definitions.set(sqlObjectKey16({ identity: object.identity }), inspected.definition);
9817
10000
  }
9818
10001
  for (const [key, expected] of before) {
@@ -9820,32 +10003,173 @@ async function compareReplayMigrationObjects(params) {
9820
10003
  continue;
9821
10004
  const inspected = await inspectSqlObject({ executor: params.executor, dialect: "postgres", identity: expected.identity });
9822
10005
  if (inspected.state !== "present" || canonicalMigrationJson({ value: inspected.definition }) !== canonicalMigrationJson({ value: expected }))
9823
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Custom scratch replacement changed a dependent object outside the declared replacement set. Supply reviewed SQL.", meta: { reason: "migration-object-dependency-unavailable", identity: expected.identity } });
10006
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Custom scratch replacement changed a dependent object outside the declared replacement set. Supply reviewed SQL.", meta: { reason: "migration-object-dependency-unavailable", identity: expected.identity } });
9824
10007
  }
9825
10008
  await checkSentinel();
9826
10009
  }).then(() => ({ ok: true }), (cause) => ({ ok: false, error: comparisonFailure({ cause }) }));
9827
- const rollback = await Promise.resolve().then(() => params.executor({ text: "ROLLBACK" })).then(() => ({ ok: true }), (cause) => ({ ok: false, error: cause instanceof VibeError45 ? cause : new VibeError45({ code: "VIBE_MIGRATION", message: "Scratch rollback failed", meta: { reason: "migration-object-rollback-failed" }, cause }) }));
10010
+ const rollback = await Promise.resolve().then(() => params.executor({ text: "ROLLBACK" })).then(() => ({ ok: true }), (cause) => ({ ok: false, error: cause instanceof VibeError46 ? cause : new VibeError46({ code: "VIBE_MIGRATION", message: "Scratch rollback failed", meta: { reason: "migration-object-rollback-failed" }, cause }) }));
9828
10011
  if (!rollback.ok)
9829
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Custom comparison scratch could not be rolled back. Discard the scratch reference and inspect the preserved execution and cleanup errors.", meta: { reason: "migration-object-rollback-failed", shadowInvalid: true, cleanupFailure: rollback.error }, cause: outcome.ok ? rollback.error : outcome.error });
10012
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Custom comparison scratch could not be rolled back. Discard the scratch reference and inspect the preserved execution and cleanup errors.", meta: { reason: "migration-object-rollback-failed", shadowInvalid: true, cleanupFailure: rollback.error }, cause: outcome.ok ? rollback.error : outcome.error });
9830
10013
  if (!outcome.ok)
9831
10014
  throw outcome.error;
9832
10015
  for (const object of objects) {
9833
10016
  const key = sqlObjectKey16({ identity: object.identity });
9834
10017
  const previous = before.get(key);
9835
10018
  if (previous !== undefined && canonicalMigrationJson({ value: previous }) !== canonicalMigrationJson({ value: definitions.get(key) }))
9836
- throw new VibeError45({ code: "VIBE_MIGRATION", message: "Authored custom object differs from replayed history. Supply a reviewed forward SQL migration; generation will not silently replace its body.", meta: { reason: "migration-object-replacement-required", shadowReusable: true, identity: object.identity, id: object.id } });
10019
+ throw new VibeError46({ code: "VIBE_MIGRATION", message: "Authored custom object differs from replayed history. Supply a reviewed forward SQL migration; generation will not silently replace its body.", meta: { reason: "migration-object-replacement-required", shadowReusable: true, identity: object.identity, id: object.id } });
9837
10020
  }
9838
10021
  return { ...params.schema, migrationObjects: objects.map((object) => ({ ...object, expected: definitions.get(sqlObjectKey16({ identity: object.identity })) })) };
9839
10022
  }
9840
10023
  // packages/migrate/src/replay-plan.ts
9841
- import { VibeError as VibeError49, sqlObjectKey as sqlObjectKey18 } from "@vibeorm/schema";
10024
+ import { VibeError as VibeError51, sqlObjectKey as sqlObjectKey19 } from "@vibeorm/schema";
10025
+
10026
+ // packages/migrate/src/sqlite-fts-upgrade.ts
10027
+ import { VibeError as VibeError47, sqlObjectKey as sqlObjectKey17 } from "@vibeorm/schema";
10028
+ import { sqliteFtsUpdateUpgrade } from "@vibeorm/sql";
10029
+ var actionReceipts = new WeakMap;
10030
+ function specs(params) {
10031
+ const result = [];
10032
+ for (const group of params.collected.groups)
10033
+ for (const artifact of group.objects) {
10034
+ const owner = artifact.owns;
10035
+ const expected = artifact.expected;
10036
+ if (owner.kind !== "trigger" || artifact.create.length !== 0 || artifact.drop.length !== 0 || expected?.identity.kind !== "trigger" || expected.identity.table.namespace !== "main" || expected.format !== "catalog-v2" || expected.facts.type !== "trigger" || typeof expected.facts.definition !== "string" || expected.dependencies.length !== 0)
10037
+ continue;
10038
+ const identity = { kind: "trigger", table: { namespace: "main", name: owner.table }, name: owner.name };
10039
+ if (sqlObjectKey17({ identity }) !== sqlObjectKey17({ identity: expected.identity }))
10040
+ continue;
10041
+ const matches = [];
10042
+ for (const bundle of group.objects) {
10043
+ if (bundle.owns.kind !== "trigger" || bundle.owns.table !== owner.table || bundle.create.length !== 4 || bundle.drop.length !== 3)
10044
+ continue;
10045
+ const create = bundle.create[2];
10046
+ const parsed = sqliteFtsUpdateUpgrade({ create, installed: expected.facts.definition, table: owner.table, name: owner.name });
10047
+ if (parsed?.kind !== "tableRequired" || bundle.owns.name !== `${parsed.ftsTable}_ai`)
10048
+ continue;
10049
+ const tableArtifact = group.objects.find((entry) => entry.owns.kind === "table" && entry.owns.name === parsed.ftsTable && entry.create.length === 1);
10050
+ if (tableArtifact === undefined)
10051
+ continue;
10052
+ const declared = sqliteFtsUpdateUpgrade({ create, installed: expected.facts.definition, table: owner.table, name: owner.name, tableDefinition: tableArtifact.create[0] });
10053
+ if (declared?.kind !== "recognized" || declared.installed !== "current")
10054
+ continue;
10055
+ matches.push({ artifact, bundle, tableArtifact, identity, create, ftsTable: declared.ftsTable, currentDefinition: declared.currentDefinition });
10056
+ }
10057
+ if (matches.length === 1)
10058
+ result.push(matches[0]);
10059
+ }
10060
+ return result;
10061
+ }
10062
+ function tableIdentity(params) {
10063
+ return { kind: "table", name: { namespace: "main", name: params.spec.ftsTable } };
10064
+ }
10065
+ function unavailable2(params) {
10066
+ return { identity: params.identity, ownership: "migration", state: "unavailable", reason: params.reason, differences: [] };
10067
+ }
10068
+ async function planSqliteFtsUpdates(params) {
10069
+ const states = new Map;
10070
+ const forcedArtifacts = new Set;
10071
+ const before = [];
10072
+ const after = [];
10073
+ const checks = [];
10074
+ for (const spec of specs(params)) {
10075
+ const key = sqlObjectKey17({ identity: spec.identity });
10076
+ const live = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: spec.identity });
10077
+ const state = compareInspectedSqlObject({ identity: spec.identity, ownership: "migration", expected: spec.artifact.expected, live, dialect: "sqlite" });
10078
+ states.set(key, state);
10079
+ if (live.state === "unavailable")
10080
+ continue;
10081
+ const beforeTrigger = live.state === "present" && typeof live.definition.facts.definition === "string" ? live.definition.facts.definition : undefined;
10082
+ if (live.state === "present" && beforeTrigger === undefined)
10083
+ continue;
10084
+ const candidate = sqliteFtsUpdateUpgrade({ create: spec.create, installed: beforeTrigger ?? spec.currentDefinition, table: spec.identity.table.name, name: spec.identity.name });
10085
+ if (candidate?.kind !== "tableRequired")
10086
+ continue;
10087
+ const table = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: tableIdentity({ spec }) });
10088
+ if (table.state === "unavailable") {
10089
+ states.set(key, unavailable2({ identity: spec.identity, reason: table.reason }));
10090
+ continue;
10091
+ }
10092
+ const beforeTable = table.state === "present" && typeof table.definition.facts.definition === "string" ? table.definition.facts.definition : undefined;
10093
+ const recognized = sqliteFtsUpdateUpgrade({
10094
+ create: spec.create,
10095
+ installed: beforeTrigger ?? spec.currentDefinition,
10096
+ table: spec.identity.table.name,
10097
+ name: spec.identity.name,
10098
+ tableDefinition: table.state === "missing" ? spec.tableArtifact.create[0] : beforeTable ?? ""
10099
+ });
10100
+ if (recognized?.kind !== "recognized") {
10101
+ states.set(key, unavailable2({ identity: spec.identity, reason: "sqlite-fts-table-unverified" }));
10102
+ continue;
10103
+ }
10104
+ const installBundle = table.state === "missing" || !params.installed.triggers?.has(`${spec.ftsTable}_ai`) || !params.installed.triggers?.has(`${spec.ftsTable}_ad`);
10105
+ if (table.state === "missing")
10106
+ forcedArtifacts.add(spec.tableArtifact.id);
10107
+ if (installBundle)
10108
+ forcedArtifacts.add(spec.bundle.id);
10109
+ else if (live.state === "missing")
10110
+ after.push(spec.create, spec.bundle.create[3]);
10111
+ const legacy = live.state === "present" && recognized.installed === "legacy";
10112
+ if (legacy) {
10113
+ before.push(recognized.drop);
10114
+ if (!installBundle)
10115
+ after.push(spec.create);
10116
+ states.set(key, { ...state, state: "changed", reason: "sqlite-fts-update-upgrade" });
10117
+ }
10118
+ checks.push({ spec, beforeTrigger, beforeTable, changes: installBundle || live.state === "missing" || legacy });
10119
+ }
10120
+ return { states, forcedArtifacts, before, after, checks };
10121
+ }
10122
+ function retainSqliteFtsPlan(params) {
10123
+ if (params.fts.checks.length > 0)
10124
+ actionReceipts.set(params.plan, { fts: params.fts, ...params.transaction === undefined ? {} : { transaction: params.transaction } });
10125
+ }
10126
+ function sqliteFtsTransaction(params) {
10127
+ return actionReceipts.get(params.plan)?.transaction;
10128
+ }
10129
+ function stateChanged(params) {
10130
+ return new VibeError47({ code: "VIBE_MIGRATION", message: "SQLite FTS state changed after planning; re-plan before replacing synchronization", meta: { reason: "sqlite-fts-state-changed", identity: params.identity } });
10131
+ }
10132
+ async function assertSqliteFtsApplyState(params) {
10133
+ const receipt = actionReceipts.get(params.plan)?.fts;
10134
+ if (receipt === undefined)
10135
+ return;
10136
+ for (const check of receipt.checks) {
10137
+ if (!check.changes)
10138
+ continue;
10139
+ for (const observation of [{ identity: check.spec.identity, definition: check.beforeTrigger }, { identity: tableIdentity({ spec: check.spec }), definition: check.beforeTable }]) {
10140
+ const live = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: observation.identity });
10141
+ if (observation.definition === undefined ? live.state !== "missing" : live.state !== "present" || live.definition.facts.definition !== observation.definition)
10142
+ throw stateChanged({ identity: observation.identity });
10143
+ }
10144
+ }
10145
+ }
10146
+ async function verifySqliteFtsApplyState(params) {
10147
+ const receipt = actionReceipts.get(params.plan)?.fts;
10148
+ if (receipt === undefined)
10149
+ return;
10150
+ for (const { spec } of receipt.checks) {
10151
+ const live = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: spec.identity });
10152
+ const expected = { identity: spec.identity, format: "catalog-v2", facts: { definition: spec.currentDefinition, type: "trigger" }, dependencies: [] };
10153
+ const compared = compareInspectedSqlObject({ identity: spec.identity, ownership: "migration", expected, live, dialect: "sqlite" });
10154
+ if (compared.state !== "matching")
10155
+ throw new VibeError47({ code: "VIBE_MIGRATION", message: "SQLite FTS UPDATE synchronization did not match its independent definition after apply", meta: { reason: "migration-state-unverified", objects: [compared] } });
10156
+ const table = await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity: tableIdentity({ spec }) });
10157
+ if (table.state !== "present" || typeof table.definition.facts.definition !== "string" || sqliteFtsUpdateUpgrade({ create: spec.create, installed: spec.currentDefinition, table: spec.identity.table.name, name: spec.identity.name, tableDefinition: table.definition.facts.definition })?.kind !== "recognized")
10158
+ throw stateChanged({ identity: tableIdentity({ spec }) });
10159
+ for (const suffix of ["ai", "ad"]) {
10160
+ const identity = { kind: "trigger", table: spec.identity.table, name: `${spec.ftsTable}_${suffix}` };
10161
+ if ((await inspectSqlObject({ executor: params.executor, dialect: "sqlite", identity })).state !== "present")
10162
+ throw stateChanged({ identity });
10163
+ }
10164
+ }
10165
+ }
9842
10166
 
9843
10167
  // packages/migrate/src/planner.ts
9844
- import { VibeError as VibeError48, sqlObjectKey as sqlObjectKey17, qualifiedSqlName as qualifiedSqlName12 } from "@vibeorm/schema";
10168
+ import { VibeError as VibeError50, sqlObjectKey as sqlObjectKey18, qualifiedSqlName as qualifiedSqlName13 } from "@vibeorm/schema";
9845
10169
  import { renderSqliteRebuildObjects, sqliteDialect as sqliteDialect4 } from "@vibeorm/sql";
9846
10170
 
9847
10171
  // packages/migrate/src/artifact-probe.ts
9848
- import { VibeError as VibeError46 } from "@vibeorm/schema";
10172
+ import { VibeError as VibeError48 } from "@vibeorm/schema";
9849
10173
  var SAVEPOINT = "vibeorm_artifact_probe";
9850
10174
  async function probeChangedArtifacts(params) {
9851
10175
  const { executor, namespace } = params;
@@ -9868,7 +10192,7 @@ async function probeChangedArtifacts(params) {
9868
10192
  await executor({ text: `RELEASE SAVEPOINT ${SAVEPOINT}` });
9869
10193
  }
9870
10194
  if (after.state !== "present")
9871
- throw new VibeError46({ code: "VIBE_MIGRATION", message: `Extension artifact ${artifact.id} did not produce its object when probed; its create SQL cannot be verified against the replayed history`, meta: { reason: "artifact-probe-unavailable", id: artifact.id, identity } });
10195
+ throw new VibeError48({ code: "VIBE_MIGRATION", message: `Extension artifact ${artifact.id} did not produce its object when probed; its create SQL cannot be verified against the replayed history`, meta: { reason: "artifact-probe-unavailable", id: artifact.id, identity } });
9872
10196
  if (canonicalMigrationJson({ value: before.definition.facts }) === canonicalMigrationJson({ value: after.definition.facts }))
9873
10197
  continue;
9874
10198
  const definition = before.definition.facts.definition;
@@ -9878,8 +10202,8 @@ async function probeChangedArtifacts(params) {
9878
10202
  }
9879
10203
 
9880
10204
  // packages/migrate/src/index-predicates.ts
9881
- import { VibeError as VibeError47 } from "@vibeorm/schema";
9882
- import { defaultIndexName as defaultIndexName3, qualifiedSqlName as qualifiedSqlName11 } from "@vibeorm/schema";
10205
+ import { VibeError as VibeError49 } from "@vibeorm/schema";
10206
+ import { defaultIndexName as defaultIndexName3, qualifiedSqlName as qualifiedSqlName12 } from "@vibeorm/schema";
9883
10207
  function physicalIndexName(params) {
9884
10208
  return params.index.name ?? defaultIndexName3({ table: params.table, columns: params.columns, unique: params.index.kind === "unique", dialect: "postgres" });
9885
10209
  }
@@ -9906,7 +10230,7 @@ async function prepareIndexPredicateDiff(params) {
9906
10230
  indexes.push(index);
9907
10231
  continue;
9908
10232
  }
9909
- const deparsed = await deparseIndexPredicate({ executor: params.executor, table: qualifiedSqlName11({ name: table, namespace: live.namespace, defaultNamespace: params.namespace }), where: next.where });
10233
+ const deparsed = await deparseIndexPredicate({ executor: params.executor, table: qualifiedSqlName12({ name: table, namespace: live.namespace, defaultNamespace: params.namespace }), where: next.where });
9910
10234
  indexes.push(deparsed === index.where ? { ...index, where: next.where } : index);
9911
10235
  }
9912
10236
  models.push({ ...live, indexes });
@@ -9919,7 +10243,7 @@ async function deparseIndexPredicate(params) {
9919
10243
  const match = /^CHECK \((.*)\)$/s.exec(definition);
9920
10244
  return match === null ? undefined : match[1];
9921
10245
  } catch (error) {
9922
- if (error instanceof VibeError47 && error.code === "VIBE_MIGRATION")
10246
+ if (error instanceof VibeError49 && error.code === "VIBE_MIGRATION")
9923
10247
  throw error;
9924
10248
  return;
9925
10249
  }
@@ -9943,19 +10267,23 @@ async function planSchemaMigration(params) {
9943
10267
  assertRetainedMigrationObjects({ from: (previous?.schema.migrationObjects ?? []).filter((object) => object.identity.kind !== "namespace" || !schemaNamespaces({ schema }).includes(object.identity.name)), to: objects });
9944
10268
  for (const object of objects)
9945
10269
  if (object.dialect !== dialect)
9946
- throw new VibeError48({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}`, meta: { feature: "migrationObject", dialect } });
10270
+ throw new VibeError50({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}`, meta: { feature: "migrationObject", dialect } });
9947
10271
  const physical = params.physical ?? (dialect === "postgres" ? await introspectPhysicalPostgres({ executor, namespaces }) : dialect === "mysql" ? await introspectMysql({ executor }) : await introspectSqlite({ executor }));
9948
10272
  const comparablePhysical = external === undefined ? physical : projectExternalSchema({ schema: physical, external });
9949
10273
  if (previous !== undefined)
9950
10274
  assertMigrationState({ report: await verifyMigrationState({ executor, expected: previous, dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
9951
10275
  const inventory = params.physicalInventory ?? external?.inventory ?? (await Promise.all((dialect === "postgres" ? namespaces : [namespace]).map((scope) => inspectMigrationInventory({ executor, dialect, namespace: scope })))).flat();
9952
10276
  const comparisonInventory = external === undefined ? inventory : projectExternalInventory({ inventory, external });
9953
- const visible = normalizeSchema({ schema: physical, dialect });
10277
+ const visibleModels = new Set(physical.models.map((model) => physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect })));
10278
+ let visible;
9954
10279
  for (const object of inventory) {
9955
10280
  const identity = object.identity;
9956
- if (identity.kind !== "table" || physical.models.some((model) => physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect }) === physicalNameKey({ ...identity.name, dialect })) || visible.joinTables.has(physicalNameKey({ ...identity.name, dialect })))
10281
+ if (identity.kind !== "table" || visibleModels.has(physicalNameKey({ ...identity.name, dialect })))
10282
+ continue;
10283
+ visible ??= normalizeSchema({ schema: physical, dialect });
10284
+ if (visible.joinTables.has(physicalNameKey({ ...identity.name, dialect })))
9957
10285
  continue;
9958
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Catalog table is not visible to schema introspection; automatic planning cannot prove the complete physical state", meta: { reason: "inventory-unavailable", identity: object.identity } });
10286
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Catalog table is not visible to schema introspection; automatic planning cannot prove the complete physical state", meta: { reason: "inventory-unavailable", identity: object.identity } });
9959
10287
  }
9960
10288
  const states = resolved.unavailable.map((identity) => {
9961
10289
  const exists = identity.kind === "column" && [physical, previous?.schema].some((source) => source?.models.some((model) => physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect }) === physicalNameKey({ ...identity.table, dialect }) && model.fields.some((field) => (field.dbName ?? field.name) === identity.column)));
@@ -9966,13 +10294,13 @@ async function planSchemaMigration(params) {
9966
10294
  const creates = [];
9967
10295
  for (const object of objects) {
9968
10296
  for (const dependency of object.dependsOn) {
9969
- if ([...objects.map((entry) => entry.identity), ...schemaObjectIdentities({ schema, dialect, namespace })].some((identity) => sqlObjectKey17({ identity }) === sqlObjectKey17({ identity: dependency })))
10297
+ if ([...objects.map((entry) => entry.identity), ...schemaObjectIdentities({ schema, dialect, namespace })].some((identity) => sqlObjectKey18({ identity }) === sqlObjectKey18({ identity: dependency })))
9970
10298
  continue;
9971
10299
  const state = await inspectSqlObject({ executor, dialect, identity: dependency });
9972
10300
  if (state.state !== "present")
9973
10301
  states.push({ identity: dependency, ownership: "migration", state: "unavailable", reason: "dependency-unavailable", differences: [] });
9974
10302
  }
9975
- const expected = object.expected ?? previous?.definitions.find((entry) => sqlObjectKey17({ identity: entry.identity }) === sqlObjectKey17({ identity: object.identity }));
10303
+ const expected = object.expected ?? previous?.definitions.find((entry) => sqlObjectKey18({ identity: entry.identity }) === sqlObjectKey18({ identity: object.identity }));
9976
10304
  const live = await inspectSqlObject({ executor, dialect, identity: object.identity });
9977
10305
  if (object.identity.kind === "namespace") {
9978
10306
  states.push({ identity: object.identity, ownership: "schema", state: live.state === "present" ? "matching" : live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} });
@@ -9984,14 +10312,21 @@ async function planSchemaMigration(params) {
9984
10312
  states.push({ identity: object.identity, ownership: "migration", state: "missing", differences: [] });
9985
10313
  creates.push(...object.create);
9986
10314
  } else {
9987
- const report = await verifyMigrationState({ executor, dialect, expected: { schema: { ...schema, models: [], migrationObjects: [object] }, definitions: [expected] } });
9988
- states.push(...report.objects);
10315
+ states.push(compareInspectedSqlObject({ identity: object.identity, ownership: "migration", expected, live, dialect }));
9989
10316
  }
9990
10317
  }
10318
+ for (const object of legacyObjects)
10319
+ if (objects.some((entry) => sqlObjectKey18({ identity: entry.identity }) === sqlObjectKey18({ identity: object.identity })))
10320
+ throw new VibeError50({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate combined and migration-only ownership", meta: { identity: object.identity } });
10321
+ const installed = await probeInstalledExtensionState({ executor, dialect, artifacts });
10322
+ const sqliteFts = dialect === "sqlite" && collected.objects.some((artifact) => artifact.owns.kind === "trigger" && artifact.create.length === 0 && artifact.drop.length === 0 && artifact.expected !== undefined) ? await planSqliteFtsUpdates({ executor, collected, installed }) : undefined;
9991
10323
  for (const object of legacyObjects) {
9992
- if (objects.some((entry) => sqlObjectKey17({ identity: entry.identity }) === sqlObjectKey17({ identity: object.identity })))
9993
- throw new VibeError48({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate combined and migration-only ownership", meta: { identity: object.identity } });
9994
- const expected = object.expected ?? previous?.definitions.find((entry) => sqlObjectKey17({ identity: entry.identity }) === sqlObjectKey17({ identity: object.identity }));
10324
+ const qualifiedFts = sqliteFts?.states.get(sqlObjectKey18({ identity: object.identity }));
10325
+ if (qualifiedFts !== undefined) {
10326
+ states.push(qualifiedFts);
10327
+ continue;
10328
+ }
10329
+ const expected = object.expected ?? previous?.definitions.find((entry) => sqlObjectKey18({ identity: entry.identity }) === sqlObjectKey18({ identity: object.identity }));
9995
10330
  if (expected === undefined)
9996
10331
  states.push({ identity: object.identity, ownership: "migration", state: "unavailable", reason: "legacy-definition-required", differences: [] });
9997
10332
  else {
@@ -9999,7 +10334,7 @@ async function planSchemaMigration(params) {
9999
10334
  if (live.state === "missing")
10000
10335
  states.push({ identity: object.identity, ownership: "migration", state: "missing", differences: [] });
10001
10336
  else
10002
- states.push(...(await verifyMigrationState({ executor, dialect, expected: { schema: { ...schema, models: [], migrationObjects: [object] }, definitions: [expected] } })).objects);
10337
+ states.push(compareInspectedSqlObject({ identity: object.identity, ownership: "migration", expected, live, dialect }));
10003
10338
  }
10004
10339
  }
10005
10340
  const protectedObjects = [...legacyObjects, ...objects, ...previous?.schema.migrationObjects ?? []];
@@ -10007,16 +10342,17 @@ async function planSchemaMigration(params) {
10007
10342
  const checked = await prepareCheckDiff({ executor, from: stripped, to: schema, dialect, ...params.probe === undefined ? {} : { probe: params.probe }, ...previous === undefined ? {} : { previous }, ...params.replaceChecks === undefined ? {} : { replaceChecks: params.replaceChecks } });
10008
10343
  const exclusionsPrepared = await prepareExclusionDiff({ ...params, ...previous === undefined ? {} : { previous }, from: checked, to: schema });
10009
10344
  const from = dialect === "postgres" ? await prepareOperatorClassDiff({ executor, from: await prepareIndexPredicateDiff({ executor, from: exclusionsPrepared, to: schema, namespace, ...params.probe === undefined ? {} : { probe: params.probe } }), to: schema }) : exclusionsPrepared;
10010
- const steps = diffSchemas({ from, to: schema, dialect });
10011
- const known = new Set(schemaObjectIdentities({ schema: previous?.schema ?? schema, dialect, namespace }).map((identity) => sqlObjectKey17({ identity })));
10012
- const declaredIndexes = normalizeSchema({ schema: previous?.schema ?? schema, dialect });
10345
+ const comparison = diffSchemasWithNormalization({ from, to: schema, dialect });
10346
+ const steps = comparison.steps;
10347
+ const declaredIndexes = previous === undefined ? comparison.to : normalizeSchema({ schema: previous.schema, dialect });
10348
+ const known = new Set(schemaObjectIdentitiesFromNormalized({ schema: previous?.schema ?? schema, dialect, namespace, normalized: declaredIndexes }).map((identity) => sqlObjectKey18({ identity })));
10013
10349
  const physicalIndexes = normalizeSchema({ schema: physical, dialect });
10014
10350
  for (const [table, model] of physicalIndexes.tables)
10015
10351
  for (const [key, index] of model.indexes) {
10016
10352
  if (index.constraintName !== undefined && declaredIndexes.tables.get(table)?.indexes.has(key))
10017
- known.add(sqlObjectKey17({ identity: { kind: "constraint", constraintType: "unique", table: qualifiedSqlName12({ name: model.table, namespace: model.namespace, defaultNamespace: namespace }), name: index.constraintName } }));
10353
+ known.add(sqlObjectKey18({ identity: { kind: "constraint", constraintType: "unique", table: qualifiedSqlName13({ name: model.table, namespace: model.namespace, defaultNamespace: namespace }), name: index.constraintName } }));
10018
10354
  }
10019
- const migrationOwned = new Set(protectedObjects.map((object) => sqlObjectKey17({ identity: object.identity })));
10355
+ const migrationOwned = new Set(protectedObjects.map((object) => sqlObjectKey18({ identity: object.identity })));
10020
10356
  const removals = steps.flatMap((step) => {
10021
10357
  const identity = removalIdentity({ step, namespace });
10022
10358
  return identity === undefined ? [] : [identity];
@@ -10025,30 +10361,30 @@ async function planSchemaMigration(params) {
10025
10361
  for (const identity of removals) {
10026
10362
  const owner = externalObjectOwner({ external, identity });
10027
10363
  if (owner !== undefined)
10028
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Automatic DDL cannot remove externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
10364
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Automatic DDL cannot remove externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
10029
10365
  }
10030
10366
  const enumRemovals = new Set(removals.flatMap((identity) => identity.kind === "enum" ? [physicalNameKey({ ...identity.name, dialect })] : []));
10031
10367
  if (external !== undefined)
10032
10368
  await refuseExternalDependents({ executor, dialect, external, steps, namespace });
10033
10369
  const droppedTables = new Set(steps.flatMap((step) => step.kind === "dropTable" || step.kind === "dropJoinTable" ? [physicalNameKey({ name: step.table, namespace: step.namespace, dialect })] : []));
10034
10370
  const unmanagedRemovals = [];
10035
- const invalid = dialect === "sqlite" ? sqliteInvalidatedTables({ steps, from: physical }) : { rebuilt: new Set, dropped: new Set };
10371
+ const invalid = dialect === "sqlite" && steps.length > 0 ? sqliteInvalidatedTables({ steps, from: physical }) : { rebuilt: new Set, dropped: new Set };
10036
10372
  if (dialect === "sqlite")
10037
10373
  await assertSqliteRebuildTableOptions({ executor, tables: invalid.rebuilt });
10038
10374
  for (const object of inventory) {
10039
- const key = sqlObjectKey17({ identity: object.identity });
10375
+ const key = sqlObjectKey18({ identity: object.identity });
10040
10376
  const normalizedOwner = object.backingConstraint === undefined ? undefined : inventory.find((entry) => entry.identity.kind === "constraint" && entry.identity.name === object.backingConstraint && ("table" in object.identity) && entry.identity.table.name === object.identity.table.name && entry.identity.table.namespace === object.identity.table.namespace);
10041
- const ownership = migrationOwned.has(key) ? "migration" : known.has(key) || normalizedOwner !== undefined && known.has(sqlObjectKey17({ identity: normalizedOwner.identity })) ? "schema" : "unknown";
10377
+ const ownership = migrationOwned.has(key) ? "migration" : known.has(key) || normalizedOwner !== undefined && known.has(sqlObjectKey18({ identity: normalizedOwner.identity })) ? "schema" : "unknown";
10042
10378
  const externalOwner = external === undefined ? undefined : externalObjectOwner({ identity: object.identity, external });
10043
10379
  if (object.inspectionUnavailable !== undefined)
10044
10380
  states.push({ identity: object.identity, ownership, state: "unavailable", reason: object.inspectionUnavailable, differences: [] });
10045
10381
  if (object.inspectionUnavailable !== undefined && invalid.rebuilt.has("table" in object.identity ? object.identity.table.name : typeof object.identity.name === "object" ? object.identity.name.name : object.identity.name))
10046
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "SQLite rebuild cannot discard an unrepresented constraint; declare its independent definition or use a reviewed migration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
10382
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "SQLite rebuild cannot discard an unrepresented constraint; declare its independent definition or use a reviewed migration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
10047
10383
  if (ownership === "unknown" && comparisonInventory.includes(object))
10048
10384
  states.push({ identity: object.identity, ownership, state: "unavailable", reason: "unmanaged", differences: [] });
10049
10385
  let collateral = "table" in object.identity && droppedTables.has(physicalNameKey({ ...object.identity.table, dialect }));
10050
- const declared = protectedObjects.find((entry) => sqlObjectKey17({ identity: entry.identity }) === key);
10051
- if (declared?.dependsOn.some((dependency) => removals.some((identity) => sqlObjectKey17({ identity }) === sqlObjectKey17({ identity: dependency })) || ("table" in dependency) && droppedTables.has(physicalNameKey({ ...dependency.table, dialect }))))
10386
+ const declared = protectedObjects.find((entry) => sqlObjectKey18({ identity: entry.identity }) === key);
10387
+ if (declared?.dependsOn.some((dependency) => removals.some((identity) => sqlObjectKey18({ identity }) === sqlObjectKey18({ identity: dependency })) || ("table" in dependency) && droppedTables.has(physicalNameKey({ ...dependency.table, dialect }))))
10052
10388
  collateral = true;
10053
10389
  if (ownership === "unknown" && object.identity.kind === "function" && steps.some((step) => step.kind === "dropTable" || step.kind === "dropColumn" || step.kind === "dropJoinTable" || step.kind === "dropEnum"))
10054
10390
  states.push({ identity: object.identity, ownership, state: "unavailable", reason: "dependency-inspection-unavailable", differences: [] });
@@ -10062,7 +10398,7 @@ async function planSchemaMigration(params) {
10062
10398
  for (const address of addresses) {
10063
10399
  const value = JSON.parse(String(address));
10064
10400
  if (!Array.isArray(value) || !Array.isArray(value[2]))
10065
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency address" });
10401
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency address" });
10066
10402
  const names = value[2];
10067
10403
  if (typeof names[0] !== "string" || typeof names[1] !== "string")
10068
10404
  continue;
@@ -10074,22 +10410,23 @@ async function planSchemaMigration(params) {
10074
10410
  }
10075
10411
  }
10076
10412
  if (dialect === "sqlite" && ownership === "unknown" && object.identity.kind === "generatedColumn" && invalid.rebuilt.has(object.identity.table.name))
10077
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "SQLite generated-column preservation requires an independent ownership declaration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
10078
- const removed = collateral || removals.some((identity) => sqlObjectKey17({ identity }) === key);
10413
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "SQLite generated-column preservation requires an independent ownership declaration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
10414
+ const removed = collateral || removals.some((identity) => sqlObjectKey18({ identity }) === key);
10079
10415
  if (removed && externalOwner !== undefined)
10080
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Automatic or collateral DDL cannot remove externally owned objects", meta: { reason: "external-owned-removal", owner: externalOwner, identity: object.identity } });
10416
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Automatic or collateral DDL cannot remove externally owned objects", meta: { reason: "external-owned-removal", owner: externalOwner, identity: object.identity } });
10081
10417
  if (removed && ownership === "migration")
10082
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Automatic DDL would remove a migration-owned object", meta: { reason: "migration-owned-removal", identity: object.identity } });
10418
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Automatic DDL would remove a migration-owned object", meta: { reason: "migration-owned-removal", identity: object.identity } });
10083
10419
  if (removed && ownership === "unknown" && comparisonInventory.includes(object) && object.backingConstraint === undefined)
10084
10420
  unmanagedRemovals.push(object.identity);
10085
10421
  }
10086
10422
  const invalidated = new Set([...invalid.rebuilt, ...invalid.dropped]);
10087
- const installed = await probeInstalledExtensionState({ executor, dialect, artifacts });
10088
10423
  const triggers = installed.triggers === undefined ? undefined : new Set(installed.triggers);
10089
10424
  for (const artifact of artifacts)
10090
10425
  if (artifact.owns.kind === "trigger" && invalidated.has(artifact.owns.table))
10091
10426
  triggers?.delete(artifact.owns.name);
10092
- const missing = planMissingArtifacts({ introspected: physical, artifacts, installed: { ...installed, ...triggers === undefined ? {} : { triggers } } });
10427
+ const absentArtifacts = planMissingArtifacts({ introspected: physical, artifacts, installed: { ...installed, ...triggers === undefined ? {} : { triggers } } });
10428
+ const absentIds = new Set(absentArtifacts.map((artifact) => artifact.id));
10429
+ const missing = artifacts.filter((artifact) => absentIds.has(artifact.id) || sqliteFts?.forcedArtifacts.has(artifact.id));
10093
10430
  const declaredPrevious = params.previousDeclarations ?? previous?.schema;
10094
10431
  const previousDeclarations = declaredPrevious === undefined || external === undefined ? declaredPrevious : projectExternalSchema({ schema: declaredPrevious, external });
10095
10432
  const previousArtifacts = previousDeclarations === undefined ? undefined : collectExtensionArtifacts({ schema: previousDeclarations, dialect, extensions: params.extensions ?? [] });
@@ -10127,11 +10464,11 @@ async function planSchemaMigration(params) {
10127
10464
  continue;
10128
10465
  const live = await inspectSqlObject({ executor, dialect, identity });
10129
10466
  if (live.state !== "present" || identity.kind !== "columnDefault" && typeof live.definition.facts.columnDefinition !== "string")
10130
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Cannot read the verified SQLite column definition for preservation", meta: { reason: "sqlite-preservation-unavailable", identity } });
10467
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Cannot read the verified SQLite column definition for preservation", meta: { reason: "sqlite-preservation-unavailable", identity } });
10131
10468
  if (identity.kind === "columnDefault") {
10132
10469
  const definition = live.definition.facts.definition;
10133
10470
  if (definition !== null && typeof definition !== "string")
10134
- throw new VibeError48({ code: "VIBE_MIGRATION", message: "Cannot preserve SQLite default", meta: { identity } });
10471
+ throw new VibeError50({ code: "VIBE_MIGRATION", message: "Cannot preserve SQLite default", meta: { identity } });
10135
10472
  const defaults = preservedDefaults.get(identity.table.name) ?? new Map;
10136
10473
  defaults.set(identity.column, definition);
10137
10474
  preservedDefaults.set(identity.table.name, defaults);
@@ -10152,25 +10489,31 @@ async function planSchemaMigration(params) {
10152
10489
  const expression = defaults.get(column);
10153
10490
  return { ...rest, ...typeof expression === "string" ? { default: { kind: "dbgenerated", sql: expression } } : {} };
10154
10491
  }) })) };
10155
- const core = renderSteps({ steps, dialect, from: dialect === "sqlite" ? physical : from, to: dialect === "mysql" ? { ...target, migrationObjects: protectedObjects } : target, sqliteRestore: restore, sqliteColumns: preservedColumns });
10492
+ const core = steps.length === 0 ? [] : renderSteps({ steps, dialect, from: dialect === "sqlite" ? physical : from, to: dialect === "mysql" ? { ...target, migrationObjects: protectedObjects } : target, sqliteRestore: restore, sqliteColumns: preservedColumns });
10156
10493
  const planned = new Set([...missing, ...rebuilt].map((artifact) => artifact.id));
10157
- const sql = [
10158
- ...artifactCreateSql({ artifacts: missing.filter((artifact) => artifact.owns.kind === "postgresExtension") }),
10159
- ...core,
10494
+ const extensionSql = [
10495
+ ...sqliteFts?.before ?? [],
10160
10496
  ...artifactDropSql({ artifacts: rebuilt }),
10161
- ...artifactCreateSql({ artifacts: collected.objects.filter((artifact) => planned.has(artifact.id) && !(artifact.owns.kind === "trigger" && invalid.rebuilt.has(artifact.owns.table))) }),
10497
+ ...artifactCreateSql({ artifacts: collected.objects.filter((artifact) => planned.has(artifact.id) && (!(artifact.owns.kind === "trigger" && invalid.rebuilt.has(artifact.owns.table)) || sqliteFts?.forcedArtifacts.has(artifact.id))) }),
10498
+ ...sqliteFts?.after ?? [],
10162
10499
  ...creates
10163
10500
  ];
10501
+ const atomicFts = dialect === "sqlite" && sqliteFts?.checks.some((check) => check.changes) === true && usesSelfManagedTransactions({ sql: core });
10502
+ const setupSql = artifactCreateSql({ artifacts: missing.filter((artifact) => artifact.owns.kind === "postgresExtension") });
10503
+ const sql = [...setupSql, ...core, ...atomicFts ? ["BEGIN;"] : [], ...extensionSql, ...atomicFts ? ["COMMIT;"] : []];
10164
10504
  if (sql.length === 0) {
10165
10505
  const definitions = [...previous?.definitions ?? []];
10166
10506
  for (const object of objects)
10167
- if (object.expected !== undefined && !definitions.some((definition) => sqlObjectKey17({ identity: definition.identity }) === sqlObjectKey17({ identity: object.identity })))
10507
+ if (object.expected !== undefined && !definitions.some((definition) => sqlObjectKey18({ identity: definition.identity }) === sqlObjectKey18({ identity: object.identity })))
10168
10508
  definitions.push(object.expected);
10169
10509
  const report = await verifyMigrationState({ executor, dialect, expected: { schema, definitions }, ...params.probe === undefined ? {} : { probe: params.probe } });
10170
10510
  states.push(...report.objects);
10171
10511
  }
10172
- const blocked = states.some((state) => state.reason === "dependency-inspection-unavailable" || state.ownership !== "unknown" && state.reason !== "legacy-definition-required" && (state.state === "unavailable" || state.state === "changed" || state.state === "missing" && sql.length === 0));
10173
- return { physical, from, to: schema, namespace, steps, objects: states, unmanagedRemovals, blocked, sql: blocked ? [] : sql, rebuiltArtifacts };
10512
+ const blocked = states.some((state) => state.reason === "dependency-inspection-unavailable" || state.ownership !== "unknown" && state.reason !== "legacy-definition-required" && state.reason !== "sqlite-fts-update-upgrade" && (state.state === "unavailable" || state.state === "changed" || state.state === "missing" && sql.length === 0));
10513
+ const plan = { physical, from, to: schema, namespace, steps, objects: states, unmanagedRemovals, blocked, sql: blocked ? [] : sql, rebuiltArtifacts };
10514
+ if (sqliteFts !== undefined)
10515
+ retainSqliteFtsPlan({ plan, fts: sqliteFts, ...atomicFts ? { transaction: { begin: setupSql.length + core.length, commit: sql.length - 1 } } : {} });
10516
+ return plan;
10174
10517
  }
10175
10518
 
10176
10519
  // packages/migrate/src/replay-plan.ts
@@ -10196,13 +10539,13 @@ async function planReplayMigration(params) {
10196
10539
  moduleHeads.set(migration.moduleOrigin.moduleId, migration);
10197
10540
  const immutableObjects = new Map([...moduleHeads.values()].flatMap((migration) => {
10198
10541
  const state = migration.moduleOrigin.source.desiredState.to;
10199
- return (state.schema.migrationObjects ?? []).map((object) => [sqlObjectKey18({ identity: object.identity }), object.expected ?? state.definitions.find((definition) => sqlObjectKey18({ identity: definition.identity }) === sqlObjectKey18({ identity: object.identity }))]);
10542
+ return (state.schema.migrationObjects ?? []).map((object) => [sqlObjectKey19({ identity: object.identity }), object.expected ?? state.definitions.find((definition) => sqlObjectKey19({ identity: definition.identity }) === sqlObjectKey19({ identity: object.identity }))]);
10200
10543
  }));
10201
10544
  const authored = reference.dialect === "postgres" ? (await resolvePostgresTypeIdentities({ executor, schema: params.schema, previous: reference.state.schema, mode: "verify" })).schema : params.schema;
10202
- const compared = await compareReplayMigrationObjects({ executor, reference, schema: { ...authored, migrationObjects: (authored.migrationObjects ?? []).filter((object) => !immutableObjects.has(sqlObjectKey18({ identity: object.identity }))) } });
10203
- const comparedObjects = new Map((compared.migrationObjects ?? []).map((object) => [sqlObjectKey18({ identity: object.identity }), object]));
10545
+ const compared = await compareReplayMigrationObjects({ executor, reference, schema: { ...authored, migrationObjects: (authored.migrationObjects ?? []).filter((object) => !immutableObjects.has(sqlObjectKey19({ identity: object.identity }))) } });
10546
+ const comparedObjects = new Map((compared.migrationObjects ?? []).map((object) => [sqlObjectKey19({ identity: object.identity }), object]));
10204
10547
  const schema = { ...compared, migrationObjects: (params.schema.migrationObjects ?? []).map((object) => {
10205
- const key = sqlObjectKey18({ identity: object.identity });
10548
+ const key = sqlObjectKey19({ identity: object.identity });
10206
10549
  if (!immutableObjects.has(key))
10207
10550
  return comparedObjects.get(key);
10208
10551
  const expected = immutableObjects.get(key);
@@ -10221,57 +10564,57 @@ async function planReplayMigration(params) {
10221
10564
  const identity = removalIdentity({ step, namespace: plan.namespace });
10222
10565
  return identity === undefined ? [] : [identity];
10223
10566
  })])
10224
- removals.set(sqlObjectKey18({ identity }), identity);
10225
- const declared = new Set((schema.migrationObjects ?? []).map((object) => sqlObjectKey18({ identity: object.identity })));
10567
+ removals.set(sqlObjectKey19({ identity }), identity);
10568
+ const declared = new Set((schema.migrationObjects ?? []).map((object) => sqlObjectKey19({ identity: object.identity })));
10226
10569
  const historicalObjects = new Map;
10227
10570
  for (const migration of params.migrations)
10228
10571
  for (const object of migration.desiredState?.to.schema.migrationObjects ?? []) {
10229
10572
  if (external === undefined || externalObjectOwner({ identity: object.identity, external }) === undefined)
10230
- historicalObjects.set(sqlObjectKey18({ identity: object.identity }), object.identity);
10573
+ historicalObjects.set(sqlObjectKey19({ identity: object.identity }), object.identity);
10231
10574
  }
10232
- const present = new Set(reference.inventory.map((object) => sqlObjectKey18({ identity: object.identity })));
10233
- const retainedObjects = reference.inventory.filter((object) => !declared.has(sqlObjectKey18({ identity: object.identity })) && !removals.has(sqlObjectKey18({ identity: object.identity })) && (object.identity.kind === "function" || object.identity.kind === "trigger" || historicalObjects.has(sqlObjectKey18({ identity: object.identity })))).map((object) => object.identity);
10575
+ const present = new Set(reference.inventory.map((object) => sqlObjectKey19({ identity: object.identity })));
10576
+ const retainedObjects = reference.inventory.filter((object) => !declared.has(sqlObjectKey19({ identity: object.identity })) && !removals.has(sqlObjectKey19({ identity: object.identity })) && (object.identity.kind === "function" || object.identity.kind === "trigger" || historicalObjects.has(sqlObjectKey19({ identity: object.identity })))).map((object) => object.identity);
10234
10577
  const removedRegistrations = [...historicalObjects].filter(([key]) => present.has(key) && !declared.has(key)).map(([, identity]) => identity);
10235
10578
  if (removedRegistrations.length > 0)
10236
- throw new VibeError49({ code: "VIBE_MIGRATION", message: "Custom registrations removed from declarations still exist in replayed history. Restore the registration or supply reviewed SQL removing the exact named objects before generating again.", meta: { reason: "migration-object-removal-review-required", shadowReusable: true, objects: removedRegistrations } });
10579
+ throw new VibeError51({ code: "VIBE_MIGRATION", message: "Custom registrations removed from declarations still exist in replayed history. Restore the registration or supply reviewed SQL removing the exact named objects before generating again.", meta: { reason: "migration-object-removal-review-required", shadowReusable: true, objects: removedRegistrations } });
10237
10580
  if (external !== undefined)
10238
10581
  assertExternalOwnershipClaims({ external, identities: [...removals.values()] });
10239
10582
  return { ...plan, requiredRemovals: [...removals.values()], retainedObjects, securityChanges: securityChangesOf({ steps: plan.steps }), replayRemovals: params.migrations.flatMap((migration) => migration.desiredState?.unmanagedRemovals ?? []).filter((identity) => external === undefined || externalObjectOwner({ identity, external }) === undefined) };
10240
10583
  }
10241
10584
  function assertReplayRemovals(params) {
10242
- const wanted = new Set([...params.plan.requiredRemovals, ...params.plan.replayRemovals].map((identity) => sqlObjectKey18({ identity })));
10243
- const keys = params.approved.map((identity) => sqlObjectKey18({ identity }));
10585
+ const wanted = new Set([...params.plan.requiredRemovals, ...params.plan.replayRemovals].map((identity) => sqlObjectKey19({ identity })));
10586
+ const keys = params.approved.map((identity) => sqlObjectKey19({ identity }));
10244
10587
  if (new Set(keys).size !== keys.length)
10245
- throw new VibeError49({ code: "VIBE_MIGRATION", message: "Removal decisions contain duplicate identities", meta: { reason: "removal-decision-invalid", shadowReusable: true } });
10246
- const unused = params.approved.filter((identity) => !wanted.has(sqlObjectKey18({ identity })));
10588
+ throw new VibeError51({ code: "VIBE_MIGRATION", message: "Removal decisions contain duplicate identities", meta: { reason: "removal-decision-invalid", shadowReusable: true } });
10589
+ const unused = params.approved.filter((identity) => !wanted.has(sqlObjectKey19({ identity })));
10247
10590
  if (unused.length > 0)
10248
- throw new VibeError49({ code: "VIBE_MIGRATION", message: "Removal decisions do not identify proposed removals. Custom objects without a generated removal require a reviewed hand-authored SQL migration.", meta: { reason: "removal-decision-unused", shadowReusable: true, objects: unused } });
10591
+ throw new VibeError51({ code: "VIBE_MIGRATION", message: "Removal decisions do not identify proposed removals. Custom objects without a generated removal require a reviewed hand-authored SQL migration.", meta: { reason: "removal-decision-unused", shadowReusable: true, objects: unused } });
10249
10592
  assertNamedRemovals({ removals: params.plan.requiredRemovals, approved: params.approved, acceptIntegrityLoss: params.acceptIntegrityLoss });
10250
10593
  }
10251
10594
  // packages/migrate/src/baseline.ts
10252
- import { VibeError as VibeError52, sqlObjectKey as sqlObjectKey20 } from "@vibeorm/schema";
10595
+ import { VibeError as VibeError54, sqlObjectKey as sqlObjectKey21 } from "@vibeorm/schema";
10253
10596
 
10254
10597
  // packages/migrate/src/reference-state.ts
10255
- import { VibeError as VibeError50, sqlObjectKey as sqlObjectKey19 } from "@vibeorm/schema";
10598
+ import { VibeError as VibeError52, sqlObjectKey as sqlObjectKey20 } from "@vibeorm/schema";
10256
10599
  async function captureMigrationReference(params) {
10257
10600
  const namespace = await migrationNamespace({ executor: params.referenceExecutor, dialect: params.dialect });
10258
10601
  const identities = [...schemaObjectIdentities({ schema: params.schema, dialect: params.dialect, namespace }), ...(params.schema.migrationObjects ?? []).map((object) => object.identity)];
10259
10602
  const definitions = [];
10260
- for (const identity of new Map(identities.map((identity) => [sqlObjectKey19({ identity }), identity])).values()) {
10603
+ for (const identity of new Map(identities.map((identity) => [sqlObjectKey20({ identity }), identity])).values()) {
10261
10604
  if (identity.kind === "namespace")
10262
10605
  continue;
10263
- if (!params.schema.migrationObjects?.some((object) => sqlObjectKey19({ identity: object.identity }) === sqlObjectKey19({ identity })) && !(identity.kind === "constraint" && (identity.constraintType === "check" || identity.constraintType === "exclusion")))
10606
+ if (!params.schema.migrationObjects?.some((object) => sqlObjectKey20({ identity: object.identity }) === sqlObjectKey20({ identity })) && !(identity.kind === "constraint" && (identity.constraintType === "check" || identity.constraintType === "exclusion")))
10264
10607
  continue;
10265
10608
  const result = await inspectSqlObject({ executor: params.referenceExecutor, dialect: params.dialect, identity });
10266
10609
  if (result.state !== "present")
10267
- throw new VibeError50({ code: "VIBE_MIGRATION", message: "Independent reference object is missing or unavailable", meta: { reason: "reference-unavailable", identity, state: result.state } });
10610
+ throw new VibeError52({ code: "VIBE_MIGRATION", message: "Independent reference object is missing or unavailable", meta: { reason: "reference-unavailable", identity, state: result.state } });
10268
10611
  definitions.push(result.definition);
10269
10612
  }
10270
10613
  let schema = params.schema;
10271
10614
  if (params.dialect === "postgres" && schema.models.some((model) => model.fields.some((field) => field.isUnsupported === true))) {
10272
10615
  const resolved = await resolvePostgresTypeIdentities({ executor: params.referenceExecutor, schema, mode: "reference" });
10273
10616
  if (resolved.unavailable.length > 0)
10274
- throw new VibeError50({ code: "VIBE_MIGRATION", message: "Resolve unsupported types in the independent reference before capture", meta: { reason: "type-reference-required", columns: resolved.unavailable } });
10617
+ throw new VibeError52({ code: "VIBE_MIGRATION", message: "Resolve unsupported types in the independent reference before capture", meta: { reason: "type-reference-required", columns: resolved.unavailable } });
10275
10618
  const physical = await introspectPhysicalPostgres({ executor: params.referenceExecutor, namespaces: ["public", ...schemaNamespaces({ schema })] });
10276
10619
  for (const model of resolved.schema.models)
10277
10620
  for (const field of model.fields) {
@@ -10279,7 +10622,7 @@ async function captureMigrationReference(params) {
10279
10622
  continue;
10280
10623
  const actual = physical.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace }) === physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace }))?.fields.find((entry) => entry.name === (field.dbName ?? field.name));
10281
10624
  if (actual === undefined || columnTypeKey({ field }) !== columnTypeKey({ field: actual }))
10282
- throw new VibeError50({ code: "VIBE_MIGRATION", message: "Independent reference column does not match its declared PostgreSQL type", meta: { reason: "reference-type-mismatch", table: model.dbName ?? model.name, column: field.dbName ?? field.name } });
10625
+ throw new VibeError52({ code: "VIBE_MIGRATION", message: "Independent reference column does not match its declared PostgreSQL type", meta: { reason: "reference-type-mismatch", table: model.dbName ?? model.name, column: field.dbName ?? field.name } });
10283
10626
  }
10284
10627
  schema = resolved.schema;
10285
10628
  }
@@ -10287,13 +10630,13 @@ async function captureMigrationReference(params) {
10287
10630
  }
10288
10631
 
10289
10632
  // packages/migrate/src/compile-desired-state.ts
10290
- import { VibeError as VibeError51, qualifiedSqlName as qualifiedSqlName13 } from "@vibeorm/schema";
10633
+ import { VibeError as VibeError53, qualifiedSqlName as qualifiedSqlName14 } from "@vibeorm/schema";
10291
10634
  import { renderExclusionSetup as renderExclusionSetup2, getSqlDialect as getSqlDialect16, renderMigrationServerVersion as renderMigrationServerVersion2 } from "@vibeorm/sql";
10292
10635
  async function compileDesiredState(params) {
10293
10636
  if (params.dialect !== "postgres")
10294
- throw new VibeError51({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Rollback declaration compilation requires PostgreSQL", meta: { feature: "desiredStateCompilation", dialect: params.dialect } });
10637
+ throw new VibeError53({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Rollback declaration compilation requires PostgreSQL", meta: { feature: "desiredStateCompilation", dialect: params.dialect } });
10295
10638
  if (params.steps.some((step) => step.kind === "alterColumn" || step.kind === "addColumn" || step.kind === "addEnumValue"))
10296
- throw new VibeError51({ code: "VIBE_MIGRATION", message: "CHECK compilation after column or enum changes requires an independently built reference with the new type context", meta: { reason: "reference-required" } });
10639
+ throw new VibeError53({ code: "VIBE_MIGRATION", message: "CHECK compilation after column or enum changes requires an independently built reference with the new type context", meta: { reason: "reference-required" } });
10297
10640
  const definitions = (params.schema.migrationObjects ?? []).flatMap((object) => object.expected === undefined ? [] : [object.expected]);
10298
10641
  await params.executor({ text: "SAVEPOINT vibeorm_desired_state" });
10299
10642
  try {
@@ -10302,7 +10645,7 @@ async function compileDesiredState(params) {
10302
10645
  await params.executor({ text });
10303
10646
  const models = [];
10304
10647
  for (const model of params.schema.models) {
10305
- const table = qualifiedSqlName13({ name: model.dbName ?? model.name, namespace: model.namespace });
10648
+ const table = qualifiedSqlName14({ name: model.dbName ?? model.name, namespace: model.namespace });
10306
10649
  const exclusions = [];
10307
10650
  for (const exclusion of model.exclusions ?? []) {
10308
10651
  for (const text of renderExclusionSetup2({ dialect: getSqlDialect16({ dialect: "postgres" }), exclusion }))
@@ -10316,7 +10659,7 @@ async function compileDesiredState(params) {
10316
10659
  const query = renderMigrationServerVersion2({ dialect: getSqlDialect16({ dialect: params.dialect }) });
10317
10660
  const version = (await params.executor({ text: query.text, values: [] }))[0]?.version;
10318
10661
  if (typeof version !== "string")
10319
- throw new VibeError51({ code: "VIBE_MIGRATION", message: "Cannot determine definition normalization version" });
10662
+ throw new VibeError53({ code: "VIBE_MIGRATION", message: "Cannot determine definition normalization version" });
10320
10663
  return { ...createDesiredState({ schema: { ...params.schema, models }, dialect: params.dialect, definitions }), normalization: { format: "catalog-v1", dialect: params.dialect, serverVersion: version } };
10321
10664
  } finally {
10322
10665
  await params.executor({ text: "ROLLBACK TO SAVEPOINT vibeorm_desired_state" });
@@ -10329,19 +10672,19 @@ function generateBaselineMigration(params) {
10329
10672
  const objects = orderedMigrationObjects({ objects: params.schema.migrationObjects ?? [] });
10330
10673
  for (const object of objects)
10331
10674
  if (object.dialect !== params.dialect)
10332
- throw new VibeError52({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Baseline object ${object.id} requires ${object.dialect}`, meta: { identity: object.identity, dialect: params.dialect } });
10675
+ throw new VibeError54({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Baseline object ${object.id} requires ${object.dialect}`, meta: { identity: object.identity, dialect: params.dialect } });
10333
10676
  const prerequisites = objects.filter((object) => object.identity.kind === "postgresExtension" || object.identity.kind === "enum" || object.identity.kind === "sequence");
10334
- const prerequisiteKeys = new Set(prerequisites.map((object) => sqlObjectKey20({ identity: object.identity })));
10677
+ const prerequisiteKeys = new Set(prerequisites.map((object) => sqlObjectKey21({ identity: object.identity })));
10335
10678
  for (const object of prerequisites) {
10336
- const unresolved = object.dependsOn.filter((identity) => !prerequisiteKeys.has(sqlObjectKey20({ identity })));
10679
+ const unresolved = object.dependsOn.filter((identity) => !prerequisiteKeys.has(sqlObjectKey21({ identity })));
10337
10680
  if (unresolved.length > 0)
10338
- throw new VibeError52({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Baseline prerequisite depends on objects created later. Supply reviewed initial SQL with an explicit dependency order.", meta: { reason: "baseline-prerequisite-order", identity: object.identity, dependencies: unresolved } });
10681
+ throw new VibeError54({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Baseline prerequisite depends on objects created later. Supply reviewed initial SQL with an explicit dependency order.", meta: { reason: "baseline-prerequisite-order", identity: object.identity, dependencies: unresolved } });
10339
10682
  }
10340
- const schema = { ...params.schema, migrationObjects: objects.filter((object) => !prerequisiteKeys.has(sqlObjectKey20({ identity: object.identity }))) };
10683
+ const schema = { ...params.schema, migrationObjects: objects.filter((object) => !prerequisiteKeys.has(sqlObjectKey21({ identity: object.identity }))) };
10341
10684
  const generated = generateMigration({ from: { datasource: schema.datasource, models: [], enums: [], extensions: [] }, to: schema, name: params.name, dialect: params.dialect, extensions: params.extensions ?? [] });
10342
10685
  const sql = splitSqlStatements({ sql: joinSqlStatements({ sql: [...prerequisites.flatMap((object) => object.create), ...generated.sql] }), dialect: params.dialect });
10343
10686
  if (sql.length === 0)
10344
- throw new VibeError52({ code: "VIBE_MIGRATION", message: "A baseline must create declared database objects", meta: { reason: "baseline-empty" } });
10687
+ throw new VibeError54({ code: "VIBE_MIGRATION", message: "A baseline must create declared database objects", meta: { reason: "baseline-empty" } });
10345
10688
  return {
10346
10689
  name: generated.name,
10347
10690
  sql,
@@ -10353,8 +10696,8 @@ function generateBaselineMigration(params) {
10353
10696
  async function verifyBaselineDeclarations(params) {
10354
10697
  const captured = await captureMigrationReference({ referenceExecutor: params.executor, schema: params.schema, dialect: params.dialect });
10355
10698
  const authored = (params.schema.migrationObjects ?? []).flatMap((object) => object.expected === undefined ? [] : [object.expected]);
10356
- const customKeys = new Set((params.schema.migrationObjects ?? []).map((object) => sqlObjectKey20({ identity: object.identity })));
10357
- const customDefinitions = captured.definitions.filter((definition) => customKeys.has(sqlObjectKey20({ identity: definition.identity })) && !authored.some((entry) => sqlObjectKey20({ identity: entry.identity }) === sqlObjectKey20({ identity: definition.identity })));
10699
+ const customKeys = new Set((params.schema.migrationObjects ?? []).map((object) => sqlObjectKey21({ identity: object.identity })));
10700
+ const customDefinitions = captured.definitions.filter((definition) => customKeys.has(sqlObjectKey21({ identity: definition.identity })) && !authored.some((entry) => sqlObjectKey21({ identity: entry.identity }) === sqlObjectKey21({ identity: definition.identity })));
10358
10701
  const verify = async () => {
10359
10702
  const compiled = params.dialect === "postgres" ? await compileDesiredState({ ...params, schema: captured.schema, steps: [], probe: "rollback" }) : { schema: captured.schema, definitions: authored };
10360
10703
  const expected = { ...compiled, definitions: [...compiled.definitions, ...customDefinitions] };
@@ -10370,48 +10713,48 @@ async function verifyBaselineDeclarations(params) {
10370
10713
  async function verifyBaselineTarget(params) {
10371
10714
  const { reference } = params;
10372
10715
  if (reference.unavailable.length > 0 || reference.inventory.some((object) => object.inspectionUnavailable !== undefined))
10373
- throw new VibeError52({ code: "VIBE_MIGRATION", message: "Baseline reference contains objects that cannot be inspected. Supply supported reviewed declarations before adoption.", meta: { reason: "baseline-reference-unavailable", objects: reference.unavailable, inventory: reference.inventory.filter((object) => object.inspectionUnavailable !== undefined) } });
10716
+ throw new VibeError54({ code: "VIBE_MIGRATION", message: "Baseline reference contains objects that cannot be inspected. Supply supported reviewed declarations before adoption.", meta: { reason: "baseline-reference-unavailable", objects: reference.unavailable, inventory: reference.inventory.filter((object) => object.inspectionUnavailable !== undefined) } });
10374
10717
  const comparison = await verifyMigrationReference({ executor: params.executor, dialect: reference.dialect, reference, referenceExecutor: params.referenceExecutor }).catch((cause) => {
10375
- if (cause instanceof VibeError52 && cause.meta.reason === "verification-rls-reference-required")
10376
- throw new VibeError52({ code: cause.code, message: cause.message, meta: { reason: "baseline-rls-reference-required" }, cause });
10718
+ if (cause instanceof VibeError54 && cause.meta.reason === "verification-rls-reference-required")
10719
+ throw new VibeError54({ code: cause.code, message: cause.message, meta: { reason: "baseline-rls-reference-required" }, cause });
10377
10720
  throw cause;
10378
10721
  });
10379
10722
  const security = comparison.objects.filter((object) => object.reason === "native-rls-mismatch");
10380
10723
  if (security.length > 0)
10381
- throw new VibeError52({ code: "VIBE_MIGRATION", message: "Target native RLS policies or flags differ from the independent baseline. Restore the reviewed security declarations before recording; no target policies were changed.", meta: { reason: "baseline-rls-mismatch", shadowReusable: true, expected: security.flatMap((object) => object.differences.flatMap((difference) => difference.expected === undefined ? [] : [difference.expected])), actual: security.flatMap((object) => object.differences.flatMap((difference) => difference.actual === undefined ? [] : [difference.actual])) } });
10724
+ throw new VibeError54({ code: "VIBE_MIGRATION", message: "Target native RLS policies or flags differ from the independent baseline. Restore the reviewed security declarations before recording; no target policies were changed.", meta: { reason: "baseline-rls-mismatch", shadowReusable: true, expected: security.flatMap((object) => object.differences.flatMap((difference) => difference.expected === undefined ? [] : [difference.expected])), actual: security.flatMap((object) => object.differences.flatMap((difference) => difference.actual === undefined ? [] : [difference.actual])) } });
10382
10725
  if (comparison.unavailable.length > 0)
10383
- throw new VibeError52({ code: "VIBE_MIGRATION", message: "Baseline comparison contains objects whose definitions cannot be verified in the target context; use a supported reference before adoption.", meta: { reason: "baseline-target-unavailable", shadowReusable: true, objects: comparison.unavailable } });
10726
+ throw new VibeError54({ code: "VIBE_MIGRATION", message: "Baseline comparison contains objects whose definitions cannot be verified in the target context; use a supported reference before adoption.", meta: { reason: "baseline-target-unavailable", shadowReusable: true, objects: comparison.unavailable } });
10384
10727
  const report = { inSync: comparison.objects.every((object) => object.state === "matching"), objects: comparison.objects };
10385
10728
  assertMigrationState({ report });
10386
10729
  const unmanaged = comparison.unmanaged.map((object) => object.identity);
10387
- const extras = new Set(unmanaged.map((identity) => sqlObjectKey20({ identity })));
10730
+ const extras = new Set(unmanaged.map((identity) => sqlObjectKey21({ identity })));
10388
10731
  const exclusions = params.excludeUnmanaged ?? [];
10389
- const excluded = new Set(exclusions.map((identity) => sqlObjectKey20({ identity })));
10390
- if (excluded.size !== exclusions.length || exclusions.some((identity) => !extras.has(sqlObjectKey20({ identity }))))
10391
- throw new VibeError52({ code: "VIBE_CONFIG", message: "Baseline exclusions must uniquely name actual unmanaged target objects, never declared objects", meta: { reason: "baseline-exclusion-invalid", shadowReusable: true, exclusions } });
10392
- const unreviewed = unmanaged.filter((identity) => !excluded.has(sqlObjectKey20({ identity })));
10732
+ const excluded = new Set(exclusions.map((identity) => sqlObjectKey21({ identity })));
10733
+ if (excluded.size !== exclusions.length || exclusions.some((identity) => !extras.has(sqlObjectKey21({ identity }))))
10734
+ throw new VibeError54({ code: "VIBE_CONFIG", message: "Baseline exclusions must uniquely name actual unmanaged target objects, never declared objects", meta: { reason: "baseline-exclusion-invalid", shadowReusable: true, exclusions } });
10735
+ const unreviewed = unmanaged.filter((identity) => !excluded.has(sqlObjectKey21({ identity })));
10393
10736
  if (unreviewed.length > 0)
10394
- throw new VibeError52({ code: "VIBE_MIGRATION", message: "Target has unmanaged objects. Register their SQL or explicitly exclude each identity with --exclude-unmanaged; no target objects were removed.", meta: { reason: "baseline-unmanaged-objects", shadowReusable: true, objects: unreviewed } });
10737
+ throw new VibeError54({ code: "VIBE_MIGRATION", message: "Target has unmanaged objects. Register their SQL or explicitly exclude each identity with --exclude-unmanaged; no target objects were removed.", meta: { reason: "baseline-unmanaged-objects", shadowReusable: true, objects: unreviewed } });
10395
10738
  return { ...report, unmanaged };
10396
10739
  }
10397
10740
  // packages/migrate/src/baseline-record.ts
10398
- import { VibeError as VibeError53 } from "@vibeorm/schema";
10741
+ import { VibeError as VibeError55 } from "@vibeorm/schema";
10399
10742
  import { getSqlDialect as getSqlDialect17, renderStatement } from "@vibeorm/sql";
10400
10743
  function transactionFailure2(params) {
10401
- if (params.cause instanceof VibeError53)
10744
+ if (params.cause instanceof VibeError55)
10402
10745
  return params.cause;
10403
- return new VibeError53({ code: "VIBE_MIGRATION", message: "SQLite baseline recording failed; inspect the journal and transaction outcome before retrying.", meta: { reason: "baseline-record-transaction-failed" }, cause: params.cause });
10746
+ return new VibeError55({ code: "VIBE_MIGRATION", message: "SQLite baseline recording failed; inspect the journal and transaction outcome before retrying.", meta: { reason: "baseline-record-transaction-failed" }, cause: params.cause });
10404
10747
  }
10405
10748
  async function recordBaselineMigration(params) {
10406
10749
  if (params.migration.moduleOrigin !== undefined)
10407
- throw new VibeError53({ code: "VIBE_MIGRATION", message: "Package module migrations must use the immutable module installation workflow, not baseline adoption", meta: { reason: "baseline-module-unsupported" } });
10750
+ throw new VibeError55({ code: "VIBE_MIGRATION", message: "Package module migrations must use the immutable module installation workflow, not baseline adoption", meta: { reason: "baseline-module-unsupported" } });
10408
10751
  validateMigrationReplay({ dialect: params.dialect, migrations: [params.migration], acceptSecurityChanges: params.acceptSecurityChanges });
10409
10752
  await withMigrationLock({ executor: params.executor, dialect: params.dialect, run: async (executor) => {
10410
10753
  const record = async () => {
10411
10754
  const assertEmpty = async () => {
10412
10755
  const history = await readMigrationHistory({ executor, migrations: [params.migration], dialect: params.dialect });
10413
10756
  if (history.recorded.length > 0)
10414
- throw new VibeError53({ code: "VIBE_MIGRATION", message: "Baseline recording requires an absent or empty migration journal. Use normal apply or explicit recovery for existing history.", meta: { reason: "baseline-journal-not-empty", shadowReusable: true, recorded: history.recorded.map((row) => ({ name: row.name, state: row.state })) } });
10757
+ throw new VibeError55({ code: "VIBE_MIGRATION", message: "Baseline recording requires an absent or empty migration journal. Use normal apply or explicit recovery for existing history.", meta: { reason: "baseline-journal-not-empty", shadowReusable: true, recorded: history.recorded.map((row) => ({ name: row.name, state: row.state })) } });
10415
10758
  };
10416
10759
  await assertEmpty();
10417
10760
  await params.verify({ executor });
@@ -10436,14 +10779,14 @@ async function recordBaselineMigration(params) {
10436
10779
  await executor({ text: "COMMIT" });
10437
10780
  }).then(undefined, async (cause) => {
10438
10781
  await executor({ text: "ROLLBACK" }).then(undefined, (rollback) => {
10439
- throw new VibeError53({ code: "VIBE_MIGRATION", message: "Baseline recording failed and its SQLite transaction could not be rolled back. Inspect the journal and connection state before retrying; the outcome is uncertain.", meta: { reason: "baseline-record-rollback-failed" }, cause: new AggregateError([cause, rollback], "Baseline recording and rollback failed") });
10782
+ throw new VibeError55({ code: "VIBE_MIGRATION", message: "Baseline recording failed and its SQLite transaction could not be rolled back. Inspect the journal and connection state before retrying; the outcome is uncertain.", meta: { reason: "baseline-record-rollback-failed" }, cause: new AggregateError([cause, rollback], "Baseline recording and rollback failed") });
10440
10783
  });
10441
10784
  throw transactionFailure2({ cause });
10442
10785
  });
10443
10786
  } });
10444
10787
  }
10445
10788
  // packages/migrate/src/down.ts
10446
- import { VibeError as VibeError54, sqlObjectKey as sqlObjectKey21 } from "@vibeorm/schema";
10789
+ import { VibeError as VibeError56, sqlObjectKey as sqlObjectKey22 } from "@vibeorm/schema";
10447
10790
  function buildRollbackTarget(params) {
10448
10791
  const { from, to, dialect } = params;
10449
10792
  const skipped = [];
@@ -10547,10 +10890,10 @@ function generateDownMigration(params) {
10547
10890
  }
10548
10891
  restoreSql.push(...entry.restore);
10549
10892
  }
10550
- const previousKeys = new Set((from.migrationObjects ?? []).map((object) => sqlObjectKey21({ identity: object.identity })));
10893
+ const previousKeys = new Set((from.migrationObjects ?? []).map((object) => sqlObjectKey22({ identity: object.identity })));
10551
10894
  const ownedDropSql = [];
10552
10895
  for (const object of orderedMigrationObjects({ objects: to.migrationObjects ?? [] }).reverse()) {
10553
- if (previousKeys.has(sqlObjectKey21({ identity: object.identity })))
10896
+ if (previousKeys.has(sqlObjectKey22({ identity: object.identity })))
10554
10897
  continue;
10555
10898
  if (object.identity.kind === "namespace") {
10556
10899
  skipped.push(`namespace ${object.identity.name} remains installed`);
@@ -10561,7 +10904,7 @@ function generateDownMigration(params) {
10561
10904
  continue;
10562
10905
  }
10563
10906
  if (object.drop.length === 0)
10564
- throw new VibeError54({ code: "VIBE_MIGRATION", message: `Migration object ${object.id} needs reviewed reverse SQL`, meta: { reason: "migration-object-reverse-required", id: object.id } });
10907
+ throw new VibeError56({ code: "VIBE_MIGRATION", message: `Migration object ${object.id} needs reviewed reverse SQL`, meta: { reason: "migration-object-reverse-required", id: object.id } });
10565
10908
  ownedDropSql.push(...object.drop);
10566
10909
  }
10567
10910
  return {
@@ -10574,11 +10917,11 @@ function generateDownMigration(params) {
10574
10917
  };
10575
10918
  }
10576
10919
  // packages/migrate/src/push.ts
10577
- import { VibeError as VibeError55, assertValidSchemaIR as assertValidSchemaIR4, providerToDialect as providerToDialect6, sqlObjectKey as sqlObjectKey22, unsupportedCapability as unsupportedCapability14 } from "@vibeorm/schema";
10920
+ import { VibeError as VibeError57, assertValidSchemaIR as assertValidSchemaIR4, providerToDialect as providerToDialect7, sqlObjectKey as sqlObjectKey23, unsupportedCapability as unsupportedCapability14 } from "@vibeorm/schema";
10578
10921
  import { DIALECT_CAPABILITIES as DIALECT_CAPABILITIES4 } from "@vibeorm/sql";
10579
10922
  async function push(params) {
10580
10923
  assertValidSchemaIR4({ schema: params.schema });
10581
- const dialect = providerToDialect6({ provider: params.schema.datasource.provider });
10924
+ const dialect = providerToDialect7({ provider: params.schema.datasource.provider });
10582
10925
  if (params.rebuildArtifacts === true && dialect !== "postgres")
10583
10926
  throw unsupportedCapability14({ dialect, feature: "rebuilding extension artifacts on push (rebuildArtifacts)", hint: "drop the extension's objects by hand and push again; the transactional rebuild exists on PostgreSQL only" });
10584
10927
  if (dialect !== "postgres")
@@ -10618,11 +10961,11 @@ async function pushChanges(params) {
10618
10961
  if (params.dryRun === true)
10619
10962
  return { steps, applied: [], planned: sql, securityChanges, objects: plan.objects, blocked: plan.blocked, unmanagedRemovals: plan.unmanagedRemovals };
10620
10963
  if (plan.blocked)
10621
- throw new VibeError55({ code: "VIBE_MIGRATION", message: "Schema push is blocked by unverified object definitions", meta: { reason: "migration-state-unverified", objects: plan.objects } });
10964
+ throw new VibeError57({ code: "VIBE_MIGRATION", message: "Schema push is blocked by unverified object definitions", meta: { reason: "migration-state-unverified", objects: plan.objects } });
10622
10965
  assertSecurityAcknowledgement({ securityChanges, ...params.acceptSecurityChanges === undefined ? {} : { acceptSecurityChanges: params.acceptSecurityChanges } });
10623
10966
  const destructive = steps.filter((step) => step.destructive);
10624
10967
  if (destructive.length > 0 && params.acceptDataLoss !== true)
10625
- throw new VibeError55({ code: "VIBE_MIGRATION", message: `push refused: ${destructive.length} destructive step(s) would lose data; pass acceptDataLoss: true: ${destructive.map((step) => describeStep({ step })).join("; ")}`, meta: { destructive: destructive.map((step) => describeStep({ step })) } });
10968
+ throw new VibeError57({ code: "VIBE_MIGRATION", message: `push refused: ${destructive.length} destructive step(s) would lose data; pass acceptDataLoss: true: ${destructive.map((step) => describeStep({ step })).join("; ")}`, meta: { destructive: destructive.map((step) => describeStep({ step })) } });
10626
10969
  assertRemovalAuthorization({
10627
10970
  steps,
10628
10971
  dialect,
@@ -10631,16 +10974,26 @@ async function pushChanges(params) {
10631
10974
  ...params.acceptIntegrityLoss === undefined ? {} : { acceptIntegrityLoss: params.acceptIntegrityLoss },
10632
10975
  ...params.removeUnmanaged === undefined ? {} : { removeUnmanaged: params.removeUnmanaged }
10633
10976
  });
10634
- const approved = new Set((params.removeUnmanaged ?? []).map((identity) => sqlObjectKey22({ identity })));
10635
- const unapproved = plan.unmanagedRemovals.filter((identity) => !approved.has(sqlObjectKey22({ identity })));
10977
+ const approved = new Set((params.removeUnmanaged ?? []).map((identity) => sqlObjectKey23({ identity })));
10978
+ const unapproved = plan.unmanagedRemovals.filter((identity) => !approved.has(sqlObjectKey23({ identity })));
10636
10979
  if (unapproved.length > 0)
10637
- throw new VibeError55({ code: "VIBE_MIGRATION", message: "DDL would remove unmanaged dependent objects; approve their exact identities", meta: { reason: "unmanaged-removal-required", objects: unapproved } });
10980
+ throw new VibeError57({ code: "VIBE_MIGRATION", message: "DDL would remove unmanaged dependent objects; approve their exact identities", meta: { reason: "unmanaged-removal-required", objects: unapproved } });
10638
10981
  const wrap = dialect !== "postgres" && DIALECT_CAPABILITIES4[dialect].transactionalDdl && !usesSelfManagedTransactions({ sql });
10982
+ const ftsTransaction = dialect === "sqlite" ? sqliteFtsTransaction({ plan }) : undefined;
10639
10983
  if (wrap)
10640
10984
  await executor({ text: "BEGIN" });
10641
10985
  try {
10642
- for (const statement of sql)
10986
+ if (dialect === "sqlite" && ftsTransaction === undefined)
10987
+ await assertSqliteFtsApplyState({ plan, executor });
10988
+ for (const [index, statement] of sql.entries()) {
10989
+ if (ftsTransaction?.commit === index)
10990
+ await verifySqliteFtsApplyState({ plan, executor });
10643
10991
  await executeStatement({ executor, statement, dialect });
10992
+ if (ftsTransaction?.begin === index)
10993
+ await assertSqliteFtsApplyState({ plan, executor });
10994
+ }
10995
+ if (dialect === "sqlite" && ftsTransaction === undefined)
10996
+ await verifySqliteFtsApplyState({ plan, executor });
10644
10997
  if ((schema.migrationObjects?.length ?? 0) > 0)
10645
10998
  assertMigrationState({ report: await verifyMigrationState({ executor, dialect, expected: createDesiredState({ schema: { ...schema, models: [] }, dialect }), ...params.external === undefined ? {} : { external: params.external } }) });
10646
10999
  if (wrap)
@@ -10652,7 +11005,7 @@ async function pushChanges(params) {
10652
11005
  } catch {}
10653
11006
  } else if (dialect === "sqlite")
10654
11007
  await cleanupSelfManagedFailure({ executor });
10655
- throw new VibeError55({ code: "VIBE_MIGRATION", message: `push failed while applying schema changes: ${error instanceof Error ? error.message : String(error)}`, meta: { statements: sql }, cause: error });
11008
+ throw new VibeError57({ code: "VIBE_MIGRATION", message: `push failed while applying schema changes: ${error instanceof Error ? error.message : String(error)}`, meta: { statements: sql }, cause: error });
10656
11009
  }
10657
11010
  return { steps, applied: sql, securityChanges, objects: plan.objects, desiredState: createDesiredState({ schema: plan.to, dialect }) };
10658
11011
  }
@@ -10667,11 +11020,11 @@ async function pull(params) {
10667
11020
  }
10668
11021
  }
10669
11022
  // packages/migrate/src/module-export.ts
10670
- import { VibeError as VibeError56, providerToDialect as providerToDialect7 } from "@vibeorm/schema";
11023
+ import { VibeError as VibeError58, providerToDialect as providerToDialect8 } from "@vibeorm/schema";
10671
11024
  function runtimeSchema(params) {
10672
11025
  const byName = (left, right) => left.name.localeCompare(right.name);
10673
11026
  return {
10674
- datasource: { provider: providerToDialect7({ provider: params.schema.datasource.provider }) },
11027
+ datasource: { provider: providerToDialect8({ provider: params.schema.datasource.provider }) },
10675
11028
  models: [...params.schema.models].sort(byName),
10676
11029
  enums: [...params.schema.enums].sort(byName),
10677
11030
  extensions: [...params.schema.extensions].sort(byName),
@@ -10682,13 +11035,13 @@ function emitModuleMigrationExport(params) {
10682
11035
  moduleMigrationFiles({ bundle: params.bundle, prefix: "export" });
10683
11036
  const origin = params.bundle.migrations.at(-1).moduleOrigin;
10684
11037
  if (!origin.contracts.includes(params.contract))
10685
- throw new VibeError56({ code: "VIBE_CONFIG", message: "The package runtime contract is not supported by its migration head", meta: { reason: "module-contract-mismatch", moduleId: origin.moduleId } });
11038
+ throw new VibeError58({ code: "VIBE_CONFIG", message: "The package runtime contract is not supported by its migration head", meta: { reason: "module-contract-mismatch", moduleId: origin.moduleId } });
10686
11039
  if (params.client !== undefined) {
10687
11040
  const { module, schema } = params.client;
10688
- if (module.id !== origin.moduleId || module.revision !== origin.revision || module.contract !== params.contract || providerToDialect7({ provider: schema.datasource.provider }) !== origin.dialect)
10689
- throw new VibeError56({ code: "VIBE_CONFIG", message: "The generated module identity, revision or contract differs from its migration head", meta: { reason: "module-client-head-mismatch", moduleId: origin.moduleId } });
11041
+ if (module.id !== origin.moduleId || module.revision !== origin.revision || module.contract !== params.contract || providerToDialect8({ provider: schema.datasource.provider }) !== origin.dialect)
11042
+ throw new VibeError58({ code: "VIBE_CONFIG", message: "The generated module identity, revision or contract differs from its migration head", meta: { reason: "module-client-head-mismatch", moduleId: origin.moduleId } });
10690
11043
  if (canonicalMigrationJson({ value: runtimeSchema({ schema }) }) !== canonicalMigrationJson({ value: runtimeSchema({ schema: origin.source.desiredState.to.schema }) }))
10691
- throw new VibeError56({ code: "VIBE_CONFIG", message: "The generated module schema differs from its immutable migration head", meta: { reason: "module-client-schema-mismatch", moduleId: origin.moduleId } });
11044
+ throw new VibeError58({ code: "VIBE_CONFIG", message: "The generated module schema differs from its immutable migration head", meta: { reason: "module-client-schema-mismatch", moduleId: origin.moduleId } });
10692
11045
  }
10693
11046
  const expectation = { moduleId: origin.moduleId, revision: origin.revision, artifactChecksum: origin.artifactChecksum, contract: params.contract };
10694
11047
  return [
@@ -10707,16 +11060,16 @@ export const moduleReadiness = Object.freeze(${JSON.stringify(expectation)});
10707
11060
  ];
10708
11061
  }
10709
11062
  // packages/migrate/src/module-readiness.ts
10710
- import { VibeError as VibeError57 } from "@vibeorm/schema";
11063
+ import { VibeError as VibeError59 } from "@vibeorm/schema";
10711
11064
  async function inspectModuleReadiness(params) {
10712
11065
  const ids = new Set;
10713
11066
  for (const expected of params.modules) {
10714
11067
  if (ids.has(expected.moduleId) || !expected.moduleId || !expected.revision || !expected.contract || !/^[a-f0-9]{64}$/.test(expected.artifactChecksum))
10715
- throw new VibeError57({ code: "VIBE_CONFIG", message: "Module readiness needs a unique module id, revision, contract and immutable artifact checksum", meta: { reason: "module-readiness-expectation", moduleId: expected.moduleId } });
11068
+ throw new VibeError59({ code: "VIBE_CONFIG", message: "Module readiness needs a unique module id, revision, contract and immutable artifact checksum", meta: { reason: "module-readiness-expectation", moduleId: expected.moduleId } });
10716
11069
  ids.add(expected.moduleId);
10717
11070
  }
10718
11071
  if (params.compatibility !== undefined && params.compatibility !== "exact" && params.compatibility !== "contract")
10719
- throw new VibeError57({ code: "VIBE_CONFIG", message: "Unknown module compatibility mode", meta: { reason: "module-readiness-expectation" } });
11072
+ throw new VibeError59({ code: "VIBE_CONFIG", message: "Unknown module compatibility mode", meta: { reason: "module-readiness-expectation" } });
10720
11073
  const records = await readModuleJournal(params);
10721
11074
  const heads = moduleHistoryHeads({ records });
10722
11075
  const unresolved = [...records.values()].some(({ row, entry }) => row.error !== undefined || row.statementIndex < 0 || entry !== undefined && (!entry.complete || row.statementIndex !== entry.migration.sql.length));
@@ -10751,7 +11104,7 @@ async function inspectModuleReadiness(params) {
10751
11104
  return { ready: !unresolved && modules.every((module) => (module.revision === "matching" || module.revision === "compatible") && module.structural.status !== "drifted"), modules };
10752
11105
  }
10753
11106
  // packages/migrate/src/adoption.ts
10754
- import { VibeError as VibeError58 } from "@vibeorm/schema";
11107
+ import { VibeError as VibeError60 } from "@vibeorm/schema";
10755
11108
  async function adoptMigration(params) {
10756
11109
  const report = await verifyMigrationState({ executor: params.executor, expected: params.reference, dialect: params.dialect });
10757
11110
  assertMigrationState({ report });
@@ -10767,11 +11120,11 @@ function deployedDesiredState(params) {
10767
11120
  for (const [index, row] of params.recorded.entries()) {
10768
11121
  const file = params.migrations[index];
10769
11122
  if (row.state !== "applied" || row.checksum === "" || file?.name !== row.name || migrationChecksum(file) !== row.checksum)
10770
- throw new VibeError58({ code: "VIBE_MIGRATION", message: "Desired state requires complete, checksum-verified migration history", meta: { reason: "desired-state-history-unverified", migration: row.name, state: row.state } });
11123
+ throw new VibeError60({ code: "VIBE_MIGRATION", message: "Desired state requires complete, checksum-verified migration history", meta: { reason: "desired-state-history-unverified", migration: row.name, state: row.state } });
10771
11124
  }
10772
11125
  const applied = new Set(params.recorded.map((row) => row.name));
10773
11126
  if (params.migrations.some((migration) => applied.has(migration.name) && migration.desiredStatePending === true))
10774
- throw new VibeError58({ code: "VIBE_MIGRATION", message: "Applied unfinalized metadata cannot establish desired state", meta: { reason: "desired-state-history-unverified" } });
11127
+ throw new VibeError60({ code: "VIBE_MIGRATION", message: "Applied unfinalized metadata cannot establish desired state", meta: { reason: "desired-state-history-unverified" } });
10775
11128
  const last = [...params.migrations].reverse().find((migration) => applied.has(migration.name));
10776
11129
  return last?.desiredState?.to;
10777
11130
  }
@@ -10886,4 +11239,4 @@ export {
10886
11239
  withMigrationReference
10887
11240
  };
10888
11241
 
10889
- //# debugId=715A0F1724FB835564756E2164756E21
11242
+ //# debugId=F91BB3120BEADA3064756E2164756E21