@moyodb/sdk 0.0.0-alpha.3 → 1.0.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.
Files changed (38) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +10 -2
  3. package/dist/assets/worker-R2dy1Ju5.js +4 -0
  4. package/dist/assets/worker-R2dy1Ju5.js.map +1 -0
  5. package/dist/change-events.d.ts.map +1 -1
  6. package/dist/codec.d.ts.map +1 -1
  7. package/dist/compression.d.ts.map +1 -1
  8. package/dist/engine/README.md +20 -4
  9. package/dist/engine/moyodb_engine.d.ts +115 -54
  10. package/dist/engine/moyodb_engine.js +885 -391
  11. package/dist/engine/moyodb_engine_bg.wasm +0 -0
  12. package/dist/engine/moyodb_engine_bg.wasm.d.ts +55 -47
  13. package/dist/engine/package.json +3 -3
  14. package/dist/engine/snippets/moyodb-engine-8ebe6af0cc2e81b7/js/opfs_shim.js +513 -0
  15. package/dist/engine/snippets/moyodb-engine-cff0a0e2441a7a8f/js/opfs_shim.js +192 -81
  16. package/dist/errors.d.ts +8 -0
  17. package/dist/errors.d.ts.map +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/indexing.d.ts.map +1 -1
  20. package/dist/moyodb-sdk.js +1853 -1985
  21. package/dist/moyodb-sdk.js.map +1 -1
  22. package/dist/registry.d.ts +2 -1
  23. package/dist/registry.d.ts.map +1 -1
  24. package/dist/subscriptions.d.ts.map +1 -1
  25. package/dist/types.d.ts +29 -1
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/worker-api.d.ts +51 -37
  28. package/dist/worker-api.d.ts.map +1 -1
  29. package/dist/worker-client.d.ts +5 -2
  30. package/dist/worker-client.d.ts.map +1 -1
  31. package/dist/worker-protocol.d.ts +14 -3
  32. package/dist/worker-protocol.d.ts.map +1 -1
  33. package/dist/worker-server.d.ts.map +1 -1
  34. package/package.json +10 -8
  35. package/dist/assets/worker-CaOzbBz1.js +0 -2351
  36. package/dist/assets/worker-CaOzbBz1.js.map +0 -1
  37. package/dist/moyodb-sdk.umd.cjs +0 -2
  38. package/dist/moyodb-sdk.umd.cjs.map +0 -1
@@ -12,6 +12,15 @@ const TEXT_DECODER = new TextDecoder();
12
12
  let rootDir = null;
13
13
  let nextSessionId = 1;
14
14
  const sessions = new Map();
