{"id":42386902,"url":"https://github.com/harperfast/rocksdb-js","last_synced_at":"2026-04-24T23:01:34.541Z","repository":{"id":318934569,"uuid":"857381805","full_name":"HarperFast/rocksdb-js","owner":"HarperFast","description":"RocksDB binding for Node.js","archived":false,"fork":false,"pushed_at":"2026-04-23T23:25:33.000Z","size":33332,"stargazers_count":5,"open_issues_count":20,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-04-24T01:31:17.204Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HarperFast.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2024-09-14T14:05:11.000Z","updated_at":"2026-04-23T05:54:16.000Z","dependencies_parsed_at":"2025-12-15T22:00:19.091Z","dependency_job_id":null,"html_url":"https://github.com/HarperFast/rocksdb-js","commit_stats":null,"previous_names":["harperfast/rocksdb-js"],"tags_count":254,"template":false,"template_full_name":null,"purl":"pkg:github/HarperFast/rocksdb-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarperFast%2Frocksdb-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarperFast%2Frocksdb-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarperFast%2Frocksdb-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarperFast%2Frocksdb-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HarperFast","download_url":"https://codeload.github.com/HarperFast/rocksdb-js/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HarperFast%2Frocksdb-js/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32243803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T13:21:15.438Z","status":"ssl_error","status_checked_at":"2026-04-24T13:21:15.005Z","response_time":64,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-27T23:09:46.427Z","updated_at":"2026-04-24T23:01:34.520Z","avatar_url":"https://github.com/HarperFast.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rocksdb-js\n\nA Node.js binding for the RocksDB library.\n\n## Features\n\n- Supports optimistic and pessimistic transactions\n- Hybrid sync/async data retrieval\n- Range queries return an iterable with array-like methods and lazy evaluation\n- Transaction log system for recording transaction related data\n- Custom stores provide ability to override default database interactions\n- Efficient binary key and value encoding\n- Access to internal RocksDB statistics\n- Designed for Node.js and Bun on Linux, macOS, and Windows\n\n## Example\n\n```typescript\nconst db = RocksDatabase.open('/path/to/db');\n\nfor (const key of ['a', 'b', 'c', 'd', 'e']) {\n\tawait db.put(key, `value ${key}`);\n}\n\nconsole.log(await db.get('b')); // `value b`\n\nfor (const { key, value } of db.getRange({ start: 'b', end: 'd' })) {\n\tconsole.log(`${key} = ${value}`);\n}\n\nawait db.transaction(async (txn: Transaction) =\u003e {\n\tawait txn.put('f', 'value f');\n\tawait txn.remove('c');\n});\n```\n\n## Usage\n\n### `new RocksDatabase(path, options?)`\n\nCreates a new database instance.\n\n- `path: string` The path to write the database files to. This path does not need to exist, but the\n  parent directories do.\n- `options: object` [optional]\n  - `disableWAL: boolean` Whether to disable the RocksDB write ahead log. Defaults to `false`.\n  - `enableStats: boolean` When `true` and the database is open, RocksDB will captures stats that\n    are retrieved by calling `db.getStats()`. Enabling statistics imposes 5-10% in overhead.\n    Defaults to `false`.\n  - `name: string` The column family name. Defaults to `\"default\"`.\n  - `noBlockCache: boolean` When `true`, disables the block cache. Block caching is enabled by\n    default and the cache is shared across all database instances.\n  - `parallelismThreads: number` The number of background threads to use for flush and compaction.\n    Defaults to `1`.\n  - `pessimistic: boolean` When `true`, throws conflict errors when they occur instead of waiting\n    until commit. Defaults to `false`.\n  - `readOnly: boolean` When `true`, the database is opened in read-only mode. Read operations are\n    permitted. Write operations will throw an error with code `ERR_DATABASE_READONLY`. Transactions\n    are a no-op in read-only mode.\n  - `statsLevel: StatsLevel` Controls which type of statistics to skip and reduce statistic\n    overhead. Defaults to `StatsLevel.ExceptDetailedTimers`.\n  - `store: Store` A custom store that handles all interaction between the `RocksDatabase` or\n    `Transaction` instances and the native database interface. See [Custom Store](#custom-store) for\n    more information.\n  - `transactionLogMaxAgeThreshold: number` The threshold for the transaction log file's last\n    modified time to be older than the retention period before it is rotated to the next sequence\n    number. Value must be between `0.0` and `1.0`. A threshold of `0.0` means ignore age check.\n    Defaults to `0.75`.\n  - `transactionLogMaxSize: number` The maximum size of a transaction log file. If a log file is\n    empty, the first log entry will always be added regardless if it's larger than the max size. If\n    a log file is not empty and the entry is larger than the space available, the log file is\n    rotated to the next sequence number. Defaults to 16 MB.\n  - `transactionLogRetention: string | number` The number of minutes to retain transaction logs\n    before purging. Defaults to `'3d'` (3 days).\n  - `transactionLogsPath: string` The path to store transaction logs. Defaults to\n    `\"${db.path}/transaction_logs\"`.\n\n### `db.close()`\n\nCloses a database. This function can be called multiple times and will only close an opened\ndatabase. A database instance can be reopened once its closed.\n\n```typescript\nconst db = RocksDatabase.open('foo');\ndb.close();\n```\n\n### `db.config(options)`\n\nSets global database settings.\n\n- `options: object`\n  - `blockCacheSize: number` The amount of memory in bytes to use to cache uncompressed blocks.\n    Defaults to 32MB. Set to `0` (zero) disables block cache for future opened databases. Existing\n    block cache for any opened databases is resized immediately. Negative values throw an error.\n\n```typescript\nRocksDatabase.config({\n\tblockCacheSize: 100 * 1024 * 1024, // 100MB\n});\n```\n\n### `db.isOpen(): boolean`\n\nReturns `true` if the database is open, otherwise false.\n\n```typescript\nconsole.log(db.isOpen()); // true or false\n```\n\n### `db.name: string`\n\nReturns the database column family's name.\n\n```typescript\nconst db = new RocksDatabase('path/to/db');\nconsole.log(db.name); // 'default'\n\nconst db2 = new RocksDatabase('path/to/db', { name: 'users' });\nconsole.log(db.name); // 'users'\n```\n\n### `db.open(): RocksDatabase`\n\nOpens the database at the given path. This must be called before performing any data operations.\n\n```typescript\nimport { RocksDatabase } from '@harperfast/rocksdb-js';\n\nconst db = new RocksDatabase('path/to/db');\ndb.open();\n```\n\nThere's also a static `open()` method for convenience that performs the same thing:\n\n```typescript\nconst db = RocksDatabase.open('path/to/db');\n```\n\n### `db.status: 'opened' | 'closed'`\n\nReturns a string `'opened'` or `'closed'` indicating if the database is opened or closed.\n\n```typescript\nconsole.log(db.status);\n```\n\n## Data Operations\n\n### `db.clear(options?): Promise\u003cnumber\u003e`\n\nAsychronously removes all data in the current database.\n\n- `options: object`\n  - `batchSize?: number` The number of records to remove at once. Defaults to `10000`.\n\nReturns the number of entries that were removed.\n\nNote: This does not remove data from other column families within the same database path.\n\n```typescript\nfor (let i = 0; i \u003c 10; i++) {\n\tdb.putSync(`key${i}`, `value${i}`);\n}\nconst entriesRemoved = await db.clear();\nconsole.log(entriesRemoved); // 10\n```\n\n### `db.clearSync(options?): number`\n\nSynchronous version of `db.clear()`.\n\n- `options: object`\n  - `batchSize?: number` The number of records to remove at once. Defaults to `10000`.\n\n```typescript\nfor (let i = 0; i \u003c 10; i++) {\n\tdb.putSync(`key${i}`, `value${i}`);\n}\nconst entriesRemoved = db.clearSync();\nconsole.log(entriesRemoved); // 10\n```\n\n### `db.destroy(): void`\n\nCompletely removes a database based on the `db` instance's path including all data, column families,\nand files on disk.\n\n```typescript\ndb.destroy();\nconsole.log(fs.existsSync(db.path)); // false\n```\n\n### `db.drop(): Promise\u003cvoid\u003e`\n\nRemoves all entries in the database. If the database was opened with a `name`, the database will be\ndeleted on close.\n\n```typescript\nconst db = RocksDatabase.open('path/to/db', { name: 'users' });\nawait db.drop();\ndb.close();\n```\n\n### `db.dropSync(): void`\n\nSynchronous version of `db.drop()`.\n\n```typescript\nconst db = RocksDatabase.open('path/to/db');\ndb.dropSync();\ndb.close();\n```\n\n### `db.get(key: Key, options?: GetOptions): MaybePromise\u003cany\u003e`\n\nRetreives the value for a given key. If the key does not exist, it will resolve `undefined`.\n\n```typescript\nconst result = await db.get('foo');\nassert.equal(result, 'foo');\n```\n\nIf the value is in the memtable or block cache, `get()` will immediately return the value\nsynchronously instead of returning a promise.\n\n```typescript\nconst result = db.get('foo');\nconst value = result instanceof Promise ? await result : result;\nassert.equal(result, 'foo');\n```\n\nNote that all errors are returned as rejected promises.\n\n### `db.getSync(key: Key, options?: GetOptions): any`\n\nSynchronous version of `get()`.\n\n### `db.getKeys(options?: IteratorOptions): ExtendedIterable`\n\nRetrieves all keys within a range.\n\n```typescript\nfor (const key of db.getKeys()) {\n\tconsole.log(key);\n}\n```\n\n### `db.getKeysCount(options?: RangeOptions): number`\n\nRetrieves the number of keys within a range.\n\n```typescript\nconst total = db.getKeysCount();\nconst range = db.getKeysCount({ start: 'a', end: 'z' });\n```\n\n### `db.getMonotonicTimestamp(): number`\n\nReturns the current timestamp as a monotonically increasing timestamp in milliseconds represented as\na decimal number.\n\n```typescript\nconst ts = db.getMonotonicTimestamp();\nconsole.log(ts); // 1764307857213.739\n```\n\n### `db.getOldestSnapshotTimestamp(): number`\n\nReturns a number representing a unix timestamp of the oldest unreleased snapshot.\n\nSnapshots are only created during transactions. When the database is opened in optimistic mode (the\ndefault), the snapshot will be created on the first read. When the database is opened in pessimistic\nmode, the snapshot will be created on the first read or write.\n\n```typescript\nconsole.log(db.getOldestSnapshotTimestamp()); // returns `0`, no snapshots\n\nconst promise = db.transaction(async (txn) =\u003e {\n\t// perform a write to create a snapshot\n\tawait txn.get('foo');\n\tawait setTimeout(100);\n});\n\nconsole.log(db.getOldestSnapshotTimestamp()); // returns `1752102248558`\n\nawait promise;\n// transaction completes, snapshot released\n\nconsole.log(db.getOldestSnapshotTimestamp()); // returns `0`, no snapshots\n```\n\n### `db.getDBProperty(propertyName: string): string`\n\nGets a RocksDB database property as a string.\n\n- `propertyName: string` The name of the property to retrieve (e.g., ) `'rocksdb.levelstats'`.\n\n```typescript\nconst db = RocksDatabase.open('/path/to/database');\nconst levelStats = db.getDBProperty('rocksdb.levelstats');\nconst stats = db.getDBProperty('rocksdb.stats');\n```\n\n### `db.getDBIntProperty(propertyName: string): number`\n\nGets a RocksDB database property as an integer.\n\n- `propertyName: string` The name of the property to retrieve (e.g., ) `'rocksdb.num-blob-files'`.\n\n```typescript\nconst db = RocksDatabase.open('/path/to/database');\nconst blobFiles = db.getDBIntProperty('rocksdb.num-blob-files');\nconst numKeys = db.getDBIntProperty('rocksdb.estimate-num-keys');\n```\n\n### `db.getRange(options?: IteratorOptions): ExtendedIterable`\n\nRetrieves a range of keys and their values. Supports both synchronous and asynchronous iteration.\n\n```typescript\n// sync\nfor (const { key, value } of db.getRange()) {\n\tconsole.log({ key, value });\n}\n\n// async\nfor await (const { key, value } of db.getRange()) {\n\tconsole.log({ key, value });\n}\n\n// key range\nfor (const { key, value } of db.getRange({ start: 'a', end: 'z' })) {\n\tconsole.log({ key, value });\n}\n```\n\n### `db.getUserSharedBuffer(key: Key, defaultBuffer: ArrayBuffer, options?)`\n\nCreates a new buffer with the contents of `defaultBuffer` that can be accessed across threads. This\nis useful for storing data such as flags, counters, or any ArrayBuffer-based data.\n\n- `options?: object`\n  - `callback?: () =\u003e void` A optional callback is called when `notify()` on the returned buffer is\n    called.\n\nReturns a new `ArrayBuffer` with two additional methods:\n\n- `notify()` - Invokes the `options.callback`, if specified.\n- `cancel()` - Removes the callback; future `notify()` calls do nothing\n\nNote: If a shared buffer already exists for the given `key`, the returned `ArrayBuffer` will\nreference this existing shared buffer. Once all `ArrayBuffer` instances have gone out of scope and\ngarbage collected, the underlying memory and notify callback will be freed.\n\n```typescript\nconst buffer = new Uint8Array(db.getUserSharedBuffer('isDone', new ArrayBuffer(1)));\ndone[0] = 0;\n\nif (done[0] !== 1) {\n\tdone[1] = 1;\n}\n```\n\n```typescript\nconst incrementer = new BigInt64Array(\n\tdb.getUserSharedBuffer('next-id', new BigInt64Array(1).buffer)\n);\nincrementer[0] = 1n;\n\nfunction getNextId() {\n\treturn Atomics.add(incrementer, 0, 1n);\n}\n```\n\n### `db.put(key: Key, value: any, options?: PutOptions): Promise`\n\nStores a value for a given key.\n\n```typescript\nawait db.put('foo', 'bar');\n```\n\n### `db.putSync(key: Key, value: any, options?: PutOptions): void`\n\nSynchronous version of `put()`.\n\n### `db.remove(key: Key): Promise`\n\nRemoves the value for a given key.\n\n```typescript\nawait db.remove('foo');\n```\n\n### `db.removeSync(key: Key): void`\n\nSynchronous version of `remove()`.\n\n## Transactions\n\n### `db.transaction\u003cT\u003e(callback: TransactionCallback\u003cT\u003e, options?: DBTransactionOptions): Promise\u003cT\u003e`\n\nExecutes all database operations within the specified callback within a single transaction. If the\ncallback completes without error, the database operations are automatically committed. However, if\nan error is thrown during the callback, all database operations will be rolled back.\n\n```typescript\nimport type { Transaction } from '@harperfast/rocksdb-js';\nawait db.transaction(async (txn: Transaction) =\u003e {\n\tawait txn.put('foo', 'baz');\n});\n```\n\nAdditionally, you may pass the transaction into any database data method:\n\n```typescript\nawait db.transaction(async (transaction: Transaction) =\u003e {\n\tawait db.put('foo', 'baz', { transaction });\n});\n```\n\nNote that `db.transaction()` returns whatever value the transaction callback returns:\n\n```typescript\nconst isBar = await db.transaction(async (txn: Transaction) =\u003e {\n\tconst foo = await txn.get('foo');\n\treturn foo === 'bar';\n});\nconsole.log(isBar ? 'Foo is bar' : 'Foo is not bar');\n```\n\n### `db.transactionSync\u003cT\u003e(callback: TransactionCallback\u003cT\u003e, options?: TransactionOptions): T`\n\nExecutes a transaction callback and commits synchronously. Once the transaction callback returns,\nthe commit is executed synchronously and blocks the current thread until finished.\n\nInside a synchronous transaction, use `getSync()`, `putSync()`, and `removeSync()`.\n\n```typescript\nimport type { Transaction } from '@harperfast/rocksdb-js';\ndb.transactionSync((txn: Transaction) =\u003e {\n\ttxn.putSync('foo', 'baz');\n});\n```\n\n### Optimistic and Pessimistic Modes\n\n`rocksdb-js` supports two different transaction modes: optimistic and pessimistic. The default mode\nis optimistic.\n\n- Optimistic: Conflicts detected at commit time.\n- Pessimistic: Conflicts throw immediately on detection.\n\nWhen a database is opened in optimistic mode, transactions are not locked and can be retried if\nthey fail with a conflict. When a database is opened in pessimistic mode, transactions are aborted\nand cannot be retried if they fail with a conflict.\n\nOptimistic mode is the default mode and is recommended for most use cases. Pessimistic mode is\nrecommended for use cases where you need to know immediately if a conflict occurs.\n\nIf a database is opened in one mode, it cannot be opened in a different mode. An error will be\nthrown when trying to open it in a different mode without closing the database first.\n\n### `TransactionCallback\u003cT\u003e`\n\n`(txn: Transaction, attempt: number) =\u003e T | PromiseLike\u003cT\u003e`\n\nA sync or async function to encapsulate all of the transaction operations. Once the function is\nexecuted, the transaction is automatically committed. If the function returns a value, it will be\nreturned from the transaction call.\n\nThe `txn` parameter is a `Transaction`. See the [Transaction](#class-transaction) section for more\ndetails.\n\nThe `attempt` parameter is the number of times the transaction has been retried.\n\n### `TransactionOptions`\n\n- `disableSnapshot?: boolean` Whether to disable snapshots. Defaults to `false`.\n- `maxRetries?: number` The maximum number of times to retry the transaction. Defaults to `3`.\n- `retryOnBusy?: boolean` Whether to retry the transaction if the commit fails with `IsBusy`.\n  Defaults to `true` when the transaction is bound to a transaction log, otherwise `false`.\n\n### Transaction Retry Logic\n\nThe retry mechanism will only be active when the `retryOnBusy` option is `true` or when\n`retryOnBusy` is `undefined` and the transaction is bound to a transaction log. The attempts starts\nat `1` and ends at `maxRetries`.\n\nWhen using a transaction log and the commit fails with `ERR_BUSY`, the transaction log will be in\na bad state and the transaction will need to be retried. If the max retries is reached or the\ntransaction is not retried, a `ERR_TRANSACTION_ABANDONED` error will be thrown.\n\nUsers should use the `attempt` transaction callback parameter to ensure duplicate transaction log\nentries are not added.\n\n### Class: `Transaction`\n\nThe transaction callback is passed in a `Transaction` instance which contains all of the same data\noperations methods as the `RocksDatabase` instance plus:\n\n- `txn.abort()` Rolls back and closes the transaction. This method is automatically called after the\n  transaction callback returns, so you shouldn't need to call it, but it's ok to do so. Once called,\n  no further transaction operations are permitted. Calling this method multiple times has no effect.\n- `txn.commit(): Promise\u003cvoid\u003e` Asynchronously commits the transaction and closes the transaction.\n- `txn.commitSync()` Synchronously commits and closes the transaction.\n- `txn.getTimestamp(): number` Retrieves the transaction start timestamp in seconds as a decimal. It\n  defaults to the time at which the transaction was created.\n- `txn.id: number` The read-only transaction ID. Transaction IDs are unique to the RocksDB database\n  path, regardless the database name/column family.\n- `txn.setTimestamp(ts?: number): void` Overrides the transaction start timestamp. If called without\n  a timestamp, it will set the timestamp to the current time. The value must be in seconds with\n  higher precision in the decimal.\n\n#### `txn.abort(): void`\n\nRolls back and closes the transaction. This method is automatically called after the transaction\ncallback returns, so you shouldn't need to call it, but it's ok to do so. Once called, no further\ntransaction operations are permitted.\n\n#### `txn.commit(): Promise\u003cvoid\u003e`\n\nCommits and closes the transaction. This is a non-blocking operation and runs on a background\nthread. Once called, no further transaction operations are permitted.\n\n#### `txn.commitSync(): void`\n\nSynchronously commits and closes the transaction. This is a blocking operation on the main thread.\nOnce called, no further transaction operations are permitted.\n\n#### `txn.getTimestamp(): number`\n\nRetrieves the transaction start timestamp in seconds as a decimal. It defaults to the time at which\nthe transaction was created.\n\n#### `txn.id`\n\nType: `number`\n\nThe transaction ID represented as a 32-bit unsigned integer. Transaction IDs are unique to the\nRocksDB database path, regardless the database name/column family.\n\n#### `txn.setTimestamp(ts: number?): void`\n\nOverrides the transaction start timestamp. If called without a timestamp, it will set the timestamp\nto the current time. The value must be in seconds with higher precision in the decimal.\n\n```typescript\nawait db.transaction(async (txn) =\u003e {\n\ttxn.setTimestamp(Date.now() / 1000);\n});\n```\n\n## Events\n\n### Event: `'aftercommit'`\n\nThe `'aftercommit'` event is emitted after a transaction has been committed and the transaction has\ncompleted including waiting for the async worker thread to finish.\n\n- `result: object`\n  - `next: null`\n  - `last: null`\n  - `txnId: number` The id of the transaction that was just committed.\n\n### Event: `'beforecommit'`\n\nThe `'beforecommit'` event is emitted before a transaction is about to be committed.\n\n### Event: `'begin-transaction'`\n\nThe `'begin-transaction'` event is emitted right before the transaction function is executed.\n\n### Event: `'committed'`\n\nThe `'committed'` event is emitted after the transaction has been written. When this event is\nemitted, the transaction is still cleaning up. If you need to know when the transaction is fully\ncomplete, use the `'aftercommit'` event.\n\n## Event API\n\n`rocksdb-js` provides a EventEmitter-like API that lets you asynchronously notify events to one or\nmore synchronous listener callbacks. Events are scoped by database path.\n\nUnlike `EventEmitter`, events are emitted asynchronously, but in the same order that the listeners\nwere added.\n\n```typescript\nconst callback = (name) =\u003e console.log(`Hi from ${name}`);\ndb.addListener('foo', callback);\ndb.notify('foo');\ndb.notify('foo', 'bar');\ndb.removeListener('foo', callback);\n```\n\n### `addListener(event: string, callback: () =\u003e void): void`\n\nAdds a listener callback for the specific key.\n\n```typescript\ndb.addListener('foo', () =\u003e {\n\t// this callback will be executed asynchronously\n});\n\ndb.addListener(1234, (...args) =\u003e {\n\tconsole.log(args);\n});\n```\n\n### `listeners(event: string): number`\n\nGets the number of listeners for the given key.\n\n```typescript\ndb.listeners('foo'); // 0\ndb.addListener('foo', () =\u003e {});\ndb.listeners('foo'); // 1\n```\n\n### `on(event: string, callback: () =\u003e void): void`\n\nAlias for `addListener()`.\n\n### `once(event: string, callback: () =\u003e void): void`\n\nAdds a one-time listener, then automatically removes it.\n\n```typescript\ndb.once('foo', () =\u003e {\n\tconsole.log('This will only ever be called once');\n});\n```\n\n### `removeListener(event: string, callback: () =\u003e void): boolean`\n\nRemoves an event listener. You must specify the exact same callback that was used in\n`addListener()`.\n\n```typescript\nconst callback = () =\u003e {};\ndb.addListener('foo', callback);\n\ndb.removeListener('foo', callback); // return `true`\ndb.removeListener('foo', callback); // return `false`, callback not found\n```\n\n### `off(event: string, callback: () =\u003e void): boolean`\n\nAlias for `removeListener()`.\n\n### `notify(event: string, ...args?): boolean`\n\nCall all listeners for the given key. Returns `true` if any callbacks were found, otherwise `false`.\n\nUnlike `EventEmitter`, events are emitted asynchronously, but in the same order that the listeners\nwere added.\n\nYou can optionally emit one or more arguments. Note that the arguments must be serializable. In\nother words, `undefined`, `null`, strings, booleans, numbers, arrays, and objects are supported.\n\n```typescript\ndb.notify('foo');\ndb.notify(1234);\ndb.notify({ key: 'bar' }, { value: 'baz' });\n```\n\n## Statistics\n\nRetrieve RocksDB statistics at runtime. You must set `enableStats: true` when calling `db.open()`.\nStatistics are captured at the database level and include all column families.\n\nRocksDB has two types of statistics: tickers and histograms. Tickers are 64-bit unsigned integers\nthat measure counters. Histograms are objects containing various measurements of statistic\ndistribution across all operations.\n\n```typescript\nimport { RocksDatabase, stats } from '@harperfast/rocksdb-js';\nconst db = RocksDatabase.open('/path/to/db', {\n\tenableStats: true,\n\tstatsLevel: stats.StatsLevel.ExceptDetailedTimers, // default\n});\nconsole.log(db.getStats());\n```\n\n### `db.getStat(statName: string): RocksDBStat`\n\nRetrieves a single statistic value. Return value is either a `number` or `StatsHistogramData`\nobject.\n\n```typescript\nconsole.log(db.getStat('rocksdb.block.cache.miss'));\n```\n\n### `db.getStats(all?: boolean): RocksDBStats`\n\nReturns an object containing a curated list of column family-level properties, internal tickers\nstats, and internal histogram stats. Return value is an object with the stat name as the key and\na `RocksDBStat` as the value.\n\nBy default, it only returns the most meaningful internal stats. When `all = true`, it returns the\nsame column family-level properties, but includes all internal tickers and histogram stats.\n\nColumn family and ticker stat values are 64-bit unsigned integers and histogram values are\n`StatsHistogramData` objects.\n\n```typescript\n// get essential stats\nconsole.log(db.getStats());\n\n// get all stats\nconsole.log(db.getStats(true));\n```\n\n### `stats`\n\nAn object containing stat specific constants including ticker and histogram names.\n\n#### `stats.histograms`\n\nAn array of internal histogram stat names.\n\n```typescript\nimport { stats } from '@harperfast/rocksdb-js';\nconsole.log(stats.histograms);\n```\n\n#### `stats.tickers`\n\nAn array of internal ticker stat names.\n\n```typescript\nimport { stats } from '@harperfast/rocksdb-js';\nconsole.log(stats.tickers);\n```\n\n#### `stats.StatsLevel`\n\nThe `stats.StatsLevel` contains constants used to set which types of skip and reduce statistic overhead.\n\n- `StatsLevel.DisableAll` Disable all metrics.\n- `StatsLevel.ExceptTickers` Disable all tickers.\n- `StatsLevel.ExceptHistogramOrTimers` Disable timer stats and skip histogram stats.\n- `StatsLevel.ExceptTimers` Skip timer stats\n- `StatsLevel.ExceptDetailedTimers` Skip time waiting for mutex locks and compression.\n- `StatsLevel.ExceptTimeForMutex` Skip time waiting for mutex locks.\n- `StatsLevel.All` Collects all stats.\n\n### `type RocksDBStat = number | StatsHistogramData`\n\nA `RocksDBStat` is either a `number` or `StatsHistogramData` object.\n\n### `type RocksDBStats = Record\u003cstring, RocksDBStat\u003e`\n\nA `RocksDBStats` is an object with the stat name as the key and a `RocksDBStat` as the value.\n\n### `type StatsHistogramData`\n\nAn object is a record with the following properties:\n\n- `average: number` A double containing the average value.\n- `count: number` An unsigned 64-bit integer containing the number of values.\n- `max: number` A double containing the maximum value.\n- `median: number` A double containing the median value.\n- `min: number` A double containing the minimum value.\n- `percentile95: number` A double containing the 95th percentile value.\n- `percentile99: number` A double containing the 99th percentile value.\n- `standardDeviation: number` A double containing the standard deviation.\n- `sum: number` An unsigned 64-bit integer containing the sum of all values.\n\n## Exclusive Locking\n\n`rocksdb-js` includes a handful of functions for executing thread-safe mutually exclusive functions.\n\n### `db.hasLock(key: Key): boolean`\n\nReturns `true` if the database has a lock for the given key, otherwise `false`.\n\n```typescript\ndb.hasLock('foo'); // false\ndb.tryLock('foo'); // true\ndb.hasLock('foo'); // true\n```\n\n### `db.tryLock(key: Key, onUnlocked?: () =\u003e void): boolean`\n\nAttempts to acquire a lock for a given key. If the lock is available, the function returns `true`\nand the optional `onUnlocked` callback is never called. If the lock is not available, the function\nreturns `false` and the `onUnlocked` callback is queued until the lock is released.\n\nWhen a database is closed, all locks associated to it will be unlocked.\n\n```typescript\ndb.tryLock('foo', () =\u003e {\n\tconsole.log('never fired');\n}); // true, callback ignored\n\ndb.tryLock('foo', () =\u003e {\n\tconsole.log('hello world');\n}); // false, already locked, callback queued\n\ndb.unlock('foo'); // fires second lock callback\n```\n\nThe `onUnlocked` callback function can be used to signal to retry acquiring the lock:\n\n```typescript\nfunction doSomethingExclusively() {\n\t// if lock is unavailable, queue up callback to recursively retry\n\tif (db.tryLock('foo', () =\u003e doSomethingExclusively())) {\n\t\t// lock acquired, do something exclusive\n\n\t\tdb.unlock('foo');\n\t}\n}\n```\n\n### `db.unlock(key): boolean`\n\nReleases the lock on the given key and calls any queued `onUnlocked` callback handlers. Returns\n`true` if the lock was released or `false` if the lock did not exist.\n\n```typescript\ndb.tryLock('foo');\ndb.unlock('foo'); // true\ndb.unlock('foo'); // false, already unlocked\n```\n\n### `db.withLock(key: Key, callback: () =\u003e void | Promise\u003cvoid\u003e): Promise\u003cvoid\u003e`\n\nRuns a function with guaranteed exclusive access across all threads.\n\n```typescript\nawait db.withLock('key', async () =\u003e {\n\t// do something exclusive\n\tconsole.log(db.hasLock('key')); // true\n});\n```\n\nIf there are more than one simultaneous lock requests, it will block them until the lock is\navailable.\n\n```typescript\nawait Promise.all([\n\tdb.withLock('key', () =\u003e {\n\t\tconsole.log('first lock blocking for 100ms');\n\t\treturn new Promise((resolve) =\u003e setTimeout(resolve, 100));\n\t}),\n\tdb.withLock('key', () =\u003e {\n\t\tconsole.log('second lock blocking for 100ms');\n\t\treturn new Promise((resolve) =\u003e setTimeout(resolve, 100));\n\t}),\n\tdb.withLock('key', () =\u003e {\n\t\tconsole.log('third lock acquired');\n\t}),\n]);\n```\n\nNote: If the `callback` throws an error, Node.js suppress the error. Node.js 18.3.0 introduced a\n`--force-node-api-uncaught-exceptions-policy` flag which will cause errors to emit the\n`'uncaughtException'` event. Future Node.js releases will enable this flag by default.\n\n### `db.flush(): Promise\u003cvoid\u003e`\n\nFlushes all in-memory data to disk asynchronously.\n\n```typescript\nawait db.flush();\n```\n\n### `db.flushSync(): void`\n\nFlushes all in-memory data to disk synchronously. Note that this can be an expensive operation, so\nit is recommended to use `flush()` if you want to keep the event loop free.\n\n```typescript\ndb.flushSync();\n```\n\n## Transaction Log\n\nA user controlled API for logging transactions. This API is designed to be generic so that you can\nlog gets, puts, and deletes, but also arbitrary entries.\n\nTransaction logs are isolated by the database path allowing different column families in the same\ndatabase to share the transaction log store, but not other databases.\n\n### `db.listLogs(): string[]`\n\nReturns an array of log store names.\n\n```typescript\nconst names = db.listLogs();\n```\n\n### `db.purgeLogs(options?): string[]`\n\nDeletes transaction log files older than the `transactionLogRetention` (defaults to 3 days).\n\n- `options: object`\n  - `before?: number` Remove all transaction log files older than the specified timestamp.\n  - `destroy?: boolean` When `true`, deletes transaction log stores including all log sequence files\n    on disk.\n  - `name?: string` The name of a store to limit the purging to.\n\nReturns an array with the full path of each log file deleted.\n\n```typescript\nconst removed = db.purgeLogs();\nconsole.log(`Removed ${removed.length} log files`);\n```\n\n### `db.useLog(name): TransactionLog`\n\nGets or creates a `TransactionLog` instance. Internally, the `TransactionLog` interfaces with a\nshared transaction log store that is used by all threads. Multiple worker threads can use the same\nlog at the same time.\n\n- `name: string | number` The name of the log. Numeric log names are converted to a string.\n\n```typescript\nconst log1 = db.useLog('foo');\nconst log2 = db.useLog('foo'); // gets existing instance (e.g. log1 === log2)\nconst log3 = db.useLog(123);\n```\n\n`Transaction` instances also provide a `useLog()` method that binds the returned transaction log to\nthe transaction so you don't need to pass in the transaction id every time you add an entry.\n\n```typescript\nawait db.transaction(async (txn) =\u003e {\n\tconst log = txn.useLog('foo');\n\tlog.addEntry(Buffer.from('hello'));\n});\n```\n\n### Class: `TransactionLog`\n\nA `TransactionLog` lets you add arbitrary data bound to a transaction that is automatically written\nto disk right before the transaction is committed. You may add multiple enties per transaction. The\nunderlying architecture is thread safe.\n\n- `log.addEntry()`\n- `log.path`\n- `log.query()`\n\n#### `log.addEntry(data, transactionId): void`\n\nAdds an entry to the transaction log.\n\n- `data: Buffer | UInt8Array` The entry data to store. There is no inherent limit beyond what\n  Node.js can handle.\n- `transactionId: Number` A related transaction used to batch entries on commit.\n\n```typescript\nconst log = db.useLog('foo');\nawait db.transaction(async (txn) =\u003e {\n\tlog.addEntry(Buffer.from('hello'), txn.id);\n});\n```\n\nIf using `txn.useLog()` (instead of `db.useLog()`), you can omit the transaction id from\n`addEntry()` calls.\n\n```typescript\nawait db.transaction(async (txn) =\u003e {\n\tconst log = txn.useLog('foo');\n\tlog.addEntry(Buffer.from('hello'));\n});\n```\n\nNote that the `TransactionLog` class also has internal methods `_getMemoryMapOfFile`,\n`_findPosition`, and `_getLastCommittedPosition` that should not be used directly and may change in\nany version.\n\n#### `log.path: string`\n\nReturns the path to the transaction log store files.\n\n```typescript\nconst log = db.useLog('foo');\nconsole.log(log.path);\n```\n\n#### `log.query(options?): IterableIterator\u003cTransactionLogEntry\u003e`\n\nReturns an iterable/iterator that streams all log entries for the given filter.\n\n- `options: object`\n  - `start?: number` The transaction start timestamp.\n  - `end?: string` The transction end timestamp.\n  - `exclusiveStart?: boolean` When `true`, this will only match transactions with timestamps after\n    the start timestamp.\n  - `exactStart?: boolean` When `true`, this will only match and iterate starting from a transaction\n    with the given start timestamp. Once the specified transaction is found, all subsequent\n    transactions will be returned (regardless of whether their timestamp comes before the `start`\n    time). This can be combined with `exactStart`, finding the specified transaction, and returning\n    all transactions that follow. By default, all transactions equal to or greater than the start\n    timestamp will be included.\n  - `readUncommitted?: boolean` When `true`, this will include uncommitted transaction entries.\n    Normally transaction entries that haven't finished committed are not included. This is\n    particularly useful for replaying transaction logs on startup where many entries may have been\n    written to the log but are no longer considered committed if they were not flushed to disk.\n  - `startFromLastFlushed?: boolean` When `true`, this will only match transactions that have been\n    flushed from RocksDB's memtables to disk (and are within any provided `start` and `end` filters,\n    if included). This is useful for replaying transaction logs on startup where many entries may\n    have been written to the log but are no longer considered committed if they were not flushed to\n    disk.\n\nThe iterator produces an object with the log entry timestamp and data.\n\n- `object`\n  - `data: Buffer` The entry data.\n  - `timestamp: number` The entry timestamp used to collate entries by transaction.\n  - `endTxn: boolean` This is `true` when the entry is the last entry in a transaction.\n\n```typescript\nconst log = db.useLog('foo');\nconst iter = log.query({});\nfor (const entry of iter) {\n\tconsole.log(entry);\n}\n\nconst lastHour = Date.now() - 60 * 60 * 1000;\nconst rangeIter = log.query({ start: lastHour, end: Date.now() });\nfor (const entry of rangeIter) {\n\tconsole.log(entry.timestamp, entry.data);\n}\n```\n\n#### `log.getLogFileSize(sequenceNumber?: number): number`\n\nReturns the size of the given transaction log sequence file in bytes. Omit the sequence number to\nget the total size of all the transaction log sequence files for this log.\n\n### Transaction Log Parser\n\n#### `parseTransactionLog(file)`\n\nIn general, you should use `log.query()` to query the transaction log, however, if you need to load\nan entire transaction log into memory and want detailed information about entries, you can use the\n`parseTransactionLog()` utility function.\n\n```typescript\nconst everything = parseTransactionLog('/path/to/file.txnlog');\nconsole.log(everything);\n```\n\nReturns an object containing all of the information in the log file.\n\n- `size: number` The size of the file.\n- `version: number` The log file format version.\n- `entries: LogEntry[]` An array of transaction log entries.\n  - `data: Buffer` The entry data.\n  - `flags: number` Transaction related flags.\n  - `length: number` The size of the entry data.\n  - `timestamp: number` The entry timestamp.\n\n### `currentThreadId(): number`\n\nReturns the current thread ID.\n\n```typescript\nimport { currentThreadId } from '@harperfast/rocksdb-js';\nconsole.log(currentThreadId());\n```\n\n### `registryStatus(): RegistryStatus`\n\nReturns an array containing that status of all active RocksDB instances.\n\n- `path: string` The database path.\n- `refCount: number` The number of JavaScript database instances plus the registry's reference.\n- `columnFamiles: object` A map of column family names and their their info.\n  - `userSharedBuffers: number` The count of active user shared buffers.\n- `transactions: number` The count of active transactions.\n- `closables: number` The count of active database, transactions, and iterators.\n- `locks: number` The count of active locks.\n- `listenerCallbacks: number` The count of in-flight callbacks.\n\n```typescript\nimport { registryStatus } from '@harperfast/rocksdb-js';\nconsole.log(registryStatus());\n```\n\n### `shutdown(): void`\n\nThe `shutdown()` will flush all in-memory data to disk and wait for any outstanding compactions to\nfinish, for all open databases. It is highly recommended to call this in a `process` `exit` event\nlistener (on the main thread), to ensure that all data is flushed to disk before the process exits:\n\n```typescript\nimport { shutdown } from '@harperfast/rocksdb-js';\nprocess.on('exit', shutdown);\n```\n\n### `versions: { 'rocksdb': string; 'rocksdb-js': string }`\n\nReturns the `rocksdb-js` and RocksDB version.\n\n```typescript\nimport { versions } from '@harperfast/rocksdb-js';\nconsole.log(versions); // { \"rocksdb\": \"10.10.1\", \"rocksdb-js\": \"0.1.2\" }\n```\n\n## Custom Store\n\nThe store is a class that sits between the `RocksDatabase` or `Transaction` instance and the native\nRocksDB interface. It owns the native RocksDB instance along with various settings including\nencoding and the db name. It handles all interactions with the native RocksDB instance.\n\nThe default `Store` contains the following methods which can be overridden:\n\n- `constructor(path, options?)`\n- `close()`\n- `decodeKey(key)`\n- `decodeValue(value)`\n- `encodeKey(key)`\n- `encodeValue(value)`\n- `get(context, key, resolve, reject, txnId?)`\n- `getCount(context, options?, txnId?)`\n- `getKeys(context, options?)`\n- `getKeysCount(context, options?)`\n- `getRange(context, options?)`\n- `getSync(context, key, options?)`\n- `getUserSharedBuffer(key, defaultBuffer?)`\n- `hasLock(key)`\n- `isOpen()`\n- `listLogs()`\n- `open()`\n- `putSync(context, key, value, options?)`\n- `removeSync(context, key, options?)`\n- `tryLock(key, onUnlocked?)`\n- `unlock(key)`\n- `useLog(context, name)`\n- `withLock(key, callback?)`\n\nTo use it, extend the default `Store` and pass in an instance of your store into the `RocksDatabase`\nconstructor.\n\n```typescript\nimport { RocksDatabase, Store } from '@harperfast/rocksdb-js';\n\nclass MyStore extends Store {\n  get(context, key, resolve, reject, txnId) {\n    console.log('Getting:' key);\n    return super.get(context, key, resolve, reject, txnId);\n  }\n\n  putSync(context, key, value, options) {\n    console.log('Putting:', key);\n    return super.putSync(context, key, value, options);\n  }\n}\n\nconst myStore = new MyStore('path/to/db');\nconst db = RocksDatabase.open(myStore);\nawait db.put('foo', 'bar');\nconsole.log(await db.get('foo'));\n```\n\n\u003e [!IMPORTANT]\n\u003e If your custom store overrides `putSync()` without calling `super.putSync()` and it performs its\n\u003e own `this.encodeKey(key)`, then you MUST encode the VALUE before you encode the KEY.\n\u003e\n\u003e Keys are encoded into a shared buffer. If the database is opened with the `sharedStructuresKey`\n\u003e option, encoding the value will load and save the structures which encodes the\n\u003e `sharedStructuresKey` overwriting the encoded key in the shared key buffer, so it's ultra\n\u003e important that you encode the value first!\n\n## Interfaces\n\n### `RocksDBOptions`\n\n- `options: object`\n  - `adaptiveReadahead: boolean` When `true`, RocksDB will do some enhancements for prefetching the\n    data. Defaults to `true`. Note that RocksDB defaults this to `false`.\n  - `asyncIO: boolean` When `true`, RocksDB will prefetch some data async and apply it if reads are\n    sequential and its internal automatic prefetching. Defaults to `true`. Note that RocksDB\n    defaults this to `false`.\n  - `autoReadaheadSize: boolean` When `true`, RocksDB will auto-tune the readahead size during scans\n    internally based on the block cache data when block caching is enabled, an end key (e.g. upper\n    bound) is set, and prefix is the same as the start key. Defaults to `true`.\n  - `backgroundPurgeOnIteratorCleanup: boolean` When `true`, after the iterator is closed, a\n    background job is scheduled to flush the job queue and delete obsolete files. Defaults to\n    `true`. Note that RocksDB defaults this to `false`.\n  - `fillCache: boolean` When `true`, the iterator will fill the block cache. Filling the block\n    cache is not desirable for bulk scans and could impact eviction order. Defaults to `false`. Note\n    that RocksDB defaults this to `true`.\n  - `readaheadSize: number` The RocksDB readahead size. RocksDB does auto-readahead for iterators\n    when there is more than two reads for a table file. The readahead starts at 8KB and doubles on\n    every additional read up to 256KB. This option can help if most of the range scans are large and\n    if a larger readahead than that enabled by auto-readahead is needed. Using a large readahead\n    size (\u003e 2MB) can typically improve the performance of forward iteration on spinning disks.\n    Defaults to `0`.\n  - `tailing: boolean` When `true`, creates a \"tailing iterator\" which is a special iterator that\n    has a view of the complete database including newly added data and is optimized for sequential\n    reads. This will return records that were inserted into the database after the creation of the\n    iterator. Defaults to `false`.\n\n### `RangeOptions`\n\nExtends `RocksDBOptions`.\n\n- `options: object`\n  - `end: Key | Uint8Array` The range end key, otherwise known as the \"upper bound\". Defaults to the\n    last key in the database.\n  - `exclusiveStart: boolean` When `true`, the iterator will exclude the first key if it matches the\n    start key. Defaults to `false`.\n  - `inclusiveEnd: boolean` When `true`, the iterator will include the last key if it matches the\n    end key. Defaults to `false`.\n  - `start: Key | Uint8Array` The range start key, otherwise known as the \"lower bound\". Defaults to\n    the first key in the database.\n\n### `IteratorOptions`\n\nExtends `RangeOptions`.\n\n- `options: object`\n  - `reverse: boolean` When `true`, the iterator will iterate in reverse order. Defaults to `false`.\n\n## Development\n\nThis package requires Node.js 18 or higher, pnpm, and a C++ compiler.\n\n\u003e [!TIP]\n\u003e Enable pnpm log streaming to see full build output:\n\u003e\n\u003e ```\n\u003e pnpm config set stream true\n\u003e ```\n\n### Building\n\nThere are two things being built: the native binding and the TypeScript code. Each of those can be\nbuilt to be debug friendly.\n\n| Description                                  | Command                                  |\n| -------------------------------------------- | ---------------------------------------- |\n| Production build (minified + native binding) | `pnpm build`                             |\n| TypeScript only (minified)                   | `pnpm build:bundle`                      |\n| TypeScript only (unminified)                 | `pnpm build:debug`                       |\n| Native binding only (prod)                   | `pnpm rebuild`                           |\n| Native binding only (with debug logging)     | `pnpm rebuild:debug`                     |\n| Debug build everything                       | `pnpm build:debug \u0026\u0026 pnpm rebuild:debug` |\n\nWhen building the native binding, it will download the appropriate prebuilt RocksDB library for your\nplatform and architecture from the\n[rocksdb-prebuilds](https://github.com/HarperFast/rocksdb-prebuilds) GitHub repository. It defaults\nto the pinned version in the `package.json` file. You can override this by setting the\n`ROCKSDB_VERSION` environment variable. For example:\n\n```bash\nROCKSDB_VERSION=10.9.1 pnpm build\n```\n\nYou may also specify `latest` to use the latest prebuilt version.\n\n```bash\nROCKSDB_VERSION=latest pnpm build\n```\n\nOptionally, you may also create a `.env` file in the root of the project to specify various\nsettings. For example:\n\n```bash\necho \"ROCKSDB_VERSION=10.9.1\" \u003e\u003e .env\n```\n\n### Linux C runtime versions\n\nWhen you compile `rocksdb-js`, you can specify the `ROCKSDB_LIBC` environment variable to choose\neither `glibc` (default) or `musl`.\n\n```bash\nROCKSDB_LIBC=musl pnpm rebuild\n```\n\n### Windows C runtime versions\n\nBy default on Windows, `rocksdb-js` is compiled with the `/MT` flag. This will statically link the\nC runtime making the binary self-contained and portable.\n\n### Building RocksDB from Source\n\nTo build RocksDB from source, simply set the `ROCKSDB_PATH` environment variable to the path of the\nlocal `rocksdb` repo:\n\n```bash\ngit clone https://github.com/facebook/rocksdb.git /path/to/rocksdb\necho \"ROCKSDB_PATH=/path/to/rocksdb\" \u003e\u003e .env\npnpm rebuild\n```\n\n### Debugging\n\nIt is often helpful to do a debug build and see the internal debug logging of the native binding.\nYou can do a debug build by running:\n\n```bash\npnpm rebuild:debug\n```\n\nEach debug log message is prefixed with the thread id. Most debug log messages include the instance\naddress making it easier to trace through the log output.\n\n#### Debugging on macOS\n\nIn the event Node.js crashes, re-run Node.js in `lldb`:\n\n```bash\nlldb node\n# Then in lldb:\n# (lldb) run your-program.js\n# When the crash occurs, print the stack trace:\n# (lldb) bt\n```\n\n### Testing\n\nTo run the tests, run:\n\n```bash\npnpm coverage\n```\n\nTo run the tests without code coverage, run:\n\n```bash\npnpm test\n```\n\nTo run a specific test suite, for example `\"ranges\"`, run:\n\n```bash\npnpm test ranges\n# or\npnpm test test/ranges\n```\n\nTo run a specific unit test, for example all tests that mention `\"column family\"`, run:\n\n```bash\npnpm test -t \"column family\"\n```\n\nVitest's terminal renderer will often overwrite the debug log output, so it's highly recommended to\nspecify the `CI=1` environment variable to prevent Vitest from erasing log output:\n\n```bash\nCI=1 pnpm test\n```\n\nBy default, the test runner deletes all test databases after the tests finish. To keep the temp\ndatabases for closer inspection, set the `KEEP_FILES=1` environment variable:\n\n```bash\nCI=1 KEEP_FILES=1 pnpm test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharperfast%2Frocksdb-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharperfast%2Frocksdb-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharperfast%2Frocksdb-js/lists"}