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,86 @@
# `tenant`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides `buildTenantPluginMiddleware`, the Hono middleware factory
the plugin installs on `ctx.app` during `onInit`. The factory composes
`createTenantMiddleware` from `@suaveplan/api-tenant` (the canonical
resolver + status-gate implementation) with an outer wrapper that
records OTel resolution timing and outcome for every request. The
result is a single middleware that handles per-request tenant
resolution, fail-closed behaviour, and observability.
## Features
- Delegates the core resolution + status-gate (`deleted` /
`suspended` / `pending`) to the well-tested
`@suaveplan/api-tenant` middleware.
- Wraps every invocation with `performance.now()` timing so the OTel
histogram captures real latency, including the inner middleware's
work.
- Records `tenant_plugin.resolutions_total{outcome, strategy}` with
outcomes `resolved`, `missing`, `error` so dashboards can break
down failures by strategy.
- Returns the inner middleware's response verbatim — no shape changes
— so error handlers see the same `TenantError` they would see if
they called `createTenantMiddleware` directly.
## Usage: Basic Example
```ts
import { buildTenantPluginMiddleware } from "@suaveplan/api-plugin-tenant";
import { headerResolver } from "@suaveplan/api-tenant";
import { Hono } from "hono";
const resolver = headerResolver("x-tenant-id", lookupFn);
const app = new Hono();
app.use("*", buildTenantPluginMiddleware({
resolver,
required: false,
allowSuspended: false,
}));
```
## Usage: Advanced Example
```ts
import { buildTenantPluginMiddleware } from "@suaveplan/api-plugin-tenant";
import { chainResolvers, headerResolver, subdomainResolver, TenantCache } from "@suaveplan/api-tenant";
const resolver = TenantCache.wrap(
chainResolvers(
headerResolver("x-tenant-id", store.byId),
subdomainResolver("app.example.com", store.bySlug)
),
{ ttlMs: 60_000, negativeTtlMs: 5_000, maxEntries: 10_000 }
);
app.use("*", buildTenantPluginMiddleware({
resolver,
required: true,
allowSuspended: false,
excludePaths: ["/health", "/ready", "/openapi.json", "/public"],
}));
```
## API Reference
- `interface BuildTenantPluginMiddlewareOptions` — `{ resolver,
required, allowSuspended, excludePaths? }`.
- `function buildTenantPluginMiddleware(options): MiddlewareHandler` —
returns a Hono `MiddlewareHandler` ready for `app.use("*", ...)`.
## Implementation Notes
Timing is recorded for every code path including the rethrow path so
the histogram captures slow failures (network errors in the store,
slow JWT verifications, etc.). The outer wrapper does not call
`next()` directly — that responsibility stays inside
`createTenantMiddleware`, which also owns the short-circuit for
`excludePaths`. Splitting responsibilities this way means a future
upgrade of `@suaveplan/api-tenant` (e.g. adding sampling or distributed
tracing) lands automatically without changes to the plugin.