@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.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- //#region \0rolldown/runtime.js
2
+ // \0rolldown/runtime.js
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -25,7 +25,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  enumerable: true
26
26
  }) : target, mod));
27
27
 
28
- //#endregion
29
28
  let node_child_process = require("node:child_process");
30
29
  let node_fs = require("node:fs");
31
30
  let node_module = require("node:module");
@@ -37,7 +36,7 @@ ordered_binary = __toESM(ordered_binary, 1);
37
36
  let _harperfast_extended_iterable = require("@harperfast/extended-iterable");
38
37
  let msgpackr = require("msgpackr");
39
38
 
40
- //#region src/load-binding.ts
39
+ // src/load-binding.ts
41
40
  const nativeExtRE = /\.node$/;
42
41
  const req = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
43
42
  /**
@@ -172,7 +171,7 @@ const currentThreadId = binding.currentThreadId;
172
171
  * Reads the process-wide steady clock: `std::chrono::steady_clock` as
173
172
  * fractional milliseconds from an unspecified origin that is fixed for the life
174
173
  * of the process. Every sample in the process — main thread and every
175
- * `worker_threads` worker, whenever started or restarted, on Node, Bun and Deno
174
+ * `worker_threads` worker, whenever started or restarted, on Node and Bun
176
175
  * — is in one domain, and the difference between two samples is real elapsed
177
176
  * time unaffected by wall-clock steps. Non-decreasing but not unique (two
178
177
  * samples may be equal; treat `==` as "not later"). Windows QPC samples from
@@ -210,12 +209,30 @@ const transactionLogMapCount = binding.transactionLogMapCount;
210
209
  */
211
210
  const lockWakeCallbackCount = binding.lockWakeCallbackCount;
212
211
  /**
212
+ * The calling thread's counters for the per-transaction iterator pool: pooled
213
+ * iterators created, and idle ones reused by a later range. Internal — used by
214
+ * tests to prove a range was served by a reused iterator.
215
+ */
216
+ const iteratorPoolStats = binding.iteratorPoolStats;
217
+ /**
218
+ * Whether transaction `getRange()` reuses pooled native iterators (off with
219
+ * `ROCKSDB_JS_ITERATOR_POOL=0`). Internal.
220
+ */
221
+ const iteratorPoolEnabled = binding.iteratorPoolEnabled;
222
+ /**
213
223
  * Test-only: force the next `count` transaction commits to fail with TryAgain (rolled back, so
214
224
  * no data is committed), reproducing a stranded-snapshot conflict deterministically. Pass 0 to
215
225
  * disarm. Used by the ERR_TRY_AGAIN retry regression test.
216
226
  */
217
227
  const forceTryAgainForTesting = binding.forceTryAgainForTesting;
218
228
  /**
229
+ * Test-only: turn the RocksDB conflict check before the transaction-log write off (`true`) or back
230
+ * on, so a test can make a logged batch conflict at commit from a stale snapshot.
231
+ */
232
+ const disablePrelogConflictCheckForTesting = binding.disablePrelogConflictCheckForTesting;
233
+ /** Test-only: run the pre-log conflict check on retries alone (`true`, the default) or on every attempt. */
234
+ const setPrelogConflictCheckRetriesOnlyForTesting = binding.setPrelogConflictCheckRetriesOnlyForTesting;
235
+ /**
219
236
  * Test-only seam: makes the physical column-family drop report failure.
220
237
  * `0` = inert, `1` = fail without dropping (the retry performs the drop),
221
238
  * `2` = drop for real and then report failure (the retry resolves as
@@ -258,8 +275,7 @@ const nativeValidateTransactionLog = binding.validateTransactionLog;
258
275
  const stats = binding.stats;
259
276
  const version = binding.version;
260
277
 
261
- //#endregion
262
- //#region src/validate-transaction-log.ts
278
+ // src/validate-transaction-log.ts
263
279
  /**
264
280
  * Validates a transaction log store directory: every `<sequence>.txnlog` file
265
281
  * is checked for a valid header (token, version) and intact v1 entry framing
@@ -299,8 +315,7 @@ function validateTransactionLogStore(path, options) {
299
315
  return new Promise((resolve, reject) => nativeValidateTransactionLog(resolve, reject, path, options?.strict ?? false));
300
316
  }
301
317
 
302
- //#endregion
303
- //#region src/backup.ts
318
+ // src/backup.ts
304
319
  /** Subdirectory (under a backup directory) holding per-backup transaction log snapshots. */
305
320
  const TRANSACTION_LOGS_DIRNAME = "transaction_logs";
306
321
  /** Non-blocking existence check (`fs.existsSync` would block the event loop). */
@@ -550,6 +565,14 @@ const backups = {
550
565
  * Verifies a backup's file sizes, and optionally their checksums (which
551
566
  * requires reading all backed-up data).
552
567
  *
568
+ * Set `verifyTables: true` to also prove this build can read the backup
569
+ * before restoring it: every table file is opened, one at a time, and its
570
+ * footer (including `format_version`) and properties must be readable, and
571
+ * every compression algorithm it records must be supported by this build.
572
+ * A checksum-valid table written by a newer or differently built RocksDB
573
+ * otherwise passes and fails on the first read after a restore. Blob files
574
+ * are not checked.
575
+ *
553
576
  * When the backup was created with `transactionLogs: true`, the backup's
554
577
  * transaction log snapshot (`<backupDir>/transaction_logs/<backupId>/`) is
555
578
  * also validated — every log file's header and entry framing must be intact
@@ -557,7 +580,9 @@ const backups = {
557
580
  * is a verification failure). Set `verifyTransactionLogs: false` to skip.
558
581
  */
