hyperdb-wasm 0.0.27367-dev.wasm.g50457039 → 0.0.27368-dev.wasm.ge6142229

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 CHANGED
@@ -14,6 +14,8 @@ export interface HyperDBClient {
14
14
  mount(source: MountSource, path: string, mode: MountAccessMode): Promise<WasmResult>;
15
15
  unmount(path: string): Promise<WasmResult>;
16
16
  refreshMount(path: string): Promise<WasmResult>;
17
+ listMount(path: string): Promise<WasmResult>;
18
+ readMountFile(path: string, file: string): Promise<WasmResult>;
17
19
  pollQuery(query: number): Promise<WasmResult>;
18
20
  cancelQuery(query: number): Promise<WasmResult>;
19
21
  finishQuery(query: number): Promise<WasmResult>;
@@ -31,8 +33,14 @@ export interface MountOptions {
31
33
  export interface MountedResource {
32
34
  readonly path: string;
33
35
  refresh(): Promise<void>;
36
+ list(): Promise<readonly MountFileEntry[]>;
37
+ readFile(path: string): Promise<Blob>;
34
38
  close(): Promise<void>;
35
39
  }
40
+ export interface MountFileEntry {
41
+ readonly path: string;
42
+ readonly size: number;
43
+ }
36
44
  export declare function validateMountPath(path: unknown): asserts path is string;
37
45
  export type HyperDBSettingValue = string | number | boolean | null | readonly HyperDBSettingValue[] | {
38
46
  readonly [key: string]: HyperDBSettingValue;
package/dist/browser.d.ts CHANGED
@@ -8,6 +8,10 @@ export interface BrowserWorkerLike {
8
8
  terminate(): void;
9
9
  }
10
10
  export interface BrowserClientOptions {
11
+ /** Blob workers inline both scripts so pthreads can start without an origin */
12
+ readonly worker?: 'url' | 'blob';
13
+ /** Memory storage is private to this engine and discarded on termination */
14
+ readonly storage?: 'persistent' | 'memory';
11
15
  readonly workerUrl?: string | URL;
12
16
  readonly engineUrl?: string | URL;
13
17
  readonly wasmUrl?: string | URL;
@@ -30,10 +34,11 @@ type BrowserEnvironment = {
30
34
  };
31
35
  };
32
36
  };
33
- export declare function assertBrowserSupport(environment?: BrowserEnvironment): void;
37
+ export declare function assertBrowserSupport(environment?: BrowserEnvironment, storage?: BrowserClientOptions['storage']): void;
34
38
  export declare class BrowserWorker implements WorkerTransport {
35
39
  private readonly worker;
36
- constructor(worker: BrowserWorkerLike);
40
+ private blobUrl?;
41
+ constructor(worker: BrowserWorkerLike, blobUrl?: string | undefined);
37
42
  postMessage(message: EngineRequest, transfer?: readonly ArrayBuffer[]): void;
38
43
  onMessage(listener: (message: unknown) => void): () => void;
39
44
  onError(listener: (error: Error) => void): () => void;
@@ -41,5 +46,11 @@ export declare class BrowserWorker implements WorkerTransport {
41
46
  terminate(): void;
42
47
  }
43
48
  export declare function resolveBrowserWorkerUrl(workerUrl: string | URL, engineUrl: string | URL, wasmUrl?: string | URL, storagePath?: string, baseUrl?: string | URL): URL;
44
- export declare function createBrowserClient(options?: BrowserClientOptions): HyperDBEngineClient;
49
+ export declare function createBrowserClient(options: BrowserClientOptions & {
50
+ worker: 'blob';
51
+ }): Promise<HyperDBEngineClient>;
52
+ export declare function createBrowserClient(options?: BrowserClientOptions & {
53
+ worker?: 'url';
54
+ }): HyperDBEngineClient;
55
+ export declare function createBrowserClient(options: BrowserClientOptions): HyperDBEngineClient | Promise<HyperDBEngineClient>;
45
56
  export {};
package/dist/browser.js CHANGED
@@ -28,6 +28,10 @@ function isWasmResult(value) {
28
28
  case "ok":
29
29
  case "chunk":
30
30
  return hasKeys(value, ["state", "payload"]) && value.payload instanceof Uint8Array;
31
+ case "mountList":
32
+ return hasKeys(value, ["state", "entries"]) && Array.isArray(value.entries) && value.entries.every((entry) => isRecord(entry) && hasKeys(entry, ["path", "size"]) && typeof entry.path === "string" && !!entry.path && Number.isSafeInteger(entry.size) && entry.size >= 0);
33
+ case "mountFile":
34
+ return hasKeys(value, ["state", "blob"]) && value.blob instanceof Blob;
31
35
  case "pending":
32
36
  case "done":
33
37
  return hasKeys(value, ["state"]);
@@ -127,6 +131,12 @@ var init_client = __esm({
127
131
  refreshMount(path) {
128
132
  return this.request({ id: this.nextId++, type: "refreshMount", path });
129
133
  }
134
+ listMount(path) {
135
+ return this.request({ id: this.nextId++, type: "listMount", path });
136
+ }
137
+ readMountFile(path, file) {
138
+ return this.request({ id: this.nextId++, type: "readMountFile", path, file });
139
+ }
130
140
  pollQuery(query) {
131
141
  return this.request({ id: this.nextId++, type: "pollQuery", query });
132
142
  }
@@ -210,7 +220,7 @@ __export(browser_exports, {
210
220
  createBrowserClient: () => createBrowserClient,
211
221
  resolveBrowserWorkerUrl: () => resolveBrowserWorkerUrl
212
222
  });
213
- function assertBrowserSupport(environment = globalThis) {
223
+ function assertBrowserSupport(environment = globalThis, storage = "persistent") {
214
224
  if (typeof environment.WebAssembly?.validate !== "function") {
215
225
  throw new Error("HyperDB cannot initialize because this browser does not support WebAssembly");
216
226
  }
@@ -241,7 +251,7 @@ function assertBrowserSupport(environment = globalThis) {
241
251
  if (environment.crossOriginIsolated !== true) {
242
252
  throw new Error("HyperDB cannot initialize because this page is not cross-origin isolated");
243
253
  }
244
- if (typeof environment.navigator?.storage?.getDirectory !== "function") {
254
+ if (storage !== "memory" && typeof environment.navigator?.storage?.getDirectory !== "function") {
245
255
  throw new Error("HyperDB cannot initialize because this browser does not support the origin-private file system");
246
256
  }
247
257
  }
@@ -253,15 +263,51 @@ function resolveBrowserWorkerUrl(workerUrl, engineUrl, wasmUrl, storagePath, bas
253
263
  return resolved;
254
264
  }
255
265
  function createBrowserClient(options = {}) {
256
- assertBrowserSupport();
266
+ if (options.storage === "memory" && options.storagePath !== void 0) throw new Error("storagePath cannot be used with memory storage");
267
+ if (options.storage !== void 0 && options.storage !== "persistent" && options.storage !== "memory") throw new Error("Unsupported storage mode");
268
+ if (options.worker !== void 0 && options.worker !== "url" && options.worker !== "blob") throw new Error("Unsupported worker mode");
269
+ assertBrowserSupport(globalThis, options.storage);
257
270
  const workerUrl = options.workerUrl ?? new URL("./browser_worker.js", import.meta.url);
258
271
  const engineUrl = options.engineUrl ?? new URL("./hyperdb-wasm.js", import.meta.url);
272
+ if (options.worker === "blob") return createBlobClient(options, workerUrl, engineUrl);
273
+ const resolved = resolveBrowserWorkerUrl(workerUrl, engineUrl, options.wasmUrl, options.storagePath);
274
+ if (options.storage === "memory") resolved.searchParams.set("storage", "memory");
259
275
  return new HyperDBEngineClient(
260
- new BrowserWorker(new Worker(resolveBrowserWorkerUrl(workerUrl, engineUrl, options.wasmUrl, options.storagePath), { type: "classic" })),
276
+ new BrowserWorker(new Worker(resolved, { type: "classic" })),
261
277
  options.onSetupProgress,
262
278
  options.onLog
263
279
  );
264
280
  }
281
+ async function createBlobClient(options, workerUrl, engineUrl) {
282
+ const baseUrl = globalThis.document?.baseURI ?? globalThis.location?.href ?? import.meta.url;
283
+ const resolvedEngineUrl = new URL(engineUrl, baseUrl);
284
+ const configuration = {
285
+ engineUrl: resolvedEngineUrl.href,
286
+ wasmUrl: options.wasmUrl === void 0 ? new URL("hyperdb-wasm.wasm.br", resolvedEngineUrl).href : new URL(options.wasmUrl, baseUrl).href,
287
+ storage: options.storage ?? "persistent",
288
+ storagePath: options.storagePath ?? "",
289
+ inlineEngine: true
290
+ };
291
+ const fetchScript = async (url) => {
292
+ const response = await fetch(url, { mode: "cors", credentials: "omit" });
293
+ if (!response.ok) throw new Error(`Failed to fetch HyperDB script: ${response.status} ${response.statusText}`);
294
+ return (await response.text()).replace(/^#![^\r\n]*/, "");
295
+ };
296
+ const [workerScript, engineScript] = await Promise.all([fetchScript(new URL(workerUrl, baseUrl)), fetchScript(resolvedEngineUrl)]);
297
+ const blobUrl = URL.createObjectURL(new Blob([
298
+ `self.HYPERDB_WASM_CONFIGURATION = ${JSON.stringify(configuration)};
299
+ `,
300
+ workerScript,
301
+ "\n",
302
+ engineScript
303
+ ], { type: "text/javascript" }));
304
+ try {
305
+ return new HyperDBEngineClient(new BrowserWorker(new Worker(blobUrl, { type: "classic" }), blobUrl), options.onSetupProgress, options.onLog);
306
+ } catch (error) {
307
+ URL.revokeObjectURL(blobUrl);
308
+ throw error;
309
+ }
310
+ }
265
311
  var memory64Probe, threadsProbe, exceptionsProbe, tailCallsProbe, BrowserWorker;
266
312
  var init_browser = __esm({
267
313
  "src/browser.ts"() {
@@ -272,10 +318,12 @@ var init_browser = __esm({
272
318
  exceptionsProbe = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 13, 3, 1, 0, 0]);
273
319
  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]);
274
320
  BrowserWorker = class {
275
- constructor(worker) {
321
+ constructor(worker, blobUrl) {
276
322
  this.worker = worker;
323
+ this.blobUrl = blobUrl;
277
324
  }
278
325
  worker;
326
+ blobUrl;
279
327
  postMessage(message, transfer = []) {
280
328
  this.worker.postMessage(message, [...transfer]);
281
329
  }
@@ -293,7 +341,12 @@ var init_browser = __esm({
293
341
  return () => void 0;
294
342
  }
295
343
  terminate() {
296
- this.worker.terminate();
344
+ try {
345
+ this.worker.terminate();
346
+ } finally {
347
+ if (this.blobUrl !== void 0) URL.revokeObjectURL(this.blobUrl);
348
+ this.blobUrl = void 0;
349
+ }
297
350
  }
298
351
  };
299
352
  }
@@ -301,6 +354,43 @@ var init_browser = __esm({
301
354
 
302
355
  // src/api.ts
303
356
  import { RecordBatchReader, Table } from "apache-arrow";
357
+
358
+ // src/memory_directory.ts
359
+ function validateMemoryFilePath(path) {
360
+ if (!path || path.startsWith("/") || path.endsWith("/") || path.includes("\\") || path.includes("\0") || path.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
361
+ throw new Error("Memory directory file path must contain safe relative path segments");
362
+ }
363
+ }
364
+ var MemoryDirectory = class {
365
+ kind = "memory-directory";
366
+ files;
367
+ maxBytes;
368
+ constructor(options = {}) {
369
+ if (options.maxBytes !== void 0 && (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 0)) {
370
+ throw new Error("Memory directory maxBytes must be a nonnegative safe integer");
371
+ }
372
+ const files = { ...options.files };
373
+ let size = 0;
374
+ for (const [path, blob] of Object.entries(files)) {
375
+ validateMemoryFilePath(path);
376
+ if (!(blob instanceof Blob)) throw new Error("Memory directory files must be Blobs");
377
+ size += blob.size;
378
+ if (!Number.isSafeInteger(size)) throw new Error("Memory directory size must be a safe integer");
379
+ const segments = path.split("/");
380
+ for (let index = 1; index < segments.length; ++index) {
381
+ if (Object.hasOwn(files, segments.slice(0, index).join("/"))) throw new Error("Memory directory file paths overlap");
382
+ }
383
+ }
384
+ if (options.maxBytes !== void 0 && size > options.maxBytes) throw new Error("Memory directory quota exceeded (maxBytes)");
385
+ this.files = Object.freeze(files);
386
+ if (options.maxBytes !== void 0) this.maxBytes = options.maxBytes;
387
+ }
388
+ toMountSource() {
389
+ return { kind: "memory-directory", files: this.files, ...this.maxBytes !== void 0 && { maxBytes: this.maxBytes } };
390
+ }
391
+ };
392
+
393
+ // src/api.ts
304
394
  function validateMountPath(path) {
305
395
  if (typeof path !== "string" || !path.startsWith("/mnt/external/")) {
306
396
  throw new Error("Mount path must be an absolute path below /mnt/external");
@@ -526,21 +616,33 @@ var EngineQueryOperation = class {
526
616
  }
527
617
  };
528
618
  var MountedResourceImpl = class {
529
- constructor(path, unmount, refreshMount) {
619
+ constructor(path, unmount, refreshMount, listMount, readMountFile) {
530
620
  this.path = path;
531
621
  this.unmount = unmount;
532
622
  this.refreshMount = refreshMount;
623
+ this.listMount = listMount;
624
+ this.readMountFile = readMountFile;
533
625
  }
534
626
  path;
535
627
  unmount;
536
628
  refreshMount;
629
+ listMount;
630
+ readMountFile;
537
631
  closed = false;
538
632
  closing;
539
633
  async refresh() {
540
- if (this.closed) throw new Error("mounted resource is closed");
541
- if (this.closing) throw new Error("mounted resource is closing");
634
+ this.ensureOpen();
542
635
  await this.refreshMount(this.path);
543
636
  }
637
+ async list() {
638
+ this.ensureOpen();
639
+ return this.listMount(this.path);
640
+ }
641
+ async readFile(path) {
642
+ this.ensureOpen();
643
+ validateMemoryFilePath(path);
644
+ return this.readMountFile(this.path, path);
645
+ }
544
646
  async close() {
545
647
  if (this.closed) return;
546
648
  if (!this.closing) {
@@ -559,6 +661,10 @@ var MountedResourceImpl = class {
559
661
  releaseAfterShutdown() {
560
662
  this.closed = true;
561
663
  }
664
+ ensureOpen() {
665
+ if (this.closed) throw new Error("mounted resource is closed");
666
+ if (this.closing) throw new Error("mounted resource is closing");
667
+ }
562
668
  };
563
669
  var HyperDB = class _HyperDB {
564
670
  constructor(client) {
@@ -572,7 +678,7 @@ var HyperDB = class _HyperDB {
572
678
  mountedResources = /* @__PURE__ */ new Set();
573
679
  static async create(options = {}) {
574
680
  const { createBrowserClient: createBrowserClient2 } = await Promise.resolve().then(() => (init_browser(), browser_exports));
575
- const client = createBrowserClient2(options);
681
+ const client = await createBrowserClient2(options);
576
682
  const database = new _HyperDB(client);
577
683
  try {
578
684
  await database.ready();
@@ -608,6 +714,16 @@ var HyperDB = class _HyperDB {
608
714
  (mounted) => this.unmount(mounted),
609
715
  (path2) => this.runDatabaseOperation(async () => {
610
716
  expectOk(await this.client.refreshMount(path2), "refresh mount");
717
+ }),
718
+ (path2) => this.runDatabaseOperation(async () => {
719
+ const result2 = await this.client.listMount(path2);
720
+ if (result2.state !== "mountList") throw readErrorFromResult(result2, "list mount");
721
+ return result2.entries;
722
+ }),
723
+ (path2, file) => this.runDatabaseOperation(async () => {
724
+ const result2 = await this.client.readMountFile(path2, file);
725
+ if (result2.state !== "mountFile") throw readErrorFromResult(result2, "read mount file");
726
+ return result2.blob;
611
727
  })
612
728
  );
613
729
  this.mountedResources.add(resource);
@@ -861,7 +977,7 @@ var HyperDBConnection = class {
861
977
  init_browser();
862
978
  var HyperDB2 = class _HyperDB extends HyperDB {
863
979
  static async create(options = {}) {
864
- const client = createBrowserClient(options);
980
+ const client = await createBrowserClient(options);
865
981
  const database = new _HyperDB(client);
866
982
  try {
867
983
  await database.ready();
@@ -878,6 +994,11 @@ var HyperDB2 = class _HyperDB extends HyperDB {
878
994
  async mount(source, options) {
879
995
  const path = options?.path;
880
996
  validateMountPath(path);
997
+ if (source && "kind" in source && source.kind === "memory-directory") {
998
+ const mode2 = options.mode ?? "read";
999
+ if (mode2 !== "read" && mode2 !== "readwrite") throw new Error(`Unsupported mount mode: ${String(mode2)}`);
1000
+ return this.mountSource(new MemoryDirectory(source).toMountSource(), path, mode2);
1001
+ }
881
1002
  if (typeof File !== "undefined" && source instanceof File) {
882
1003
  if (options.mode !== void 0 && options.mode !== "read") throw new Error("File mounts are always read-only");
883
1004
  if (!source.name) throw new Error("Mounted file name must not be empty");
@@ -907,5 +1028,6 @@ var HyperDB2 = class _HyperDB extends HyperDB {
907
1028
  export {
908
1029
  HyperDB2 as HyperDB,
909
1030
  HyperDBConnection,
910
- HyperDBError
1031
+ HyperDBError,
1032
+ MemoryDirectory
911
1033
  };