@tikoci/rosetta 0.8.0 → 0.8.1
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/package.json +1 -1
- package/src/mcp.ts +5 -2
- package/src/release.test.ts +16 -0
- package/src/setup.test.ts +31 -1
- package/src/setup.ts +4 -2
package/package.json
CHANGED
package/src/mcp.ts
CHANGED
|
@@ -66,10 +66,13 @@ async function ensureDbReady(log: (msg: string) => void): Promise<void> {
|
|
|
66
66
|
const dbPath = resolveDbPath(import.meta.dirname);
|
|
67
67
|
const runningVersion = resolveVersion(import.meta.dirname);
|
|
68
68
|
|
|
69
|
-
/** Probe an existing DB. Returns null if the file is missing or unreadable.
|
|
69
|
+
/** Probe an existing DB. Returns null if the file is missing or unreadable.
|
|
70
|
+
* Do NOT pass { readonly: true } — freshly written SQLite WAL-mode files fail
|
|
71
|
+
* to open readonly on macOS until a read-write connection initialises the WAL
|
|
72
|
+
* shared-memory file. Same gotcha as setup.ts::probeDb. */
|
|
70
73
|
function probe(): { pages: number; schemaVersion: number; releaseTag: string | null } | null {
|
|
71
74
|
try {
|
|
72
|
-
const check = new sqlite(dbPath
|
|
75
|
+
const check = new sqlite(dbPath);
|
|
73
76
|
const pages = (check.prepare("SELECT COUNT(*) AS c FROM pages").get() as { c: number }).c;
|
|
74
77
|
const ver = (check.prepare("PRAGMA user_version").get() as { user_version: number }).user_version;
|
|
75
78
|
let releaseTag: string | null = null;
|
package/src/release.test.ts
CHANGED
|
@@ -202,6 +202,22 @@ describe("setup.ts", () => {
|
|
|
202
202
|
expect(src).toContain("Still incompatible after re-download");
|
|
203
203
|
expect(src).toMatch(/Still incompatible[\s\S]{0,400}process\.exit\(1\)/);
|
|
204
204
|
});
|
|
205
|
+
|
|
206
|
+
test("no DB open uses { readonly: true } (WAL-shm init trap on macOS)", () => {
|
|
207
|
+
// Freshly-renamed WAL-mode DBs fail to open readonly on macOS until a
|
|
208
|
+
// read-write connection initialises the .shm file. downloadDb explicitly
|
|
209
|
+
// deletes .wal/.shm before the rename, so every subsequent open must be
|
|
210
|
+
// read-write. Regressing this ships a bunx path that can't open its own
|
|
211
|
+
// validated download (see v0.8.0 "DB=unreadable" bug).
|
|
212
|
+
for (const file of ["src/mcp.ts", "src/setup.ts", "src/db.ts"]) {
|
|
213
|
+
// Strip line + block comments before scanning, so the "do NOT pass
|
|
214
|
+
// { readonly: true }" warnings themselves don't trip the check.
|
|
215
|
+
const src = readText(file)
|
|
216
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
217
|
+
.replace(/\/\/.*$/gm, "");
|
|
218
|
+
expect(src).not.toMatch(/readonly\s*:\s*true/);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
205
221
|
});
|
|
206
222
|
|
|
207
223
|
// ---------------------------------------------------------------------------
|
package/src/setup.test.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import sqlite from "bun:sqlite";
|
|
10
10
|
import { afterAll, describe, expect, test } from "bun:test";
|
|
11
|
-
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { existsSync, mkdtempSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { tmpdir } from "node:os";
|
|
13
13
|
import path from "node:path";
|
|
14
14
|
|
|
@@ -91,6 +91,36 @@ describe("probeDb", () => {
|
|
|
91
91
|
expect(probe?.releaseTag).toBe("v0.0.0-test");
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
+
test("opens a freshly-renamed WAL-mode DB with no .shm sibling", () => {
|
|
95
|
+
// Reproduces the exact state downloadDb leaves the DB in: journal_mode=WAL
|
|
96
|
+
// on disk, but the .wal/.shm siblings are deleted just before the rename.
|
|
97
|
+
// On macOS, opening such a file with { readonly: true } fails with
|
|
98
|
+
// "unable to open database file" because SQLite cannot create the shm
|
|
99
|
+
// from a read-only handle. probeDb intentionally opens read-write.
|
|
100
|
+
const dbFile = path.join(tmp, "wal-no-shm.db");
|
|
101
|
+
const seed = new sqlite(dbFile);
|
|
102
|
+
seed.exec("PRAGMA journal_mode = WAL");
|
|
103
|
+
seed.run(`PRAGMA user_version = ${SCHEMA_VERSION};`);
|
|
104
|
+
seed.run("CREATE TABLE pages (id INTEGER PRIMARY KEY);");
|
|
105
|
+
seed.run("CREATE TABLE commands (id INTEGER PRIMARY KEY);");
|
|
106
|
+
seed.run("INSERT INTO pages (id) VALUES (1), (2), (3);");
|
|
107
|
+
seed.run("INSERT INTO commands (id) VALUES (1), (2);");
|
|
108
|
+
seed.close();
|
|
109
|
+
|
|
110
|
+
// Simulate downloadDb's post-rename cleanup — WAL file on disk marks WAL
|
|
111
|
+
// mode in the header, but the transient .wal/.shm are gone.
|
|
112
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
113
|
+
const p = dbFile + suffix;
|
|
114
|
+
if (existsSync(p)) unlinkSync(p);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const probe = probeDb(dbFile);
|
|
118
|
+
expect(probe).not.toBeNull();
|
|
119
|
+
expect(probe?.schemaVersion).toBe(SCHEMA_VERSION);
|
|
120
|
+
expect(probe?.pages).toBe(3);
|
|
121
|
+
expect(probe?.commands).toBe(2);
|
|
122
|
+
});
|
|
123
|
+
|
|
94
124
|
test("releaseTag is null when db_meta is absent (pre-v5 schema)", () => {
|
|
95
125
|
const dbFile = path.join(tmp, "no-meta.db");
|
|
96
126
|
const db = new sqlite(dbFile);
|
package/src/setup.ts
CHANGED
|
@@ -26,12 +26,14 @@ const MIN_COMMANDS = 1000;
|
|
|
26
26
|
const MIN_DECOMPRESSED_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
27
27
|
const SQLITE_MAGIC = "SQLite format 3\0";
|
|
28
28
|
|
|
29
|
-
/** Check if a DB file exists and has actual page data
|
|
29
|
+
/** Check if a DB file exists and has actual page data.
|
|
30
|
+
* Opens read-write — see probeDb's note: freshly written WAL-mode files can
|
|
31
|
+
* fail to open readonly on macOS when the .shm file is missing. */
|
|
30
32
|
function dbHasData(dbPath: string): boolean {
|
|
31
33
|
if (!existsSync(dbPath)) return false;
|
|
32
34
|
try {
|
|
33
35
|
const { default: sqlite } = require("bun:sqlite");
|
|
34
|
-
const check = new sqlite(dbPath
|
|
36
|
+
const check = new sqlite(dbPath);
|
|
35
37
|
const row = check.prepare("SELECT COUNT(*) AS c FROM pages").get() as { c: number };
|
|
36
38
|
check.close();
|
|
37
39
|
return row.c > 0;
|