hyperdb-wasm 0.0.26792-dev.wasm.g72cc44c1 → 0.0.26862-dev.wasm.g42f1012b
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 +71 -9
- package/dist/browser.js +293 -80
- package/dist/browser_external_directory.d.ts +30 -0
- package/dist/browser_worker.js +541 -9
- package/dist/client.d.ts +7 -1
- package/dist/hyperdb-wasm.cjs +1 -1
- package/dist/hyperdb-wasm.js +1 -1
- package/dist/hyperdb-wasm.wasm.br +0 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.node.d.ts +6 -4
- package/dist/node.d.ts +1 -1
- package/dist/node.js +12389 -191
- package/dist/node_external_directory.d.ts +30 -0
- package/dist/node_worker.js +716 -20
- package/dist/protocol.d.ts +44 -1
- package/dist/raw.browser.js +176 -5
- package/dist/raw.d.ts +1 -1
- package/dist/raw.node.js +167 -6
- package/dist/wasm_bindings.d.ts +46 -0
- package/dist/worker_setup.d.ts +19 -1
- package/examples/browser-attached-databases.html +5 -5
- package/examples/browser.html +1 -1
- package/examples/node-attached-databases.mjs +8 -8
- package/examples/node.mjs +11 -9
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { AsyncRecordBatchStreamReader, TypeMap } from 'apache-arrow';
|
|
|
3
3
|
import type { HyperDBEngineClient } from './client.js';
|
|
4
4
|
import type { BrowserClientOptions } from './browser.js';
|
|
5
5
|
import type { WasmResult } from './protocol.js';
|
|
6
|
+
import type { HyperDBErrorSource } from './protocol.js';
|
|
6
7
|
export interface HyperDBClient {
|
|
7
8
|
ready(): Promise<void>;
|
|
8
9
|
initialize(settings: string): Promise<WasmResult>;
|
|
@@ -16,12 +17,18 @@ export interface HyperDBClient {
|
|
|
16
17
|
attachDatabase(connection: number, databaseName: string, alias: string): Promise<WasmResult>;
|
|
17
18
|
detachDatabase(connection: number, alias: string): Promise<WasmResult>;
|
|
18
19
|
startQuery(connection: number, sql: string): Promise<WasmResult>;
|
|
20
|
+
startQueryWithInfo(connection: number, sql: string): Promise<WasmResult>;
|
|
19
21
|
insertArrowIPCFromPath(connection: number, path: string, name: string, schema: string | null, create: boolean, internal?: boolean): Promise<WasmResult>;
|
|
20
22
|
createTemporaryFile(bytes: Uint8Array): Promise<WasmResult>;
|
|
21
23
|
removeFile(path: string): Promise<WasmResult>;
|
|
24
|
+
registerExternalFile(path: string): Promise<WasmResult>;
|
|
25
|
+
registerExternalFile(name: string, url: string, size: number): Promise<WasmResult>;
|
|
26
|
+
registerExternalDirectory(handle: unknown, mode: 'read' | 'readwrite'): Promise<WasmResult>;
|
|
27
|
+
removeExternalRegistration(path: string): Promise<WasmResult>;
|
|
22
28
|
pollQuery(query: number): Promise<WasmResult>;
|
|
23
29
|
cancelQuery(query: number): Promise<WasmResult>;
|
|
24
30
|
releaseQuery(query: number): Promise<WasmResult>;
|
|
31
|
+
releaseQueryWithInfo(query: number): Promise<WasmResult>;
|
|
25
32
|
shutdown(): Promise<WasmResult>;
|
|
26
33
|
terminate(): Promise<void>;
|
|
27
34
|
}
|
|
@@ -31,8 +38,20 @@ export interface ArrowInsertOptions {
|
|
|
31
38
|
readonly create?: boolean;
|
|
32
39
|
}
|
|
33
40
|
export interface DatabaseCreateOptions {
|
|
34
|
-
readonly storage?: '
|
|
41
|
+
readonly storage?: 'transient' | 'persistent';
|
|
35
42
|
}
|
|
43
|
+
export interface ExternalFileRegistration {
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly url: string;
|
|
46
|
+
readonly size: number;
|
|
47
|
+
}
|
|
48
|
+
export type ExternalDirectoryMode = 'read' | 'readwrite';
|
|
49
|
+
export interface ExternalDirectoryRegistrationOptions {
|
|
50
|
+
readonly mode?: ExternalDirectoryMode;
|
|
51
|
+
}
|
|
52
|
+
type BrowserExternalFileArguments = readonly [name: string, url: string, size: number];
|
|
53
|
+
type NodeExternalFileArguments = readonly [path: string];
|
|
54
|
+
export type ExternalFileArguments = BrowserExternalFileArguments | NodeExternalFileArguments;
|
|
36
55
|
export type HyperDBSettingValue = string | number | boolean | null | readonly HyperDBSettingValue[] | {
|
|
37
56
|
readonly [key: string]: HyperDBSettingValue;
|
|
38
57
|
};
|
|
@@ -43,11 +62,50 @@ export interface HyperDBCreateOptions {
|
|
|
43
62
|
export type BrowserCreateOptions = BrowserClientOptions & HyperDBCreateOptions;
|
|
44
63
|
export interface DatabaseMetadata {
|
|
45
64
|
readonly name: string;
|
|
46
|
-
readonly storage: '
|
|
65
|
+
readonly storage: 'transient' | 'persistent';
|
|
66
|
+
}
|
|
67
|
+
export type QueryMetricValue = null | boolean | number | string | readonly QueryMetricValue[] | {
|
|
68
|
+
readonly [key: string]: QueryMetricValue;
|
|
69
|
+
};
|
|
70
|
+
export type QueryMetrics = Readonly<Record<string, QueryMetricValue>>;
|
|
71
|
+
export interface QueryTimings {
|
|
72
|
+
readonly elapsedSeconds: number | null;
|
|
73
|
+
readonly parsingSeconds: number | null;
|
|
74
|
+
readonly compilationSeconds: number | null;
|
|
75
|
+
readonly executionSeconds: number | null;
|
|
76
|
+
readonly commitSeconds: number | null;
|
|
77
|
+
readonly timeToScheduleSeconds: number | null;
|
|
78
|
+
readonly timeToFirstResultSeconds: number | null;
|
|
79
|
+
readonly cancellationSeconds: number | null;
|
|
80
|
+
}
|
|
81
|
+
export interface QueryInfo {
|
|
82
|
+
readonly status: 'succeeded' | 'failed' | 'canceled';
|
|
83
|
+
readonly started: boolean;
|
|
84
|
+
readonly statementKind: string | null;
|
|
85
|
+
readonly producesRows: boolean;
|
|
86
|
+
readonly rowCount: bigint | null;
|
|
87
|
+
readonly columnCount: number | null;
|
|
88
|
+
readonly cancellationRequestedByUser: boolean;
|
|
89
|
+
readonly queryPlan: Readonly<Record<string, unknown>> | null;
|
|
90
|
+
readonly dependencies: readonly string[];
|
|
91
|
+
readonly timings: QueryTimings;
|
|
92
|
+
readonly preExecutionMetrics: QueryMetrics | null;
|
|
93
|
+
readonly executionMetrics: QueryMetrics | null;
|
|
94
|
+
}
|
|
95
|
+
export interface QueryResult<Result> {
|
|
96
|
+
readonly result: Result;
|
|
97
|
+
readonly info: Promise<QueryInfo>;
|
|
98
|
+
}
|
|
99
|
+
export declare class HyperDBError extends Error {
|
|
100
|
+
readonly sqlState: string;
|
|
101
|
+
readonly errorSource: HyperDBErrorSource;
|
|
102
|
+
readonly detail: string;
|
|
103
|
+
readonly hint: string;
|
|
104
|
+
constructor(message: string, sqlState: string, errorSource: HyperDBErrorSource, detail?: string, hint?: string);
|
|
47
105
|
}
|
|
48
106
|
declare function initializeHyperDB(client: HyperDBClient, settings: HyperDBSettings | undefined): Promise<void>;
|
|
49
|
-
export declare class HyperDB {
|
|
50
|
-
|
|
107
|
+
export declare class HyperDB<ExternalFileArgs extends ExternalFileArguments = BrowserExternalFileArguments> {
|
|
108
|
+
protected readonly client: HyperDBClient;
|
|
51
109
|
private terminated;
|
|
52
110
|
private termination;
|
|
53
111
|
private activeDatabaseOperations;
|
|
@@ -59,11 +117,14 @@ export declare class HyperDB {
|
|
|
59
117
|
createDatabase(databaseName: string, options?: DatabaseCreateOptions): Promise<void>;
|
|
60
118
|
openDatabase(databaseName: string): Promise<void>;
|
|
61
119
|
listDatabases(): Promise<DatabaseMetadata[]>;
|
|
120
|
+
registerExternalFile(...args: ExternalFileArgs): Promise<string>;
|
|
121
|
+
removeExternalRegistration(path: string): Promise<void>;
|
|
122
|
+
protected registerExternalDirectorySource(source: unknown, mode: ExternalDirectoryMode): Promise<string>;
|
|
62
123
|
checkpointDatabase(databaseName: string): Promise<void>;
|
|
63
124
|
dropDatabase(databaseName: string): Promise<void>;
|
|
64
125
|
terminate(): Promise<void>;
|
|
65
126
|
private ensureRunning;
|
|
66
|
-
|
|
127
|
+
protected runDatabaseOperation<Result>(operation: () => Promise<Result>): Promise<Result>;
|
|
67
128
|
}
|
|
68
129
|
export declare class HyperDBConnection {
|
|
69
130
|
private readonly client;
|
|
@@ -76,19 +137,20 @@ export declare class HyperDBConnection {
|
|
|
76
137
|
close(): Promise<void>;
|
|
77
138
|
attachDatabase(databaseName: string, alias?: string): Promise<void>;
|
|
78
139
|
detachDatabase(alias: string): Promise<void>;
|
|
79
|
-
sendIPC(sql: string): AsyncIterable<Uint8Array
|
|
80
|
-
queryIPC(sql: string): Promise<Uint8Array
|
|
140
|
+
sendIPC(sql: string): QueryResult<AsyncIterable<Uint8Array>>;
|
|
141
|
+
queryIPC(sql: string): QueryResult<Promise<Uint8Array>>;
|
|
81
142
|
send<T extends TypeMap = {
|
|
82
143
|
[key: string]: DataType;
|
|
83
|
-
}>(sql: string): Promise<AsyncRecordBatchStreamReader<T
|
|
144
|
+
}>(sql: string): QueryResult<Promise<AsyncRecordBatchStreamReader<T>>>;
|
|
84
145
|
query<T extends TypeMap = {
|
|
85
146
|
[key: string]: DataType;
|
|
86
|
-
}>(sql: string): Promise<Table<T
|
|
147
|
+
}>(sql: string): QueryResult<Promise<Table<T>>>;
|
|
87
148
|
insertArrowTable(table: Table, options: ArrowInsertOptions): Promise<void>;
|
|
88
149
|
insertArrowIPCFromPath(path: string, options: ArrowInsertOptions): Promise<void>;
|
|
89
150
|
private insertArrowIPCFromPathInternal;
|
|
90
151
|
private ensureAvailable;
|
|
91
152
|
private release;
|
|
153
|
+
private releaseWithInfo;
|
|
92
154
|
closeForTermination(): Promise<void>;
|
|
93
155
|
}
|
|
94
156
|
export type { HyperDBEngineClient };
|
package/dist/browser.js
CHANGED
|
@@ -32,12 +32,16 @@ function isWasmResult(value) {
|
|
|
32
32
|
case "done":
|
|
33
33
|
return hasKeys(value, ["state"]);
|
|
34
34
|
case "error":
|
|
35
|
+
return hasKeys(value, ["state", "error"]) && (typeof value.error === "string" || isHyperDBErrorData(value.error));
|
|
35
36
|
case "busy":
|
|
36
37
|
return hasKeys(value, ["state", "error"]) && typeof value.error === "string";
|
|
37
38
|
default:
|
|
38
39
|
return false;
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
function isHyperDBErrorData(value) {
|
|
43
|
+
return isRecord(value) && hasKeys(value, ["sqlState", "errorSource", "message", "detail", "hint"]) && typeof value.sqlState === "string" && /^[0-9A-Z]{5}$/.test(value.sqlState) && (value.errorSource === "User" || value.errorSource === "System" || value.errorSource === "OverloadRejection") && typeof value.message === "string" && typeof value.detail === "string" && typeof value.hint === "string";
|
|
44
|
+
}
|
|
41
45
|
function isWorkerEvent(message) {
|
|
42
46
|
if (!isRecord(message) || typeof message.type !== "string") return false;
|
|
43
47
|
switch (message.type) {
|
|
@@ -126,8 +130,11 @@ var init_client = __esm({
|
|
|
126
130
|
shutdown() {
|
|
127
131
|
return this.request({ id: this.nextId++, type: "shutdown" });
|
|
128
132
|
}
|
|
129
|
-
startQuery(connection, sql) {
|
|
130
|
-
return this.request({ id: this.nextId++, type: "startQuery", connection, sql });
|
|
133
|
+
startQuery(connection, sql, sqlTestMode = false) {
|
|
134
|
+
return this.request({ id: this.nextId++, type: "startQuery", connection, sql, ...sqlTestMode && { sqlTestMode } });
|
|
135
|
+
}
|
|
136
|
+
startQueryWithInfo(connection, sql) {
|
|
137
|
+
return this.request({ id: this.nextId++, type: "startQueryWithInfo", connection, sql });
|
|
131
138
|
}
|
|
132
139
|
insertArrowIPCFromPath(connection, path, name, schema, create, internal = false) {
|
|
133
140
|
return this.request({ id: this.nextId++, type: "insertArrowIPCFromPath", connection, path, name, schema, create, internal });
|
|
@@ -139,6 +146,15 @@ var init_client = __esm({
|
|
|
139
146
|
removeFile(path) {
|
|
140
147
|
return this.request({ id: this.nextId++, type: "removeFile", path });
|
|
141
148
|
}
|
|
149
|
+
registerExternalFile(pathOrName, url, size) {
|
|
150
|
+
return url === void 0 || size === void 0 ? this.request({ id: this.nextId++, type: "registerExternalFile", path: pathOrName }) : this.request({ id: this.nextId++, type: "registerExternalFile", name: pathOrName, url, size });
|
|
151
|
+
}
|
|
152
|
+
registerExternalDirectory(source, mode) {
|
|
153
|
+
return typeof source === "string" ? this.request({ id: this.nextId++, type: "registerExternalDirectory", path: source, mode }) : this.request({ id: this.nextId++, type: "registerExternalDirectory", handle: source, mode });
|
|
154
|
+
}
|
|
155
|
+
removeExternalRegistration(path) {
|
|
156
|
+
return this.request({ id: this.nextId++, type: "removeExternalRegistration", path });
|
|
157
|
+
}
|
|
142
158
|
pollQuery(query) {
|
|
143
159
|
return this.request({ id: this.nextId++, type: "pollQuery", query });
|
|
144
160
|
}
|
|
@@ -148,6 +164,9 @@ var init_client = __esm({
|
|
|
148
164
|
releaseQuery(query) {
|
|
149
165
|
return this.request({ id: this.nextId++, type: "releaseQuery", query });
|
|
150
166
|
}
|
|
167
|
+
releaseQueryWithInfo(query) {
|
|
168
|
+
return this.request({ id: this.nextId++, type: "releaseQueryWithInfo", query });
|
|
169
|
+
}
|
|
151
170
|
async terminate() {
|
|
152
171
|
await this.transport.terminate();
|
|
153
172
|
this.fail(new Error("engine worker terminated"));
|
|
@@ -211,6 +230,12 @@ function assertBrowserSupport(environment = globalThis) {
|
|
|
211
230
|
if (!environment.WebAssembly.validate(memory64Probe)) {
|
|
212
231
|
throw new Error("HyperDB cannot initialize because this browser does not yet support WebAssembly memory64. Support for platforms such as iOS is coming soon.");
|
|
213
232
|
}
|
|
233
|
+
try {
|
|
234
|
+
const Memory64 = environment.WebAssembly.Memory;
|
|
235
|
+
new Memory64({ initial: 1n, maximum: 262144n, shared: true, address: "i64" });
|
|
236
|
+
} catch {
|
|
237
|
+
throw new Error("HyperDB cannot initialize because this browser does not support a shared 16 GiB WebAssembly memory64 declaration");
|
|
238
|
+
}
|
|
214
239
|
if (typeof environment.BigInt !== "function") {
|
|
215
240
|
throw new Error("HyperDB cannot initialize because this browser does not yet support WebAssembly BigInt integration. Support for platforms such as iOS is coming soon.");
|
|
216
241
|
}
|
|
@@ -220,6 +245,9 @@ function assertBrowserSupport(environment = globalThis) {
|
|
|
220
245
|
if (!environment.WebAssembly.validate(exceptionsProbe)) {
|
|
221
246
|
throw new Error("HyperDB cannot initialize because this browser does not support WebAssembly exception handling");
|
|
222
247
|
}
|
|
248
|
+
if (!environment.WebAssembly.validate(tailCallsProbe)) {
|
|
249
|
+
throw new Error("HyperDB cannot initialize because this browser does not support WebAssembly tail calls");
|
|
250
|
+
}
|
|
223
251
|
if (typeof environment.Worker !== "function") {
|
|
224
252
|
throw new Error("HyperDB cannot initialize because this browser does not support Web Workers");
|
|
225
253
|
}
|
|
@@ -245,7 +273,7 @@ function createBrowserClient(options = {}) {
|
|
|
245
273
|
options.onSetupProgress
|
|
246
274
|
);
|
|
247
275
|
}
|
|
248
|
-
var memory64Probe, threadsProbe, exceptionsProbe, BrowserWorker;
|
|
276
|
+
var memory64Probe, threadsProbe, exceptionsProbe, tailCallsProbe, BrowserWorker;
|
|
249
277
|
var init_browser = __esm({
|
|
250
278
|
"src/browser.ts"() {
|
|
251
279
|
"use strict";
|
|
@@ -253,6 +281,7 @@ var init_browser = __esm({
|
|
|
253
281
|
memory64Probe = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 5, 3, 1, 4, 1]);
|
|
254
282
|
threadsProbe = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 5, 4, 1, 3, 1, 1]);
|
|
255
283
|
exceptionsProbe = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 13, 3, 1, 0, 0]);
|
|
284
|
+
tailCallsProbe = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 3, 3, 2, 0, 0, 10, 9, 2, 4, 0, 18, 1, 11, 2, 0, 11]);
|
|
256
285
|
BrowserWorker = class {
|
|
257
286
|
constructor(worker) {
|
|
258
287
|
this.worker = worker;
|
|
@@ -12123,7 +12152,72 @@ RecordBatchFileWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
|
|
|
12123
12152
|
RecordBatchStreamWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
|
|
12124
12153
|
|
|
12125
12154
|
// src/api.ts
|
|
12155
|
+
var HyperDBError = class extends Error {
|
|
12156
|
+
sqlState;
|
|
12157
|
+
errorSource;
|
|
12158
|
+
detail;
|
|
12159
|
+
hint;
|
|
12160
|
+
constructor(message, sqlState, errorSource, detail = "", hint = "") {
|
|
12161
|
+
super(message);
|
|
12162
|
+
this.name = "HyperDBError";
|
|
12163
|
+
this.sqlState = sqlState;
|
|
12164
|
+
this.errorSource = errorSource;
|
|
12165
|
+
this.detail = detail;
|
|
12166
|
+
this.hint = hint;
|
|
12167
|
+
}
|
|
12168
|
+
};
|
|
12126
12169
|
var maxPersistentDatabaseNameLength = 200;
|
|
12170
|
+
function deferred() {
|
|
12171
|
+
let resolve;
|
|
12172
|
+
let reject;
|
|
12173
|
+
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
12174
|
+
resolve = resolvePromise;
|
|
12175
|
+
reject = rejectPromise;
|
|
12176
|
+
});
|
|
12177
|
+
void promise.catch(() => void 0);
|
|
12178
|
+
return { promise, resolve, reject };
|
|
12179
|
+
}
|
|
12180
|
+
function queryResult(result, info) {
|
|
12181
|
+
return { result, info };
|
|
12182
|
+
}
|
|
12183
|
+
function isRecord2(value) {
|
|
12184
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12185
|
+
}
|
|
12186
|
+
function decodeQueryInfo(payload) {
|
|
12187
|
+
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(payload));
|
|
12188
|
+
if (!isRecord2(value) || value.version !== 1) throw new Error("query information has an unsupported version");
|
|
12189
|
+
if (value.status !== "succeeded" && value.status !== "failed" && value.status !== "canceled") throw new Error("query information has an invalid status");
|
|
12190
|
+
if (typeof value.started !== "boolean" || typeof value.producesRows !== "boolean" || typeof value.cancellationRequestedByUser !== "boolean") {
|
|
12191
|
+
throw new Error("query information has invalid flags");
|
|
12192
|
+
}
|
|
12193
|
+
if (value.statementKind !== null && typeof value.statementKind !== "string") throw new Error("query information has an invalid statement kind");
|
|
12194
|
+
if (value.rowCount !== null && (typeof value.rowCount !== "string" || !/^\d+$/.test(value.rowCount))) throw new Error("query information has an invalid row count");
|
|
12195
|
+
if (value.columnCount !== null && (!Number.isSafeInteger(value.columnCount) || value.columnCount < 0)) throw new Error("query information has an invalid column count");
|
|
12196
|
+
if (value.queryPlan !== null && !isRecord2(value.queryPlan)) throw new Error("query information has an invalid query plan");
|
|
12197
|
+
if (!Array.isArray(value.dependencies) || value.dependencies.some((dependency) => typeof dependency !== "string")) throw new Error("query information has invalid dependencies");
|
|
12198
|
+
if (!isRecord2(value.timings)) throw new Error("query information has invalid timings");
|
|
12199
|
+
const timingNames = ["elapsedSeconds", "parsingSeconds", "compilationSeconds", "executionSeconds", "commitSeconds", "timeToScheduleSeconds", "timeToFirstResultSeconds", "cancellationSeconds"];
|
|
12200
|
+
for (const name of timingNames) {
|
|
12201
|
+
const timing = value.timings[name];
|
|
12202
|
+
if (timing !== null && (typeof timing !== "number" || !Number.isFinite(timing) || timing < 0)) throw new Error(`query information has an invalid ${name}`);
|
|
12203
|
+
}
|
|
12204
|
+
if (value.preExecutionMetrics !== null && !isRecord2(value.preExecutionMetrics)) throw new Error("query information has invalid pre-execution metrics");
|
|
12205
|
+
if (value.executionMetrics !== null && !isRecord2(value.executionMetrics)) throw new Error("query information has invalid execution metrics");
|
|
12206
|
+
return {
|
|
12207
|
+
status: value.status,
|
|
12208
|
+
started: value.started,
|
|
12209
|
+
statementKind: value.statementKind,
|
|
12210
|
+
producesRows: value.producesRows,
|
|
12211
|
+
rowCount: value.rowCount === null ? null : BigInt(value.rowCount),
|
|
12212
|
+
columnCount: value.columnCount,
|
|
12213
|
+
cancellationRequestedByUser: value.cancellationRequestedByUser,
|
|
12214
|
+
queryPlan: value.queryPlan,
|
|
12215
|
+
dependencies: value.dependencies,
|
|
12216
|
+
timings: value.timings,
|
|
12217
|
+
preExecutionMetrics: value.preExecutionMetrics,
|
|
12218
|
+
executionMetrics: value.executionMetrics
|
|
12219
|
+
};
|
|
12220
|
+
}
|
|
12127
12221
|
function normalizeArrowInsertOptions(options) {
|
|
12128
12222
|
if (!options.name) throw new Error("Arrow insert table name must not be empty");
|
|
12129
12223
|
return { name: options.name, schema: options.schema ?? null, create: options.create ?? true };
|
|
@@ -12172,7 +12266,11 @@ function readHandleFromResult(result, operation) {
|
|
|
12172
12266
|
return new DataView(result.payload.buffer, result.payload.byteOffset, result.payload.byteLength).getUint32(0, true);
|
|
12173
12267
|
}
|
|
12174
12268
|
function readErrorFromResult(result, operation) {
|
|
12175
|
-
if (result.state === "error"
|
|
12269
|
+
if (result.state === "error") {
|
|
12270
|
+
if (typeof result.error === "string") return new Error(result.error);
|
|
12271
|
+
return new HyperDBError(result.error.message, result.error.sqlState, result.error.errorSource, result.error.detail, result.error.hint);
|
|
12272
|
+
}
|
|
12273
|
+
if (result.state === "busy") return new Error(result.error);
|
|
12176
12274
|
return new Error(`${operation} returned unexpected state ${result.state}`);
|
|
12177
12275
|
}
|
|
12178
12276
|
function expectOk(result, operation) {
|
|
@@ -12206,7 +12304,7 @@ function readDatabasesFromResult(result) {
|
|
|
12206
12304
|
throw invalidResult();
|
|
12207
12305
|
}
|
|
12208
12306
|
if (!name || name.includes("\0") || rawStorage !== 0 && rawStorage !== 1) throw invalidResult();
|
|
12209
|
-
databases.push({ name, storage: rawStorage === 0 ? "
|
|
12307
|
+
databases.push({ name, storage: rawStorage === 0 ? "transient" : "persistent" });
|
|
12210
12308
|
offset += nameLength;
|
|
12211
12309
|
}
|
|
12212
12310
|
if (offset !== result.payload.byteLength) throw invalidResult();
|
|
@@ -12253,7 +12351,7 @@ var HyperDB = class _HyperDB {
|
|
|
12253
12351
|
}
|
|
12254
12352
|
async createDatabase(databaseName, options = {}) {
|
|
12255
12353
|
validateDatabaseName(databaseName);
|
|
12256
|
-
if (options.storage !== void 0 && options.storage !== "
|
|
12354
|
+
if (options.storage !== void 0 && options.storage !== "transient" && options.storage !== "persistent") {
|
|
12257
12355
|
throw new Error(`Unsupported database storage: ${String(options.storage)}`);
|
|
12258
12356
|
}
|
|
12259
12357
|
if (options.storage === "persistent") validatePersistentDatabaseName(databaseName);
|
|
@@ -12270,6 +12368,57 @@ var HyperDB = class _HyperDB {
|
|
|
12270
12368
|
listDatabases() {
|
|
12271
12369
|
return this.runDatabaseOperation(async () => readDatabasesFromResult(await this.client.listDatabases()));
|
|
12272
12370
|
}
|
|
12371
|
+
async registerExternalFile(...args) {
|
|
12372
|
+
if (args.length === 1) {
|
|
12373
|
+
const [sourcePath] = args;
|
|
12374
|
+
if (!sourcePath) throw new Error("External file path must not be empty");
|
|
12375
|
+
if (sourcePath.includes("\0")) throw new Error("External file path must not contain NUL");
|
|
12376
|
+
return this.runDatabaseOperation(async () => {
|
|
12377
|
+
const result = await this.client.registerExternalFile(sourcePath);
|
|
12378
|
+
expectOk(result, "register external file");
|
|
12379
|
+
const path = new TextDecoder("utf-8", { fatal: true }).decode(result.payload);
|
|
12380
|
+
if (!/^\/mnt\/external\/[^/]+\/[^/]+$/.test(path)) throw new Error("register external file returned an invalid path");
|
|
12381
|
+
return path;
|
|
12382
|
+
});
|
|
12383
|
+
}
|
|
12384
|
+
const [name, url, size] = args;
|
|
12385
|
+
if (!name) throw new Error("External file name must not be empty");
|
|
12386
|
+
if (name.includes("\0")) throw new Error("External file name must not contain NUL");
|
|
12387
|
+
if (name === "." || name === ".." || name.includes("/") || name.includes("\\")) {
|
|
12388
|
+
throw new Error("External file name must be a basename without path separators");
|
|
12389
|
+
}
|
|
12390
|
+
if (!Number.isSafeInteger(size) || size < 0) throw new Error("External file size must be a nonnegative safe integer");
|
|
12391
|
+
let scheme;
|
|
12392
|
+
try {
|
|
12393
|
+
scheme = new URL(url).protocol;
|
|
12394
|
+
} catch {
|
|
12395
|
+
throw new Error("External file URL must use the blob scheme");
|
|
12396
|
+
}
|
|
12397
|
+
if (scheme !== "blob:") throw new Error("External file URL must use the blob scheme");
|
|
12398
|
+
return this.runDatabaseOperation(async () => {
|
|
12399
|
+
const result = await this.client.registerExternalFile(name, url, size);
|
|
12400
|
+
expectOk(result, "register external file");
|
|
12401
|
+
const path = new TextDecoder("utf-8", { fatal: true }).decode(result.payload);
|
|
12402
|
+
if (!/^\/mnt\/external\/[^/]+\/[^/]+$/.test(path)) throw new Error("register external file returned an invalid path");
|
|
12403
|
+
return path;
|
|
12404
|
+
});
|
|
12405
|
+
}
|
|
12406
|
+
async removeExternalRegistration(path) {
|
|
12407
|
+
if (!path) throw new Error("External registration path must not be empty");
|
|
12408
|
+
if (path.includes("\0")) throw new Error("External registration path must not contain null bytes");
|
|
12409
|
+
await this.runDatabaseOperation(async () => {
|
|
12410
|
+
expectOk(await this.client.removeExternalRegistration(path), "remove external registration");
|
|
12411
|
+
});
|
|
12412
|
+
}
|
|
12413
|
+
registerExternalDirectorySource(source, mode) {
|
|
12414
|
+
return this.runDatabaseOperation(async () => {
|
|
12415
|
+
const result = await this.client.registerExternalDirectory(source, mode);
|
|
12416
|
+
expectOk(result, "register external directory");
|
|
12417
|
+
const path = new TextDecoder("utf-8", { fatal: true }).decode(result.payload);
|
|
12418
|
+
if (!/^\/mnt\/external\/[^/]+\/[^/]+$/.test(path)) throw new Error("register external directory returned an invalid path");
|
|
12419
|
+
return path;
|
|
12420
|
+
});
|
|
12421
|
+
}
|
|
12273
12422
|
async checkpointDatabase(databaseName) {
|
|
12274
12423
|
validateDatabaseName(databaseName);
|
|
12275
12424
|
await this.runDatabaseOperation(async () => {
|
|
@@ -12374,89 +12523,111 @@ var HyperDBConnection = class {
|
|
|
12374
12523
|
}
|
|
12375
12524
|
sendIPC(sql) {
|
|
12376
12525
|
const connection = this;
|
|
12377
|
-
|
|
12378
|
-
|
|
12526
|
+
const queryInfo = deferred();
|
|
12527
|
+
const result = (async function* () {
|
|
12528
|
+
let query;
|
|
12529
|
+
let terminal = false;
|
|
12530
|
+
let prefetched;
|
|
12531
|
+
let acquired = false;
|
|
12532
|
+
try {
|
|
12379
12533
|
connection.ensureAvailable();
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
while (result.state === "pending") {
|
|
12389
|
-
await yieldToEventLoop();
|
|
12390
|
-
result = await connection.client.pollQuery(query);
|
|
12391
|
-
}
|
|
12392
|
-
if (result.state === "chunk") {
|
|
12393
|
-
prefetched = connection.client.pollQuery(query);
|
|
12394
|
-
void prefetched.catch(() => void 0);
|
|
12395
|
-
yield result.payload;
|
|
12396
|
-
continue;
|
|
12397
|
-
}
|
|
12398
|
-
terminal = true;
|
|
12399
|
-
if (result.state === "done") return;
|
|
12400
|
-
throw readErrorFromResult(result, "poll query");
|
|
12534
|
+
acquired = true;
|
|
12535
|
+
query = readHandleFromResult(await connection.client.startQueryWithInfo(connection.connection, sql), "start query");
|
|
12536
|
+
for (; ; ) {
|
|
12537
|
+
let result2 = prefetched ? await prefetched : await connection.client.pollQuery(query);
|
|
12538
|
+
prefetched = void 0;
|
|
12539
|
+
while (result2.state === "pending") {
|
|
12540
|
+
await yieldToEventLoop();
|
|
12541
|
+
result2 = await connection.client.pollQuery(query);
|
|
12401
12542
|
}
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12543
|
+
if (result2.state === "chunk") {
|
|
12544
|
+
prefetched = connection.client.pollQuery(query);
|
|
12545
|
+
void prefetched.catch(() => void 0);
|
|
12546
|
+
yield result2.payload;
|
|
12547
|
+
continue;
|
|
12548
|
+
}
|
|
12549
|
+
terminal = true;
|
|
12550
|
+
if (result2.state === "done") return;
|
|
12551
|
+
throw readErrorFromResult(result2, "poll query");
|
|
12552
|
+
}
|
|
12553
|
+
} catch (error) {
|
|
12554
|
+
if (query === void 0 || !terminal) queryInfo.reject(error);
|
|
12555
|
+
throw error;
|
|
12556
|
+
} finally {
|
|
12557
|
+
try {
|
|
12558
|
+
if (query !== void 0) {
|
|
12559
|
+
let cleanupError;
|
|
12560
|
+
let info;
|
|
12561
|
+
if (prefetched) {
|
|
12562
|
+
try {
|
|
12563
|
+
await prefetched;
|
|
12564
|
+
} catch (error) {
|
|
12565
|
+
cleanupError = error;
|
|
12420
12566
|
}
|
|
12567
|
+
prefetched = void 0;
|
|
12568
|
+
}
|
|
12569
|
+
if (!terminal) {
|
|
12421
12570
|
try {
|
|
12422
|
-
await connection.
|
|
12571
|
+
expectOk(await connection.client.cancelQuery(query), "cancel query");
|
|
12423
12572
|
} catch (error) {
|
|
12424
12573
|
cleanupError ??= error;
|
|
12425
12574
|
}
|
|
12426
|
-
if (cleanupError) throw cleanupError;
|
|
12427
12575
|
}
|
|
12428
|
-
|
|
12429
|
-
|
|
12576
|
+
try {
|
|
12577
|
+
info = await connection.releaseWithInfo(query);
|
|
12578
|
+
} catch (error) {
|
|
12579
|
+
cleanupError ??= error;
|
|
12580
|
+
}
|
|
12581
|
+
if (cleanupError) {
|
|
12582
|
+
queryInfo.reject(cleanupError);
|
|
12583
|
+
throw cleanupError;
|
|
12584
|
+
}
|
|
12585
|
+
queryInfo.resolve(info);
|
|
12430
12586
|
}
|
|
12587
|
+
} finally {
|
|
12588
|
+
if (acquired) connection.active = false;
|
|
12431
12589
|
}
|
|
12432
12590
|
}
|
|
12433
|
-
};
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
chunks
|
|
12440
|
-
length
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
offset
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
return
|
|
12454
|
-
}
|
|
12455
|
-
|
|
12456
|
-
const
|
|
12457
|
-
const
|
|
12458
|
-
|
|
12459
|
-
|
|
12591
|
+
})();
|
|
12592
|
+
return queryResult(result, queryInfo.promise);
|
|
12593
|
+
}
|
|
12594
|
+
queryIPC(sql) {
|
|
12595
|
+
const stream = this.sendIPC(sql);
|
|
12596
|
+
const result = (async () => {
|
|
12597
|
+
const chunks = [];
|
|
12598
|
+
let length = 0;
|
|
12599
|
+
for await (const chunk of stream.result) {
|
|
12600
|
+
chunks.push(chunk);
|
|
12601
|
+
length += chunk.byteLength;
|
|
12602
|
+
}
|
|
12603
|
+
const bytes = new Uint8Array(length);
|
|
12604
|
+
let offset = 0;
|
|
12605
|
+
for (const chunk of chunks) {
|
|
12606
|
+
bytes.set(chunk, offset);
|
|
12607
|
+
offset += chunk.byteLength;
|
|
12608
|
+
}
|
|
12609
|
+
return bytes;
|
|
12610
|
+
})();
|
|
12611
|
+
return queryResult(result, stream.info);
|
|
12612
|
+
}
|
|
12613
|
+
send(sql) {
|
|
12614
|
+
const stream = this.sendIPC(sql);
|
|
12615
|
+
const result = (async () => {
|
|
12616
|
+
const reader = await RecordBatchReader.from(stream.result);
|
|
12617
|
+
if (!reader.isAsync() || !reader.isStream()) throw new Error("query did not produce an Arrow async stream");
|
|
12618
|
+
return reader;
|
|
12619
|
+
})();
|
|
12620
|
+
return queryResult(result, stream.info);
|
|
12621
|
+
}
|
|
12622
|
+
query(sql) {
|
|
12623
|
+
const stream = this.send(sql);
|
|
12624
|
+
const result = (async () => {
|
|
12625
|
+
const reader = await stream.result;
|
|
12626
|
+
const batches = [];
|
|
12627
|
+
for await (const batch of reader) batches.push(batch);
|
|
12628
|
+
return new Table(reader.schema, batches);
|
|
12629
|
+
})();
|
|
12630
|
+
return queryResult(result, stream.info);
|
|
12460
12631
|
}
|
|
12461
12632
|
async insertArrowTable(table, options) {
|
|
12462
12633
|
this.ensureAvailable();
|
|
@@ -12520,12 +12691,54 @@ var HyperDBConnection = class {
|
|
|
12520
12691
|
delay = Math.min(delay * 2, 32);
|
|
12521
12692
|
}
|
|
12522
12693
|
}
|
|
12694
|
+
async releaseWithInfo(query) {
|
|
12695
|
+
let delay = 1;
|
|
12696
|
+
for (; ; ) {
|
|
12697
|
+
const result = await this.client.releaseQueryWithInfo(query);
|
|
12698
|
+
if (result.state === "ok") return decodeQueryInfo(result.payload);
|
|
12699
|
+
if (result.state !== "pending") throw readErrorFromResult(result, "release query");
|
|
12700
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
12701
|
+
delay = Math.min(delay * 2, 32);
|
|
12702
|
+
}
|
|
12703
|
+
}
|
|
12523
12704
|
async closeForTermination() {
|
|
12524
12705
|
if (this.active) throw new Error("cannot terminate while a connection has an active operation");
|
|
12525
12706
|
await this.close();
|
|
12526
12707
|
}
|
|
12527
12708
|
};
|
|
12709
|
+
|
|
12710
|
+
// src/index.ts
|
|
12711
|
+
init_browser();
|
|
12712
|
+
var HyperDB2 = class _HyperDB extends HyperDB {
|
|
12713
|
+
static async create(options = {}) {
|
|
12714
|
+
const client = createBrowserClient(options);
|
|
12715
|
+
const database = new _HyperDB(client);
|
|
12716
|
+
try {
|
|
12717
|
+
await database.ready();
|
|
12718
|
+
await initializeHyperDB(client, options.settings);
|
|
12719
|
+
return database;
|
|
12720
|
+
} catch (error) {
|
|
12721
|
+
try {
|
|
12722
|
+
await client.terminate();
|
|
12723
|
+
} catch {
|
|
12724
|
+
}
|
|
12725
|
+
throw error;
|
|
12726
|
+
}
|
|
12727
|
+
}
|
|
12728
|
+
async registerExternalDirectory(handle, options = {}) {
|
|
12729
|
+
const mode = options.mode ?? "read";
|
|
12730
|
+
if (mode !== "read" && mode !== "readwrite") throw new Error(`Unsupported external directory mode: ${String(mode)}`);
|
|
12731
|
+
if (!handle || handle.kind !== "directory" || typeof handle.queryPermission !== "function" || typeof handle.requestPermission !== "function") {
|
|
12732
|
+
throw new Error("External directory handle must be a FileSystemDirectoryHandle");
|
|
12733
|
+
}
|
|
12734
|
+
let permission = await handle.queryPermission({ mode });
|
|
12735
|
+
if (permission !== "granted") permission = await handle.requestPermission({ mode });
|
|
12736
|
+
if (permission !== "granted") throw new Error(`External directory ${mode} permission was not granted`);
|
|
12737
|
+
return this.registerExternalDirectorySource(handle, mode);
|
|
12738
|
+
}
|
|
12739
|
+
};
|
|
12528
12740
|
export {
|
|
12529
|
-
HyperDB,
|
|
12530
|
-
HyperDBConnection
|
|
12741
|
+
HyperDB2 as HyperDB,
|
|
12742
|
+
HyperDBConnection,
|
|
12743
|
+
HyperDBError
|
|
12531
12744
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ExternalDirectoryBackend } from './wasm_bindings.js';
|
|
2
|
+
export declare class BrowserExternalDirectoryBackend implements ExternalDirectoryBackend {
|
|
3
|
+
private readonly directories;
|
|
4
|
+
private readonly paths;
|
|
5
|
+
private readonly files;
|
|
6
|
+
private readonly writers;
|
|
7
|
+
register(handle: unknown, mode: 'read' | 'readwrite'): {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly locator: string;
|
|
10
|
+
};
|
|
11
|
+
registered(path: string, locator: string): void;
|
|
12
|
+
removed(pathOrLocator: string): void;
|
|
13
|
+
list(locator: string): Promise<readonly string[]>;
|
|
14
|
+
open(locator: string, path: string): Promise<{
|
|
15
|
+
readonly locator: string;
|
|
16
|
+
readonly size: number;
|
|
17
|
+
}>;
|
|
18
|
+
read(locator: string, offset: number, length: number, expectedSize: number): Promise<Uint8Array> | undefined;
|
|
19
|
+
createDirectories(locator: string, path: string): Promise<void>;
|
|
20
|
+
openWriter(writer: number, locator: string, path: string): Promise<void>;
|
|
21
|
+
write(writer: number, offset: number, bytes: Uint8Array): Promise<void>;
|
|
22
|
+
finish(writer: number, size: number): Promise<void>;
|
|
23
|
+
abort(writer: number): Promise<void>;
|
|
24
|
+
shutdown(): void;
|
|
25
|
+
private getDirectory;
|
|
26
|
+
private getWritableDirectory;
|
|
27
|
+
private getOpenWriter;
|
|
28
|
+
private cleanupWriter;
|
|
29
|
+
private enqueue;
|
|
30
|
+
}
|