@minnowdb/core 0.9.1 → 0.10.1

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.
Files changed (47) hide show
  1. package/dist/engine/auto-store.d.ts +52 -0
  2. package/dist/engine/auto-store.js +157 -0
  3. package/dist/engine/buffered-writer.d.ts +2 -0
  4. package/dist/engine/buffered-writer.js +15 -2
  5. package/dist/engine/client-audit-harness.js +123 -0
  6. package/dist/engine/client.d.ts +55 -6
  7. package/dist/engine/client.js +176 -46
  8. package/dist/engine/database.d.ts +15 -1
  9. package/dist/engine/database.js +1276 -251
  10. package/dist/engine/errors.d.ts +61 -2
  11. package/dist/engine/errors.js +116 -3
  12. package/dist/engine/index.d.ts +1 -0
  13. package/dist/engine/index.js +2 -0
  14. package/dist/engine/live.d.ts +24 -1
  15. package/dist/engine/live.js +33 -9
  16. package/dist/engine/scope-write-set.js +36 -0
  17. package/dist/engine/worker-auto.d.ts +1 -0
  18. package/dist/engine/worker-auto.js +3 -0
  19. package/dist/engine/worker-host.d.ts +2 -1
  20. package/dist/engine/worker-host.js +19 -1
  21. package/dist/engine/worker-server.d.ts +53 -1
  22. package/dist/engine/worker-server.js +122 -19
  23. package/dist/engine/worker-store-auto.js +36 -0
  24. package/dist/engine/worker-store-opfs.js +3 -2
  25. package/dist/engine/write-coordinator.js +44 -2
  26. package/dist/storage/indexeddb-audit-helpers.js +269 -0
  27. package/dist/storage/indexeddb.js +599 -374
  28. package/dist/storage/opfs/coordination-helpers.js +54 -0
  29. package/dist/storage/opfs/index.d.ts +1 -1
  30. package/dist/storage/opfs/index.js +3 -2
  31. package/dist/storage/opfs/leader.js +243 -17
  32. package/dist/storage/opfs/power-loss-model.js +62 -0
  33. package/dist/storage/opfs/rpc.js +24 -43
  34. package/dist/storage/opfs/store.d.ts +32 -0
  35. package/dist/storage/opfs/store.js +531 -65
  36. package/dist/storage/toolkit/record-core.js +67 -38
  37. package/dist/storage/toolkit/wal.js +16 -0
  38. package/dist/storage/toolkit/wire.d.ts +1 -1
  39. package/dist/storage/toolkit/wire.js +4 -4
  40. package/dist/storage/types.d.ts +31 -10
  41. package/dist/storage/types.js +27 -16
  42. package/dist/testing/opfs-shim.js +14 -6
  43. package/dist/transactions/index.d.ts +19 -0
  44. package/dist/transactions/index.js +99 -25
  45. package/dist/worker-protocol/index.d.ts +50 -2
  46. package/dist/worker-protocol/index.js +106 -4
  47. package/package.json +7 -2
@@ -1,5 +1,5 @@
1
1
  import { dateIsoString, dateMilliseconds } from "../date-value.js";
