@harperfast/rocksdb-js 2.8.0 → 2.9.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
@@ -20,7 +20,7 @@ var __copyProps = (to, from, except, desc) => {
20
20
  }
21
21
  return to;
22
22
  };
23
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
24
24
  value: mod,
25
25
  enumerable: true
26
26
  }) : target, mod));
@@ -91,6 +91,26 @@ const binding = req(locateBinding());
91
91
  */
92
92
  const BackgroundError = binding.BackgroundError;
93
93
  const config = binding.config;
94
+ /**
95
+ * Reads the live state of the process-wide `WriteBufferManager`: its budget,
96
+ * charged memory, mutable share, stall state, and attached column-family
97
+ * inventory. The manager is shared by every database and worker thread in the
98
+ * process.
99
+ *
100
+ * A WriteBufferManager stall does not pass through RocksDB's `WriteController`,
101
+ * so it is not reflected by `rocksdb.stall.micros`, `db.isWriteStalled()`, or
102
+ * the `'writeStall'` event. Use `stallActive` to distinguish this condition from
103
+ * an idle database.
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const wbm = getWriteBufferManagerStats();
108
+ * if (wbm.stallActive) {
109
+ * log.warn(`writes stalled for ${wbm.stallActiveMs}ms`);
110
+ * }
111
+ * ```
112
+ */
113
+ const getWriteBufferManagerStats = binding.getWriteBufferManagerStats;
94
114
  const FRESH_VERSION_FLAG$1 = binding.constants.FRESH_VERSION_FLAG;
95
115
  const addGlobalListener = binding.addListener;
96
116
  const removeGlobalListener = binding.removeListener;
@@ -139,6 +159,8 @@ const transactionLogMapCount = binding.transactionLogMapCount;
139
159
  * disarm. Used by the ERR_TRY_AGAIN retry regression test.
140
160
  */
141
161
  const forceTryAgainForTesting = binding.forceTryAgainForTesting;
162
+ /** Delays one selected watchdog join in the concurrent-shutdown regression test. */
163
+ const setWriteBufferManagerJoinDelayForTesting = binding.setWriteBufferManagerJoinDelayForTesting;
142
164
  /**
143
165
  * Creates a native file lock using the specified file path (`flock` on POSIX,
144
166
  * `LockFileEx` on Windows), creating the file and any missing parent
@@ -667,14 +689,12 @@ function parseDuration(duration) {
667
689
  num *= 1e3;
668
690
  break;
669
691
  case "m":
670
- num *= 1e3 * 60;
692
+ num *= 6e4;
671
693
  break;
672
694
  case "h":
673
- num *= 1e3 * 60 * 60;
674
- break;
675
- case "d":
676
- num *= 1e3 * 60 * 60 * 24;
695
+ num *= 36e5;
677
696
  break;
697
+ case "d": num *= 864e5;
678
698
  }
679
699
  result += num;
680
700
  }
@@ -1377,10 +1397,10 @@ function createFixedBuffer(size) {
1377
1397
 
1378
1398
  //#endregion
1379
1399
  //#region src/store.ts
1380
- 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 } = constants;
1400
+ 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;
1381
1401
  const KEY_BUFFER_SIZE = 4096;
1382
1402
  const KEY_BUFFER = createFixedBuffer(KEY_BUFFER_SIZE);
1383
- const VALUE_BUFFER = createFixedBuffer(64 * 1024);
1403
+ const VALUE_BUFFER = createFixedBuffer(65536);
1384
1404
  /**
1385
1405
  * Backing buffer for the shared iterator state. Layout (Uint32Array view):
1386
1406
  * [0] = key length written into KEY_BUFFER by the most recent iterator step
@@ -1388,7 +1408,7 @@ const VALUE_BUFFER = createFixedBuffer(64 * 1024);
1388
1408
  */
1389
1409
  const ITERATOR_STATE_BUFFER = Buffer.allocUnsafeSlow(8);
1390
1410
  const ITERATOR_STATE = new Uint32Array(ITERATOR_STATE_BUFFER.buffer, ITERATOR_STATE_BUFFER.byteOffset, 2);
1391
- const MAX_KEY_SIZE = 1024 * 1024;
1411
+ const MAX_KEY_SIZE = 1048576;
1392
1412
  const SAVE_BUFFER_SIZE = 8192;
