Skip to content

Commit 06c7a20

Browse files
fix: replace better-sqlite3 with sql.js for cross-platform compatibility (JavaScriptSolidServer#93)
- Remove better-sqlite3 from package.json dependencies - Use sql.js (WASM) directly for ActivityPub storage - Simplify code by removing try/catch fallback logic - Update README to reference sql.js This fixes installation failures on: - Android/Termux (gyp: Undefined variable android_ndk_path) - Windows (node-gyp compilation errors) - ARM devices and restricted environments Performance impact: Sub-millisecond difference for federation operations (only used for ActivityPub/Nostr, not main pod storage) Resolves JavaScriptSolidServer#92
1 parent 2431da9 commit 06c7a20

File tree

3 files changed

+39
-65
lines changed

3 files changed

+39
-65
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@ Minimal dependencies for a fast, secure server:
10471047
- **oidc-provider** - OpenID Connect Identity Provider (only when IdP enabled)
10481048
- **bcryptjs** - Password hashing (only when IdP enabled)
10491049
- **microfed** - ActivityPub primitives (only when activitypub enabled)
1050-
- **better-sqlite3** - SQLite storage for federation data
1050+
- **sql.js** - SQLite storage for federation data (WASM, cross-platform)
10511051

10521052
## License
10531053

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
"@fastify/websocket": "^8.3.1",
2929
"@simplewebauthn/server": "^13.2.2",
3030
"bcryptjs": "^3.0.3",
31-
"better-sqlite3": "^12.5.0",
3231
"commander": "^14.0.2",
3332
"fastify": "^4.29.1",
3433
"fs-extra": "^11.2.0",

src/ap/store.js

Lines changed: 38 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22
* ActivityPub SQLite Storage
33
* Persistence layer for federation data
44
*
5-
* Uses better-sqlite3 when available (native, fast)
6-
* Falls back to sql.js on Android/platforms without native builds
5+
* Uses sql.js (WASM) for cross-platform compatibility
6+
* Works on Android/Termux, Windows, and all platforms
77
*/
88

99
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
1010
import { dirname } from 'path'
1111

1212
let db = null
1313
let dbPath = null
14-
let usingSqlJs = false
1514

1615
// SQL schema
1716
const SCHEMA = `
@@ -59,7 +58,7 @@ const SCHEMA = `
5958

6059
/**
6160
* Initialize the database
62-
* Tries better-sqlite3 first, falls back to sql.js
61+
* Uses sql.js (WASM) for cross-platform compatibility
6362
* @param {string} path - Path to SQLite file
6463
*/
6564
export async function initStore(path = 'data/activitypub.db') {
@@ -71,43 +70,31 @@ export async function initStore(path = 'data/activitypub.db') {
7170

7271
dbPath = path
7372

74-
// Try better-sqlite3 first (fast, native)
75-
try {
76-
const Database = (await import('better-sqlite3')).default
77-
db = new Database(path)
78-
db.exec(SCHEMA)
79-
usingSqlJs = false
80-
return db
81-
} catch (e) {
82-
// Fall back to sql.js (WASM, works everywhere)
83-
console.log('ActivityPub: Using sql.js (WASM) for SQLite storage')
84-
85-
const initSqlJs = (await import('sql.js')).default
86-
const SQL = await initSqlJs()
87-
88-
// Load existing database if it exists
89-
if (existsSync(path)) {
90-
const buffer = readFileSync(path)
91-
db = new SQL.Database(buffer)
92-
} else {
93-
db = new SQL.Database()
94-
}
95-
96-
db.run(SCHEMA)
97-
usingSqlJs = true
98-
99-
// Save initial database
100-
saveDatabase()
101-
102-
return db
73+
// Use sql.js (WASM, works everywhere)
74+
const initSqlJs = (await import('sql.js')).default
75+
const SQL = await initSqlJs()
76+
77+
// Load existing database if it exists
78+
if (existsSync(path)) {
79+
const buffer = readFileSync(path)
80+
db = new SQL.Database(buffer)
81+
} else {
82+
db = new SQL.Database()
10383
}
84+
85+
db.run(SCHEMA)
86+
87+
// Save initial database
88+
saveDatabase()
89+
90+
return db
10491
}
10592

10693
/**
10794
* Save sql.js database to disk
10895
*/
10996
function saveDatabase() {
110-
if (usingSqlJs && db && dbPath) {
97+
if (db && dbPath) {
11198
const data = db.export()
11299
const buffer = Buffer.from(data)
113100
writeFileSync(dbPath, buffer)
@@ -124,45 +111,33 @@ export function getStore() {
124111
return db
125112
}
126113

127-
// Helper to run prepared statements across both implementations
114+
// Helper functions for sql.js API
128115
function runStmt(sql, params = []) {
129-
if (usingSqlJs) {
130-
db.run(sql, params)
131-
saveDatabase()
132-
} else {
133-
db.prepare(sql).run(...params)
134-
}
116+
db.run(sql, params)
117+
saveDatabase()
135118
}
136119

137120
function getOne(sql, params = []) {
138-
if (usingSqlJs) {
139-
const stmt = db.prepare(sql)
140-
stmt.bind(params)
141-
if (stmt.step()) {
142-
const row = stmt.getAsObject()
143-
stmt.free()
144-
return row
145-
}
121+
const stmt = db.prepare(sql)
122+
stmt.bind(params)
123+
if (stmt.step()) {
124+
const row = stmt.getAsObject()
146125
stmt.free()
147-
return null
148-
} else {
149-
return db.prepare(sql).get(...params)
126+
return row
150127
}
128+
stmt.free()
129+
return null
151130
}
152131

153132
function getAll(sql, params = []) {
154-
if (usingSqlJs) {
155-
const results = []
156-
const stmt = db.prepare(sql)
157-
stmt.bind(params)
158-
while (stmt.step()) {
159-
results.push(stmt.getAsObject())
160-
}
161-
stmt.free()
162-
return results
163-
} else {
164-
return db.prepare(sql).all(...params)
133+
const results = []
134+
const stmt = db.prepare(sql)
135+
stmt.bind(params)
136+
while (stmt.step()) {
137+
results.push(stmt.getAsObject())
165138
}
139+
stmt.free()
140+
return results
166141
}
167142

168143
// Followers

0 commit comments

Comments
 (0)