{"_id":"@ambicuity/sqlite","_rev":"5-b3c547c71a71db507acac88fb1efaaf3","name":"@ambicuity/sqlite","dist-tags":{"latest":"1.0.3"},"versions":{"1.0.0":{"name":"@ambicuity/sqlite","version":"1.0.0","keywords":["sqlite","sql","storely","storage","adapter","key","value","store","cache","ttl"],"author":{"name":"Ritesh Rana","email":"contact@riteshrana.engineer"},"license":"MIT","_id":"@ambicuity/sqlite@1.0.0","maintainers":[{"name":"ambicuity","email":"riteshrana36@gmail.com"}],"tsd":{"directory":"test"},"dist":{"shasum":"e123fa3cc303fd2ebc4b79a873f000273dbfc7b9","tarball":"https://registry.npmjs.org/@ambicuity/sqlite/-/sqlite-1.0.0.tgz","fileCount":16,"integrity":"sha512-hIRedRnJyo7kdtE+i0jIrfgAubTQTEG20nVfLV0rl1DBklxGNoZj4xyKVyq0Bc4dICTKmmGk+maDzAyceCb0jw==","signatures":[{"sig":"MEUCIDJmzcKl25eoriAslY0fz93zMcYTdO6UEtfMw8RWpgsqAiEA0ZPoYJDKO+Zxf6Aaef23wRn4NdFZ3o22gLbDMonV4O4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":126299},"main":"./dist/index.mjs","type":"module","_from":"file:ambicuity-sqlite-1.0.0.tgz","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":">= 18"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"biome check --write --error-on-warnings","test":"pnpm lint && vitest run --coverage","build":"tsdown","clean":"rimraf ./node_modules ./coverage ./test/testdb.sqlite ./dist","lint:ci":"biome check --error-on-warnings","test:ci":"pnpm lint:ci && vitest --run --sequence.setupFiles=list --coverage","benchmark":"tsx benchmark/combined.ts","benchmark:readme":"tsx benchmark/readme.ts"},"_npmUser":{"name":"ambicuity","email":"riteshrana36@gmail.com"},"_resolved":"/private/var/folders/tc/9srvxb_11h5bpdgnjy16lbqr0000gn/T/eaa07a7574e86bc50bbeaab6026d3b90/ambicuity-sqlite-1.0.0.tgz","_integrity":"sha512-hIRedRnJyo7kdtE+i0jIrfgAubTQTEG20nVfLV0rl1DBklxGNoZj4xyKVyq0Bc4dICTKmmGk+maDzAyceCb0jw==","_npmVersion":"11.12.1","description":"SQLite storage adapter for Storely","directories":{},"_nodeVersion":"25.9.0","dependencies":{"hookified":"^2.0.0","better-sqlite3":"^11.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","sqlite3":"^5.1.7","tinybench":"^6.0.0","@ambicuity/test-suite":"^1.0.0","@types/better-sqlite3":"^7.6.0","@monstermann/tinybench-pretty-printer":"^0.3.0"},"peerDependencies":{"@ambicuity/storely":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sqlite_1.0.0_1778541873736_0.3850967920807389","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @ambicuity/storely instead."},"1.0.1":{"name":"@ambicuity/sqlite","version":"1.0.1","keywords":["sqlite","sql","storely","storage","adapter","key","value","store","cache","ttl"],"author":{"name":"Ritesh Rana","email":"contact@riteshrana.engineer"},"license":"MIT","_id":"@ambicuity/sqlite@1.0.1","maintainers":[{"name":"ambicuity","email":"riteshrana36@gmail.com"}],"tsd":{"directory":"test"},"dist":{"shasum":"5b00b27bf83358b34a519702797ceb1286799f25","tarball":"https://registry.npmjs.org/@ambicuity/sqlite/-/sqlite-1.0.1.tgz","fileCount":16,"integrity":"sha512-oGBs434H8gnm45P6a2e/Ob2R50VD9pJcaDSK/24p25UM55DuH4wIf8e9BmNOZVaq1OvR7xlXtE/5fv4K9Herhw==","signatures":[{"sig":"MEUCIQD6RoK+g5sgZqkRHsAMeOJZJTY87qlzdQhEHV1ZFXozngIgRr6n/P5ZMqQVkpgKB7VCVbvfMOm1rRXu89LGawlOiD8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":126369},"main":"./dist/index.mjs","type":"module","_from":"file:ambicuity-sqlite-1.0.1.tgz","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":">= 18"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"biome check --write --error-on-warnings","test":"pnpm lint && vitest run --coverage","build":"tsdown","clean":"rimraf ./node_modules ./coverage ./test/testdb.sqlite ./dist","lint:ci":"biome check --error-on-warnings","test:ci":"pnpm lint:ci && vitest --run --sequence.setupFiles=list --coverage","benchmark":"tsx benchmark/combined.ts","benchmark:readme":"tsx benchmark/readme.ts"},"_npmUser":{"name":"ambicuity","email":"riteshrana36@gmail.com"},"_resolved":"/private/var/folders/tc/9srvxb_11h5bpdgnjy16lbqr0000gn/T/53e6a4469a572049927017cc313ba8d7/ambicuity-sqlite-1.0.1.tgz","_integrity":"sha512-oGBs434H8gnm45P6a2e/Ob2R50VD9pJcaDSK/24p25UM55DuH4wIf8e9BmNOZVaq1OvR7xlXtE/5fv4K9Herhw==","_npmVersion":"11.12.1","description":"SQLite storage adapter for Storely","directories":{},"_nodeVersion":"25.9.0","dependencies":{"hookified":"^2.0.0","better-sqlite3":"^11.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","sqlite3":"^5.1.7","tinybench":"^6.0.0","@ambicuity/test-suite":"^1.0.1","@types/better-sqlite3":"^7.6.0","@monstermann/tinybench-pretty-printer":"^0.3.0"},"peerDependencies":{"@ambicuity/storely-core":"^1.0.1"},"_npmOperationalInternal":{"tmp":"tmp/sqlite_1.0.1_1778543123387_0.7556913838553996","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @ambicuity/storely instead."},"1.0.2":{"name":"@ambicuity/sqlite","version":"1.0.2","keywords":["sqlite","sql","storely","storage","adapter","key","value","store","cache","ttl"],"author":{"name":"Ritesh Rana","email":"contact@riteshrana.engineer"},"license":"MIT","_id":"@ambicuity/sqlite@1.0.2","maintainers":[{"name":"ambicuity","email":"riteshrana36@gmail.com"}],"tsd":{"directory":"test"},"dist":{"shasum":"e5bd7818eeeeff4e62e9b2ee94694e7a054a2019","tarball":"https://registry.npmjs.org/@ambicuity/sqlite/-/sqlite-1.0.2.tgz","fileCount":16,"integrity":"sha512-5Kxe/cQP6dqaX3tXZp3t+kfydcgdZ5/zRA+sq53010iNQ23lB3xTcA1ND40tfONcsRbQWK3SR8zgAEW01npz/Q==","signatures":[{"sig":"MEQCIAad/JC3v9SSexSRkz2mIZwTrZjCKYGm6LSNUY/14DilAiBsv58rmpsog0rPu5BcenhU34ClFFTyeZWKsjAvDYTe5w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":126257},"main":"./dist/index.mjs","type":"module","_from":"file:ambicuity-sqlite-1.0.2.tgz","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":">= 18"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"biome check --write --error-on-warnings","test":"pnpm lint && vitest run --coverage","build":"tsdown","clean":"rimraf ./node_modules ./coverage ./test/testdb.sqlite ./dist","lint:ci":"biome check --error-on-warnings","test:ci":"pnpm lint:ci && vitest --run --sequence.setupFiles=list --coverage","benchmark":"tsx benchmark/combined.ts","benchmark:readme":"tsx benchmark/readme.ts"},"_npmUser":{"name":"ambicuity","email":"riteshrana36@gmail.com"},"_resolved":"/private/var/folders/tc/9srvxb_11h5bpdgnjy16lbqr0000gn/T/db296261a79cd32a037aac6e81a2515c/ambicuity-sqlite-1.0.2.tgz","_integrity":"sha512-5Kxe/cQP6dqaX3tXZp3t+kfydcgdZ5/zRA+sq53010iNQ23lB3xTcA1ND40tfONcsRbQWK3SR8zgAEW01npz/Q==","_npmVersion":"11.12.1","description":"SQLite storage adapter for Storely","directories":{},"_nodeVersion":"25.9.0","dependencies":{"hookified":"^2.0.0","better-sqlite3":"^11.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","sqlite3":"^5.1.7","tinybench":"^6.0.0","@ambicuity/test-suite":"^1.0.2","@types/better-sqlite3":"^7.6.0","@monstermann/tinybench-pretty-printer":"^0.3.0"},"peerDependencies":{"@ambicuity/core":"^1.0.2"},"_npmOperationalInternal":{"tmp":"tmp/sqlite_1.0.2_1778543468534_0.566164333486904","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @ambicuity/storely instead."},"1.0.3":{"name":"@ambicuity/sqlite","version":"1.0.3","keywords":["sqlite","sql","storely","storage","adapter","key","value","store","cache","ttl"],"author":{"name":"Ritesh Rana","email":"contact@riteshrana.engineer"},"license":"MIT","_id":"@ambicuity/sqlite@1.0.3","maintainers":[{"name":"ambicuity","email":"riteshrana36@gmail.com"}],"tsd":{"directory":"test"},"dist":{"shasum":"4f26abe7d2a67fcdf976876f3d4bc5ff752c39f3","tarball":"https://registry.npmjs.org/@ambicuity/sqlite/-/sqlite-1.0.3.tgz","fileCount":16,"integrity":"sha512-U0cn64zmTvMS7m9OLuOUS+oYBoqzm0blDqP9WgEeEM8N+hFju5Pcv5CNaE6LDgLoJhnCp0RW6+0BePZaV7nZSw==","signatures":[{"sig":"MEQCIDhlgY6TARjsEwMeNm5E0cjw/Y73xBs4fOp9bFaF0QV3AiACTs+mJwStm3r/eN580f3XsQLBy2a1OCccfrfNaLOXzw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":126313},"main":"./dist/index.mjs","type":"module","_from":"file:ambicuity-sqlite-1.0.3.tgz","types":"./dist/index.d.mts","module":"./dist/index.mjs","engines":{"node":">= 18"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"biome check --write --error-on-warnings","test":"pnpm lint && vitest run --coverage","build":"tsdown","clean":"rimraf ./node_modules ./coverage ./test/testdb.sqlite ./dist","lint:ci":"biome check --error-on-warnings","test:ci":"pnpm lint:ci && vitest --run --sequence.setupFiles=list --coverage","benchmark":"tsx benchmark/combined.ts","benchmark:readme":"tsx benchmark/readme.ts"},"_npmUser":{"name":"ambicuity","email":"riteshrana36@gmail.com"},"_resolved":"/private/var/folders/tc/9srvxb_11h5bpdgnjy16lbqr0000gn/T/1ac1a74212cc66b8082ecf8b35301e65/ambicuity-sqlite-1.0.3.tgz","_integrity":"sha512-U0cn64zmTvMS7m9OLuOUS+oYBoqzm0blDqP9WgEeEM8N+hFju5Pcv5CNaE6LDgLoJhnCp0RW6+0BePZaV7nZSw==","_npmVersion":"11.12.1","description":"SQLite storage adapter for Storely","directories":{},"_nodeVersion":"25.9.0","dependencies":{"hookified":"^2.0.0","better-sqlite3":"^11.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsd":"^0.33.0","sqlite3":"^5.1.7","tinybench":"^6.0.0","@ambicuity/test-suite":"^1.0.3","@types/better-sqlite3":"^7.6.0","@monstermann/tinybench-pretty-printer":"^0.3.0"},"peerDependencies":{"@ambicuity/ambicore":"^1.0.3"},"_npmOperationalInternal":{"tmp":"tmp/sqlite_1.0.3_1778543872825_0.6927055777959563","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Deprecated: use @ambicuity/storely instead."}},"time":{"created":"2026-05-11T23:24:33.668Z","modified":"2026-05-14T01:27:30.196Z","1.0.0":"2026-05-11T23:24:33.957Z","1.0.1":"2026-05-11T23:45:23.541Z","1.0.2":"2026-05-11T23:51:08.688Z","1.0.3":"2026-05-11T23:57:52.984Z"},"author":{"name":"Ritesh Rana","email":"contact@riteshrana.engineer"},"license":"MIT","keywords":["sqlite","sql","storely","storage","adapter","key","value","store","cache","ttl"],"description":"SQLite storage adapter for Storely","maintainers":[{"name":"ambicuity","email":"riteshrana36@gmail.com"}],"readme":"\n> SQLite storage adapter for Storely with multi-driver support for `nodejs`, `bun`, and custom drivers.\n\n[![npm](https://img.shields.io/npm/v/@ambicuity/sqlite.svg)](https://www.npmjs.com/package/@ambicuity/sqlite)\n[![npm](https://img.shields.io/npm/dm/@ambicuity/sqlite)](https://npmjs.com/package/@ambicuity/sqlite)\n\nSQLite storage adapter for Storely.\n\n## Table of Contents\n\n- [Install](#install)\n- [Usage](#usage)\n- [Using createStorely](#using-createstorely)\n- [Multi-Driver Support](#multi-driver-support)\n- [Creating a Custom Driver](#creating-a-custom-driver)\n- [Using sqlite3](#using-sqlite3)\n- [Migrating to v6](#migrating-to-v6)\n- [Constructor Options](#constructor-options)\n- [Properties](#properties)\n  - [namespace](#namespace)\n  - [uri](#uri)\n  - [table](#table)\n  - [keySize](#keysize)\n  - [namespaceLength](#namespacelength)\n  - [db](#db)\n  - [iterationLimit](#iterationlimit)\n  - [wal](#wal)\n  - [busyTimeout](#busytimeout)\n  - [driver](#driver)\n  - [driverName](#drivername)\n  - [clearExpiredInterval](#clearexpiredinterval)\n  - [ready](#ready)\n  - [opts](#opts)\n- [Methods](#methods)\n  - [.set(key, value, ttl?)](#setkey-value-ttl)\n  - [.setMany(entries)](#setmanyentries)\n  - [.get(key)](#getkey)\n  - [.getMany(keys)](#getmanykeys)\n  - [.has(key)](#haskey)\n  - [.hasMany(keys)](#hasmanykeys)\n  - [.delete(key)](#deletekey)\n  - [.deleteMany(keys)](#deletemanykeys)\n  - [.clear()](#clear)\n  - [.clearExpired()](#clearexpired)\n  - [.iterator()](#iterator)\n  - [.disconnect()](#disconnect)\n- [Clearing Expired Keys](#clearing-expired-keys)\n- [WAL Mode](#wal-mode)\n- [Benchmarks](#benchmarks)\n- [License](#license)\n\n# Install\n\n```shell\nnpm install --save storely @ambicuity/sqlite\n```\n\n# Usage\n\n```js\nimport Storely from '@ambicuity/ambicore';\nimport StorelySqlite from '@ambicuity/sqlite';\n\nconst storely = new Storely({ store: new StorelySqlite('sqlite://path/to/database.sqlite') });\nstorely.on('error', err => console.error(err));\n```\n\nYou can specify the `table`, `busyTimeout`, and `wal` options:\n\n```js\nconst storelySqlite = new StorelySqlite({\n  uri: 'sqlite://path/to/database.sqlite',\n  table: 'cache',\n  busyTimeout: 10000,\n  wal: true,\n});\nconst storely = new Storely({ store: storelySqlite });\n```\n\n# Using createStorely\n\nThe `createStorely` helper creates a `Storely` instance with `StorelySqlite` as the store in one call:\n\n```js\nimport { createStorely } from '@ambicuity/sqlite';\n\n// With a URI string\nconst storely = createStorely('sqlite://path/to/database.sqlite');\n\n// With an options object\nconst storely = createStorely({\n  uri: 'sqlite://path/to/database.sqlite',\n  table: 'cache',\n  wal: true,\n});\n```\n\n# Multi-Driver Support\n\n`@ambicuity/sqlite` supports multiple SQLite drivers and automatically selects the best one available for your runtime:\n\n| Driver | Package | Runtime | Type |\n| --- | --- | --- | --- |\n| `better-sqlite3` | `better-sqlite3` | Node.js | Synchronous (fallback) |\n| `node:sqlite` | Built-in | Node.js 22.5+ | Synchronous |\n| `bun:sqlite` | Built-in | Bun | Synchronous |\n\n`better-sqlite3` is included as a direct dependency and used as a fallback when native runtime drivers are unavailable. On Bun, the native `bun:sqlite` driver is preferred. On Node.js 22.5+, the built-in `node:sqlite` driver is preferred. If you still need to use `sqlite3` then go to the [using sqlite3](#using-sqlite3).\n\n## Selecting a specific driver\n\nYou can explicitly choose a driver via the `driver` option:\n\n```js\nconst store = new StorelySqlite({\n  uri: 'sqlite://path/to/database.sqlite',\n  driver: 'better-sqlite3', // or 'node:sqlite' or 'bun:sqlite'\n});\n```\n\n## Auto-detection order\n\nWhen no `driver` is specified, the adapter tries drivers in this order:\n\n- **Bun**: `bun:sqlite` then `better-sqlite3`\n- **Node.js**: `node:sqlite` then `better-sqlite3`\n\n# Creating a Custom Driver\n\nYou can pass a custom driver object that implements the `SqliteDriver` interface. A custom driver must provide a `name` and a `connect()` method that returns `{ query, close }`:\n\n```ts\nimport StorelySqlite from '@ambicuity/sqlite';\nimport type { SqliteDriver } from '@ambicuity/sqlite';\n\nconst customDriver: SqliteDriver = {\n  name: 'custom',\n  async connect(options) {\n    // options: { filename: string, busyTimeout?: number, wal?: boolean }\n    return {\n      async query(sql, ...params) {\n        // Execute SQL and return rows for SELECT/PRAGMA, empty array for mutations\n      },\n      async close() {\n        // Close the database connection\n      },\n    };\n  },\n};\n\nconst store = new StorelySqlite({\n  uri: 'sqlite://path/to/database.sqlite',\n  driver: customDriver,\n});\n```\n\nThe `query` function must return an array of row objects for `SELECT` and `PRAGMA` statements, and an empty array for all other statements (`INSERT`, `UPDATE`, `DELETE`, etc.).\n\n## Type exports\n\nThe following types are available for building custom drivers:\n\n```ts\nimport type {\n  SqliteDriver,        // Driver interface: { name, connect() }\n  SqliteDriverName,    // 'better-sqlite3' | 'node:sqlite' | 'bun:sqlite' | 'custom'\n  StorelySqliteOptions,   // Constructor options\n  Sqlite3ModuleLike,   // Structural type for the sqlite3 module\n  Sqlite3DatabaseLike, // Structural type for a sqlite3.Database instance\n} from '@ambicuity/sqlite';\n```\n\nThe `createSqlite3Driver` export is a real-world example of a custom driver — see [Using sqlite3](#using-sqlite3).\n\n# Using sqlite3\n\nThe callback-based [`sqlite3`](https://www.npmjs.com/package/sqlite3) package is not auto-detected or bundled with `@ambicuity/sqlite`. If you need to use it, install it in your project and pass it via the `createSqlite3Driver` helper:\n\n```bash\nnpm install sqlite3\n```\n\n```ts\nimport StorelySqlite, { createSqlite3Driver } from '@ambicuity/sqlite';\nimport sqlite3 from 'sqlite3';\n\nconst store = new StorelySqlite({\n  uri: 'sqlite://path/to/database.sqlite',\n  driver: createSqlite3Driver(sqlite3),\n});\n```\n\n`sqlite3.verbose()` also works:\n\n```ts\nconst store = new StorelySqlite({\n  uri: 'sqlite://path/to/database.sqlite',\n  driver: createSqlite3Driver(sqlite3.verbose()),\n});\n```\n\nAll standard options (`wal`, `busyTimeout`, etc.) are supported.\n\n# Migrating to v6\n\n## Breaking changes\n\n### Properties instead of opts\n\nThe `opts` getter still exists for backward compatibility and returns all current settings as a plain object. New top-level getters and setters have been added for `namespace` and `clearExpiredInterval`:\n\n```js\nstore.namespace = 'my-namespace';\nstore.clearExpiredInterval = 60_000;\n```\n\n### Native namespace support\n\nIn v5, namespaces were stored as key prefixes in the `key` column (e.g. `key=\"myns:mykey\"` with no namespace column). In v6, the namespace is stored in a dedicated `namespace` column (e.g. `key=\"mykey\"`, `namespace=\"myns\"`). This enables more efficient queries and proper namespace isolation.\n\nThe adapter automatically detects old schemas and migrates existing data on connect — no manual migration steps are needed. During migration, prefixed keys like `myns:mykey` are split into `key=\"mykey\"` and `namespace=\"myns\"`.\n\n### Hookified integration\n\nThe adapter now extends [Hookified](https://hookified.org) instead of a custom EventEmitter. Events work the same (`on`, `emit`), but hooks are also available via the standard Hookified API.\n\n## New features\n\n### Native TTL support with `expires` column\n\nv6 adds an `expires BIGINT` column to the table. When values are stored with a TTL via Storely core, the adapter automatically extracts the `expires` timestamp from the serialized value and stores it in the column. A partial index is created on the `expires` column for efficient cleanup queries.\n\nThe schema migration is automatic on connect — existing tables get the column added via `ALTER TABLE ... ADD COLUMN`.\n\n### `clearExpired()` method\n\nA new utility method that deletes all rows where the `expires` column is set and the timestamp is in the past:\n\n```js\nawait store.clearExpired();\n```\n\n### `clearExpiredInterval` option\n\nSet an interval (in milliseconds) to automatically call `clearExpired()` on a schedule. Disabled by default (`0`). The timer uses `unref()` so it won't keep the Node.js process alive.\n\n```js\nconst store = new StorelySqlite({\n  uri: 'sqlite://path/to/database.sqlite',\n  clearExpiredInterval: 60_000, // clean up every 60 seconds\n});\n```\n\n### Bulk operations\n\nNew methods for efficient multi-key operations:\n\n- `.setMany(entries)` — bulk upsert with automatic batching (249 entries per batch to stay within SQLite's 999 parameter limit)\n- `.getMany(keys)` — bulk retrieve with automatic batching\n- `.deleteMany(keys)` — bulk delete with automatic batching\n- `.hasMany(keys)` — bulk existence check\n\n### `createStorely()` helper\n\nA convenience function to create a `Storely` instance with `StorelySqlite` as the store in one call:\n\n```js\nimport { createStorely } from '@ambicuity/sqlite';\n\nconst storely = createStorely('sqlite://path/to/database.sqlite');\n```\n\n### Multi-driver support\n\nv6 replaces the callback-based `sqlite3` package with `better-sqlite3` as the default driver and adds support for `node:sqlite` (Node.js 22.5+) and `bun:sqlite` (Bun). The driver is auto-detected or can be explicitly selected via the `driver` option. See [Multi-Driver Support](#multi-driver-support) for details.\n\n### Improved iterator\n\nThe iterator now uses cursor-based (keyset) pagination instead of `OFFSET`. This handles concurrent modifications during iteration without skipping entries and is more efficient for large datasets.\n\n# Constructor Options\n\n`StorelySqlite` accepts a connection URI string or an options object:\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `uri` | `string` | `'sqlite://:memory:'` | SQLite connection URI |\n| `table` | `string` | `'storely'` | Table name for key-value storage |\n| `keySize` | `number` | `255` | Maximum key column length (VARCHAR length, max 65535). Alias: `keyLength` |\n| `namespaceLength` | `number` | `255` | Maximum namespace column length (VARCHAR length) |\n| `busyTimeout` | `number` | `undefined` | SQLite [busy timeout](https://sqlite.org/c3ref/busy_timeout.html) in milliseconds |\n| `iterationLimit` | `number` | `10` | Number of rows fetched per batch during iteration |\n| `wal` | `boolean` | `false` | Enable [WAL mode](https://sqlite.org/wal.html) for better concurrency |\n| `clearExpiredInterval` | `number` | `0` | Interval in milliseconds to automatically clear expired entries (0 = disabled) |\n| `driver` | `string \\| SqliteDriver` | `undefined` | Explicit driver selection (`'better-sqlite3'`, `'node:sqlite'`, `'bun:sqlite'`) or custom driver object. Auto-detected if omitted |\n\n# Properties\n\n## namespace\n\nGet or set the namespace for the adapter. Used for key prefixing and scoping operations like `clear()` and `iterator()`.\n\n- Type: `string | undefined`\n- Default: `undefined`\n\n```js\nconst store = new StorelySqlite('sqlite://path/to/database.sqlite');\nstore.namespace = 'my-namespace';\nconsole.log(store.namespace); // 'my-namespace'\n```\n\n## uri\n\nGet the SQLite connection URI.\n\n- Type: `string`\n- Default: `'sqlite://:memory:'`\n\n```js\nconst store = new StorelySqlite('sqlite://path/to/database.sqlite');\nconsole.log(store.uri); // 'sqlite://path/to/database.sqlite'\n```\n\n## table\n\nGet or set the table name used for storage. The name is sanitized and escaped for safe use in SQL queries to prevent SQL injection.\n\n- Type: `string`\n- Default: `'storely'`\n\n```js\nconst store = new StorelySqlite({ uri: 'sqlite://:memory:', table: 'cache' });\nconsole.log(store.table); // 'cache'\nstore.table = 'sessions';\n```\n\n## keySize\n\nGet or set the maximum key length (VARCHAR length) for the key column.\n\n- Type: `number`\n- Default: `255`\n\n```js\nconst store = new StorelySqlite({ uri: 'sqlite://:memory:', keySize: 512 });\nconsole.log(store.keySize); // 512\n```\n\n## namespaceLength\n\nGet or set the maximum namespace column length (VARCHAR length).\n\n- Type: `number`\n- Default: `255`\n\n```js\nconst store = new StorelySqlite({ uri: 'sqlite://:memory:', namespaceLength: 128 });\nconsole.log(store.namespaceLength); // 128\n```\n\n## db\n\nGet the resolved file path for the SQLite database, derived from the URI.\n\n- Type: `string`\n- Default: `':memory:'`\n\n```js\nconst store = new StorelySqlite('sqlite://data/app.sqlite');\nconsole.log(store.db); // 'data/app.sqlite'\n```\n\n## iterationLimit\n\nGet or set the number of rows to fetch per iteration batch.\n\n- Type: `number`\n- Default: `10`\n\n```js\nconst store = new StorelySqlite({ uri: 'sqlite://:memory:', iterationLimit: 50 });\nconsole.log(store.iterationLimit); // 50\n```\n\n## wal\n\nGet whether WAL (Write-Ahead Logging) mode is enabled.\n\n- Type: `boolean`\n- Default: `false`\n\n```js\nconst store = new StorelySqlite({ uri: 'sqlite://path/to/database.sqlite', wal: true });\nconsole.log(store.wal); // true\n```\n\n## busyTimeout\n\nGet the SQLite busy timeout in milliseconds.\n\n- Type: `number | undefined`\n- Default: `undefined`\n\n```js\nconst store = new StorelySqlite({ uri: 'sqlite://:memory:', busyTimeout: 5000 });\nconsole.log(store.busyTimeout); // 5000\n```\n\n## driver\n\nGet the explicit driver selection. Returns `undefined` when auto-detected.\n\n- Type: `string | SqliteDriver | undefined`\n- Default: `undefined` (auto-detected)\n\n```js\nconst store = new StorelySqlite({ uri: 'sqlite://:memory:', driver: 'better-sqlite3' });\nconsole.log(store.driver); // 'better-sqlite3'\n```\n\n## driverName\n\nGet the name of the resolved driver after connection. This is useful to check which driver was auto-detected.\n\n- Type: `string | undefined`\n- Default: `undefined` (set after connection is established)\n\n```js\nconst store = new StorelySqlite('sqlite://:memory:');\nawait store.ready;\nconsole.log(store.driverName); // 'better-sqlite3', 'node:sqlite', 'bun:sqlite', or 'custom'\n```\n\n## clearExpiredInterval\n\nGet or set the interval in milliseconds between automatic expired-entry cleanup runs. When set to a value greater than 0, the adapter will automatically call `clearExpired()` at the specified interval. The timer uses `unref()` so it won't keep the Node.js process alive. Setting to 0 disables the automatic cleanup.\n\n- Type: `number`\n- Default: `0` (disabled)\n\n```js\n// Clean up expired entries every 60 seconds\nconst store = new StorelySqlite({\n  uri: 'sqlite://path/to/database.sqlite',\n  clearExpiredInterval: 60_000,\n});\nconsole.log(store.clearExpiredInterval); // 60000\n\n// Disable it later\nstore.clearExpiredInterval = 0;\n```\n\n## ready\n\nA promise that resolves when the database connection and schema setup are complete. You can optionally await this before the first operation to ensure the adapter is fully initialized.\n\n- Type: `Promise<void>`\n\n```js\nconst store = new StorelySqlite('sqlite://path/to/database.sqlite');\nawait store.ready; // connection and schema migration complete\n```\n\n## opts\n\nGet all current settings as a plain object. This getter exists for backward compatibility.\n\n- Type: `StorelySqliteOptions`\n\n```js\nconst store = new StorelySqlite({\n  uri: 'sqlite://:memory:',\n  table: 'cache',\n  wal: true,\n});\nconsole.log(store.uri); // 'sqlite://:memory:'\nconsole.log(store.table); // 'cache'\n```\n\n# Methods\n\n## .set(key, value, ttl?)\n\nSet a key-value pair. Returns `true` on success, `false` on failure.\n\n- `key` *(string)* - The key to set.\n- `value` *(any)* - The value to store.\n- `ttl` *(number, optional)* - Time to live in milliseconds.\n- Returns: `Promise<boolean>`\n\n```js\nawait storely.set('foo', 'bar');\nawait storely.set('foo', 'bar', 5000); // expires in 5 seconds\n```\n\n## .setMany(entries)\n\nSet multiple key-value pairs at once. Each entry is a `StorelyEntry<Value>` object (`{ key: string, value: Value, ttl?: number }`), where `Value` is inferred from the entries provided. Entries are automatically batched (249 per batch) to stay within SQLite's bind parameter limit. Returns a `boolean[]` with per-entry success tracking. Each batch is atomic — if a batch fails, entries in that batch return `false` while entries in successful batches return `true`. On batch failure, an `error` event is emitted.\n\n```js\nconst results = await storely.setMany([\n  { key: 'foo', value: 'bar' },\n  { key: 'baz', value: 'qux' },\n]); // [true, true]\n```\n\n## .get(key)\n\nGet a value by key. Returns `undefined` if the key does not exist.\n\n```js\nconst value = await storely.get('foo'); // 'bar'\n```\n\n## .getMany(keys)\n\nGet multiple values at once. Returns an array of values in the same order as the keys, with `undefined` for missing keys.\n\n```js\nconst values = await storely.getMany(['foo', 'baz']); // ['bar', 'qux']\n```\n\n## .has(key)\n\nCheck if a key exists. Returns a boolean.\n\n```js\nconst exists = await storely.has('foo'); // true\n```\n\n## .hasMany(keys)\n\nCheck if multiple keys exist. Returns an array of booleans in the same order as the input keys.\n\n```js\nconst results = await storely.hasMany(['foo', 'baz', 'unknown']); // [true, true, false]\n```\n\n## .delete(key)\n\nDelete a key. Returns `true` if the key existed, `false` otherwise.\n\n```js\nconst deleted = await storely.delete('foo'); // true\n```\n\n## .deleteMany(keys)\n\nDelete multiple keys at once. Returns a `boolean[]` indicating whether each key existed.\n\n```js\nconst results = await storely.deleteMany(['foo', 'baz']); // [true, true]\n```\n\n## .clear()\n\nClear all keys in the current namespace.\n\n```js\nawait storely.clear();\n```\n\n## .clearExpired()\n\nUtility helper method to delete all expired entries from the store. This removes any rows where the `expires` column is set and the timestamp is in the past. This is useful for periodic cleanup of expired data.\n\n```js\nawait store.clearExpired();\n```\n\n## .iterator()\n\nIterate over all key-value pairs. The iterator uses the namespace configured on the instance. Uses cursor-based pagination controlled by the `iterationLimit` option.\n\n```js\nconst iterator = storely.iterator();\nfor await (const [key, value] of iterator) {\n  console.log(key, value);\n}\n```\n\n## .disconnect()\n\nDisconnect from the SQLite database and release resources. Stops the automatic expired-entry cleanup interval if running.\n\n```js\nawait store.disconnect();\n```\n\n# Clearing Expired Keys\n\nWhen a key is stored with a TTL, the adapter records the expiration timestamp in the `expires` column. Storely core enforces TTL automatically — expired keys return `undefined` from `get()` and `false` from `has()`, and are lazily deleted from the store when accessed via `get()`, `getMany()`, or iteration.\n\nHowever, expired rows that are never accessed again will remain in the database. The `clearExpired()` method and `clearExpiredInterval` option provide bulk cleanup to remove these stale rows efficiently via SQL, without needing to deserialize every row.\n\n## Automatic cleanup\n\nSet the `clearExpiredInterval` option (in milliseconds) to automatically remove expired entries on a recurring timer. The timer uses `unref()` so it won't keep the Node.js process alive.\n\n```js\nconst store = new StorelySqlite({\n  uri: 'sqlite://path/to/database.sqlite',\n  clearExpiredInterval: 60_000, // clean up every 60 seconds\n});\n```\n\nYou can change or disable the interval at runtime:\n\n```js\n// Change to every 5 minutes\nstore.clearExpiredInterval = 300_000;\n\n// Disable automatic cleanup\nstore.clearExpiredInterval = 0;\n```\n\n## Manual cleanup\n\nCall `clearExpired()` directly to remove all expired entries on demand:\n\n```js\nawait store.clearExpired();\n```\n\n# WAL Mode\n\nBy default, SQLite uses the rollback journal for transactions. Enabling [WAL (Write-Ahead Logging)](https://sqlite.org/wal.html) mode can significantly improve concurrency and write performance for most workloads.\n\n```js\nconst store = new StorelySqlite({\n  uri: 'sqlite://path/to/database.sqlite',\n  wal: true,\n});\nconst storely = new Storely({ store });\n```\n\n**Note:** WAL mode is not supported for in-memory databases (`:memory:`). If enabled for an in-memory database, a warning will be logged and the option will be ignored.\n\nFrom the [SQLite documentation](https://sqlite.org/wal.html):\n\n> WAL provides more concurrency as readers do not block writers and a writer does not block readers. Reading and writing can proceed concurrently. WAL is significantly faster than the default rollback journal in most scenarios involving a single database connection, and is also faster in many scenarios involving multiple database connections.\n\n# Benchmarks\n\nSimple `set` / `get` benchmarks comparing the built-in SQLite drivers plus an optional `sqlite3` custom-driver setup using in-memory databases with 10,000 pre-generated key-value pairs. Results will vary across machines and runs — they are meant as a relative comparison, not absolute performance numbers.\n\n<!-- BENCHMARK-RESULTS-START -->\n| name                |  summary  |   ops/sec |   time/op |  margin  |   samples |\n|---------------------|:---------:|----------:|----------:|:--------:|----------:|\n| bun set / get       |    🥇     |       64K |      18µs |  ±0.79%  |       57K |\n| better set / get    |  -32.0%   |       44K |      25µs |  ±2.34%  |       40K |\n| node set / get      |  -32.7%   |       43K |      25µs |  ±2.46%  |       40K |\n| sqlite3 set / get   |  -74.7%   |       16K |      67µs |  ±1.25%  |       15K |\n<!-- BENCHMARK-RESULTS-END -->\n\nNote: we included `sqlite3` tests in this but by default we do not have it as a dependency as our fallback is `better-sqlite3` now. Please refor to [using sqlite3](#using-sqlite3) if you want to use it.\n\n# License\n\n[MIT © Ritesh Rana](LICENCE)\n","readmeFilename":"README.md"}