@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,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,EAUV,QAAQ,EACT,MAAM,iBAAiB,CAAC;AASzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAyH/C,uEAAuE;AACvE,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6M1F"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SchemaIR } from "@vibeorm/schema";
|
|
2
|
+
import type { SqlExecutor } from "../types.ts";
|
|
3
|
+
/** Catalog attributes are intentionally not represented as authored policy declarations. */
|
|
4
|
+
export type PostgresRlsPolicy = {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly command: string;
|
|
7
|
+
readonly permissive: boolean;
|
|
8
|
+
readonly roles: string;
|
|
9
|
+
readonly using: string | null;
|
|
10
|
+
readonly withCheck: string | null;
|
|
11
|
+
readonly comment: string | null;
|
|
12
|
+
};
|
|
13
|
+
/** Every public ordinary/partitioned table, including tables with flags but no policies. */
|
|
14
|
+
export type PostgresRlsTable = {
|
|
15
|
+
readonly table: string;
|
|
16
|
+
readonly enabled: boolean;
|
|
17
|
+
readonly force: boolean;
|
|
18
|
+
readonly policies: readonly PostgresRlsPolicy[];
|
|
19
|
+
};
|
|
20
|
+
/** Read both clauses and all structural attributes; callers pin search_path and transaction. */
|
|
21
|
+
export declare function readPostgresRls(params: {
|
|
22
|
+
executor: SqlExecutor;
|
|
23
|
+
}): Promise<PostgresRlsTable[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Compare against a trusted deployed declaration, not a policy name/comment/hash.
|
|
26
|
+
* An explicit previousSchema/status check requires every declared policy to exist.
|
|
27
|
+
* Push without a previous schema permits installing protection on an ordinary table.
|
|
28
|
+
* Must run inside the caller's transaction. Locks survive the rolled-back probe.
|
|
29
|
+
*/
|
|
30
|
+
export declare function verifyPostgresRls(params: {
|
|
31
|
+
executor: SqlExecutor;
|
|
32
|
+
schema?: SchemaIR;
|
|
33
|
+
requireInstalled?: boolean;
|
|
34
|
+
}): Promise<ReadonlySet<string>>;
|
|
35
|
+
/** Attach only verified declarations, mapped to the physical names introspection uses. No SQL parsing. */
|
|
36
|
+
export declare function projectPostgresRls(params: {
|
|
37
|
+
physical: SchemaIR;
|
|
38
|
+
declared?: SchemaIR;
|
|
39
|
+
matched: ReadonlySet<string>;
|
|
40
|
+
}): SchemaIR;
|
|
41
|
+
//# sourceMappingURL=postgres-rls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-rls.d.ts","sourceRoot":"","sources":["../../src/introspect/postgres-rls.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAwB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,4FAA4F;AAC5F,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CAAC;AACF,4FAA4F;AAC5F,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACjD,CAAC;AAeF,gGAAgG;AAChG,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAepG;AAUD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAyD/B;AAED,0GAA0G;AAC1G,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,GAAG,QAAQ,CAsB9H"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SchemaIR } from "@vibeorm/schema";
|
|
2
|
+
import type { SqlExecutor } from "../types.ts";
|
|
3
|
+
/** Introspect a live postgres/pglite database (public schema) into SchemaIR. */
|
|
4
|
+
export declare function introspectPostgres(params: {
|
|
5
|
+
executor: SqlExecutor;
|
|
6
|
+
/** Trusted policy declaration; missing this refuses lossy RLS introspection/export. */
|
|
7
|
+
rlsSchema?: SchemaIR;
|
|
8
|
+
/** The caller owns BEGIN/COMMIT, keeping probe locks through subsequent migration DDL. */
|
|
9
|
+
inTransaction?: boolean;
|
|
10
|
+
requireInstalledRls?: boolean;
|
|
11
|
+
}): Promise<SchemaIR>;
|
|
12
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/introspect/postgres.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EASV,QAAQ,EACT,MAAM,iBAAiB,CAAC;AASzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA0L/C,gFAAgF;AAChF,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,QAAQ,EAAE,WAAW,CAAC;IACtB,uFAAuF;IACvF,SAAS,CAAC,EAAE,QAAQ,CAAC;IACrB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuBpB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
/** `UNIQUE NULLS NOT DISTINCT` (postgres 15+). */
|
|
45
|
+
readonly nullsNotDistinct?: boolean;
|
|
46
|
+
};
|
|
47
|
+
/** A detected physical join table. */
|
|
48
|
+
export type JoinTableInfo = {
|
|
49
|
+
readonly tableA: string;
|
|
50
|
+
readonly tableB: string;
|
|
51
|
+
readonly relationName?: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Detect whether a physical table is an implicit many-to-many join table:
|
|
55
|
+
* name starts with `_`, columns are exactly "A"/"B", two single-column FKs
|
|
56
|
+
* cover them, and a unique index spans (A, B). Purely structural — never
|
|
57
|
+
* reads relation types (round-trip stability, LEARNINGS).
|
|
58
|
+
*/
|
|
59
|
+
export declare function detectJoinTable(params: {
|
|
60
|
+
table: string;
|
|
61
|
+
columnNames: readonly string[];
|
|
62
|
+
fkGroups: readonly FkGroup[];
|
|
63
|
+
indexes: readonly IntrospectedIndex[];
|
|
64
|
+
}): JoinTableInfo | null;
|
|
65
|
+
/** Add manyToOne + inverse oneToMany relation pairs for every FK group. */
|
|
66
|
+
export declare function addFkRelations(params: {
|
|
67
|
+
models: ModelIR[];
|
|
68
|
+
fkGroups: readonly FkGroup[];
|
|
69
|
+
joinTables: ReadonlyMap<string, JoinTableInfo>;
|
|
70
|
+
}): void;
|
|
71
|
+
/** Add the two unnamed oneToMany list sides for every detected join table. */
|
|
72
|
+
export declare function addJoinTableRelations(params: {
|
|
73
|
+
models: ModelIR[];
|
|
74
|
+
joinTables: ReadonlyMap<string, JoinTableInfo>;
|
|
75
|
+
}): void;
|
|
76
|
+
/** Lower-case the first character (relation field names from table names). */
|
|
77
|
+
export declare function lowerFirst(value: string): string;
|
|
78
|
+
//# 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;IACxB,kDAAkD;IAClD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACrC,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,CAuI3F;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,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bookkeeping table's name, alone in a leaf module.
|
|
3
|
+
*
|
|
4
|
+
* It used to live in runner.ts, which the three introspectors import while
|
|
5
|
+
* runner.ts is itself importing THEM — a cycle whose outcome depended on which
|
|
6
|
+
* module the process happened to evaluate first, and which surfaced as
|
|
7
|
+
* "Cannot access 'MIGRATIONS_TABLE_NAME' before initialization" as soon as the
|
|
8
|
+
* import order shifted. A leaf module has no such ordering: it can be
|
|
9
|
+
* evaluated before anything that needs it.
|
|
10
|
+
*
|
|
11
|
+
* runner.ts re-exports the name, so the public surface is unchanged.
|
|
12
|
+
*/
|
|
13
|
+
/** Name of the migrations bookkeeping table (skipped by introspection). */
|
|
14
|
+
export declare const MIGRATIONS_TABLE_NAME: string;
|
|
15
|
+
//# sourceMappingURL=journal-table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal-table.d.ts","sourceRoot":"","sources":["../src/journal-table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,EAAE,MAA2B,CAAC"}
|
|
@@ -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;AAIH,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,143 @@
|
|
|
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
|
+
/** `UNIQUE NULLS NOT DISTINCT` (postgres 15+). */
|
|
43
|
+
readonly nullsNotDistinct?: boolean;
|
|
44
|
+
};
|
|
45
|
+
/** A foreign key ready for comparison. `key` excludes the cascade actions. */
|
|
46
|
+
export type NormalizedFk = {
|
|
47
|
+
readonly key: string;
|
|
48
|
+
readonly constraintName: string;
|
|
49
|
+
readonly columns: readonly string[];
|
|
50
|
+
readonly refTable: string;
|
|
51
|
+
readonly refColumns: readonly string[];
|
|
52
|
+
readonly onDelete: CascadeAction;
|
|
53
|
+
readonly onUpdate: CascadeAction;
|
|
54
|
+
};
|
|
55
|
+
/** A model collapsed to its database shape. */
|
|
56
|
+
export type NormalizedTable = {
|
|
57
|
+
readonly model: ModelIR;
|
|
58
|
+
/** Table db name. */
|
|
59
|
+
readonly table: string;
|
|
60
|
+
readonly columns: ReadonlyMap<string, NormalizedColumn>;
|
|
61
|
+
/** Primary-key column db names (empty = none). */
|
|
62
|
+
readonly pk: readonly string[];
|
|
63
|
+
readonly indexes: ReadonlyMap<string, NormalizedIndex>;
|
|
64
|
+
readonly fks: ReadonlyMap<string, NormalizedFk>;
|
|
65
|
+
/**
|
|
66
|
+
* CHECK constraints, name → raw expression. NAME-keyed in the diff —
|
|
67
|
+
* engines re-serialize expressions on introspection, so text is never
|
|
68
|
+
* compared (an expression change = rename the check).
|
|
69
|
+
*/
|
|
70
|
+
readonly checks: ReadonlyMap<string, string>;
|
|
71
|
+
};
|
|
72
|
+
/** An implicit M2M join table collapsed to its database shape. */
|
|
73
|
+
export type NormalizedJoinTable = {
|
|
74
|
+
readonly table: string;
|
|
75
|
+
readonly a: JoinTableEnd;
|
|
76
|
+
readonly b: JoinTableEnd;
|
|
77
|
+
};
|
|
78
|
+
/** A whole schema, ready for diffing. */
|
|
79
|
+
export type NormalizedSchema = {
|
|
80
|
+
readonly tables: ReadonlyMap<string, NormalizedTable>;
|
|
81
|
+
readonly joinTables: ReadonlyMap<string, NormalizedJoinTable>;
|
|
82
|
+
/** Enums keyed by db name. */
|
|
83
|
+
readonly enums: ReadonlyMap<string, EnumIR>;
|
|
84
|
+
/** View models keyed by db name — present for drop detection only (#26). */
|
|
85
|
+
readonly views: ReadonlyMap<string, ModelIR>;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Collapse a SchemaIR into its comparable database shape. `dialect` (default
|
|
89
|
+
* postgres) selects the column comparison keys: on sqlite columns compare by
|
|
90
|
+
* STORAGE CLASS (TEXT/INTEGER/REAL/BLOB — the TEXT-ambiguity rule), on mysql
|
|
91
|
+
* by mysql column type; enum columns on both compare by VALUE LIST, never by
|
|
92
|
+
* name (the database does not store one).
|
|
93
|
+
*/
|
|
94
|
+
export declare function normalizeSchema(params: {
|
|
95
|
+
schema: SchemaIR;
|
|
96
|
+
dialect?: Dialect;
|
|
97
|
+
}): NormalizedSchema;
|
|
98
|
+
/** Refuse string storage that cannot preserve every enum label verbatim. */
|
|
99
|
+
export declare function assertStringEnumLabelsFit(params: {
|
|
100
|
+
field: FieldIR;
|
|
101
|
+
values: readonly string[];
|
|
102
|
+
dialect: Dialect;
|
|
103
|
+
table: string;
|
|
104
|
+
}): void;
|
|
105
|
+
/** Dialect-routed column type comparison key. */
|
|
106
|
+
export declare function columnTypeKeyFor(params: {
|
|
107
|
+
field: FieldIR;
|
|
108
|
+
dialect: Dialect;
|
|
109
|
+
enums: ReadonlyMap<string, EnumIR>;
|
|
110
|
+
}): string;
|
|
111
|
+
/** Dialect-routed default fingerprint. */
|
|
112
|
+
export declare function defaultFingerprintFor(params: {
|
|
113
|
+
field: FieldIR;
|
|
114
|
+
dialect: Dialect;
|
|
115
|
+
}): string | null;
|
|
116
|
+
/**
|
|
117
|
+
* Comparison key for a partial-index predicate.
|
|
118
|
+
*
|
|
119
|
+
* The engines re-serialize these expressions on introspection (postgres adds
|
|
120
|
+
* parentheses around every operand, quotes identifiers and appends `::type`
|
|
121
|
+
* casts), so the key must absorb THAT and nothing else. Erasing quotes,
|
|
122
|
+
* parentheses and case wholesale (what this did before, F29) made
|
|
123
|
+
* `'ACTIVE'` equal to `'active'` and `(a OR b) AND c` equal to
|
|
124
|
+
* `a OR (b AND c)` — two different indexes comparing equal is a migration
|
|
125
|
+
* step that never gets generated.
|
|
126
|
+
*
|
|
127
|
+
* So: string literals keep their bytes; identifiers and keywords fold to lower
|
|
128
|
+
* case and lose their quoting; `::type` casts and REDUNDANT parentheses are
|
|
129
|
+
* dropped by parsing the boolean structure and re-emitting it with the minimum
|
|
130
|
+
* parenthesization, which keeps genuine grouping differences visible.
|
|
131
|
+
*
|
|
132
|
+
* A predicate this parser cannot read falls back to its token sequence — never
|
|
133
|
+
* to the old lossy key.
|
|
134
|
+
*/
|
|
135
|
+
export declare function normalizeWhere(params: {
|
|
136
|
+
where: string | undefined;
|
|
137
|
+
}): string;
|
|
138
|
+
/** Resolve a field name to its db column name on a model. */
|
|
139
|
+
export declare function fieldDbName(params: {
|
|
140
|
+
model: ModelIR;
|
|
141
|
+
name: string;
|
|
142
|
+
}): string;
|
|
143
|
+
//# 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,CAsC5E;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,51 @@
|
|
|
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
|
+
/** Separate authorization for removing or potentially widening native policies. */
|
|
35
|
+
acceptSecurityChanges?: boolean;
|
|
36
|
+
/** Deployed declaration when changing a policy or its referenced columns. */
|
|
37
|
+
previousSchema?: SchemaIR;
|
|
38
|
+
/** Return planned SQL and security changes without applying or requiring acknowledgements. */
|
|
39
|
+
dryRun?: boolean;
|
|
40
|
+
/** Configured extension instances — their artifacts apply with the diff. */
|
|
41
|
+
extensions?: readonly VibeExtension[];
|
|
42
|
+
}): Promise<PushResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Introspect the live database into SchemaIR (the CLI's `db pull`).
|
|
45
|
+
* `dialect` selects the introspector — postgres if omitted (back-compat).
|
|
46
|
+
*/
|
|
47
|
+
export declare function pull(params: {
|
|
48
|
+
executor: SqlExecutor;
|
|
49
|
+
dialect?: Dialect;
|
|
50
|
+
}): Promise<SchemaIR>;
|
|
51
|
+
//# 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,EAAqB,QAAQ,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAuB3F,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,mFAAmF;IACnF,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,6EAA6E;IAC7E,cAAc,CAAC,EAAE,QAAQ,CAAC;IAC1B,8FAA8F;IAC9F,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACvC,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBtB;AAiJD;;;GAGG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE;IACjC,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAGpB"}
|