@henols/c64-re-tools 2.0.0-rc.11 → 2.0.0-rc.13

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 (54) hide show
  1. package/README.md +7 -5
  2. package/package.json +1 -1
  3. package/skills/c64-assembler/SKILL.md +11 -2
  4. package/skills/c64-assembler/scripts/assemble.ts +4 -4
  5. package/skills/c64-basic/SKILL.md +8 -1
  6. package/skills/c64-basic/scripts/basic.ts +3 -3
  7. package/skills/c64-disk/SKILL.md +10 -3
  8. package/skills/c64-disk/scripts/disk.ts +4 -4
  9. package/skills/c64-emulator/SKILL.md +4 -0
  10. package/skills/c64-knowledge/SKILL.md +62 -12
  11. package/skills/c64-knowledge/scripts/knowledge.ts +352 -102
  12. package/skills/c64-memory-map/SKILL.md +7 -0
  13. package/skills/c64-memory-map/scripts/memmap.ts +1 -1
  14. package/skills/c64-provenance/SKILL.md +56 -3
  15. package/skills/c64-provenance/scripts/library.ts +584 -0
  16. package/skills/c64-provenance/scripts/provenance.ts +201 -9
  17. package/skills/c64-reverse-engineering/SKILL.md +16 -2
  18. package/skills/c64-source/SKILL.md +38 -7
  19. package/skills/c64-source/scripts/generate.ts +213 -29
  20. package/skills/c64-source/scripts/loader.ts +3 -1
  21. package/skills/c64-source/scripts/opcodes.ts +19 -0
  22. package/skills/c64-source/scripts/source.ts +387 -35
  23. package/skills/c64-static-analysis/SKILL.md +11 -2
  24. package/skills/c64-static-analysis/scripts/analyze.ts +95 -8
  25. package/skills/c64-static-analysis/scripts/macros.ts +233 -0
  26. package/skills/c64-testing/SKILL.md +8 -1
  27. package/skills/c64-testing/references/scenario-format.md +1 -1
  28. package/skills/c64-testing/scripts/checklist.ts +2 -2
  29. package/skills/c64-testing/scripts/run.ts +4 -4
  30. package/skills/c64-testing/scripts/scenario.ts +1 -1
  31. package/skills/c64-testing/scripts/test.ts +4 -4
  32. package/skills/c64-unpacker/SKILL.md +11 -4
  33. package/skills/c64-unpacker/scripts/unpack.ts +4 -4
  34. package/src/cli/main.ts +5 -3
  35. package/src/host-client/image.ts +1 -1
  36. package/src/host-client/transfer.ts +34 -10
  37. package/src/host-client/vice-session.ts +9 -9
  38. package/src/knowledge/conflicts.ts +157 -0
  39. package/src/knowledge/database.ts +150 -38
  40. package/src/knowledge/history.ts +54 -20
  41. package/src/knowledge/import.ts +3 -2
  42. package/src/knowledge/read.ts +169 -20
  43. package/src/knowledge/schema.ts +115 -72
  44. package/src/knowledge/text.ts +391 -0
  45. package/src/knowledge/write.ts +508 -51
  46. package/src/mcp/output.ts +14 -7
  47. package/src/mcp/tools/debug.ts +8 -6
  48. package/src/mcp/tools/media.ts +17 -13
  49. package/src/mcp/tools/memory.ts +9 -5
  50. package/src/mcp/tools/video.ts +6 -5
  51. package/src/platform.ts +28 -5
  52. package/src/project.ts +166 -3
  53. package/src/protocol/tools.ts +1 -1
  54. package/src/script.ts +46 -7
@@ -1,9 +1,10 @@
1
- // The knowledge.db schema: one version, created in an empty database.
1
+ // The schema of the knowledge index, .c64-re-tools/local/knowledge.db (D69).
2
+ // The index is made from the text in .c64-re-tools/knowledge/; an index with
3
+ // another version is emptied and made again.
2
4
 
