@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.
Files changed (73) hide show
  1. package/README.md +33 -66
  2. package/dist/ddl/drop-order.d.ts +25 -0
  3. package/dist/ddl/drop-order.d.ts.map +1 -0
  4. package/dist/ddl/mysql.d.ts +46 -0
  5. package/dist/ddl/mysql.d.ts.map +1 -0
  6. package/dist/ddl/postgres.d.ts +24 -0
  7. package/dist/ddl/postgres.d.ts.map +1 -0
  8. package/dist/ddl/render.d.ts +25 -0
  9. package/dist/ddl/render.d.ts.map +1 -0
  10. package/dist/ddl/rls.d.ts +40 -0
  11. package/dist/ddl/rls.d.ts.map +1 -0
  12. package/dist/ddl/sqlite.d.ts +79 -0
  13. package/dist/ddl/sqlite.d.ts.map +1 -0
  14. package/dist/differ.d.ts +41 -0
  15. package/dist/differ.d.ts.map +1 -0
  16. package/dist/down.d.ts +57 -0
  17. package/dist/down.d.ts.map +1 -0
  18. package/dist/enum-values.d.ts +32 -0
  19. package/dist/enum-values.d.ts.map +1 -0
  20. package/dist/execute.d.ts +124 -0
  21. package/dist/execute.d.ts.map +1 -0
  22. package/dist/execution.d.ts +168 -0
  23. package/dist/execution.d.ts.map +1 -0
  24. package/dist/extensions.d.ts +74 -0
  25. package/dist/extensions.d.ts.map +1 -0
  26. package/dist/index.d.ts +32 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +5688 -0
  29. package/dist/index.js.map +37 -0
  30. package/dist/introspect/mysql.d.ts +31 -0
  31. package/dist/introspect/mysql.d.ts.map +1 -0
  32. package/dist/introspect/postgres-rls.d.ts +41 -0
  33. package/dist/introspect/postgres-rls.d.ts.map +1 -0
  34. package/dist/introspect/postgres.d.ts +12 -0
  35. package/dist/introspect/postgres.d.ts.map +1 -0
  36. package/dist/introspect/shared.d.ts +78 -0
  37. package/dist/introspect/shared.d.ts.map +1 -0
  38. package/dist/introspect/sqlite.d.ts +53 -0
  39. package/dist/introspect/sqlite.d.ts.map +1 -0
  40. package/dist/journal-table.d.ts +15 -0
  41. package/dist/journal-table.d.ts.map +1 -0
  42. package/dist/mysql-types.d.ts +66 -0
  43. package/dist/mysql-types.d.ts.map +1 -0
  44. package/dist/normalize.d.ts +143 -0
  45. package/dist/normalize.d.ts.map +1 -0
  46. package/dist/postgres-types.d.ts +95 -0
  47. package/dist/postgres-types.d.ts.map +1 -0
  48. package/dist/push.d.ts +51 -0
  49. package/dist/push.d.ts.map +1 -0
  50. package/dist/recovery.d.ts +41 -0
  51. package/dist/recovery.d.ts.map +1 -0
  52. package/dist/relations.d.ts +40 -0
  53. package/dist/relations.d.ts.map +1 -0
  54. package/dist/renames.d.ts +58 -0
  55. package/dist/renames.d.ts.map +1 -0
  56. package/dist/runner.d.ts +181 -0
  57. package/dist/runner.d.ts.map +1 -0
  58. package/dist/sqlite-types.d.ts +101 -0
  59. package/dist/sqlite-types.d.ts.map +1 -0
  60. package/dist/types.d.ts +329 -0
  61. package/dist/types.d.ts.map +1 -0
  62. package/package.json +34 -23
  63. package/src/cascade-actions.ts +0 -88
  64. package/src/ddl-builder.ts +0 -415
  65. package/src/index.ts +0 -41
  66. package/src/introspector.ts +0 -684
  67. package/src/migration-runner.ts +0 -127
  68. package/src/relation-utils.ts +0 -79
  69. package/src/schema-differ.ts +0 -865
  70. package/src/schema-printer.ts +0 -259
  71. package/src/snapshot.ts +0 -141
  72. package/src/sql-utils.ts +0 -45
  73. package/src/types.ts +0 -13
package/README.md CHANGED
@@ -1,95 +1,62 @@
1
1
  # @vibeorm/migrate
2
2
 
