Skip to content

Commit a3df907

Browse files
Use node:sqlite on node version >= 22
1 parent dcb0901 commit a3df907

File tree

7 files changed

+219
-23
lines changed

7 files changed

+219
-23
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- name: Setup Node.js
2424
uses: actions/setup-node@v4
2525
with:
26-
node-version: "20"
26+
node-version: "24"
2727

2828
- name: Cache node_modules
2929
uses: actions/cache@v4
@@ -33,7 +33,7 @@ jobs:
3333
~/.nvm
3434
~/work/better-sqlite3-wrapper/better-sqlite3-wrapper/node_modules
3535
~/work/better-sqlite3-wrapper/better-sqlite3-wrapper/package-lock.json
36-
key: ${{ runner.os }}-node_modules-cache-v4-${{ hashFiles('**/package-lock.json') }}
36+
key: ${{ runner.os }}-node_modules-cache-v4-${{ hashFiles('package.json') }}
3737
restore-keys: |
3838
${{ runner.os }}-node_modules-cache-v4-
3939

.github/workflows/ci_bun.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ on:
1111
jobs:
1212
build:
1313
runs-on: ubuntu-latest
14+
timeout-minutes: 20
1415
steps:
1516
- name: Git checkout
1617
uses: actions/checkout@v4

.github/workflows/ci_matrix.yml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Node Matrix CI
2+
3+
on:
4+
push:
5+
paths-ignore:
6+
- "*.md"
7+
branches: [main]
8+
pull_request:
9+
branches: [main]
10+
11+
jobs:
12+
build:
13+
strategy:
14+
matrix:
15+
os:
16+
- ubuntu-20.04
17+
- macos-latest
18+
- macos-14
19+
- windows-2019
20+
node:
21+
- 16
22+
- 18
23+
- 20
24+
- 22
25+
name: Testing Node ${{ matrix.node }} on ${{ matrix.os }}
26+
runs-on: ${{ matrix.os }}
27+
timeout-minutes: 20
28+
steps:
29+
- name: Git checkout
30+
uses: actions/checkout@v4
31+
with:
32+
fetch-depth: 0
33+
34+
- name: Setup Node.js
35+
uses: actions/setup-node@v4
36+
with:
37+
node-version: ${{ matrix.node }}
38+
39+
- name: Cache node_modules on non-windows os
40+
if: ${{ !startsWith(matrix.os, 'windows') }}
41+
uses: actions/cache@v4
42+
with:
43+
path: |
44+
~/.npm
45+
~/.nvm
46+
~/work/better-sqlite3-wrapper/better-sqlite3-wrapper/node_modules
47+
~/work/better-sqlite3-wrapper/better-sqlite3-wrapper/package-lock.json
48+
key: ${{ runner.os }}-${{ matrix.node }}-node_modules-cache-v1-${{ hashFiles('package.json') }}
49+
restore-keys: |
50+
${{ runner.os }}-${{ matrix.node }}-node_modules-cache-v1-
51+
52+
- name: Cache node_modules on windows os
53+
if: ${{ startsWith(matrix.os, 'windows') }}
54+
uses: actions/cache@v4
55+
with:
56+
path: |
57+
~/AppData/Roaming/npm
58+
D:/a/better-sqlite3-wrapper/better-sqlite3-wrapper/node_modules
59+
D:/a/better-sqlite3-wrapper/better-sqlite3-wrapper/package-lock.json
60+
key: ${{ runner.os }}-${{ matrix.node }}-node_modules-cache-v1-${{ hashFiles('package.json') }}
61+
restore-keys: |
62+
${{ runner.os }}-${{ matrix.node }}-node_modules-cache-v1-
63+
64+
- name: Run tests
65+
run: |
66+
npm i
67+
node ./index.test.js

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
1-
21
[![CI](https://github.com/farjs/better-sqlite3-wrapper/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/farjs/better-sqlite3-wrapper/actions/workflows/ci.yml?query=workflow%3Aci+branch%3Amain)
32
[![npm version](https://img.shields.io/npm/v/@farjs/better-sqlite3-wrapper)](https://www.npmjs.com/package/@farjs/better-sqlite3-wrapper)
43

54
## @farjs/better-sqlite3-wrapper
65

7-
Thin api wrapper around [better-sqlite3](https://github.com/WiseLibs/better-sqlite3)
8-
and [bun:sqlite](https://bun.sh/docs/api/sqlite) to allow cross- runtime/engine usage.
6+
Thin api wrapper around:
7+
8+
- [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) on node version < 22
9+
- [node:sqlite](https://nodejs.org/docs/latest-v22.x/api/sqlite.html) on node version >= 22
10+
- and [bun:sqlite](https://bun.sh/docs/api/sqlite)
11+
12+
to allow cross- runtime/engine usage.
913

1014
It exposes only [a subset](./index.d.ts) of `better-sqlite3` api because not everything
11-
is available in `bun:sqlite`.
15+
is available in `bun:sqlite`/`node:sqlite`.
1216

1317
For example, since `run` returns `undefined` you can use the following queries to get
14-
`changes` and/or `lastInsertRowid`:
18+
`changes` and/or `lastInsertRowid`:
1519

1620
```javascript
1721
const changes = db.prepare("SELECT changes() AS changes;").get().changes;
18-
const lastInsertRowId = db.prepare("SELECT last_insert_rowid() AS id;").get().id;
22+
const lastInsertRowId = db
23+
.prepare("SELECT last_insert_rowid() AS id;")
24+
.get().id;
1925
```
2026

2127
Also, since `db.pragma` is not available in `bun:sqlite`, you can run:

index.js

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,70 @@ const wrapper = function () {
33
return require("bun:sqlite").Database;
44
}
55

6-
return require("better-sqlite3");
6+
if (isModuleAvailable("better-sqlite3")) {
7+
return require("better-sqlite3");
8+
}
9+
10+
const { DatabaseSync } = require("node:sqlite");
11+
function dbCtor(path, options) {
12+
if (options) {
13+
return nodeDbWrapper(new DatabaseSync(path, options));
14+
}
15+
16+
return nodeDbWrapper(new DatabaseSync(path));
17+
}
18+
return dbCtor;
719
};
820

21+
function isModuleAvailable(name) {
22+
try {
23+
require.resolve(name);
24+
return true;
25+
} catch {}
26+
27+
return false;
28+
}
29+
30+
function nodeDbWrapper(db) {
31+
//check if node supports it
32+
//see: https://github.com/nodejs/node/issues/57431
33+
if (db.transaction) {
34+
return db;
35+
}
36+
37+
//see: https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#transactionfunction---function
38+
db.transaction = (f) => {
39+
const doTx = (...args) => {
40+
try {
41+
const res = f(...args);
42+
db.prepare("COMMIT").run();
43+
return res;
44+
} catch (error) {
45+
db.prepare("ROLLBACK").run();
46+
throw error;
47+
}
48+
};
49+
50+
const txFn = (...args) => {
51+
db.prepare("BEGIN").run();
52+
return doTx(...args);
53+
};
54+
txFn.deferred = (...args) => {
55+
db.prepare("BEGIN DEFERRED").run();
56+
return doTx(...args);
57+
};
58+
txFn.immediate = (...args) => {
59+
db.prepare("BEGIN IMMEDIATE").run();
60+
return doTx(...args);
61+
};
62+
txFn.exclusive = (...args) => {
63+
db.prepare("BEGIN EXCLUSIVE").run();
64+
return doTx(...args);
65+
};
66+
67+
return txFn;
68+
};
69+
return db;
70+
}
71+
972
module.exports = wrapper();

index.test.js

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@ it("should open in-memory database", () => {
1414
const query = db.prepare("select 'Hello world' as message;");
1515

1616
//then
17-
assert.deepEqual(query.get(), { message: "Hello world" });
17+
assert.deepEqual({ ...query.get() }, { message: "Hello world" });
1818

1919
//when & then
2020
const results = query.all();
2121
assert.deepEqual(Array.isArray(results), true);
22-
assert.deepEqual(results, [{ message: "Hello world" }]);
22+
assert.deepEqual(
23+
results.map((r) => {
24+
return { ...r };
25+
}),
26+
[{ message: "Hello world" }]
27+
);
2328

2429
//when & then
2530
db.close();
@@ -62,19 +67,73 @@ it("should create file-based database", () => {
6267
const results = db.prepare("select * from test order by id;").all();
6368

6469
//then
65-
assert.deepEqual(results, [
66-
{
67-
id: 1,
68-
name: "test1",
69-
},
70-
{
71-
id: 2,
72-
name: "test2",
73-
},
74-
]);
70+
assert.deepEqual(
71+
results.map((r) => {
72+
return { ...r };
73+
}),
74+
[
75+
{
76+
id: 1,
77+
name: "test1",
78+
},
79+
{
80+
id: 2,
81+
name: "test2",
82+
},
83+
]
84+
);
7585

7686
//cleanup
7787
db.close();
7888
fs.unlinkSync(file);
7989
fs.rmdirSync(tmpDir);
8090
});
91+
92+
it("should rollback failed transaction", () => {
93+
//when
94+
const db = new Database(":memory:");
95+
const changesQuery = db.prepare("SELECT changes() AS changes;");
96+
const lastInsertRowIdQuery = db.prepare("SELECT last_insert_rowid() AS id;");
97+
db.prepare(
98+
`
99+
create table test(
100+
id integer primary key,
101+
name text not null
102+
);
103+
`
104+
).run();
105+
assert.deepEqual(changesQuery.get().changes, 0);
106+
assert.deepEqual(lastInsertRowIdQuery.get().id, 0);
107+
108+
const error = Error("test error");
109+
const txFn = db.transaction((name) => {
110+
const insert = db.prepare("insert into test (name) values (?);");
111+
insert.run(name);
112+
throw error;
113+
});
114+
let caughtError = null;
115+
116+
//when
117+
try {
118+
txFn("test1");
119+
} catch (err) {
120+
caughtError = err;
121+
}
122+
123+
//then
124+
assert.deepEqual(caughtError === error, true);
125+
assert.deepEqual(changesQuery.get().changes, 1);
126+
assert.deepEqual(lastInsertRowIdQuery.get().id, 1);
127+
128+
//when & then
129+
const query = db.prepare("select id, name from test;");
130+
assert.deepEqual(
131+
query.all().map((r) => {
132+
return { ...r };
133+
}),
134+
[]
135+
);
136+
137+
//cleanup
138+
db.close();
139+
});

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@
3838
},
3939
"browserslist": "maintained node versions",
4040
"engines": {
41-
"node": ">=18",
41+
"node": ">=16",
4242
"bun": ">=0.8"
4343
},
4444
"optionalDependencies": {
45-
"better-sqlite3": "^11.5.0"
45+
"better-sqlite3": "8.7.0"
4646
},
4747
"devDependencies": {
4848
"prettier": "^2.8.8",

0 commit comments

Comments
 (0)