@harperfast/rocksdb-js 2.11.0 → 2.13.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/index.mjs CHANGED
@@ -8,11 +8,10 @@ import * as orderedBinary from "ordered-binary";
8
8
  import { ExtendedIterable } from "@harperfast/extended-iterable";
9
9
  import { Encoder } from "msgpackr";
10
10
 
11
- //#region \0rolldown/runtime.js
11
+ // \0rolldown/runtime.js
12
12
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
13
13
 
14
- //#endregion
15
- //#region src/load-binding.ts
14
+ // src/load-binding.ts
16
15
  const nativeExtRE = /\.node$/;
17
16
  const req = createRequire(import.meta.url);
18
17
  /**
@@ -147,7 +146,7 @@ const currentThreadId = binding.currentThreadId;
147
146
  * Reads the process-wide steady clock: `std::chrono::steady_clock` as
148
147
  * fractional milliseconds from an unspecified origin that is fixed for the life
149
148
  * of the process. Every sample in the process — main thread and every
150
- * `worker_threads` worker, whenever started or restarted, on Node, Bun and Deno
149
+ * `worker_threads` worker, whenever started or restarted, on Node and Bun
151
150
  * — is in one domain, and the difference between two samples is real elapsed
152
151
  * time unaffected by wall-clock steps. Non-decreasing but not unique (two
153
152
  * samples may be equal; treat `==` as "not later"). Windows QPC samples from
@@ -185,12 +184,30 @@ const transactionLogMapCount = binding.transactionLogMapCount;
185
184
  */
186
185
  const lockWakeCallbackCount = binding.lockWakeCallbackCount;
187
186
  /**
187
+ * The calling thread's counters for the per-transaction iterator pool: pooled
188
+ * iterators created, and idle ones reused by a later range. Internal — used by
189
+ * tests to prove a range was served by a reused iterator.
190
+ */
191
+ const iteratorPoolStats = binding.iteratorPoolStats;
192
+ /**
193
+ * Whether transaction `getRange()` reuses pooled native iterators (off with
194
+ * `ROCKSDB_JS_ITERATOR_POOL=0`). Internal.
195
+ */
196
+ const iteratorPoolEnabled = binding.iteratorPoolEnabled;
197
+ /**
188
198
  * Test-only: force the next `count` transaction commits to fail with TryAgain (rolled back, so
189
199
  * no data is committed), reproducing a stranded-snapshot conflict deterministically. Pass 0 to
190
200
  * disarm. Used by the ERR_TRY_AGAIN retry regression test.
191
201
  */
192
202
  const forceTryAgainForTesting = binding.forceTryAgainForTesting;
193
203
  /**
204
+ * Test-only: turn the RocksDB conflict check before the transaction-log write off (`true`) or back
205
+ * on, so a test can make a logged batch conflict at commit from a stale snapshot.
206
+ */
207
+ const disablePrelogConflictCheckForTesting = binding.disablePrelogConflictCheckForTesting;
208
+ /** Test-only: run the pre-log conflict check on retries alone (`true`, the default) or on every attempt. */
209
+ const setPrelogConflictCheckRetriesOnlyForTesting = binding.setPrelogConflictCheckRetriesOnlyForTesting;
210
+ /**
194
211
  * Test-only seam: makes the physical column-family drop report failure.
195
212
  * `0` = inert, `1` = fail without dropping (the retry performs the drop),
196
213
  * `2` = drop for real and then report failure (the retry resolves as
@@ -233,8 +250,7 @@ const nativeValidateTransactionLog = binding.validateTransactionLog;
233
250
  const stats = binding.stats;
234
251
  const version = binding.version;
235
252
 
236
- //#endregion
237
- //#region src/validate-transaction-log.ts
253
+ // src/validate-transaction-log.ts
238
254
  /**
239
255
  * Validates a transaction log store directory: every `<sequence>.txnlog` file
240
256
  * is checked for a valid header (token, version) and intact v1 entry framing
@@ -274,8 +290,7 @@ function validateTransactionLogStore(path, options) {
274
290
  return new Promise((resolve, reject) => nativeValidateTransactionLog(resolve, reject, path, options?.strict ?? false));
275
291
  }
276
292
 
277
- //#endregion
278
- //#region src/backup.ts
293
+ // src/backup.ts
279
294
  /** Subdirectory (under a backup directory) holding per-backup transaction log snapshots. */
280
295
  const TRANSACTION_LOGS_DIRNAME = "transaction_logs";
281
296
  /** Non-blocking existence check (`fs.existsSync` would block the event loop). */
@@ -525,6 +540,14 @@ const backups = {
525
540
  * Verifies a backup's file sizes, and optionally their checksums (which
526
541
  * requires reading all backed-up data).
527
542
  *
543
+ * Set `verifyTables: true` to also prove this build can read the backup
544
+ * before restoring it: every table file is opened, one at a time, and its
545
+ * footer (including `format_version`) and properties must be readable, and
546
+ * every compression algorithm it records must be supported by this build.
547
+ * A checksum-valid table written by a newer or differently built RocksDB
548
+ * otherwise passes and fails on the first read after a restore. Blob files
549
+ * are not checked.
550
+ *
528
551
  * When the backup was created with `transactionLogs: true`, the backup's
529
552
  * transaction log snapshot (`<backupDir>/transaction_logs/<backupId>/`) is
530
553
  * also validated — every log file's header and entry framing must be intact
@@ -532,7 +555,9 @@ const backups = {
532
555
  * is a verification failure). Set `verifyTransactionLogs: false` to skip.
533
556
  */
534
557
  async verify(backupDir, backupId, options) {
535
- await new Promise((resolve, reject) => nativeBackupVerify(resolve, reject, backupDir, backupId, options?.verifyWithChecksum ?? false));
558
+ const verifyTables = options?.verifyTables;
559
+ if (verifyTables !== void 0 && typeof verifyTables !== "boolean") throw new TypeError(`verifyTables must be a boolean, received ${verifyTables === null ? "null" : typeof verifyTables}`);
560
+ await new Promise((resolve, reject) => nativeBackupVerify(resolve, reject, backupDir, backupId, options?.verifyWithChecksum ?? false, verifyTables === true));
536
561
  if (options?.verifyTransactionLogs === false) return;
537
562
  const logsDir = join(backupDir, TRANSACTION_LOGS_DIRNAME, String(backupId));
538
563
  if (!await exists(logsDir)) return;
@@ -549,8 +574,7 @@ const backups = {
549
574
  }
550
575
  };
