Initial Commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user