Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,279 @@
# 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<SurrealDBConfig>): Promise<Surreal>
function connectWith(config?: Partial<SurrealDBConfig>): Promise<Surreal>
function getSingleton(config?: Partial<SurrealDBConfig>): Promise<Surreal>
function hasSingleton(): boolean
function closeSingleton(): Promise<void>
```
### 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<void>
disconnect(): Promise<void>
query<T>(sql: string, params?: Record<string, unknown>): Promise<T[][]>
create<T>(table: string, data: Partial<T>): Promise<T>
select<T>(thing: string): Promise<T[]>
update<T>(thing: string, data: Partial<T>): Promise<T>
merge<T>(thing: string, data: Partial<T>): Promise<T>
delete(thing: string): Promise<void>
readonly connectionState: ConnectionState
}
class SurrealAdapter implements SurrealAdapter
```
### Schema utilities
```ts
function getDefaultSchemaDir(): string
function discoverSchemas(dir: string, options?: DiscoverOptions): Promise<SchemaFile[]>
function filterSchemas(files: SchemaFile[], filter: SchemaFilter): SchemaFile[]
function getSchemasToLoad(dir: string, filter?: SchemaFilter): Promise<SchemaFile[]>
function readSchemaContent(file: SchemaFile): Promise<string>
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<InitResult>
function initAndClose(options: DbConfig): Promise<InitResult>
function createInitializer(options: InitOptions): (db: Surreal) => Promise<InitResult>
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<SurrealDBConfig>
}
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 = unknown> = T & { id: RecordId }
type QueryStatementResult<T = unknown> = { status: 'OK' | 'ERR'; time?: string; result?: T; error?: string }
type RawQueryResult<T = unknown> = QueryStatementResult<T>[]
type QueryResult<T = unknown> = 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<VersionInfo>` — 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.