3
5
  import type { DatabaseSync } from "node:sqlite";
4
6
 
5
7
  import { REFERENCE_KINDS, type ReferenceKind } from "../c64.ts";
6
- import { KnowledgeError } from "./database.ts";
7
8
  import { OPERAND_SELECTS } from "./expression.ts";
8
9
 
9
10
  /** heuristic: 6502 idioms that the static analysis finds by pattern (D44); an analyzer origin. */
@@ -49,8 +50,14 @@ export type SuppressionEntity = (typeof SUPPRESSION_ENTITIES)[number];
49
50
 
50
51
  /** A module file of the generated source (D48): a plain name that ends in .asm, no directory. */
51
52
  export const MODULE_FILE = /^[A-Za-z0-9_][A-Za-z0-9_-]{0,59}\.asm$/;
53
+ /** A switch of the generated source (D72): upper case, so it reads as a build option and not as a label. */
54
+ export const SWITCH_NAME = /^[A-Z][A-Z0-9_]{1,31}$/;
55
+ /** A macro name and a macro parameter (D73). The body names a parameter with a dot in front: .dst. */
56
+ export const MACRO_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,31}$/;
57
+ export const MACRO_PARAM = /^[a-z_][a-z0-9_]{0,31}$/;
58
+
52
59
  /** Files that c64-source writes itself: no module can have these names. */
53
- export const GENERATED_FILES = ["main.asm", "loader.asm", "constants.asm"] as const;
60
+ export const GENERATED_FILES = ["main.asm", "loader.asm", "constants.asm", "macros.asm"] as const;
54
61
 
55
62
  /** True for a generated file name in any letter case: the file systems of macOS and Windows ignore case. */
