hyperdb-wasm 0.0.26845-dev.wasm.g5b622080 → 0.0.26862-dev.wasm.g8c0876f6
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 +64 -14
- package/dist/browser.js +252 -95
- package/dist/browser_external_directory.d.ts +30 -0
- package/dist/browser_worker.js +518 -20
- package/dist/client.d.ts +5 -3
- 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 +12351 -209
- package/dist/node_external_directory.d.ts +30 -0
- package/dist/node_worker.js +694 -31
- package/dist/protocol.d.ts +27 -3
- package/dist/raw.browser.js +153 -22
- package/dist/raw.d.ts +1 -1
- package/dist/raw.node.js +150 -23
- package/dist/wasm_bindings.d.ts +43 -2
- package/dist/worker_setup.d.ts +18 -0
- 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>;
|
|
@@ -19,11 +20,13 @@ export interface HyperDBClient {
|
|
|
19
20
|
insertArrowIPCFromPath(connection: number, path: string, name: string, schema: string | null, create: boolean, internal?: boolean): Promise<WasmResult>;
|
|
20
21
|
createTemporaryFile(bytes: Uint8Array): Promise<WasmResult>;
|
|
21
22
|
removeFile(path: string): Promise<WasmResult>;
|
|
23
|
+
registerExternalFile(path: string): Promise<WasmResult>;
|
|
22
24
|
registerExternalFile(name: string, url: string, size: number): Promise<WasmResult>;
|
|
23
|
-
|
|
25
|
+
registerExternalDirectory(handle: unknown, mode: 'read' | 'readwrite'): Promise<WasmResult>;
|
|
26
|
+
removeExternalRegistration(path: string): Promise<WasmResult>;
|
|
24
27
|
pollQuery(query: number): Promise<WasmResult>;
|
|
25
28
|
cancelQuery(query: number): Promise<WasmResult>;
|
|
26
|
-
|
|
29
|
+
finishQuery(query: number): Promise<WasmResult>;
|
|
27
30
|
shutdown(): Promise<WasmResult>;
|
|
28
31
|
terminate(): Promise<void>;
|
|
29
32
|
}
|
|
@@ -33,13 +36,20 @@ export interface ArrowInsertOptions {
|
|
|
33
36
|
readonly create?: boolean;
|
|
34
37
|
}
|
|
35
38
|
export interface DatabaseCreateOptions {
|
|
36
|
-
readonly storage?: '
|
|
39
|
+
readonly storage?: 'transient' | 'persistent';
|
|
37
40
|
}
|
|
38
41
|
export interface ExternalFileRegistration {
|
|
39
42
|
readonly name: string;
|
|
40
43
|
readonly url: string;
|
|
41
44
|
readonly size: number;
|
|
42
45
|
}
|
|
46
|
+
export type ExternalDirectoryMode = 'read' | 'readwrite';
|
|
47
|
+
export interface ExternalDirectoryRegistrationOptions {
|
|
48
|
+
readonly mode?: ExternalDirectoryMode;
|
|
49
|
+
}
|
|
50
|
+
type BrowserExternalFileArguments = readonly [name: string, url: string, size: number];
|
|
51
|
+
type NodeExternalFileArguments = readonly [path: string];
|
|
52
|
+
export type ExternalFileArguments = BrowserExternalFileArguments | NodeExternalFileArguments;
|
|
43
53
|
export type HyperDBSettingValue = string | number | boolean | null | readonly HyperDBSettingValue[] | {
|
|
44
54
|
readonly [key: string]: HyperDBSettingValue;
|
|
45
55
|
};
|
|
@@ -50,11 +60,50 @@ export interface HyperDBCreateOptions {
|
|
|
50
60
|
export type BrowserCreateOptions = BrowserClientOptions & HyperDBCreateOptions;
|
|
51
61
|
export interface DatabaseMetadata {
|
|
52
62
|
readonly name: string;
|
|
53
|
-
readonly storage: '
|
|
63
|
+
readonly storage: 'transient' | 'persistent';
|
|
64
|
+
}
|
|
65
|
+
export type QueryMetricValue = null | boolean | number | string | readonly QueryMetricValue[] | {
|
|
66
|
+
readonly [key: string]: QueryMetricValue;
|
|
67
|
+
};
|
|
68
|
+
export type QueryMetrics = Readonly<Record<string, QueryMetricValue>>;
|
|
69
|
+
export interface QueryTimings {
|
|
70
|
+
readonly elapsedSeconds: number | null;
|
|
71
|
+
readonly parsingSeconds: number | null;
|
|
72
|
+
readonly compilationSeconds: number | null;
|
|
73
|
+
readonly executionSeconds: number | null;
|
|
74
|
+
readonly commitSeconds: number | null;
|
|
75
|
+
readonly timeToScheduleSeconds: number | null;
|
|
76
|
+
readonly timeToFirstResultSeconds: number | null;
|
|
77
|
+
readonly cancellationSeconds: number | null;
|
|
78
|
+
}
|
|
79
|
+
export interface QueryInfo {
|
|
80
|
+
readonly status: 'succeeded' | 'failed' | 'canceled';
|
|
81
|
+
readonly started: boolean;
|
|
82
|
+
readonly statementKind: string | null;
|
|
83
|
+
readonly producesRows: boolean;
|
|
84
|
+
readonly rowCount: bigint | null;
|
|
85
|
+
readonly columnCount: number | null;
|
|
86
|
+
readonly cancellationRequestedByUser: boolean;
|
|
87
|
+
readonly queryPlan: Readonly<Record<string, unknown>> | null;
|
|
88
|
+
readonly dependencies: readonly string[];
|
|
89
|
+
readonly timings: QueryTimings;
|
|
90
|
+
readonly preExecutionMetrics: QueryMetrics | null;
|
|
91
|
+
readonly executionMetrics: QueryMetrics | null;
|
|
92
|
+
}
|
|
93
|
+
export interface QueryResult<Result> {
|
|
94
|
+
readonly result: Result;
|
|
95
|
+
readonly info: Promise<QueryInfo>;
|
|
96
|
+
}
|
|
97
|
+
export declare class HyperDBError extends Error {
|
|
98
|
+
readonly sqlState: string;
|
|
99
|
+
readonly errorSource: HyperDBErrorSource;
|
|
100
|
+
readonly detail: string;
|
|
101
|
+
readonly hint: string;
|
|
102
|
+
constructor(message: string, sqlState: string, errorSource: HyperDBErrorSource, detail?: string, hint?: string);
|
|
54
103
|
}
|
|
55
104
|
declare function initializeHyperDB(client: HyperDBClient, settings: HyperDBSettings | undefined): Promise<void>;
|
|
56
|
-
export declare class HyperDB {
|
|
57
|
-
|
|
105
|
+
export declare class HyperDB<ExternalFileArgs extends ExternalFileArguments = BrowserExternalFileArguments> {
|
|
106
|
+
protected readonly client: HyperDBClient;
|
|
58
107
|
private terminated;
|
|
59
108
|
private termination;
|
|
60
109
|
private activeDatabaseOperations;
|
|
@@ -66,13 +115,14 @@ export declare class HyperDB {
|
|
|
66
115
|
createDatabase(databaseName: string, options?: DatabaseCreateOptions): Promise<void>;
|
|
67
116
|
openDatabase(databaseName: string): Promise<void>;
|
|
68
117
|
listDatabases(): Promise<DatabaseMetadata[]>;
|
|
69
|
-
registerExternalFile(
|
|
70
|
-
|
|
118
|
+
registerExternalFile(...args: ExternalFileArgs): Promise<string>;
|
|
119
|
+
removeExternalRegistration(path: string): Promise<void>;
|
|
120
|
+
protected registerExternalDirectorySource(source: unknown, mode: ExternalDirectoryMode): Promise<string>;
|
|
71
121
|
checkpointDatabase(databaseName: string): Promise<void>;
|
|
72
122
|
dropDatabase(databaseName: string): Promise<void>;
|
|
73
123
|
terminate(): Promise<void>;
|
|
74
124
|
private ensureRunning;
|
|
75
|
-
|
|
125
|
+
protected runDatabaseOperation<Result>(operation: () => Promise<Result>): Promise<Result>;
|
|
76
126
|
}
|
|
77
127
|
export declare class HyperDBConnection {
|
|
78
128
|
private readonly client;
|
|
@@ -85,19 +135,19 @@ export declare class HyperDBConnection {
|
|
|
85
135
|
close(): Promise<void>;
|
|
86
136
|
attachDatabase(databaseName: string, alias?: string): Promise<void>;
|
|
87
137
|
detachDatabase(alias: string): Promise<void>;
|
|
88
|
-
sendIPC(sql: string): AsyncIterable<Uint8Array
|
|
89
|
-
queryIPC(sql: string): Promise<Uint8Array
|
|
138
|
+
sendIPC(sql: string): QueryResult<AsyncIterable<Uint8Array>>;
|
|
139
|
+
queryIPC(sql: string): QueryResult<Promise<Uint8Array>>;
|
|
90
140
|
send<T extends TypeMap = {
|
|
91
141
|
[key: string]: DataType;
|
|
92
|
-
}>(sql: string): Promise<AsyncRecordBatchStreamReader<T
|
|
142
|
+
}>(sql: string): QueryResult<Promise<AsyncRecordBatchStreamReader<T>>>;
|
|
93
143
|
query<T extends TypeMap = {
|
|
94
144
|
[key: string]: DataType;
|
|
95
|
-
}>(sql: string): Promise<Table<T
|
|
145
|
+
}>(sql: string): QueryResult<Promise<Table<T>>>;
|
|
96
146
|
insertArrowTable(table: Table, options: ArrowInsertOptions): Promise<void>;
|
|
97
147
|
insertArrowIPCFromPath(path: string, options: ArrowInsertOptions): Promise<void>;
|
|
98
148
|
private insertArrowIPCFromPathInternal;
|
|
99
149
|
private ensureAvailable;
|
|
100
|
-
private
|
|
150
|
+
private finishQuery;
|
|
101
151
|
closeForTermination(): Promise<void>;
|
|
102
152
|
}
|
|
103
153
|
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,8 @@ 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 } });
|
|
131
135
|
}
|
|
132
136
|
insertArrowIPCFromPath(connection, path, name, schema, create, internal = false) {
|
|
133
137
|
return this.request({ id: this.nextId++, type: "insertArrowIPCFromPath", connection, path, name, schema, create, internal });
|
|
@@ -139,11 +143,14 @@ var init_client = __esm({
|
|
|
139
143
|
removeFile(path) {
|
|
140
144
|
return this.request({ id: this.nextId++, type: "removeFile", path });
|
|
141
145
|
}
|
|
142
|
-
registerExternalFile(
|
|
143
|
-
return this.request({ id: this.nextId++, type: "registerExternalFile", name, url, size });
|
|
146
|
+
registerExternalFile(pathOrName, url, size) {
|
|
147
|
+
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 });
|
|
148
|
+
}
|
|
149
|
+
registerExternalDirectory(source, mode) {
|
|
150
|
+
return typeof source === "string" ? this.request({ id: this.nextId++, type: "registerExternalDirectory", path: source, mode }) : this.request({ id: this.nextId++, type: "registerExternalDirectory", handle: source, mode });
|
|
144
151
|
}
|
|
145
|
-
|
|
146
|
-
return this.request({ id: this.nextId++, type: "
|
|
152
|
+
removeExternalRegistration(path) {
|
|
153
|
+
return this.request({ id: this.nextId++, type: "removeExternalRegistration", path });
|
|
147
154
|
}
|
|
148
155
|
pollQuery(query) {
|
|
149
156
|
return this.request({ id: this.nextId++, type: "pollQuery", query });
|
|
@@ -151,8 +158,8 @@ var init_client = __esm({
|
|
|
151
158
|
cancelQuery(query) {
|
|
152
159
|
return this.request({ id: this.nextId++, type: "cancelQuery", query });
|
|
153
160
|
}
|
|
154
|
-
|
|
155
|
-
return this.request({ id: this.nextId++, type: "
|
|
161
|
+
finishQuery(query) {
|
|
162
|
+
return this.request({ id: this.nextId++, type: "finishQuery", query });
|
|
156
163
|
}
|
|
157
164
|
async terminate() {
|
|
158
165
|
await this.transport.terminate();
|
|
@@ -232,6 +239,9 @@ function assertBrowserSupport(environment = globalThis) {
|
|
|
232
239
|
if (!environment.WebAssembly.validate(exceptionsProbe)) {
|
|
233
240
|
throw new Error("HyperDB cannot initialize because this browser does not support WebAssembly exception handling");
|
|
234
241
|
}
|
|
242
|
+
if (!environment.WebAssembly.validate(tailCallsProbe)) {
|
|
243
|
+
throw new Error("HyperDB cannot initialize because this browser does not support WebAssembly tail calls");
|
|
244
|
+
}
|
|
235
245
|
if (typeof environment.Worker !== "function") {
|
|
236
246
|
throw new Error("HyperDB cannot initialize because this browser does not support Web Workers");
|
|
237
247
|
}
|
|
@@ -257,7 +267,7 @@ function createBrowserClient(options = {}) {
|
|
|
257
267
|
options.onSetupProgress
|
|
258
268
|
);
|
|
259
269
|
}
|
|
260
|
-
var memory64Probe, threadsProbe, exceptionsProbe, BrowserWorker;
|
|
270
|
+
var memory64Probe, threadsProbe, exceptionsProbe, tailCallsProbe, BrowserWorker;
|
|
261
271
|
var init_browser = __esm({
|
|
262
272
|
"src/browser.ts"() {
|
|
263
273
|
"use strict";
|
|
@@ -265,6 +275,7 @@ var init_browser = __esm({
|
|
|
265
275
|
memory64Probe = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 5, 3, 1, 4, 1]);
|
|
266
276
|
threadsProbe = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 5, 4, 1, 3, 1, 1]);
|
|
267
277
|
exceptionsProbe = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 13, 3, 1, 0, 0]);
|
|
278
|
+
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]);
|
|
268
279
|
BrowserWorker = class {
|
|
269
280
|
constructor(worker) {
|
|
270
281
|
this.worker = worker;
|
|
@@ -12135,7 +12146,72 @@ RecordBatchFileWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
|
|
|
12135
12146
|
RecordBatchStreamWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
|
|
12136
12147
|
|
|
12137
12148
|
// src/api.ts
|
|
12149
|
+
var HyperDBError = class extends Error {
|
|
12150
|
+
sqlState;
|
|
12151
|
+
errorSource;
|
|
12152
|
+
detail;
|
|
12153
|
+
hint;
|
|
12154
|
+
constructor(message, sqlState, errorSource, detail = "", hint = "") {
|
|
12155
|
+
super(message);
|
|
12156
|
+
this.name = "HyperDBError";
|
|
12157
|
+
this.sqlState = sqlState;
|
|
12158
|
+
this.errorSource = errorSource;
|
|
12159
|
+
this.detail = detail;
|
|
12160
|
+
this.hint = hint;
|
|
12161
|
+
}
|
|
12162
|
+
};
|
|
12138
12163
|
var maxPersistentDatabaseNameLength = 200;
|
|
12164
|
+
function deferred() {
|
|
12165
|
+
let resolve;
|
|
12166
|
+
let reject;
|
|
12167
|
+
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
12168
|
+
resolve = resolvePromise;
|
|
12169
|
+
reject = rejectPromise;
|
|
12170
|
+
});
|
|
12171
|
+
void promise.catch(() => void 0);
|
|
12172
|
+
return { promise, resolve, reject };
|
|
12173
|
+
}
|
|
12174
|
+
function queryResult(result, info) {
|
|
12175
|
+
return { result, info };
|
|
12176
|
+
}
|
|
12177
|
+
function isRecord2(value) {
|
|
12178
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12179
|
+
}
|
|
12180
|
+
function decodeQueryInfo(payload) {
|
|
12181
|
+
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(payload));
|
|
12182
|
+
if (!isRecord2(value) || value.version !== 1) throw new Error("query information has an unsupported version");
|
|
12183
|
+
if (value.status !== "succeeded" && value.status !== "failed" && value.status !== "canceled") throw new Error("query information has an invalid status");
|
|
12184
|
+
if (typeof value.started !== "boolean" || typeof value.producesRows !== "boolean" || typeof value.cancellationRequestedByUser !== "boolean") {
|
|
12185
|
+
throw new Error("query information has invalid flags");
|
|
12186
|
+
}
|
|
12187
|
+
if (value.statementKind !== null && typeof value.statementKind !== "string") throw new Error("query information has an invalid statement kind");
|
|
12188
|
+
if (value.rowCount !== null && (typeof value.rowCount !== "string" || !/^\d+$/.test(value.rowCount))) throw new Error("query information has an invalid row count");
|
|
12189
|
+
if (value.columnCount !== null && (!Number.isSafeInteger(value.columnCount) || value.columnCount < 0)) throw new Error("query information has an invalid column count");
|
|
12190
|
+
if (value.queryPlan !== null && !isRecord2(value.queryPlan)) throw new Error("query information has an invalid query plan");
|
|
12191
|
+
if (!Array.isArray(value.dependencies) || value.dependencies.some((dependency) => typeof dependency !== "string")) throw new Error("query information has invalid dependencies");
|
|
12192
|
+
if (!isRecord2(value.timings)) throw new Error("query information has invalid timings");
|
|
12193
|
+
const timingNames = ["elapsedSeconds", "parsingSeconds", "compilationSeconds", "executionSeconds", "commitSeconds", "timeToScheduleSeconds", "timeToFirstResultSeconds", "cancellationSeconds"];
|
|
12194
|
+
for (const name of timingNames) {
|
|
12195
|
+
const timing = value.timings[name];
|
|
12196
|
+
if (timing !== null && (typeof timing !== "number" || !Number.isFinite(timing) || timing < 0)) throw new Error(`query information has an invalid ${name}`);
|
|
12197
|
+
}
|
|
12198
|
+
if (value.preExecutionMetrics !== null && !isRecord2(value.preExecutionMetrics)) throw new Error("query information has invalid pre-execution metrics");
|
|
12199
|
+
if (value.executionMetrics !== null && !isRecord2(value.executionMetrics)) throw new Error("query information has invalid execution metrics");
|
|
12200
|
+
return {
|
|
12201
|
+
status: value.status,
|
|
12202
|
+
started: value.started,
|
|
12203
|
+
statementKind: value.statementKind,
|
|
12204
|
+
producesRows: value.producesRows,
|
|
12205
|
+
rowCount: value.rowCount === null ? null : BigInt(value.rowCount),
|
|
12206
|
+
columnCount: value.columnCount,
|
|
12207
|
+
cancellationRequestedByUser: value.cancellationRequestedByUser,
|
|
12208
|
+
queryPlan: value.queryPlan,
|
|
12209
|
+
dependencies: value.dependencies,
|
|
12210
|
+
timings: value.timings,
|
|
12211
|
+
preExecutionMetrics: value.preExecutionMetrics,
|
|
12212
|
+
executionMetrics: value.executionMetrics
|
|
12213
|
+
};
|
|
12214
|
+
}
|
|
12139
12215
|
function normalizeArrowInsertOptions(options) {
|
|
12140
12216
|
if (!options.name) throw new Error("Arrow insert table name must not be empty");
|
|
12141
12217
|
return { name: options.name, schema: options.schema ?? null, create: options.create ?? true };
|
|
@@ -12184,7 +12260,11 @@ function readHandleFromResult(result, operation) {
|
|
|
12184
12260
|
return new DataView(result.payload.buffer, result.payload.byteOffset, result.payload.byteLength).getUint32(0, true);
|
|
12185
12261
|
}
|
|
12186
12262
|
function readErrorFromResult(result, operation) {
|
|
12187
|
-
if (result.state === "error"
|
|
12263
|
+
if (result.state === "error") {
|
|
12264
|
+
if (typeof result.error === "string") return new Error(result.error);
|
|
12265
|
+
return new HyperDBError(result.error.message, result.error.sqlState, result.error.errorSource, result.error.detail, result.error.hint);
|
|
12266
|
+
}
|
|
12267
|
+
if (result.state === "busy") return new Error(result.error);
|
|
12188
12268
|
return new Error(`${operation} returned unexpected state ${result.state}`);
|
|
12189
12269
|
}
|
|
12190
12270
|
function expectOk(result, operation) {
|
|
@@ -12218,7 +12298,7 @@ function readDatabasesFromResult(result) {
|
|
|
12218
12298
|
throw invalidResult();
|
|
12219
12299
|
}
|
|
12220
12300
|
if (!name || name.includes("\0") || rawStorage !== 0 && rawStorage !== 1) throw invalidResult();
|
|
12221
|
-
databases.push({ name, storage: rawStorage === 0 ? "
|
|
12301
|
+
databases.push({ name, storage: rawStorage === 0 ? "transient" : "persistent" });
|
|
12222
12302
|
offset += nameLength;
|
|
12223
12303
|
}
|
|
12224
12304
|
if (offset !== result.payload.byteLength) throw invalidResult();
|
|
@@ -12265,7 +12345,7 @@ var HyperDB = class _HyperDB {
|
|
|
12265
12345
|
}
|
|
12266
12346
|
async createDatabase(databaseName, options = {}) {
|
|
12267
12347
|
validateDatabaseName(databaseName);
|
|
12268
|
-
if (options.storage !== void 0 && options.storage !== "
|
|
12348
|
+
if (options.storage !== void 0 && options.storage !== "transient" && options.storage !== "persistent") {
|
|
12269
12349
|
throw new Error(`Unsupported database storage: ${String(options.storage)}`);
|
|
12270
12350
|
}
|
|
12271
12351
|
if (options.storage === "persistent") validatePersistentDatabaseName(databaseName);
|
|
@@ -12282,7 +12362,20 @@ var HyperDB = class _HyperDB {
|
|
|
12282
12362
|
listDatabases() {
|
|
12283
12363
|
return this.runDatabaseOperation(async () => readDatabasesFromResult(await this.client.listDatabases()));
|
|
12284
12364
|
}
|
|
12285
|
-
async registerExternalFile(
|
|
12365
|
+
async registerExternalFile(...args) {
|
|
12366
|
+
if (args.length === 1) {
|
|
12367
|
+
const [sourcePath] = args;
|
|
12368
|
+
if (!sourcePath) throw new Error("External file path must not be empty");
|
|
12369
|
+
if (sourcePath.includes("\0")) throw new Error("External file path must not contain NUL");
|
|
12370
|
+
return this.runDatabaseOperation(async () => {
|
|
12371
|
+
const result = await this.client.registerExternalFile(sourcePath);
|
|
12372
|
+
expectOk(result, "register external file");
|
|
12373
|
+
const path = new TextDecoder("utf-8", { fatal: true }).decode(result.payload);
|
|
12374
|
+
if (!/^\/mnt\/external\/[^/]+\/[^/]+$/.test(path)) throw new Error("register external file returned an invalid path");
|
|
12375
|
+
return path;
|
|
12376
|
+
});
|
|
12377
|
+
}
|
|
12378
|
+
const [name, url, size] = args;
|
|
12286
12379
|
if (!name) throw new Error("External file name must not be empty");
|
|
12287
12380
|
if (name.includes("\0")) throw new Error("External file name must not contain NUL");
|
|
12288
12381
|
if (name === "." || name === ".." || name.includes("/") || name.includes("\\")) {
|
|
@@ -12304,10 +12397,20 @@ var HyperDB = class _HyperDB {
|
|
|
12304
12397
|
return path;
|
|
12305
12398
|
});
|
|
12306
12399
|
}
|
|
12307
|
-
async
|
|
12308
|
-
if (!path) throw new Error("External
|
|
12400
|
+
async removeExternalRegistration(path) {
|
|
12401
|
+
if (!path) throw new Error("External registration path must not be empty");
|
|
12402
|
+
if (path.includes("\0")) throw new Error("External registration path must not contain null bytes");
|
|
12309
12403
|
await this.runDatabaseOperation(async () => {
|
|
12310
|
-
expectOk(await this.client.
|
|
12404
|
+
expectOk(await this.client.removeExternalRegistration(path), "remove external registration");
|
|
12405
|
+
});
|
|
12406
|
+
}
|
|
12407
|
+
registerExternalDirectorySource(source, mode) {
|
|
12408
|
+
return this.runDatabaseOperation(async () => {
|
|
12409
|
+
const result = await this.client.registerExternalDirectory(source, mode);
|
|
12410
|
+
expectOk(result, "register external directory");
|
|
12411
|
+
const path = new TextDecoder("utf-8", { fatal: true }).decode(result.payload);
|
|
12412
|
+
if (!/^\/mnt\/external\/[^/]+\/[^/]+$/.test(path)) throw new Error("register external directory returned an invalid path");
|
|
12413
|
+
return path;
|
|
12311
12414
|
});
|
|
12312
12415
|
}
|
|
12313
12416
|
async checkpointDatabase(databaseName) {
|
|
@@ -12414,89 +12517,111 @@ var HyperDBConnection = class {
|
|
|
12414
12517
|
}
|
|
12415
12518
|
sendIPC(sql) {
|
|
12416
12519
|
const connection = this;
|
|
12417
|
-
|
|
12418
|
-
|
|
12520
|
+
const queryInfo = deferred();
|
|
12521
|
+
const result = (async function* () {
|
|
12522
|
+
let query;
|
|
12523
|
+
let terminal = false;
|
|
12524
|
+
let prefetched;
|
|
12525
|
+
let acquired = false;
|
|
12526
|
+
try {
|
|
12419
12527
|
connection.ensureAvailable();
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
while (result.state === "pending") {
|
|
12429
|
-
await yieldToEventLoop();
|
|
12430
|
-
result = await connection.client.pollQuery(query);
|
|
12431
|
-
}
|
|
12432
|
-
if (result.state === "chunk") {
|
|
12433
|
-
prefetched = connection.client.pollQuery(query);
|
|
12434
|
-
void prefetched.catch(() => void 0);
|
|
12435
|
-
yield result.payload;
|
|
12436
|
-
continue;
|
|
12437
|
-
}
|
|
12438
|
-
terminal = true;
|
|
12439
|
-
if (result.state === "done") return;
|
|
12440
|
-
throw readErrorFromResult(result, "poll query");
|
|
12528
|
+
acquired = true;
|
|
12529
|
+
query = readHandleFromResult(await connection.client.startQuery(connection.connection, sql), "start query");
|
|
12530
|
+
for (; ; ) {
|
|
12531
|
+
let result2 = prefetched ? await prefetched : await connection.client.pollQuery(query);
|
|
12532
|
+
prefetched = void 0;
|
|
12533
|
+
while (result2.state === "pending") {
|
|
12534
|
+
await yieldToEventLoop();
|
|
12535
|
+
result2 = await connection.client.pollQuery(query);
|
|
12441
12536
|
}
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
|
|
12447
|
-
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12537
|
+
if (result2.state === "chunk") {
|
|
12538
|
+
prefetched = connection.client.pollQuery(query);
|
|
12539
|
+
void prefetched.catch(() => void 0);
|
|
12540
|
+
yield result2.payload;
|
|
12541
|
+
continue;
|
|
12542
|
+
}
|
|
12543
|
+
terminal = true;
|
|
12544
|
+
if (result2.state === "done") return;
|
|
12545
|
+
throw readErrorFromResult(result2, "poll query");
|
|
12546
|
+
}
|
|
12547
|
+
} catch (error) {
|
|
12548
|
+
if (query === void 0 || !terminal) queryInfo.reject(error);
|
|
12549
|
+
throw error;
|
|
12550
|
+
} finally {
|
|
12551
|
+
try {
|
|
12552
|
+
if (query !== void 0) {
|
|
12553
|
+
let cleanupError;
|
|
12554
|
+
let info;
|
|
12555
|
+
if (prefetched) {
|
|
12556
|
+
try {
|
|
12557
|
+
await prefetched;
|
|
12558
|
+
} catch (error) {
|
|
12559
|
+
cleanupError = error;
|
|
12460
12560
|
}
|
|
12561
|
+
prefetched = void 0;
|
|
12562
|
+
}
|
|
12563
|
+
if (!terminal) {
|
|
12461
12564
|
try {
|
|
12462
|
-
await connection.
|
|
12565
|
+
expectOk(await connection.client.cancelQuery(query), "cancel query");
|
|
12463
12566
|
} catch (error) {
|
|
12464
12567
|
cleanupError ??= error;
|
|
12465
12568
|
}
|
|
12466
|
-
if (cleanupError) throw cleanupError;
|
|
12467
12569
|
}
|
|
12468
|
-
|
|
12469
|
-
|
|
12570
|
+
try {
|
|
12571
|
+
info = decodeQueryInfo(await connection.finishQuery(query));
|
|
12572
|
+
} catch (error) {
|
|
12573
|
+
cleanupError ??= error;
|
|
12574
|
+
}
|
|
12575
|
+
if (cleanupError) {
|
|
12576
|
+
queryInfo.reject(cleanupError);
|
|
12577
|
+
throw cleanupError;
|
|
12578
|
+
}
|
|
12579
|
+
queryInfo.resolve(info);
|
|
12470
12580
|
}
|
|
12581
|
+
} finally {
|
|
12582
|
+
if (acquired) connection.active = false;
|
|
12471
12583
|
}
|
|
12472
12584
|
}
|
|
12473
|
-
};
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
chunks
|
|
12480
|
-
length
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
offset
|
|
12487
|
-
|
|
12488
|
-
|
|
12489
|
-
|
|
12490
|
-
|
|
12491
|
-
|
|
12492
|
-
|
|
12493
|
-
return
|
|
12494
|
-
}
|
|
12495
|
-
|
|
12496
|
-
const
|
|
12497
|
-
const
|
|
12498
|
-
|
|
12499
|
-
|
|
12585
|
+
})();
|
|
12586
|
+
return queryResult(result, queryInfo.promise);
|
|
12587
|
+
}
|
|
12588
|
+
queryIPC(sql) {
|
|
12589
|
+
const stream = this.sendIPC(sql);
|
|
12590
|
+
const result = (async () => {
|
|
12591
|
+
const chunks = [];
|
|
12592
|
+
let length = 0;
|
|
12593
|
+
for await (const chunk of stream.result) {
|
|
12594
|
+
chunks.push(chunk);
|
|
12595
|
+
length += chunk.byteLength;
|
|
12596
|
+
}
|
|
12597
|
+
const bytes = new Uint8Array(length);
|
|
12598
|
+
let offset = 0;
|
|
12599
|
+
for (const chunk of chunks) {
|
|
12600
|
+
bytes.set(chunk, offset);
|
|
12601
|
+
offset += chunk.byteLength;
|
|
12602
|
+
}
|
|
12603
|
+
return bytes;
|
|
12604
|
+
})();
|
|
12605
|
+
return queryResult(result, stream.info);
|
|
12606
|
+
}
|
|
12607
|
+
send(sql) {
|
|
12608
|
+
const stream = this.sendIPC(sql);
|
|
12609
|
+
const result = (async () => {
|
|
12610
|
+
const reader = await RecordBatchReader.from(stream.result);
|
|
12611
|
+
if (!reader.isAsync() || !reader.isStream()) throw new Error("query did not produce an Arrow async stream");
|
|
12612
|
+
return reader;
|
|
12613
|
+
})();
|
|
12614
|
+
return queryResult(result, stream.info);
|
|
12615
|
+
}
|
|
12616
|
+
query(sql) {
|
|
12617
|
+
const stream = this.send(sql);
|
|
12618
|
+
const result = (async () => {
|
|
12619
|
+
const reader = await stream.result;
|
|
12620
|
+
const batches = [];
|
|
12621
|
+
for await (const batch of reader) batches.push(batch);
|
|
12622
|
+
return new Table(reader.schema, batches);
|
|
12623
|
+
})();
|
|
12624
|
+
return queryResult(result, stream.info);
|
|
12500
12625
|
}
|
|
12501
12626
|
async insertArrowTable(table, options) {
|
|
12502
12627
|
this.ensureAvailable();
|
|
@@ -12536,7 +12661,7 @@ var HyperDBConnection = class {
|
|
|
12536
12661
|
}
|
|
12537
12662
|
} finally {
|
|
12538
12663
|
try {
|
|
12539
|
-
if (query !== void 0) await this.
|
|
12664
|
+
if (query !== void 0) await this.finishQuery(query);
|
|
12540
12665
|
} finally {
|
|
12541
12666
|
if (acquire) this.active = false;
|
|
12542
12667
|
}
|
|
@@ -12550,12 +12675,12 @@ var HyperDBConnection = class {
|
|
|
12550
12675
|
}
|
|
12551
12676
|
this.active = true;
|
|
12552
12677
|
}
|
|
12553
|
-
async
|
|
12678
|
+
async finishQuery(query) {
|
|
12554
12679
|
let delay = 1;
|
|
12555
12680
|
for (; ; ) {
|
|
12556
|
-
const result = await this.client.
|
|
12557
|
-
if (result.state === "ok") return;
|
|
12558
|
-
if (result.state !== "pending") throw readErrorFromResult(result, "
|
|
12681
|
+
const result = await this.client.finishQuery(query);
|
|
12682
|
+
if (result.state === "ok") return result.payload;
|
|
12683
|
+
if (result.state !== "pending") throw readErrorFromResult(result, "finish query");
|
|
12559
12684
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
12560
12685
|
delay = Math.min(delay * 2, 32);
|
|
12561
12686
|
}
|
|
@@ -12565,7 +12690,39 @@ var HyperDBConnection = class {
|
|
|
12565
12690
|
await this.close();
|
|
12566
12691
|
}
|
|
12567
12692
|
};
|
|
12693
|
+
|
|
12694
|
+
// src/index.ts
|
|
12695
|
+
init_browser();
|
|
12696
|
+
var HyperDB2 = class _HyperDB extends HyperDB {
|
|
12697
|
+
static async create(options = {}) {
|
|
12698
|
+
const client = createBrowserClient(options);
|
|
12699
|
+
const database = new _HyperDB(client);
|
|
12700
|
+
try {
|
|
12701
|
+
await database.ready();
|
|
12702
|
+
await initializeHyperDB(client, options.settings);
|
|
12703
|
+
return database;
|
|
12704
|
+
} catch (error) {
|
|
12705
|
+
try {
|
|
12706
|
+
await client.terminate();
|
|
12707
|
+
} catch {
|
|
12708
|
+
}
|
|
12709
|
+
throw error;
|
|
12710
|
+
}
|
|
12711
|
+
}
|
|
12712
|
+
async registerExternalDirectory(handle, options = {}) {
|
|
12713
|
+
const mode = options.mode ?? "read";
|
|
12714
|
+
if (mode !== "read" && mode !== "readwrite") throw new Error(`Unsupported external directory mode: ${String(mode)}`);
|
|
12715
|
+
if (!handle || handle.kind !== "directory" || typeof handle.queryPermission !== "function" || typeof handle.requestPermission !== "function") {
|
|
12716
|
+
throw new Error("External directory handle must be a FileSystemDirectoryHandle");
|
|
12717
|
+
}
|
|
12718
|
+
let permission = await handle.queryPermission({ mode });
|
|
12719
|
+
if (permission !== "granted") permission = await handle.requestPermission({ mode });
|
|
12720
|
+
if (permission !== "granted") throw new Error(`External directory ${mode} permission was not granted`);
|
|
12721
|
+
return this.registerExternalDirectorySource(handle, mode);
|
|
12722
|
+
}
|
|
12723
|
+
};
|
|
12568
12724
|
export {
|
|
12569
|
-
HyperDB,
|
|
12570
|
-
HyperDBConnection
|
|
12725
|
+
HyperDB2 as HyperDB,
|
|
12726
|
+
HyperDBConnection,
|
|
12727
|
+
HyperDBError
|
|
12571
12728
|
};
|
|
@@ -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
|
+
}
|