Initial Commit
This commit is contained in:
146
openspec/archive/2026-04-24-enterprise-db-sqlite/proposal.md
Normal file
146
openspec/archive/2026-04-24-enterprise-db-sqlite/proposal.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# OpenSpec Change: enterprise-db-sqlite
|
||||
|
||||
- **Change ID**: enterprise-db-sqlite
|
||||
- **Package**: `@suaveplan/db-sqlite`
|
||||
- **Category**: universal
|
||||
- **Target path**: `universal/database/db-sqlite`
|
||||
- **Status**: IN PROGRESS
|
||||
- **Date**: 2026-04-23
|
||||
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/db-sqlite.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Greenfield implementation of `@suaveplan/db-sqlite` for the genesis branch. Universal SQLite adapter — `bun:sqlite` on the Bun server, `@sqlite.org/sqlite-wasm` with OPFS persistence in the browser, unified behind a single Drizzle-ORM surface. One schema definition, one query builder API, two runtimes selected transparently via `package.json` conditional exports.
|
||||
|
||||
The main-branch version (v1.0.0) provides the baseline public-API surface to preserve. Genesis adds enterprise-grade structured errors, an LRU prepared-statement cache, a forward-only migration runner with a ledger table and dry-run mode, OTel instrumentation, property-based tests, benchmarks, and a full co-located documentation suite.
|
||||
|
||||
## Current-state audit (genesis)
|
||||
|
||||
The package does not exist on genesis yet — this is a fresh build. The `universal/database/` domain directory does not exist either and must be created alongside the required predecessor package `@suaveplan/db`.
|
||||
|
||||
## Available workspace deps on genesis
|
||||
|
||||
| Package | Status | Notes |
|
||||
|---------|--------|-------|
|
||||
| `@suaveplan/error` | ✅ built | Extend for `SqliteError` with `SQLITE.*` codes |
|
||||
| `@suaveplan/types` | ✅ built | `Clock`, `IdGenerator`, `LoggerLike` interfaces |
|
||||
| `@suaveplan/db` | ❌ not yet built | Must land before `db-sqlite` — provides `DatabaseAdapter`, `DatabaseConfig`, `MigrationConfig`, `MigrationResult`, singleton helpers |
|
||||
| `@suaveplan/logger` | ✅ built | Structured logging inside adapter |
|
||||
| `@suaveplan/opentelemetry` | ❌ not yet built | Use `@opentelemetry/api` directly |
|
||||
| `@suaveplan/schema-registry` | ✅ built | Wire Zod schemas for config/row types |
|
||||
| `@suaveplan/retry` | ✅ built | Retry on `SQLITE_BUSY` write paths |
|
||||
| `@suaveplan/feature-flags` | ✅ built | Gate risky migration or pragma changes |
|
||||
|
||||
External deps — require a new `catalog:database` block in the root `package.json` (none of these exist in any current catalog):
|
||||
|
||||
| Package | Catalog entry to add |
|
||||
|---------|---------------------|
|
||||
| `drizzle-orm` | `catalog:database` |
|
||||
| `@sqlite.org/sqlite-wasm` | `catalog:database` |
|
||||
|
||||
`bun:sqlite` is a built-in Bun module — no npm dep needed; import is conditional.
|
||||
|
||||
Dev deps (all already in `catalog:testing`):
|
||||
|
||||
- `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata`
|
||||
|
||||
Build deps (already in `catalog:build`):
|
||||
|
||||
- `vite`, `vite-plugin-dts`, `tslib`
|
||||
|
||||
## Preserved public API surface (from main branch)
|
||||
|
||||
All of the following must be exported from the genesis build at `"."` and at the
|
||||
corresponding feature subpaths.
|
||||
|
||||
### Types
|
||||
|
||||
- `SqliteAdapterOptions` — alias for `BunDriverOptions` (filename, readonly)
|
||||
- `BunDriverOptions` — `{ filename?: string; readonly?: boolean }`
|
||||
- `BunDriverResult` — `{ sqlite: Database; close: () => Promise<void> }`
|
||||
- `WasmDriverOptions` — `{ filename?: string }`
|
||||
- `WasmDriverResult<TSchema>` — `{ db: SqliteRemoteDatabase<TSchema>; close: () => Promise<void> }`
|
||||
|
||||
### Classes
|
||||
|
||||
- `SqliteAdapter<TSchema>` — implements `DatabaseAdapter` from `@suaveplan/db`
|
||||
- `constructor(options?: SqliteAdapterOptions, schema?: TSchema)`
|
||||
- `connect(config?: Partial<DatabaseConfig>): Promise<void>`
|
||||
- `disconnect(): Promise<void>`
|
||||
- `query<T>(sql: string, params?: unknown[]): Promise<T[]>`
|
||||
- `execute(sql: string, params?: unknown[]): Promise<void>`
|
||||
- `transaction<T>(fn: () => Promise<T>): Promise<T>`
|
||||
- `migrate(config?: MigrationConfig): Promise<MigrationResult>`
|
||||
- `get isConnected: boolean`
|
||||
- `get drizzle: BunSQLiteDatabase<TSchema>` — available after `connect()`
|
||||
|
||||
### Functions
|
||||
|
||||
- `createBunDriver(options?: BunDriverOptions): Promise<BunDriverResult>`
|
||||
- `isBunEnvironment(): boolean`
|
||||
- `createWasmDriver<TSchema>(options?: WasmDriverOptions, schema?: TSchema): Promise<WasmDriverResult<TSchema>>`
|
||||
- `getDefaultSqlite(options?, config?, name?): Promise<SqliteAdapter>`
|
||||
- `closeSqlite(name: string): Promise<void>`
|
||||
- `closeAllSqlite(): Promise<void>`
|
||||
- `hasSqlite(name: string): boolean`
|
||||
|
||||
### Unsafe subpath (`@suaveplan/db-sqlite/unsafe`)
|
||||
|
||||
- `rawQuery(db, sql, params?)` — direct SQL execution, bypasses query builder
|
||||
|
||||
## New additions for genesis
|
||||
|
||||
### Modules
|
||||
|
||||
- **`src/errors/`** — `SqliteError` extending `SuaveplanError`; stable code registry `SQLITE_CODES`:
|
||||
`SQLITE.NOT_CONNECTED`, `SQLITE.QUERY_FAILED`, `SQLITE.EXECUTE_FAILED`,
|
||||
`SQLITE.TRANSACTION_FAILED`, `SQLITE.MIGRATION_FAILED`, `SQLITE.ENVIRONMENT_ERROR`,
|
||||
`SQLITE.BUSY`, `SQLITE.CONSTRAINT`, `SQLITE.CORRUPT`, `SQLITE.DRIVER_INIT_FAILED`
|
||||
|
||||
- **`src/cache/`** — `PreparedStatementCache` — LRU-bounded (configurable max size, default 128),
|
||||
keyed by SQL text, invalidated on migration, OTel hit/miss counters
|
||||
|
||||
- **`src/migration/`** — `MigrationRunner` — forward-only, idempotent, ledger table
|
||||
(`__sqlite_migrations`), dry-run mode, `MigrationRunnerOptions` Zod schema,
|
||||
integrates with Drizzle migrator; emits OTel span per migration file applied
|
||||
|
||||
- **`src/schemas/`** — Zod schemas for `SqliteAdapterOptions`, `BunDriverOptions`,
|
||||
`WasmDriverOptions`, `MigrationRunnerOptions`; exported as re-usable validators
|
||||
|
||||
- **`src/otel/`** — `withQuerySpan(name, sql, fn)` wrapper — sanitises SQL (strips
|
||||
literal values), records `db.system=sqlite`, `db.statement`, latency histogram,
|
||||
error event on failure; no-op when no OTel provider registered
|
||||
|
||||
- **`src/conformance/`** — shared test suite (`conformance-suite.ts`) exercising both
|
||||
Bun and WASM drivers: CRUD, transactions, rollback semantics, prepared-statement
|
||||
cache hits, migration ledger idempotency, `AbortSignal` cancellation
|
||||
|
||||
- **`src/__benchmarks__/`** — mitata benchmarks: single-row insert, bulk insert (1 k
|
||||
rows), point-lookup, full-table scan, prepared vs. ad-hoc statement comparison
|
||||
|
||||
### Cross-cutting enterprise requirements
|
||||
|
||||
- `SqliteAdapter` constructor throws `SqliteError(SQLITE.ENVIRONMENT_ERROR)` when
|
||||
`isBunEnvironment()` is false — keeps runtime guarantee at the type level
|
||||
- `PreparedStatementCache` evicts on `migrate()` to prevent stale plan reuse
|
||||
- All public-facing async methods accept optional `AbortSignal` where the underlying
|
||||
driver supports cancellation (query abort path on `bun:sqlite` via `statement.finalize()`)
|
||||
- `transaction()` uses `BEGIN IMMEDIATE` to avoid deadlock on concurrent writes
|
||||
- `sideEffects: false` confirmed and guarded by a tree-shake size test
|
||||
- Property-based tests (fast-check) for: SQL-injection resistance via Drizzle
|
||||
parameterisation, transaction isolation invariants, cache eviction correctness
|
||||
- Zero `any`, zero `ts-ignore`, zero mock usage in tests — real in-memory SQLite only
|
||||
- Injectable `clock` for migration ledger timestamps (defaults to `Date.now`)
|
||||
- Conditional `package.json` exports route `./adapter` to `adapter.bun.js` (Bun runtime)
|
||||
and `adapter.wasm.js` (browser), keeping hot-path free of runtime detection
|
||||
|
||||
## Impact
|
||||
|
||||
- Introduces a new domain directory: `universal/database/`
|
||||
- Adds `catalog:database` block to root `package.json` (new entries: `drizzle-orm`,
|
||||
`@sqlite.org/sqlite-wasm`)
|
||||
- Blocks on `@suaveplan/db` landing first (dependency-order constraint)
|
||||
- No existing genesis packages depend on `@suaveplan/db-sqlite` yet — zero breaking-change
|
||||
risk within the current tree
|
||||
- WASM test suite requires `fileParallelism: false` in the browser-environment
|
||||
vitest config (WSL2 concurrent OPFS forks limitation)
|
||||
Reference in New Issue
Block a user