@lightninglabs/pi-402 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,240 @@
1
+ // Main-thread bridge between Go WASM and the dedicated SQLite oo1 worker.
2
+ //
3
+ // The worker is a browser Web Worker in a browser and a node:worker_threads
4
+ // worker under Node. The two APIs disagree on nearly everything at the seam
5
+ // (construction, event delivery, teardown), so the Node side is wrapped in an
6
+ // adapter that presents the browser Worker shape and the rest of this file
7
+ // stays host-independent.
8
+
9
+ (() => {
10
+ const bridgeProtocolVersion = 1;
11
+ const expectedWorkerProtocolVersion = 1;
12
+ let worker = null;
13
+ let nextRequestId = 1;
14
+ let initPromise = null;
15
+ const pending = new Map();
16
+ const bridgeScriptURL = globalThis.document?.currentScript?.src || "";
17
+
18
+ // isNodeHost reports whether this is Node rather than a browser. Node has no
19
+ // global Worker constructor, which is both the thing that distinguishes it
20
+ // and the exact reason the adapter below has to exist.
21
+ const isNodeHost =
22
+ typeof globalThis.process === "object" &&
23
+ !!globalThis.process?.versions?.node &&
24
+ typeof globalThis.Worker !== "function";
25
+
26
+ function workerURL(override) {
27
+ if (override) return override;
28
+ if (globalThis.sqliteBridgeWorkerURL) return globalThis.sqliteBridgeWorkerURL;
29
+ if (bridgeScriptURL) return new URL("sqlite-worker.js", bridgeScriptURL).href;
30
+ return "sqlite-worker.js";
31
+ }
32
+
33
+ // nodeAssetDir resolves the directory holding the runtime assets. A host
34
+ // that evaluates this file as a string (which is how a Go WASM host usually
35
+ // loads it) has no __dirname, so sqliteBridgeAssetDir is the supported knob.
36
+ function nodeAssetDir() {
37
+ const nodePath = require("node:path");
38
+ if (globalThis.sqliteBridgeAssetDir) {
39
+ return nodePath.resolve(globalThis.sqliteBridgeAssetDir);
40
+ }
41
+ if (typeof __dirname === "string") return __dirname;
42
+ return process.cwd();
43
+ }
44
+
45
+ // createNodeWorker starts the worker under node:worker_threads and adapts it
46
+ // to the browser Worker interface this bridge is written against.
47
+ //
48
+ // The worker source is read and evaluated rather than being handed to Node
49
+ // as a path, because a path is interpreted through the nearest package.json:
50
+ // a ".js" file that lands inside a "type": "module" package would be loaded
51
+ // as ESM and its require() calls would fail. Evaluating the source sidesteps
52
+ // the question entirely, since an eval'd worker is always CommonJS.
53
+ function createNodeWorker(override) {
54
+ const { Worker: NodeWorker } = require("node:worker_threads");
55
+ const nodeFS = require("node:fs");
56
+ const nodePath = require("node:path");
57
+
58
+ const assetDir = nodeAssetDir();
59
+ const scriptPath = override
60
+ ? nodePath.resolve(assetDir, override)
61
+ : nodePath.join(assetDir, "sqlite-worker.js");
62
+
63
+ let source;
64
+ try {
65
+ source = nodeFS.readFileSync(scriptPath, "utf8");
66
+ } catch (error) {
67
+ throw new Error(
68
+ `unable to read sqlite-worker.js at ${scriptPath}: ${error.message}; ` +
69
+ "set globalThis.sqliteBridgeAssetDir to the runtime asset directory"
70
+ );
71
+ }
72
+
73
+ const nodeWorker = new NodeWorker(source, {
74
+ eval: true,
75
+ workerData: { assetDir: nodePath.dirname(scriptPath) },
76
+ });
77
+
78
+ const adapter = {
79
+ onmessage: null,
80
+ onerror: null,
81
+ postMessage: (message) => nodeWorker.postMessage(message),
82
+ terminate: () => nodeWorker.terminate(),
83
+ };
84
+ nodeWorker.on("message", (data) => adapter.onmessage?.({ data }));
85
+ nodeWorker.on("error", (error) => adapter.onerror?.(error));
86
+
87
+ return adapter;
88
+ }
89
+
90
+ function ensureWorker(override) {
91
+ if (worker) return;
92
+
93
+ worker = isNodeHost ? createNodeWorker(override) : new Worker(workerURL(override));
94
+ worker.onmessage = (event) => {
95
+ const message = event.data || {};
96
+ const waiter = pending.get(message.id);
97
+ if (!waiter) return;
98
+
99
+ pending.delete(message.id);
100
+ if (message.ok) {
101
+ waiter.resolve(message.result || {});
102
+ } else {
103
+ waiter.reject(new Error(message.error || "sqlite worker request failed"));
104
+ }
105
+ };
106
+ worker.onerror = (event) => {
107
+ const err = new Error(event.message || "sqlite worker error");
108
+ for (const waiter of pending.values()) waiter.reject(err);
109
+ pending.clear();
110
+ };
111
+ }
112
+
113
+ function request(method, args = {}, transfer = []) {
114
+ if (!worker) throw new Error("SQLite worker is not initialized");
115
+
116
+ const id = nextRequestId++;
117
+ const promise = new Promise((resolve, reject) => {
118
+ pending.set(id, { resolve, reject });
119
+ });
120
+ worker.postMessage({ id, method, args }, transfer);
121
+ return promise;
122
+ }
123
+
124
+ globalThis.sqliteBridge = {
125
+ protocolVersion: bridgeProtocolVersion,
126
+
127
+ async init(options = {}) {
128
+ ensureWorker(options.workerURL);
129
+ if (!initPromise) {
130
+ initPromise = request("init", {
131
+ sqliteJSURL: options.sqliteJSURL || globalThis.sqliteBridgeSQLiteJSURL || "",
132
+ // rootDir anchors relative database names for the node:fs VFS. It is
133
+ // ignored in a browser, where names are origin-local OPFS paths.
134
+ rootDir: options.rootDir || globalThis.sqliteBridgeRootDir || "",
135
+ expectedBridgeProtocolVersion: bridgeProtocolVersion
136
+ }).then((result) => {
137
+ if (result.workerProtocolVersion !== expectedWorkerProtocolVersion) {
138
+ throw new Error(`SQLite worker protocol mismatch: expected ${expectedWorkerProtocolVersion}, got ${result.workerProtocolVersion}`);
139
+ }
140
+ console.log("SQLite oo1 worker initialized:", result.version?.libVersion || "unknown");
141
+ return { ok: true, bridgeProtocolVersion, ...result };
142
+ });
143
+ }
144
+ return initPromise;
145
+ },
146
+
147
+ async open(optionsOrFilename = {}, maybeVFS = "opfs") {
148
+ await globalThis.sqliteBridge.init();
149
+
150
+ let options = optionsOrFilename;
151
+ if (typeof optionsOrFilename === "string") {
152
+ options = { file: optionsOrFilename, vfs: maybeVFS };
153
+ }
154
+
155
+ const result = await request("open", options || {});
156
+ return {
157
+ ok: true,
158
+ dbId: result.dbId,
159
+ filename: result.filename,
160
+ vfsType: result.vfs,
161
+ resolvedVFS: result.vfs,
162
+ persistent: !!result.persistent
163
+ };
164
+ },
165
+
166
+ async exec(dbId, sql, params = []) {
167
+ const result = await request("exec", { dbId, sql, params });
168
+ return { ok: true, ...result };
169
+ },
170
+
171
+ async execBatch(dbId, sql, paramRows = [], options = {}) {
172
+ const result = await request("execBatch", {
173
+ dbId,
174
+ sql,
175
+ paramRows,
176
+ transaction: options.transaction !== false
177
+ });
178
+ return { ok: true, ...result };
179
+ },
180
+
181
+ async query(dbId, sql, params = []) {
182
+ const result = await request("query", { dbId, sql, params });
183
+ return {
184
+ ok: true,
185
+ columns: result.columnNames || [],
186
+ rows: result.resultRows || []
187
+ };
188
+ },
189
+
190
+ async begin(dbId) {
191
+ await request("begin", { dbId });
192
+ return { ok: true };
193
+ },
194
+
195
+ async commit(dbId) {
196
+ await request("commit", { dbId });
197
+ return { ok: true };
198
+ },
199
+
200
+ async rollback(dbId) {
201
+ await request("rollback", { dbId });
202
+ return { ok: true };
203
+ },
204
+
205
+ async close(dbId) {
206
+ await request("close", { dbId });
207
+ return { ok: true };
208
+ },
209
+
210
+ async dump(dbId) {
211
+ const result = await request("dump", { dbId });
212
+ return { ok: true, dump: result.dump || "" };
213
+ },
214
+
215
+ async load(dbId, sql) {
216
+ await request("load", { dbId, sql });
217
+ return { ok: true };
218
+ },
219
+
220
+ // shutdown tears the worker down. A browser page discards its workers when
221
+ // it unloads, but a Node worker thread keeps the host process alive, so a
222
+ // short-lived Node program needs a way to say it is finished. Databases
223
+ // should be closed first; this does not flush anything on their behalf.
224
+ async shutdown() {
225
+ if (!worker) return { ok: true };
226
+
227
+ const stopping = worker;
228
+ worker = null;
229
+ initPromise = null;
230
+ for (const waiter of pending.values()) {
231
+ waiter.reject(new Error("sqlite worker shut down"));
232
+ }
233
+ pending.clear();
234
+ if (typeof stopping.terminate === "function") {
235
+ await stopping.terminate();
236
+ }
237
+ return { ok: true };
238
+ }
239
+ };
240
+ })();