From 84d22ddc05b0d0e660d2a889b00831b868d23799 Mon Sep 17 00:00:00 2001 From: Max Levchuk Date: Tue, 19 May 2026 19:18:37 +0200 Subject: [PATCH 1/2] feat(drivers): add Bun-native Redis, SQLite, and Postgres adapters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds first-class support for Bun runtime users by introducing three new driver entry points that talk to Bun's built-in clients directly, bypassing the Node-targeted bindings used by the existing drivers. Existing drivers stay untouched — Bun is an opt-in alternative, not a replacement. New drivers (all L2, Bun runtime only): - `bentocache/drivers/bun_redis` → `bunRedisDriver`, uses `import { RedisClient } from 'bun'`. No ioredis dependency. Implements get/set (PX TTL)/pull (GETDEL)/delete/deleteMany (UNLINK)/ clear (SCAN + UNLINK loop)/namespace/disconnect. - `bentocache/drivers/bun_sqlite` → `bunSqliteDriver`, plugs a new `BunSqliteAdapter` into the existing `DatabaseDriver`. Uses `bun:sqlite` Database + prepared statements (lazily built so they reference the table only after `setTableName`). `deleteMany` runs inside a single transaction. - `bentocache/drivers/bun_postgres` → `bunPostgresDriver`, plugs `BunPostgresAdapter` into `DatabaseDriver`. Built on `import { SQL } from 'bun'` tagged-template queries with identifier interpolation via `sql(tableName)`. Same column shape as the existing Kysely PG path (key PK, value TEXT, expires_at BIGINT). Postgres has no native TTL so users should set `pruneInterval` — same caveat as the existing pg adapters. Runtime compatibility: - No `engines` restriction; Node users see no behaviour change. - All Bun imports in the type layer use `import type` only, so `drivers_options.ts` still loads cleanly under Node. - `require('bun')` / `require('bun:sqlite')` live only inside the new driver files. A Node user accidentally importing them gets a clear ERR_MODULE_NOT_FOUND. Tests: - Three new spec files under `tests/drivers/bun_*.spec.ts` that gate registration on `typeof Bun !== 'undefined'` and dynamically import the driver inside the guard, so Japa loads them on Node without resolving Bun-only modules. Reuse `registerCacheDriverTestSuite` for the standard get/set/pull/delete/deleteMany/clear/namespace/TTL coverage. - New `quick:test:bun` script (`bun bin/test.ts`) for running the Bun specs locally. - Verified end-to-end: Node typecheck/lint/build clean; Node tests run unchanged (existing memory/redis suites pass, Bun specs report NO TESTS EXECUTED); under Bun, all three specs pass (22 + 21 + 21). Docs: - `quick_setup.md` and `telemetry.md` codegroups gain a `bun` tab next to npm/pnpm/yarn. - `packages/otel/README.md` install block now lists all four package managers. - `cache_drivers.md` gets three new sections (Bun Redis, Bun SQLite, Bun Postgres). Each is explicitly tagged "L2 driver, Bun runtime only" so the topology constraint is discoverable in-page. Package wiring: - Three new `./drivers/bun_*` entries in `package.json` exports map. - `@types/bun` added to devDependencies (no peerDep — Bun is a runtime, not an npm package consumers install). Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/content/docs/cache_drivers.md | 105 ++++++++++++++++++ docs/content/docs/quick_setup.md | 5 + docs/content/docs/telemetry.md | 5 + packages/bentocache/package.json | 5 + packages/bentocache/src/drivers/bun_redis.ts | 103 +++++++++++++++++ .../drivers/database/adapters/bun_postgres.ts | 93 ++++++++++++++++ .../drivers/database/adapters/bun_sqlite.ts | 103 +++++++++++++++++ .../src/types/options/drivers_options.ts | 26 +++++ .../tests/drivers/bun_postgres.spec.ts | 29 +++++ .../tests/drivers/bun_redis.spec.ts | 34 ++++++ .../tests/drivers/bun_sqlite.spec.ts | 26 +++++ packages/otel/README.md | 3 + pnpm-lock.yaml | 17 +++ 13 files changed, 554 insertions(+) create mode 100644 packages/bentocache/src/drivers/bun_redis.ts create mode 100644 packages/bentocache/src/drivers/database/adapters/bun_postgres.ts create mode 100644 packages/bentocache/src/drivers/database/adapters/bun_sqlite.ts create mode 100644 packages/bentocache/tests/drivers/bun_postgres.spec.ts create mode 100644 packages/bentocache/tests/drivers/bun_redis.spec.ts create mode 100644 packages/bentocache/tests/drivers/bun_sqlite.spec.ts diff --git a/docs/content/docs/cache_drivers.md b/docs/content/docs/cache_drivers.md index d51b45e2..d77fb8e0 100644 --- a/docs/content/docs/cache_drivers.md +++ b/docs/content/docs/cache_drivers.md @@ -56,6 +56,44 @@ const bento = new BentoCache({ |--------------|-------------------------------------------------------------------------------|---------| | `connection` | The connection options to use to connect to Redis or an instance of `ioredis` | N/A | +### Bun Redis + +**L2 driver, Bun runtime only.** Uses Bun's native `RedisClient` — no `ioredis` dependency. If you're running under Bun, this avoids the JavaScript-protocol implementation of `ioredis` in favor of Bun's Zig-backed client. + +```ts +import { BentoCache, bentostore } from 'bentocache' +import { bunRedisDriver } from 'bentocache/drivers/bun_redis' + +const bento = new BentoCache({ + default: 'redis', + stores: { + redis: bentostore().useL2Layer(bunRedisDriver({ + connection: 'redis://127.0.0.1:6379' + })) + } +}) +``` + +You can also pass an existing `RedisClient` to reuse a connection: + +```ts +import { RedisClient } from 'bun' + +const client = new RedisClient('redis://127.0.0.1:6379') + +const bento = new BentoCache({ + default: 'redis', + stores: { + redis: bentostore().useL2Layer(bunRedisDriver({ connection: client })) + } +}) +``` + +| Option | Description | Default | +|--------------|----------------------------------------------------------------------------------------------|---------| +| `connection` | A connection URL string, a `Bun.RedisOptions` object, or an existing `Bun.RedisClient`. | N/A | +| `options` | Extra `Bun.RedisOptions` forwarded to the `RedisClient` constructor. | N/A | + ## Filesystem The filesystem driver will store your cache in a distributed way in several files/folders on your filesystem. @@ -259,3 +297,70 @@ export const bento = new BentoCache({ } }) ``` + +### Bun SQLite + +**L2 driver, Bun runtime only.** Talks to `bun:sqlite` directly using prepared statements — no `better-sqlite3` native binding required. + +```ts +import { BentoCache, bentostore } from 'bentocache' +import { bunSqliteDriver } from 'bentocache/drivers/bun_sqlite' + +const bento = new BentoCache({ + default: 'sqlite', + stores: { + sqlite: bentostore().useL2Layer(bunSqliteDriver({ + connection: './cache.sqlite3' + })) + } +}) +``` + +You can also pass an existing `Database` instance: + +```ts +import { Database } from 'bun:sqlite' + +const db = new Database('./cache.sqlite3') + +const bento = new BentoCache({ + default: 'sqlite', + stores: { + sqlite: bentostore().useL2Layer(bunSqliteDriver({ connection: db })) + } +}) +``` + +Inherits the common SQL driver options (`tableName`, `autoCreateTable`, `pruneInterval`). + +| Option | Description | Default | +|--------------|--------------------------------------------------------------------------------------|---------| +| `connection` | A filename (`':memory:'` for in-memory) or an existing `bun:sqlite` `Database`. | N/A | +| `options` | Open flags forwarded to `new Database(...)` (`readonly`, `create`, `readwrite`, …). | N/A | + +### Bun Postgres + +**L2 driver, Bun runtime only.** Uses Bun's native `SQL` Postgres client — no `pg` dependency. + +```ts +import { BentoCache, bentostore } from 'bentocache' +import { bunPostgresDriver } from 'bentocache/drivers/bun_postgres' + +const bento = new BentoCache({ + default: 'pg', + stores: { + pg: bentostore().useL2Layer(bunPostgresDriver({ + connection: 'postgres://user:pass@localhost:5432/app', + pruneInterval: '1h', + })) + } +}) +``` + +You can also pass an existing `SQL` instance (e.g. `Bun.sql`) to reuse a connection. + +Postgres has no native TTL, so set `pruneInterval` if you want expired entries cleaned automatically — same caveat as the existing Knex/Kysely Postgres path. + +| Option | Description | Default | +|--------------|--------------------------------------------------------------------------|---------| +| `connection` | A Postgres connection URL string, or an existing `Bun.SQL` instance. | N/A | diff --git a/docs/content/docs/quick_setup.md b/docs/content/docs/quick_setup.md index da08824a..604c8666 100644 --- a/docs/content/docs/quick_setup.md +++ b/docs/content/docs/quick_setup.md @@ -21,6 +21,11 @@ pnpm add bentocache // title: yarn yarn add bentocache ``` + +```sh +// title: bun +bun add bentocache +``` ::: diff --git a/docs/content/docs/telemetry.md b/docs/content/docs/telemetry.md index f16d0368..72f6abfe 100644 --- a/docs/content/docs/telemetry.md +++ b/docs/content/docs/telemetry.md @@ -86,6 +86,11 @@ pnpm add @bentocache/otel // title: yarn yarn add @bentocache/otel ``` + +```sh +// title: bun +bun add @bentocache/otel +``` ::: ## Basic setup diff --git a/packages/bentocache/package.json b/packages/bentocache/package.json index 7d248af5..f3542faa 100644 --- a/packages/bentocache/package.json +++ b/packages/bentocache/package.json @@ -24,6 +24,9 @@ "./drivers/knex": "./build/src/drivers/database/adapters/knex.js", "./drivers/kysely": "./build/src/drivers/database/adapters/kysely.js", "./drivers/orchid": "./build/src/drivers/database/adapters/orchid.js", + "./drivers/bun_redis": "./build/src/drivers/bun_redis.js", + "./drivers/bun_sqlite": "./build/src/drivers/database/adapters/bun_sqlite.js", + "./drivers/bun_postgres": "./build/src/drivers/database/adapters/bun_postgres.js", "./types": "./build/src/types/main.js", "./plugins/*": "./build/plugins/*.js", "./test_suite": "./build/src/test_suite.js" @@ -57,6 +60,7 @@ "typecheck": "tsc --noEmit", "lint": "eslint .", "quick:test": "cross-env NODE_NO_WARNINGS=1 node --enable-source-maps --loader=ts-node/esm bin/test.ts", + "quick:test:bun": "bun bin/test.ts", "pretest": "pnpm lint", "test": "c8 pnpm quick:test", "build": "pnpm clean && tsup-node", @@ -101,6 +105,7 @@ "devDependencies": { "@aws-sdk/client-dynamodb": "^3.1019.0", "@types/better-sqlite3": "^7.6.13", + "@types/bun": "^1.2.0", "@types/pg": "^8.20.0", "better-sqlite3": "^12.8.0", "dayjs": "^1.11.20", diff --git a/packages/bentocache/src/drivers/bun_redis.ts b/packages/bentocache/src/drivers/bun_redis.ts new file mode 100644 index 00000000..a8f929be --- /dev/null +++ b/packages/bentocache/src/drivers/bun_redis.ts @@ -0,0 +1,103 @@ +import { RedisClient } from 'bun' + +import { BaseDriver } from './base_driver.js' +import type { BunRedisConfig, CreateDriverResult, L2CacheDriver } from '../types/main.js' + +export function bunRedisDriver(options: BunRedisConfig): CreateDriverResult { + return { options, factory: (config: BunRedisConfig) => new BunRedisDriver(config) } +} + +export class BunRedisDriver extends BaseDriver implements L2CacheDriver { + type = 'l2' as const + #connection: RedisClient + declare config: BunRedisConfig + + constructor(config: BunRedisConfig) { + super(config) + + if (config.connection instanceof RedisClient) { + this.#connection = config.connection + return + } + + if (typeof config.connection === 'string') { + this.#connection = new RedisClient(config.connection, config.options) + return + } + + const { url, ...rest } = config.connection + this.#connection = new RedisClient(url, { ...rest, ...config.options }) + } + + getConnection() { + return this.#connection + } + + namespace(namespace: string) { + return new BunRedisDriver({ + ...this.config, + connection: this.#connection, + prefix: this.createNamespacePrefix(namespace), + }) + } + + async get(key: string) { + const result = await this.#connection.get(this.getItemKey(key)) + return result ?? undefined + } + + async pull(key: string) { + const result = (await this.#connection.send('GETDEL', [this.getItemKey(key)])) as string | null + return result ?? undefined + } + + async set(key: string, value: string, ttl?: number) { + const itemKey = this.getItemKey(key) + + if (!ttl) { + const result = (await this.#connection.send('SET', [itemKey, value])) as string | null + return result === 'OK' + } + + const result = (await this.#connection.send('SET', [itemKey, value, 'PX', String(ttl)])) as + | string + | null + return result === 'OK' + } + + async clear() { + let cursor = '0' + const pattern = this.prefix ? `${this.prefix}:*` : '*' + + do { + const [nextCursor, keys] = (await this.#connection.send('SCAN', [ + cursor, + 'MATCH', + pattern, + 'COUNT', + '1000', + ])) as [string, string[]] + + if (keys.length) await this.#connection.send('UNLINK', keys) + cursor = nextCursor + } while (cursor !== '0') + } + + async delete(key: string) { + const deleted = (await this.#connection.send('UNLINK', [this.getItemKey(key)])) as number + return deleted > 0 + } + + async deleteMany(keys: string[]) { + if (keys.length === 0) return true + await this.#connection.send( + 'UNLINK', + keys.map((key) => this.getItemKey(key)), + ) + return true + } + + async disconnect() { + this.#connection.close() + } +} diff --git a/packages/bentocache/src/drivers/database/adapters/bun_postgres.ts b/packages/bentocache/src/drivers/database/adapters/bun_postgres.ts new file mode 100644 index 00000000..749ae91d --- /dev/null +++ b/packages/bentocache/src/drivers/database/adapters/bun_postgres.ts @@ -0,0 +1,93 @@ +import { SQL } from 'bun' + +import { DatabaseDriver } from '../database.js' +import type { BunPostgresConfig, CreateDriverResult, DatabaseAdapter } from '../../../types/main.js' + +export function bunPostgresDriver(options: BunPostgresConfig): CreateDriverResult { + return { + options, + factory: (config: BunPostgresConfig) => { + const adapter = new BunPostgresAdapter(config) + return new DatabaseDriver(adapter, config) + }, + } +} + +export class BunPostgresAdapter implements DatabaseAdapter { + #sql: SQL + #tableName!: string + + constructor(config: BunPostgresConfig) { + this.#sql = config.connection instanceof SQL ? config.connection : new SQL(config.connection) + } + + setTableName(tableName: string) { + this.#tableName = tableName + } + + #table() { + return this.#sql(this.#tableName) + } + + async get(key: string) { + const rows = await this.#sql< + Array<{ value: string; expires_at: number | string | null }> + >`SELECT value, expires_at FROM ${this.#table()} WHERE key = ${key}` + + const row = rows[0] + if (!row) return + + return { + value: row.value, + expiresAt: + row.expires_at !== null && row.expires_at !== undefined ? Number(row.expires_at) : null, + } + } + + async set(row: { key: string; value: any; expiresAt: Date | null }) { + const expiresAt = row.expiresAt?.getTime() ?? null + + await this.#sql` + INSERT INTO ${this.#table()} (key, value, expires_at) + VALUES (${row.key}, ${row.value}, ${expiresAt}) + ON CONFLICT (key) DO UPDATE + SET value = EXCLUDED.value, expires_at = EXCLUDED.expires_at + ` + } + + async delete(key: string) { + const result = await this.#sql`DELETE FROM ${this.#table()} WHERE key = ${key}` + return (result.count ?? 0) > 0 + } + + async deleteMany(keys: string[]) { + let count = 0 + for (const key of keys) { + const result = await this.#sql`DELETE FROM ${this.#table()} WHERE key = ${key}` + count += result.count ?? 0 + } + return count + } + + async createTableIfNotExists() { + await this.#sql` + CREATE TABLE IF NOT EXISTS ${this.#table()} ( + key VARCHAR(255) PRIMARY KEY NOT NULL, + value TEXT, + expires_at BIGINT + ) + ` + } + + async pruneExpiredEntries() { + await this.#sql`DELETE FROM ${this.#table()} WHERE expires_at < ${Date.now()}` + } + + async clear(prefix: string) { + await this.#sql`DELETE FROM ${this.#table()} WHERE key LIKE ${`${prefix}%`}` + } + + async disconnect() { + await this.#sql.end() + } +} diff --git a/packages/bentocache/src/drivers/database/adapters/bun_sqlite.ts b/packages/bentocache/src/drivers/database/adapters/bun_sqlite.ts new file mode 100644 index 00000000..59243d05 --- /dev/null +++ b/packages/bentocache/src/drivers/database/adapters/bun_sqlite.ts @@ -0,0 +1,103 @@ +import { Database } from 'bun:sqlite' +import type { Statement } from 'bun:sqlite' + +import { DatabaseDriver } from '../database.js' +import type { BunSqliteConfig, CreateDriverResult, DatabaseAdapter } from '../../../types/main.js' + +export function bunSqliteDriver(options: BunSqliteConfig): CreateDriverResult { + return { + options, + factory: (config: BunSqliteConfig) => { + const adapter = new BunSqliteAdapter(config) + return new DatabaseDriver(adapter, config) + }, + } +} + +interface CachedStatements { + get: Statement<{ value: string; expires_at: number | null }, [string]> + set: Statement + delete: Statement + prune: Statement + clear: Statement +} + +export class BunSqliteAdapter implements DatabaseAdapter { + #db: Database + #tableName!: string + #statements?: CachedStatements + + constructor(config: BunSqliteConfig) { + this.#db = + config.connection instanceof Database + ? config.connection + : new Database(config.connection, config.options) + } + + setTableName(tableName: string) { + this.#tableName = tableName + } + + #prepared(): CachedStatements { + if (this.#statements) return this.#statements + + this.#statements = { + get: this.#db.prepare(`SELECT value, expires_at FROM ${this.#tableName} WHERE key = ?`), + set: this.#db.prepare( + `INSERT INTO ${this.#tableName} (key, value, expires_at) VALUES (?, ?, ?) + ON CONFLICT(key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at`, + ), + delete: this.#db.prepare(`DELETE FROM ${this.#tableName} WHERE key = ?`), + prune: this.#db.prepare(`DELETE FROM ${this.#tableName} WHERE expires_at < ?`), + clear: this.#db.prepare(`DELETE FROM ${this.#tableName} WHERE key LIKE ?`), + } + + return this.#statements + } + + async get(key: string) { + const row = this.#prepared().get.get(key) + if (!row) return + return { value: row.value, expiresAt: row.expires_at } + } + + async set(row: { key: string; value: any; expiresAt: Date | null }) { + this.#prepared().set.run(row.key, row.value, row.expiresAt?.getTime() ?? null) + } + + async delete(key: string) { + return this.#prepared().delete.run(key).changes > 0 + } + + async deleteMany(keys: string[]) { + const stmt = this.#prepared().delete + const tx = this.#db.transaction((ks: string[]) => { + let count = 0 + for (const k of ks) count += stmt.run(k).changes + return count + }) + return tx(keys) + } + + async createTableIfNotExists() { + this.#db.exec( + `CREATE TABLE IF NOT EXISTS ${this.#tableName} ( + key TEXT PRIMARY KEY NOT NULL, + value TEXT, + expires_at INTEGER + )`, + ) + } + + async pruneExpiredEntries() { + this.#prepared().prune.run(Date.now()) + } + + async clear(prefix: string) { + this.#prepared().clear.run(`${prefix}%`) + } + + async disconnect() { + this.#db.close(false) + } +} diff --git a/packages/bentocache/src/types/options/drivers_options.ts b/packages/bentocache/src/types/options/drivers_options.ts index 1b255b16..d2ec277f 100644 --- a/packages/bentocache/src/types/options/drivers_options.ts +++ b/packages/bentocache/src/types/options/drivers_options.ts @@ -1,7 +1,13 @@ import type { Knex } from 'knex' import type { Kysely } from 'kysely' +import type { Database as BunSqliteDatabase } from 'bun:sqlite' import type { DynamoDBClientConfig } from '@aws-sdk/client-dynamodb' import type { DbResult, DefaultColumnTypes, DefaultSchemaConfig } from 'orchid-orm' +import type { + RedisClient as BunRedisClient, + RedisOptions as BunRedisOptions, + SQL as BunSQL, +} from 'bun' import type { Redis as IoRedis, RedisOptions as IoRedisOptions, @@ -185,3 +191,23 @@ export interface OrchidConfig extends DatabaseConfig { */ connection: DbResult> } + +export type BunRedisConfig = { + connection: BunRedisClient | string | (BunRedisOptions & { url?: string }) + options?: BunRedisOptions +} & DriverCommonOptions + +export interface BunSqliteConfig extends DatabaseConfig { + connection: BunSqliteDatabase | string + options?: { + readonly?: boolean + create?: boolean + readwrite?: boolean + safeIntegers?: boolean + strict?: boolean + } +} + +export interface BunPostgresConfig extends DatabaseConfig { + connection: BunSQL | string +} diff --git a/packages/bentocache/tests/drivers/bun_postgres.spec.ts b/packages/bentocache/tests/drivers/bun_postgres.spec.ts new file mode 100644 index 00000000..ed83bbe4 --- /dev/null +++ b/packages/bentocache/tests/drivers/bun_postgres.spec.ts @@ -0,0 +1,29 @@ +import { test } from '@japa/runner' + +import { POSTGRES_CREDENTIALS } from '../helpers/index.js' +import { registerCacheDriverTestSuite } from '../helpers/driver_test_suite.js' + +if (typeof (globalThis as any).Bun !== 'undefined') { + const { BunPostgresAdapter } = await import('../../src/drivers/database/adapters/bun_postgres.js') + const { DatabaseDriver } = await import('../../src/drivers/database/database.js') + + const port = process.env.BUN_POSTGRES_PORT || '5432' + const connectionUrl = `postgres://${POSTGRES_CREDENTIALS.user}:${POSTGRES_CREDENTIALS.password}@localhost:${port}/postgres` + + test.group('Bun Postgres driver', (group) => { + registerCacheDriverTestSuite({ + test, + group, + createDriver: (options) => { + const config = { + connection: connectionUrl, + prefix: 'japa', + pruneInterval: false as const, + ...options, + } + const adapter = new BunPostgresAdapter(config) + return new DatabaseDriver(adapter, config) + }, + }) + }) +} diff --git a/packages/bentocache/tests/drivers/bun_redis.spec.ts b/packages/bentocache/tests/drivers/bun_redis.spec.ts new file mode 100644 index 00000000..8a55ab2f --- /dev/null +++ b/packages/bentocache/tests/drivers/bun_redis.spec.ts @@ -0,0 +1,34 @@ +import { test } from '@japa/runner' + +import { REDIS_CREDENTIALS } from '../helpers/index.js' +import { registerCacheDriverTestSuite } from '../helpers/driver_test_suite.js' + +if (typeof (globalThis as any).Bun !== 'undefined') { + const { BunRedisDriver } = await import('../../src/drivers/bun_redis.js') + + test.group('Bun Redis driver', (group) => { + registerCacheDriverTestSuite({ + test, + group, + createDriver: (options) => + new BunRedisDriver({ + prefix: 'japa', + connection: `redis://${REDIS_CREDENTIALS.host}:${REDIS_CREDENTIALS.port}`, + ...options, + }), + }) + + test('should accept an existing Bun.RedisClient instance', async ({ assert, cleanup }) => { + const { RedisClient } = await import('bun') + const client = new RedisClient(`redis://${REDIS_CREDENTIALS.host}:${REDIS_CREDENTIALS.port}`) + const driver = new BunRedisDriver({ connection: client }) + + cleanup(async () => { + await driver.disconnect() + client.close() + }) + + assert.equal(driver.getConnection(), client) + }) + }) +} diff --git a/packages/bentocache/tests/drivers/bun_sqlite.spec.ts b/packages/bentocache/tests/drivers/bun_sqlite.spec.ts new file mode 100644 index 00000000..f9b9f8cf --- /dev/null +++ b/packages/bentocache/tests/drivers/bun_sqlite.spec.ts @@ -0,0 +1,26 @@ +import { test } from '@japa/runner' + +import { registerCacheDriverTestSuite } from '../helpers/driver_test_suite.js' + +if (typeof (globalThis as any).Bun !== 'undefined') { + const { BunSqliteAdapter } = await import('../../src/drivers/database/adapters/bun_sqlite.js') + const { DatabaseDriver } = await import('../../src/drivers/database/database.js') + + test.group('Bun SQLite driver', (group) => { + registerCacheDriverTestSuite({ + test, + group, + supportsMilliseconds: false, + createDriver: (options) => { + const config = { + connection: './bun-cache.sqlite3', + prefix: 'japa', + pruneInterval: false as const, + ...options, + } + const adapter = new BunSqliteAdapter(config) + return new DatabaseDriver(adapter, config) + }, + }) + }) +} diff --git a/packages/otel/README.md b/packages/otel/README.md index 040a23ed..4dbdd337 100644 --- a/packages/otel/README.md +++ b/packages/otel/README.md @@ -5,7 +5,10 @@ Official OpenTelemetry instrumentation for Bentocache. ## Install ```bash +npm i @bentocache/otel pnpm add @bentocache/otel +yarn add @bentocache/otel +bun add @bentocache/otel ``` ## Usage diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ac1eb03..d24a207c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -205,6 +205,9 @@ importers: '@types/better-sqlite3': specifier: ^7.6.13 version: 7.6.13 + '@types/bun': + specifier: ^1.2.0 + version: 1.3.14 '@types/pg': specifier: ^8.20.0 version: 8.20.0 @@ -2922,6 +2925,9 @@ packages: '@types/better-sqlite3@7.6.13': resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} + '@types/bun@1.3.14': + resolution: {integrity: sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw==} + '@types/bytes@3.1.5': resolution: {integrity: sha512-VgZkrJckypj85YxEsEavcMmmSOIzkUHqWmM4CCyia5dc54YwsXzJ5uT4fYxBQNEXx+oF1krlhgCbvfubXqZYsQ==} @@ -3551,6 +3557,9 @@ packages: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} + bun-types@1.3.14: + resolution: {integrity: sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ==} + bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -10658,6 +10667,10 @@ snapshots: dependencies: '@types/node': 25.5.0 + '@types/bun@1.3.14': + dependencies: + bun-types: 1.3.14 + '@types/bytes@3.1.5': {} '@types/chai@5.2.3': @@ -11437,6 +11450,10 @@ snapshots: builtin-modules@3.3.0: {} + bun-types@1.3.14: + dependencies: + '@types/node': 25.5.0 + bundle-name@4.1.0: dependencies: run-applescript: 7.0.0 From bb8060a7fca7d880bc1f64771eb33d4ec0c3febd Mon Sep 17 00:00:00 2001 From: Max Levchuk <66306912+unisol1020@users.noreply.github.com> Date: Wed, 20 May 2026 12:32:54 +0200 Subject: [PATCH 2/2] Add Bun-native adapters for Redis, SQLite, and Postgres Add Bun-native Redis, SQLite, and Postgres adapters to drivers. --- .changeset/hungry-dodos-cheer.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/hungry-dodos-cheer.md diff --git a/.changeset/hungry-dodos-cheer.md b/.changeset/hungry-dodos-cheer.md new file mode 100644 index 00000000..a90eb3f0 --- /dev/null +++ b/.changeset/hungry-dodos-cheer.md @@ -0,0 +1,7 @@ +--- +"bentocache": patch +"@bentocache/otel": patch +"@bentocache/docs": patch +--- + +feat(drivers): add Bun-native Redis, SQLite, and Postgres adapters