Skip to content

Commit db90702

Browse files
authored
Merge pull request #541 from powersync-ja/node-create-directory
Improve documentation on `dbLocation` and related error message on node
2 parents 7d2a67b + 191e0ce commit db90702

File tree

9 files changed

+68
-21
lines changed

9 files changed

+68
-21
lines changed

.changeset/lazy-moons-prove.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@powersync/node': patch
3+
---
4+
5+
Fix compilation errors on Windows.

.changeset/nice-steaks-approve.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@powersync/node': patch
3+
---
4+
5+
Provide a more actionable error message when using the `dbLocation` option with a directory that doesn't exist.

packages/common/src/client/SQLOpenFactory.ts

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ export interface SQLOpenOptions {
77
dbFilename: string;
88
/**
99
* Directory where the database file is located.
10+
*
11+
* When set, the directory must exist when the database is opened, it will
12+
* not be created automatically.
1013
*/
1114
dbLocation?: string;
1215

packages/node/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"@powersync/common": "workspace:^1.22.0"
4848
},
4949
"dependencies": {
50-
"@powersync/better-sqlite3": "^0.1.0",
50+
"@powersync/better-sqlite3": "^0.1.1",
5151
"@powersync/common": "workspace:*",
5252
"async-lock": "^1.4.0",
5353
"bson": "^6.6.0",

packages/node/src/db/BetterSQLite3DBAdapter.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fs from 'node:fs/promises';
12
import * as path from 'node:path';
23
import { Worker } from 'node:worker_threads';
34
import * as Comlink from 'comlink';
@@ -53,6 +54,19 @@ export class BetterSQLite3DBAdapter extends BaseObserver<DBAdapterListener> impl
5354
async initialize() {
5455
let dbFilePath = this.options.dbFilename;
5556
if (this.options.dbLocation !== undefined) {
57+
// Make sure the dbLocation exists, we get a TypeError from better-sqlite3 otherwise.
58+
let directoryExists = false;
59+
try {
60+
const stat = await fs.stat(this.options.dbLocation);
61+
directoryExists = stat.isDirectory();
62+
} catch (_) {
63+
// If we can't even stat, the directory won't be accessible to SQLite either.
64+
}
65+
66+
if (!directoryExists) {
67+
throw new Error(`The dbLocation directory at "${this.options.dbLocation}" does not exist. Please create it before opening the PowerSync database!`);
68+
}
69+
5670
dbFilePath = path.join(this.options.dbLocation, dbFilePath);
5771
}
5872

@@ -65,7 +79,7 @@ export class BetterSQLite3DBAdapter extends BaseObserver<DBAdapterListener> impl
6579
if (isCommonJsModule) {
6680
worker = workerFactory(path.resolve(__dirname, 'DefaultWorker.cjs'), { name: workerName });
6781
} else {
68-
worker = workerFactory(new URL('./DefaultWorker.js', import.meta.url), { name: workerName});
82+
worker = workerFactory(new URL('./DefaultWorker.js', import.meta.url), { name: workerName });
6983
}
7084

7185
const listeners = new WeakMap<EventListenerOrEventListenerObject, (e: any) => void>();

packages/node/src/db/SqliteWorker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export function startPowerSyncWorker(options?: Partial<PowerSyncWorkerOptions>)
138138

139139
return resolved;
140140
},
141-
...options,
141+
...options
142142
};
143143

144144
Comlink.expose(new BetterSqliteWorker(resolvedOptions), parentPort! as Comlink.Endpoint);

packages/node/tests/PowerSyncDatabase.test.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import * as path from 'node:path';
2+
import * as fs from 'node:fs/promises';
13
import { Worker } from 'node:worker_threads';
2-
import fs from 'node:fs/promises';
34