559
582
  async verify(backupDir, backupId, options) {
560
- await new Promise((resolve, reject) => nativeBackupVerify(resolve, reject, backupDir, backupId, options?.verifyWithChecksum ?? false));
583
+ const verifyTables = options?.verifyTables;
584
+ if (verifyTables !== void 0 && typeof verifyTables !== "boolean") throw new TypeError(`verifyTables must be a boolean, received ${verifyTables === null ? "null" : typeof verifyTables}`);
585
+ await new Promise((resolve, reject) => nativeBackupVerify(resolve, reject, backupDir, backupId, options?.verifyWithChecksum ?? false, verifyTables === true));
561
586
  if (options?.verifyTransactionLogs === false) return;
562
587
  const logsDir = (0, node_path.join)(backupDir, TRANSACTION_LOGS_DIRNAME, String(backupId));
563
588
  if (!await exists(logsDir)) return;
@@ -574,8 +599,7 @@ const backups = {
574
599
  }
575
600
  };
576
601
 
577
- //#endregion
578
- //#region src/count-estimator.ts
602
+ // src/count-estimator.ts
579
603
  /**
580
604
  * Below this many traversed entries the observed density is too noisy to
581
605
  * calibrate with, so `estimate()` returns the raw statistical estimate.
@@ -718,8 +742,7 @@ var CountEstimator = class {
718
742
  }
719
743
  };
720
744
 
721
- //#endregion
722
- //#region src/util.ts
745
+ // src/util.ts
723
746
  /**
724
747
  * Parses a duration string into milliseconds.
725
748
  *
@@ -790,8 +813,7 @@ function when(subject, callback, errback) {
790
813
  }
791
814
  }
792
815
 
793
- //#endregion
794
- //#region src/dbi.ts
816
+ // src/dbi.ts
795
817
  /**
796
818
  * The base class for all database operations. This base class is shared by
797
819
  * `RocksDatabase` and `Transaction`.
@@ -1076,8 +1098,7 @@ var DBI = class DBI {
1076
1098
  }
1077
1099
  };
1078
1100
 
1079
- //#endregion
1080
- //#region src/tar.ts
1101
+ // src/tar.ts
1081
1102
  /**
1082
1103
  * A minimal, dependency-free streaming USTAR (POSIX tar) encoder.
1083
1104
  *
@@ -1283,8 +1304,7 @@ var TarEncoder = class {
1283
1304
  }
1284
1305
  };
1285
1306
 
1286
- //#endregion
1287
- //#region src/backup-stream.ts
1307
+ // src/backup-stream.ts
1288
1308
  /** Native event discriminator: a new file header (vs. a payload chunk). */
1289
1309
  const EVENT_FILE = 0;
1290
1310
  /**
@@ -1328,9 +1348,22 @@ async function backupToStream(db, stream, options) {
1328
1348
  }
1329
1349
  }
1330
1350
 
1331
- //#endregion
1332
- //#region src/dbi-iterator.ts
1333
- const { ITERATOR_RESULT_DONE, ITERATOR_RESULT_FAST } = constants;
1351
+ // src/dbi-iterator.ts
1352
+ const { ITERATOR_RESULT_DONE, ITERATOR_RESULT_FAST, ITERATOR_RESULT_RELEASED } = constants;
1353
+ const transactionsToTrim = /* @__PURE__ */ new Set();
1354
+ function trimIdleIterators() {
1355
+ try {
1356
+ for (const transaction of transactionsToTrim) transaction.trimIterators();
1357
+ } finally {
1358
+ transactionsToTrim.clear();
1359
+ }
1360
+ }
1361
+ function scheduleTrim(transaction) {
1362
+ if (transaction !== void 0) {
1363
+ if (transactionsToTrim.size === 0) setImmediate(trimIdleIterators);
1364
+ transactionsToTrim.add(transaction);
1365
+ }
1366
+ }
1334
1367
  const DONE_RESULT = Object.freeze({
1335
1368
  done: true,
1336
1369
  value: void 0
@@ -1350,10 +1383,12 @@ var DBIterator = class {
1350
1383
  #includeValues;
1351
1384
  #limit;
1352
1385
  #yielded = 0;
1353
- constructor(iterator, store, includeValues, limit) {
1386
+ #transaction;
1387
+ constructor(iterator, store, includeValues, limit, transaction) {
1354
1388
  this.iterator = iterator;
1355
1389
  this.store = store;
1356
1390
  this.#includeValues = includeValues;
1391
+ this.#transaction = transaction;
1357
1392
  this.#limit = limit ?? void 0;
1358
1393
  }
1359
1394
  [Symbol.iterator]() {
@@ -1361,7 +1396,7 @@ var DBIterator = class {
1361
1396
  }
1362
1397
  next() {
1363
1398
  if (this.#limit !== void 0 && this.#yielded >= this.#limit) {
1364
- this.iterator.return?.();
1399
+ this.#release(this.iterator.return());
1365
1400
  return DONE_RESULT;
1366
1401
  }
1367
1402
  let result;
@@ -1369,11 +1404,15 @@ var DBIterator = class {
1369
1404
  result = this.iterator.next();
1370
1405
  } catch (err) {
1371
1406
  try {
1372
- this.iterator.return?.();
1407
+ this.#release(this.iterator.return());
1373
1408
  } catch {}
1374
1409
  throw err;
1375
1410
  }
1376
1411
  if (result === ITERATOR_RESULT_DONE) return DONE_RESULT;
1412
+ if (result === ITERATOR_RESULT_RELEASED) {
1413
+ scheduleTrim(this.#transaction);
1414
+ return DONE_RESULT;
1415
+ }
1377
1416
  this.#yielded++;
1378
1417
  const includeValues = this.#includeValues;
1379
1418
  const value = {};
@@ -1394,20 +1433,23 @@ var DBIterator = class {
1394
1433
  };
1395
1434
  }
1396
1435
  return(value) {
1397
- this.iterator.return?.();
1436
+ this.#release(this.iterator.return());
1398
1437
  return {
1399
1438
  done: true,
1400
1439
  value
1401
1440
  };
1402
1441
  }
1403
1442
  throw(err) {
1404
- this.iterator.throw?.(err);
1443
+ this.#release(this.iterator.throw(err));
1405
1444
  throw err;
1406
1445
  }
1446
+ #release(pooled) {
1447
+ if (pooled) scheduleTrim(this.#transaction);
1448
+ this.#transaction = void 0;
1449
+ }
1407
1450
  };
1408
1451
 
1409
- //#endregion
1410
- //#region src/encoding.ts
1452
+ // src/encoding.ts
1411
1453
  /**
1412
1454
  * Initializes the key encoder functions.
1413
1455
  *
@@ -1474,8 +1516,7 @@ function createFixedBuffer(size) {
1474
1516
  return buffer;
1475
1517
  }
1476
1518
 
1477
- //#endregion
1478
- //#region src/store.ts
1519
+ // src/store.ts
1479
1520
  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;
1480
1521
  const KEY_BUFFER_SIZE = 4096;
1481
1522
  const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
@@ -1776,6 +1817,10 @@ var Store = class {
1776
1817
  */
1777
1818
  writeBufferSize;
1778
1819
  /**
1820
+ * The memtable arena block size in bytes; `0` or unset is automatic.
1821
+ */
1822
+ arenaBlockSize;
1823
+ /**
1779
1824
  * The function used to encode keys using the shared `keyBuffer`.
1780
1825
  */
1781
1826
  writeKey;
@@ -1828,6 +1873,7 @@ var Store = class {
1828
1873
  this.timestampFloorLog = options?.timestampFloorLog;
1829
1874
  this.verificationTable = options?.verificationTable;
1830
1875
  this.writeBufferSize = options?.writeBufferSize;
1876
+ this.arenaBlockSize = options?.arenaBlockSize;
1831
1877
  this.writeKey = writeKey;
1832
1878
  this.#columnFamilyOptions = options ? { ...options } : void 0;
1833
1879
  }
@@ -2078,7 +2124,9 @@ var Store = class {
2078
2124
  if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
2079
2125
  if (txnId !== void 0) flags |= ITERATOR_HAS_TRANSACTION_ID_FLAG;
2080
2126
  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;
2081
- return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions, txnId), this, includeValues, options?.limit));
2127
+ const nativeIterator = new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions, txnId);
2128
+ const transaction = context !== this.db ? context : options?.transaction?._context;
2129
+ return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(nativeIterator, this, includeValues, options?.limit, transaction));
2082
2130
  }