551
576
 
552
- //#endregion
553
- //#region src/count-estimator.ts
577
+ // src/count-estimator.ts
554
578
  /**
555
579
  * Below this many traversed entries the observed density is too noisy to
556
580
  * calibrate with, so `estimate()` returns the raw statistical estimate.
@@ -693,8 +717,7 @@ var CountEstimator = class {
693
717
  }
694
718
  };
695
719
 
696
- //#endregion
697
- //#region src/util.ts
720
+ // src/util.ts
698
721
  /**
699
722
  * Parses a duration string into milliseconds.
700
723
  *
@@ -765,8 +788,7 @@ function when(subject, callback, errback) {
765
788
  }
766
789
  }
767
790
 
768
- //#endregion
769
- //#region src/dbi.ts
791
+ // src/dbi.ts
770
792
  /**
771
793
  * The base class for all database operations. This base class is shared by
772
794
  * `RocksDatabase` and `Transaction`.
@@ -1051,8 +1073,7 @@ var DBI = class DBI {
1051
1073
  }
1052
1074
  };
1053
1075
 
1054
- //#endregion
1055
- //#region src/tar.ts
1076
+ // src/tar.ts
1056
1077
  /**
1057
1078
  * A minimal, dependency-free streaming USTAR (POSIX tar) encoder.
1058
1079
  *
@@ -1258,8 +1279,7 @@ var TarEncoder = class {
1258
1279
  }
1259
1280
  };
1260
1281
 
1261
- //#endregion
1262
- //#region src/backup-stream.ts
1282
+ // src/backup-stream.ts
1263
1283
  /** Native event discriminator: a new file header (vs. a payload chunk). */
1264
1284
  const EVENT_FILE = 0;
