@vibeorm/migrate 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-probe.d.ts +41 -0
- package/dist/artifact-probe.d.ts.map +1 -0
- package/dist/down.d.ts +21 -3
- package/dist/down.d.ts.map +1 -1
- package/dist/extensions.d.ts +52 -0
- package/dist/extensions.d.ts.map +1 -1
- package/dist/fs.js +46 -3
- package/dist/fs.js.map +4 -4
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +195 -82
- package/dist/index.js.map +10 -9
- package/dist/planner.d.ts +28 -0
- package/dist/planner.d.ts.map +1 -1
- package/dist/push.d.ts +11 -0
- package/dist/push.d.ts.map +1 -1
- package/dist/replay-plan.d.ts.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6798,6 +6798,7 @@ function collectExtensionArtifacts(params) {
|
|
|
6798
6798
|
const { schema, dialect, extensions } = params;
|
|
6799
6799
|
const setup = [];
|
|
6800
6800
|
const objects = [];
|
|
6801
|
+
const groups = [];
|
|
6801
6802
|
for (const extension of extensions) {
|
|
6802
6803
|
const artifactsHook = extension.migrate?.artifacts;
|
|
6803
6804
|
if (artifactsHook === undefined)
|
|
@@ -6805,6 +6806,7 @@ function collectExtensionArtifacts(params) {
|
|
|
6805
6806
|
const declarations = extensionDeclarations({ schema, name: extension.name });
|
|
6806
6807
|
if (Object.keys(declarations).length === 0 && extension.schema?.configOnly !== true)
|
|
6807
6808
|
continue;
|
|
6809
|
+
const contributed = [];
|
|
6808
6810
|
for (const artifact of artifactsHook({
|
|
6809
6811
|
schema,
|
|
6810
6812
|
dialect,
|
|
@@ -6812,10 +6814,19 @@ function collectExtensionArtifacts(params) {
|
|
|
6812
6814
|
options: extension.options,
|
|
6813
6815
|
declarations
|
|
6814
6816
|
})) {
|
|
6815
|
-
(artifact.owns.kind === "postgresExtension"
|
|
6817
|
+
if (artifact.owns.kind === "postgresExtension")
|
|
6818
|
+
setup.push(artifact);
|
|
6819
|
+
else {
|
|
6820
|
+
objects.push(artifact);
|
|
6821
|
+
contributed.push(artifact);
|
|
6822
|
+
}
|
|
6816
6823
|
}
|
|
6824
|
+
groups.push({ extension: extension.name, objects: contributed });
|
|
6817
6825
|
}
|
|
6818
|
-
return { setup, objects };
|
|
6826
|
+
return { setup, objects, groups };
|
|
6827
|
+
}
|
|
6828
|
+
function lastDeclaredSchema(params) {
|
|
6829
|
+
return [...params.migrations].reverse().find((migration) => migration.desiredState !== undefined)?.desiredState?.to.schema;
|
|
6819
6830
|
}
|
|
6820
6831
|
function ownedTableNames(params) {
|
|
6821
6832
|
const exact = new Set;
|
|
@@ -6917,6 +6928,36 @@ async function probeInstalledExtensionState(params) {
|
|
|
6917
6928
|
function artifactCreateSql(params) {
|
|
6918
6929
|
return params.artifacts.flatMap((artifact) => [...artifact.create]);
|
|
6919
6930
|
}
|
|
6931
|
+
function planChangedArtifacts(params) {
|
|
6932
|
+
const previousById = new Map((params.previous?.objects ?? []).map((artifact) => [artifact.id, artifact]));
|
|
6933
|
+
const probedIds = new Set((params.probed ?? []).map((artifact) => artifact.id));
|
|
6934
|
+
const missingIds = new Set(params.missing.map((artifact) => artifact.id));
|
|
6935
|
+
const rebuilt = new Set;
|
|
6936
|
+
for (const group of params.current.groups) {
|
|
6937
|
+
const changed = group.objects.filter((artifact) => {
|
|
6938
|
+
if (missingIds.has(artifact.id))
|
|
6939
|
+
return false;
|
|
6940
|
+
if (probedIds.has(artifact.id))
|
|
6941
|
+
return true;
|
|
6942
|
+
const before = previousById.get(artifact.id);
|
|
6943
|
+
return before !== undefined && JSON.stringify(before.owns) === JSON.stringify(artifact.owns) && JSON.stringify(before.create) !== JSON.stringify(artifact.create);
|
|
6944
|
+
});
|
|
6945
|
+
if (changed.length === 0)
|
|
6946
|
+
continue;
|
|
6947
|
+
const rebuiltColumnTables = new Set(changed.flatMap((artifact) => artifact.owns.kind === "column" ? [artifact.owns.table] : []));
|
|
6948
|
+
for (const artifact of group.objects) {
|
|
6949
|
+
if (missingIds.has(artifact.id))
|
|
6950
|
+
continue;
|
|
6951
|
+
const dependent = (artifact.owns.kind === "index" || artifact.owns.kind === "trigger") && rebuiltColumnTables.has(artifact.owns.table);
|
|
6952
|
+
if (changed.includes(artifact) || dependent)
|
|
6953
|
+
rebuilt.add(artifact.id);
|
|
6954
|
+
}
|
|
6955
|
+
}
|
|
6956
|
+
return params.current.objects.filter((artifact) => rebuilt.has(artifact.id));
|
|
6957
|
+
}
|
|
6958
|
+
function artifactDropSql(params) {
|
|
6959
|
+
return [...params.artifacts].reverse().flatMap((artifact) => [...artifact.drop]);
|
|
6960
|
+
}
|
|
6920
6961
|
// packages/migrate/src/runner.ts
|
|
6921
6962
|
function migrationRlsSecurityChanges(params) {
|
|
6922
6963
|
const source = params.migration.rls;
|
|
@@ -7067,6 +7108,7 @@ function generateMigration(params) {
|
|
|
7067
7108
|
extensions: params.extensions ?? []
|
|
7068
7109
|
});
|
|
7069
7110
|
const allArtifacts = [...artifacts.setup, ...artifacts.objects];
|
|
7111
|
+
const rebuilt = planChangedArtifacts({ previous: collectExtensionArtifacts({ schema: from, dialect, extensions: params.extensions ?? [] }), current: artifacts, missing: [] });
|
|
7070
7112
|
assertRetainedMigrationObjects({ from: from.migrationObjects ?? [], to: to.migrationObjects ?? [] });
|
|
7071
7113
|
const strippedFrom = preserveMigrationObjects({ from: stripExtensionArtifacts({ schema: from, artifacts: allArtifacts }), to, objects: [...from.migrationObjects ?? [], ...to.migrationObjects ?? []] });
|
|
7072
7114
|
const steps = diffSchemas({ from: strippedFrom, to, dialect, ...params.renames === undefined ? {} : { renames: params.renames } });
|
|
@@ -7077,6 +7119,7 @@ function generateMigration(params) {
|
|
|
7077
7119
|
artifacts: missing.filter((artifact) => artifact.owns.kind === "postgresExtension")
|
|
7078
7120
|
}),
|
|
7079
7121
|
...coreSql,
|
|
7122
|
+
...artifactDropSql({ artifacts: rebuilt }),
|
|
7080
7123
|
...artifactCreateSql({
|
|
7081
7124
|
artifacts: missing.filter((artifact) => artifact.owns.kind !== "postgresExtension")
|
|
7082
7125
|
}),
|
|
@@ -8434,14 +8477,47 @@ async function compareReplayMigrationObjects(params) {
|
|
|
8434
8477
|
return { ...params.schema, migrationObjects: objects.map((object) => ({ ...object, expected: definitions.get(sqlObjectKey11({ identity: object.identity })) })) };
|
|
8435
8478
|
}
|
|
8436
8479
|
// packages/migrate/src/replay-plan.ts
|
|
8437
|
-
import { VibeError as
|
|
8480
|
+
import { VibeError as VibeError40, sqlObjectKey as sqlObjectKey13 } from "@vibeorm/schema";
|
|
8438
8481
|
|
|
8439
8482
|
// packages/migrate/src/planner.ts
|
|
8440
|
-
import { VibeError as
|
|
8483
|
+
import { VibeError as VibeError39, sqlObjectKey as sqlObjectKey12 } from "@vibeorm/schema";
|
|
8441
8484
|
import { renderSqliteRebuildObjects, sqliteDialect as sqliteDialect4 } from "@vibeorm/sql";
|
|
8442
8485
|
|
|
8443
|
-
// packages/migrate/src/
|
|
8486
|
+
// packages/migrate/src/artifact-probe.ts
|
|
8444
8487
|
import { VibeError as VibeError37 } from "@vibeorm/schema";
|
|
8488
|
+
var SAVEPOINT = "vibeorm_artifact_probe";
|
|
8489
|
+
async function probeChangedArtifacts(params) {
|
|
8490
|
+
const { executor, namespace } = params;
|
|
8491
|
+
const changed = [];
|
|
8492
|
+
for (const artifact of params.artifacts) {
|
|
8493
|
+
if (artifact.owns.kind === "postgresExtension" || artifact.drop.length === 0)
|
|
8494
|
+
continue;
|
|
8495
|
+
const identity = legacyMigrationObjects({ artifacts: [artifact], dialect: "postgres", namespace })[0].identity;
|
|
8496
|
+
const before = await inspectSqlObject({ executor, dialect: "postgres", identity });
|
|
8497
|
+
if (before.state !== "present")
|
|
8498
|
+
continue;
|
|
8499
|
+
await executor({ text: `SAVEPOINT ${SAVEPOINT}` });
|
|
8500
|
+
let after;
|
|
8501
|
+
try {
|
|
8502
|
+
for (const statement of [...artifact.drop, ...artifact.create])
|
|
8503
|
+
await executor({ text: statement });
|
|
8504
|
+
after = await inspectSqlObject({ executor, dialect: "postgres", identity });
|
|
8505
|
+
} finally {
|
|
8506
|
+
await executor({ text: `ROLLBACK TO SAVEPOINT ${SAVEPOINT}` });
|
|
8507
|
+
await executor({ text: `RELEASE SAVEPOINT ${SAVEPOINT}` });
|
|
8508
|
+
}
|
|
8509
|
+
if (after.state !== "present")
|
|
8510
|
+
throw new VibeError37({ code: "VIBE_MIGRATION", message: `Extension artifact ${artifact.id} did not produce its object when probed; its create SQL cannot be verified against the replayed history`, meta: { reason: "artifact-probe-unavailable", id: artifact.id, identity } });
|
|
8511
|
+
if (canonicalMigrationJson({ value: before.definition.facts }) === canonicalMigrationJson({ value: after.definition.facts }))
|
|
8512
|
+
continue;
|
|
8513
|
+
const definition = before.definition.facts.definition;
|
|
8514
|
+
changed.push({ artifact, ...identity.kind === "index" && typeof definition === "string" ? { restore: [`${definition};`] } : {} });
|
|
8515
|
+
}
|
|
8516
|
+
return changed;
|
|
8517
|
+
}
|
|
8518
|
+
|
|
8519
|
+
// packages/migrate/src/index-predicates.ts
|
|
8520
|
+
import { VibeError as VibeError38 } from "@vibeorm/schema";
|
|
8445
8521
|
import { defaultIndexName as defaultIndexName2 } from "@vibeorm/schema";
|
|
8446
8522
|
function physicalIndexName(params) {
|
|
8447
8523
|
return params.index.name ?? defaultIndexName2({ table: params.table, columns: params.columns, unique: params.index.kind === "unique", dialect: "postgres" });
|
|
@@ -8482,7 +8558,7 @@ async function deparseIndexPredicate(params) {
|
|
|
8482
8558
|
const match = /^CHECK \((.*)\)$/s.exec(definition);
|
|
8483
8559
|
return match === null ? undefined : match[1];
|
|
8484
8560
|
} catch (error) {
|
|
8485
|
-
if (error instanceof
|
|
8561
|
+
if (error instanceof VibeError38 && error.code === "VIBE_MIGRATION")
|
|
8486
8562
|
throw error;
|
|
8487
8563
|
return;
|
|
8488
8564
|
}
|
|
@@ -8501,7 +8577,7 @@ async function planSchemaMigration(params) {
|
|
|
8501
8577
|
assertRetainedMigrationObjects({ from: params.previous?.schema.migrationObjects ?? [], to: objects });
|
|
8502
8578
|
for (const object of objects)
|
|
8503
8579
|
if (object.dialect !== dialect)
|
|
8504
|
-
throw new
|
|
8580
|
+
throw new VibeError39({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Migration object ${object.id} requires ${object.dialect}`, meta: { feature: "migrationObject", dialect } });
|
|
8505
8581
|
const physical = params.physical ?? (dialect === "postgres" ? await introspectPhysicalPostgres({ executor }) : dialect === "mysql" ? await introspectMysql({ executor }) : await introspectSqlite({ executor }));
|
|
8506
8582
|
if (params.previous !== undefined)
|
|
8507
8583
|
assertMigrationState({ report: await verifyMigrationState({ executor, expected: params.previous, dialect, ...params.probe === undefined ? {} : { probe: params.probe } }) });
|
|
@@ -8510,7 +8586,7 @@ async function planSchemaMigration(params) {
|
|
|
8510
8586
|
const identity = object.identity;
|
|
8511
8587
|
if (identity.kind !== "table" || physical.models.some((model) => (model.dbName ?? model.name) === identity.name.name) || normalizeSchema({ schema: physical, dialect }).joinTables.has(identity.name.name))
|
|
8512
8588
|
continue;
|
|
8513
|
-
throw new
|
|
8589
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Catalog table is not visible to schema introspection; automatic planning cannot prove the complete physical state", meta: { reason: "inventory-unavailable", identity: object.identity } });
|
|
8514
8590
|
}
|
|
8515
8591
|
const states = resolved.unavailable.map((identity) => {
|
|
8516
8592
|
const exists = identity.kind === "column" && [physical, params.previous?.schema].some((source) => source?.models.some((model) => (model.dbName ?? model.name) === identity.table.name && model.fields.some((field) => (field.dbName ?? field.name) === identity.column)));
|
|
@@ -8541,7 +8617,7 @@ async function planSchemaMigration(params) {
|
|
|
8541
8617
|
}
|
|
8542
8618
|
for (const object of legacyObjects) {
|
|
8543
8619
|
if (objects.some((entry) => sqlObjectKey12({ identity: entry.identity }) === sqlObjectKey12({ identity: object.identity })))
|
|
8544
|
-
throw new
|
|
8620
|
+
throw new VibeError39({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate combined and migration-only ownership", meta: { identity: object.identity } });
|
|
8545
8621
|
const expected = object.expected ?? params.previous?.definitions.find((entry) => sqlObjectKey12({ identity: entry.identity }) === sqlObjectKey12({ identity: object.identity }));
|
|
8546
8622
|
if (expected === undefined)
|
|
8547
8623
|
states.push({ identity: object.identity, ownership: "migration", state: "unavailable", reason: "legacy-definition-required", differences: [] });
|
|
@@ -8584,7 +8660,7 @@ async function planSchemaMigration(params) {
|
|
|
8584
8660
|
if (object.inspectionUnavailable !== undefined)
|
|
8585
8661
|
states.push({ identity: object.identity, ownership, state: "unavailable", reason: object.inspectionUnavailable, differences: [] });
|
|
8586
8662
|
if (object.inspectionUnavailable !== undefined && invalid.rebuilt.has("table" in object.identity ? object.identity.table.name : object.identity.name.name))
|
|
8587
|
-
throw new
|
|
8663
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "SQLite rebuild cannot discard an unrepresented constraint; declare its independent definition or use a reviewed migration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
|
|
8588
8664
|
if (ownership === "unknown")
|
|
8589
8665
|
states.push({ identity: object.identity, ownership, state: "unavailable", reason: "unmanaged", differences: [] });
|
|
8590
8666
|
let collateral = "table" in object.identity && object.identity.table.namespace === namespace && droppedTables.has(object.identity.table.name);
|
|
@@ -8603,7 +8679,7 @@ async function planSchemaMigration(params) {
|
|
|
8603
8679
|
for (const address of addresses) {
|
|
8604
8680
|
const value = JSON.parse(String(address));
|
|
8605
8681
|
if (!Array.isArray(value) || !Array.isArray(value[2]))
|
|
8606
|
-
throw new
|
|
8682
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Malformed catalog dependency address" });
|
|
8607
8683
|
const names = value[2];
|
|
8608
8684
|
if (names[0] !== namespace || typeof names[1] !== "string")
|
|
8609
8685
|
continue;
|
|
@@ -8613,10 +8689,10 @@ async function planSchemaMigration(params) {
|
|
|
8613
8689
|
}
|
|
8614
8690
|
}
|
|
8615
8691
|
if (dialect === "sqlite" && ownership === "unknown" && object.identity.kind === "generatedColumn" && invalid.rebuilt.has(object.identity.table.name))
|
|
8616
|
-
throw new
|
|
8692
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "SQLite generated-column preservation requires an independent ownership declaration", meta: { reason: "sqlite-preservation-unavailable", identity: object.identity } });
|
|
8617
8693
|
const removed = collateral || removals.some((identity) => sqlObjectKey12({ identity }) === key);
|
|
8618
8694
|
if (removed && ownership === "migration")
|
|
8619
|
-
throw new
|
|
8695
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Automatic DDL would remove a migration-owned object", meta: { reason: "migration-owned-removal", identity: object.identity } });
|
|
8620
8696
|
if (removed && ownership === "unknown" && object.backingConstraint === undefined)
|
|
8621
8697
|
unmanagedRemovals.push(object.identity);
|
|
8622
8698
|
}
|
|
@@ -8627,6 +8703,19 @@ async function planSchemaMigration(params) {
|
|
|
8627
8703
|
if (artifact.owns.kind === "trigger" && invalidated.has(artifact.owns.table))
|
|
8628
8704
|
triggers?.delete(artifact.owns.name);
|
|
8629
8705
|
const missing = planMissingArtifacts({ introspected: physical, artifacts, installed: { ...installed, ...triggers === undefined ? {} : { triggers } } });
|
|
8706
|
+
const previousDeclarations = params.previousDeclarations ?? params.previous?.schema;
|
|
8707
|
+
const previousArtifacts = previousDeclarations === undefined ? undefined : collectExtensionArtifacts({ schema: previousDeclarations, dialect, extensions: params.extensions ?? [] });
|
|
8708
|
+
const missingIds = new Set(missing.map((artifact) => artifact.id));
|
|
8709
|
+
const probed = dialect === "postgres" && params.probe === "rollback" && params.probeArtifacts === true ? await probeChangedArtifacts({ executor, namespace, artifacts: collected.objects.filter((artifact) => !missingIds.has(artifact.id)) }) : [];
|
|
8710
|
+
const rebuilt = params.rebuildArtifacts === true ? collected.objects.filter((artifact) => !missingIds.has(artifact.id)) : planChangedArtifacts({ ...previousArtifacts === undefined ? {} : { previous: previousArtifacts }, current: collected, missing, probed: probed.map((entry) => entry.artifact) });
|
|
8711
|
+
const probedById = new Map(probed.map((entry) => [entry.artifact.id, entry]));
|
|
8712
|
+
const rebuiltArtifacts = rebuilt.map((artifact) => {
|
|
8713
|
+
const probe = probedById.get(artifact.id);
|
|
8714
|
+
if (probe !== undefined)
|
|
8715
|
+
return probe;
|
|
8716
|
+
const declared = previousArtifacts?.objects.find((entry) => entry.id === artifact.id);
|
|
8717
|
+
return { artifact, restore: [...(declared ?? artifact).create] };
|
|
8718
|
+
});
|
|
8630
8719
|
const restore = new Map;
|
|
8631
8720
|
const preservedColumns = new Map;
|
|
8632
8721
|
const preservedDefaults = new Map;
|
|
@@ -8650,11 +8739,11 @@ async function planSchemaMigration(params) {
|
|
|
8650
8739
|
continue;
|
|
8651
8740
|
const live = await inspectSqlObject({ executor, dialect, identity });
|
|
8652
8741
|
if (live.state !== "present" || identity.kind !== "columnDefault" && typeof live.definition.facts.columnDefinition !== "string")
|
|
8653
|
-
throw new
|
|
8742
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Cannot read the verified SQLite column definition for preservation", meta: { reason: "sqlite-preservation-unavailable", identity } });
|
|
8654
8743
|
if (identity.kind === "columnDefault") {
|
|
8655
8744
|
const definition = live.definition.facts.definition;
|
|
8656
8745
|
if (definition !== null && typeof definition !== "string")
|
|
8657
|
-
throw new
|
|
8746
|
+
throw new VibeError39({ code: "VIBE_MIGRATION", message: "Cannot preserve SQLite default", meta: { identity } });
|
|
8658
8747
|
const defaults = preservedDefaults.get(identity.table.name) ?? new Map;
|
|
8659
8748
|
defaults.set(identity.column, definition);
|
|
8660
8749
|
preservedDefaults.set(identity.table.name, defaults);
|
|
@@ -8676,10 +8765,12 @@ async function planSchemaMigration(params) {
|
|
|
8676
8765
|
return { ...rest, ...typeof expression === "string" ? { default: { kind: "dbgenerated", sql: expression } } : {} };
|
|
8677
8766
|
}) })) };
|
|
8678
8767
|
const core = renderSteps({ steps, dialect, from: dialect === "sqlite" ? physical : from, to: dialect === "mysql" ? { ...target, migrationObjects: protectedObjects } : target, sqliteRestore: restore, sqliteColumns: preservedColumns });
|
|
8768
|
+
const planned = new Set([...missing, ...rebuilt].map((artifact) => artifact.id));
|
|
8679
8769
|
const sql = [
|
|
8680
8770
|
...artifactCreateSql({ artifacts: missing.filter((artifact) => artifact.owns.kind === "postgresExtension") }),
|
|
8681
8771
|
...core,
|
|
8682
|
-
...
|
|
8772
|
+
...artifactDropSql({ artifacts: rebuilt }),
|
|
8773
|
+
...artifactCreateSql({ artifacts: collected.objects.filter((artifact) => planned.has(artifact.id) && !(artifact.owns.kind === "trigger" && invalid.rebuilt.has(artifact.owns.table))) }),
|
|
8683
8774
|
...creates
|
|
8684
8775
|
];
|
|
8685
8776
|
if (sql.length === 0) {
|
|
@@ -8691,7 +8782,7 @@ async function planSchemaMigration(params) {
|
|
|
8691
8782
|
states.push(...report.objects);
|
|
8692
8783
|
}
|
|
8693
8784
|
const blocked = states.some((state) => state.reason === "dependency-inspection-unavailable" || state.ownership !== "unknown" && state.reason !== "legacy-definition-required" && (state.state === "unavailable" || state.state === "changed" || state.state === "missing" && sql.length === 0));
|
|
8694
|
-
return { physical, from, to: schema, namespace, steps, objects: states, unmanagedRemovals, blocked, sql: blocked ? [] : sql };
|
|
8785
|
+
return { physical, from, to: schema, namespace, steps, objects: states, unmanagedRemovals, blocked, sql: blocked ? [] : sql, rebuiltArtifacts };
|
|
8695
8786
|
}
|
|
8696
8787
|
|
|
8697
8788
|
// packages/migrate/src/replay-plan.ts
|
|
@@ -8718,7 +8809,8 @@ async function planReplayMigration(params) {
|
|
|
8718
8809
|
const probe = reference.dialect === "postgres" ? "rollback" : reference.dialect === "mysql" ? "temporary" : "none";
|
|
8719
8810
|
if (probe === "rollback")
|
|
8720
8811
|
await executor({ text: "BEGIN" });
|
|
8721
|
-
const
|
|
8812
|
+
const previousDeclarations = lastDeclaredSchema({ migrations: params.migrations });
|
|
8813
|
+
const plan = await Promise.resolve().then(() => planSchemaMigration({ executor, schema, physical: reference.state.schema, physicalInventory: reference.inventory, dialect: reference.dialect, namespace: reference.namespace, extensions: params.extensions, replaceChecks: params.replaceChecks, probe, probeArtifacts: probe === "rollback", ...previousDeclarations === undefined ? {} : { previousDeclarations } })).finally(async () => {
|
|
8722
8814
|
if (probe === "rollback")
|
|
8723
8815
|
await executor({ text: "ROLLBACK" });
|
|
8724
8816
|
});
|
|
@@ -8737,24 +8829,24 @@ async function planReplayMigration(params) {
|
|
|
8737
8829
|
const retainedObjects = reference.inventory.filter((object) => !declared.has(sqlObjectKey13({ identity: object.identity })) && !removals.has(sqlObjectKey13({ identity: object.identity })) && (object.identity.kind === "function" || object.identity.kind === "trigger" || historicalObjects.has(sqlObjectKey13({ identity: object.identity })))).map((object) => object.identity);
|
|
8738
8830
|
const removedRegistrations = [...historicalObjects].filter(([key]) => present.has(key) && !declared.has(key)).map(([, identity]) => identity);
|
|
8739
8831
|
if (removedRegistrations.length > 0)
|
|
8740
|
-
throw new
|
|
8832
|
+
throw new VibeError40({ code: "VIBE_MIGRATION", message: "Custom registrations removed from declarations still exist in replayed history. Restore the registration or supply reviewed SQL removing the exact named objects before generating again.", meta: { reason: "migration-object-removal-review-required", shadowReusable: true, objects: removedRegistrations } });
|
|
8741
8833
|
return { ...plan, requiredRemovals: [...removals.values()], retainedObjects, securityChanges: securityChangesOf({ steps: plan.steps }), replayRemovals: params.migrations.flatMap((migration) => migration.desiredState?.unmanagedRemovals ?? []) };
|
|
8742
8834
|
}
|
|
8743
8835
|
function assertReplayRemovals(params) {
|
|
8744
8836
|
const wanted = new Set([...params.plan.requiredRemovals, ...params.plan.replayRemovals].map((identity) => sqlObjectKey13({ identity })));
|
|
8745
8837
|
const keys = params.approved.map((identity) => sqlObjectKey13({ identity }));
|
|
8746
8838
|
if (new Set(keys).size !== keys.length)
|
|
8747
|
-
throw new
|
|
8839
|
+
throw new VibeError40({ code: "VIBE_MIGRATION", message: "Removal decisions contain duplicate identities", meta: { reason: "removal-decision-invalid", shadowReusable: true } });
|
|
8748
8840
|
const unused = params.approved.filter((identity) => !wanted.has(sqlObjectKey13({ identity })));
|
|
8749
8841
|
if (unused.length > 0)
|
|
8750
|
-
throw new
|
|
8842
|
+
throw new VibeError40({ code: "VIBE_MIGRATION", message: "Removal decisions do not identify proposed removals. Custom objects without a generated removal require a reviewed hand-authored SQL migration.", meta: { reason: "removal-decision-unused", shadowReusable: true, objects: unused } });
|
|
8751
8843
|
assertNamedRemovals({ removals: params.plan.requiredRemovals, approved: params.approved, acceptIntegrityLoss: params.acceptIntegrityLoss });
|
|
8752
8844
|
}
|
|
8753
8845
|
// packages/migrate/src/baseline.ts
|
|
8754
|
-
import { VibeError as
|
|
8846
|
+
import { VibeError as VibeError45, sqlObjectKey as sqlObjectKey16 } from "@vibeorm/schema";
|
|
8755
8847
|
|
|
8756
8848
|
// packages/migrate/src/reference-state.ts
|
|
8757
|
-
import { VibeError as
|
|
8849
|
+
import { VibeError as VibeError41, sqlObjectKey as sqlObjectKey14 } from "@vibeorm/schema";
|
|
8758
8850
|
async function captureMigrationReference(params) {
|
|
8759
8851
|
const namespace = await migrationNamespace({ executor: params.referenceExecutor, dialect: params.dialect });
|
|
8760
8852
|
const identities = [...schemaObjectIdentities({ schema: params.schema, dialect: params.dialect, namespace }), ...(params.schema.migrationObjects ?? []).map((object) => object.identity)];
|
|
@@ -8764,14 +8856,14 @@ async function captureMigrationReference(params) {
|
|
|
8764
8856
|
continue;
|
|
8765
8857
|
const result = await inspectSqlObject({ executor: params.referenceExecutor, dialect: params.dialect, identity });
|
|
8766
8858
|
if (result.state !== "present")
|
|
8767
|
-
throw new
|
|
8859
|
+
throw new VibeError41({ code: "VIBE_MIGRATION", message: "Independent reference object is missing or unavailable", meta: { reason: "reference-unavailable", identity, state: result.state } });
|
|
8768
8860
|
definitions.push(result.definition);
|
|
8769
8861
|
}
|
|
8770
8862
|
let schema = params.schema;
|
|
8771
8863
|
if (params.dialect === "postgres" && schema.models.some((model) => model.fields.some((field) => field.isUnsupported === true))) {
|
|
8772
8864
|
const resolved = await resolvePostgresTypeIdentities({ executor: params.referenceExecutor, schema, mode: "reference" });
|
|
8773
8865
|
if (resolved.unavailable.length > 0)
|
|
8774
|
-
throw new
|
|
8866
|
+
throw new VibeError41({ code: "VIBE_MIGRATION", message: "Resolve unsupported types in the independent reference before capture", meta: { reason: "type-reference-required", columns: resolved.unavailable } });
|
|
8775
8867
|
const physical = await introspectPhysicalPostgres({ executor: params.referenceExecutor });
|
|
8776
8868
|
for (const model of resolved.schema.models)
|
|
8777
8869
|
for (const field of model.fields) {
|
|
@@ -8779,7 +8871,7 @@ async function captureMigrationReference(params) {
|
|
|
8779
8871
|
continue;
|
|
8780
8872
|
const actual = physical.models.find((entry) => entry.name === (model.dbName ?? model.name))?.fields.find((entry) => entry.name === (field.dbName ?? field.name));
|
|
8781
8873
|
if (actual === undefined || columnTypeKey({ field }) !== columnTypeKey({ field: actual }))
|
|
8782
|
-
throw new
|
|
8874
|
+
throw new VibeError41({ code: "VIBE_MIGRATION", message: "Independent reference column does not match its declared PostgreSQL type", meta: { reason: "reference-type-mismatch", table: model.dbName ?? model.name, column: field.dbName ?? field.name } });
|
|
8783
8875
|
}
|
|
8784
8876
|
schema = resolved.schema;
|
|
8785
8877
|
}
|
|
@@ -8787,13 +8879,13 @@ async function captureMigrationReference(params) {
|
|
|
8787
8879
|
}
|
|
8788
8880
|
|
|
8789
8881
|
// packages/migrate/src/compile-desired-state.ts
|
|
8790
|
-
import { VibeError as
|
|
8882
|
+
import { VibeError as VibeError42 } from "@vibeorm/schema";
|
|
8791
8883
|
import { renderExclusionSetup as renderExclusionSetup2, getSqlDialect as getSqlDialect12, renderMigrationServerVersion as renderMigrationServerVersion2 } from "@vibeorm/sql";
|
|
8792
8884
|
async function compileDesiredState(params) {
|
|
8793
8885
|
if (params.dialect !== "postgres")
|
|
8794
|
-
throw new
|
|
8886
|
+
throw new VibeError42({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Rollback declaration compilation requires PostgreSQL", meta: { feature: "desiredStateCompilation", dialect: params.dialect } });
|
|
8795
8887
|
if (params.steps.some((step) => step.kind === "alterColumn" || step.kind === "addColumn" || step.kind === "addEnumValue"))
|
|
8796
|
-
throw new
|
|
8888
|
+
throw new VibeError42({ code: "VIBE_MIGRATION", message: "CHECK compilation after column or enum changes requires an independently built reference with the new type context", meta: { reason: "reference-required" } });
|
|
8797
8889
|
const definitions = (params.schema.migrationObjects ?? []).flatMap((object) => object.expected === undefined ? [] : [object.expected]);
|
|
8798
8890
|
await params.executor({ text: "SAVEPOINT vibeorm_desired_state" });
|
|
8799
8891
|
try {
|
|
@@ -8816,7 +8908,7 @@ async function compileDesiredState(params) {
|
|
|
8816
8908
|
const query = renderMigrationServerVersion2({ dialect: getSqlDialect12({ dialect: params.dialect }) });
|
|
8817
8909
|
const version = (await params.executor({ text: query.text, values: [] }))[0]?.version;
|
|
8818
8910
|
if (typeof version !== "string")
|
|
8819
|
-
throw new
|
|
8911
|
+
throw new VibeError42({ code: "VIBE_MIGRATION", message: "Cannot determine definition normalization version" });
|
|
8820
8912
|
return { ...createDesiredState({ schema: { ...params.schema, models }, dialect: params.dialect, definitions }), normalization: { format: "catalog-v1", dialect: params.dialect, serverVersion: version } };
|
|
8821
8913
|
} finally {
|
|
8822
8914
|
await params.executor({ text: "ROLLBACK TO SAVEPOINT vibeorm_desired_state" });
|
|
@@ -8825,7 +8917,7 @@ async function compileDesiredState(params) {
|
|
|
8825
8917
|
}
|
|
8826
8918
|
|
|
8827
8919
|
// packages/migrate/src/verify-history.ts
|
|
8828
|
-
import { VibeError as
|
|
8920
|
+
import { VibeError as VibeError44, sqlObjectKey as sqlObjectKey15 } from "@vibeorm/schema";
|
|
8829
8921
|
import { getSqlDialect as getSqlDialect13, renderReplayCoverageGaps as renderReplayCoverageGaps2 } from "@vibeorm/sql";
|
|
8830
8922
|
|
|
8831
8923
|
// packages/migrate/src/compare-sqlite-primary-key.ts
|
|
@@ -8898,7 +8990,7 @@ function compareSqlitePrimaryKeyRepresentations(params) {
|
|
|
8898
8990
|
}
|
|
8899
8991
|
|
|
8900
8992
|
// packages/migrate/src/compare-reference-rls.ts
|
|
8901
|
-
import { VibeError as
|
|
8993
|
+
import { VibeError as VibeError43 } from "@vibeorm/schema";
|
|
8902
8994
|
function securityFacts(params) {
|
|
8903
8995
|
return new Map(params.tables.filter((table) => table.enabled || table.force || table.policies.length > 0).map((table) => [table.table, {
|
|
8904
8996
|
table: table.table,
|
|
@@ -8909,7 +9001,7 @@ function securityFacts(params) {
|
|
|
8909
9001
|
}
|
|
8910
9002
|
async function comparePostgresReferenceRls(params) {
|
|
8911
9003
|
if (params.schema.rls !== undefined && params.referenceExecutor === undefined)
|
|
8912
|
-
throw new
|
|
9004
|
+
throw new VibeError43({ code: "VIBE_MIGRATION", message: "Native RLS verification requires the independently replayed reference connection; target policy probes are not permitted.", meta: { reason: "verification-rls-reference-required" } });
|
|
8913
9005
|
const expected = securityFacts({ tables: params.schema.rls === undefined ? [] : await readPostgresRls({ executor: params.referenceExecutor }) });
|
|
8914
9006
|
const actual = securityFacts({ tables: await readPostgresRls({ executor: params.executor }) });
|
|
8915
9007
|
const objects = [...new Set([...expected.keys(), ...actual.keys()])].sort().map((table) => {
|
|
@@ -8933,14 +9025,14 @@ function selectMigrationVerificationPrefix(params) {
|
|
|
8933
9025
|
for (const [index, file] of params.migrations.entries()) {
|
|
8934
9026
|
const previous = params.migrations[index - 1];
|
|
8935
9027
|
if (seen.has(file.name) || previous !== undefined && previous.name.localeCompare(file.name) > 0) {
|
|
8936
|
-
throw new
|
|
9028
|
+
throw new VibeError44({ code: "VIBE_MIGRATION", message: "Verification requires a complete local migration chain in unique name order", meta: { reason: "verification-history-order", migration: file.name } });
|
|
8937
9029
|
}
|
|
8938
9030
|
seen.add(file.name);
|
|
8939
9031
|
}
|
|
8940
9032
|
for (const [index, row] of params.recorded.entries()) {
|
|
8941
9033
|
const file = params.migrations[index];
|
|
8942
9034
|
if (row.state !== "applied" || row.statementIndex < 0 || row.checksum === "" || file?.name !== row.name || file.desiredStatePending === true || migrationChecksum(file) !== row.checksum) {
|
|
8943
|
-
throw new
|
|
9035
|
+
throw new VibeError44({ code: "VIBE_MIGRATION", message: "Verification requires an exact, complete, checksum-verified applied prefix; inspect migrate status and resolve the history discrepancy first", meta: { reason: "verification-history-unverified", migration: row.name, state: row.state } });
|
|
8944
9036
|
}
|
|
8945
9037
|
}
|
|
8946
9038
|
return params.migrations.slice(0, params.recorded.length);
|
|
@@ -8957,7 +9049,7 @@ async function verifyMigrationReference(params) {
|
|
|
8957
9049
|
const coverage = renderReplayCoverageGaps2({ dialect: getSqlDialect13({ dialect }), namespace });
|
|
8958
9050
|
const gaps = await executor({ text: coverage.text, values: [...coverage.values] });
|
|
8959
9051
|
if (gaps.length > 0)
|
|
8960
|
-
throw new
|
|
9052
|
+
throw new VibeError44({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Target contains objects whose full definitions are outside migration inspection coverage; verification is unavailable. No target changes were made.", meta: { feature: "targetInventoryCoverage", dialect, namespace, objects: gaps } });
|
|
8961
9053
|
const security = dialect === "postgres" ? await comparePostgresReferenceRls({ executor, referenceExecutor: params.referenceExecutor, schema: reference.state.schema }) : { inSync: true, objects: [] };
|
|
8962
9054
|
const inspected = await verifyMigrationState({ executor, dialect, expected: reference.state, probe: "none", batch: true });
|
|
8963
9055
|
const structural = dialect === "sqlite" ? compareSqlitePrimaryKeyRepresentations({ report: inspected }) : inspected;
|
|
@@ -9104,7 +9196,7 @@ async function mysqlComparisonFacts(params) {
|
|
|
9104
9196
|
return { ...facts, members };
|
|
9105
9197
|
}
|
|
9106
9198
|
function referenceContextError() {
|
|
9107
|
-
return new
|
|
9199
|
+
return new VibeError44({ code: "VIBE_MIGRATION", message: "Replay and target dialect/namespace differ; safe reference translation is unavailable. Use a supported same-namespace reference setup", meta: { reason: "verification-reference-context" } });
|
|
9108
9200
|
}
|
|
9109
9201
|
|
|
9110
9202
|
// packages/migrate/src/baseline.ts
|
|
@@ -9112,19 +9204,19 @@ function generateBaselineMigration(params) {
|
|
|
9112
9204
|
const objects = orderedMigrationObjects({ objects: params.schema.migrationObjects ?? [] });
|
|
9113
9205
|
for (const object of objects)
|
|
9114
9206
|
if (object.dialect !== params.dialect)
|
|
9115
|
-
throw new
|
|
9207
|
+
throw new VibeError45({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Baseline object ${object.id} requires ${object.dialect}`, meta: { identity: object.identity, dialect: params.dialect } });
|
|
9116
9208
|
const prerequisites = objects.filter((object) => object.identity.kind === "postgresExtension" || object.identity.kind === "enum" || object.identity.kind === "sequence");
|
|
9117
9209
|
const prerequisiteKeys = new Set(prerequisites.map((object) => sqlObjectKey16({ identity: object.identity })));
|
|
9118
9210
|
for (const object of prerequisites) {
|
|
9119
9211
|
const unresolved = object.dependsOn.filter((identity) => !prerequisiteKeys.has(sqlObjectKey16({ identity })));
|
|
9120
9212
|
if (unresolved.length > 0)
|
|
9121
|
-
throw new
|
|
9213
|
+
throw new VibeError45({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Baseline prerequisite depends on objects created later. Supply reviewed initial SQL with an explicit dependency order.", meta: { reason: "baseline-prerequisite-order", identity: object.identity, dependencies: unresolved } });
|
|
9122
9214
|
}
|
|
9123
9215
|
const schema = { ...params.schema, migrationObjects: objects.filter((object) => !prerequisiteKeys.has(sqlObjectKey16({ identity: object.identity }))) };
|
|
9124
9216
|
const generated = generateMigration({ from: { datasource: schema.datasource, models: [], enums: [], extensions: [] }, to: schema, name: params.name, dialect: params.dialect, extensions: params.extensions ?? [] });
|
|
9125
9217
|
const sql = splitSqlStatements({ sql: joinSqlStatements({ sql: [...prerequisites.flatMap((object) => object.create), ...generated.sql] }), dialect: params.dialect });
|
|
9126
9218
|
if (sql.length === 0)
|
|
9127
|
-
throw new
|
|
9219
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "A baseline must create declared database objects", meta: { reason: "baseline-empty" } });
|
|
9128
9220
|
return {
|
|
9129
9221
|
name: generated.name,
|
|
9130
9222
|
sql,
|
|
@@ -9153,17 +9245,17 @@ async function verifyBaselineDeclarations(params) {
|
|
|
9153
9245
|
async function verifyBaselineTarget(params) {
|
|
9154
9246
|
const { reference } = params;
|
|
9155
9247
|
if (reference.unavailable.length > 0 || reference.inventory.some((object) => object.inspectionUnavailable !== undefined))
|
|
9156
|
-
throw new
|
|
9248
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "Baseline reference contains objects that cannot be inspected. Supply supported reviewed declarations before adoption.", meta: { reason: "baseline-reference-unavailable", objects: reference.unavailable, inventory: reference.inventory.filter((object) => object.inspectionUnavailable !== undefined) } });
|
|
9157
9249
|
const comparison = await verifyMigrationReference({ executor: params.executor, dialect: reference.dialect, reference, referenceExecutor: params.referenceExecutor }).catch((cause) => {
|
|
9158
|
-
if (cause instanceof
|
|
9159
|
-
throw new
|
|
9250
|
+
if (cause instanceof VibeError45 && cause.meta.reason === "verification-rls-reference-required")
|
|
9251
|
+
throw new VibeError45({ code: cause.code, message: cause.message, meta: { reason: "baseline-rls-reference-required" }, cause });
|
|
9160
9252
|
throw cause;
|
|
9161
9253
|
});
|
|
9162
9254
|
const security = comparison.objects.filter((object) => object.reason === "native-rls-mismatch");
|
|
9163
9255
|
if (security.length > 0)
|
|
9164
|
-
throw new
|
|
9256
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "Target native RLS policies or flags differ from the independent baseline. Restore the reviewed security declarations before recording; no target policies were changed.", meta: { reason: "baseline-rls-mismatch", shadowReusable: true, expected: security.flatMap((object) => object.differences.flatMap((difference) => difference.expected === undefined ? [] : [difference.expected])), actual: security.flatMap((object) => object.differences.flatMap((difference) => difference.actual === undefined ? [] : [difference.actual])) } });
|
|
9165
9257
|
if (comparison.unavailable.length > 0)
|
|
9166
|
-
throw new
|
|
9258
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "Baseline comparison contains objects whose definitions cannot be verified in the target context; use a supported reference before adoption.", meta: { reason: "baseline-target-unavailable", shadowReusable: true, objects: comparison.unavailable } });
|
|
9167
9259
|
const report = { inSync: comparison.objects.every((object) => object.state === "matching"), objects: comparison.objects };
|
|
9168
9260
|
assertMigrationState({ report });
|
|
9169
9261
|
const unmanaged = comparison.unmanaged.map((object) => object.identity);
|
|
@@ -9171,14 +9263,14 @@ async function verifyBaselineTarget(params) {
|
|
|
9171
9263
|
const exclusions = params.excludeUnmanaged ?? [];
|
|
9172
9264
|
const excluded = new Set(exclusions.map((identity) => sqlObjectKey16({ identity })));
|
|
9173
9265
|
if (excluded.size !== exclusions.length || exclusions.some((identity) => !extras.has(sqlObjectKey16({ identity }))))
|
|
9174
|
-
throw new
|
|
9266
|
+
throw new VibeError45({ code: "VIBE_CONFIG", message: "Baseline exclusions must uniquely name actual unmanaged target objects, never declared objects", meta: { reason: "baseline-exclusion-invalid", shadowReusable: true, exclusions } });
|
|
9175
9267
|
const unreviewed = unmanaged.filter((identity) => !excluded.has(sqlObjectKey16({ identity })));
|
|
9176
9268
|
if (unreviewed.length > 0)
|
|
9177
|
-
throw new
|
|
9269
|
+
throw new VibeError45({ code: "VIBE_MIGRATION", message: "Target has unmanaged objects. Register their SQL or explicitly exclude each identity with --exclude-unmanaged; no target objects were removed.", meta: { reason: "baseline-unmanaged-objects", shadowReusable: true, objects: unreviewed } });
|
|
9178
9270
|
return { ...report, unmanaged };
|
|
9179
9271
|
}
|
|
9180
9272
|
// packages/migrate/src/baseline-record.ts
|
|
9181
|
-
import { VibeError as
|
|
9273
|
+
import { VibeError as VibeError46 } from "@vibeorm/schema";
|
|
9182
9274
|
import { getSqlDialect as getSqlDialect15, renderStatement } from "@vibeorm/sql";
|
|
9183
9275
|
|
|
9184
9276
|
// packages/migrate/src/history.ts
|
|
@@ -9194,20 +9286,20 @@ async function readMigrationHistory(params) {
|
|
|
9194
9286
|
|
|
9195
9287
|
// packages/migrate/src/baseline-record.ts
|
|
9196
9288
|
function transactionFailure(params) {
|
|
9197
|
-
if (params.cause instanceof
|
|
9289
|
+
if (params.cause instanceof VibeError46)
|
|
9198
9290
|
return params.cause;
|
|
9199
|
-
return new
|
|
9291
|
+
return new VibeError46({ code: "VIBE_MIGRATION", message: "SQLite baseline recording failed; inspect the journal and transaction outcome before retrying.", meta: { reason: "baseline-record-transaction-failed" }, cause: params.cause });
|
|
9200
9292
|
}
|
|
9201
9293
|
async function recordBaselineMigration(params) {
|
|
9202
9294
|
if (params.migration.moduleOrigin !== undefined)
|
|
9203
|
-
throw new
|
|
9295
|
+
throw new VibeError46({ code: "VIBE_MIGRATION", message: "Package module migrations must use the immutable module installation workflow, not baseline adoption", meta: { reason: "baseline-module-unsupported" } });
|
|
9204
9296
|
validateMigrationReplay({ dialect: params.dialect, migrations: [params.migration], acceptSecurityChanges: params.acceptSecurityChanges });
|
|
9205
9297
|
await withMigrationLock({ executor: params.executor, dialect: params.dialect, run: async (executor) => {
|
|
9206
9298
|
const record = async () => {
|
|
9207
9299
|
const assertEmpty = async () => {
|
|
9208
9300
|
const history = await readMigrationHistory({ executor, migrations: [params.migration], dialect: params.dialect });
|
|
9209
9301
|
if (history.recorded.length > 0)
|
|
9210
|
-
throw new
|
|
9302
|
+
throw new VibeError46({ code: "VIBE_MIGRATION", message: "Baseline recording requires an absent or empty migration journal. Use normal apply or explicit recovery for existing history.", meta: { reason: "baseline-journal-not-empty", shadowReusable: true, recorded: history.recorded.map((row) => ({ name: row.name, state: row.state })) } });
|
|
9211
9303
|
};
|
|
9212
9304
|
await assertEmpty();
|
|
9213
9305
|
await params.verify({ executor });
|
|
@@ -9232,14 +9324,14 @@ async function recordBaselineMigration(params) {
|
|
|
9232
9324
|
await executor({ text: "COMMIT" });
|
|
9233
9325
|
}).then(undefined, async (cause) => {
|
|
9234
9326
|
await executor({ text: "ROLLBACK" }).then(undefined, (rollback) => {
|
|
9235
|
-
throw new
|
|
9327
|
+
throw new VibeError46({ code: "VIBE_MIGRATION", message: "Baseline recording failed and its SQLite transaction could not be rolled back. Inspect the journal and connection state before retrying; the outcome is uncertain.", meta: { reason: "baseline-record-rollback-failed" }, cause: new AggregateError([cause, rollback], "Baseline recording and rollback failed") });
|
|
9236
9328
|
});
|
|
9237
9329
|
throw transactionFailure({ cause });
|
|
9238
9330
|
});
|
|
9239
9331
|
} });
|
|
9240
9332
|
}
|
|
9241
9333
|
// packages/migrate/src/down.ts
|
|
9242
|
-
import { VibeError as
|
|
9334
|
+
import { VibeError as VibeError47, sqlObjectKey as sqlObjectKey17 } from "@vibeorm/schema";
|
|
9243
9335
|
function buildRollbackTarget(params) {
|
|
9244
9336
|
const { from, to, dialect } = params;
|
|
9245
9337
|
const skipped = [];
|
|
@@ -9292,6 +9384,7 @@ function generateDownMigration(params) {
|
|
|
9292
9384
|
extensions: params.extensions ?? []
|
|
9293
9385
|
});
|
|
9294
9386
|
const allArtifacts = [...artifacts.setup, ...artifacts.objects];
|
|
9387
|
+
const previous = collectExtensionArtifacts({ schema: params.previousDeclarations ?? from, dialect, extensions: params.extensions ?? [] });
|
|
9295
9388
|
const owned = [...from.migrationObjects ?? [], ...to.migrationObjects ?? []];
|
|
9296
9389
|
const strippedFrom = preserveMigrationObjects({ from: stripExtensionArtifacts({ schema: from, artifacts: allArtifacts }), to: from, objects: owned });
|
|
9297
9390
|
const { target, skipped } = buildRollbackTarget({ from: strippedFrom, to, dialect });
|
|
@@ -9315,14 +9408,27 @@ function generateDownMigration(params) {
|
|
|
9315
9408
|
return added?.kind === "addColumn" ? { ...field, isOptional: added.column.isOptional } : field;
|
|
9316
9409
|
}) })) };
|
|
9317
9410
|
const coreSql = renderSteps({ steps, dialect, from: to, to: dialect === "sqlite" ? sqlitePreservedSchema({ from, to: restored, objects: from.migrationObjects ?? [] }) : restored });
|
|
9318
|
-
const
|
|
9319
|
-
const
|
|
9411
|
+
const declaredBefore = new Set([...previous.setup, ...previous.objects].map((artifact) => JSON.stringify([artifact.id, artifact.owns])));
|
|
9412
|
+
const created = params.physical === undefined ? allArtifacts.filter((artifact) => !declaredBefore.has(JSON.stringify([artifact.id, artifact.owns]))) : planMissingArtifacts({ introspected: params.physical, artifacts: allArtifacts, ...params.installed === undefined ? {} : { installed: params.installed } });
|
|
9413
|
+
const createdDropSql = [];
|
|
9320
9414
|
for (const artifact of [...created].reverse()) {
|
|
9321
9415
|
if (artifact.owns.kind === "postgresExtension") {
|
|
9322
9416
|
skipped.push(`extension "${artifact.owns.name}" stays installed \u2014 other objects may depend on it (drop it manually if intended)`);
|
|
9323
9417
|
continue;
|
|
9324
9418
|
}
|
|
9325
|
-
|
|
9419
|
+
createdDropSql.push(...artifact.drop);
|
|
9420
|
+
}
|
|
9421
|
+
const rebuilt = params.rebuilt ?? planChangedArtifacts({ previous, current: artifacts, missing: created }).map((artifact) => {
|
|
9422
|
+
const declared = previous.objects.find((entry) => entry.id === artifact.id);
|
|
9423
|
+
return { artifact, ...declared === undefined ? {} : { restore: artifactCreateSql({ artifacts: [declared] }) } };
|
|
9424
|
+
});
|
|
9425
|
+
const restoreSql = [];
|
|
9426
|
+
for (const entry of rebuilt) {
|
|
9427
|
+
if (entry.restore === undefined) {
|
|
9428
|
+
skipped.push(`rebuilt ${entry.artifact.id} cannot be restored automatically \u2014 its previous definition is not spelled by the catalog; write it into a reviewed down migration`);
|
|
9429
|
+
continue;
|
|
9430
|
+
}
|
|
9431
|
+
restoreSql.push(...entry.restore);
|
|
9326
9432
|
}
|
|
9327
9433
|
const previousKeys = new Set((from.migrationObjects ?? []).map((object) => sqlObjectKey17({ identity: object.identity })));
|
|
9328
9434
|
const ownedDropSql = [];
|
|
@@ -9334,24 +9440,26 @@ function generateDownMigration(params) {
|
|
|
9334
9440
|
continue;
|
|
9335
9441
|
}
|
|
9336
9442
|
if (object.drop.length === 0)
|
|
9337
|
-
throw new
|
|
9443
|
+
throw new VibeError47({ code: "VIBE_MIGRATION", message: `Migration object ${object.id} needs reviewed reverse SQL`, meta: { reason: "migration-object-reverse-required", id: object.id } });
|
|
9338
9444
|
ownedDropSql.push(...object.drop);
|
|
9339
9445
|
}
|
|
9340
9446
|
return {
|
|
9341
9447
|
name,
|
|
9342
9448
|
target: restored,
|
|
9343
|
-
sql: [...ownedDropSql, ...artifactDropSql, ...coreSql],
|
|
9449
|
+
sql: [...ownedDropSql, ...createdDropSql, ...artifactDropSql({ artifacts: rebuilt.map((entry) => entry.artifact) }), ...coreSql, ...restoreSql],
|
|
9344
9450
|
destructive: steps.some((step) => step.destructive),
|
|
9345
9451
|
...securityChangesOf({ steps }).length === 0 ? {} : { securityChanges: securityChangesOf({ steps }) },
|
|
9346
9452
|
skipped
|
|
9347
9453
|
};
|
|
9348
9454
|
}
|
|
9349
9455
|
// packages/migrate/src/push.ts
|
|
9350
|
-
import { VibeError as
|
|
9456
|
+
import { VibeError as VibeError48, assertValidSchemaIR as assertValidSchemaIR4, providerToDialect as providerToDialect4, sqlObjectKey as sqlObjectKey18, unsupportedCapability as unsupportedCapability9 } from "@vibeorm/schema";
|
|
9351
9457
|
import { DIALECT_CAPABILITIES as DIALECT_CAPABILITIES4 } from "@vibeorm/sql";
|
|
9352
9458
|
async function push(params) {
|
|
9353
9459
|
assertValidSchemaIR4({ schema: params.schema });
|
|
9354
9460
|
const dialect = providerToDialect4({ provider: params.schema.datasource.provider });
|
|
9461
|
+
if (params.rebuildArtifacts === true && dialect !== "postgres")
|
|
9462
|
+
throw unsupportedCapability9({ dialect, feature: "rebuilding extension artifacts on push (rebuildArtifacts)", hint: "drop the extension's objects by hand and push again; the transactional rebuild exists on PostgreSQL only" });
|
|
9355
9463
|
if (dialect !== "postgres")
|
|
9356
9464
|
return pushChanges({ ...params, dialect });
|
|
9357
9465
|
await params.executor({ text: "BEGIN" });
|
|
@@ -9379,6 +9487,7 @@ async function pushChanges(params) {
|
|
|
9379
9487
|
...params.previousState === undefined ? {} : { previous: params.previousState },
|
|
9380
9488
|
...params.extensions === undefined ? {} : { extensions: params.extensions },
|
|
9381
9489
|
...params.probe === undefined ? {} : { probe: params.probe },
|
|
9490
|
+
...params.rebuildArtifacts === undefined ? {} : { rebuildArtifacts: params.rebuildArtifacts },
|
|
9382
9491
|
...params.replaceChecks === undefined ? {} : { replaceChecks: params.replaceChecks }
|
|
9383
9492
|
});
|
|
9384
9493
|
const { steps, sql } = plan;
|
|
@@ -9386,11 +9495,11 @@ async function pushChanges(params) {
|
|
|
9386
9495
|
if (params.dryRun === true)
|
|
9387
9496
|
return { steps, applied: [], planned: sql, securityChanges, objects: plan.objects, blocked: plan.blocked, unmanagedRemovals: plan.unmanagedRemovals };
|
|
9388
9497
|
if (plan.blocked)
|
|
9389
|
-
throw new
|
|
9498
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: "Schema push is blocked by unverified object definitions", meta: { reason: "migration-state-unverified", objects: plan.objects } });
|
|
9390
9499
|
assertSecurityAcknowledgement({ securityChanges, ...params.acceptSecurityChanges === undefined ? {} : { acceptSecurityChanges: params.acceptSecurityChanges } });
|
|
9391
9500
|
const destructive = steps.filter((step) => step.destructive);
|
|
9392
9501
|
if (destructive.length > 0 && params.acceptDataLoss !== true)
|
|
9393
|
-
throw new
|
|
9502
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: `push refused: ${destructive.length} destructive step(s) would lose data; pass acceptDataLoss: true: ${destructive.map((step) => describeStep({ step })).join("; ")}`, meta: { destructive: destructive.map((step) => describeStep({ step })) } });
|
|
9394
9503
|
assertRemovalAuthorization({
|
|
9395
9504
|
steps,
|
|
9396
9505
|
dialect,
|
|
@@ -9402,7 +9511,7 @@ async function pushChanges(params) {
|
|
|
9402
9511
|
const approved = new Set((params.removeUnmanaged ?? []).map((identity) => sqlObjectKey18({ identity })));
|
|
9403
9512
|
const unapproved = plan.unmanagedRemovals.filter((identity) => !approved.has(sqlObjectKey18({ identity })));
|
|
9404
9513
|
if (unapproved.length > 0)
|
|
9405
|
-
throw new
|
|
9514
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: "DDL would remove unmanaged dependent objects; approve their exact identities", meta: { reason: "unmanaged-removal-required", objects: unapproved } });
|
|
9406
9515
|
const wrap = dialect !== "postgres" && DIALECT_CAPABILITIES4[dialect].transactionalDdl && !usesSelfManagedTransactions({ sql });
|
|
9407
9516
|
if (wrap)
|
|
9408
9517
|
await executor({ text: "BEGIN" });
|
|
@@ -9420,7 +9529,7 @@ async function pushChanges(params) {
|
|
|
9420
9529
|
} catch {}
|
|
9421
9530
|
} else if (dialect === "sqlite")
|
|
9422
9531
|
await cleanupSelfManagedFailure({ executor });
|
|
9423
|
-
throw new
|
|
9532
|
+
throw new VibeError48({ code: "VIBE_MIGRATION", message: `push failed while applying schema changes: ${error instanceof Error ? error.message : String(error)}`, meta: { statements: sql }, cause: error });
|
|
9424
9533
|
}
|
|
9425
9534
|
return { steps, applied: sql, securityChanges, objects: plan.objects, desiredState: createDesiredState({ schema: plan.to, dialect }) };
|
|
9426
9535
|
}
|
|
@@ -9435,23 +9544,23 @@ async function pull(params) {
|
|
|
9435
9544
|
}
|
|
9436
9545
|
}
|
|
9437
9546
|
// packages/migrate/src/module-export.ts
|
|
9438
|
-
import { VibeError as
|
|
9547
|
+
import { VibeError as VibeError50 } from "@vibeorm/schema";
|
|
9439
9548
|
|
|
9440
9549
|
// packages/migrate/src/module-bundle.ts
|
|
9441
|
-
import { VibeError as
|
|
9550
|
+
import { VibeError as VibeError49 } from "@vibeorm/schema";
|
|
9442
9551
|
function defineModuleMigrations(params) {
|
|
9443
9552
|
if (!Array.isArray(params.steps) || params.steps.length === 0)
|
|
9444
|
-
throw new
|
|
9553
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "A module upgrade bundle must contain its initial revision", meta: { reason: "module-chain-invalid", moduleId: params.moduleId } });
|
|
9445
9554
|
const migrations = [];
|
|
9446
9555
|
const revisions = new Set;
|
|
9447
9556
|
let previous = null;
|
|
9448
9557
|
for (const step of params.steps) {
|
|
9449
9558
|
if (step.previousRevision !== previous || revisions.has(step.revision))
|
|
9450
|
-
throw new
|
|
9559
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: `Module ${params.moduleId} has a duplicate revision or broken predecessor chain`, meta: { reason: "module-chain-invalid", moduleId: params.moduleId, revision: step.revision } });
|
|
9451
9560
|
const origin = createModuleOrigin({ moduleId: params.moduleId, dialect: params.dialect, revision: step.revision, previousRevision: step.previousRevision, contracts: step.contracts, owned: step.owned, source: step.migration });
|
|
9452
9561
|
const predecessor = migrations.at(-1)?.moduleOrigin;
|
|
9453
9562
|
if (predecessor !== undefined && canonicalMigrationJson({ value: predecessor.source.desiredState.to }) !== canonicalMigrationJson({ value: origin.source.desiredState.from }))
|
|
9454
|
-
throw new
|
|
9563
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "Module source state differs from its predecessor", meta: { reason: "module-predecessor-state", moduleId: params.moduleId } });
|
|
9455
9564
|
validateMigrationReplay({ migrations: [origin.source], dialect: params.dialect, acceptSecurityChanges: true, acceptIntegrityLoss: true, removeUnmanaged: origin.source.desiredState?.unmanagedRemovals ?? [] });
|
|
9456
9565
|
migrations.push(Object.freeze({ ...origin.source, moduleOrigin: origin }));
|
|
9457
9566
|
revisions.add(step.revision);
|
|
@@ -9461,13 +9570,13 @@ function defineModuleMigrations(params) {
|
|
|
9461
9570
|
}
|
|
9462
9571
|
function moduleMigrationFiles(params) {
|
|
9463
9572
|
if (params.bundle === null || typeof params.bundle !== "object" || !Array.isArray(params.bundle.migrations))
|
|
9464
|
-
throw new
|
|
9573
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "Module bundle must contain an immutable migration chain", meta: { reason: "module-chain-invalid" } });
|
|
9465
9574
|
if (!/^[A-Za-z0-9_-]+$/.test(params.prefix))
|
|
9466
|
-
throw new
|
|
9575
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "Module migration prefix must be a safe local directory name", meta: { reason: "module-prefix-invalid" } });
|
|
9467
9576
|
const bundle = defineModuleMigrations({ moduleId: params.bundle.moduleId, dialect: params.bundle.dialect, steps: params.bundle.migrations.map((migration) => {
|
|
9468
9577
|
const origin = migration?.moduleOrigin;
|
|
9469
9578
|
if (origin === undefined || origin.moduleId !== params.bundle.moduleId || origin.dialect !== params.bundle.dialect)
|
|
9470
|
-
throw new
|
|
9579
|
+
throw new VibeError49({ code: "VIBE_MIGRATION", message: "Module bundle lacks matching immutable origin metadata", meta: { reason: "module-origin-invalid" } });
|
|
9471
9580
|
assertModuleOrigin({ value: origin });
|
|
9472
9581
|
restoreModuleMigration({ migration, origin });
|
|
9473
9582
|
return { revision: origin.revision, previousRevision: origin.previousRevision, contracts: origin.contracts, owned: origin.owned, migration: origin.source };
|
|
@@ -9480,7 +9589,7 @@ function emitModuleMigrationExport(params) {
|
|
|
9480
9589
|
moduleMigrationFiles({ bundle: params.bundle, prefix: "export" });
|
|
9481
9590
|
const origin = params.bundle.migrations.at(-1).moduleOrigin;
|
|
9482
9591
|
if (!origin.contracts.includes(params.contract))
|
|
9483
|
-
throw new
|
|
9592
|
+
throw new VibeError50({ code: "VIBE_CONFIG", message: "The package runtime contract is not supported by its migration head", meta: { reason: "module-contract-mismatch", moduleId: origin.moduleId } });
|
|
9484
9593
|
const expectation = { moduleId: origin.moduleId, revision: origin.revision, artifactChecksum: origin.artifactChecksum, contract: params.contract };
|
|
9485
9594
|
return [
|
|
9486
9595
|
{ name: "migrations.js", content: `// Generated immutable migration data; no driver, filesystem or runner import.
|
|
@@ -9497,9 +9606,9 @@ export const moduleReadiness = Object.freeze(${JSON.stringify(expectation)});
|
|
|
9497
9606
|
];
|
|
9498
9607
|
}
|
|
9499
9608
|
// packages/migrate/src/module-pending.ts
|
|
9500
|
-
import { VibeError as
|
|
9609
|
+
import { VibeError as VibeError51, sqlObjectKey as sqlObjectKey19 } from "@vibeorm/schema";
|
|
9501
9610
|
function refusal3(params) {
|
|
9502
|
-
return new
|
|
9611
|
+
return new VibeError51({ code: "VIBE_MIGRATION", message: "Module registration or immutable upgrade history conflicts with the application's declarations; review the registration and deployment sequence", meta: params });
|
|
9503
9612
|
}
|
|
9504
9613
|
function compose(params) {
|
|
9505
9614
|
const { current, from, to } = params;
|
|
@@ -9607,16 +9716,16 @@ function pendingModuleMigrations(params) {
|
|
|
9607
9716
|
return Object.freeze(result);
|
|
9608
9717
|
}
|
|
9609
9718
|
// packages/migrate/src/module-readiness.ts
|
|
9610
|
-
import { VibeError as
|
|
9719
|
+
import { VibeError as VibeError52 } from "@vibeorm/schema";
|
|
9611
9720
|
async function inspectModuleReadiness(params) {
|
|
9612
9721
|
const ids = new Set;
|
|
9613
9722
|
for (const expected of params.modules) {
|
|
9614
9723
|
if (ids.has(expected.moduleId) || !expected.moduleId || !expected.revision || !expected.contract || !/^[a-f0-9]{64}$/.test(expected.artifactChecksum))
|
|
9615
|
-
throw new
|
|
9724
|
+
throw new VibeError52({ code: "VIBE_CONFIG", message: "Module readiness needs a unique module id, revision, contract and immutable artifact checksum", meta: { reason: "module-readiness-expectation", moduleId: expected.moduleId } });
|
|
9616
9725
|
ids.add(expected.moduleId);
|
|
9617
9726
|
}
|
|
9618
9727
|
if (params.compatibility !== undefined && params.compatibility !== "exact" && params.compatibility !== "contract")
|
|
9619
|
-
throw new
|
|
9728
|
+
throw new VibeError52({ code: "VIBE_CONFIG", message: "Unknown module compatibility mode", meta: { reason: "module-readiness-expectation" } });
|
|
9620
9729
|
const records = await readModuleJournal(params);
|
|
9621
9730
|
const heads = moduleHistoryHeads({ records });
|
|
9622
9731
|
const unresolved = [...records.values()].some(({ row, entry }) => row.error !== undefined || row.statementIndex < 0 || entry !== undefined && (!entry.complete || row.statementIndex !== entry.migration.sql.length));
|
|
@@ -9650,7 +9759,7 @@ async function inspectModuleReadiness(params) {
|
|
|
9650
9759
|
return { ready: !unresolved && modules.every((module) => (module.revision === "matching" || module.revision === "compatible") && module.structural.status !== "drifted"), modules };
|
|
9651
9760
|
}
|
|
9652
9761
|
// packages/migrate/src/adoption.ts
|
|
9653
|
-
import { VibeError as
|
|
9762
|
+
import { VibeError as VibeError53 } from "@vibeorm/schema";
|
|
9654
9763
|
async function adoptMigration(params) {
|
|
9655
9764
|
const report = await verifyMigrationState({ executor: params.executor, expected: params.reference, dialect: params.dialect });
|
|
9656
9765
|
assertMigrationState({ report });
|
|
@@ -9666,11 +9775,11 @@ function deployedDesiredState(params) {
|
|
|
9666
9775
|
for (const [index, row] of params.recorded.entries()) {
|
|
9667
9776
|
const file = params.migrations[index];
|
|
9668
9777
|
if (row.state !== "applied" || row.checksum === "" || file?.name !== row.name || migrationChecksum(file) !== row.checksum)
|
|
9669
|
-
throw new
|
|
9778
|
+
throw new VibeError53({ code: "VIBE_MIGRATION", message: "Desired state requires complete, checksum-verified migration history", meta: { reason: "desired-state-history-unverified", migration: row.name, state: row.state } });
|
|
9670
9779
|
}
|
|
9671
9780
|
const applied = new Set(params.recorded.map((row) => row.name));
|
|
9672
9781
|
if (params.migrations.some((migration) => applied.has(migration.name) && migration.desiredStatePending === true))
|
|
9673
|
-
throw new
|
|
9782
|
+
throw new VibeError53({ code: "VIBE_MIGRATION", message: "Applied unfinalized metadata cannot establish desired state", meta: { reason: "desired-state-history-unverified" } });
|
|
9674
9783
|
const last = [...params.migrations].reverse().find((migration) => applied.has(migration.name));
|
|
9675
9784
|
return last?.desiredState?.to;
|
|
9676
9785
|
}
|
|
@@ -9684,6 +9793,7 @@ export {
|
|
|
9684
9793
|
adoptMigration,
|
|
9685
9794
|
applyMigrations,
|
|
9686
9795
|
artifactCreateSql,
|
|
9796
|
+
artifactDropSql,
|
|
9687
9797
|
assertMigrationState,
|
|
9688
9798
|
assertProbeMode,
|
|
9689
9799
|
assertRemovalAuthorization,
|
|
@@ -9720,6 +9830,7 @@ export {
|
|
|
9720
9830
|
introspectPostgres,
|
|
9721
9831
|
introspectSqlite,
|
|
9722
9832
|
isDefaultExecution,
|
|
9833
|
+
lastDeclaredSchema,
|
|
9723
9834
|
legacyMigrationObjects,
|
|
9724
9835
|
migrationChecksum,
|
|
9725
9836
|
migrationNamespace,
|
|
@@ -9731,11 +9842,13 @@ export {
|
|
|
9731
9842
|
parseExecutionSidecar,
|
|
9732
9843
|
parseModuleOrigin,
|
|
9733
9844
|
pendingModuleMigrations,
|
|
9845
|
+
planChangedArtifacts,
|
|
9734
9846
|
planMissingArtifacts,
|
|
9735
9847
|
planRenames,
|
|
9736
9848
|
planReplayMigration,
|
|
9737
9849
|
planSchemaMigration,
|
|
9738
9850
|
prepareCheckDiff,
|
|
9851
|
+
probeChangedArtifacts,
|
|
9739
9852
|
probeInstalledExtensionState,
|
|
9740
9853
|
pull,
|
|
9741
9854
|
push,
|
|
@@ -9762,4 +9875,4 @@ export {
|
|
|
9762
9875
|
withMigrationReference
|
|
9763
9876
|
};
|
|
9764
9877
|
|
|
9765
|
-
//# debugId=
|
|
9878
|
+
//# debugId=9F525A8FE96388CC64756E2164756E21
|