@context-vault/core 2.17.0 → 3.0.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.
Files changed (101) hide show
  1. package/dist/capture.d.ts +21 -0
  2. package/dist/capture.d.ts.map +1 -0
  3. package/dist/capture.js +269 -0
  4. package/dist/capture.js.map +1 -0
  5. package/dist/categories.d.ts +6 -0
  6. package/dist/categories.d.ts.map +1 -0
  7. package/dist/categories.js +50 -0
  8. package/dist/categories.js.map +1 -0
  9. package/dist/config.d.ts +4 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +190 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/constants.d.ts +33 -0
  14. package/dist/constants.d.ts.map +1 -0
  15. package/dist/constants.js +23 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/db.d.ts +13 -0
  18. package/dist/db.d.ts.map +1 -0
  19. package/dist/db.js +191 -0
  20. package/dist/db.js.map +1 -0
  21. package/dist/embed.d.ts +5 -0
  22. package/dist/embed.d.ts.map +1 -0
  23. package/dist/embed.js +78 -0
  24. package/dist/embed.js.map +1 -0
  25. package/dist/files.d.ts +13 -0
  26. package/dist/files.d.ts.map +1 -0
  27. package/dist/files.js +66 -0
  28. package/dist/files.js.map +1 -0
  29. package/dist/formatters.d.ts +8 -0
  30. package/dist/formatters.d.ts.map +1 -0
  31. package/dist/formatters.js +18 -0
  32. package/dist/formatters.js.map +1 -0
  33. package/dist/frontmatter.d.ts +12 -0
  34. package/dist/frontmatter.d.ts.map +1 -0
  35. package/dist/frontmatter.js +101 -0
  36. package/dist/frontmatter.js.map +1 -0
  37. package/dist/index.d.ts +10 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +297 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/ingest-url.d.ts +20 -0
  42. package/dist/ingest-url.d.ts.map +1 -0
  43. package/dist/ingest-url.js +113 -0
  44. package/dist/ingest-url.js.map +1 -0
  45. package/dist/main.d.ts +14 -0
  46. package/dist/main.d.ts.map +1 -0
  47. package/dist/main.js +25 -0
  48. package/dist/main.js.map +1 -0
  49. package/dist/search.d.ts +18 -0
  50. package/dist/search.d.ts.map +1 -0
  51. package/dist/search.js +238 -0
  52. package/dist/search.js.map +1 -0
  53. package/dist/types.d.ts +176 -0
  54. package/dist/types.d.ts.map +1 -0
  55. package/dist/types.js +2 -0
  56. package/dist/types.js.map +1 -0
  57. package/package.json +66 -17
  58. package/src/capture.ts +308 -0
  59. package/src/categories.ts +54 -0
  60. package/src/{core/config.js → config.ts} +34 -33
  61. package/src/{constants.js → constants.ts} +6 -3
  62. package/src/db.ts +229 -0
  63. package/src/{index/embed.js → embed.ts} +10 -35
  64. package/src/files.ts +80 -0
  65. package/src/{capture/formatters.js → formatters.ts} +13 -11
  66. package/src/{core/frontmatter.js → frontmatter.ts} +27 -33
  67. package/src/index.ts +351 -0
  68. package/src/ingest-url.ts +99 -0
  69. package/src/main.ts +111 -0
  70. package/src/search.ts +285 -0
  71. package/src/types.ts +166 -0
  72. package/src/capture/file-ops.js +0 -97
  73. package/src/capture/import-pipeline.js +0 -46
  74. package/src/capture/importers.js +0 -387
  75. package/src/capture/index.js +0 -236
  76. package/src/capture/ingest-url.js +0 -252
  77. package/src/consolidation/index.js +0 -112
  78. package/src/core/categories.js +0 -72
  79. package/src/core/error-log.js +0 -54
  80. package/src/core/files.js +0 -108
  81. package/src/core/status.js +0 -350
  82. package/src/core/telemetry.js +0 -90
  83. package/src/index/db.js +0 -416
  84. package/src/index/index.js +0 -522
  85. package/src/index.js +0 -66
  86. package/src/retrieve/index.js +0 -500
  87. package/src/server/helpers.js +0 -44
  88. package/src/server/tools/clear-context.js +0 -47
  89. package/src/server/tools/context-status.js +0 -182
  90. package/src/server/tools/create-snapshot.js +0 -231
  91. package/src/server/tools/delete-context.js +0 -60
  92. package/src/server/tools/get-context.js +0 -678
  93. package/src/server/tools/ingest-project.js +0 -244
  94. package/src/server/tools/ingest-url.js +0 -88
  95. package/src/server/tools/list-buckets.js +0 -116
  96. package/src/server/tools/list-context.js +0 -163
  97. package/src/server/tools/save-context.js +0 -609
  98. package/src/server/tools/session-start.js +0 -285
  99. package/src/server/tools/submit-feedback.js +0 -55
  100. package/src/server/tools.js +0 -174
  101. package/src/sync/sync.js +0 -235
