first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
# @suaveplan/api-plugin-database - SurrealDB-backed database plugin for @suaveplan/api. Pools @suaveplan/surrealdb's SurrealAdapter (implementing @suaveplan/db DatabaseAdapter), runs .surql migrations through @suaveplan/api-migrations under an advisory lock, provides tenant provisioning callables, retry/backoff, RFC 7807 problem-json rendering, and a full OTel surface. Path A wrapper — adapter surface delegates to suaveplan SurrealAdapter; this plugin owns the orthogonal pool/migration/tenancy/retry/observability concerns.
SurrealDB-backed database plugin for `@suaveplan/api`. Pools `@suaveplan/surrealdb`'s `SurrealAdapter` (implementing `@suaveplan/db`'s `DatabaseAdapter`), runs `.surql` migrations through `@suaveplan/api-migrations` under an advisory lock, ships tenant-provisioning callables for the three canonical multi-tenancy strategies, and emits a complete OpenTelemetry surface (pool checkouts / checkins / saturation + query duration histogram). RFC 7807 problem-json rendering rounds out the error path.
This is a **Path-A wrapper**: every adapter call delegates to the canonical `SurrealAdapter`; this package owns only the orthogonal pool / migration / tenancy / retry / observability concerns. Consumers obtain the `DatabaseAdapter` through `ctx.getService("db")` and call the suaveplan surface defined by `@suaveplan/db`.
<!-- begin:auto-docs -->
## Documentation tree
- [config](./src/config/config-schema.md) — Defines the Zod schemas that validate every public option bundle accepted by `@suaveplan/api-plugin-database`. The sche…
- [config-schema](./src/config/config-schema.md) — Defines the Zod schemas that validate every public option bundle accepted by `@suaveplan/api-plugin-database`. The sche…
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable `DATABASE_*` error-code registry consumed by every other module in the `@suaveplan/api-plugin-databa…
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable `DATABASE_*` error-code registry consumed by every other module in the `@suaveplan/api-plugin-databa…
- [lock](./src/migrations/lock.md) — Implements the advisory lock that prevents two concurrent migration runners from racing each other. The lock is a singl…
- [runner](./src/migrations/runner.md) — Implements `runMigrations`, the top-level orchestrator for the migration phase of `createDatabasePlugin`'s `onReady` ho…
- [otel](./src/otel/instruments.md) — Provides the lazily-cached bundle of OpenTelemetry instruments emitted by `@suaveplan/api-plugin-database`. Every count…
- [instruments](./src/otel/instruments.md) — Provides the lazily-cached bundle of OpenTelemetry instruments emitted by `@suaveplan/api-plugin-database`. Every count…
- [plugin](./src/plugin/plugin.md) — Exports `createDatabasePlugin`, the top-level factory that produces the `PluginDescriptor` consumed by `@suaveplan/api-…
- [plugin](./src/plugin/plugin.md) — Exports `createDatabasePlugin`, the top-level factory that produces the `PluginDescriptor` consumed by `@suaveplan/api-…
- [pool](./src/pool/pool.md) — Implements `ConnectionPool`, a bounded pool of `SurrealAdapter` instances that backs the `db` service registered by `cr…
- [extended-adapter](./src/pool/extended-adapter.md) — Defines `PooledSurrealAdapter`, a thin subclass of `@suaveplan/surrealdb`'s `SurrealAdapter` that exposes the underlyin…
- [pool](./src/pool/pool.md) — Implements `ConnectionPool`, a bounded pool of `SurrealAdapter` instances that backs the `db` service registered by `cr…
- [retry](./src/retry/retry.md) — Provides `withRetry`, a small exponential-backoff helper used by the connection pool, the migration runner, and any cal…
- [retry](./src/retry/retry.md) — Provides `withRetry`, a small exponential-backoff helper used by the connection pool, the migration runner, and any cal…
- [routes](./src/routes/problem-json.md) — Renders arbitrary throwables as RFC 7807 `application/problem+json` bodies for HTTP handlers that wrap database calls.…
- [problem-json](./src/routes/problem-json.md) — Renders arbitrary throwables as RFC 7807 `application/problem+json` bodies for HTTP handlers that wrap database calls.…
- [services](./src/services/registry-augmentation.md) — Augments the `ServiceRegistry` interface from `@suaveplan/api-plugin` to register the `db` slot typed as `@suaveplan/db…
- [registry-augmentation](./src/services/registry-augmentation.md) — Augments the `ServiceRegistry` interface from `@suaveplan/api-plugin` to register the `db` slot typed as `@suaveplan/db…
- [tenancy](./src/tenancy/tenancy.md) — Exports the `provisionTenant` and `decommissionTenant` callables that downstream hosts (typically `@suaveplan/api-plugi…
- [tenancy](./src/tenancy/tenancy.md) — Exports the `provisionTenant` and `decommissionTenant` callables that downstream hosts (typically `@suaveplan/api-plugi…
- [types](./src/types/types.md) — Houses the internal type aliases and shared interfaces consumed by the rest of the package — the adapter factory seam,…
- [types](./src/types/types.md) — Houses the internal type aliases and shared interfaces consumed by the rest of the package — the adapter factory seam,…
<!-- end:auto-docs -->
## Install
```bash
bun add @suaveplan/api-plugin-database
```
## Quick start
```ts
import { createPluginRegistry } from "@suaveplan/api-plugin";
import { createDatabasePlugin } from "@suaveplan/api-plugin-database";
const registry = createPluginRegistry();
registry.register(
createDatabasePlugin({
url: "ws://localhost:8000/rpc",
namespace: "app",
database: "main",
username: process.env.SURREAL_USER ?? "root",
password: process.env.SURREAL_PASS ?? "root",
pool: { min: 2, max: 20, idleMs: 30_000, acquireTimeoutMs: 5_000 },
migrations: {
autoRun: true,
directory: "./surql",
ledgerTable: "_migrations",
lockTable: "_migration_lock",
leaseMs: 60_000,
},
})
);
await registry.initialize();
// Inside any other plugin
const db = ctx.getService("db");
const users = await db?.query("SELECT * FROM user");
```
## Modules
- [`plugin/plugin.md`](./src/plugin/plugin.md) — top-level factory `createDatabasePlugin`.
- [`pool/pool.md`](./src/pool/pool.md) — bounded `ConnectionPool` over `SurrealAdapter`.
- [`pool/extended-adapter.md`](./src/pool/extended-adapter.md) — `PooledSurrealAdapter` subclass surfacing `getRawClient()`.
- [`migrations/runner.md`](./src/migrations/runner.md) — `.surql` runner under advisory lock.
- [`migrations/lock.md`](./src/migrations/lock.md) — advisory-lock primitive.
- [`tenancy/tenancy.md`](./src/tenancy/tenancy.md) — `provisionTenant` / `decommissionTenant` callables.
- [`retry/retry.md`](./src/retry/retry.md) — exponential-backoff helper.
- [`routes/problem-json.md`](./src/routes/problem-json.md) — RFC 7807 renderer.
- [`config/config-schema.md`](./src/config/config-schema.md) — Zod-validated configuration boundary.
- [`error-codes/error-codes.md`](./src/error-codes/error-codes.md) — `DATABASE_*` stable error registry.
- [`otel/instruments.md`](./src/otel/instruments.md) — OpenTelemetry instrument bundle.
- [`services/registry-augmentation.md`](./src/services/registry-augmentation.md) — `ServiceRegistry["db"]` module augmentation.
## Tenancy
Three strategies are supported via `tenancy.strategy`:
- `shared` — single namespace + single database; rows carry a `tenant_id` discriminator.
- `namespace-per-tenant` — one SurrealDB namespace per tenant.
- `database-per-tenant` — one SurrealDB database per tenant inside the active namespace.
Tenant lifecycle is exposed as callable exports (`provisionTenant` / `decommissionTenant`) rather than descriptor hooks because the `PluginDescriptor` has no tenant-lifecycle slots. Wire them to your host's tenant events.
## Observability
Counters / histograms emitted under the `@suaveplan/api-plugin-database` meter:
- `db.pool.checkouts_total`
- `db.pool.checkins_total`
- `db.pool.saturation` (up-down-counter)
- `db.query.duration_ms` (histogram, `{ outcome }` label)
Migration metrics are emitted by `@suaveplan/api-migrations` under its own meter.
## Errors
Every failure path raises `DatabaseError` carrying one of eight `DATABASE_*` codes: `CONNECTION_FAILED`, `POOL_EXHAUSTED`, `POOL_CLOSED`, `QUERY_FAILED`, `MIGRATION_FAILED`, `LOCK_CONTENDED`, `TENANT_INVALID`, `INVALID_CONFIG`. See [`error-codes/error-codes.md`](./src/error-codes/error-codes.md).