5.7 KiB
@suaveplan/api-db-worker-proxy - Worker-side proxy for the api db capability. Marshals AdminDatabaseLike / TenantScopedDatabaseLike calls — including transactions and cursor streams — over @suaveplan/api-worker-rpc MessagePort frames. Tenant scoping is enforced main-side; this package is the transport.
Worker-side proxy for the api db capability. Marshals
AdminDatabaseLike and TenantScopedDatabaseLike calls — including
transactions and cursor streams — over @suaveplan/api-worker-rpc
MessagePort frames.
The main side mounts a real DB adapter and exposes it over a port; the worker side imports a proxy whose API surface is identical to a direct adapter. Worker plugin code never touches a real connection and never sees plaintext credentials.
Tenant scoping is enforced on the main side, NOT in this package.
wrapTenantScoped(adminProxy, tenantId) is purely a type-level facade
that prevents an admin reference from being passed where a tenant
reference is required by the type system.
Documentation tree
- error-codes — This module owns the
DB_PROXY.*stable error-code registry for@suaveplan/api-db-worker-proxy. Every failure that c…- error-codes — This module owns the
DB_PROXY.*stable error-code registry for@suaveplan/api-db-worker-proxy. Every failure that c…
- error-codes — This module owns the
- main-side — This module exposes
createDbProxyServer(port, db)— the trusted-side endpoint of the worker DB proxy. It mounts an `A…- main-side — This module exposes
createDbProxyServer(port, db)— the trusted-side endpoint of the worker DB proxy. It mounts an `A…
- main-side — This module exposes
- tenant-scoped — This module exposes
wrapTenantScoped(adminProxy, tenantId)— a worker-side type-level facade that re-presents an admi…- tenant-scoped — This module exposes
wrapTenantScoped(adminProxy, tenantId)— a worker-side type-level facade that re-presents an admi…
- tenant-scoped — This module exposes
- testing — This module provides the in-memory test harness for
@suaveplan/api-db-worker-proxy. It exposes `createInMemoryAdminDb…- testing — This module provides the in-memory test harness for
@suaveplan/api-db-worker-proxy. It exposes `createInMemoryAdminDb…
- testing — This module provides the in-memory test harness for
- worker-side — This module exposes
createDbProxyClient(port, { engine })— the untrusted-side endpoint of the worker DB proxy. It re…- worker-side — This module exposes
createDbProxyClient(port, { engine })— the untrusted-side endpoint of the worker DB proxy. It re…
- worker-side — This module exposes
Why
Per the api-plugin foundation design (§5.1, §5.2a, §6) every
capability ships in two surfaces: a direct in-process binding for
trusted plugins and a MessagePort-marshalled binding for
isolated / sandboxed plugins. The db capability is special because
- its operations return result sets that can be huge (favouring streaming over buffering),
- its operations are transactional (transaction handles MUST be bound to a single connection lifetime), and
- tenant-scoping is a security boundary the worker MUST NOT be able to circumvent.
This package is Stage-3 worker-isolation infrastructure: every
Stage-4 DB adapter (api-db-adapter, api-db-sqlite-memory, …) MUST
pass conformance under BOTH the direct surface AND this proxy
surface before can be declared complete.
Install
bun add @suaveplan/api-db-worker-proxy
Quick start
// main side
import { createDbProxyServer } from "@suaveplan/api-db-worker-proxy/main-side";
import { MessageChannel, Worker } from "node:worker_threads";
const channel = new MessageChannel();
createDbProxyServer(channel.port1, realAdminAdapter);
const worker = new Worker("./plugin-worker.js");
worker.postMessage({ port: channel.port2, engine: "sqlite" }, [channel.port2]);
// worker side
import { parentPort } from "node:worker_threads";
import { createDbProxyClient } from "@suaveplan/api-db-worker-proxy/worker-side";
import { wrapTenantScoped } from "@suaveplan/api-db-worker-proxy/tenant-scoped";
import type { TenantId } from "@suaveplan/api-types/tenant";
parentPort?.once("message", (boot) => {
const admin = createDbProxyClient(boot.port, { engine: boot.engine });
// request scope
const tenantScoped = wrapTenantScoped(admin, "acme" as TenantId);
void runHandler(tenantScoped);
});
Surface
./main-side—createDbProxyServer(port, db): serves anAdminDatabaseLikeover@suaveplan/api-worker-rpc. Returns aDbProxyServerwithclose(),activeTransactionCount(),activePreparedCount()../worker-side—createDbProxyClient(port, { engine }): returns anAdminDatabaseLike-shaped object pluscursor(sql, params)for streaming row iteration anddispose()for abrupt teardown../tenant-scoped—wrapTenantScoped(adminProxy, tenantId): type-level facade. Tenant scoping is enforced main-side../error-codes—DB_PROXY.*codes:UNSAFE_NOT_PERMITTED,TRANSACTION_LOST,STREAM_INTERRUPTED,PROXY_DISPOSED../testing—createInMemoryAdminDb+createDbProxyHarnessfor downstream conformance suites.