2083
2131
  getSync(context, key, alwaysCreateNewBuffer = false, options) {
2084
2132
  const keyParam = getKeyParam(this.encodeKey(key));
@@ -2192,7 +2240,8 @@ var Store = class {
2192
2240
  transactionLogsPath: this.transactionLogsPath,
2193
2241
  timestampFloorLog: this.timestampFloorLog,
2194
2242
  verificationTable: this.verificationTable,
2195
- writeBufferSize: this.writeBufferSize
2243
+ writeBufferSize: this.writeBufferSize,
2244
+ arenaBlockSize: this.arenaBlockSize
2196
2245
  });
2197
2246
  this.identityPath = this.db.identityPath;
2198
2247
  return false;
@@ -2298,8 +2347,7 @@ function getKeyParam(keyBuffer) {
2298
2347
  return keyBuffer.length;
2299
2348
  }
2300
2349
 
2301
- //#endregion
2302
- //#region src/transaction.ts
2350
+ // src/transaction.ts
2303
2351
  /**
2304
2352
  * Sentinel value returned by `commit()` when `coordinatedRetry: true` and the
2305
2353
  * transaction encountered an IsBusy conflict. The native layer parks on VT
@@ -2323,10 +2371,18 @@ var TransactionAlreadyAbortedError = class extends Error {
2323
2371
  */
2324
2372
  var TransactionRetryableError = class extends Error {
2325
2373
  hasLog;
2374
+ /**
2375
+ * The attempt was refused before writing its transaction-log batch (`transactionLogAbortMarkers`):
2376
+ * an older transaction held a write intent on one of its keys (`transactionLogWriteIntents`), or, on a retry, RocksDB's conflict
2377
+ * check found a write that already committed (see `lastConflictKind()`). Nothing of it is in the
2378
+ * log, so a retry may stage its batch afresh.
2379
+ */
2380
+ refusedBeforeLog;
2326
2381
  txn;
2327
2382
  constructor(error, txn) {
2328
2383
  super(error.message);
2329
2384
  this.hasLog = error.hasLog ?? false;
2385
+ this.refusedBeforeLog = error.refusedBeforeLog ?? false;
2330
2386
  this.txn = txn;
2331
2387
  }
2332
2388
  };
@@ -2356,6 +2412,38 @@ var TransactionAbandonedError = class extends Error {
2356
2412
  }
2357
2413
  };
2358
2414
  /**
2415
+ * `abort()` of a transaction whose transaction-log batch could not be marked aborted
2416
+ * (`transactionLogAbortMarkers`). The batch stays unresolved, so committed readers do not pass it and
2417
+ * the log store refuses appends, until a retry on the next append, database flush or store close
2418
+ * marks it.
2419
+ */
2420
+ var TransactionAbortUnresolvedError = class extends Error {
2421
+ code = "ERR_TRANSACTION_ABORT_UNRESOLVED";
2422
+ txn;
2423
+ constructor(error, txn) {
2424
+ super(error.message);
2425
+ this.txn = txn;
2426
+ }
2427
+ };
2428
+ /**
2429
+ * Why a transaction's most recent attempt was refused or retried, from {@link Transaction.lastConflictKind}.
2430
+ */
2431
+ const ConflictKind = {
2432
+ none: 0,
2433
+ /** Refused on an older transaction's write intent before writing its log batch. */
2434
+ intentBeforeLog: 1,
2435
+ /** Refused on an older transaction's write intent after its log batch was written. */
2436
+ intentAfterLog: 2,
2437
+ /** RocksDB found a conflict at commit. */
2438
+ atCommit: 3,
2439
+ /**
2440
+ * RocksDB's conflict check, run before the log write, found a write that already committed, or,
2441
+ * as at commit, could not validate because the memtable history it needs was flushed (needs a
2442
+ * RocksDB build that exports the check).
2443
+ */
2444
+ checkBeforeLog: 4
2445
+ };
2446
+ /**
2359
2447
  * Provides transaction level operations to a transaction callback.
2360
2448
  */
