diff.ts61 lines · main
1import type { SchemaDef } from './schema.js';
2import type { TableDef } from './table.js';
3
4export type Change =
5 | { kind: 'create_table'; table: string; def: TableDef }
6 | { kind: 'drop_table'; table: string }
7 | { kind: 'add_column'; table: string; column: string; def: TableDef['columns'][string] }
8 | { kind: 'drop_column'; table: string; column: string };
9
10export interface DiffResult {
11 readonly changes: readonly Change[];
12 readonly destructive: boolean;
13}
14
15/**
16 * Compute the set of changes to go from `prev` to `next`.
17 *
18 * Phase 1 scope is deliberately narrow: add/drop tables and add/drop
19 * columns. It does not yet detect column-type changes, index changes,
20 * or default-value updates — those land alongside the migration runner
21 * in Phase 2 per BUILD_PLAN.md.
22 */
23export function diff(prev: SchemaDef | null, next: SchemaDef): DiffResult {
24 const changes: Change[] = [];
25 const prevTables = prev?.tables ?? {};
26
27 for (const [name, def] of Object.entries(next.tables)) {
28 const existing = prevTables[name];
29 if (!existing) {
30 changes.push({ kind: 'create_table', table: name, def });
31 continue;
32 }
33 for (const [colName, colDef] of Object.entries(def.columns)) {
34 if (!(colName in existing.columns)) {
35 changes.push({ kind: 'add_column', table: name, column: colName, def: colDef });
36 }
37 }
38 for (const colName of Object.keys(existing.columns)) {
39 if (!(colName in def.columns)) {
40 changes.push({ kind: 'drop_column', table: name, column: colName });
41 }
42 }
43 }
44
45 for (const name of Object.keys(prevTables)) {
46 if (!(name in next.tables)) {
47 changes.push({ kind: 'drop_table', table: name });
48 }
49 }
50
51 const destructive = changes.some((c) => c.kind === 'drop_table' || c.kind === 'drop_column');
52 return { changes, destructive };
53}
54
55export function summariseDiff(result: DiffResult): Record<string, number> {
56 const counts: Record<string, number> = {};
57 for (const c of result.changes) {
58 counts[c.kind] = (counts[c.kind] ?? 0) + 1;
59 }
60 return counts;
61}