package/src/index/db.js DELETED
@@ -1,416 +0,0 @@
1
- import { unlinkSync, copyFileSync, existsSync } from "node:fs";
2
- import { DatabaseSync } from "node:sqlite";
3
-
4
- export class NativeModuleError extends Error {
5
- constructor(originalError) {
6
- const diagnostic = formatNativeModuleError(originalError);
7
- super(diagnostic);
8
- this.name = "NativeModuleError";
9
- this.originalError = originalError;
10
- }
11
- }
12
-
13
- function formatNativeModuleError(err) {
14
- const msg = err.message || "";
15
- return [
16
- `sqlite-vec extension failed to load: ${msg}`,
17
- "",
18
- ` Running Node.js: ${process.version} (${process.execPath})`,
19
- "",
20
- " Fix: Reinstall context-vault:",
21
- " npx -y context-vault@latest setup",
22
- ].join("\n");
23
- }
24
-
25
- let _sqliteVec = null;
26
-
27
- async function loadSqliteVec() {
28
- if (_sqliteVec) return _sqliteVec;
29
- const vecMod = await import("sqlite-vec");
30
- _sqliteVec = vecMod;
31
- return _sqliteVec;
32
- }
33
-
34
- function runTransaction(db, fn) {
35
- db.exec("BEGIN");
36
- try {
37
- fn();
38
- db.exec("COMMIT");
39
- } catch (e) {
40
- db.exec("ROLLBACK");
41
- throw e;
42
- }
43
- }
44
-
45
- export const SCHEMA_DDL = `
46
- CREATE TABLE IF NOT EXISTS vault (
47
- id TEXT PRIMARY KEY,
48
- kind TEXT NOT NULL,
49
- category TEXT NOT NULL DEFAULT 'knowledge',
50
- title TEXT,
51
- body TEXT NOT NULL,
52
- meta TEXT,
53
- tags TEXT,
54
- source TEXT,
55
- file_path TEXT UNIQUE,
56
- identity_key TEXT,
57
- expires_at TEXT,
58
- superseded_by TEXT,
59
- created_at TEXT DEFAULT (datetime('now')),
60
- updated_at TEXT,
61
- user_id TEXT,
62
- team_id TEXT,
63
- body_encrypted BLOB,
64
- title_encrypted BLOB,
65
- meta_encrypted BLOB,
66
- iv BLOB,
67
- hit_count INTEGER DEFAULT 0,
68
- last_accessed_at TEXT,
69
- source_files TEXT,
70
- tier TEXT DEFAULT 'working' CHECK(tier IN ('ephemeral', 'working', 'durable'))
71
- );
72
-
73
- CREATE INDEX IF NOT EXISTS idx_vault_kind ON vault(kind);
74
- CREATE INDEX IF NOT EXISTS idx_vault_category ON vault(category);
75
- CREATE INDEX IF NOT EXISTS idx_vault_category_created ON vault(category, created_at DESC);
76
- CREATE INDEX IF NOT EXISTS idx_vault_updated ON vault(updated_at DESC);
77
- CREATE INDEX IF NOT EXISTS idx_vault_user ON vault(user_id);
78
- CREATE INDEX IF NOT EXISTS idx_vault_team ON vault(team_id);
79
- CREATE UNIQUE INDEX IF NOT EXISTS idx_vault_identity ON vault(user_id, kind, identity_key) WHERE identity_key IS NOT NULL;
80
- CREATE INDEX IF NOT EXISTS idx_vault_superseded ON vault(superseded_by) WHERE superseded_by IS NOT NULL;
81
- CREATE INDEX IF NOT EXISTS idx_vault_tier ON vault(tier);
82
-
83
- -- Single FTS5 table
84
- CREATE VIRTUAL TABLE IF NOT EXISTS vault_fts USING fts5(
85
- title, body, tags, kind,
86
- content='vault', content_rowid='rowid'
87
- );
88
-
89
- -- FTS sync triggers
90
- CREATE TRIGGER IF NOT EXISTS vault_ai AFTER INSERT ON vault BEGIN
91
- INSERT INTO vault_fts(rowid, title, body, tags, kind)
92
- VALUES (new.rowid, new.title, new.body, new.tags, new.kind);
93
- END;
94
- CREATE TRIGGER IF NOT EXISTS vault_ad AFTER DELETE ON vault BEGIN
95
- INSERT INTO vault_fts(vault_fts, rowid, title, body, tags, kind)
96
- VALUES ('delete', old.rowid, old.title, old.body, old.tags, old.kind);
97
- END;
98
- CREATE TRIGGER IF NOT EXISTS vault_au AFTER UPDATE ON vault BEGIN
99
- INSERT INTO vault_fts(vault_fts, rowid, title, body, tags, kind)
100
- VALUES ('delete', old.rowid, old.title, old.body, old.tags, old.kind);
101
- INSERT INTO vault_fts(rowid, title, body, tags, kind)
102
- VALUES (new.rowid, new.title, new.body, new.tags, new.kind);
103
- END;
104
-
105
- -- Single vec table (384-dim float32 for all-MiniLM-L6-v2)
106
- CREATE VIRTUAL TABLE IF NOT EXISTS vault_vec USING vec0(embedding float[384]);
107
- `;
108
-
109
- export async function initDatabase(dbPath) {
110
- const sqliteVec = await loadSqliteVec();
111
-
112
- function createDb(path) {
113
- const db = new DatabaseSync(path, { allowExtension: true });
114
- db.exec("PRAGMA journal_mode = WAL");
115
- db.exec("PRAGMA foreign_keys = ON");
116
- try {
117
- sqliteVec.load(db);
118
- } catch (e) {
119
- throw new NativeModuleError(e);
120
- }
121
- return db;
122
- }
123
-
124
- const db = createDb(dbPath);
125
- const version = db.prepare("PRAGMA user_version").get().user_version;
126
-
127
- // Enforce fresh-DB-only — old schemas get a full rebuild (with backup)
128
- if (version > 0 && version < 5) {
129
- console.error(
130
- `[context-vault] Schema v${version} is outdated. Rebuilding database...`,
131
- );
132
-
133
- // Backup old DB before destroying it
134
- const backupPath = `${dbPath}.v${version}.backup`;
135
- try {
136
- db.close();
137
- if (existsSync(dbPath)) {
138
- copyFileSync(dbPath, backupPath);
139
- console.error(
140
- `[context-vault] Backed up old database to: ${backupPath}`,
141
- );
142
- }
143
- } catch (backupErr) {
144
- console.error(
145
- `[context-vault] Warning: could not backup old database: ${backupErr.message}`,
146
- );
147
- }
148
-
149
- unlinkSync(dbPath);
150
- try {
151
- unlinkSync(dbPath + "-wal");
152
- } catch {}
153
- try {
154
- unlinkSync(dbPath + "-shm");
155
- } catch {}
156
-
157
- const freshDb = createDb(dbPath);
158
- freshDb.exec(SCHEMA_DDL);
159
- freshDb.exec("PRAGMA user_version = 12");
160
- return freshDb;
161
- }
162
-
163
- if (version < 5) {
164
- db.exec(SCHEMA_DDL);
165
- db.exec("PRAGMA user_version = 12");
166
- } else if (version === 5) {
167
- // v5 -> v6 migration: add multi-tenancy + encryption columns
168
- // Wrapped in transaction with duplicate-column guards for idempotent retry
169
- runTransaction(db, () => {
170
- const addColumnSafe = (sql) => {
171
- try {
172
- db.exec(sql);
173
- } catch (e) {
174
- if (!e.message.includes("duplicate column")) throw e;
175
- }
176
- };
177
- addColumnSafe(`ALTER TABLE vault ADD COLUMN user_id TEXT`);
178
- addColumnSafe(`ALTER TABLE vault ADD COLUMN body_encrypted BLOB`);
179
- addColumnSafe(`ALTER TABLE vault ADD COLUMN title_encrypted BLOB`);
180
- addColumnSafe(`ALTER TABLE vault ADD COLUMN meta_encrypted BLOB`);
181
- addColumnSafe(`ALTER TABLE vault ADD COLUMN iv BLOB`);
182
- addColumnSafe(`ALTER TABLE vault ADD COLUMN team_id TEXT`);
183
- addColumnSafe(`ALTER TABLE vault ADD COLUMN updated_at TEXT`);
184
- addColumnSafe(`ALTER TABLE vault ADD COLUMN superseded_by TEXT`);
185
- db.exec(`CREATE INDEX IF NOT EXISTS idx_vault_user ON vault(user_id)`);
186
- db.exec(`CREATE INDEX IF NOT EXISTS idx_vault_team ON vault(team_id)`);
187
- db.exec(`DROP INDEX IF EXISTS idx_vault_identity`);
188
- db.exec(
189
- `CREATE UNIQUE INDEX IF NOT EXISTS idx_vault_identity ON vault(user_id, kind, identity_key) WHERE identity_key IS NOT NULL`,
190
- );
191
- db.exec(
192
- `UPDATE vault SET updated_at = created_at WHERE updated_at IS NULL`,
193
- );
194
- db.exec(
195
- `CREATE INDEX IF NOT EXISTS idx_vault_updated ON vault(updated_at DESC)`,
196
- );
197
- db.exec(
198
- `CREATE INDEX IF NOT EXISTS idx_vault_superseded ON vault(superseded_by) WHERE superseded_by IS NOT NULL`,
199
- );
200
- addColumnSafe(`ALTER TABLE vault ADD COLUMN hit_count INTEGER DEFAULT 0`);
201
- addColumnSafe(`ALTER TABLE vault ADD COLUMN last_accessed_at TEXT`);
202
- db.exec("PRAGMA user_version = 10");
203
- });
204
- } else if (version === 6) {
205
- // v6 -> v7+v8+v9 migration: add team_id, updated_at, superseded_by columns
206
- runTransaction(db, () => {
207
- try {
208
- db.exec(`ALTER TABLE vault ADD COLUMN team_id TEXT`);
209
- } catch (e) {
210
- if (!e.message.includes("duplicate column")) throw e;
211
- }
212
- try {
213
- db.exec(`ALTER TABLE vault ADD COLUMN updated_at TEXT`);
214
- } catch (e) {
215
- if (!e.message.includes("duplicate column")) throw e;
216
- }
217
- try {
218
- db.exec(`ALTER TABLE vault ADD COLUMN superseded_by TEXT`);
219
- } catch (e) {
220
- if (!e.message.includes("duplicate column")) throw e;
221
- }
222
- db.exec(`CREATE INDEX IF NOT EXISTS idx_vault_team ON vault(team_id)`);
223
- db.exec(
224
- `UPDATE vault SET updated_at = created_at WHERE updated_at IS NULL`,
225
- );
226
- db.exec(
227
- `CREATE INDEX IF NOT EXISTS idx_vault_updated ON vault(updated_at DESC)`,
228
- );
229
- db.exec(
230
- `CREATE INDEX IF NOT EXISTS idx_vault_superseded ON vault(superseded_by) WHERE superseded_by IS NOT NULL`,
231
- );
232
- try {
233
- db.exec(`ALTER TABLE vault ADD COLUMN hit_count INTEGER DEFAULT 0`);
234
- } catch (e) {
235
- if (!e.message.includes("duplicate column")) throw e;
236
- }
237
- try {
238
- db.exec(`ALTER TABLE vault ADD COLUMN last_accessed_at TEXT`);
239
- } catch (e) {
240
- if (!e.message.includes("duplicate column")) throw e;
241
- }
242
- db.exec("PRAGMA user_version = 10");
243
- });
244
- } else if (version === 7) {
245
- // v7 -> v8+v9 migration: add updated_at, superseded_by columns
246
- runTransaction(db, () => {
247
- try {
248
- db.exec(`ALTER TABLE vault ADD COLUMN updated_at TEXT`);
249
- } catch (e) {
250
- if (!e.message.includes("duplicate column")) throw e;
251
- }
252
- try {
253
- db.exec(`ALTER TABLE vault ADD COLUMN superseded_by TEXT`);
254
- } catch (e) {
255
- if (!e.message.includes("duplicate column")) throw e;
256
- }
257
- db.exec(
258
- `UPDATE vault SET updated_at = created_at WHERE updated_at IS NULL`,
259
- );
260
- db.exec(
261
- `CREATE INDEX IF NOT EXISTS idx_vault_updated ON vault(updated_at DESC)`,
262
- );
263
- db.exec(
264
- `CREATE INDEX IF NOT EXISTS idx_vault_superseded ON vault(superseded_by) WHERE superseded_by IS NOT NULL`,
265
- );
266
- try {
267
- db.exec(`ALTER TABLE vault ADD COLUMN hit_count INTEGER DEFAULT 0`);
268
- } catch (e) {
269
- if (!e.message.includes("duplicate column")) throw e;
270
- }
271
- try {
272
- db.exec(`ALTER TABLE vault ADD COLUMN last_accessed_at TEXT`);
273
- } catch (e) {
274
- if (!e.message.includes("duplicate column")) throw e;
275
- }
276
- db.exec("PRAGMA user_version = 10");
277
- });
278
- } else if (version === 8) {
279
- // v8 -> v9 migration: add superseded_by column
280
- runTransaction(db, () => {
281
- try {
282
- db.exec(`ALTER TABLE vault ADD COLUMN superseded_by TEXT`);
283
- } catch (e) {
284
- if (!e.message.includes("duplicate column")) throw e;
285
- }
286
- db.exec(
287
- `CREATE INDEX IF NOT EXISTS idx_vault_superseded ON vault(superseded_by) WHERE superseded_by IS NOT NULL`,
288
- );
289
- db.exec("PRAGMA user_version = 9");
290
- });
291
- // fall through to v9 migration
292
- runTransaction(db, () => {
293
- const addColumnSafe = (sql) => {
294
- try {
295
- db.exec(sql);
296
- } catch (e) {
297
- if (!e.message.includes("duplicate column")) throw e;
298
- }
299
- };
300
- addColumnSafe(`ALTER TABLE vault ADD COLUMN hit_count INTEGER DEFAULT 0`);
301
- addColumnSafe(`ALTER TABLE vault ADD COLUMN last_accessed_at TEXT`);
302
- db.exec("PRAGMA user_version = 10");
303
- });
304
- } else if (version === 9) {
305
- // v9 -> v10 migration: add hit_count + last_accessed_at columns
306
- runTransaction(db, () => {
307
- const addColumnSafe = (sql) => {
308
- try {
309
- db.exec(sql);
310
- } catch (e) {
311
- if (!e.message.includes("duplicate column")) throw e;
312
- }
313
- };
314
- addColumnSafe(`ALTER TABLE vault ADD COLUMN hit_count INTEGER DEFAULT 0`);
315
- addColumnSafe(`ALTER TABLE vault ADD COLUMN last_accessed_at TEXT`);
316
- db.exec("PRAGMA user_version = 10");
317
- });
318
- }
319
-
320
- if (version >= 5 && version <= 10) {
321
- // v10 -> v11 migration: add source_files column for stale-linking
322
- runTransaction(db, () => {
323
- try {
324
- db.exec(`ALTER TABLE vault ADD COLUMN source_files TEXT`);
325
- } catch (e) {
326
- if (!e.message.includes("duplicate column")) throw e;
327
- }
328
- db.exec("PRAGMA user_version = 11");
329
- });
330
- }
331
-
332
- if (version >= 5 && version <= 11) {
333
- // v11 -> v12 migration: add tier column for memory tiers
334
- runTransaction(db, () => {
335
- try {
336
- db.exec(
337
- `ALTER TABLE vault ADD COLUMN tier TEXT DEFAULT 'working' CHECK(tier IN ('ephemeral', 'working', 'durable'))`,
338
- );
339
- } catch (e) {
340
- if (!e.message.includes("duplicate column")) throw e;
341
- }
342
- db.exec(`CREATE INDEX IF NOT EXISTS idx_vault_tier ON vault(tier)`);
343
- db.exec("PRAGMA user_version = 12");
344
- });
345
- }
346
-
347
- return db;
348
- }
349
-
350
- export function prepareStatements(db) {
351
- try {
352
- return {
353
- insertEntry: db.prepare(
354
- `INSERT INTO vault (id, user_id, kind, category, title, body, meta, tags, source, file_path, identity_key, expires_at, created_at, updated_at, source_files, tier) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
355
- ),
356
- insertEntryEncrypted: db.prepare(
357
- `INSERT INTO vault (id, user_id, kind, category, title, body, meta, tags, source, file_path, identity_key, expires_at, created_at, updated_at, body_encrypted, title_encrypted, meta_encrypted, iv, source_files, tier) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
358
- ),
359
- updateEntry: db.prepare(
360
- `UPDATE vault SET title = ?, body = ?, meta = ?, tags = ?, source = ?, category = ?, identity_key = ?, expires_at = ?, updated_at = datetime('now') WHERE file_path = ?`,
361
- ),
362
- deleteEntry: db.prepare(`DELETE FROM vault WHERE id = ?`),
363
- getRowid: db.prepare(`SELECT rowid FROM vault WHERE id = ?`),
364
- getRowidByPath: db.prepare(`SELECT rowid FROM vault WHERE file_path = ?`),
365
- getEntryById: db.prepare(`SELECT * FROM vault WHERE id = ?`),
366
- getByIdentityKey: db.prepare(
367
- `SELECT * FROM vault WHERE kind = ? AND identity_key = ? AND user_id IS ?`,
368
- ),
369
- upsertByIdentityKey: db.prepare(
370
- `UPDATE vault SET title = ?, body = ?, meta = ?, tags = ?, source = ?, category = ?, file_path = ?, expires_at = ?, source_files = ?, updated_at = datetime('now') WHERE kind = ? AND identity_key = ? AND user_id IS ?`,
371
- ),
372
- updateSourceFiles: db.prepare(
373
- `UPDATE vault SET source_files = ? WHERE id = ?`,
374
- ),
375
- insertVecStmt: db.prepare(
376
- `INSERT INTO vault_vec (rowid, embedding) VALUES (?, ?)`,
377
- ),
378
- deleteVecStmt: db.prepare(`DELETE FROM vault_vec WHERE rowid = ?`),
379
- updateSupersededBy: db.prepare(
380
- `UPDATE vault SET superseded_by = ? WHERE id = ?`,
381
- ),
382
- clearSupersededByRef: db.prepare(
383
- `UPDATE vault SET superseded_by = NULL WHERE superseded_by = ?`,
384
- ),
385
- };
386
- } catch (e) {
387
- throw new Error(
388
- `Failed to prepare database statements. The database may be corrupted.\n` +
389
- `Try deleting and rebuilding: context-vault reindex\n` +
390
- `Original error: ${e.message}`,
391
- );
392
- }
393
- }
394
-
395
- export function insertVec(stmts, rowid, embedding) {
396
- // sqlite-vec requires BigInt for primary key — node:sqlite may bind Number as REAL
397
- // for vec0 virtual tables which only accept INTEGER rowids
398
- const safeRowid = BigInt(rowid);
399
- if (safeRowid < 1n) throw new Error(`Invalid rowid: ${rowid}`);
400
- stmts.insertVecStmt.run(safeRowid, embedding);
401
- }
402
-
403
- export function deleteVec(stmts, rowid) {
404
- const safeRowid = BigInt(rowid);
405
- if (safeRowid < 1n) throw new Error(`Invalid rowid: ${rowid}`);
406
- stmts.deleteVecStmt.run(safeRowid);
407
- }
408
-
409
- export function testConnection(db) {
410
- try {
411
- db.prepare("SELECT 1").get();
412
- return true;
413
- } catch {
414
- return false;
415
- }
416
- }