4-
import { vi, expect, test, onTestFinished } from 'vitest';
5-
import { AppSchema, createTempDir, databaseTest } from './utils';
5+
import { vi, expect, test } from 'vitest';
6+
import { AppSchema, databaseTest, tempDirectoryTest } from './utils';
67
import { PowerSyncDatabase } from '../lib';
78
import { WorkerOpener } from '../lib/db/options';
89

@@ -12,15 +13,14 @@ test('validates options', async () => {
1213
schema: AppSchema,
1314
database: {
1415
dbFilename: '/dev/null',
15-
readWorkerCount: 0,
16+
readWorkerCount: 0
1617
}
1718
});
1819
await database.init();
1920
}).rejects.toThrowError('Needs at least one worker for reads');
2021
});
2122

22-
test('can customize loading workers', async () => {
23-
const directory = await createTempDir();
23+
tempDirectoryTest('can customize loading workers', async ({ tmpdir }) => {
2424
const defaultWorker: WorkerOpener = (...args) => new Worker(...args);
2525

2626
const openFunction = vi.fn(defaultWorker); // Wrap in vi.fn to count invocations
@@ -29,7 +29,7 @@ test('can customize loading workers', async () => {
2929
schema: AppSchema,
3030
database: {
3131
dbFilename: 'test.db',
32-
dbLocation: directory,
32+
dbLocation: tmpdir,
3333
openWorker: openFunction,
3434
readWorkerCount: 2
3535
}
@@ -38,8 +38,6 @@ test('can customize loading workers', async () => {
3838
await database.get('SELECT 1;'); // Make sure the database is ready and works
3939
expect(openFunction).toHaveBeenCalledTimes(3); // One writer, two readers
4040
await database.close();
41-
42-
onTestFinished(async () => fs.rm(directory, { recursive: true }));
4341
});
4442

4543
databaseTest('links powersync', async ({ database }) => {
@@ -95,6 +93,22 @@ databaseTest('can watch tables', async ({ database }) => {
9593
await expect.poll(() => fn).toHaveBeenCalledTimes(2);
9694
});
9795

96+
tempDirectoryTest('throws error if target directory does not exist', async ({ tmpdir }) => {
97+
const directory = path.join(tmpdir, 'some', 'nested', 'location', 'that', 'does', 'not', 'exist');
98+
99+
expect(async () => {
100+
const database = new PowerSyncDatabase({
101+
schema: AppSchema,
102+
database: {
103+
dbFilename: 'test.db',
104+
dbLocation: directory,
105+
readWorkerCount: 2
106+
}
107+
});
108+
await database.waitForReady();
109+
}).rejects.toThrowError(/The dbLocation directory at ".+" does not exist/);
110+
});
111+
98112
databaseTest.skip('can watch queries', async ({ database }) => {
99113
const query = await database.watch('SELECT * FROM todos;', [])[Symbol.asyncIterator]();
100114
expect((await query.next()).value.rows).toHaveLength(0);

packages/node/tests/utils.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,23 @@ export const AppSchema = new Schema({
2323

2424
export type Database = (typeof AppSchema)['types'];
2525

26-
export const databaseTest = test.extend<{ database: PowerSyncDatabase }>({
27-
database: async ({}, use) => {
26+
export const tempDirectoryTest = test.extend<{ tmpdir: string }>({
27+
tmpdir: async ({}, use) => {
2828
const directory = await createTempDir();
29+
await use(directory);
30+
await fs.rm(directory, { recursive: true });
31+
}
32+
});
33+
34+
export const databaseTest = tempDirectoryTest.extend<{ database: PowerSyncDatabase }>({
35+
database: async ({ tmpdir }, use) => {
2936
const database = new PowerSyncDatabase({
3037
schema: AppSchema,
3138
database: {
3239
dbFilename: 'test.db',
33-
dbLocation: directory
40+
dbLocation: tmpdir
3441
}
3542
});
3643
await use(database);
37-
await fs.rm(directory, { recursive: true });
3844
}
3945
});

pnpm-lock.yaml

+5-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)