@softure-ai/db 0.1.5 → 0.1.7
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/CHANGELOG.md +25 -0
- package/README.md +261 -22
- package/dist/cli/run.d.ts +8 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +71 -21
- package/dist/cli/run.js.map +1 -1
- package/dist/client.d.ts +39 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +64 -8
- package/dist/client.js.map +1 -1
- package/dist/configured.d.ts +31 -0
- package/dist/configured.d.ts.map +1 -0
- package/dist/configured.js +74 -0
- package/dist/configured.js.map +1 -0
- package/dist/driver-errors.d.ts +21 -0
- package/dist/driver-errors.d.ts.map +1 -0
- package/dist/driver-errors.js +25 -0
- package/dist/driver-errors.js.map +1 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/migrations/adopt.d.ts +8 -1
- package/dist/migrations/adopt.d.ts.map +1 -1
- package/dist/migrations/adopt.js +53 -87
- package/dist/migrations/adopt.js.map +1 -1
- package/dist/migrations/apply.d.ts +35 -0
- package/dist/migrations/apply.d.ts.map +1 -0
- package/dist/migrations/apply.js +115 -0
- package/dist/migrations/apply.js.map +1 -0
- package/dist/migrations/migrator.d.ts +55 -27
- package/dist/migrations/migrator.d.ts.map +1 -1
- package/dist/migrations/migrator.js +187 -103
- package/dist/migrations/migrator.js.map +1 -1
- package/dist/migrations/problems.d.ts +9 -0
- package/dist/migrations/problems.d.ts.map +1 -1
- package/dist/migrations/problems.js +9 -3
- package/dist/migrations/problems.js.map +1 -1
- package/dist/migrations/reference.d.ts +23 -0
- package/dist/migrations/reference.d.ts.map +1 -0
- package/dist/migrations/reference.js +64 -0
- package/dist/migrations/reference.js.map +1 -0
- package/dist/migrations/session.d.ts +2 -1
- package/dist/migrations/session.d.ts.map +1 -1
- package/dist/migrations/session.js +19 -2
- package/dist/migrations/session.js.map +1 -1
- package/dist/migrations/stubs.d.ts +23 -0
- package/dist/migrations/stubs.d.ts.map +1 -0
- package/dist/migrations/stubs.js +99 -0
- package/dist/migrations/stubs.js.map +1 -0
- package/dist/process.d.ts +23 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +82 -0
- package/dist/process.js.map +1 -0
- package/dist/shared.d.ts +2 -0
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +9 -0
- package/dist/shared.js.map +1 -1
- package/dist/testing.d.ts +11 -2
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +28 -9
- package/dist/testing.js.map +1 -1
- package/package.json +22 -5
- package/src/cli/run.ts +84 -23
- package/src/client.ts +88 -16
- package/src/configured.ts +109 -0
- package/src/driver-errors.ts +40 -0
- package/src/index.ts +12 -1
- package/src/migrations/adopt.ts +65 -100
- package/src/migrations/apply.ts +135 -0
- package/src/migrations/migrator.ts +246 -128
- package/src/migrations/problems.ts +13 -5
- package/src/migrations/reference.ts +78 -0
- package/src/migrations/session.ts +28 -2
- package/src/migrations/stubs.ts +139 -0
- package/src/process.ts +110 -0
- package/src/shared.ts +9 -0
- package/src/testing.ts +38 -10
package/dist/migrations/adopt.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
// Adoption (docs/02-module-standard.md §4, docs/05-adoption-playbook.md step 3): an existing app
|
|
2
2
|
// has moved its own tables into a module's schema with its own migration. `adoptModule` checks
|
|
3
|
-
// that the schema now equals what the module's
|
|
4
|
-
//
|
|
5
|
-
// migrated when it is not is the one mistake a later
|
|
3
|
+
// that the schema now equals what the module's files 1..through would create, then records those
|
|
4
|
+
// files as `adopted` without running them; the later files stay pending for `migrate`. Any
|
|
5
|
+
// difference refuses: marking a schema as migrated when it is not is the one mistake a later
|
|
6
|
+
// migration cannot recover from. The pending files of the modules it depends on run first.
|
|
6
7
|
import { ok } from "@softure-ai/core";
|
|
7
|
-
import {
|
|
8
|
+
import { applyFile, recordAdoption, withMigrationLock } from "./apply.js";
|
|
8
9
|
import { describeSchema, diffSchemas } from "./introspect.js";
|
|
9
|
-
import { LEDGER_MODULE_ID, readJournal
|
|
10
|
-
import {
|
|
10
|
+
import { LEDGER_MODULE_ID, readJournal } from "./ledger.js";
|
|
11
|
+
import { checkThrough, compareJournal, prepareUnits, toStep } from "./migrator.js";
|
|
11
12
|
import { failWith } from "./problems.js";
|
|
13
|
+
import { buildReferenceSchema, collectWithDependencies, listOwnedSchemas } from "./reference.js";
|
|
12
14
|
import { withSession } from "./session.js";
|
|
15
|
+
import { readAppTables } from "./stubs.js";
|
|
13
16
|
export async function adoptModule(handle, options) {
|
|
14
17
|
const target = options.modules.find((module) => module.id === options.module);
|
|
15
18
|
if (target === undefined) {
|
|
@@ -26,106 +29,69 @@ export async function adoptModule(handle, options) {
|
|
|
26
29
|
if (unit === undefined) {
|
|
27
30
|
return failWith([{ code: "db.adopt_no_schema", module: target.id }]);
|
|
28
31
|
}
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
const through = options.through ?? unit.files.length;
|
|
33
|
+
const throughProblems = checkThrough(unit, through);
|
|
34
|
+
if (throughProblems.length > 0) {
|
|
35
|
+
return failWith(throughProblems);
|
|
32
36
|
}
|
|
33
37
|
return withSession(handle, (session) => withMigrationLock(session, async () => {
|
|
34
38
|
const journal = await readJournal(session);
|
|
35
39
|
const comparison = compareJournal(units.value, journal);
|
|
36
|
-
const problems = [...comparison.problems, ...checkAdoptable(
|
|
40
|
+
const problems = [...comparison.problems, ...checkAdoptable(unit, journal)];
|
|
37
41
|
if (problems.length > 0) {
|
|
38
42
|
return failWith(problems);
|
|
39
43
|
}
|
|
44
|
+
const dependencyIds = collectWithDependencies(options.modules, target.id);
|
|
45
|
+
dependencyIds.delete(target.id);
|
|
46
|
+
const ledger = comparison.pending.filter((step) => step.module === LEDGER_MODULE_ID);
|
|
47
|
+
const dependencies = comparison.pending.filter((step) => dependencyIds.has(step.module));
|
|
48
|
+
const files = unit.files.filter((file) => file.version <= through);
|
|
49
|
+
const adopted = files.map((file) => toStep(unit, file));
|
|
50
|
+
const report = { module: unit.module, version: options.version, dryRun: options.dryRun === true, ledger, dependencies, adopted };
|
|
51
|
+
// Built from the live database's app tables (module SQL may reference them, issue #170), so
|
|
52
|
+
// inside the lock and only once the checks above passed.
|
|
53
|
+
const appTables = await readAppTables(session, listOwnedSchemas(units.value));
|
|
54
|
+
const reference = await buildReferenceSchema({ modules: options.modules, units: units.value, target: unit, through, appTables });
|
|
55
|
+
if (!reference.ok) {
|
|
56
|
+
return reference;
|
|
57
|
+
}
|
|
58
|
+
// The target's own schema only, compared before anything is written: applying a dependency
|
|
59
|
+
// never changes it.
|
|
40
60
|
const differences = diffSchemas(reference.value, await describeSchema(session, unit.schema));
|
|
41
61
|
if (differences.length > 0) {
|
|
42
62
|
return failWith([{ code: "db.schema_mismatch", module: unit.module, schema: unit.schema, differences }]);
|
|
43
63
|
}
|
|
44
|
-
const ledger = comparison.pending.filter((step) => step.module === LEDGER_MODULE_ID);
|
|
45
|
-
const adopted = unit.files.map((file) => ({ module: unit.module, schema: unit.schema, version: file.version, name: file.name, checksum: file.checksum }));
|
|
46
|
-
const report = { module: unit.module, version: options.version, dryRun: options.dryRun === true, ledger, adopted };
|
|
47
64
|
if (options.dryRun === true) {
|
|
48
65
|
return ok(report);
|
|
49
66
|
}
|
|
50
|
-
const
|
|
67
|
+
const ledgerFailure = await applyPending(session, units.value, ledger);
|
|
68
|
+
if (ledgerFailure !== null) {
|
|
69
|
+
return failWith([ledgerFailure.problem]);
|
|
70
|
+
}
|
|
71
|
+
const dependencyFailure = await applyPending(session, units.value, dependencies);
|
|
72
|
+
if (dependencyFailure !== null) {
|
|
73
|
+
return failWith([dependencyFailure.problem, { code: "db.adopt_dependency_pending", module: unit.module, dependency: dependencyFailure.step.module }]);
|
|
74
|
+
}
|
|
75
|
+
const failure = await recordAdoption(session, unit, files);
|
|
51
76
|
return failure === null ? ok(report) : failWith([failure]);
|
|
52
77
|
}));
|
|
53
78
|
}
|
|
54
|
-
/** The adopted module must be new to the ledger
|
|
55
|
-
function checkAdoptable(
|
|
56
|
-
|
|
57
|
-
return [{ code: "db.adopt_already_applied", module: unit.module }];
|
|
58
|
-
}
|
|
59
|
-
return Object.keys(target.manifest.dependsOn)
|
|
60
|
-
.filter((dependency) => pending.some((step) => step.module === dependency))
|
|
61
|
-
.map((dependency) => ({ code: "db.adopt_dependency_pending", module: unit.module, dependency }));
|
|
79
|
+
/** The adopted module must be new to the ledger. */
|
|
80
|
+
function checkAdoptable(unit, journal) {
|
|
81
|
+
return journal.some((row) => row.module === unit.module) ? [{ code: "db.adopt_already_applied", module: unit.module }] : [];
|
|
62
82
|
}
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const result = await migrate(scratch, { modules, ...(options.migrationsDir ? { migrationsDir: options.migrationsDir } : {}) });
|
|
72
|
-
if (!result.ok) {
|
|
73
|
-
const reason = result.problems.map((problem) => problem.code === "db.migration_failed" ? `${problem.module} ${problem.name}: ${problem.reason}` : problem.code).join("; ");
|
|
74
|
-
return failWith([{ code: "db.adopt_reference_failed", module: target.id, reason }]);
|
|
83
|
+
/** Applies the steps in order, each file in its own transaction; stops at the first failure. */
|
|
84
|
+
async function applyPending(session, units, steps) {
|
|
85
|
+
for (const step of steps) {
|
|
86
|
+
const unit = units.find((candidate) => candidate.module === step.module);
|
|
87
|
+
const file = unit?.files.find((candidate) => candidate.version === step.version);
|
|
88
|
+
if (unit === undefined || file === undefined) {
|
|
89
|
+
throw new Error(`adoptModule: step ${step.module} ${step.version} has no file; compareJournal is broken`);
|
|
75
90
|
}
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
finally {
|
|
80
|
-
await scratch.close();
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function collectWithDependencies(modules, target) {
|
|
84
|
-
const byId = new Map(modules.map((module) => [module.id, module]));
|
|
85
|
-
const collected = new Map();
|
|
86
|
-
const visit = (module) => {
|
|
87
|
-
if (collected.has(module.id))
|
|
88
|
-
return;
|
|
89
|
-
collected.set(module.id, module);
|
|
90
|
-
for (const dependency of Object.keys(module.manifest.dependsOn)) {
|
|
91
|
-
const listed = byId.get(dependency);
|
|
92
|
-
if (listed !== undefined)
|
|
93
|
-
visit(listed);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
visit(target);
|
|
97
|
-
return [...collected.values()];
|
|
98
|
-
}
|
|
99
|
-
/** Applies a pending ledger migration, then records every module file as adopted in one transaction. */
|
|
100
|
-
async function writeAdoption(session, units, unit) {
|
|
101
|
-
const ledger = units.find((candidate) => candidate.module === LEDGER_MODULE_ID);
|
|
102
|
-
const journal = await readJournal(session);
|
|
103
|
-
for (const file of ledger?.files ?? []) {
|
|
104
|
-
if (ledger !== undefined && !journal.some((row) => row.module === LEDGER_MODULE_ID && row.version === file.version)) {
|
|
105
|
-
const failure = await applyFile(session, { unit: ledger, file, method: "applied" });
|
|
106
|
-
if (failure !== null)
|
|
107
|
-
return failure;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
await session.exec("BEGIN");
|
|
112
|
-
for (const file of unit.files) {
|
|
113
|
-
await recordMigration(session, {
|
|
114
|
-
module: unit.module,
|
|
115
|
-
version: file.version,
|
|
116
|
-
name: file.name,
|
|
117
|
-
checksum: file.checksum,
|
|
118
|
-
moduleVersion: unit.moduleVersion,
|
|
119
|
-
method: "adopted",
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
await session.exec("COMMIT");
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
const reason = await rollBack(session, error);
|
|
127
|
-
const first = unit.files[0];
|
|
128
|
-
return { code: "db.migration_failed", module: unit.module, version: first?.version ?? 0, name: first?.name ?? "", reason };
|
|
91
|
+
const failure = await applyFile(session, { unit, file, method: "applied" });
|
|
92
|
+
if (failure !== null)
|
|
93
|
+
return { problem: failure, step };
|
|
129
94
|
}
|
|
95
|
+
return null;
|
|
130
96
|
}
|
|
131
97
|
//# sourceMappingURL=adopt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adopt.js","sourceRoot":"","sources":["../../src/migrations/adopt.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,+FAA+F;AAC/F,2FAA2F;AAC3F,
|
|
1
|
+
{"version":3,"file":"adopt.js","sourceRoot":"","sources":["../../src/migrations/adopt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { MigrationFile } from "./files.js";
|
|
2
|
+
import { type MigrationMethod } from "./ledger.js";
|
|
3
|
+
import type { MigrationProblem } from "./problems.js";
|
|
4
|
+
import type { MigrationSession } from "./session.js";
|
|
5
|
+
/** One migration owner: the ledger itself or an enabled module with a schema. */
|
|
6
|
+
export interface MigrationUnit {
|
|
7
|
+
readonly module: string;
|
|
8
|
+
readonly schema: string;
|
|
9
|
+
readonly moduleVersion: string;
|
|
10
|
+
readonly files: readonly MigrationFile[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Runs one file and its ledger row in one transaction, inside the unit's schema. Returns the
|
|
14
|
+
* problem when the SQL fails; the transaction is rolled back first.
|
|
15
|
+
*/
|
|
16
|
+
export declare function applyFile(session: MigrationSession, input: {
|
|
17
|
+
unit: MigrationUnit;
|
|
18
|
+
file: MigrationFile;
|
|
19
|
+
method: MigrationMethod;
|
|
20
|
+
}): Promise<MigrationProblem | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Rolls the open transaction back and returns the reason to report. A failing ROLLBACK (a lost
|
|
23
|
+
* connection) is added to the reason instead of hiding the original error.
|
|
24
|
+
*/
|
|
25
|
+
export declare function rollBack(session: MigrationSession, error: unknown): Promise<string>;
|
|
26
|
+
/** Holds the session-level advisory lock while `run` runs; a second runner waits for it. */
|
|
27
|
+
export declare function withMigrationLock<T>(session: MigrationSession, run: () => Promise<T>): Promise<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Records `files` of the unit as adopted, all in one transaction: either every row is written or
|
|
30
|
+
* none. Returns the problem when a write fails.
|
|
31
|
+
*/
|
|
32
|
+
export declare function recordAdoption(session: MigrationSession, unit: MigrationUnit, files: readonly MigrationFile[]): Promise<MigrationProblem | null>;
|
|
33
|
+
export declare function quoteIdentifier(name: string): string;
|
|
34
|
+
export declare function describeError(error: unknown): string;
|
|
35
|
+
//# sourceMappingURL=apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/migrations/apply.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;CAC1C;AAKD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAC3E,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA6BlC;AAeD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAQzF;AAED,4FAA4F;AAC5F,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAgBvG;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAoBtJ;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { recordMigration } from "./ledger.js";
|
|
2
|
+
// Any constant works; it only has to be the same for every runner of every app.
|
|
3
|
+
const MIGRATION_LOCK_KEY = "73012026";
|
|
4
|
+
/**
|
|
5
|
+
* Runs one file and its ledger row in one transaction, inside the unit's schema. Returns the
|
|
6
|
+
* problem when the SQL fails; the transaction is rolled back first.
|
|
7
|
+
*/
|
|
8
|
+
export async function applyFile(session, input) {
|
|
9
|
+
const { unit, file, method } = input;
|
|
10
|
+
// The schema passed the manifest's lower_snake_case rule and checkModule; quoting is a second line.
|
|
11
|
+
const schema = quoteIdentifier(unit.schema);
|
|
12
|
+
try {
|
|
13
|
+
await session.exec(`BEGIN; CREATE SCHEMA IF NOT EXISTS ${schema}; SET LOCAL search_path TO ${schema}, public;`);
|
|
14
|
+
const transactionId = await readTransactionId(session);
|
|
15
|
+
if (method === "applied") {
|
|
16
|
+
await session.exec(file.sql);
|
|
17
|
+
}
|
|
18
|
+
// Second line behind findTransactionControl: a file that still ended the transaction left
|
|
19
|
+
// part of itself committed, so it must not be reported as cleanly rolled back.
|
|
20
|
+
if ((await readTransactionId(session)) !== transactionId) {
|
|
21
|
+
throw new TransactionEndedError();
|
|
22
|
+
}
|
|
23
|
+
await recordMigration(session, {
|
|
24
|
+
module: unit.module,
|
|
25
|
+
version: file.version,
|
|
26
|
+
name: file.name,
|
|
27
|
+
checksum: file.checksum,
|
|
28
|
+
moduleVersion: unit.moduleVersion,
|
|
29
|
+
method,
|
|
30
|
+
});
|
|
31
|
+
await session.exec("COMMIT");
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const reason = await rollBack(session, error);
|
|
36
|
+
return { code: "db.migration_failed", module: unit.module, version: file.version, name: file.name, reason };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
class TransactionEndedError extends Error {
|
|
40
|
+
constructor() {
|
|
41
|
+
super("the file ended the migrator's transaction, so the statements before that point may be committed; check the schema by hand");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// The transaction id, not now(): two transactions can share a start time (PGlite's clock ticks in
|
|
45
|
+
// milliseconds), but never an id.
|
|
46
|
+
async function readTransactionId(session) {
|
|
47
|
+
const [row] = await session.query("SELECT pg_current_xact_id()::text AS id");
|
|
48
|
+
return row?.id ?? "";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Rolls the open transaction back and returns the reason to report. A failing ROLLBACK (a lost
|
|
52
|
+
* connection) is added to the reason instead of hiding the original error.
|
|
53
|
+
*/
|
|
54
|
+
export async function rollBack(session, error) {
|
|
55
|
+
const reason = describeError(error);
|
|
56
|
+
try {
|
|
57
|
+
await session.exec("ROLLBACK");
|
|
58
|
+
return reason;
|
|
59
|
+
}
|
|
60
|
+
catch (rollbackError) {
|
|
61
|
+
return `${reason} (and ROLLBACK failed: ${describeError(rollbackError)})`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Holds the session-level advisory lock while `run` runs; a second runner waits for it. */
|
|
65
|
+
export async function withMigrationLock(session, run) {
|
|
66
|
+
await session.query("SELECT pg_advisory_lock($1)", [MIGRATION_LOCK_KEY]);
|
|
67
|
+
let result;
|
|
68
|
+
try {
|
|
69
|
+
result = await run();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// The run's own error wins; an unlock failure here is secondary (the session is discarded).
|
|
73
|
+
await session.query("SELECT pg_advisory_unlock($1)", [MIGRATION_LOCK_KEY]).catch((unlockError) => {
|
|
74
|
+
throw new Error(`migration run failed (${describeError(error)}) and releasing the lock failed: ${describeError(unlockError)}`, {
|
|
75
|
+
cause: error,
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
await session.query("SELECT pg_advisory_unlock($1)", [MIGRATION_LOCK_KEY]);
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Records `files` of the unit as adopted, all in one transaction: either every row is written or
|
|
85
|
+
* none. Returns the problem when a write fails.
|
|
86
|
+
*/
|
|
87
|
+
export async function recordAdoption(session, unit, files) {
|
|
88
|
+
try {
|
|
89
|
+
await session.exec("BEGIN");
|
|
90
|
+
for (const file of files) {
|
|
91
|
+
await recordMigration(session, {
|
|
92
|
+
module: unit.module,
|
|
93
|
+
version: file.version,
|
|
94
|
+
name: file.name,
|
|
95
|
+
checksum: file.checksum,
|
|
96
|
+
moduleVersion: unit.moduleVersion,
|
|
97
|
+
method: "adopted",
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
await session.exec("COMMIT");
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
const reason = await rollBack(session, error);
|
|
105
|
+
const first = files[0];
|
|
106
|
+
return { code: "db.migration_failed", module: unit.module, version: first?.version ?? 0, name: first?.name ?? "", reason };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function quoteIdentifier(name) {
|
|
110
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
111
|
+
}
|
|
112
|
+
export function describeError(error) {
|
|
113
|
+
return error instanceof Error ? error.message : String(error);
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/migrations/apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type AnySoftureModule } from "@softure-ai/core";
|
|
2
2
|
import type { DatabaseHandle } from "../client.js";
|
|
3
|
-
import { type
|
|
4
|
-
import { type JournalRow
|
|
3
|
+
import { type MigrationUnit } from "./apply.js";
|
|
4
|
+
import { type JournalRow } from "./ledger.js";
|
|
5
5
|
import { type MigrationProblem, type MigrationResult } from "./problems.js";
|
|
6
|
-
|
|
6
|
+
export { applyFile, recordAdoption, rollBack, withMigrationLock, type MigrationUnit } from "./apply.js";
|
|
7
7
|
export interface MigrateOptions {
|
|
8
8
|
/** The enabled modules, in any order; dependencies are applied first. */
|
|
9
9
|
readonly modules: readonly AnySoftureModule[];
|
|
@@ -14,7 +14,36 @@ export interface MigrateOptions {
|
|
|
14
14
|
readonly migrationsDir?: URL;
|
|
15
15
|
/** Called after each file is committed, e.g. to print progress. */
|
|
16
16
|
readonly onApplied?: (step: MigrationStep) => void;
|
|
17
|
+
/** Called after each file a baseline recorded as adopted, e.g. to print progress. */
|
|
18
|
+
readonly onAdopted?: (step: MigrationStep) => void;
|
|
19
|
+
/**
|
|
20
|
+
* The app's own migrations around the module files, and its baseline. `planMigrations` runs no
|
|
21
|
+
* hook; it reads only the baseline.
|
|
22
|
+
*/
|
|
23
|
+
readonly app?: AppMigrations;
|
|
24
|
+
/** Called after an app hook succeeded, e.g. to print progress. */
|
|
25
|
+
readonly onAppMigrated?: (phase: AppMigrationPhase) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The app's own migration runner, called by `migrate` under the migration lock on every run (it
|
|
29
|
+
* must skip what it already applied, as drizzle's migrator does). A throw or rejection is reported
|
|
30
|
+
* as `db.app_migration_failed`.
|
|
31
|
+
*/
|
|
32
|
+
export type AppMigrationHook = (handle: DatabaseHandle) => Promise<void>;
|
|
33
|
+
export interface AppMigrations {
|
|
34
|
+
/** Runs before the module files: app tables that module SQL references (normally the app's whole history). */
|
|
35
|
+
readonly before?: AppMigrationHook;
|
|
36
|
+
/** Runs after the module files: app migrations that reference tables a module creates. */
|
|
37
|
+
readonly after?: AppMigrationHook;
|
|
38
|
+
/**
|
|
39
|
+
* The module files the app's own history already creates: module id → the last such file (1..n).
|
|
40
|
+
* For a listed module the ledger has never seen, `migrate` compares the module's schema with
|
|
41
|
+
* files 1..n once `before` ran and records them as `adopted`, then applies the later files; an
|
|
42
|
+
* empty schema is migrated normally. Once the module is in the ledger the entry has no effect.
|
|
43
|
+
*/
|
|
44
|
+
readonly baseline?: Readonly<Record<string, number>>;
|
|
17
45
|
}
|
|
46
|
+
export type AppMigrationPhase = "before" | "after";
|
|
18
47
|
export interface MigrationStep {
|
|
19
48
|
readonly module: string;
|
|
20
49
|
readonly schema: string;
|
|
@@ -25,21 +54,36 @@ export interface MigrationStep {
|
|
|
25
54
|
export interface MigrationPlan {
|
|
26
55
|
/** What `migrate` would apply, in order. */
|
|
27
56
|
readonly pending: readonly MigrationStep[];
|
|
57
|
+
/**
|
|
58
|
+
* The pending steps a baseline may adopt instead of applying: they are adopted when the module's
|
|
59
|
+
* schema already holds objects after `before` (which a plan does not run).
|
|
60
|
+
*/
|
|
61
|
+
readonly baseline: readonly MigrationStep[];
|
|
28
62
|
}
|
|
29
63
|
export interface MigrationReport {
|
|
30
64
|
readonly applied: readonly MigrationStep[];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
readonly
|
|
35
|
-
readonly schema: string;
|
|
36
|
-
readonly moduleVersion: string;
|
|
37
|
-
readonly files: readonly MigrationFile[];
|
|
65
|
+
/** The files a baseline recorded as adopted, in order. */
|
|
66
|
+
readonly adopted: readonly MigrationStep[];
|
|
67
|
+
/** The app hooks that ran, in order. */
|
|
68
|
+
readonly app: readonly AppMigrationPhase[];
|
|
38
69
|
}
|
|
39
70
|
/** The dry run: what `migrate` would apply. Takes no lock and writes nothing. */
|
|
40
71
|
export declare function planMigrations(handle: DatabaseHandle, options: MigrateOptions): Promise<MigrationResult<MigrationPlan>>;
|
|
41
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Applies every pending migration under the advisory lock: the ledger, `app.before`, the module
|
|
74
|
+
* files (adopting a baseline where it applies), then `app.after`. Nothing runs when a check fails.
|
|
75
|
+
*/
|
|
42
76
|
export declare function migrate(handle: DatabaseHandle, options: MigrateOptions): Promise<MigrationResult<MigrationReport>>;
|
|
77
|
+
/** `through` must be a whole number from 1 to the unit's file count. */
|
|
78
|
+
export declare function checkThrough(unit: MigrationUnit, through: number): MigrationProblem[];
|
|
79
|
+
export declare function toStep(unit: MigrationUnit, file: MigrationUnit["files"][number]): MigrationStep;
|
|
80
|
+
/**
|
|
81
|
+
* Runs one app hook alone under the migration lock (the CLI's `--adopt` runs `before` first, so the
|
|
82
|
+
* app migration that moves a table and the adoption are one command). Ok when there is no hook.
|
|
83
|
+
*/
|
|
84
|
+
export declare function runAppMigrations(handle: DatabaseHandle, app: AppMigrations, phase: AppMigrationPhase): Promise<MigrationResult<{
|
|
85
|
+
ran: boolean;
|
|
86
|
+
}>>;
|
|
43
87
|
/**
|
|
44
88
|
* Validates the module list and reads every file: the ledger first, then the modules with a
|
|
45
89
|
* schema in dependency order. Fails with every problem found.
|
|
@@ -50,20 +94,4 @@ export declare function compareJournal(units: readonly MigrationUnit[], journal:
|
|
|
50
94
|
problems: MigrationProblem[];
|
|
51
95
|
pending: MigrationStep[];
|
|
52
96
|
};
|
|
53
|
-
/**
|
|
54
|
-
* Runs one file and its ledger row in one transaction, inside the unit's schema. Returns the
|
|
55
|
-
* problem when the SQL fails; the transaction is rolled back first.
|
|
56
|
-
*/
|
|
57
|
-
export declare function applyFile(session: MigrationSession, input: {
|
|
58
|
-
unit: MigrationUnit;
|
|
59
|
-
file: MigrationFile;
|
|
60
|
-
method: MigrationMethod;
|
|
61
|
-
}): Promise<MigrationProblem | null>;
|
|
62
|
-
/**
|
|
63
|
-
* Rolls the open transaction back and returns the reason to report. A failing ROLLBACK (a lost
|
|
64
|
-
* connection) is added to the reason instead of hiding the original error.
|
|
65
|
-
*/
|
|
66
|
-
export declare function rollBack(session: MigrationSession, error: unknown): Promise<string>;
|
|
67
|
-
/** Holds the session-level advisory lock while `run` runs; a second runner waits for it. */
|
|
68
|
-
export declare function withMigrationLock<T>(session: MigrationSession, run: () => Promise<T>): Promise<T>;
|
|
69
97
|
//# sourceMappingURL=migrator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrator.d.ts","sourceRoot":"","sources":["../../src/migrations/migrator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrator.d.ts","sourceRoot":"","sources":["../../src/migrations/migrator.ts"],"names":[],"mappings":"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"}
|