@vibeorm/migrate 1.3.1 → 2.0.0-alpha.2

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 (61) 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/sqlite.d.ts +62 -0
  11. package/dist/ddl/sqlite.d.ts.map +1 -0
  12. package/dist/differ.d.ts +33 -0
  13. package/dist/differ.d.ts.map +1 -0
  14. package/dist/down.d.ts +55 -0
  15. package/dist/down.d.ts.map +1 -0
  16. package/dist/enum-values.d.ts +19 -0
  17. package/dist/enum-values.d.ts.map +1 -0
  18. package/dist/execute.d.ts +41 -0
  19. package/dist/execute.d.ts.map +1 -0
  20. package/dist/extensions.d.ts +74 -0
  21. package/dist/extensions.d.ts.map +1 -0
  22. package/dist/index.d.ts +26 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +3858 -0
  25. package/dist/index.js.map +31 -0
  26. package/dist/introspect/mysql.d.ts +31 -0
  27. package/dist/introspect/mysql.d.ts.map +1 -0
  28. package/dist/introspect/postgres.d.ts +25 -0
  29. package/dist/introspect/postgres.d.ts.map +1 -0
  30. package/dist/introspect/shared.d.ts +76 -0
  31. package/dist/introspect/shared.d.ts.map +1 -0
  32. package/dist/introspect/sqlite.d.ts +53 -0
  33. package/dist/introspect/sqlite.d.ts.map +1 -0
  34. package/dist/mysql-types.d.ts +66 -0
  35. package/dist/mysql-types.d.ts.map +1 -0
  36. package/dist/normalize.d.ts +115 -0
  37. package/dist/normalize.d.ts.map +1 -0
  38. package/dist/postgres-types.d.ts +95 -0
  39. package/dist/postgres-types.d.ts.map +1 -0
  40. package/dist/push.d.ts +45 -0
  41. package/dist/push.d.ts.map +1 -0
  42. package/dist/relations.d.ts +40 -0
  43. package/dist/relations.d.ts.map +1 -0
  44. package/dist/runner.d.ts +120 -0
  45. package/dist/runner.d.ts.map +1 -0
  46. package/dist/sqlite-types.d.ts +87 -0
  47. package/dist/sqlite-types.d.ts.map +1 -0
  48. package/dist/types.d.ts +217 -0
  49. package/dist/types.d.ts.map +1 -0
  50. package/package.json +33 -23
  51. package/src/cascade-actions.ts +0 -88
  52. package/src/ddl-builder.ts +0 -415
  53. package/src/index.ts +0 -41
  54. package/src/introspector.ts +0 -684
  55. package/src/migration-runner.ts +0 -127
  56. package/src/relation-utils.ts +0 -79
  57. package/src/schema-differ.ts +0 -865
  58. package/src/schema-printer.ts +0 -259
  59. package/src/snapshot.ts +0 -141
  60. package/src/sql-utils.ts +0 -45
  61. package/src/types.ts +0 -13