56
63
  export function isGeneratedFile(file: string): boolean {
@@ -68,13 +75,13 @@ const SPACE_CHECK = "space IN ('c64', 'drive8') OR space GLOB 'bank[0-9]' OR spa
68
75
 
69
76
  const list = (values: readonly string[]) => values.map((value) => `'${value}'`).join(", ");
70
77
 
71
- /** The schema version this build reads and writes. */
72
- export const SCHEMA_VERSION = 7;
73
-
74
78
  /**
75
79
  * Creates the schema in an empty database. Knowledge rows are temporal: a
76
80
  * change closes the current row (valid_to_revision) and inserts a new one in
77
- * the same revision.
81
+ * the same revision. Revision and row ids are the text ids of the knowledge
82
+ * files; they sort in time order. The rules for current rows (one symbol at
83
+ * an address, ...) are not unique indexes, because a merged text can break
84
+ * them: the conflict check finds those rows (conflicts.ts).
78
85
  */
79
86
  function createSchema(db: DatabaseSync): void {
80
87
  db.exec(`
@@ -83,7 +90,7 @@ function createSchema(db: DatabaseSync): void {
83
90
  value TEXT NOT NULL
84
91
  );
85
92
  CREATE TABLE revisions (
86
- id INTEGER PRIMARY KEY,
93
+ id TEXT PRIMARY KEY,
87
94
  created_at TEXT NOT NULL,
88
95
  origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
89
96
  operation TEXT NOT NULL,
@@ -92,65 +99,65 @@ function createSchema(db: DatabaseSync): void {
92
99
  description TEXT
93
100
  );
94
101
  CREATE TABLE symbols (
95
- id INTEGER PRIMARY KEY,
102
+ id TEXT PRIMARY KEY,
96
103
  space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
97
104
  address INTEGER NOT NULL CHECK (address BETWEEN 0 AND 65535),
98
105
  name TEXT NOT NULL CHECK (length(name) > 0),
99
106
  kind TEXT NOT NULL CHECK (kind IN (${list(SYMBOL_KINDS)})),
100
107
  origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
101
- valid_from_revision INTEGER NOT NULL REFERENCES revisions (id),
102
- valid_to_revision INTEGER REFERENCES revisions (id)
108
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
109
+ valid_to_revision TEXT REFERENCES revisions (id)
103
110
  );
104
- CREATE UNIQUE INDEX symbols_current_address ON symbols (space, address) WHERE valid_to_revision IS NULL;
105
- CREATE UNIQUE INDEX symbols_current_name ON symbols (space, name) WHERE valid_to_revision IS NULL;
111
+ CREATE INDEX symbols_current_address ON symbols (space, address) WHERE valid_to_revision IS NULL;
112
+ CREATE INDEX symbols_current_name ON symbols (space, name) WHERE valid_to_revision IS NULL;
106
113
  CREATE INDEX symbols_address ON symbols (space, address);
107
114
  CREATE TABLE regions (
108
- id INTEGER PRIMARY KEY,
115
+ id TEXT PRIMARY KEY,
109
116
  space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
110
117
  start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
111
118
  end_address INTEGER NOT NULL CHECK (end_address BETWEEN start_address AND 65535),
112
119
  type TEXT NOT NULL CHECK (type IN (${list(REGION_TYPES)})),
113
120
  element INTEGER CHECK (element BETWEEN 1 AND ${MAX_ELEMENT}),
114
121
  origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
115
- valid_from_revision INTEGER NOT NULL REFERENCES revisions (id),
116
- valid_to_revision INTEGER REFERENCES revisions (id)
122
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
123
+ valid_to_revision TEXT REFERENCES revisions (id)
117
124
  );
118
125
  CREATE INDEX regions_start ON regions (space, start_address);
119
126
  CREATE TABLE evidence (
120
127
  id INTEGER PRIMARY KEY,
121
- revision INTEGER NOT NULL REFERENCES revisions (id),
128
+ revision TEXT NOT NULL REFERENCES revisions (id),
122
129
  kind TEXT NOT NULL CHECK (kind IN (${list(EVIDENCE_KINDS)})),
123
130
  ref TEXT NOT NULL CHECK (length(ref) > 0),
124
131
  sha256 TEXT CHECK (sha256 IS NULL OR length(sha256) = 64)
125
132
  );
126
133
  CREATE INDEX evidence_revision ON evidence (revision);
127
134
  CREATE TABLE comments (
128
- id INTEGER PRIMARY KEY,
135
+ id TEXT PRIMARY KEY,
129
136
  space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
130
137
  address INTEGER NOT NULL CHECK (address BETWEEN 0 AND 65535),
131
138
  placement TEXT NOT NULL CHECK (placement IN (${list(COMMENT_PLACEMENTS)})),
132
139
  text TEXT NOT NULL CHECK (length(text) > 0),
133
140
  origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
134
- valid_from_revision INTEGER NOT NULL REFERENCES revisions (id),
135
- valid_to_revision INTEGER REFERENCES revisions (id)
141
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
142
+ valid_to_revision TEXT REFERENCES revisions (id)
136
143
  );
137
- CREATE UNIQUE INDEX comments_current ON comments (space, address, placement) WHERE valid_to_revision IS NULL;
144
+ CREATE INDEX comments_current ON comments (space, address, placement) WHERE valid_to_revision IS NULL;
138
145
  CREATE INDEX comments_address ON comments (space, address);
139
146
  CREATE TABLE "references" (
140
- id INTEGER PRIMARY KEY,
147
+ id TEXT PRIMARY KEY,
141
148
  space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
142
149
  from_address INTEGER NOT NULL CHECK (from_address BETWEEN 0 AND 65535),
143
150
  to_address INTEGER NOT NULL CHECK (to_address BETWEEN 0 AND 65535),
144
151
  kind TEXT NOT NULL CHECK (kind IN (${list(REFERENCE_KINDS)})),
145
152
  origin TEXT NOT NULL CHECK (origin IN (${list(ORIGINS)})),
146
- valid_from_revision INTEGER NOT NULL REFERENCES revisions (id),
147
- valid_to_revision INTEGER REFERENCES revisions (id)
153
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
154
+ valid_to_revision TEXT REFERENCES revisions (id)
148
155
  );
149
- CREATE UNIQUE INDEX references_current ON "references" (space, from_address, to_address, kind) WHERE valid_to_revision IS NULL;
156
+ CREATE INDEX references_current ON "references" (space, from_address, to_address, kind) WHERE valid_to_revision IS NULL;
150
157
  CREATE INDEX references_from ON "references" (space, from_address);
151
158
  CREATE INDEX references_to ON "references" (space, to_address);
152
159
  CREATE TABLE suppressions (
153
- id INTEGER PRIMARY KEY,
160
+ id TEXT PRIMARY KEY,
154
161
  space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
155
162
  entity TEXT NOT NULL CHECK (entity IN (${list(SUPPRESSION_ENTITIES)})),
156
163
  start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
@@ -158,26 +165,26 @@ function createSchema(db: DatabaseSync): void {
158
165
  to_address INTEGER CHECK (to_address BETWEEN 0 AND 65535),
159
166
  kind TEXT CHECK (kind IN (${list(REFERENCE_KINDS)})),
160
167
  origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
161
- valid_from_revision INTEGER NOT NULL REFERENCES revisions (id),
162
- valid_to_revision INTEGER REFERENCES revisions (id),
168
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
169
+ valid_to_revision TEXT REFERENCES revisions (id),
163
170
  CHECK ((entity = 'reference') = (to_address IS NOT NULL AND kind IS NOT NULL)),
164
171
  CHECK (entity = 'region' OR start_address = end_address)
165
172
  );
166
173
  CREATE INDEX suppressions_start ON suppressions (space, start_address);
167
174
  CREATE TABLE modules (
168
- id INTEGER PRIMARY KEY,
175
+ id TEXT PRIMARY KEY,
169
176
  space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
170
177
  start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
171
178
  file TEXT NOT NULL CHECK (length(file) > 0),
172
179
  title TEXT CHECK (title IS NULL OR length(title) > 0),
173
180
  origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
174
- valid_from_revision INTEGER NOT NULL REFERENCES revisions (id),
175
- valid_to_revision INTEGER REFERENCES revisions (id)
181
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
182
+ valid_to_revision TEXT REFERENCES revisions (id)
176
183
  );
177
- CREATE UNIQUE INDEX modules_current_start ON modules (space, start_address) WHERE valid_to_revision IS NULL;
178
- CREATE UNIQUE INDEX modules_current_file ON modules (space, file) WHERE valid_to_revision IS NULL;
184
+ CREATE INDEX modules_current_start ON modules (space, start_address) WHERE valid_to_revision IS NULL;
185
+ CREATE INDEX modules_current_file ON modules (space, file) WHERE valid_to_revision IS NULL;
179
186
  CREATE TABLE operands (
180
- id INTEGER PRIMARY KEY,
187
+ id TEXT PRIMARY KEY,
181
188
  space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
182
189
  address INTEGER NOT NULL CHECK (address BETWEEN 0 AND 65535),
183
190
  selector TEXT NOT NULL CHECK (selector IN (${list(OPERAND_SELECTS)})),
@@ -185,13 +192,50 @@ function createSchema(db: DatabaseSync): void {
185
192
  offset INTEGER NOT NULL DEFAULT 0,
186
193
  expression TEXT CHECK (expression IS NULL OR length(expression) > 0),
187
194
  origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
188
- valid_from_revision INTEGER NOT NULL REFERENCES revisions (id),
189
- valid_to_revision INTEGER REFERENCES revisions (id),
195
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
196
+ valid_to_revision TEXT REFERENCES revisions (id),
190
197
  CHECK ((selector = 'expr') = (expression IS NOT NULL AND symbol IS NULL AND offset = 0)),
191
198
  CHECK (selector = 'expr' OR symbol IS NOT NULL)
192
199
  );
193
- CREATE UNIQUE INDEX operands_current ON operands (space, address) WHERE valid_to_revision IS NULL;
200
+ CREATE INDEX operands_current ON operands (space, address) WHERE valid_to_revision IS NULL;
194
201
  CREATE INDEX operands_address ON operands (space, address);
202
+ CREATE TABLE patches (
203
+ id TEXT PRIMARY KEY,
204
+ space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
205
+ start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
206
+ end_address INTEGER NOT NULL CHECK (end_address BETWEEN start_address AND 65535),
207
+ switch_name TEXT NOT NULL CHECK (length(switch_name) > 0),
208
+ code TEXT NOT NULL,
209
+ comment TEXT CHECK (comment IS NULL OR length(comment) > 0),
210
+ origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
211
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
212
+ valid_to_revision TEXT REFERENCES revisions (id)
213
+ );
214
+ CREATE INDEX patches_start ON patches (space, start_address);
215
+ CREATE TABLE macros (
216
+ id TEXT PRIMARY KEY,
217
+ space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
218
+ name TEXT NOT NULL CHECK (length(name) > 0),
219
+ params TEXT NOT NULL,
220
+ body TEXT NOT NULL CHECK (length(body) > 0),
221
+ comment TEXT CHECK (comment IS NULL OR length(comment) > 0),
222
+ origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
223
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
224
+ valid_to_revision TEXT REFERENCES revisions (id)
225
+ );
226
+ CREATE INDEX macros_name ON macros (space, name);
227
+ CREATE TABLE macro_uses (
228
+ id TEXT PRIMARY KEY,
229
+ space TEXT NOT NULL DEFAULT 'c64' CHECK (${SPACE_CHECK}),
230
+ start_address INTEGER NOT NULL CHECK (start_address BETWEEN 0 AND 65535),
231
+ end_address INTEGER NOT NULL CHECK (end_address BETWEEN start_address AND 65535),
232
+ macro TEXT NOT NULL CHECK (length(macro) > 0),
233
+ args TEXT NOT NULL,
234
+ origin TEXT NOT NULL CHECK (origin IN ('user', 'llm')),
235
+ valid_from_revision TEXT NOT NULL REFERENCES revisions (id),
236
+ valid_to_revision TEXT REFERENCES revisions (id)
237
+ );
238
+ CREATE INDEX macro_uses_start ON macro_uses (space, start_address);
195
239
  CREATE VIEW current_symbols AS SELECT * FROM symbols WHERE valid_to_revision IS NULL;
196
240
  CREATE VIEW current_regions AS SELECT * FROM regions WHERE valid_to_revision IS NULL;
197
241
  CREATE VIEW current_comments AS SELECT * FROM comments WHERE valid_to_revision IS NULL;
@@ -199,52 +243,51 @@ function createSchema(db: DatabaseSync): void {
199
243
  CREATE VIEW current_suppressions AS SELECT * FROM suppressions WHERE valid_to_revision IS NULL;
200
244
  CREATE VIEW current_modules AS SELECT * FROM modules WHERE valid_to_revision IS NULL;
201
245
  CREATE VIEW current_operands AS SELECT * FROM operands WHERE valid_to_revision IS NULL;
202
- INSERT INTO meta (key, value) VALUES ('current_revision', '0');
246
+ CREATE VIEW current_patches AS SELECT * FROM patches WHERE valid_to_revision IS NULL;
247
+ CREATE VIEW current_macros AS SELECT * FROM macros WHERE valid_to_revision IS NULL;
248
+ CREATE VIEW current_macro_uses AS SELECT * FROM macro_uses WHERE valid_to_revision IS NULL;
203
249
  `);
204
250
  }
205
251
 
206
- export function schemaVersion(db: DatabaseSync): number {
252
+ /** The version of the index this build makes. An index with another version is emptied and made again. */
253
+ export const INDEX_VERSION = 2;
254
+
255
+ function indexVersion(db: DatabaseSync): number | undefined {
207
256
  const hasMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'meta'").get();
208
- if (hasMeta === undefined) {
209
- const tables = db.prepare("SELECT count(*) AS n FROM sqlite_master WHERE type = 'table'").get() as { n: number };
210
- if (tables.n > 0) throw new KnowledgeError("invalid-database", "The knowledge database has tables but no meta table; it is not a c64-re-tools database.");
211
- return 0;
212
- }
213
- const row = db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get() as { value: unknown } | undefined;
214
- if (row === undefined) throw new KnowledgeError("invalid-database", "The knowledge database has no schema version.");
215
- const value = String(row.value);
216
- if (!/^(0|[1-9][0-9]{0,8})$/.test(value)) throw new KnowledgeError("invalid-database", `The schema version of the knowledge database is not a whole number: ${JSON.stringify(value)}.`);
217
- return Number(value);
257
+ if (hasMeta === undefined) return undefined;
258
+ const row = db.prepare("SELECT value FROM meta WHERE key = 'index_version'").get() as { value: unknown } | undefined;
259
+ return row === undefined ? undefined : Number(row.value);
218
260
  }
219
261
 
220
- /** Refuses a database with any schema version other than the one this build reads and writes. */
221
- export function checkVersion(version: number): void {
222
- if (version !== SCHEMA_VERSION) {
223
- throw new KnowledgeError(
224
- "unsupported-schema",
225
- `The knowledge database has schema version ${version}. This c64-re-tools reads and writes only version ${SCHEMA_VERSION}. Use the c64-re-tools release that wrote it.`,
226
- );
227
- }
262
+ /** Drops every table and view of the database; the indexes go with their tables. */
263
+ function dropAll(db: DatabaseSync): void {
264
+ const objects = db.prepare("SELECT type, name FROM sqlite_master WHERE name NOT LIKE 'sqlite_%' AND type IN ('view', 'table')").all() as Array<{ type: string; name: string }>;
265
+ for (const { name } of objects.filter((object) => object.type === "view")) db.exec(`DROP VIEW "${name}"`);
266
+ for (const { name } of objects.filter((object) => object.type === "table")) db.exec(`DROP TABLE "${name}"`);
228
267
  }
229
268
 
230
269
  /**
231
- * Creates the schema in an empty database and refuses any other version. The
232
- * version is read again under the write lock, so two processes that open a
233
- * new database at the same time create the schema once.
270
+ * Makes the index schema of this build. An empty database gets it; a database
271
+ * with another index version, or one that is not an index, is emptied first.
272
+ * The version is read again under the write lock, so two processes that open
273
+ * a new index at the same time make the schema once.
234
274
  */
235
275
  export function ensureSchema(db: DatabaseSync): void {
236
- if (schemaVersion(db) === 0) {
237
- db.exec("BEGIN IMMEDIATE");
238
- try {
239
- if (schemaVersion(db) === 0) {
240
- createSchema(db);
241
- db.prepare("INSERT INTO meta (key, value) VALUES ('schema_version', ?)").run(String(SCHEMA_VERSION));
242
- }
243
- db.exec("COMMIT");
244
- } catch (error) {
245
- db.exec("ROLLBACK");
246
- throw error;
276
+ if (indexVersion(db) === INDEX_VERSION) return;
277
+ // PRAGMA foreign_keys has no effect inside a transaction: switch it before.
278
+ db.exec("PRAGMA foreign_keys = OFF");
279
+ db.exec("BEGIN IMMEDIATE");
280
+ try {
281
+ if (indexVersion(db) !== INDEX_VERSION) {
282
+ dropAll(db);
283
+ createSchema(db);
284
+ db.prepare("INSERT INTO meta (key, value) VALUES ('index_version', ?)").run(String(INDEX_VERSION));
247
285
  }
286
+ db.exec("COMMIT");
287
+ } catch (error) {
288
+ db.exec("ROLLBACK");
289
+ throw error;
290
+ } finally {
291
+ db.exec("PRAGMA foreign_keys = ON");
248
292
  }
249
- checkVersion(schemaVersion(db));
250
293
  }