Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/utils-reference/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
61 changes: 60 additions & 1 deletion src/sql-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,66 @@ export async function baseExecuteSQL<T = unknown>(
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<T>(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<T = unknown>(
databasePath: string,
query: string,
options?: {
signal?: AbortSignal;
},
): Promise<T[]> {
const abortSignal = options?.signal;
let workaroundCopiedDb: string | undefined;

let spawned = childProcess.spawn("sqlite3", ["--json", "--readonly", databasePath, query], { signal: abortSignal });
let spawnedPromise = getSpawnedPromise(spawned);
Expand All @@ -45,6 +103,7 @@ export async function baseExecuteSQL<T = unknown>(
// 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 });
Expand Down
Loading