hyperdb-wasm 0.0.26862-dev.wasm.g8c0876f6 → 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 -40
- package/dist/browser.d.ts +3 -1
- package/dist/browser.js +94 -182
- package/dist/browser_external_directory.d.ts +13 -0
- package/dist/browser_worker.js +241 -165
- package/dist/client.d.ts +4 -12
- 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.d.ts +1 -0
- package/dist/node.js +77 -182
- package/dist/node_external_directory.d.ts +11 -0
- package/dist/node_worker.js +260 -170
- package/dist/protocol.d.ts +17 -49
- package/dist/raw.browser.js +127 -134
- package/dist/raw.d.ts +2 -1
- package/dist/raw.node.js +124 -132
- package/dist/wasm_bindings.d.ts +22 -8
- package/dist/worker_setup.d.ts +4 -2
- package/examples/node-attached-databases.mjs +10 -9
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -2,28 +2,19 @@ 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>;
|
|
9
9
|
initialize(settings: string): Promise<WasmResult>;
|
|
10
10
|
connect(): Promise<WasmResult>;
|
|
11
11
|
disconnect(connection: number): Promise<WasmResult>;
|
|
12
|
-
createDatabase(databaseName: string, persistent: boolean): Promise<WasmResult>;
|
|
13
|
-
openDatabase(databaseName: string): Promise<WasmResult>;
|
|
14
|
-
listDatabases(): Promise<WasmResult>;
|
|
15
|
-
checkpointDatabase(databaseName: string): Promise<WasmResult>;
|
|
16
|
-
dropDatabase(databaseName: string): Promise<WasmResult>;
|
|
17
|
-
attachDatabase(connection: number, databaseName: string, alias: string): Promise<WasmResult>;
|
|
18
|
-
detachDatabase(connection: number, alias: string): Promise<WasmResult>;
|
|
19
12
|
startQuery(connection: number, sql: string): Promise<WasmResult>;
|
|
20
13
|
insertArrowIPCFromPath(connection: number, path: string, name: string, schema: string | null, create: boolean, internal?: boolean): Promise<WasmResult>;
|
|
21
14
|
createTemporaryFile(bytes: Uint8Array): Promise<WasmResult>;
|
|
22
15
|
removeFile(path: string): Promise<WasmResult>;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
registerExternalDirectory(handle: unknown, mode: 'read' | 'readwrite'): Promise<WasmResult>;
|
|
26
|
-
removeExternalRegistration(path: string): Promise<WasmResult>;
|
|
16
|
+
mount(source: MountSource, path: string, mode: MountAccessMode): Promise<WasmResult>;
|
|
17
|
+
unmount(path: string): Promise<WasmResult>;
|
|
27
18
|
pollQuery(query: number): Promise<WasmResult>;
|
|
28
19
|
cancelQuery(query: number): Promise<WasmResult>;
|
|
29
20
|
finishQuery(query: number): Promise<WasmResult>;
|
|
@@ -35,21 +26,16 @@ export interface ArrowInsertOptions {
|
|
|
35
26
|
readonly schema?: string;
|
|
36
27
|
readonly create?: boolean;
|
|
37
28
|
}
|
|
38
|
-
export
|
|
39
|
-
|
|
29
|
+
export type MountAccessMode = 'read' | 'readwrite';
|
|
30
|
+
export interface MountOptions {
|
|
31
|
+
readonly path: string;
|
|
32
|
+
readonly mode?: MountAccessMode;
|
|
40
33
|
}
|
|
41
|
-
export interface
|
|
42
|
-
readonly
|
|
43
|
-
|
|
44
|
-
readonly size: number;
|
|
45
|
-
}
|
|
46
|
-
export type ExternalDirectoryMode = 'read' | 'readwrite';
|
|
47
|
-
export interface ExternalDirectoryRegistrationOptions {
|
|
48
|
-
readonly mode?: ExternalDirectoryMode;
|
|
34
|
+
export interface MountedResource {
|
|
35
|
+
readonly path: string;
|
|
36
|
+
close(): Promise<void>;
|
|
49
37
|
}
|
|
50
|
-
|
|
51
|
-
type NodeExternalFileArguments = readonly [path: string];
|
|
52
|
-
export type ExternalFileArguments = BrowserExternalFileArguments | NodeExternalFileArguments;
|
|
38
|
+
export declare function validateMountPath(path: unknown): asserts path is string;
|
|
53
39
|
export type HyperDBSettingValue = string | number | boolean | null | readonly HyperDBSettingValue[] | {
|
|
54
40
|
readonly [key: string]: HyperDBSettingValue;
|
|
55
41
|
};
|
|
@@ -58,10 +44,6 @@ export interface HyperDBCreateOptions {
|
|
|
58
44
|
readonly settings?: HyperDBSettings;
|
|
59
45
|
}
|
|
60
46
|
export type BrowserCreateOptions = BrowserClientOptions & HyperDBCreateOptions;
|
|
61
|
-
export interface DatabaseMetadata {
|
|
62
|
-
readonly name: string;
|
|
63
|
-
readonly storage: 'transient' | 'persistent';
|
|
64
|
-
}
|
|
65
47
|
export type QueryMetricValue = null | boolean | number | string | readonly QueryMetricValue[] | {
|
|
66
48
|
readonly [key: string]: QueryMetricValue;
|
|
67
49
|
};
|
|
@@ -102,27 +84,22 @@ export declare class HyperDBError extends Error {
|
|
|
102
84
|
constructor(message: string, sqlState: string, errorSource: HyperDBErrorSource, detail?: string, hint?: string);
|
|
103
85
|
}
|
|
104
86
|
declare function initializeHyperDB(client: HyperDBClient, settings: HyperDBSettings | undefined): Promise<void>;
|
|
105
|
-
export declare class HyperDB
|
|
87
|
+
export declare class HyperDB {
|
|
106
88
|
protected readonly client: HyperDBClient;
|
|
107
89
|
private terminated;
|
|
108
90
|
private termination;
|
|
109
91
|
private activeDatabaseOperations;
|
|
110
92
|
private readonly connections;
|
|
93
|
+
private readonly mountedResources;
|
|
111
94
|
constructor(client: HyperDBClient);
|
|
112
95
|
static create(options?: BrowserCreateOptions): Promise<HyperDB>;
|
|
113
96
|
ready(): Promise<void>;
|
|
114
97
|
connect(): Promise<HyperDBConnection>;
|
|
115
|
-
|
|
116
|
-
openDatabase(databaseName: string): Promise<void>;
|
|
117
|
-
listDatabases(): Promise<DatabaseMetadata[]>;
|
|
118
|
-
registerExternalFile(...args: ExternalFileArgs): Promise<string>;
|
|
119
|
-
removeExternalRegistration(path: string): Promise<void>;
|
|
120
|
-
protected registerExternalDirectorySource(source: unknown, mode: ExternalDirectoryMode): Promise<string>;
|
|
121
|
-
checkpointDatabase(databaseName: string): Promise<void>;
|
|
122
|
-
dropDatabase(databaseName: string): Promise<void>;
|
|
98
|
+
protected mountSource(source: MountSource, path: string, mode: MountAccessMode, cleanup?: () => void): Promise<MountedResource>;
|
|
123
99
|
terminate(): Promise<void>;
|
|
124
100
|
private ensureRunning;
|
|
125
101
|
protected runDatabaseOperation<Result>(operation: () => Promise<Result>): Promise<Result>;
|
|
102
|
+
private unmount;
|
|
126
103
|
}
|
|
127
104
|
export declare class HyperDBConnection {
|
|
128
105
|
private readonly client;
|
|
@@ -133,8 +110,6 @@ export declare class HyperDBConnection {
|
|
|
133
110
|
private closing;
|
|
134
111
|
constructor(client: HyperDBClient, connection: number, onClose?: () => void);
|
|
135
112
|
close(): Promise<void>;
|
|
136
|
-
attachDatabase(databaseName: string, alias?: string): Promise<void>;
|
|
137
|
-
detachDatabase(alias: string): Promise<void>;
|
|
138
113
|
sendIPC(sql: string): QueryResult<AsyncIterable<Uint8Array>>;
|
|
139
114
|
queryIPC(sql: string): QueryResult<Promise<Uint8Array>>;
|
|
140
115
|
send<T extends TypeMap = {
|
package/dist/browser.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface BrowserClientOptions {
|
|
|
11
11
|
readonly workerUrl?: string | URL;
|
|
12
12
|
readonly engineUrl?: string | URL;
|
|
13
13
|
readonly wasmUrl?: string | URL;
|
|
14
|
+
/** OPFS-relative directory mounted as the durable filesystem root. */
|
|
15
|
+
readonly storagePath?: string;
|
|
14
16
|
readonly onSetupProgress?: (progress: SetupProgress) => void;
|
|
15
17
|
}
|
|
16
18
|
type BrowserEnvironment = {
|
|
@@ -36,6 +38,6 @@ export declare class BrowserWorker implements WorkerTransport {
|
|
|
36
38
|
onClose(): () => void;
|
|
37
39
|
terminate(): void;
|
|
38
40
|
}
|
|
39
|
-
export declare function resolveBrowserWorkerUrl(workerUrl: string | URL, engineUrl: string | URL, wasmUrl?: string | URL): URL;
|
|
41
|
+
export declare function resolveBrowserWorkerUrl(workerUrl: string | URL, engineUrl: string | URL, wasmUrl?: string | URL, storagePath?: string): URL;
|
|
40
42
|
export declare function createBrowserClient(options?: BrowserClientOptions): HyperDBEngineClient;
|
|
41
43
|
export {};
|
package/dist/browser.js
CHANGED
|
@@ -106,27 +106,6 @@ var init_client = __esm({
|
|
|
106
106
|
disconnect(connection) {
|
|
107
107
|
return this.request({ id: this.nextId++, type: "disconnect", connection });
|
|
108
108
|
}
|
|
109
|
-
createDatabase(databaseName, persistent) {
|
|
110
|
-
return this.request({ id: this.nextId++, type: "createDatabase", databaseName, persistent });
|
|
111
|
-
}
|
|
112
|
-
openDatabase(databaseName) {
|
|
113
|
-
return this.request({ id: this.nextId++, type: "openDatabase", databaseName });
|
|
114
|
-
}
|
|
115
|
-
listDatabases() {
|
|
116
|
-
return this.request({ id: this.nextId++, type: "listDatabases" });
|
|
117
|
-
}
|
|
118
|
-
checkpointDatabase(databaseName) {
|
|
119
|
-
return this.request({ id: this.nextId++, type: "checkpointDatabase", databaseName });
|
|
120
|
-
}
|
|
121
|
-
dropDatabase(databaseName) {
|
|
122
|
-
return this.request({ id: this.nextId++, type: "dropDatabase", databaseName });
|
|
123
|
-
}
|
|
124
|
-
attachDatabase(connection, databaseName, alias) {
|
|
125
|
-
return this.request({ id: this.nextId++, type: "attachDatabase", connection, databaseName, alias });
|
|
126
|
-
}
|
|
127
|
-
detachDatabase(connection, alias) {
|
|
128
|
-
return this.request({ id: this.nextId++, type: "detachDatabase", connection, alias });
|
|
129
|
-
}
|
|
130
109
|
shutdown() {
|
|
131
110
|
return this.request({ id: this.nextId++, type: "shutdown" });
|
|
132
111
|
}
|
|
@@ -143,14 +122,11 @@ var init_client = __esm({
|
|
|
143
122
|
removeFile(path) {
|
|
144
123
|
return this.request({ id: this.nextId++, type: "removeFile", path });
|
|
145
124
|
}
|
|
146
|
-
|
|
147
|
-
return
|
|
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 });
|
|
125
|
+
mount(source, path, mode) {
|
|
126
|
+
return this.request({ id: this.nextId++, type: "mount", source, path, mode });
|
|
151
127
|
}
|
|
152
|
-
|
|
153
|
-
return this.request({ id: this.nextId++, type: "
|
|
128
|
+
unmount(path) {
|
|
129
|
+
return this.request({ id: this.nextId++, type: "unmount", path });
|
|
154
130
|
}
|
|
155
131
|
pollQuery(query) {
|
|
156
132
|
return this.request({ id: this.nextId++, type: "pollQuery", query });
|
|
@@ -252,10 +228,11 @@ function assertBrowserSupport(environment = globalThis) {
|
|
|
252
228
|
throw new Error("HyperDB cannot initialize because this browser does not support the origin-private file system");
|
|
253
229
|
}
|
|
254
230
|
}
|
|
255
|
-
function resolveBrowserWorkerUrl(workerUrl, engineUrl, wasmUrl) {
|
|
231
|
+
function resolveBrowserWorkerUrl(workerUrl, engineUrl, wasmUrl, storagePath) {
|
|
256
232
|
const resolved = new URL(workerUrl);
|
|
257
233
|
resolved.searchParams.set("engineUrl", new URL(engineUrl).href);
|
|
258
234
|
if (wasmUrl != null) resolved.searchParams.set("wasmUrl", new URL(wasmUrl).href);
|
|
235
|
+
if (storagePath != null) resolved.searchParams.set("storagePath", storagePath);
|
|
259
236
|
return resolved;
|
|
260
237
|
}
|
|
261
238
|
function createBrowserClient(options = {}) {
|
|
@@ -263,7 +240,7 @@ function createBrowserClient(options = {}) {
|
|
|
263
240
|
const workerUrl = options.workerUrl ?? new URL("./browser_worker.js", import.meta.url);
|
|
264
241
|
const engineUrl = options.engineUrl ?? new URL("./hyperdb-wasm.js", import.meta.url);
|
|
265
242
|
return new HyperDBEngineClient(
|
|
266
|
-
new BrowserWorker(new Worker(resolveBrowserWorkerUrl(workerUrl, engineUrl, options.wasmUrl), { type: "classic" })),
|
|
243
|
+
new BrowserWorker(new Worker(resolveBrowserWorkerUrl(workerUrl, engineUrl, options.wasmUrl, options.storagePath), { type: "classic" })),
|
|
267
244
|
options.onSetupProgress
|
|
268
245
|
);
|
|
269
246
|
}
|
|
@@ -12146,6 +12123,15 @@ RecordBatchFileWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
|
|
|
12146
12123
|
RecordBatchStreamWriter["throughDOM"] = recordBatchWriterThroughDOMStream;
|
|
12147
12124
|
|
|
12148
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
|
+
}
|
|
12149
12135
|
var HyperDBError = class extends Error {
|
|
12150
12136
|
sqlState;
|
|
12151
12137
|
errorSource;
|
|
@@ -12160,7 +12146,6 @@ var HyperDBError = class extends Error {
|
|
|
12160
12146
|
this.hint = hint;
|
|
12161
12147
|
}
|
|
12162
12148
|
};
|
|
12163
|
-
var maxPersistentDatabaseNameLength = 200;
|
|
12164
12149
|
function deferred() {
|
|
12165
12150
|
let resolve;
|
|
12166
12151
|
let reject;
|
|
@@ -12216,26 +12201,6 @@ function normalizeArrowInsertOptions(options) {
|
|
|
12216
12201
|
if (!options.name) throw new Error("Arrow insert table name must not be empty");
|
|
12217
12202
|
return { name: options.name, schema: options.schema ?? null, create: options.create ?? true };
|
|
12218
12203
|
}
|
|
12219
|
-
function validateDatabaseName(databaseName) {
|
|
12220
|
-
if (!databaseName) throw new Error("Database name must not be empty");
|
|
12221
|
-
if (databaseName.includes("\0")) throw new Error("Database name must not contain NUL");
|
|
12222
|
-
}
|
|
12223
|
-
function validatePersistentDatabaseName(databaseName) {
|
|
12224
|
-
validateDatabaseName(databaseName);
|
|
12225
|
-
if (databaseName.length > maxPersistentDatabaseNameLength) {
|
|
12226
|
-
throw new Error(`Persistent database name must be at most ${maxPersistentDatabaseNameLength} characters`);
|
|
12227
|
-
}
|
|
12228
|
-
if (!/^[a-z0-9][a-z0-9_-]*$/.test(databaseName)) {
|
|
12229
|
-
throw new Error("Persistent database name must start with a lowercase ASCII letter or digit and contain only lowercase ASCII letters, digits, hyphens, and underscores");
|
|
12230
|
-
}
|
|
12231
|
-
if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/.test(databaseName)) {
|
|
12232
|
-
throw new Error("Persistent database name is reserved by Windows");
|
|
12233
|
-
}
|
|
12234
|
-
}
|
|
12235
|
-
function validateDatabaseAlias(alias) {
|
|
12236
|
-
if (!alias) throw new Error("Database alias must not be empty");
|
|
12237
|
-
if (alias.includes("\0")) throw new Error("Database alias must not contain NUL");
|
|
12238
|
-
}
|
|
12239
12204
|
function materializeDictionaryColumns(table) {
|
|
12240
12205
|
const dictionaryColumns = table.schema.fields.map((field, index) => DataType.isDictionary(field.type) ? index : -1).filter((index) => index !== -1);
|
|
12241
12206
|
if (dictionaryColumns.length === 0) return table;
|
|
@@ -12276,37 +12241,46 @@ async function initializeHyperDB(client, settings) {
|
|
|
12276
12241
|
if (serialized === void 0) throw new Error("HyperDB settings must be JSON-serializable");
|
|
12277
12242
|
expectOk(await client.initialize(serialized), "initialize HyperDB settings");
|
|
12278
12243
|
}
|
|
12279
|
-
function readDatabasesFromResult(result) {
|
|
12280
|
-
expectOk(result, "list databases");
|
|
12281
|
-
const invalidResult = () => new Error("list databases returned invalid metadata");
|
|
12282
|
-
if (result.payload.byteLength < 4) throw invalidResult();
|
|
12283
|
-
const view = new DataView(result.payload.buffer, result.payload.byteOffset, result.payload.byteLength);
|
|
12284
|
-
const count = view.getUint32(0, true);
|
|
12285
|
-
let offset = 4;
|
|
12286
|
-
const databases = [];
|
|
12287
|
-
const decoder2 = new TextDecoder("utf-8", { fatal: true });
|
|
12288
|
-
for (let index = 0; index < count; ++index) {
|
|
12289
|
-
if (offset + 8 > result.payload.byteLength) throw invalidResult();
|
|
12290
|
-
const rawStorage = view.getUint32(offset, true);
|
|
12291
|
-
const nameLength = view.getUint32(offset + 4, true);
|
|
12292
|
-
offset += 8;
|
|
12293
|
-
if (nameLength > result.payload.byteLength - offset) throw invalidResult();
|
|
12294
|
-
let name;
|
|
12295
|
-
try {
|
|
12296
|
-
name = decoder2.decode(result.payload.subarray(offset, offset + nameLength));
|
|
12297
|
-
} catch {
|
|
12298
|
-
throw invalidResult();
|
|
12299
|
-
}
|
|
12300
|
-
if (!name || name.includes("\0") || rawStorage !== 0 && rawStorage !== 1) throw invalidResult();
|
|
12301
|
-
databases.push({ name, storage: rawStorage === 0 ? "transient" : "persistent" });
|
|
12302
|
-
offset += nameLength;
|
|
12303
|
-
}
|
|
12304
|
-
if (offset !== result.payload.byteLength) throw invalidResult();
|
|
12305
|
-
return databases;
|
|
12306
|
-
}
|
|
12307
12244
|
function yieldToEventLoop() {
|
|
12308
12245
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
12309
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
|
+
};
|
|
12310
12284
|
var HyperDB = class _HyperDB {
|
|
12311
12285
|
constructor(client) {
|
|
12312
12286
|
this.client = client;
|
|
@@ -12316,6 +12290,7 @@ var HyperDB = class _HyperDB {
|
|
|
12316
12290
|
termination;
|
|
12317
12291
|
activeDatabaseOperations = 0;
|
|
12318
12292
|
connections = /* @__PURE__ */ new Set();
|
|
12293
|
+
mountedResources = /* @__PURE__ */ new Set();
|
|
12319
12294
|
static async create(options = {}) {
|
|
12320
12295
|
const { createBrowserClient: createBrowserClient2 } = await Promise.resolve().then(() => (init_browser(), browser_exports));
|
|
12321
12296
|
const client = createBrowserClient2(options);
|
|
@@ -12343,86 +12318,15 @@ var HyperDB = class _HyperDB {
|
|
|
12343
12318
|
this.connections.add(result);
|
|
12344
12319
|
return result;
|
|
12345
12320
|
}
|
|
12346
|
-
|
|
12347
|
-
validateDatabaseName(databaseName);
|
|
12348
|
-
if (options.storage !== void 0 && options.storage !== "transient" && options.storage !== "persistent") {
|
|
12349
|
-
throw new Error(`Unsupported database storage: ${String(options.storage)}`);
|
|
12350
|
-
}
|
|
12351
|
-
if (options.storage === "persistent") validatePersistentDatabaseName(databaseName);
|
|
12352
|
-
await this.runDatabaseOperation(async () => {
|
|
12353
|
-
expectOk(await this.client.createDatabase(databaseName, options.storage === "persistent"), "create database");
|
|
12354
|
-
});
|
|
12355
|
-
}
|
|
12356
|
-
async openDatabase(databaseName) {
|
|
12357
|
-
validatePersistentDatabaseName(databaseName);
|
|
12358
|
-
await this.runDatabaseOperation(async () => {
|
|
12359
|
-
expectOk(await this.client.openDatabase(databaseName), "open database");
|
|
12360
|
-
});
|
|
12361
|
-
}
|
|
12362
|
-
listDatabases() {
|
|
12363
|
-
return this.runDatabaseOperation(async () => readDatabasesFromResult(await this.client.listDatabases()));
|
|
12364
|
-
}
|
|
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;
|
|
12379
|
-
if (!name) throw new Error("External file name must not be empty");
|
|
12380
|
-
if (name.includes("\0")) throw new Error("External file name must not contain NUL");
|
|
12381
|
-
if (name === "." || name === ".." || name.includes("/") || name.includes("\\")) {
|
|
12382
|
-
throw new Error("External file name must be a basename without path separators");
|
|
12383
|
-
}
|
|
12384
|
-
if (!Number.isSafeInteger(size) || size < 0) throw new Error("External file size must be a nonnegative safe integer");
|
|
12385
|
-
let scheme;
|
|
12386
|
-
try {
|
|
12387
|
-
scheme = new URL(url).protocol;
|
|
12388
|
-
} catch {
|
|
12389
|
-
throw new Error("External file URL must use the blob scheme");
|
|
12390
|
-
}
|
|
12391
|
-
if (scheme !== "blob:") throw new Error("External file URL must use the blob scheme");
|
|
12321
|
+
mountSource(source, path, mode, cleanup = () => void 0) {
|
|
12392
12322
|
return this.runDatabaseOperation(async () => {
|
|
12393
|
-
const result = await this.client.
|
|
12394
|
-
expectOk(result, "
|
|
12395
|
-
const
|
|
12396
|
-
if (
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
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");
|
|
12403
|
-
await this.runDatabaseOperation(async () => {
|
|
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;
|
|
12414
|
-
});
|
|
12415
|
-
}
|
|
12416
|
-
async checkpointDatabase(databaseName) {
|
|
12417
|
-
validateDatabaseName(databaseName);
|
|
12418
|
-
await this.runDatabaseOperation(async () => {
|
|
12419
|
-
expectOk(await this.client.checkpointDatabase(databaseName), "checkpoint database");
|
|
12420
|
-
});
|
|
12421
|
-
}
|
|
12422
|
-
async dropDatabase(databaseName) {
|
|
12423
|
-
validateDatabaseName(databaseName);
|
|
12424
|
-
await this.runDatabaseOperation(async () => {
|
|
12425
|
-
expectOk(await this.client.dropDatabase(databaseName), "drop database");
|
|
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;
|
|
12426
12330
|
});
|
|
12427
12331
|
}
|
|
12428
12332
|
async terminate() {
|
|
@@ -12442,6 +12346,8 @@ var HyperDB = class _HyperDB {
|
|
|
12442
12346
|
this.termination = void 0;
|
|
12443
12347
|
throw error;
|
|
12444
12348
|
}
|
|
12349
|
+
for (const resource of this.mountedResources) resource.releaseAfterShutdown();
|
|
12350
|
+
this.mountedResources.clear();
|
|
12445
12351
|
try {
|
|
12446
12352
|
await this.client.terminate();
|
|
12447
12353
|
this.terminated = true;
|
|
@@ -12466,6 +12372,12 @@ var HyperDB = class _HyperDB {
|
|
|
12466
12372
|
--this.activeDatabaseOperations;
|
|
12467
12373
|
}
|
|
12468
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
|
+
}
|
|
12469
12381
|
};
|
|
12470
12382
|
var HyperDBConnection = class {
|
|
12471
12383
|
constructor(client, connection, onClose = () => void 0) {
|
|
@@ -12496,25 +12408,6 @@ var HyperDBConnection = class {
|
|
|
12496
12408
|
}
|
|
12497
12409
|
await this.closing;
|
|
12498
12410
|
}
|
|
12499
|
-
async attachDatabase(databaseName, alias = databaseName) {
|
|
12500
|
-
validateDatabaseName(databaseName);
|
|
12501
|
-
validateDatabaseAlias(alias);
|
|
12502
|
-
this.ensureAvailable();
|
|
12503
|
-
try {
|
|
12504
|
-
expectOk(await this.client.attachDatabase(this.connection, databaseName, alias), "attach database");
|
|
12505
|
-
} finally {
|
|
12506
|
-
this.active = false;
|
|
12507
|
-
}
|
|
12508
|
-
}
|
|
12509
|
-
async detachDatabase(alias) {
|
|
12510
|
-
validateDatabaseAlias(alias);
|
|
12511
|
-
this.ensureAvailable();
|
|
12512
|
-
try {
|
|
12513
|
-
expectOk(await this.client.detachDatabase(this.connection, alias), "detach database");
|
|
12514
|
-
} finally {
|
|
12515
|
-
this.active = false;
|
|
12516
|
-
}
|
|
12517
|
-
}
|
|
12518
12411
|
sendIPC(sql) {
|
|
12519
12412
|
const connection = this;
|
|
12520
12413
|
const queryInfo = deferred();
|
|
@@ -12709,16 +12602,35 @@ var HyperDB2 = class _HyperDB extends HyperDB {
|
|
|
12709
12602
|
throw error;
|
|
12710
12603
|
}
|
|
12711
12604
|
}
|
|
12712
|
-
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;
|
|
12713
12625
|
const mode = options.mode ?? "read";
|
|
12714
|
-
if (mode !== "read" && mode !== "readwrite") throw new Error(`Unsupported
|
|
12626
|
+
if (mode !== "read" && mode !== "readwrite") throw new Error(`Unsupported mount mode: ${String(mode)}`);
|
|
12715
12627
|
if (!handle || handle.kind !== "directory" || typeof handle.queryPermission !== "function" || typeof handle.requestPermission !== "function") {
|
|
12716
|
-
throw new Error("
|
|
12628
|
+
throw new Error("Mount source must be a File or FileSystemDirectoryHandle");
|
|
12717
12629
|
}
|
|
12718
12630
|
let permission = await handle.queryPermission({ mode });
|
|
12719
12631
|
if (permission !== "granted") permission = await handle.requestPermission({ mode });
|
|
12720
|
-
if (permission !== "granted") throw new Error(`
|
|
12721
|
-
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);
|
|
12722
12634
|
}
|
|
12723
12635
|
};
|
|
12724
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;
|