zenstack-kit 0.1.4 → 0.1.7

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 (36) hide show
  1. package/README.md +18 -6
  2. package/dist/cli/app.d.ts.map +1 -1
  3. package/dist/cli/app.js +7 -1
  4. package/dist/cli/commands.d.ts +2 -0
  5. package/dist/cli/commands.d.ts.map +1 -1
  6. package/dist/cli/commands.js +97 -6
  7. package/dist/cli/prompts.d.ts.map +1 -1
  8. package/dist/cli/prompts.js +1 -3
  9. package/dist/config/loader.d.ts +1 -1
  10. package/dist/config/loader.d.ts.map +1 -1
  11. package/dist/config/loader.js +11 -9
  12. package/dist/migrations/prisma/apply.d.ts +54 -0
  13. package/dist/migrations/prisma/apply.d.ts.map +1 -0
  14. package/dist/migrations/prisma/apply.js +384 -0
  15. package/dist/migrations/prisma/create.d.ts +63 -0
  16. package/dist/migrations/prisma/create.d.ts.map +1 -0
  17. package/dist/migrations/prisma/create.js +119 -0
  18. package/dist/migrations/prisma/diff.d.ts +104 -0
  19. package/dist/migrations/prisma/diff.d.ts.map +1 -0
  20. package/dist/migrations/prisma/diff.js +442 -0
  21. package/dist/migrations/prisma/log.d.ts +31 -0
  22. package/dist/migrations/prisma/log.d.ts.map +1 -0
  23. package/dist/migrations/prisma/log.js +101 -0
  24. package/dist/migrations/prisma/rename.d.ts +23 -0
  25. package/dist/migrations/prisma/rename.d.ts.map +1 -0
  26. package/dist/migrations/prisma/rename.js +57 -0
  27. package/dist/migrations/prisma/snapshot.d.ts +32 -0
  28. package/dist/migrations/prisma/snapshot.d.ts.map +1 -0
  29. package/dist/migrations/prisma/snapshot.js +65 -0
  30. package/dist/migrations/prisma.d.ts +5 -202
  31. package/dist/migrations/prisma.d.ts.map +1 -1
  32. package/dist/migrations/prisma.js +5 -1168
  33. package/dist/schema/pull.d.ts +2 -0
  34. package/dist/schema/pull.d.ts.map +1 -1
  35. package/dist/schema/pull.js +102 -4
  36. package/package.json +1 -1
