@substrat-run/model-emit 0.4.1 → 0.5.0

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/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { emitTables, columnsOf, primaryKeyConstraint, uniqueConstraints, type EmitSqlOptions, type EmittedColumn, } from './emit-sql.js';
2
2
  export { journalColumns, journalUniques, journalPrimaryKeys } from './journal.js';
3
+ export { readSchema, statements, type TableSchema } from './replay.js';
3
4
  export { planMigration, parseJournal, type Journal, type JournalEntry, type MigrationPlan, } from './plan.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EACL,aAAa,EACb,YAAY,EACZ,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EACL,aAAa,EACb,YAAY,EACZ,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { emitTables, columnsOf, primaryKeyConstraint, uniqueConstraints, } from './emit-sql.js';
2
2
  export { journalColumns, journalUniques, journalPrimaryKeys } from './journal.js';
3
+ export { readSchema, statements } from './replay.js';
3
4
  export { planMigration, parseJournal, } from './plan.js';
4
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EACL,aAAa,EACb,YAAY,GAIb,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,UAAU,EAAoB,MAAM,aAAa,CAAC;AACvE,OAAO,EACL,aAAa,EACb,YAAY,GAIb,MAAM,WAAW,CAAC"}
package/dist/journal.d.ts CHANGED
@@ -1,36 +1,35 @@
1
- /**
2
- * Reading a migration journal — the verification half of `emitTables`.
3
- *
4
- * The emitter's claim is "what this emits is what the database ends up with";
5
- * this is how that gets checked. They live together because they are two halves
6
- * of one statement, and a reader that drifts from its emitter checks nothing.
7
- */
8
1
  /**
9
2
  * Column names per table, read out of a migration journal's SQL.
10
3
  *
11
- * Three engines had hand-rolled a copy of this and the copies had already drifted — none followed `RENAME TO`, so a journal that
12
- * rebuilds a table under a temporary name would report the pre-rebuild columns
13
- * forever.
4
+ * Three engines had hand-rolled a copy of this and the copies had already
5
+ * drifted none followed `RENAME TO`, so a journal that rebuilds a table under
6
+ * a temporary name would report the pre-rebuild columns forever.
14
7
  *
15
8
  * It exists because a registry and a journal are two descriptions of one schema
16
9
  * until migrations are derived from the registry. Holding them to each other is
17
10
  * what keeps that duplication safe in the meantime.
18
11
  *
19
- * Handles what real journals do: multi-line `CHECK (...)` constraints (tracked
20
- * by paren depth, so a continuation line is not read as a column), `ADD COLUMN`,
21
- * `DROP TABLE`, `RENAME COLUMN` and `RENAME TO` append-only journals rebuild a table by
22
- * creating a `_new`, copying, dropping the original and renaming onto its name.
12
+ * Everything a real journal does is handled, because SQLite handles it:
13
+ * `ADD COLUMN`, `DROP TABLE`, `RENAME COLUMN`, and the `RENAME TO` that an
14
+ * append-only journal uses to rebuild a table by creating a `_new`, copying,
15
+ * dropping the original and renaming onto its name.
23
16
  */
24
17
  export declare function journalColumns(sql: string): Map<string, Set<string>>;
25
18
  /**
26
19
  * UNIQUE constraints per table, read out of a journal.
27
20
  *
28
- * `journalColumns` deliberately skips constraint lines it answers "which
29
- * columns exist". But a declared `key` is a schema fact too, and adding one to
30
- * an entity that already has a table is a change SQLite cannot make in place.
31
- * Without this the planner reported "up to date" over a missing constraint.
21
+ * `journalColumns` answers "which columns exist". But a declared `key` is a
22
+ * schema fact too, and adding one to an entity that already has a table is a
23
+ * change SQLite cannot make in place. Without this the planner reported "up to
24
+ * date" over a missing constraint.
25
+ *
26
+ * All three spellings count, because the database does not distinguish them:
27
+ * table-level `UNIQUE (a, b)`, column-level `b TEXT UNIQUE`, and
28
+ * `CREATE UNIQUE INDEX … ON t (b)`. A PARTIAL index (`… WHERE deleted_at IS
29
+ * NULL`) is excluded — it constrains a subset of the rows, so counting it would
30
+ * claim a guarantee the database does not make.
32
31
  *
33
- * Normalised to `a, b` (single space, declaration order preserved) so the same
32
+ * Normalised to `a, b` (single space, key order preserved) so the same
34
33
  * constraint written two ways compares equal.
35
34
  */