1393
1413
  /**
1394
1414
  * Normalizes the public `compression` option into the primitive fields the
@@ -1423,6 +1443,15 @@ function normalizeCompression(option) {
1423
1443
  };
1424
1444
  }
1425
1445
  /**
1446
+ * Whether `codec` is a pre-constructed encoder/decoder instance that `open()`
1447
+ * would reuse as-is (rather than instantiate fresh from an `Encoder` factory).
1448
+ * Such an instance carries per-column-family mutable state (its `name`, msgpack
1449
+ * `structures`), so it cannot be shared across column families.
1450
+ */
1451
+ function sharesMutableCodec(codec) {
1452
+ return codec != null && typeof codec === "object" && typeof codec.Encoder !== "function" && (typeof codec.encode === "function" || typeof codec.decode === "function");
1453
+ }
1454
+ /**
1426
1455
  * A store wraps the `NativeDatabase` binding and database settings so that a
1427
1456
  * single database instance can be shared between the main `RocksDatabase`
1428
1457
  * instance and the `Transaction` instance.
@@ -1441,6 +1470,13 @@ var Store = class {
1441
1470
  */
1442
1471
  #claimed = false;
1443
1472
  /**
1473
+ * Shallow snapshot of the options this store was constructed with, used by
1474
+ * `createColumnFamilyStore()` to derive sibling column-family stores. Snapshot
1475
+ * (not the caller's object) so later mutations of the caller's options don't
1476
+ * leak into derived views.
1477
+ */
1478
+ #columnFamilyOptions;
1479
+ /**
1444
1480
  * The database instance.
1445
1481
  */
1446
1482
  db;
