@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
package/src/ddl-builder.ts
DELETED
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DDL Builder — Schema IR → DDL SQL
|
|
3
|
-
*
|
|
4
|
-
* Takes a parsed Schema IR and produces the complete set of DDL statements
|
|
5
|
-
* to create the database from scratch (enums, tables, constraints, indexes, FKs).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { Schema, Model, Field, ScalarField, EnumField, RelationField, DefaultValue, Enum, EnumValue } from "@vibeorm/parser";
|
|
9
|
-
import { PRISMA_TO_PG } from "@vibeorm/parser";
|
|
10
|
-
import { buildFkActionClause } from "./cascade-actions.ts";
|
|
11
|
-
import { detectImplicitM2MPairs } from "./relation-utils.ts";
|
|
12
|
-
|
|
13
|
-
export type DDLResult = {
|
|
14
|
-
/** The full DDL SQL wrapped in BEGIN/COMMIT */
|
|
15
|
-
sql: string;
|
|
16
|
-
/** Individual DDL statements (without BEGIN/COMMIT wrapper) */
|
|
17
|
-
statements: string[];
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// ─── Helpers ──────────────────────────────────────────────────────
|
|
21
|
-
|
|
22
|
-
function q(params: { name: string }): string {
|
|
23
|
-
return `"${params.name}"`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function getDbName(params: { model: Model }): string {
|
|
27
|
-
return params.model.dbName;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function getFieldDbName(params: { field: ScalarField | EnumField }): string {
|
|
31
|
-
return params.field.dbName;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function resolveColumnType(params: { field: ScalarField }): string {
|
|
35
|
-
const { field } = params;
|
|
36
|
-
|
|
37
|
-
// Autoincrement uses SERIAL
|
|
38
|
-
if (field.default?.kind === "autoincrement") {
|
|
39
|
-
return field.prismaType === "BigInt" ? "BIGSERIAL" : "SERIAL";
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Native type override (e.g., @db.VarChar(255)) — lowercased to canonical PG form
|
|
43
|
-
if (field.nativeType) {
|
|
44
|
-
return field.nativeType.toLowerCase();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// List fields use PostgreSQL arrays
|
|
48
|
-
if (field.isList) {
|
|
49
|
-
return `${PRISMA_TO_PG[field.prismaType]}[]`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return PRISMA_TO_PG[field.prismaType];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Resolve to the base column type (no SERIAL — used for FK reference columns) */
|
|
56
|
-
function resolveBaseColumnType(params: { field: ScalarField }): string {
|
|
57
|
-
const { field } = params;
|
|
58
|
-
if (field.nativeType) return field.nativeType.toLowerCase();
|
|
59
|
-
if (field.isList) return `${PRISMA_TO_PG[field.prismaType]}[]`;
|
|
60
|
-
return PRISMA_TO_PG[field.prismaType];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function resolveDefault(params: { field: ScalarField | EnumField }): string | undefined {
|
|
64
|
-
const { field } = params;
|
|
65
|
-
const def = field.default;
|
|
66
|
-
if (!def) return undefined;
|
|
67
|
-
|
|
68
|
-
switch (def.kind) {
|
|
69
|
-
case "autoincrement":
|
|
70
|
-
// Handled by SERIAL type, no explicit DEFAULT needed
|
|
71
|
-
return undefined;
|
|
72
|
-
case "now":
|
|
73
|
-
return "CURRENT_TIMESTAMP";
|
|
74
|
-
case "uuid":
|
|
75
|
-
return "gen_random_uuid()::TEXT";
|
|
76
|
-
case "cuid":
|
|
77
|
-
// No native PG equivalent, handled at application level
|
|
78
|
-
return undefined;
|
|
79
|
-
case "nanoid":
|
|
80
|
-
// No native PG equivalent, handled at application level
|
|
81
|
-
return undefined;
|
|
82
|
-
case "ulid":
|
|
83
|
-
// No native PG equivalent, handled at application level
|
|
84
|
-
return undefined;
|
|
85
|
-
case "dbgenerated":
|
|
86
|
-
return def.value;
|
|
87
|
-
case "literal": {
|
|
88
|
-
const val = def.value;
|
|
89
|
-
if (typeof val === "string") return `'${val.replace(/'/g, "''")}'`;
|
|
90
|
-
if (typeof val === "boolean") return val ? "true" : "false";
|
|
91
|
-
return String(val);
|
|
92
|
-
}
|
|
93
|
-
default:
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ─── Enum DDL ─────────────────────────────────────────────────────
|
|
99
|
-
|
|
100
|
-
function buildEnumStatements(params: { enums: Enum[] }): string[] {
|
|
101
|
-
const statements: string[] = [];
|
|
102
|
-
|
|
103
|
-
for (const enumDef of params.enums) {
|
|
104
|
-
const typeName = enumDef.dbName ?? enumDef.name;
|
|
105
|
-
const values = enumDef.values
|
|
106
|
-
.map((v) => `'${(v.dbName ?? v.name).replace(/'/g, "''")}'`)
|
|
107
|
-
.join(", ");
|
|
108
|
-
|
|
109
|
-
// Use DO block for safe creation (no IF NOT EXISTS for CREATE TYPE)
|
|
110
|
-
statements.push(
|
|
111
|
-
`DO $$ BEGIN\n` +
|
|
112
|
-
` CREATE TYPE ${q({ name: typeName })} AS ENUM (${values});\n` +
|
|
113
|
-
`EXCEPTION WHEN duplicate_object THEN NULL;\n` +
|
|
114
|
-
`END $$;`
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return statements;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// ─── Table DDL ────────────────────────────────────────────────────
|
|
122
|
-
|
|
123
|
-
function buildTableStatement(params: { model: Model; enums: Enum[] }): string {
|
|
124
|
-
const { model, enums } = params;
|
|
125
|
-
const tableName = getDbName({ model });
|
|
126
|
-
const columns: string[] = [];
|
|
127
|
-
const enumNames = new Set(enums.map((e) => e.name));
|
|
128
|
-
|
|
129
|
-
// Collect scalar + enum fields (not relations)
|
|
130
|
-
for (const field of model.fields) {
|
|
131
|
-
if (field.kind === "relation") continue;
|
|
132
|
-
|
|
133
|
-
const colName = getFieldDbName({ field });
|
|
134
|
-
let colType: string;
|
|
135
|
-
|
|
136
|
-
if (field.kind === "enum") {
|
|
137
|
-
const enumDef = enums.find((e) => e.name === field.enumName);
|
|
138
|
-
const enumDbName = enumDef?.dbName ?? field.enumName;
|
|
139
|
-
colType = q({ name: enumDbName });
|
|
140
|
-
if (field.isList) colType += "[]";
|
|
141
|
-
} else {
|
|
142
|
-
colType = resolveColumnType({ field });
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
let colDef = ` ${q({ name: colName })} ${colType}`;
|
|
146
|
-
|
|
147
|
-
// NOT NULL (unless optional or autoincrement serial which is implicitly NOT NULL)
|
|
148
|
-
if (field.kind === "scalar" && field.isRequired && field.default?.kind !== "autoincrement") {
|
|
149
|
-
colDef += " NOT NULL";
|
|
150
|
-
} else if (field.kind === "enum" && field.isRequired) {
|
|
151
|
-
colDef += " NOT NULL";
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// DEFAULT
|
|
155
|
-
const defaultExpr = resolveDefault({ field });
|
|
156
|
-
if (defaultExpr) {
|
|
157
|
-
colDef += ` DEFAULT ${defaultExpr}`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Inline UNIQUE for single-field unique
|
|
161
|
-
if (field.isUnique) {
|
|
162
|
-
colDef += " UNIQUE";
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
columns.push(colDef);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Primary key constraint
|
|
169
|
-
const pkFields = model.primaryKey.fields
|
|
170
|
-
.map((f) => {
|
|
171
|
-
const field = model.fields.find((fld) => fld.name === f);
|
|
172
|
-
if (field && field.kind !== "relation") {
|
|
173
|
-
return q({ name: getFieldDbName({ field }) });
|
|
174
|
-
}
|
|
175
|
-
return q({ name: f });
|
|
176
|
-
})
|
|
177
|
-
.join(", ");
|
|
178
|
-
|
|
179
|
-
const pkName = `${tableName}_pkey`;
|
|
180
|
-
columns.push(` CONSTRAINT ${q({ name: pkName })} PRIMARY KEY (${pkFields})`);
|
|
181
|
-
|
|
182
|
-
return `CREATE TABLE ${q({ name: tableName })} (\n${columns.join(",\n")}\n);`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// ─── Unique Index DDL ─────────────────────────────────────────────
|
|
186
|
-
|
|
187
|
-
function buildUniqueIndexStatements(params: { model: Model }): string[] {
|
|
188
|
-
const { model } = params;
|
|
189
|
-
const statements: string[] = [];
|
|
190
|
-
const tableName = getDbName({ model });
|
|
191
|
-
|
|
192
|
-
for (const unique of model.uniqueConstraints) {
|
|
193
|
-
// Skip single-field uniques (handled inline on column)
|
|
194
|
-
if (unique.fields.length === 1) continue;
|
|
195
|
-
|
|
196
|
-
const cols = unique.fields
|
|
197
|
-
.map((f) => {
|
|
198
|
-
const field = model.fields.find((fld) => fld.name === f);
|
|
199
|
-
if (field && field.kind !== "relation") {
|
|
200
|
-
return q({ name: getFieldDbName({ field }) });
|
|
201
|
-
}
|
|
202
|
-
return q({ name: f });
|
|
203
|
-
})
|
|
204
|
-
.join(", ");
|
|
205
|
-
|
|
206
|
-
const idxName = unique.name ?? `${tableName}_${unique.fields.join("_")}_key`;
|
|
207
|
-
statements.push(
|
|
208
|
-
`CREATE UNIQUE INDEX ${q({ name: idxName })} ON ${q({ name: tableName })} (${cols});`
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return statements;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// ─── Regular Index DDL ────────────────────────────────────────────
|
|
216
|
-
|
|
217
|
-
function buildIndexStatements(params: { model: Model }): string[] {
|
|
218
|
-
const { model } = params;
|
|
219
|
-
const statements: string[] = [];
|
|
220
|
-
const tableName = getDbName({ model });
|
|
221
|
-
|
|
222
|
-
for (const idx of model.indexes) {
|
|
223
|
-
const cols = idx.fields
|
|
224
|
-
.map((f) => {
|
|
225
|
-
const field = model.fields.find((fld) => fld.name === f);
|
|
226
|
-
if (field && field.kind !== "relation") {
|
|
227
|
-
return q({ name: getFieldDbName({ field }) });
|
|
228
|
-
}
|
|
229
|
-
return q({ name: f });
|
|
230
|
-
})
|
|
231
|
-
.join(", ");
|
|
232
|
-
|
|
233
|
-
const idxName = idx.name ?? `${tableName}_${idx.fields.join("_")}_idx`;
|
|
234
|
-
statements.push(
|
|
235
|
-
`CREATE INDEX ${q({ name: idxName })} ON ${q({ name: tableName })} (${cols});`
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return statements;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// ─── Foreign Key DDL ──────────────────────────────────────────────
|
|
243
|
-
|
|
244
|
-
function buildForeignKeyStatements(params: { model: Model; allModels: Model[] }): string[] {
|
|
245
|
-
const { model, allModels } = params;
|
|
246
|
-
const statements: string[] = [];
|
|
247
|
-
const tableName = getDbName({ model });
|
|
248
|
-
|
|
249
|
-
for (const field of model.fields) {
|
|
250
|
-
if (field.kind !== "relation") continue;
|
|
251
|
-
if (!field.relation.isForeignKey) continue;
|
|
252
|
-
if (field.relation.fields.length === 0) continue;
|
|
253
|
-
|
|
254
|
-
const relatedModel = allModels.find((m) => m.name === field.relatedModel);
|
|
255
|
-
if (!relatedModel) continue;
|
|
256
|
-
|
|
257
|
-
const relatedTableName = getDbName({ model: relatedModel });
|
|
258
|
-
|
|
259
|
-
const fkCols = field.relation.fields
|
|
260
|
-
.map((f) => {
|
|
261
|
-
const fld = model.fields.find((mf) => mf.name === f);
|
|
262
|
-
if (fld && fld.kind !== "relation") return q({ name: getFieldDbName({ field: fld }) });
|
|
263
|
-
return q({ name: f });
|
|
264
|
-
})
|
|
265
|
-
.join(", ");
|
|
266
|
-
|
|
267
|
-
const refCols = field.relation.references
|
|
268
|
-
.map((f) => {
|
|
269
|
-
const fld = relatedModel.fields.find((mf) => mf.name === f);
|
|
270
|
-
if (fld && fld.kind !== "relation") return q({ name: getFieldDbName({ field: fld }) });
|
|
271
|
-
return q({ name: f });
|
|
272
|
-
})
|
|
273
|
-
.join(", ");
|
|
274
|
-
|
|
275
|
-
const constraintName = `${tableName}_${field.relation.fields.join("_")}_fkey`;
|
|
276
|
-
const actionClause = buildFkActionClause({ rel: field });
|
|
277
|
-
statements.push(
|
|
278
|
-
`ALTER TABLE ${q({ name: tableName })} ADD CONSTRAINT ${q({ name: constraintName })} ` +
|
|
279
|
-
`FOREIGN KEY (${fkCols}) REFERENCES ${q({ name: relatedTableName })} (${refCols}) ` +
|
|
280
|
-
`${actionClause};`
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return statements;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// ─── Implicit M:N Join Table ──────────────────────────────────────
|
|
288
|
-
|
|
289
|
-
type ImplicitManyToMany = {
|
|
290
|
-
modelA: string;
|
|
291
|
-
modelB: string;
|
|
292
|
-
tableA: string;
|
|
293
|
-
tableB: string;
|
|
294
|
-
pkFieldA: string;
|
|
295
|
-
pkFieldB: string;
|
|
296
|
-
pkTypeA: string;
|
|
297
|
-
pkTypeB: string;
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
function detectImplicitManyToMany(params: { schema: Schema }): ImplicitManyToMany[] {
|
|
301
|
-
const { schema } = params;
|
|
302
|
-
|
|
303
|
-
// Detection lives in the shared helper (single source of truth with the
|
|
304
|
-
// schema differ); here we only resolve the per-table DDL details (PK column
|
|
305
|
-
// names + types) for each detected pair.
|
|
306
|
-
return detectImplicitM2MPairs({ schema }).map((pair) => {
|
|
307
|
-
const modelADef = schema.models.find((m) => m.name === pair.modelA)!;
|
|
308
|
-
const modelBDef = schema.models.find((m) => m.name === pair.modelB)!;
|
|
309
|
-
|
|
310
|
-
const pkFieldA = modelADef.primaryKey.fields[0]!;
|
|
311
|
-
const pkFieldB = modelBDef.primaryKey.fields[0]!;
|
|
312
|
-
|
|
313
|
-
const pkFieldDefA = modelADef.fields.find((f) => f.name === pkFieldA);
|
|
314
|
-
const pkFieldDefB = modelBDef.fields.find((f) => f.name === pkFieldB);
|
|
315
|
-
|
|
316
|
-
const pkTypeA = pkFieldDefA && pkFieldDefA.kind === "scalar"
|
|
317
|
-
? resolveBaseColumnType({ field: pkFieldDefA })
|
|
318
|
-
: "INTEGER";
|
|
319
|
-
const pkTypeB = pkFieldDefB && pkFieldDefB.kind === "scalar"
|
|
320
|
-
? resolveBaseColumnType({ field: pkFieldDefB })
|
|
321
|
-
: "INTEGER";
|
|
322
|
-
|
|
323
|
-
return {
|
|
324
|
-
modelA: pair.modelA,
|
|
325
|
-
modelB: pair.modelB,
|
|
326
|
-
tableA: getDbName({ model: modelADef }),
|
|
327
|
-
tableB: getDbName({ model: modelBDef }),
|
|
328
|
-
pkFieldA,
|
|
329
|
-
pkFieldB,
|
|
330
|
-
pkTypeA,
|
|
331
|
-
pkTypeB,
|
|
332
|
-
};
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function buildJoinTableStatements(params: { joinTable: ImplicitManyToMany }): string[] {
|
|
337
|
-
const { joinTable } = params;
|
|
338
|
-
const tableName = `_${joinTable.modelA}To${joinTable.modelB}`;
|
|
339
|
-
const statements: string[] = [];
|
|
340
|
-
|
|
341
|
-
// Create join table
|
|
342
|
-
statements.push(
|
|
343
|
-
`CREATE TABLE ${q({ name: tableName })} (\n` +
|
|
344
|
-
` "A" ${joinTable.pkTypeA} NOT NULL,\n` +
|
|
345
|
-
` "B" ${joinTable.pkTypeB} NOT NULL\n` +
|
|
346
|
-
`);`
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
// Composite unique index on (A, B)
|
|
350
|
-
statements.push(
|
|
351
|
-
`CREATE UNIQUE INDEX ${q({ name: `${tableName}_AB_unique` })} ON ${q({ name: tableName })} ("A", "B");`
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
// Index on B for reverse lookups
|
|
355
|
-
statements.push(
|
|
356
|
-
`CREATE INDEX ${q({ name: `${tableName}_B_index` })} ON ${q({ name: tableName })} ("B");`
|
|
357
|
-
);
|
|
358
|
-
|
|
359
|
-
// Foreign key A → modelA
|
|
360
|
-
statements.push(
|
|
361
|
-
`ALTER TABLE ${q({ name: tableName })} ADD CONSTRAINT ${q({ name: `${tableName}_A_fkey` })} ` +
|
|
362
|
-
`FOREIGN KEY ("A") REFERENCES ${q({ name: joinTable.tableA })} ("${joinTable.pkFieldA}") ` +
|
|
363
|
-
`ON DELETE CASCADE ON UPDATE CASCADE;`
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
// Foreign key B → modelB
|
|
367
|
-
statements.push(
|
|
368
|
-
`ALTER TABLE ${q({ name: tableName })} ADD CONSTRAINT ${q({ name: `${tableName}_B_fkey` })} ` +
|
|
369
|
-
`FOREIGN KEY ("B") REFERENCES ${q({ name: joinTable.tableB })} ("${joinTable.pkFieldB}") ` +
|
|
370
|
-
`ON DELETE CASCADE ON UPDATE CASCADE;`
|
|
371
|
-
);
|
|
372
|
-
|
|
373
|
-
return statements;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// ─── Main Entry Point ─────────────────────────────────────────────
|
|
377
|
-
|
|
378
|
-
export function buildDDL(params: { schema: Schema }): DDLResult {
|
|
379
|
-
const { schema } = params;
|
|
380
|
-
const statements: string[] = [];
|
|
381
|
-
|
|
382
|
-
// 1. Enums
|
|
383
|
-
statements.push(...buildEnumStatements({ enums: schema.enums }));
|
|
384
|
-
|
|
385
|
-
// 2. Tables
|
|
386
|
-
for (const model of schema.models) {
|
|
387
|
-
statements.push(buildTableStatement({ model, enums: schema.enums }));
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// 3. Unique indexes (multi-column only; single-column handled inline)
|
|
391
|
-
for (const model of schema.models) {
|
|
392
|
-
statements.push(...buildUniqueIndexStatements({ model }));
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// 4. Regular indexes
|
|
396
|
-
for (const model of schema.models) {
|
|
397
|
-
statements.push(...buildIndexStatements({ model }));
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// 5. Foreign keys (after all tables exist)
|
|
401
|
-
for (const model of schema.models) {
|
|
402
|
-
statements.push(...buildForeignKeyStatements({ model, allModels: schema.models }));
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// 6. Implicit M:N join tables
|
|
406
|
-
const joinTables = detectImplicitManyToMany({ schema });
|
|
407
|
-
for (const jt of joinTables) {
|
|
408
|
-
statements.push(...buildJoinTableStatements({ joinTable: jt }));
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Wrap in transaction
|
|
412
|
-
const sql = `BEGIN;\n\n${statements.join("\n\n")}\n\nCOMMIT;`;
|
|
413
|
-
|
|
414
|
-
return { sql, statements };
|
|
415
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @vibeorm/migrate
|
|
3
|
-
*
|
|
4
|
-
* Core migration modules: DDL generation, database introspection,
|
|
5
|
-
* schema diffing, snapshot management, migration execution, and schema printing.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export { buildDDL } from "./ddl-builder.ts";
|
|
9
|
-
export type { DDLResult } from "./ddl-builder.ts";
|
|
10
|
-
|
|
11
|
-
export { introspect } from "./introspector.ts";
|
|
12
|
-
|
|
13
|
-
export { diffSchemas } from "./schema-differ.ts";
|
|
14
|
-
export type { DiffOperation, DiffOperationType } from "./schema-differ.ts";
|
|
15
|
-
|
|
16
|
-
export {
|
|
17
|
-
saveSnapshot,
|
|
18
|
-
loadLatestSnapshot,
|
|
19
|
-
loadSnapshot,
|
|
20
|
-
saveJournal,
|
|
21
|
-
loadJournal,
|
|
22
|
-
generateTimestamp,
|
|
23
|
-
computeChecksum,
|
|
24
|
-
} from "./snapshot.ts";
|
|
25
|
-
export type { JournalEntry, Journal } from "./snapshot.ts";
|
|
26
|
-
|
|
27
|
-
export {
|
|
28
|
-
ensureMigrationTable,
|
|
29
|
-
getAppliedMigrations,
|
|
30
|
-
applyMigration,
|
|
31
|
-
removeMigrationRecord,
|
|
32
|
-
markMigrationApplied,
|
|
33
|
-
rollbackMigration,
|
|
34
|
-
} from "./migration-runner.ts";
|
|
35
|
-
export type { AppliedMigration } from "./migration-runner.ts";
|
|
36
|
-
|
|
37
|
-
export { printSchema } from "./schema-printer.ts";
|
|
38
|
-
|
|
39
|
-
export { splitSqlStatements } from "./sql-utils.ts";
|
|
40
|
-
|
|
41
|
-
export type { SqlExecutor } from "./types.ts";
|