36
35
  export declare function journalUniques(sql: string): Map<string, Set<string>>;
@@ -43,15 +42,13 @@ export declare function journalUniques(sql: string): Map<string, Set<string>>;
43
42
  * names, types and nullability across 63 tables and reported 63/63 while 15 of
44
43
  * the emitted tables had no primary key at all.
45
44
  *
46
- * Both spellings are read, because journals use both: inline
47
- * (`id TEXT PRIMARY KEY NOT NULL`) for one column, and a table-level
48
- * `PRIMARY KEY (a, b)` for several. A table with no primary key maps to an
49
- * EMPTY array rather than being absent — "the journal never built this table"
50
- * and "the journal built it without a key" are different answers, and only one
51
- * of them is a bug.
45
+ * A table with no primary key maps to an EMPTY array rather than being absent —
46
+ * "the journal never built this table" and "the journal built it without a key"
47
+ * are different answers, and only one of them is a bug.
52
48
  *
53
- * Order is preserved: a composite primary key is the index its columns are
54
- * searched by, so `(a, b)` and `(b, a)` are not the same key.
49
+ * Order is the KEY's order, which is not always declaration order: a composite
50
+ * primary key is the index its columns are searched by, so `(a, b)` and `(b, a)`
51
+ * are not the same key.
55
52
  */
56
53
  export declare function journalPrimaryKeys(sql: string): Map<string, string[]>;
57
54
  //# sourceMappingURL=journal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CA4CpE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAwDpE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAyDrE"}
1
+ {"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAIpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAIpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAIrE"}
package/dist/journal.js CHANGED
@@ -4,127 +4,60 @@
4
4
  * The emitter's claim is "what this emits is what the database ends up with";
5
5
  * this is how that gets checked. They live together because they are two halves
6
6
  * of one statement, and a reader that drifts from its emitter checks nothing.
7
+ *
8
+ * All three readers are now views onto one replay (`./replay.ts`): the journal
9
+ * is run into an in-memory SQLite and the schema read back through `PRAGMA`.
10
+ * Before that they were three hand-rolled parsers over SQL text, and #807 is
11
+ * what that cost — the same table reformatted read as a different schema, and
12
+ * the three `RENAME TO` / `RENAME COLUMN` / `DROP TABLE` replay loops that used
13
+ * to live here, one per reader, were each a re-implementation of what the
14
+ * database does for free.
7
15
  */
16
+ import { readSchema } from './replay.js';
8
17
  /**
9
18
  * Column names per table, read out of a migration journal's SQL.
10
19
  *
11
- * Three engines had hand-rolled a copy of this and the copies had already drifted — none followed `RENAME TO`, so a journal that
12
- * rebuilds a table under a temporary name would report the pre-rebuild columns
13
- * forever.
20
+ * Three engines had hand-rolled a copy of this and the copies had already
21
+ * drifted none followed `RENAME TO`, so a journal that rebuilds a table under
22
+ * a temporary name would report the pre-rebuild columns forever.
14
23
  *
15
24
  * It exists because a registry and a journal are two descriptions of one schema
16
25
  * until migrations are derived from the registry. Holding them to each other is
17
26
  * what keeps that duplication safe in the meantime.
18
27
  *
19
- * Handles what real journals do: multi-line `CHECK (...)` constraints (tracked
20
- * by paren depth, so a continuation line is not read as a column), `ADD COLUMN`,
21
- * `DROP TABLE`, `RENAME COLUMN` and `RENAME TO` append-only journals rebuild a table by
22
- * creating a `_new`, copying, dropping the original and renaming onto its name.
28
+ * Everything a real journal does is handled, because SQLite handles it:
29
+ * `ADD COLUMN`, `DROP TABLE`, `RENAME COLUMN`, and the `RENAME TO` that an
30
+ * append-only journal uses to rebuild a table by creating a `_new`, copying,
31
+ * dropping the original and renaming onto its name.
23
32
  */