@@ -0,0 +1,31 @@
1
+ /**
2
+ * MySQL introspection: live database → SchemaIR, from
3
+ * `information_schema.{tables,views,columns,statistics,key_column_usage,
4
+ * referential_constraints}` (scoped to `DATABASE()`).
5
+ *
6
+ * Representation mirrors introspect/postgres.ts exactly (shared.ts carries
7
+ * the one relation/join-table rule). MySQL-specific decisions:
8
+ *
9
+ * - `COLUMN_TYPE` is the reverse-mapping source: `tinyint(1)` → Boolean (the
10
+ * marker), `varchar(191)` → String, `enum('A','B')` → an enum whose values
11
+ * are parsed from the type and whose NAME is synthesized as
12
+ * `<Table>_<column>` — mysql enums are nameless, so the schema-declared
13
+ * name is unrecoverable. The differ compares enum columns by VALUE LIST
14
+ * (normalize.ts), so the synthetic names never produce phantom steps.
15
+ * - `EXTRA = auto_increment` → `@default(autoincrement())`;
16
+ * `DEFAULT_GENERATED` expressions → `dbgenerated` (CURRENT_TIMESTAMP(…) →
17
+ * `now` first).
18
+ * - Non-canonical column types keep `COLUMN_TYPE` in `nativeType`; unknown
19
+ * types become `isUnsupported` fields (F3: keep, don't drop).
20
+ * - Indexes named like an FK constraint on the same table are SKIPPED: mysql
21
+ * auto-creates them for FKs without a covering index, and surfacing them
22
+ * would churn round-trips with phantom index steps.
23
+ * - The `_vibe_migrations` bookkeeping table is skipped.
24
+ */
25
+ import type { SchemaIR } from "@vibeorm/schema";
26
+ import type { SqlExecutor } from "../types.ts";
27
+ /** Introspect a live mysql database (current schema) into SchemaIR. */
28
+ export declare function introspectMysql(params: {
29
+ executor: SqlExecutor;
30
+ }): Promise<SchemaIR>;
31
+ //# sourceMappingURL=mysql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../src/introspect/mysql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EASV,QAAQ,EACT,MAAM,iBAAiB,CAAC;AASzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA0G/C,uEAAuE;AACvE,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0K1F"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Postgres / PGlite introspection: live database → SchemaIR.
3
+ *
4
+ * Representation contract (the differ's round-trip depends on it, LEARNINGS):
5
+ *
6
+ * - Type mapping is the REVERSE of dialect-matrix §2; `nativeType` is set only
7
+ * when the column's type is NOT the canonical mapping for its scalar.
8
+ * - Identity columns and `nextval(...)` defaults → `@default(autoincrement())`.
9
+ * - Enum ARRAY columns (`data_type = 'ARRAY'`, `udt_name = '_EnumName'`) are
10
+ * enum lists — never `String[]`.
11
+ * - Unknown column types (tsvector, geometry, …) become `isUnsupported` fields
12
+ * with the raw type in `nativeType` — kept, not dropped.
13
+ * - FK/join-table representation comes from introspect/shared.ts — the ONE
14
+ * structural rule every dialect introspector uses.
15
+ * - Views (`isView: true`) are returned with their columns only.
16
+ * - The `_vibe_migrations` bookkeeping table is skipped.
17
+ * - Hardcoded to the `public` schema (v1 parity).
18
+ */
19
+ import type { SchemaIR } from "@vibeorm/schema";
20
+ import type { SqlExecutor } from "../types.ts";
21
+ /** Introspect a live postgres/pglite database (public schema) into SchemaIR. */
22
+ export declare function introspectPostgres(params: {
23
+ executor: SqlExecutor;
24
+ }): Promise<SchemaIR>;
25
+ //# sourceMappingURL=postgres.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/introspect/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAQV,QAAQ,EACT,MAAM,iBAAiB,CAAC;AASzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAsK/C,gFAAgF;AAChF,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwK7F"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Introspection helpers shared by ALL dialect introspectors — the join-table
3
+ * detection rule and the relation/naming conventions live HERE, once
4
+ * (LEARNINGS: implicit-M2M conventions must never be re-implemented per
5
+ * consumer). Every dialect introspector produces the same representation:
6
+ *
7
+ * - FKs become an UNNAMED `manyToOne` on the owning model plus an UNNAMED
8
+ * `oneToMany` list side on the target — EXCEPT when the model pair carries
9
+ * more than one unnamed relation pair (mutual FKs, twin FKs to one target,
10
+ * an FK alongside a default-named join table): those FK pairs get a
11
+ * synthesized `@relation` name so `db pull` output re-parses (the parser
12
+ * rejects the unnamed shape as `ambiguous-relation`) and implicit-M2M
13
+ * detection keeps its name keys unique.
14
+ * - A table matching the implicit join-table shape (name `_X`, exactly columns
15
+ * "A" and "B", two single-column FKs, a unique index on (A, B)) is NOT a
16
+ * model: it becomes two unnamed `oneToMany` list sides on the joined models,
17
+ * carrying `relationName` when the physical name differs from the default
18
+ * `_AToB`.
19
+ */
20
+ import type { CascadeAction, ModelIR } from "@vibeorm/schema";
21
+ /** A grouped foreign key (composite-capable), dialect-independent. */
22
+ export type FkGroup = {
23
+ readonly constraintName: string;
24
+ readonly table: string;
25
+ readonly columns: readonly string[];
26
+ readonly refTable: string;
27
+ readonly refColumns: readonly string[];
28
+ readonly onDelete: CascadeAction;
29
+ readonly onUpdate: CascadeAction;
30
+ /** Every FK column NOT NULL (drives isOptional on the relation). */
31
+ readonly required: boolean;
32
+ };
33
+ /** An introspected index in the shape join-table detection needs. */
34
+ export type IntrospectedIndex = {
35
+ readonly table: string;
36
+ readonly name: string;
37
+ readonly unique: boolean;
38
+ readonly kind: string;
39
+ readonly columns: readonly {
40
+ readonly name: string;
41
+ readonly desc: boolean;
42
+ }[];
43
+ readonly where?: string;
44
+ };
45
+ /** A detected physical join table. */
46
+ export type JoinTableInfo = {
47
+ readonly tableA: string;
48
+ readonly tableB: string;
49
+ readonly relationName?: string;
50
+ };
51
+ /**
52
+ * Detect whether a physical table is an implicit many-to-many join table:
53
+ * name starts with `_`, columns are exactly "A"/"B", two single-column FKs
54
+ * cover them, and a unique index spans (A, B). Purely structural — never
55
+ * reads relation types (round-trip stability, LEARNINGS).
56
+ */
57
+ export declare function detectJoinTable(params: {
58
+ table: string;
59
+ columnNames: readonly string[];
60
+ fkGroups: readonly FkGroup[];
61
+ indexes: readonly IntrospectedIndex[];
62
+ }): JoinTableInfo | null;
63
+ /** Add manyToOne + inverse oneToMany relation pairs for every FK group. */
64
+ export declare function addFkRelations(params: {
65
+ models: ModelIR[];
66
+ fkGroups: readonly FkGroup[];
67
+ joinTables: ReadonlyMap<string, JoinTableInfo>;
68
+ }): void;
69
+ /** Add the two unnamed oneToMany list sides for every detected join table. */
70
+ export declare function addJoinTableRelations(params: {
71
+ models: ModelIR[];
72
+ joinTables: ReadonlyMap<string, JoinTableInfo>;
73
+ }): void;
74
+ /** Lower-case the first character (relation field names from table names). */
75
+ export declare function lowerFirst(value: string): string;
76
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/introspect/shared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAc,MAAM,iBAAiB,CAAC;AAI1E,sEAAsE;AACtE,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,sCAAsC;AACtC,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAIF;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACvC,GAAG,aAAa,GAAG,IAAI,CA8BvB;AAID,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,MAAM,EAAE;IACrC,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAChD,GAAG,IAAI,CA+CP;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAChD,GAAG,IAAI,CAuCP;AAmGD,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * SQLite introspection: live database → SchemaIR, from `sqlite_master` (table
3
+ * and index SQL text) plus `PRAGMA table_info / index_list / index_xinfo /
4
+ * foreign_key_list`.
5
+ *
6
+ * THE TEXT-TYPE AMBIGUITY, stated honestly: sqlite stores only the four
7
+ * storage classes we emit, so the reverse mapping is TEXT→String,
8
+ * INTEGER→Int, REAL→Float, BLOB→Bytes — ALWAYS. A DateTime/Decimal/Json
9
+ * column pulls back as String and a Boolean/BigInt as Int; there is no marker
10
+ * to recover the richer type. The differ compensates by comparing columns by
11
+ * storage class on sqlite (normalize.ts), so round-trips stay diff-clean.
12
+ * Two markers DO survive:
13
+ * - enums: our named CHECK constraints (`<table>_<column>_enum_<Enum>`)
14
+ * recover both the enum's name and values;
15
+ * - autoincrement: the literal `PRIMARY KEY AUTOINCREMENT` text on a
16
+ * single-column INTEGER PK.
17
+ * Any other declared type (VARCHAR(30), DATETIME, … from a foreign database)
18
+ * becomes an `isUnsupported` field with the raw type preserved in
19
+ * `nativeType` (F3: keep, don't drop).
20
+ *
21
+ * Representation mirrors introspect/postgres.ts exactly (shared.ts is the one
22
+ * relation/join-table rule): unnamed relation pairs, structural join-table
23
+ * detection, single-column uniques folded into `isUnique`, composite PKs on
24
+ * `model.primaryKey` (PK columns are never optional), views with columns
25
+ * only. Skipped: the `_vibe_migrations` table, `sqlite_*` internals
26
+ * (sqlite_sequence comes with AUTOINCREMENT), and expression indexes
27
+ * (index_xinfo reports no column name for them).
28
+ *
29
+ * Every row-returning query passes `values: []` — through `toSqlExecutor` a
30
+ * value-less text runs on the adapter's multi-statement batch path, which
31
+ * returns no rows (adapter-sqlite contract).
32
+ */
33
+ import type { SchemaIR } from "@vibeorm/schema";
34
+ import type { SqlExecutor } from "../types.ts";
35
+ /** Introspect a live sqlite database into SchemaIR. */
36
+ export declare function introspectSqlite(params: {
37
+ executor: SqlExecutor;
38
+ }): Promise<SchemaIR>;
39
+ /**
40
+ * Recover an expression index's column list from its `sqlite_master.sql`
41
+ * (SQLITE-2). Only shapes WE emit parse — plain quoted columns and
42
+ * `CAST("col" AS REAL)`, each with an optional direction; a trailing partial
43
+ * WHERE clause is fine (it sits OUTSIDE the column list — closure item 2).
44
+ * Anything else (other expressions, COLLATE clauses) returns null and the
45
+ * caller skips the index as before.
46
+ */
47
+ export declare function parseIndexColumnsFromSql(params: {
48
+ sql: string | null;
49
+ }): {
50
+ name: string;
51
+ desc: boolean;
52
+ }[] | null;
53
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/introspect/sqlite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAQV,QAAQ,EACT,MAAM,iBAAiB,CAAC;AAUzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAyB/C,uDAAuD;AACvD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAwH3F;AAwGD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EAAE,GAAG,IAAI,CAqD3C"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * MySQL column-type mapping (IR → column type and back), native-type
3
+ * normalization, comparison keys and default rendering.
4
+ *
5
+ * dialect-matrix §2: String → varchar(191) by default (191 because
6
+ * utf8mb4 × 4 bytes keeps the column inside the 767-byte legacy index-key
7
+ * limit; `@db.Text` opts into TEXT, which we never index), Boolean →
8
+ * tinyint(1) (THE marker that lets introspection recover Boolean),
9
+ * Int → int, BigInt → bigint, Float → double, Decimal → decimal(65,30),
10
+ * DateTime → datetime(3), Json → json, Bytes → longblob, enums → inline
11
+ * `ENUM('A', 'B')` — nameless in the database, so comparison keys use the
12
+ * VALUE LIST (like sqlite) and a pulled schema synthesizes
13
+ * `<Table>_<column>` enum names. Scalar lists are unsupported.
14
+ */
15
+ import type { EnumIR, FieldIR, ScalarType } from "@vibeorm/schema";
16
+ /** IR scalar → canonical mysql column type (information_schema spelling). */
17
+ export declare const SCALAR_TO_MYSQL: Readonly<Record<ScalarType, string>>;
18
+ /** Render-time backstop for scalar-list columns (the differ refuses earlier). */
19
+ export declare function refuseMysqlScalarList(params: {
20
+ field: FieldIR;
21
+ }): never;
22
+ /**
23
+ * Normalize a mysql type string for comparison: lowercase, whitespace
24
+ * stripped, base de-aliased, integer display widths dropped (mysql 8.0.19+
25
+ * omits them; older catalogs report `int(11)`) — EXCEPT `tinyint(1)`, which is
26
+ * the Boolean marker and must survive normalization.
27
+ */
28
+ export declare function normalizeMysqlType(params: {
29
+ raw: string;
30
+ }): string;
31
+ /**
32
+ * Comparison key for a column on mysql. Enum columns key on their VALUE LIST
33
+ * (order-sensitive — mysql ENUM order defines sort order); a native type equal
34
+ * to the scalar's canonical mapping is dropped so `String` and
35
+ * `String @db.VarChar(191)` compare equal.
36
+ */
37
+ export declare function mysqlColumnTypeKey(params: {
38
+ field: FieldIR;
39
+ enums: ReadonlyMap<string, EnumIR>;
40
+ }): string;
41
+ /**
42
+ * Default fingerprint on mysql: postgres rules verbatim — tinyint(1) lets the
43
+ * introspector map `DEFAULT '1'` back to a boolean literal, so booleans keep
44
+ * their `bool:` fingerprint (unlike sqlite).
45
+ */
46
+ export declare function mysqlDefaultFingerprint(params: {
47
+ field: FieldIR;
48
+ }): string | null;
49
+ /** Render the mysql column type for a field (no NOT NULL, no default). */
50
+ export declare function mysqlColumnTypeSql(params: {
51
+ field: FieldIR;
52
+ enums: ReadonlyMap<string, EnumIR>;
53
+ }): string;
54
+ /**
55
+ * Render the `DEFAULT` expression for a field on mysql, or `undefined` when
56
+ * the column takes none. `now()` renders `CURRENT_TIMESTAMP(3)` (precision
57
+ * must match the datetime(3) column); booleans store as 1/0.
58
+ */
59
+ export declare function mysqlDefaultSql(params: {
60
+ field: FieldIR;
61
+ }): string | undefined;
62
+ /** Parse an information_schema `COLUMN_TYPE` of form `enum('A','B')`. */
63
+ export declare function parseMysqlEnumColumnType(params: {
64
+ columnType: string;
65
+ }): string[] | null;
66
+ //# sourceMappingURL=mysql-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql-types.d.ts","sourceRoot":"","sources":["../src/mysql-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMnE,6EAA6E;AAC7E,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAUhE,CAAC;AAEF,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAMvE;AAaD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CASlE;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,GAAG,MAAM,CAgBT;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,IAAI,CAEjF;AAID,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,GAAG,MAAM,CA8BT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CAuB9E;AAID,yEAAyE;AACzE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAQxF"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Schema-IR normalization for diffing.
3
+ *
4
+ * Both sides of a diff (a parsed/DSL schema and an introspected one) collapse
5
+ * into the same normalized shape, so representation differences never produce
6
+ * phantom steps. The normalizations, each pinned by a differ test:
7
+ *
8
+ * - `@unique` on a field and a `kind: "unique"` model index are the same
9
+ * unique index (the database stores both identically).
10
+ * - A single `@id` field and a single-field `@@id` are the same primary key.
11
+ * - Field names resolve to db names (`@map` / `@@map`) everywhere.
12
+ * - FK cascade actions resolve Prisma's defaults (required → Restrict,
13
+ * optional → SetNull, onUpdate → Cascade) before comparing.
14
+ * - App-level defaults (uuid/cuid/nanoid/ulid) compare as "no database
15
+ * default"; `isUpdatedAt` is app-level and ignored entirely.
16
+ * - Implicit M2M pairs collapse to their join table regardless of whether the
17
+ * sides are parser `manyToMany` fields or introspected unnamed `oneToMany`
18
+ * list sides (structural detection, LEARNINGS).
19
+ * - Views never join the table diff (migrate does not manage view DDL in this
20
+ * wave) but ARE collected by db name, so the differ can emit DROP VIEW for
21
+ * views the target schema no longer declares (board #26).
22
+ */
23
+ import type { CascadeAction, Dialect, EnumIR, FieldIR, ModelIR, SchemaIR } from "@vibeorm/schema";
24
+ import type { IndexColumn, JoinTableEnd } from "./types.ts";
25
+ /** A column ready for comparison. */
26
+ export type NormalizedColumn = {
27
+ /** Column db name. */
28
+ readonly name: string;
29
+ readonly field: FieldIR;
30
+ readonly typeKey: string;
31
+ readonly nullable: boolean;
32
+ readonly defaultKey: string | null;
33
+ };
34
+ /** An index (unique or not) ready for comparison. `key` ignores the name. */
35
+ export type NormalizedIndex = {
36
+ readonly key: string;
37
+ readonly name: string;
38
+ readonly kind: string;
39
+ readonly unique: boolean;
40
+ readonly columns: readonly IndexColumn[];
41
+ readonly where?: string;
42
+ };
43
+ /** A foreign key ready for comparison. `key` excludes the cascade actions. */
44
+ export type NormalizedFk = {
45
+ readonly key: string;
46
+ readonly constraintName: string;
47
+ readonly columns: readonly string[];
48
+ readonly refTable: string;
49
+ readonly refColumns: readonly string[];
50
+ readonly onDelete: CascadeAction;
51
+ readonly onUpdate: CascadeAction;
52
+ };
53
+ /** A model collapsed to its database shape. */
54
+ export type NormalizedTable = {
55
+ readonly model: ModelIR;
56
+ /** Table db name. */
57
+ readonly table: string;
58
+ readonly columns: ReadonlyMap<string, NormalizedColumn>;
59
+ /** Primary-key column db names (empty = none). */
60
+ readonly pk: readonly string[];
61
+ readonly indexes: ReadonlyMap<string, NormalizedIndex>;
62
+ readonly fks: ReadonlyMap<string, NormalizedFk>;
63
+ };
64
+ /** An implicit M2M join table collapsed to its database shape. */
65
+ export type NormalizedJoinTable = {
66
+ readonly table: string;
67
+ readonly a: JoinTableEnd;
68
+ readonly b: JoinTableEnd;
69
+ };
70
+ /** A whole schema, ready for diffing. */
71
+ export type NormalizedSchema = {
72
+ readonly tables: ReadonlyMap<string, NormalizedTable>;
73
+ readonly joinTables: ReadonlyMap<string, NormalizedJoinTable>;
74
+ /** Enums keyed by db name. */
75
+ readonly enums: ReadonlyMap<string, EnumIR>;
76
+ /** View models keyed by db name — present for drop detection only (#26). */
77
+ readonly views: ReadonlyMap<string, ModelIR>;
78
+ };
79
+ /**
80
+ * Collapse a SchemaIR into its comparable database shape. `dialect` (default
81
+ * postgres) selects the column comparison keys: on sqlite columns compare by
82
+ * STORAGE CLASS (TEXT/INTEGER/REAL/BLOB — the TEXT-ambiguity rule), on mysql
83
+ * by mysql column type; enum columns on both compare by VALUE LIST, never by
84
+ * name (the database does not store one).
85
+ */
86
+ export declare function normalizeSchema(params: {
87
+ schema: SchemaIR;
88
+ dialect?: Dialect;
89
+ }): NormalizedSchema;
90
+ /** Dialect-routed column type comparison key. */
91
+ export declare function columnTypeKeyFor(params: {
92
+ field: FieldIR;
93
+ dialect: Dialect;
94
+ enums: ReadonlyMap<string, EnumIR>;
95
+ }): string;
96
+ /** Dialect-routed default fingerprint. */
97
+ export declare function defaultFingerprintFor(params: {
98
+ field: FieldIR;
99
+ dialect: Dialect;
100
+ }): string | null;
101
+ /**
102
+ * Partial-index predicates are compared loosely: postgres re-serializes the
103
+ * expression (extra parentheses, quoted identifiers), so the comparison key
104
+ * strips quoting, parentheses, whitespace and case. `::type` casts postgres
105
+ * adds during normalization (e.g. `'x'::text`) are stripped too.
106
+ */
107
+ export declare function normalizeWhere(params: {
108
+ where: string | undefined;
109
+ }): string;
110
+ /** Resolve a field name to its db column name on a model. */
111
+ export declare function fieldDbName(params: {
112
+ model: ModelIR;
113
+ name: string;
114
+ }): string;
115
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Postgres column-type mapping (IR → pg text and back), native-type
3
+ * normalization, default-value fingerprints/rendering, and cascade-action
4
+ * resolution.
5
+ *
6
+ * The forward mapping implements dialect-matrix §2; the fingerprint helpers are
7
+ * the differ's normalization layer — both the differ and the introspector lean
8
+ * on them so `diff(introspect(push(X)), X) = []` holds by construction.
9
+ */
10
+ import type { CascadeAction, FieldIR, ScalarType } from "@vibeorm/schema";
11
+ /** IR scalar → canonical postgres column type. */
12
+ export declare const SCALAR_TO_PG: Readonly<Record<ScalarType, string>>;
13
+ /**
14
+ * Normalize a native type string for comparison: lowercase, whitespace
15
+ * stripped, base name de-aliased. `"VarChar(191)"` and `"character varying(191)"`
16
+ * both become `"varchar(191)"`.
17
+ */
18
+ export declare function normalizeNativeType(params: {
19
+ raw: string;
20
+ }): string;
21
+ /**
22
+ * Comparison key for a column's type. Two columns with equal keys need no
23
+ * ALTER. A native type equal to the scalar's canonical mapping is dropped, so
24
+ * `DateTime` and `DateTime @db.Timestamptz` compare equal — and so do the
25
+ * mapping's `CANONICAL_EQUIVALENTS` (`@db.Timestamptz(6)`, the same type
26
+ * spelled explicitly, and `@db.Timestamptz(3)`, what the 2.0 alphas created).
27
+ * Any other precision stays distinct and still diffs.
28
+ */
29
+ export declare function columnTypeKey(params: {
30
+ field: FieldIR;
31
+ }): string;
32
+ /**
33
+ * True when a type change from `from` to `to` is the safe scalar→array repair
34
+ * (same base type, scalar becomes a one-element array via a `USING` clause —
35
+ * the LEARNINGS enum-array lesson). Every other type change is treated as
36
+ * potentially narrowing, i.e. destructive.
37
+ */
38
+ export declare function isScalarToArrayChange(params: {
39
+ from: FieldIR;
40
+ to: FieldIR;
41
+ }): boolean;
42
+ /** Render the postgres column type for a field (no identity, no default). */
43
+ export declare function columnTypeSql(params: {
44
+ field: FieldIR;
45
+ }): string;
46
+ /** True when the field renders as an identity column (autoincrement Int/BigInt). */
47
+ export declare function isIdentityColumn(params: {
48
+ field: FieldIR;
49
+ }): boolean;
50
+ /**
51
+ * Normalized fingerprint of a column default for diffing, or `null` when the
52
+ * database carries no default. App-level id generators (uuid / cuid / nanoid /
53
+ * ulid) fingerprint as `null`: the runtime injects them client-side, so the
54
+ * database honestly has no default — this is what keeps introspection
55
+ * round-trips stable for `@default(uuid())` columns.
56
+ */
57
+ export declare function defaultFingerprint(params: {
58
+ field: FieldIR;
59
+ }): string | null;
60
+ /**
61
+ * Render the `DEFAULT` expression for a field, or `undefined` when the column
62
+ * takes no database default (no default declared, app-level generators,
63
+ * autoincrement — which renders as identity instead).
64
+ */
65
+ export declare function defaultSql(params: {
66
+ field: FieldIR;
67
+ }): string | undefined;
68
+ /** True for scalars whose defaults postgres reports back as numeric literals. */
69
+ export declare function isNumericScalar(params: {
70
+ type: ScalarType;
71
+ }): boolean;
72
+ /**
73
+ * `pg_constraint.confdeltype` / `confupdtype` single-char codes → IR actions
74
+ * (used by the introspector).
75
+ */
76
+ export declare const PG_CHAR_TO_CASCADE_ACTION: Readonly<Record<string, CascadeAction>>;
77
+ /** CascadeAction → SQL clause body. */
78
+ export declare const CASCADE_ACTION_SQL: Readonly<Record<CascadeAction, string>>;
79
+ /**
80
+ * Effective onDelete with Prisma's postgres defaults applied (LEARNINGS):
81
+ * required FK → Restrict, optional FK → SetNull.
82
+ */
83
+ export declare function resolveOnDelete(params: {
84
+ explicit: CascadeAction | undefined;
85
+ required: boolean;
86
+ }): CascadeAction;
87
+ /** Effective onUpdate — Prisma's postgres default is Cascade. */
88
+ export declare function resolveOnUpdate(params: {
89
+ explicit: CascadeAction | undefined;
90
+ }): CascadeAction;
91
+ /** Quote a postgres string literal (single quotes, doubled embedded quotes). */
92
+ export declare function quotePgString(params: {
93
+ value: string;
94
+ }): string;
95
+ //# sourceMappingURL=postgres-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-types.d.ts","sourceRoot":"","sources":["../src/postgres-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAK1E,kDAAkD;AAClD,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAU7D,CAAC;AA+CF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAMnE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAmBhE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAMrF;AAID,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAsBhE;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAQpE;AAID;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,IAAI,CAmC5E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CAuBzE;AAKD,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAAG,OAAO,CAGrE;AAID;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAM7E,CAAC;AAEF,uCAAuC;AACvC,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAMtE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAC;IACpC,QAAQ,EAAE,OAAO,CAAC;CACnB,GAAG,aAAa,CAGhB;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,aAAa,GAAG,SAAS,CAAA;CAAE,GAAG,aAAa,CAE9F;AAID,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE/D"}
package/dist/push.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `db push` / `db pull` at library level (the CLI wires these).
3
+ *
4
+ * push = introspect → diff → render → apply, routed per dialect
5
+ * (`providerToDialect(schema.datasource.provider)`):
6
+ *
7
+ * - postgres/pglite: all statements in ONE transaction (transactional DDL).
8
+ * - sqlite: transactional too, UNLESS the script contains a table-rebuild
9
+ * block — those manage their own `PRAGMA foreign_keys` + BEGIN/COMMIT
10
+ * sequence (ddl/sqlite.ts) and run unwrapped; `PRAGMA foreign_key_check`
11
+ * violations become VIBE_MIGRATION errors (execute.ts).
12
+ * - mysql: DDL auto-commits — statements run sequentially, no wrap.
13
+ *
14
+ * Destructive steps are REFUSED unless `acceptDataLoss` is set — the error
15
+ * lists every destructive step, loudly (dialect honesty: never silently drop
16
+ * data).
17
+ */
18
+ import type { Dialect, SchemaIR, VibeExtension } from "@vibeorm/schema";
19
+ import type { PushResult, SqlExecutor } from "./types.ts";
20
+ /**
21
+ * Make the live database match `schema`. Returns the steps that were applied
22
+ * and the SQL executed (both empty when the database already matched — pushing
23
+ * twice is a no-op by the differ's round-trip property).
24
+ *
25
+ * @throws VibeError VIBE_SCHEMA_INVALID when the target schema fails validation.
26
+ * @throws VibeError VIBE_UNSUPPORTED_CAPABILITY for dialect-impossible constructs
27
+ * (scalar lists on sqlite/mysql, partial indexes on mysql, enum-value removal).
28
+ * @throws VibeError VIBE_MIGRATION when destructive steps exist and `acceptDataLoss` is not set.
29
+ */
30
+ export declare function push(params: {
31
+ executor: SqlExecutor;
32
+ schema: SchemaIR;
33
+ acceptDataLoss?: boolean;
34
+ /** Configured extension instances — their artifacts apply with the diff. */
35
+ extensions?: readonly VibeExtension[];
36
+ }): Promise<PushResult>;
37
+ /**
38
+ * Introspect the live database into SchemaIR (the CLI's `db pull`).
39
+ * `dialect` selects the introspector — postgres if omitted (back-compat).
40
+ */
41
+ export declare function pull(params: {
42
+ executor: SqlExecutor;
43
+ dialect?: Dialect;
44
+ }): Promise<SchemaIR>;
45
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAoBxE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;;;;;GASG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE;IACjC,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,QAAQ,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACvC,GAAG,OAAO,CAAC,UAAU,CAAC,CA8EtB;AAED;;;GAGG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE;IACjC,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAEpB"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Implicit many-to-many detection on Schema IR.
3
+ *
4
+ * ONE structural rule, kept in lock-step with the parser's
5
+ * `findImplicitM2MCounterpart` (see LEARNINGS): two relation 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,EAAuB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAErE,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,CAwCtF"}