3
- Migration, introspection, and schema diff toolkit for VibeORM. Handles DDL generation, database introspection, schema comparison, migration tracking, and `.prisma` schema printing.
3
+ > Part of **[VibeORM](https://github.com/vibeorm/vibeorm)** — a type-safe TypeScript ORM for Bun and Node. Prisma-schema or TypeScript-DSL input, a canonical schema IR, a generated client, and a dialect-aware SQL layer over PostgreSQL, PGlite, SQLite and MySQL.
4
4
 
5
- ## Installation
5
+ The migration engine: it diffs two schema IRs into dialect-neutral steps, renders those steps as per-dialect DDL, applies them with bookkeeping, and introspects a live database back into IR. It is its own package so that the CLI and tests can run schema changes without pulling in the query runtime, and so every SQL-text decision stays in one reviewable place.
6
6
 
7
7
  ```bash
8
- bun add @vibeorm/migrate
8
+ bun add @vibeorm/migrate@alpha
9
9
  ```
10
10
 
11
- ## Features
11
+ > **Pre-release.** `2.0.0-alpha.x`, published under the `alpha` dist-tag. `npm i vibeorm` still resolves to the stable v1 line.
12
12
 
13
- - **DDL generation** — schema IR to PostgreSQL DDL (enums, tables, constraints, indexes, foreign keys, M:N join tables)
14
- - **Introspection** — reverse-engineer a live PostgreSQL database into schema IR
15
- - **Schema diffing** — compute migration operations between two schema versions
16
- - **Migration runner** — track and apply migrations via a `_vibeorm_migrations` table
17
- - **Snapshot management** — serialize/deserialize schema IR as JSON for diffing
18
- - **Schema printing** — convert schema IR back to `.prisma` format
13
+ Most users never install this directly — it ships as a dependency of [`vibeorm`](https://www.npmjs.com/package/vibeorm) and of the generated client. Reach for it when you are writing an adapter or tooling against the runtime.
19
14
 
20
- ## API
15
+ ## What it does
21
16
 
22
- ### DDL
17
+ Three stages, each usable on its own.
23
18
 
24
- ```ts
25
- import { buildDDL } from "@vibeorm/migrate";
19
+ **Diff.** `diffSchemas({ from, to, dialect })` returns ordered `MigrationStep` values — `createTable`, `addColumn`, `alterColumn`, `createIndex`, `addForeignKey`, `createJoinTable`, `dropView` and the rest. Steps carry IR fragments, never SQL text, and are emitted in dependency-safe order. Both sides normalize before comparison, so `diffSchemas({ from: introspect(push(X)), to: X })` is empty: pushing twice is a no-op. There is no rename detection — a renamed table diffs as drop plus create.
26
20
 
27
- const { sql, statements } = buildDDL({ schema });
28
- // sql: full DDL wrapped in BEGIN/COMMIT
29
- // statements: individual DDL statements
30
- ```
21
+ **Render.** `renderSteps({ steps, dialect, from, to })` produces the SQL strings for postgres, sqlite (including the table-rebuild recipe for ALTERs sqlite cannot express) or mysql. `describeStep({ step })` gives the one-line human description used in CLI output and refusal messages.
31
22
 
32
- ### Introspection
23
+ **Run.** `applyMigrations` executes `MigrationFile`s in order under an advisory lock, recording each in the `_vibe_migrations` bookkeeping table with a sha256 checksum over its statements. Re-applying a migration whose SQL changed is a `VIBE_MIGRATION` error, not a silent divergence. On postgres and sqlite each migration runs inside one transaction with its bookkeeping row; on mysql, where DDL auto-commits, progress is recorded per statement and a re-run resumes from where it stopped. `migrationStatus` reports applied, pending, mismatched and mid-rollback migrations, `rollbackMigration` runs a migration's `downSql`, and `resolveMigration` repairs the bookkeeping table without running SQL.
33
24
 
34
- ```ts
35
- import { introspect } from "@vibeorm/migrate";
25
+ **Introspect.** `introspectPostgres`, `introspectSqlite` and `introspectMysql` read a live database back into `SchemaIR`; `pull` picks the right one for a dialect.
36
26
 
37
- const schema = await introspect({ executor });
38
- // Returns a Schema IR from a live database
39
- ```
27
+ ## push vs migrate
40
28
 
41
- ### Schema Diffing
29
+ `push` goes straight from the live database to your schema — introspect, diff, render, apply — with no files kept. It suits prototyping and disposable databases. `generateMigration` plus `applyMigrations` keeps named, checksummed SQL files you can review and replay, which is what shared and production databases need. Details in [docs/migrations.md](https://github.com/vibeorm/vibeorm/blob/master/docs/migrations.md).
42
30
 
43
- ```ts
44
- import { diffSchemas } from "@vibeorm/migrate";
31
+ ## Destructive steps
45
32
 
46
- const operations = diffSchemas({ previous: oldSchema, current: newSchema });
47
- // Returns DiffOperation[] with isDestructive flags
48
- ```
33
+ Every step carries a `destructive` flag: drops of tables, columns, enums and join tables, plus narrowing type changes. `push` refuses to run when any step is destructive unless you pass `acceptDataLoss: true`, and the thrown `VibeError` (`VIBE_MIGRATION`) lists each offending step by description. `generateMigration` surfaces the same information as `MigrationFile.destructive` so callers can require confirmation before applying. Constructs a dialect cannot express — scalar lists on sqlite and mysql, partial indexes on mysql, removing a value from a surviving enum — throw `VIBE_UNSUPPORTED_CAPABILITY` instead of degrading.
49
34
 
50
- ### Migration Runner
35
+ ## Usage
51
36
 
52
37
  ```ts
53
- import {
54
- ensureMigrationTable,
55
- getAppliedMigrations,
56
- applyMigration,
57
- removeMigrationRecord,
58
- markMigrationApplied,
59
- } from "@vibeorm/migrate";
60
-
61
- await ensureMigrationTable({ executor });
62
- const applied = await getAppliedMigrations({ executor });
63
- await applyMigration({ executor, migrationName: "20260101_init", sql, checksum });
64
- ```
65
-
66
- ### Snapshots
38
+ import { applyMigrations, generateMigration, push, pull } from "@vibeorm/migrate";
39
+ import { toSqlExecutor } from "@vibeorm/runtime";
67
40
 
68
- ```ts
69
- import {
70
- saveSnapshot,
71
- loadLatestSnapshot,
72
- loadSnapshot,
73
- saveJournal,
74
- loadJournal,
75
- generateTimestamp,
76
- computeChecksum,
77
- } from "@vibeorm/migrate";
78
- ```
41
+ const executor = toSqlExecutor({ adapter });
79
42
 
80
- ### Schema Printing
43
+ // Prototyping: make the database match the schema.
44
+ const result = await push({ executor, schema, acceptDataLoss: false });
45
+ console.log(result.steps, result.applied);
81
46
 
82
- ```ts
83
- import { printSchema } from "@vibeorm/migrate";
47
+ // Versioned: render a named migration from a diff, then apply it.
48
+ const migration = generateMigration({ from: previous, to: schema, name: "add_posts", dialect: "postgres" });
49
+ await applyMigrations({ executor, migrations: [migration], dialect: "postgres" });
84
50
 
85
- const prismaText = printSchema({ schema });
86
- // Returns .prisma formatted schema text
51
+ // Introspect the live database back into IR.
52
+ const live = await pull({ executor, dialect: "postgres" });
87
53
  ```
88
54
 
89
- ### Diff Operation Types
90
-
91
- 18 operation types: `createEnum`, `addEnumValue`, `removeEnumValue`, `dropEnum`, `createTable`, `dropTable`, `addColumn`, `dropColumn`, `alterColumnType`, `alterColumnNullability`, `alterColumnDefault`, `addUnique`, `dropUnique`, `addIndex`, `dropIndex`, `addForeignKey`, `dropForeignKey`, `createJoinTable`, `dropJoinTable`.
55
+ `SqlExecutor` calls must all reach the same database session — the engine issues `BEGIN`/`COMMIT`, `PRAGMA` and advisory-lock statements through it. Pool-backed executors must pin one connection for the whole operation.
92
56
 
93
- ## License
57
+ ## Links
94
58
 
95
- [MIT](../../LICENSE)
59
+ - [Documentation](https://github.com/vibeorm/vibeorm/tree/master/docs)
60
+ - [Query API](https://github.com/vibeorm/vibeorm/blob/master/docs/queries.md)
61
+ - [Repository and issues](https://github.com/vibeorm/vibeorm)
62
+ - MIT licensed
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Topological ordering for DROP TABLE statements — children (referencing
3
+ * tables) first.
4
+ *
5
+ * postgres never needs this: `DROP TABLE … CASCADE` takes inbound FK
6
+ * constraints with it. sqlite and mysql have no working cascade for inbound
7
+ * foreign keys — mysql refuses outright (errno 3730
8
+ * `ER_FK_CANNOT_DROP_PARENT`, observed live on 8.4 when a fixture switch
9
+ * dropped `Project` while the also-being-dropped `ProjectMember` still
10
+ * referenced it). ONE shared implementation, consumed by both renderers —
11
+ * the LEARNINGS rule: a structural rule is never re-implemented per consumer.
12
+ */
13
+ import type { NormalizedSchema } from "../normalize.ts";
14
+ /**
15
+ * Order the tables in `names` so that no table is dropped while another
16
+ * to-be-dropped table still references it. Tables unknown to `fromNorm` (or a
17
+ * missing `fromNorm`, or a reference cycle) fall back to the given order —
18
+ * rendering proceeds and the server reports what it cannot do (dialect
19
+ * honesty beats silently dropping statements).
20
+ */
21
+ export declare function orderDropsChildrenFirst(params: {
22
+ names: readonly string[];
23
+ fromNorm: NormalizedSchema | undefined;
24
+ }): string[];
25
+ //# sourceMappingURL=drop-order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drop-order.d.ts","sourceRoot":"","sources":["../../src/ddl/drop-order.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,gBAAgB,GAAG,SAAS,CAAC;CACxC,GAAG,MAAM,EAAE,CAwCX"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * MySQL DDL renderer: migration steps → SQL statements.
3
+ *
4
+ * Every identifier is backtick-quoted through `mysqlDialect.quoteIdent` — the
5
+ * ONE quoting decision point. Pinned choices:
6
+ *
7
+ * - String → varchar(191): utf8mb4 × 4 bytes keeps indexed String columns
8
+ * inside the 767-byte legacy InnoDB index-key limit (`@db.Text` opts out —
9
+ * TEXT columns are never indexed by us; indexing one would need a prefix
10
+ * length we do not emit).
11
+ * - autoincrement renders `AUTO_INCREMENT` (the column must be a key — the
12
+ * PRIMARY KEY clause in the same CREATE TABLE satisfies that; ALTERing one
13
+ * onto an existing column is refused, pg parity).
14
+ * - Enums are inline `ENUM('A', 'B')` column types; the standalone enum steps
15
+ * (createEnum/dropEnum/addEnumValue) render NOTHING — value changes arrive
16
+ * as alterColumn steps (value-based type keys) and render as MODIFY COLUMN
17
+ * with the widened list.
18
+ * - alterColumn type/nullability → one `MODIFY COLUMN` with the full target
19
+ * definition (mysql redefines the column wholesale); default-only changes
20
+ * use `ALTER COLUMN … SET/DROP DEFAULT`.
21
+ * - No partial indexes on mysql → VIBE_UNSUPPORTED_CAPABILITY naming the
22
+ * index. Index kinds other than btree are refused the same way.
23
+ * - `DROP INDEX … ON table` needs the table — the differ's dropIndex steps
24
+ * carry it; a step without one is a VIBE_MIGRATION error.
25
+ * - `DROP TABLE` has no cascade (mysql parses `CASCADE` and ignores it), and
26
+ * dropping a parent still referenced by another to-be-dropped table fails
27
+ * with errno 3730 `ER_FK_CANNOT_DROP_PARENT` (observed live on 8.4). FK
28
+ * drops on SURVIVING tables come from the differ's bucket order; drops of
29
+ * whole dependent groups are topologically reordered children-first here
30
+ * (the shared `orderDropsChildrenFirst`, same rule as sqlite).
31
+ */
32
+ import type { SchemaIR } from "@vibeorm/schema";
33
+ import type { MigrationStep } from "../types.ts";
34
+ /**
35
+ * Render migration steps to mysql SQL. `to` (the target schema) is required
36
+ * whenever an enum column is rendered — the inline `ENUM(…)` type needs the
37
+ * enum's values, which live on the schema, not the step. `from` enables the
38
+ * topological drop order (children before referenced parents); without it,
39
+ * drops render in step order.
40
+ */
41
+ export declare function renderMysqlSteps(params: {
42
+ steps: readonly MigrationStep[];
43
+ from?: SchemaIR;
44
+ to?: SchemaIR;
45
+ }): string[];
46
+ //# sourceMappingURL=mysql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../src/ddl/mysql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAmB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAMjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf,GAAG,MAAM,EAAE,CAyBX"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Postgres DDL renderer: migration steps → SQL statements.
3
+ *
4
+ * Every identifier is quoted through `postgresDialect.quoteIdent` — the ONE
5
+ * quoting decision point (@vibeorm/sql). Choices, each pinned by a renderer
6
+ * test:
7
+ *
8
+ * - autoincrement renders as `GENERATED BY DEFAULT AS IDENTITY` (SQL-standard,
9
+ * no sequence-name coupling; postgres ≥10 and pglite both support it) — not
10
+ * SERIAL. The introspector maps identity columns back to autoincrement.
11
+ * - `DROP TABLE ... CASCADE`: inbound FK constraints drop with the table.
12
+ * - scalar→array type changes carry the LEARNINGS `USING CASE WHEN col IS NULL
13
+ * THEN NULL ELSE ARRAY[col] END` repair clause (postgres cannot implicit-cast).
14
+ * - `ALTER TYPE ... ADD VALUE IF NOT EXISTS` appends at the end of the enum
15
+ * (no BEFORE/AFTER positioning); fine inside a transaction on postgres ≥12.
16
+ * - Unsupported columns render their `nativeType` verbatim and migrate like
17
+ * any other column.
18
+ */
19
+ import type { MigrationStep } from "../types.ts";
20
+ /** Render migration steps to postgres SQL (one string per statement, in order). */
21
+ export declare function renderPostgresSteps(params: {
22
+ steps: readonly MigrationStep[];
23
+ }): string[];
24
+ //# sourceMappingURL=postgres.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/ddl/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAYH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,mFAAmF;AACnF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,SAAS,aAAa,EAAE,CAAA;CAAE,GAAG,MAAM,EAAE,CAGzF"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Dialect dispatcher for DDL rendering — the ONE public entry point. Routes to
3
+ * the per-dialect renderers (ddl/postgres.ts, ddl/sqlite.ts, ddl/mysql.ts) so
4
+ * every SQL-text decision stays in exactly one dialect module.
5
+ */
6
+ import type { Dialect, SchemaIR } from "@vibeorm/schema";
7
+ import type { MigrationStep } from "../types.ts";
8
+ /**
9
+ * Render migration steps to SQL statements (one string per statement, in
10
+ * order) for a dialect.
11
+ *
12
+ * `from`/`to` — the schemas the steps were diffed from — are OPTIONAL for
13
+ * postgres (ignored) but needed by sqlite whenever a step compiles to the
14
+ * table-rebuild recipe (the target table shape lives on the schema, not the
15
+ * step) and by sqlite/mysql for enum columns (CHECK / inline ENUM values).
16
+ * `generateMigration` and `push` always pass them; direct callers rendering
17
+ * rebuild-free, enum-free steps may omit them.
18
+ */
19
+ export declare function renderSteps(params: {
20
+ steps: readonly MigrationStep[];
21
+ dialect: Dialect;
22
+ from?: SchemaIR;
23
+ to?: SchemaIR;
24
+ }): string[];
25
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/ddl/render.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf,GAAG,MAAM,EAAE,CAsBX"}
@@ -0,0 +1,40 @@
1
+ import type { Dialect, ModelIR, SchemaIR } from "@vibeorm/schema";
2
+ import type { MigrationStep } from "../types.ts";
3
+ /** Whether a model requires ENABLE + FORCE and a managed policy. */
4
+ export declare function isRlsProtected(params: {
5
+ model: ModelIR;
6
+ }): boolean;
7
+ /** Parent policies before through children; callers validate acyclic IR first. */
8
+ export declare function orderedRlsModels(params: {
9
+ schema: SchemaIR;
10
+ }): ModelIR[];
11
+ /** RLS declarations refuse on unsupported dialects even when the physical diff is empty. */
12
+ export declare function assertRlsDialect(params: {
13
+ schema: SchemaIR;
14
+ dialect: Dialect;
15
+ }): void;
16
+ /** Compose child-first policy drops, physical DDL, parent-first installations, then explicit removals. */
17
+ export declare function withRlsSteps(params: {
18
+ from: SchemaIR;
19
+ to: SchemaIR;
20
+ steps: readonly MigrationStep[];
21
+ }): MigrationStep[];
22
+ /** Structural step discriminator shared by all three migration renderers. */
23
+ export declare function isRlsStep(params: {
24
+ step: MigrationStep;
25
+ }): boolean;
26
+ /** Dispatch only: absence of a SQL-layer migration renderer is a loud refusal, never synthesized SQL. */
27
+ export declare function renderRlsStep(params: {
28
+ step: MigrationStep;
29
+ dialect: Dialect;
30
+ }): string[];
31
+ /** Human preview of security changes, deliberately separate from destructive data steps. */
32
+ export declare function securityChangesOf(params: {
33
+ steps: readonly MigrationStep[];
34
+ }): string[];
35
+ /** Shared push/apply/rollback refusal. Data-loss flags are intentionally not accepted here. */
36
+ export declare function assertSecurityAcknowledgement(params: {
37
+ securityChanges: readonly string[];
38
+ acceptSecurityChanges?: boolean;
39
+ }): void;
40
+ //# sourceMappingURL=rls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rls.d.ts","sourceRoot":"","sources":["../../src/ddl/rls.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAElE;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GAAG,OAAO,EAAE,CAgBxE;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAMrF;AAED,0GAA0G;AAC1G,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;CACjC,GAAG,aAAa,EAAE,CAuDlB;AAED,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GAAG,OAAO,CAElE;AAED,yGAAyG;AACzG,wBAAgB,aAAa,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,EAAE,CAmBzF;AAED,4FAA4F;AAC5F,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,SAAS,aAAa,EAAE,CAAA;CAAE,GAAG,MAAM,EAAE,CAEvF;AAED,+FAA+F;AAC/F,wBAAgB,6BAA6B,CAAC,MAAM,EAAE;IAAE,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAOnI"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * SQLite DDL renderer: migration steps → SQL statements, including THE TABLE
3
+ * REBUILD — sqlite has no ALTER COLUMN, cannot add/drop constraints, and its
4
+ * DROP COLUMN is heavily restricted, so any such step compiles to the
5
+ * documented 12-step recipe (https://sqlite.org/lang_altertable.html §7),
6
+ * emitted as an ordered, self-contained statement block:
7
+ *
8
+ * PRAGMA foreign_keys=OFF;
9
+ * BEGIN;
10
+ * CREATE TABLE "T_new" (…target shape: columns, PK, FKs, enum CHECKs…);
11
+ * INSERT INTO "T_new" (common cols) SELECT common cols FROM "T";
12
+ * DROP TABLE "T";
13
+ * ALTER TABLE "T_new" RENAME TO "T";
14
+ * CREATE …INDEX… (every index of the target table);
15
+ * PRAGMA foreign_key_check;
16
+ * COMMIT;
17
+ * PRAGMA foreign_keys=ON;
18
+ *
19
+ * Because the block manages its own transaction (PRAGMA foreign_keys is a
20
+ * no-op inside one), callers must NOT wrap scripts containing it — push and
21
+ * the runner detect this via `usesSelfManagedTransactions` (execute.ts).
22
+ * Rebuild blocks are emitted AFTER all other statements so a rebuilt table's
23
+ * new FKs can reference tables created earlier in the same script — EXCEPT a
24
+ * rebuild that removes an FK to a table this migration drops, which is hoisted
25
+ * ahead of the drops: sqlite's DROP TABLE cascades into surviving child rows
26
+ * while the FK is still installed (F27).
27
+ *
28
+ * Cheap paths are preferred wherever sqlite's plain ALTER suffices:
29
+ * `ADD COLUMN` (unless the column is required without a literal default, or
30
+ * defaults to CURRENT_TIMESTAMP / an expression — both forbidden in ADD
31
+ * COLUMN) and `DROP COLUMN` (unless the column is in the PK, any index or
32
+ * partial-index predicate, an FK on either side, or carries an enum CHECK).
33
+ *
34
+ * Column-value transfer on rebuild: dropped columns are omitted, added
35
+ * columns take their default (or NULL), type-changed columns are a PLAIN COPY
36
+ * — sqlite's flexible typing keeps the stored values as-is, so e.g. non-numeric
37
+ * TEXT survives verbatim inside an INTEGER column (documented lossy-ness:
38
+ * nothing is coerced, nothing is validated beyond the new CHECK constraints).
39
+ *
40
+ * Other pinned choices:
41
+ * - autoincrement renders `INTEGER PRIMARY KEY AUTOINCREMENT` (monotonic ids,
42
+ * Prisma parity) — plain `INTEGER PRIMARY KEY` would reuse rowids after
43
+ * deletes. The `PRIMARY KEY AUTOINCREMENT` text is also the introspection
44
+ * marker. Costs the `sqlite_sequence` bookkeeping table (skipped on pull).
45
+ * - Composite / non-autoincrement PKs render as an unnamed table-level
46
+ * `PRIMARY KEY (…)` (sqlite keeps constraint names only as noise).
47
+ * - Enums render TEXT + a NAMED column-level CHECK (see sqlite-types.ts) —
48
+ * the name (`<table>_<column>_enum_<Enum>`) is how pull recovers the enum.
49
+ * - `DROP TABLE` has no CASCADE on sqlite; table drops are topologically
50
+ * ordered (referencing tables first) using the `from` schema.
51
+ * - Index kinds other than btree/unique throw VIBE_UNSUPPORTED_CAPABILITY.
52
+ */
53
+ import type { SchemaIR } from "@vibeorm/schema";
54
+ import type { MigrationStep } from "../types.ts";
55
+ /**
56
+ * Render migration steps to sqlite SQL. `from`/`to` (the schemas that produced
57
+ * the steps) are required whenever a step triggers a table rebuild or renders
58
+ * an enum column — `generateMigration` and `push` always pass them.
59
+ */
60
+ export declare function renderSqliteSteps(params: {
61
+ steps: readonly MigrationStep[];
62
+ from?: SchemaIR;
63
+ to?: SchemaIR;
64
+ }): string[];
65
+ /**
66
+ * Tables whose sqlite DDL will DROP and re-create them (the rebuild recipe) or
67
+ * drop them outright — i.e. every table that loses its attached objects.
68
+ * SQLite's `DROP TABLE` takes the table's triggers with it, and the rebuild
69
+ * restores only the core indexes, so callers holding extension artifacts (FTS
70
+ * synchronization triggers) must re-create theirs afterwards (F31).
71
+ */
72
+ export declare function sqliteInvalidatedTables(params: {
73
+ steps: readonly MigrationStep[];
74
+ from?: SchemaIR;
75
+ }): {
76
+ rebuilt: Set<string>;
77
+ dropped: Set<string>;
78
+ };
79
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/ddl/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAGH,OAAO,KAAK,EAAmB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAajE,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,aAAa,CAAC;AAK9D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf,GAAG,MAAM,EAAE,CA0NX;AAgED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB,GAAG;IAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAUjD"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * IR-vs-IR schema differ → dialect-neutral migration steps.
3
+ *
4
+ * The contract that matters most: `diffSchemas({ from: introspect(push(X)),
5
+ * to: X })` is EMPTY — both sides normalize through normalize.ts before
6
+ * comparison, so representation differences (implicit list sides vs explicit
7
+ * M2M fields, `@unique` vs `@@unique`, app-level defaults, cascade-action
8
+ * defaults) never produce phantom steps and `db push` twice is a no-op.
9
+ *
10
+ * No rename detection (v1 parity): a renamed table/column diffs as drop +
11
+ * create. Removing a value from a surviving enum throws
12
+ * VIBE_UNSUPPORTED_CAPABILITY — postgres cannot drop enum values without a
13
+ * manual type rebuild, and silence would be dialect dishonesty.
14
+ */
15
+ import type { Dialect, SchemaIR } from "@vibeorm/schema";
16
+ import type { RenameIntent } from "./renames.ts";
17
+ import type { MigrationStep } from "./types.ts";
18
+ /**
19
+ * Diff two Schema IRs into an ordered list of dialect-neutral migration steps
20
+ * that transform the `from` database shape into the `to` shape.
21
+ *
22
+ * `dialect` (default postgres) selects the comparison rules: sqlite/mysql
23
+ * compare columns by physical type (see normalize.ts), refuse scalar-list
24
+ * columns up front (no array types — VIBE_UNSUPPORTED_CAPABILITY naming the
25
+ * model.field), skip standalone enum steps (their enums are inline: TEXT +
26
+ * CHECK on sqlite, `ENUM(…)` on mysql — value changes flow through
27
+ * `alterColumn`), and refuse enum-value REMOVAL at the column level.
28
+ */
29
+ export declare function diffSchemas(params: {
30
+ from: SchemaIR;
31
+ to: SchemaIR;
32
+ dialect?: Dialect;
33
+ /**
34
+ * EXPLICIT physical renames, written down by whoever wants them (renames.ts).
35
+ * Applied to the source schema before the diff runs, so a renamed table or
36
+ * column produces a rename step instead of the drop/create pair the differ
37
+ * would otherwise — correctly — derive. Nothing here is ever inferred.
38
+ */
39
+ renames?: readonly RenameIntent[];
40
+ }): MigrationStep[];
41
+ //# sourceMappingURL=differ.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"differ.d.ts","sourceRoot":"","sources":["../src/differ.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAU,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAOjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAqB,aAAa,EAAE,MAAM,YAAY,CAAC;AAoCnE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACnC,GAAG,aAAa,EAAE,CAmElB"}
package/dist/down.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Down-migration generation: the REVERSE diff of an up migration, with v1's
3
+ * two rollback rules baked in (LEARNINGS / issue #15):
4
+ *
5
+ * 1. RE-ADDED COLUMNS COME BACK NULLABLE. The reverse of `dropColumn` is
6
+ * `addColumn`, but the dropped column's data is gone — re-adding it
7
+ * NOT NULL would fail on any populated table. Every `addColumn` step in a
8
+ * down migration is therefore forced optional (its default, when present,
9
+ * is kept). Whole-table re-creates (`createTable`) keep their NOT NULLs:
10
+ * the re-created table is empty.
11
+ * 2. IRREVERSIBLE UP-STEPS ARE SKIPPED, NOT THROWN. An up migration that
12
+ * added an enum value has no reverse — postgres cannot drop enum values,
13
+ * and sqlite/mysql refuse narrowing an inline enum whose rows may hold the
14
+ * value. The rollback target keeps those values and the skip is reported
15
+ * in `skipped`, so "undo the last migration" still works (v1's
16
+ * `isReversible: false` behavior).
17
+ *
18
+ * Extension artifacts: object artifacts the up migration created (columns,
19
+ * indexes, shadow tables, triggers) are dropped by the down migration via
20
+ * their own `drop` SQL. `CREATE EXTENSION`-class artifacts are never dropped
21
+ * — other database objects may depend on the extension — and are reported in
22
+ * `skipped` instead.
23
+ */
24
+ import type { Dialect, SchemaIR, VibeExtension } from "@vibeorm/schema";
25
+ /** A generated down migration: reverse SQL plus what could NOT be reversed. */
26
+ export type DownMigrationFile = {
27
+ readonly name: string;
28
+ /** One SQL string per statement, in execution order. */
29
+ readonly sql: readonly string[];
30
+ /** True when any reverse step can lose data (e.g. dropping an up-added column). */
31
+ readonly destructive: boolean;
32
+ /** Separate review category: never covered by acceptDataLoss. */
33
+ readonly securityChanges?: readonly string[];
34
+ /**
35
+ * Human descriptions of up-changes this down migration cannot reverse
36
+ * (added enum values, installed postgres extensions). Empty = full reverse.
37
+ */
38
+ readonly skipped: readonly string[];
39
+ };
40
+ /**
41
+ * Generate the DOWN migration for an up migration described by the same
42
+ * `from`/`to` pair handed to `generateMigration`: `from` is the current
43
+ * database shape, `to` the schema the up migration moves to. The down
44
+ * migration transforms `to` back into `from` (modulo the skipped
45
+ * irreversibles), so callers write it alongside the up file and apply it
46
+ * newest-first to roll back.
47
+ */
48
+ export declare function generateDownMigration(params: {
49
+ from: SchemaIR;
50
+ to: SchemaIR;
51
+ name: string;
52
+ /** Rendering dialect; postgres if omitted. */
53
+ dialect?: Dialect;
54
+ /** Configured extension instances — their up-created artifacts are dropped. */
55
+ extensions?: readonly VibeExtension[];
56
+ }): DownMigrationFile;
57
+ //# sourceMappingURL=down.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"down.d.ts","sourceRoot":"","sources":["../src/down.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAU,QAAQ,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAahF,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,mFAAmF;IACnF,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC,CAAC;AAyDF;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACvC,GAAG,iBAAiB,CAwDpB"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Enum-value resolution shared by the sqlite/mysql type mappers.
3
+ *
4
+ * On dialects without named enum types (sqlite: TEXT + CHECK, mysql: inline
5
+ * `ENUM(...)`) the enum's NAME does not survive in the database — only its
6
+ * VALUES do. Column comparison keys and DDL rendering therefore resolve a
7
+ * field's enum reference to its value list; the name is presentation only.
8
+ */
9
+ import type { Dialect, EnumIR, FieldIR } from "@vibeorm/schema";
10
+ /**
11
+ * Resolve an enum name to its values. The map is keyed by db name
12
+ * (`dbName ?? name`, the normalize.ts convention); a schema-side reference by
13
+ * IR name falls back to a scan so both key styles resolve.
14
+ */
15
+ export declare function resolveEnumValues(params: {
16
+ name: string;
17
+ enums: ReadonlyMap<string, EnumIR>;
18
+ }): readonly string[] | undefined;
19
+ /** Storage transitions are detected on physical fields, after normalization. */
20
+ export declare function isEnumStringTransition(params: {
21
+ from: FieldIR;
22
+ to: FieldIR;
23
+ }): boolean;
24
+ /** Shared differ/renderer backstop for storage casts that are not yet lossless. */
25
+ export declare function assertEnumStringTransition(params: {
26
+ from: FieldIR;
27
+ to: FieldIR;
28
+ dialect: Dialect;
29
+ table: string;
30
+ column: string;
31
+ }): void;
32
+ //# sourceMappingURL=enum-values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enum-values.d.ts","sourceRoot":"","sources":["../src/enum-values.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAEhE;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAQhC;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAGtF;AAED,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAaxI"}