@@ -1576,6 +1612,11 @@ var Store = class {
1576
1612
  */
1577
1613
  path;
1578
1614
  /**
1615
+ * The open database's resolved filesystem identity, read once from
1616
+ * `NativeDatabase.identityPath`. `undefined` until opened.
1617
+ */
1618
+ identityPath;
1619
+ /**
1579
1620
  * Whether to use pessimistic locking for transactions. When `true`,
1580
1621
  * transactions will fail as soon as a conflict is detected. When `false`,
1581
1622
  * transactions will only fail when `commit()` is called.
@@ -1589,6 +1630,13 @@ var Store = class {
1589
1630
  */
1590
1631
  readOnly;
1591
1632
  /**
1633
+ * The secondary instance's workspace directory when the database is opened
1634
+ * as a secondary (see `RocksDatabaseOptions.secondaryPath`); `undefined`
1635
+ * otherwise. A secondary is read-only, so `readOnly` is `true` whenever
1636
+ * this is set.
1637
+ */
1638
+ secondaryPath;
1639
+ /**
1592
1640
  * Encoder specific flag used to signal that the encoder should use a random
1593
1641
  * access structure.
1594
1642
  */
@@ -1674,7 +1722,10 @@ var Store = class {
1674
1722
  this.parallelismThreads = options?.parallelismThreads;
1675
1723
  this.path = path;
1676
1724
  this.pessimistic = options?.pessimistic ?? false;
1677
- this.readOnly = options?.readOnly ?? false;
1725
+ const secondaryPath = options?.secondaryPath ?? void 0;
1726
+ if (secondaryPath !== void 0 && options?.readOnly === false) throw new Error("A secondary open is read-only; secondaryPath cannot be combined with readOnly: false");
1727
+ this.readOnly = options?.readOnly ?? secondaryPath !== void 0;
1728
+ this.secondaryPath = secondaryPath;
1678
1729
  this.randomAccessStructure = options?.randomAccessStructure ?? false;
1679
1730
  this.readKey = readKey;
1680
1731
  this.sharedStructuresKey = options?.sharedStructuresKey;
@@ -1686,6 +1737,36 @@ var Store = class {
1686
1737
  this.verificationTable = options?.verificationTable;
1687
1738
  this.writeBufferSize = options?.writeBufferSize;
1688
1739
  this.writeKey = writeKey;
1740
+ this.#columnFamilyOptions = options ? { ...options } : void 0;
1741
+ }
1742
+ /**
1743
+ * Creates an independent `Store` for another column family of the same
1744
+ * database, used by {@link RocksDatabase#use}. The returned store shares the
1745
+ * underlying database but must have its own codec state: a mutable
1746
+ * encoder/decoder instance cannot be shared across column families (opening a
1747
+ * view mutates the encoder's `name`, and clearing a view resets its
1748
+ * `structures`), so a pre-constructed instance is rejected here — supply an
1749
+ * encoder factory (`{ Encoder }`) or a named `encoding` instead.
1750
+ *
1751
+ * Override this in a `Store` subclass whose constructor cannot be recreated
1752
+ * from `(path, options)` (e.g. it takes injected dependencies) or that needs
1753
+ * to control how a view's codec state is built.
1754
+ *
1755
+ * @param name - The column family name for the derived store.
1756
+ * @param options - Options overriding this store's, for the derived store.
1757
+ * @returns A new, independent `Store` bound to `name`.
1758
+ */
1759
+ createColumnFamilyStore(name, options) {
1760
+ const derived = {
1761
+ ...this.#columnFamilyOptions,
1762
+ ...options,
1763
+ name
1764
+ };
1765
+ if (sharesMutableCodec(derived.encoder) || sharesMutableCodec(derived.decoder)) throw new Error("Cannot derive a column family view from a Store configured with a pre-constructed encoder/decoder instance — its mutable state would be shared across column families. Provide an encoder factory (`{ Encoder }`) or a named `encoding`, or override `Store#createColumnFamilyStore`.");
1766
+ const StoreClass = this.constructor;
1767
+ const store = new StoreClass(this.path, derived);
1768
+ if (store.name !== name) throw new Error(`Store#createColumnFamilyStore produced a store bound to "${store.name}" instead of "${name}"; a custom Store must forward options to super() or override this method.`);
1769
+ return store;
1689
1770
  }
1690
1771
  /**
1691
1772
  * Closes the database.
@@ -1867,6 +1948,7 @@ var Store = class {
1867
1948
  let endUnencoded = options?.key ?? options?.end;
1868
1949
  const includeValues = options?.values ?? true;
1869
1950
  const reverse = options?.reverse ?? false;
1951
+ const txnId = this.getTxnId(options);
1870
1952
  let exclusiveStart = options?.exclusiveStart ?? false;
1871
1953
  let inclusiveEnd = options?.inclusiveEnd ?? false;
1872
1954
  if (options?.key !== void 0) inclusiveEnd = true;
@@ -1885,13 +1967,15 @@ var Store = class {
1885
1967
  startKeyEnd = this.writeKey(startUnencoded, keyBuffer, 0);
1886
1968
  if (startKeyEnd === 0) throw new Error("Zero length key is not allowed");
1887
1969
  }
1888
- if (endUnencoded !== void 0) if (endUnencoded === startUnencoded) {
1889
- endKeyStart = 0;
1890
- endKeyEnd = startKeyEnd;
1891
- } else {
1892
- endKeyStart = startKeyEnd;
1893
- endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
1894
- if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
1970
+ if (endUnencoded !== void 0) {
1971
+ if (endUnencoded === startUnencoded) {
1972
+ endKeyStart = 0;
1973
+ endKeyEnd = startKeyEnd;
1974
+ } else {
1975
+ endKeyStart = startKeyEnd;
1976
+ endKeyEnd = this.writeKey(endUnencoded, keyBuffer, endKeyStart);
1977
+ if (endKeyEnd === endKeyStart) throw new Error("Zero length key is not allowed");
1978
+ }
1895
1979
  }
1896
1980
  let flags = 0;
1897
1981
  if (reverse) flags |= ITERATOR_REVERSE_FLAG;
@@ -1900,8 +1984,9 @@ var Store = class {
1900
1984
  if (includeValues) flags |= ITERATOR_INCLUDE_VALUES_FLAG;
1901
1985
  if (includeValues && !this.decoderCopies) flags |= ITERATOR_NEEDS_STABLE_VALUE_BUFFER_FLAG;
1902
1986
  if (context !== this.db) flags |= ITERATOR_CONTEXT_IS_TRANSACTION_FLAG;
1987
+ if (txnId !== void 0) flags |= ITERATOR_HAS_TRANSACTION_ID_FLAG;
1903
1988
  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;
1904
- return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions), this, includeValues, options?.limit));
1989
+ return new _harperfast_extended_iterable.ExtendedIterable(new DBIterator(new NativeIterator(context, flags, startKeyEnd, endKeyStart, endKeyEnd, advancedOptions, txnId), this, includeValues, options?.limit));
1905
1990
  }
1906
1991
  getSync(context, key, alwaysCreateNewBuffer = false, options) {
1907
1992
  const keyParam = getKeyParam(this.encodeKey(key));
@@ -1922,9 +2007,12 @@ var Store = class {
1922
2007
  */
1923
2008
  getTxnId(options) {
1924
2009
  let txnId;
1925
- if (!this.readOnly && options?.transaction) {
1926
- txnId = options.transaction.id;
2010
+ const transaction = options?.transaction;
2011
+ if (!this.readOnly && transaction) {
2012
+ txnId = transaction.id;
1927
2013
  if (txnId === void 0) throw new TypeError("Invalid transaction");
2014
+ if (transaction.store === void 0) throw new TypeError("Invalid transaction");
2015
+ if (this.identityPath !== void 0 && transaction.store.identityPath !== this.identityPath) throw new TypeError("Transaction belongs to a different database");
1928
2016
  }
1929
2017
  return txnId;
1930
2018
  }
@@ -1982,7 +2070,10 @@ var Store = class {
1982
2070
  * are performed.
1983
2071
  */
1984
2072
  open() {
1985
- if (this.db.opened) return true;
2073
+ if (this.db.opened) {
2074
+ this.identityPath = this.db.identityPath;
2075
+ return true;
2076
+ }
1986
2077
  const { compression, compressionLevel } = normalizeCompression(this.compression);
1987
2078
  this.db.open(this.path, {
1988
2079
  compression,
@@ -2001,6 +2092,7 @@ var Store = class {
2001
2092
  noBlockCache: this.noBlockCache,
2002
2093
  parallelismThreads: this.parallelismThreads,
2003
2094
  readOnly: this.readOnly,
2095
+ secondaryPath: this.secondaryPath,
2004
2096
  statsLevel: this.statsLevel,
2005
2097
  transactionLogMaxAgeThreshold: this.transactionLogMaxAgeThreshold,
2006
2098
  transactionLogMaxSize: this.transactionLogMaxSize,
@@ -2009,6 +2101,7 @@ var Store = class {
2009
2101
  verificationTable: this.verificationTable,
2010
2102
  writeBufferSize: this.writeBufferSize
2011
2103
  });
2104
+ this.identityPath = this.db.identityPath;
2012
2105
  return false;
2013
2106
  }
2014
2107
  putSync(context, key, value, options) {
@@ -2269,10 +2362,12 @@ var Transaction = class extends DBI {
2269
2362
  return err;
2270
2363
  }
2271
2364
  /**
2272
- * Returns the transaction start timestamp in seconds. Defaults to the time at which
2273
- * the transaction was created.
2365
+ * Returns the transaction timestamp in milliseconds since the Unix epoch. It
2366
+ * defaults to a process-wide monotonic value assigned when the transaction
2367
+ * was created. A producer may use this value as its write identity; the
2368
+ * transaction log uses it as the batch key.
2274
2369
  *
2275
- * @returns The transaction start timestamp in seconds.
2370
+ * @returns The transaction timestamp in milliseconds.
2276
2371
  */
2277
2372
  getTimestamp() {
2278
2373
  return this.#txn.getTimestamp();
@@ -2284,9 +2379,14 @@ var Transaction = class extends DBI {
2284
2379
  return this.#txn.id;
2285
2380
  }
2286
2381
  /**
2287
- * Set the transaction start timestamp in seconds.
2382
+ * Overrides the transaction timestamp in milliseconds since the Unix epoch.
2383
+ * Call this before reading the timestamp or staging writes when adopting an
2384
+ * origin timestamp for replication or replay. Once a write or log entry is
2385
+ * staged, the timestamp is frozen, but reapplying the same timestamp remains
2386
+ * idempotent while the transaction is pending. The value must be finite,
2387
+ * positive, and below 8640000000000000.
2288
2388
  *
2289
- * @param timestamp - The timestamp to set in seconds.
2389
+ * @param timestamp - The timestamp to set in milliseconds.
2290
2390
  */
2291
2391
  setTimestamp(timestamp) {
2292
2392
  this.#txn.setTimestamp(timestamp);
@@ -2320,9 +2420,23 @@ function backgroundErrorSeverityName(severity) {
2320
2420
  */
2321
2421
  var RocksDatabase = class RocksDatabase extends DBI {
2322
2422
  /**
2323
- * The name of the database.
2423
+ * The name of the database's column family.
2324
2424
  */
2325
2425
  #name;
2426
+ /**
2427
+ * Weak cache of column-family views opened via `use()`, keyed by column
2428
+ * family name — mirroring `useLog`'s get-or-create semantics. The reference
2429
+ * is weak so a view is never pinned for this database's lifetime: an
2430
+ * unreferenced view can be collected (closing its own `DBHandle`), and the
2431
+ * next `use()` transparently recreates it. `#columnFamilyRegistry` deletes an
2432
+ * entry once its view is collected so a churn of distinct names (e.g.
2433
+ * `use(uuid())`) does not grow the map without bound.
2434
+ */
2435
+ #columnFamilies = /* @__PURE__ */ new Map();
2436
+ #columnFamilyRegistry = new FinalizationRegistry((name) => {
2437
+ const ref = this.#columnFamilies.get(name);
2438
+ if (ref !== void 0 && ref.deref() === void 0) this.#columnFamilies.delete(name);
2439
+ });
2326
2440
  constructor(pathOrStore, options) {
2327
2441
  let store;
2328
2442
  if (typeof pathOrStore === "string") store = new Store(pathOrStore, options);
@@ -2330,7 +2444,7 @@ var RocksDatabase = class RocksDatabase extends DBI {
2330
2444
  else throw new TypeError("Invalid database path or store");
2331
2445
  store.claim();
2332
2446
  super(store);
2333
- this.#name = options?.name ?? "default";
2447
+ this.#name = this.store.name;
2334
2448
  }
2335
2449
  /**
2336
2450
  * Removes all data from the database asynchronously.
@@ -2549,6 +2663,40 @@ var RocksDatabase = class RocksDatabase extends DBI {
2549
2663
  static notify(event, ...args) {
2550
2664
  return globalNotify(event, args);
2551
2665
  }
2666
+ /**
2667
+ * Advances a secondary instance to the primary's current state by tailing
2668
+ * and replaying the primary's MANIFEST and WAL. A secondary does not see
2669
+ * the primary's writes until this (or {@link catchUpWithPrimarySync}) is
2670
+ * called. Concurrent reads through this handle remain safe while the
2671
+ * catch-up runs.
2672
+ *
2673
+ * Only valid on a database opened with the `secondaryPath` option; throws
2674
+ * with code `ERR_NOT_SECONDARY` otherwise.
2675
+ *
2676
+ * Advances the **database** view only. Against a cross-process primary,
2677
+ * transaction-log reads keep serving the stores discovered at open (a store
2678
+ * the primary creates later, and its appends, need a reopen); a primary in
2679
+ * the same process shares the store registry, so its stores and appends are
2680
+ * visible immediately. Either way a log entry can describe data this
2681
+ * handle's database view does not have yet — the log write completes before
2682
+ * the RocksDB commit for every writer, so consumers must tolerate the log
2683
+ * leading the database.
2684
+ *
2685
+ * Await one call before starting the next: the replay holds a libuv worker
2686
+ * for its whole duration and concurrent calls queue on a per-database mutex
2687
+ * while holding theirs, so overlapping catch-ups on a backlogged follower
2688
+ * can exhaust the default four-thread pool and stall unrelated I/O.
2689
+ */
2690
+ catchUpWithPrimary() {
2691
+ return new Promise((resolve, reject) => this.store.db.catchUpWithPrimary(resolve, reject));
2692
+ }
2693
+ /**
2694
+ * Synchronous version of {@link catchUpWithPrimary}. The replay runs on the
2695
+ * JS thread, so prefer the async form unless the caller is already blocking.
2696
+ */
2697
+ catchUpWithPrimarySync() {
2698
+ this.store.db.catchUpWithPrimarySync();
2699
+ }
2552
2700
  destroy() {
2553
2701
  this.store.db.destroy();
2554
2702
  }
@@ -2701,6 +2849,29 @@ var RocksDatabase = class RocksDatabase extends DBI {
2701
2849
  return this.store.db.getDBProperty(propertyName);
2702
2850
  }
2703
2851
  /**
2852
+ * Whether RocksDB is currently applying write backpressure to this database —
2853
+ * either delaying (rate-limiting) or fully stopping writes. This is the
2854
+ * authoritative, live pull counterpart to the `'writeStall'`
2855
+ * [event](../README.md#event-writestall): the event pushes per-column-family
2856
+ * transitions (and is rate-limited), while this reads the current state on
2857
+ * demand and can never be stale.
2858
+ *
2859
+ * The write controller is database-wide (shared across every column family),
2860
+ * so this reflects the database as a whole, not a single column family — a
2861
+ * stall triggered by any column family stops writes for all of them. Reads
2862
+ * `rocksdb.is-write-stopped` and `rocksdb.actual-delayed-write-rate`.
2863
+ *
2864
+ * @example
2865
+ * ```typescript
2866
+ * db.on('writeStall', (cf) => {
2867
+ * if (db.isWriteStalled()) log.warn(`writes throttled/blocked (triggered by ${cf})`);
2868
+ * });
2869
+ * ```
2870
+ */
2871
+ isWriteStalled() {
2872
+ return this.getDBIntProperty("rocksdb.is-write-stopped") === 1 || (this.getDBIntProperty("rocksdb.actual-delayed-write-rate") ?? 0) > 0;
2873
+ }
2874
+ /**
2704
2875
  * Retrieves the estimated number of keys in the database. This is an alias
2705
2876
  * for `db.estimateCount().count`; use `estimateCount()` for range support
2706
2877
  * and a confidence indicator.
@@ -2878,18 +3049,29 @@ var RocksDatabase = class RocksDatabase extends DBI {
2878
3049
  return this.store.listLogs();
2879
3050
  }
2880
3051
  /**
2881
- * The name of the database.
3052
+ * The name of the database's column family.
2882
3053
  */
2883
3054
  get name() {
2884
3055
  return this.#name;
2885
3056
  }
2886
3057
  /**
2887
- * Whether the database is open in readonly mode.
3058
+ * Whether the database is open in readonly mode. Also `true` for a
3059
+ * secondary instance (see {@link secondaryPath}), which is read-only by
3060
+ * construction.
2888
3061
  */
2889
3062
  get readOnly() {
2890
3063
  return this.store.readOnly;
2891
3064
  }
2892
3065
  /**
3066
+ * The secondary instance's workspace directory when the database was opened
3067
+ * as a secondary (a read-only follower of a live primary — see the
3068
+ * `secondaryPath` open option), or `undefined` for a regular or plain
3069
+ * read-only open.
3070
+ */
3071
+ get secondaryPath() {
3072
+ return this.store.secondaryPath;
3073
+ }
3074
+ /**
2893
3075
  * Sugar method for opening a database.
2894
3076
  *
2895
3077
  * @param pathOrStore - The filesystem path to the database or a custom store.
@@ -3162,6 +3344,57 @@ var RocksDatabase = class RocksDatabase extends DBI {
3162
3344
  return this.store.unlock(key);
3163
3345
  }
3164
3346
  /**
3347
+ * Returns a `RocksDatabase` bound to the `name` column family of this same
3348
+ * database, opening (and creating, if missing) the column family on first
3349
+ * use. This is a factory — create-and-open sugar — not a stateful switch: the
3350
+ * returned view is an independent instance whose own reads/writes target its
3351
+ * column family, while sharing the underlying database (so a transaction,
3352
+ * backup, or checkpoint still spans every column family). Because the view
3353
+ * opens its own handle, this database need not itself be open — a discarded
3354
+ * parent (`new RocksDatabase(path).use('events')`) still yields a valid,
3355
+ * opened view.
3356
+ *
3357
+ * Get-or-create, backed by a weak cache: `db.use('events') === db.use('events')`
3358
+ * while the view is still referenced **and open**, and calling with this
3359
+ * database's own column-family name returns `this`. A view that has been
3360
+ * closed (or garbage-collected — the cache does not pin it) is transparently
3361
+ * recreated on the next `use()`. Views are independent handles: closing this
3362
+ * database does not close them, and vice versa — the underlying database
3363
+ * stays open until every handle is closed or collected.
3364
+ *
3365
+ * The view's store is derived by {@link Store#createColumnFamilyStore}, which
3366
+ * builds an independent store of the same class with its own codec state (so
3367
+ * views never share a mutable encoder/decoder) and inherits the options this
3368
+ * store was constructed with; pass `options` to override them for this column
3369
+ * family. A database configured with a pre-constructed encoder/decoder
3370
+ * instance cannot derive views (its state would be shared) — use an encoder
3371
+ * factory (`{ Encoder }`) or a named `encoding`, or override
3372
+ * `createColumnFamilyStore` on a custom `Store`.
3373
+ *
3374
+ * @param name - The column family name.
3375
+ * @param options - Options for the column family, overriding inherited ones.
3376
+ * @returns A `RocksDatabase` bound to the column family.
3377
+ *
3378
+ * @example
3379
+ * ```typescript
3380
+ * const db = RocksDatabase.open('/path/to/database');
3381
+ * const events = db.use('events');
3382
+ * await events.put('e1', payload);
3383
+ * await db.put('k', 'v'); // default column family, unaffected
3384
+ * ```
3385
+ */
3386
+ use(name, options) {
3387
+ if (typeof name !== "string" || name === "") throw new TypeError("Column family name must be a non-empty string");
3388
+ if (name === this.#name) return this.open();
3389
+ const cached = this.#columnFamilies.get(name)?.deref();
3390
+ if (cached !== void 0 && cached.isOpen()) return cached;
3391
+ const columnFamily = new RocksDatabase(this.store.createColumnFamilyStore(name, options));
3392
+ columnFamily.open();
3393
+ this.#columnFamilies.set(name, new WeakRef(columnFamily));
3394
+ this.#columnFamilyRegistry.register(columnFamily, name);
3395
+ return columnFamily;
3396
+ }
3397
+ /**
3165
3398
  * Excecutes a function using a thread-safe lock to ensure mutual
3166
3399
  * exclusion.
3167
3400
  *
@@ -3468,11 +3701,12 @@ Object.defineProperty(TransactionLog.prototype, "query", { value({ start, end, e
3468
3701
  position += TRANSACTION_LOG_ENTRY_HEADER_SIZE;
3469
3702
  let matchesRange;
3470
3703
  if (foundExactStart) matchesRange = (!exclusiveStart || timestamp !== start) && timestamp < end;
3471
- else if (exactStart) if (timestamp === start) {
3472
- matchesRange = !exclusiveStart;
3473
- foundExactStart = true;
3474
- } else matchesRange = false;
3475
- else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
3704
+ else if (exactStart) {
3705
+ if (timestamp === start) {
3706
+ matchesRange = !exclusiveStart;
3707
+ foundExactStart = true;
3708
+ } else matchesRange = false;
3709
+ } else matchesRange = (exclusiveStart ? timestamp > start : timestamp >= start) && timestamp < end;
3476
3710
  const entryStart = position;
3477
3711
  position += length;
3478
3712
  if (matchesRange) return {
@@ -3555,7 +3789,7 @@ function loadLastPosition(transactionLog, readUncommitted) {
3555
3789
  //#region src/index.ts
3556
3790
  const versions = {
3557
3791
  rocksdb: version,
3558
- "rocksdb-js": "2.8.0"
3792
+ "rocksdb-js": "2.9.0"
3559
3793
  };
3560
3794
 
3561
3795
  //#endregion
@@ -3573,6 +3807,7 @@ exports.constants = constants;
3573
3807
  exports.coolTransactionLogs = coolTransactionLogs;
3574
3808
  exports.currentThreadId = currentThreadId;
3575
3809
  exports.fileLockRelease = fileLockRelease;
3810
+ exports.getWriteBufferManagerStats = getWriteBufferManagerStats;
3576
3811
  exports.parseTransactionLog = parseTransactionLog;
3577
3812
  exports.registryStatus = registryStatus;
3578
3813
  exports.shutdown = shutdown;