hyperdb-wasm 0.0.26862-dev.wasm.g89537caa → 0.0.27000-dev.wasm.g0bde3882
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 +15 -19
- package/dist/browser.js +91 -60
- package/dist/browser_external_directory.d.ts +13 -0
- package/dist/browser_worker.js +217 -100
- package/dist/client.d.ts +4 -5
- 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 +9 -8
- package/dist/index.node.d.ts +4 -4
- package/dist/node.js +74 -60
- package/dist/node_external_directory.d.ts +11 -0
- package/dist/node_worker.js +254 -117
- package/dist/protocol.d.ts +17 -18
- package/dist/raw.browser.js +122 -74
- package/dist/raw.d.ts +2 -1
- package/dist/raw.node.js +122 -74
- package/dist/wasm_bindings.d.ts +21 -1
- package/dist/worker_setup.d.ts +4 -2
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { DataType, Table } from 'apache-arrow';
|
|
|
2
2
|
import type { AsyncRecordBatchStreamReader, TypeMap } from 'apache-arrow';
|
|
3
3
|
import type { HyperDBEngineClient } from './client.js';
|
|
4
4
|
import type { BrowserClientOptions } from './browser.js';
|
|
5
|
-
import type { WasmResult } from './protocol.js';
|
|
5
|
+
import type { MountSource, WasmResult } from './protocol.js';
|
|
6
6
|
import type { HyperDBErrorSource } from './protocol.js';
|
|
7
7
|
export interface HyperDBClient {
|
|
8
8
|
ready(): Promise<void>;
|
|
@@ -13,10 +13,8 @@ export interface HyperDBClient {
|
|
|
13
13
|
insertArrowIPCFromPath(connection: number, path: string, name: string, schema: string | null, create: boolean, internal?: boolean): Promise<WasmResult>;
|
|
14
14
|
createTemporaryFile(bytes: Uint8Array): Promise<WasmResult>;
|
|
15
15
|
removeFile(path: string): Promise<WasmResult>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
registerExternalDirectory(handle: unknown, mode: 'read' | 'readwrite'): Promise<WasmResult>;
|
|
19
|
-
removeExternalRegistration(path: string): Promise<WasmResult>;
|
|
16
|
+
mount(source: MountSource, path: string, mode: MountAccessMode): Promise<WasmResult>;
|
|
17
|
+
unmount(path: string): Promise<WasmResult>;
|
|
20
18
|
pollQuery(query: number): Promise<WasmResult>;
|
|
21
19
|
cancelQuery(query: number): Promise<WasmResult>;
|
|
22
20
|
finishQuery(query: number): Promise<WasmResult>;
|
|
@@ -28,18 +26,16 @@ export interface ArrowInsertOptions {
|
|
|
28
26
|
readonly schema?: string;
|
|
29
27
|
readonly create?: boolean;
|
|
30
28
|
}
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
readonly
|
|
34
|
-
readonly
|
|
29
|
+
export type MountAccessMode = 'read' | 'readwrite';
|
|
30
|
+
export interface MountOptions {
|
|
31
|
+
readonly path: string;
|
|
32
|
+
readonly mode?: MountAccessMode;
|
|
35
33
|
}
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
export interface MountedResource {
|
|
35
|
+
readonly path: string;
|
|
36
|
+
close(): Promise<void>;
|
|
39
37
|
}
|
|
40
|
-
|
|
41
|
-
type NodeExternalFileArguments = readonly [path: string];
|
|
42
|
-
export type ExternalFileArguments = BrowserExternalFileArguments | NodeExternalFileArguments;
|
|
38
|
+
export declare function validateMountPath(path: unknown): asserts path is string;
|
|
43
39
|
export type HyperDBSettingValue = string | number | boolean | null | readonly HyperDBSettingValue[] | {
|
|
44
40
|
readonly [key: string]: HyperDBSettingValue;
|
|
45
41
|
};
|
|
@@ -88,22 +84,22 @@ export declare class HyperDBError extends Error {
|
|
|
88
84
|
constructor(message: string, sqlState: string, errorSource: HyperDBErrorSource, detail?: string, hint?: string);
|
|
89
85
|
}
|
|
90
86
|
declare function initializeHyperDB(client: HyperDBClient, settings: HyperDBSettings | undefined): Promise<void>;
|
|
91
|
-
export declare class HyperDB
|
|
87
|
+
export declare class HyperDB {
|
|
92
88
|
protected readonly client: HyperDBClient;
|
|
93
89
|
private terminated;
|
|
94
90
|
private termination;
|
|
95
91
|
private activeDatabaseOperations;
|
|
96
92
|
private readonly connections;
|
|
93
|
+
private readonly mountedResources;
|
|
97
94
|
constructor(client: HyperDBClient);
|
|
98
95
|
static create(options?: BrowserCreateOptions): Promise<HyperDB>;
|
|
99
96
|
ready(): Promise<void>;
|
|
100
97
|
connect(): Promise<HyperDBConnection>;
|
|
101
|
-
|
|
102
|
-
removeExternalRegistration(path: string): Promise<void>;
|
|
103
|
-
protected registerExternalDirectorySource(source: unknown, mode: ExternalDirectoryMode): Promise<string>;
|
|
98
|
+
protected mountSource(source: MountSource, path: string, mode: MountAccessMode, cleanup?: () => void): Promise<MountedResource>;
|
|
104
99
|
terminate(): Promise<void>;
|
|
105
100
|
private ensureRunning;
|
|
106
101
|
protected runDatabaseOperation<Result>(operation: () => Promise<Result>): Promise<Result>;
|
|
102
|
+
private unmount;
|
|
107
103
|
}
|
|
108
104
|
export declare class HyperDBConnection {
|
|
109
105
|
private readonly client;
|
package/dist/browser.js
CHANGED
|
@@ -122,14 +122,11 @@ var init_client = __esm({
|
|
|
122
122
|
removeFile(path) {
|
|
123
123
|
return this.request({ id: this.nextId++, type: "removeFile", path });
|
|
124
124
|
}
|
|
125
|
-
|
|
126
|
-
return
|
|
125
|
+
mount(source, path, mode) {
|
|
126
|
+
return this.request({ id: this.nextId++, type: "mount", source, path, mode });
|
|
127
127
|
}
|
|
128
|
-
|
|
129
|
-
return
|
|
130
|
-
}
|
|
131
|
-
removeExternalRegistration(path) {
|
|
132
|
-
return this.request({ id: this.nextId++, type: "removeExternalRegistration", path });
|
|
128
|
+
unmount(path) {
|
|
129
|
+
return this.request({ id: this.nextId++, type: "unmount", path });
|
|
133
130
|
}
|
|
134
131
|
pollQuery(query) {
|
|
135
132
|
return this.request({ id: this.nextId++, type: "pollQuery", query });
|
|
@@ -12126,6 +12123,15 @@ RecordBatchFileWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
|
|
|
12126
12123
|
RecordBatchStreamWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
|
|
12127
12124
|
|
|
12128
12125
|
// src/api.ts
|
|
12126
|
+
function validateMountPath(path) {
|
|
12127
|
+
if (typeof path !== "string" || !path.startsWith("/mnt/external/")) {
|
|
12128
|
+
throw new Error("Mount path must be an absolute path below /mnt/external");
|
|
12129
|
+
}
|
|
12130
|
+
const relativePath = path.slice("/mnt/external/".length);
|
|
12131
|
+
if (new TextEncoder().encode(relativePath).byteLength === 0 || new TextEncoder().encode(relativePath).byteLength > 200 || relativePath.split("/").some((component) => !component || component === "." || component === ".." || !/^[A-Za-z0-9._-]+$/.test(component))) {
|
|
12132
|
+
throw new Error("Mount path below /mnt/external must be 1-200 bytes of slash-separated components containing ASCII letters, digits, '.', '-', or '_'");
|
|
12133
|
+
}
|
|
12134
|
+
}
|
|
12129
12135
|
var HyperDBError = class extends Error {
|
|
12130
12136
|
sqlState;
|
|
12131
12137
|
errorSource;
|
|
@@ -12238,6 +12244,43 @@ async function initializeHyperDB(client, settings) {
|
|
|
12238
12244
|
function yieldToEventLoop() {
|
|
12239
12245
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
12240
12246
|
}
|
|
12247
|
+
var MountedResourceImpl = class {
|
|
12248
|
+
constructor(path, unmount, cleanup) {
|
|
12249
|
+
this.path = path;
|
|
12250
|
+
this.unmount = unmount;
|
|
12251
|
+
this.cleanup = cleanup;
|
|
12252
|
+
}
|
|
12253
|
+
path;
|
|
12254
|
+
unmount;
|
|
12255
|
+
cleanup;
|
|
12256
|
+
closed = false;
|
|
12257
|
+
closing;
|
|
12258
|
+
cleaned = false;
|
|
12259
|
+
async close() {
|
|
12260
|
+
if (this.closed) return;
|
|
12261
|
+
if (!this.closing) {
|
|
12262
|
+
this.closing = (async () => {
|
|
12263
|
+
try {
|
|
12264
|
+
await this.unmount(this);
|
|
12265
|
+
} catch (error) {
|
|
12266
|
+
this.closing = void 0;
|
|
12267
|
+
throw error;
|
|
12268
|
+
}
|
|
12269
|
+
this.release();
|
|
12270
|
+
})();
|
|
12271
|
+
}
|
|
12272
|
+
await this.closing;
|
|
12273
|
+
}
|
|
12274
|
+
releaseAfterShutdown() {
|
|
12275
|
+
this.release();
|
|
12276
|
+
}
|
|
12277
|
+
release() {
|
|
12278
|
+
this.closed = true;
|
|
12279
|
+
if (this.cleaned) return;
|
|
12280
|
+
this.cleaned = true;
|
|
12281
|
+
this.cleanup();
|
|
12282
|
+
}
|
|
12283
|
+
};
|
|
12241
12284
|
var HyperDB = class _HyperDB {
|
|
12242
12285
|
constructor(client) {
|
|
12243
12286
|
this.client = client;
|
|
@@ -12247,6 +12290,7 @@ var HyperDB = class _HyperDB {
|
|
|
12247
12290
|
termination;
|
|
12248
12291
|
activeDatabaseOperations = 0;
|
|
12249
12292
|
connections = /* @__PURE__ */ new Set();
|
|
12293
|
+
mountedResources = /* @__PURE__ */ new Set();
|
|
12250
12294
|
static async create(options = {}) {
|
|
12251
12295
|
const { createBrowserClient: createBrowserClient2 } = await Promise.resolve().then(() => (init_browser(), browser_exports));
|
|
12252
12296
|
const client = createBrowserClient2(options);
|
|
@@ -12274,55 +12318,15 @@ var HyperDB = class _HyperDB {
|
|
|
12274
12318
|
this.connections.add(result);
|
|
12275
12319
|
return result;
|
|
12276
12320
|
}
|
|
12277
|
-
|
|
12278
|
-
if (args.length === 1) {
|
|
12279
|
-
const [sourcePath] = args;
|
|
12280
|
-
if (!sourcePath) throw new Error("External file path must not be empty");
|
|
12281
|
-
if (sourcePath.includes("\0")) throw new Error("External file path must not contain NUL");
|
|
12282
|
-
return this.runDatabaseOperation(async () => {
|
|
12283
|
-
const result = await this.client.registerExternalFile(sourcePath);
|
|
12284
|
-
expectOk(result, "register external file");
|
|
12285
|
-
const path = new TextDecoder("utf-8", { fatal: true }).decode(result.payload);
|
|
12286
|
-
if (!/^\/mnt\/external\/[^/]+\/[^/]+$/.test(path)) throw new Error("register external file returned an invalid path");
|
|
12287
|
-
return path;
|
|
12288
|
-
});
|
|
12289
|
-
}
|
|
12290
|
-
const [name, url, size] = args;
|
|
12291
|
-
if (!name) throw new Error("External file name must not be empty");
|
|
12292
|
-
if (name.includes("\0")) throw new Error("External file name must not contain NUL");
|
|
12293
|
-
if (name === "." || name === ".." || name.includes("/") || name.includes("\\")) {
|
|
12294
|
-
throw new Error("External file name must be a basename without path separators");
|
|
12295
|
-
}
|
|
12296
|
-
if (!Number.isSafeInteger(size) || size < 0) throw new Error("External file size must be a nonnegative safe integer");
|
|
12297
|
-
let scheme;
|
|
12298
|
-
try {
|
|
12299
|
-
scheme = new URL(url).protocol;
|
|
12300
|
-
} catch {
|
|
12301
|
-
throw new Error("External file URL must use the blob scheme");
|
|
12302
|
-
}
|
|
12303
|
-
if (scheme !== "blob:") throw new Error("External file URL must use the blob scheme");
|
|
12321
|
+
mountSource(source, path, mode, cleanup = () => void 0) {
|
|
12304
12322
|
return this.runDatabaseOperation(async () => {
|
|
12305
|
-
const result = await this.client.
|
|
12306
|
-
expectOk(result, "
|
|
12307
|
-
const
|
|
12308
|
-
if (
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
async removeExternalRegistration(path) {
|
|
12313
|
-
if (!path) throw new Error("External registration path must not be empty");
|
|
12314
|
-
if (path.includes("\0")) throw new Error("External registration path must not contain null bytes");
|
|
12315
|
-
await this.runDatabaseOperation(async () => {
|
|
12316
|
-
expectOk(await this.client.removeExternalRegistration(path), "remove external registration");
|
|
12317
|
-
});
|
|
12318
|
-
}
|
|
12319
|
-
registerExternalDirectorySource(source, mode) {
|
|
12320
|
-
return this.runDatabaseOperation(async () => {
|
|
12321
|
-
const result = await this.client.registerExternalDirectory(source, mode);
|
|
12322
|
-
expectOk(result, "register external directory");
|
|
12323
|
-
const path = new TextDecoder("utf-8", { fatal: true }).decode(result.payload);
|
|
12324
|
-
if (!/^\/mnt\/external\/[^/]+\/[^/]+$/.test(path)) throw new Error("register external directory returned an invalid path");
|
|
12325
|
-
return path;
|
|
12323
|
+
const result = await this.client.mount(source, path, mode);
|
|
12324
|
+
expectOk(result, "mount");
|
|
12325
|
+
const mountedPath = new TextDecoder("utf-8", { fatal: true }).decode(result.payload);
|
|
12326
|
+
if (mountedPath !== path) throw new Error("mount returned an invalid path");
|
|
12327
|
+
const resource = new MountedResourceImpl(mountedPath, (mounted) => this.unmount(mounted), cleanup);
|
|
12328
|
+
this.mountedResources.add(resource);
|
|
12329
|
+
return resource;
|
|
12326
12330
|
});
|
|
12327
12331
|
}
|
|
12328
12332
|
async terminate() {
|
|
@@ -12342,6 +12346,8 @@ var HyperDB = class _HyperDB {
|
|
|
12342
12346
|
this.termination = void 0;
|
|
12343
12347
|
throw error;
|
|
12344
12348
|
}
|
|
12349
|
+
for (const resource of this.mountedResources) resource.releaseAfterShutdown();
|
|
12350
|
+
this.mountedResources.clear();
|
|
12345
12351
|
try {
|
|
12346
12352
|
await this.client.terminate();
|
|
12347
12353
|
this.terminated = true;
|
|
@@ -12366,6 +12372,12 @@ var HyperDB = class _HyperDB {
|
|
|
12366
12372
|
--this.activeDatabaseOperations;
|
|
12367
12373
|
}
|
|
12368
12374
|
}
|
|
12375
|
+
async unmount(resource) {
|
|
12376
|
+
await this.runDatabaseOperation(async () => {
|
|
12377
|
+
expectOk(await this.client.unmount(resource.path), "unmount");
|
|
12378
|
+
this.mountedResources.delete(resource);
|
|
12379
|
+
});
|
|
12380
|
+
}
|
|
12369
12381
|
};
|
|
12370
12382
|
var HyperDBConnection = class {
|
|
12371
12383
|
constructor(client, connection, onClose = () => void 0) {
|
|
@@ -12590,16 +12602,35 @@ var HyperDB2 = class _HyperDB extends HyperDB {
|
|
|
12590
12602
|
throw error;
|
|
12591
12603
|
}
|
|
12592
12604
|
}
|
|
12593
|
-
async
|
|
12605
|
+
async mount(source, options) {
|
|
12606
|
+
const path = options?.path;
|
|
12607
|
+
validateMountPath(path);
|
|
12608
|
+
if (typeof File !== "undefined" && source instanceof File) {
|
|
12609
|
+
if (options.mode !== void 0 && options.mode !== "read") throw new Error("File mounts are always read-only");
|
|
12610
|
+
if (!source.name) throw new Error("Mounted file name must not be empty");
|
|
12611
|
+
if (source.name.includes("\0")) throw new Error("Mounted file name must not contain NUL");
|
|
12612
|
+
if (source.name === "." || source.name === ".." || source.name.includes("/") || source.name.includes("\\")) {
|
|
12613
|
+
throw new Error("Mounted file name must be a basename without path separators");
|
|
12614
|
+
}
|
|
12615
|
+
if (!Number.isSafeInteger(source.size) || source.size < 0) throw new Error("Mounted file size must be a nonnegative safe integer");
|
|
12616
|
+
const url = URL.createObjectURL(source);
|
|
12617
|
+
try {
|
|
12618
|
+
return await this.mountSource({ kind: "browser-file", name: source.name, url, size: source.size }, path, "read", () => URL.revokeObjectURL(url));
|
|
12619
|
+
} catch (error) {
|
|
12620
|
+
URL.revokeObjectURL(url);
|
|
12621
|
+
throw error;
|
|
12622
|
+
}
|
|
12623
|
+
}
|
|
12624
|
+
const handle = source;
|
|
12594
12625
|
const mode = options.mode ?? "read";
|
|
12595
|
-
if (mode !== "read" && mode !== "readwrite") throw new Error(`Unsupported
|
|
12626
|
+
if (mode !== "read" && mode !== "readwrite") throw new Error(`Unsupported mount mode: ${String(mode)}`);
|
|
12596
12627
|
if (!handle || handle.kind !== "directory" || typeof handle.queryPermission !== "function" || typeof handle.requestPermission !== "function") {
|
|
12597
|
-
throw new Error("
|
|
12628
|
+
throw new Error("Mount source must be a File or FileSystemDirectoryHandle");
|
|
12598
12629
|
}
|
|
12599
12630
|
let permission = await handle.queryPermission({ mode });
|
|
12600
12631
|
if (permission !== "granted") permission = await handle.requestPermission({ mode });
|
|
12601
|
-
if (permission !== "granted") throw new Error(`
|
|
12602
|
-
return this.
|
|
12632
|
+
if (permission !== "granted") throw new Error(`Directory ${mode} permission was not granted`);
|
|
12633
|
+
return this.mountSource({ kind: "browser-directory", handle }, path, mode);
|
|
12603
12634
|
}
|
|
12604
12635
|
};
|
|
12605
12636
|
export {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { ExternalDirectoryBackend } from './wasm_bindings.js';
|
|
2
|
+
export type BrowserDirectoryEntry = {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly kind: 'file' | 'directory';
|
|
5
|
+
};
|
|
2
6
|
export declare class BrowserExternalDirectoryBackend implements ExternalDirectoryBackend {
|
|
3
7
|
private readonly directories;
|
|
4
8
|
private readonly paths;
|
|
@@ -11,6 +15,14 @@ export declare class BrowserExternalDirectoryBackend implements ExternalDirector
|
|
|
11
15
|
registered(path: string, locator: string): void;
|
|
12
16
|
removed(pathOrLocator: string): void;
|
|
13
17
|
list(locator: string): Promise<readonly string[]>;
|
|
18
|
+
listEntries(locator: string, path: string): Promise<readonly BrowserDirectoryEntry[]>;
|
|
19
|
+
lookup(locator: string, path: string): Promise<{
|
|
20
|
+
readonly kind: 'directory';
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'file';
|
|
23
|
+
readonly locator: string;
|
|
24
|
+
readonly size: number;
|
|
25
|
+
} | undefined>;
|
|
14
26
|
open(locator: string, path: string): Promise<{
|
|
15
27
|
readonly locator: string;
|
|
16
28
|
readonly size: number;
|
|
@@ -24,6 +36,7 @@ export declare class BrowserExternalDirectoryBackend implements ExternalDirector
|
|
|
24
36
|
shutdown(): void;
|
|
25
37
|
private getDirectory;
|
|
26
38
|
private getWritableDirectory;
|
|
39
|
+
private openDirectory;
|
|
27
40
|
private getOpenWriter;
|
|
28
41
|
private cleanupWriter;
|
|
29
42
|
private enqueue;
|