# `tenant-scoped` _(no submodules)_ ## Purpose This module exposes `wrapTenantScoped(adminProxy, tenantId)` — a worker-side type-level facade that re-presents an admin-surface `AdminDatabaseLike` as a `TenantScopedDatabaseLike`. The facade matters because the two surface types are deliberately non-assignable in `@suaveplan/api-types`: a request handler declared to accept `TenantScopedDatabaseLike` MUST NOT accidentally receive an unscoped admin proxy, and vice versa. **Tenant scoping is enforced on the main side.** The actual SQL rewrite / row-level security / connection routing all live in the host's adapter. This wrapper does NOT add policy; it asserts a shape so the type system can guarantee the slot in the request context has been narrowed to a tenant. ## Features - Returns a fresh facade object on every call so the same admin proxy can back many concurrent request scopes. - Exposes `tenantId` on the returned object for audit logging. - Preserves the optional `prepare` / `transaction` capabilities only if the admin proxy advertised them; absent capabilities throw `DB_PROXY.UNSAFE_NOT_PERMITTED`. - `close` is intentionally omitted because `TenantScopedDatabaseLike` does not expose it — the admin proxy retains lifecycle ownership. ## Usage: Basic ```ts import { wrapTenantScoped } from "@suaveplan/api-db-worker-proxy/tenant-scoped"; import type { TenantId } from "@suaveplan/api-types/tenant"; const tenantId = "acme" as TenantId; const scoped = wrapTenantScoped(adminProxy, tenantId); const rows = await scoped.query("SELECT * FROM invoices"); ``` ## Usage: Advanced Use the facade inside a request middleware that materialises `RequestContext.db`. The middleware constructs the wrapper from the already-resolved `tenantId` so route handlers only ever see the tenant-scoped type. ```ts function attachDb(adminProxy: AdminDatabaseLike) { return async (c: Context, next: Next): Promise => { const tenantId = c.get("tenantId") as TenantId; const scoped = wrapTenantScoped(adminProxy, tenantId); c.set("db", scoped); await next(); }; } ``` ## API Reference - `wrapTenantScoped(adminProxy: AdminDatabaseLike, tenantId: TenantId): TenantScopedDatabaseLike`. - Throws `DB_PROXY.UNSAFE_NOT_PERMITTED` from `prepare` / `transaction` when the underlying admin proxy doesn't expose that capability. ## Implementation Notes The wrapper rebinds `prepare` and `transaction` so the underlying admin proxy's `this`-context survives the indirection (the methods are looked up off the proxy each call, not captured eagerly, so admin-side disposal is observed on the next call rather than at wrap time). The `tenantId` field is set on the returned object purely so audit loggers and `RequestContext` consumers can read it directly. The field name and type match `TenantScopedDatabaseLike.tenantId` exactly; both are `TenantId` (a branded string).