3.9 KiB
plugin
(no submodules)
Purpose
Exports createDatabasePlugin, the top-level factory that produces the PluginDescriptor consumed by @suaveplan/api-plugin's PluginRegistry. The factory composes every other module in the package — DatabasePluginOptionsSchema validates options at the boundary, ConnectionPool owns lifecycle and concurrency control, runMigrations handles ledger-backed .surql application under an advisory lock, the db service slot is augmented onto ServiceRegistry, and the OTel instrument bundle is shared with the pool. This file is the single integration point — every consumer of the package builds its descriptor through this one function.
The descriptor binds three lifecycle hooks. onInit constructs the pool, eagerly acquires the primary adapter, and registers it under ServiceRegistry["db"]. onReady runs migrations under advisory lock when migrations.autoRun is true. onClose drains the pool. Construction-time Zod validation surfaces invalid options as DatabaseError(DATABASE.INVALID_CONFIG) so misconfigurations fail at registry intake rather than during the first request.
Features
- Zod-validated options at construction time; invalid input surfaces
DATABASE.INVALID_CONFIG. - Three lifecycle hooks wired against a single
PluginRuntimecell — no cross-hook globals. - Injectable
adapterFactoryso tests run against a fake adapter without touching surrealdb. - Auto-registers the canonical
dbservice slot via module augmentation. - Pool-acquired primary adapter exposed as
db— every other plugin sees a fully connectedDatabaseAdapter.
Usage: Basic
import { createDatabasePlugin } from "@suaveplan/api-plugin-database/plugin/plugin";
import { createPluginRegistry } from "@suaveplan/api-plugin";
const registry = createPluginRegistry();
registry.register(
createDatabasePlugin({ url: "ws://localhost:8000/rpc" })
);
await registry.initialize();
Usage: Advanced
import { createDatabasePlugin } from "@suaveplan/api-plugin-database";
import { PooledSurrealAdapter } from "@suaveplan/api-plugin-database";
const plugin = createDatabasePlugin({
url: process.env.SURREAL_URL,
namespace: "prod",
database: "main",
username: process.env.SURREAL_USER,
password: process.env.SURREAL_PASS,
pool: { min: 2, max: 50, idleMs: 60_000, acquireTimeoutMs: 5_000 },
retry: { maxAttempts: 5, baseBackoffMs: 100, maxBackoffMs: 10_000, jitter: true },
tenancy: { strategy: "namespace-per-tenant" },
migrations: {
autoRun: true,
directory: "./surql",
ledgerTable: "_migrations",
lockTable: "_migration_lock",
leaseMs: 60_000,
},
adapterFactory: (cfg) => new PooledSurrealAdapter({ ...cfg }),
});
API Reference
createDatabasePlugin(options?): PluginDescriptor<DatabasePluginConfigParsed>
Returns the plugin descriptor consumed by PluginRegistry.register. Throws DatabaseError(DATABASE.INVALID_CONFIG) synchronously if options fail Zod validation.
Implementation Notes
- The factory keeps the pool, the primary adapter, and the runtime cell inside a closure so multiple
createDatabasePlugincalls produce isolated runtimes. There is no module-level global state. onReadycallsgetRawClient()on the primary adapter to obtain the rawSurrealClientLikethe migration runner needs. Because the primary adapter is acquired inonInitand held untilonClose, the raw client is always live by the timeonReadyruns.onClosereleases the primary adapter back to the pool before callingclose()so the pool's drain phase isn't fighting aninUsemember. The release is best-effort —pool.release(adapter)silently ignores unknown adapters, so multiple shutdown attempts are safe.- The descriptor declares zero
dependencies— consumers that need the database service register this plugin first.