1265
1285
  /**
@@ -1303,9 +1323,22 @@ async function backupToStream(db, stream, options) {
1303
1323
  }
1304
1324
  }
1305
1325
 
1306
- //#endregion
1307
- //#region src/dbi-iterator.ts
1308
- const { ITERATOR_RESULT_DONE, ITERATOR_RESULT_FAST } = constants;
1326
+ // src/dbi-iterator.ts
1327
+ const { ITERATOR_RESULT_DONE, ITERATOR_RESULT_FAST, ITERATOR_RESULT_RELEASED } = constants;
1328
+ const transactionsToTrim = /* @__PURE__ */ new Set();
1329
+ function trimIdleIterators() {
1330
+ try {
1331
+ for (const transaction of transactionsToTrim) transaction.trimIterators();
1332
+ } finally {
1333
+ transactionsToTrim.clear();
1334
+ }
1335
+ }
1336
+ function scheduleTrim(transaction) {
1337
+ if (transaction !== void 0) {
1338
+ if (transactionsToTrim.size === 0) setImmediate(trimIdleIterators);
1339
+ transactionsToTrim.add(transaction);
1340
+ }
1341
+ }
1309
1342
  const DONE_RESULT = Object.freeze({
1310
1343
  done: true,
1311
1344
  value: void 0
@@ -1325,10 +1358,12 @@ var DBIterator = class {
1325
1358
  #includeValues;
1326
1359
  #limit;
1327
1360
  #yielded = 0;
1328
- constructor(iterator, store, includeValues, limit) {
1361
+ #transaction;
1362
+ constructor(iterator, store, includeValues, limit, transaction) {
1329
1363
  this.iterator = iterator;
1330
1364
  this.store = store;
1331
1365
  this.#includeValues = includeValues;
1366
+ this.#transaction = transaction;
1332
1367
  this.#limit = limit ?? void 0;
1333
1368
  }
1334
1369
  [Symbol.iterator]() {
@@ -1336,7 +1371,7 @@ var DBIterator = class {
1336
1371
  }
1337
1372
  next() {
1338
1373
  if (this.#limit !== void 0 && this.#yielded >= this.#limit) {
1339
- this.iterator.return?.();
1374
+ this.#release(this.iterator.return());
1340
1375
  return DONE_RESULT;
1341
1376
  }
1342
1377
  let result;
@@ -1344,11 +1379,15 @@ var DBIterator = class {
1344
1379
  result = this.iterator.next();
1345
1380
  } catch (err) {
1346
1381
  try {
1347
- this.iterator.return?.();
1382
+ this.#release(this.iterator.return());
1348
1383
  } catch {}
1349
1384
  throw err;
1350
1385
  }
1351
1386
  if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
1387
+ if (result === ITERATOR_RESULT_RELEASED) {
1388
+ scheduleTrim(this.#transaction);
1389
+ return DONE_RESULT;
1390
+ }
1352
1391
  this.#yielded++;
1353
1392
  const includeValues = this.#includeValues;
1354
1393
  const value = {};
@@ -1369,20 +1408,23 @@ var DBIterator = class {
1369
1408
  };
1370
1409
  }
1371
1410
  return(value) {
1372
- this.iterator.return?.();
1411
+ this.#release(this.iterator.return());
1373
1412
  return {
1374
1413
  done: true,
1375
1414
  value
1376
1415
  };
1377
1416
  }
1378
1417
  throw(err) {
1379
- this.iterator.throw?.(err);
1418
+ this.#release(this.iterator.throw(err));
1380
1419
  throw err;
1381
1420
  }
1421
+ #release(pooled) {
1422
+ if (pooled) scheduleTrim(this.#transaction);
1423
+ this.#transaction = void 0;
1424
+ }
1382
1425
  };
1383
1426
 
1384
- //#endregion
1385
- //#region src/encoding.ts
1427
+ // src/encoding.ts
1386
1428
  /**
1387
1429
  * Initializes the key encoder functions.
1388
1430
  *
@@ -1449,8 +1491,7 @@ function createFixedBuffer(size) {
1449
1491
  return buffer;
1450
1492
  }
1451
1493
 
1452
- //#endregion
1453
- //#region src/store.ts
1494
+ // src/store.ts
1454
1495
  const { ONLY_IF_IN_MEMORY_CACHE_FLAG, NOT_IN_MEMORY_CACHE_FLAG, ALWAYS_CREATE_NEW_BUFFER_FLAG, FRESH_VERSION_FLAG, POPULATE_VERSION_FLAG, ITERATOR_REVERSE_FLAG, ITERATOR_INCLUSIVE_END_FLAG, ITERATOR_EXCLUSIVE_START_FLAG, ITERATOR_INCLUDE_VALUES_FLAG, ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG, ITERATOR_CONTEXT_IS_TRANSACTION_FLAG, ITERATOR_HAS_TRANSACTION_ID_FLAG } = constants;
1455
1496
  const KEY_BUFFER_SIZE = 4096;
1456
1497
  const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
@@ -1751,6 +1792,10 @@ var Store = class {
1751
1792
  */
1752
1793
  writeBufferSize;
1753
1794
  /**
1795
+ * The memtable arena block size in bytes; `0` or unset is automatic.
1796
+ */
1797
+ arenaBlockSize;
1798
+ /**
1754
1799
  * The function used to encode keys using the shared `keyBuffer`.
1755
1800
  */
1756
1801
  writeKey;
@@ -1803,6 +1848,7 @@ var Store = class {
1803
1848
  this.timestampFloorLog = options?.timestampFloorLog;
1804
1849
  this.verificationTable = options?.verificationTable;
1805
1850
  this.writeBufferSize = options?.writeBufferSize;
1851
+ this.arenaBlockSize = options?.arenaBlockSize;
1806
1852
  this.writeKey = writeKey;
1807
1853
  this.#columnFamilyOptions = options ? { ...options } : void 0;
1808
1854
  }
@@ -2053,7 +2099,9 @@ var Store = class {
2053
2099
  if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
2054
2100
  if (txnId !== void 0) flags |= ITERATOR_HAS_TRANSACTION_ID_FLAG;
2055
2101
  const advancedOptions = options !== void 0 && (options.adaptiveReadahead !== void 0 || options.asyncIO !== void 0 || options.autoReadaheadSize !== void 0 || options.backgroundPurgeOnIteratorCleanup !== void 0 || options.fillCache !== void 0 || options.readaheadSize !== void 0 || options.tailing !== void 0) ? options : void 0;
2056
- return new ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions, txnId), this, includeValues, options?.limit));
2102
+ const nativeIterator = new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions, txnId);
2103
+ const transaction = context !== this.db ? context : options?.transaction?._context;
2104
+ return new ExtendedIterable(new DBIterator(nativeIterator, this, includeValues, options?.limit, transaction));
2057
2105
  }
2058
2106
  getSync(context, key, alwaysCreateNewBuffer = false, options) {
2059
2107
  const keyParam = getKeyParam(this.encodeKey(key));
@@ -2167,7 +2215,8 @@ var Store = class {
2167
2215
  transactionLogsPath: this.transactionLogsPath,
2168
2216
  timestampFloorLog: this.timestampFloorLog,
2169
2217
  verificationTable: this.verificationTable,
2170
- writeBufferSize: this.writeBufferSize
2218
+ writeBufferSize: this.writeBufferSize,
2219
+ arenaBlockSize: this.arenaBlockSize
2171
2220
  });
2172
2221
  this.identityPath = this.db.identityPath;
2173
2222
  return false;
@@ -2273,8 +2322,7 @@ function getKeyParam(keyBuffer) {
2273
2322
  return keyBuffer.length;
2274
2323
  }
2275
2324
 
2276
- //#endregion
2277
- //#region src/transaction.ts
2325
+ // src/transaction.ts
2278
2326
  /**
2279
2327
  * Sentinel value returned by `commit()` when `coordinatedRetry: true` and the
2280
2328
  * transaction encountered an IsBusy conflict. The native layer parks on VT
@@ -2298,10 +2346,18 @@ var TransactionAlreadyAbortedError = class extends Error {
2298
2346
  */
2299
2347
  var TransactionRetryableError = class extends Error {
2300
2348
  hasLog;
2349
+ /**
2350
+ * The attempt was refused before writing its transaction-log batch (`transactionLogAbortMarkers`):
2351
+ * an older transaction held a write intent on one of its keys (`transactionLogWriteIntents`), or, on a retry, RocksDB's conflict
2352
+ * check found a write that already committed (see `lastConflictKind()`). Nothing of it is in the
2353
+ * log, so a retry may stage its batch afresh.
2354
+ */
2355
+ refusedBeforeLog;
2301
2356
  txn;
2302
2357
  constructor(error, txn) {
2303
2358
  super(error.message);
2304
2359
  this.hasLog = error.hasLog ?? false;
2360
+ this.refusedBeforeLog = error.refusedBeforeLog ?? false;
2305
2361
  this.txn = txn;
2306
2362
  }
2307
2363
  };
@@ -2331,6 +2387,38 @@ var TransactionAbandonedError = class extends Error {
2331
2387
  }
2332
2388
  };