2
- import { assertTransactionArtifactBatchLimits, assertTransactionArtifactJournalLimits, GarbageCollectionJobConflictError, LeaseConflictError, LeaseExpiredError, MAX_LEVEL_ZERO_SEGMENTS, MAX_MANIFEST_BLOCK_PRESENCE_IDS, MAX_LEASE_TTL_MS, MAX_TRANSACTION_STAGE_BLOCKS, MAX_TRANSACTION_STAGE_BYTES, MAX_TRANSACTION_STAGE_SEGMENTS, MAX_TRANSACTION_COMMIT_DELTA_BYTES, MAX_TRANSACTION_COMMIT_DELTA_ENTRIES, transactionCommitDeltaRetainedBytes, SnapshotManifestMissingError, TransactionRecordConflictError, WriteConflictError } from "../storage/types.js";
2
+ import { assertTransactionArtifactBatchLimits, GarbageCollectionJobConflictError, LeaseConflictError, LeaseExpiredError, MAX_LEVEL_ZERO_SEGMENTS, MAX_MANIFEST_BLOCK_PRESENCE_IDS, MAX_LEASE_TTL_MS, MAX_TRANSACTION_STAGE_BLOCKS, MAX_TRANSACTION_STAGE_BYTES, MAX_TRANSACTION_STAGE_SEGMENTS, MAX_TRANSACTION_COMMIT_DELTA_BYTES, MAX_TRANSACTION_COMMIT_DELTA_ENTRIES, transactionCommitDeltaRetainedBytes, SnapshotManifestMissingError, TransactionRecordConflictError, UnknownOutcomeError, WriteConflictError } from "../storage/types.js";
3
3
  const DEFAULT_TRANSACTION_TTL_MS = 3e4;
