@minnowdb/core 0.9.0 → 0.10.0
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/engine/auto-store.d.ts +40 -0
- package/dist/engine/auto-store.js +115 -0
- package/dist/engine/buffered-writer.d.ts +2 -0
- package/dist/engine/buffered-writer.js +15 -2
- package/dist/engine/client.d.ts +55 -6
- package/dist/engine/client.js +155 -40
- package/dist/engine/database.d.ts +15 -1
- package/dist/engine/database.js +1085 -227
- package/dist/engine/errors.d.ts +61 -2
- package/dist/engine/errors.js +116 -3
- package/dist/engine/index.d.ts +1 -0
- package/dist/engine/index.js +2 -0
- package/dist/engine/live.d.ts +24 -1
- package/dist/engine/live.js +33 -9
- package/dist/engine/scope-write-set.js +36 -0
- package/dist/engine/worker-auto.d.ts +1 -0
- package/dist/engine/worker-auto.js +3 -0
- package/dist/engine/worker-host.d.ts +2 -1
- package/dist/engine/worker-host.js +17 -1
- package/dist/engine/worker-server.d.ts +53 -1
- package/dist/engine/worker-server.js +118 -14
- package/dist/engine/worker-store-auto.js +36 -0
- package/dist/engine/worker-store-opfs.js +3 -2
- package/dist/engine/write-coordinator.js +24 -2
- package/dist/storage/indexeddb.js +494 -207
- package/dist/storage/opfs/leader.js +201 -14
- package/dist/storage/opfs/rpc.js +23 -43
- package/dist/storage/opfs/store.d.ts +20 -0
- package/dist/storage/opfs/store.js +501 -70
- package/dist/storage/toolkit/record-core.js +67 -38
- package/dist/storage/toolkit/wire.d.ts +1 -1
- package/dist/storage/toolkit/wire.js +1 -1
- package/dist/storage/types.d.ts +29 -8
- package/dist/storage/types.js +27 -16
- package/dist/testing/opfs-shim.js +14 -6
- package/dist/transactions/index.d.ts +12 -0
- package/dist/transactions/index.js +83 -23
- package/dist/worker-protocol/index.d.ts +50 -2
- package/dist/worker-protocol/index.js +106 -4
- package/package.json +7 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dateIsoString, dateMilliseconds } from "../date-value.js";
|
|
2
|
-
import { assertTransactionArtifactBatchLimits,
|
|
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, 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.#
|
|
193
|
+
return [...this.#journal().blockIds];
|
|
161
194
|
}
|
|
162
195
|
get pendingSegmentIds() {
|
|
163
|
-
return [
|
|
164
|
-
|
|
165
|
-
|
|
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);
|
|
@@ -191,7 +234,7 @@ class DatabaseTransaction {
|
|
|
191
234
|
await this.#renewOwnership(true);
|
|
192
235
|
}
|
|
193
236
|
get stagedWorkCount() {
|
|
194
|
-
return this.
|
|
237
|
+
return this.pendingBlockCount + this.pendingSegmentCount + this.#uniqueKeyChanges.length + this.#ftsChanges.size + this.#compactionSourceBlockIds.size;
|
|
195
238
|
}
|
|
196
239
|
checkpoint() {
|
|
197
240
|
this.#assertActive();
|
|
@@ -218,9 +261,9 @@ class DatabaseTransaction {
|
|
|
218
261
|
bytes = checkpointByteSum(bytes, 16 + value.length * 2);
|
|
219
262
|
};
|
|
220
263
|
addString(this.id);
|
|
221
|
-
for (const id of this.
|
|
264
|
+
for (const id of this.#journal().blockIds)
|
|
222
265
|
addString(id);
|
|
223
|
-
for (const id of this.
|
|
266
|
+
for (const id of this.#journal().segmentIds)
|
|
224
267
|
addString(id);
|
|
225
268
|
for (const change of this.#uniqueKeyChanges) {
|
|
226
269
|
addString(change.tableId);
|
|
@@ -258,9 +301,11 @@ class DatabaseTransaction {
|
|
|
258
301
|
const segmentPrefix = checkpoint.pendingSegmentIds;
|
|
259
302
|
const retainedBlocks = new Set(blockPrefix);
|
|
260
303
|
const retainedSegments = new Set(segmentPrefix);
|
|
261
|
-
const currentBlockIds = this.
|
|
262
|
-
const currentSegmentIds = this.
|
|
263
|
-
|
|
304
|
+
const currentBlockIds = this.#journal().blockIds;
|
|
305
|
+
const currentSegmentIds = this.#journal().segmentIds;
|
|
306
|
+
const currentBlocks = new Set(currentBlockIds);
|
|
307
|
+
const currentSegments = new Set(currentSegmentIds);
|
|
308
|
+
if (blockPrefix.some((id) => !currentBlocks.has(id)) || segmentPrefix.some((id) => !currentSegments.has(id))) {
|
|
264
309
|
throw new TypeError("A transaction checkpoint is no longer reachable");
|
|
265
310
|
}
|
|
266
311
|
const removedBlockIds = currentBlockIds.filter((id) => !retainedBlocks.has(id));
|
|
@@ -290,6 +335,7 @@ class DatabaseTransaction {
|
|
|
290
335
|
}
|
|
291
336
|
for (const id of removedSegmentIds)
|
|
292
337
|
this.#knownSegments.delete(id);
|
|
338
|
+
this.#stagedSegmentsView = void 0;
|
|
293
339
|
this.#uniqueKeyChanges.splice(0);
|
|
294
340
|
this.#ftsChanges.clear();
|
|
295
341
|
this.#commitDeltaBytes = 0;
|
|
@@ -319,20 +365,23 @@ class DatabaseTransaction {
|
|
|
319
365
|
const deferred = this.#deferredBlocks.find((block) => block.id === id);
|
|
320
366
|
if (deferred !== void 0)
|
|
321
367
|
return new Uint8Array(deferred.bytes);
|
|
322
|
-
if (this.#
|
|
368
|
+
if (this.#journal().journaledBlocks.has(id))
|
|
323
369
|
return this.store.getBlock(id);
|
|
324
370
|
return (await this.snapshot()).getBlock(id);
|
|
325
371
|
}
|
|
326
372
|
#assertProspectiveArtifactJournal(blocks, segments) {
|
|
327
|
-
const
|
|
328
|
-
const
|
|
373
|
+
const journal = this.#journal();
|
|
374
|
+
const blockIds = /* @__PURE__ */ new Set();
|
|
375
|
+
const deferredBlocks = new Set(this.#deferredBlocks.map((block) => block.id));
|
|
329
376
|
for (const block of blocks) {
|
|
330
377
|
if (block.id.length === 0)
|
|
331
378
|
throw new TypeError("Block ID cannot be empty");
|
|
332
|
-
if (blockIds.has(block.id))
|
|
379
|
+
if (journal.journaledBlocks.has(block.id) || deferredBlocks.has(block.id) || blockIds.has(block.id)) {
|
|
333
380
|
throw new Error(`Block already exists: ${block.id}`);
|
|
381
|
+
}
|
|
334
382
|
blockIds.add(block.id);
|
|
335
383
|
}
|
|
384
|
+
const segmentIds = new Set(journal.segmentIds);
|
|
336
385
|
for (const segment of segments) {
|
|
337
386
|
if (segment.id.length === 0)
|
|
338
387
|
throw new TypeError("Segment ID cannot be empty");
|
|
@@ -340,7 +389,6 @@ class DatabaseTransaction {
|
|
|
340
389
|
throw new Error(`Segment already exists: ${segment.id}`);
|
|
341
390
|
segmentIds.add(segment.id);
|
|
342
391
|
}
|
|
343
|
-
assertTransactionArtifactJournalLimits([...blockIds], [...segmentIds]);
|
|
344
392
|
}
|
|
345
393
|
async #stageArtifactBatch(blocks, segments) {
|
|
346
394
|
if (blocks.length === 0 && segments.length === 0)
|
|
@@ -348,6 +396,7 @@ class DatabaseTransaction {
|
|
|
348
396
|
assertTransactionArtifactBatchLimits(blocks, segments);
|
|
349
397
|
await this.#ensurePersisted();
|
|
350
398
|
await this.#renewOwnership();
|
|
399
|
+
const previous = this.#record;
|
|
351
400
|
try {
|
|
352
401
|
this.#record = await this.store.stageTransactionArtifacts({
|
|
353
402
|
transactionId: this.id,
|
|
@@ -359,6 +408,7 @@ class DatabaseTransaction {
|
|
|
359
408
|
} catch (error) {
|
|
360
409
|
await this.#recoverStagedAcknowledgement(error, blocks, segments);
|
|
361
410
|
}
|
|
411
|
+
this.#journalAppended(previous, blocks.map((block) => block.id));
|
|
362
412
|
}
|
|
363
413
|
async stageBlock(id, bytes) {
|
|
364
414
|
return this.stageBlocks([{ id, bytes }]);
|
|
@@ -376,7 +426,7 @@ class DatabaseTransaction {
|
|
|
376
426
|
throw new Error(`Segment ${segment.id} belongs to another transaction`);
|
|
377
427
|
}
|
|
378
428
|
}
|
|
379
|
-
const ordinalBase = this.
|
|
429
|
+
const ordinalBase = this.pendingSegmentCount;
|
|
380
430
|
const ordered = segments.map((segment, index) => ({
|
|
381
431
|
...segment,
|
|
382
432
|
commitOrdinal: ordinalBase + index
|
|
@@ -392,6 +442,7 @@ class DatabaseTransaction {
|
|
|
392
442
|
this.#deferredSegments.push(...structuredClone(ordered));
|
|
393
443
|
for (const segment of ordered)
|
|
394
444
|
this.#knownSegments.set(segment.id, structuredClone(segment));
|
|
445
|
+
this.#stagedSegmentsView = void 0;
|
|
395
446
|
for (const segment of ordered)
|
|
396
447
|
this.#changedTableIds.add(segment.tableId);
|
|
397
448
|
return;
|
|
@@ -402,6 +453,7 @@ class DatabaseTransaction {
|
|
|
402
453
|
}
|
|
403
454
|
for (const segment of ordered)
|
|
404
455
|
this.#knownSegments.set(segment.id, structuredClone(segment));
|
|
456
|
+
this.#stagedSegmentsView = void 0;
|
|
405
457
|
for (const segment of ordered)
|
|
406
458
|
this.#changedTableIds.add(segment.tableId);
|
|
407
459
|
}
|
|
@@ -412,6 +464,17 @@ class DatabaseTransaction {
|
|
|
412
464
|
throw new Error(`Segment ${segment.id} belongs to another transaction`);
|
|
413
465
|
}
|
|
414
466
|
}
|
|
467
|
+
if (this.#deferredBlocks.length > 0 || this.#deferredSegments.length > 0) {
|
|
468
|
+
const combined = transactionArtifactBatches([...this.#deferredBlocks, ...blocks], [...this.#deferredSegments, ...segments]);
|
|
469
|
+
if (combined.length === 1) {
|
|
470
|
+
blocks = [...this.#deferredBlocks, ...blocks];
|
|
471
|
+
segments = [...this.#deferredSegments, ...segments];
|
|
472
|
+
this.#deferredBlocks.length = 0;
|
|
473
|
+
this.#deferredSegments.length = 0;
|
|
474
|
+
} else {
|
|
475
|
+
await this.#persistDeferredArtifacts();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
415
478
|
const pendingBlockIds = new Set(this.#record.pendingBlockIds);
|
|
416
479
|
const pendingSegmentIds = new Set(this.#record.pendingSegmentIds);
|
|
417
480
|
await this.#assertRetainedArtifactsUnchanged(blocks.filter((block) => pendingBlockIds.has(block.id)), segments.filter((segment) => pendingSegmentIds.has(segment.id)));
|
|
@@ -446,6 +509,7 @@ class DatabaseTransaction {
|
|
|
446
509
|
const ftsChanges = this.#materializedFtsChanges();
|
|
447
510
|
for (const segment of ordered)
|
|
448
511
|
this.#knownSegments.set(segment.id, structuredClone(segment));
|
|
512
|
+
this.#stagedSegmentsView = void 0;
|
|
449
513
|
for (const segment of ordered)
|
|
450
514
|
this.#changedTableIds.add(segment.tableId);
|
|
451
515
|
try {
|
|
@@ -496,7 +560,6 @@ class DatabaseTransaction {
|
|
|
496
560
|
if (additions.some((id) => id.length === 0)) {
|
|
497
561
|
throw new TypeError("Block ID cannot be empty");
|
|
498
562
|
}
|
|
499
|
-
assertTransactionArtifactJournalLimits([...this.#record.pendingBlockIds, ...additions], this.#record.pendingSegmentIds);
|
|
500
563
|
for (const id of additions) {
|
|
501
564
|
if (await this.store.getBlock(id) === void 0) {
|
|
502
565
|
throw new Error(`Cannot stage a missing existing block: ${id}`);
|
|
@@ -530,10 +593,6 @@ class DatabaseTransaction {
|
|
|
530
593
|
if (record.commitOrdinal !== this.#record.pendingSegmentIds.length) {
|
|
531
594
|
throw new Error(`Existing segment ${segmentId} is not the next journal ordinal`);
|
|
532
595
|
}
|
|
533
|
-
assertTransactionArtifactJournalLimits(this.#record.pendingBlockIds, [
|
|
534
|
-
...this.#record.pendingSegmentIds,
|
|
535
|
-
segmentId
|
|
536
|
-
]);
|
|
537
596
|
this.#registerLevelZeroSegments([record]);
|
|
538
597
|
this.#changedTableIds.add(record.tableId);
|
|
539
598
|
await this.#ensurePersisted();
|
|
@@ -821,6 +880,7 @@ class DatabaseTransaction {
|
|
|
821
880
|
this.#deferredBlocks.length = 0;
|
|
822
881
|
this.#deferredSegments.length = 0;
|
|
823
882
|
this.#knownSegments.clear();
|
|
883
|
+
this.#stagedSegmentsView = void 0;
|
|
824
884
|
this.#record = { ...this.#record, status: "aborted", updatedAt: dateIsoString(this.now()) };
|
|
825
885
|
this.#stopHeartbeat();
|
|
826
886
|
await this.#releaseSnapshotLease().catch(() => void 0);
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.10.0",
|
|
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"
|