2333
2389
  /**
2390
+ * `abort()` of a transaction whose transaction-log batch could not be marked aborted
2391
+ * (`transactionLogAbortMarkers`). The batch stays unresolved, so committed readers do not pass it and
2392
+ * the log store refuses appends, until a retry on the next append, database flush or store close
2393
+ * marks it.
2394
+ */
2395
+ var TransactionAbortUnresolvedError = class extends Error {
2396
+ code = "ERR_TRANSACTION_ABORT_UNRESOLVED";
2397
+ txn;
2398
+ constructor(error, txn) {
2399
+ super(error.message);
2400
+ this.txn = txn;
2401
+ }
2402
+ };
2403
+ /**
2404
+ * Why a transaction's most recent attempt was refused or retried, from {@link Transaction.lastConflictKind}.
2405
+ */
2406
+ const ConflictKind = {
2407
+ none: 0,
2408
+ /** Refused on an older transaction's write intent before writing its log batch. */
2409
+ intentBeforeLog: 1,
2410
+ /** Refused on an older transaction's write intent after its log batch was written. */
2411
+ intentAfterLog: 2,
2412
+ /** RocksDB found a conflict at commit. */
2413
+ atCommit: 3,
2414
+ /**
2415
+ * RocksDB's conflict check, run before the log write, found a write that already committed, or,
2416
+ * as at commit, could not validate because the memtable history it needs was flushed (needs a
2417
+ * RocksDB build that exports the check).
2418
+ */
2419
+ checkBeforeLog: 4
2420
+ };
2421
+ /**
2334
2422
  * Provides transaction level operations to a transaction callback.
2335
2423
  */