4
4
  class TransactionClosedError extends Error {
5
5
  transactionId;
@@ -118,6 +118,7 @@ class DatabaseTransaction {
118
118
  #deferredBlocks = [];
119
119
  #deferredSegments = [];
120
120
  #knownSegments = /* @__PURE__ */ new Map();
121
+ #stagedSegmentsView;
121
122
  #persisted;
122
123
  #coalesceArtifacts;
123
124
  #snapshotLease;
@@ -156,14 +157,56 @@ class DatabaseTransaction {
156
157
  get snapshotVersion() {
157
158
  return this.#record.snapshotVersion;
158
159
  }
160
+ #journalView;
161
+ #journal() {
162
+ const view = this.#journalView;
163
+ if (view?.record === this.#record && view.deferredBlocks === this.#deferredBlocks.length && view.deferredSegments === this.#deferredSegments.length) {
164
+ return view;
165
+ }
166
+ const journaledBlocks = view?.record === this.#record ? view.journaledBlocks : new Set(this.#record.pendingBlockIds);
167
+ const next = {
168
+ record: this.#record,
169
+ deferredBlocks: this.#deferredBlocks.length,
170
+ deferredSegments: this.#deferredSegments.length,
171
+ blockIds: [...this.#record.pendingBlockIds, ...this.#deferredBlocks.map((block) => block.id)],
172
+ segmentIds: [
173
+ ...this.#record.pendingSegmentIds,
174
+ ...this.#deferredSegments.map((segment) => segment.id)
175
+ ],
176
+ journaledBlocks
177
+ };
178
+ this.#journalView = next;
179
+ return next;
180
+ }
181
+ #journalAppended(previous, blockIds) {
182
+ const view = this.#journalView;
183
+ if (view?.record !== previous || this.#record.pendingBlockIds.length !== previous.pendingBlockIds.length + blockIds.length) {
184
+ this.#journalView = void 0;
185
+ return;
186
+ }
187
+ for (const id of blockIds)
188
+ view.journaledBlocks.add(id);
189
+ view.record = this.#record;
190
+ view.deferredBlocks = -1;
191
+ }
159
192
  get pendingBlockIds() {
160
- return [...this.#record.pendingBlockIds, ...this.#deferredBlocks.map((block) => block.id)];
193
+ return [...this.#journal().blockIds];
161
194
  }
162
195
  get pendingSegmentIds() {
163
- return [
164
- ...this.#record.pendingSegmentIds,
165
- ...this.#deferredSegments.map((segment) => segment.id)
166
- ];
196
+ return [...this.#journal().segmentIds];
197
+ }
198
+ hasPendingBlock(id) {
199
+ return this.#journal().journaledBlocks.has(id) || this.#deferredBlocks.some((block) => block.id === id);
200
+ }
201
+ get stagedSegments() {
202
+ this.#stagedSegmentsView ??= [...this.#knownSegments.values()];
203
+ return this.#stagedSegmentsView;
204
+ }
205
+ get pendingBlockCount() {
206
+ return this.#record.pendingBlockIds.length + this.#deferredBlocks.length;
207
+ }
208
+ get pendingSegmentCount() {
209
+ return this.#record.pendingSegmentIds.length + this.#deferredSegments.length;
167
210
  }
168
211
  get deferredSegments() {
169
212
  return structuredClone(this.#deferredSegments);
@@ -190,8 +233,13 @@ class DatabaseTransaction {
190
233
  this.#assertActive();
191
234
  await this.#renewOwnership(true);
192
235
  }
236
+ async renewIfDue() {
237
+ if (this.#record.status !== "active")
238
+ return;
239
+ await this.#renewOwnership(false);
240
+ }
193
241
  get stagedWorkCount() {
194
- return this.pendingBlockIds.length + this.pendingSegmentIds.length + this.#uniqueKeyChanges.length + this.#ftsChanges.size + this.#compactionSourceBlockIds.size;
242
+ return this.pendingBlockCount + this.pendingSegmentCount + this.#uniqueKeyChanges.length + this.#ftsChanges.size + this.#compactionSourceBlockIds.size;
195
243
  }
196
244
  checkpoint() {
197
245
  this.#assertActive();
@@ -218,9 +266,9 @@ class DatabaseTransaction {
218
266
  bytes = checkpointByteSum(bytes, 16 + value.length * 2);
219
267
  };
220
268
  addString(this.id);
221
- for (const id of this.pendingBlockIds)
269
+ for (const id of this.#journal().blockIds)
222
270
  addString(id);
223
- for (const id of this.pendingSegmentIds)
271
+ for (const id of this.#journal().segmentIds)
224
272
  addString(id);
225
273
  for (const change of this.#uniqueKeyChanges) {
226
274
  addString(change.tableId);
@@ -258,9 +306,11 @@ class DatabaseTransaction {
258
306
  const segmentPrefix = checkpoint.pendingSegmentIds;
259
307
  const retainedBlocks = new Set(blockPrefix);
260
308
  const retainedSegments = new Set(segmentPrefix);
261
- const currentBlockIds = this.pendingBlockIds;
262
- const currentSegmentIds = this.pendingSegmentIds;
263
- if (blockPrefix.some((id) => !currentBlockIds.includes(id)) || segmentPrefix.some((id) => !currentSegmentIds.includes(id))) {
309
+ const currentBlockIds = this.#journal().blockIds;
310
+ const currentSegmentIds = this.#journal().segmentIds;
311
+ const currentBlocks = new Set(currentBlockIds);
312
+ const currentSegments = new Set(currentSegmentIds);
313
+ if (blockPrefix.some((id) => !currentBlocks.has(id)) || segmentPrefix.some((id) => !currentSegments.has(id))) {
264
314
  throw new TypeError("A transaction checkpoint is no longer reachable");
265
315
  }
266
316
  const removedBlockIds = currentBlockIds.filter((id) => !retainedBlocks.has(id));
@@ -290,6 +340,7 @@ class DatabaseTransaction {
290
340
  }
291
341
  for (const id of removedSegmentIds)
292
342
  this.#knownSegments.delete(id);
343
+ this.#stagedSegmentsView = void 0;
293
344
  this.#uniqueKeyChanges.splice(0);
294
345
  this.#ftsChanges.clear();
295
346
  this.#commitDeltaBytes = 0;
@@ -319,20 +370,23 @@ class DatabaseTransaction {
319
370
  const deferred = this.#deferredBlocks.find((block) => block.id === id);
320
371
  if (deferred !== void 0)
321
372
  return new Uint8Array(deferred.bytes);
322
- if (this.#record.pendingBlockIds.includes(id))
373
+ if (this.#journal().journaledBlocks.has(id))
323
374
  return this.store.getBlock(id);
324
375
  return (await this.snapshot()).getBlock(id);
325
376
  }
326
377
  #assertProspectiveArtifactJournal(blocks, segments) {
327
- const blockIds = new Set(this.pendingBlockIds);
328
- const segmentIds = new Set(this.pendingSegmentIds);
378
+ const journal = this.#journal();
379
+ const blockIds = /* @__PURE__ */ new Set();
380
+ const deferredBlocks = new Set(this.#deferredBlocks.map((block) => block.id));
329
381
  for (const block of blocks) {
330
382
  if (block.id.length === 0)
331
383
  throw new TypeError("Block ID cannot be empty");
332
- if (blockIds.has(block.id))
384
+ if (journal.journaledBlocks.has(block.id) || deferredBlocks.has(block.id) || blockIds.has(block.id)) {
333
385
  throw new Error(`Block already exists: ${block.id}`);
386
+ }
334
387
  blockIds.add(block.id);
335
388
  }
389
+ const segmentIds = new Set(journal.segmentIds);
336
390
  for (const segment of segments) {
337
391
  if (segment.id.length === 0)
338
392
  throw new TypeError("Segment ID cannot be empty");
@@ -340,7 +394,6 @@ class DatabaseTransaction {
340
394
  throw new Error(`Segment already exists: ${segment.id}`);
341
395
  segmentIds.add(segment.id);
342
396
  }
343
- assertTransactionArtifactJournalLimits([...blockIds], [...segmentIds]);
344
397
  }
345
398
  async #stageArtifactBatch(blocks, segments) {
346
399
  if (blocks.length === 0 && segments.length === 0)
@@ -348,6 +401,7 @@ class DatabaseTransaction {
348
401
  assertTransactionArtifactBatchLimits(blocks, segments);
349
402
  await this.#ensurePersisted();
350
403
  await this.#renewOwnership();
404
+ const previous = this.#record;
351
405
  try {
352
406
  this.#record = await this.store.stageTransactionArtifacts({
353
407
  transactionId: this.id,
@@ -357,8 +411,9 @@ class DatabaseTransaction {
357
411
  updatedAt: dateIsoString(this.now())
358
412
  });
359
413
  } catch (error) {
360
- await this.#recoverStagedAcknowledgement(error, blocks, segments);
414
+ this.#record = await this.#recoverStagedAcknowledgement(error, blocks, segments);
361
415
  }
416
+ this.#journalAppended(previous, blocks.map((block) => block.id));
362
417
  }
363
418
  async stageBlock(id, bytes) {
364
419
  return this.stageBlocks([{ id, bytes }]);
@@ -376,7 +431,7 @@ class DatabaseTransaction {
376
431
  throw new Error(`Segment ${segment.id} belongs to another transaction`);
377
432
  }
378
433
  }
379
- const ordinalBase = this.pendingSegmentIds.length;
434
+ const ordinalBase = this.pendingSegmentCount;
380
435
  const ordered = segments.map((segment, index) => ({
381
436
  ...segment,
382
437
  commitOrdinal: ordinalBase + index
@@ -392,6 +447,7 @@ class DatabaseTransaction {
392
447
  this.#deferredSegments.push(...structuredClone(ordered));
393
448
  for (const segment of ordered)
394
449
  this.#knownSegments.set(segment.id, structuredClone(segment));
450
+ this.#stagedSegmentsView = void 0;
395
451
  for (const segment of ordered)
396
452
  this.#changedTableIds.add(segment.tableId);
397
453
  return;
@@ -402,6 +458,7 @@ class DatabaseTransaction {
402
458
  }
403
459
  for (const segment of ordered)
404
460
  this.#knownSegments.set(segment.id, structuredClone(segment));
461
+ this.#stagedSegmentsView = void 0;
405
462
  for (const segment of ordered)
406
463
  this.#changedTableIds.add(segment.tableId);
407
464
  }
@@ -412,6 +469,17 @@ class DatabaseTransaction {
412
469
  throw new Error(`Segment ${segment.id} belongs to another transaction`);
413
470
  }
414
471
  }
472
+ if (this.#deferredBlocks.length > 0 || this.#deferredSegments.length > 0) {
473
+ const combined = transactionArtifactBatches([...this.#deferredBlocks, ...blocks], [...this.#deferredSegments, ...segments]);
474
+ if (combined.length === 1) {
475
+ blocks = [...this.#deferredBlocks, ...blocks];
476
+ segments = [...this.#deferredSegments, ...segments];
477
+ this.#deferredBlocks.length = 0;
478
+ this.#deferredSegments.length = 0;
479
+ } else {
480
+ await this.#persistDeferredArtifacts();
481
+ }
482
+ }
415
483
  const pendingBlockIds = new Set(this.#record.pendingBlockIds);
416
484
  const pendingSegmentIds = new Set(this.#record.pendingSegmentIds);
417
485
  await this.#assertRetainedArtifactsUnchanged(blocks.filter((block) => pendingBlockIds.has(block.id)), segments.filter((segment) => pendingSegmentIds.has(segment.id)));
@@ -446,6 +514,7 @@ class DatabaseTransaction {
446
514
  const ftsChanges = this.#materializedFtsChanges();
447
515
  for (const segment of ordered)
448
516
  this.#knownSegments.set(segment.id, structuredClone(segment));
517
+ this.#stagedSegmentsView = void 0;
449
518
  for (const segment of ordered)
450
519
  this.#changedTableIds.add(segment.tableId);
451
520
  try {
@@ -496,7 +565,6 @@ class DatabaseTransaction {
496
565
  if (additions.some((id) => id.length === 0)) {
497
566
  throw new TypeError("Block ID cannot be empty");
498
567
  }
499
- assertTransactionArtifactJournalLimits([...this.#record.pendingBlockIds, ...additions], this.#record.pendingSegmentIds);
500
568
  for (const id of additions) {
501
569
  if (await this.store.getBlock(id) === void 0) {
502
570
  throw new Error(`Cannot stage a missing existing block: ${id}`);
@@ -530,10 +598,6 @@ class DatabaseTransaction {
530
598
  if (record.commitOrdinal !== this.#record.pendingSegmentIds.length) {
531
599
  throw new Error(`Existing segment ${segmentId} is not the next journal ordinal`);
532
600
  }
533
- assertTransactionArtifactJournalLimits(this.#record.pendingBlockIds, [
534
- ...this.#record.pendingSegmentIds,
535
- segmentId
536
- ]);
537
601
  this.#registerLevelZeroSegments([record]);
538
602
  this.#changedTableIds.add(record.tableId);
539
603
  await this.#ensurePersisted();
@@ -821,6 +885,7 @@ class DatabaseTransaction {
821
885
  this.#deferredBlocks.length = 0;
822
886
  this.#deferredSegments.length = 0;
823
887
  this.#knownSegments.clear();
888
+ this.#stagedSegmentsView = void 0;
824
889
  this.#record = { ...this.#record, status: "aborted", updatedAt: dateIsoString(this.now()) };
825
890
  this.#stopHeartbeat();
826
891
  await this.#releaseSnapshotLease().catch(() => void 0);
@@ -852,7 +917,14 @@ class DatabaseTransaction {
852
917
  if (error instanceof SnapshotManifestMissingError) {
853
918
  throw new WriteConflictError(this.#record.snapshotVersion, await this.store.getCurrentManifestVersion());
854
919
  }
855
- throw error;
920
+ const persisted = await this.store.getTransaction(this.id).catch(() => void 0);
921
+ if (persisted?.status !== "active" || persisted.ownerId !== this.#record.ownerId || persisted.revision !== this.#record.revision || persisted.snapshotVersion !== this.#record.snapshotVersion || persisted.pendingBlockIds.length !== 0 || persisted.pendingSegmentIds.length !== 0) {
922
+ throw error;
923
+ }
924
+ this.#record = persisted;
925
+ this.#persisted = true;
926
+ if (!(error instanceof UnknownOutcomeError))
927
+ throw error;
856
928
  }
857
929
  this.#persisted = true;
858
930
  void this.#releaseSnapshotLease().catch(() => void 0);
@@ -1010,6 +1082,8 @@ class DatabaseTransaction {
1010
1082
  })) {
1011
1083
  this.#record = persisted;
1012
1084
  this.#persisted = true;
1085
+ if (error instanceof UnknownOutcomeError)
1086
+ return persisted;
1013
1087
  }
1014
1088
  }
1015
1089
  } catch {
@@ -40,15 +40,63 @@ export type RpcResponse = {
40
40
  };
41
41
  /**
42
42
  * A structured-clone-safe error carrying the constructor name and every cloneable own property,
43
- * so typed engine errors survive the channel and can be rehydrated for instanceof checks.
43
+ * so typed engine errors survive the channel and can be rehydrated for instanceof checks. The
44
+ * `cause` chain travels to a bounded depth, and a platform exception keeps its `DOMException`
45
+ * identity, so a quota refusal is still a `DOMException` named `QuotaExceededError` on the
46
+ * other side.
44
47
  */
45
48
  export interface SerializedError {
46
49
  name: string;
47
50
  message: string;
48
51
  stack?: string;
49
52
  props?: Record<string, unknown>;
53
+ /** True for platform exceptions, which rehydrate as `DOMException` rather than `Error`. */
54
+ domException?: true;
55
+ cause?: SerializedError;
50
56
  }
51
- export declare function serializeError(error: unknown): SerializedError;
57
+ /** How many `cause` links a serialized error carries before the chain is cut. */
58
+ export declare const MAX_SERIALIZED_CAUSE_DEPTH = 8;
59
+ export declare function serializeError(error: unknown, depth?: number): SerializedError;
60
+ /** Constructors whose prototype a rehydrated error adopts, keyed by the `name` they carry. */
61
+ export type ErrorRegistry = ReadonlyMap<string, new (...args: never[]) => Error>;
62
+ /**
63
+ * Rebuilds an `Error` from its serialized form: a registry hit adopts that class's prototype,
64
+ * a platform exception becomes a `DOMException`, a built-in subclass name (`TypeError`) keeps
65
+ * its built-in prototype, and anything else is a plain `Error` carrying the original name.
66
+ * Own properties and the `cause` chain are restored; the stack stays the worker's.
67
+ */
68
+ export declare function rehydrateError(serialized: SerializedError, registry: ErrorRegistry): Error;
69
+ /** The event-frame handle id that carries worker diagnostics. Never issued for a real handle. */
70
+ export declare const WORKER_DIAGNOSTIC_HANDLE_ID = "$worker";
71
+ export type WorkerErrorKind =
72
+ /** A script error nobody caught, in a timer, a channel listener, or another callback. */
73
+ "uncaught"
74
+ /** A promise rejected with no handler attached. */
75
+ | "unhandled-rejection"
76
+ /** A frame the worker could not deserialize. */
77
+ | "messageerror"
78
+ /** Background maintenance: a checkpoint, cleanup, or collection step that failed. */
79
+ | "maintenance"
80
+ /** Multi-tab coordination: an election, handover, or served request that failed. */
81
+ | "coordination";
82
+ export interface WorkerErrorReport {
83
+ kind: WorkerErrorKind;
84
+ /** Where in the worker it happened, for a log line: "opfs election", "auto collection". */
85
+ context: string;
86
+ error: SerializedError;
87
+ }
88
+ export declare function workerErrorEvent(report: WorkerErrorReport): RpcResponse;
89
+ /**
90
+ * How often the worker tells the client that a call is still being worked on. The client's
91
+ * request deadline counts silence, not wall time, so a large batch write is not cut off — and
92
+ * its client left permanently failed — merely for being large.
93
+ */
94
+ export declare const WORKER_KEEPALIVE_INTERVAL_MS = 5000;
95
+ /** The shortest pace a client may ask for; below this the reports would be the load. */
96
+ export declare const MIN_WORKER_KEEPALIVE_INTERVAL_MS = 100;
97
+ /** A keepalive for one in-flight request: the worker is alive and still holds it. */
98
+ export declare function workerKeepaliveEvent(requestId: string): RpcResponse;
99
+ export declare function isWorkerErrorReport(value: unknown): value is WorkerErrorReport;
52
100
  export declare function rpcResult(requestId: string, result: unknown): RpcResponse;
53
101
  export declare function rpcFailure(requestId: string, error: unknown): RpcResponse;
54
102
  export declare function rpcEvent(handleId: string, event: string, payload: unknown): RpcResponse;
@@ -1,12 +1,21 @@
1
1
  const protocolVersion = 7;
2
2
  const MAX_DATABASE_RPC_IN_FLIGHT = 256;
3
- function serializeError(error) {
3
+ const MAX_SERIALIZED_CAUSE_DEPTH = 8;
4
+ function serializeError(error, depth = 0) {
5
+ if (typeof DOMException !== "undefined" && error instanceof DOMException) {
6
+ return {
7
+ name: error.name,
8
+ message: error.message,
9
+ domException: true,
10
+ ...typeof error.stack === "string" ? { stack: error.stack } : {}
11
+ };
12
+ }
4
13
  if (!(error instanceof Error)) {
5
14
  return { name: "Error", message: String(error) };
6
15
  }
7
16
  const props = {};
8
17
  for (const key of Object.keys(error)) {
9
- if (key === "name" || key === "message" || key === "stack")
18
+ if (key === "name" || key === "message" || key === "stack" || key === "cause")
10
19
  continue;
11
20
  const value = error[key];
12
21
  try {
@@ -15,13 +24,98 @@ function serializeError(error) {
15
24
  } catch {
16
25
  }
17
26
  }
27
+ const cause = error.cause === void 0 || depth >= MAX_SERIALIZED_CAUSE_DEPTH ? void 0 : serializeError(error.cause, depth + 1);
18
28
  return {
19
29
  name: error.name,
20
30
  message: error.message,
21
31
  ...error.stack === void 0 ? {} : { stack: error.stack },
22
- ...Object.keys(props).length === 0 ? {} : { props }
32
+ ...Object.keys(props).length === 0 ? {} : { props },
33
+ ...cause === void 0 ? {} : { cause }
23
34
  };
24
35
  }
36
+ const builtinErrorNames = /* @__PURE__ */ new Set([
37
+ "TypeError",
38
+ "RangeError",
39
+ "SyntaxError",
40
+ "ReferenceError",
41
+ "EvalError",
42
+ "URIError",
43
+ "AggregateError"
44
+ ]);
45
+ function rehydrateError(serialized, registry) {
46
+ const cause = serialized.cause === void 0 ? void 0 : rehydrateError(serialized.cause, registry);
47
+ if (serialized.domException === true) {
48
+ const exception = new DOMException(serialized.message, serialized.name);
49
+ if (cause !== void 0) {
50
+ Object.defineProperty(exception, "cause", {
51
+ value: cause,
52
+ writable: true,
53
+ configurable: true
54
+ });
55
+ }
56
+ return exception;
57
+ }
58
+ const constructor = registry.get(serialized.name);
59
+ let error;
60
+ if (constructor !== void 0) {
61
+ error = Object.create(constructor.prototype);
62
+ } else if (builtinErrorNames.has(serialized.name)) {
63
+ const builtin = globalThis[serialized.name];
64
+ error = typeof builtin === "function" ? Object.create(builtin.prototype) : new Error(serialized.message);
65
+ } else {
66
+ error = new Error(serialized.message);
67
+ }
68
+ Object.defineProperty(error, "message", {
69
+ value: serialized.message,
70
+ writable: true,
71
+ configurable: true
72
+ });
73
+ Object.defineProperty(error, "name", {
74
+ value: serialized.name,
75
+ writable: true,
76
+ configurable: true
77
+ });
78
+ if (serialized.stack !== void 0) {
79
+ Object.defineProperty(error, "stack", {
80
+ value: serialized.stack,
81
+ writable: true,
82
+ configurable: true
83
+ });
84
+ }
85
+ if (cause !== void 0) {
86
+ Object.defineProperty(error, "cause", { value: cause, writable: true, configurable: true });
87
+ }
88
+ if (serialized.props !== void 0)
89
+ Object.assign(error, serialized.props);
90
+ return error;
91
+ }
92
+ const WORKER_DIAGNOSTIC_HANDLE_ID = "$worker";
93
+ function workerErrorEvent(report) {
94
+ return rpcEvent(WORKER_DIAGNOSTIC_HANDLE_ID, "error", report);
95
+ }
96
+ const WORKER_KEEPALIVE_INTERVAL_MS = 5e3;
97
+ const MIN_WORKER_KEEPALIVE_INTERVAL_MS = 100;
98
+ function workerKeepaliveEvent(requestId) {
99
+ return {
100
+ version: protocolVersion,
101
+ requestId,
102
+ kind: "rpc-event",
103
+ handleId: WORKER_DIAGNOSTIC_HANDLE_ID,
104
+ event: "keepalive",
105
+ payload: null
106
+ };
107
+ }
108
+ function isWorkerErrorReport(value) {
109
+ if (typeof value !== "object" || value === null)
110
+ return false;
111
+ const report = value;
112
+ if (typeof report.kind !== "string" || typeof report.context !== "string")
113
+ return false;
114
+ if (typeof report.error !== "object" || report.error === null)
115
+ return false;
116
+ const error = report.error;
117
+ return typeof error.name === "string" && typeof error.message === "string";
118
+ }
25
119
  function rpcResult(requestId, result) {
26
120
  return { version: protocolVersion, requestId, kind: "rpc-result", result };
27
121
  }
@@ -69,11 +163,19 @@ function parseRpcResponse(value) {
69
163
  }
70
164
  export {
71
165
  MAX_DATABASE_RPC_IN_FLIGHT,
166
+ MAX_SERIALIZED_CAUSE_DEPTH,
167
+ MIN_WORKER_KEEPALIVE_INTERVAL_MS,
168
+ WORKER_DIAGNOSTIC_HANDLE_ID,
169
+ WORKER_KEEPALIVE_INTERVAL_MS,
170
+ isWorkerErrorReport,
72
171
  parseRpcRequest,
73
172
  parseRpcResponse,
74
173
  protocolVersion,
174
+ rehydrateError,
75
175
  rpcEvent,
76
176
  rpcFailure,
77
177
  rpcResult,
78
- serializeError
178
+ serializeError,
179
+ workerErrorEvent,
180
+ workerKeepaliveEvent
79
181
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minnowdb/core",
3
- "version": "0.9.1",
3
+ "version": "0.10.1",
4
4
  "description": "A columnar SQL database for the browser: PostgreSQL-style SQL over durable IndexedDB or OPFS data, with no server or WebAssembly module.",
5
5
  "license": "MIT",
6
6
  "author": "Eric Wilhite",
@@ -29,7 +29,8 @@
29
29
  "./dist/engine/worker.js",
30
30
  "./dist/engine/worker-indexeddb.js",
31
31
  "./dist/engine/worker-opfs.js",
32
- "./dist/engine/worker-memory.js"
32
+ "./dist/engine/worker-memory.js",
33
+ "./dist/engine/worker-auto.js"
33
34
  ],
34
35
  "scripts": {
35
36
  "prepack": "node ../../scripts/prepare-package.mjs --strip-comments"
@@ -79,6 +80,10 @@
79
80
  "types": "./dist/engine/worker-memory.d.ts",
80
81
  "default": "./dist/engine/worker-memory.js"
81
82
  },
83
+ "./worker/auto": {
84
+ "types": "./dist/engine/worker-auto.d.ts",
85
+ "default": "./dist/engine/worker-auto.js"
86
+ },
82
87
  "./worker-host": {
83
88
  "types": "./dist/engine/worker-host.d.ts",
84
89
  "default": "./dist/engine/worker-host.js"