# `main-side` _(no submodules)_ ## Purpose This module exposes `createDbProxyServer(port, db)` — the trusted-side endpoint of the worker DB proxy. It mounts an `AdminDatabaseLike` adapter on a `MessagePort` and turns every worker-issued RPC frame into a real call on the wrapped adapter. The main side is the only side that touches the underlying DB driver; the worker code that imported `@suaveplan/api-db-worker-proxy/worker-side` never sees a real connection, never sees plaintext credentials, and cannot fabricate a tenant identity. The server keeps a registry of live transaction handles and prepared statements keyed by id so the worker can drive them across multiple round-trips without leaking driver handles into the worker's address space. ## Features - Marshals `query`, `execute`, `prepare`, `transaction`, `cursor`, `engine`, and `close` over `@suaveplan/api-worker-rpc`. - Streams cursor rows as `stream-chunk` frames; worker-side break triggers `cancel` which the handler observes via its `AbortSignal`. - Bounds transaction lifetimes by port: closing the server (or receiving `goodbye`) raises `TRANSACTION_LOST` on every dangling handle rather than leaking driver state. - Exposes diagnostic counters `activeTransactionCount` and `activePreparedCount`. ## Usage: Basic ```ts import { createDbProxyServer } from "@suaveplan/api-db-worker-proxy/main-side"; import { Worker, MessageChannel } from "node:worker_threads"; const channel = new MessageChannel(); const server = createDbProxyServer(channel.port1, realAdapter); const worker = new Worker("./worker.js"); worker.postMessage({ port: channel.port2 }, [channel.port2]); ``` ## Usage: Advanced When the host needs to scope the adapter per request, pass an already-tenant-scoped adapter to a per-request server. The proxy itself does NOT rewrite SQL — that responsibility lives in the adapter the host hands in here. `wrapTenantScoped` on the worker side is purely a type-level facade. ```ts const scoped = realAdapter.scopedFor(tenantId); // host responsibility const server = createDbProxyServer(port, scoped); // shut down at request end: server.close("request finished"); ``` ## API Reference - `createDbProxyServer(port: PortLike, db: AdminDatabaseLike): DbProxyServer`. - `DbProxyServer.close(reason?: string): void` — closes the server, rolling back live transactions and finalising prepared statements. - `DbProxyServer.activeTransactionCount(): number`. - `DbProxyServer.activePreparedCount(): number`. ## Implementation Notes The cursor handler returns an `AsyncGenerator`; `serveWorkerRpc`'s streaming path interprets the return as `AsyncIterable` and emits one `stream-chunk` per yield, then `stream-end ok`. Cancellation from the worker comes in as an `AbortSignal.aborted = true` on the handler's `signal`; the generator throws `STREAM_INTERRUPTED` which the server marshals as `stream-end ok=false` carrying the `DB_PROXY.STREAM_INTERRUPTED` code. Transactions are implemented by holding the adapter's `transaction()` promise open until either `tx.commit` or `tx.rollback` fires from the worker. Rolling back is signalled by throwing inside the adapter callback, which mirrors the adapter's own rollback semantics without adding a parallel state machine.