2336
2424
  var Transaction = class extends DBI {
@@ -2345,9 +2433,10 @@ var Transaction = class extends DBI {
2345
2433
  if (store.readOnly) {
2346
2434
  super(store);
2347
2435
  this.#txn = { id: 0 };
2348
- this.abandonWrites = this.abort = this.commitSync = this.setTimestamp = () => {};
2436
+ this.abandonWrites = this.abort = this.commitSync = this.setTimestamp = this.setParkDeadline = () => {};
2349
2437
  this.commit = async () => {};
2350
- this.getTimestamp = () => 0;
2438
+ this.getTimestamp = this.lastConflictKind = () => 0;
2439
+ this.logWritten = () => false;
2351
2440
  } else {
2352
2441
  const txn = new NativeTransaction(store.db, options);
2353
2442
  super(store, txn);
@@ -2373,6 +2462,7 @@ var Transaction = class extends DBI {
2373
2462
  this.#txn.abort();
2374
2463
  } catch (err) {
2375
2464
  if (err instanceof Error && "code" in err && err.code === "ERR_TRANSACTION_ABANDONED") throw new TransactionAbandonedError(err, this);
2465
+ if (err instanceof Error && "code" in err && err.code === "ERR_TRANSACTION_ABORT_UNRESOLVED") throw new TransactionAbortUnresolvedError(err, this);
2376
2466
  throw err;
2377
2467
  }
2378
2468
  }
@@ -2441,6 +2531,28 @@ var Transaction = class extends DBI {
2441
2531
  return this.#txn.getTimestamp();
2442
2532
  }
2443
2533
  /**
2534
+ * Whether this transaction's batch is in the transaction log. A commit conflict after the batch
2535
+ * was written keeps it for the retry, which must not stage it again; an attempt refused before
2536
+ * writing it (`refusedBeforeLog`) leaves nothing.
2537
+ */
2538
+ logWritten() {
2539
+ return this.#txn.logWritten();
2540
+ }
2541
+ /**
2542
+ * Why the most recent attempt was refused or retried; one of {@link ConflictKind}.
2543
+ */
2544
+ lastConflictKind() {
2545
+ return this.#txn.lastConflictKind();
2546
+ }
2547
+ /**
2548
+ * Bounds coordinated-retry parks: past `deadline` (epoch milliseconds) a parked commit resolves
2549
+ * `RETRY_NOW` at once, so a caller enforcing its own deadline can abort instead of waiting out
2550
+ * the default park bound. 0 clears it.
2551
+ */
2552
+ setParkDeadline(deadline) {
2553
+ this.#txn.setParkDeadline(deadline);
2554
+ }
2555
+ /**
2444
2556
  * Get the transaction id.
2445
2557
  */
2446
2558
  get id() {
@@ -2461,8 +2573,8 @@ var Transaction = class extends DBI {
2461
2573
  }
2462
2574
  };
2463
2575
 
2464
- //#endregion
2465
- //#region src/database.ts
2576
+ // src/database.ts
2577
+ const backoffCell = new Int32Array(new SharedArrayBuffer(4));
2466
2578
  const BACKGROUND_ERROR_SEVERITY_NAMES = [
2467
2579
  "none",
2468
2580
  "soft",
@@ -2872,6 +2984,19 @@ var RocksDatabase = class RocksDatabase extends DBI {
2872
2984
  return this.store.db.flushSync(options);
2873
2985
  }
2874
2986
  /**
2987
+ * Syncs the write-ahead log to stable storage (`DB::SyncWAL`), so every WAL-backed write
2988
+ * committed before the call survives a power loss afterwards. Unlike {@link flush} it writes
2989
+ * no SST file and never stalls writers. Not covered: writes that skipped the WAL
2990
+ * (`disableWAL`), and transaction-log files, which keep their own durability. Runs on the
2991
+ * libuv threadpool; RocksDB serializes concurrent syncs of one WAL, so keep one in flight
2992
+ * per database rather than parking pool workers on a queue of them.
2993
+ *
2994
+ * On a read-only database the returned promise resolves without syncing.
2995
+ */
2996
+ syncWal() {
2997
+ return new Promise((resolve, reject) => this.store.db.syncWal(resolve, reject));
2998
+ }
2999
+ /**
2875
3000
  * Returns the most recent {@link BackgroundError} observed on this database, or
2876
3001
  * `null` when none has occurred. The `'error'` event is the push counterpart;
2877
3002
  * this is the pull equivalent for on-demand checks (e.g. a health probe) and
@@ -3340,7 +3465,10 @@ var RocksDatabase = class RocksDatabase extends DBI {
3340
3465
  async transaction(callback, options) {
3341
3466
  if (typeof callback !== "function") throw new TypeError("Callback must be a function");
3342
3467
  const maxRetries = options?.maxRetries ?? 3;
3343
- const txn = new Transaction(this.store, options);
3468
+ const txn = new Transaction(this.store, options?.retryOnBusy === false && options.coordinatedRetry ? {
3469
+ ...options,
3470
+ coordinatedRetry: false
3471
+ } : options);
3344
3472
  let result;
3345
3473
  this.notify("begin-transaction");
3346
3474
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
@@ -3354,7 +3482,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
3354
3482
  commitResult = await txn.commit();
3355
3483
  } catch (commitErr) {
3356
3484
  if (commitErr instanceof TransactionAlreadyAbortedError) return;
3357
- if (commitErr instanceof TransactionRetryableError && (options?.retryOnBusy ?? commitErr.hasLog) && attempt < maxRetries) continue;
3485
+ if (commitErr instanceof TransactionRetryableError && (options?.retryOnBusy ?? (commitErr.hasLog || commitErr.refusedBeforeLog)) && attempt < maxRetries) continue;
3358
3486
  this.#abandonTransaction(txn, commitErr);
3359
3487
  return;
3360
3488
  }
@@ -3386,10 +3514,13 @@ var RocksDatabase = class RocksDatabase extends DBI {
3386
3514
  if (typeof callback !== "function") throw new TypeError("Callback must be a function");
3387
3515
  const maxRetries = options?.maxRetries ?? 3;
3388
3516
  const isRetryable = (err, attempt) => {
3389
- return err instanceof TransactionRetryableError && (options?.retryOnBusy ?? err.hasLog) && attempt <= maxRetries;
3517
+ if (!(err instanceof TransactionRetryableError && (options?.retryOnBusy ?? (err.hasLog || err.refusedBeforeLog)) && attempt <= maxRetries)) return false;
3518
+ const kind = txn.lastConflictKind();
3519
+ if (kind === ConflictKind.intentBeforeLog || kind === ConflictKind.intentAfterLog) Atomics.wait(backoffCell, 0, 0, Math.min(2 ** attempt, 50));
3520
+ return true;
3390
3521
  };
3522
+ const txn = new Transaction(this.store, options);
3391
3523
  const runAttempt = (attempt) => {
3392
- const txn = new Transaction(this.store, options);
3393
3524
  let result;
3394
3525
  try {
3395
3526
  result = callback(txn, attempt);
@@ -3405,7 +3536,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
3405
3536
  if (isRetryable(commitErr, attempt)) return runAttempt(attempt + 1);
3406
3537
  this.#abandonTransaction(txn, commitErr);
3407
3538
  }
3408
- });
3539
+ }, (callbackErr) => this.#abortTransaction(txn, callbackErr));
3409
3540
  try {
3410
3541
  txn.commitSync();
3411
3542
  return result;
@@ -3430,7 +3561,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
3430
3561
  try {
3431
3562
  txn.abort();
3432
3563
  } catch (abortErr) {
3433
- if (abortErr instanceof TransactionAbandonedError) throw abortErr;
3564
+ if (abortErr instanceof TransactionAbandonedError || abortErr instanceof TransactionAbortUnresolvedError) throw abortErr;
3434
3565
  }
3435
3566
  throw commitErr;
3436
3567
  }
@@ -3545,10 +3676,10 @@ var RocksDatabase = class RocksDatabase extends DBI {
3545
3676
  }
3546
3677
  };
3547
3678
 
3548
- //#endregion
3549
- //#region src/parse-transaction-log.ts
3550
- const { TRANSACTION_LOG_TOKEN, TRANSACTION_LOG_ENTRY_HEADER_SIZE: TRANSACTION_LOG_ENTRY_HEADER_SIZE$1 } = constants;
3679
+ // src/parse-transaction-log.ts
3680
+ const { TRANSACTION_LOG_TOKEN, TRANSACTION_LOG_ENTRY_HEADER_SIZE: TRANSACTION_LOG_ENTRY_HEADER_SIZE$1, TRANSACTION_LOG_ENTRY_LAST_FLAG: TRANSACTION_LOG_ENTRY_LAST_FLAG$1, TRANSACTION_LOG_ENTRY_ABORTED_FLAG: TRANSACTION_LOG_ENTRY_ABORTED_FLAG$1 } = constants;
3551
3681
  const MIN_VALID_TIMESTAMP = Date.UTC(2026, 0, 27);
3682
+ const VALID_FLAGS_MASK = TRANSACTION_LOG_ENTRY_LAST_FLAG$1 | TRANSACTION_LOG_ENTRY_ABORTED_FLAG$1;
3552
3683
  /**
3553
3684
  * Loads an entire transaction log file into memory.
3554
3685
  * @param path - The path to the transaction log file.
@@ -3586,6 +3717,8 @@ function parseTransactionLog(path, options = {}) {
3586
3717
  if (!Number.isFinite(timestamp) || timestamp < MIN_VALID_TIMESTAMP) anomalies.push(`Header timestamp ${timestamp} predates 2026-01-27 (possible corruption)`);
3587
3718
  const entries = [];
3588
3719
  let entryAnomalyCount = 0;
3720
+ let previousEndedTransaction = true;
3721
+ let previousTimestamp = NaN;
3589
3722
  while (fileOffset < size) {
3590
3723
  const timestamp = read(8).readDoubleBE(0);
3591
3724
  if (timestamp === 0) {
@@ -3602,7 +3735,11 @@ function parseTransactionLog(path, options = {}) {
3602
3735
  const data = read(length);
3603
3736
  const entryAnomalies = [];
3604
3737
  if (!Number.isFinite(timestamp) || timestamp < MIN_VALID_TIMESTAMP) entryAnomalies.push(`timestamp ${timestamp} predates 2026-01-27 (possible corruption)`);
3605
- if ((flags & -2) !== 0) entryAnomalies.push(`flags 0x${flags.toString(16).padStart(2, "0")} contains undefined bits (expected 0x00 or 0x01)`);
3738
+ if ((flags & ~VALID_FLAGS_MASK) !== 0) entryAnomalies.push(`flags 0x${flags.toString(16).padStart(2, "0")} contains undefined bits (expected 0x00-0x03)`);
3739
+ const startsTransaction = previousEndedTransaction || timestamp !== previousTimestamp;
3740
+ if ((flags & TRANSACTION_LOG_ENTRY_ABORTED_FLAG$1) !== 0 && !startsTransaction) entryAnomalies.push("abort flag on an entry that does not start a transaction");
3741
+ previousEndedTransaction = (flags & TRANSACTION_LOG_ENTRY_LAST_FLAG$1) !== 0;
3742
+ previousTimestamp = timestamp;
3606
3743
  const entry = {
3607
3744
  timestamp,
3608
3745
  length,
@@ -3631,11 +3768,11 @@ function parseTransactionLog(path, options = {}) {
3631
3768
  }
3632
3769
  }
3633
3770
 
3634
- //#endregion
3635
- //#region src/transaction-log-reader.ts
3771
+ // src/transaction-log-reader.ts
3636
3772
  const FLOAT_TO_UINT32 = /* @__PURE__ */ new Float64Array(1);
3637
3773
  const UINT32_FROM_FLOAT = new Uint32Array(FLOAT_TO_UINT32.buffer);
3638
- const { TRANSACTION_LOG_FILE_HEADER_SIZE, TRANSACTION_LOG_ENTRY_HEADER_SIZE } = constants;
3774
+ const EXACT_START_SPAN = /* @__PURE__ */ new Float64Array(3);
3775
+ const { TRANSACTION_LOG_FILE_HEADER_SIZE, TRANSACTION_LOG_ENTRY_HEADER_SIZE, TRANSACTION_LOG_ENTRY_LAST_FLAG, TRANSACTION_LOG_ENTRY_ABORTED_FLAG } = constants;
3639
3776
  /**
3640
3777
  * Number of consecutive well-formed frames that, on their own, signal that real log data has
3641
3778
  * resumed after a framing break. Kept in step with `RESYNC_MIN_FRAMES` in
@@ -3671,6 +3808,32 @@ var CorruptFrameError = class extends RangeError {
3671
3808
  }
3672
3809
  };
3673
3810
  /**
3811
+ * Thrown when an iterator advances past segments it never read and that can no longer be read —
3812
+ * typically ones retention purged while the reader lagged. The entries they held cannot be
3813
+ * recovered from this log, so a consumer replicating it must not treat the stream as contiguous.
3814
+ * Missing sequence numbers prove only that continuity is unknown, not that each held entries.
3815
+ *
3816
+ * The iterator is left positioned at the start of `resumeLogId`, so a caller that accepts the
3817
+ * discontinuity calls `next()` again to continue from there.
3818
+ *
3819
+ * Not a `RangeError`: this is not broken framing, and consumers classify those by class.
3820
+ */
3821
+ var TransactionLogGapError = class extends Error {
3822
+ /** Sequence number of the last segment this iterator read up to. */
3823
+ logId;
3824
+ /** Sequence number of the segment iteration resumes in. */
3825
+ resumeLogId;
3826
+ /** Segments between `logId` and `resumeLogId` that could not be read. Empty segments are not counted. */
3827
+ unavailableLogCount;
3828
+ constructor(logId, resumeLogId, unavailableLogCount) {
3829
+ super(`Transaction log iterator skipped ${unavailableLogCount} unavailable segment(s) between log ${logId} and log ${resumeLogId}; entries in them were not read`);
3830
+ this.name = "TransactionLogGapError";
3831
+ this.logId = logId;
3832
+ this.resumeLogId = resumeLogId;
3833
+ this.unavailableLogCount = unavailableLogCount;
3834
+ }
3835
+ };
3836
+ /**
3674
3837
  * Whether a complete, in-bounds frame begins at `pos`. The only sane bound on an entry's length is
3675
3838
  * the written extent: a single entry can legitimately exceed the rotation threshold (the first
3676
3839
  * entry written to a fresh file is always written in full). A zero timestamp is the
@@ -3746,6 +3909,14 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3746
3909
  let dataView;
3747
3910
  let logBuffer = this._currentLogBuffer?.deref();
3748
3911
  let foundExactStart = false;
3912
+ let stopLogId = 0;
3913
+ let stopPosition = 0;
3914
+ let tailLogId = 0;
3915
+ let tailPosition = 0;
3916
+ let segmentRemovals = 0;
3917
+ let previousEndedTransaction = true;
3918
+ let previousTimestamp = NaN;
3919
+ let skippingAborted = false;
3749
3920
  if (start === void 0 && !startFromLastFlushed) {
3750
3921
  position = size;
3751
3922
  if (position === 0) position = TRANSACTION_LOG_FILE_HEADER_SIZE;
@@ -3755,11 +3926,32 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3755
3926
  FLOAT_TO_UINT32[0] = this._getLastFlushed();
3756
3927
  if (FLOAT_TO_UINT32[0] === 0) FLOAT_TO_UINT32[0] = this._findPosition(0);
3757
3928
  start ??= 0;
3929
+ } else if (exactStart) {
3930
+ const startPosition = this._findExactStart(start, EXACT_START_SPAN);
3931
+ FLOAT_TO_UINT32[0] = EXACT_START_SPAN[0];
3932
+ stopLogId = UINT32_FROM_FLOAT[1];
3933
+ stopPosition = UINT32_FROM_FLOAT[0];
3934
+ FLOAT_TO_UINT32[0] = EXACT_START_SPAN[1];
3935
+ tailLogId = UINT32_FROM_FLOAT[1];
3936
+ tailPosition = UINT32_FROM_FLOAT[0];
3937
+ segmentRemovals = EXACT_START_SPAN[2];
3938
+ FLOAT_TO_UINT32[0] = startPosition;
3758
3939
  } else FLOAT_TO_UINT32[0] = this._findPosition(start);
3759
3940
  logId = UINT32_FROM_FLOAT[1];
3760
3941
  position = UINT32_FROM_FLOAT[0];
3761
3942
  if (position === 0) position = TRANSACTION_LOG_FILE_HEADER_SIZE;
3762
3943
  }
3944
+ const positionedAt = position;
3945
+ if (stopLogId !== 0 && pastStop(logId, position, stopLogId, stopPosition)) {
3946
+ stopLogId = 0;
3947
+ if (skipToTail(this, logId, position, tailLogId, tailPosition, segmentRemovals)) {
3948
+ logId = tailLogId;
3949
+ position = tailPosition;
3950
+ logBuffer = tailLogBuffer ?? logBuffer;
3951
+ }
3952
+ tailLogBuffer = void 0;
3953
+ }
3954
+ let gapFloor = logId;
3763
3955
  if (logBuffer === void 0 || logBuffer.logId !== logId) {
3764
3956
  logBuffer = getLogMemoryMap(this, logId);
3765
3957
  if (logBuffer && latestLogId === logId && !readUncommitted && this._currentLogBuffer?.deref() !== logBuffer) this._currentLogBuffer = new WeakRef(logBuffer);
@@ -3769,6 +3961,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3769
3961
  logBuffer.size = 0;
3770
3962
  logBuffer.dataView = new DataView(logBuffer.buffer);
3771
3963
  size = 0;
3964
+ if (positionedAt <= TRANSACTION_LOG_FILE_HEADER_SIZE || logId > 0 && this.getLogFileSize(logId) > 0) gapFloor = logId - 1;
3772
3965
  }
3773
3966
  }
3774
3967
  dataView = logBuffer.dataView;
@@ -3778,7 +3971,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3778
3971
  if (cachedSize === void 0) size = logBuffer.size = readableExtent(logBuffer);
3779
3972
  else size = cachedSize;
3780
3973
  }
3781
- return {
3974
+ const iterator = {
3782
3975
  [Symbol.iterator]() {
3783
3976
  return this;
3784
3977
  },
@@ -3790,13 +3983,17 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3790
3983
  if (latestLogId > logBuffer.logId) {
3791
3984
  size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
3792
3985
  if (position >= size) {
3793
- const nextLogBuffer = nextReadableLogBuffer(transactionLog, logBuffer.logId, latestLogId);
3986
+ const fromLogId = logBuffer.logId;
3987
+ const nextLogBuffer = nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor);
3794
3988
  if (nextLogBuffer) {
3989
+ const unavailable = unavailableLogCount;
3795
3990
  dataView = nextLogBuffer.dataView;
3796
3991
  logBuffer = nextLogBuffer;
3797
3992
  if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
3798
3993
  else size = latestSize;
3799
3994
  position = TRANSACTION_LOG_FILE_HEADER_SIZE;
3995
+ previousEndedTransaction = true;
3996
+ if (unavailable > 0) throw new TransactionLogGapError(Math.max(fromLogId, gapFloor), logBuffer.logId, unavailable);
3800
3997
  }
3801
3998
  }
3802
3999
  }
@@ -3816,30 +4013,55 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3816
4013
  if (position + TRANSACTION_LOG_ENTRY_HEADER_SIZE > limit) {
3817
4014
  const broken = corruptFrame(`Corrupt transaction log: truncated entry header at position ${position.toString(16)} of log ${logBuffer.logId} (available=${limit - position})`, transactionLog, dataView, logBuffer, position, limit, size, !!readUncommitted);
3818
4015
  position = broken.nextPosition;
4016
+ previousEndedTransaction = true;
3819
4017
  throw broken.error;
3820
4018
  }
3821
4019
  const length = dataView.getUint32(position + 8);
3822
4020
  if (length === 0 || position + TRANSACTION_LOG_ENTRY_HEADER_SIZE + length > limit) {
3823
4021
  const broken = corruptFrame(length === 0 ? `Corrupt transaction log entry at position ${position.toString(16)} of log ${logBuffer.logId}: zero-length entry` : `Corrupt transaction log entry at position ${position.toString(16)} of log ${logBuffer.logId}: declared length ${length} overruns the log (limit=${limit})`, transactionLog, dataView, logBuffer, position, limit, size, !!readUncommitted);
3824
4022
  position = broken.nextPosition;
4023
+ previousEndedTransaction = true;
3825
4024
  throw broken.error;
3826
4025
  }
3827
4026
  position += TRANSACTION_LOG_ENTRY_HEADER_SIZE;
4027
+ const flags = logBuffer[position - 1];
4028
+ const aborted = (flags & TRANSACTION_LOG_ENTRY_ABORTED_FLAG) !== 0;
4029
+ if (previousEndedTransaction || timestamp !== previousTimestamp || aborted) skippingAborted = aborted;
4030
+ previousEndedTransaction = (flags & TRANSACTION_LOG_ENTRY_LAST_FLAG) !== 0;
4031
+ previousTimestamp = timestamp;
3828
4032
  let matchesRange;
3829
- if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
4033
+ if (skippingAborted) matchesRange = false;
4034
+ else if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
3830
4035
  else if (exactStart) {
4036
+ matchesRange = false;
3831
4037
  if (timestamp === start) {
3832
4038
  matchesRange = !exclusiveStart;
3833
4039
  foundExactStart = true;
3834
- } else matchesRange = false;
4040
+ stopLogId = 0;
4041
+ }
3835
4042
  } else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
4043
+ if (!matchesRange && stopLogId !== 0 && pastStop(logBuffer.logId, position + length, stopLogId, stopPosition)) {
4044
+ stopLogId = 0;
4045
+ position += length;
4046
+ if (skipToTail(transactionLog, logBuffer.logId, position, tailLogId, tailPosition, segmentRemovals)) {
4047
+ if (tailLogBuffer) {
4048
+ logBuffer = tailLogBuffer;
4049
+ dataView = tailLogBuffer.dataView;
4050
+ }
4051
+ position = tailPosition;
4052
+ previousEndedTransaction = true;
4053
+ size = 0;
4054
+ }
4055
+ tailLogBuffer = void 0;
4056
+ return iterator.next();
4057
+ }
3836
4058
  const entryStart = position;
3837
4059
  position += length;
3838
4060
  if (matchesRange) return {
3839
4061
  done: false,
3840
4062
  value: {
3841
4063
  timestamp,
3842
- endTxn: Boolean(logBuffer[entryStart - 1] & 1),
4064
+ endTxn: previousEndedTransaction,
3843
4065
  data: logBuffer.subarray(entryStart, position)
3844
4066
  }
3845
4067
  };
@@ -3850,11 +4072,13 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3850
4072
  size = logBuffer.size ?? readableExtent(logBuffer);
3851
4073
  if (!readUncommitted) logBuffer.size = size;
3852
4074
  if (position < size) continue;
3853
- const nextLogBuffer = nextReadableLogBuffer(transactionLog, logBuffer.logId, latestLogId);
4075
+ const fromLogId = logBuffer.logId;
4076
+ const nextLogBuffer = nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor);
3854
4077
  if (!nextLogBuffer) return {
3855
4078
  done: true,
3856
4079
  value: void 0
3857
4080
  };
4081
+ const unavailable = unavailableLogCount;
3858
4082
  logBuffer = nextLogBuffer;
3859
4083
  dataView = logBuffer.dataView;
3860
4084
  if (latestLogId === logBuffer.logId) size = latestSize;
@@ -3866,6 +4090,8 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3866
4090
  } else size = cachedSize;
3867
4091
  }
3868
4092
  position = TRANSACTION_LOG_FILE_HEADER_SIZE;
4093
+ previousEndedTransaction = true;
4094
+ if (unavailable > 0) throw new TransactionLogGapError(Math.max(fromLogId, gapFloor), logBuffer.logId, unavailable);
3869
4095
  }
3870
4096
  }
3871
4097
  }
@@ -3875,7 +4101,29 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3875
4101
  };
3876
4102
  }
3877
4103
  };
4104
+ return iterator;
3878
4105
  } });
4106
+ function pastStop(logId, position, stopLogId, stopPosition) {
4107
+ return logId > stopLogId || logId === stopLogId && position >= stopPosition;
4108
+ }
4109
+ let tailLogBuffer;
4110
+ /**
4111
+ * Whether an exact-start lookup at `position` of `fromLogId` may take its one-shot jump to the tail.
4112
+ * next() calls it on a frame boundary, so a mapping error leaves a resumable cursor; from query()
4113
+ * the error escapes before any iterator exists. The removal
4114
+ * check follows the mapping and holds the store's lock, so a purge either declines the jump (the
4115
+ * scan then reports the gap) or follows it like a purge of segments already read.
4116
+ */
4117
+ function skipToTail(transactionLog, fromLogId, position, tailLogId, tailPosition, segmentRemovals) {
4118
+ tailLogBuffer = void 0;
4119
+ if (tailLogId === fromLogId) return tailPosition > position;
4120
+ if (tailLogId < fromLogId) return false;
4121
+ const mapped = getLogMemoryMap(transactionLog, tailLogId);
4122
+ if (mapped === void 0 || transactionLog._segmentRemovals() !== segmentRemovals) return false;
4123
+ tailLogBuffer = mapped;
4124
+ return true;
4125
+ }
4126
+ let unavailableLogCount = 0;
3879
4127
  /**
3880
4128
  * Maps the next readable segment after `fromLogId`, skipping a run retention has deleted —
3881
4129
  * stopping at the hole wedges the iterator there for every later poll (invariant 30).
@@ -3884,15 +4132,22 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3884
4132
  * `_findPosition(0)`, which names the bottom of the contiguous run ending at the current segment
3885
4133
  * and so lands *past* a survivor sitting between two holes.
3886
4134
  */
3887
- function nextReadableLogBuffer(transactionLog, fromLogId, latestLogId) {
4135
+ function nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor) {
4136
+ unavailableLogCount = 0;
3888
4137
  let candidateLogId = fromLogId + 1;
3889
4138
  while (candidateLogId <= latestLogId) {
3890
4139
  const logBuffer = getLogMemoryMap(transactionLog, candidateLogId);
3891
4140
  if (logBuffer) {
3892
4141
  if (candidateLogId === latestLogId || readableExtent(logBuffer) > TRANSACTION_LOG_FILE_HEADER_SIZE) return logBuffer;
3893
- } else if (transactionLog.getLogFileSize(candidateLogId) > TRANSACTION_LOG_FILE_HEADER_SIZE) return;
3894
- candidateLogId = transactionLog._nextLogId(candidateLogId);
3895
- if (candidateLogId === 0) return;
4142
+ } else {
4143
+ const fileSize = transactionLog.getLogFileSize(candidateLogId);
4144
+ if (fileSize > TRANSACTION_LOG_FILE_HEADER_SIZE) return;
4145
+ if (fileSize === 0 && candidateLogId > gapFloor) unavailableLogCount++;
4146
+ }
4147
+ const nextLogId = transactionLog._nextLogId(candidateLogId);
4148
+ if (nextLogId === 0) return;
4149
+ unavailableLogCount += Math.max(0, nextLogId - 1 - Math.max(candidateLogId, gapFloor));
4150
+ candidateLogId = nextLogId;
3896
4151
  }
3897
4152
  }
3898
4153
  /**
@@ -3946,13 +4201,11 @@ function loadLastPosition(transactionLog, readUncommitted) {
3946
4201
  };
3947
4202
  }
3948
4203
 
3949
- //#endregion
3950
- //#region src/index.ts
4204
+ // src/index.ts
3951
4205
  const versions = {
3952
4206
  rocksdb: version,
3953
- "rocksdb-js": "2.11.0"
4207
+ "rocksdb-js": "2.13.0"
3954
4208
  };
3955
4209
 
3956
- //#endregion
3957
- export { BackgroundError, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionLog, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getRegistryStatus, getWriteBufferManagerStats, parseTransactionLog, registryStatus, shutdown, stats, steadyClockNow, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
4210
+ export { BackgroundError, ConflictKind, CorruptFrameError, CountEstimator, DBI, DBIterator, RocksDatabase, Store, Transaction, TransactionAbortUnresolvedError, TransactionLog, TransactionLogGapError, backups, constants, coolTransactionLogs, currentThreadId, fileLockRelease, getRegistryStatus, getWriteBufferManagerStats, parseTransactionLog, registryStatus, shutdown, stats, steadyClockNow, supportedCompression, tryFileLock, validateTransactionLogStore, versions };
3958
4211
  //# sourceMappingURL=index.mjs.map