@vibeorm/migrate 1.3.1 → 2.0.0-alpha.10
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/README.md +33 -66
- package/dist/ddl/drop-order.d.ts +25 -0
- package/dist/ddl/drop-order.d.ts.map +1 -0
- package/dist/ddl/mysql.d.ts +46 -0
- package/dist/ddl/mysql.d.ts.map +1 -0
- package/dist/ddl/postgres.d.ts +24 -0
- package/dist/ddl/postgres.d.ts.map +1 -0
- package/dist/ddl/render.d.ts +25 -0
- package/dist/ddl/render.d.ts.map +1 -0
- package/dist/ddl/rls.d.ts +40 -0
- package/dist/ddl/rls.d.ts.map +1 -0
- package/dist/ddl/sqlite.d.ts +79 -0
- package/dist/ddl/sqlite.d.ts.map +1 -0
- package/dist/differ.d.ts +41 -0
- package/dist/differ.d.ts.map +1 -0
- package/dist/down.d.ts +57 -0
- package/dist/down.d.ts.map +1 -0
- package/dist/enum-values.d.ts +32 -0
- package/dist/enum-values.d.ts.map +1 -0
- package/dist/execute.d.ts +124 -0
- package/dist/execute.d.ts.map +1 -0
- package/dist/execution.d.ts +168 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/extensions.d.ts +74 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5688 -0
- package/dist/index.js.map +37 -0
- package/dist/introspect/mysql.d.ts +31 -0
- package/dist/introspect/mysql.d.ts.map +1 -0
- package/dist/introspect/postgres-rls.d.ts +41 -0
- package/dist/introspect/postgres-rls.d.ts.map +1 -0
- package/dist/introspect/postgres.d.ts +12 -0
- package/dist/introspect/postgres.d.ts.map +1 -0
- package/dist/introspect/shared.d.ts +78 -0
- package/dist/introspect/shared.d.ts.map +1 -0
- package/dist/introspect/sqlite.d.ts +53 -0
- package/dist/introspect/sqlite.d.ts.map +1 -0
- package/dist/journal-table.d.ts +15 -0
- package/dist/journal-table.d.ts.map +1 -0
- package/dist/mysql-types.d.ts +66 -0
- package/dist/mysql-types.d.ts.map +1 -0
- package/dist/normalize.d.ts +143 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/postgres-types.d.ts +95 -0
- package/dist/postgres-types.d.ts.map +1 -0
- package/dist/push.d.ts +51 -0
- package/dist/push.d.ts.map +1 -0
- package/dist/recovery.d.ts +41 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/relations.d.ts +40 -0
- package/dist/relations.d.ts.map +1 -0
- package/dist/renames.d.ts +58 -0
- package/dist/renames.d.ts.map +1 -0
- package/dist/runner.d.ts +181 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/sqlite-types.d.ts +101 -0
- package/dist/sqlite-types.d.ts.map +1 -0
- package/dist/types.d.ts +329 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +34 -23
- package/src/cascade-actions.ts +0 -88
- package/src/ddl-builder.ts +0 -415
- package/src/index.ts +0 -41
- package/src/introspector.ts +0 -684
- package/src/migration-runner.ts +0 -127
- package/src/relation-utils.ts +0 -79
- package/src/schema-differ.ts +0 -865
- package/src/schema-printer.ts +0 -259
- package/src/snapshot.ts +0 -141
- package/src/sql-utils.ts +0 -45
- package/src/types.ts +0 -13
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Establishing what actually happened, before a nontransactional migration
|
|
3
|
+
* re-runs anything.
|
|
4
|
+
*
|
|
5
|
+
* A migration that runs outside a transaction can die between a statement
|
|
6
|
+
* committing and the journal recording it. The only honest recovery is to ask
|
|
7
|
+
* the database what the world looks like now and compare it against the
|
|
8
|
+
* statement's DECLARED postcondition (execution.ts). Three answers exist:
|
|
9
|
+
*
|
|
10
|
+
* - `"satisfied"` — the live catalog already matches the declaration. Skip it.
|
|
11
|
+
* - `"runnable"` — the declaration is demonstrably not in effect, and running
|
|
12
|
+
* the statement is the way to get there.
|
|
13
|
+
* - a thrown `VibeError` — the state is neither, and no automatic action is
|
|
14
|
+
* safe. Nothing is dropped, nothing is re-run: the operator decides.
|
|
15
|
+
*
|
|
16
|
+
* There is deliberately NO fourth answer of the form "probably fine, re-run
|
|
17
|
+
* it". This module never claims exactly-once execution of arbitrary SQL; it
|
|
18
|
+
* only decides the cases a declared postcondition can actually settle.
|
|
19
|
+
*
|
|
20
|
+
* Comparison is conservative by design. Expressions must match verbatim apart
|
|
21
|
+
* from surrounding whitespace. Quotes and whitespace inside literals can change
|
|
22
|
+
* meaning; any difference is a CONFLICT — a false conflict costs an operator one
|
|
23
|
+
* look, a false match corrupts a schema.
|
|
24
|
+
*/
|
|
25
|
+
import type { Dialect } from "@vibeorm/schema";
|
|
26
|
+
import type { MigrationOperation } from "./execution.ts";
|
|
27
|
+
import type { SqlExecutor } from "./types.ts";
|
|
28
|
+
/** Whether a declared operation's postcondition already holds. */
|
|
29
|
+
export type OperationState = "satisfied" | "runnable";
|
|
30
|
+
/**
|
|
31
|
+
* Ask the database whether a declared operation is already in effect.
|
|
32
|
+
*
|
|
33
|
+
* PostgreSQL only: it is the one dialect with a nontransactional migration
|
|
34
|
+
* regime here, and every probe reads its catalogs.
|
|
35
|
+
*/
|
|
36
|
+
export declare function establishOperationState(params: {
|
|
37
|
+
executor: SqlExecutor;
|
|
38
|
+
dialect: Dialect;
|
|
39
|
+
operation: MigrationOperation;
|
|
40
|
+
}): Promise<OperationState>;
|
|
41
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAS/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,UAAU,CAAC;AA6BtD;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,kBAAkB,CAAC;CAC/B,GAAG,OAAO,CAAC,cAAc,CAAC,CAkG1B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implicit many-to-many detection on Schema IR.
|
|
3
|
+
*
|
|
4
|
+
* ONE structural rule in @vibeorm/schema, also used by RLS validation and
|
|
5
|
+
* matching the parser's ambiguity rule (see LEARNINGS): two fields form an
|
|
6
|
+
* implicit M:N **iff** both are lists that declare no FK columns, they are
|
|
7
|
+
* distinct fields (self-exclusion — a 1:N self-relation must never pair with
|
|
8
|
+
* itself), their relation names match symmetrically (both unnamed, or both
|
|
9
|
+
* the same name), and each side is the OTHER'S SOLE counterpart under that
|
|
10
|
+
* name key. Detection never reads `relation.type`, because an introspected
|
|
11
|
+
* schema represents a real join table as two unnamed `oneToMany` list sides —
|
|
12
|
+
* trusting the type would break round-trip stability.
|
|
13
|
+
*
|
|
14
|
+
* The sole-counterpart requirement is the phantom-join-table fix, round two:
|
|
15
|
+
* two models holding foreign keys INTO EACH OTHER introspect as an unnamed
|
|
16
|
+
* fieldless list on each side (every FK synthesizes one on its target), and
|
|
17
|
+
* name-matching alone would marry those lists into a join table that exists
|
|
18
|
+
* nowhere. Counting EVERY relation under the name key — FK-owning sides
|
|
19
|
+
* included — disambiguates: the parser rejects the same shape as
|
|
20
|
+
* `ambiguous-relation` at parse time, so parsed and introspected schemas
|
|
21
|
+
* resolve identically.
|
|
22
|
+
*
|
|
23
|
+
* The join-table naming itself comes from the ONE shared convention in
|
|
24
|
+
* `@vibeorm/schema` (`implicitJoinTable`) — never re-derived here.
|
|
25
|
+
*/
|
|
26
|
+
import type { SchemaIR } from "@vibeorm/schema";
|
|
27
|
+
/** A detected implicit many-to-many relation. `modelA` sorts before `modelB`. */
|
|
28
|
+
export type ImplicitM2MPair = {
|
|
29
|
+
readonly modelA: string;
|
|
30
|
+
readonly modelB: string;
|
|
31
|
+
/** Shared explicit relation name, when both sides declare one. */
|
|
32
|
+
readonly relationName?: string;
|
|
33
|
+
/** Join table name per the shared convention (`_PostToTag`, `_tags`). */
|
|
34
|
+
readonly tableName: string;
|
|
35
|
+
};
|
|
36
|
+
/** Detect every implicit many-to-many pair in a schema. */
|
|
37
|
+
export declare function detectImplicitM2MPairs(params: {
|
|
38
|
+
schema: SchemaIR;
|
|
39
|
+
}): ImplicitM2MPair[];
|
|
40
|
+
//# sourceMappingURL=relations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,iFAAiF;AACjF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GAAG,eAAe,EAAE,CA4BtF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EXPLICIT physical renames.
|
|
3
|
+
*
|
|
4
|
+
* The differ deliberately has no rename detection: two schemas that differ by
|
|
5
|
+
* a name are indistinguishable from a drop plus a create, and guessing between
|
|
6
|
+
* them from "similar shapes" is how a migration tool silently destroys a
|
|
7
|
+
* column. A rename therefore only ever happens because someone WROTE one down,
|
|
8
|
+
* as an old physical name and a new physical name, and it is validated against
|
|
9
|
+
* both schemas before a single statement is rendered.
|
|
10
|
+
*
|
|
11
|
+
* `@map` / `@@map` remains the no-DDL option: mapping a model or field to the
|
|
12
|
+
* name the database already uses changes generated code only. Use a rename
|
|
13
|
+
* intent when the DATABASE object must actually move.
|
|
14
|
+
*
|
|
15
|
+
* What a physical rename preserves, on every dialect that supports it: the
|
|
16
|
+
* rows, the column defaults, the indexes and constraints attached to the
|
|
17
|
+
* object, and inbound foreign keys — the object keeps its identity, only its
|
|
18
|
+
* name changes. PostgreSQL policy expressions follow the rename too, because
|
|
19
|
+
* they are stored parsed and reference the object by identity, not by text.
|
|
20
|
+
*
|
|
21
|
+
* What refuses instead of guessing: renaming a table and one of its columns in
|
|
22
|
+
* the same plan (the second intent's table name is then ambiguous), an intent
|
|
23
|
+
* whose endpoints do not both check out against the two schemas, and two
|
|
24
|
+
* intents pointing at the same object.
|
|
25
|
+
*/
|
|
26
|
+
import type { Dialect, SchemaIR } from "@vibeorm/schema";
|
|
27
|
+
import type { MigrationStep } from "./types.ts";
|
|
28
|
+
/** One deliberately written rename, in PHYSICAL (database) names. */
|
|
29
|
+
export type RenameIntent = {
|
|
30
|
+
readonly kind: "table";
|
|
31
|
+
readonly from: string;
|
|
32
|
+
readonly to: string;
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: "column";
|
|
35
|
+
readonly table: string;
|
|
36
|
+
readonly from: string;
|
|
37
|
+
readonly to: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Validate rename intents against the two schemas and turn them into steps,
|
|
41
|
+
* returning the SOURCE schema rewritten as if the renames had happened.
|
|
42
|
+
*
|
|
43
|
+
* The rewrite is what keeps the rest of the diff honest: once the source model
|
|
44
|
+
* carries the new physical name, the ordinary differ sees one table on both
|
|
45
|
+
* sides and emits no drop/create pair for it. Only `dbName` moves — the
|
|
46
|
+
* logical IR graph (relations, index field lists) is untouched, so nothing
|
|
47
|
+
* downstream has to know a rename happened.
|
|
48
|
+
*/
|
|
49
|
+
export declare function planRenames(params: {
|
|
50
|
+
from: SchemaIR;
|
|
51
|
+
to: SchemaIR;
|
|
52
|
+
renames: readonly RenameIntent[];
|
|
53
|
+
dialect: Dialect;
|
|
54
|
+
}): {
|
|
55
|
+
readonly steps: readonly MigrationStep[];
|
|
56
|
+
readonly from: SchemaIR;
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=renames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renames.d.ts","sourceRoot":"","sources":["../src/renames.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAW,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qEAAqE;AACrE,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAuBpG;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAkGxE"}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration runner: generate named migrations from IR diffs, apply them with
|
|
3
|
+
* bookkeeping in `_vibe_migrations`, report status, and repair records.
|
|
4
|
+
*
|
|
5
|
+
* Concurrency: `applyMigrations` and `rollbackMigration` take an advisory lock
|
|
6
|
+
* for the whole run — `pg_advisory_lock(1447645253)` on postgres ("VIBE" in
|
|
7
|
+
* ASCII, blocking, released in a finally), `GET_LOCK('vibeorm_migrate', 60)`
|
|
8
|
+
* on mysql (throws VIBE_MIGRATION on timeout). sqlite needs none: it is
|
|
9
|
+
* single-writer and `BEGIN` serializes. Locks are session-scoped — the
|
|
10
|
+
* SqlExecutor contract (types.ts) requires one session for the whole
|
|
11
|
+
* operation.
|
|
12
|
+
*
|
|
13
|
+
* Lock ORDER matters: the lock is taken BEFORE `ensureMigrationsTable`, never
|
|
14
|
+
* after. Two concurrent first-time runs against a fresh postgres database
|
|
15
|
+
* would otherwise both issue `CREATE TABLE IF NOT EXISTS _vibe_migrations`,
|
|
16
|
+
* which is a catalog race on postgres (a duplicate-key error on
|
|
17
|
+
* `pg_type_typname_nsp_index`, not a no-op). `migrationStatus` and
|
|
18
|
+
* `resolveMigration` deliberately take NO lock — wrapping reads in the
|
|
19
|
+
* migration lock would make `status` block behind a running migrate — so two
|
|
20
|
+
* concurrent FIRST-EVER `status`/`resolve` calls on a fresh database can still
|
|
21
|
+
* hit that race; retrying either is enough, since the table then exists.
|
|
22
|
+
*
|
|
23
|
+
* Execution strategy per dialect (`DIALECT_CAPABILITIES[dialect]`) AND per
|
|
24
|
+
* script:
|
|
25
|
+
*
|
|
26
|
+
* - transactionalDdl (postgres, sqlite): each migration runs inside
|
|
27
|
+
* BEGIN/COMMIT with its bookkeeping INSERT — atomic.
|
|
28
|
+
* - EXCEPT sqlite scripts containing a table-rebuild block, which manage their
|
|
29
|
+
* own transactions (execute.ts `usesSelfManagedTransactions`) and therefore
|
|
30
|
+
* cannot be wrapped. Those take the SAME per-statement bookkeeping as mysql
|
|
31
|
+
* (below). They are NOT simply "re-runnable": a pure rebuild block is
|
|
32
|
+
* effectively idempotent, but any plain statement in the same script
|
|
33
|
+
* auto-commits on its own, so without a recorded statement_index a crash
|
|
34
|
+
* wedged the migration forever (every later run replayed statements that had
|
|
35
|
+
* already run). Resuming into a rebuild block rewinds to that block's
|
|
36
|
+
* `PRAGMA foreign_keys=OFF` (execute.ts `selfManagedResumeStart`): the block
|
|
37
|
+
* is transactional, so nothing of it survived, and the pragma is
|
|
38
|
+
* session-scoped and gone after a crash.
|
|
39
|
+
* - mysql (transactionalDdl: false — DDL auto-commits): the runner records
|
|
40
|
+
* PER-STATEMENT progress instead. The row is inserted up front
|
|
41
|
+
* (statement_index 0), advanced after every statement, and on failure the
|
|
42
|
+
* error lands in the `error` column. Re-running RESUMES from the recorded
|
|
43
|
+
* statement_index — and REFUSES if the migration's checksum changed since
|
|
44
|
+
* the partial application. A row is "complete" when its statement_index
|
|
45
|
+
* reaches the statement count (or its checksum is empty — resolveMigration
|
|
46
|
+
* records, which store statement_index 2147483647).
|
|
47
|
+
*
|
|
48
|
+
* `usesStatementBookkeeping` is the ONE predicate deciding which of the two
|
|
49
|
+
* regimes a (dialect, script) pair is in; apply, status and rollback all read
|
|
50
|
+
* it, so they can never disagree about whether a record is complete.
|
|
51
|
+
*
|
|
52
|
+
* Checksums (sha256 over the joined SQL) pin a migration's content:
|
|
53
|
+
* re-applying a renamed-or-edited migration is a VIBE_MIGRATION error, never
|
|
54
|
+
* a silent divergence.
|
|
55
|
+
*/
|
|
56
|
+
import type { Dialect, SchemaIR, VibeExtension } from "@vibeorm/schema";
|
|
57
|
+
import type { MigrationExecution } from "./execution.ts";
|
|
58
|
+
import type { RenameIntent } from "./renames.ts";
|
|
59
|
+
import type { AppliedResult, MigrationFile, MigrationStatus, RecordedMigration, SqlExecutor } from "./types.ts";
|
|
60
|
+
export { MIGRATIONS_TABLE_NAME } from "./journal-table.ts";
|
|
61
|
+
/** Create the bookkeeping table when missing. */
|
|
62
|
+
export declare function ensureMigrationsTable(params: {
|
|
63
|
+
executor: SqlExecutor;
|
|
64
|
+
dialect?: Dialect;
|
|
65
|
+
}): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* sha256 hex over the migration's statements — its identity across runs.
|
|
68
|
+
*
|
|
69
|
+
* A NON-default execution block (execution.ts) folds a canonical description
|
|
70
|
+
* of itself in, so changing how a migration executes after it was applied is
|
|
71
|
+
* caught by the same checksum refusal that catches changing its SQL. A default
|
|
72
|
+
* or absent block contributes NOTHING, which is what keeps every migration
|
|
73
|
+
* written before this feature — and every journal row recording one — valid.
|
|
74
|
+
*/
|
|
75
|
+
export declare function migrationChecksum(params: {
|
|
76
|
+
sql: readonly string[];
|
|
77
|
+
execution?: MigrationExecution;
|
|
78
|
+
}): string;
|
|
79
|
+
/**
|
|
80
|
+
* Diff two IRs and render a named migration for a dialect (postgres if
|
|
81
|
+
* omitted). `destructive` aggregates the steps' flags so callers can gate
|
|
82
|
+
* confirmation before applying.
|
|
83
|
+
*/
|
|
84
|
+
export declare function generateMigration(params: {
|
|
85
|
+
from: SchemaIR;
|
|
86
|
+
to: SchemaIR;
|
|
87
|
+
name: string;
|
|
88
|
+
/** Rendering dialect; postgres if omitted. */
|
|
89
|
+
dialect?: Dialect;
|
|
90
|
+
/** Configured extension instances — their artifacts land in the migration. */
|
|
91
|
+
extensions?: readonly VibeExtension[];
|
|
92
|
+
/** Explicit physical renames (renames.ts); never inferred from the diff. */
|
|
93
|
+
renames?: readonly RenameIntent[];
|
|
94
|
+
/** Explicit execution semantics for the generated file. */
|
|
95
|
+
execution?: MigrationExecution;
|
|
96
|
+
}): MigrationFile;
|
|
97
|
+
/**
|
|
98
|
+
* Apply migrations in order under an advisory lock (see module doc).
|
|
99
|
+
* Already-recorded migrations are checksum-verified and skipped; a mismatch
|
|
100
|
+
* throws VIBE_MIGRATION (a recorded migration whose SQL changed is
|
|
101
|
+
* corruption, not drift to paper over). Where progress is recorded per
|
|
102
|
+
* statement (mysql, and self-managed sqlite scripts), a partially-applied
|
|
103
|
+
* migration RESUMES from its recorded statement_index. Records created by
|
|
104
|
+
* `resolveMigration` (empty checksum) skip verification.
|
|
105
|
+
*/
|
|
106
|
+
export declare function applyMigrations(params: {
|
|
107
|
+
executor: SqlExecutor;
|
|
108
|
+
migrations: readonly MigrationFile[];
|
|
109
|
+
acceptSecurityChanges?: boolean;
|
|
110
|
+
/** Execution dialect; postgres if omitted. */
|
|
111
|
+
dialect?: Dialect;
|
|
112
|
+
}): Promise<AppliedResult>;
|
|
113
|
+
/** Compare known migrations against the bookkeeping table. */
|
|
114
|
+
export declare function migrationStatus(params: {
|
|
115
|
+
executor: SqlExecutor;
|
|
116
|
+
migrations: readonly MigrationFile[];
|
|
117
|
+
/** Bookkeeping dialect; postgres if omitted. */
|
|
118
|
+
dialect?: Dialect;
|
|
119
|
+
/** Trusted current deployment; policy state is verified independently of migration records. */
|
|
120
|
+
schema?: SchemaIR;
|
|
121
|
+
}): Promise<MigrationStatus>;
|
|
122
|
+
/**
|
|
123
|
+
* The bookkeeping journal itself, in the order rows were written — i.e. the
|
|
124
|
+
* order migrations were actually applied, which is what a rollback must walk
|
|
125
|
+
* backwards. Every row is returned and classified, including the ones
|
|
126
|
+
* `migrationStatus` folds away: a mismatched record, a partially applied one
|
|
127
|
+
* and a record whose file is gone all still occupy a position in history, and
|
|
128
|
+
* a caller that skips them rolls back the WRONG migration (F28).
|
|
129
|
+
*
|
|
130
|
+
* Takes no lock, exactly like `migrationStatus`.
|
|
131
|
+
*/
|
|
132
|
+
export declare function recordedMigrations(params: {
|
|
133
|
+
executor: SqlExecutor;
|
|
134
|
+
/** Known migration files — classification needs their SQL. */
|
|
135
|
+
migrations: readonly MigrationFile[];
|
|
136
|
+
/** Bookkeeping dialect; postgres if omitted. */
|
|
137
|
+
dialect?: Dialect;
|
|
138
|
+
}): Promise<RecordedMigration[]>;
|
|
139
|
+
/**
|
|
140
|
+
* Repair the bookkeeping table without running SQL: mark a migration as
|
|
141
|
+
* `"applied"` (upsert its record; pass `checksum` to also pin its content —
|
|
142
|
+
* omitted, the record stores an empty checksum and later runs skip
|
|
143
|
+
* verification for it) or `"rolled-back"` (delete its record).
|
|
144
|
+
*/
|
|
145
|
+
export declare function resolveMigration(params: {
|
|
146
|
+
executor: SqlExecutor;
|
|
147
|
+
name: string;
|
|
148
|
+
as: "applied" | "rolled-back";
|
|
149
|
+
checksum?: string;
|
|
150
|
+
/** Bookkeeping dialect; postgres if omitted. */
|
|
151
|
+
dialect?: Dialect;
|
|
152
|
+
}): Promise<void>;
|
|
153
|
+
/**
|
|
154
|
+
* Roll back ONE applied migration by executing its `downSql` and deleting its
|
|
155
|
+
* bookkeeping row, under the same advisory lock as `applyMigrations`.
|
|
156
|
+
*
|
|
157
|
+
* Guards, in order: the migration must carry `downSql` (older migrations
|
|
158
|
+
* predate down-migration support), must be recorded as applied, and — when
|
|
159
|
+
* the record pins a checksum — its up SQL must still match (rolling back with
|
|
160
|
+
* drifted files would run a down that no longer mirrors what was applied).
|
|
161
|
+
*
|
|
162
|
+
* Dialect behavior mirrors apply exactly (same `usesStatementBookkeeping`
|
|
163
|
+
* predicate, read over the DOWN script): postgres/sqlite run the down inside
|
|
164
|
+
* BEGIN/COMMIT with the bookkeeping DELETE (atomic). mysql DDL auto-commits,
|
|
165
|
+
* and a sqlite down containing a table-rebuild block cannot be wrapped, so
|
|
166
|
+
* both record progress per-statement in the existing `statement_index`
|
|
167
|
+
* column, NEGATIVE to mark the rollback direction: after k down statements,
|
|
168
|
+
* the row stores -(k+1). A failed rollback resumes from the recorded
|
|
169
|
+
* statement on the next call; `applyMigrations` refuses a mid-rollback record
|
|
170
|
+
* (that check reads the sign only, so it holds for every dialect). A
|
|
171
|
+
* partially APPLIED migration cannot be rolled back — its down mirrors the
|
|
172
|
+
* full up; finish applying (apply resumes) or repair with `resolveMigration`.
|
|
173
|
+
*/
|
|
174
|
+
export declare function rollbackMigration(params: {
|
|
175
|
+
executor: SqlExecutor;
|
|
176
|
+
migration: MigrationFile;
|
|
177
|
+
acceptSecurityChanges?: boolean;
|
|
178
|
+
/** Execution dialect; postgres if omitted. */
|
|
179
|
+
dialect?: Dialect;
|
|
180
|
+
}): Promise<void>;
|
|
181
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite column-type mapping (IR → storage class and back), comparison keys,
|
|
3
|
+
* default rendering/fingerprints, and the enum CHECK-constraint convention.
|
|
4
|
+
*
|
|
5
|
+
* dialect-matrix §2: sqlite columns use the four storage classes only —
|
|
6
|
+
* String/DateTime/Decimal/Json → TEXT, Boolean/Int/BigInt → INTEGER,
|
|
7
|
+
* Float → REAL, Bytes → BLOB. Enums are TEXT plus a named CHECK constraint
|
|
8
|
+
* (`CONSTRAINT "<table>_<column>_enum_<Enum>" CHECK ("<column>" IN (…))`) —
|
|
9
|
+
* the constraint NAME is the marker that lets introspection recover the enum's
|
|
10
|
+
* name, and the IN list recovers its values. Scalar lists are unsupported
|
|
11
|
+
* (refused by the differ; this module throws as the render-time backstop).
|
|
12
|
+
*
|
|
13
|
+
* THE TEXT AMBIGUITY, stated honestly: DateTime, Decimal and Json all live in
|
|
14
|
+
* TEXT, and Boolean/BigInt live in INTEGER — a pulled schema reports such
|
|
15
|
+
* columns as String/Int (there is no marker to say otherwise). Column
|
|
16
|
+
* comparison is therefore STORAGE-CLASS based on sqlite (`sqliteColumnTypeKey`
|
|
17
|
+
* compares what the database actually stores), so `diff(introspect(push(X)),
|
|
18
|
+
* X)` stays [] even though the introspected IR says String where X said
|
|
19
|
+
* DateTime.
|
|
20
|
+
*/
|
|
21
|
+
import type { FieldIR, ScalarType } from "@vibeorm/schema";
|
|
22
|
+
import type { EnumIR } from "@vibeorm/schema";
|
|
23
|
+
/** IR scalar → sqlite storage class. */
|
|
24
|
+
export declare const SCALAR_TO_SQLITE: Readonly<Record<ScalarType, string>>;
|
|
25
|
+
/** Render-time backstop for scalar-list columns (the differ refuses earlier). */
|
|
26
|
+
export declare function refuseSqliteScalarList(params: {
|
|
27
|
+
field: FieldIR;
|
|
28
|
+
}): never;
|
|
29
|
+
/**
|
|
30
|
+
* Storage class for a column. Unsupported columns render their nativeType
|
|
31
|
+
* verbatim (fidelity over guessing); enums are TEXT (the CHECK is separate).
|
|
32
|
+
*/
|
|
33
|
+
export declare function sqliteStorageType(params: {
|
|
34
|
+
field: FieldIR;
|
|
35
|
+
}): string;
|
|
36
|
+
/**
|
|
37
|
+
* Comparison key for a column on sqlite: the STORAGE CLASS, not the IR type —
|
|
38
|
+
* String and DateTime are both `t:TEXT` because the database cannot tell them
|
|
39
|
+
* apart. Enum columns key on their VALUE LIST (`e:A|B`), never their name
|
|
40
|
+
* (the name only lives in our CHECK-constraint convention): adding an enum
|
|
41
|
+
* value changes the key and flows through `alterColumn` → table rebuild with
|
|
42
|
+
* the widened CHECK.
|
|
43
|
+
*/
|
|
44
|
+
export declare function sqliteColumnTypeKey(params: {
|
|
45
|
+
field: FieldIR;
|
|
46
|
+
enums: ReadonlyMap<string, EnumIR>;
|
|
47
|
+
}): string;
|
|
48
|
+
/**
|
|
49
|
+
* Default fingerprint on sqlite: postgres rules, except booleans compare as
|
|
50
|
+
* their INTEGER storage (`true` → `num:1`) — an introspected sqlite schema can
|
|
51
|
+
* only ever report `DEFAULT 1`, and the fingerprint must not churn on that.
|
|
52
|
+
*/
|
|
53
|
+
export declare function sqliteDefaultFingerprint(params: {
|
|
54
|
+
field: FieldIR;
|
|
55
|
+
}): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Render the `DEFAULT` expression for a field on sqlite, or `undefined` when
|
|
58
|
+
* the column takes none (app-level generators, autoincrement — which renders
|
|
59
|
+
* as `INTEGER PRIMARY KEY AUTOINCREMENT` instead). Booleans store as 1/0.
|
|
60
|
+
*/
|
|
61
|
+
export declare function sqliteDefaultSql(params: {
|
|
62
|
+
field: FieldIR;
|
|
63
|
+
}): string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Name of the enum CHECK constraint for a column — the `_enum_` infix is the
|
|
66
|
+
* introspection marker that recovers the enum's name from `sqlite_master.sql`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function sqliteEnumCheckName(params: {
|
|
69
|
+
table: string;
|
|
70
|
+
column: string;
|
|
71
|
+
enumName: string;
|
|
72
|
+
}): string;
|
|
73
|
+
/** One parsed enum CHECK constraint from a table's `sqlite_master.sql`. */
|
|
74
|
+
export type ParsedEnumCheck = {
|
|
75
|
+
readonly column: string;
|
|
76
|
+
readonly enumName: string;
|
|
77
|
+
readonly values: readonly string[];
|
|
78
|
+
};
|
|
79
|
+
/** One parsed NAMED user CHECK from a table's `sqlite_master.sql`. */
|
|
80
|
+
export type ParsedNamedCheck = {
|
|
81
|
+
readonly name: string;
|
|
82
|
+
readonly expression: string;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Every `CONSTRAINT "name" CHECK (…)` in a table's `sqlite_master.sql`, with
|
|
86
|
+
* a balanced-paren walk for the expression (a regex capture would stop at the
|
|
87
|
+
* first `)`). The caller filters out the enum-convention names — those are
|
|
88
|
+
* column machinery, not user checks.
|
|
89
|
+
*/
|
|
90
|
+
export declare function parseSqliteNamedChecks(params: {
|
|
91
|
+
tableSql: string;
|
|
92
|
+
}): ParsedNamedCheck[];
|
|
93
|
+
/**
|
|
94
|
+
* Recover enum columns from a table's `sqlite_master.sql` text. Only checks
|
|
95
|
+
* following OUR naming convention are recognized; foreign CHECK constraints
|
|
96
|
+
* are ignored (and — documented limitation — do not survive a table rebuild).
|
|
97
|
+
*/
|
|
98
|
+
export declare function parseSqliteEnumChecks(params: {
|
|
99
|
+
tableSql: string;
|
|
100
|
+
}): ParsedEnumCheck[];
|
|
101
|
+
//# sourceMappingURL=sqlite-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-types.d.ts","sourceRoot":"","sources":["../src/sqlite-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAI9C,wCAAwC;AACxC,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAUjE,CAAC;AAEF,iFAAiF;AACjF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAMxE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAepE;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,GAAG,MAAM,CAWT;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,IAAI,CAOlF;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CAuB/E;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,CAET;AAED,2EAA2E;AAC3E,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,CAAC;AAKF,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,EAAE,CAuBvF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,eAAe,EAAE,CAYrF"}
|