@vibeorm/migrate 2.4.0 → 2.6.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 (86) hide show
  1. package/README.md +2 -0
  2. package/dist/artifact-probe.d.ts +41 -0
  3. package/dist/artifact-probe.d.ts.map +1 -0
  4. package/dist/compare-sqlite-primary-key.d.ts +1 -1
  5. package/dist/compare-sqlite-primary-key.d.ts.map +1 -1
  6. package/dist/ddl/mysql.d.ts.map +1 -1
  7. package/dist/ddl/postgres.d.ts +1 -1
  8. package/dist/ddl/rls.d.ts.map +1 -1
  9. package/dist/ddl/sqlite.d.ts.map +1 -1
  10. package/dist/differ.d.ts.map +1 -1
  11. package/dist/down.d.ts +21 -3
  12. package/dist/down.d.ts.map +1 -1
  13. package/dist/enum-values.d.ts +1 -0
  14. package/dist/enum-values.d.ts.map +1 -1
  15. package/dist/exclusions.d.ts +1 -0
  16. package/dist/exclusions.d.ts.map +1 -1
  17. package/dist/extensions.d.ts +52 -0
  18. package/dist/extensions.d.ts.map +1 -1
  19. package/dist/external-dependencies.d.ts +37 -0
  20. package/dist/external-dependencies.d.ts.map +1 -0
  21. package/dist/external-objects.d.ts +122 -0
  22. package/dist/external-objects.d.ts.map +1 -0
  23. package/dist/fs.d.ts.map +1 -1
  24. package/dist/fs.js +1293 -651
  25. package/dist/fs.js.map +35 -31
  26. package/dist/index.d.ts +11 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +5424 -4300
  29. package/dist/index.js.map +57 -50
  30. package/dist/inspect-objects.d.ts +5 -0
  31. package/dist/inspect-objects.d.ts.map +1 -1
  32. package/dist/introspect/postgres-rls.d.ts.map +1 -1
  33. package/dist/introspect/postgres.d.ts +4 -2
  34. package/dist/introspect/postgres.d.ts.map +1 -1
  35. package/dist/introspect/shared.d.ts +2 -1
  36. package/dist/introspect/shared.d.ts.map +1 -1
  37. package/dist/inventory.d.ts.map +1 -1
  38. package/dist/migration-checksum.d.ts +2 -0
  39. package/dist/migration-checksum.d.ts.map +1 -1
  40. package/dist/module-adoption-record.d.ts +22 -0
  41. package/dist/module-adoption-record.d.ts.map +1 -0
  42. package/dist/module-adoption-reference.d.ts +27 -0
  43. package/dist/module-adoption-reference.d.ts.map +1 -0
  44. package/dist/module-adoption.d.ts +26 -0
  45. package/dist/module-adoption.d.ts.map +1 -0
  46. package/dist/module-bundle.d.ts.map +1 -1
  47. package/dist/module-export.d.ts +6 -0
  48. package/dist/module-export.d.ts.map +1 -1
  49. package/dist/module-history.d.ts.map +1 -1
  50. package/dist/module-origin.d.ts +1 -1
  51. package/dist/module-origin.d.ts.map +1 -1
  52. package/dist/module-ownership.d.ts.map +1 -1
  53. package/dist/module-pending.d.ts +6 -0
  54. package/dist/module-pending.d.ts.map +1 -1
  55. package/dist/module-readiness.d.ts +5 -0
  56. package/dist/module-readiness.d.ts.map +1 -1
  57. package/dist/normalize.d.ts +15 -2
  58. package/dist/normalize.d.ts.map +1 -1
  59. package/dist/object-ownership.d.ts.map +1 -1
  60. package/dist/operator-classes.d.ts +9 -0
  61. package/dist/operator-classes.d.ts.map +1 -0
  62. package/dist/planner.d.ts +33 -0
  63. package/dist/planner.d.ts.map +1 -1
  64. package/dist/postgres-types.d.ts.map +1 -1
  65. package/dist/push.d.ts +14 -0
  66. package/dist/push.d.ts.map +1 -1
  67. package/dist/reference-state.d.ts.map +1 -1
  68. package/dist/relations.d.ts +1 -0
  69. package/dist/relations.d.ts.map +1 -1
  70. package/dist/renames.d.ts.map +1 -1
  71. package/dist/replay-plan.d.ts.map +1 -1
  72. package/dist/replay-reference.d.ts +10 -0
  73. package/dist/replay-reference.d.ts.map +1 -1
  74. package/dist/runner.d.ts +3 -0
  75. package/dist/runner.d.ts.map +1 -1
  76. package/dist/shadow-dedicated.d.ts.map +1 -1
  77. package/dist/shadow-guard.d.ts +4 -0
  78. package/dist/shadow-guard.d.ts.map +1 -1
  79. package/dist/shadow-types.d.ts +10 -0
  80. package/dist/shadow-types.d.ts.map +1 -1
  81. package/dist/shadow.d.ts.map +1 -1
  82. package/dist/types.d.ts +22 -1
  83. package/dist/types.d.ts.map +1 -1
  84. package/dist/verify-history.d.ts +4 -0
  85. package/dist/verify-history.d.ts.map +1 -1
  86. package/package.json +3 -3
package/dist/fs.js CHANGED
@@ -15,8 +15,8 @@ function hasMysqlGlobalCatalogVisibility(params) {
15
15
  }
16
16
 
17
17
  // packages/migrate/src/module-ownership.ts
18
- import { VibeError as VibeError17, sqlObjectKey as sqlObjectKey6 } from "@vibeorm/schema";
19
- import { getSqlDialect as getSqlDialect3, renderModuleDependencyQuery } from "@vibeorm/sql";
18
+ import { VibeError as VibeError18, sqlObjectKey as sqlObjectKey7, sqlObjectNamespace as sqlObjectNamespace3 } from "@vibeorm/schema";
19
+ import { getSqlDialect as getSqlDialect4, renderModuleDependencyQuery } from "@vibeorm/sql";
20
20
 
21
21
  // packages/migrate/src/desired-state.ts
22
22
  import { VibeError, assertValidSchemaIR, isSqlObjectDefinition, isSqlObjectIdentity, providerToDialect, sqlObjectKey } from "@vibeorm/schema";
@@ -115,14 +115,11 @@ function parseMigrationMetadata(params) {
115
115
  }
116
116
  }
117
117
 
118
- // packages/migrate/src/object-ownership.ts
119
- import { VibeError as VibeError6, defaultPrimaryKeyName, enumStorageOf as enumStorageOf2, implicitJoinTableObjectNames, postgresGeneratedObjectName, sqlObjectKey as sqlObjectKey2 } from "@vibeorm/schema";
120
-
121
118
  // packages/migrate/src/normalize.ts
122
- import { VibeError as VibeError5, defaultForeignKeyName, defaultIndexName, enumStorageOf, isEnumType as isEnumType5, unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
119
+ import { assertIndexOperatorClasses, VibeError as VibeError5, defaultForeignKeyName, defaultIndexName, enumStorageOf, isEnumType as isEnumType5, qualifiedSqlName as qualifiedSqlName3, assertSqlNamespace, unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
123
120
 
124
121
  // packages/migrate/src/relations.ts
125
- import { implicitJoinTable, isImplicitManyToManyRelation } from "@vibeorm/schema";
122
+ import { implicitJoinTable, isImplicitManyToManyRelation, qualifiedSqlName, unsupportedCapability, providerToDialect as providerToDialect2 } from "@vibeorm/schema";
126
123
  function detectImplicitM2MPairs(params) {
127
124
  const { schema } = params;
128
125
  const pairs = [];
@@ -138,14 +135,20 @@ function detectImplicitM2MPairs(params) {
138
135
  modelB: relation.targetModel,
139
136
  ...relation.relationName === undefined ? {} : { relationName: relation.relationName }
140
137
  });
141
- if (seen.has(joinTable.tableName))
138
+ const target = schema.models.find((entry) => entry.name === relation.targetModel);
139
+ const namespace = qualifiedSqlName({ name: model.name, namespace: model.namespace }).namespace;
140
+ if (namespace !== qualifiedSqlName({ name: target.name, namespace: target.namespace }).namespace)
141
+ throw unsupportedCapability({ dialect: providerToDialect2({ provider: schema.datasource.provider }), feature: "implicitJoinNamespaces", hint: "Implicit join parents must share one physical namespace" });
142
+ const key = JSON.stringify([namespace, joinTable.tableName]);
143
+ if (seen.has(key))
142
144
  continue;
143
- seen.add(joinTable.tableName);
145
+ seen.add(key);
144
146
  pairs.push({
145
147
  modelA: joinTable.modelA,
146
148
  modelB: joinTable.modelB,
147
149
  ...relation.relationName === undefined ? {} : { relationName: relation.relationName },
148
- tableName: joinTable.tableName
150
+ tableName: joinTable.tableName,
151
+ ...model.namespace === undefined && target.namespace === undefined ? {} : { namespace }
149
152
  });
150
153
  }
151
154
  }
@@ -153,7 +156,7 @@ function detectImplicitM2MPairs(params) {
153
156
  }
154
157
 
155
158
  // packages/migrate/src/postgres-types.ts
156
- import { VibeError as VibeError2, isEnumType, scalarListDefault, serializeJsonDefault } from "@vibeorm/schema";
159
+ import { VibeError as VibeError2, isEnumType, scalarListDefault, serializeJsonDefault, qualifiedSqlName as qualifiedSqlName2 } from "@vibeorm/schema";
157
160
  import { POSTGRES_CODECS, postgresDialect, toPgArrayLiteral, parsePostgresTypeName, renderPostgresTypeName } from "@vibeorm/sql";
158
161
  var SCALAR_TO_PG = {
159
162
  String: "text",
@@ -272,7 +275,8 @@ function columnTypeKey(params) {
272
275
  return `u:${JSON.stringify([namespace ?? null, parsed.name, parsed.modifier, parsed.array || field.isList])}`;
273
276
  }
274
277
  if (isEnumType(field.type)) {
275
- return `e:${field.type.enum}${listSuffix}`;
278
+ const identity = qualifiedSqlName2({ name: field.type.enum, namespace: field.type.namespace });
279
+ return `e:${JSON.stringify([identity.namespace, identity.name])}${listSuffix}`;
276
280
  }
277
281
  if (field.nativeType !== undefined) {
278
282
  const normalized = normalizeNativeType({ raw: field.nativeType });
@@ -309,7 +313,7 @@ function columnTypeSql(params) {
309
313
  }
310
314
  const listSuffix = field.isList ? "[]" : "";
311
315
  if (isEnumType(field.type)) {
312
- return `${postgresDialect.quoteIdent({ name: "public" })}.${postgresDialect.quoteIdent({ name: field.type.enum })}${listSuffix}`;
316
+ return `${postgresDialect.quoteIdent({ name: field.type.namespace ?? "public" })}.${postgresDialect.quoteIdent({ name: field.type.enum })}${listSuffix}`;
313
317
  }
314
318
  if (field.nativeType !== undefined) {
315
319
  const normalized = normalizeNativeType({ raw: field.nativeType });
@@ -426,18 +430,18 @@ function quotePgString(params) {
426
430
  }
427
431
 
428
432
  // packages/migrate/src/sqlite-types.ts
429
- import { VibeError as VibeError3, isEnumType as isEnumType3, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
433
+ import { VibeError as VibeError3, isEnumType as isEnumType3, unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
430
434
  import { ParamCollector, sqliteDialect } from "@vibeorm/sql";
431
435
 
432
436
  // packages/migrate/src/enum-values.ts
433
- import { isEnumType as isEnumType2, unsupportedCapability } from "@vibeorm/schema";
437
+ import { isEnumType as isEnumType2, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
434
438
  function resolveEnumValues(params) {
435
439
  const { name, enums } = params;
436
440
  const direct = enums.get(name);
437
441
  if (direct !== undefined)
438
442
  return direct.values;
439
443
  for (const enumDef of enums.values()) {
440
- if (enumDef.name === name)
444
+ if ((enumDef.name === name || enumDef.dbName === name) && (params.namespace === undefined || (enumDef.namespace ?? "public") === params.namespace))
441
445
  return enumDef.values;
442
446
  }
443
447
  return;
@@ -452,7 +456,7 @@ function assertEnumStringTransition(params) {
452
456
  return;
453
457
  const reason = from.isList !== to.isList ? "change scalar/list shape separately from enum storage" : from.default?.kind === "dbgenerated" || to.default?.kind === "dbgenerated" ? "database-expression defaults may depend on the native enum type \u2014 migrate the default explicitly first" : dialect === "mysql" && isEnumType2(to.type) ? "non-strict mysql can silently replace out-of-list strings with '' \u2014 validate and migrate this column manually in strict mode" : undefined;
454
458
  if (reason !== undefined) {
455
- throw unsupportedCapability({ dialect, feature: `enum storage conversion on "${table}"."${column}"`, hint: reason });
459
+ throw unsupportedCapability2({ dialect, feature: `enum storage conversion on "${table}"."${column}"`, hint: reason });
456
460
  }
457
461
  }
458
462
 
@@ -469,7 +473,7 @@ var SCALAR_TO_SQLITE = {
469
473
  Bytes: "BLOB"
470
474
  };
471
475
  function refuseSqliteScalarList(params) {
472
- throw unsupportedCapability2({
476
+ throw unsupportedCapability3({
473
477
  dialect: "sqlite",
474
478
  feature: `scalar list column "${params.field.name}"`,
475
479
  hint: "sqlite has no array column type \u2014 model the list as a separate table or a Json column"
@@ -605,7 +609,7 @@ function parseSqliteEnumChecks(params) {
605
609
  }
606
610
 
607
611
  // packages/migrate/src/mysql-types.ts
608
- import { VibeError as VibeError4, isEnumType as isEnumType4, unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
612
+ import { VibeError as VibeError4, isEnumType as isEnumType4, unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
609
613
  import { mysqlDialect } from "@vibeorm/sql";
610
614
  var SCALAR_TO_MYSQL = {
611
615
  String: `varchar(${mysqlDialect.conflictKeyEquality.defaultStringMaxLength})`,
@@ -619,7 +623,7 @@ var SCALAR_TO_MYSQL = {
619
623
  Bytes: "longblob"
620
624
  };
621
625
  function refuseMysqlScalarList(params) {
622
- throw unsupportedCapability3({
626
+ throw unsupportedCapability4({
623
627
  dialect: "mysql",
624
628
  feature: `scalar list column "${params.field.name}"`,
625
629
  hint: "mysql has no array column type \u2014 model the list as a separate table or a JSON column"
@@ -691,7 +695,7 @@ function mysqlColumnTypeSql(params) {
691
695
  });
692
696
  }
693
697
  if (values.some((value) => /[\\\x00-\x1f\x7f]/.test(value)))
694
- throw unsupportedCapability3({ dialect: "mysql", feature: "mysqlEnumLiteralEscaping", hint: "ENUM labels containing backslashes or control characters require a reviewed SQL-mode-specific migration" });
698
+ throw unsupportedCapability4({ dialect: "mysql", feature: "mysqlEnumLiteralEscaping", hint: "ENUM labels containing backslashes or control characters require a reviewed SQL-mode-specific migration" });
695
699
  return `ENUM(${values.map((value) => quotePgString({ value })).join(", ")})`;
696
700
  }
697
701
  if (field.nativeType !== undefined) {
@@ -750,25 +754,31 @@ function parseMysqlEnumColumnType(params) {
750
754
  function normalizeSchema(params) {
751
755
  const { schema } = params;
752
756
  const dialect = params.dialect ?? "postgres";
757
+ assertIndexOperatorClasses({ schema, dialect, physical: true });
753
758
  const enums = new Map;
754
759
  for (const enumDef of schema.enums) {
760
+ if (enumDef.namespace !== undefined)
761
+ assertSqlNamespace({ namespace: enumDef.namespace, dialect });
755
762
  if (enumStorageOf({ enumDef }) === "string")
756
763
  continue;
757
- enums.set(enumDef.dbName ?? enumDef.name, enumDef);
764
+ enums.set(physicalNameKey({ name: enumDef.dbName ?? enumDef.name, namespace: enumDef.namespace, dialect }), enumDef);
758
765
  }
759
766
  const tables = new Map;
760
767
  const views = new Map;
761
768
  for (const model of schema.models) {
769
+ if (model.namespace !== undefined)
770
+ assertSqlNamespace({ namespace: model.namespace, dialect });
762
771
  if (model.isView) {
763
- views.set(model.dbName ?? model.name, model);
772
+ views.set(physicalNameKey({ name: model.dbName ?? model.name, namespace: model.namespace, dialect }), model);
764
773
  continue;
765
774
  }
766
775
  const table = normalizeModel({ model, schema, dialect, enums });
767
- tables.set(table.table, table);
776
+ tables.set(physicalNameKey({ name: table.table, namespace: table.namespace, dialect }), table);
768
777
  }
769
778
  const joinTables = new Map;
770
779
  for (const pair of detectImplicitM2MPairs({ schema })) {
771
- joinTables.set(pair.tableName, normalizeJoinTable({ pair, schema, dialect }));
780
+ const join = normalizeJoinTable({ pair, schema, dialect });
781
+ joinTables.set(physicalNameKey({ name: join.table, namespace: join.namespace, dialect }), join);
772
782
  }
773
783
  return { tables, joinTables, enums, views };
774
784
  }
@@ -776,12 +786,15 @@ function physicalEnumField(params) {
776
786
  const { field, schema, dialect, table } = params;
777
787
  if (!isEnumType5(field.type))
778
788
  return field;
779
- const name = field.type.enum;
780
- const enumDef = schema.enums.find((candidate) => candidate.name === name || candidate.dbName === name);
789
+ const type = field.type;
790
+ if (type.namespace !== undefined)
791
+ assertSqlNamespace({ namespace: type.namespace, dialect });
792
+ const name = type.enum;
793
+ const enumDef = schema.enums.find((candidate) => (candidate.name === name || candidate.dbName === name) && (type.namespace === undefined || qualifiedSqlName3({ name, namespace: candidate.namespace }).namespace === type.namespace));
781
794
  if (enumDef === undefined)
782
795
  return field;
783
796
  if (enumStorageOf({ enumDef }) !== "string") {
784
- return dialect === "postgres" ? { ...field, type: { enum: enumDef.dbName ?? enumDef.name } } : field;
797
+ return dialect === "postgres" ? { ...field, type: { enum: enumDef.dbName ?? enumDef.name, ...enumDef.namespace === undefined ? {} : { namespace: enumDef.namespace } } } : field;
785
798
  }
786
799
  const physical = {
787
800
  ...field,
@@ -808,7 +821,7 @@ function assertStringEnumLabelsFit(params) {
808
821
  const charLimit = varchar?.[1] === undefined ? undefined : Number(varchar[1]);
809
822
  const tooLong = values.some((value) => charLimit !== undefined && [...value].length > charLimit || textBytes !== undefined && new TextEncoder().encode(value).length > textBytes);
810
823
  if (!supported || tooLong) {
811
- throw unsupportedCapability4({
824
+ throw unsupportedCapability5({
812
825
  dialect,
813
826
  feature: `string enum column "${table}"."${field.dbName ?? field.name}" stored as ${type}`,
814
827
  hint: supported ? "an enum label exceeds the physical column limit \u2014 choose a wider varchar or text type" : "use text or varchar storage; other types cannot preserve arbitrary enum labels verbatim"
@@ -858,7 +871,7 @@ function normalizeModel(params) {
858
871
  const checks = new Map;
859
872
  for (const check of model.checks ?? [])
860
873
  checks.set(check.name, check.expression);
861
- return { model, table, columns, pk, indexes, fks, checks };
874
+ return { model, table, ...model.namespace === undefined ? {} : { namespace: model.namespace }, columns, pk, indexes, fks, checks };
862
875
  }
863
876
  function normalizePk(params) {
864
877
  const { model } = params;
@@ -879,7 +892,8 @@ function normalizeIndexes(params) {
879
892
  return {
880
893
  name: fieldDbName({ model, name: field.name }),
881
894
  desc: field.sort === "desc",
882
- ...decimal ? { decimal: true } : {}
895
+ ...decimal ? { decimal: true } : {},
896
+ ...field.operatorClass === undefined ? {} : { operatorClass: field.operatorClass }
883
897
  };
884
898
  });
885
899
  const where = index.where;
@@ -910,7 +924,7 @@ function normalizeIndexes(params) {
910
924
  }
911
925
  function indexComparisonKey(params) {
912
926
  const { unique, kind, columns, where, nullsNotDistinct } = params;
913
- const cols = columns.map((column) => `${column.name}${column.desc ? " desc" : ""}`).join(",");
927
+ const cols = columns.map((column) => `${column.name}${column.desc ? " desc" : ""}${column.operatorClass === undefined ? "" : ` ops:${JSON.stringify([column.operatorClass.namespace, column.operatorClass.name])}`}`).join(",");
914
928
  return `${unique ? "u" : "i"}${nullsNotDistinct === true ? "n" : ""}:${kind}:${cols}:${normalizeWhere({ where })}`;
915
929
  }
916
930
  function normalizeWhere(params) {
@@ -1088,7 +1102,7 @@ function normalizeFks(params) {
1088
1102
  const field = model.fields.find((candidate) => candidate.name === name);
1089
1103
  return field === undefined ? true : !field.isOptional;
1090
1104
  });
1091
- const key = `${columns.join(",")}\u2192${refTable}.${refColumns.join(",")}`;
1105
+ const key = `${columns.join(",")}\u2192${physicalNameKey({ name: refTable, namespace: target?.namespace, dialect: params.dialect })}.${refColumns.join(",")}`;
1092
1106
  if (result.has(key))
1093
1107
  continue;
1094
1108
  result.set(key, {
@@ -1096,6 +1110,7 @@ function normalizeFks(params) {
1096
1110
  constraintName: relation.constraintName ?? defaultForeignKeyName({ table, columns, dialect: params.dialect }),
1097
1111
  columns,
1098
1112
  refTable,
1113
+ ...target?.namespace === undefined ? {} : { refNamespace: target.namespace },
1099
1114
  refColumns,
1100
1115
  onDelete: resolveOnDelete({ explicit: relation.onDelete, required }),
1101
1116
  onUpdate: resolveOnUpdate({ explicit: relation.onUpdate })
@@ -1107,6 +1122,7 @@ function normalizeJoinTable(params) {
1107
1122
  const { pair, schema, dialect } = params;
1108
1123
  return {
1109
1124
  table: pair.tableName,
1125
+ ...pair.namespace === undefined ? {} : { namespace: pair.namespace },
1110
1126
  a: joinTableEnd({ modelName: pair.modelA, schema, dialect, joinTable: pair.tableName }),
1111
1127
  b: joinTableEnd({ modelName: pair.modelB, schema, dialect, joinTable: pair.tableName })
1112
1128
  };
@@ -1132,10 +1148,24 @@ function joinTableEnd(params) {
1132
1148
  }
1133
1149
  return {
1134
1150
  table: model.dbName ?? model.name,
1151
+ ...model.namespace === undefined ? {} : { namespace: model.namespace },
1135
1152
  column: pkField.dbName ?? pkField.name,
1136
1153
  field: physicalEnumField({ field: pkField, schema, dialect: params.dialect, table: model.dbName ?? model.name })
1137
1154
  };
1138
1155
  }
1156
+ function physicalNameKey(params) {
1157
+ if (params.dialect !== undefined && params.dialect !== "postgres")
1158
+ return params.name;
1159
+ const identity = qualifiedSqlName3(params);
1160
+ return JSON.stringify([identity.namespace, identity.name]);
1161
+ }
1162
+ function schemaNamespaces(params) {
1163
+ return [...new Set([
1164
+ ...params.schema.models.flatMap((model) => model.namespace === undefined || model.namespace === "public" ? [] : [model.namespace]),
1165
+ ...params.schema.enums.flatMap((entry) => enumStorageOf({ enumDef: entry }) === "string" || entry.namespace === undefined || entry.namespace === "public" ? [] : [entry.namespace]),
1166
+ ...(params.schema.migrationObjects ?? []).flatMap((object) => object.identity.kind === "namespace" ? [object.identity.name] : [])
1167
+ ])];
1168
+ }
1139
1169
  function fieldDbName(params) {
1140
1170
  const { model, name } = params;
1141
1171
  const field = model.fields.find((candidate) => candidate.name === name);
@@ -1143,6 +1173,7 @@ function fieldDbName(params) {
1143
1173
  }
1144
1174
 
1145
1175
  // packages/migrate/src/object-ownership.ts
1176
+ import { VibeError as VibeError6, defaultPrimaryKeyName, enumStorageOf as enumStorageOf2, implicitJoinTableObjectNames, postgresGeneratedObjectName, qualifiedSqlName as qualifiedSqlName4, sqlObjectKey as sqlObjectKey2 } from "@vibeorm/schema";
1146
1177
  function legacyMigrationObjects(params) {
1147
1178
  return params.artifacts.map((artifact) => {
1148
1179
  const owner = artifact.owns;
@@ -1166,13 +1197,14 @@ function legacyMigrationObjects(params) {
1166
1197
  }
1167
1198
  function preserveMigrationObjects(params) {
1168
1199
  const namespace = params.namespace ?? (params.to.datasource.provider === "sqlite" ? "main" : "public");
1169
- const objects = params.objects.filter((object) => ("table" in object.identity ? object.identity.table.namespace : object.identity.name.namespace) === namespace);
1200
+ const objects = params.objects;
1170
1201
  const models = params.from.models.flatMap((model) => {
1171
1202
  const table = model.dbName ?? model.name;
1172
- const target = params.to.models.find((entry) => (entry.dbName ?? entry.name) === table);
1173
- if (objects.some((object) => (object.identity.kind === "table" || object.identity.kind === "view") && object.identity.name.name === table))
1203
+ const placement = qualifiedSqlName4({ name: table, namespace: model.namespace, defaultNamespace: namespace });
1204
+ const target = params.to.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect: params.to.datasource.provider === "sqlite" ? "sqlite" : params.to.datasource.provider === "mysql" ? "mysql" : "postgres" }) === physicalNameKey({ ...placement, dialect: params.to.datasource.provider === "sqlite" ? "sqlite" : params.to.datasource.provider === "mysql" ? "mysql" : "postgres" }));
1205
+ if (objects.some((object) => (object.identity.kind === "table" || object.identity.kind === "view") && object.identity.name.name === table && object.identity.name.namespace === placement.namespace))
1174
1206
  return [];
1175
- const owners = objects.map((object) => object.identity).filter((identity) => ("table" in identity) && identity.table.name === table);
1207
+ const owners = objects.map((object) => object.identity).filter((identity) => ("table" in identity) && identity.table.name === table && identity.table.namespace === placement.namespace);
1176
1208
  return [{
1177
1209
  ...model,
1178
1210
  fields: model.fields.flatMap((field) => {
@@ -1194,7 +1226,7 @@ function preserveMigrationObjects(params) {
1194
1226
  relations: model.relations.filter((relation) => !owners.some((owner) => owner.kind === "constraint" && owner.constraintType === "foreignKey" && owner.name === relation.constraintName))
1195
1227
  }];
1196
1228
  });
1197
- const enums = params.from.enums.filter((enumDef) => enumStorageOf2({ enumDef }) === "string" || !objects.some((object) => object.identity.kind === "enum" && object.identity.name.name === (enumDef.dbName ?? enumDef.name)));
1229
+ const enums = params.from.enums.filter((enumDef) => enumStorageOf2({ enumDef }) === "string" || !objects.some((object) => object.identity.kind === "enum" && object.identity.name.name === (enumDef.dbName ?? enumDef.name) && object.identity.name.namespace === (enumDef.namespace ?? namespace)));
1198
1230
  return { ...params.from, models, enums };
1199
1231
  }
1200
1232
  function sqlitePreservedSchema(params) {
@@ -1213,7 +1245,8 @@ function sqlitePreservedSchema(params) {
1213
1245
  }) };
1214
1246
  }
1215
1247
  function removalIdentity(params) {
1216
- const { step, namespace } = params;
1248
+ const { step } = params;
1249
+ const namespace = step.namespace ?? params.namespace;
1217
1250
  const table = "table" in step && step.table !== undefined ? { namespace, name: step.table } : undefined;
1218
1251
  switch (step.kind) {
1219
1252
  case "dropEnum":
@@ -1244,18 +1277,22 @@ function removalIdentity(params) {
1244
1277
  function schemaObjectIdentities(params) {
1245
1278
  const normalized = normalizeSchema({ schema: params.schema, dialect: params.dialect });
1246
1279
  const result = [];
1247
- if (params.dialect === "postgres")
1248
- for (const name of normalized.enums.keys())
1249
- result.push({ kind: "enum", name: { namespace: params.namespace, name } });
1250
- for (const [name, model] of normalized.tables) {
1251
- const table = { namespace: params.namespace, name };
1280
+ if (params.dialect === "postgres") {
1281
+ for (const namespace of schemaNamespaces({ schema: params.schema }))
1282
+ result.push({ kind: "namespace", name: namespace });
1283
+ for (const enumDef of normalized.enums.values())
1284
+ result.push({ kind: "enum", name: qualifiedSqlName4({ name: enumDef.dbName ?? enumDef.name, namespace: enumDef.namespace, defaultNamespace: params.namespace }) });
1285
+ }
1286
+ for (const model of normalized.tables.values()) {
1287
+ const name = model.table;
1288
+ const table = qualifiedSqlName4({ name, namespace: model.namespace, defaultNamespace: params.namespace });
1252
1289
  result.push({ kind: "table", name: table });
1253
1290
  for (const [column, definition] of model.columns) {
1254
1291
  result.push({ kind: "column", table, column });
1255
1292
  if (definition.defaultKey !== null)
1256
1293
  result.push({ kind: "columnDefault", table, column });
1257
1294
  if (params.dialect === "postgres" && definition.field.default?.kind === "autoincrement")
1258
- result.push({ kind: "sequence", name: { namespace: params.namespace, name: postgresGeneratedObjectName({ name1: name, name2: column, label: "seq" }) } });
1295
+ result.push({ kind: "sequence", name: { namespace: table.namespace, name: postgresGeneratedObjectName({ name1: name, name2: column, label: "seq" }) } });
1259
1296
  }
1260
1297
  for (const exclusion of model.model.exclusions ?? [])
1261
1298
  result.push({ kind: "constraint", constraintType: "exclusion", table, name: exclusion.name });
@@ -1271,16 +1308,17 @@ function schemaObjectIdentities(params) {
1271
1308
  if (model.pk.length > 0)
1272
1309
  result.push({ kind: "constraint", constraintType: "primaryKey", table, name: model.model.primaryKeyConstraintName ?? (params.dialect === "mysql" ? "PRIMARY" : defaultPrimaryKeyName({ table: name, dialect: params.dialect })) });
1273
1310
  }
1274
- for (const name of normalized.joinTables.keys()) {
1275
- const table = { namespace: params.namespace, name };
1311
+ for (const join of normalized.joinTables.values()) {
1312
+ const name = join.table;
1313
+ const table = qualifiedSqlName4({ name, namespace: join.namespace, defaultNamespace: params.namespace });
1276
1314
  result.push({ kind: "table", name: table });
1277
1315
  const derived = implicitJoinTableObjectNames({ table: name });
1278
1316
  result.push({ kind: "column", table, column: "A" }, { kind: "constraint", constraintType: "foreignKey", table, name: derived.foreignKeyA });
1279
1317
  result.push({ kind: "column", table, column: "B" }, { kind: "constraint", constraintType: "foreignKey", table, name: derived.foreignKeyB });
1280
1318
  result.push({ kind: "index", table, name: derived.uniqueIndex }, { kind: "index", table, name: derived.indexB });
1281
1319
  }
1282
- for (const name of normalized.views.keys())
1283
- result.push({ kind: "view", name: { namespace: params.namespace, name } });
1320
+ for (const view of normalized.views.values())
1321
+ result.push({ kind: "view", name: qualifiedSqlName4({ name: view.dbName ?? view.name, namespace: view.namespace, defaultNamespace: params.namespace }) });
1284
1322
  return result;
1285
1323
  }
1286
1324
  function assertRetainedMigrationObjects(params) {
@@ -1340,7 +1378,7 @@ function assertRemovalAuthorization(params) {
1340
1378
  integrity.push(identity);
1341
1379
  if (step.kind === "dropTable" || step.kind === "dropJoinTable") {
1342
1380
  for (const child of [...declared, ...params.removeUnmanaged ?? []]) {
1343
- if ((child.kind === "constraint" || child.kind === "index") && child.table.namespace === params.namespace && child.table.name === step.table)
1381
+ if ((child.kind === "constraint" || child.kind === "index") && child.table.namespace === (step.namespace ?? params.namespace) && child.table.name === step.table)
1344
1382
  integrity.push(child);
1345
1383
  }
1346
1384
  }
@@ -1352,22 +1390,311 @@ function assertRemovalAuthorization(params) {
1352
1390
  }
1353
1391
 
1354
1392
  // packages/migrate/src/inventory.ts
1355
- import { VibeError as VibeError16, defaultForeignKeyName as defaultForeignKeyName2, defaultPrimaryKeyName as defaultPrimaryKeyName2, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey5 } from "@vibeorm/schema";
1356
- import { getSqlDialect as getSqlDialect2, renderMigrationInventory, renderSqliteColumnInventory as renderSqliteColumnInventory2, renderMysqlObjectMembers as renderMysqlObjectMembers2, sqliteTableConstraints as sqliteTableConstraints3, renderSqliteForeignKeyInventory } from "@vibeorm/sql";
1393
+ import { VibeError as VibeError17, defaultForeignKeyName as defaultForeignKeyName2, defaultPrimaryKeyName as defaultPrimaryKeyName2, isSqlObjectIdentity as isSqlObjectIdentity2, sqlObjectKey as sqlObjectKey6 } from "@vibeorm/schema";
1394
+ import { getSqlDialect as getSqlDialect3, renderMigrationInventory, renderSqliteColumnInventory as renderSqliteColumnInventory2, renderMysqlObjectMembers as renderMysqlObjectMembers2, sqliteTableConstraints as sqliteTableConstraints3, renderSqliteForeignKeyInventory } from "@vibeorm/sql";
1395
+
1396
+ // packages/migrate/src/external-objects.ts
1397
+ import { IDENTIFIER_LIMITS, VibeError as VibeError7, identifierLength, sqlNamespaceIssue, sqlObjectKey as sqlObjectKey3, sqlObjectNamespace, qualifiedSqlName as qualifiedSqlName5, enumStorageOf as enumStorageOf3, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
1398
+ import { getSqlDialect, renderExternalOwnershipCatalog, renderReplayCoverageGaps } from "@vibeorm/sql";
1399
+ var partitionPatterns = new WeakMap;
1400
+ function invalid2(params) {
1401
+ return new VibeError7({ 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 } });
1402
+ }
1403
+ function record2(value) {
1404
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1405
+ }
1406
+ function identifier(value) {
1407
+ return typeof value === "string" && value.trim().length > 0 && !value.includes("\x00") && identifierLength({ name: value, dialect: "postgres" }) <= IDENTIFIER_LIMITS.postgres.max;
1408
+ }
1409
+ function names(params) {
1410
+ if (params.value === undefined)
1411
+ return;
1412
+ if (!Array.isArray(params.value) || !params.value.every(identifier) || new Set(params.value).size !== params.value.length)
1413
+ throw invalid2({ detail: `${params.key} must contain unique non-empty PostgreSQL identifiers` });
1414
+ return [...params.value];
1415
+ }
1416
+ async function parseExternalObjects(params) {
1417
+ return Promise.resolve().then(() => {
1418
+ if (!Array.isArray(params.value))
1419
+ throw invalid2({ detail: "external must be an array" });
1420
+ if (params.value.length > 0 && params.dialect !== undefined && params.dialect !== "postgres")
1421
+ throw unsupportedCapability6({ dialect: params.dialect, feature: "externalObjects" });
1422
+ const owners = new Set;
1423
+ const claims = new Set;
1424
+ const compiled = [];
1425
+ const tableClaims = [];
1426
+ const result = [];
1427
+ for (const value of params.value) {
1428
+ if (!record2(value) || typeof value.owner !== "string" || value.owner.trim().length === 0 || value.owner.includes("\x00"))
1429
+ throw invalid2({ detail: "each entry needs a non-empty owner" });
1430
+ const owner = value.owner;
1431
+ if (owners.has(owner))
1432
+ throw invalid2({ detail: "an owner must have one declaration", owner });
1433
+ owners.add(owner);
1434
+ const allowed = value.schema === undefined ? ["owner", "tables", "partitions", "functions"] : ["owner", "schema"];
1435
+ if (Object.keys(value).some((key) => !allowed.includes(key)))
1436
+ throw invalid2({ detail: "whole-schema and named-object declarations are exclusive; unknown keys refuse", owner });
1437
+ const claim = (options) => {
1438
+ const { kind, name } = options;
1439
+ const key = JSON.stringify([kind, name]);
1440
+ if (claims.has(key))
1441
+ throw invalid2({ detail: `${kind} ${JSON.stringify(name)} has conflicting owners`, owner });
1442
+ claims.add(key);
1443
+ };
1444
+ if (value.schema !== undefined) {
1445
+ const issue = sqlNamespaceIssue({ namespace: value.schema, allowPublic: false });
1446
+ if (issue !== undefined || typeof value.schema !== "string")
1447
+ throw invalid2({ detail: issue ?? "schema must be a string", owner });
1448
+ claim({ kind: "namespace", name: value.schema });
1449
+ result.push(Object.freeze({ owner, schema: value.schema }));
1450
+ continue;
1451
+ }
1452
+ const tables = names({ value: value.tables, key: "tables" });
1453
+ const functions = names({ value: value.functions, key: "functions" });
1454
+ const partitions = [];
1455
+ const ownerPatterns = [];
1456
+ if (value.partitions !== undefined) {
1457
+ if (!Array.isArray(value.partitions))
1458
+ throw invalid2({ detail: "partitions must be an array", owner });
1459
+ for (const partition of value.partitions) {
1460
+ if (!record2(partition) || Object.keys(partition).some((key) => key !== "parent" && key !== "pattern") || !identifier(partition.parent) || typeof partition.pattern !== "string" || partition.pattern.length === 0 || !tables?.includes(partition.parent))
1461
+ throw invalid2({ detail: "partition patterns require a parent in the same owner's tables", owner });
1462
+ const pattern = new RegExp(`^(?:${partition.pattern})$`);
1463
+ compiled.push({ owner, pattern });
1464
+ ownerPatterns.push({ parent: partition.parent, pattern });
1465
+ partitions.push(Object.freeze({ parent: partition.parent, pattern: partition.pattern }));
1466
+ }
1467
+ }
1468
+ if ((tables?.length ?? 0) + (functions?.length ?? 0) + partitions.length === 0)
1469
+ throw invalid2({ detail: "named ownership needs at least one table or function", owner });
1470
+ for (const name of tables ?? []) {
1471
+ if (name === "_vibe_migrations")
1472
+ throw invalid2({ detail: "the migration journal cannot be claimed externally", owner });
1473
+ claim({ kind: "table", name });
1474
+ tableClaims.push({ owner, name });
1475
+ }
1476
+ for (const name of functions ?? [])
1477
+ claim({ kind: "function", name });
1478
+ const declaration = Object.freeze({ owner, ...tables === undefined ? {} : { tables: Object.freeze(tables) }, ...functions === undefined ? {} : { functions: Object.freeze(functions) }, ...value.partitions === undefined ? {} : { partitions: Object.freeze(partitions) } });
1479
+ partitionPatterns.set(declaration, ownerPatterns);
1480
+ result.push(declaration);
1481
+ }
1482
+ for (const table of tableClaims)
1483
+ for (const pattern of compiled) {
1484
+ if (table.owner !== pattern.owner && pattern.pattern.exec(table.name)?.[0] === table.name)
1485
+ throw invalid2({ detail: "a full-name pattern claims another owner's named table", owner: pattern.owner });
1486
+ }
1487
+ return Object.freeze(result);
1488
+ }).catch((cause) => {
1489
+ if (cause instanceof VibeError7)
1490
+ throw cause;
1491
+ throw invalid2({ detail: "regular-expression pattern cannot be compiled", cause });
1492
+ });
1493
+ }
1494
+ function nameKey(name) {
1495
+ return sqlObjectKey3({ identity: { kind: "table", name } });
1496
+ }
1497
+ function schemaOwner(params) {
1498
+ return params.external.find((entry) => entry.schema === params.namespace)?.owner;
1499
+ }
1500
+ function declaredTableOwner(params) {
1501
+ const whole = schemaOwner({ external: params.external, namespace: params.name.namespace });
1502
+ if (whole !== undefined || params.name.namespace !== "public")
1503
+ return whole;
1504
+ const matches = params.external.filter((entry) => {
1505
+ if (entry.tables?.includes(params.name.name))
1506
+ return true;
1507
+ const patterns = partitionPatterns.get(entry);
1508
+ if ((entry.partitions?.length ?? 0) > 0 && patterns === undefined)
1509
+ throw invalid2({ detail: "resolve partition declarations returned by parseExternalObjects", owner: entry.owner });
1510
+ return patterns?.some(({ pattern }) => pattern.exec(params.name.name)?.[0] === params.name.name) === true;
1511
+ });
1512
+ if (matches.length > 1)
1513
+ throw new VibeError7({ 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) } });
1514
+ return matches[0]?.owner;
1515
+ }
1516
+ function externalObjectOwner(params) {
1517
+ const { identity, external } = params;
1518
+ const whole = schemaOwner({ external: external.declarations, namespace: sqlObjectNamespace({ identity }) });
1519
+ if (whole !== undefined)
1520
+ return whole;
1521
+ if (identity.kind === "table" || "table" in identity) {
1522
+ const name = "table" in identity ? identity.table : identity.name;
1523
+ return external.tables.find((entry) => nameKey(entry.name) === nameKey(name))?.owner ?? declaredTableOwner({ external: external.declarations, name });
1524
+ }
1525
+ if (identity.kind === "sequence")
1526
+ return external.sequences.find((entry) => nameKey(entry.name) === nameKey(identity.name))?.owner;
1527
+ if (identity.kind === "function" && identity.name.namespace === "public")
1528
+ return external.declarations.find((entry) => entry.functions?.includes(identity.name.name))?.owner;
1529
+ return;
1530
+ }
1531
+ function resolveExternalObjectOwnership(params) {
1532
+ if (params.external.length > 0 && params.dialect !== "postgres")
1533
+ throw unsupportedCapability6({ dialect: params.dialect, feature: "externalObjects" });
1534
+ if (params.catalog.inventory.some((object) => object.inspectionUnavailable !== undefined))
1535
+ throw new VibeError7({ code: "VIBE_MIGRATION", message: "External ownership requires complete catalog visibility", meta: { reason: "external-inventory-unavailable", objects: params.catalog.inventory.filter((object) => object.inspectionUnavailable !== undefined) } });
1536
+ const tables = new Map;
1537
+ const assign = (entry) => {
1538
+ const key = nameKey(entry.name);
1539
+ const prior = tables.get(key)?.owner ?? declaredTableOwner({ external: params.external, name: entry.name });
1540
+ if (prior !== undefined && prior !== entry.owner)
1541
+ throw new VibeError7({ 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] } });
1542
+ if (tables.has(key))
1543
+ return false;
1544
+ tables.set(key, entry);
1545
+ return true;
1546
+ };
1547
+ for (const declaration of params.external)
1548
+ for (const name of declaration.tables ?? [])
1549
+ assign({ owner: declaration.owner, name: { namespace: "public", name } });
1550
+ for (const object of params.catalog.inventory) {
1551
+ if (object.identity.kind !== "table")
1552
+ continue;
1553
+ const owner = declaredTableOwner({ external: params.external, name: object.identity.name });
1554
+ if (owner !== undefined)
1555
+ assign({ owner, name: object.identity.name });
1556
+ }
1557
+ let changed = true;
1558
+ while (changed) {
1559
+ changed = false;
1560
+ for (const edge of params.catalog.inheritance) {
1561
+ const owner = tables.get(nameKey(edge.parent))?.owner;
1562
+ if (owner !== undefined && assign({ owner, name: edge.child }))
1563
+ changed = true;
1564
+ }
1565
+ }
1566
+ const sequences = params.catalog.ownedSequences.flatMap((edge) => {
1567
+ const owner = tables.get(nameKey(edge.table))?.owner;
1568
+ if (owner === undefined)
1569
+ return [];
1570
+ const whole = schemaOwner({ external: params.external, namespace: edge.sequence.namespace });
1571
+ if (whole !== undefined && whole !== owner)
1572
+ throw new VibeError7({ 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] } });
1573
+ return [{ owner, name: edge.sequence }];
1574
+ });
1575
+ const closure = { declarations: params.external, catalog: params.catalog, inventory: params.catalog.inventory, tables: [...tables.values()], sequences, owners: [], identities: [] };
1576
+ for (const edge of params.catalog.foreignKeys) {
1577
+ const owner = externalObjectOwner({ external: closure, identity: { kind: "table", name: edge.table } });
1578
+ const targetOwner = externalObjectOwner({ external: closure, identity: { kind: "table", name: edge.target } });
1579
+ if (owner !== targetOwner && (owner !== undefined || targetOwner !== undefined))
1580
+ throw new VibeError7({ 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" } });
1581
+ }
1582
+ const observed = new Map;
1583
+ for (const namespace of params.catalog.namespaces) {
1584
+ if (schemaOwner({ external: params.external, namespace }) !== undefined)
1585
+ observed.set(sqlObjectKey3({ identity: { kind: "namespace", name: namespace } }), { kind: "namespace", name: namespace });
1586
+ }
1587
+ for (const object of params.catalog.inventory)
1588
+ observed.set(sqlObjectKey3({ identity: object.identity }), object.identity);
1589
+ const owners = params.external.map((entry) => {
1590
+ const identities = [...observed.values()].filter((identity) => externalObjectOwner({ identity, external: closure }) === entry.owner);
1591
+ const missing = [];
1592
+ if (entry.schema !== undefined && !params.catalog.namespaces.includes(entry.schema))
1593
+ missing.push({ kind: "namespace", name: entry.schema });
1594
+ for (const name of entry.tables ?? [])
1595
+ if (!params.catalog.inventory.some(({ identity }) => identity.kind === "table" && identity.name.namespace === "public" && identity.name.name === name))
1596
+ missing.push({ kind: "table", name: { namespace: "public", name } });
1597
+ for (const name of entry.functions ?? [])
1598
+ if (!params.catalog.inventory.some(({ identity }) => identity.kind === "function" && identity.name.namespace === "public" && identity.name.name === name))
1599
+ missing.push({ kind: "function", name: { namespace: "public", name } });
1600
+ return { owner: entry.owner, state: "excluded", count: identities.length, identities, missing };
1601
+ });
1602
+ return { ...closure, owners, identities: owners.flatMap((owner) => owner.identities) };
1603
+ }
1604
+ function catalogName(params) {
1605
+ const namespace = params.row[params.namespace];
1606
+ const name = params.row[params.name];
1607
+ if (!identifier(namespace) || !identifier(name))
1608
+ throw new VibeError7({ code: "VIBE_MIGRATION", message: "Malformed external ownership catalog endpoint", meta: { reason: "external-inventory-unavailable" } });
1609
+ return { namespace, name };
1610
+ }
1611
+ async function inspectExternalObjects(params) {
1612
+ const external = await parseExternalObjects({ value: params.external, dialect: params.dialect });
1613
+ if (params.dialect !== "postgres")
1614
+ return resolveExternalObjectOwnership({ external, dialect: params.dialect, catalog: { namespaces: [], inventory: params.inventory ?? [], inheritance: [], ownedSequences: [], foreignKeys: [] } });
1615
+ const read = async (operation) => {
1616
+ const query = renderExternalOwnershipCatalog({ dialect: getSqlDialect({ dialect: params.dialect }), operation });
1617
+ return params.executor({ text: query.text, values: [...query.values] });
1618
+ };
1619
+ const namespaceRows = await read("namespaces");
1620
+ if (namespaceRows.some((row) => !identifier(row.namespace) || row.visible !== true))
1621
+ throw new VibeError7({ code: "VIBE_MIGRATION", message: "External ownership cannot prove visibility of every inspected namespace", meta: { reason: "external-inventory-unavailable" } });
1622
+ const namespaces = namespaceRows.map((row) => String(row.namespace));
1623
+ const [inherited, owned, foreignKeys] = await Promise.all([read("inheritance"), read("ownedSequences"), read("foreignKeys")]);
1624
+ const inventory = params.inventory ?? (await Promise.all(namespaces.map((namespace) => inspectMigrationInventory({ executor: params.executor, dialect: params.dialect, namespace })))).flat();
1625
+ const coverageGaps = (await Promise.all(namespaces.map(async (namespace) => {
1626
+ const query = renderReplayCoverageGaps({ dialect: getSqlDialect({ dialect: params.dialect }), namespace });
1627
+ return params.executor({ text: query.text, values: [...query.values] });
1628
+ }))).flat();
1629
+ return resolveExternalObjectOwnership({ external, dialect: params.dialect, catalog: {
1630
+ namespaces,
1631
+ inventory,
1632
+ coverageGaps,
1633
+ inheritance: inherited.map((row) => ({ parent: catalogName({ row, namespace: "parentNamespace", name: "parentName" }), child: catalogName({ row, namespace: "namespace", name: "name" }) })),
1634
+ ownedSequences: owned.map((row) => ({ table: catalogName({ row, namespace: "tableNamespace", name: "tableName" }), sequence: catalogName({ row, namespace: "namespace", name: "name" }) })),
1635
+ foreignKeys: foreignKeys.map((row) => ({ table: catalogName({ row, namespace: "namespace", name: "table" }), target: catalogName({ row, namespace: "refNamespace", name: "refTable" }), name: catalogName({ row, namespace: "namespace", name: "name" }).name }))
1636
+ } });
1637
+ }
1638
+ function assertExternalOwnershipClaims(params) {
1639
+ for (const identity of params.identities) {
1640
+ 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);
1641
+ if (owner !== undefined)
1642
+ throw new VibeError7({ code: "VIBE_CONFIG", message: "Application or module declarations collide with external ownership", meta: { reason: "external-owned-object-collision", owner, identity } });
1643
+ }
1644
+ }
1645
+ function projectExternalSchema(params) {
1646
+ const { schema, external } = params;
1647
+ const models = schema.models.filter((model) => externalObjectOwner({ external, identity: { kind: model.isView ? "view" : "table", name: qualifiedSqlName5({ name: model.dbName ?? model.name, namespace: model.namespace }) } }) === undefined);
1648
+ const removed = new Set(schema.models.filter((model) => !models.includes(model)).map((model) => model.name));
1649
+ for (const model of models)
1650
+ for (const relation of model.relations)
1651
+ if (removed.has(relation.targetModel))
1652
+ throw new VibeError7({ 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 } });
1653
+ return {
1654
+ ...schema,
1655
+ models,
1656
+ enums: schema.enums.filter((entry) => enumStorageOf3({ enumDef: entry }) === "string" || externalObjectOwner({ external, identity: { kind: "enum", name: qualifiedSqlName5({ name: entry.dbName ?? entry.name, namespace: entry.namespace }) } }) === undefined),
1657
+ ...schema.migrationObjects === undefined ? {} : { migrationObjects: schema.migrationObjects.filter((object) => externalObjectOwner({ external, identity: object.identity }) === undefined) }
1658
+ };
1659
+ }
1660
+ function projectExternalState(params) {
1661
+ return { ...params.state, schema: projectExternalSchema({ schema: params.state.schema, external: params.external }), definitions: params.state.definitions.filter((definition) => externalObjectOwner({ external: params.external, identity: definition.identity }) === undefined) };
1662
+ }
1663
+ function projectExternalInventory(params) {
1664
+ return params.inventory.filter((object) => externalObjectOwner({ identity: object.identity, external: params.external }) === undefined);
1665
+ }
1666
+ function migrationCoverageGaps(params) {
1667
+ const recognized = new Set([...params.namespaces, ...params.external?.tables.map((entry) => entry.name.namespace) ?? [], ...params.external?.sequences.map((entry) => entry.name.namespace) ?? []]);
1668
+ const kept = params.gaps.filter((gap) => {
1669
+ const namespace = typeof gap.namespace === "string" ? gap.namespace : params.namespace;
1670
+ if (gap.kind === "namespace" && typeof gap.name === "string" && recognized.has(gap.name))
1671
+ return false;
1672
+ const external = params.external;
1673
+ if (external === undefined)
1674
+ return true;
1675
+ if (external.declarations.some((entry) => entry.schema === namespace || gap.kind === "namespace" && entry.schema === gap.name))
1676
+ return false;
1677
+ const name = gap.kind === "rule" ? gap.parent : gap.name;
1678
+ if ((gap.kind === "relation-layout" || gap.kind === "rule") && typeof name === "string" && externalObjectOwner({ external, identity: { kind: "table", name: { namespace, name } } }) !== undefined)
1679
+ return false;
1680
+ return true;
1681
+ });
1682
+ return [...new Map(kept.map((gap) => [JSON.stringify(gap), gap])).values()];
1683
+ }
1357
1684
 
1358
1685
  // packages/migrate/src/exclusions.ts
1359
1686
  import { randomUUID } from "crypto";
1360
- import { VibeError as VibeError7, sqlObjectKey as sqlObjectKey3 } from "@vibeorm/schema";
1687
+ import { VibeError as VibeError8, sqlObjectKey as sqlObjectKey4 } from "@vibeorm/schema";
1361
1688
  import { postgresDialect as postgresDialect2, renderExclusionCatalog, renderExclusionProbe } from "@vibeorm/sql";
1362
1689
  async function deparseExclusion(params) {
1363
1690
  const probe = `vibe_exclusion_${randomUUID().replaceAll("-", "")}`;
1364
1691
  await params.executor({ text: "SAVEPOINT vibe_exclusion_probe" });
1365
1692
  try {
1366
- for (const text of renderExclusionProbe({ dialect: postgresDialect2, table: params.table, probe, definition: params.definition }))
1693
+ for (const text of renderExclusionProbe({ dialect: postgresDialect2, table: params.table, ...params.namespace === undefined ? {} : { namespace: params.namespace }, probe, definition: params.definition }))
1367
1694
  await params.executor({ text });
1368
1695
  const rows = await params.executor({ text: renderExclusionCatalog({ dialect: postgresDialect2, probe }) });
1369
1696
  if (typeof rows[0]?.definition !== "string")
1370
- throw new VibeError7({ code: "VIBE_MIGRATION", message: "Exclusion probe unavailable", meta: { reason: "exclusion-inspection-unavailable" } });
1697
+ throw new VibeError8({ code: "VIBE_MIGRATION", message: "Exclusion probe unavailable", meta: { reason: "exclusion-inspection-unavailable" } });
1371
1698
  return rows[0].definition;
1372
1699
  } finally {
1373
1700
  await params.executor({ text: "ROLLBACK TO SAVEPOINT vibe_exclusion_probe" });
@@ -1378,7 +1705,7 @@ async function prepareExclusionDiff(params) {
1378
1705
  const models = [];
1379
1706
  for (const model of params.from.models) {
1380
1707
  const table = model.dbName ?? model.name;
1381
- const target = params.to.models.find((entry) => (entry.dbName ?? entry.name) === table);
1708
+ const target = params.to.models.find((entry) => (entry.dbName ?? entry.name) === table && (entry.namespace ?? "public") === (model.namespace ?? "public"));
1382
1709
  const exclusions = [];
1383
1710
  for (const before of model.exclusions ?? []) {
1384
1711
  const next = target?.exclusions?.find((entry) => entry.name === before.name);
@@ -1386,22 +1713,22 @@ async function prepareExclusionDiff(params) {
1386
1713
  exclusions.push(before);
1387
1714
  continue;
1388
1715
  }
1389
- const identity = { kind: "constraint", constraintType: "exclusion", table: { namespace: "public", name: table }, name: before.name };
1716
+ const identity = { kind: "constraint", constraintType: "exclusion", table: { namespace: model.namespace ?? "public", name: table }, name: before.name };
1390
1717
  if (params.probe === "rollback") {
1391
- const normalized = await deparseExclusion({ executor: params.executor, table, definition: next.definition });
1718
+ const normalized = await deparseExclusion({ executor: params.executor, table, ...model.namespace === undefined ? {} : { namespace: model.namespace }, definition: next.definition });
1392
1719
  exclusions.push(normalized === before.definition ? next : { name: before.name, definition: before.definition, requiredExtensions: before.requiredExtensions });
1393
1720
  continue;
1394
1721
  }
1395
- const previous = params.previous?.schema.models.find((entry) => (entry.dbName ?? entry.name) === table)?.exclusions?.find((entry) => entry.name === before.name);
1722
+ const previous = params.previous?.schema.models.find((entry) => (entry.dbName ?? entry.name) === table && (entry.namespace ?? "public") === (model.namespace ?? "public"))?.exclusions?.find((entry) => entry.name === before.name);
1396
1723
  if (previous !== undefined) {
1397
1724
  exclusions.push(previous);
1398
1725
  continue;
1399
1726
  }
1400
- if (params.replaceChecks?.some((entry) => sqlObjectKey3({ identity: entry }) === sqlObjectKey3({ identity }))) {
1727
+ if (params.replaceChecks?.some((entry) => sqlObjectKey4({ identity: entry }) === sqlObjectKey4({ identity }))) {
1401
1728
  exclusions.push({ name: before.name, definition: before.definition, requiredExtensions: before.requiredExtensions });
1402
1729
  continue;
1403
1730
  }
1404
- throw new VibeError7({ code: "VIBE_MIGRATION", message: "Exclusion equivalence requires a rollback probe or explicit replacement", meta: { reason: "explicit-replacement-required", identity } });
1731
+ throw new VibeError8({ code: "VIBE_MIGRATION", message: "Exclusion equivalence requires a rollback probe or explicit replacement", meta: { reason: "explicit-replacement-required", identity } });
1405
1732
  }
1406
1733
  models.push({ ...model, exclusions });
1407
1734
  }
@@ -1410,20 +1737,20 @@ async function prepareExclusionDiff(params) {
1410
1737
  async function verifyExclusions(params) {
1411
1738
  if (!params.expected.schema.models.some((model) => (model.exclusions?.length ?? 0) > 0))
1412
1739
  return [];
1413
- const rows = await params.executor({ text: renderExclusionCatalog({ dialect: postgresDialect2 }) });
1740
+ const rows = (await Promise.all([...new Set(params.expected.schema.models.map((model) => model.namespace ?? "public"))].map(async (namespace) => (await params.executor({ text: renderExclusionCatalog({ dialect: postgresDialect2, namespace }) })).map((row) => ({ ...row, namespace }))))).flat();
1414
1741
  const objects = [];
1415
1742
  for (const model of params.expected.schema.models)
1416
1743
  for (const exclusion of model.exclusions ?? []) {
1417
1744
  const table = model.dbName ?? model.name;
1418
- const identity = { kind: "constraint", constraintType: "exclusion", table: { namespace: "public", name: table }, name: exclusion.name };
1419
- const row = rows.find((entry) => entry.table_name === table && entry.constraint_name === exclusion.name);
1745
+ const identity = { kind: "constraint", constraintType: "exclusion", table: { namespace: model.namespace ?? "public", name: table }, name: exclusion.name };
1746
+ const row = rows.find((entry) => entry.namespace === (model.namespace ?? "public") && entry.table_name === table && entry.constraint_name === exclusion.name);
1420
1747
  if (row === undefined) {
1421
1748
  objects.push({ identity, ownership: "schema", state: "missing", differences: [] });
1422
1749
  continue;
1423
1750
  }
1424
1751
  const differences = ["validated", "valid", "ready"].flatMap((attribute) => row[attribute] === true ? [] : [{ attribute, expected: true, actual: row[attribute] }]);
1425
- const independentlyVerified = params.expected.definitions.some((entry) => sqlObjectKey3({ identity: entry.identity }) === sqlObjectKey3({ identity }));
1426
- const definition = independentlyVerified ? row.definition : params.probe === "rollback" ? await deparseExclusion({ executor: params.executor, table, definition: exclusion.definition }) : (exclusion.normalizationSource === exclusion.definition ? exclusion.normalizedDefinition : undefined) ?? exclusion.definition;
1752
+ const independentlyVerified = params.expected.definitions.some((entry) => sqlObjectKey4({ identity: entry.identity }) === sqlObjectKey4({ identity }));
1753
+ const definition = independentlyVerified ? row.definition : params.probe === "rollback" ? await deparseExclusion({ executor: params.executor, table, ...model.namespace === undefined ? {} : { namespace: model.namespace }, definition: exclusion.definition }) : (exclusion.normalizationSource === exclusion.definition ? exclusion.normalizedDefinition : undefined) ?? exclusion.definition;
1427
1754
  if (definition !== row.definition)
1428
1755
  differences.push({ attribute: "definition", expected: definition, actual: row.definition });
1429
1756
  objects.push({ identity, ownership: "schema", state: differences.length === 0 ? "matching" : params.probe === "rollback" ? "changed" : "unavailable", differences });
@@ -1433,22 +1760,19 @@ async function verifyExclusions(params) {
1433
1760
 
1434
1761
  // packages/migrate/src/inspect-objects.ts
1435
1762
  import { randomUUID as randomUUID2 } from "crypto";
1436
- import { VibeError as VibeError15, sqlObjectKey as sqlObjectKey4, DIALECT_CAPABILITIES as DIALECT_CAPABILITIES2 } from "@vibeorm/schema";
1437
- import { getSqlDialect, renderMigrationObjectBatch, renderMigrationObjectQuery, renderCheckProbe, renderMigrationNamespace, renderMigrationDependencies, renderMysqlObjectMembers, renderMysqlCheckProbe, sqliteColumnClauses as sqliteColumnClauses2, renderMigrationVisibility, sqliteTableConstraints as sqliteTableConstraints2, renderSqliteColumnInventory } from "@vibeorm/sql";
1763
+ import { VibeError as VibeError16, sqlObjectKey as sqlObjectKey5, sqlObjectNamespace as sqlObjectNamespace2, DIALECT_CAPABILITIES as DIALECT_CAPABILITIES2, qualifiedSqlName as qualifiedSqlName8 } from "@vibeorm/schema";
1764
+ import { getSqlDialect as getSqlDialect2, renderMigrationObjectBatch, renderMigrationObjectQuery as renderMigrationObjectQuery2, renderCheckProbe, renderMigrationNamespace, renderMigrationDependencies, renderMysqlObjectMembers, renderMysqlCheckProbe, sqliteColumnClauses as sqliteColumnClauses2, renderMigrationVisibility, sqliteTableConstraints as sqliteTableConstraints2, renderSqliteColumnInventory } from "@vibeorm/sql";
1438
1765
 
1439
1766
  // packages/migrate/src/introspect/postgres.ts
1440
1767
  import { renderExclusionCatalog as renderExclusionCatalog2 } from "@vibeorm/sql";
1441
1768
  import { parsePgArrayLiteral } from "@vibeorm/sql";
1442
1769
  import { scalarListDefault as scalarListDefault2 } from "@vibeorm/schema";
1443
1770
  import { exactIntegerLiteral } from "@vibeorm/schema";
1444
- import { postgresDialect as postgresDialect6, renderRlsReadinessQuery } from "@vibeorm/sql";
1445
- import { VibeError as VibeError12 } from "@vibeorm/schema";
1446
-
1447
- // packages/migrate/src/journal-table.ts
1448
- var MIGRATIONS_TABLE_NAME = "_vibe_migrations";
1771
+ import { postgresDialect as postgresDialect6, renderRlsReadinessQuery, renderPostgresPhysicalCatalog, renderPostgresIndexKeys, renderMigrationObjectQuery } from "@vibeorm/sql";
1772
+ import { VibeError as VibeError13, assertSqlNamespace as assertSqlNamespace2, isReservedClientName } from "@vibeorm/schema";
1449
1773
 
1450
1774
  // packages/migrate/src/introspect/shared.ts
1451
- import { VibeError as VibeError8 } from "@vibeorm/schema";
1775
+ import { VibeError as VibeError9 } from "@vibeorm/schema";
1452
1776
  function detectJoinTable(params) {
1453
1777
  const { table, columnNames, fkGroups, indexes } = params;
1454
1778
  if (!table.startsWith("_"))
@@ -1466,8 +1790,8 @@ function detectJoinTable(params) {
1466
1790
  if (fkA.refTable > fkB.refTable || tableFks.some((fk) => !fk.required || fk.onDelete !== "Cascade" || fk.onUpdate !== "Cascade"))
1467
1791
  return null;
1468
1792
  const tableIndexes = indexes.filter((index) => index.table === table);
1469
- const canonicalAb = (index) => index.unique && index.kind === "btree" && index.where === undefined && index.nullsNotDistinct !== true && index.columns.length === 2 && index.columns[0]?.name === "A" && index.columns[1]?.name === "B" && index.columns.every((column) => !column.desc);
1470
- const canonicalB = (index) => !index.unique && index.kind === "btree" && index.where === undefined && index.columns.length === 1 && index.columns[0]?.name === "B" && !index.columns[0].desc;
1793
+ const canonicalAb = (index) => index.unique && index.kind === "btree" && index.where === undefined && index.nullsNotDistinct !== true && index.columns.length === 2 && index.columns[0]?.name === "A" && index.columns[1]?.name === "B" && index.columns.every((column) => !column.desc && column.operatorClass === undefined);
1794
+ const canonicalB = (index) => !index.unique && index.kind === "btree" && index.where === undefined && index.columns.length === 1 && index.columns[0]?.name === "B" && !index.columns[0].desc && index.columns[0].operatorClass === undefined;
1471
1795
  if (tableIndexes.length !== 2 || tableIndexes.filter(canonicalAb).length !== 1 || tableIndexes.filter(canonicalB).length !== 1)
1472
1796
  return null;
1473
1797
  const sorted = [fkA.refTable, fkB.refTable].sort();
@@ -1487,7 +1811,7 @@ function addFkRelations(params) {
1487
1811
  const primary = target?.primaryKey?.fields ?? target?.fields.filter((field) => field.isId).map((field) => field.name) ?? [];
1488
1812
  const columns = primary.map((name) => target?.fields.find((field) => field.name === name)?.dbName ?? name);
1489
1813
  if (columns.length !== 1 || fk.refColumns.length !== 1 || columns[0] !== fk.refColumns[0])
1490
- throw new VibeError8({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Join table "${table}" does not reference the single-column primary key of "${fk.refTable}"; automatic join compression is unavailable`, meta: { feature: "implicitJoinReferencedKey", table, target: fk.refTable } });
1814
+ throw new VibeError9({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Join table "${table}" does not reference the single-column primary key of "${fk.refTable}"; automatic join compression is unavailable`, meta: { feature: "implicitJoinReferencedKey", table, target: fk.refTable } });
1491
1815
  }
1492
1816
  const relationsByModel = new Map;
1493
1817
  const takeName = nameAllocator({ models });
@@ -1499,9 +1823,9 @@ function addFkRelations(params) {
1499
1823
  const target = modelsByName.get(group.refTable);
1500
1824
  if (owner === undefined || target === undefined)
1501
1825
  continue;
1502
- const relationName = ambiguousPairs.has(modelPairKey({ a: group.table, b: group.refTable })) ? `${group.table}_${group.columns.join("_")}To${group.refTable}` : undefined;
1826
+ const relationName = ambiguousPairs.has(modelPairKey({ a: group.table, b: group.refTable })) ? `${owner.dbName ?? owner.name}_${group.columns.join("_")}To${target.dbName ?? target.name}` : undefined;
1503
1827
  const fkSide = {
1504
- name: takeName({ model: owner.name, base: lowerFirst(group.refTable) }),
1828
+ name: takeName({ model: owner.name, base: lowerFirst(target.dbName ?? target.name) }),
1505
1829
  model: owner.name,
1506
1830
  targetModel: group.refTable,
1507
1831
  type: "manyToOne",
@@ -1516,7 +1840,7 @@ function addFkRelations(params) {
1516
1840
  };
1517
1841
  pushRelation({ relationsByModel, relation: fkSide });
1518
1842
  const listSide = {
1519
- name: takeName({ model: target.name, base: lowerFirst(group.table) }),
1843
+ name: takeName({ model: target.name, base: lowerFirst(owner.dbName ?? owner.name) }),
1520
1844
  model: target.name,
1521
1845
  targetModel: group.table,
1522
1846
  type: "oneToMany",
@@ -1542,7 +1866,7 @@ function addJoinTableRelations(params) {
1542
1866
  pushRelation({
1543
1867
  relationsByModel,
1544
1868
  relation: {
1545
- name: takeName({ model: modelA.name, base: lowerFirst(modelB.name) }),
1869
+ name: takeName({ model: modelA.name, base: lowerFirst(modelB.dbName ?? modelB.name) }),
1546
1870
  model: modelA.name,
1547
1871
  targetModel: modelB.name,
1548
1872
  type: "oneToMany",
@@ -1554,7 +1878,7 @@ function addJoinTableRelations(params) {
1554
1878
  pushRelation({
1555
1879
  relationsByModel,
1556
1880
  relation: {
1557
- name: takeName({ model: modelB.name, base: lowerFirst(modelA.name) }),
1881
+ name: takeName({ model: modelB.name, base: lowerFirst(modelA.dbName ?? modelA.name) }),
1558
1882
  model: modelB.name,
1559
1883
  targetModel: modelA.name,
1560
1884
  type: "oneToMany",
@@ -1635,14 +1959,14 @@ function lowerFirst(value) {
1635
1959
  }
1636
1960
 
1637
1961
  // packages/migrate/src/postgres-type-identity.ts
1638
- import { VibeError as VibeError9 } from "@vibeorm/schema";
1962
+ import { VibeError as VibeError10, qualifiedSqlName as qualifiedSqlName6 } from "@vibeorm/schema";
1639
1963
  import { postgresDialect as postgresDialect3, renderPostgresTypeIdentity } from "@vibeorm/sql";
1640
1964
  async function resolvePostgresTypeIdentities(params) {
1641
1965
  const unavailable = [];
1642
1966
  const models = [];
1643
1967
  for (const model of params.schema.models) {
1644
- const table = { namespace: "public", name: model.dbName ?? model.name };
1645
- const previous = params.previous?.models.find((entry) => (entry.dbName ?? entry.name) === table.name);
1968
+ const table = qualifiedSqlName6({ name: model.dbName ?? model.name, namespace: model.namespace });
1969
+ const previous = params.previous?.models.find((entry) => (entry.dbName ?? entry.name) === table.name && (entry.namespace ?? "public") === table.namespace);
1646
1970
  const fields = [];
1647
1971
  for (const field of model.fields) {
1648
1972
  if (field.isUnsupported !== true) {
@@ -1653,7 +1977,7 @@ async function resolvePostgresTypeIdentities(params) {
1653
1977
  const parsed = parseUnsupportedPostgresType({ raw: field.nativeType ?? "" });
1654
1978
  const evidence = field.nativeTypeIdentity;
1655
1979
  if (evidence !== undefined && (evidence.source !== field.nativeType || parsed?.namespace !== undefined && parsed.namespace !== evidence.namespace)) {
1656
- throw new VibeError9({ code: "VIBE_MIGRATION", message: "PostgreSQL type evidence no longer matches its declaration; recapture the independent reference", meta: { reason: "type-reference-invalid", shadowReusable: true, table: table.name, column } });
1980
+ throw new VibeError10({ code: "VIBE_MIGRATION", message: "PostgreSQL type evidence no longer matches its declaration; recapture the independent reference", meta: { reason: "type-reference-invalid", shadowReusable: true, table: table.name, column } });
1657
1981
  }
1658
1982
  const prior = previous?.fields.find((entry) => (entry.dbName ?? entry.name) === column && entry.isUnsupported === true);
1659
1983
  const priorType = parseUnsupportedPostgresType({ raw: prior?.nativeType ?? "" });
@@ -1663,7 +1987,7 @@ async function resolvePostgresTypeIdentities(params) {
1663
1987
  const source = params.mode === "catalog" ? { table, column } : { type: { name: parsed?.name ?? "", ...namespace === undefined && params.mode === "reference" ? {} : { namespace: namespace ?? "pg_catalog" } } };
1664
1988
  const query = renderPostgresTypeIdentity({ dialect: postgresDialect3, source });
1665
1989
  const rows = parsed === undefined && params.mode !== "catalog" ? [] : await params.executor({ text: query.text, values: [...query.values] }).then((rows) => rows, (cause) => {
1666
- throw new VibeError9({ code: "VIBE_MIGRATION", message: "PostgreSQL type identity inspection failed", meta: { reason: "type-inspection-unavailable", table: table.name, column }, cause });
1990
+ throw new VibeError10({ code: "VIBE_MIGRATION", message: "PostgreSQL type identity inspection failed", meta: { reason: "type-inspection-unavailable", table: table.name, column }, cause });
1667
1991
  });
1668
1992
  const resolved = rows.length === 1 ? rows[0] : undefined;
1669
1993
  namespace = typeof resolved?.namespace === "string" && (resolved.name === parsed?.name || parsed === undefined && params.mode === "catalog") ? resolved.namespace : undefined;
@@ -1681,11 +2005,11 @@ async function resolvePostgresTypeIdentities(params) {
1681
2005
  }
1682
2006
 
1683
2007
  // packages/migrate/src/introspect/postgres-rls.ts
1684
- import { VibeError as VibeError11, assertValidSchemaIR as assertValidSchemaIR3 } from "@vibeorm/schema";
2008
+ import { VibeError as VibeError12, assertValidSchemaIR as assertValidSchemaIR3 } from "@vibeorm/schema";
1685
2009
  import { NATIVE_RLS, postgresDialect as postgresDialect5, renderRlsPolicy as renderRlsPolicy2 } from "@vibeorm/sql";
1686
2010
 
1687
2011
  // packages/migrate/src/ddl/rls.ts
1688
- import { VibeError as VibeError10, assertValidSchemaIR as assertValidSchemaIR2, unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
2012
+ import { VibeError as VibeError11, assertValidSchemaIR as assertValidSchemaIR2, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
1689
2013
  import * as sql from "@vibeorm/sql";
1690
2014
  function isRlsProtected(params) {
1691
2015
  return params.model.rls !== undefined && params.model.rls !== "unprotected";
@@ -1715,7 +2039,18 @@ function assertRlsDialect(params) {
1715
2039
  if (params.schema.rls === undefined && !params.schema.models.some((model) => model.rls !== undefined))
1716
2040
  return;
1717
2041
  if (!sql.DIALECT_CAPABILITIES[params.dialect].nativeRowLevelSecurity) {
1718
- throw unsupportedCapability5({ dialect: params.dialect, feature: "nativeRowLevelSecurity" });
2042
+ throw unsupportedCapability7({ dialect: params.dialect, feature: "nativeRowLevelSecurity" });
2043
+ }
2044
+ for (const model of params.schema.models) {
2045
+ if (model.namespace !== undefined && model.namespace !== "public" && isRlsProtected({ model }))
2046
+ throw unsupportedCapability7({ dialect: params.dialect, feature: "fixedNamespaceNativeRls", hint: "Named package schemas do not support native RLS placement in this contract" });
2047
+ const policy = model.rls;
2048
+ if (isRlsProtected({ model }) && typeof policy === "object" && "through" in policy) {
2049
+ const relation = model.relations.find((entry) => entry.name === policy.through);
2050
+ const parent = params.schema.models.find((entry) => entry.name === relation?.targetModel);
2051
+ if (parent?.namespace !== undefined && parent.namespace !== "public")
2052
+ throw unsupportedCapability7({ dialect: params.dialect, feature: "fixedNamespaceNativeRls" });
2053
+ }
1719
2054
  }
1720
2055
  assertValidSchemaIR2({ schema: params.schema });
1721
2056
  }
@@ -1723,8 +2058,9 @@ function withRlsSteps(params) {
1723
2058
  const { from, to, steps } = params;
1724
2059
  const before = orderedRlsModels({ schema: from });
1725
2060
  const after = orderedRlsModels({ schema: to });
1726
- const beforeByTable = new Map(before.map((model) => [model.dbName ?? model.name, model]));
1727
- const afterByTable = new Map(after.map((model) => [model.dbName ?? model.name, model]));
2061
+ const tableKey = (params) => physicalNameKey(params);
2062
+ const beforeByTable = new Map(before.map((model) => [tableKey({ name: model.dbName ?? model.name, namespace: model.namespace }), model]));
2063
+ const afterByTable = new Map(after.map((model) => [tableKey({ name: model.dbName ?? model.name, namespace: model.namespace }), model]));
1728
2064
  const changed = new Set;
1729
2065
  const policyChanges = new Set;
1730
2066
  for (const [table, model] of beforeByTable) {
@@ -1736,7 +2072,7 @@ function withRlsSteps(params) {
1736
2072
  }
1737
2073
  for (const step of steps) {
1738
2074
  if (step.kind === "dropTable" || step.kind === "dropColumn" || step.kind === "alterColumn" || step.kind === "dropForeignKey" || step.kind === "dropPrimaryKey")
1739
- changed.add(step.table);
2075
+ changed.add(tableKey({ name: step.table, namespace: step.namespace }));
1740
2076
  }
1741
2077
  for (const schema of [from, to]) {
1742
2078
  for (const model of orderedRlsModels({ schema })) {
@@ -1745,9 +2081,9 @@ function withRlsSteps(params) {
1745
2081
  const through = model.rls.through;
1746
2082
  const relation = model.relations.find((entry) => entry.name === through);
1747
2083
  const parent = schema.models.find((entry) => entry.name === relation?.targetModel);
1748
- if (parent !== undefined && changed.has(parent.dbName ?? parent.name)) {
1749
- changed.add(model.dbName ?? model.name);
1750
- policyChanges.add(model.dbName ?? model.name);
2084
+ if (parent !== undefined && changed.has(tableKey({ name: parent.dbName ?? parent.name, namespace: parent.namespace }))) {
2085
+ changed.add(tableKey({ name: model.dbName ?? model.name, namespace: model.namespace }));
2086
+ policyChanges.add(tableKey({ name: model.dbName ?? model.name, namespace: model.namespace }));
1751
2087
  }
1752
2088
  }
1753
2089
  }
@@ -1756,20 +2092,22 @@ function withRlsSteps(params) {
1756
2092
  const removals = [];
1757
2093
  for (const model of [...before].reverse()) {
1758
2094
  const table = model.dbName ?? model.name;
1759
- if (!changed.has(table))
2095
+ const key = tableKey({ name: table, namespace: model.namespace });
2096
+ if (!changed.has(key))
1760
2097
  continue;
1761
- const target = afterByTable.get(table);
1762
- const securityChange = target === undefined ? `remove native RLS protection from "${table}"` : policyChanges.has(table) && target.rls !== "deny" ? `replace native RLS policy on "${table}" (access may widen)` : undefined;
2098
+ const target = afterByTable.get(key);
2099
+ const securityChange = target === undefined ? `remove native RLS protection from "${table}"` : policyChanges.has(key) && target.rls !== "deny" ? `replace native RLS policy on "${table}" (access may widen)` : undefined;
1763
2100
  drops.push({ kind: "dropPolicy", table, destructive: false, ...securityChange === undefined ? {} : { securityChange } });
1764
- if (target === undefined && to.models.some((entry) => (entry.dbName ?? entry.name) === table)) {
2101
+ if (target === undefined && to.models.some((entry) => tableKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace }) === key)) {
1765
2102
  removals.push({ kind: "noForceRls", table, destructive: false }, { kind: "disableRls", table, destructive: false });
1766
2103
  }
1767
2104
  }
1768
2105
  for (const model of after) {
1769
2106
  const table = model.dbName ?? model.name;
1770
- if (beforeByTable.has(table) && !changed.has(table))
2107
+ const key = tableKey({ name: table, namespace: model.namespace });
2108
+ if (beforeByTable.has(key) && !changed.has(key))
1771
2109
  continue;
1772
- if (!beforeByTable.has(table))
2110
+ if (!beforeByTable.has(key))
1773
2111
  installs.push({ kind: "enableRls", table, destructive: false }, { kind: "forceRls", table, destructive: false });
1774
2112
  installs.push({ kind: "createPolicy", table, schema: to, model: model.name, destructive: false });
1775
2113
  }
@@ -1781,7 +2119,7 @@ function isRlsStep(params) {
1781
2119
  function renderRlsStep(params) {
1782
2120
  const { step, dialect } = params;
1783
2121
  if (!sql.DIALECT_CAPABILITIES[dialect].nativeRowLevelSecurity)
1784
- throw unsupportedCapability5({ dialect, feature: "nativeRowLevelSecurity" });
2122
+ throw unsupportedCapability7({ dialect, feature: "nativeRowLevelSecurity" });
1785
2123
  const common = { dialect: sql.postgresDialect, table: "table" in step ? step.table ?? "" : "" };
1786
2124
  switch (step.kind) {
1787
2125
  case "enableRls":
@@ -1797,11 +2135,11 @@ function renderRlsStep(params) {
1797
2135
  const renderers = sql;
1798
2136
  const renderer = renderers[name];
1799
2137
  if (renderer === undefined)
1800
- throw unsupportedCapability5({ dialect, feature: "nativeRowLevelSecurity", hint: `The SQL package must provide ${name} before this migration can execute` });
2138
+ throw unsupportedCapability7({ dialect, feature: "nativeRowLevelSecurity", hint: `The SQL package must provide ${name} before this migration can execute` });
1801
2139
  return [renderer(common).text];
1802
2140
  }
1803
2141
  default:
1804
- throw new VibeError10({ code: "VIBE_MIGRATION", message: "Expected a native RLS migration step" });
2142
+ throw new VibeError11({ code: "VIBE_MIGRATION", message: "Expected a native RLS migration step" });
1805
2143
  }
1806
2144
  }
1807
2145
  function securityChangesOf(params) {
@@ -1810,7 +2148,7 @@ function securityChangesOf(params) {
1810
2148
  function assertSecurityAcknowledgement(params) {
1811
2149
  if (params.securityChanges.length === 0 || params.acceptSecurityChanges === true)
1812
2150
  return;
1813
- throw new VibeError10({
2151
+ throw new VibeError11({
1814
2152
  code: "VIBE_MIGRATION",
1815
2153
  message: `Security changes require acceptSecurityChanges: true; acceptDataLoss does not authorize them:
1816
2154
  ${params.securityChanges.join(`
@@ -1849,7 +2187,7 @@ async function readPostgresRls(params) {
1849
2187
  return [...tables.values()];
1850
2188
  }
1851
2189
  function conflict(params) {
1852
- throw new VibeError11({
2190
+ throw new VibeError12({
1853
2191
  code: "VIBE_MIGRATION",
1854
2192
  message: `Native RLS on public.${params.table} cannot be managed safely (${params.attributes.join(", ")}). Review the live policies and restore the deployed declaration, or remove conflicting policies explicitly with administrator credentials. Supply rlsSchema/previousSchema for the deployed policy; no policy is deleted or adopted automatically.`,
1855
2193
  meta: params
@@ -1860,10 +2198,11 @@ async function verifyPostgresRls(params) {
1860
2198
  if (schema !== undefined && schema.rls !== undefined)
1861
2199
  assertValidSchemaIR3({ schema });
1862
2200
  await executor({ text: "SET LOCAL search_path = ''" });
1863
- const declared = new Map((schema?.models ?? []).map((model) => [model.dbName ?? model.name, model]));
2201
+ const declared = (schema?.models ?? []).filter((model) => model.namespace === undefined || model.namespace === "public");
2202
+ const byTable = new Map(declared.map((model) => [model.dbName ?? model.name, model]));
1864
2203
  let tables = await readPostgresRls({ executor });
1865
2204
  for (const state of tables) {
1866
- const model = declared.get(state.table);
2205
+ const model = byTable.get(state.table);
1867
2206
  if (!state.enabled && !state.force && state.policies.length === 0 && (model === undefined || !isRlsProtected({ model })))
1868
2207
  continue;
1869
2208
  await executor({ text: `LOCK TABLE "public".${postgresDialect5.quoteIdent({ name: state.table })} IN ACCESS EXCLUSIVE MODE` });
@@ -1871,7 +2210,7 @@ async function verifyPostgresRls(params) {
1871
2210
  tables = await readPostgresRls({ executor });
1872
2211
  const matched = new Set;
1873
2212
  for (const state of tables) {
1874
- const model = declared.get(state.table);
2213
+ const model = byTable.get(state.table);
1875
2214
  const active = state.enabled || state.force || state.policies.length > 0;
1876
2215
  if (schema === undefined && active)
1877
2216
  conflict({ table: state.table, reason: "rls-unmanaged", attributes: ["security metadata cannot be exported losslessly without a trusted declaration"] });
@@ -1918,7 +2257,7 @@ async function verifyPostgresRls(params) {
1918
2257
  matched.add(state.table);
1919
2258
  }
1920
2259
  if (params.requireInstalled === true) {
1921
- for (const [table, model] of declared) {
2260
+ for (const [table, model] of byTable) {
1922
2261
  if (isRlsProtected({ model }) && !matched.has(table))
1923
2262
  conflict({ table, reason: "rls-drift", attributes: ["missing table"] });
1924
2263
  }
@@ -1930,8 +2269,9 @@ function projectPostgresRls(params) {
1930
2269
  if (declared?.rls === undefined || matched.size === 0)
1931
2270
  return physical;
1932
2271
  const models = physical.models.map((model) => {
1933
- const source = declared.models.find((entry) => (entry.dbName ?? entry.name) === model.name);
1934
- if (source === undefined || !matched.has(model.name))
2272
+ const physicalName = model.dbName ?? model.name;
2273
+ const source = declared.models.find((entry) => (entry.dbName ?? entry.name) === physicalName && (entry.namespace ?? "public") === (model.namespace ?? "public"));
2274
+ if (source === undefined || model.namespace !== undefined && model.namespace !== "public" || !matched.has(physicalName))
1935
2275
  return { ...model, rls: "unprotected" };
1936
2276
  const sourcePolicy = source.rls;
1937
2277
  let rls = "deny";
@@ -1942,7 +2282,8 @@ function projectPostgresRls(params) {
1942
2282
  const target = declared.models.find((entry) => entry.name === sourceRelation.targetModel);
1943
2283
  const fields = sourceRelation.fields.map((name) => source.fields.find((entry) => entry.name === name)?.dbName ?? name);
1944
2284
  const references = sourceRelation.references.map((name) => target.fields.find((entry) => entry.name === name)?.dbName ?? name);
1945
- const relation = model.relations.find((entry) => entry.targetModel === (target.dbName ?? target.name) && JSON.stringify(entry.fields) === JSON.stringify(fields) && JSON.stringify(entry.references) === JSON.stringify(references));
2285
+ const physicalTarget = physical.models.find((entry) => (entry.dbName ?? entry.name) === (target.dbName ?? target.name) && (entry.namespace ?? "public") === (target.namespace ?? "public"));
2286
+ const relation = model.relations.find((entry) => entry.targetModel === physicalTarget?.name && JSON.stringify(entry.fields) === JSON.stringify(fields) && JSON.stringify(entry.references) === JSON.stringify(references));
1946
2287
  if (relation === undefined)
1947
2288
  conflict({ table: model.name, reason: "rls-drift", attributes: ["through foreign key"] });
1948
2289
  rls = { through: relation.name };
@@ -1953,146 +2294,6 @@ function projectPostgresRls(params) {
1953
2294
  }
1954
2295
 
1955
2296
  // packages/migrate/src/introspect/postgres.ts
1956
- var EXTENSION_OWNED_FILTER = `NOT EXISTS (
1957
- SELECT 1
1958
- FROM pg_catalog.pg_depend d
1959
- JOIN pg_catalog.pg_class c ON c.oid = d.objid
1960
- JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
1961
- WHERE d.classid = 'pg_class'::regclass
1962
- AND d.deptype = 'e'
1963
- AND n.nspname = 'public'
1964
- AND c.relname = table_name
1965
- )`;
1966
- var TABLES_QUERY = `
1967
- SELECT table_name
1968
- FROM information_schema.tables
1969
- WHERE table_schema = 'public'
1970
- AND table_type = 'BASE TABLE'
1971
- AND table_name <> '${MIGRATIONS_TABLE_NAME}'
1972
- AND ${EXTENSION_OWNED_FILTER}
1973
- ORDER BY table_name;
1974
- `;
1975
- var VIEWS_QUERY = `
1976
- SELECT table_name
1977
- FROM information_schema.views
1978
- WHERE table_schema = 'public'
1979
- AND ${EXTENSION_OWNED_FILTER}
1980
- ORDER BY table_name;
1981
- `;
1982
- var COLUMNS_QUERY = `
1983
- SELECT
1984
- table_name,
1985
- column_name,
1986
- data_type,
1987
- udt_name,
1988
- is_nullable,
1989
- is_identity,
1990
- is_generated,
1991
- collation_name,
1992
- column_default,
1993
- datetime_precision,
1994
- character_maximum_length,
1995
- numeric_precision,
1996
- numeric_scale,
1997
- ordinal_position,
1998
- (
1999
- SELECT format_type(a.atttypid, a.atttypmod)
2000
- FROM pg_attribute a
2001
- JOIN pg_class c ON c.oid = a.attrelid
2002
- JOIN pg_namespace n ON n.oid = c.relnamespace
2003
- WHERE n.nspname = 'public'
2004
- AND c.relname = columns.table_name
2005
- AND a.attname = columns.column_name
2006
- ) AS full_type
2007
- FROM information_schema.columns AS columns
2008
- WHERE table_schema = 'public'
2009
- ORDER BY table_name, ordinal_position;
2010
- `;
2011
- var CONSTRAINTS_QUERY = `
2012
- SELECT
2013
- c.conname AS constraint_name,
2014
- CASE c.contype WHEN 'p' THEN 'PRIMARY KEY' ELSE 'UNIQUE' END AS constraint_type,
2015
- cl.relname AS table_name,
2016
- att.attname AS column_name
2017
- FROM pg_catalog.pg_constraint c
2018
- JOIN pg_catalog.pg_class cl ON cl.oid = c.conrelid
2019
- JOIN pg_catalog.pg_namespace n ON n.oid = cl.relnamespace
2020
- JOIN unnest(c.conkey) WITH ORDINALITY AS k(attnum, ord) ON true
2021
- JOIN pg_catalog.pg_attribute att ON att.attrelid = c.conrelid AND att.attnum = k.attnum
2022
- WHERE c.contype IN ('p', 'u')
2023
- AND n.nspname = 'public'
2024
- ORDER BY cl.relname, c.conname, k.ord;
2025
- `;
2026
- var CHECKS_QUERY = `
2027
- SELECT
2028
- cl.relname AS table_name,
2029
- c.conname AS constraint_name,
2030
- pg_get_constraintdef(c.oid) AS definition
2031
- FROM pg_catalog.pg_constraint c
2032
- JOIN pg_catalog.pg_class cl ON cl.oid = c.conrelid
2033
- JOIN pg_catalog.pg_namespace n ON n.oid = cl.relnamespace
2034
- WHERE c.contype = 'c'
2035
- AND n.nspname = 'public'
2036
- ORDER BY cl.relname, c.conname;
2037
- `;
2038
- var FOREIGN_KEYS_QUERY = `
2039
- SELECT
2040
- c.conname AS constraint_name,
2041
- cl.relname AS table_name,
2042
- att.attname AS column_name,
2043
- fcl.relname AS foreign_table_name,
2044
- fatt.attname AS foreign_column_name,
2045
- c.confdeltype AS on_delete_char,
2046
- c.confupdtype AS on_update_char,
2047
- c.convalidated AS validated,
2048
- c.condeferrable AS deferrable,
2049
- (SELECT bool_and(t.tgenabled IN ('O', 'A')) FROM pg_catalog.pg_trigger t WHERE t.tgconstraint = c.oid) AS triggers_enabled
2050
- FROM pg_catalog.pg_constraint c
2051
- JOIN pg_catalog.pg_class cl ON cl.oid = c.conrelid
2052
- JOIN pg_catalog.pg_namespace n ON n.oid = cl.relnamespace
2053
- JOIN pg_catalog.pg_class fcl ON fcl.oid = c.confrelid
2054
- JOIN pg_catalog.pg_namespace fn ON fn.oid = fcl.relnamespace
2055
- JOIN unnest(c.conkey) WITH ORDINALITY AS lk(attnum, ord) ON true
2056
- JOIN unnest(c.confkey) WITH ORDINALITY AS rk(attnum, ord) ON lk.ord = rk.ord
2057
- JOIN pg_catalog.pg_attribute att ON att.attrelid = c.conrelid AND att.attnum = lk.attnum
2058
- JOIN pg_catalog.pg_attribute fatt ON fatt.attrelid = c.confrelid AND fatt.attnum = rk.attnum
2059
- WHERE c.contype = 'f'
2060
- AND n.nspname = 'public'
2061
- AND fn.nspname = 'public'
2062
- ORDER BY cl.relname, c.conname, lk.ord;
2063
- `;
2064
- var ENUMS_QUERY = `
2065
- SELECT
2066
- t.typname AS type_name,
2067
- e.enumlabel AS enum_value,
2068
- e.enumsortorder AS sort_order
2069
- FROM pg_type t
2070
- JOIN pg_enum e ON t.oid = e.enumtypid
2071
- JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
2072
- WHERE n.nspname = 'public'
2073
- ORDER BY t.typname, e.enumsortorder;
2074
- `;
2075
- var INDEXES_QUERY = `
2076
- SELECT
2077
- tablename AS table_name,
2078
- indexname AS index_name,
2079
- indexdef AS index_def,
2080
- con.conname AS backing_constraint,
2081
- con.contype AS backing_constraint_type,
2082
- health.indisvalid AS valid,
2083
- health.indisready AS ready,
2084
- health.indimmediate AS immediate,
2085
- health.indexprs IS NULL AS plain_columns,
2086
- health.indnkeyatts AS key_count,
2087
- health.indnatts AS attribute_count
2088
- FROM pg_indexes ix
2089
- JOIN pg_catalog.pg_namespace ns ON ns.nspname = ix.schemaname
2090
- JOIN pg_catalog.pg_class idx ON idx.relnamespace = ns.oid AND idx.relname = ix.indexname
2091
- JOIN pg_catalog.pg_index health ON health.indexrelid = idx.oid
2092
- LEFT JOIN pg_catalog.pg_constraint con ON con.conindid = idx.oid AND con.contype IN ('p', 'u', 'x')
2093
- WHERE schemaname = 'public'
2094
- ORDER BY tablename, indexname;
2095
- `;
2096
2297
  async function introspectPostgres(params) {
2097
2298
  const { executor } = params;
2098
2299
  if (params.inTransaction !== true)
@@ -2101,14 +2302,14 @@ async function introspectPostgres(params) {
2101
2302
  const savedPath = await executor(renderRlsReadinessQuery({ dialect: postgresDialect6, query: "readSearchPath" }));
2102
2303
  const searchPath = savedPath[0]?.searchPath;
2103
2304
  if (typeof searchPath !== "string")
2104
- throw new VibeError12({ code: "VIBE_MIGRATION", message: "Cannot read the migration search_path" });
2305
+ throw new VibeError13({ code: "VIBE_MIGRATION", message: "Cannot read the migration search_path" });
2105
2306
  const matched = await verifyPostgresRls({ executor, ...params.rlsSchema === undefined ? {} : { schema: params.rlsSchema }, ...params.requireInstalledRls === undefined ? {} : { requireInstalled: params.requireInstalledRls } }).then((verified) => verified, (error) => {
2106
- if (error instanceof VibeError12)
2307
+ if (error instanceof VibeError13)
2107
2308
  throw error;
2108
- throw new VibeError12({ code: "VIBE_MIGRATION", message: "Native RLS verification could not complete. Check migration privileges and supply the deployed schema as previousSchema/rlsSchema before changing referenced columns.", meta: { reason: "rls-verification-unavailable" }, cause: error });
2309
+ throw new VibeError13({ code: "VIBE_MIGRATION", message: "Native RLS verification could not complete. Check migration privileges and supply the deployed schema as previousSchema/rlsSchema before changing referenced columns.", meta: { reason: "rls-verification-unavailable" }, cause: error });
2109
2310
  });
2110
2311
  await executor(renderRlsReadinessQuery({ dialect: postgresDialect6, query: "setSearchPath", searchPath }));
2111
- const physical = await introspectPhysicalPostgres({ executor });
2312
+ const physical = await introspectPhysicalPostgres({ executor, namespaces: params.namespaces ?? ["public", ...params.rlsSchema?.models.flatMap((model) => model.namespace === undefined ? [] : [model.namespace]) ?? []] });
2112
2313
  return projectPostgresRls({ physical, ...params.rlsSchema === undefined ? {} : { declared: params.rlsSchema }, matched });
2113
2314
  } finally {
2114
2315
  if (params.inTransaction !== true)
@@ -2117,17 +2318,51 @@ async function introspectPostgres(params) {
2117
2318
  }
2118
2319
  async function introspectPhysicalPostgres(params) {
2119
2320
  const { executor } = params;
2321
+ const namespaces = [...new Set(params.namespaces ?? ["public"])];
2322
+ const physicalNames = new Map;
2323
+ const enumIdentities = new Map;
2324
+ const namespaceObjects = [];
2325
+ const read = async (query) => {
2326
+ const groups = await Promise.all(namespaces.map(async (namespace) => {
2327
+ assertSqlNamespace2({ namespace, dialect: "postgres" });
2328
+ const sql = renderPostgresPhysicalCatalog({ dialect: postgresDialect6, namespace, query });
2329
+ const rows = await executor({ text: sql.text, values: [...sql.values] });
2330
+ return rows.map((row) => {
2331
+ const name = typeof row.table_name === "string" ? row.table_name : undefined;
2332
+ const table = name === undefined ? undefined : physicalNameKey({ namespace, name });
2333
+ if (table !== undefined && name !== undefined)
2334
+ physicalNames.set(table, { namespace, name });
2335
+ const typeName = typeof row.type_name === "string" ? row.type_name : undefined;
2336
+ const enumKey = typeName === undefined ? undefined : physicalNameKey({ namespace, name: typeName });
2337
+ if (enumKey !== undefined && typeName !== undefined)
2338
+ enumIdentities.set(enumKey, { namespace, name: typeName });
2339
+ return { ...row, ...table === undefined ? {} : { table_name: table }, ...enumKey === undefined ? {} : { type_name: enumKey }, ...typeof row.foreign_table_name === "string" ? { foreign_table_name: physicalNameKey({ namespace: typeof row.foreign_namespace === "string" ? row.foreign_namespace : namespace, name: row.foreign_table_name }) } : {} };
2340
+ });
2341
+ }));
2342
+ return groups.flat();
2343
+ };
2344
+ for (const namespace of namespaces) {
2345
+ if (namespace === "public")
2346
+ continue;
2347
+ const query = renderMigrationObjectQuery({ dialect: postgresDialect6, identity: { kind: "namespace", name: namespace } });
2348
+ if ((await executor({ text: query.text, values: [...query.values] })).length > 0)
2349
+ namespaceObjects.push({ id: `namespace:${namespace}`, dialect: "postgres", identity: { kind: "namespace", name: namespace }, create: [], drop: [], dependsOn: [] });
2350
+ }
2120
2351
  const [tableRows, viewRows, columnRows, constraintRows, fkRows, enumRows, indexRows, checkRows, exclusionRows] = await Promise.all([
2121
- executor({ text: TABLES_QUERY }),
2122
- executor({ text: VIEWS_QUERY }),
2123
- executor({ text: COLUMNS_QUERY }),
2124
- executor({ text: CONSTRAINTS_QUERY }),
2125
- executor({ text: FOREIGN_KEYS_QUERY }),
2126
- executor({ text: ENUMS_QUERY }),
2127
- executor({ text: INDEXES_QUERY }),
2128
- executor({ text: CHECKS_QUERY }),
2129
- executor({ text: renderExclusionCatalog2({ dialect: postgresDialect6 }) })
2352
+ read("tables"),
2353
+ read("views"),
2354
+ read("columns"),
2355
+ read("constraints"),
2356
+ read("foreignKeys"),
2357
+ read("enums"),
2358
+ read("indexes"),
2359
+ read("checks"),
2360
+ 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())
2130
2361
  ]);
2362
+ const keysQuery = renderPostgresIndexKeys({ dialect: postgresDialect6 });
2363
+ const indexKeys = await executor({ text: keysQuery.text, values: [...keysQuery.values] });
2364
+ 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))
2365
+ throw new VibeError13({ code: "VIBE_MIGRATION", message: "PostgreSQL index-key identity is incomplete", meta: { reason: "index-inspection-unavailable" } });
2131
2366
  const tables = tableRows.map((row) => row.table_name);
2132
2367
  const views = viewRows.map((row) => row.table_name);
2133
2368
  const columns = columnRows.map((row) => ({
@@ -2135,6 +2370,7 @@ async function introspectPhysicalPostgres(params) {
2135
2370
  name: row.column_name,
2136
2371
  dataType: row.data_type,
2137
2372
  udtName: row.udt_name,
2373
+ udtNamespace: typeof row.udt_schema === "string" ? row.udt_schema : "public",
2138
2374
  nullable: row.is_nullable === "YES",
2139
2375
  identity: row.is_identity === "YES",
2140
2376
  columnDefault: row.column_default ?? null,
@@ -2167,7 +2403,7 @@ async function introspectPhysicalPostgres(params) {
2167
2403
  const table = row.table_name;
2168
2404
  const constraintName = row.constraint_name;
2169
2405
  const columnName = row.column_name;
2170
- constraintNames.add(constraintName);
2406
+ constraintNames.add(JSON.stringify([table, constraintName]));
2171
2407
  if (row.constraint_type === "PRIMARY KEY") {
2172
2408
  pkNames.set(table, constraintName);
2173
2409
  const list = pkByTable.get(table);
@@ -2176,9 +2412,10 @@ async function introspectPhysicalPostgres(params) {
2176
2412
  else
2177
2413
  list.push(columnName);
2178
2414
  } else {
2179
- const entry = uniquesByConstraint.get(constraintName);
2415
+ const constraintKey = JSON.stringify([table, constraintName]);
2416
+ const entry = uniquesByConstraint.get(constraintKey);
2180
2417
  if (entry === undefined)
2181
- uniquesByConstraint.set(constraintName, { table, columns: [columnName] });
2418
+ uniquesByConstraint.set(constraintKey, { table, name: constraintName, columns: [columnName] });
2182
2419
  else
2183
2420
  entry.columns.push(columnName);
2184
2421
  }
@@ -2225,17 +2462,35 @@ async function introspectPhysicalPostgres(params) {
2225
2462
  for (const row of indexRows) {
2226
2463
  if (row.backing_constraint_type === "x")
2227
2464
  continue;
2228
- const parsed = parseIndexDef({
2229
- table: row.table_name,
2230
- name: row.index_name,
2231
- def: row.index_def
2232
- });
2233
- if (parsed !== null)
2234
- indexes.push(parsed);
2465
+ 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);
2466
+ if (row.plain_columns === true && operands.length !== Number(row.key_count))
2467
+ throw new VibeError13({ code: "VIBE_MIGRATION", message: "Plain index keys could not be read completely; refusing name-only operator-class introspection", meta: { reason: "index-inspection-unavailable", table: row.table_name, index: row.index_name } });
2468
+ if (operands.length > 0) {
2469
+ if (operands.every((key) => typeof key.column_name === "string"))
2470
+ indexes.push({
2471
+ table: String(row.table_name),
2472
+ name: String(row.index_name),
2473
+ unique: operands[0].unique === true,
2474
+ kind: String(operands[0].method),
2475
+ columns: operands.map((key) => ({ name: String(key.column_name), desc: key.descending === true, ...key.exact_default === true ? {} : { operatorClass: { namespace: String(key.operator_namespace), name: String(key.operator_name) } } })),
2476
+ ...typeof operands[0].predicate === "string" ? { where: operands[0].predicate } : {},
2477
+ ...operands[0].nulls_not_distinct === true ? { nullsNotDistinct: true } : {}
2478
+ });
2479
+ else {
2480
+ const parsed = parseIndexDef({ table: String(row.table_name), name: String(row.index_name), def: String(row.index_def) });
2481
+ if (parsed !== null)
2482
+ indexes.push(parsed);
2483
+ }
2484
+ } else {
2485
+ const parsed = parseIndexDef({ table: String(row.table_name), name: String(row.index_name), def: String(row.index_def) });
2486
+ if (parsed !== null)
2487
+ indexes.push(parsed);
2488
+ }
2235
2489
  }
2236
2490
  const joinTables = new Map;
2237
2491
  for (const table of tables) {
2238
- if (!table.startsWith("_"))
2492
+ const physicalTable = physicalNames.get(table);
2493
+ if (!physicalTable.name.startsWith("_"))
2239
2494
  continue;
2240
2495
  const tableFks = fkRows.filter((row) => row.table_name === table);
2241
2496
  const tableIndexes = indexRows.filter((row) => row.table_name === table);
@@ -2251,14 +2506,16 @@ async function introspectPhysicalPostgres(params) {
2251
2506
  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;
2252
2507
  }))
2253
2508
  continue;
2509
+ if (tableFks.some((row) => row.foreign_namespace !== undefined && row.foreign_namespace !== physicalTable.namespace))
2510
+ continue;
2254
2511
  const detected = detectJoinTable({
2255
- table,
2512
+ table: physicalTable.name,
2256
2513
  columnNames: columns.filter((column) => column.table === table).map((column) => column.name),
2257
- fkGroups,
2258
- indexes
2514
+ fkGroups: fkGroups.filter((group) => group.table === table).map((group) => ({ ...group, table: physicalTable.name, refTable: physicalNames.get(group.refTable)?.name ?? group.refTable })),
2515
+ indexes: indexes.filter((index) => index.table === table).map((index) => ({ ...index, table: physicalTable.name }))
2259
2516
  });
2260
2517
  if (detected !== null)
2261
- joinTables.set(table, detected);
2518
+ 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 }) });
2262
2519
  }
2263
2520
  const models = [];
2264
2521
  for (const table of tables) {
@@ -2266,6 +2523,7 @@ async function introspectPhysicalPostgres(params) {
2266
2523
  continue;
2267
2524
  models.push(buildModel({
2268
2525
  table,
2526
+ physicalTable: physicalNames.get(table).name,
2269
2527
  isView: false,
2270
2528
  columns,
2271
2529
  pkByTable,
@@ -2281,6 +2539,7 @@ async function introspectPhysicalPostgres(params) {
2281
2539
  for (const view of views) {
2282
2540
  models.push(buildModel({
2283
2541
  table: view,
2542
+ physicalTable: physicalNames.get(view).name,
2284
2543
  isView: true,
2285
2544
  columns,
2286
2545
  pkByTable,
@@ -2294,14 +2553,59 @@ async function introspectPhysicalPostgres(params) {
2294
2553
  }
2295
2554
  addFkRelations({ models, fkGroups, joinTables });
2296
2555
  addJoinTableRelations({ models, joinTables });
2556
+ const logicalNames = new Set([...physicalNames.values(), ...enumIdentities.values()].map((entry) => entry.name));
2557
+ const logicalName = (params) => {
2558
+ if (!params.duplicates && /^[$_\p{ID_Start}][$_\u200c\u200d\p{ID_Continue}]*$/u.test(params.name) && !isReservedClientName({ name: params.name.charAt(0).toLowerCase() + params.name.slice(1) }))
2559
+ return params.name;
2560
+ const hex = Array.from(new TextEncoder().encode(params.key), (byte) => byte.toString(16).padStart(2, "0")).join("");
2561
+ let name = `VibePhysical_${hex}`;
2562
+ while (logicalNames.has(name))
2563
+ name += "_";
2564
+ logicalNames.add(name);
2565
+ return name;
2566
+ };
2567
+ const modelNames = new Map(models.map((model) => {
2568
+ const identity = physicalNames.get(model.name);
2569
+ const duplicates = [...physicalNames.values()].filter((entry) => entry.name === identity.name).length > 1;
2570
+ return [model.name, logicalName({ key: model.name, name: identity.name, duplicates })];
2571
+ }));
2572
+ const enumLogicalNames = new Map(enums.map((entry) => {
2573
+ const identity = enumIdentities.get(entry.name);
2574
+ const duplicates = [...enumIdentities.values()].filter((candidate) => candidate.name === identity.name).length > 1;
2575
+ return [entry.name, logicalName({ key: entry.name, name: identity.name, duplicates })];
2576
+ }));
2577
+ const qualifiedModels = models.map((model) => {
2578
+ const identity = physicalNames.get(model.name);
2579
+ const name = modelNames.get(model.name);
2580
+ const { dbName: _physicalName, ...metadata } = model;
2581
+ return {
2582
+ ...metadata,
2583
+ name,
2584
+ ...name === identity.name ? {} : { dbName: identity.name },
2585
+ ...identity.namespace === "public" ? {} : { namespace: identity.namespace },
2586
+ fields: model.fields.map((field) => {
2587
+ if (typeof field.type === "string")
2588
+ return field;
2589
+ const identity = enumIdentities.get(field.type.enum);
2590
+ return { ...field, type: { enum: enumLogicalNames.get(field.type.enum), ...identity.namespace === "public" ? {} : { namespace: identity.namespace } } };
2591
+ }),
2592
+ relations: model.relations.map((relation) => ({ ...relation, name: /^[$_\p{ID_Start}][$_\u200c\u200d\p{ID_Continue}]*$/u.test(relation.name) ? relation.name : `relation_${Array.from(new TextEncoder().encode(relation.name), (byte) => byte.toString(16).padStart(2, "0")).join("")}`, model: name, targetModel: modelNames.get(relation.targetModel) ?? relation.targetModel }))
2593
+ };
2594
+ });
2595
+ const qualifiedEnums = enums.map((entry) => {
2596
+ const identity = enumIdentities.get(entry.name);
2597
+ const name = enumLogicalNames.get(entry.name);
2598
+ return { ...entry, name, ...name === identity.name ? {} : { dbName: identity.name }, ...identity.namespace === "public" ? {} : { namespace: identity.namespace } };
2599
+ });
2297
2600
  const resolved = await resolvePostgresTypeIdentities({ executor, mode: "catalog", schema: {
2298
2601
  datasource: { provider: "postgresql" },
2299
- models,
2300
- enums,
2301
- extensions: []
2602
+ models: qualifiedModels,
2603
+ enums: qualifiedEnums,
2604
+ extensions: [],
2605
+ ...namespaceObjects.length === 0 ? {} : { migrationObjects: namespaceObjects }
2302
2606
  } });
2303
2607
  if (resolved.unavailable.length > 0)
2304
- throw new VibeError12({ code: "VIBE_MIGRATION", message: "PostgreSQL column type identity could not be read", meta: { reason: "type-inspection-unavailable", columns: resolved.unavailable } });
2608
+ throw new VibeError13({ code: "VIBE_MIGRATION", message: "PostgreSQL column type identity could not be read", meta: { reason: "type-inspection-unavailable", columns: resolved.unavailable } });
2305
2609
  return resolved.schema;
2306
2610
  }
2307
2611
  function buildModel(params) {
@@ -2310,18 +2614,19 @@ function buildModel(params) {
2310
2614
  const pk = pkByTable.get(table) ?? [];
2311
2615
  const uniqueSingles = new Set;
2312
2616
  const multiUniques = [];
2313
- for (const [name, unique] of uniquesByConstraint) {
2617
+ for (const unique of uniquesByConstraint.values()) {
2618
+ const name = unique.name;
2314
2619
  if (unique.table !== table)
2315
2620
  continue;
2316
2621
  if (unique.columns.length === 1)
2317
2622
  uniqueSingles.add(unique.columns[0]);
2318
2623
  multiUniques.push({ name, columns: unique.columns });
2319
2624
  }
2320
- const tableIndexes = indexes.filter((index) => index.table === table && !constraintNames.has(index.name));
2625
+ const tableIndexes = indexes.filter((index) => index.table === table && !constraintNames.has(JSON.stringify([table, index.name])));
2321
2626
  for (const index of tableIndexes) {
2322
2627
  if (!index.unique)
2323
2628
  continue;
2324
- if (index.columns.length === 1 && index.where === undefined && index.nullsNotDistinct !== true) {
2629
+ if (index.columns.length === 1 && index.where === undefined && index.nullsNotDistinct !== true && index.columns[0]?.operatorClass === undefined) {
2325
2630
  uniqueSingles.add(index.columns[0].name);
2326
2631
  }
2327
2632
  }
@@ -2336,7 +2641,7 @@ function buildModel(params) {
2336
2641
  });
2337
2642
  }
2338
2643
  for (const index of tableIndexes) {
2339
- if (index.unique && index.columns.length === 1 && index.where === undefined && index.nullsNotDistinct !== true && index.name === `${table}_${index.columns[0].name}_key`) {
2644
+ if (index.unique && index.columns.length === 1 && index.where === undefined && index.nullsNotDistinct !== true && index.columns[0]?.operatorClass === undefined && index.name === `${params.physicalTable}_${index.columns[0].name}_key`) {
2340
2645
  continue;
2341
2646
  }
2342
2647
  modelIndexes.push({
@@ -2344,7 +2649,8 @@ function buildModel(params) {
2344
2649
  kind: index.unique ? "unique" : index.kind,
2345
2650
  fields: index.columns.map((column) => ({
2346
2651
  name: column.name,
2347
- ...column.desc ? { sort: "desc" } : {}
2652
+ ...column.desc ? { sort: "desc" } : {},
2653
+ ...column.operatorClass === undefined ? {} : { operatorClass: column.operatorClass }
2348
2654
  })),
2349
2655
  ...index.where === undefined ? {} : { where: index.where },
2350
2656
  ...index.nullsNotDistinct === true ? { nullsNotDistinct: true } : {}
@@ -2352,6 +2658,7 @@ function buildModel(params) {
2352
2658
  }
2353
2659
  return {
2354
2660
  name: table,
2661
+ dbName: params.physicalTable,
2355
2662
  fields,
2356
2663
  relations: [],
2357
2664
  indexes: modelIndexes,
@@ -2389,8 +2696,9 @@ function reverseMapType(params) {
2389
2696
  const { column, enumNames } = params;
2390
2697
  const isList = column.dataType === "ARRAY";
2391
2698
  const udt = isList ? column.udtName.replace(/^_/, "") : column.udtName;
2392
- if (enumNames.has(udt)) {
2393
- return { type: { enum: udt }, isList };
2699
+ const enumKey = physicalNameKey({ namespace: column.udtNamespace, name: udt });
2700
+ if (enumNames.has(enumKey)) {
2701
+ return { type: { enum: enumKey }, isList };
2394
2702
  }
2395
2703
  const canonical = CANONICAL_UDT[udt];
2396
2704
  if (canonical !== undefined) {
@@ -2596,7 +2904,12 @@ function toNumberOrNull(value) {
2596
2904
  // packages/migrate/src/introspect/mysql.ts
2597
2905
  import { exactIntegerLiteral as exactIntegerLiteral2 } from "@vibeorm/schema";
2598
2906
  import { mysqlDialect as mysqlDialect2 } from "@vibeorm/sql";
2599
- var TABLES_QUERY2 = `
2907
+
2908
+ // packages/migrate/src/journal-table.ts
2909
+ var MIGRATIONS_TABLE_NAME = "_vibe_migrations";
2910
+
2911
+ // packages/migrate/src/introspect/mysql.ts
2912
+ var TABLES_QUERY = `
2600
2913
  SELECT table_name, engine
2601
2914
  FROM information_schema.tables
2602
2915
  WHERE table_schema = DATABASE()
@@ -2604,13 +2917,13 @@ var TABLES_QUERY2 = `
2604
2917
  AND table_name <> '${MIGRATIONS_TABLE_NAME}'
2605
2918
  ORDER BY table_name;
2606
2919
  `;
2607
- var VIEWS_QUERY2 = `
2920
+ var VIEWS_QUERY = `
2608
2921
  SELECT table_name
2609
2922
  FROM information_schema.views
2610
2923
  WHERE table_schema = DATABASE()
2611
2924
  ORDER BY table_name;
2612
2925
  `;
2613
- var COLUMNS_QUERY2 = `
2926
+ var COLUMNS_QUERY = `
2614
2927
  SELECT
2615
2928
  table_name,
2616
2929
  column_name,
@@ -2641,7 +2954,7 @@ var STATISTICS_QUERY = `
2641
2954
  WHERE table_schema = DATABASE()
2642
2955
  ORDER BY table_name, index_name, seq_in_index;
2643
2956
  `;
2644
- var FOREIGN_KEYS_QUERY2 = `
2957
+ var FOREIGN_KEYS_QUERY = `
2645
2958
  SELECT
2646
2959
  constraint_name,
2647
2960
  table_name,
@@ -2661,7 +2974,7 @@ var REFERENTIAL_QUERY = `
2661
2974
  FROM information_schema.referential_constraints
2662
2975
  WHERE constraint_schema = DATABASE();
2663
2976
  `;
2664
- var CHECKS_QUERY2 = `
2977
+ var CHECKS_QUERY = `
2665
2978
  SELECT
2666
2979
  tc.table_name AS table_name,
2667
2980
  tc.constraint_name AS constraint_name,
@@ -2688,13 +3001,13 @@ function catalogField(params) {
2688
3001
  async function introspectMysql(params) {
2689
3002
  const { executor } = params;
2690
3003
  const [tableRows, viewRows, columnRows, statisticRows, fkRows, refRows, checkRows] = await Promise.all([
2691
- executor({ text: TABLES_QUERY2 }),
2692
- executor({ text: VIEWS_QUERY2 }),
2693
- executor({ text: COLUMNS_QUERY2 }),
3004
+ executor({ text: TABLES_QUERY }),
3005
+ executor({ text: VIEWS_QUERY }),
3006
+ executor({ text: COLUMNS_QUERY }),
2694
3007
  executor({ text: STATISTICS_QUERY }),
2695
- executor({ text: FOREIGN_KEYS_QUERY2 }),
3008
+ executor({ text: FOREIGN_KEYS_QUERY }),
2696
3009
  executor({ text: REFERENTIAL_QUERY }),
2697
- executor({ text: CHECKS_QUERY2 })
3010
+ executor({ text: CHECKS_QUERY })
2698
3011
  ]);
2699
3012
  const checksByTable = new Map;
2700
3013
  for (const row of checkRows) {
@@ -3448,7 +3761,7 @@ function q(name) {
3448
3761
  }
3449
3762
 
3450
3763
  // packages/migrate/src/execute.ts
3451
- import { VibeError as VibeError13 } from "@vibeorm/schema";
3764
+ import { VibeError as VibeError14 } from "@vibeorm/schema";
3452
3765
  var SELF_MANAGED_MARKER = "PRAGMA foreign_keys=OFF";
3453
3766
  var FK_CHECK_PREFIX = "PRAGMA foreign_key_check";
3454
3767
  function usesSelfManagedTransactions(params) {
@@ -3489,7 +3802,7 @@ function assertSelfManagedResumeSafe(params) {
3489
3802
  const completedIndex = params.direction === "up" ? params.start + 1 : -(params.start + 2);
3490
3803
  const rewind = selfManagedResumeStart({ sql: params.sql, start: params.start });
3491
3804
  const retryIndex = params.direction === "up" ? rewind : -(rewind + 1);
3492
- throw new VibeError13({
3805
+ throw new VibeError14({
3493
3806
  code: "VIBE_MIGRATION",
3494
3807
  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.`,
3495
3808
  meta: { reason: "uncertain-statement", migration: params.migration, statementIndex: params.start, direction: params.direction, dialect: "sqlite" }
@@ -3518,7 +3831,7 @@ function unsupportedScript(params) {
3518
3831
  const { migration, direction, index, problem, found } = params;
3519
3832
  const subject = direction === "down" ? `the down script of migration "${migration}"` : `migration "${migration}"`;
3520
3833
  const position = `${direction === "down" ? "down statement" : "statement"} ${index + 1} of ${params.statements}`;
3521
- return new VibeError13({
3834
+ return new VibeError14({
3522
3835
  code: "VIBE_MIGRATION",
3523
3836
  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.',
3524
3837
  meta: { reason: "unsupported-self-managed-script", problem, migration, statementIndex: index, direction }
@@ -3574,7 +3887,7 @@ async function executeStatement(params) {
3574
3887
  const rows = await executor({ text: statement, values: [] });
3575
3888
  if (rows.length > 0) {
3576
3889
  const sample = rows.slice(0, 5).map((row) => `${String(row.table)} rowid ${String(row.rowid)} \u2192 ${String(row.parent)}`).join("; ");
3577
- throw new VibeError13({
3890
+ throw new VibeError14({
3578
3891
  code: "VIBE_MIGRATION",
3579
3892
  message: `foreign key check failed after table rebuild: ${rows.length} violation(s) \u2014 ${sample}`,
3580
3893
  meta: { violations: rows }
@@ -3593,9 +3906,44 @@ async function cleanupSelfManagedFailure(params) {
3593
3906
  } catch {}
3594
3907
  }
3595
3908
 
3909
+ // packages/migrate/src/operator-classes.ts
3910
+ import { defaultIndexName as defaultIndexName2, qualifiedSqlName as qualifiedSqlName7 } from "@vibeorm/schema";
3911
+ import { postgresDialect as postgresDialect7, renderPostgresIndexKeys as renderPostgresIndexKeys2 } from "@vibeorm/sql";
3912
+ async function prepareOperatorClassDiff(params) {
3913
+ const models = [];
3914
+ for (const live of params.from.models) {
3915
+ const table = qualifiedSqlName7({ name: live.dbName ?? live.name, namespace: live.namespace });
3916
+ const authored = params.to.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace }) === physicalNameKey(table));
3917
+ if (authored === undefined || !authored.indexes.some((index) => index.fields.some((field) => field.operatorClass !== undefined))) {
3918
+ models.push(live);
3919
+ continue;
3920
+ }
3921
+ const query = renderPostgresIndexKeys2({ dialect: postgresDialect7, table });
3922
+ const rows = await params.executor({ text: query.text, values: [...query.values] });
3923
+ const indexes = live.indexes.map((index) => {
3924
+ const declared = authored.indexes.find((entry) => (entry.name ?? defaultIndexName2({ table: table.name, columns: entry.fields.map((field) => authored.fields.find((candidate) => candidate.name === field.name)?.dbName ?? field.name), unique: entry.kind === "unique", dialect: "postgres" })) === index.name && entry.kind === index.kind);
3925
+ if (declared === undefined)
3926
+ return index;
3927
+ return { ...index, fields: index.fields.map((field, position) => {
3928
+ const requested = declared.fields[position];
3929
+ const operatorClass = requested?.operatorClass;
3930
+ const physicalColumn = authored.fields.find((entry) => entry.name === requested?.name)?.dbName ?? requested?.name;
3931
+ if (field.operatorClass !== undefined || operatorClass === undefined || physicalColumn !== field.name)
3932
+ return field;
3933
+ const row = rows.find((entry) => entry.index_name === index.name && Number(entry.ordinal) === position + 1 && entry.column_name === field.name);
3934
+ if (row?.exact_default !== true || row.operator_namespace !== operatorClass.namespace || row.operator_name !== operatorClass.name || row.method !== (index.kind === "unique" ? "btree" : index.kind))
3935
+ return field;
3936
+ return { ...field, operatorClass };
3937
+ }) };
3938
+ });
3939
+ models.push({ ...live, indexes });
3940
+ }
3941
+ return { ...params.from, models };
3942
+ }
3943
+
3596
3944
  // packages/migrate/src/postgres-default-equivalence.ts
3597
- import { VibeError as VibeError14 } from "@vibeorm/schema";
3598
- import { postgresDialect as postgresDialect7, renderPostgresDefaultFunctionIdentity } from "@vibeorm/sql";
3945
+ import { VibeError as VibeError15 } from "@vibeorm/schema";
3946
+ import { postgresDialect as postgresDialect8, renderPostgresDefaultFunctionIdentity } from "@vibeorm/sql";
3599
3947
  var IDENTIFIER = '(?:"(?:[^"\\u0000]|"")+"|[A-Za-z_][A-Za-z_0-9$]*)';
3600
3948
  var CALL = new RegExp(`^\\s*(${IDENTIFIER})(?:\\s*\\.\\s*(${IDENTIFIER}))?\\s*\\(\\s*\\)\\s*$`);
3601
3949
  function parseCall(params) {
@@ -3611,9 +3959,9 @@ async function equivalentPostgresDefaultFunction(params) {
3611
3959
  const observed = parseCall({ expression: params.actual });
3612
3960
  if (expected?.namespace === undefined || observed === undefined)
3613
3961
  return false;
3614
- const query = renderPostgresDefaultFunctionIdentity({ dialect: postgresDialect7, table: params.table, column: params.column });
3962
+ const query = renderPostgresDefaultFunctionIdentity({ dialect: postgresDialect8, table: params.table, column: params.column });
3615
3963
  const rows = await params.executor({ text: query.text, values: [...query.values] }).catch((cause) => {
3616
- 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 });
3964
+ throw new VibeError15({ code: "VIBE_MIGRATION", message: "Cannot inspect the stored default function identity", meta: { reason: "default-inspection-unavailable", table: params.table, column: params.column }, cause });
3617
3965
  });
3618
3966
  if (rows.length !== 1)
3619
3967
  return false;
@@ -3641,8 +3989,8 @@ function factsOf(params) {
3641
3989
  }
3642
3990
  async function inspectSqlObject(params) {
3643
3991
  return Promise.resolve().then(async () => {
3644
- const namespace = "table" in params.identity ? params.identity.table.namespace : params.identity.name.namespace;
3645
- const query = renderMigrationObjectQuery({ dialect: getSqlDialect({ dialect: params.dialect }), identity: params.identity });
3992
+ const namespace = sqlObjectNamespace2({ identity: params.identity });
3993
+ const query = renderMigrationObjectQuery2({ dialect: getSqlDialect2({ dialect: params.dialect }), identity: params.identity });
3646
3994
  if (params.dialect === "mysql")
3647
3995
  await assertCatalogVisibility({ executor: params.executor, dialect: params.dialect, namespace });
3648
3996
  const rows = await params.executor({ text: query.text, values: [...query.values] });
@@ -3666,7 +4014,7 @@ async function inspectSqlObject(params) {
3666
4014
  const constraintType = params.identity.constraintType;
3667
4015
  const constraint = sqliteTableConstraints2({ sql: String(facts.definition ?? "") }).find((entry) => entry.name === name && entry.type === constraintType);
3668
4016
  if (constraint === undefined && constraintType === "primaryKey" && name === `${params.identity.table.name}_pkey`) {
3669
- const query = renderSqliteColumnInventory({ dialect: getSqlDialect({ dialect: "sqlite" }), namespace: params.identity.table.namespace, table: params.identity.table.name });
4017
+ const query = renderSqliteColumnInventory({ dialect: getSqlDialect2({ dialect: "sqlite" }), namespace: params.identity.table.namespace, table: params.identity.table.name });
3670
4018
  const columns = (await params.executor({ text: query.text, values: [...query.values] })).filter((column) => Number(column.pk) > 0).sort((a, b) => Number(a.pk) - Number(b.pk));
3671
4019
  if (columns.length === 0)
3672
4020
  return { state: "missing" };
@@ -3689,12 +4037,12 @@ async function inspectSqlObject(params) {
3689
4037
  if (params.identity.kind === "columnDefault")
3690
4038
  facts = { definition: facts.definition ?? null };
3691
4039
  if (params.dialect === "postgres") {
3692
- const query = renderMigrationDependencies({ dialect: getSqlDialect({ dialect: params.dialect }), identity: params.identity });
4040
+ const query = renderMigrationDependencies({ dialect: getSqlDialect2({ dialect: params.dialect }), identity: params.identity });
3693
4041
  const rows = await params.executor({ text: query.text, values: [...query.values] });
3694
4042
  return postgresInspection({ identity: params.identity, facts, addresses: rows.map((row) => row.address) });
3695
4043
  }
3696
4044
  if (params.dialect === "mysql") {
3697
- const query = renderMysqlObjectMembers({ dialect: getSqlDialect({ dialect: params.dialect }), identity: params.identity });
4045
+ const query = renderMysqlObjectMembers({ dialect: getSqlDialect2({ dialect: params.dialect }), identity: params.identity });
3698
4046
  if (query !== undefined) {
3699
4047
  const rows = await params.executor({ text: query.text, values: [...query.values] });
3700
4048
  if (params.identity.kind === "function" && rows.length === 0)
@@ -3708,7 +4056,7 @@ async function inspectSqlObject(params) {
3708
4056
  }
3709
4057
  }
3710
4058
  return { state: "present", definition: { identity: params.identity, facts, dependencies: [], format: "catalog-v2" } };
3711
- }).catch((error) => ({ state: "unavailable", reason: error instanceof VibeError15 ? String(error.meta.feature ?? error.code) : "catalog-query-failed" }));
4059
+ }).catch((error) => ({ state: "unavailable", reason: error instanceof VibeError16 ? String(error.meta.feature ?? error.code) : "catalog-query-failed" }));
3712
4060
  }
3713
4061
  function postgresInspection(params) {
3714
4062
  if (!params.addresses.every((address) => typeof address === "string"))
@@ -3723,7 +4071,7 @@ function postgresInspection(params) {
3723
4071
  return { state: "present", definition: { identity: params.identity, facts, dependencies, format: "catalog-v2" } };
3724
4072
  }
3725
4073
  function unavailableReason(params) {
3726
- return params.error instanceof VibeError15 ? String(params.error.meta.feature ?? params.error.meta.reason ?? params.error.code) : "catalog-query-failed";
4074
+ return params.error instanceof VibeError16 ? String(params.error.meta.feature ?? params.error.meta.reason ?? params.error.code) : "catalog-query-failed";
3727
4075
  }
3728
4076
  async function readBatch(params) {
3729
4077
  const positions = new Set(params.batch.positions);
@@ -3731,7 +4079,7 @@ async function readBatch(params) {
3731
4079
  const rows = new Map;
3732
4080
  for (const row of await params.executor({ text: params.batch.text, values: [...params.batch.values] })) {
3733
4081
  if (typeof row.position !== "number" || !Number.isInteger(row.position) || !positions.has(row.position))
3734
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Invalid batched catalog identity", meta: { reason: "catalog-query-failed" } });
4082
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Invalid batched catalog identity", meta: { reason: "catalog-query-failed" } });
3735
4083
  const matches = rows.get(row.position) ?? [];
3736
4084
  matches.push(row);
3737
4085
  rows.set(row.position, matches);
@@ -3759,7 +4107,7 @@ async function inspectSqlObjects(params) {
3759
4107
  }
3760
4108
  return Promise.resolve().then(async () => {
3761
4109
  const results = params.identities.map(() => ({ state: "unavailable", reason: "catalog-query-failed" }));
3762
- for (const batch of renderMigrationObjectBatch({ dialect: getSqlDialect({ dialect: params.dialect }), identities: params.identities })) {
4110
+ for (const batch of renderMigrationObjectBatch({ dialect: getSqlDialect2({ dialect: params.dialect }), identities: params.identities })) {
3763
4111
  const outcome = await readBatch({ executor: params.executor, batch });
3764
4112
  for (const position of batch.positions) {
3765
4113
  results[position] = outcome.ok ? batchedInspection({ identity: params.identities[position], matches: outcome.rows.get(position) ?? [] }) : { state: "unavailable", reason: outcome.reason };
@@ -3771,12 +4119,12 @@ async function inspectSqlObjects(params) {
3771
4119
  async function assertCatalogVisibility(params) {
3772
4120
  if (params.dialect !== "mysql")
3773
4121
  return;
3774
- const query = renderMigrationVisibility({ dialect: getSqlDialect({ dialect: params.dialect }), namespace: params.namespace });
4122
+ const query = renderMigrationVisibility({ dialect: getSqlDialect2({ dialect: params.dialect }), namespace: params.namespace });
3775
4123
  const rows = await params.executor({ text: query.text, values: [...query.values] });
3776
4124
  const grants = new Set(rows.map((row) => row.privilege));
3777
4125
  const missing = ["SELECT", "TRIGGER", "SHOW VIEW", "EXECUTE"].filter((privilege) => !grants.has(privilege));
3778
4126
  if (missing.length > 0)
3779
- 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 } });
4127
+ 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 } });
3780
4128
  }
3781
4129
  var CATALOG_V1_OMITTED_FACTS = Object.freeze({
3782
4130
  function: ["definerOwner"],
@@ -3794,27 +4142,29 @@ function compare(params) {
3794
4142
  if (expected === undefined !== (actual === undefined) || canonicalMigrationJson({ value: expected }) !== canonicalMigrationJson({ value: actual }))
3795
4143
  differences.push({ attribute: key, expected, actual });
3796
4144
  }
3797
- const expected = params.expected.dependencies.map((identity) => sqlObjectKey4({ identity })).sort();
3798
- const actual = params.live.dependencies.map((identity) => sqlObjectKey4({ identity })).sort();
4145
+ const expected = params.expected.dependencies.map((identity) => sqlObjectKey5({ identity })).sort();
4146
+ const actual = params.live.dependencies.map((identity) => sqlObjectKey5({ identity })).sort();
3799
4147
  if (JSON.stringify(expected) !== JSON.stringify(actual))
3800
4148
  differences.push({ attribute: "dependencies", expected, actual });
3801
4149
  return differences;
3802
4150
  }
3803
4151
  function assertProbeMode(params) {
3804
4152
  if (params.probe !== undefined && params.probe !== "none" && DIALECT_CAPABILITIES2[params.dialect].checkNormalization !== params.probe)
3805
- throw new VibeError15({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `${params.probe} CHECK probes are unavailable on ${params.dialect}`, meta: { feature: "checkProbe", dialect: params.dialect } });
4153
+ throw new VibeError16({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `${params.probe} CHECK probes are unavailable on ${params.dialect}`, meta: { feature: "checkProbe", dialect: params.dialect } });
3806
4154
  }
3807
4155
  async function verifyMigrationState(params) {
3808
4156
  return verifyState(params);
3809
4157
  }
3810
4158
  async function verifyState(params) {
4159
+ if (params.external !== undefined)
4160
+ params = { ...params, expected: projectExternalState({ state: params.expected, external: params.external }) };
3811
4161
  assertProbeMode({ dialect: params.dialect, probe: params.probe });
3812
4162
  const declarations = params.expected.schema.migrationObjects ?? [];
3813
- const owned = new Set(declarations.map((object) => sqlObjectKey4({ identity: object.identity })));
3814
- const definitions = new Map(params.expected.definitions.map((definition) => [sqlObjectKey4({ identity: definition.identity }), definition]));
3815
- const identities = new Map(params.expected.definitions.map((definition) => [sqlObjectKey4({ identity: definition.identity }), definition.identity]));
4163
+ const owned = new Set(declarations.map((object) => sqlObjectKey5({ identity: object.identity })));
4164
+ const definitions = new Map(params.expected.definitions.map((definition) => [sqlObjectKey5({ identity: definition.identity }), definition]));
4165
+ const identities = new Map(params.expected.definitions.map((definition) => [sqlObjectKey5({ identity: definition.identity }), definition.identity]));
3816
4166
  for (const object of declarations)
3817
- identities.set(sqlObjectKey4({ identity: object.identity }), object.identity);
4167
+ identities.set(sqlObjectKey5({ identity: object.identity }), object.identity);
3818
4168
  const objects = [];
3819
4169
  const inspections = params.batch === true ? await inspectSqlObjects({ ...params, identities: [...identities.values()] }) : undefined;
3820
4170
  let position = 0;
@@ -3825,6 +4175,8 @@ async function verifyState(params) {
3825
4175
  position++;
3826
4176
  if (live.state !== "present") {
3827
4177
  objects.push({ identity, ownership, state: live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} });
4178
+ } else if (expected === undefined && identity.kind === "namespace") {
4179
+ objects.push({ identity, ownership, state: "matching", differences: [] });
3828
4180
  } else if (expected === undefined) {
3829
4181
  objects.push({ identity, ownership, state: "unavailable", reason: "independent-definition-required", differences: [] });
3830
4182
  } else if (params.dialect === "postgres" && identity.kind === "table" && (expected.facts.persistence === undefined || expected.facts.options === undefined)) {
@@ -3834,23 +4186,31 @@ async function verifyState(params) {
3834
4186
  objects.push({ identity, ownership, state: differences.length === 0 ? "matching" : "changed", differences });
3835
4187
  }
3836
4188
  }
3837
- if (params.expected.schema.models.length > 0 || params.expected.schema.enums.length > 0)
4189
+ if (params.dialect === "postgres" && schemaNamespaces({ schema: params.expected.schema }).length > 0 || params.expected.schema.models.length > 0 || params.expected.schema.enums.length > 0)
3838
4190
  objects.push(...await verifySchemaObjects(params));
3839
4191
  if (params.dialect === "postgres")
3840
4192
  objects.push(...await verifyExclusions(params));
3841
4193
  return { inSync: objects.every((object) => object.state === "matching"), objects };
3842
4194
  }
3843
4195
  async function verifySchemaObjects(params) {
3844
- const physical = params.dialect === "postgres" ? await introspectPhysicalPostgres({ executor: params.executor }) : params.dialect === "mysql" ? await introspectMysql({ executor: params.executor }) : params.sqliteRestorePending === true ? await introspectSqliteBeforeForeignKeyRestore({ executor: params.executor }) : await introspectSqlite({ executor: params.executor });
4196
+ const physical = params.dialect === "postgres" ? await introspectPhysicalPostgres({ executor: params.executor, namespaces: ["public", ...params.namespaces ?? [], ...schemaNamespaces({ schema: params.expected.schema })] }) : params.dialect === "mysql" ? await introspectMysql({ executor: params.executor }) : params.sqliteRestorePending === true ? await introspectSqliteBeforeForeignKeyRestore({ executor: params.executor }) : await introspectSqlite({ executor: params.executor });
3845
4197
  const resolved = params.dialect === "postgres" ? await resolvePostgresTypeIdentities({ executor: params.executor, schema: params.expected.schema, mode: "verify" }) : { schema: params.expected.schema, unavailable: [] };
3846
4198
  const expected = normalizeSchema({ schema: resolved.schema, dialect: params.dialect });
3847
- const actual = normalizeSchema({ schema: physical, dialect: params.dialect });
4199
+ const physicalComparison = params.external === undefined ? physical : projectExternalSchema({ schema: physical, external: params.external });
4200
+ const comparable = params.dialect === "postgres" ? await prepareOperatorClassDiff({ executor: params.executor, from: physicalComparison, to: resolved.schema }) : physicalComparison;
4201
+ const actual = normalizeSchema({ schema: comparable, dialect: params.dialect });
3848
4202
  const objects = resolved.unavailable.map((identity) => ({ identity, ownership: "schema", state: "unavailable", reason: "type-reference-required", differences: [] }));
3849
4203
  const namespace = await migrationNamespace({ executor: params.executor, dialect: params.dialect });
3850
4204
  const sqliteTableOptions = params.dialect === "sqlite" && expected.tables.size > 0 ? await params.executor({ text: "PRAGMA main.table_list;", values: [] }) : undefined;
3851
4205
  if (params.dialect === "postgres")
3852
- for (const [name, desired] of expected.enums) {
3853
- const identity = { kind: "enum", name: { namespace, name } };
4206
+ for (const name of schemaNamespaces({ schema: params.expected.schema })) {
4207
+ const identity = { kind: "namespace", name };
4208
+ const inspection = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
4209
+ objects.push({ identity, ownership: "schema", state: inspection.state === "present" ? "matching" : inspection.state, differences: [], ...inspection.state === "unavailable" ? { reason: inspection.reason } : {} });
4210
+ }
4211
+ if (params.dialect === "postgres")
4212
+ for (const desired of expected.enums.values()) {
4213
+ const identity = { kind: "enum", name: qualifiedSqlName8({ name: desired.dbName ?? desired.name, namespace: desired.namespace, defaultNamespace: namespace }) };
3854
4214
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
3855
4215
  if (live.state !== "present") {
3856
4216
  objects.push({ identity, ownership: "schema", state: live.state, differences: [], ...live.state === "unavailable" ? { reason: live.reason } : {} });
@@ -3864,9 +4224,10 @@ async function verifySchemaObjects(params) {
3864
4224
  const matching = canonicalMigrationJson({ value: desired.values }) === canonicalMigrationJson({ value: values });
3865
4225
  objects.push({ identity, ownership: "schema", state: matching ? "matching" : "changed", differences: matching ? [] : [{ attribute: "values", expected: desired.values, actual: values }] });
3866
4226
  }
3867
- for (const [name, desired] of expected.tables) {
3868
- const table = { namespace, name };
3869
- const live = actual.tables.get(name);
4227
+ for (const [key, desired] of expected.tables) {
4228
+ const name = desired.table;
4229
+ const table = qualifiedSqlName8({ name, namespace: desired.namespace, defaultNamespace: namespace });
4230
+ const live = actual.tables.get(key);
3870
4231
  if (live === undefined) {
3871
4232
  const presence = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity: { kind: "table", name: table } });
3872
4233
  objects.push({ identity: { kind: "table", name: table }, ownership: "schema", state: presence.state === "missing" ? "missing" : "unavailable", ...presence.state === "missing" ? {} : { reason: "schema-definition-not-visible" }, differences: [] });
@@ -3892,7 +4253,7 @@ async function verifySchemaObjects(params) {
3892
4253
  if (params.dialect === "mysql" && field.field.collation !== undefined) {
3893
4254
  pin(`columns.${column}.collation`, field.field.collation.toLowerCase(), stored?.field.collation?.toLowerCase());
3894
4255
  }
3895
- const ownedDefault = params.expected.schema.migrationObjects?.some((object) => (object.identity.kind === "columnDefault" || object.identity.kind === "generatedColumn") && object.identity.table.namespace === namespace && object.identity.table.name === name && object.identity.column === column);
4256
+ const ownedDefault = params.expected.schema.migrationObjects?.some((object) => (object.identity.kind === "columnDefault" || object.identity.kind === "generatedColumn") && object.identity.table.namespace === table.namespace && object.identity.table.name === name && object.identity.column === column);
3896
4257
  if (!ownedDefault) {
3897
4258
  const desiredDefault = field.field.default;
3898
4259
  const actualDefault = stored?.field.default;
@@ -3903,10 +4264,10 @@ async function verifySchemaObjects(params) {
3903
4264
  }
3904
4265
  for (const [key, fk] of desired.fks) {
3905
4266
  const stored = live.fks.get(key);
3906
- pin(`foreignKeys.${fk.constraintName}`, { columns: fk.columns, refTable: fk.refTable, refColumns: fk.refColumns, onDelete: fk.onDelete, onUpdate: fk.onUpdate }, stored === undefined ? undefined : { columns: stored.columns, refTable: stored.refTable, refColumns: stored.refColumns, onDelete: stored.onDelete, onUpdate: stored.onUpdate });
4267
+ pin(`foreignKeys.${fk.constraintName}`, { columns: fk.columns, refTable: fk.refTable, refNamespace: fk.refNamespace ?? namespace, refColumns: fk.refColumns, onDelete: fk.onDelete, onUpdate: fk.onUpdate }, stored === undefined ? undefined : { columns: stored.columns, refTable: stored.refTable, refNamespace: stored.refNamespace ?? namespace, refColumns: stored.refColumns, onDelete: stored.onDelete, onUpdate: stored.onUpdate });
3907
4268
  if (params.dialect === "postgres" && stored !== undefined) {
3908
4269
  const identity = { kind: "constraint", constraintType: "foreignKey", table, name: stored.constraintName };
3909
- if (params.expected.definitions.some((definition) => sqlObjectKey4({ identity: definition.identity }) === sqlObjectKey4({ identity })))
4270
+ if (params.expected.definitions.some((definition) => sqlObjectKey5({ identity: definition.identity }) === sqlObjectKey5({ identity })))
3910
4271
  continue;
3911
4272
  const inspection = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
3912
4273
  if (inspection.state !== "present") {
@@ -3922,7 +4283,7 @@ async function verifySchemaObjects(params) {
3922
4283
  if (params.dialect !== "postgres" || !live.indexes.has(key))
3923
4284
  continue;
3924
4285
  const identity = { kind: "index", table, name: live.indexes.get(key).name };
3925
- if (params.expected.definitions.some((definition) => sqlObjectKey4({ identity: definition.identity }) === sqlObjectKey4({ identity })))
4286
+ if (params.expected.definitions.some((definition) => sqlObjectKey5({ identity: definition.identity }) === sqlObjectKey5({ identity })))
3926
4287
  continue;
3927
4288
  const inspection = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
3928
4289
  if (inspection.state !== "present") {
@@ -3935,7 +4296,7 @@ async function verifySchemaObjects(params) {
3935
4296
  objects.push({ identity: { kind: "table", name: table }, ownership: "schema", state: differences.length === 0 ? "matching" : "changed", differences });
3936
4297
  for (const [check, expression] of desired.checks) {
3937
4298
  const identity = { kind: "constraint", constraintType: "check", table, name: check };
3938
- if (params.expected.definitions.some((definition) => sqlObjectKey4({ identity: definition.identity }) === sqlObjectKey4({ identity })))
4299
+ if (params.expected.definitions.some((definition) => sqlObjectKey5({ identity: definition.identity }) === sqlObjectKey5({ identity })))
3939
4300
  continue;
3940
4301
  const stored = live.checks.get(check);
3941
4302
  if (stored === undefined) {
@@ -3975,13 +4336,14 @@ async function verifySchemaObjects(params) {
3975
4336
  }
3976
4337
  for (const [name, join] of expected.joinTables) {
3977
4338
  const stored = actual.joinTables.get(name);
3978
- const shape = (value) => value === undefined ? undefined : { a: { table: value.a.table, column: value.a.column }, b: { table: value.b.table, column: value.b.column } };
4339
+ const shape = (value) => value === undefined ? undefined : { a: { table: value.a.table, namespace: value.a.namespace ?? namespace, column: value.a.column }, b: { table: value.b.table, namespace: value.b.namespace ?? namespace, column: value.b.column } };
3979
4340
  const matching = canonicalMigrationJson({ value: shape(join) }) === canonicalMigrationJson({ value: shape(stored) });
3980
- objects.push({ identity: { kind: "table", name: { namespace, name } }, ownership: "schema", state: stored === undefined ? "missing" : matching ? "matching" : "changed", differences: matching ? [] : [{ attribute: "joinTable", expected: shape(join), actual: shape(stored) }] });
4341
+ objects.push({ identity: { kind: "table", name: qualifiedSqlName8({ name: join.table, namespace: join.namespace, defaultNamespace: namespace }) }, ownership: "schema", state: stored === undefined ? "missing" : matching ? "matching" : "changed", differences: matching ? [] : [{ attribute: "joinTable", expected: shape(join), actual: shape(stored) }] });
3981
4342
  }
3982
- for (const [name] of expected.views) {
3983
- if (!params.expected.definitions.some((definition) => definition.identity.kind === "view" && definition.identity.name.namespace === namespace && definition.identity.name.name === name))
3984
- objects.push({ identity: { kind: "view", name: { namespace, name } }, ownership: "schema", state: actual.views.has(name) ? "unavailable" : "missing", reason: "independent-definition-required", differences: [] });
4343
+ for (const [key, view] of expected.views) {
4344
+ const name = qualifiedSqlName8({ name: view.dbName ?? view.name, namespace: view.namespace, defaultNamespace: namespace });
4345
+ if (!params.expected.definitions.some((definition) => definition.identity.kind === "view" && definition.identity.name.namespace === name.namespace && definition.identity.name.name === name.name))
4346
+ objects.push({ identity: { kind: "view", name }, ownership: "schema", state: actual.views.has(key) ? "unavailable" : "missing", reason: "independent-definition-required", differences: [] });
3985
4347
  }
3986
4348
  return objects;
3987
4349
  }
@@ -3990,16 +4352,25 @@ async function verifyMigrationTransition(params) {
3990
4352
  }
3991
4353
  async function verifySqliteTransitionBeforeRestore(params) {
3992
4354
  if (params.dialect !== "sqlite")
3993
- throw new VibeError15({ code: "VIBE_INTERNAL", message: "SQLite rebuild verification requires the SQLite dialect" });
4355
+ throw new VibeError16({ code: "VIBE_INTERNAL", message: "SQLite rebuild verification requires the SQLite dialect" });
3994
4356
  await executeStatement({ executor: params.executor, dialect: "sqlite", statement: "PRAGMA foreign_key_check;" });
3995
4357
  return verifyTransition({ ...params, sqliteRestorePending: true });
3996
4358
  }
3997
4359
  async function verifyTransition(params) {
3998
- const report = await verifyState({ executor: params.executor, dialect: params.dialect, expected: params.to, ...params.sqliteRestorePending === true ? { sqliteRestorePending: true } : {}, ...params.probe === undefined ? {} : { probe: params.probe } });
4360
+ const external = params.external;
4361
+ if (external !== undefined) {
4362
+ for (const identity of params.removals ?? []) {
4363
+ const owner = externalObjectOwner({ identity, external });
4364
+ if (owner !== undefined)
4365
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Transition removals cannot target externally owned objects", meta: { reason: "external-owned-removal", owner, identity } });
4366
+ }
4367
+ params = { ...params, from: projectExternalState({ state: params.from, external }), to: projectExternalState({ state: params.to, external }) };
4368
+ }
4369
+ const report = await verifyState({ executor: params.executor, dialect: params.dialect, expected: params.to, ...params.namespaces === undefined ? {} : { namespaces: params.namespaces }, ...external === undefined ? {} : { external }, ...params.sqliteRestorePending === true ? { sqliteRestorePending: true } : {}, ...params.probe === undefined ? {} : { probe: params.probe } });
3999
4370
  const namespace = await migrationNamespace(params);
4000
4371
  const identities = (state) => [...schemaObjectIdentities({ schema: state.schema, dialect: params.dialect, namespace }), ...(state.schema.migrationObjects ?? []).map((object) => object.identity), ...state.definitions.map((definition) => definition.identity)];
4001
- const retained = new Set(identities(params.to).map((identity) => sqlObjectKey4({ identity })));
4002
- const removed = new Map([...identities(params.from).filter((identity) => !retained.has(sqlObjectKey4({ identity }))), ...params.removals ?? []].map((identity) => [sqlObjectKey4({ identity }), identity]));
4372
+ const retained = new Set(identities(params.to).map((identity) => sqlObjectKey5({ identity })));
4373
+ const removed = new Map([...identities(params.from).filter((identity) => identity.kind !== "namespace" && !retained.has(sqlObjectKey5({ identity }))), ...params.removals ?? []].map((identity) => [sqlObjectKey5({ identity }), identity]));
4003
4374
  const objects = [...report.objects];
4004
4375
  for (const identity of removed.values()) {
4005
4376
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
@@ -4012,16 +4383,16 @@ async function verifyTransition(params) {
4012
4383
  async function migrationNamespace(params) {
4013
4384
  if (params.dialect !== "mysql")
4014
4385
  return params.dialect === "sqlite" ? "main" : "public";
4015
- const query = renderMigrationNamespace({ dialect: getSqlDialect({ dialect: params.dialect }) });
4386
+ const query = renderMigrationNamespace({ dialect: getSqlDialect2({ dialect: params.dialect }) });
4016
4387
  const rows = await params.executor({ text: query.text, values: [] });
4017
4388
  const namespace = rows[0]?.namespace;
4018
4389
  if (typeof namespace !== "string" || namespace.length === 0)
4019
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Cannot determine the migration namespace", meta: { reason: "inspection-unavailable" } });
4390
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Cannot determine the migration namespace", meta: { reason: "inspection-unavailable" } });
4020
4391
  return namespace;
4021
4392
  }
4022
4393
  function assertMigrationState(params) {
4023
4394
  if (!params.report.inSync)
4024
- 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") } });
4395
+ 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") } });
4025
4396
  }
4026
4397
  async function deparsePostgresCheck(params) {
4027
4398
  const definition = await deparsePostgresCheckDefinition({ ...params, constraint: "vibeorm_check_probe" });
@@ -4031,11 +4402,11 @@ async function deparsePostgresCheckDefinition(params) {
4031
4402
  const name = "vibeorm_check_probe";
4032
4403
  await params.executor({ text: "SAVEPOINT vibeorm_check_probe" });
4033
4404
  try {
4034
- const query = renderCheckProbe({ dialect: getSqlDialect({ dialect: "postgres" }), table: params.table, expression: params.expression, name });
4405
+ const query = renderCheckProbe({ dialect: getSqlDialect2({ dialect: "postgres" }), table: params.table, expression: params.expression, name });
4035
4406
  await params.executor({ text: query.text });
4036
4407
  const result = await inspectSqlObject({ executor: params.executor, dialect: "postgres", identity: { kind: "constraint", constraintType: "check", table: params.table, name } });
4037
4408
  if (result.state !== "present" || typeof result.definition.facts.definition !== "string")
4038
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "CHECK normalization probe unavailable", meta: { reason: "check-inspection-unavailable" } });
4409
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "CHECK normalization probe unavailable", meta: { reason: "check-inspection-unavailable" } });
4039
4410
  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 } };
4040
4411
  } finally {
4041
4412
  await params.executor({ text: "ROLLBACK TO SAVEPOINT vibeorm_check_probe" });
@@ -4044,7 +4415,7 @@ async function deparsePostgresCheckDefinition(params) {
4044
4415
  }
4045
4416
  async function deparseMysqlCheck(params) {
4046
4417
  const probe = `vibe_check_${randomUUID2().replaceAll("-", "")}`;
4047
- const dialect = getSqlDialect({ dialect: "mysql" });
4418
+ const dialect = getSqlDialect2({ dialect: "mysql" });
4048
4419
  const clone = renderMysqlCheckProbe({ dialect, probe, operation: "clone", table: params.table });
4049
4420
  await params.executor({ text: clone.text });
4050
4421
  let created = true;
@@ -4053,7 +4424,7 @@ async function deparseMysqlCheck(params) {
4053
4424
  const source = await params.executor({ text: read.text, values: [] });
4054
4425
  const definition = source[0]?.["Create Table"];
4055
4426
  if (typeof definition !== "string")
4056
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Cannot read temporary clone definition" });
4427
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Cannot read temporary clone definition" });
4057
4428
  const drop = renderMysqlCheckProbe({ dialect, probe, operation: "drop" });
4058
4429
  await params.executor({ text: drop.text });
4059
4430
  created = false;
@@ -4064,7 +4435,7 @@ async function deparseMysqlCheck(params) {
4064
4435
  const normalizedDefinition = rows[0]?.["Create Table"];
4065
4436
  const check = typeof normalizedDefinition === "string" ? parseSqliteNamedChecks({ tableSql: normalizedDefinition }).find((entry) => entry.name === probe) : undefined;
4066
4437
  if (check === undefined)
4067
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "MySQL temporary CHECK deparse unavailable", meta: { reason: "check-inspection-unavailable" } });
4438
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "MySQL temporary CHECK deparse unavailable", meta: { reason: "check-inspection-unavailable" } });
4068
4439
  return check.expression;
4069
4440
  } finally {
4070
4441
  if (created) {
@@ -4078,7 +4449,7 @@ async function prepareCheckDiff(params) {
4078
4449
  const models = [];
4079
4450
  for (const model of params.from.models) {
4080
4451
  const table = model.dbName ?? model.name;
4081
- const target = params.to.models.find((entry) => (entry.dbName ?? entry.name) === table);
4452
+ const target = params.to.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect: params.dialect }) === physicalNameKey({ name: table, namespace: model.namespace, dialect: params.dialect }));
4082
4453
  const checks = [];
4083
4454
  for (const check of model.checks ?? []) {
4084
4455
  const next = target?.checks?.find((entry) => entry.name === check.name);
@@ -4086,12 +4457,12 @@ async function prepareCheckDiff(params) {
4086
4457
  checks.push({ name: check.name, expression: check.expression });
4087
4458
  continue;
4088
4459
  }
4089
- const identity = { kind: "constraint", constraintType: "check", table: { namespace: await migrationNamespace({ executor: params.executor, dialect: params.dialect }), name: table }, name: check.name };
4460
+ const identity = { kind: "constraint", constraintType: "check", table: qualifiedSqlName8({ name: table, namespace: model.namespace, defaultNamespace: await migrationNamespace({ executor: params.executor, dialect: params.dialect }) }), name: check.name };
4090
4461
  if (params.dialect === "postgres" && params.probe === "rollback") {
4091
4462
  const desired = await deparsePostgresCheck({ executor: params.executor, table: identity.table, expression: next.expression, probe: "rollback" });
4092
4463
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
4093
4464
  if (live.state !== "present")
4094
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4465
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4095
4466
  if (live.definition.facts.definition === desired) {
4096
4467
  checks.push(next);
4097
4468
  continue;
@@ -4103,20 +4474,20 @@ async function prepareCheckDiff(params) {
4103
4474
  const desired = await deparseMysqlCheck({ executor: params.executor, table: identity.table, expression: next.expression, probe: "temporary" });
4104
4475
  const live = await inspectSqlObject({ executor: params.executor, dialect: params.dialect, identity });
4105
4476
  if (live.state !== "present")
4106
- throw new VibeError15({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4477
+ throw new VibeError16({ code: "VIBE_MIGRATION", message: "Live CHECK inspection unavailable", meta: { reason: "check-inspection-unavailable", identity } });
4107
4478
  checks.push(desired === live.definition.facts.definition ? next : { name: check.name, expression: check.expression });
4108
4479
  continue;
4109
4480
  }
4110
- const previous = params.previous?.schema.models.find((entry) => (entry.dbName ?? entry.name) === table)?.checks?.find((entry) => entry.name === check.name);
4481
+ const previous = params.previous?.schema.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect: params.dialect }) === physicalNameKey({ name: table, namespace: model.namespace, dialect: params.dialect }))?.checks?.find((entry) => entry.name === check.name);
4111
4482
  if (previous !== undefined) {
4112
4483
  checks.push(previous.expression === next.expression ? next : { name: check.name, expression: check.expression });
4113
4484
  continue;
4114
4485
  }
4115
- if (params.replaceChecks?.some((entry) => sqlObjectKey4({ identity: entry }) === sqlObjectKey4({ identity }))) {
4486
+ if (params.replaceChecks?.some((entry) => sqlObjectKey5({ identity: entry }) === sqlObjectKey5({ identity }))) {
4116
4487
  checks.push({ name: check.name, expression: check.expression });
4117
4488
  continue;
4118
4489
  }
4119
- 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 } });
4490
+ 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 } });
4120
4491
  }
4121
4492
  models.push({ ...model, checks });
4122
4493
  }
@@ -4126,7 +4497,7 @@ async function prepareCheckDiff(params) {
4126
4497
  // packages/migrate/src/inventory.ts
4127
4498
  async function inspectMigrationInventory(params) {
4128
4499
  await assertCatalogVisibility(params);
4129
- const query = renderMigrationInventory({ dialect: getSqlDialect2({ dialect: params.dialect }), namespace: params.namespace });
4500
+ const query = renderMigrationInventory({ dialect: getSqlDialect3({ dialect: params.dialect }), namespace: params.namespace });
4130
4501
  const rows = await params.executor({ text: query.text, values: [...query.values] });
4131
4502
  const objects = [];
4132
4503
  const kinds = {
@@ -4143,14 +4514,18 @@ async function inspectMigrationInventory(params) {
4143
4514
  for (const row of rows) {
4144
4515
  const name = row.name;
4145
4516
  if (typeof name !== "string" || typeof row.kind !== "string")
4146
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Malformed catalog identity", meta: { reason: "inventory-unavailable" } });
4517
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Malformed catalog identity", meta: { reason: "inventory-unavailable" } });
4147
4518
  const qualified = { namespace: params.namespace, name };
4148
4519
  const table = { namespace: params.namespace, name: String(row.parent ?? "") };
4149
4520
  let identity;
4150
4521
  switch (row.kind) {
4522
+ case "namespace":
4523
+ identity = { kind: "namespace", name };
4524
+ break;
4151
4525
  case "table":
4152
4526
  case "view":
4153
4527
  case "sequence":
4528
+ case "enum":
4154
4529
  identity = { kind: row.kind, name: qualified };
4155
4530
  break;
4156
4531
  case "trigger":
@@ -4165,27 +4540,27 @@ async function inspectMigrationInventory(params) {
4165
4540
  case "constraint": {
4166
4541
  const constraintType = kinds[String(row.subtype)];
4167
4542
  if (constraintType === undefined)
4168
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Unknown constraint catalog kind", meta: { reason: "inventory-unavailable" } });
4543
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Unknown constraint catalog kind", meta: { reason: "inventory-unavailable" } });
4169
4544
  identity = { kind: "constraint", table, name, constraintType };
4170
4545
  break;
4171
4546
  }
4172
4547
  case "function": {
4173
4548
  let argumentTypes = JSON.parse(String(row.signature));
4174
4549
  if (params.dialect === "mysql") {
4175
- const query = renderMysqlObjectMembers2({ dialect: getSqlDialect2({ dialect: params.dialect }), identity: { kind: "function", name: qualified, argumentTypes: [] } });
4550
+ const query = renderMysqlObjectMembers2({ dialect: getSqlDialect3({ dialect: params.dialect }), identity: { kind: "function", name: qualified, argumentTypes: [] } });
4176
4551
  const members = await params.executor({ text: query.text, values: [...query.values] });
4177
4552
  if (members.length === 0)
4178
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "MySQL function signature unavailable", meta: { reason: "inventory-unavailable", name } });
4553
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "MySQL function signature unavailable", meta: { reason: "inventory-unavailable", name } });
4179
4554
  argumentTypes = members.filter((member) => member.mode !== null).map((member) => ({ namespace: "mysql", name: String(member.type) }));
4180
4555
  }
4181
4556
  const candidate = { kind: "function", name: qualified, argumentTypes };
4182
4557
  if (!isSqlObjectIdentity2(candidate))
4183
- throw new VibeError16({ code: "VIBE_MIGRATION", message: "Invalid function signature in catalog", meta: { reason: "inventory-unavailable" } });
4558
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: "Invalid function signature in catalog", meta: { reason: "inventory-unavailable" } });
4184
4559
  identity = candidate;
4185
4560
  break;
4186
4561
  }
4187
4562
  default:
4188
- throw new VibeError16({ code: "VIBE_MIGRATION", message: `Unknown catalog object kind ${row.kind}`, meta: { reason: "inventory-unavailable" } });
4563
+ throw new VibeError17({ code: "VIBE_MIGRATION", message: `Unknown catalog object kind ${row.kind}`, meta: { reason: "inventory-unavailable" } });
4189
4564
  }
4190
4565
  objects.push({ identity, ...typeof row.backing === "string" ? { backingConstraint: row.backing } : {} });
4191
4566
  if (params.dialect === "sqlite" && row.kind === "table" && typeof row.definition === "string" && !/^\s*CREATE\s+VIRTUAL\s+TABLE\b/i.test(row.definition)) {
@@ -4201,11 +4576,11 @@ async function inspectMigrationInventory(params) {
4201
4576
  objects.push({ identity: { kind: "constraint", constraintType: "check", table: qualified, name: check.name } });
4202
4577
  if ([...row.definition.matchAll(/\bCHECK\s*\(/gi)].length > checks.length)
4203
4578
  objects.push({ identity: { kind: "table", name: qualified }, inspectionUnavailable: "sqlite-unnamed-check-definition-unavailable" });
4204
- const query = renderSqliteColumnInventory2({ dialect: getSqlDialect2({ dialect: params.dialect }), namespace: params.namespace, table: name });
4579
+ const query = renderSqliteColumnInventory2({ dialect: getSqlDialect3({ dialect: params.dialect }), namespace: params.namespace, table: name });
4205
4580
  const columns = await params.executor({ text: query.text, values: [...query.values] });
4206
4581
  if (columns.some((column) => Number(column.pk) > 0) && !clauses.some((clause) => clause.type === "primaryKey"))
4207
4582
  objects.push({ identity: { kind: "constraint", constraintType: "primaryKey", table: qualified, name: defaultPrimaryKeyName2({ table: qualified.name, dialect: "sqlite" }) } });
4208
- const fkQuery = renderSqliteForeignKeyInventory({ dialect: getSqlDialect2({ dialect: params.dialect }), namespace: params.namespace, table: name });
4583
+ const fkQuery = renderSqliteForeignKeyInventory({ dialect: getSqlDialect3({ dialect: params.dialect }), namespace: params.namespace, table: name });
4209
4584
  const foreignKeys = await params.executor({ text: fkQuery.text, values: [...fkQuery.values] });
4210
4585
  const groups = new Map;
4211
4586
  for (const fk of foreignKeys)
@@ -4224,48 +4599,47 @@ async function inspectMigrationInventory(params) {
4224
4599
  }
4225
4600
  }
4226
4601
  }
4227
- return [...new Map(objects.map((object) => [sqlObjectKey5({ identity: object.identity }), object])).values()];
4602
+ return [...new Map(objects.map((object) => [sqlObjectKey6({ identity: object.identity }), object])).values()];
4228
4603
  }
4229
4604
 
4230
4605
  // packages/migrate/src/module-ownership.ts
4231
4606
  function refusal(params) {
4232
- return new VibeError17({ code: "VIBE_MIGRATION", message: "Module ownership or dependencies cannot be reconciled with the declared and live schema; no package SQL was authorized", meta: params });
4607
+ 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 });
4233
4608
  }
4234
4609
  function identities(params) {
4235
4610
  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)];
4236
4611
  }
4237
4612
  function moduleNamespace(params) {
4238
- if (params.origin.dialect !== "mysql")
4239
- return params.origin.dialect === "sqlite" ? "main" : "public";
4240
- const first = params.origin.owned[0];
4241
- if (first === undefined)
4613
+ const namespaces = [...new Set(params.origin.owned.map((identity) => sqlObjectNamespace3({ identity })))];
4614
+ if (namespaces.length > 1 || namespaces.length === 0 && params.origin.dialect === "mysql")
4242
4615
  throw refusal({ reason: "module-namespace-required", moduleId: params.origin.moduleId });
4243
- return "table" in first ? first.table.namespace : first.name.namespace;
4616
+ return namespaces[0] ?? (params.origin.dialect === "sqlite" ? "main" : "public");
4244
4617
  }
4245
4618
  function moduleOwnedObjects(params) {
4246
4619
  const { desiredState } = params;
4620
+ if (params.namespace === undefined && desiredState.dialect === "mysql")
4621
+ throw refusal({ reason: "module-namespace-required" });
4247
4622
  const namespace = params.namespace ?? (desiredState.dialect === "sqlite" ? "main" : "public");
4248
4623
  const all = [desiredState.from, desiredState.to, ...desiredState.down === undefined ? [] : [desiredState.down]].flatMap((state) => identities({ state, dialect: desiredState.dialect, namespace }));
4249
- return [...new Map(all.map((identity) => [sqlObjectKey6({ identity }), identity])).values()].sort((a, b) => sqlObjectKey6({ identity: a }).localeCompare(sqlObjectKey6({ identity: b })));
4624
+ return [...new Map(all.map((identity) => [sqlObjectKey7({ identity }), identity])).values()].sort((a, b) => sqlObjectKey7({ identity: a }).localeCompare(sqlObjectKey7({ identity: b })));
4250
4625
  }
4251
4626
  function assertModuleOwnership(params) {
4252
4627
  const { origin } = params;
4253
4628
  const state = origin.source.desiredState;
4254
4629
  const namespace = moduleNamespace({ origin });
4255
4630
  const expected = moduleOwnedObjects({ desiredState: state, namespace });
4256
- const owned = new Set(origin.owned.map((identity) => sqlObjectKey6({ identity })));
4257
- if (expected.length !== owned.size || expected.some((identity) => !owned.has(sqlObjectKey6({ identity }))))
4631
+ const owned = new Set(origin.owned.map((identity) => sqlObjectKey7({ identity })));
4632
+ if (expected.length !== owned.size || expected.some((identity) => !owned.has(sqlObjectKey7({ identity }))))
4258
4633
  throw refusal({ reason: "module-ownership-invalid", moduleId: origin.moduleId });
4259
4634
  for (const identity of expected) {
4260
- const name = "table" in identity ? identity.table : typeof identity.name === "object" ? identity.name : undefined;
4261
- if (name?.namespace !== namespace || name.name === "_vibe_migrations")
4635
+ if (sqlObjectNamespace3({ identity }) !== namespace || ("table" in identity ? identity.table.name : typeof identity.name === "object" ? identity.name.name : undefined) === "_vibe_migrations")
4262
4636
  throw refusal({ reason: "module-ownership-invalid", moduleId: origin.moduleId, identity });
4263
4637
  }
4264
4638
  for (const target of [state.from, state.to, ...state.down === undefined ? [] : [state.down]]) {
4265
4639
  if (target.schema.rls !== undefined || target.schema.extensions.length > 0)
4266
4640
  throw refusal({ reason: "module-schema-unsupported", moduleId: origin.moduleId });
4267
4641
  for (const dependency of [...target.definitions.flatMap((definition) => definition.dependencies), ...(target.schema.migrationObjects ?? []).flatMap((object) => object.dependsOn), ...state.unmanagedRemovals ?? []]) {
4268
- if (!owned.has(sqlObjectKey6({ identity: dependency })))
4642
+ if (!owned.has(sqlObjectKey7({ identity: dependency })))
4269
4643
  throw refusal({ reason: "module-external-dependency", moduleId: origin.moduleId, identity: dependency });
4270
4644
  }
4271
4645
  }
@@ -4273,28 +4647,29 @@ function assertModuleOwnership(params) {
4273
4647
  return;
4274
4648
  const local = params.desiredState;
4275
4649
  const rest = (actual, source) => {
4276
- const tables = new Set(source.schema.models.map((model) => model.dbName ?? model.name));
4277
- const enums = new Set(source.schema.enums.map((entry) => entry.dbName ?? entry.name));
4278
- const selected = actual.schema.models.filter((model) => tables.has(model.dbName ?? model.name));
4279
- const selectedEnums = actual.schema.enums.filter((entry) => enums.has(entry.dbName ?? entry.name));
4650
+ const keyOf = (entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect: origin.dialect });
4651
+ const tables = new Set(source.schema.models.map(keyOf));
4652
+ const enums = new Set(source.schema.enums.map(keyOf));
4653
+ const selected = actual.schema.models.filter((model) => tables.has(keyOf(model)));
4654
+ const selectedEnums = actual.schema.enums.filter((entry) => enums.has(keyOf(entry)));
4280
4655
  if (canonicalMigrationJson({ value: selected }) !== canonicalMigrationJson({ value: source.schema.models }) || canonicalMigrationJson({ value: selectedEnums }) !== canonicalMigrationJson({ value: source.schema.enums }))
4281
4656
  throw refusal({ reason: "module-source-state-mismatch", moduleId: origin.moduleId });
4282
- const selectedObjects = (actual.schema.migrationObjects ?? []).filter((object) => owned.has(sqlObjectKey6({ identity: object.identity })));
4283
- const selectedDefinitions = actual.definitions.filter((definition) => owned.has(sqlObjectKey6({ identity: definition.identity })));
4657
+ const selectedObjects = (actual.schema.migrationObjects ?? []).filter((object) => owned.has(sqlObjectKey7({ identity: object.identity })));
4658
+ const selectedDefinitions = actual.definitions.filter((definition) => owned.has(sqlObjectKey7({ identity: definition.identity })));
4284
4659
  if (canonicalMigrationJson({ value: selectedObjects }) !== canonicalMigrationJson({ value: source.schema.migrationObjects ?? [] }) || canonicalMigrationJson({ value: selectedDefinitions }) !== canonicalMigrationJson({ value: source.definitions }))
4285
4660
  throw refusal({ reason: "module-source-state-mismatch", moduleId: origin.moduleId });
4286
- return { ...actual, schema: { ...actual.schema, models: actual.schema.models.filter((model) => !tables.has(model.dbName ?? model.name)), enums: actual.schema.enums.filter((entry) => !enums.has(entry.dbName ?? entry.name)), migrationObjects: (actual.schema.migrationObjects ?? []).filter((object) => !owned.has(sqlObjectKey6({ identity: object.identity }))) }, definitions: actual.definitions.filter((definition) => !owned.has(sqlObjectKey6({ identity: definition.identity }))) };
4661
+ return { ...actual, schema: { ...actual.schema, models: actual.schema.models.filter((model) => !tables.has(keyOf(model))), enums: actual.schema.enums.filter((entry) => !enums.has(keyOf(entry))), migrationObjects: (actual.schema.migrationObjects ?? []).filter((object) => !owned.has(sqlObjectKey7({ identity: object.identity }))) }, definitions: actual.definitions.filter((definition) => !owned.has(sqlObjectKey7({ identity: definition.identity }))) };
4287
4662
  };
4288
4663
  const before = canonicalMigrationJson({ value: rest(local.from, state.from) });
4289
4664
  if (before !== canonicalMigrationJson({ value: rest(local.to, state.to) }) || before !== canonicalMigrationJson({ value: rest(local.down ?? local.from, state.down ?? state.from) }))
4290
4665
  throw refusal({ reason: "module-mutates-application-state", moduleId: origin.moduleId });
4291
4666
  }
4292
4667
  function dependencyUnavailable(params) {
4293
- return new VibeError17({ 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 } });
4668
+ 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 } });
4294
4669
  }
4295
4670
  async function assertCrossNamespaceModuleDependencies(params) {
4296
4671
  const { executor, origin } = params;
4297
- const dialect = getSqlDialect3({ dialect: origin.dialect });
4672
+ const dialect = getSqlDialect4({ dialect: origin.dialect });
4298
4673
  const query = renderModuleDependencyQuery({ dialect, operation: { kind: "foreignKeys", namespace: params.namespace, tables: [...params.tables] } });
4299
4674
  if (query === undefined)
4300
4675
  return;
@@ -4321,15 +4696,15 @@ async function assertCrossNamespaceModuleDependencies(params) {
4321
4696
  async function assertLiveModuleOwnership(params) {
4322
4697
  const { executor, origin } = params;
4323
4698
  const namespace = moduleNamespace({ origin });
4324
- if (await migrationNamespace({ executor, dialect: origin.dialect }) !== namespace)
4699
+ if (origin.dialect !== "postgres" && await migrationNamespace({ executor, dialect: origin.dialect }) !== namespace)
4325
4700
  throw refusal({ reason: "module-placement-mismatch", moduleId: origin.moduleId });
4326
4701
  const inventory = await inspectMigrationInventory({ executor, dialect: origin.dialect, namespace });
4327
- const before = new Set(identities({ state: origin.source.desiredState.from, dialect: origin.dialect, namespace }).map((identity) => sqlObjectKey6({ identity })));
4328
- const owned = new Set(origin.owned.map((identity) => sqlObjectKey6({ identity })));
4702
+ const before = new Set(identities({ state: origin.source.desiredState.from, dialect: origin.dialect, namespace }).map((identity) => sqlObjectKey7({ identity })));
4703
+ const owned = new Set(origin.owned.map((identity) => sqlObjectKey7({ identity })));
4329
4704
  const tables = new Set(origin.owned.flatMap((identity) => identity.kind === "table" ? [identity.name.name] : []));
4330
4705
  await assertCrossNamespaceModuleDependencies({ executor, origin, namespace, tables });
4331
4706
  for (const object of inventory) {
4332
- const key = sqlObjectKey6({ identity: object.identity });
4707
+ const key = sqlObjectKey7({ identity: object.identity });
4333
4708
  if (params.resume !== true && owned.has(key) && !before.has(key))
4334
4709
  throw refusal({ reason: "module-object-collision", moduleId: origin.moduleId, identity: object.identity });
4335
4710
  if (!owned.has(key) && (["function", "view", "trigger"].includes(object.identity.kind) || ("table" in object.identity) && tables.has(object.identity.table.name) && object.backingConstraint === undefined))
@@ -4337,7 +4712,7 @@ async function assertLiveModuleOwnership(params) {
4337
4712
  if (object.inspectionUnavailable !== undefined && (owned.has(key) || ("table" in object.identity) && tables.has(object.identity.table.name)))
4338
4713
  throw refusal({ reason: "module-dependency-unavailable", moduleId: origin.moduleId, identity: object.identity });
4339
4714
  }
4340
- const physical = origin.dialect === "postgres" ? await introspectPhysicalPostgres({ executor }) : origin.dialect === "mysql" ? await introspectMysql({ executor }) : params.sqliteRestorePending === true ? await introspectSqliteBeforeForeignKeyRestore({ executor }) : await introspectSqlite({ executor });
4715
+ const physical = origin.dialect === "postgres" ? await introspectPhysicalPostgres({ executor, namespaces: [namespace] }) : origin.dialect === "mysql" ? await introspectMysql({ executor }) : params.sqliteRestorePending === true ? await introspectSqliteBeforeForeignKeyRestore({ executor }) : await introspectSqlite({ executor });
4341
4716
  for (const model of physical.models)
4342
4717
  for (const relation of model.relations) {
4343
4718
  const target = physical.models.find((candidate) => candidate.name === relation.targetModel);
@@ -4352,12 +4727,51 @@ async function assertApplicationModuleState(params) {
4352
4727
  const options = { executor: params.executor, dialect: origin.dialect, from: state.from, to: state.to, removals: state.unmanagedRemovals, ...params.probe === undefined ? {} : { probe: params.probe } };
4353
4728
  const report = params.sqliteRestorePending === true ? await verifySqliteTransitionBeforeRestore(options) : await verifyMigrationTransition(options);
4354
4729
  if (!report.inSync)
4355
- throw new VibeError17({ 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 } });
4730
+ 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 } });
4356
4731
  }
4357
4732
  }
4358
4733
 
4734
+ // packages/migrate/src/module-adoption.ts
4735
+ import { VibeError as VibeError19 } from "@vibeorm/schema";
4736
+ var MODULE_ADOPTION_METADATA = Object.freeze({ filename: "module-adoption.json", version: 1 });
4737
+ function invalid3() {
4738
+ return new VibeError19({ code: "VIBE_MIGRATION", message: "Local module adoption proof is malformed or lacks immutable source provenance; restore the reviewed local history", meta: { reason: "module-adoption-invalid" } });
4739
+ }
4740
+ function assertMarker(params) {
4741
+ const value = params.value;
4742
+ if (value === null || typeof value !== "object" || Array.isArray(value) || Object.keys(value).length !== 3 || !("version" in value) || value.version !== 1 || !("reverse" in value) || value.reverse !== "refuse" || !("referenceFingerprint" in value) || typeof value.referenceFingerprint !== "string" || !/^[a-f0-9]{64}$/.test(value.referenceFingerprint))
4743
+ throw invalid3();
4744
+ }
4745
+ function assertModuleAdoption(params) {
4746
+ if (params.migration.moduleAdoption === undefined)
4747
+ return;
4748
+ assertMarker({ value: params.migration.moduleAdoption });
4749
+ if (params.migration.moduleOrigin === undefined)
4750
+ throw invalid3();
4751
+ assertModuleOrigin({ value: params.migration.moduleOrigin });
4752
+ }
4753
+ function parseModuleAdoption(params) {
4754
+ return parseMigrationMetadata({ text: params.text, failure: invalid3, parse: (value) => {
4755
+ const checked = { value };
4756
+ assertMarker(checked);
4757
+ return Object.freeze(checked.value);
4758
+ } });
4759
+ }
4760
+ function moduleAdoptionChecksumSuffix(params) {
4761
+ if (params.moduleAdoption === undefined)
4762
+ return "";
4763
+ assertModuleAdoption({ migration: params });
4764
+ return `
4765
+ -- vibeorm:module-adoption ${canonicalMigrationJson({ value: params.moduleAdoption })}`;
4766
+ }
4767
+ function serializeModuleAdoption(params) {
4768
+ assertModuleAdoption(params);
4769
+ return params.migration.moduleAdoption === undefined ? undefined : JSON.stringify(params.migration.moduleAdoption, null, 2) + `
4770
+ `;
4771
+ }
4772
+
4359
4773
  // packages/migrate/src/sql-statements.ts
4360
- import { VibeError as VibeError18 } from "@vibeorm/schema";
4774
+ import { VibeError as VibeError20 } from "@vibeorm/schema";
4361
4775
  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;
4362
4776
  var BLOCK_OPENERS = new Set(["BEGIN", "CASE", "IF", "LOOP", "WHILE", "REPEAT"]);
4363
4777
  function splitSqlStatements(params) {
@@ -4433,7 +4847,7 @@ function splitSqlStatements(params) {
4433
4847
  continue;
4434
4848
  }
4435
4849
  if (dialect === "mysql" || dialect === undefined) {
4436
- throw new VibeError18({ 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 } });
4850
+ 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 } });
4437
4851
  }
4438
4852
  }
4439
4853
  if (value === endQuote) {
@@ -4507,15 +4921,15 @@ function joinSqlStatements(params) {
4507
4921
 
4508
4922
  // packages/migrate/src/module-origin.ts
4509
4923
  import { createHash } from "crypto";
4510
- import { VibeError as VibeError19, isSqlObjectIdentity as isSqlObjectIdentity3, sqlObjectKey as sqlObjectKey7 } from "@vibeorm/schema";
4924
+ import { VibeError as VibeError21, isSqlObjectIdentity as isSqlObjectIdentity3, sqlObjectKey as sqlObjectKey8 } from "@vibeorm/schema";
4511
4925
  var MODULE_ORIGIN_METADATA = Object.freeze({ filename: "module-origin.json", version: 1 });
4512
- function invalid2() {
4513
- return new VibeError19({ 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" } });
4926
+ function invalid4() {
4927
+ 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" } });
4514
4928
  }
4515
- function record2(value) {
4929
+ function record3(value) {
4516
4930
  return value !== null && typeof value === "object" && !Array.isArray(value);
4517
4931
  }
4518
- function identifier(value) {
4932
+ function identifier2(value) {
4519
4933
  return typeof value === "string" && value.trim().length > 0 && !value.includes("\x00");
4520
4934
  }
4521
4935
  function strings(value) {
@@ -4533,17 +4947,17 @@ function freezeData(params) {
4533
4947
  }
4534
4948
  function assertModuleOrigin(params) {
4535
4949
  const value = params.value;
4536
- if (!record2(value) || value.version !== 1 || !identifier(value.moduleId) || !identifier(value.revision) || !(value.previousRevision === null || identifier(value.previousRevision)) || value.previousRevision === value.revision || !["postgres", "sqlite", "mysql"].includes(String(value.dialect)) || !strings(value.contracts) || value.contracts.length === 0 || !value.contracts.every(identifier) || new Set(value.contracts).size !== value.contracts.length || !Array.isArray(value.owned) || !value.owned.every(isSqlObjectIdentity3) || !record2(value.source) || "moduleOrigin" in value.source || !identifier(value.source.name) || !strings(value.source.sql) || typeof value.source.destructive !== "boolean" || value.source.downSql !== undefined && !strings(value.source.downSql) || value.source.desiredState === undefined || value.source.desiredStatePending !== undefined || typeof value.artifactChecksum !== "string" || !/^[a-f0-9]{64}$/.test(value.artifactChecksum))
4537
- throw invalid2();
4538
- if (new Set(value.owned.map((identity) => sqlObjectKey7({ identity }))).size !== value.owned.length)
4539
- throw invalid2();
4950
+ if (!record3(value) || value.version !== 1 || !identifier2(value.moduleId) || !identifier2(value.revision) || !(value.previousRevision === null || identifier2(value.previousRevision)) || value.previousRevision === value.revision || !["postgres", "sqlite", "mysql"].includes(String(value.dialect)) || !strings(value.contracts) || value.contracts.length === 0 || !value.contracts.every(identifier2) || new Set(value.contracts).size !== value.contracts.length || !Array.isArray(value.owned) || !value.owned.every(isSqlObjectIdentity3) || !record3(value.source) || "moduleOrigin" in value.source || "moduleAdoption" in value.source || !identifier2(value.source.name) || !strings(value.source.sql) || typeof value.source.destructive !== "boolean" || value.source.downSql !== undefined && !strings(value.source.downSql) || value.source.desiredState === undefined || value.source.desiredStatePending !== undefined || typeof value.artifactChecksum !== "string" || !/^[a-f0-9]{64}$/.test(value.artifactChecksum))
4951
+ throw invalid4();
4952
+ if (new Set(value.owned.map((identity) => sqlObjectKey8({ identity }))).size !== value.owned.length)
4953
+ throw invalid4();
4540
4954
  const desired = { value: value.source.desiredState, migration: value.source.name };
4541
4955
  assertDesiredState(desired);
4542
4956
  if (desired.value.dialect !== value.dialect)
4543
- throw invalid2();
4957
+ throw invalid4();
4544
4958
  const { artifactChecksum, ...payload } = value;
4545
4959
  if (checksum({ value: payload }) !== artifactChecksum)
4546
- throw invalid2();
4960
+ throw invalid4();
4547
4961
  assertModuleOwnership({ origin: value });
4548
4962
  }
4549
4963
  function createModuleOrigin(params) {
@@ -4566,7 +4980,7 @@ function parseModuleOrigin(params) {
4566
4980
  const checked = { value };
4567
4981
  assertModuleOrigin(checked);
4568
4982
  return checked.value;
4569
- }, failure: invalid2 });
4983
+ }, failure: invalid4 });
4570
4984
  }
4571
4985
  function assertMaterializedModuleMigration(params) {
4572
4986
  const { migration } = params;
@@ -4574,7 +4988,7 @@ function assertMaterializedModuleMigration(params) {
4574
4988
  return;
4575
4989
  const restored = restoreModuleMigration({ migration, origin: migration.moduleOrigin });
4576
4990
  if (migration.desiredState === undefined || canonicalMigrationJson({ value: restored }) !== canonicalMigrationJson({ value: migration }))
4577
- throw new VibeError19({ code: "VIBE_MIGRATION", message: "Materialized module metadata differs from its immutable source", meta: { reason: "module-source-state-mismatch", moduleId: migration.moduleOrigin.moduleId } });
4991
+ 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 } });
4578
4992
  }
4579
4993
  function restoreModuleMigration(params) {
4580
4994
  const { migration, origin } = params;
@@ -4585,25 +4999,27 @@ function restoreModuleMigration(params) {
4585
4999
  continue;
4586
5000
  const normalized = (sql) => sql.flatMap((text) => splitSqlStatements({ sql: text, dialect: origin.dialect }));
4587
5001
  if (canonicalMigrationJson({ value: normalized(actual) }) !== canonicalMigrationJson({ value: normalized(origin.source[key] ?? []) }))
4588
- throw invalid2();
5002
+ throw invalid4();
4589
5003
  }
4590
5004
  for (const key of ["execution", "downExecution", "rls", "securityChanges", "downSecurityChanges"]) {
4591
5005
  if (migration[key] !== undefined && canonicalMigrationJson({ value: migration[key] }) !== canonicalMigrationJson({ value: origin.source[key] }))
4592
- throw invalid2();
5006
+ throw invalid4();
4593
5007
  }
4594
- return { ...origin.source, name: migration.name, ...migration.desiredState === undefined ? {} : { desiredState: migration.desiredState }, moduleOrigin: origin };
5008
+ const restored = { ...origin.source, name: migration.name, ...migration.desiredState === undefined ? {} : { desiredState: migration.desiredState }, moduleOrigin: origin, ...migration.moduleAdoption === undefined ? {} : { moduleAdoption: migration.moduleAdoption } };
5009
+ assertModuleAdoption({ migration: restored });
5010
+ return restored;
4595
5011
  }
4596
5012
 
4597
5013
  // packages/migrate/src/fs.ts
4598
5014
  import { readFile, readdir } from "fs/promises";
4599
5015
  import { join } from "path";
4600
- import { VibeError as VibeError33, assertValidSchemaIR as assertValidSchemaIR4 } from "@vibeorm/schema";
5016
+ import { VibeError as VibeError36, assertValidSchemaIR as assertValidSchemaIR4 } from "@vibeorm/schema";
4601
5017
 
4602
5018
  // packages/migrate/src/migration-checksum.ts
4603
5019
  import { createHash as createHash2 } from "crypto";
4604
5020
 
4605
5021
  // packages/migrate/src/execution.ts
4606
- import { VibeError as VibeError20 } from "@vibeorm/schema";
5022
+ import { VibeError as VibeError22 } from "@vibeorm/schema";
4607
5023
  var EXECUTION_SIDECAR_FILENAME = "execution.json";
4608
5024
  var EXECUTION_SIDECAR_VERSION = 1;
4609
5025
  function isDefaultExecution(params) {
@@ -4641,8 +5057,8 @@ function executionChecksumSuffix(params) {
4641
5057
  ${lines.join(`
4642
5058
  `)}`;
4643
5059
  }
4644
- function invalid3(params) {
4645
- return new VibeError20({
5060
+ function invalid5(params) {
5061
+ return new VibeError22({
4646
5062
  code: "VIBE_MIGRATION",
4647
5063
  message: `invalid execution metadata for migration "${params.migration}": ${params.detail}`,
4648
5064
  meta: { reason: "execution-metadata-invalid", migration: params.migration }
@@ -4653,22 +5069,22 @@ function assertValidExecution(params) {
4653
5069
  if (execution === undefined)
4654
5070
  return;
4655
5071
  if (execution.transaction !== "default" && execution.transaction !== "none") {
4656
- throw invalid3({ migration, detail: `unknown transaction mode ${JSON.stringify(execution.transaction)}` });
5072
+ throw invalid5({ migration, detail: `unknown transaction mode ${JSON.stringify(execution.transaction)}` });
4657
5073
  }
4658
5074
  for (const [label, value] of [["lockTimeoutMs", execution.lockTimeoutMs], ["statementTimeoutMs", execution.statementTimeoutMs]]) {
4659
5075
  if (value === undefined)
4660
5076
  continue;
4661
5077
  if (!Number.isSafeInteger(value) || value < 0) {
4662
- throw invalid3({ migration, detail: `${label} must be a non-negative integer number of milliseconds` });
5078
+ throw invalid5({ migration, detail: `${label} must be a non-negative integer number of milliseconds` });
4663
5079
  }
4664
5080
  }
4665
5081
  const seen = new Set;
4666
5082
  for (const binding of execution.operations ?? []) {
4667
5083
  if (!Number.isSafeInteger(binding.statement) || binding.statement < 0 || binding.statement >= statements) {
4668
- throw invalid3({ migration, detail: `operation refers to statement ${String(binding.statement)}, which is outside the migration's ${String(statements)} statement(s)` });
5084
+ throw invalid5({ migration, detail: `operation refers to statement ${String(binding.statement)}, which is outside the migration's ${String(statements)} statement(s)` });
4669
5085
  }
4670
5086
  if (seen.has(binding.statement)) {
4671
- throw invalid3({ migration, detail: `statement ${String(binding.statement)} carries more than one declared operation` });
5087
+ throw invalid5({ migration, detail: `statement ${String(binding.statement)} carries more than one declared operation` });
4672
5088
  }
4673
5089
  seen.add(binding.statement);
4674
5090
  }
@@ -4693,7 +5109,7 @@ function parseExecutionSidecar(params) {
4693
5109
  try {
4694
5110
  parsed = JSON.parse(params.text);
4695
5111
  } catch (error) {
4696
- throw new VibeError20({
5112
+ throw new VibeError22({
4697
5113
  code: "VIBE_MIGRATION",
4698
5114
  message: `invalid execution metadata for migration "${migration}": not valid JSON`,
4699
5115
  meta: { reason: "execution-metadata-invalid", migration },
@@ -4701,10 +5117,10 @@ function parseExecutionSidecar(params) {
4701
5117
  });
4702
5118
  }
4703
5119
  if (parsed === null || typeof parsed !== "object")
4704
- throw invalid3({ migration, detail: "expected a JSON object" });
5120
+ throw invalid5({ migration, detail: "expected a JSON object" });
4705
5121
  const body = parsed;
4706
5122
  if (body.version !== EXECUTION_SIDECAR_VERSION) {
4707
- throw invalid3({ migration, detail: `unsupported sidecar version ${String(body.version)} (this build reads version ${String(EXECUTION_SIDECAR_VERSION)})` });
5123
+ throw invalid5({ migration, detail: `unsupported sidecar version ${String(body.version)} (this build reads version ${String(EXECUTION_SIDECAR_VERSION)})` });
4708
5124
  }
4709
5125
  const up = body.up === undefined ? undefined : body.up;
4710
5126
  const down = body.down === undefined ? undefined : body.down;
@@ -4786,22 +5202,25 @@ function describeMigrationRisks(params) {
4786
5202
  function migrationChecksum(params) {
4787
5203
  const suffix = executionChecksumSuffix({ ...params.execution === undefined ? {} : { execution: params.execution } });
4788
5204
  return createHash2("sha256").update(params.sql.join(`
4789
- `) + suffix + moduleOriginChecksumSuffix(params) + (params.desiredStatePending === true ? `
5205
+ `) + suffix + moduleOriginChecksumSuffix(params) + moduleAdoptionChecksumSuffix(params) + (params.desiredStatePending === true ? `
4790
5206
  -- vibeorm:desired-state unfinalized-v1` : "") + desiredStateChecksumSuffix({ ...params.desiredState === undefined ? {} : { desiredState: params.desiredState } }) + (params.desiredState === undefined ? "" : `
4791
5207
  -- vibeorm:down ${JSON.stringify(params.downSql ?? null)}${executionChecksumSuffix({ ...params.downExecution === undefined ? {} : { execution: params.downExecution } })}`)).digest("hex");
4792
5208
  }
4793
5209
 
4794
5210
  // packages/migrate/src/shadow-guard.ts
4795
- import { VibeError as VibeError21 } from "@vibeorm/schema";
4796
- import { getSqlDialect as getSqlDialect4, renderShadowOperation } from "@vibeorm/sql";
5211
+ import { VibeError as VibeError23 } from "@vibeorm/schema";
5212
+ import { getSqlDialect as getSqlDialect5, renderShadowOperation } from "@vibeorm/sql";
4797
5213
  var replayExecutors = new WeakSet;
5214
+ function isShadowReplayExecutor(params) {
5215
+ return replayExecutors.has(params.executor);
5216
+ }
4798
5217
  async function assertDatabaseNotShadow(params) {
4799
5218
  if (params.dialect === "sqlite" || replayExecutors.has(params.executor))
4800
5219
  return;
4801
- const [query] = renderShadowOperation({ dialect: getSqlDialect4({ dialect: params.dialect }), operation: { kind: "markerExists" } });
5220
+ const [query] = renderShadowOperation({ dialect: getSqlDialect5({ dialect: params.dialect }), operation: { kind: "markerExists" } });
4802
5221
  const rows = await params.executor({ text: query.text, values: [...query.values] });
4803
5222
  if (rows.length !== 0)
4804
- throw new VibeError21({ 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" } });
5223
+ 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" } });
4805
5224
  }
4806
5225
  function runShadowReplay(params) {
4807
5226
  replayExecutors.add(params.executor);
@@ -4810,10 +5229,95 @@ function runShadowReplay(params) {
4810
5229
  });
4811
5230
  }
4812
5231
 
5232
+ // packages/migrate/src/external-dependencies.ts
5233
+ import { VibeError as VibeError24, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
5234
+ import { getSqlDialect as getSqlDialect6, renderExternalDependents } from "@vibeorm/sql";
5235
+ function externalDependencyTargets(params) {
5236
+ return params.steps.flatMap((step) => {
5237
+ const namespace = step.namespace ?? params.namespace;
5238
+ switch (step.kind) {
5239
+ case "dropEnum":
5240
+ return [{ kind: "type", namespace, name: step.enumName, dependency: { kind: "enum", name: { namespace, name: step.enumName } } }];
5241
+ case "dropTable":
5242
+ case "dropJoinTable":
5243
+ return [{ kind: "relation", namespace, name: step.table, dependency: { kind: "table", name: { namespace, name: step.table } } }];
5244
+ case "dropView":
5245
+ return [{ kind: "relation", namespace, name: step.table, dependency: { kind: "view", name: { namespace, name: step.table } } }];
5246
+ case "dropColumn":
5247
+ return [{ kind: "column", namespace, name: step.table, column: step.column, dependency: { kind: "column", table: { namespace, name: step.table }, column: step.column } }];
5248
+ case "alterColumn":
5249
+ return step.changes.includes("type") ? [{ kind: "column", namespace, name: step.table, column: step.column, dependency: { kind: "column", table: { namespace, name: step.table }, column: step.column } }] : [];
5250
+ default:
5251
+ return [];
5252
+ }
5253
+ });
5254
+ }
5255
+ function unavailable() {
5256
+ return new VibeError24({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency row", meta: { reason: "external-inventory-unavailable" } });
5257
+ }
5258
+ function text(params) {
5259
+ const value = params.row[params.key];
5260
+ if (value === null || value === undefined)
5261
+ return;
5262
+ if (typeof value !== "string")
5263
+ throw unavailable();
5264
+ return value;
5265
+ }
5266
+ function dependentOwner(params) {
5267
+ const { row, external } = params;
5268
+ const relationNamespace = text({ row, key: "relationNamespace" });
5269
+ const relationName = text({ row, key: "relationName" });
5270
+ const relationKind = text({ row, key: "relationKind" });
5271
+ const column = text({ row, key: "columnName" });
5272
+ const dependentClass = text({ row, key: "dependentClass" });
5273
+ const objectNamespace = text({ row, key: "objectNamespace" });
5274
+ const objectName = text({ row, key: "objectName" });
5275
+ if (relationNamespace !== undefined && relationName !== undefined) {
5276
+ const name = { namespace: relationNamespace, name: relationName };
5277
+ const anchor = relationKind === "r" || relationKind === "p" || relationKind === "f" ? { kind: "table", name } : relationKind === "v" || relationKind === "m" ? { kind: "view", name } : relationKind === "S" ? { kind: "sequence", name } : { kind: "namespace", name: relationNamespace };
5278
+ const owner = externalObjectOwner({ identity: anchor, external });
5279
+ const identity = anchor.kind === "table" && column !== undefined && dependentClass === "pg_attrdef" ? { kind: "columnDefault", table: name, column } : anchor.kind === "table" && column !== undefined ? { kind: "column", table: name, column } : anchor.kind === "namespace" ? undefined : anchor;
5280
+ return { ...owner === undefined ? {} : { owner }, ...identity === undefined ? {} : { identity } };
5281
+ }
5282
+ const anchor = dependentClass === "pg_proc" && objectNamespace !== undefined && objectName !== undefined ? { kind: "function", name: { namespace: objectNamespace, name: objectName }, argumentTypes: [] } : objectNamespace === undefined ? undefined : { kind: "namespace", name: objectNamespace };
5283
+ const owner = anchor === undefined ? undefined : externalObjectOwner({ identity: anchor, external });
5284
+ return owner === undefined ? {} : { owner };
5285
+ }
5286
+ function assertNoExternalDependents(params) {
5287
+ for (const row of params.rows) {
5288
+ const target = typeof row.position === "number" ? params.targets[row.position] : undefined;
5289
+ const type = text({ row, key: "dependentType" });
5290
+ const identity = text({ row, key: "dependentIdentity" });
5291
+ const referenced = text({ row, key: "referenced" });
5292
+ if (target === undefined || type === undefined || identity === undefined || referenced === undefined)
5293
+ throw unavailable();
5294
+ const dependent = dependentOwner({ row, external: params.external });
5295
+ if (dependent.owner === undefined)
5296
+ continue;
5297
+ throw new VibeError24({
5298
+ code: "VIBE_MIGRATION",
5299
+ message: "An externally owned object depends on an application object this change drops or retypes; automatic DDL is refused",
5300
+ meta: { reason: "external-owned-dependency", owner: dependent.owner, ...dependent.identity === undefined ? {} : { identity: dependent.identity }, dependent: { type, identity }, dependency: target.dependency, referenced }
5301
+ });
5302
+ }
5303
+ }
5304
+ async function refuseExternalDependents(params) {
5305
+ if (params.external.declarations.length === 0)
5306
+ return;
5307
+ if (params.dialect !== "postgres")
5308
+ throw unsupportedCapability8({ dialect: params.dialect, feature: "externalObjects" });
5309
+ const targets = externalDependencyTargets({ steps: params.steps, namespace: params.namespace });
5310
+ if (targets.length === 0)
5311
+ return;
5312
+ const query = renderExternalDependents({ dialect: getSqlDialect6({ dialect: params.dialect }), targets: targets.map(({ dependency, ...target }) => target) });
5313
+ const rows = await params.executor({ text: query.text, values: [...query.values] });
5314
+ assertNoExternalDependents({ external: params.external, targets, rows });
5315
+ }
5316
+
4813
5317
  // packages/migrate/src/module-history.ts
4814
- import { VibeError as VibeError22, sqlObjectKey as sqlObjectKey8 } from "@vibeorm/schema";
5318
+ import { VibeError as VibeError25, sqlObjectKey as sqlObjectKey9, providerToDialect as providerToDialect3 } from "@vibeorm/schema";
4815
5319
  function refusal2(params) {
4816
- return new VibeError22({ code: "VIBE_MIGRATION", message: `Module ${params.moduleId} history, ownership or predecessor state is inconsistent; restore or complete the reviewed upgrade first`, meta: params });
5320
+ 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 });
4817
5321
  }
4818
5322
  function moduleHistoryHeads(params) {
4819
5323
  const heads = new Map;
@@ -4848,20 +5352,22 @@ function applicationModuleOrigins(params) {
4848
5352
  return origins;
4849
5353
  }
4850
5354
  function preserves(params) {
4851
- const tables = new Set(params.expected.schema.models.map((model) => model.dbName ?? model.name));
4852
- const enums = new Set(params.expected.schema.enums.map((entry) => entry.dbName ?? entry.name));
4853
- const models = params.actual.schema.models.filter((model) => tables.has(model.dbName ?? model.name));
4854
- const declaredEnums = params.actual.schema.enums.filter((entry) => enums.has(entry.dbName ?? entry.name));
5355
+ const dialect = providerToDialect3({ provider: params.expected.schema.datasource.provider });
5356
+ const keyOf = (entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect });
5357
+ const tables = new Set(params.expected.schema.models.map(keyOf));
5358
+ const enums = new Set(params.expected.schema.enums.map(keyOf));
5359
+ const models = params.actual.schema.models.filter((model) => tables.has(keyOf(model)));
5360
+ const declaredEnums = params.actual.schema.enums.filter((entry) => enums.has(keyOf(entry)));
4855
5361
  if (canonicalMigrationJson({ value: models }) !== canonicalMigrationJson({ value: params.expected.schema.models }) || canonicalMigrationJson({ value: declaredEnums }) !== canonicalMigrationJson({ value: params.expected.schema.enums }))
4856
5362
  throw refusal2({ reason: "module-application-mutation", moduleId: params.moduleId });
4857
- const objectKeys = new Set((params.expected.schema.migrationObjects ?? []).map((object) => sqlObjectKey8({ identity: object.identity })));
4858
- const definitionKeys = new Set(params.expected.definitions.map((definition) => sqlObjectKey8({ identity: definition.identity })));
4859
- if (canonicalMigrationJson({ value: (params.actual.schema.migrationObjects ?? []).filter((object) => objectKeys.has(sqlObjectKey8({ identity: object.identity }))) }) !== canonicalMigrationJson({ value: params.expected.schema.migrationObjects ?? [] }) || canonicalMigrationJson({ value: params.actual.definitions.filter((definition) => definitionKeys.has(sqlObjectKey8({ identity: definition.identity }))) }) !== canonicalMigrationJson({ value: params.expected.definitions }))
5363
+ const objectKeys = new Set((params.expected.schema.migrationObjects ?? []).map((object) => sqlObjectKey9({ identity: object.identity })));
5364
+ const definitionKeys = new Set(params.expected.definitions.map((definition) => sqlObjectKey9({ identity: definition.identity })));
5365
+ if (canonicalMigrationJson({ value: (params.actual.schema.migrationObjects ?? []).filter((object) => objectKeys.has(sqlObjectKey9({ identity: object.identity }))) }) !== canonicalMigrationJson({ value: params.expected.schema.migrationObjects ?? [] }) || canonicalMigrationJson({ value: params.actual.definitions.filter((definition) => definitionKeys.has(sqlObjectKey9({ identity: definition.identity }))) }) !== canonicalMigrationJson({ value: params.expected.definitions }))
4860
5366
  throw refusal2({ reason: "module-application-mutation", moduleId: params.moduleId });
4861
5367
  for (const model of params.actual.schema.models)
4862
5368
  for (const relation of model.relations) {
4863
5369
  const target = params.actual.schema.models.find((candidate) => candidate.name === relation.targetModel);
4864
- if (target !== undefined && tables.has(model.dbName ?? model.name) !== tables.has(target.dbName ?? target.name))
5370
+ if (target !== undefined && tables.has(keyOf(model)) !== tables.has(keyOf(target)))
4865
5371
  throw refusal2({ reason: "module-external-dependency", moduleId: params.moduleId });
4866
5372
  }
4867
5373
  }
@@ -4871,7 +5377,7 @@ function assertModuleUpgradeHistory(params) {
4871
5377
  const register = (migration) => {
4872
5378
  const origin = migration.moduleOrigin;
4873
5379
  for (const identity of origin.owned) {
4874
- const key = sqlObjectKey8({ identity });
5380
+ const key = sqlObjectKey9({ identity });
4875
5381
  if (owners.has(key) && owners.get(key) !== origin.moduleId)
4876
5382
  throw refusal2({ reason: "module-object-collision", moduleId: origin.moduleId });
4877
5383
  owners.set(key, origin.moduleId);
@@ -4918,11 +5424,11 @@ function assertModuleUpgradeHistory(params) {
4918
5424
  }
4919
5425
 
4920
5426
  // packages/migrate/src/module-journal.ts
4921
- import { VibeError as VibeError24 } from "@vibeorm/schema";
4922
- import { getSqlDialect as getSqlDialect5, MODULE_JOURNAL_COLUMN, renderModuleJournal } from "@vibeorm/sql";
5427
+ import { VibeError as VibeError27 } from "@vibeorm/schema";
5428
+ import { getSqlDialect as getSqlDialect7, MODULE_JOURNAL_COLUMN, renderModuleJournal } from "@vibeorm/sql";
4923
5429
 
4924
5430
  // packages/migrate/src/journal-read.ts
4925
- import { VibeError as VibeError23 } from "@vibeorm/schema";
5431
+ import { VibeError as VibeError26 } from "@vibeorm/schema";
4926
5432
  var MISSING_TABLE_IDENTIFIERS = new Set(["42P01", "42S02", "ER_NO_SUCH_TABLE"]);
4927
5433
  function isMissingTable(params) {
4928
5434
  let current = params.cause;
@@ -4946,7 +5452,7 @@ async function readJournalRows(params) {
4946
5452
  if (presence.state === "missing")
4947
5453
  return;
4948
5454
  }
4949
- throw new VibeError23({ code: "VIBE_MIGRATION", message: "Cannot inspect migration history; check SELECT privileges", meta: { reason: "history-unavailable" }, cause });
5455
+ throw new VibeError26({ code: "VIBE_MIGRATION", message: "Cannot inspect migration history; check SELECT privileges", meta: { reason: "history-unavailable" }, cause });
4950
5456
  });
4951
5457
  if (rows === undefined)
4952
5458
  return;
@@ -4954,7 +5460,7 @@ async function readJournalRows(params) {
4954
5460
  for (const row of rows) {
4955
5461
  const index = row.statement_index;
4956
5462
  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)) {
4957
- throw new VibeError23({ code: "VIBE_MIGRATION", message: "Malformed migration journal row", meta: { reason: "history-unavailable" } });
5463
+ throw new VibeError26({ code: "VIBE_MIGRATION", message: "Malformed migration journal row", meta: { reason: "history-unavailable" } });
4958
5464
  }
4959
5465
  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 } : {} });
4960
5466
  }
@@ -4962,41 +5468,41 @@ async function readJournalRows(params) {
4962
5468
  }
4963
5469
 
4964
5470
  // packages/migrate/src/module-journal.ts
4965
- function invalid4() {
4966
- return new VibeError24({ 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" } });
5471
+ function invalid6() {
5472
+ 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" } });
4967
5473
  }
4968
5474
  function parseModuleJournalEntry(params) {
4969
- return parseMigrationMetadata({ text: params.text, failure: invalid4, parse: (value) => {
5475
+ return parseMigrationMetadata({ text: params.text, failure: invalid6, parse: (value) => {
4970
5476
  if (value === null || typeof value !== "object" || !("version" in value) || value.version !== 1 || !("complete" in value) || typeof value.complete !== "boolean" || !("migration" in value) || value.migration === null || typeof value.migration !== "object")
4971
- throw invalid4();
5477
+ throw invalid6();
4972
5478
  const migration = value.migration;
4973
5479
  assertModuleOrigin({ value: migration.moduleOrigin });
4974
5480
  if (migration.name !== params.name || migrationChecksum(migration) !== params.checksum)
4975
- throw invalid4();
5481
+ throw invalid6();
4976
5482
  assertMaterializedModuleMigration({ migration });
4977
5483
  assertModuleOwnership({ origin: migration.moduleOrigin, desiredState: migration.desiredState });
4978
5484
  return { version: 1, complete: value.complete, migration };
4979
5485
  } });
4980
5486
  }
4981
5487
  async function readModuleJournal(params) {
4982
- const query = renderModuleJournal({ dialect: getSqlDialect5({ dialect: params.dialect }), table: MIGRATIONS_TABLE_NAME, operation: { kind: "read" } });
5488
+ const query = renderModuleJournal({ dialect: getSqlDialect7({ dialect: params.dialect }), table: MIGRATIONS_TABLE_NAME, operation: { kind: "read" } });
4983
5489
  const rows = await readJournalRows({ ...params, query });
4984
5490
  return new Map([...rows ?? []].map(([name, row]) => {
4985
5491
  const entry = row.moduleEntry === undefined ? undefined : parseModuleJournalEntry({ text: row.moduleEntry, name, checksum: row.checksum });
4986
5492
  if (entry !== undefined && entry.migration.moduleOrigin.dialect !== params.dialect)
4987
- throw invalid4();
5493
+ throw invalid6();
4988
5494
  return [name, { row, ...entry === undefined ? {} : { entry } }];
4989
5495
  }));
4990
5496
  }
4991
5497
  async function ensureModuleJournal(params) {
4992
- const dialect = getSqlDialect5({ dialect: params.dialect });
5498
+ const dialect = getSqlDialect7({ dialect: params.dialect });
4993
5499
  const columns = await params.executor(renderModuleJournal({ dialect, table: MIGRATIONS_TABLE_NAME, operation: { kind: "columns" } }));
4994
5500
  if (columns.some((row) => row.name === MODULE_JOURNAL_COLUMN))
4995
5501
  return;
4996
5502
  await params.executor(renderModuleJournal({ dialect, table: MIGRATIONS_TABLE_NAME, operation: { kind: "addColumn" } }));
4997
5503
  }
4998
5504
  async function insertModuleJournal(params) {
4999
- await params.executor(renderModuleJournal({ dialect: getSqlDialect5({ dialect: params.dialect }), table: MIGRATIONS_TABLE_NAME, operation: {
5505
+ await params.executor(renderModuleJournal({ dialect: getSqlDialect7({ dialect: params.dialect }), table: MIGRATIONS_TABLE_NAME, operation: {
5000
5506
  kind: "insert",
5001
5507
  name: params.migration.name,
5002
5508
  checksum: migrationChecksum(params.migration),
@@ -5005,7 +5511,7 @@ async function insertModuleJournal(params) {
5005
5511
  } }));
5006
5512
  }
5007
5513
  async function completeModuleJournal(params) {
5008
- await params.executor(renderModuleJournal({ dialect: getSqlDialect5({ dialect: params.dialect }), table: MIGRATIONS_TABLE_NAME, operation: {
5514
+ await params.executor(renderModuleJournal({ dialect: getSqlDialect7({ dialect: params.dialect }), table: MIGRATIONS_TABLE_NAME, operation: {
5009
5515
  kind: "complete",
5010
5516
  name: params.migration.name,
5011
5517
  entry: canonicalMigrationJson({ value: { version: 1, migration: params.migration, complete: true } })
@@ -5014,27 +5520,27 @@ async function completeModuleJournal(params) {
5014
5520
  function moduleRecordComplete(params) {
5015
5521
  const { migration, stored } = params;
5016
5522
  if (stored.moduleEntry === undefined || stored.checksum === "")
5017
- throw invalid4();
5523
+ throw invalid6();
5018
5524
  const entry = parseModuleJournalEntry({ text: stored.moduleEntry, name: migration.name, checksum: stored.checksum });
5019
5525
  if (migrationChecksum(migration) !== stored.checksum)
5020
- throw invalid4();
5526
+ throw invalid6();
5021
5527
  return entry.complete && stored.error === undefined && stored.statementIndex === migration.sql.length;
5022
5528
  }
5023
5529
 
5024
5530
  // packages/migrate/src/runner.ts
5025
- import { VibeError as VibeError32 } from "@vibeorm/schema";
5531
+ import { VibeError as VibeError35 } from "@vibeorm/schema";
5026
5532
  import {
5027
5533
  DIALECT_CAPABILITIES as DIALECT_CAPABILITIES3,
5028
- getSqlDialect as getSqlDialect7,
5534
+ getSqlDialect as getSqlDialect9,
5029
5535
  renderMigrationSessionRead,
5030
5536
  renderMigrationSessionRestore,
5031
5537
  renderMigrationSessionSet
5032
5538
  } from "@vibeorm/sql";
5033
5539
 
5034
5540
  // packages/migrate/src/recovery.ts
5035
- import { VibeError as VibeError25 } from "@vibeorm/schema";
5541
+ import { VibeError as VibeError28 } from "@vibeorm/schema";
5036
5542
  import {
5037
- getSqlDialect as getSqlDialect6,
5543
+ getSqlDialect as getSqlDialect8,
5038
5544
  renderColumnStateQuery,
5039
5545
  renderConstraintStateQuery,
5040
5546
  renderIndexStateQuery,
@@ -5048,7 +5554,7 @@ function constraintUnverifiable(params) {
5048
5554
  });
5049
5555
  }
5050
5556
  function conflict2(params) {
5051
- return new VibeError25({
5557
+ return new VibeError28({
5052
5558
  code: "VIBE_MIGRATION",
5053
5559
  message: params.message,
5054
5560
  meta: { reason: params.reason, ...params.meta }
@@ -5073,7 +5579,7 @@ function sameShape(params) {
5073
5579
  }
5074
5580
  async function establishOperationState(params) {
5075
5581
  const { executor, operation } = params;
5076
- const dialect = getSqlDialect6({ dialect: params.dialect });
5582
+ const dialect = getSqlDialect8({ dialect: params.dialect });
5077
5583
  const run = async (query) => executor({ text: query.text, values: [...query.values] });
5078
5584
  switch (operation.kind) {
5079
5585
  case "createIndexConcurrently": {
@@ -5152,11 +5658,11 @@ async function establishOperationState(params) {
5152
5658
  }
5153
5659
 
5154
5660
  // packages/migrate/src/differ.ts
5155
- import { isEnumType as isEnumType6, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
5156
- import { VibeError as VibeError27 } from "@vibeorm/schema";
5661
+ import { assertIndexOperatorClasses as assertIndexOperatorClasses2, isEnumType as isEnumType6, qualifiedSqlName as qualifiedSqlName9, unsupportedCapability as unsupportedCapability10 } from "@vibeorm/schema";
5662
+ import { VibeError as VibeError30 } from "@vibeorm/schema";
5157
5663
 
5158
5664
  // packages/migrate/src/renames.ts
5159
- import { VibeError as VibeError26 } from "@vibeorm/schema";
5665
+ import { VibeError as VibeError29, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
5160
5666
  function tableOf(model) {
5161
5667
  return model.dbName ?? model.name;
5162
5668
  }
@@ -5167,7 +5673,7 @@ function hasColumn(params) {
5167
5673
  return params.model.fields.some((field) => (field.dbName ?? field.name) === params.column);
5168
5674
  }
5169
5675
  function ambiguous(params) {
5170
- return new VibeError26({
5676
+ return new VibeError29({
5171
5677
  code: "VIBE_MIGRATION",
5172
5678
  message: `rename refused: ${params.detail}`,
5173
5679
  meta: { reason: "rename-ambiguous", ...params.meta }
@@ -5177,6 +5683,8 @@ function planRenames(params) {
5177
5683
  const { to, dialect } = params;
5178
5684
  if (params.renames.length === 0)
5179
5685
  return { steps: [], from: params.from };
5686
+ if ([params.from, params.to].some((schema) => schema.models.some((model) => model.namespace !== undefined && model.namespace !== "public" && params.renames.some((intent) => intent.kind === "table" ? [intent.from, intent.to].includes(tableOf(model)) : intent.table === tableOf(model)))))
5687
+ throw unsupportedCapability9({ dialect, feature: "qualifiedRenameIntent", hint: "Use reviewed namespace-qualified SQL for a physical rename in a fixed namespace" });
5180
5688
  const renamedTables = new Map;
5181
5689
  const renamedColumns = new Map;
5182
5690
  const steps = [];
@@ -5268,9 +5776,19 @@ function planRenames(params) {
5268
5776
  // packages/migrate/src/differ.ts
5269
5777
  function diffSchemas(params) {
5270
5778
  const dialect = params.dialect ?? "postgres";
5779
+ assertIndexOperatorClasses2({ schema: params.to, dialect });
5780
+ for (const kind of ["models", "enums"])
5781
+ for (const entry of params.to[kind]) {
5782
+ const key = physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect });
5783
+ 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 })));
5784
+ 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))
5785
+ throw unsupportedCapability10({ dialect, feature: "automaticNamespaceMove", hint: "Moving populated objects to a fixed namespace requires an explicit reviewed migration" });
5786
+ }
5787
+ for (const schema of [params.from, params.to])
5788
+ normalizeSchema({ schema, dialect });
5271
5789
  for (const schema of [params.from, params.to])
5272
5790
  if (dialect !== "postgres" && schema.models.some((model) => (model.exclusions?.length ?? 0) > 0))
5273
- throw unsupportedCapability6({ dialect, feature: "exclusionConstraints" });
5791
+ throw unsupportedCapability10({ dialect, feature: "exclusionConstraints" });
5274
5792
  assertRlsDialect({ schema: params.from, dialect });
5275
5793
  assertRlsDialect({ schema: params.to, dialect });
5276
5794
  if (dialect !== "postgres")
@@ -5280,6 +5798,7 @@ function diffSchemas(params) {
5280
5798
  const from = normalizeSchema({ schema: params.from, dialect });
5281
5799
  const to = normalizeSchema({ schema: params.to, dialect });
5282
5800
  const buckets = {
5801
+ createNamespace: dialect === "postgres" ? schemaNamespaces({ schema: params.to }).filter((namespace) => !schemaNamespaces({ schema: params.from }).includes(namespace)).map((namespace) => ({ kind: "createNamespace", namespace, destructive: false })) : [],
5283
5802
  dropExclusion: [],
5284
5803
  addExclusion: [],
5285
5804
  dropView: [],
@@ -5309,16 +5828,17 @@ function diffSchemas(params) {
5309
5828
  diffTables({ from, to, buckets, dialect });
5310
5829
  for (const model of params.to.models) {
5311
5830
  const table = model.dbName ?? model.name;
5312
- const previous = params.from.models.find((entry) => (entry.dbName ?? entry.name) === table);
5831
+ const placement = model.namespace === undefined ? {} : { namespace: model.namespace };
5832
+ const previous = params.from.models.find((entry) => physicalNameKey({ name: entry.dbName ?? entry.name, namespace: entry.namespace, dialect }) === physicalNameKey({ name: table, namespace: model.namespace, dialect }));
5313
5833
  for (const exclusion of model.exclusions ?? []) {
5314
5834
  const before = previous?.exclusions?.find((entry) => entry.name === exclusion.name);
5315
5835
  if (before?.definition === exclusion.definition)
5316
5836
  continue;
5317
5837
  if (before?.catalogDefinition)
5318
- throw new VibeError27({ code: "VIBE_MIGRATION", message: "Exclusion comparison requires normalized evidence", meta: { reason: "exclusion-inspection-unavailable", table, name: exclusion.name } });
5838
+ throw new VibeError30({ code: "VIBE_MIGRATION", message: "Exclusion comparison requires normalized evidence", meta: { reason: "exclusion-inspection-unavailable", table, name: exclusion.name } });
5319
5839
  if (before !== undefined)
5320
- buckets.dropExclusion.push({ kind: "dropExclusion", table, exclusion: before, destructive: false });
5321
- buckets.addExclusion.push({ kind: "addExclusion", table, exclusion, destructive: false });
5840
+ buckets.dropExclusion.push({ kind: "dropExclusion", table, ...placement, exclusion: before, destructive: false });
5841
+ buckets.addExclusion.push({ kind: "addExclusion", table, ...placement, exclusion, destructive: false });
5322
5842
  }
5323
5843
  for (const exclusion of previous?.exclusions ?? [])
5324
5844
  if (!model.exclusions?.some((entry) => entry.name === exclusion.name))
@@ -5326,6 +5846,7 @@ function diffSchemas(params) {
5326
5846
  }
5327
5847
  refuseDependentEnumStorageChanges({ from, to, buckets, dialect });
5328
5848
  return withRlsSteps({ from: params.from, to: params.to, steps: [
5849
+ ...buckets.createNamespace,
5329
5850
  ...renamed.steps,
5330
5851
  ...buckets.dropView,
5331
5852
  ...buckets.createEnum,
@@ -5358,7 +5879,7 @@ function refuseScalarListColumns(params) {
5358
5879
  for (const field of model.fields) {
5359
5880
  if (!field.isList || field.isUnsupported === true)
5360
5881
  continue;
5361
- throw unsupportedCapability6({
5882
+ throw unsupportedCapability10({
5362
5883
  dialect,
5363
5884
  feature: `scalar list column "${model.name}.${field.name}"`,
5364
5885
  hint: `${dialect} has no array column type \u2014 model the list as a separate table or a ${dialect === "mysql" ? "JSON" : "Json"} column`
@@ -5378,12 +5899,12 @@ function diffEnums(params) {
5378
5899
  const currentValues = new Set(enumDef.values);
5379
5900
  for (const value of enumDef.values) {
5380
5901
  if (!previousValues.has(value)) {
5381
- buckets.addEnumValue.push({ kind: "addEnumValue", enumName: name, value, destructive: false });
5902
+ buckets.addEnumValue.push({ kind: "addEnumValue", enumName: enumDef.dbName ?? enumDef.name, ...enumDef.namespace === undefined ? {} : { namespace: enumDef.namespace }, value, destructive: false });
5382
5903
  }
5383
5904
  }
5384
5905
  for (const value of previous.values) {
5385
5906
  if (!currentValues.has(value)) {
5386
- throw unsupportedCapability6({
5907
+ throw unsupportedCapability10({
5387
5908
  dialect: "postgres",
5388
5909
  feature: `removing value "${value}" from enum "${name}"`,
5389
5910
  hint: "postgres cannot drop enum values \u2014 recreate the type manually (create new type, ALTER COLUMN ... USING, drop old type)"
@@ -5391,9 +5912,9 @@ function diffEnums(params) {
5391
5912
  }
5392
5913
  }
5393
5914
  }
5394
- for (const [name] of from.enums) {
5915
+ for (const [name, enumDef] of from.enums) {
5395
5916
  if (!to.enums.has(name)) {
5396
- buckets.dropEnum.push({ kind: "dropEnum", enumName: name, destructive: true });
5917
+ buckets.dropEnum.push({ kind: "dropEnum", enumName: enumDef.dbName ?? enumDef.name, ...enumDef.namespace === undefined ? {} : { namespace: enumDef.namespace }, destructive: true });
5397
5918
  }
5398
5919
  }
5399
5920
  }
@@ -5403,10 +5924,10 @@ function refuseDependentEnumStorageChanges(params) {
5403
5924
  if (step.kind !== "alterColumn" || !isEnumStringTransition({ from: step.from, to: step.to }))
5404
5925
  continue;
5405
5926
  for (const normalized of [from, to]) {
5406
- const hasFk = [...normalized.tables.values()].some((table) => [...table.fks.values()].some((fk) => table.table === step.table && fk.columns.includes(step.column) || fk.refTable === step.table && fk.refColumns.includes(step.column)));
5407
- const hasJoin = [...normalized.joinTables.values()].some((join) => [join.a, join.b].some((end) => end.table === step.table && end.column === step.column));
5927
+ const hasFk = [...normalized.tables.values()].some((table) => [...table.fks.values()].some((fk) => table.table === step.table && (table.namespace ?? "public") === (step.namespace ?? "public") && fk.columns.includes(step.column) || fk.refTable === step.table && (fk.refNamespace ?? "public") === (step.namespace ?? "public") && fk.refColumns.includes(step.column)));
5928
+ const hasJoin = [...normalized.joinTables.values()].some((join) => [join.a, join.b].some((end) => end.table === step.table && (end.namespace ?? "public") === (step.namespace ?? "public") && end.column === step.column));
5408
5929
  if (hasFk || hasJoin) {
5409
- throw unsupportedCapability6({
5930
+ throw unsupportedCapability10({
5410
5931
  dialect,
5411
5932
  feature: `enum storage conversion on "${step.table}"."${step.column}" with foreign-key dependencies`,
5412
5933
  hint: "migrate the referencing columns and constraints together in a reviewed manual migration"
@@ -5417,28 +5938,31 @@ function refuseDependentEnumStorageChanges(params) {
5417
5938
  }
5418
5939
  function diffViews(params) {
5419
5940
  const { from, to, buckets } = params;
5420
- for (const [name] of from.views) {
5941
+ for (const [name, view] of from.views) {
5421
5942
  if (!to.views.has(name)) {
5422
- buckets.dropView.push({ kind: "dropView", table: name, destructive: true });
5943
+ buckets.dropView.push({ kind: "dropView", table: view.dbName ?? view.name, ...view.namespace === undefined ? {} : { namespace: view.namespace }, destructive: true });
5423
5944
  }
5424
5945
  }
5425
5946
  }
5426
5947
  function diffJoinTables(params) {
5427
5948
  const { from, to, buckets } = params;
5428
- for (const [table, joinTable] of to.joinTables) {
5429
- if (!from.joinTables.has(table)) {
5949
+ for (const [key, joinTable] of to.joinTables) {
5950
+ if (!from.joinTables.has(key)) {
5951
+ const table = joinTable.table;
5952
+ const placement = joinTable.namespace === undefined ? {} : { namespace: joinTable.namespace };
5430
5953
  buckets.createJoinTable.push({
5431
5954
  kind: "createJoinTable",
5432
5955
  table,
5956
+ ...placement,
5433
5957
  a: joinTable.a,
5434
5958
  b: joinTable.b,
5435
5959
  destructive: false
5436
5960
  });
5437
5961
  }
5438
5962
  }
5439
- for (const [table] of from.joinTables) {
5440
- if (!to.joinTables.has(table)) {
5441
- buckets.dropJoinTable.push({ kind: "dropJoinTable", table, destructive: true });
5963
+ for (const [key, joinTable] of from.joinTables) {
5964
+ if (!to.joinTables.has(key)) {
5965
+ buckets.dropJoinTable.push({ kind: "dropJoinTable", table: joinTable.table, ...joinTable.namespace === undefined ? {} : { namespace: joinTable.namespace }, destructive: true });
5442
5966
  }
5443
5967
  }
5444
5968
  }
@@ -5463,9 +5987,9 @@ function diffTables(params) {
5463
5987
  continue;
5464
5988
  if (dialect !== "sqlite")
5465
5989
  for (const fk of table.fks.values()) {
5466
- buckets.dropForeignKey.push({ kind: "dropForeignKey", table: name, constraintName: fk.constraintName, destructive: false });
5990
+ buckets.dropForeignKey.push({ kind: "dropForeignKey", table: table.table, ...table.namespace === undefined ? {} : { namespace: table.namespace }, constraintName: fk.constraintName, destructive: false });
5467
5991
  }
5468
- buckets.dropTable.push({ kind: "dropTable", table: name, destructive: true });
5992
+ buckets.dropTable.push({ kind: "dropTable", table: table.table, ...table.namespace === undefined ? {} : { namespace: table.namespace }, destructive: true });
5469
5993
  }
5470
5994
  }
5471
5995
  function createWholeTable(params) {
@@ -5473,6 +5997,7 @@ function createWholeTable(params) {
5473
5997
  buckets.createTable.push({
5474
5998
  kind: "createTable",
5475
5999
  table: table.table,
6000
+ ...table.namespace === undefined ? {} : { namespace: table.namespace },
5476
6001
  columns: [...table.columns.values()].map((column) => column.field),
5477
6002
  primaryKey: table.pk,
5478
6003
  ...table.model.primaryKeyConstraintName === undefined ? {} : { primaryKeyConstraintName: table.model.primaryKeyConstraintName },
@@ -5483,6 +6008,7 @@ function createWholeTable(params) {
5483
6008
  buckets.createIndex.push({
5484
6009
  kind: "createIndex",
5485
6010
  table: table.table,
6011
+ ...table.namespace === undefined ? {} : { namespace: table.namespace },
5486
6012
  name: index.name,
5487
6013
  indexKind: index.kind,
5488
6014
  unique: index.unique,
@@ -5496,6 +6022,7 @@ function createWholeTable(params) {
5496
6022
  buckets.addForeignKey.push({
5497
6023
  kind: "addForeignKey",
5498
6024
  table: table.table,
6025
+ ...table.namespace === undefined ? {} : { namespace: table.namespace },
5499
6026
  constraintName: fk.constraintName,
5500
6027
  columns: fk.columns,
5501
6028
  refTable: fk.refTable,
@@ -5509,10 +6036,11 @@ function createWholeTable(params) {
5509
6036
  function diffOneTable(params) {
5510
6037
  const { previous, current, buckets, dialect, fromEnums, toEnums } = params;
5511
6038
  const table = current.table;
6039
+ const placement = current.namespace === undefined ? {} : { namespace: current.namespace };
5512
6040
  for (const [name, column] of current.columns) {
5513
6041
  const before = previous.columns.get(name);
5514
6042
  if (before === undefined) {
5515
- buckets.addColumn.push({ kind: "addColumn", table, column: column.field, destructive: false });
6043
+ buckets.addColumn.push({ kind: "addColumn", table, ...placement, column: column.field, destructive: false });
5516
6044
  continue;
5517
6045
  }
5518
6046
  const changes = [];
@@ -5528,13 +6056,13 @@ function diffOneTable(params) {
5528
6056
  if (changes.length === 0)
5529
6057
  continue;
5530
6058
  if (changes.includes("type") && [...previous.checks].some(([name, expression]) => current.checks.get(name) === expression)) {
5531
- throw unsupportedCapability6({ dialect, feature: "alterColumnWithRetainedCheck", hint: "CHECK dependencies and type compatibility are not proven; use a reviewed migration that replaces and revalidates the affected constraints" });
6059
+ throw unsupportedCapability10({ dialect, feature: "alterColumnWithRetainedCheck", hint: "CHECK dependencies and type compatibility are not proven; use a reviewed migration that replaces and revalidates the affected constraints" });
5532
6060
  }
5533
6061
  const storageChange = isEnumStringTransition({ from: before.field, to: column.field });
5534
6062
  if (storageChange) {
5535
6063
  assertEnumStringTransition({ from: before.field, to: column.field, dialect, table, column: name });
5536
6064
  if (isEnumType6(before.field.type) && column.field.type === "String") {
5537
- const values = resolveEnumValues({ name: before.field.type.enum, enums: fromEnums });
6065
+ const values = resolveEnumValues({ name: before.field.type.enum, namespace: before.field.type.namespace, enums: fromEnums });
5538
6066
  if (values !== undefined)
5539
6067
  assertStringEnumLabelsFit({ field: column.field, values, dialect, table });
5540
6068
  }
@@ -5546,7 +6074,7 @@ function diffOneTable(params) {
5546
6074
  if (fromValues !== undefined && toValues !== undefined) {
5547
6075
  const removed = fromValues.filter((value) => !toValues.includes(value));
5548
6076
  if (removed.length > 0) {
5549
- throw unsupportedCapability6({
6077
+ throw unsupportedCapability10({
5550
6078
  dialect,
5551
6079
  feature: `removing value(s) ${removed.map((value) => `"${value}"`).join(", ")} from enum column "${table}"."${name}"`,
5552
6080
  hint: "migrate the stored values away first, then recreate the column with the narrower enum"
@@ -5558,6 +6086,7 @@ function diffOneTable(params) {
5558
6086
  buckets.alterColumn.push({
5559
6087
  kind: "alterColumn",
5560
6088
  table,
6089
+ ...placement,
5561
6090
  column: name,
5562
6091
  from: before.field,
5563
6092
  to: column.field,
@@ -5567,19 +6096,20 @@ function diffOneTable(params) {
5567
6096
  }
5568
6097
  for (const [name] of previous.columns) {
5569
6098
  if (!current.columns.has(name)) {
5570
- buckets.dropColumn.push({ kind: "dropColumn", table, column: name, destructive: true });
6099
+ buckets.dropColumn.push({ kind: "dropColumn", table, ...placement, column: name, destructive: true });
5571
6100
  }
5572
6101
  }
5573
6102
  const pkBefore = previous.pk.join(",");
5574
6103
  const pkAfter = current.pk.join(",");
5575
6104
  if (pkBefore !== pkAfter) {
5576
6105
  if (previous.pk.length > 0) {
5577
- buckets.dropPrimaryKey.push({ kind: "dropPrimaryKey", table, destructive: false, ...previous.model.primaryKeyConstraintName === undefined ? {} : { constraintName: previous.model.primaryKeyConstraintName } });
6106
+ buckets.dropPrimaryKey.push({ kind: "dropPrimaryKey", table, ...placement, destructive: false, ...previous.model.primaryKeyConstraintName === undefined ? {} : { constraintName: previous.model.primaryKeyConstraintName } });
5578
6107
  }
5579
6108
  if (current.pk.length > 0) {
5580
6109
  buckets.addPrimaryKey.push({
5581
6110
  kind: "addPrimaryKey",
5582
6111
  table,
6112
+ ...placement,
5583
6113
  columns: current.pk,
5584
6114
  ...current.model.primaryKeyConstraintName === undefined ? {} : { constraintName: current.model.primaryKeyConstraintName },
5585
6115
  destructive: false
@@ -5591,15 +6121,15 @@ function diffOneTable(params) {
5591
6121
  if (before === expression)
5592
6122
  continue;
5593
6123
  if (before !== undefined && previous.model.checks?.some((check) => check.name === name && check.catalogExpression === true))
5594
- throw new VibeError27({ 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 } });
6124
+ 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 } });
5595
6125
  if (before !== undefined)
5596
- buckets.dropCheck.push({ kind: "dropCheck", table, name, destructive: false });
5597
- buckets.addCheck.push({ kind: "addCheck", table, name, expression, destructive: false });
6126
+ buckets.dropCheck.push({ kind: "dropCheck", table, ...placement, name, destructive: false });
6127
+ buckets.addCheck.push({ kind: "addCheck", table, ...placement, name, expression, destructive: false });
5598
6128
  }
5599
6129
  for (const [name] of previous.checks) {
5600
6130
  if (current.checks.has(name))
5601
6131
  continue;
5602
- buckets.dropCheck.push({ kind: "dropCheck", table, name, destructive: false });
6132
+ buckets.dropCheck.push({ kind: "dropCheck", table, ...placement, name, destructive: false });
5603
6133
  }
5604
6134
  for (const [key, index] of current.indexes) {
5605
6135
  if (previous.indexes.has(key))
@@ -5607,6 +6137,7 @@ function diffOneTable(params) {
5607
6137
  buckets.createIndex.push({
5608
6138
  kind: "createIndex",
5609
6139
  table,
6140
+ ...placement,
5610
6141
  name: index.name,
5611
6142
  indexKind: index.kind,
5612
6143
  unique: index.unique,
@@ -5619,7 +6150,7 @@ function diffOneTable(params) {
5619
6150
  for (const [key, index] of previous.indexes) {
5620
6151
  if (current.indexes.has(key))
5621
6152
  continue;
5622
- buckets.dropIndex.push({ kind: "dropIndex", name: index.name, table, destructive: false, ...index.constraintName === undefined ? {} : { constraintName: index.constraintName } });
6153
+ buckets.dropIndex.push({ kind: "dropIndex", name: index.name, table, ...placement, destructive: false, ...index.constraintName === undefined ? {} : { constraintName: index.constraintName } });
5623
6154
  }
5624
6155
  for (const [key, fk] of current.fks) {
5625
6156
  const before = previous.fks.get(key);
@@ -5627,9 +6158,11 @@ function diffOneTable(params) {
5627
6158
  buckets.addForeignKey.push({
5628
6159
  kind: "addForeignKey",
5629
6160
  table,
6161
+ ...placement,
5630
6162
  constraintName: fk.constraintName,
5631
6163
  columns: fk.columns,
5632
6164
  refTable: fk.refTable,
6165
+ ...fk.refNamespace === undefined ? {} : { refNamespace: fk.refNamespace },
5633
6166
  refColumns: fk.refColumns,
5634
6167
  onDelete: fk.onDelete,
5635
6168
  onUpdate: fk.onUpdate,
@@ -5641,15 +6174,18 @@ function diffOneTable(params) {
5641
6174
  buckets.dropForeignKey.push({
5642
6175
  kind: "dropForeignKey",
5643
6176
  table,
6177
+ ...placement,
5644
6178
  constraintName: before.constraintName,
5645
6179
  destructive: false
5646
6180
  });
5647
6181
  buckets.addForeignKey.push({
5648
6182
  kind: "addForeignKey",
5649
6183
  table,
6184
+ ...placement,
5650
6185
  constraintName: fk.constraintName,
5651
6186
  columns: fk.columns,
5652
6187
  refTable: fk.refTable,
6188
+ ...fk.refNamespace === undefined ? {} : { refNamespace: fk.refNamespace },
5653
6189
  refColumns: fk.refColumns,
5654
6190
  onDelete: fk.onDelete,
5655
6191
  onUpdate: fk.onUpdate,
@@ -5663,6 +6199,7 @@ function diffOneTable(params) {
5663
6199
  buckets.dropForeignKey.push({
5664
6200
  kind: "dropForeignKey",
5665
6201
  table,
6202
+ ...placement,
5666
6203
  constraintName: fk.constraintName,
5667
6204
  destructive: false
5668
6205
  });
@@ -5670,12 +6207,12 @@ function diffOneTable(params) {
5670
6207
  }
5671
6208
 
5672
6209
  // packages/migrate/src/ddl/render.ts
5673
- import { VibeError as VibeError31, sqlObjectKey as sqlObjectKey9 } from "@vibeorm/schema";
6210
+ import { VibeError as VibeError34, sqlObjectKey as sqlObjectKey10 } from "@vibeorm/schema";
5674
6211
 
5675
6212
  // packages/migrate/src/ddl/postgres.ts
5676
6213
  import { renderExclusionSetup } from "@vibeorm/sql";
5677
- import { VibeError as VibeError28, defaultPrimaryKeyName as defaultPrimaryKeyName3, implicitJoinTableObjectNames as implicitJoinTableObjectNames2, isEnumType as isEnumType7 } from "@vibeorm/schema";
5678
- import { postgresDialect as postgresDialect8, renderRlsPolicyPath } from "@vibeorm/sql";
6214
+ import { VibeError as VibeError31, assertSqlNamespace as assertSqlNamespace3, defaultPrimaryKeyName as defaultPrimaryKeyName3, implicitJoinTableObjectNames as implicitJoinTableObjectNames2, isEnumType as isEnumType7 } from "@vibeorm/schema";
6215
+ import { postgresDialect as postgresDialect9, renderRlsPolicyPath, renderIndexColumns } from "@vibeorm/sql";
5679
6216
  function renderPostgresSteps(params) {
5680
6217
  refuseSameTransactionEnumUse({ steps: params.steps });
5681
6218
  return params.steps.flatMap((step) => renderStep({ step }));
@@ -5685,19 +6222,21 @@ function refuseSameTransactionEnumUse(params) {
5685
6222
  for (const step of params.steps) {
5686
6223
  if (step.kind !== "addEnumValue")
5687
6224
  continue;
5688
- const values = added.get(step.enumName) ?? new Set;
6225
+ const key = JSON.stringify([step.namespace ?? "public", step.enumName]);
6226
+ const values = added.get(key) ?? new Set;
5689
6227
  values.add(step.value);
5690
- added.set(step.enumName, values);
6228
+ added.set(key, values);
5691
6229
  }
5692
6230
  if (added.size === 0)
5693
6231
  return;
5694
6232
  const use = (field) => {
5695
6233
  if (!isEnumType7(field.type))
5696
6234
  return;
5697
- const enumName = field.type.enum;
6235
+ const type = field.type;
6236
+ const enumName = type.enum;
5698
6237
  const def = field.default;
5699
6238
  const candidates = def?.kind === "list" ? def.values : def?.kind === "enumValue" || def?.kind === "literal" ? [def.value] : [];
5700
- const value = candidates.find((entry) => typeof entry === "string" && added.get(enumName)?.has(entry) === true);
6239
+ const value = candidates.find((entry) => typeof entry === "string" && added.get(JSON.stringify([type.namespace ?? "public", enumName]))?.has(entry) === true);
5701
6240
  return value === undefined ? undefined : { enumName, value };
5702
6241
  };
5703
6242
  for (const step of params.steps) {
@@ -5709,7 +6248,7 @@ function refuseSameTransactionEnumUse(params) {
5709
6248
  const found = use(field);
5710
6249
  if (found === undefined)
5711
6250
  continue;
5712
- throw new VibeError28({
6251
+ throw new VibeError31({
5713
6252
  code: "VIBE_MIGRATION",
5714
6253
  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`,
5715
6254
  meta: { enum: found.enumName, value: found.value, table: step.table, column: field.dbName ?? field.name, rule: "enum-value-used-in-adding-transaction" }
@@ -5720,27 +6259,30 @@ function refuseSameTransactionEnumUse(params) {
5720
6259
  function renderStep(params) {
5721
6260
  const { step } = params;
5722
6261
  switch (step.kind) {
6262
+ case "createNamespace":
6263
+ assertSqlNamespace3({ namespace: step.namespace, dialect: "postgres", allowPublic: false });
6264
+ return [`CREATE SCHEMA ${q2(step.namespace)};`];
5723
6265
  case "addExclusion":
5724
- return [...renderExclusionSetup({ dialect: postgresDialect8, exclusion: step.exclusion }), `ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(step.exclusion.name)} ${step.exclusion.definition};`];
6266
+ return [...renderExclusionSetup({ dialect: postgresDialect9, exclusion: step.exclusion }), `ALTER TABLE ${qualifiedName(step.table, step.namespace)} ADD CONSTRAINT ${q2(step.exclusion.name)} ${step.exclusion.definition};`];
5725
6267
  case "dropExclusion":
5726
- return [`ALTER TABLE ${publicName(step.table)} DROP CONSTRAINT ${q2(step.exclusion.name)};`];
6268
+ return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} DROP CONSTRAINT ${q2(step.exclusion.name)};`];
5727
6269
  case "enableRls":
5728
6270
  case "forceRls":
5729
6271
  case "disableRls":
5730
6272
  case "noForceRls":
5731
6273
  case "createPolicy":
5732
6274
  case "dropPolicy":
5733
- return step.kind === "createPolicy" ? [renderRlsPolicyPath({ dialect: postgresDialect8, stage: "save" }).text, renderRlsPolicyPath({ dialect: postgresDialect8, stage: "empty" }).text, ...renderRlsStep({ step, dialect: "postgres" }), renderRlsPolicyPath({ dialect: postgresDialect8, stage: "restore" }).text] : renderRlsStep({ step, dialect: "postgres" });
6275
+ return step.kind === "createPolicy" ? [renderRlsPolicyPath({ dialect: postgresDialect9, stage: "save" }).text, renderRlsPolicyPath({ dialect: postgresDialect9, stage: "empty" }).text, ...renderRlsStep({ step, dialect: "postgres" }), renderRlsPolicyPath({ dialect: postgresDialect9, stage: "restore" }).text] : renderRlsStep({ step, dialect: "postgres" });
5734
6276
  case "createEnum": {
5735
6277
  const name = step.enumDef.dbName ?? step.enumDef.name;
5736
6278
  const values = step.enumDef.values.map((value) => quotePgString({ value })).join(", ");
5737
- return [`CREATE TYPE ${publicName(name)} AS ENUM (${values});`];
6279
+ return [`CREATE TYPE ${qualifiedName(name, step.enumDef.namespace)} AS ENUM (${values});`];
5738
6280
  }
5739
6281
  case "dropEnum":
5740
- return [`DROP TYPE ${publicName(step.enumName)};`];
6282
+ return [`DROP TYPE ${qualifiedName(step.enumName, step.namespace)};`];
5741
6283
  case "addEnumValue":
5742
6284
  return [
5743
- `ALTER TYPE ${publicName(step.enumName)} ADD VALUE IF NOT EXISTS ${quotePgString({ value: step.value })};`
6285
+ `ALTER TYPE ${qualifiedName(step.enumName, step.namespace)} ADD VALUE IF NOT EXISTS ${quotePgString({ value: step.value })};`
5744
6286
  ];
5745
6287
  case "createTable": {
5746
6288
  const lines = step.columns.map((column) => ` ${columnDefSql({ field: column })}`);
@@ -5751,101 +6293,101 @@ function renderStep(params) {
5751
6293
  for (const check of step.checks ?? []) {
5752
6294
  lines.push(` CONSTRAINT ${q2(check.name)} CHECK (${check.expression})`);
5753
6295
  }
5754
- return [`CREATE TABLE ${publicName(step.table)} (
6296
+ return [`CREATE TABLE ${qualifiedName(step.table, step.namespace)} (
5755
6297
  ${lines.join(`,
5756
6298
  `)}
5757
6299
  );`];
5758
6300
  }
5759
6301
  case "dropTable":
5760
6302
  case "dropJoinTable":
5761
- return [`DROP TABLE ${publicName(step.table)};`];
6303
+ return [`DROP TABLE ${qualifiedName(step.table, step.namespace)};`];
5762
6304
  case "dropView":
5763
- return [`DROP VIEW IF EXISTS ${publicName(step.table)};`];
6305
+ return [`DROP VIEW IF EXISTS ${qualifiedName(step.table, step.namespace)};`];
5764
6306
  case "addColumn":
5765
- return [`ALTER TABLE ${publicName(step.table)} ADD COLUMN ${columnDefSql({ field: step.column })};`];
6307
+ return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} ADD COLUMN ${columnDefSql({ field: step.column })};`];
5766
6308
  case "dropColumn":
5767
- return [`ALTER TABLE ${publicName(step.table)} DROP COLUMN ${q2(step.column)};`];
6309
+ return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} DROP COLUMN ${q2(step.column)};`];
5768
6310
  case "alterColumn":
5769
6311
  return renderAlterColumn({ step });
5770
6312
  case "createIndex": {
5771
6313
  const unique = step.unique ? "UNIQUE " : "";
5772
6314
  const using = !step.unique && step.indexKind !== "btree" ? ` USING ${step.indexKind}` : "";
5773
- const cols = step.columns.map((column) => `${q2(column.name)}${column.desc ? " DESC" : ""}`).join(", ");
6315
+ const cols = renderIndexColumns({ dialect: postgresDialect9, columns: step.columns, unique: step.unique }).join(", ");
5774
6316
  const nulls = step.nullsNotDistinct === true ? " NULLS NOT DISTINCT" : "";
5775
6317
  const where = step.where === undefined ? "" : ` WHERE ${step.where}`;
5776
6318
  return [
5777
- `CREATE ${unique}INDEX ${q2(step.name)} ON ${publicName(step.table)}${using} (${cols})${nulls}${where};`
6319
+ `CREATE ${unique}INDEX ${q2(step.name)} ON ${qualifiedName(step.table, step.namespace)}${using} (${cols})${nulls}${where};`
5778
6320
  ];
5779
6321
  }
5780
6322
  case "dropIndex":
5781
6323
  if (step.constraintName !== undefined) {
5782
6324
  if (step.table === undefined)
5783
- throw new VibeError28({ code: "VIBE_MIGRATION", message: "Constraint-backed index removal requires its owning table" });
5784
- return [`ALTER TABLE ${publicName(step.table)} DROP CONSTRAINT ${q2(step.constraintName)};`];
6325
+ throw new VibeError31({ code: "VIBE_MIGRATION", message: "Constraint-backed index removal requires its owning table" });
6326
+ return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} DROP CONSTRAINT ${q2(step.constraintName)};`];
5785
6327
  }
5786
- return [`DROP INDEX ${publicName(step.name)};`];
6328
+ return [`DROP INDEX ${qualifiedName(step.name, step.namespace)};`];
5787
6329
  case "addCheck":
5788
6330
  return [
5789
- `ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(step.name)} CHECK (${step.expression});`
6331
+ `ALTER TABLE ${qualifiedName(step.table, step.namespace)} ADD CONSTRAINT ${q2(step.name)} CHECK (${step.expression});`
5790
6332
  ];
5791
6333
  case "dropCheck":
5792
- return [`ALTER TABLE ${publicName(step.table)} DROP CONSTRAINT ${q2(step.name)};`];
6334
+ return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} DROP CONSTRAINT ${q2(step.name)};`];
5793
6335
  case "addForeignKey": {
5794
6336
  const cols = step.columns.map((column) => q2(column)).join(", ");
5795
6337
  const refCols = step.refColumns.map((column) => q2(column)).join(", ");
5796
6338
  return [
5797
- `ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(step.constraintName)} FOREIGN KEY (${cols}) REFERENCES ${publicName(step.refTable)} (${refCols}) ON DELETE ${CASCADE_ACTION_SQL[step.onDelete]} ON UPDATE ${CASCADE_ACTION_SQL[step.onUpdate]};`
6339
+ `ALTER TABLE ${qualifiedName(step.table, step.namespace)} ADD CONSTRAINT ${q2(step.constraintName)} FOREIGN KEY (${cols}) REFERENCES ${qualifiedName(step.refTable, step.refNamespace)} (${refCols}) ON DELETE ${CASCADE_ACTION_SQL[step.onDelete]} ON UPDATE ${CASCADE_ACTION_SQL[step.onUpdate]};`
5798
6340
  ];
5799
6341
  }
5800
6342
  case "addForeignKeyNotValid": {
5801
6343
  const cols = step.columns.map((column) => q2(column)).join(", ");
5802
6344
  const refCols = step.refColumns.map((column) => q2(column)).join(", ");
5803
6345
  return [
5804
- `ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(step.constraintName)} FOREIGN KEY (${cols}) REFERENCES ${publicName(step.refTable)} (${refCols}) ON DELETE ${CASCADE_ACTION_SQL[step.onDelete]} ON UPDATE ${CASCADE_ACTION_SQL[step.onUpdate]} NOT VALID;`
6346
+ `ALTER TABLE ${qualifiedName(step.table, step.namespace)} ADD CONSTRAINT ${q2(step.constraintName)} FOREIGN KEY (${cols}) REFERENCES ${qualifiedName(step.refTable, step.refNamespace)} (${refCols}) ON DELETE ${CASCADE_ACTION_SQL[step.onDelete]} ON UPDATE ${CASCADE_ACTION_SQL[step.onUpdate]} NOT VALID;`
5805
6347
  ];
5806
6348
  }
5807
6349
  case "addCheckNotValid":
5808
6350
  return [
5809
- `ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(step.name)} CHECK (${step.expression}) NOT VALID;`
6351
+ `ALTER TABLE ${qualifiedName(step.table, step.namespace)} ADD CONSTRAINT ${q2(step.name)} CHECK (${step.expression}) NOT VALID;`
5810
6352
  ];
5811
6353
  case "validateConstraint":
5812
- return [`ALTER TABLE ${publicName(step.table)} VALIDATE CONSTRAINT ${q2(step.name)};`];
6354
+ return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} VALIDATE CONSTRAINT ${q2(step.name)};`];
5813
6355
  case "renameTable":
5814
- return [`ALTER TABLE ${publicName(step.from)} RENAME TO ${q2(step.to)};`];
6356
+ return [`ALTER TABLE ${qualifiedName(step.from, step.namespace)} RENAME TO ${q2(step.to)};`];
5815
6357
  case "renameColumn":
5816
- return [`ALTER TABLE ${publicName(step.table)} RENAME COLUMN ${q2(step.from)} TO ${q2(step.to)};`];
6358
+ return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} RENAME COLUMN ${q2(step.from)} TO ${q2(step.to)};`];
5817
6359
  case "dropForeignKey":
5818
- return [`ALTER TABLE ${publicName(step.table)} DROP CONSTRAINT ${q2(step.constraintName)};`];
6360
+ return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} DROP CONSTRAINT ${q2(step.constraintName)};`];
5819
6361
  case "createJoinTable": {
5820
6362
  const aType = columnTypeSql({ field: { ...step.a.field, isList: false } });
5821
6363
  const bType = columnTypeSql({ field: { ...step.b.field, isList: false } });
5822
6364
  const names = implicitJoinTableObjectNames2({ table: step.table });
5823
6365
  return [
5824
- `CREATE TABLE ${publicName(step.table)} (
6366
+ `CREATE TABLE ${qualifiedName(step.table, step.namespace)} (
5825
6367
  "A" ${aType} NOT NULL,
5826
6368
  "B" ${bType} NOT NULL
5827
6369
  );`,
5828
- `CREATE UNIQUE INDEX ${q2(names.uniqueIndex)} ON ${publicName(step.table)} ("A", "B");`,
5829
- `CREATE INDEX ${q2(names.indexB)} ON ${publicName(step.table)} ("B");`,
5830
- `ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(names.foreignKeyA)} FOREIGN KEY ("A") REFERENCES ${publicName(step.a.table)} (${q2(step.a.column)}) ON DELETE CASCADE ON UPDATE CASCADE;`,
5831
- `ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(names.foreignKeyB)} FOREIGN KEY ("B") REFERENCES ${publicName(step.b.table)} (${q2(step.b.column)}) ON DELETE CASCADE ON UPDATE CASCADE;`
6370
+ `CREATE UNIQUE INDEX ${q2(names.uniqueIndex)} ON ${qualifiedName(step.table, step.namespace)} ("A", "B");`,
6371
+ `CREATE INDEX ${q2(names.indexB)} ON ${qualifiedName(step.table, step.namespace)} ("B");`,
6372
+ `ALTER TABLE ${qualifiedName(step.table, step.namespace)} ADD CONSTRAINT ${q2(names.foreignKeyA)} FOREIGN KEY ("A") REFERENCES ${qualifiedName(step.a.table, step.a.namespace)} (${q2(step.a.column)}) ON DELETE CASCADE ON UPDATE CASCADE;`,
6373
+ `ALTER TABLE ${qualifiedName(step.table, step.namespace)} ADD CONSTRAINT ${q2(names.foreignKeyB)} FOREIGN KEY ("B") REFERENCES ${qualifiedName(step.b.table, step.b.namespace)} (${q2(step.b.column)}) ON DELETE CASCADE ON UPDATE CASCADE;`
5832
6374
  ];
5833
6375
  }
5834
6376
  case "addPrimaryKey": {
5835
6377
  const cols = step.columns.map((column) => q2(column)).join(", ");
5836
6378
  return [
5837
- `ALTER TABLE ${publicName(step.table)} ADD CONSTRAINT ${q2(step.constraintName ?? defaultPrimaryKeyName3({ table: step.table, dialect: "postgres" }))} PRIMARY KEY (${cols});`
6379
+ `ALTER TABLE ${qualifiedName(step.table, step.namespace)} ADD CONSTRAINT ${q2(step.constraintName ?? defaultPrimaryKeyName3({ table: step.table, dialect: "postgres" }))} PRIMARY KEY (${cols});`
5838
6380
  ];
5839
6381
  }
5840
6382
  case "dropPrimaryKey":
5841
- return [`ALTER TABLE ${publicName(step.table)} DROP CONSTRAINT ${q2(step.constraintName ?? defaultPrimaryKeyName3({ table: step.table, dialect: "postgres" }))};`];
6383
+ return [`ALTER TABLE ${qualifiedName(step.table, step.namespace)} DROP CONSTRAINT ${q2(step.constraintName ?? defaultPrimaryKeyName3({ table: step.table, dialect: "postgres" }))};`];
5842
6384
  }
5843
6385
  }
5844
6386
  function columnDefSql(params) {
5845
6387
  const { field } = params;
5846
6388
  const name = q2(field.dbName ?? field.name);
5847
6389
  if (field.default?.kind === "autoincrement" && !isIdentityColumn({ field })) {
5848
- throw new VibeError28({
6390
+ throw new VibeError31({
5849
6391
  code: "VIBE_MIGRATION",
5850
6392
  message: `autoincrement default on "${field.name}" requires a non-list Int or BigInt column`,
5851
6393
  meta: { field: field.name }
@@ -5865,7 +6407,7 @@ function renderAlterColumn(params) {
5865
6407
  const { step } = params;
5866
6408
  const statements = [];
5867
6409
  const column = q2(step.column);
5868
- const alterPrefix = `ALTER TABLE ${publicName(step.table)} ALTER COLUMN ${column}`;
6410
+ const alterPrefix = `ALTER TABLE ${qualifiedName(step.table, step.namespace)} ALTER COLUMN ${column}`;
5869
6411
  const storageChange = step.changes.includes("type") && isEnumStringTransition({ from: step.from, to: step.to });
5870
6412
  if (storageChange)
5871
6413
  assertEnumStringTransition({ from: step.from, to: step.to, dialect: "postgres", table: step.table, column: step.column });
@@ -5881,7 +6423,7 @@ function renderAlterColumn(params) {
5881
6423
  }
5882
6424
  if (step.changes.includes("default") || droppedDefault) {
5883
6425
  if (step.to.default?.kind === "autoincrement") {
5884
- throw new VibeError28({
6426
+ throw new VibeError31({
5885
6427
  code: "VIBE_MIGRATION",
5886
6428
  message: `cannot add autoincrement to existing column "${step.table}"."${step.column}" \u2014 drop and recreate the column instead`,
5887
6429
  meta: { table: step.table, column: step.column }
@@ -5892,15 +6434,16 @@ function renderAlterColumn(params) {
5892
6434
  }
5893
6435
  return statements;
5894
6436
  }
5895
- function publicName(name) {
5896
- return `${q2("public")}.${q2(name)}`;
6437
+ function qualifiedName(name, namespace = "public") {
6438
+ assertSqlNamespace3({ namespace, dialect: "postgres" });
6439
+ return `${q2(namespace)}.${q2(name)}`;
5897
6440
  }
5898
6441
  function q2(name) {
5899
- return postgresDialect8.quoteIdent({ name });
6442
+ return postgresDialect9.quoteIdent({ name });
5900
6443
  }
5901
6444
 
5902
6445
  // packages/migrate/src/ddl/sqlite.ts
5903
- import { VibeError as VibeError29, implicitJoinTableObjectNames as implicitJoinTableObjectNames3, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
6446
+ import { VibeError as VibeError32, implicitJoinTableObjectNames as implicitJoinTableObjectNames3, isEnumType as isEnumType8, unsupportedCapability as unsupportedCapability11 } from "@vibeorm/schema";
5904
6447
  import { sqliteDialect as sqliteDialect3 } from "@vibeorm/sql";
5905
6448
 
5906
6449
  // packages/migrate/src/ddl/drop-order.ts
@@ -5950,6 +6493,8 @@ function orderDropsChildrenFirst(params) {
5950
6493
  function describeStep(params) {
5951
6494
  const { step } = params;
5952
6495
  switch (step.kind) {
6496
+ case "createNamespace":
6497
+ return `create namespace ${JSON.stringify(step.namespace)}`;
5953
6498
  case "addExclusion":
5954
6499
  return `add exclusion "${step.exclusion.name}" on "${step.table}"`;
5955
6500
  case "dropExclusion":
@@ -6020,6 +6565,12 @@ function describeStep(params) {
6020
6565
  // packages/migrate/src/ddl/sqlite.ts
6021
6566
  function renderSqliteSteps(params) {
6022
6567
  const { steps } = params;
6568
+ for (const step of steps) {
6569
+ if (step.kind === "createIndex" && step.columns.some((column) => column.operatorClass !== undefined))
6570
+ throw unsupportedCapability11({ dialect: "sqlite", feature: "indexOperatorClasses" });
6571
+ if (step.kind === "createNamespace" || step.namespace !== undefined || step.refNamespace !== undefined)
6572
+ throw unsupportedCapability11({ dialect: "sqlite", feature: "fixedNamespaces" });
6573
+ }
6023
6574
  for (const step of steps)
6024
6575
  if (isRlsStep({ step }))
6025
6576
  renderRlsStep({ step, dialect: "sqlite" });
@@ -6040,7 +6591,7 @@ function renderSqliteSteps(params) {
6040
6591
  continue;
6041
6592
  const awaitsNewParent = [...toNorm?.tables.get(table)?.fks.values() ?? []].some((fk) => createdTables.has(fk.refTable));
6042
6593
  if (awaitsNewParent) {
6043
- throw new VibeError29({
6594
+ throw new VibeError32({
6044
6595
  code: "VIBE_MIGRATION",
6045
6596
  message: `cannot order this sqlite migration safely: rebuilding "${table}" must happen BEFORE the dropped table(s) it references (${[
6046
6597
  ...droppedTables
@@ -6069,7 +6620,7 @@ function renderSqliteSteps(params) {
6069
6620
  switch (step.kind) {
6070
6621
  case "addExclusion":
6071
6622
  case "dropExclusion":
6072
- throw new VibeError29({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6623
+ throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6073
6624
  case "createEnum":
6074
6625
  case "dropEnum":
6075
6626
  case "addEnumValue":
@@ -6155,7 +6706,7 @@ function renderSqliteSteps(params) {
6155
6706
  case "renameColumn": {
6156
6707
  const renamed = step.kind === "renameTable" ? step.from : step.table;
6157
6708
  if (rebuildSet.has(renamed) || step.kind === "renameTable" && rebuildSet.has(step.to)) {
6158
- throw new VibeError29({
6709
+ throw new VibeError32({
6159
6710
  code: "VIBE_MIGRATION",
6160
6711
  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`,
6161
6712
  meta: { reason: "rename-ambiguous", table: renamed, dialect: "sqlite" }
@@ -6167,7 +6718,7 @@ function renderSqliteSteps(params) {
6167
6718
  case "addForeignKeyNotValid":
6168
6719
  case "addCheckNotValid":
6169
6720
  case "validateConstraint":
6170
- throw unsupportedCapability7({
6721
+ throw unsupportedCapability11({
6171
6722
  dialect: "sqlite",
6172
6723
  feature: "stagedConstraints",
6173
6724
  hint: "sqlite applies constraint changes by rebuilding the table; add the constraint in one step"
@@ -6209,7 +6760,7 @@ function classifyRebuilds(params) {
6209
6760
  switch (step.kind) {
6210
6761
  case "addExclusion":
6211
6762
  case "dropExclusion":
6212
- throw new VibeError29({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6763
+ throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "sqlite", feature: "exclusionConstraints" } });
6213
6764
  case "alterColumn":
6214
6765
  case "dropForeignKey":
6215
6766
  case "addPrimaryKey":
@@ -6261,7 +6812,7 @@ async function assertSqliteRebuildTableOptions(params) {
6261
6812
  for (const table of params.tables) {
6262
6813
  const row = rows.find((entry) => entry.schema === "main" && entry.name === table);
6263
6814
  if (row === undefined || Number(row.strict) !== 0 || Number(row.wr) !== 0) {
6264
- throw unsupportedCapability7({ dialect: "sqlite", feature: "sqliteRebuildTableOptions", hint: `Cannot reproduce the existing table options of "${table}"; use a reviewed migration that preserves STRICT and WITHOUT ROWID` });
6815
+ throw unsupportedCapability11({ dialect: "sqlite", feature: "sqliteRebuildTableOptions", hint: `Cannot reproduce the existing table options of "${table}"; use a reviewed migration that preserves STRICT and WITHOUT ROWID` });
6265
6816
  }
6266
6817
  }
6267
6818
  }
@@ -6270,7 +6821,7 @@ function renderTableRebuild(params) {
6270
6821
  const source = from.tables.get(table);
6271
6822
  const target = to.tables.get(table);
6272
6823
  if (source === undefined || target === undefined) {
6273
- throw new VibeError29({
6824
+ throw new VibeError32({
6274
6825
  code: "VIBE_MIGRATION",
6275
6826
  message: `cannot rebuild "${table}" \u2014 the table is missing from the ${source === undefined ? "from" : "to"} schema`,
6276
6827
  meta: { table }
@@ -6297,7 +6848,7 @@ function renderTableRebuild(params) {
6297
6848
  }));
6298
6849
  const common = [...new Set([...target.columns.keys(), ...params.preservedColumns.keys()])].filter((name) => params.preservedColumns.get(name)?.generated !== true && (source.columns.has(name) || params.preservedColumns.has(name)));
6299
6850
  if (common.length === 0) {
6300
- throw unsupportedCapability7({ dialect: "sqlite", feature: "sqliteRebuildWithoutRowTransfer", hint: `Rebuilding "${table}" has no transferable columns and would discard every row; use explicit renames or a reviewed data migration` });
6851
+ throw unsupportedCapability11({ dialect: "sqlite", feature: "sqliteRebuildWithoutRowTransfer", hint: `Rebuilding "${table}" has no transferable columns and would discard every row; use explicit renames or a reviewed data migration` });
6301
6852
  }
6302
6853
  const cols = common.map((name) => q3(name)).join(", ");
6303
6854
  statements.push(`INSERT INTO ${q3(tmp)} (${cols}) SELECT ${cols} FROM ${q3(table)};`);
@@ -6405,7 +6956,7 @@ function columnDefSql2(params) {
6405
6956
  const { field, constraintTable, enums, inlinePkAutoincrement } = params;
6406
6957
  const name = field.dbName ?? field.name;
6407
6958
  if (field.default?.kind === "autoincrement" && !inlinePkAutoincrement) {
6408
- throw new VibeError29({
6959
+ throw new VibeError32({
6409
6960
  code: "VIBE_MIGRATION",
6410
6961
  message: `autoincrement on sqlite requires "${field.name}" to be the single-column Int or BigInt primary key`,
6411
6962
  meta: { field: field.name }
@@ -6424,7 +6975,7 @@ function columnDefSql2(params) {
6424
6975
  const enumName = field.type.enum;
6425
6976
  const values = resolveEnumValues({ name: enumName, enums });
6426
6977
  if (values === undefined) {
6427
- throw new VibeError29({
6978
+ throw new VibeError32({
6428
6979
  code: "VIBE_MIGRATION",
6429
6980
  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`,
6430
6981
  meta: { field: field.name, enum: enumName }
@@ -6438,14 +6989,14 @@ function columnDefSql2(params) {
6438
6989
  function createIndexSql(params) {
6439
6990
  const { table, name, unique, kind, columns, where } = params;
6440
6991
  if (params.nullsNotDistinct === true) {
6441
- throw unsupportedCapability7({
6992
+ throw unsupportedCapability11({
6442
6993
  dialect: "sqlite",
6443
6994
  feature: `UNIQUE NULLS NOT DISTINCT (index "${name}")`,
6444
6995
  hint: "sqlite treats NULLs as distinct in unique indexes and has no switch \u2014 gate the flag per dialect"
6445
6996
  });
6446
6997
  }
6447
6998
  if (!unique && kind !== "btree") {
6448
- throw unsupportedCapability7({
6999
+ throw unsupportedCapability11({
6449
7000
  dialect: "sqlite",
6450
7001
  feature: `index kind "${kind}" (index "${name}")`,
6451
7002
  hint: "sqlite indexes are btree only; use an extension-specific virtual table for others"
@@ -6459,7 +7010,7 @@ function requireSchemas(params) {
6459
7010
  const { step, fromNorm, toNorm } = params;
6460
7011
  if (fromNorm !== undefined && toNorm !== undefined)
6461
7012
  return;
6462
- throw new VibeError29({
7013
+ throw new VibeError32({
6463
7014
  code: "VIBE_MIGRATION",
6464
7015
  message: `sqlite step "${describeStep({ step })}" requires a table rebuild \u2014 renderSteps needs the { from, to } schemas to construct it`,
6465
7016
  meta: { step: step.kind }
@@ -6470,10 +7021,14 @@ function q3(name) {
6470
7021
  }
6471
7022
 
6472
7023
  // packages/migrate/src/ddl/mysql.ts
6473
- import { VibeError as VibeError30, implicitJoinTableObjectNames as implicitJoinTableObjectNames4, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
7024
+ import { VibeError as VibeError33, implicitJoinTableObjectNames as implicitJoinTableObjectNames4, unsupportedCapability as unsupportedCapability12 } from "@vibeorm/schema";
6474
7025
  import { mysqlDialect as mysqlDialect3 } from "@vibeorm/sql";
6475
7026
  function renderMysqlSteps(params) {
6476
7027
  for (const step of params.steps) {
7028
+ if (step.namespace !== undefined || step.refNamespace !== undefined || step.kind === "createNamespace")
7029
+ throw unsupportedCapability12({ dialect: "mysql", feature: "fixedNamespaces" });
7030
+ if (step.kind === "createIndex" && step.columns.some((column) => column.operatorClass !== undefined))
7031
+ throw unsupportedCapability12({ dialect: "mysql", feature: "indexOperatorClasses" });
6477
7032
  if (isRlsStep({ step }))
6478
7033
  renderRlsStep({ step, dialect: "mysql" });
6479
7034
  if (step.kind !== "alterColumn" || !step.changes.some((change) => change === "type" || change === "nullability" || change === "collation"))
@@ -6483,7 +7038,7 @@ function renderMysqlSteps(params) {
6483
7038
  return object.dialect === "mysql" && (identity.kind === "columnDefault" || identity.kind === "generatedColumn" || identity.kind === "column") && identity.table.name === step.table && identity.column === step.column;
6484
7039
  });
6485
7040
  if (owner !== undefined)
6486
- throw unsupportedCapability8({ dialect: "mysql", feature: "mysqlOwnedColumnRewrite", hint: `MODIFY COLUMN would overwrite migration-owned ${owner.id}; use a reviewed migration that preserves the complete column definition` });
7041
+ throw unsupportedCapability12({ dialect: "mysql", feature: "mysqlOwnedColumnRewrite", hint: `MODIFY COLUMN would overwrite migration-owned ${owner.id}; use a reviewed migration that preserves the complete column definition` });
6487
7042
  }
6488
7043
  const enums = new Map;
6489
7044
  for (const enumDef of params.to?.enums ?? []) {
@@ -6503,7 +7058,7 @@ function renderMysqlSteps(params) {
6503
7058
  continue;
6504
7059
  const retainedIndex = [...table.indexes.values()].some((index) => index.columns[0]?.name === auto && !params.steps.some((candidate) => candidate.kind === "dropIndex" && candidate.table === step.table && candidate.name === index.name));
6505
7060
  if (!retainedIndex)
6506
- throw unsupportedCapability8({ dialect: "mysql", feature: "mysqlAutoIncrementKeyRemoval", hint: `Removing the last leading key of AUTO_INCREMENT column "${step.table}.${auto}" requires a reviewed combined ALTER` });
7061
+ throw unsupportedCapability12({ dialect: "mysql", feature: "mysqlAutoIncrementKeyRemoval", hint: `Removing the last leading key of AUTO_INCREMENT column "${step.table}.${auto}" requires a reviewed combined ALTER` });
6507
7062
  }
6508
7063
  let dropCursor = 0;
6509
7064
  const inlineIndexes = new Map;
@@ -6512,7 +7067,7 @@ function renderMysqlSteps(params) {
6512
7067
  const columns = step.kind === "createTable" ? step.columns : step.kind === "addColumn" ? [step.column] : [];
6513
7068
  const counters = columns.filter((field) => field.default?.kind === "autoincrement");
6514
7069
  if (counters.length > 1)
6515
- throw unsupportedCapability8({ dialect: "mysql", feature: "multiple auto-increment columns" });
7070
+ throw unsupportedCapability12({ dialect: "mysql", feature: "multiple auto-increment columns" });
6516
7071
  for (const column of counters) {
6517
7072
  const name = column.dbName ?? column.name;
6518
7073
  if (step.kind === "createTable" && step.primaryKey[0] === name)
@@ -6521,7 +7076,7 @@ function renderMysqlSteps(params) {
6521
7076
  continue;
6522
7077
  const key = params.steps.find((candidate) => candidate.kind === "createIndex" && candidate.table === step.table && candidate.columns[0]?.name === name && !candidate.columns[0]?.desc && candidate.where === undefined && candidate.nullsNotDistinct !== true && (candidate.unique || candidate.indexKind === "btree"));
6523
7078
  if (key === undefined)
6524
- throw unsupportedCapability8({ dialect: "mysql", feature: `auto-increment column ${step.table}.${name} without an accompanying leading index` });
7079
+ throw unsupportedCapability12({ dialect: "mysql", feature: `auto-increment column ${step.table}.${name} without an accompanying leading index` });
6525
7080
  inlineIndexes.set(step, key);
6526
7081
  consumed.add(key);
6527
7082
  }
@@ -6540,6 +7095,8 @@ function renderMysqlSteps(params) {
6540
7095
  function renderStep2(params) {
6541
7096
  const { step, enums } = params;
6542
7097
  switch (step.kind) {
7098
+ case "createNamespace":
7099
+ throw unsupportedCapability12({ dialect: "mysql", feature: "fixedNamespaces" });
6543
7100
  case "enableRls":
6544
7101
  case "forceRls":
6545
7102
  case "disableRls":
@@ -6581,21 +7138,21 @@ ${lines.join(`,
6581
7138
  return renderAlterColumn2({ step, enums });
6582
7139
  case "createIndex": {
6583
7140
  if (step.where !== undefined) {
6584
- throw unsupportedCapability8({
7141
+ throw unsupportedCapability12({
6585
7142
  dialect: "mysql",
6586
7143
  feature: `partial index "${step.name}"`,
6587
7144
  hint: "mysql has no partial indexes \u2014 drop the WHERE predicate or gate the index per dialect"
6588
7145
  });
6589
7146
  }
6590
7147
  if (step.nullsNotDistinct === true) {
6591
- throw unsupportedCapability8({
7148
+ throw unsupportedCapability12({
6592
7149
  dialect: "mysql",
6593
7150
  feature: `UNIQUE NULLS NOT DISTINCT (index "${step.name}")`,
6594
7151
  hint: "mysql treats NULLs as distinct in unique indexes and has no switch \u2014 gate the flag per dialect"
6595
7152
  });
6596
7153
  }
6597
7154
  if (!step.unique && step.indexKind !== "btree") {
6598
- throw unsupportedCapability8({
7155
+ throw unsupportedCapability12({
6599
7156
  dialect: "mysql",
6600
7157
  feature: `index kind "${step.indexKind}" (index "${step.name}")`,
6601
7158
  hint: "mysql secondary indexes are btree only"
@@ -6612,12 +7169,12 @@ ${lines.join(`,
6612
7169
  ];
6613
7170
  case "addExclusion":
6614
7171
  case "dropExclusion":
6615
- throw new VibeError30({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "mysql", feature: "exclusionConstraints" } });
7172
+ throw new VibeError33({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Exclusion constraints require PostgreSQL", meta: { dialect: "mysql", feature: "exclusionConstraints" } });
6616
7173
  case "dropCheck":
6617
7174
  return [`ALTER TABLE ${q4(step.table)} DROP CHECK ${q4(step.name)};`];
6618
7175
  case "dropIndex": {
6619
7176
  if (step.table === undefined) {
6620
- throw new VibeError30({
7177
+ throw new VibeError33({
6621
7178
  code: "VIBE_MIGRATION",
6622
7179
  message: `dropIndex "${step.name}" carries no table \u2014 mysql's DROP INDEX requires one`,
6623
7180
  meta: { index: step.name }
@@ -6639,7 +7196,7 @@ ${lines.join(`,
6639
7196
  case "addForeignKeyNotValid":
6640
7197
  case "addCheckNotValid":
6641
7198
  case "validateConstraint":
6642
- throw unsupportedCapability8({
7199
+ throw unsupportedCapability12({
6643
7200
  dialect: "mysql",
6644
7201
  feature: "stagedConstraints",
6645
7202
  hint: "MySQL validates a constraint when it is added; add it in one step, or take the outage window that scan needs"
@@ -6674,7 +7231,7 @@ function columnDefSql3(params) {
6674
7231
  const name = q4(field.dbName ?? field.name);
6675
7232
  const autoincrement = field.default?.kind === "autoincrement";
6676
7233
  if (autoincrement && field.type !== "Int" && field.type !== "BigInt") {
6677
- throw new VibeError30({
7234
+ throw new VibeError33({
6678
7235
  code: "VIBE_MIGRATION",
6679
7236
  message: `autoincrement default on "${field.name}" requires a non-list Int or BigInt column`,
6680
7237
  meta: { field: field.name }
@@ -6696,7 +7253,7 @@ function renderAlterColumn2(params) {
6696
7253
  const { step, enums } = params;
6697
7254
  assertEnumStringTransition({ from: step.from, to: step.to, dialect: "mysql", table: step.table, column: step.column });
6698
7255
  if (step.to.default?.kind === "autoincrement" && step.from.default?.kind !== "autoincrement") {
6699
- throw new VibeError30({
7256
+ throw new VibeError33({
6700
7257
  code: "VIBE_MIGRATION",
6701
7258
  message: `cannot add autoincrement to existing column "${step.table}"."${step.column}" \u2014 drop and recreate the column instead`,
6702
7259
  meta: { table: step.table, column: step.column }
@@ -6729,31 +7286,31 @@ function declaredSqlitePreservation(params) {
6729
7286
  if (params.from === undefined || to === undefined || (params.from.migrationObjects?.length ?? 0) === 0)
6730
7287
  return { to, restore, columns };
6731
7288
  const invalid = sqliteInvalidatedTables({ steps: params.steps, from: params.from });
6732
- const retained = new Set((to.migrationObjects ?? []).map((object) => sqlObjectKey9({ identity: object.identity })));
7289
+ const retained = new Set((to.migrationObjects ?? []).map((object) => sqlObjectKey10({ identity: object.identity })));
6733
7290
  for (const object of params.from.migrationObjects ?? []) {
6734
7291
  const identity = object.identity;
6735
- if (!("table" in identity) || identity.table.namespace !== "main" || !invalid.rebuilt.has(identity.table.name) || !retained.has(sqlObjectKey9({ identity })))
7292
+ if (!("table" in identity) || identity.table.namespace !== "main" || !invalid.rebuilt.has(identity.table.name) || !retained.has(sqlObjectKey10({ identity })))
6736
7293
  continue;
6737
7294
  const facts = object.expected?.facts;
6738
7295
  if (facts === undefined)
6739
- throw new VibeError31({ code: "VIBE_MIGRATION", message: "SQLite rebuild requires independently declared preservation definitions", meta: { reason: "sqlite-preservation-unavailable", identity } });
7296
+ throw new VibeError34({ code: "VIBE_MIGRATION", message: "SQLite rebuild requires independently declared preservation definitions", meta: { reason: "sqlite-preservation-unavailable", identity } });
6740
7297
  if (identity.kind === "trigger" || identity.kind === "index") {
6741
7298
  if (identity.kind === "index" && to.models.some((model) => (model.dbName ?? model.name) === identity.table.name && model.indexes.some((index) => index.name === identity.name)))
6742
7299
  continue;
6743
7300
  if (typeof facts.definition !== "string")
6744
- throw new VibeError31({ code: "VIBE_MIGRATION", message: "Missing SQLite restoration SQL", meta: { reason: "sqlite-preservation-unavailable", identity } });
7301
+ throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite restoration SQL", meta: { reason: "sqlite-preservation-unavailable", identity } });
6745
7302
  const statements = restore.get(identity.table.name) ?? [];
6746
7303
  statements.push(facts.definition);
6747
7304
  restore.set(identity.table.name, statements);
6748
7305
  } else if (identity.kind === "column" || identity.kind === "generatedColumn") {
6749
7306
  if (typeof facts.columnDefinition !== "string")
6750
- throw new VibeError31({ code: "VIBE_MIGRATION", message: "Missing SQLite column clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7307
+ throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite column clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
6751
7308
  const fields = columns.get(identity.table.name) ?? new Map;
6752
7309
  fields.set(identity.column, { sql: facts.columnDefinition, generated: Number(facts.generated) > 1 });
6753
7310
  columns.set(identity.table.name, fields);
6754
7311
  } else if (identity.kind === "columnDefault") {
6755
7312
  if (facts.definition !== null && typeof facts.definition !== "string")
6756
- throw new VibeError31({ code: "VIBE_MIGRATION", message: "Missing SQLite default clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
7313
+ throw new VibeError34({ code: "VIBE_MIGRATION", message: "Missing SQLite default clause", meta: { reason: "sqlite-preservation-unavailable", identity } });
6757
7314
  const expression = facts.definition;
6758
7315
  to = { ...to, models: to.models.map((model) => (model.dbName ?? model.name) !== identity.table.name ? model : { ...model, fields: model.fields.map((field) => {
6759
7316
  if ((field.dbName ?? field.name) !== identity.column)
@@ -6795,6 +7352,7 @@ function collectExtensionArtifacts(params) {
6795
7352
  const { schema, dialect, extensions } = params;
6796
7353
  const setup = [];
6797
7354
  const objects = [];
7355
+ const groups = [];
6798
7356
  for (const extension of extensions) {
6799
7357
  const artifactsHook = extension.migrate?.artifacts;
6800
7358
  if (artifactsHook === undefined)
@@ -6802,6 +7360,7 @@ function collectExtensionArtifacts(params) {
6802
7360
  const declarations = extensionDeclarations({ schema, name: extension.name });
6803
7361
  if (Object.keys(declarations).length === 0 && extension.schema?.configOnly !== true)
6804
7362
  continue;
7363
+ const contributed = [];
6805
7364
  for (const artifact of artifactsHook({
6806
7365
  schema,
6807
7366
  dialect,
@@ -6809,10 +7368,19 @@ function collectExtensionArtifacts(params) {
6809
7368
  options: extension.options,
6810
7369
  declarations
6811
7370
  })) {
6812
- (artifact.owns.kind === "postgresExtension" ? setup : objects).push(artifact);
7371
+ if (artifact.owns.kind === "postgresExtension")
7372
+ setup.push(artifact);
7373
+ else {
7374
+ objects.push(artifact);
7375
+ contributed.push(artifact);
7376
+ }
6813
7377
  }
7378
+ groups.push({ extension: extension.name, objects: contributed });
6814
7379
  }
6815
- return { setup, objects };
7380
+ return { setup, objects, groups };
7381
+ }
7382
+ function lastDeclaredSchema(params) {
7383
+ return [...params.migrations].reverse().find((migration) => migration.desiredState !== undefined)?.desiredState?.to.schema;
6816
7384
  }
6817
7385
  function ownedTableNames(params) {
6818
7386
  const exact = new Set;
@@ -6914,6 +7482,36 @@ async function probeInstalledExtensionState(params) {
6914
7482
  function artifactCreateSql(params) {
6915
7483
  return params.artifacts.flatMap((artifact) => [...artifact.create]);
6916
7484
  }
7485
+ function planChangedArtifacts(params) {
7486
+ const previousById = new Map((params.previous?.objects ?? []).map((artifact) => [artifact.id, artifact]));
7487
+ const probedIds = new Set((params.probed ?? []).map((artifact) => artifact.id));
7488
+ const missingIds = new Set(params.missing.map((artifact) => artifact.id));
7489
+ const rebuilt = new Set;
7490
+ for (const group of params.current.groups) {
7491
+ const changed = group.objects.filter((artifact) => {
7492
+ if (missingIds.has(artifact.id))
7493
+ return false;
7494
+ if (probedIds.has(artifact.id))
7495
+ return true;
7496
+ const before = previousById.get(artifact.id);
7497
+ return before !== undefined && JSON.stringify(before.owns) === JSON.stringify(artifact.owns) && JSON.stringify(before.create) !== JSON.stringify(artifact.create);
7498
+ });
7499
+ if (changed.length === 0)
7500
+ continue;
7501
+ const rebuiltColumnTables = new Set(changed.flatMap((artifact) => artifact.owns.kind === "column" ? [artifact.owns.table] : []));
7502
+ for (const artifact of group.objects) {
7503
+ if (missingIds.has(artifact.id))
7504
+ continue;
7505
+ const dependent = (artifact.owns.kind === "index" || artifact.owns.kind === "trigger") && rebuiltColumnTables.has(artifact.owns.table);
7506
+ if (changed.includes(artifact) || dependent)
7507
+ rebuilt.add(artifact.id);
7508
+ }
7509
+ }
7510
+ return params.current.objects.filter((artifact) => rebuilt.has(artifact.id));
7511
+ }
7512
+ function artifactDropSql(params) {
7513
+ return [...params.artifacts].reverse().flatMap((artifact) => [...artifact.drop]);
7514
+ }
6917
7515
  // packages/migrate/src/runner.ts
6918
7516
  function migrationRlsSecurityChanges(params) {
6919
7517
  const source = params.migration.rls;
@@ -6997,10 +7595,10 @@ function assertNativeMigrationMetadata(params) {
6997
7595
  if (!native)
6998
7596
  return;
6999
7597
  if (!DIALECT_CAPABILITIES3[params.dialect].nativeRowLevelSecurity) {
7000
- throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Native RLS migrations are unsupported on ${params.dialect}`, meta: { feature: "nativeRowLevelSecurity", dialect: params.dialect } });
7598
+ throw new VibeError35({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Native RLS migrations are unsupported on ${params.dialect}`, meta: { feature: "nativeRowLevelSecurity", dialect: params.dialect } });
7001
7599
  }
7002
7600
  if (params.migration.rls === undefined) {
7003
- throw new VibeError32({ 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 } });
7601
+ 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 } });
7004
7602
  }
7005
7603
  }
7006
7604
  var PG_ADVISORY_LOCK_KEY = 1447645253;
@@ -7045,11 +7643,11 @@ async function assertForwardStart(params) {
7045
7643
  const state = params.migration.desiredState;
7046
7644
  if (state === undefined)
7047
7645
  return;
7048
- const report = await verifyMigrationState({ executor: params.executor, dialect: params.dialect, expected: state.from, ...params.probe === undefined ? {} : { probe: params.probe } });
7646
+ const report = await verifyMigrationState({ executor: params.executor, dialect: params.dialect, expected: state.from, external: params.external, ...params.probe === undefined ? {} : { probe: params.probe } });
7049
7647
  if (report.inSync)
7050
7648
  return;
7051
7649
  if (state.down !== undefined) {
7052
- const restored = await verifyMigrationState({ executor: params.executor, dialect: params.dialect, expected: state.down, ...params.probe === undefined ? {} : { probe: params.probe } });
7650
+ const restored = await verifyMigrationState({ executor: params.executor, dialect: params.dialect, expected: state.down, external: params.external, ...params.probe === undefined ? {} : { probe: params.probe } });
7053
7651
  if (restored.inSync)
7054
7652
  return;
7055
7653
  }
@@ -7064,6 +7662,7 @@ function generateMigration(params) {
7064
7662
  extensions: params.extensions ?? []
7065
7663
  });
7066
7664
  const allArtifacts = [...artifacts.setup, ...artifacts.objects];
7665
+ const rebuilt = planChangedArtifacts({ previous: collectExtensionArtifacts({ schema: from, dialect, extensions: params.extensions ?? [] }), current: artifacts, missing: [] });
7067
7666
  assertRetainedMigrationObjects({ from: from.migrationObjects ?? [], to: to.migrationObjects ?? [] });
7068
7667
  const strippedFrom = preserveMigrationObjects({ from: stripExtensionArtifacts({ schema: from, artifacts: allArtifacts }), to, objects: [...from.migrationObjects ?? [], ...to.migrationObjects ?? []] });
7069
7668
  const steps = diffSchemas({ from: strippedFrom, to, dialect, ...params.renames === undefined ? {} : { renames: params.renames } });
@@ -7074,17 +7673,18 @@ function generateMigration(params) {
7074
7673
  artifacts: missing.filter((artifact) => artifact.owns.kind === "postgresExtension")
7075
7674
  }),
7076
7675
  ...coreSql,
7676
+ ...artifactDropSql({ artifacts: rebuilt }),
7077
7677
  ...artifactCreateSql({
7078
7678
  artifacts: missing.filter((artifact) => artifact.owns.kind !== "postgresExtension")
7079
7679
  }),
7080
7680
  ...orderedMigrationObjects({ objects: to.migrationObjects ?? [] }).flatMap((object) => {
7081
7681
  if (object.dialect !== dialect)
7082
- throw new VibeError32({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}` });
7682
+ throw new VibeError35({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}` });
7083
7683
  const previous = (params.desiredState?.from.schema.migrationObjects ?? from.migrationObjects)?.find((entry) => entry.id === object.id);
7084
7684
  if (previous === undefined)
7085
7685
  return [...object.create];
7086
7686
  if (JSON.stringify(previous) !== JSON.stringify(object))
7087
- throw new VibeError32({ 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 } });
7687
+ 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 } });
7088
7688
  return [];
7089
7689
  })
7090
7690
  ];
@@ -7110,8 +7710,8 @@ async function withMigrationLock(params) {
7110
7710
  }, (cause) => releaseLock({ executor, dialect: params.dialect }).then(() => {
7111
7711
  throw cause;
7112
7712
  }, (cleanupCause) => {
7113
- if (cause instanceof VibeError32)
7114
- throw new VibeError32({ code: cause.code, message: cause.message, meta: { ...cause.meta, cleanupCause }, cause });
7713
+ if (cause instanceof VibeError35)
7714
+ throw new VibeError35({ code: cause.code, message: cause.message, meta: { ...cause.meta, cleanupCause }, cause });
7115
7715
  throw cause;
7116
7716
  }));
7117
7717
  };
@@ -7135,16 +7735,17 @@ function validateMigrationReplay(params) {
7135
7735
  }
7136
7736
  function assertMigrationMetadata(params) {
7137
7737
  const { migration, dialect } = params;
7738
+ assertModuleAdoption({ migration });
7138
7739
  if (migration.moduleOrigin !== undefined) {
7139
7740
  assertMaterializedModuleMigration({ migration });
7140
7741
  assertModuleOwnership({ origin: migration.moduleOrigin, desiredState: migration.desiredState });
7141
7742
  }
7142
7743
  if (migration.desiredStatePending === true)
7143
- throw new VibeError32({ 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 } });
7744
+ 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 } });
7144
7745
  if (migration.desiredState !== undefined) {
7145
7746
  assertDesiredState({ value: migration.desiredState, migration: migration.name });
7146
7747
  if (migration.desiredState.dialect !== dialect)
7147
- throw new VibeError32({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch", migration: migration.name } });
7748
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch", migration: migration.name } });
7148
7749
  }
7149
7750
  assertNativeMigrationMetadata({ migration, sql: migration.sql, dialect });
7150
7751
  assertExecutable({ migration, execution: migration.execution, sql: migration.sql, dialect });
@@ -7152,20 +7753,25 @@ function assertMigrationMetadata(params) {
7152
7753
  async function applyMigrations(params) {
7153
7754
  const { executor, migrations } = params;
7154
7755
  const dialect = params.dialect ?? "postgres";
7756
+ const trustedReplay = isShadowReplayExecutor({ executor });
7155
7757
  if (params.history !== undefined && params.history !== "complete" && params.history !== "subset")
7156
- throw new VibeError32({ code: "VIBE_MIGRATION", message: "Unknown migration history validation mode", meta: { reason: "history-mode-invalid" } });
7758
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Unknown migration history validation mode", meta: { reason: "history-mode-invalid" } });
7157
7759
  if (params.history === "complete")
7158
7760
  assertDistinctMigrationNames({ migrations });
7159
7761
  for (const migration of migrations)
7160
7762
  assertMigrationMetadata({ migration, dialect });
7763
+ const externalDeclarations = await parseExternalObjects({ value: params.external ?? [], dialect });
7161
7764
  return withMigrationLock({ executor, dialect, run: async (executor) => {
7765
+ const external = externalDeclarations.length === 0 ? undefined : await inspectExternalObjects({ executor, dialect, external: externalDeclarations });
7162
7766
  let moduleRecords = await readModuleJournal({ executor, dialect });
7163
7767
  const recorded = new Map([...moduleRecords].map(([name, record]) => [name, record.row]));
7768
+ if (external !== undefined)
7769
+ assertExternalOwnershipClaims({ external, identities: [...migrations.flatMap((migration) => migration.moduleOrigin?.owned ?? []), ...[...moduleRecords.values()].flatMap((record) => record.entry?.migration.moduleOrigin?.owned ?? [])] });
7164
7770
  if (migrations.some((migration) => migration.moduleOrigin !== undefined) || [...moduleRecords.values()].some((record) => record.entry !== undefined)) {
7165
7771
  assertModuleUpgradeHistory({ migrations, records: moduleRecords });
7166
7772
  const namespace = await migrationNamespace({ executor, dialect });
7167
- if (migrations.some((migration) => migration.moduleOrigin !== undefined && moduleNamespace({ origin: migration.moduleOrigin }) !== namespace))
7168
- throw new VibeError32({ code: "VIBE_MIGRATION", message: "Module placement differs from the active database namespace", meta: { reason: "module-placement-mismatch" } });
7773
+ if (dialect !== "postgres" && migrations.some((migration) => migration.moduleOrigin !== undefined && moduleNamespace({ origin: migration.moduleOrigin }) !== namespace))
7774
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Module placement differs from the active database namespace", meta: { reason: "module-placement-mismatch" } });
7169
7775
  }
7170
7776
  if (params.history === "complete")
7171
7777
  assertCompleteHistory({ migrations, recorded, dialect });
@@ -7174,6 +7780,12 @@ async function applyMigrations(params) {
7174
7780
  const stored = recorded.get(migration.name);
7175
7781
  return stored === undefined || !isCompleteRecord({ dialect, migration, stored });
7176
7782
  });
7783
+ const adopted = pending.find((migration) => migration.moduleAdoption !== undefined);
7784
+ if (adopted !== undefined && !trustedReplay)
7785
+ 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 } });
7786
+ if (external !== undefined && !trustedReplay)
7787
+ for (const migration of pending)
7788
+ await assertExternalMigrationMutation({ executor, migration, dialect, namespace: await migrationNamespace({ executor, dialect }), external });
7177
7789
  validateMigrationReplay({ ...params, migrations: pending, dialect });
7178
7790
  await ensureMigrationsTable({ executor, dialect });
7179
7791
  if (migrations.some((migration) => migration.moduleOrigin !== undefined))
@@ -7186,7 +7798,7 @@ async function applyMigrations(params) {
7186
7798
  await assertLiveModuleOwnership({ executor, origin: migration.moduleOrigin, resume: stored !== undefined });
7187
7799
  if (stored !== undefined) {
7188
7800
  if (stored.statementIndex < 0) {
7189
- throw new VibeError32({
7801
+ throw new VibeError35({
7190
7802
  code: "VIBE_MIGRATION",
7191
7803
  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`,
7192
7804
  meta: { migration: migration.name, statementIndex: stored.statementIndex }
@@ -7208,6 +7820,7 @@ async function applyMigrations(params) {
7208
7820
  migration,
7209
7821
  start: stored.statementIndex,
7210
7822
  moduleOrigins: applicationModuleOrigins({ migration, records: moduleRecords }),
7823
+ external,
7211
7824
  ...params.probe === undefined ? {} : { probe: params.probe },
7212
7825
  insertRecord: false,
7213
7826
  ...stored.error === undefined ? {} : { storedError: stored.error }
@@ -7223,10 +7836,10 @@ async function applyMigrations(params) {
7223
7836
  executor,
7224
7837
  dialect,
7225
7838
  ...migration.execution === undefined ? {} : { execution: migration.execution },
7226
- run: () => runNonTransactional({ executor, dialect, migration, moduleOrigins: applicationModuleOrigins({ migration, records: moduleRecords }), start: 0, insertRecord: true, ...params.probe === undefined ? {} : { probe: params.probe } })
7839
+ run: () => runNonTransactional({ executor, dialect, migration, external, moduleOrigins: applicationModuleOrigins({ migration, records: moduleRecords }), start: 0, insertRecord: true, ...params.probe === undefined ? {} : { probe: params.probe } })
7227
7840
  });
7228
7841
  } else {
7229
- await runTransactional({ executor, dialect, migration, moduleOrigins: applicationModuleOrigins({ migration, records: moduleRecords }), ...params.probe === undefined ? {} : { probe: params.probe } });
7842
+ await runTransactional({ executor, dialect, migration, external, moduleOrigins: applicationModuleOrigins({ migration, records: moduleRecords }), ...params.probe === undefined ? {} : { probe: params.probe } });
7230
7843
  }
7231
7844
  if (migration.moduleOrigin !== undefined)
7232
7845
  moduleRecords = await readModuleJournal({ executor, dialect });
@@ -7234,10 +7847,11 @@ async function applyMigrations(params) {
7234
7847
  }
7235
7848
  const final = migrations.at(-1);
7236
7849
  if (final?.desiredState !== undefined) {
7850
+ const finalExternal = externalDeclarations.length === 0 ? undefined : await inspectExternalObjects({ executor, dialect, external: externalDeclarations });
7237
7851
  if (params.probe === "rollback")
7238
7852
  await executor({ text: "BEGIN" });
7239
7853
  try {
7240
- assertMigrationState({ report: await verifyMigrationTransition({ executor, dialect, from: final.desiredState.from, to: final.desiredState.to, removals: final.desiredState.unmanagedRemovals, ...params.probe === undefined ? {} : { probe: params.probe } }) });
7854
+ assertMigrationState({ report: await verifyMigrationTransition({ executor, dialect, from: final.desiredState.from, to: final.desiredState.to, external: finalExternal, removals: comparisonRemovals({ migration: final, external: finalExternal }), ...params.probe === undefined ? {} : { probe: params.probe } }) });
7241
7855
  } finally {
7242
7856
  if (params.probe === "rollback")
7243
7857
  await executor({ text: "ROLLBACK" });
@@ -7246,6 +7860,27 @@ async function applyMigrations(params) {
7246
7860
  return { applied, skipped };
7247
7861
  } });
7248
7862
  }
7863
+ function comparisonRemovals(params) {
7864
+ return params.migration.desiredState?.unmanagedRemovals?.filter((identity) => params.external === undefined || externalObjectOwner({ identity, external: params.external }) === undefined);
7865
+ }
7866
+ async function assertExternalMigrationMutation(params) {
7867
+ assertExternalOwnershipClaims({ external: params.external, identities: params.migration.desiredState?.unmanagedRemovals ?? [] });
7868
+ const state = params.migration.desiredState;
7869
+ if (state === undefined)
7870
+ return;
7871
+ const identities = [];
7872
+ const steps = diffSchemas({ from: state.from.schema, to: state.to.schema, dialect: params.dialect });
7873
+ for (const step of steps) {
7874
+ if (step.kind === "createNamespace")
7875
+ identities.push({ kind: "namespace", name: step.namespace });
7876
+ else if ("table" in step && typeof step.table === "string")
7877
+ identities.push({ kind: "table", name: { namespace: step.namespace ?? params.namespace, name: step.table } });
7878
+ else if ("enumName" in step)
7879
+ identities.push({ kind: "enum", name: { namespace: step.namespace ?? params.namespace, name: step.enumName } });
7880
+ }
7881
+ assertExternalOwnershipClaims({ external: params.external, identities });
7882
+ await refuseExternalDependents({ executor: params.executor, dialect: params.dialect, external: params.external, steps, namespace: params.namespace });
7883
+ }
7249
7884
  async function migrationStatus(params) {
7250
7885
  const { executor, migrations } = params;
7251
7886
  const dialect = params.dialect ?? "postgres";
@@ -7321,7 +7956,7 @@ async function repairMigration(params) {
7321
7956
  const { executor, name, dialect } = params;
7322
7957
  const moduleRecord = (await readModuleJournal({ executor, dialect })).get(name);
7323
7958
  if (moduleRecord?.entry !== undefined)
7324
- throw new VibeError32({ 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 } });
7959
+ 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 } });
7325
7960
  await ensureMigrationsTable({ executor, dialect });
7326
7961
  if (params.as === "applied") {
7327
7962
  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})`;
@@ -7343,7 +7978,7 @@ async function rollbackMigration(params) {
7343
7978
  const stored = assertRollbackRecord({ migration, stored: recorded.get(migration.name), dialect });
7344
7979
  const newest = [...recorded.keys()].at(-1);
7345
7980
  if (newest !== migration.name)
7346
- throw new VibeError32({ 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 } });
7981
+ 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 } });
7347
7982
  await validateRollbackRecovery({ executor, migration, downSql, stored, dialect });
7348
7983
  await executeRollback({ ...params, executor, dialect, downSql, stored });
7349
7984
  } });
@@ -7361,7 +7996,7 @@ async function rollbackMigrations(params) {
7361
7996
  const target = journal.findIndex((row) => row.name === params.to);
7362
7997
  const row = journal[target];
7363
7998
  if (row?.state !== "applied")
7364
- throw new VibeError32({ 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 } });
7999
+ 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 } });
7365
8000
  assertMigrationMetadata({ migration: byName.get(row.name), dialect });
7366
8001
  start = target + 1;
7367
8002
  }
@@ -7369,7 +8004,7 @@ async function rollbackMigrations(params) {
7369
8004
  for (const row of journal.slice(start).reverse()) {
7370
8005
  const migration = byName.get(row.name);
7371
8006
  if (migration === undefined)
7372
- throw new VibeError32({ code: "VIBE_MIGRATION", message: `Cannot roll back "${row.name}" without its migration file`, meta: { reason: "rollback-file-missing", migration: row.name } });
8007
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: `Cannot roll back "${row.name}" without its migration file`, meta: { reason: "rollback-file-missing", migration: row.name } });
7373
8008
  const stored = assertRollbackRecord({ migration, stored: recorded.get(row.name), dialect });
7374
8009
  const downSql = validateRollbackFile({ ...params, migration, dialect });
7375
8010
  plan.push({ migration, downSql, stored });
@@ -7386,20 +8021,23 @@ async function rollbackMigrations(params) {
7386
8021
  }
7387
8022
  function validateRollbackFile(params) {
7388
8023
  const { migration, dialect } = params;
8024
+ assertModuleAdoption({ migration });
8025
+ if (migration.moduleAdoption !== undefined)
8026
+ 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 } });
7389
8027
  if (migration.desiredStatePending === true)
7390
- throw new VibeError32({ code: "VIBE_MIGRATION", message: "Unfinalized desired state cannot be rolled back", meta: { reason: "desired-state-unfinalized", migration: migration.name } });
8028
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Unfinalized desired state cannot be rolled back", meta: { reason: "desired-state-unfinalized", migration: migration.name } });
7391
8029
  if (migration.moduleOrigin !== undefined) {
7392
8030
  assertMaterializedModuleMigration({ migration });
7393
8031
  assertModuleOwnership({ origin: migration.moduleOrigin, desiredState: migration.desiredState });
7394
8032
  }
7395
8033
  const downSql = migration.downSql;
7396
8034
  if (downSql === undefined)
7397
- throw new VibeError32({ 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 } });
8035
+ 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 } });
7398
8036
  assertProbeMode({ dialect, probe: params.probe });
7399
8037
  if (migration.desiredState !== undefined) {
7400
8038
  assertDesiredState({ value: migration.desiredState, migration: migration.name });
7401
8039
  if (migration.desiredState.dialect !== dialect)
7402
- throw new VibeError32({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch" } });
8040
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Migration desired-state dialect mismatch", meta: { reason: "desired-state-dialect-mismatch" } });
7403
8041
  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 } });
7404
8042
  }
7405
8043
  assertNativeMigrationMetadata({ migration, sql: downSql, dialect });
@@ -7412,10 +8050,10 @@ function validateRollbackFile(params) {
7412
8050
  function assertRollbackRecord(params) {
7413
8051
  const { migration, stored, dialect } = params;
7414
8052
  if (stored === undefined)
7415
- throw new VibeError32({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is not recorded as applied; nothing to roll back`, meta: { migration: migration.name } });
8053
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: `migration "${migration.name}" is not recorded as applied; nothing to roll back`, meta: { migration: migration.name } });
7416
8054
  assertChecksumMatches({ migration, stored: stored.checksum });
7417
8055
  if (stored.statementIndex >= 0 && (!isCompleteRecord({ dialect, migration, stored }) || stored.error !== undefined))
7418
- throw new VibeError32({ 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 } });
8056
+ 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 } });
7419
8057
  return stored;
7420
8058
  }
7421
8059
  async function validateRollbackRecovery(params) {
@@ -7424,7 +8062,7 @@ async function validateRollbackRecovery(params) {
7424
8062
  return;
7425
8063
  const start = -stored.statementIndex - 1;
7426
8064
  if (start > downSql.length)
7427
- throw new VibeError32({ code: "VIBE_MIGRATION", message: "Rollback progress exceeds its statement count", meta: { reason: "rollback-progress-invalid", migration: migration.name } });
8065
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: "Rollback progress exceeds its statement count", meta: { reason: "rollback-progress-invalid", migration: migration.name } });
7428
8066
  if (dialect === "sqlite" && usesSelfManagedTransactions({ sql: downSql }))
7429
8067
  assertSelfManagedResumeSafe({ sql: downSql, start, migration: migration.name, direction: "down" });
7430
8068
  if (!marksInflight({ dialect, execution: migration.downExecution }) || start >= downSql.length)
@@ -7459,9 +8097,9 @@ async function runTransactionalDown(params) {
7459
8097
  const limits = migration.downExecution;
7460
8098
  if (wrap && dialect === "postgres" && limits !== undefined) {
7461
8099
  if ((limits.lockTimeoutMs ?? 0) > 0)
7462
- await executor({ text: renderMigrationSessionSet({ dialect: getSqlDialect7({ dialect }), setting: "lock_timeout", milliseconds: limits.lockTimeoutMs, local: true }).text });
8100
+ await executor({ text: renderMigrationSessionSet({ dialect: getSqlDialect9({ dialect }), setting: "lock_timeout", milliseconds: limits.lockTimeoutMs, local: true }).text });
7463
8101
  if ((limits.statementTimeoutMs ?? 0) > 0)
7464
- await executor({ text: renderMigrationSessionSet({ dialect: getSqlDialect7({ dialect }), setting: "statement_timeout", milliseconds: limits.statementTimeoutMs, local: true }).text });
8102
+ await executor({ text: renderMigrationSessionSet({ dialect: getSqlDialect9({ dialect }), setting: "statement_timeout", milliseconds: limits.statementTimeoutMs, local: true }).text });
7465
8103
  }
7466
8104
  if (migration.desiredState !== undefined)
7467
8105
  assertMigrationState({ report: await verifyMigrationTransition({ executor, dialect, from: migration.desiredState.from, to: migration.desiredState.to, removals: migration.desiredState.unmanagedRemovals, ...params.probe === undefined ? {} : { probe: params.probe } }) });
@@ -7490,7 +8128,7 @@ async function runTransactionalDown(params) {
7490
8128
  } else {
7491
8129
  await cleanupSelfManagedFailure({ executor });
7492
8130
  }
7493
- throw new VibeError32({
8131
+ throw new VibeError35({
7494
8132
  code: "VIBE_MIGRATION",
7495
8133
  message: `rollback of "${migration.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
7496
8134
  meta: { migration: migration.name, ...timeoutDiagnostic({ error }) },
@@ -7573,7 +8211,7 @@ async function runNonTransactionalDown(params) {
7573
8211
  values: [marks ? `${INFLIGHT_PREFIX}${index}` : error instanceof Error ? error.message : String(error), migration.name]
7574
8212
  });
7575
8213
  } catch {}
7576
- throw new VibeError32({
8214
+ throw new VibeError35({
7577
8215
  code: "VIBE_MIGRATION",
7578
8216
  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."}`,
7579
8217
  meta: { migration: migration.name, statementIndex: index, ...marks ? { outcome: "unknown" } : {}, ...timeoutDiagnostic({ error }) },
@@ -7600,11 +8238,11 @@ async function runTransactional(params) {
7600
8238
  const limits = migration.execution;
7601
8239
  if (wrap && dialect === "postgres" && limits !== undefined) {
7602
8240
  if ((limits.lockTimeoutMs ?? 0) > 0)
7603
- await executor({ text: renderMigrationSessionSet({ dialect: getSqlDialect7({ dialect }), setting: "lock_timeout", milliseconds: limits.lockTimeoutMs, local: true }).text });
8241
+ await executor({ text: renderMigrationSessionSet({ dialect: getSqlDialect9({ dialect }), setting: "lock_timeout", milliseconds: limits.lockTimeoutMs, local: true }).text });
7604
8242
  if ((limits.statementTimeoutMs ?? 0) > 0)
7605
- await executor({ text: renderMigrationSessionSet({ dialect: getSqlDialect7({ dialect }), setting: "statement_timeout", milliseconds: limits.statementTimeoutMs, local: true }).text });
8243
+ await executor({ text: renderMigrationSessionSet({ dialect: getSqlDialect9({ dialect }), setting: "statement_timeout", milliseconds: limits.statementTimeoutMs, local: true }).text });
7606
8244
  }
7607
- await assertForwardStart({ executor, dialect, migration, ...params.probe === undefined ? {} : { probe: params.probe } });
8245
+ await assertForwardStart({ executor, dialect, migration, external: params.external, ...params.probe === undefined ? {} : { probe: params.probe } });
7608
8246
  await assertApplicationModuleState({ executor, origins: params.moduleOrigins, probe: params.probe });
7609
8247
  if (migration.rls !== undefined)
7610
8248
  await introspectPostgres({ executor, rlsSchema: migration.rls.from, inTransaction: true, requireInstalledRls: true });
@@ -7614,7 +8252,7 @@ async function runTransactional(params) {
7614
8252
  statementIndex += 1;
7615
8253
  }
7616
8254
  if (migration.desiredState !== undefined)
7617
- assertMigrationState({ report: await verifyMigrationTransition({ executor, from: migration.desiredState.from, to: migration.desiredState.to, removals: migration.desiredState.unmanagedRemovals, dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
8255
+ assertMigrationState({ report: await verifyMigrationTransition({ executor, from: migration.desiredState.from, to: migration.desiredState.to, external: params.external, removals: comparisonRemovals({ migration, external: params.external }), dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
7618
8256
  if (migration.rls !== undefined)
7619
8257
  await introspectPostgres({ executor, rlsSchema: migration.rls.to, inTransaction: true, requireInstalledRls: true });
7620
8258
  await assertApplicationModuleState({ executor, origins: params.moduleOrigins, probe: params.probe });
@@ -7635,7 +8273,7 @@ async function runTransactional(params) {
7635
8273
  } else {
7636
8274
  await cleanupSelfManagedFailure({ executor });
7637
8275
  }
7638
- throw new VibeError32({
8276
+ throw new VibeError35({
7639
8277
  code: "VIBE_MIGRATION",
7640
8278
  message: `migration "${migration.name}" failed: ${error instanceof Error ? error.message : String(error)}`,
7641
8279
  meta: { migration: migration.name },
@@ -7658,9 +8296,9 @@ async function runNonTransactional(params) {
7658
8296
  await assertApplicationModuleState({ executor, origins: params.moduleOrigins, probe: params.probe });
7659
8297
  if (insertRecord) {
7660
8298
  if (migration.desiredState !== undefined) {
7661
- await assertForwardStart({ executor, dialect, migration, ...params.probe === undefined ? {} : { probe: params.probe } });
8299
+ await assertForwardStart({ executor, dialect, migration, external: params.external, ...params.probe === undefined ? {} : { probe: params.probe } });
7662
8300
  if (migration.sql.length === 0)
7663
- assertMigrationState({ report: await verifyMigrationTransition({ executor, from: migration.desiredState.from, to: migration.desiredState.to, removals: migration.desiredState.unmanagedRemovals, dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
8301
+ assertMigrationState({ report: await verifyMigrationTransition({ executor, from: migration.desiredState.from, to: migration.desiredState.to, external: params.external, removals: comparisonRemovals({ migration, external: params.external }), dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
7664
8302
  }
7665
8303
  if (migration.moduleOrigin !== undefined)
7666
8304
  await insertModuleJournal({ executor, dialect, migration, index: 0, complete: false });
@@ -7683,7 +8321,7 @@ async function runNonTransactional(params) {
7683
8321
  const state = await establishOperationState({ executor, dialect, operation });
7684
8322
  if (state === "satisfied") {
7685
8323
  if (start + 1 === migration.sql.length && migration.desiredState !== undefined)
7686
- assertMigrationState({ report: await verifyMigrationTransition({ executor, dialect, from: migration.desiredState.from, to: migration.desiredState.to, removals: migration.desiredState.unmanagedRemovals, ...params.probe === undefined ? {} : { probe: params.probe } }) });
8324
+ assertMigrationState({ report: await verifyMigrationTransition({ executor, dialect, from: migration.desiredState.from, to: migration.desiredState.to, external: params.external, removals: comparisonRemovals({ migration, external: params.external }), ...params.probe === undefined ? {} : { probe: params.probe } }) });
7687
8325
  if (start + 1 === migration.sql.length)
7688
8326
  await assertApplicationModuleState({ executor, origins: params.moduleOrigins, probe: params.probe });
7689
8327
  await checkpoint(start + 1);
@@ -7713,7 +8351,7 @@ async function runNonTransactional(params) {
7713
8351
  }
7714
8352
  await executeStatement({ executor, statement: migration.sql[index], dialect });
7715
8353
  if (index === migration.sql.length - 1 && migration.desiredState !== undefined)
7716
- assertMigrationState({ report: await verifyMigrationTransition({ executor, from: migration.desiredState.from, to: migration.desiredState.to, removals: migration.desiredState.unmanagedRemovals, dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
8354
+ assertMigrationState({ report: await verifyMigrationTransition({ executor, from: migration.desiredState.from, to: migration.desiredState.to, external: params.external, removals: comparisonRemovals({ migration, external: params.external }), dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
7717
8355
  if (index === migration.sql.length - 1)
7718
8356
  await assertApplicationModuleState({ executor, origins: params.moduleOrigins, probe: params.probe });
7719
8357
  if (atomic) {
@@ -7731,7 +8369,7 @@ async function runNonTransactional(params) {
7731
8369
  } catch {}
7732
8370
  const sqlState = sqlStateOf(error);
7733
8371
  const timeout = sqlState === "55P03" ? "lock-timeout" : sqlState === "57014" ? "statement-timeout" : undefined;
7734
- throw new VibeError32({
8372
+ throw new VibeError35({
7735
8373
  code: "VIBE_MIGRATION",
7736
8374
  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."}`,
7737
8375
  meta: { migration: migration.name, statementIndex: index, ...marks ? { outcome: "unknown" } : {}, ...timeout === undefined ? {} : { reason: timeout, sqlState } },
@@ -7742,7 +8380,7 @@ async function runNonTransactional(params) {
7742
8380
  await checkpoint(index + 1);
7743
8381
  }
7744
8382
  if (migration.moduleOrigin !== undefined) {
7745
- assertMigrationState({ report: await verifyMigrationState({ executor, dialect, expected: migration.desiredState.to }) });
8383
+ assertMigrationState({ report: await verifyMigrationState({ executor, dialect, expected: migration.desiredState.to, external: params.external }) });
7746
8384
  await completeModuleJournal({ executor, dialect, migration });
7747
8385
  }
7748
8386
  }
@@ -7759,7 +8397,7 @@ async function acquireLock(params) {
7759
8397
  });
7760
8398
  const acquired = Number(rows[0]?.vibe_lock) === 1;
7761
8399
  if (!acquired) {
7762
- throw new VibeError32({
8400
+ throw new VibeError35({
7763
8401
  code: "VIBE_MIGRATION",
7764
8402
  message: `could not acquire the migration lock ('${MYSQL_LOCK_NAME}') within ${MYSQL_LOCK_TIMEOUT_SECONDS}s \u2014 another migration run is in progress`,
7765
8403
  meta: { lock: MYSQL_LOCK_NAME }
@@ -7794,7 +8432,7 @@ function uncertainStatement(params) {
7794
8432
  const subject = direction === "down" ? `rollback of "${migration}"` : `migration "${migration}"`;
7795
8433
  const position = `${direction === "down" ? "down statement" : "statement"} ${index + 1} of ${statements.length}`;
7796
8434
  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.";
7797
- return new VibeError32({
8435
+ return new VibeError35({
7798
8436
  code: "VIBE_MIGRATION",
7799
8437
  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)}`,
7800
8438
  meta: {
@@ -7812,7 +8450,7 @@ function assertExecutable(params) {
7812
8450
  if (execution === undefined || isDefaultExecution({ execution }))
7813
8451
  return;
7814
8452
  if (dialect !== "postgres") {
7815
- throw new VibeError32({
8453
+ throw new VibeError35({
7816
8454
  code: "VIBE_UNSUPPORTED_CAPABILITY",
7817
8455
  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`,
7818
8456
  meta: { reason: "nontransactional-unsupported", dialect, migration: migration.name, feature: "nontransactionalMigrations" }
@@ -7835,7 +8473,7 @@ async function withSessionLimits(params) {
7835
8473
  const statement = execution?.statementTimeoutMs ?? 0;
7836
8474
  if (dialect !== "postgres" || lock === 0 && statement === 0)
7837
8475
  return params.run();
7838
- const sqlDialect = getSqlDialect7({ dialect });
8476
+ const sqlDialect = getSqlDialect9({ dialect });
7839
8477
  const previous = (await executor({ text: renderMigrationSessionRead({ dialect: sqlDialect }).text, values: [] }))[0];
7840
8478
  if (lock > 0)
7841
8479
  await executor({ text: renderMigrationSessionSet({ dialect: sqlDialect, setting: "lock_timeout", milliseconds: lock }).text });
@@ -7888,7 +8526,7 @@ function assertChecksumMatches(params) {
7888
8526
  const actual = checksumOf({ migration });
7889
8527
  if (stored === actual)
7890
8528
  return;
7891
- throw new VibeError32({
8529
+ throw new VibeError35({
7892
8530
  code: "VIBE_MIGRATION",
7893
8531
  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)`,
7894
8532
  meta: { migration: migration.name, recorded: stored, current: actual }
@@ -7899,7 +8537,7 @@ function assertResumable(params) {
7899
8537
  const actual = checksumOf({ migration });
7900
8538
  if (stored.checksum === actual)
7901
8539
  return;
7902
- throw new VibeError32({
8540
+ throw new VibeError35({
7903
8541
  code: "VIBE_MIGRATION",
7904
8542
  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`,
7905
8543
  meta: {
@@ -7919,7 +8557,7 @@ function assertDistinctMigrationNames(params) {
7919
8557
  const names = new Set;
7920
8558
  for (const migration of params.migrations) {
7921
8559
  if (names.has(migration.name))
7922
- throw new VibeError32({ code: "VIBE_MIGRATION", message: `Duplicate migration file identity "${migration.name}"`, meta: { reason: "history-duplicate-file", migration: migration.name } });
8560
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: `Duplicate migration file identity "${migration.name}"`, meta: { reason: "history-duplicate-file", migration: migration.name } });
7923
8561
  names.add(migration.name);
7924
8562
  }
7925
8563
  }
@@ -7927,15 +8565,15 @@ function assertCompleteHistory(params) {
7927
8565
  const byName = new Map(params.migrations.map((migration) => [migration.name, migration]));
7928
8566
  for (const name of params.recorded.keys()) {
7929
8567
  if (!byName.has(name))
7930
- throw new VibeError32({ code: "VIBE_MIGRATION", message: `Recorded migration "${name}" has no file in the complete chain`, meta: { reason: "history-unknown-file", migration: name } });
8568
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: `Recorded migration "${name}" has no file in the complete chain`, meta: { reason: "history-unknown-file", migration: name } });
7931
8569
  }
7932
8570
  let index = 0;
7933
8571
  for (const [name, stored] of params.recorded) {
7934
8572
  const migration = params.migrations[index];
7935
8573
  if (migration?.name !== name)
7936
- throw new VibeError32({ 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 } });
8574
+ 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 } });
7937
8575
  if (stored.statementIndex >= 0 && !isCompleteRecord({ dialect: params.dialect, migration, stored }) && index !== params.recorded.size - 1)
7938
- throw new VibeError32({ code: "VIBE_MIGRATION", message: `Partial migration "${name}" precedes later recorded work`, meta: { reason: "history-partial-not-last", migration: name } });
8576
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: `Partial migration "${name}" precedes later recorded work`, meta: { reason: "history-partial-not-last", migration: name } });
7939
8577
  index++;
7940
8578
  }
7941
8579
  }
@@ -7944,16 +8582,16 @@ async function assertApplyHistory(params) {
7944
8582
  const byName = new Map(migrations.map((migration) => [migration.name, migration]));
7945
8583
  for (const [name, stored] of recorded) {
7946
8584
  if (stored.statementIndex < 0) {
7947
- throw new VibeError32({ 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 } });
8585
+ 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 } });
7948
8586
  }
7949
8587
  const migration = byName.get(name);
7950
8588
  if (migration === undefined) {
7951
8589
  if (stored.error !== undefined)
7952
- throw new VibeError32({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error but no supplied file`, meta: { reason: "history-unknown-dirty", migration: name } });
8590
+ 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 } });
7953
8591
  continue;
7954
8592
  }
7955
8593
  if (isCompleteRecord({ dialect, migration, stored }) && stored.error !== undefined)
7956
- throw new VibeError32({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error`, meta: { reason: "history-dirty", migration: name } });
8594
+ throw new VibeError35({ code: "VIBE_MIGRATION", message: `Migration "${name}" has an unresolved journal error`, meta: { reason: "history-dirty", migration: name } });
7957
8595
  if (isCompleteRecord({ dialect, migration, stored }))
7958
8596
  assertChecksumMatches({ migration, stored: stored.checksum });
7959
8597
  else
@@ -7978,10 +8616,10 @@ async function assertApplyHistory(params) {
7978
8616
  }
7979
8617
  }
7980
8618
  function q5(dialect, name) {
7981
- return getSqlDialect7({ dialect }).quoteIdent({ name });
8619
+ return getSqlDialect9({ dialect }).quoteIdent({ name });
7982
8620
  }
7983
8621
  function ph(dialect, index) {
7984
- return getSqlDialect7({ dialect }).placeholder({ index });
8622
+ return getSqlDialect9({ dialect }).placeholder({ index });
7985
8623
  }
7986
8624
 
7987
8625
  // packages/migrate/src/fs.ts
@@ -7989,7 +8627,7 @@ function absent(error) {
7989
8627
  return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
7990
8628
  }
7991
8629
  function failure(params) {
7992
- return new VibeError33({ code: "VIBE_MIGRATION", message: `Cannot read migration file ${params.path}`, meta: { reason: "migration-file-unreadable", path: params.path }, cause: params.cause });
8630
+ return new VibeError36({ code: "VIBE_MIGRATION", message: `Cannot read migration file ${params.path}`, meta: { reason: "migration-file-unreadable", path: params.path }, cause: params.cause });
7993
8631
  }
7994
8632
  async function optionalFile(params) {
7995
8633
  return readFile(params.path, "utf-8").catch((cause) => {
@@ -8003,13 +8641,13 @@ function rlsMetadata(params) {
8003
8641
  const metadata = JSON.parse(params.text);
8004
8642
  const strings = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
8005
8643
  if (metadata?.version !== 1 || typeof metadata.destructive !== "boolean" || metadata.rls === undefined || !strings(metadata.securityChanges) || !strings(metadata.downSecurityChanges) || metadata.sqlChecksum !== migrationChecksum({ sql: params.migration.sql }) || metadata.downChecksum !== migrationChecksum({ sql: params.migration.downSql ?? [] })) {
8006
- throw new VibeError33({ code: "VIBE_MIGRATION", message: "Invalid native RLS metadata" });
8644
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: "Invalid native RLS metadata" });
8007
8645
  }
8008
8646
  assertValidSchemaIR4({ schema: metadata.rls.from });
8009
8647
  assertValidSchemaIR4({ schema: metadata.rls.to });
8010
8648
  return { ...params.migration, rls: metadata.rls, destructive: metadata.destructive, securityChanges: metadata.securityChanges, downSecurityChanges: metadata.downSecurityChanges };
8011
8649
  }).catch((cause) => {
8012
- throw new VibeError33({ code: "VIBE_MIGRATION", message: `Invalid native RLS metadata for migration "${params.migration.name}"`, meta: { reason: "rls-migration-metadata-invalid", migration: params.migration.name }, cause });
8650
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Invalid native RLS metadata for migration "${params.migration.name}"`, meta: { reason: "rls-migration-metadata-invalid", migration: params.migration.name }, cause });
8013
8651
  });
8014
8652
  }
8015
8653
  async function loadMigrationFiles(params) {
@@ -8042,13 +8680,17 @@ async function loadMigrationFiles(params) {
8042
8680
  const desired = await optionalFile({ path: join(directory, DESIRED_STATE_METADATA.filename) });
8043
8681
  if (desired !== undefined) {
8044
8682
  const pending = await Promise.resolve().then(() => isUnfinalizedDesiredState({ text: desired })).catch((cause) => {
8045
- throw new VibeError33({ code: "VIBE_MIGRATION", message: `Malformed desired state for ${name}`, meta: { reason: "desired-state-invalid", migration: name }, cause });
8683
+ throw new VibeError36({ code: "VIBE_MIGRATION", message: `Malformed desired state for ${name}`, meta: { reason: "desired-state-invalid", migration: name }, cause });
8046
8684
  });
8047
8685
  migration = pending ? { ...migration, desiredStatePending: true } : { ...migration, desiredState: parseDesiredStateSidecar({ text: desired, migration: name }) };
8048
8686
  }
8049
8687
  const origin = await optionalFile({ path: join(directory, MODULE_ORIGIN_METADATA.filename) });
8050
8688
  if (origin !== undefined)
8051
8689
  migration = restoreModuleMigration({ migration, origin: parseModuleOrigin({ text: origin }) });
8690
+ const adoption = await optionalFile({ path: join(directory, MODULE_ADOPTION_METADATA.filename) });
8691
+ if (adoption !== undefined)
8692
+ migration = { ...migration, moduleAdoption: parseModuleAdoption({ text: adoption }) };
8693
+ assertModuleAdoption({ migration });
8052
8694
  migrations.push(migration);
8053
8695
  }
8054
8696
  return migrations;
@@ -8059,4 +8701,4 @@ export {
8059
8701
  splitSqlStatements
8060
8702
  };
8061
8703
 
8062
- //# debugId=BB17658EE109D81A64756E2164756E21
8704
+ //# debugId=91A1E2329350564C64756E2164756E21