Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
# storage
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<string> {
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<T>(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.