Skip to content

Commit 9e268e6

Browse files
Copilothotlong
andcommitted
feat: implement @objectql/driver-pg-wasm package
Implement PostgreSQL WASM driver for browser environments following Q1 Phase 2 work plan. Package features: - Composition pattern wrapping SqlDriver for query compilation - Custom Knex client adapter for PGlite WASM - IndexedDB/OPFS/Memory persistence backends with auto-fallback - Environment detection with ObjectQLError for missing WASM - PostgreSQL-specific features: JSONB operators, full-text search - Full transaction support with savepoints and isolation levels - Comprehensive test suite (23 tests passing) Implementation follows same patterns as @objectql/driver-sqlite-wasm: - Library-agnostic public API (references "PostgreSQL WASM", not "PGlite") - ~3MB bundle size documented for informed choice vs SQLite WASM - No Node.js support (browser-only driver) - Lazy WASM module loading Files created: - src/index.ts - Main PgWasmDriver class - src/environment.ts - Environment detection utilities - src/wasm-loader.ts - PGlite WASM lazy loader - src/knex-adapter.ts - Custom Knex client for PGlite - test/index.test.ts - Comprehensive unit tests - README.md - Complete documentation - package.json, tsconfig.json - Package configuration All builds and tests passing. Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 7cfe347 commit 9e268e6

9 files changed

Lines changed: 1670 additions & 0 deletions

File tree

packages/drivers/pg-wasm/README.md

Lines changed: 518 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"name": "@objectql/driver-pg-wasm",
3+
"version": "4.2.0",
4+
"description": "PostgreSQL WASM driver for ObjectQL - Browser-native PostgreSQL database with IndexedDB/OPFS persistence",
5+
"keywords": [
6+
"objectql",
7+
"driver",
8+
"postgresql",
9+
"postgres",
10+
"wasm",
11+
"webassembly",
12+
"browser",
13+
"pglite",
14+
"opfs",
15+
"indexeddb",
16+
"database",
17+
"adapter"
18+
],
19+
"license": "MIT",
20+
"main": "dist/index.js",
21+
"types": "dist/index.d.ts",
22+
"exports": {
23+
".": {
24+
"types": "./dist/index.d.ts",
25+
"default": "./dist/index.js"
26+
}
27+
},
28+
"scripts": {
29+
"build": "tsc",
30+
"test": "vitest run"
31+
},
32+
"dependencies": {
33+
"@electric-sql/pglite": "^0.1.5",
34+
"@objectql/driver-sql": "workspace:*",
35+
"@objectql/types": "workspace:*",
36+
"@objectstack/spec": "^1.1.0",
37+
"knex": "^3.1.0",
38+
"nanoid": "^3.3.11",
39+
"zod": "^3.24.1"
40+
},
41+
"devDependencies": {
42+
"@types/node": "^20.0.0",
43+
"pg": "^8.11.3",
44+
"typescript": "^5.0.0",
45+
"vitest": "^1.0.0"
46+
},
47+
"repository": {
48+
"type": "git",
49+
"url": "https://github.com/objectstack-ai/objectql.git",
50+
"directory": "packages/drivers/pg-wasm"
51+
}
52+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* ObjectQL
3+
* Copyright (c) 2026-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
import { ObjectQLError } from '@objectql/types';
10+
11+
/**
12+
* Environment detection utilities for browser PostgreSQL WASM support
13+
*/
14+
15+
/**
16+
* Check if WebAssembly is available
17+
*/
18+
export function checkWebAssembly(): void {
19+
if (typeof globalThis.WebAssembly === 'undefined') {
20+
throw new ObjectQLError({
21+
code: 'ENVIRONMENT_ERROR',
22+
message: 'WebAssembly is not supported in this environment. PostgreSQL WASM driver requires WebAssembly support.'
23+
});
24+
}
25+
}
26+
27+
/**
28+
* Check if IndexedDB is available
29+
*/
30+
export async function checkIndexedDB(): Promise<boolean> {
31+
try {
32+
if (typeof indexedDB === 'undefined') {
33+
return false;
34+
}
35+
36+
// Try to open a test database
37+
return new Promise((resolve) => {
38+
const request = indexedDB.open('__opfs_test__', 1);
39+
request.onsuccess = () => {
40+
request.result.close();
41+
indexedDB.deleteDatabase('__opfs_test__');
42+
resolve(true);
43+
};
44+
request.onerror = () => resolve(false);
45+
});
46+
} catch {
47+
return false;
48+
}
49+
}
50+
51+
/**
52+
* Check if OPFS (Origin Private File System) is available
53+
*/
54+
export async function checkOPFS(): Promise<boolean> {
55+
try {
56+
if (typeof navigator === 'undefined' || !navigator.storage) {
57+
return false;
58+
}
59+
60+
// Check if getDirectory method exists
61+
if (typeof (navigator.storage as any).getDirectory !== 'function') {
62+
return false;
63+
}
64+
65+
// Try to actually access it
66+
const root = await (navigator.storage as any).getDirectory();
67+
return !!root;
68+
} catch {
69+
return false;
70+
}
71+
}
72+
73+
/**
74+
* Detect the best available storage backend
75+
*/
76+
export async function detectStorageBackend(): Promise<'idb' | 'opfs' | 'memory'> {
77+
const hasOPFS = await checkOPFS();
78+
if (hasOPFS) {
79+
return 'opfs';
80+
}
81+
82+
const hasIDB = await checkIndexedDB();
83+
if (hasIDB) {
84+
return 'idb';
85+
}
86+
87+
return 'memory';
88+
}

0 commit comments

Comments
 (0)