@@ -28,6 +28,31 @@ import type {
|
28 | 28 | const DEBUG_PROXY_CAPTURE_DIR_MODE = 0o700; |
29 | 29 | const DEBUG_PROXY_CAPTURE_FILE_MODE = 0o600; |
30 | 30 | |
| 31 | +function isInMemoryDatabasePath(dbPath: string): boolean { |
| 32 | +if (dbPath === ":memory:") { |
| 33 | +return true; |
| 34 | +} |
| 35 | +if (!dbPath.startsWith("file:")) { |
| 36 | +return false; |
| 37 | +} |
| 38 | +const fragmentIndex = dbPath.indexOf("#"); |
| 39 | +const uriWithoutFragment = fragmentIndex === -1 ? dbPath : dbPath.slice(0, fragmentIndex); |
| 40 | +const queryIndex = uriWithoutFragment.indexOf("?"); |
| 41 | +const uriPath = |
| 42 | +queryIndex === -1 ? uriWithoutFragment : uriWithoutFragment.slice(0, queryIndex); |
| 43 | +try { |
| 44 | +if (decodeURIComponent(uriPath.slice("file:".length)) === ":memory:") { |
| 45 | +return true; |
| 46 | +} |
| 47 | +} catch { |
| 48 | +// Malformed escapes cannot identify a memory URI; retain file-backed handling. |
| 49 | +} |
| 50 | +return ( |
| 51 | +queryIndex !== -1 && |
| 52 | +new URLSearchParams(uriWithoutFragment.slice(queryIndex + 1)).get("mode") === "memory" |
| 53 | +); |
| 54 | +} |
| 55 | + |
31 | 56 | function ensureParentDir(filePath: string) { |
32 | 57 | fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: DEBUG_PROXY_CAPTURE_DIR_MODE }); |
33 | 58 | } |
@@ -46,19 +71,24 @@ type OpenedDatabase = {
|
46 | 71 | }; |
47 | 72 | |
48 | 73 | function openDatabase(dbPath: string): OpenedDatabase { |
49 | | -ensureParentDir(dbPath); |
50 | | -if (!fs.existsSync(dbPath)) { |
51 | | -fs.closeSync(fs.openSync(dbPath, "a", DEBUG_PROXY_CAPTURE_FILE_MODE)); |
| 74 | +const fileBackedPath = isInMemoryDatabasePath(dbPath) ? undefined : dbPath; |
| 75 | +if (fileBackedPath) { |
| 76 | +ensureParentDir(fileBackedPath); |
| 77 | +if (!fs.existsSync(fileBackedPath)) { |
| 78 | +fs.closeSync(fs.openSync(fileBackedPath, "a", DEBUG_PROXY_CAPTURE_FILE_MODE)); |
| 79 | +} |
52 | 80 | } |
53 | 81 | const { DatabaseSync } = requireNodeSqlite(); |
54 | 82 | const db = new DatabaseSync(dbPath); |
55 | 83 | let walMaintenance: SqliteWalMaintenance | undefined; |
56 | 84 | try { |
57 | | -applyPrivateModeSync(dbPath, DEBUG_PROXY_CAPTURE_FILE_MODE); |
| 85 | +if (fileBackedPath) { |
| 86 | +applyPrivateModeSync(fileBackedPath, DEBUG_PROXY_CAPTURE_FILE_MODE); |
| 87 | +} |
58 | 88 | walMaintenance = configureSqliteConnectionPragmas(db, { |
59 | 89 | busyTimeoutMs: 5000, |
60 | 90 | databaseLabel: "debug-proxy-capture", |
61 | | -databasePath: dbPath, |
| 91 | +...(fileBackedPath ? { databasePath: fileBackedPath } : {}), |
62 | 92 | }); |
63 | 93 | db.exec(` |
64 | 94 | CREATE TABLE IF NOT EXISTS capture_sessions ( |
@@ -98,7 +128,9 @@ function openDatabase(dbPath: string): OpenedDatabase {
|
98 | 128 | CREATE INDEX IF NOT EXISTS capture_events_session_ts_idx ON capture_events(session_id, ts); |
99 | 129 | CREATE INDEX IF NOT EXISTS capture_events_flow_idx ON capture_events(flow_id, ts); |
100 | 130 | `); |
101 | | -hardenDatabaseFiles(dbPath); |
| 131 | +if (fileBackedPath) { |
| 132 | +hardenDatabaseFiles(fileBackedPath); |
| 133 | +} |
102 | 134 | return { db, walMaintenance }; |
103 | 135 | } catch (err) { |
104 | 136 | walMaintenance?.close(); |
|