# storage _(no submodules)_ ## Purpose The `storage` module ships the opt-in `AsyncLocalStorage` thread for the resolved tenant. defaults the thread OFF — the canonical access path is `c.get("tenant")` — and exposes `withTenantStorage(true)` plus the matching `tenantStorageMiddleware()` for applications that legitimately need to read the tenant from deep async code without threading the Hono `Context` through every function signature. This eliminates the upstream dual-access-path footgun where `c.get("tenant")` and `getCurrentTenant()` could disagree. With ALS off, `getCurrentTenant()` always returns `undefined`, so any code that "accidentally" relied on the thread surfaces immediately rather than silently picking up stale state from a previous request. ## Features - Opt-in via `withTenantStorage(true)` — defaults to `false`. - `tenantStorageMiddleware()` — pass-through when ALS off, wraps `next()` in `tenantStorage.run(tenant, ...)` when ALS on. - `getCurrentTenant()` / `getCurrentTenantId()` — read the threaded value, `undefined` when off or outside a scope. - `runWithTenant(tenant, fn)` — explicit scope helper for background jobs spawned outside a request lifecycle. - `isTenantStorageEnabled()` — inspect the toggle (primarily for tests). - `node:async_hooks` import is bare so bundlers route it to the Node runtime; safe on Bun (which provides a compatible implementation). ## Usage: Basic ```ts import { createTenantMiddleware, headerResolver, tenantStorageMiddleware, withTenantStorage, getCurrentTenant, } from "@suaveplan/api-tenant"; withTenantStorage(true); app.use( "*", createTenantMiddleware({ resolver: headerResolver("x-tenant-id", lookup), }) ); app.use("*", tenantStorageMiddleware()); async function deepCall(): Promise { return getCurrentTenant()?.id ?? "anonymous"; } ``` ## Usage: Advanced Background job that spawns from a queue worker and needs the tenant context inside an inner async boundary: ```ts import { runWithTenant, withTenantStorage } from "@suaveplan/api-tenant"; withTenantStorage(true); async function processJob(job: { tenant: Tenant; payload: unknown }) { return runWithTenant(job.tenant, async () => { await runUserDefinedHandler(job.payload); }); } ``` ## API Reference - `withTenantStorage(enabled: boolean): void` — toggle. - `tenantStorageMiddleware(): MiddlewareHandler`. - `getCurrentTenant(): Tenant | undefined`. - `getCurrentTenantId(): string | undefined`. - `runWithTenant(tenant: Tenant, fn: () => T): T`. - `isTenantStorageEnabled(): boolean`. ## Implementation Notes The toggle is a module-level boolean rather than a per-instance flag because `AsyncLocalStorage` itself is a process-wide construct — two parallel `enabled=true` regions would conflict regardless of how the flag is stored. The middleware short-circuits in three places (ALS disabled, no tenant resolved, or non-Hono caller) so the cost when disabled is two cheap reads plus the call to `next()`. The `runWithTenant` helper is the one piece that runs `fn` synchronously when ALS is off, keeping the call signature stable across both modes.