# OpenSpec Change: enterprise-surrealdb - **Change ID**: enterprise-surrealdb - **Package**: `@suaveplan/surrealdb` - **Category**: universal - **Target path**: `universal/database/surrealdb` - **Status**: PROPOSED - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/surrealdb.md` --- ## Summary Deliver `@suaveplan/surrealdb` on the genesis branch from scratch at enterprise quality. The package provides a universal SurrealDB client wrapper with automatic version detection (v1/v2/v3), connection lifecycle management (connect/disconnect/singleton registry), schema discovery and loading, a migration runner, and a pluggable adapter layer that normalises the raw `surrealdb.js` driver API into a stable typed surface. Genesis adds structured errors via `@suaveplan/error`, Zod schemas for every public options type, OTel instrumentation on all async operations, `AbortSignal` cancellation throughout, retry integration via `@suaveplan/retry`, property-based tests (fast-check), and a mitata benchmark suite. The SurrealDB-specific migration runner (internal to this package in main) is split out to `@suaveplan/migration` on genesis; only the connection + adapter surface lives here. --- ## Current-state audit (genesis) The package does not exist on genesis yet — this is a fresh build. No prior source, no test results, no coverage data. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/error` (`workspace:^`) | Base class for `SurrealError`; `defineCodes` for `SURREAL.*` codes | | `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock` (injectable for timeout helpers) | | `@suaveplan/db` (`workspace:^`) | `DatabaseAdapter` interface, singleton registry helpers (`getSingleton`/`closeSingleton`) | | `@suaveplan/retry` (`workspace:^`) | `RetryStrategy`, `withRetry` — wired on connect and query paths | | `surrealdb` (`catalog:database`) | Raw SurrealDB JS driver | | `zod` (`catalog:forms`) | Zod schemas for all public option types | | `@opentelemetry/api` (`catalog:observability`) | Spans + counters on connect, query, disconnect | | `tslib` (`catalog:build`) | Runtime helpers | Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata` (all `catalog:testing`), `@suaveplan/testing` (`workspace:^`). --- ## Preserved public API surface (from main branch) All symbols below are carried forward; signatures may gain optional genesis-only fields but existing call sites remain valid. ### Connection ```ts interface SurrealDBConfig { url: string namespace: string database: string auth: { username: string; password: string } } function getDefaultConfig(): SurrealDBConfig function createConnection(config?: Partial): Promise function connectWith(config?: Partial): Promise function getSingleton(config?: Partial): Promise function hasSingleton(): boolean function closeSingleton(): Promise ``` ### Adapter ```ts // Re-exported from surrealdb driver (not re-wrapped — direct pass-through for types) type Patch type RecordIdValue class RecordId class StringRecordId class Surreal class Table // Adapter base interface interface SurrealAdapter { connect(config: SurrealDBConfig): Promise disconnect(): Promise query(sql: string, params?: Record): Promise create(table: string, data: Partial): Promise select(thing: string): Promise update(thing: string, data: Partial): Promise merge(thing: string, data: Partial): Promise delete(thing: string): Promise readonly connectionState: ConnectionState } class SurrealAdapter implements SurrealAdapter ``` ### Schema utilities ```ts function getDefaultSchemaDir(): string function discoverSchemas(dir: string, options?: DiscoverOptions): Promise function filterSchemas(files: SchemaFile[], filter: SchemaFilter): SchemaFile[] function getSchemasToLoad(dir: string, filter?: SchemaFilter): Promise function readSchemaContent(file: SchemaFile): Promise const DEFAULT_SCHEMA_ORDER: readonly string[] interface SchemaFile { name: string; path: string; content?: string } type SchemaFilter = (file: SchemaFile) => boolean ``` ### Init ```ts function initDatabase(db: Surreal, options: InitOptions): Promise function initAndClose(options: DbConfig): Promise function createInitializer(options: InitOptions): (db: Surreal) => Promise interface InitOptions { schemaDir?: string schemas?: SchemaFile[] filter?: SchemaFilter hooks?: SchemaHook[] dryRun?: boolean } interface InitResult { applied: string[]; skipped: string[]; errors: string[] } interface DbConfig extends InitOptions { connection?: Partial } type FilterStrategy = 'include' | 'exclude' interface SchemaHook { before?: HookFn; after?: HookFn } type AdvancedInitOptions = InitOptions & { parallel?: boolean; timeout?: number } ``` ### Common types ```ts type SurrealVersion = string type MajorVersion = 1 | 2 | 3 interface VersionInfo { version: SurrealVersion; major: MajorVersion; minor: number; patch: number; prerelease?: string } type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'authenticated' | 'error' interface RecordId { tb: string; id: string | number } type RecordResult = T & { id: RecordId } type QueryStatementResult = { status: 'OK' | 'ERR'; time?: string; result?: T; error?: string } type RawQueryResult = QueryStatementResult[] type QueryResult = T[][] interface AuthCredentials { username: string; password: string } ``` ### Error codes ```ts type SurrealErrorCode = | 'SURREAL_CONNECT_FAILED' | 'SURREAL_QUERY_FAILED' | 'SURREAL_AUTH_FAILED' | 'SURREAL_NOT_CONNECTED' | 'SURREAL_TIMEOUT' | 'SURREAL_VERSION_UNSUPPORTED' | 'SURREAL_SCHEMA_LOAD_FAILED' | 'SURREAL_INIT_FAILED' ``` --- ## New additions for genesis ### 1. Structured `SurrealError` `SurrealError` extends `SuaveplanError` from `@suaveplan/error`, carrying a `SurrealErrorCode` discriminant. All internal throw sites use named factory helpers. ### 2. Zod schemas for all public option types - `SurrealDBConfigSchema` - `InitOptionsSchema` - `DiscoverOptionsSchema` - `SchemaFileSchema` - `AdvancedInitOptionsSchema` ### 3. Retry integration `createConnection` and `SurrealAdapter.query` accept an optional `retry?: RetryStrategyOptions` field. When provided the connection/query is wrapped with `@suaveplan/retry`'s `withRetry`. Default: no retry (caller opt-in). ### 4. AbortSignal throughout All async public functions accept an optional `signal?: AbortSignal`. Pending connections abort immediately on signal; in-flight queries are cancelled via the driver's abort mechanism where supported. ### 5. OTel instrumentation | Operation | Signal | |---|---| | `createConnection` | span `surreal.connect`, attributes `surreal.url`, `surreal.namespace` | | `SurrealAdapter.query` | span `surreal.query`, histogram `surreal.query_duration_ms`, counter `surreal.query_total` | | `SurrealAdapter.connect` | counter `surreal.connect_total` | | `closeSingleton` | span `surreal.disconnect` | | `initDatabase` | span `surreal.init`, attribute `surreal.schema_count` | ### 6. Version detector utility `detectVersion(db: Surreal): Promise` — calls `db.version()`, parses semver, returns typed `VersionInfo`. Throws `SurrealError` with code `SURREAL_VERSION_UNSUPPORTED` for major versions outside 1–3. ### 7. `DatabaseAdapter` bridge `SurrealAdapter` implements `@suaveplan/db`'s `DatabaseAdapter` interface so it can be consumed by `@suaveplan/migration` and other db-layer packages without tight coupling to `surrealdb.js`. ### 8. Property-based tests (fast-check) - `detectVersion` parser: for arbitrary semver strings, assert round-trip correctness of `major.minor.patch`. - `filterSchemas`: for N random schema files with random names, assert that include/exclude filter with the same predicate is the complement of the other. - `SurrealAdapter` state machine: transitions are deterministic — `connect` moves to `connecting` → `connected`; `disconnect` from any state moves to `disconnected`. ### 9. Benchmark suite - `createConnection_cold`: connect + authenticate + disconnect (requires live SurrealDB or skip with `SKIP_DB_BENCHMARKS`). - `query_hot`: 1000 `SELECT * FROM test` queries against a pre-connected adapter (requires live DB). - `detectVersion_parse`: 10 000 semver parse iterations (pure, no IO). --- ## File layout ```text universal/database/surrealdb/ package.json tsconfig.json vitest.config.ts src/ index.ts # Public barrel error-codes.ts # SURREAL.* codes constants/ index.ts # DEFAULT_SCHEMA_ORDER, connection defaults types/ common.ts # All shared TS types common.md connection/ connection/ connection.ts # createConnection, connectWith, getSingleton, hasSingleton, closeSingleton connection.md connection.types.ts # SurrealDBConfig connection.test.ts index.ts adapters/ surreal-adapter/ surreal-adapter.ts # SurrealAdapter class surreal-adapter.md surreal-adapter.types.ts # SurrealAdapter interface surreal-adapter.test.ts index.ts schema/ discovery/ discovery.ts # discoverSchemas, filterSchemas, getSchemasToLoad, readSchemaContent discovery.md discovery.types.ts discovery.test.ts index.ts init/ init/ init.ts # initDatabase, initAndClose, createInitializer init.md init.types.ts # InitOptions, InitResult, DbConfig, SchemaHook init.test.ts index.ts utils/ version-detector.ts # detectVersion version-detector.md version-detector.test.ts __benchmarks__/ surreal.bench.ts tests/ integration.test.ts # Full connect → query → disconnect (real SurrealDB via docker) property.test.ts # fast-check property tests ``` --- ## Impact **Downstream packages**: `@suaveplan/migration` depends on `SurrealAdapter` as a `DatabaseAdapter` implementation. No other genesis package currently requires it. **Breaking changes from main-branch API**: none. All main-branch call sites compile unchanged; new optional fields (`retry`, `signal`, `logger`) are additive. **Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/db`, `@suaveplan/retry` must be published to Verdaccio before this package can be built or tested.