hyperdb-wasm 0.0.27356-dev.wasm.ga5fd9868 → 0.0.27367-dev.wasm.g50457039

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
@@ -5,6 +5,7 @@ import type { BrowserClientOptions } from './browser.js';
5
5
  import type { MountSource, WasmResult } from './protocol.js';
6
6
  import type { HyperDBErrorSource } from './protocol.js';
7
7
  export interface HyperDBClient {
8
+ readonly failure?: Error | undefined;
8
9
  ready(): Promise<void>;
9
10
  initialize(settings: HyperDBSettings): Promise<WasmResult>;
10
11
  connect(settings?: ConnectionSettings): Promise<WasmResult>;
@@ -125,6 +126,7 @@ export declare class HyperDBConnection {
125
126
  execute(sql: string, options?: QueryOptions): QueryPromise<bigint | null>;
126
127
  private ensureAvailable;
127
128
  closeForTermination(): Promise<void>;
129
+ releaseAfterShutdown(): void;
128
130
  }
129
131
  export type { HyperDBEngineClient };
130
132
  export { initializeHyperDB as initializeHyperDBInternal };
package/dist/browser.d.ts CHANGED
@@ -14,6 +14,8 @@ export interface BrowserClientOptions {
14
14
  /** OPFS-relative directory mounted as the durable filesystem root. */
15
15
  readonly storagePath?: string;
16
16
  readonly onSetupProgress?: (progress: SetupProgress) => void;
17
+ /** Receives engine output; omitted callbacks discard output, and callback exceptions are ignored */
18
+ readonly onLog?: (stream: 'stdout' | 'stderr', text: string) => void;
17
19
  }
18
20
  type BrowserEnvironment = {
19
21
  readonly WebAssembly?: typeof WebAssembly;
package/dist/browser.js CHANGED
@@ -52,7 +52,7 @@ function isWorkerEvent(message) {
52
52
  case "fatal":
53
53
  return hasKeys(message, ["type", "error"]) && typeof message.error === "string";
54
54
  case "log":
55
- return hasKeys(message, ["type", "text"]) && typeof message.text === "string";
55
+ return hasKeys(message, ["type", "stream", "text"]) && (message.stream === "stdout" || message.stream === "stderr") && typeof message.text === "string";
56
56
  case "response":
57
57
  return hasKeys(message, ["id", "type", "result"]) && Number.isSafeInteger(message.id) && isWasmResult(message.result);
58
58
  default:
@@ -72,8 +72,9 @@ var init_client = __esm({
72
72
  "use strict";
73
73
  init_protocol();
74
74
  HyperDBEngineClient = class {
75
- constructor(transport, onSetupProgress) {
75
+ constructor(transport, onSetupProgress, onLog) {
76
76
  this.onSetupProgress = onSetupProgress;
77
+ this.onLog = onLog;
77
78
  this.transport = transport;
78
79
  this.readyPromise = new Promise((resolve, reject) => {
79
80
  this.resolveReady = resolve;
@@ -81,11 +82,12 @@ var init_client = __esm({
81
82
  });
82
83
  this.unsubscribe = [
83
84
  transport.onMessage((message) => this.onMessage(message)),
84
- transport.onError((error) => this.fail(error)),
85
+ transport.onError((error) => this.failAndTerminate(error)),
85
86
  transport.onClose((error) => this.fail(error))
86
87
  ];
87
88
  }
88
89
  onSetupProgress;
90
+ onLog;
89
91
  transport;
90
92
  pending = /* @__PURE__ */ new Map();
91
93
  readyPromise;
@@ -93,10 +95,14 @@ var init_client = __esm({
93
95
  rejectReady;
94
96
  nextId = 1;
95
97
  failed;
98
+ termination;
96
99
  unsubscribe;
97
100
  ready() {
98
101
  return this.readyPromise;
99
102
  }
103
+ get failure() {
104
+ return this.failed;
105
+ }
100
106
  initialize(settings) {
101
107
  return this.request({ id: this.nextId++, type: "initialize", settings });
102
108
  }
@@ -131,8 +137,15 @@ var init_client = __esm({
131
137
  return this.request({ id: this.nextId++, type: "finishQuery", query });
132
138
  }
133
139
  async terminate() {
134
- await this.transport.terminate();
135
- this.fail(new Error("engine worker terminated"));
140
+ if (!this.termination) {
141
+ this.termination = Promise.resolve().then(() => this.transport.terminate()).then(() => {
142
+ this.fail(new Error("engine worker terminated"));
143
+ }, (error) => {
144
+ this.termination = void 0;
145
+ throw error;
146
+ });
147
+ }
148
+ await this.termination;
136
149
  }
137
150
  request(request, transfer = []) {
138
151
  if (this.failed) return Promise.reject(this.failed);
@@ -156,8 +169,15 @@ var init_client = __esm({
156
169
  this.onSetupProgress?.({ bytesLoaded: message.bytesLoaded, bytesTotal: message.bytesTotal });
157
170
  return;
158
171
  }
172
+ if (message.type === "log") {
173
+ try {
174
+ this.onLog?.(message.stream, message.text);
175
+ } catch {
176
+ }
177
+ return;
178
+ }
159
179
  if (message.type === "fatal") {
160
- this.fail(new Error(message.error || "engine aborted"));
180
+ this.failAndTerminate(new Error(message.error || "engine aborted"));
161
181
  return;
162
182
  }
163
183
  if (message.type !== "response") return;
@@ -166,6 +186,10 @@ var init_client = __esm({
166
186
  this.pending.delete(message.id);
167
187
  pending.resolve(message.result);
168
188
  }
189
+ failAndTerminate(error) {
190
+ this.fail(error);
191
+ void this.terminate().catch(() => void 0);
192
+ }
169
193
  fail(error) {
170
194
  if (this.failed) return;
171
195
  this.failed = error;
@@ -234,7 +258,8 @@ function createBrowserClient(options = {}) {
234
258
  const engineUrl = options.engineUrl ?? new URL("./hyperdb-wasm.js", import.meta.url);
235
259
  return new HyperDBEngineClient(
236
260
  new BrowserWorker(new Worker(resolveBrowserWorkerUrl(workerUrl, engineUrl, options.wasmUrl, options.storagePath), { type: "classic" })),
237
- options.onSetupProgress
261
+ options.onSetupProgress,
262
+ options.onLog
238
263
  );
239
264
  }
240
265
  var memory64Probe, threadsProbe, exceptionsProbe, tailCallsProbe, BrowserWorker;
@@ -591,25 +616,23 @@ var HyperDB = class _HyperDB {
591
616
  }
592
617
  async terminate() {
593
618
  if (this.terminated) return;
594
- if (this.activeDatabaseOperations !== 0) throw new Error("database has an active operation");
619
+ if (this.activeDatabaseOperations !== 0 && !this.client.failure) throw new Error("database has an active operation");
595
620
  if (!this.termination) {
596
621
  this.termination = (async () => {
597
622
  try {
598
- await Promise.all([...this.connections].map(async (connection) => connection.closeForTermination()));
599
- } catch (error) {
600
- this.termination = void 0;
601
- throw error;
602
- }
603
- try {
604
- expectOk(await this.client.shutdown(), "shutdown");
605
- } catch (error) {
606
- this.termination = void 0;
607
- throw error;
608
- }
609
- for (const resource of this.mountedResources) resource.releaseAfterShutdown();
610
- this.mountedResources.clear();
611
- try {
623
+ if (!this.client.failure) {
624
+ try {
625
+ await Promise.all([...this.connections].map(async (connection) => connection.closeForTermination()));
626
+ expectOk(await this.client.shutdown(), "shutdown");
627
+ } catch (error) {
628
+ if (!this.client.failure) throw error;
629
+ }
630
+ }
612
631
  await this.client.terminate();
632
+ for (const connection of this.connections) connection.releaseAfterShutdown();
633
+ this.connections.clear();
634
+ for (const resource of this.mountedResources) resource.releaseAfterShutdown();
635
+ this.mountedResources.clear();
613
636
  this.terminated = true;
614
637
  } catch (error) {
615
638
  this.termination = void 0;
@@ -742,12 +765,17 @@ var HyperDBConnection = class {
742
765
  const result = {
743
766
  [Symbol.asyncIterator]() {
744
767
  return {
745
- next() {
746
- if (operation.aborted && !abortDelivered) {
747
- abortDelivered = true;
748
- return Promise.reject(operation.abortReason);
768
+ async next() {
769
+ try {
770
+ if (operation.aborted && !abortDelivered) {
771
+ abortDelivered = true;
772
+ throw operation.abortReason;
773
+ }
774
+ return await operation.interrupt(generator.next());
775
+ } catch (error) {
776
+ await generator.return(void 0);
777
+ throw error;
749
778
  }
750
- return operation.interrupt(generator.next());
751
779
  },
752
780
  return(value) {
753
781
  return generator.return(value);
@@ -791,12 +819,14 @@ var HyperDBConnection = class {
791
819
  return queryPromise(result, stream.info);
792
820
  }
793
821
  query(sql, options = {}) {
794
- const stream = this.queryStream(sql, options);
822
+ const stream = this.queryStreamIPC(sql, options);
795
823
  const result = (async () => {
796
- const reader = await stream;
824
+ const reader = await RecordBatchReader.from(stream);
825
+ await reader.open();
826
+ const schema = reader.schema;
797
827
  const batches = [];
798
828
  for await (const batch of reader) batches.push(batch);
799
- return new Table(reader.schema, batches);
829
+ return schema ? new Table(schema, batches) : new Table();
800
830
  })();
801
831
  return queryPromise(result, stream.info);
802
832
  }
@@ -821,6 +851,10 @@ var HyperDBConnection = class {
821
851
  if (this.active) throw new Error("cannot terminate while a connection has an active operation");
822
852
  await this.close();
823
853
  }
854
+ releaseAfterShutdown() {
855
+ this.closed = true;
856
+ this.onClose();
857
+ }
824
858
  };
825
859
 
826
860
  // src/index.ts
@@ -0,0 +1,11 @@
1
+ import type { DatabaseLockBackend } from './database_locks.js';
2
+ /** Paths must include the full OPFS path below /mnt/fs, including the configured storage directory */
3
+ export declare class BrowserDatabaseLockBackend implements DatabaseLockBackend {
4
+ private readonly locks;
5
+ private readonly requests;
6
+ private nextToken;
7
+ private closed;
8
+ acquire(path: string, shared: boolean): Promise<number>;
9
+ release(token: number): Promise<void>;
10
+ close(): Promise<void>;
11
+ }
@@ -145,20 +145,25 @@
145
145
  const pointer = safeInteger(this.invoke(request), "result pointer");
146
146
  const result = this.decodeResult(pointer);
147
147
  if (request.type === "shutdown" && result.state === "ok") {
148
- this.mountedPaths.clear();
149
- this.mountedDirectoryPaths.clear();
150
- const completion = completionFor([
151
- this.module.externalFileBackend?.shutdown(),
152
- this.module.externalDirectoryBackend?.shutdown()
153
- ]);
148
+ const completion2 = (async () => {
149
+ await this.module.hyperdbWasmDatabaseLocksDrain?.();
150
+ await this.module.databaseLockBackend?.close();
151
+ await Promise.all([
152
+ this.module.externalFileBackend?.shutdown(),
153
+ this.module.externalDirectoryBackend?.shutdown()
154
+ ]);
155
+ this.mountedPaths.clear();
156
+ this.mountedDirectoryPaths.clear();
157
+ })();
154
158
  return {
155
159
  response: { id: request.id, type: "response", result },
156
160
  transfer: [],
157
- ...completion && { completion }
161
+ completion: completion2
158
162
  };
159
163
  }
160
164
  const response = { id: request.id, type: "response", result };
161
- return { response, transfer: result.state === "chunk" ? [result.payload.buffer] : [] };
165
+ const completion = request.type === "disconnect" || request.type === "finishQuery" || request.type === "shutdown" ? this.module.hyperdbWasmDatabaseLocksDrain?.() : void 0;
166
+ return { response, transfer: result.state === "chunk" ? [result.payload.buffer] : [], ...completion && { completion } };
162
167
  }
163
168
  invoke(request) {
164
169
  switch (request.type) {
@@ -378,6 +383,29 @@
378
383
  }
379
384
  }
380
385
 
386
+ // src/database_locks.ts
387
+ var DatabaseLockConflictError = class extends Error {
388
+ constructor(path, options) {
389
+ super(`Database lock is already held: ${path}`, options);
390
+ this.name = "DatabaseLockConflictError";
391
+ }
392
+ };
393
+ function normalizeDatabaseLockPath(path) {
394
+ if (!path.startsWith("/") || path.endsWith("/") || path.includes("\0") || path.includes("\\")) {
395
+ throw new Error("Database lock path must be an absolute file path below /mnt/fs");
396
+ }
397
+ const segments = [];
398
+ for (const segment of path.split("/")) {
399
+ if (!segment || segment === ".") continue;
400
+ if (segment === "..") segments.pop();
401
+ else segments.push(segment);
402
+ }
403
+ if (segments[0] !== "mnt" || segments[1] !== "fs" || segments.length < 3) {
404
+ throw new Error("Database lock path must be an absolute file path below /mnt/fs");
405
+ }
406
+ return `/${segments.join("/")}`;
407
+ }
408
+
381
409
  // src/worker_setup.ts
382
410
  function errorText(error) {
383
411
  return error instanceof Error ? error.message : String(error);
@@ -455,11 +483,11 @@
455
483
  try {
456
484
  completeFetch(id, allocate(module, bytes), bytes.byteLength);
457
485
  } catch (error) {
458
- transport.postMessage({ type: "log", text: `fetch failed: ${errorText(error)}` });
486
+ transport.postMessage({ type: "log", stream: "stderr", text: `fetch failed: ${errorText(error)}` });
459
487
  completeFetch(id, 0, -1);
460
488
  }
461
489
  }).catch(() => {
462
- transport.postMessage({ type: "log", text: "fetch failed" });
490
+ transport.postMessage({ type: "log", stream: "stderr", text: "fetch failed" });
463
491
  completeFetch(id, 0, -1);
464
492
  });
465
493
  };
@@ -470,7 +498,7 @@
470
498
  if (!backend) return;
471
499
  module.hyperdbWasmExternalDirectoryReleaseGeneration = (locator, generation) => {
472
500
  void Promise.resolve().then(() => backend.releaseGeneration(locator, generation)).catch(() => {
473
- transport.postMessage({ type: "log", text: "External directory generation cleanup failed; mount removal will retry" });
501
+ transport.postMessage({ type: "log", stream: "stderr", text: "External directory generation cleanup failed; mount removal will retry" });
474
502
  });
475
503
  };
476
504
  const listComplete = module._hyperdb_wasm_external_directory_list_complete;
@@ -603,10 +631,50 @@
603
631
  }).catch(() => complete(id, 0, -1));
604
632
  };
605
633
  }
634
+ function installDatabaseLockRuntime(module, fatal) {
635
+ const releases = /* @__PURE__ */ new Set();
636
+ const complete = (id, token, error) => {
637
+ if (!module._hyperdb_wasm_database_lock_complete) throw new Error("Database lock completion export is unavailable");
638
+ const status = error === void 0 ? 0 : error instanceof DatabaseLockConflictError ? 1 : 2;
639
+ const pointer = error === void 0 ? 0 : safePointer(module.stringToNewUTF8(errorText(error)), "database lock error pointer");
640
+ if (error !== void 0 && pointer === 0) throw new Error("Database lock error allocation failed");
641
+ module._hyperdb_wasm_database_lock_complete(id, token, status, pointer);
642
+ };
643
+ module.hyperdbWasmDatabaseLockAcquireStart = (id, path, shared) => {
644
+ void Promise.resolve().then(() => {
645
+ if (!module.databaseLockBackend) throw new Error("Host database locking is unavailable");
646
+ return module.databaseLockBackend.acquire(path, shared);
647
+ }).then((token) => complete(id, token), (error) => complete(id, 0, error)).catch((error) => fatal(`Database lock completion failed: ${errorText(error)}`));
648
+ };
649
+ module.hyperdbWasmDatabaseLockReleaseStart = (id, token) => {
650
+ const release = Promise.resolve().then(() => {
651
+ if (!module.databaseLockBackend) throw new Error("Host database locking is unavailable");
652
+ return module.databaseLockBackend.release(token);
653
+ }).then(() => {
654
+ if (id !== 0) complete(id, 0);
655
+ });
656
+ releases.add(release);
657
+ void release.then(() => releases.delete(release), (error) => fatal(`Database lock release failed: ${errorText(error)}`));
658
+ };
659
+ module.hyperdbWasmDatabaseLocksDrain = async () => {
660
+ while (releases.size) await Promise.all([...releases]);
661
+ };
662
+ }
606
663
  function configureWorker(module, transport, configureFilesystem = () => void 0) {
607
664
  const queuedRequests = [];
608
665
  let bindings;
666
+ let failed = false;
667
+ const fatal = (error) => {
668
+ if (failed) return;
669
+ failed = true;
670
+ try {
671
+ module.PThread?.terminateAllThreads();
672
+ } catch {
673
+ }
674
+ transport.postMessage({ type: "fatal", error });
675
+ };
609
676
  const handle = (request) => {
677
+ if (failed) return;
610
678
  if (!bindings) {
611
679
  queuedRequests.push(request);
612
680
  return;
@@ -614,37 +682,48 @@
614
682
  try {
615
683
  const result = bindings.handle(request);
616
684
  const complete = () => {
685
+ if (failed) return;
617
686
  if (request.type === "shutdown" && result.response.result.state === "ok") module.PThread.terminateAllThreads();
618
687
  transport.postMessage(result.response, result.transfer);
619
688
  };
620
- if (result.completion) void result.completion.then(complete, (error) => {
621
- transport.postMessage({ id: request.id, type: "response", result: { state: "error", error: errorText(error) } });
689
+ if (result.completion) void result.completion.then(complete).catch((error) => {
690
+ if (failed) return;
691
+ if (request.type === "shutdown" || request.type === "disconnect" || request.type === "finishQuery") {
692
+ fatal(`engine cleanup failed: ${errorText(error)}`);
693
+ } else {
694
+ transport.postMessage({ id: request.id, type: "response", result: { state: "error", error: errorText(error) } });
695
+ }
622
696
  });
623
697
  else complete();
624
698
  } catch (error) {
699
+ if (failed) return;
625
700
  transport.postMessage({ id: request.id, type: "response", result: { state: "error", error: errorText(error) } });
626
701
  }
627
702
  };
628
703
  transport.onMessage((message) => {
629
704
  if (isEngineRequest(message)) handle(message);
630
705
  });
631
- module.print = (text) => transport.postMessage({ type: "log", text });
632
- module.printErr = (text) => transport.postMessage({ type: "log", text });
706
+ module.print = (text) => transport.postMessage({ type: "log", stream: "stdout", text });
707
+ module.printErr = (text) => transport.postMessage({ type: "log", stream: "stderr", text });
633
708
  module.onRuntimeInitialized = () => {
709
+ if (failed) return;
634
710
  try {
635
711
  configureFilesystem(module);
636
712
  installHttpRuntime(module, transport);
637
713
  installHostFileRuntime(module);
638
714
  installExternalDirectoryRuntime(module, transport);
639
715
  installFilesystemProjectionRuntime(module);
716
+ installDatabaseLockRuntime(module, fatal);
640
717
  bindings = new HyperDBBindings(module);
641
718
  transport.postMessage({ type: "ready" });
642
719
  for (const request of queuedRequests.splice(0)) handle(request);
643
720
  } catch (error) {
644
- transport.postMessage({ type: "fatal", error: `engine initialization failed: ${errorText(error)}` });
721
+ fatal(`engine initialization failed: ${errorText(error)}`);
645
722
  }
646
723
  };
647
- module.onAbort = (reason) => transport.postMessage({ type: "fatal", error: `engine aborted: ${String(reason)}` });
724
+ module.onAbort = (reason) => {
725
+ fatal(`engine aborted: ${String(reason)}`);
726
+ };
648
727
  }
649
728
 
650
729
  // src/browser_external_directory.ts
@@ -960,6 +1039,64 @@
960
1039
  }
961
1040
  };
962
1041
 
1042
+ // src/browser_database_locks.ts
1043
+ var BrowserDatabaseLockBackend = class {
1044
+ locks = /* @__PURE__ */ new Map();
1045
+ requests = /* @__PURE__ */ new Set();
1046
+ nextToken = 1;
1047
+ closed = false;
1048
+ async acquire(path, shared) {
1049
+ if (this.closed) throw new Error("Database lock backend is closed");
1050
+ const normalized = normalizeDatabaseLockPath(path);
1051
+ const manager = globalThis.navigator?.locks;
1052
+ if (!manager || typeof manager.request !== "function") throw new Error("Database locking requires Web Locks");
1053
+ if (this.nextToken > Number.MAX_SAFE_INTEGER) throw new Error("Database lock tokens exhausted");
1054
+ const token = this.nextToken++;
1055
+ return new Promise((resolve, reject) => {
1056
+ let unlock;
1057
+ const held = new Promise((resolveHeld) => {
1058
+ unlock = resolveHeld;
1059
+ });
1060
+ const completion = manager.request(`hyperdb-wasm-database:${normalized}`, {
1061
+ mode: shared ? "shared" : "exclusive",
1062
+ ifAvailable: true
1063
+ }, async (lock) => {
1064
+ if (!lock) throw new DatabaseLockConflictError(normalized);
1065
+ if (this.closed) throw new Error("Database lock backend is closed");
1066
+ this.locks.set(token, { unlock, completion, releasing: false });
1067
+ resolve(token);
1068
+ await held;
1069
+ });
1070
+ this.requests.add(completion);
1071
+ void completion.then(() => {
1072
+ this.requests.delete(completion);
1073
+ }, (error) => {
1074
+ this.requests.delete(completion);
1075
+ reject(error);
1076
+ });
1077
+ });
1078
+ }
1079
+ async release(token) {
1080
+ const lock = this.locks.get(token);
1081
+ if (!lock || lock.releasing) throw new Error("Unknown database lock token");
1082
+ lock.releasing = true;
1083
+ lock.unlock();
1084
+ try {
1085
+ await lock.completion;
1086
+ } finally {
1087
+ this.locks.delete(token);
1088
+ }
1089
+ }
1090
+ async close() {
1091
+ this.closed = true;
1092
+ const releases = [...this.locks].map(([token, lock]) => lock.releasing ? lock.completion : this.release(token));
1093
+ const results = await Promise.allSettled(releases);
1094
+ await Promise.allSettled([...this.requests]);
1095
+ const failures = results.filter((result) => result.status === "rejected").map((result) => result.reason);
1096
+ if (failures.length) throw new AggregateError(failures, "Database lock cleanup failed");
1097
+ }
1098
+ };
1099
+
963
1100
  // src/browser_worker.ts
964
1101
  var configuredEngineUrl = new URL(self.location.href).searchParams.get("engineUrl");
965
1102
  var engineUrl = self.HYPERDB_WASM_ENGINE_URL ?? configuredEngineUrl ?? "hyperdb-wasm.js";
@@ -1020,6 +1157,7 @@
1020
1157
  };
1021
1158
  self.Module = module;
1022
1159
  configureWorker(module, transport, (runtime) => {
1160
+ runtime.databaseLockBackend = new BrowserDatabaseLockBackend();
1023
1161
  runtime.externalDirectoryBackend = new BrowserExternalDirectoryBackend();
1024
1162
  if (!runtime.OPFS) throw new Error("OPFS backend is unavailable");
1025
1163
  runtime.FS.mkdirTree("/mnt/fs");
package/dist/client.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { ConnectionSettings, HyperDBSettings, MountAccessMode } from './api
2
2
  import type { MountSource, SetupProgress, WasmResult, WorkerTransport } from './protocol.js';
3
3
  export declare class HyperDBEngineClient {
4
4
  private readonly onSetupProgress?;
5
+ private readonly onLog?;
5
6
  private readonly transport;
6
7
  private readonly pending;
7
8
  private readonly readyPromise;
@@ -9,9 +10,11 @@ export declare class HyperDBEngineClient {
9
10
  private rejectReady;
10
11
  private nextId;
11
12
  private failed;
13
+ private termination;
12
14
  private readonly unsubscribe;
13
- constructor(transport: WorkerTransport, onSetupProgress?: ((progress: SetupProgress) => void) | undefined);
15
+ constructor(transport: WorkerTransport, onSetupProgress?: ((progress: SetupProgress) => void) | undefined, onLog?: ((stream: "stdout" | "stderr", text: string) => void) | undefined);
14
16
  ready(): Promise<void>;
17
+ get failure(): Error | undefined;
15
18
  initialize(settings: HyperDBSettings): Promise<WasmResult>;
16
19
  connect(settings?: ConnectionSettings): Promise<WasmResult>;
17
20
  disconnect(connection: number): Promise<WasmResult>;
@@ -26,5 +29,6 @@ export declare class HyperDBEngineClient {
26
29
  terminate(): Promise<void>;
27
30
  private request;
28
31
  private onMessage;
32
+ private failAndTerminate;
29
33
  private fail;
30
34
  }
@@ -0,0 +1,15 @@
1
+ /** Host ownership interface; browser workers coordinate, while Node callers provide exclusive access */
2
+ export interface DatabaseLockBackend {
3
+ /** Returns a never-reused positive safe-integer token or rejects with DatabaseLockConflictError on contention */
4
+ acquire(path: string, shared: boolean): Promise<number>;
5
+ /** Releases a positive, backend-local token and waits for host ownership to end */
6
+ release(token: number): Promise<void>;
7
+ /** Stops acquisitions and releases all held locks; repeated closes are allowed */
8
+ close(): Promise<void>;
9
+ }
10
+ /** Identifies contention separately from host or filesystem failures */
11
+ export declare class DatabaseLockConflictError extends Error {
12
+ constructor(path: string, options?: ErrorOptions);
13
+ }
14
+ /** Normalizes an absolute durable file path */
15
+ export declare function normalizeDatabaseLockPath(path: string): string;