24
33
  export function journalColumns(sql) {
25
- const tables = new Map();
26
- for (const [, table, body] of sql.matchAll(/CREATE TABLE (?:IF NOT EXISTS )?([a-z_][a-z0-9_]*)\s*\(([\s\S]*?)\n\s*\);/gi)) {
27
- if (!table || !body)
28
- continue;
29
- const cols = new Set();
30
- let depth = 0;
31
- for (const raw of body.split('\n')) {
32
- const line = raw.trim();
33
- const atTop = depth === 0;
34
- depth += (line.match(/\(/g) ?? []).length - (line.match(/\)/g) ?? []).length;
35
- if (!atTop)
36
- continue;
37
- if (!line || line.startsWith('--') || /^(PRIMARY|FOREIGN|UNIQUE|CHECK|CONSTRAINT)\b/i.test(line))
38
- continue;
39
- const name = /^([a-z_][a-z0-9_]*)\b/i.exec(line)?.[1];
40
- if (name)
41
- cols.add(name);
42
- }
43
- tables.set(table, cols);
44
- }
45
- // Replayed in statement order: a journal may add a column and later rename the
46
- // table, or rename onto a name it has just dropped.
47
- for (const m of sql.matchAll(
48
- // `RENAME COLUMN` comes first: `RENAME TO` must not swallow it. Without this
49
- // branch a renamed column reads as its old name forever, and a planner that
50
- // derives migrations would emit the same rename on every run.
51
- /(?:ALTER TABLE ([a-z_][a-z0-9_]*)\s+ADD COLUMN\s+([a-z_][a-z0-9_]*))|(?:ALTER TABLE ([a-z_][a-z0-9_]*)\s+RENAME COLUMN\s+([a-z_][a-z0-9_]*)\s+TO\s+([a-z_][a-z0-9_]*))|(?:ALTER TABLE ([a-z_][a-z0-9_]*)\s+RENAME TO\s+([a-z_][a-z0-9_]*))|(?:DROP TABLE (?:IF EXISTS )?([a-z_][a-z0-9_]*))/gi)) {
52
- const [, addTable, addCol, renTable, renFrom, renTo, fromTable, toTable, dropped] = m;
53
- if (addTable && addCol)
54
- tables.get(addTable)?.add(addCol);
55
- else if (dropped)
56
- tables.delete(dropped);
57
- else if (renTable && renFrom && renTo) {
58
- const cols = tables.get(renTable);
59
- if (cols?.delete(renFrom))
60
- cols.add(renTo);
61
- }
62
- else if (fromTable && toTable) {
63
- const cols = tables.get(fromTable);
64
- if (cols) {
65
- tables.delete(fromTable);
66
- tables.set(toTable, cols);
67
- }
68
- }
69
- }
70
- return tables;
34
+ const out = new Map();
35
+ for (const [table, schema] of readSchema(sql))
36
+ out.set(table, new Set(schema.columns));
37
+ return out;
71
38
  }
72
39
  /**
73
40
  * UNIQUE constraints per table, read out of a journal.
74
41
  *
75
- * `journalColumns` deliberately skips constraint lines it answers "which
76
- * columns exist". But a declared `key` is a schema fact too, and adding one to
77
- * an entity that already has a table is a change SQLite cannot make in place.
78
- * Without this the planner reported "up to date" over a missing constraint.
42
+ * `journalColumns` answers "which columns exist". But a declared `key` is a
43
+ * schema fact too, and adding one to an entity that already has a table is a
44
+ * change SQLite cannot make in place. Without this the planner reported "up to
45
+ * date" over a missing constraint.
46
+ *
47
+ * All three spellings count, because the database does not distinguish them:
48
+ * table-level `UNIQUE (a, b)`, column-level `b TEXT UNIQUE`, and
49
+ * `CREATE UNIQUE INDEX … ON t (b)`. A PARTIAL index (`… WHERE deleted_at IS
50
+ * NULL`) is excluded — it constrains a subset of the rows, so counting it would
51
+ * claim a guarantee the database does not make.
79
52
  *
80
- * Normalised to `a, b` (single space, declaration order preserved) so the same
53
+ * Normalised to `a, b` (single space, key order preserved) so the same
81
54
  * constraint written two ways compares equal.
82
55
  */