@@ -0,0 +1,442 @@
1
+ import { compileCreateTable, compileDropTable, compileAddColumn, compileDropColumn, compileRenameTable, compileRenameColumn, compileCreateIndex, compileDropIndex, compileAddUniqueConstraint, compileDropConstraint, compileAddForeignKeyConstraint, compileAddPrimaryKeyConstraint, compileAlterColumn, } from "../../sql/compiler.js";
2
+ function diffTableChanges(previousModel, currentModel, tableName) {
3
+ const addedFields = [];
4
+ const removedFields = [];
5
+ const alteredFields = [];
6
+ const addedUniqueConstraints = [];
7
+ const removedUniqueConstraints = [];
8
+ const addedIndexes = [];
9
+ const removedIndexes = [];
10
+ const addedForeignKeys = [];
11
+ const removedForeignKeys = [];
12
+ const primaryKeyChanges = [];
13
+ const previousFields = new Map(previousModel.columns.map((f) => [f.name, f]));
14
+ const currentFields = new Map(currentModel.columns.map((f) => [f.name, f]));
15
+ for (const [columnName, column] of currentFields.entries()) {
16
+ if (!previousFields.has(columnName)) {
17
+ addedFields.push({ tableName, column });
18
+ }
19
+ }
20
+ for (const [columnName, column] of previousFields.entries()) {
21
+ if (!currentFields.has(columnName)) {
22
+ removedFields.push({ tableName, column });
23
+ }
24
+ }
25
+ for (const [columnName, currentColumn] of currentFields.entries()) {
26
+ const previousColumn = previousFields.get(columnName);
27
+ if (!previousColumn)
28
+ continue;
29
+ if (previousColumn.type !== currentColumn.type ||
30
+ previousColumn.notNull !== currentColumn.notNull ||
31
+ previousColumn.default !== currentColumn.default) {
32
+ alteredFields.push({
33
+ tableName,
34
+ columnName,
35
+ previous: previousColumn,
36
+ current: currentColumn,
37
+ });
38
+ }
39
+ }
40
+ const prevUnique = new Map(previousModel.uniqueConstraints.map((c) => [c.name, c]));
41
+ const currUnique = new Map(currentModel.uniqueConstraints.map((c) => [c.name, c]));
42
+ for (const [name, constraint] of currUnique.entries()) {
43
+ if (!prevUnique.has(name)) {
44
+ addedUniqueConstraints.push({ tableName, constraint });
45
+ }
46
+ }
47
+ for (const [name, constraint] of prevUnique.entries()) {
48
+ if (!currUnique.has(name)) {
49
+ removedUniqueConstraints.push({ tableName, constraint });
50
+ }
51
+ }
52
+ const prevIndexes = new Map(previousModel.indexes.map((i) => [i.name, i]));
53
+ const currIndexes = new Map(currentModel.indexes.map((i) => [i.name, i]));
54
+ for (const [name, index] of currIndexes.entries()) {
55
+ if (!prevIndexes.has(name)) {
56
+ addedIndexes.push({ tableName, index });
57
+ }
58
+ }
59
+ for (const [name, index] of prevIndexes.entries()) {
60
+ if (!currIndexes.has(name)) {
61
+ removedIndexes.push({ tableName, index });
62
+ }
63
+ }
64
+ const prevFks = new Map(previousModel.foreignKeys.map((f) => [f.name, f]));
65
+ const currFks = new Map(currentModel.foreignKeys.map((f) => [f.name, f]));
66
+ for (const [name, fk] of currFks.entries()) {
67
+ if (!prevFks.has(name)) {
68
+ addedForeignKeys.push({ tableName, foreignKey: fk });
69
+ }
70
+ }
71
+ for (const [name, fk] of prevFks.entries()) {
72
+ if (!currFks.has(name)) {
73
+ removedForeignKeys.push({ tableName, foreignKey: fk });
74
+ }
75
+ }
76
+ const prevPk = previousModel.primaryKey;
77
+ const currPk = currentModel.primaryKey;
78
+ const pkEqual = (prevPk?.name ?? "") === (currPk?.name ?? "") &&
79
+ JSON.stringify(prevPk?.columns ?? []) === JSON.stringify(currPk?.columns ?? []);
80
+ if (!pkEqual) {
81
+ primaryKeyChanges.push({
82
+ tableName,
83
+ previous: prevPk,
84
+ current: currPk,
85
+ });
86
+ }
87
+ return {
88
+ addedFields,
89
+ removedFields,
90
+ alteredFields,
91
+ addedUniqueConstraints,
92
+ removedUniqueConstraints,
93
+ addedIndexes,
94
+ removedIndexes,
95
+ addedForeignKeys,
96
+ removedForeignKeys,
97
+ primaryKeyChanges,
98
+ };
99
+ }
100
+ export function diffSchemas(previous, current) {
101
+ const previousModels = new Map();
102
+ const currentModels = new Map();
103
+ previous?.tables.forEach((model) => previousModels.set(model.name, model));
104
+ current.tables.forEach((model) => currentModels.set(model.name, model));
105
+ const addedModels = [];
106
+ const removedModels = [];
107
+ for (const [tableName, model] of currentModels.entries()) {
108
+ if (!previousModels.has(tableName)) {
109
+ addedModels.push(model);
110
+ }
111
+ }
112
+ for (const [tableName, model] of previousModels.entries()) {
113
+ if (!currentModels.has(tableName)) {
114
+ removedModels.push(model);
115
+ }
116
+ }
117
+ const addedFields = [];
118
+ const removedFields = [];
119
+ const alteredFields = [];
120
+ const addedUniqueConstraints = [];
121
+ const removedUniqueConstraints = [];
122
+ const addedIndexes = [];
123
+ const removedIndexes = [];
124
+ const addedForeignKeys = [];
125
+ const removedForeignKeys = [];
126
+ const primaryKeyChanges = [];
127
+ for (const [tableName, currentModel] of currentModels.entries()) {
128
+ const previousModel = previousModels.get(tableName);
129
+ if (!previousModel)
130
+ continue;
131
+ const modelDiff = diffTableChanges(previousModel, currentModel, tableName);
132
+ addedFields.push(...modelDiff.addedFields);
133
+ removedFields.push(...modelDiff.removedFields);
134
+ alteredFields.push(...modelDiff.alteredFields);
135
+ addedUniqueConstraints.push(...modelDiff.addedUniqueConstraints);
136
+ removedUniqueConstraints.push(...modelDiff.removedUniqueConstraints);
137
+ addedIndexes.push(...modelDiff.addedIndexes);
138
+ removedIndexes.push(...modelDiff.removedIndexes);
139
+ addedForeignKeys.push(...modelDiff.addedForeignKeys);
140
+ removedForeignKeys.push(...modelDiff.removedForeignKeys);
141
+ primaryKeyChanges.push(...modelDiff.primaryKeyChanges);
142
+ }
143
+ return {
144
+ addedModels,
145
+ removedModels,
146
+ addedFields,
147
+ removedFields,
148
+ alteredFields,
149
+ addedUniqueConstraints,
150
+ removedUniqueConstraints,
151
+ addedIndexes,
152
+ removedIndexes,
153
+ addedForeignKeys,
154
+ removedForeignKeys,
155
+ primaryKeyChanges,
156
+ renamedTables: [],
157
+ renamedColumns: [],
158
+ };
159
+ }
160
+ function columnsSignature(columns) {
161
+ return columns.join("|");
162
+ }
163
+ function consumeSignature(map, signature) {
164
+ const count = map.get(signature) ?? 0;
165
+ if (count > 0) {
166
+ map.set(signature, count - 1);
167
+ return true;
168
+ }
169
+ return false;
170
+ }
171
+ function buildSignatureCount(items, getSignature) {
172
+ const counts = new Map();
173
+ for (const item of items) {
174
+ const signature = getSignature(item);
175
+ counts.set(signature, (counts.get(signature) ?? 0) + 1);
176
+ }
177
+ return counts;
178
+ }
179
+ function columnsEqual(a, b) {
180
+ return JSON.stringify(a ?? []) === JSON.stringify(b ?? []);
181
+ }
182
+ function filterRenamedConstraintChanges(previousModel, currentModel, modelDiff) {
183
+ const prevUnique = buildSignatureCount(previousModel.uniqueConstraints, (c) => columnsSignature(c.columns));
184
+ const currUnique = buildSignatureCount(currentModel.uniqueConstraints, (c) => columnsSignature(c.columns));
185
+ const prevIndexes = buildSignatureCount(previousModel.indexes, (i) => columnsSignature(i.columns));
186
+ const currIndexes = buildSignatureCount(currentModel.indexes, (i) => columnsSignature(i.columns));
187
+ const prevFks = buildSignatureCount(previousModel.foreignKeys, (f) => `${columnsSignature(f.columns)}->${f.referencedTable}:${columnsSignature(f.referencedColumns)}`);
188
+ const currFks = buildSignatureCount(currentModel.foreignKeys, (f) => `${columnsSignature(f.columns)}->${f.referencedTable}:${columnsSignature(f.referencedColumns)}`);
189
+ const addedUniqueConstraints = modelDiff.addedUniqueConstraints.filter(({ constraint }) => !consumeSignature(prevUnique, columnsSignature(constraint.columns)));
190
+ const removedUniqueConstraints = modelDiff.removedUniqueConstraints.filter(({ constraint }) => !consumeSignature(currUnique, columnsSignature(constraint.columns)));
191
+ const addedIndexes = modelDiff.addedIndexes.filter(({ index }) => !consumeSignature(prevIndexes, columnsSignature(index.columns)));
192
+ const removedIndexes = modelDiff.removedIndexes.filter(({ index }) => !consumeSignature(currIndexes, columnsSignature(index.columns)));
193
+ const addedForeignKeys = modelDiff.addedForeignKeys.filter(({ foreignKey }) => !consumeSignature(prevFks, `${columnsSignature(foreignKey.columns)}->${foreignKey.referencedTable}:${columnsSignature(foreignKey.referencedColumns)}`));
194
+ const removedForeignKeys = modelDiff.removedForeignKeys.filter(({ foreignKey }) => !consumeSignature(currFks, `${columnsSignature(foreignKey.columns)}->${foreignKey.referencedTable}:${columnsSignature(foreignKey.referencedColumns)}`));
195
+ let primaryKeyChanges = modelDiff.primaryKeyChanges;
196
+ if (previousModel.primaryKey && currentModel.primaryKey) {
197
+ if (columnsEqual(previousModel.primaryKey.columns, currentModel.primaryKey.columns)) {
198
+ primaryKeyChanges = [];
199
+ }
200
+ }
201
+ return {
202
+ ...modelDiff,
203
+ addedUniqueConstraints,
204
+ removedUniqueConstraints,
205
+ addedIndexes,
206
+ removedIndexes,
207
+ addedForeignKeys,
208
+ removedForeignKeys,
209
+ primaryKeyChanges,
210
+ };
211
+ }
212
+ export function applyRenameMappings(diff, renameTables = [], renameColumns = []) {
213
+ const removedModels = [...diff.removedModels];
214
+ const addedModels = [...diff.addedModels];
215
+ const removedFields = [...diff.removedFields];
216
+ const addedFields = [...diff.addedFields];
217
+ const alteredFields = [...diff.alteredFields];
218
+ const addedUniqueConstraints = [...diff.addedUniqueConstraints];
219
+ const removedUniqueConstraints = [...diff.removedUniqueConstraints];
220
+ const addedIndexes = [...diff.addedIndexes];
221
+ const removedIndexes = [...diff.removedIndexes];
222
+ const addedForeignKeys = [...diff.addedForeignKeys];
223
+ const removedForeignKeys = [...diff.removedForeignKeys];
224
+ const primaryKeyChanges = [...diff.primaryKeyChanges];
225
+ const renamedTables = [];
226
+ const renamedColumns = [];
227
+ const renamedTableMap = new Map();
228
+ renameTables.forEach((mapping) => {
229
+ const fromIndex = removedModels.findIndex((model) => model.name === mapping.from);
230
+ const toIndex = addedModels.findIndex((model) => model.name === mapping.to);
231
+ if (fromIndex === -1 || toIndex === -1) {
232
+ return;
233
+ }
234
+ const previousModel = removedModels[fromIndex];
235
+ const currentModel = addedModels[toIndex];
236
+ removedModels.splice(fromIndex, 1);
237
+ addedModels.splice(toIndex, 1);
238
+ renamedTables.push({ from: mapping.from, to: mapping.to });
239
+ renamedTableMap.set(mapping.from, mapping.to);
240
+ const modelDiff = filterRenamedConstraintChanges(previousModel, currentModel, diffTableChanges(previousModel, currentModel, mapping.to));
241
+ addedFields.push(...modelDiff.addedFields);
242
+ removedFields.push(...modelDiff.removedFields);
243
+ alteredFields.push(...modelDiff.alteredFields);
244
+ addedUniqueConstraints.push(...modelDiff.addedUniqueConstraints);
245
+ removedUniqueConstraints.push(...modelDiff.removedUniqueConstraints);
246
+ addedIndexes.push(...modelDiff.addedIndexes);
247
+ removedIndexes.push(...modelDiff.removedIndexes);
248
+ addedForeignKeys.push(...modelDiff.addedForeignKeys);
249
+ removedForeignKeys.push(...modelDiff.removedForeignKeys);
250
+ primaryKeyChanges.push(...modelDiff.primaryKeyChanges);
251
+ });
252
+ const remapTableName = (tableName) => renamedTableMap.get(tableName) ?? tableName;
253
+ const remapTableEntries = (items) => items.map((item) => ({ ...item, tableName: remapTableName(item.tableName) }));
254
+ if (renamedTableMap.size > 0) {
255
+ removedFields.forEach((entry) => {
256
+ const mapped = renamedTableMap.get(entry.tableName);
257
+ if (mapped) {
258
+ entry.tableName = mapped;
259
+ }
260
+ });
261
+ }
262
+ renameColumns.forEach((mapping) => {
263
+ const mappedTable = remapTableName(mapping.table);
264
+ const removedIdx = removedFields.findIndex((f) => f.tableName === mappedTable && f.column.name === mapping.from);
265
+ const addedIdx = addedFields.findIndex((f) => f.tableName === mappedTable && f.column.name === mapping.to);
266
+ if (removedIdx !== -1 && addedIdx !== -1) {
267
+ removedFields.splice(removedIdx, 1);
268
+ addedFields.splice(addedIdx, 1);
269
+ renamedColumns.push({ tableName: mappedTable, from: mapping.from, to: mapping.to });
270
+ }
271
+ });
272
+ return {
273
+ ...diff,
274
+ removedModels,
275
+ addedModels,
276
+ removedFields: remapTableEntries(removedFields),
277
+ addedFields: remapTableEntries(addedFields),
278
+ alteredFields: remapTableEntries(alteredFields),
279
+ renamedTables,
280
+ renamedColumns,
281
+ addedUniqueConstraints: remapTableEntries(addedUniqueConstraints),
282
+ removedUniqueConstraints: remapTableEntries(removedUniqueConstraints),
283
+ addedIndexes: remapTableEntries(addedIndexes),
284
+ removedIndexes: remapTableEntries(removedIndexes),
285
+ addedForeignKeys: remapTableEntries(addedForeignKeys),
286
+ removedForeignKeys: remapTableEntries(removedForeignKeys),
287
+ primaryKeyChanges: remapTableEntries(primaryKeyChanges),
288
+ };
289
+ }
290
+ /**
291
+ * Topologically sort tables so that referenced tables come before tables that reference them.
292
+ * Tables with no foreign keys come first, then tables that only reference already-ordered tables.
293
+ */
294
+ function sortTablesByDependencies(tables) {
295
+ const tableMap = new Map(tables.map((t) => [t.name, t]));
296
+ const sorted = [];
297
+ const visited = new Set();
298
+ const visiting = new Set();
299
+ function visit(tableName) {
300
+ if (visited.has(tableName))
301
+ return;
302
+ if (visiting.has(tableName)) {
303
+ // Circular dependency - just add it and let the DB handle it
304
+ return;
305
+ }
306
+ const table = tableMap.get(tableName);
307
+ if (!table)
308
+ return;
309
+ visiting.add(tableName);
310
+ // Visit all tables this table references first
311
+ for (const fk of table.foreignKeys) {
312
+ if (tableMap.has(fk.referencedTable) && fk.referencedTable !== tableName) {
313
+ visit(fk.referencedTable);
314
+ }
315
+ }
316
+ visiting.delete(tableName);
317
+ visited.add(tableName);
318
+ sorted.push(table);
319
+ }
320
+ for (const table of tables) {
321
+ visit(table.name);
322
+ }
323
+ return sorted;
324
+ }
325
+ /**
326
+ * Build SQL statements from diff
327
+ */
328
+ export function buildSqlStatements(diff, dialect) {
329
+ const up = [];
330
+ const down = [];
331
+ const compileOpts = { dialect };
332
+ // Table renames
333
+ for (const rename of diff.renamedTables) {
334
+ up.push(compileRenameTable(rename.from, rename.to, compileOpts));
335
+ down.unshift(compileRenameTable(rename.to, rename.from, compileOpts));
336
+ }
337
+ // Column renames
338
+ for (const rename of diff.renamedColumns) {
339
+ up.push(compileRenameColumn(rename.tableName, rename.from, rename.to, compileOpts));
340
+ down.unshift(compileRenameColumn(rename.tableName, rename.to, rename.from, compileOpts));
341
+ }
342
+ // Create tables (sorted by dependency order so referenced tables are created first)
343
+ const sortedAddedModels = sortTablesByDependencies(diff.addedModels);
344
+ for (const model of sortedAddedModels) {
345
+ up.push(compileCreateTable(model, compileOpts));
346
+ down.unshift(compileDropTable(model.name, compileOpts));
347
+ }
348
+ // Drop tables
349
+ for (const model of diff.removedModels) {
350
+ up.push(compileDropTable(model.name, compileOpts));
351
+ down.unshift(compileCreateTable(model, compileOpts));
352
+ }
353
+ // Primary key changes (drop old first)
354
+ for (const change of diff.primaryKeyChanges) {
355
+ if (change.previous) {
356
+ up.push(compileDropConstraint(change.tableName, change.previous.name, compileOpts));
357
+ down.unshift(compileAddPrimaryKeyConstraint(change.tableName, change.previous.name, change.previous.columns, compileOpts));
358
+ }
359
+ }
360
+ // Drop foreign keys first (before dropping columns)
361
+ for (const { tableName, foreignKey } of diff.removedForeignKeys) {
362
+ up.push(compileDropConstraint(tableName, foreignKey.name, compileOpts));
363
+ down.unshift(compileAddForeignKeyConstraint(tableName, foreignKey.name, foreignKey.columns, foreignKey.referencedTable, foreignKey.referencedColumns, compileOpts));
364
+ }
365
+ // Drop unique constraints
366
+ for (const { tableName, constraint } of diff.removedUniqueConstraints) {
367
+ up.push(compileDropConstraint(tableName, constraint.name, compileOpts));
368
+ down.unshift(compileAddUniqueConstraint(tableName, constraint.name, constraint.columns, compileOpts));
369
+ }
370
+ // Drop indexes
371
+ for (const { tableName, index } of diff.removedIndexes) {
372
+ up.push(compileDropIndex(index.name, compileOpts));
373
+ down.unshift(compileCreateIndex(tableName, index.name, index.columns, compileOpts));
374
+ }
375
+ // Add columns
376
+ for (const { tableName, column } of diff.addedFields) {
377
+ up.push(compileAddColumn(tableName, column, compileOpts));
378
+ down.unshift(compileDropColumn(tableName, column.name, compileOpts));
379
+ }
380
+ // Drop columns
381
+ for (const { tableName, column } of diff.removedFields) {
382
+ up.push(compileDropColumn(tableName, column.name, compileOpts));
383
+ down.unshift(compileAddColumn(tableName, column, compileOpts));
384
+ }
385
+ // Alter columns
386
+ for (const change of diff.alteredFields) {
387
+ const typeChanged = change.previous.type !== change.current.type;
388
+ const nullChanged = change.previous.notNull !== change.current.notNull;
389
+ const defaultChanged = change.previous.default !== change.current.default;
390
+ if (typeChanged) {
391
+ up.push(...compileAlterColumn(change.tableName, change.columnName, { setType: change.current.type }, compileOpts));
392
+ down.unshift(...compileAlterColumn(change.tableName, change.columnName, { setType: change.previous.type }, compileOpts));
393
+ }
394
+ if (nullChanged) {
395
+ if (change.current.notNull) {
396
+ up.push(...compileAlterColumn(change.tableName, change.columnName, { setNotNull: true }, compileOpts));
397
+ down.unshift(...compileAlterColumn(change.tableName, change.columnName, { dropNotNull: true }, compileOpts));
398
+ }
399
+ else {
400
+ up.push(...compileAlterColumn(change.tableName, change.columnName, { dropNotNull: true }, compileOpts));
401
+ down.unshift(...compileAlterColumn(change.tableName, change.columnName, { setNotNull: true }, compileOpts));
402
+ }
403
+ }
404
+ if (defaultChanged) {
405
+ if (change.current.default !== undefined) {
406
+ up.push(...compileAlterColumn(change.tableName, change.columnName, { setDefault: change.current.default }, compileOpts));
407
+ }
408
+ else {
409
+ up.push(...compileAlterColumn(change.tableName, change.columnName, { dropDefault: true }, compileOpts));
410
+ }
411
+ if (change.previous.default !== undefined) {
412
+ down.unshift(...compileAlterColumn(change.tableName, change.columnName, { setDefault: change.previous.default }, compileOpts));
413
+ }
414
+ else {
415
+ down.unshift(...compileAlterColumn(change.tableName, change.columnName, { dropDefault: true }, compileOpts));
416
+ }
417
+ }
418
+ }
419
+ // Primary key changes (add new)
420
+ for (const change of diff.primaryKeyChanges) {
421
+ if (change.current) {
422
+ up.push(compileAddPrimaryKeyConstraint(change.tableName, change.current.name, change.current.columns, compileOpts));
423
+ down.unshift(compileDropConstraint(change.tableName, change.current.name, compileOpts));
424
+ }
425
+ }
426
+ // Add unique constraints
427
+ for (const { tableName, constraint } of diff.addedUniqueConstraints) {
428
+ up.push(compileAddUniqueConstraint(tableName, constraint.name, constraint.columns, compileOpts));
429
+ down.unshift(compileDropConstraint(tableName, constraint.name, compileOpts));
430
+ }
431
+ // Add indexes
432
+ for (const { tableName, index } of diff.addedIndexes) {
433
+ up.push(compileCreateIndex(tableName, index.name, index.columns, compileOpts));
434
+ down.unshift(compileDropIndex(index.name, compileOpts));
435
+ }
436
+ // Add foreign keys
437
+ for (const { tableName, foreignKey } of diff.addedForeignKeys) {
438
+ up.push(compileAddForeignKeyConstraint(tableName, foreignKey.name, foreignKey.columns, foreignKey.referencedTable, foreignKey.referencedColumns, compileOpts));
439
+ down.unshift(compileDropConstraint(tableName, foreignKey.name, compileOpts));
440
+ }
441
+ return { up, down };
442
+ }
@@ -0,0 +1,31 @@
1
+ export interface MigrationLogEntry {
2
+ /** Migration folder name e.g. "20260108120000_init" */
3
+ name: string;
4
+ /** SHA256 checksum of migration.sql content (64 hex chars) */
5
+ checksum: string;
6
+ }
7
+ /**
8
+ * Calculate SHA256 checksum of migration SQL
9
+ */
10
+ export declare function calculateChecksum(sql: string): string;
11
+ /**
12
+ * Get the path to the migration log file
13
+ */
14
+ export declare function getMigrationLogPath(outputPath: string): string;
15
+ /**
16
+ * Read migration log file
17
+ */
18
+ export declare function readMigrationLog(outputPath: string): Promise<MigrationLogEntry[]>;
19
+ /**
20
+ * Write migration log file
21
+ */
22
+ export declare function writeMigrationLog(outputPath: string, entries: MigrationLogEntry[]): Promise<void>;
23
+ /**
24
+ * Append a single entry to the migration log
25
+ */
26
+ export declare function appendToMigrationLog(outputPath: string, entry: MigrationLogEntry): Promise<void>;
27
+ /**
28
+ * Scan migration folders and compute checksums for each
29
+ */
30
+ export declare function scanMigrationFolders(outputPath: string): Promise<MigrationLogEntry[]>;
31
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../../src/migrations/prisma/log.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE9D;AAwBD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAWvF;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAIvG;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAItG;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA4B3F"}
@@ -0,0 +1,101 @@
1
+ import * as fs from "fs/promises";
2
+ import * as path from "path";
3
+ import * as crypto from "crypto";
4
+ const MIGRATION_LOG_HEADER = `# zenstack-kit migration log
5
+ # Format: <migration_name> <checksum>
6
+ `;
7
+ /**
8
+ * Calculate SHA256 checksum of migration SQL
9
+ */
10
+ export function calculateChecksum(sql) {
11
+ return crypto.createHash("sha256").update(sql).digest("hex");
12
+ }
13
+ /**
14
+ * Get the path to the migration log file
15
+ */
16
+ export function getMigrationLogPath(outputPath) {
17
+ return path.join(outputPath, "meta", "_migration_log");
18
+ }
19
+ /**
20
+ * Parse migration log content into entries
21
+ */
22
+ function parseMigrationLog(content) {
23
+ return content
24
+ .split("\n")
25
+ .filter((line) => line.trim() && !line.startsWith("#"))
26
+ .map((line) => {
27
+ const [name, checksum] = line.split(" ");
28
+ return { name, checksum };
29
+ })
30
+ .filter((entry) => entry.name && entry.checksum);
31
+ }
32
+ /**
33
+ * Serialize migration log entries to string
34
+ */
35
+ function serializeMigrationLog(entries) {
36
+ const lines = entries.map((e) => `${e.name} ${e.checksum}`).join("\n");
37
+ return MIGRATION_LOG_HEADER + lines + (lines.length > 0 ? "\n" : "");
38
+ }
39
+ /**
40
+ * Read migration log file
41
+ */
42
+ export async function readMigrationLog(outputPath) {
43
+ const logPath = getMigrationLogPath(outputPath);
44
+ try {
45
+ const content = await fs.readFile(logPath, "utf-8");
46
+ return parseMigrationLog(content);
47
+ }
48
+ catch (error) {
49
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
50
+ return [];
51
+ }
52
+ throw error;
53
+ }
54
+ }
55
+ /**
56
+ * Write migration log file
57
+ */
58
+ export async function writeMigrationLog(outputPath, entries) {
59
+ const logPath = getMigrationLogPath(outputPath);
60
+ await fs.mkdir(path.dirname(logPath), { recursive: true });
61
+ await fs.writeFile(logPath, serializeMigrationLog(entries), "utf-8");
62
+ }
63
+ /**
64
+ * Append a single entry to the migration log
65
+ */
66
+ export async function appendToMigrationLog(outputPath, entry) {
67
+ const entries = await readMigrationLog(outputPath);
68
+ entries.push(entry);
69
+ await writeMigrationLog(outputPath, entries);
70
+ }
71
+ /**
72
+ * Scan migration folders and compute checksums for each
73
+ */
74
+ export async function scanMigrationFolders(outputPath) {
75
+ const entries = [];
76
+ try {
77
+ const dirEntries = await fs.readdir(outputPath, { withFileTypes: true });
78
+ const migrationFolders = dirEntries
79
+ .filter((e) => e.isDirectory() && /^\d{14}_/.test(e.name))
80
+ .map((e) => e.name)
81
+ .sort();
82
+ for (const folderName of migrationFolders) {
83
+ const sqlPath = path.join(outputPath, folderName, "migration.sql");
84
+ try {
85
+ const sqlContent = await fs.readFile(sqlPath, "utf-8");
86
+ const checksum = calculateChecksum(sqlContent);
87
+ entries.push({ name: folderName, checksum });
88
+ }
89
+ catch {
90
+ // Skip folders without migration.sql
91
+ }
92
+ }
93
+ }
94
+ catch (error) {
95
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
96
+ return [];
97
+ }
98
+ throw error;
99
+ }
100
+ return entries;
101
+ }
@@ -0,0 +1,23 @@
1
+ export interface PotentialTableRename {
2
+ from: string;
3
+ to: string;
4
+ }
5
+ export interface PotentialColumnRename {
6
+ table: string;
7
+ from: string;
8
+ to: string;
9
+ }
10
+ export interface PotentialRenames {
11
+ tables: PotentialTableRename[];
12
+ columns: PotentialColumnRename[];
13
+ }
14
+ /**
15
+ * Detect potential renames by finding removed+added pairs.
16
+ * A table rename is detected when one table is removed and one is added.
17
+ * A column rename is detected when within the same table, one column is removed and one is added.
18
+ */
19
+ export declare function detectPotentialRenames(options: {
20
+ schemaPath: string;
21
+ outputPath: string;
22
+ }): Promise<PotentialRenames>;
23
+ //# sourceMappingURL=rename.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename.d.ts","sourceRoot":"","sources":["../../../src/migrations/prisma/rename.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwD5B"}
@@ -0,0 +1,57 @@
1
+ import { generateSchemaSnapshot } from "../../schema/snapshot.js";
2
+ import { diffSchemas } from "./diff.js";
3
+ import { getSnapshotPaths, readSnapshot } from "./snapshot.js";
4
+ /**
5
+ * Detect potential renames by finding removed+added pairs.
6
+ * A table rename is detected when one table is removed and one is added.
7
+ * A column rename is detected when within the same table, one column is removed and one is added.
8
+ */
9
+ export async function detectPotentialRenames(options) {
10
+ const currentSchema = await generateSchemaSnapshot(options.schemaPath);
11
+ const { snapshotPath } = getSnapshotPaths(options.outputPath);
12
+ const previousSnapshot = await readSnapshot(snapshotPath);
13
+ const diff = diffSchemas(previousSnapshot?.schema ?? null, currentSchema);
14
+ const result = {
15
+ tables: [],
16
+ columns: [],
17
+ };
18
+ // Detect potential table renames: one removed + one added
19
+ // For simplicity, if there's exactly one removed and one added, suggest it as a rename
20
+ // For multiple, pair them up by order (user can disambiguate)
21
+ const minTablePairs = Math.min(diff.removedModels.length, diff.addedModels.length);
22
+ for (let i = 0; i < minTablePairs; i++) {
23
+ result.tables.push({
24
+ from: diff.removedModels[i].name,
25
+ to: diff.addedModels[i].name,
26
+ });
27
+ }
28
+ // Detect potential column renames within same table
29
+ // Group removed/added fields by table
30
+ const removedByTable = new Map();
31
+ const addedByTable = new Map();
32
+ for (const { tableName, column } of diff.removedFields) {
33
+ if (!removedByTable.has(tableName)) {
34
+ removedByTable.set(tableName, []);
35
+ }
36
+ removedByTable.get(tableName).push(column.name);
37
+ }
38
+ for (const { tableName, column } of diff.addedFields) {
39
+ if (!addedByTable.has(tableName)) {
40
+ addedByTable.set(tableName, []);
41
+ }
42
+ addedByTable.get(tableName).push(column.name);
43
+ }
44
+ // For each table with both removed and added columns, suggest renames
45
+ for (const [tableName, removed] of removedByTable.entries()) {
46
+ const added = addedByTable.get(tableName) || [];
47
+ const minPairs = Math.min(removed.length, added.length);
48
+ for (let i = 0; i < minPairs; i++) {
49
+ result.columns.push({
50
+ table: tableName,
51
+ from: removed[i],
52
+ to: added[i],
53
+ });
54
+ }
55
+ }
56
+ return result;
57
+ }