15
+ function namedError(name, message) {
16
+ const error = new Error(message);
17
+ error.name = name;
18
+ error.code = name;
19
+ return error;
20
+ }
21
+ function corruptionError(message) {
22
+ return namedError('CorruptionError', message);
23
+ }
15
24
  async function getRootDir() {
16
25
  if (!navigator?.storage?.getDirectory) {
17
26
  throw new Error('navigator.storage.getDirectory is unavailable');
@@ -25,18 +34,27 @@ async function getOrCreateStackdbRoot() {
25
34
  const root = await getRootDir();
26
35
  return await root.getDirectoryHandle(DB_ROOT_DIR, { create: true });
27
36
  }
37
+ function isNotFoundError(error) {
38
+ return error?.name === 'NotFoundError' || error?.name === 'TypeMismatchError';
39
+ }
28
40
  async function lookupDirectoryHandle(parent, name) {
29
41
  try {
30
42
  return await parent.getDirectoryHandle(name, { create: false });
31
- } catch (_err) {
32
- return null;
43
+ } catch (err) {
44
+ if (isNotFoundError(err)) {
45
+ return null;
46
+ }
47
+ throw err;
33
48
  }
34
49
  }
35
50
  async function lookupFileHandle(parent, name) {
36
51
  try {
37
52
  return await parent.getFileHandle(name, { create: false });
38
- } catch (_err) {
39
- return null;
53
+ } catch (err) {
54
+ if (isNotFoundError(err)) {
55
+ return null;
56
+ }
57
+ throw err;
40
58
  }
41
59
  }
42
60
  function sessionPath(encodedDbName, generationName = null) {
@@ -64,7 +82,41 @@ function fnv1a32(bytes, zeroOffset = -1, zeroLength = 0) {
64
82
  function isGenerationNameValid(name) {
65
83
  return typeof name === 'string' && /^gen-[a-z0-9]+-[a-z0-9]+$/i.test(name);
66
84
  }
67
- function encodeControlSlot(generationCounter, activeGeneration) {
85
+ // Every byte must reach the file or the call fails: a short write that returns
86
+ // normally would let callers publish state that is not on disk.
87
+ export function writeAll(handle, bytes, at) {
88
+ let writtenTotal = 0;
89
+ while (writtenTotal < bytes.length) {
90
+ const chunk = bytes.subarray(writtenTotal, writtenTotal + OPFS_WRITE_CHUNK_SIZE);
91
+ const rawWritten = handle.write(chunk, { at: at + writtenTotal });
92
+ const written = Number(rawWritten);
93
+ if (!Number.isSafeInteger(written) || written < 0 || written > chunk.length) {
94
+ throw namedError('StorageError', `opfs write failed: invalid byte count ${String(rawWritten)}`);
95
+ }
96
+ if (written === 0 && chunk.length > 0) {
97
+ throw namedError('StorageError', 'opfs write failed: wrote 0 bytes');
98
+ }
99
+ writtenTotal += written;
100
+ }
101
+ return writtenTotal;
102
+ }
103
+ // Fills `buffer` from `at`, stopping only at end of file. Bytes past EOF stay zero.
104
+ export function readAll(handle, buffer, at) {
105
+ let readTotal = 0;
106
+ while (readTotal < buffer.length) {
107
+ const rawRead = handle.read(buffer.subarray(readTotal), { at: at + readTotal });
108
+ const read = Number(rawRead);
109
+ if (!Number.isSafeInteger(read) || read < 0 || read > buffer.length - readTotal) {
110
+ throw namedError('StorageError', `opfs read failed: invalid byte count ${String(rawRead)}`);
111
+ }
112
+ if (read === 0) {
113
+ break;
114
+ }
115
+ readTotal += read;
116
+ }
117
+ return readTotal;
118
+ }
119
+ export function encodeControlSlot(generationCounter, activeGeneration) {
68
120
  if (!isGenerationNameValid(activeGeneration)) {
69
121
  throw new Error(`invalid generation name ${activeGeneration}`);
70
122
  }
@@ -86,7 +138,9 @@ function encodeControlSlot(generationCounter, activeGeneration) {
86
138
  view.setUint32(CONTROL_CHECKSUM_OFFSET, checksum, true);
87
139
  return slot;
88
140
  }
89
- function decodeControlSlot(slotIndex, bytes) {
141
+ // Returns null for any slot whose bytes do not checksum. Fields are interpreted
142
+ // only after the checksum passes, so a torn slot never masks its valid sibling.
143
+ export function decodeControlSlot(slotIndex, bytes) {
90
144
  if (bytes.length < CONTROL_SLOT_SIZE) {
91
145
  return null;
92
146
  }
@@ -96,23 +150,23 @@ function decodeControlSlot(slotIndex, bytes) {
96
150
  }
97
151
  }
98
152
  const view = slotBytesView(bytes);
99
- const version = view.getUint32(8, true);
100
- if (version !== CONTROL_VERSION) {
101
- throw new Error(`unsupported control file version ${version}`);
102
- }
103
- const expectedChecksum = fnv1a32(bytes, CONTROL_CHECKSUM_OFFSET, 4);
153
+ const expectedChecksum = fnv1a32(bytes.subarray(0, CONTROL_SLOT_SIZE), CONTROL_CHECKSUM_OFFSET, 4);
104
154
  const storedChecksum = view.getUint32(CONTROL_CHECKSUM_OFFSET, true);
105
155
  if (expectedChecksum !== storedChecksum) {
106
156
  return null;
107
157
  }
158
+ const version = view.getUint32(8, true);
159
+ if (version !== CONTROL_VERSION) {
160
+ throw corruptionError(`unsupported control file version ${version}`);
161
+ }
108
162
  const nameLength = view.getUint16(20, true);
109
163
  const nameEnd = CONTROL_NAME_OFFSET + nameLength;
110
164
  if (nameEnd > CONTROL_SLOT_SIZE) {
111
- throw new Error(`control slot name length out of bounds: ${nameLength}`);
165
+ throw corruptionError(`control slot name length out of bounds: ${nameLength}`);
112
166
  }
113
167
  const activeGeneration = TEXT_DECODER.decode(bytes.subarray(CONTROL_NAME_OFFSET, nameEnd));
114
168
  if (!isGenerationNameValid(activeGeneration)) {
115
- throw new Error(`invalid generation in control slot: ${activeGeneration}`);
169
+ throw corruptionError(`invalid generation in control slot: ${activeGeneration}`);
116
170
  }
117
171
  return {
118
172
  slotIndex,
@@ -120,25 +174,31 @@ function decodeControlSlot(slotIndex, bytes) {
120
174
  activeGeneration
121
175
  };
122
176
  }
123
- function readControlStateFromAccessHandle(accessHandle) {
124
- const buffer = new Uint8Array(CONTROL_SLOT_SIZE * 2);
177
+ // `absent` — the file is empty: no generation has ever been published.
178
+ // `invalid` — the file has bytes but no slot checksums.
179
+ // `valid` — `control` is the newest checksummed slot.
180
+ export function readControlStateFromAccessHandle(accessHandle) {
125
181
  const size = Number(accessHandle.getSize());
126
- if (size <= 0) {
127
- return null;
182
+ if (!Number.isSafeInteger(size) || size < 0) {
183
+ throw namedError('StorageError', `invalid control file size ${String(size)}`);
184
+ }
185
+ if (size === 0) {
186
+ return { status: 'absent', control: null };
128
187
  }
129
- const readLength = Math.min(buffer.length, size);
130
- accessHandle.read(buffer.subarray(0, readLength), { at: 0 });
188
+ const buffer = new Uint8Array(CONTROL_SLOT_SIZE * 2);
189
+ readAll(accessHandle, buffer.subarray(0, Math.min(buffer.length, size)), 0);
131
190
  const slot0 = decodeControlSlot(0, buffer.subarray(0, CONTROL_SLOT_SIZE));
132
191
  const slot1 = decodeControlSlot(1, buffer.subarray(CONTROL_SLOT_SIZE, CONTROL_SLOT_SIZE * 2));
133
192
  if (slot0 && slot1) {
134
- return slot0.generationCounter >= slot1.generationCounter ? slot0 : slot1;
193
+ return { status: 'valid', control: slot0.generationCounter >= slot1.generationCounter ? slot0 : slot1 };
135
194
  }
136
- return slot0 ?? slot1 ?? null;
195
+ const control = slot0 ?? slot1;
196
+ return control ? { status: 'valid', control } : { status: 'invalid', control: null };
137
197
  }
138
- async function readControlState(dbRoot) {
198
+ async function readControlFile(dbRoot) {
139
199
  const fileHandle = await lookupFileHandle(dbRoot, CONTROL_FILE_NAME);
140
200
  if (!fileHandle) {
141
- return null;
201
+ return { status: 'absent', control: null };
142
202
  }
143
203
  const accessHandle = await fileHandle.createSyncAccessHandle();
144
204
  try {
@@ -147,16 +207,62 @@ async function readControlState(dbRoot) {
147
207
  accessHandle.close();
148
208
  }
149
209
  }
150
- async function writeControlState(dbRoot, activeGeneration) {
210
+ async function hasLegacyDataFiles(dbRoot) {
211
+ for (const fileName of FILE_NAMES) {
212
+ if (await lookupFileHandle(dbRoot, fileName)) {
213
+ return true;
214
+ }
215
+ }
216
+ return false;
217
+ }
218
+ // Resolves which directory holds the live database, failing closed on damage.
219
+ //
220
+ // Legacy root files are removed before the first write into a published
221
+ // generation (see opfsCleanupInactiveEntries and opfsOpenActiveDb). So an
222
+ // unreadable control file next to legacy files can only be a torn first
223
+ // publication, and legacy is still authoritative. Without legacy files it is
224
+ // real corruption of the pointer to live data.
225
+ async function resolveActiveGeneration(dbRoot) {
226
+ const state = await readControlFile(dbRoot);
227
+ if (state.status === 'valid') {
228
+ return state.control.activeGeneration;
229
+ }
230
+ if (state.status === 'absent') {
231
+ return null;
232
+ }
233
+ if (await hasLegacyDataFiles(dbRoot)) {
234
+ return null;
235
+ }
236
+ throw corruptionError(`control file ${CONTROL_FILE_NAME} is corrupt: no slot has a valid checksum`);
237
+ }
238
+ async function writeControlState(dbRoot, activeGeneration, expectedCurrentGeneration) {
151
239
  const fileHandle = await dbRoot.getFileHandle(CONTROL_FILE_NAME, { create: true });
152
240
  const accessHandle = await fileHandle.createSyncAccessHandle();
153
241
  try {
154
242
  const current = readControlStateFromAccessHandle(accessHandle);
155
- const nextSlot = current ? (current.slotIndex === 0 ? 1 : 0) : 0;
156
- const nextGenerationCounter = (current?.generationCounter ?? 0) + 1;
243
+ if (current.status === 'invalid' && !(await hasLegacyDataFiles(dbRoot))) {
244
+ throw corruptionError('refusing to publish a generation over a corrupt control file');
245
+ }
246
+ const currentGeneration = current.control?.activeGeneration ?? null;
247
+ if (expectedCurrentGeneration !== undefined && currentGeneration !== expectedCurrentGeneration) {
248
+ throw namedError(
249
+ 'DatabaseBusyError',
250
+ `active generation changed underneath compaction: expected ${String(expectedCurrentGeneration)}, found ${String(currentGeneration)}`
251
+ );
252
+ }
253
+ const nextSlot = current.control ? (current.control.slotIndex === 0 ? 1 : 0) : 0;
254
+ const nextGenerationCounter = (current.control?.generationCounter ?? 0) + 1;
157
255
  const encoded = encodeControlSlot(nextGenerationCounter, activeGeneration);
158
- accessHandle.write(encoded, { at: nextSlot * CONTROL_SLOT_SIZE });
256
+ writeAll(accessHandle, encoded, nextSlot * CONTROL_SLOT_SIZE);
159
257
  accessHandle.flush();
258
+ const published = readControlStateFromAccessHandle(accessHandle);
259
+ if (
260
+ published.status !== 'valid' ||
261
+ published.control.activeGeneration !== activeGeneration ||
262
+ published.control.generationCounter !== nextGenerationCounter
263
+ ) {
264
+ throw namedError('StorageError', `control file does not select ${activeGeneration} after publication`);
265
+ }
160
266
  } finally {
161
267
  accessHandle.close();
162
268
  }
@@ -179,23 +285,37 @@ async function getDbRoot(encodedDbName, createIfMissing) {
179
285
  }
180
286
  return dbRoot;
181
287
  }
288
+ async function removeLegacyDataFiles(dbRoot) {
289
+ for (const fileName of FILE_NAMES) {
290
+ try {
291
+ await dbRoot.removeEntry(fileName);
292
+ } catch (err) {
293
+ if (!isNotFoundError(err)) {
294
+ throw err;
295
+ }
296
+ }
297
+ }
298
+ }
182
299
  async function resolveActiveDataDir(dbRoot) {
183
- const control = await readControlState(dbRoot);
184
- if (!control) {
300
+ const activeGeneration = await resolveActiveGeneration(dbRoot);
301
+ if (!activeGeneration) {
185
302
  return {
186
303
  dirHandle: dbRoot,
187
304
  generationName: null,
188
305
  path: dbRoot.name
189
306
  };
190
307
  }
191
- const activeDir = await lookupDirectoryHandle(dbRoot, control.activeGeneration);
308
+ const activeDir = await lookupDirectoryHandle(dbRoot, activeGeneration);
192
309
  if (!activeDir) {
193
- throw new Error(`active generation ${control.activeGeneration} is missing`);
310
+ throw corruptionError(`active generation ${activeGeneration} is missing`);
194
311
  }
312
+ // A crash after publication but before legacy cleanup leaves stale root
313
+ // files. They must be gone before this generation accepts writes.
314
+ await removeLegacyDataFiles(dbRoot);
195
315
  return {
196
316
  dirHandle: activeDir,
197
- generationName: control.activeGeneration,
198
- path: `${dbRoot.name}/${control.activeGeneration}`
317
+ generationName: activeGeneration,
318
+ path: `${dbRoot.name}/${activeGeneration}`
199
319
  };
200
320
  }
201
321
  function closeSession(sessionId) {
@@ -218,7 +338,7 @@ function closeSessionsForDb(encodedDbName) {
218
338
  }
219
339
  }
220
340
  }
221
- async function openSessionForDir(dbPath, dirHandle) {
341
+ async function openSessionForDir(dbPath, dirHandle, generationName) {
222
342
  const handles = new Map();
223
343
  try {
224
344
  for (let fileKind = 0; fileKind < FILE_NAMES.length; fileKind += 1) {
@@ -244,12 +364,12 @@ async function openSessionForDir(dbPath, dirHandle) {
244
364
  path: dbPath,
245
365
  handles
246
366
  });
247
- return { sessionId };
367
+ return { sessionId, generationName };
248
368
  }
249
369
  function getSession(sessionId) {
250
370
  const session = sessions.get(sessionId);
251
371
  if (!session) {
252
- throw new Error(`no OPFS session ${sessionId}`);
372
+ throw namedError('StorageError', `no OPFS session ${sessionId}`);
253
373
  }
254
374
  return session;
255
375
  }
@@ -257,7 +377,7 @@ function getAccessHandle(sessionId, fileKind) {
257
377
  const session = getSession(sessionId);
258
378
  const handle = session.handles.get(fileKind);
259
379
  if (!handle) {
260
- throw new Error(`no OPFS access handle for session ${sessionId} file kind ${fileKind}`);
380
+ throw namedError('StorageError', `no OPFS access handle for session ${sessionId} file kind ${fileKind}`);
261
381
  }
262
382
  return handle;
263
383
  }
@@ -292,7 +412,7 @@ async function sumDirectorySize(dirHandle, pathPrefix) {
292
412
  export async function opfsOpenActiveDb(encodedDbName, createIfMissing = true) {
293
413
  const dbRoot = await getDbRoot(encodedDbName, createIfMissing);
294
414
  const active = await resolveActiveDataDir(dbRoot);
295
- return await openSessionForDir(active.path, active.dirHandle);
415
+ return await openSessionForDir(active.path, active.dirHandle, active.generationName);
296
416
  }
297
417
  export async function opfsOpenGenerationDb(encodedDbName, generationName, createIfMissing = true) {
298
418
  if (!isGenerationNameValid(generationName)) {
@@ -300,43 +420,29 @@ export async function opfsOpenGenerationDb(encodedDbName, generationName, create
300
420
  }
301
421
  const dbRoot = await getDbRoot(encodedDbName, createIfMissing);
302
422
  const generationDir = await dbRoot.getDirectoryHandle(generationName, { create: createIfMissing });
303
- return await openSessionForDir(sessionPath(encodedDbName, generationName), generationDir);
423
+ return await openSessionForDir(sessionPath(encodedDbName, generationName), generationDir, generationName);
424
+ }
425
+ export async function opfsReadActiveGeneration(encodedDbName) {
426
+ const dbRoot = await getDbRoot(encodedDbName, false);
427
+ return await resolveActiveGeneration(dbRoot);
304
428
  }
305
429
  export function opfsReadAt(sessionId, fileKind, offset, len) {
306
430
  const handle = getAccessHandle(sessionId, fileKind);
307
431
  const buffer = new Uint8Array(len);
308
- handle.read(buffer, { at: Number(offset) });
432
+ readAll(handle, buffer, Number(offset));
309
433
  return buffer;
310
434
  }
311
435
  export function opfsWriteAt(sessionId, fileKind, offset, bytes) {
312
- const handle = getAccessHandle(sessionId, fileKind);
313
- const startOffset = Number(offset);
314
- let writtenTotal = 0;
315
- while (writtenTotal < bytes.length) {
316
- const chunk = bytes.subarray(writtenTotal, writtenTotal + OPFS_WRITE_CHUNK_SIZE);
317
- const rawWritten = handle.write(chunk, { at: startOffset + writtenTotal });
318
- const written = Number(rawWritten);
319
- if (!Number.isSafeInteger(written) || written < 0 || written > chunk.length) {
320
- throw new Error(`opfs write failed: invalid byte count ${String(rawWritten)}`);
321
- }
322
- if (written === 0 && chunk.length > 0) {
323
- throw new Error('opfs write failed: wrote 0 bytes');
324
- }
325
- writtenTotal += written;
326
- }
327
- return writtenTotal;
436
+ return writeAll(getAccessHandle(sessionId, fileKind), bytes, Number(offset));
328
437
  }
329
438
  export function opfsFlush(sessionId, fileKind) {
330
- const handle = getAccessHandle(sessionId, fileKind);
331
- handle.flush();
439
+ getAccessHandle(sessionId, fileKind).flush();
332
440
  }
333
441
  export function opfsLen(sessionId, fileKind) {
334
- const handle = getAccessHandle(sessionId, fileKind);
335
- return BigInt(handle.getSize());
442
+ return BigInt(getAccessHandle(sessionId, fileKind).getSize());
336
443
  }
337
444
  export function opfsTruncate(sessionId, fileKind, size) {
338
- const handle = getAccessHandle(sessionId, fileKind);
339
- handle.truncate(Number(size));
445
+ getAccessHandle(sessionId, fileKind).truncate(Number(size));
340
446
  }
341
447
  export function opfsCloseSession(sessionId) {
342
448
  closeSession(sessionId);
@@ -350,7 +456,10 @@ export async function opfsPrepareRebuildTarget(encodedDbName) {
350
456
  await dbRoot.getDirectoryHandle(generationName, { create: true });
351
457
  return { generationName };
352
458
  }
353
- export async function opfsSwapActiveGeneration(encodedDbName, generationName) {
459
+ // Publishes `generationName` as the live database. Resolves only after the
460
+ // control file has been flushed and read back selecting the new generation;
461
+ // the caller may switch its live engine only after that.
462
+ export async function opfsSwapActiveGeneration(encodedDbName, generationName, expectedCurrentGeneration) {
354
463
  if (!isGenerationNameValid(generationName)) {
355
464
  throw new Error(`invalid generation name ${generationName}`);
356
465
  }
@@ -359,32 +468,30 @@ export async function opfsSwapActiveGeneration(encodedDbName, generationName) {
359
468
  if (!generationDir) {
360
469
  throw new Error(`generation ${generationName} does not exist`);
361
470
  }
362
- await writeControlState(dbRoot, generationName);
471
+ await writeControlState(dbRoot, generationName, expectedCurrentGeneration ?? null);
363
472
  }
473
+ // Legacy root files are removed strictly (see resolveActiveGeneration); stale
474
+ // generation directories are best effort because nothing can select them.
364
475
  export async function opfsCleanupInactiveEntries(encodedDbName) {
365
476
  const dbRoot = await lookupDirectoryHandle(await getOrCreateStackdbRoot(), encodedDbName);
366
477
  if (!dbRoot) {
367
478
  return;
368
479
  }
369
- const control = await readControlState(dbRoot);
370
- const activeGeneration = control?.activeGeneration ?? null;
480
+ const activeGeneration = await resolveActiveGeneration(dbRoot);
481
+ if (activeGeneration) {
482
+ await removeLegacyDataFiles(dbRoot);
483
+ }
484
+ const staleDirectories = [];
371
485
  for await (const [name, handle] of dbRoot.entries()) {
372
- if (name === CONTROL_FILE_NAME) {
373
- continue;
374
- }
375
- if (handle.kind === 'directory') {
376
- if (activeGeneration && name === activeGeneration) {
377
- continue;
378
- }
379
- if (name.startsWith('gen-')) {
380
- await dbRoot.removeEntry(name, { recursive: true });
381
- }
382
- continue;
383
- }
384
- if (activeGeneration && FILE_NAMES.includes(name)) {
385
- await dbRoot.removeEntry(name);
486
+ if (handle.kind === 'directory' && name.startsWith('gen-') && name !== activeGeneration) {
487
+ staleDirectories.push(name);
386
488
  }
387
489
  }
490
+ for (const name of staleDirectories) {
491
+ try {
492
+ await dbRoot.removeEntry(name, { recursive: true });
493
+ } catch (_err) {}
494
+ }
388
495
  }
389
496
  export async function opfsDbDirectorySize(encodedDbName) {
390
497
  const dbRoot = await lookupDirectoryHandle(await getOrCreateStackdbRoot(), encodedDbName);
@@ -398,5 +505,9 @@ export async function opfsRemoveDb(encodedDbName) {
398
505
  const stackdb = await getOrCreateStackdbRoot();
399
506
  try {
400
507
  await stackdb.removeEntry(encodedDbName, { recursive: true });
401
- } catch (_err) {}
508
+ } catch (err) {
509
+ if (!isNotFoundError(err)) {
510
+ throw err;
511
+ }
512
+ }
402
513
  }
package/dist/errors.d.ts CHANGED
@@ -64,6 +64,14 @@ export declare class InjectedFailureError extends MoyoDbError {
64
64
  export declare class ChangeFeedCompactedError extends MoyoDbError {
65
65
  constructor(message: string);
66
66
  }
67
+ /**
68
+ * The outcome of a commit could not be confirmed (storage failed at or after
69
+ * the WAL append) or the engine hit an unrecoverable fault. The database
70
+ * rejects further work until it is reopened, which replays durable state.
71
+ */
72
+ export declare class RecoveryRequiredError extends MoyoDbError {
73
+ constructor(message: string);
74
+ }
67
75
  export declare class InternalError extends MoyoDbError {
68
76
  constructor(message: string);
69
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;IAClC,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAIxC;CACJ;AACD,qBAAa,wBAAyB,SAAQ,WAAW;IACrD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,iBAAkB,SAAQ,WAAW;IAC9C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,eAAgB,SAAQ,WAAW;IAC5C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,gBAAiB,SAAQ,WAAW;IAC7C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,kBAAmB,SAAQ,WAAW;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,kBAAmB,SAAQ,WAAW;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,eAAgB,SAAQ,WAAW;IAC5C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,0BAA2B,SAAQ,eAAe;IAC3D,YAAY,OAAO,EAAE,MAAM,EAG1B;CACJ;AACD,qBAAa,gCAAiC,SAAQ,WAAW;IAC7D,YAAY,OAAO,SAAmC,EAErD;CACJ;AACD,qBAAa,wBAAyB,SAAQ,WAAW;IACrD,YAAY,OAAO,SAAuD,EAEzE;CACJ;AACD,qBAAa,sBAAuB,SAAQ,WAAW;IACnD,YAAY,OAAO,SAAkC,EAEpD;CACJ;AACD,qBAAa,mBAAoB,SAAQ,WAAW;IAChD,YAAY,OAAO,SAA8B,EAEhD;CACJ;AACD,qBAAa,kBAAmB,SAAQ,WAAW;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,gBAAiB,SAAQ,WAAW;IAC7C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,qBAAsB,SAAQ,WAAW;IAClD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,sBAAuB,SAAQ,WAAW;IACnD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,iBAAkB,SAAQ,WAAW;IAC9C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,YAAa,SAAQ,WAAW;IACzC,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,kBAAmB,SAAQ,WAAW;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,oBAAqB,SAAQ,WAAW;IACjD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,wBAAyB,SAAQ,WAAW;IACrD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,aAAc,SAAQ,WAAW;IAC1C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,uBAAwB,SAAQ,WAAW;IACpD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,YAAa,SAAQ,WAAW;IACzC,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AA4CD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAcpD"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;IAClC,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAIxC;CACJ;AACD,qBAAa,wBAAyB,SAAQ,WAAW;IACrD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,iBAAkB,SAAQ,WAAW;IAC9C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,eAAgB,SAAQ,WAAW;IAC5C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,gBAAiB,SAAQ,WAAW;IAC7C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,kBAAmB,SAAQ,WAAW;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,kBAAmB,SAAQ,WAAW;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,eAAgB,SAAQ,WAAW;IAC5C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,0BAA2B,SAAQ,eAAe;IAC3D,YAAY,OAAO,EAAE,MAAM,EAG1B;CACJ;AACD,qBAAa,gCAAiC,SAAQ,WAAW;IAC7D,YAAY,OAAO,SAAmC,EAErD;CACJ;AACD,qBAAa,wBAAyB,SAAQ,WAAW;IACrD,YAAY,OAAO,SAAuD,EAEzE;CACJ;AACD,qBAAa,sBAAuB,SAAQ,WAAW;IACnD,YAAY,OAAO,SAAkC,EAEpD;CACJ;AACD,qBAAa,mBAAoB,SAAQ,WAAW;IAChD,YAAY,OAAO,SAA8B,EAEhD;CACJ;AACD,qBAAa,kBAAmB,SAAQ,WAAW;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,gBAAiB,SAAQ,WAAW;IAC7C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,qBAAsB,SAAQ,WAAW;IAClD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,sBAAuB,SAAQ,WAAW;IACnD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,iBAAkB,SAAQ,WAAW;IAC9C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,YAAa,SAAQ,WAAW;IACzC,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,kBAAmB,SAAQ,WAAW;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,oBAAqB,SAAQ,WAAW;IACjD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,wBAAyB,SAAQ,WAAW;IACrD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,WAAW;IAClD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,aAAc,SAAQ,WAAW;IAC1C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,uBAAwB,SAAQ,WAAW;IACpD,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AACD,qBAAa,YAAa,SAAQ,WAAW;IACzC,YAAY,OAAO,EAAE,MAAM,EAE1B;CACJ;AA6CD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAcpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAMR,EAAE,EAMF,WAAW,EASd,MAAM,SAAS,CAAC;AA6xBjB,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,EAAE,CAAC,CA2DjF;AACD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI1D;AACD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG5D;AACD,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,mBAAmB,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAMR,EAAE,EAMF,WAAW,EASd,MAAM,SAAS,CAAC;AAiyBjB,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,EAAE,CAAC,CA2DjF;AACD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI1D;AACD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG5D;AACD,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,mBAAmB,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"indexing.d.ts","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAU/C,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAC1D,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,WAAW,oBAAoB;IACjC,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,UAAU,CAAC;CAC1B;AACD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AACD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIxD;AACD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,EAAE,CAuD9E;AACD,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAc7G;AACD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3E;AACD,wBAAgB,mBAAmB,CAC/B,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,EACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACb,kBAAkB,GAAG,IAAI,CAE3B;AACD,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAE5G;AACD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAE9E;AACD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,kBAAkB,GAAG,UAAU,CAE5E;AACD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,GAAG,kBAAkB,CAS9E;AACD,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,CAEnG;AACD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,oBAAoB,CAS3E;AACD,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,UAAU,GAAG,KAAK,CAErE;AACD,wBAAgB,yBAAyB,CAAC,KAAK,GAAE,KAAU,GAAG,KAAK,CA4BlE;AACD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAkBzG;AACD,wBAAgB,mCAAmC,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAEnG;AACD,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,EAAE,CAS7G;AACD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,QAAQ,EAAE,CAE5F"}
1
+ {"version":3,"file":"indexing.d.ts","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAU/C,eAAO,MAAM,oBAAoB,wBAAwB,CAAC;AAC1D,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,WAAW,oBAAoB;IACjC,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,UAAU,CAAC;CAC1B;AACD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AACD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIxD;AACD,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,kBAAkB,EAAE,CAuD9E;AACD,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAc7G;AACD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3E;AACD,wBAAgB,mBAAmB,CAC/B,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,EACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACb,kBAAkB,GAAG,IAAI,CAE3B;AACD,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAE5G;AACD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAE9E;AACD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,kBAAkB,GAAG,UAAU,CAE5E;AACD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,GAAG,kBAAkB,CAS9E;AACD,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,CAEnG;AACD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,oBAAoB,CAS3E;AACD,wBAAgB,kBAAkB,CAAC,eAAe,EAAE,UAAU,GAAG,KAAK,CAErE;AACD,wBAAgB,yBAAyB,CAAC,KAAK,GAAE,KAAU,GAAG,KAAK,CA4BlE;AACD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,EAAE,UAAU,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAkBzG;AACD,wBAAgB,mCAAmC,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAEnG;AACD,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,EAAE,CAS7G;AACD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,aAAa,CAAC,kBAAkB,CAAC,GAAG,QAAQ,EAAE,CAE5F"}