83
56
  export function journalUniques(sql) {
84
- const tables = new Map();
85
- for (const [, table, body] of sql.matchAll(/CREATE TABLE (?:IF NOT EXISTS )?([a-z_][a-z0-9_]*)\s*\(([\s\S]*?)\n\s*\);/gi)) {
86
- if (!table || !body)
87
- continue;
88
- const found = new Set();
89
- for (const [, cols] of body.matchAll(/\bUNIQUE\s*\(([^)]*)\)/gi)) {
90
- if (!cols)
91
- continue;
92
- found.add(cols
93
- .split(',')
94
- .map((c) => c.trim())
95
- .filter(Boolean)
96
- .join(', '));
97
- }
98
- tables.set(table, found);
99
- }
100
- // Replayed in statement order, like `journalColumns`.
101
- for (const m of sql.matchAll(/(?:ALTER TABLE ([a-z_][a-z0-9_]*)\s+RENAME COLUMN\s+([a-z_][a-z0-9_]*)\s+TO\s+([a-z_][a-z0-9_]*))|(?:ALTER TABLE ([a-z_][a-z0-9_]*)\s+RENAME TO\s+([a-z_][a-z0-9_]*))|(?:DROP TABLE (?:IF EXISTS )?([a-z_][a-z0-9_]*))/gi)) {
102
- const [, renTable, renFrom, renTo, fromTable, toTable, dropped] = m;
103
- if (dropped) {
104
- tables.delete(dropped);
105
- }
106
- else if (renTable && renFrom && renTo) {
107
- // SQLite rewrites the constraint when a column is renamed — verified
108
- // against a real database — so the reader has to as well. Missing this
109
- // makes a renamed key look like a key the journal never had.
110
- const cs = tables.get(renTable);
111
- if (cs) {
112
- tables.set(renTable, new Set([...cs].map((c) => c
113
- .split(', ')
114
- .map((col) => (col === renFrom ? renTo : col))
115
- .join(', '))));
116
- }
117
- }
118
- else if (fromTable && toTable) {
119
- // A rebuild renames a table onto another's name; constraints travel with it.
120
- const c = tables.get(fromTable);
121
- if (c) {
122
- tables.delete(fromTable);
123
- tables.set(toTable, c);
124
- }
125
- }
126
- }
127
- return tables;
57
+ const out = new Map();
58
+ for (const [table, schema] of readSchema(sql))
59
+ out.set(table, new Set(schema.uniques));
60
+ return out;
128
61
  }
129
62
  /**
130
63
  * The primary key per table, read out of a journal.
@@ -135,71 +68,18 @@ export function journalUniques(sql) {
135
68
  * names, types and nullability across 63 tables and reported 63/63 while 15 of
136
69
  * the emitted tables had no primary key at all.
137
70
  *
138
- * Both spellings are read, because journals use both: inline
139
- * (`id TEXT PRIMARY KEY NOT NULL`) for one column, and a table-level
140
- * `PRIMARY KEY (a, b)` for several. A table with no primary key maps to an
141
- * EMPTY array rather than being absent — "the journal never built this table"
142
- * and "the journal built it without a key" are different answers, and only one
143
- * of them is a bug.
71
+ * A table with no primary key maps to an EMPTY array rather than being absent —
72
+ * "the journal never built this table" and "the journal built it without a key"
73
+ * are different answers, and only one of them is a bug.
144
74
  *
145
- * Order is preserved: a composite primary key is the index its columns are
146
- * searched by, so `(a, b)` and `(b, a)` are not the same key.
75
+ * Order is the KEY's order, which is not always declaration order: a composite
76
+ * primary key is the index its columns are searched by, so `(a, b)` and `(b, a)`
77
+ * are not the same key.
147
78
  */
