hyperdb-wasm 0.0.26281-dev.wasm.g39d82636
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.
- package/dist/api.d.ts +50 -0
- package/dist/browser.d.ts +25 -0
- package/dist/browser.js +12080 -0
- package/dist/browser_worker.d.ts +1 -0
- package/dist/browser_worker.js +226 -0
- package/dist/client.d.ts +23 -0
- package/dist/hyperdb-wasm.cjs +3 -0
- package/dist/hyperdb-wasm.js +3 -0
- package/dist/hyperdb-wasm.wasm.br +0 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.node.d.ts +6 -0
- package/dist/node.d.ts +25 -0
- package/dist/node.js +456 -0
- package/dist/node_worker.d.ts +1 -0
- package/dist/node_worker.js +236 -0
- package/dist/protocol.d.ts +67 -0
- package/dist/raw.browser.d.ts +3 -0
- package/dist/raw.browser.js +252 -0
- package/dist/raw.d.ts +3 -0
- package/dist/raw.node.d.ts +3 -0
- package/dist/raw.node.js +254 -0
- package/dist/wasm_bindings.d.ts +27 -0
- package/dist/worker_setup.d.ts +22 -0
- package/examples/browser.html +84 -0
- package/examples/node.mjs +43 -0
- package/examples/serve.mjs +35 -0
- package/package.json +41 -0
package/dist/node.js
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res, err) => function __init() {
|
|
4
|
+
if (err) throw err[0];
|
|
5
|
+
try {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
throw err = [e], e;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// protocol.ts
|
|
17
|
+
function isRecord(value) {
|
|
18
|
+
return typeof value === "object" && value !== null;
|
|
19
|
+
}
|
|
20
|
+
function hasKeys(value, keys) {
|
|
21
|
+
const actual = Object.keys(value).sort();
|
|
22
|
+
const expected = [...keys].sort();
|
|
23
|
+
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
24
|
+
}
|
|
25
|
+
function isWasmResult(value) {
|
|
26
|
+
if (!isRecord(value) || typeof value.state !== "string") return false;
|
|
27
|
+
switch (value.state) {
|
|
28
|
+
case "ok":
|
|
29
|
+
case "chunk":
|
|
30
|
+
return hasKeys(value, ["state", "payload"]) && value.payload instanceof Uint8Array;
|
|
31
|
+
case "pending":
|
|
32
|
+
case "done":
|
|
33
|
+
return hasKeys(value, ["state"]);
|
|
34
|
+
case "error":
|
|
35
|
+
case "busy":
|
|
36
|
+
return hasKeys(value, ["state", "error"]) && typeof value.error === "string";
|
|
37
|
+
default:
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function isWorkerEvent(message) {
|
|
42
|
+
if (!isRecord(message) || typeof message.type !== "string") return false;
|
|
43
|
+
switch (message.type) {
|
|
44
|
+
case "ready":
|
|
45
|
+
return hasKeys(message, ["type"]);
|
|
46
|
+
case "fatal":
|
|
47
|
+
return hasKeys(message, ["type", "error"]) && typeof message.error === "string";
|
|
48
|
+
case "log":
|
|
49
|
+
return hasKeys(message, ["type", "text"]) && typeof message.text === "string";
|
|
50
|
+
case "response":
|
|
51
|
+
return hasKeys(message, ["id", "type", "result"]) && Number.isSafeInteger(message.id) && isWasmResult(message.result);
|
|
52
|
+
default:
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
var init_protocol = __esm({
|
|
57
|
+
"protocol.ts"() {
|
|
58
|
+
"use strict";
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// client.ts
|
|
63
|
+
var HyperDBEngineClient;
|
|
64
|
+
var init_client = __esm({
|
|
65
|
+
"client.ts"() {
|
|
66
|
+
"use strict";
|
|
67
|
+
init_protocol();
|
|
68
|
+
HyperDBEngineClient = class {
|
|
69
|
+
transport;
|
|
70
|
+
pending = /* @__PURE__ */ new Map();
|
|
71
|
+
readyPromise;
|
|
72
|
+
resolveReady;
|
|
73
|
+
rejectReady;
|
|
74
|
+
nextId = 1;
|
|
75
|
+
failed;
|
|
76
|
+
unsubscribe;
|
|
77
|
+
constructor(transport) {
|
|
78
|
+
this.transport = transport;
|
|
79
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
80
|
+
this.resolveReady = resolve;
|
|
81
|
+
this.rejectReady = reject;
|
|
82
|
+
});
|
|
83
|
+
this.unsubscribe = [
|
|
84
|
+
transport.onMessage((message) => this.onMessage(message)),
|
|
85
|
+
transport.onError((error) => this.fail(error)),
|
|
86
|
+
transport.onClose((error) => this.fail(error))
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
ready() {
|
|
90
|
+
return this.readyPromise;
|
|
91
|
+
}
|
|
92
|
+
connect() {
|
|
93
|
+
return this.request({ id: this.nextId++, type: "connect" });
|
|
94
|
+
}
|
|
95
|
+
disconnect(connection) {
|
|
96
|
+
return this.request({ id: this.nextId++, type: "disconnect", connection });
|
|
97
|
+
}
|
|
98
|
+
startQuery(connection, sql) {
|
|
99
|
+
return this.request({ id: this.nextId++, type: "startQuery", connection, sql });
|
|
100
|
+
}
|
|
101
|
+
pollQuery(query) {
|
|
102
|
+
return this.request({ id: this.nextId++, type: "pollQuery", query });
|
|
103
|
+
}
|
|
104
|
+
cancelQuery(query) {
|
|
105
|
+
return this.request({ id: this.nextId++, type: "cancelQuery", query });
|
|
106
|
+
}
|
|
107
|
+
releaseQuery(query) {
|
|
108
|
+
return this.request({ id: this.nextId++, type: "releaseQuery", query });
|
|
109
|
+
}
|
|
110
|
+
async terminate() {
|
|
111
|
+
this.fail(new Error("engine worker terminated"));
|
|
112
|
+
await this.transport.terminate();
|
|
113
|
+
}
|
|
114
|
+
request(request) {
|
|
115
|
+
if (this.failed) return Promise.reject(this.failed);
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
this.pending.set(request.id, { resolve, reject });
|
|
118
|
+
try {
|
|
119
|
+
this.transport.postMessage(request);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.pending.delete(request.id);
|
|
122
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
onMessage(message) {
|
|
127
|
+
if (!isWorkerEvent(message)) return;
|
|
128
|
+
if (message.type === "ready") {
|
|
129
|
+
this.resolveReady();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (message.type === "fatal") {
|
|
133
|
+
this.fail(new Error(message.error || "engine aborted"));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (message.type !== "response") return;
|
|
137
|
+
const pending = this.pending.get(message.id);
|
|
138
|
+
if (!pending) return;
|
|
139
|
+
this.pending.delete(message.id);
|
|
140
|
+
pending.resolve(message.result);
|
|
141
|
+
}
|
|
142
|
+
fail(error) {
|
|
143
|
+
if (this.failed) return;
|
|
144
|
+
this.failed = error;
|
|
145
|
+
for (const unsubscribe of this.unsubscribe.splice(0)) unsubscribe();
|
|
146
|
+
this.rejectReady(error);
|
|
147
|
+
for (const pending of this.pending.values()) pending.reject(error);
|
|
148
|
+
this.pending.clear();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// browser.ts
|
|
155
|
+
var browser_exports = {};
|
|
156
|
+
__export(browser_exports, {
|
|
157
|
+
BrowserWorker: () => BrowserWorker,
|
|
158
|
+
createBrowserClient: () => createBrowserClient,
|
|
159
|
+
resolveBrowserWorkerUrl: () => resolveBrowserWorkerUrl
|
|
160
|
+
});
|
|
161
|
+
function resolveBrowserWorkerUrl(workerUrl, engineUrl) {
|
|
162
|
+
const resolved = new URL(workerUrl);
|
|
163
|
+
resolved.searchParams.set("engineUrl", new URL(engineUrl).href);
|
|
164
|
+
return resolved;
|
|
165
|
+
}
|
|
166
|
+
function createBrowserClient(options = {}) {
|
|
167
|
+
const workerUrl = options.workerUrl ?? new URL("./browser_worker.js", import.meta.url);
|
|
168
|
+
const engineUrl = options.engineUrl ?? new URL("./hyperdb-wasm.js", import.meta.url);
|
|
169
|
+
return new HyperDBEngineClient(new BrowserWorker(new Worker(resolveBrowserWorkerUrl(workerUrl, engineUrl), { type: "classic" })));
|
|
170
|
+
}
|
|
171
|
+
var BrowserWorker;
|
|
172
|
+
var init_browser = __esm({
|
|
173
|
+
"browser.ts"() {
|
|
174
|
+
"use strict";
|
|
175
|
+
init_client();
|
|
176
|
+
BrowserWorker = class {
|
|
177
|
+
constructor(worker) {
|
|
178
|
+
this.worker = worker;
|
|
179
|
+
}
|
|
180
|
+
worker;
|
|
181
|
+
postMessage(message, transfer = []) {
|
|
182
|
+
this.worker.postMessage(message, [...transfer]);
|
|
183
|
+
}
|
|
184
|
+
onMessage(listener) {
|
|
185
|
+
const wrapped = (event) => listener(event.data);
|
|
186
|
+
this.worker.addEventListener("message", wrapped);
|
|
187
|
+
return () => this.worker.removeEventListener("message", wrapped);
|
|
188
|
+
}
|
|
189
|
+
onError(listener) {
|
|
190
|
+
const wrapped = (event) => listener(new Error(event.message || "engine worker error"));
|
|
191
|
+
this.worker.addEventListener("error", wrapped);
|
|
192
|
+
return () => this.worker.removeEventListener("error", wrapped);
|
|
193
|
+
}
|
|
194
|
+
onClose() {
|
|
195
|
+
return () => void 0;
|
|
196
|
+
}
|
|
197
|
+
terminate() {
|
|
198
|
+
this.worker.terminate();
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// api.ts
|
|
205
|
+
import { RecordBatchReader, Table } from "apache-arrow";
|
|
206
|
+
function readHandleFromResult(result, operation) {
|
|
207
|
+
if (result.state !== "ok") throw readErrorFromResult(result, operation);
|
|
208
|
+
if (result.payload.byteLength !== 4) throw new Error(`${operation} returned an invalid handle`);
|
|
209
|
+
return new DataView(result.payload.buffer, result.payload.byteOffset, result.payload.byteLength).getUint32(0, true);
|
|
210
|
+
}
|
|
211
|
+
function readErrorFromResult(result, operation) {
|
|
212
|
+
if (result.state === "error" || result.state === "busy") return new Error(result.error);
|
|
213
|
+
return new Error(`${operation} returned unexpected state ${result.state}`);
|
|
214
|
+
}
|
|
215
|
+
function expectOk(result, operation) {
|
|
216
|
+
if (result.state !== "ok") throw readErrorFromResult(result, operation);
|
|
217
|
+
}
|
|
218
|
+
function yieldToEventLoop() {
|
|
219
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
220
|
+
}
|
|
221
|
+
var HyperDB = class _HyperDB {
|
|
222
|
+
constructor(client) {
|
|
223
|
+
this.client = client;
|
|
224
|
+
}
|
|
225
|
+
client;
|
|
226
|
+
terminated = false;
|
|
227
|
+
termination;
|
|
228
|
+
connections = /* @__PURE__ */ new Set();
|
|
229
|
+
static async create(options = {}) {
|
|
230
|
+
const { createBrowserClient: createBrowserClient2 } = await Promise.resolve().then(() => (init_browser(), browser_exports));
|
|
231
|
+
const database = new _HyperDB(createBrowserClient2(options));
|
|
232
|
+
await database.ready();
|
|
233
|
+
return database;
|
|
234
|
+
}
|
|
235
|
+
ready() {
|
|
236
|
+
this.ensureRunning();
|
|
237
|
+
return this.client.ready();
|
|
238
|
+
}
|
|
239
|
+
async connect() {
|
|
240
|
+
this.ensureRunning();
|
|
241
|
+
const connection = readHandleFromResult(await this.client.connect(), "connect");
|
|
242
|
+
const result = new HyperDBConnection(this.client, connection, () => this.connections.delete(result));
|
|
243
|
+
this.connections.add(result);
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
async terminate() {
|
|
247
|
+
if (this.terminated) return;
|
|
248
|
+
if (!this.termination) {
|
|
249
|
+
this.termination = (async () => {
|
|
250
|
+
try {
|
|
251
|
+
await Promise.all([...this.connections].map(async (connection) => connection.closeForTermination()));
|
|
252
|
+
} catch (error) {
|
|
253
|
+
this.termination = void 0;
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
this.terminated = true;
|
|
257
|
+
await this.client.terminate();
|
|
258
|
+
})();
|
|
259
|
+
}
|
|
260
|
+
await this.termination;
|
|
261
|
+
}
|
|
262
|
+
ensureRunning() {
|
|
263
|
+
if (this.terminated) throw new Error("database is terminated");
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
var HyperDBConnection = class {
|
|
267
|
+
constructor(client, connection, onClose = () => void 0) {
|
|
268
|
+
this.client = client;
|
|
269
|
+
this.connection = connection;
|
|
270
|
+
this.onClose = onClose;
|
|
271
|
+
}
|
|
272
|
+
client;
|
|
273
|
+
connection;
|
|
274
|
+
onClose;
|
|
275
|
+
closed = false;
|
|
276
|
+
active = false;
|
|
277
|
+
closing;
|
|
278
|
+
async close() {
|
|
279
|
+
if (this.closed) return;
|
|
280
|
+
if (this.active) throw new Error("connection has an active query");
|
|
281
|
+
if (!this.closing) {
|
|
282
|
+
this.closing = (async () => {
|
|
283
|
+
try {
|
|
284
|
+
expectOk(await this.client.disconnect(this.connection), "disconnect");
|
|
285
|
+
} catch (error) {
|
|
286
|
+
this.closing = void 0;
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
this.closed = true;
|
|
290
|
+
this.onClose();
|
|
291
|
+
})();
|
|
292
|
+
}
|
|
293
|
+
await this.closing;
|
|
294
|
+
}
|
|
295
|
+
sendIPC(sql) {
|
|
296
|
+
const connection = this;
|
|
297
|
+
return {
|
|
298
|
+
async *[Symbol.asyncIterator]() {
|
|
299
|
+
connection.ensureAvailable();
|
|
300
|
+
let query;
|
|
301
|
+
let terminal = false;
|
|
302
|
+
let prefetched;
|
|
303
|
+
try {
|
|
304
|
+
query = readHandleFromResult(await connection.client.startQuery(connection.connection, sql), "start query");
|
|
305
|
+
for (; ; ) {
|
|
306
|
+
let result = prefetched ? await prefetched : await connection.client.pollQuery(query);
|
|
307
|
+
prefetched = void 0;
|
|
308
|
+
while (result.state === "pending") {
|
|
309
|
+
await yieldToEventLoop();
|
|
310
|
+
result = await connection.client.pollQuery(query);
|
|
311
|
+
}
|
|
312
|
+
if (result.state === "chunk") {
|
|
313
|
+
prefetched = connection.client.pollQuery(query);
|
|
314
|
+
void prefetched.catch(() => void 0);
|
|
315
|
+
yield result.payload;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
terminal = true;
|
|
319
|
+
if (result.state === "done") return;
|
|
320
|
+
throw readErrorFromResult(result, "poll query");
|
|
321
|
+
}
|
|
322
|
+
} finally {
|
|
323
|
+
try {
|
|
324
|
+
if (query !== void 0) {
|
|
325
|
+
let cleanupError;
|
|
326
|
+
if (prefetched) {
|
|
327
|
+
try {
|
|
328
|
+
await prefetched;
|
|
329
|
+
} catch (error) {
|
|
330
|
+
cleanupError = error;
|
|
331
|
+
}
|
|
332
|
+
prefetched = void 0;
|
|
333
|
+
}
|
|
334
|
+
if (!terminal) {
|
|
335
|
+
try {
|
|
336
|
+
expectOk(await connection.client.cancelQuery(query), "cancel query");
|
|
337
|
+
} catch (error) {
|
|
338
|
+
cleanupError ??= error;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
await connection.release(query);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
cleanupError ??= error;
|
|
345
|
+
}
|
|
346
|
+
if (cleanupError) throw cleanupError;
|
|
347
|
+
}
|
|
348
|
+
} finally {
|
|
349
|
+
connection.active = false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
async queryIPC(sql) {
|
|
356
|
+
const chunks = [];
|
|
357
|
+
let length = 0;
|
|
358
|
+
for await (const chunk of this.sendIPC(sql)) {
|
|
359
|
+
chunks.push(chunk);
|
|
360
|
+
length += chunk.byteLength;
|
|
361
|
+
}
|
|
362
|
+
const result = new Uint8Array(length);
|
|
363
|
+
let offset = 0;
|
|
364
|
+
for (const chunk of chunks) {
|
|
365
|
+
result.set(chunk, offset);
|
|
366
|
+
offset += chunk.byteLength;
|
|
367
|
+
}
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
async send(sql) {
|
|
371
|
+
const reader = await RecordBatchReader.from(this.sendIPC(sql));
|
|
372
|
+
if (!reader.isAsync() || !reader.isStream()) throw new Error("query did not produce an Arrow async stream");
|
|
373
|
+
return reader;
|
|
374
|
+
}
|
|
375
|
+
async query(sql) {
|
|
376
|
+
const reader = await this.send(sql);
|
|
377
|
+
const batches = [];
|
|
378
|
+
for await (const batch of reader) batches.push(batch);
|
|
379
|
+
return new Table(reader.schema, batches);
|
|
380
|
+
}
|
|
381
|
+
ensureAvailable() {
|
|
382
|
+
if (this.closed) throw new Error("connection is closed");
|
|
383
|
+
if (this.closing) throw new Error("connection is closing");
|
|
384
|
+
if (this.active) {
|
|
385
|
+
throw new Error("connection has an active query");
|
|
386
|
+
}
|
|
387
|
+
this.active = true;
|
|
388
|
+
}
|
|
389
|
+
async release(query) {
|
|
390
|
+
let delay = 1;
|
|
391
|
+
for (; ; ) {
|
|
392
|
+
const result = await this.client.releaseQuery(query);
|
|
393
|
+
if (result.state === "ok") return;
|
|
394
|
+
if (result.state !== "pending") throw readErrorFromResult(result, "release query");
|
|
395
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
396
|
+
delay = Math.min(delay * 2, 32);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async closeForTermination() {
|
|
400
|
+
if (this.active) throw new Error("cannot terminate while a connection has an active query");
|
|
401
|
+
await this.close();
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// node.ts
|
|
406
|
+
init_client();
|
|
407
|
+
import { Worker as NativeWorker } from "node:worker_threads";
|
|
408
|
+
import { fileURLToPath } from "node:url";
|
|
409
|
+
var NodeWorker = class {
|
|
410
|
+
constructor(worker) {
|
|
411
|
+
this.worker = worker;
|
|
412
|
+
}
|
|
413
|
+
worker;
|
|
414
|
+
postMessage(message, transfer = []) {
|
|
415
|
+
this.worker.postMessage(message, transfer);
|
|
416
|
+
}
|
|
417
|
+
onMessage(listener) {
|
|
418
|
+
const wrapped = (message) => listener(message);
|
|
419
|
+
this.worker.on("message", wrapped);
|
|
420
|
+
return () => this.worker.off("message", wrapped);
|
|
421
|
+
}
|
|
422
|
+
onError(listener) {
|
|
423
|
+
const wrapped = (error) => listener(error instanceof Error ? error : new Error(String(error)));
|
|
424
|
+
this.worker.on("error", wrapped);
|
|
425
|
+
return () => this.worker.off("error", wrapped);
|
|
426
|
+
}
|
|
427
|
+
onClose(listener) {
|
|
428
|
+
const wrapped = (code) => listener(new Error(`engine worker exited with code ${String(code)}`));
|
|
429
|
+
this.worker.on("exit", wrapped);
|
|
430
|
+
return () => this.worker.off("exit", wrapped);
|
|
431
|
+
}
|
|
432
|
+
terminate() {
|
|
433
|
+
return this.worker.terminate();
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
function resolveNodeEnginePath(engineUrl) {
|
|
437
|
+
return fileURLToPath(engineUrl);
|
|
438
|
+
}
|
|
439
|
+
function createNodeClient(options = {}) {
|
|
440
|
+
const workerUrl = options.workerUrl ?? new URL("./node_worker.js", import.meta.url);
|
|
441
|
+
const engineUrl = options.engineUrl ?? resolveNodeEnginePath(new URL("./hyperdb-wasm.cjs", import.meta.url));
|
|
442
|
+
return new HyperDBEngineClient(new NodeWorker(new NativeWorker(workerUrl, { workerData: { engineUrl } })));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// index.node.ts
|
|
446
|
+
var HyperDB2 = class _HyperDB extends HyperDB {
|
|
447
|
+
static async create(options = {}) {
|
|
448
|
+
const database = new _HyperDB(createNodeClient(options));
|
|
449
|
+
await database.ready();
|
|
450
|
+
return database;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
export {
|
|
454
|
+
HyperDB2 as HyperDB,
|
|
455
|
+
HyperDBConnection
|
|
456
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// node_worker.ts
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
6
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { createBrotliDecompress } from "node:zlib";
|
|
9
|
+
|
|
10
|
+
// wasm_bindings.ts
|
|
11
|
+
var resultStates = ["ok", "pending", "chunk", "done", "error", "busy"];
|
|
12
|
+
function safeInteger(value, name) {
|
|
13
|
+
const number = typeof value === "bigint" && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : value;
|
|
14
|
+
if (typeof number !== "number" || !Number.isSafeInteger(number) || number < 0) throw new Error(`${name} must be a nonnegative safe integer`);
|
|
15
|
+
return number;
|
|
16
|
+
}
|
|
17
|
+
var HyperDBBindings = class {
|
|
18
|
+
module;
|
|
19
|
+
connect;
|
|
20
|
+
disconnect;
|
|
21
|
+
startQuery;
|
|
22
|
+
pollQuery;
|
|
23
|
+
cancelQuery;
|
|
24
|
+
releaseQuery;
|
|
25
|
+
resultState;
|
|
26
|
+
resultPayloadLength;
|
|
27
|
+
free;
|
|
28
|
+
constructor(module2) {
|
|
29
|
+
this.module = module2;
|
|
30
|
+
this.connect = module2.cwrap("hyperdb_wasm_connect", "pointer", []);
|
|
31
|
+
this.disconnect = module2.cwrap("hyperdb_wasm_disconnect", "pointer", ["number"]);
|
|
32
|
+
this.startQuery = module2.cwrap("hyperdb_wasm_query_start", "pointer", ["number", "string"]);
|
|
33
|
+
this.pollQuery = module2.cwrap("hyperdb_wasm_query_poll", "pointer", ["number"]);
|
|
34
|
+
this.cancelQuery = module2.cwrap("hyperdb_wasm_query_cancel", "pointer", ["number"]);
|
|
35
|
+
this.releaseQuery = module2.cwrap("hyperdb_wasm_query_release", "pointer", ["number"]);
|
|
36
|
+
this.resultState = module2.cwrap("hyperdb_wasm_result_state", "number", ["number"]);
|
|
37
|
+
this.resultPayloadLength = module2.cwrap("hyperdb_wasm_result_payload_len", "number", ["number"]);
|
|
38
|
+
this.free = module2.cwrap("hyperdb_wasm_free", null, ["pointer"]);
|
|
39
|
+
}
|
|
40
|
+
handle(request) {
|
|
41
|
+
const pointer = safeInteger(this.invoke(request), "result pointer");
|
|
42
|
+
const result = this.decodeResult(pointer);
|
|
43
|
+
const response = { id: request.id, type: "response", result };
|
|
44
|
+
return { response, transfer: result.state === "chunk" ? [result.payload.buffer] : [] };
|
|
45
|
+
}
|
|
46
|
+
invoke(request) {
|
|
47
|
+
switch (request.type) {
|
|
48
|
+
case "connect":
|
|
49
|
+
return this.connect();
|
|
50
|
+
case "disconnect":
|
|
51
|
+
return this.disconnect(request.connection);
|
|
52
|
+
case "startQuery":
|
|
53
|
+
return this.startQuery(request.connection, request.sql);
|
|
54
|
+
case "pollQuery":
|
|
55
|
+
return this.pollQuery(request.query);
|
|
56
|
+
case "cancelQuery":
|
|
57
|
+
return this.cancelQuery(request.query);
|
|
58
|
+
case "releaseQuery":
|
|
59
|
+
return this.releaseQuery(request.query);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
decodeResult(pointer) {
|
|
63
|
+
if (pointer === 0) return { state: "error", error: "out of memory" };
|
|
64
|
+
try {
|
|
65
|
+
const rawState = safeInteger(this.resultState(pointer), "result state");
|
|
66
|
+
const state = resultStates[rawState];
|
|
67
|
+
if (!state) return { state: "error", error: `invalid result state ${rawState}` };
|
|
68
|
+
const payloadLength = safeInteger(this.resultPayloadLength(pointer), "result payload length");
|
|
69
|
+
const payloadStart = safeInteger(pointer + 8, "result payload start");
|
|
70
|
+
const payloadEnd = safeInteger(payloadStart + payloadLength, "result payload end");
|
|
71
|
+
if (payloadEnd > this.module.HEAPU8.byteLength) throw new Error("result payload exceeds the wasm heap");
|
|
72
|
+
const payload = new Uint8Array(payloadLength);
|
|
73
|
+
payload.set(this.module.HEAPU8.subarray(payloadStart, payloadEnd));
|
|
74
|
+
switch (state) {
|
|
75
|
+
case "ok":
|
|
76
|
+
return { state, payload };
|
|
77
|
+
case "pending":
|
|
78
|
+
return { state };
|
|
79
|
+
case "chunk":
|
|
80
|
+
return { state, payload };
|
|
81
|
+
case "done":
|
|
82
|
+
return { state };
|
|
83
|
+
case "error":
|
|
84
|
+
return { state, error: new TextDecoder().decode(payload) };
|
|
85
|
+
case "busy":
|
|
86
|
+
return { state, error: new TextDecoder().decode(payload) };
|
|
87
|
+
}
|
|
88
|
+
} finally {
|
|
89
|
+
this.free(pointer);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// protocol.ts
|
|
95
|
+
function isRecord(value) {
|
|
96
|
+
return typeof value === "object" && value !== null;
|
|
97
|
+
}
|
|
98
|
+
function hasKeys(value, keys) {
|
|
99
|
+
const actual = Object.keys(value).sort();
|
|
100
|
+
const expected = [...keys].sort();
|
|
101
|
+
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
102
|
+
}
|
|
103
|
+
function isEngineRequest(value) {
|
|
104
|
+
if (!isRecord(value) || !Number.isSafeInteger(value.id) || typeof value.type !== "string") return false;
|
|
105
|
+
switch (value.type) {
|
|
106
|
+
case "connect":
|
|
107
|
+
return hasKeys(value, ["id", "type"]);
|
|
108
|
+
case "disconnect":
|
|
109
|
+
return hasKeys(value, ["id", "type", "connection"]) && Number.isSafeInteger(value.connection);
|
|
110
|
+
case "startQuery":
|
|
111
|
+
return hasKeys(value, ["id", "type", "connection", "sql"]) && Number.isSafeInteger(value.connection) && typeof value.sql === "string";
|
|
112
|
+
case "pollQuery":
|
|
113
|
+
case "cancelQuery":
|
|
114
|
+
case "releaseQuery":
|
|
115
|
+
return hasKeys(value, ["id", "type", "query"]) && Number.isSafeInteger(value.query);
|
|
116
|
+
default:
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// worker_setup.ts
|
|
122
|
+
function errorText(error) {
|
|
123
|
+
return error instanceof Error ? error.message : String(error);
|
|
124
|
+
}
|
|
125
|
+
function safePointer(value, name) {
|
|
126
|
+
const pointer = typeof value === "bigint" && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : value;
|
|
127
|
+
if (typeof pointer !== "number" || !Number.isSafeInteger(pointer) || pointer < 0) throw new Error(`${name} must be a nonnegative safe integer`);
|
|
128
|
+
return pointer;
|
|
129
|
+
}
|
|
130
|
+
function allocate(module2, bytes) {
|
|
131
|
+
const pointer = safePointer(module2._malloc(bytes.byteLength || 1), "allocation pointer");
|
|
132
|
+
if (pointer === 0) throw new Error("wasm allocation failed");
|
|
133
|
+
if (pointer + bytes.byteLength > module2.HEAPU8.byteLength) throw new Error("allocation exceeds the wasm heap");
|
|
134
|
+
module2.HEAPU8.set(bytes, pointer);
|
|
135
|
+
return pointer;
|
|
136
|
+
}
|
|
137
|
+
function installHttpRuntime(module2, transport2) {
|
|
138
|
+
module2.hyperdbWasmFetchStart = (id, url, offset, length) => {
|
|
139
|
+
const headers = new Headers();
|
|
140
|
+
if (length > 0) headers.set("Range", `bytes=${offset}-${offset + length - 1}`);
|
|
141
|
+
void fetch(url, { headers }).then(async (response) => {
|
|
142
|
+
if (!response.ok && response.status !== 206) {
|
|
143
|
+
module2._hyperdb_wasm_fetch_complete(id, 0, -1);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
147
|
+
module2._hyperdb_wasm_fetch_complete(id, allocate(module2, bytes), bytes.byteLength);
|
|
148
|
+
}).catch((error) => {
|
|
149
|
+
transport2.postMessage({ type: "log", text: `fetch failed: ${errorText(error)}` });
|
|
150
|
+
module2._hyperdb_wasm_fetch_complete(id, 0, -1);
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
module2.hyperdbWasmHttpStart = (id, method, url, headerLines, bodyPointer, bodyLength) => {
|
|
154
|
+
const headers = new Headers();
|
|
155
|
+
for (const line of headerLines.split("\n")) {
|
|
156
|
+
const separator = line.indexOf(":");
|
|
157
|
+
if (separator > 0) headers.set(line.slice(0, separator), line.slice(separator + 1));
|
|
158
|
+
}
|
|
159
|
+
const pointer = safePointer(bodyPointer, "HTTP body pointer");
|
|
160
|
+
const bodyEnd = safePointer(pointer + bodyLength, "HTTP body end");
|
|
161
|
+
if (bodyEnd > module2.HEAPU8.byteLength) throw new Error("HTTP body exceeds the wasm heap");
|
|
162
|
+
const body = bodyLength > 0 ? module2.HEAPU8.slice(pointer, bodyEnd) : void 0;
|
|
163
|
+
const init = { method, headers };
|
|
164
|
+
if (body) init.body = body;
|
|
165
|
+
void fetch(url, init).then(async (response) => {
|
|
166
|
+
const responseBody = new Uint8Array(await response.arrayBuffer());
|
|
167
|
+
let responseHeaders = "";
|
|
168
|
+
response.headers.forEach((value, name) => {
|
|
169
|
+
responseHeaders += `${name.toLowerCase()}:${value}
|
|
170
|
+
`;
|
|
171
|
+
});
|
|
172
|
+
const headersPointer = responseHeaders ? safePointer(module2.stringToNewUTF8(responseHeaders), "headers pointer") : 0;
|
|
173
|
+
const responseBodyPointer = responseBody.byteLength ? allocate(module2, responseBody) : 0;
|
|
174
|
+
module2._hyperdb_wasm_http_complete(id, response.status, headersPointer, responseBodyPointer, responseBody.byteLength);
|
|
175
|
+
}).catch((error) => {
|
|
176
|
+
const errorBytes = new TextEncoder().encode(errorText(error));
|
|
177
|
+
module2._hyperdb_wasm_http_complete(id, -1, 0, allocate(module2, errorBytes), errorBytes.byteLength);
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function configureWorker(module2, transport2) {
|
|
182
|
+
const queuedRequests = [];
|
|
183
|
+
let bindings;
|
|
184
|
+
const handle = (request) => {
|
|
185
|
+
if (!bindings) {
|
|
186
|
+
queuedRequests.push(request);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const result = bindings.handle(request);
|
|
191
|
+
transport2.postMessage(result.response, result.transfer);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
transport2.postMessage({ id: request.id, type: "response", result: { state: "error", error: errorText(error) } });
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
transport2.onMessage((message) => {
|
|
197
|
+
if (isEngineRequest(message)) handle(message);
|
|
198
|
+
});
|
|
199
|
+
module2.arguments = ["--no-demo"];
|
|
200
|
+
module2.print = (text) => transport2.postMessage({ type: "log", text });
|
|
201
|
+
module2.printErr = (text) => transport2.postMessage({ type: "log", text });
|
|
202
|
+
module2.onRuntimeInitialized = () => {
|
|
203
|
+
installHttpRuntime(module2, transport2);
|
|
204
|
+
bindings = new HyperDBBindings(module2);
|
|
205
|
+
transport2.postMessage({ type: "ready" });
|
|
206
|
+
for (const request of queuedRequests.splice(0)) handle(request);
|
|
207
|
+
};
|
|
208
|
+
module2.onAbort = (reason) => transport2.postMessage({ type: "fatal", error: `engine aborted: ${String(reason)}` });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// node_worker.ts
|
|
212
|
+
if (!parentPort) throw new Error("Node engine worker requires a parent port");
|
|
213
|
+
var enginePort = parentPort;
|
|
214
|
+
var options = workerData;
|
|
215
|
+
var engineUrl = options?.engineUrl ?? fileURLToPath(new URL("./hyperdb-wasm.cjs", import.meta.url));
|
|
216
|
+
var transport = {
|
|
217
|
+
postMessage: (message, transfer = []) => enginePort.postMessage(message, [...transfer]),
|
|
218
|
+
onMessage: (listener) => enginePort.on("message", listener)
|
|
219
|
+
};
|
|
220
|
+
process.argv.push("--no-demo");
|
|
221
|
+
var compressedWasm = join(dirname(engineUrl), "hyperdb-wasm.wasm.br");
|
|
222
|
+
var decompressedWasm = Readable.toWeb(createReadStream(compressedWasm).pipe(createBrotliDecompress()));
|
|
223
|
+
var compiledWasm = await WebAssembly.compileStreaming(new Response(decompressedWasm, {
|
|
224
|
+
headers: { "Content-Type": "application/wasm" }
|
|
225
|
+
}));
|
|
226
|
+
var moduleConfiguration = {
|
|
227
|
+
instantiateWasm: (imports, receiveInstance) => {
|
|
228
|
+
void WebAssembly.instantiate(compiledWasm, imports).then((instance) => receiveInstance(instance, compiledWasm));
|
|
229
|
+
return {};
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
var configuredGlobal = globalThis;
|
|
233
|
+
configuredGlobal.HYPERDB_WASM_MODULE = moduleConfiguration;
|
|
234
|
+
var module = createRequire(import.meta.url)(engineUrl);
|
|
235
|
+
delete configuredGlobal.HYPERDB_WASM_MODULE;
|
|
236
|
+
configureWorker(module, transport);
|