2361
2449
  var Transaction = class extends DBI {
@@ -2370,9 +2458,10 @@ var Transaction = class extends DBI {
2370
2458
  if (store.readOnly) {
2371
2459
  super(store);
2372
2460
  this.#txn = { id: 0 };
2373
- this.abandonWrites = this.abort = this.commitSync = this.setTimestamp = () => {};
2461
+ this.abandonWrites = this.abort = this.commitSync = this.setTimestamp = this.setParkDeadline = () => {};
2374
2462
  this.commit = async () => {};
2375
- this.getTimestamp = () => 0;
2463
+ this.getTimestamp = this.lastConflictKind = () => 0;
2464
+ this.logWritten = () => false;
2376
2465
  } else {
2377
2466
  const txn = new NativeTransaction(store.db, options);
2378
2467
  super(store, txn);
@@ -2398,6 +2487,7 @@ var Transaction = class extends DBI {
2398
2487
  this.#txn.abort();
2399
2488
  } catch (err) {
2400
2489
  if (err instanceof Error && "code" in err && err.code === "ERR_TRANSACTION_ABANDONED") throw new TransactionAbandonedError(err, this);
2490
+ if (err instanceof Error && "code" in err && err.code === "ERR_TRANSACTION_ABORT_UNRESOLVED") throw new TransactionAbortUnresolvedError(err, this);
2401
2491
  throw err;
2402
2492
  }
2403
2493
  }
@@ -2466,6 +2556,28 @@ var Transaction = class extends DBI {
2466
2556
  return this.#txn.getTimestamp();
2467
2557
  }
2468
2558
  /**
2559
+ * Whether this transaction's batch is in the transaction log. A commit conflict after the batch
2560
+ * was written keeps it for the retry, which must not stage it again; an attempt refused before
2561
+ * writing it (`refusedBeforeLog`) leaves nothing.
2562
+ */
2563
+ logWritten() {
2564
+ return this.#txn.logWritten();
2565
+ }
2566
+ /**
2567
+ * Why the most recent attempt was refused or retried; one of {@link ConflictKind}.
2568
+ */
2569
+ lastConflictKind() {
2570
+ return this.#txn.lastConflictKind();
2571
+ }
2572
+ /**
2573
+ * Bounds coordinated-retry parks: past `deadline` (epoch milliseconds) a parked commit resolves
2574
+ * `RETRY_NOW` at once, so a caller enforcing its own deadline can abort instead of waiting out
2575
+ * the default park bound. 0 clears it.
2576
+ */
2577
+ setParkDeadline(deadline) {
2578
+ this.#txn.setParkDeadline(deadline);
2579
+ }
2580
+ /**
2469
2581
  * Get the transaction id.
2470
2582
  */
2471
2583
  get id() {
@@ -2486,8 +2598,8 @@ var Transaction = class extends DBI {
2486
2598
  }
2487
2599
  };
2488
2600
 
2489
- //#endregion
2490
- //#region src/database.ts
2601
+ // src/database.ts
2602
+ const backoffCell = new Int32Array(new SharedArrayBuffer(4));
2491
2603
  const BACKGROUND_ERROR_SEVERITY_NAMES = [
2492
2604
  "none",
2493
2605
  "soft",
@@ -2897,6 +3009,19 @@ var RocksDatabase = class RocksDatabase extends DBI {
2897
3009
  return this.store.db.flushSync(options);
2898
3010
  }
2899
3011
  /**
3012
+ * Syncs the write-ahead log to stable storage (`DB::SyncWAL`), so every WAL-backed write
3013
+ * committed before the call survives a power loss afterwards. Unlike {@link flush} it writes
3014
+ * no SST file and never stalls writers. Not covered: writes that skipped the WAL
3015
+ * (`disableWAL`), and transaction-log files, which keep their own durability. Runs on the
3016
+ * libuv threadpool; RocksDB serializes concurrent syncs of one WAL, so keep one in flight
3017
+ * per database rather than parking pool workers on a queue of them.
3018
+ *
3019
+ * On a read-only database the returned promise resolves without syncing.
3020
+ */
3021
+ syncWal() {
3022
+ return new Promise((resolve, reject) => this.store.db.syncWal(resolve, reject));
3023
+ }
3024
+ /**
2900
3025
  * Returns the most recent {@link BackgroundError} observed on this database, or
2901
3026
  * `null` when none has occurred. The `'error'` event is the push counterpart;
2902
3027
  * this is the pull equivalent for on-demand checks (e.g. a health probe) and
@@ -3365,7 +3490,10 @@ var RocksDatabase = class RocksDatabase extends DBI {
3365
3490
  async transaction(callback, options) {
3366
3491
  if (typeof callback !== "function") throw new TypeError("Callback must be a function");
3367
3492
  const maxRetries = options?.maxRetries ?? 3;
3368
- const txn = new Transaction(this.store, options);
3493
+ const txn = new Transaction(this.store, options?.retryOnBusy === false && options.coordinatedRetry ? {
3494
+ ...options,
3495
+ coordinatedRetry: false
3496
+ } : options);
3369
3497
  let result;
3370
3498
  this.notify("begin-transaction");
3371
3499
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
@@ -3379,7 +3507,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
3379
3507
  commitResult = await txn.commit();
3380
3508
  } catch (commitErr) {
3381
3509
  if (commitErr instanceof TransactionAlreadyAbortedError) return;
3382
- if (commitErr instanceof TransactionRetryableError && (options?.retryOnBusy ?? commitErr.hasLog) && attempt < maxRetries) continue;
3510
+ if (commitErr instanceof TransactionRetryableError && (options?.retryOnBusy ?? (commitErr.hasLog || commitErr.refusedBeforeLog)) && attempt < maxRetries) continue;
3383
3511
  this.#abandonTransaction(txn, commitErr);
3384
3512
  return;
3385
3513
  }
@@ -3411,10 +3539,13 @@ var RocksDatabase = class RocksDatabase extends DBI {
3411
3539
  if (typeof callback !== "function") throw new TypeError("Callback must be a function");
3412
3540
  const maxRetries = options?.maxRetries ?? 3;
3413
3541
  const isRetryable = (err, attempt) => {
3414
- return err instanceof TransactionRetryableError && (options?.retryOnBusy ?? err.hasLog) && attempt <= maxRetries;
3542
+ if (!(err instanceof TransactionRetryableError && (options?.retryOnBusy ?? (err.hasLog || err.refusedBeforeLog)) && attempt <= maxRetries)) return false;
3543
+ const kind = txn.lastConflictKind();
3544
+ if (kind === ConflictKind.intentBeforeLog || kind === ConflictKind.intentAfterLog) Atomics.wait(backoffCell, 0, 0, Math.min(2 ** attempt, 50));
3545
+ return true;
3415
3546
  };
3547
+ const txn = new Transaction(this.store, options);
3416
3548
  const runAttempt = (attempt) => {
3417
- const txn = new Transaction(this.store, options);
3418
3549
  let result;
3419
3550
  try {
3420
3551
  result = callback(txn, attempt);
@@ -3430,7 +3561,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
3430
3561
  if (isRetryable(commitErr, attempt)) return runAttempt(attempt + 1);
3431
3562
  this.#abandonTransaction(txn, commitErr);
3432
3563
  }
3433
- });
3564
+ }, (callbackErr) => this.#abortTransaction(txn, callbackErr));
3434
3565
  try {
3435
3566
  txn.commitSync();
3436
3567
  return result;
@@ -3455,7 +3586,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
3455
3586
  try {
3456
3587
  txn.abort();
3457
3588
  } catch (abortErr) {
3458
- if (abortErr instanceof TransactionAbandonedError) throw abortErr;
3589
+ if (abortErr instanceof TransactionAbandonedError || abortErr instanceof TransactionAbortUnresolvedError) throw abortErr;
3459
3590
  }
3460
3591
  throw commitErr;
3461
3592
  }
@@ -3570,10 +3701,10 @@ var RocksDatabase = class RocksDatabase extends DBI {
3570
3701
  }
3571
3702
  };
3572
3703
 
3573
- //#endregion
3574
- //#region src/parse-transaction-log.ts
3575
- const { TRANSACTION_LOG_TOKEN, TRANSACTION_LOG_ENTRY_HEADER_SIZE: TRANSACTION_LOG_ENTRY_HEADER_SIZE$1 } = constants;
3704
+ // src/parse-transaction-log.ts
3705
+ 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;
3576
3706
  const MIN_VALID_TIMESTAMP = Date.UTC(2026, 0, 27);
3707
+ const VALID_FLAGS_MASK = TRANSACTION_LOG_ENTRY_LAST_FLAG$1 | TRANSACTION_LOG_ENTRY_ABORTED_FLAG$1;
3577
3708
  /**
3578
3709
  * Loads an entire transaction log file into memory.
3579
3710
  * @param path - The path to the transaction log file.
@@ -3611,6 +3742,8 @@ function parseTransactionLog(path, options = {}) {
3611
3742
  if (!Number.isFinite(timestamp) || timestamp < MIN_VALID_TIMESTAMP) anomalies.push(`Header timestamp ${timestamp} predates 2026-01-27 (possible corruption)`);
3612
3743
  const entries = [];
3613
3744
  let entryAnomalyCount = 0;
3745
+ let previousEndedTransaction = true;
3746
+ let previousTimestamp = NaN;
3614
3747
  while (fileOffset < size) {
3615
3748
  const timestamp = read(8).readDoubleBE(0);
3616
3749
  if (timestamp === 0) {
@@ -3627,7 +3760,11 @@ function parseTransactionLog(path, options = {}) {
3627
3760
  const data = read(length);
3628
3761
  const entryAnomalies = [];
3629
3762
  if (!Number.isFinite(timestamp) || timestamp < MIN_VALID_TIMESTAMP) entryAnomalies.push(`timestamp ${timestamp} predates 2026-01-27 (possible corruption)`);
3630
- if ((flags & -2) !== 0) entryAnomalies.push(`flags 0x${flags.toString(16).padStart(2, "0")} contains undefined bits (expected 0x00 or 0x01)`);
3763
+ if ((flags & ~VALID_FLAGS_MASK) !== 0) entryAnomalies.push(`flags 0x${flags.toString(16).padStart(2, "0")} contains undefined bits (expected 0x00-0x03)`);
3764
+ const startsTransaction = previousEndedTransaction || timestamp !== previousTimestamp;
3765
+ if ((flags & TRANSACTION_LOG_ENTRY_ABORTED_FLAG$1) !== 0 && !startsTransaction) entryAnomalies.push("abort flag on an entry that does not start a transaction");
3766
+ previousEndedTransaction = (flags & TRANSACTION_LOG_ENTRY_LAST_FLAG$1) !== 0;
3767
+ previousTimestamp = timestamp;
3631
3768
  const entry = {
3632
3769
  timestamp,
3633
3770
  length,
@@ -3656,11 +3793,11 @@ function parseTransactionLog(path, options = {}) {
3656
3793
  }
3657
3794
  }
3658
3795
 
3659
- //#endregion
3660
- //#region src/transaction-log-reader.ts
3796
+ // src/transaction-log-reader.ts
3661
3797
  const FLOAT_TO_UINT32 = /* @__PURE__ */ new Float64Array(1);
3662
3798
  const UINT32_FROM_FLOAT = new Uint32Array(FLOAT_TO_UINT32.buffer);
3663
- const { TRANSACTION_LOG_FILE_HEADER_SIZE, TRANSACTION_LOG_ENTRY_HEADER_SIZE } = constants;
3799
+ const EXACT_START_SPAN = /* @__PURE__ */ new Float64Array(3);
3800
+ const { TRANSACTION_LOG_FILE_HEADER_SIZE, TRANSACTION_LOG_ENTRY_HEADER_SIZE, TRANSACTION_LOG_ENTRY_LAST_FLAG, TRANSACTION_LOG_ENTRY_ABORTED_FLAG } = constants;
3664
3801
  /**
3665
3802
  * Number of consecutive well-formed frames that, on their own, signal that real log data has
3666
3803
  * resumed after a framing break. Kept in step with `RESYNC_MIN_FRAMES` in
@@ -3696,6 +3833,32 @@ var CorruptFrameError = class extends RangeError {
3696
3833
  }
3697
3834
  };
3698
3835
  /**
3836
+ * Thrown when an iterator advances past segments it never read and that can no longer be read —
3837
+ * typically ones retention purged while the reader lagged. The entries they held cannot be
3838
+ * recovered from this log, so a consumer replicating it must not treat the stream as contiguous.
3839
+ * Missing sequence numbers prove only that continuity is unknown, not that each held entries.
3840
+ *
3841
+ * The iterator is left positioned at the start of `resumeLogId`, so a caller that accepts the
3842
+ * discontinuity calls `next()` again to continue from there.
3843
+ *
3844
+ * Not a `RangeError`: this is not broken framing, and consumers classify those by class.
3845
+ */
3846
+ var TransactionLogGapError = class extends Error {
3847
+ /** Sequence number of the last segment this iterator read up to. */
3848
+ logId;
3849
+ /** Sequence number of the segment iteration resumes in. */
3850
+ resumeLogId;
3851
+ /** Segments between `logId` and `resumeLogId` that could not be read. Empty segments are not counted. */
3852
+ unavailableLogCount;
3853
+ constructor(logId, resumeLogId, unavailableLogCount) {
3854
+ super(`Transaction log iterator skipped ${unavailableLogCount} unavailable segment(s) between log ${logId} and log ${resumeLogId}; entries in them were not read`);
3855
+ this.name = "TransactionLogGapError";
3856
+ this.logId = logId;
3857
+ this.resumeLogId = resumeLogId;
3858
+ this.unavailableLogCount = unavailableLogCount;
3859
+ }
3860
+ };
3861
+ /**
3699
3862
  * Whether a complete, in-bounds frame begins at `pos`. The only sane bound on an entry's length is
3700
3863
  * the written extent: a single entry can legitimately exceed the rotation threshold (the first
3701
3864
  * entry written to a fresh file is always written in full). A zero timestamp is the
@@ -3771,6 +3934,14 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3771
3934
  let dataView;
3772
3935
  let logBuffer = this._currentLogBuffer?.deref();
3773
3936
  let foundExactStart = false;
3937
+ let stopLogId = 0;
3938
+ let stopPosition = 0;
3939
+ let tailLogId = 0;
3940
+ let tailPosition = 0;
3941
+ let segmentRemovals = 0;
3942
+ let previousEndedTransaction = true;
3943
+ let previousTimestamp = NaN;
3944
+ let skippingAborted = false;
3774
3945
  if (start === void 0 && !startFromLastFlushed) {
3775
3946
  position = size;
3776
3947
  if (position === 0) position = TRANSACTION_LOG_FILE_HEADER_SIZE;
@@ -3780,11 +3951,32 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3780
3951
  FLOAT_TO_UINT32[0] = this._getLastFlushed();
3781
3952
  if (FLOAT_TO_UINT32[0] === 0) FLOAT_TO_UINT32[0] = this._findPosition(0);
3782
3953
  start ??= 0;
3954
+ } else if (exactStart) {
3955
+ const startPosition = this._findExactStart(start, EXACT_START_SPAN);
3956
+ FLOAT_TO_UINT32[0] = EXACT_START_SPAN[0];
3957
+ stopLogId = UINT32_FROM_FLOAT[1];
3958
+ stopPosition = UINT32_FROM_FLOAT[0];
3959
+ FLOAT_TO_UINT32[0] = EXACT_START_SPAN[1];
3960
+ tailLogId = UINT32_FROM_FLOAT[1];
3961
+ tailPosition = UINT32_FROM_FLOAT[0];
3962
+ segmentRemovals = EXACT_START_SPAN[2];
3963
+ FLOAT_TO_UINT32[0] = startPosition;
3783
3964
  } else FLOAT_TO_UINT32[0] = this._findPosition(start);
3784
3965
  logId = UINT32_FROM_FLOAT[1];
3785
3966
  position = UINT32_FROM_FLOAT[0];
3786
3967
  if (position === 0) position = TRANSACTION_LOG_FILE_HEADER_SIZE;
3787
3968
  }
3969
+ const positionedAt = position;
3970
+ if (stopLogId !== 0 && pastStop(logId, position, stopLogId, stopPosition)) {
3971
+ stopLogId = 0;
3972
+ if (skipToTail(this, logId, position, tailLogId, tailPosition, segmentRemovals)) {
3973
+ logId = tailLogId;
3974
+ position = tailPosition;
3975
+ logBuffer = tailLogBuffer ?? logBuffer;
3976
+ }
3977
+ tailLogBuffer = void 0;
3978
+ }
3979
+ let gapFloor = logId;
3788
3980
  if (logBuffer === void 0 || logBuffer.logId !== logId) {
3789
3981
  logBuffer = getLogMemoryMap(this, logId);
3790
3982
  if (logBuffer && latestLogId === logId && !readUncommitted && this._currentLogBuffer?.deref() !== logBuffer) this._currentLogBuffer = new WeakRef(logBuffer);
@@ -3794,6 +3986,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3794
3986
  logBuffer.size = 0;
3795
3987
  logBuffer.dataView = new DataView(logBuffer.buffer);
3796
3988
  size = 0;
3989
+ if (positionedAt <= TRANSACTION_LOG_FILE_HEADER_SIZE || logId > 0 && this.getLogFileSize(logId) > 0) gapFloor = logId - 1;
3797
3990
  }
3798
3991
  }
3799
3992
  dataView = logBuffer.dataView;
@@ -3803,7 +3996,7 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3803
3996
  if (cachedSize === void 0) size = logBuffer.size = readableExtent(logBuffer);
3804
3997
  else size = cachedSize;
3805
3998
  }
3806
- return {
3999
+ const iterator = {
3807
4000
  [Symbol.iterator]() {
3808
4001
  return this;
3809
4002
  },
@@ -3815,13 +4008,17 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3815
4008
  if (latestLogId > logBuffer.logId) {
3816
4009
  size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
3817
4010
  if (position >= size) {
3818
- const nextLogBuffer = nextReadableLogBuffer(transactionLog, logBuffer.logId, latestLogId);
4011
+ const fromLogId = logBuffer.logId;
4012
+ const nextLogBuffer = nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor);
3819
4013
  if (nextLogBuffer) {
4014
+ const unavailable = unavailableLogCount;
3820
4015
  dataView = nextLogBuffer.dataView;
3821
4016
  logBuffer = nextLogBuffer;
3822
4017
  if (latestLogId > logBuffer.logId) size = logBuffer.size ?? (logBuffer.size = readableExtent(logBuffer));
3823
4018
  else size = latestSize;
3824
4019
  position = TRANSACTION_LOG_FILE_HEADER_SIZE;
4020
+ previousEndedTransaction = true;
4021
+ if (unavailable > 0) throw new TransactionLogGapError(Math.max(fromLogId, gapFloor), logBuffer.logId, unavailable);
3825
4022
  }
3826
4023
  }
3827
4024
  }
@@ -3841,30 +4038,55 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3841
4038
  if (position + TRANSACTION_LOG_ENTRY_HEADER_SIZE > limit) {
3842
4039
  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);
3843
4040
  position = broken.nextPosition;
4041
+ previousEndedTransaction = true;
3844
4042
  throw broken.error;
3845
4043
  }
3846
4044
  const length = dataView.getUint32(position + 8);
3847
4045
  if (length === 0 || position + TRANSACTION_LOG_ENTRY_HEADER_SIZE + length > limit) {
3848
4046
  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);
3849
4047
  position = broken.nextPosition;
4048
+ previousEndedTransaction = true;
3850
4049
  throw broken.error;
3851
4050
  }
3852
4051
  position += TRANSACTION_LOG_ENTRY_HEADER_SIZE;
4052
+ const flags = logBuffer[position - 1];
4053
+ const aborted = (flags & TRANSACTION_LOG_ENTRY_ABORTED_FLAG) !== 0;
4054
+ if (previousEndedTransaction || timestamp !== previousTimestamp || aborted) skippingAborted = aborted;
4055
+ previousEndedTransaction = (flags & TRANSACTION_LOG_ENTRY_LAST_FLAG) !== 0;
4056
+ previousTimestamp = timestamp;
3853
4057
  let matchesRange;
3854
- if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
4058
+ if (skippingAborted) matchesRange = false;
4059
+ else if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
3855
4060
  else if (exactStart) {
4061
+ matchesRange = false;
3856
4062
  if (timestamp === start) {
3857
4063
  matchesRange = !exclusiveStart;
3858
4064
  foundExactStart = true;
3859
- } else matchesRange = false;
4065
+ stopLogId = 0;
4066
+ }
3860
4067
  } else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
4068
+ if (!matchesRange && stopLogId !== 0 && pastStop(logBuffer.logId, position + length, stopLogId, stopPosition)) {
4069
+ stopLogId = 0;
4070
+ position += length;
4071
+ if (skipToTail(transactionLog, logBuffer.logId, position, tailLogId, tailPosition, segmentRemovals)) {
4072
+ if (tailLogBuffer) {
4073
+ logBuffer = tailLogBuffer;
4074
+ dataView = tailLogBuffer.dataView;
4075
+ }
4076
+ position = tailPosition;
4077
+ previousEndedTransaction = true;
4078
+ size = 0;
4079
+ }
4080
+ tailLogBuffer = void 0;
4081
+ return iterator.next();
4082
+ }
3861
4083
  const entryStart = position;
3862
4084
  position += length;
3863
4085
  if (matchesRange) return {
3864
4086
  done: false,
3865
4087
  value: {
3866
4088
  timestamp,
3867
- endTxn: Boolean(logBuffer[entryStart - 1] & 1),
4089
+ endTxn: previousEndedTransaction,
3868
4090
  data: logBuffer.subarray(entryStart, position)
3869
4091
  }
3870
4092
  };
@@ -3875,11 +4097,13 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3875
4097
  size = logBuffer.size ?? readableExtent(logBuffer);
3876
4098
  if (!readUncommitted) logBuffer.size = size;
3877
4099
  if (position < size) continue;
3878
- const nextLogBuffer = nextReadableLogBuffer(transactionLog, logBuffer.logId, latestLogId);
4100
+ const fromLogId = logBuffer.logId;
4101
+ const nextLogBuffer = nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor);
3879
4102
  if (!nextLogBuffer) return {
3880
4103
  done: true,
3881
4104
  value: void 0
3882
4105
  };
4106
+ const unavailable = unavailableLogCount;
3883
4107
  logBuffer = nextLogBuffer;
3884
4108
  dataView = logBuffer.dataView;
3885
4109
  if (latestLogId === logBuffer.logId) size = latestSize;
@@ -3891,6 +4115,8 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3891
4115
  } else size = cachedSize;
3892
4116
  }
3893
4117
  position = TRANSACTION_LOG_FILE_HEADER_SIZE;
4118
+ previousEndedTransaction = true;
4119
+ if (unavailable > 0) throw new TransactionLogGapError(Math.max(fromLogId, gapFloor), logBuffer.logId, unavailable);
3894
4120
  }
3895
4121
  }
3896
4122
  }
@@ -3900,7 +4126,29 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3900
4126
  };
3901
4127
  }
3902
4128
  };
4129
+ return iterator;
3903
4130
  } });
4131
+ function pastStop(logId, position, stopLogId, stopPosition) {
4132
+ return logId > stopLogId || logId === stopLogId && position >= stopPosition;
4133
+ }
4134
+ let tailLogBuffer;
4135
+ /**
4136
+ * Whether an exact-start lookup at `position` of `fromLogId` may take its one-shot jump to the tail.
4137
+ * next() calls it on a frame boundary, so a mapping error leaves a resumable cursor; from query()
4138
+ * the error escapes before any iterator exists. The removal
4139
+ * check follows the mapping and holds the store's lock, so a purge either declines the jump (the
4140
+ * scan then reports the gap) or follows it like a purge of segments already read.
4141
+ */
4142
+ function skipToTail(transactionLog, fromLogId, position, tailLogId, tailPosition, segmentRemovals) {
4143
+ tailLogBuffer = void 0;
4144
+ if (tailLogId === fromLogId) return tailPosition > position;
4145
+ if (tailLogId < fromLogId) return false;
4146
+ const mapped = getLogMemoryMap(transactionLog, tailLogId);
4147
+ if (mapped === void 0 || transactionLog._segmentRemovals() !== segmentRemovals) return false;
4148
+ tailLogBuffer = mapped;
4149
+ return true;
4150
+ }
4151
+ let unavailableLogCount = 0;
3904
4152
  /**
3905
4153
  * Maps the next readable segment after `fromLogId`, skipping a run retention has deleted —
3906
4154
  * stopping at the hole wedges the iterator there for every later poll (invariant 30).
@@ -3909,15 +4157,22 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3909
4157
  * `_findPosition(0)`, which names the bottom of the contiguous run ending at the current segment
3910
4158
  * and so lands *past* a survivor sitting between two holes.
3911
4159
  */
3912
- function nextReadableLogBuffer(transactionLog, fromLogId, latestLogId) {
4160
+ function nextReadableLogBuffer(transactionLog, fromLogId, latestLogId, gapFloor) {
4161
+ unavailableLogCount = 0;
3913
4162
  let candidateLogId = fromLogId + 1;
3914
4163
  while (candidateLogId <= latestLogId) {
3915
4164
  const logBuffer = getLogMemoryMap(transactionLog, candidateLogId);
3916
4165
  if (logBuffer) {
3917
4166
  if (candidateLogId === latestLogId || readableExtent(logBuffer) > TRANSACTION_LOG_FILE_HEADER_SIZE) return logBuffer;
3918
- } else if (transactionLog.getLogFileSize(candidateLogId) > TRANSACTION_LOG_FILE_HEADER_SIZE) return;
3919
- candidateLogId = transactionLog._nextLogId(candidateLogId);
3920
- if (candidateLogId === 0) return;
4167
+ } else {
4168
+ const fileSize = transactionLog.getLogFileSize(candidateLogId);
4169
+ if (fileSize > TRANSACTION_LOG_FILE_HEADER_SIZE) return;
4170
+ if (fileSize === 0 && candidateLogId > gapFloor) unavailableLogCount++;
4171
+ }
4172
+ const nextLogId = transactionLog._nextLogId(candidateLogId);
4173
+ if (nextLogId === 0) return;
4174
+ unavailableLogCount += Math.max(0, nextLogId - 1 - Math.max(candidateLogId, gapFloor));
4175
+ candidateLogId = nextLogId;
3921
4176
  }
3922
4177
  }
3923
4178
  /**
@@ -3971,15 +4226,14 @@ function loadLastPosition(transactionLog, readUncommitted) {
3971
4226
  };
3972
4227
  }
3973
4228
 
3974
- //#endregion
3975
- //#region src/index.ts
4229
+ // src/index.ts
3976
4230
  const versions = {
3977
4231
  rocksdb: version,
3978
- "rocksdb-js": "2.11.0"
4232
+ "rocksdb-js": "2.13.0"
3979
4233
  };
3980
4234
 
3981
- //#endregion
3982
4235
  exports.BackgroundError = BackgroundError;
4236
+ exports.ConflictKind = ConflictKind;
3983
4237
  exports.CorruptFrameError = CorruptFrameError;
3984
4238
  exports.CountEstimator = CountEstimator;
3985
4239
  exports.DBI = DBI;
@@ -3987,7 +4241,9 @@ exports.DBIterator = DBIterator;
3987
4241
  exports.RocksDatabase = RocksDatabase;
3988
4242
  exports.Store = Store;
3989
4243
  exports.Transaction = Transaction;
4244
+ exports.TransactionAbortUnresolvedError = TransactionAbortUnresolvedError;
3990
4245
  exports.TransactionLog = TransactionLog;
4246
+ exports.TransactionLogGapError = TransactionLogGapError;
3991
4247
  exports.backups = backups;
3992
4248
  exports.constants = constants;
3993
4249
  exports.coolTransactionLogs = coolTransactionLogs;