148
79
  export function journalPrimaryKeys(sql) {
149
- const tables = new Map();
150
- for (const [, table, body] of sql.matchAll(/CREATE TABLE (?:IF NOT EXISTS )?([a-z_][a-z0-9_]*)\s*\(([\s\S]*?)\n\s*\);/gi)) {
151
- if (!table || !body)
152
- continue;
153
- let key = [];
154
- let depth = 0;
155
- for (const raw of body.split('\n')) {
156
- const line = raw.trim();
157
- const atTop = depth === 0;
158
- depth += (line.match(/\(/g) ?? []).length - (line.match(/\)/g) ?? []).length;
159
- if (!atTop || !line || line.startsWith('--'))
160
- continue;
161
- // Table-level. Written as its own clause, so it wins over any inline
162
- // spelling — a table cannot legally have both, and if one somehow did,
163
- // the explicit list is the one a reader would believe.
164
- const composite = /^(?:CONSTRAINT\s+[a-z_][a-z0-9_]*\s+)?PRIMARY\s+KEY\s*\(([^)]*)\)/i.exec(line);
165
- if (composite?.[1]) {
166
- key = composite[1]
167
- .split(',')
168
- // `PRIMARY KEY (a DESC, b COLLATE NOCASE)` — the column is the first word.
169
- .map((c) => /^([a-z_][a-z0-9_]*)/i.exec(c.trim())?.[1] ?? '')
170
- .filter(Boolean);
171
- continue;
172
- }
173
- if (/^(PRIMARY|FOREIGN|UNIQUE|CHECK|CONSTRAINT)\b/i.test(line))
174
- continue;
175
- // Inline, on the column that carries it.
176
- const name = /^([a-z_][a-z0-9_]*)\b/i.exec(line)?.[1];
177
- if (name && /\bPRIMARY\s+KEY\b/i.test(line) && key.length === 0)
178
- key = [name];
179
- }
180
- tables.set(table, key);
181
- }
182
- // Replayed in statement order, like `journalColumns`. A rebuild renames a
183
- // table onto another's name and the key travels with it; `ADD COLUMN` cannot
184
- // introduce one, because SQLite refuses to add a PRIMARY KEY column.
185
- for (const m of sql.matchAll(/(?:ALTER TABLE ([a-z_][a-z0-9_]*)\s+RENAME COLUMN\s+([a-z_][a-z0-9_]*)\s+TO\s+([a-z_][a-z0-9_]*))|(?:ALTER TABLE ([a-z_][a-z0-9_]*)\s+RENAME TO\s+([a-z_][a-z0-9_]*))|(?:DROP TABLE (?:IF EXISTS )?([a-z_][a-z0-9_]*))/gi)) {
186
- const [, renTable, renFrom, renTo, fromTable, toTable, dropped] = m;
187
- if (dropped) {
188
- tables.delete(dropped);
189
- }
190
- else if (renTable && renFrom && renTo) {
191
- const key = tables.get(renTable);
192
- if (key)
193
- tables.set(renTable, key.map((c) => (c === renFrom ? renTo : c)));
194
- }
195
- else if (fromTable && toTable) {
196
- const key = tables.get(fromTable);
197
- if (key) {
198
- tables.delete(fromTable);
199
- tables.set(toTable, key);
200
- }
201
- }
202
- }
203
- return tables;
80
+ const out = new Map();
81
+ for (const [table, schema] of readSchema(sql))
82
+ out.set(table, [...schema.primaryKey]);
83
+ return out;
204
84
  }
205
85
  //# sourceMappingURL=journal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"journal.js","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"journal.js","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACvF,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACvF,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAoB,CAAC;IACxC,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACtF,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The journal's statements, split on the semicolons that actually end one.
