From a45d81993b3a6977c714c015d6ca32503ab27013 Mon Sep 17 00:00:00 2001 From: mathieudutour Date: Wed, 9 Jul 2025 11:00:53 +0200 Subject: [PATCH] Make useSQL work on Windows --- docs/utils-reference/getting-started.md | 4 ++ package-lock.json | 4 +- package.json | 2 +- src/sql-utils.ts | 61 ++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/docs/utils-reference/getting-started.md b/docs/utils-reference/getting-started.md index ec5e8d2..8698f6a 100644 --- a/docs/utils-reference/getting-started.md +++ b/docs/utils-reference/getting-started.md @@ -16,6 +16,10 @@ npm install --save @raycast/utils ## Changelog +### v2.2.0 + +- Make `useSQL` and `executeSQL` work on Windows. + ### v2.1.1 - Fix the default size of `getFavicon`. diff --git a/package-lock.json b/package-lock.json index 50f677b..48f0afc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@raycast/utils", - "version": "2.1.1", + "version": "2.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@raycast/utils", - "version": "2.1.1", + "version": "2.2.0", "license": "MIT", "dependencies": { "dequal": "^2.0.3" diff --git a/package.json b/package.json index 3d99573..6eeccbe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@raycast/utils", - "version": "2.1.1", + "version": "2.2.0", "description": "Set of utilities to streamline building Raycast extensions", "author": "Raycast Technologies Ltd.", "homepage": "https://developers.raycast.com/utilities/getting-started", diff --git a/src/sql-utils.ts b/src/sql-utils.ts index f5f5323..a6bcb0e 100644 --- a/src/sql-utils.ts +++ b/src/sql-utils.ts @@ -28,8 +28,66 @@ export async function baseExecuteSQL( throw new Error("The database does not exist"); } + let sqlite3: typeof import("node:sqlite"); + try { + sqlite3 = require("node:sqlite"); + } catch (error) { + // If sqlite3 is not available, we fallback to using the sqlite3 CLI (available on macOS and Linux by default). + return sqliteFallback(databasePath, query, options); + } + + let db = new sqlite3.DatabaseSync(databasePath, { open: false, readOnly: true }); + + const abortSignal = options?.signal; + + try { + db.open(); + } catch (error: any) { + console.log(error); + if (error.message.match("(5)") || error.message.match("(14)")) { + // That means that the DB is busy because of another app is locking it + // This happens when Chrome or Arc is opened: they lock the History db. + // As an ugly workaround, we duplicate the file and read that instead + // (with vfs unix - none to just not care about locks) + let workaroundCopiedDb: string | undefined; + if (!workaroundCopiedDb) { + const tempFolder = path.join(os.tmpdir(), "useSQL", hash(databasePath)); + await mkdir(tempFolder, { recursive: true }); + checkAborted(abortSignal); + + workaroundCopiedDb = path.join(tempFolder, "db.db"); + await copyFile(databasePath, workaroundCopiedDb); + + await writeFile(workaroundCopiedDb + "-shm", ""); + await writeFile(workaroundCopiedDb + "-wal", ""); + + checkAborted(abortSignal); + } + + db = new sqlite3.DatabaseSync(workaroundCopiedDb, { open: false, readOnly: true }); + db.open(); + checkAborted(abortSignal); + } + } + + const statement = db.prepare(query); + checkAborted(abortSignal); + + const result = statement.all(); + + db.close(); + + return result as T[]; +} + +async function sqliteFallback( + databasePath: string, + query: string, + options?: { + signal?: AbortSignal; + }, +): Promise { const abortSignal = options?.signal; - let workaroundCopiedDb: string | undefined; let spawned = childProcess.spawn("sqlite3", ["--json", "--readonly", databasePath, query], { signal: abortSignal }); let spawnedPromise = getSpawnedPromise(spawned); @@ -45,6 +103,7 @@ export async function baseExecuteSQL( // This happens when Chrome or Arc is opened: they lock the History db. // As an ugly workaround, we duplicate the file and read that instead // (with vfs unix - none to just not care about locks) + let workaroundCopiedDb: string | undefined; if (!workaroundCopiedDb) { const tempFolder = path.join(os.tmpdir(), "useSQL", hash(databasePath)); await mkdir(tempFolder, { recursive: true });