# Tasks: enterprise-surrealdb **Change ID**: enterprise-surrealdb **Package**: `@suaveplan/surrealdb` **Target path**: `universal/database/surrealdb` **Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/db`, `@suaveplan/retry` --- ## 1. Scaffold - [x] Create directory `universal/database/surrealdb/` with `src/`, `tests/`, `src/__benchmarks__/`, and all feature subdirectories per the layout in `proposal.md`. - [x] Write `package.json` at version `0.1.0` with: - `"type": "module"`, `"sideEffects": false` - `exports` map: top-level `"."` barrel plus subpaths for `./connection/connection`, `./adapters/surreal-adapter`, `./schema/discovery`, `./init/init`, `./utils/version-detector` - `dependencies`: `@suaveplan/error workspace:^`, `@suaveplan/types workspace:^`, `@suaveplan/db workspace:^`, `@suaveplan/retry workspace:^`, `surrealdb catalog:database`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build` - `devDependencies`: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `fast-check catalog:testing`, `mitata catalog:testing`, `@suaveplan/testing workspace:^` - [x] Write `tsconfig.json` extending `@suaveplan/typescript-config/library.json`. - [x] Write `vitest.config.ts` using `createTestConfig()` from `@suaveplan/testing/config` (pool: `forks`). - [x] Add `lint`, `typecheck`, `test`, `test:coverage`, `build`, `docs:check`, `bench` scripts to `package.json`. --- ## 2. Types + errors ### 2a. Common types (`src/types/common.ts` + `src/types/common.md`) - [x] Define all shared TS types: `SurrealVersion`, `MajorVersion`, `VersionInfo`, `ConnectionState`, `RecordId`, `RecordResult`, `QueryStatementResult`, `RawQueryResult`, `QueryResult`, `AuthCredentials`. - [x] Write `src/types/common.md` (≥200 words, both simple + advanced examples, API reference). ### 2b. Error codes (`src/error-codes.ts`) - [x] Import `CodeMetadata`, `ErrorCodes`, `SuaveplanError` from `@suaveplan/error`. - [x] Define `SurrealErrorCode` union — eight codes: `SURREAL_CONNECT_FAILED`, `SURREAL_QUERY_FAILED`, `SURREAL_AUTH_FAILED`, `SURREAL_NOT_CONNECTED`, `SURREAL_TIMEOUT`, `SURREAL_VERSION_UNSUPPORTED`, `SURREAL_SCHEMA_LOAD_FAILED`, `SURREAL_INIT_FAILED`. - [x] Define `SURREAL_ERROR_METADATA` with `httpStatus`, `severity`, `retryable`, `userMessageKey`, `category`, `description` for each code. - [x] Export `registerSurrealErrorCodes()` — calls `ErrorCodes.registerMany(SURREAL_ERROR_METADATA)`. Idempotent. - [x] Call `registerSurrealErrorCodes()` as an eager side-effect at module scope. - [x] Export `SurrealError` extending `SuaveplanError` carrying `SurrealErrorCode` as its `.code` discriminant. ### 2c. Zod option schemas - [x] Write `src/connection/connection/connection.types.ts` with `SurrealDBConfigSchema` and inferred `SurrealDBConfig`. - [x] Write `src/init/init/init.types.ts` with `InitOptionsSchema`, `AdvancedInitOptionsSchema`, `SchemaFileSchema`. - [x] Write `src/schema/discovery/discovery.types.ts` with `DiscoverOptionsSchema`. --- ## 3. Constants - [x] Write `src/constants/index.ts`: - `DEFAULT_SCHEMA_ORDER`: `readonly string[]` with sensible migration-first ordering. - `DEFAULT_SURREAL_URL`, `DEFAULT_SURREAL_NAMESPACE`, `DEFAULT_SURREAL_DATABASE`. - `SURREAL_SUPPORTED_MAJOR_VERSIONS: readonly MajorVersion[]` = `[1, 2, 3]`. --- ## 4. Version detector (`src/utils/version-detector.ts`) - [x] Implement `detectVersion(db: Surreal): Promise`: - Calls `db.version()`, parses the semver string with a regex. - Returns typed `VersionInfo` for major versions 1–3. - Throws `SurrealError` with code `SURREAL_VERSION_UNSUPPORTED` for unknown major versions. - [x] Write `version-detector.test.ts`: known version strings for v1/v2/v3, prerelease string, unsupported major throws. - [x] Write `version-detector.md` (≥200 words). --- ## 5. Connection (`src/connection/connection/`) - [x] Implement `getDefaultConfig(): SurrealDBConfig` — reads `DB_URL`, `DB_NAMESPACE`, `DB_NAME`, `DB_USER`, `DB_PASS` from `process.env` where available; falls back to defaults. - [x] Implement `createConnection(config?, options?)`: - Merges partial config with defaults. - Instantiates `new Surreal()`, calls `db.connect(url)`, then authenticates. - Accepts optional `signal?: AbortSignal` — throws `SurrealError(SURREAL_TIMEOUT)` on abort before connect completes. - Accepts optional `retry?: RetryStrategyOptions` — wraps the connect+auth sequence with `@suaveplan/retry`'s `withRetry`. - OTel: span `surreal.connect`, counter `surreal.connect_total`. - [x] Implement `connectWith`, `getSingleton`, `hasSingleton`, `closeSingleton` — delegate to `@suaveplan/db`'s singleton helpers where appropriate. - [x] Write `connection.test.ts` (requires real SurrealDB; gate with `process.env.SURREALDB_URL` skip guard). - [x] Write `connection.md` (≥200 words). --- ## 6. Adapter (`src/adapters/surreal-adapter/`) - [x] Define `SurrealAdapter` interface in `surreal-adapter.types.ts` — mirrors the main-branch surface; adds `DatabaseAdapter` from `@suaveplan/db` as parent interface. - [x] Implement `SurrealAdapter` class: - Internal `_state: ConnectionState` with protected getter. - `connect(config)` — calls `createConnection`, transitions state. - `disconnect()` — calls `db.close()`, transitions to `disconnected`. - `query(sql, params?, options?)` — calls `db.query(sql, params)`, wraps errors as `SurrealError(SURREAL_QUERY_FAILED)`, records OTel span + histogram. - `create`, `select`, `update`, `merge`, `delete` — delegate to driver, wrap errors. - All methods accept optional `signal?: AbortSignal`. - Fix: use `INSERT INTO` query instead of `db.create()` to avoid surrealdb.js v2 content-drop bug (see memory `feedback_surrealdb_create_content_drop`). - [x] Write `surreal-adapter.test.ts` (integration, real SurrealDB; SURREALDB_URL skip guard). - [x] Write `surreal-adapter.md` (≥200 words). --- ## 7. Schema discovery (`src/schema/discovery/`) - [x] Implement `discoverSchemas(dir, options?)` — reads directory recursively for `.surql` files; returns `SchemaFile[]` sorted by `DEFAULT_SCHEMA_ORDER` then alphabetically. - [x] Implement `filterSchemas(files, filter)` — apply caller-supplied predicate. - [x] Implement `getSchemasToLoad(dir, filter?)` — `discoverSchemas` + optional `filterSchemas`. - [x] Implement `readSchemaContent(file)` — reads file content; caches in `file.content`. - [x] Write `discovery.test.ts` — in-memory fixture files (no real FS), happy path, empty dir, filter exclude/include. - [x] Write `discovery.md` (≥200 words). --- ## 8. Init (`src/init/init/`) - [x] Implement `initDatabase(db, options)`: - Calls `getSchemasToLoad`, reads each file, executes via `db.query(content)`. - Runs `options.hooks?.before` / `after` around each schema. - Dry-run mode: skip execution, record in `result.skipped`. - Returns `InitResult { applied, skipped, errors }`. - OTel: span `surreal.init`, attribute `surreal.schema_count`. - [x] Implement `initAndClose(options)` — opens a singleton, runs init, closes. - [x] Implement `createInitializer(options)` — returns a closure `(db) => initDatabase(db, options)`. - [x] Write `init.test.ts` (unit: mock schema files, real adapter not required; integration: SURREALDB_URL guard). - [x] Write `init.md` (≥200 words). --- ## 9. Public barrel (`src/index.ts`) - [x] Re-export all public symbols from connection, adapters, schema, init, utils, types, error-codes, constants. - [x] Re-export `RecordId`, `StringRecordId`, `Surreal`, `Table`, `Patch`, `RecordIdValue` directly from `surrealdb` driver (not wrapped). --- ## 10. OTel instrumentation (cross-cutting) - [x] Obtain tracer `surreal` and meter `surreal` once at module scope via lazy `trace.getTracer` / `metrics.getMeter`. - [x] `createConnection` — span `surreal.connect` with attributes `surreal.url`, `surreal.namespace`, `surreal.database`; counter `surreal.connect_total`. - [x] `SurrealAdapter.query` — span `surreal.query`; histogram `surreal.query_duration_ms`; counter `surreal.query_total` with attribute `surreal.table` where parseable. - [x] `closeSingleton` / `disconnect` — span `surreal.disconnect`. - [x] `initDatabase` — span `surreal.init`; attribute `surreal.schema_count`. - [x] All spans record exception on error and end in `finally`. --- ## 11. Property-based tests (`tests/property.test.ts`) Use `fast-check`. All test timeouts: `{ timeout: 30000 }`. - [x] **`detectVersion` parser**: for arbitrary `major.minor.patch` strings (1–3), assert that `parseVersionString` returns the same major/minor/patch. Does not require live DB. - [x] **`filterSchemas` complement**: generate N random schema files (2–10); assert include filter with predicate P plus exclude filter with predicate P covers the full set without overlap. - [x] **`ConnectionState` state machine**: for a sequence of `connect` and `disconnect` calls on a mock adapter, assert the final state equals the result of applying transitions in order (always ends `connected` or `disconnected`). --- ## 12. Benchmarks (`src/__benchmarks__/surreal.bench.ts`) Use `mitata`. Guard IO-bound benchmarks with `SKIP_DB_BENCHMARKS` env flag. - [x] `detectVersion_parse_10k`: 10 000 pure semver parses. - [x] `query_hot` (conditional on `!SKIP_DB_BENCHMARKS`): 1000 `SELECT 1` queries on a pre-connected adapter. - [x] Document placeholder benchmark table in `README.md` under `## Benchmarks`. --- ## 13. Docs - [x] `src/types/common.md` - [x] `src/connection/connection/connection.md` - [x] `src/adapters/surreal-adapter/surreal-adapter.md` - [x] `src/schema/discovery/discovery.md` - [x] `src/init/init/init.md` - [x] `src/utils/version-detector.md` - [x] Root `universal/database/surrealdb/README.md` — purpose, installation, quick-start, dep table, API symbol list, benchmarks table. --- ## 14. Verify and publish - [x] Run `bunx turbo lint --filter=@suaveplan/surrealdb` — zero errors. - [x] Run `bunx turbo typecheck --filter=@suaveplan/surrealdb` — zero errors. - [x] Run `bunx turbo test --filter=@suaveplan/surrealdb` — all tests pass. - [x] Run `bunx turbo test:coverage --filter=@suaveplan/surrealdb` — 100% on all touched modules. - [x] Run `bun run docs:check --filter=@suaveplan/surrealdb` — zero errors. - [x] Confirm no `TODO`, `FIXME`, `@deprecated`, untyped `any`, `ts-ignore`, or `.skip` in `src/` or `tests/`. - [x] Confirm `"sideEffects": false` in `package.json`. - [x] Run `bun scripts/publish.ts --package @suaveplan/surrealdb`. - [x] Update `CLAUDE.md` package listing to include `@suaveplan/surrealdb`. - [x] Archive: move `openspec/changes/enterprise-surrealdb/` to `openspec/archive/2026-04-24-enterprise-surrealdb/` and fold spec delta into `openspec/specs/database/spec.md`.