3
+ *
4
+ * Quote- and comment-aware, because a `;` inside a string literal or a `--`
5
+ * comment ends nothing. This is the only text scanning left in the package, and
6
+ * it exists because the statements have to be filtered before they are run —
7
+ * `db.exec` would happily run the `INSERT`s too.
8
+ */
9
+ export declare function statements(sql: string): string[];
10
+ export interface TableSchema {
11
+ /** Declaration order, as `PRAGMA table_info` reports it. */
12
+ readonly columns: string[];
13
+ /** Key order, not declaration order — `(a, b)` and `(b, a)` are different keys. Empty when the table has none. */
14
+ readonly primaryKey: string[];
15
+ /** Each constraint normalised to `a, b`. Excludes the primary key and any PARTIAL index. */
16
+ readonly uniques: string[];
17
+ }
18
+ /**
19
+ * The schema a journal leaves behind, as SQLite sees it.
20
+ *
21
+ * Throws if a schema statement does not apply — which is a feature, not a
22
+ * regression. A journal that cannot be replayed is a journal that will not apply
23
+ * to a real scope either, and the old readers answered anyway.
24
+ */
25
+ export declare function readSchema(sql: string): Map<string, TableSchema>;
26
+ //# sourceMappingURL=replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAmDA;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA4BhD;AAMD,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,kHAAkH;IAClH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;IAC9B,4FAA4F;IAC5F,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;CAC5B;AAYD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CA2DhE"}
package/dist/replay.js ADDED
@@ -0,0 +1,156 @@
1
+ /**
2
+ * The journal, read by REPLAYING it.
3
+ *
4
+ * Every reader in this package used to parse SQL with regular expressions, and
5
+ * every one of them was wrong in a way nobody could have predicted from reading
6
+ * it. #807 reported two defects; probing the same cause found five more in ten
7
+ * minutes — a `CREATE TABLE` on one line, a `) STRICT;` suffix, a wrapped
8
+ * `PRIMARY KEY (` list, a quoted `"order"` identifier, the word UNIQUE inside a
9
+ * comment. That is not a list of bugs, it is a shape: a regex over SQL text has
10
+ * no bottom, and every fix is a patch against the next spelling.
11
+ *
12
+ * So this does not read SQL. It runs it, into a throwaway in-memory database,
13
+ * and asks SQLite what the schema is. The answer is the one the production
14
+ * database would give, because it comes from the same engine — which is the
15
+ * whole claim `emitTables` makes and the reason a reader exists at all.
16
+ *
17
+ * **Only schema statements are replayed.** A journal's `INSERT`s do not change
18
+ * its schema, and skipping them is what lets a vertical's journal be read on its
19
+ * own: two of this repo's verticals hand data to an engine with
20
+ * `INSERT … SELECT` into a table that lives in the ENGINE's journal (the
21
+ * decision-28 extraction handoff), which no amount of replaying the vertical
22
+ * alone can satisfy. Foreign keys stay off — SQLite's default — so a
23
+ * `REFERENCES` pointing into another module's journal is created, not refused.
24
+ *
25
+ * Node-only, deliberately. This package is a devDependency in all of its
26
+ * dependents and no `src/` file imports it, so nothing here reaches a worker
27
+ * bundle or a scope; the builder runs its gates as shell commands in a
28
+ * container. `node:sqlite` is a builtin, so this costs no dependency.
29
+ */
30
+ import { DatabaseSync } from 'node:sqlite';
31
+ /** The quote characters SQLite accepts around an identifier or a literal. */
32
+ const CLOSING = { "'": "'", '"': '"', '`': '`', '[': ']' };
33
+ /** Index after the quoted run starting at `i`; a doubled quote is an escape. */
34
+ function endOfQuoted(s, i) {
35
+ const close = CLOSING[s[i]];
36
+ let j = i + 1;
37
+ while (j < s.length) {
38
+ if (s[j] === close) {
39
+ if (s[j + 1] === close) {
40
+ j += 2;
41
+ continue;
42
+ }
43
+ return j + 1;
44
+ }
45
+ j++;
46
+ }
47
+ return s.length;
48
+ }
49
+ /**
50
+ * The journal's statements, split on the semicolons that actually end one.
51
+ *
52
+ * Quote- and comment-aware, because a `;` inside a string literal or a `--`
53
+ * comment ends nothing. This is the only text scanning left in the package, and
54
+ * it exists because the statements have to be filtered before they are run —
55
+ * `db.exec` would happily run the `INSERT`s too.
56
+ */
57
+ export function statements(sql) {
58
+ const out = [];
59
+ let start = 0;
60
+ for (let i = 0; i < sql.length; i++) {
61
+ const c = sql[i];
62
+ if (CLOSING[c]) {
63
+ i = endOfQuoted(sql, i) - 1;
64
+ continue;
65
+ }
66
+ if (c === '-' && sql[i + 1] === '-') {
67
+ while (i < sql.length && sql[i] !== '\n')
68
+ i++;
69
+ continue;
70
+ }
71
+ if (c === '/' && sql[i + 1] === '*') {
72
+ i += 2;
73
+ while (i < sql.length && !(sql[i] === '*' && sql[i + 1] === '/'))
74
+ i++;
75
+ i++;
76
+ continue;
77
+ }
78
+ if (c === ';') {
79
+ const s = sql.slice(start, i).trim();
80
+ if (s)
81
+ out.push(s);
82
+ start = i + 1;
83
+ }
84
+ }
85
+ const last = sql.slice(start).trim();
86
+ if (last)
87
+ out.push(last);
88
+ return out;
89
+ }
90
+ /** Statements that change the schema — the only ones worth replaying. */
91
+ const isSchemaStatement = (s) => /^(CREATE|ALTER|DROP)\b/i.test(s.replace(/^(?:\s|--[^\n]*\n|\/\*[\s\S]*?\*\/)*/, ''));
92
+ /**
93
+ * One replay per journal string.
94
+ *
95
+ * `planMigration` asks for columns, keys and uniques off the same journal, and
96
+ * building three identical databases to answer three questions about one schema
97
+ * would be silly. Keyed on the SQL itself, which is what makes it safe: the
98
+ * readers are pure functions of their input, and this does not change that.
99
+ */
100
+ const cache = new Map();
101
+ /**
102
+ * The schema a journal leaves behind, as SQLite sees it.
103
+ *
104
+ * Throws if a schema statement does not apply — which is a feature, not a
105
+ * regression. A journal that cannot be replayed is a journal that will not apply
106
+ * to a real scope either, and the old readers answered anyway.
107
+ */
108
+ export function readSchema(sql) {
109
+ const hit = cache.get(sql);
110
+ if (hit)
111
+ return hit;
112
+ const db = new DatabaseSync(':memory:');
113
+ try {
114
+ for (const statement of statements(sql)) {
115
+ if (!isSchemaStatement(statement))
116
+ continue;
117
+ try {
118
+ db.exec(statement);
119
+ }
120
+ catch (cause) {
121
+ const head = statement.replace(/\s+/g, ' ').slice(0, 120);
122
+ throw new Error(`journal: a schema statement does not apply — ${cause.message}\n in: ${head}…\n` +
123
+ 'The journal is replayed into SQLite to read its schema, so a statement that cannot ' +
124
+ 'run here would not run against a scope either.', { cause });
125
+ }
126
+ }
127
+ const schema = new Map();
128
+ const names = db
129
+ .prepare(`SELECT name FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name`)
130
+ .all();
131
+ for (const { name } of names) {
132
+ const info = db.prepare(`SELECT name FROM pragma_table_info(?)`).all(name);
133
+ const primaryKey = db.prepare(`SELECT name FROM pragma_table_info(?) WHERE pk > 0 ORDER BY pk`).all(name).map((r) => r.name);
134
+ const uniques = [];
135
+ const indexes = db
136
+ .prepare(`SELECT name, "unique" AS uniq, partial, origin FROM pragma_index_list(?)`)
137
+ .all(name);
138
+ for (const index of indexes) {
139
+ // `pk` is the primary key wearing an index; it has its own reader.
140
+ // A PARTIAL index constrains a subset of the rows, so reading it as a
141
+ // key would claim a guarantee the database does not make.
142
+ if (!index.uniq || index.partial || index.origin === 'pk')
143
+ continue;
144
+ const cols = db.prepare(`SELECT name FROM pragma_index_info(?) ORDER BY seqno`).all(index.name).map((r) => r.name);
145
+ uniques.push(cols.join(', '));
146
+ }
147
+ schema.set(name, { columns: info.map((c) => c.name), primaryKey, uniques });
148
+ }
149
+ cache.set(sql, schema);
150
+ return schema;
151
+ }
152
+ finally {
153
+ db.close();
154
+ }
155
+ }
156
+ //# sourceMappingURL=replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@substrat-run/model-emit",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Build-time tooling over a Substrat model — DDL emitted from the entity registry, and the journal reader that holds it honest",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -22,9 +22,10 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@substrat-run/contracts": "0.78.0"
25
+ "@substrat-run/contracts": "0.79.0"
26
26
  },
27
27
  "devDependencies": {
28
+ "@types/node": "^22.0.0",
28
29
  "typescript": "^7.0.0",
29
30
  "vitest": "^3.2.7",
30
31
  "zod": "^4.4.3"
@@ -35,6 +36,9 @@
35
36
  "peerDependencies": {
36
37
  "zod": "^4.4.0"
37
38
  },
39
+ "engines": {
40
+ "node": ">=22.5"
41
+ },
38
42
  "scripts": {
39
43
  "build": "tsc -p tsconfig.json",
40
44
  "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",