/** * Runtime-aware tenant middleware. * * The pre-existing {@link createTenantMiddleware} in this package is * built around a generic `TenantRequest` shape and an * AsyncLocalStorage-backed `TenantContext`. That works for the * runtime-agnostic usage (Fastify, generic Node servers) it was * originally designed for, but does **not** integrate with * `@venturekit/runtime`'s `RequestContext.tenant` field. * * This module fills that gap. It exposes a `Middleware` * compatible with `handler({ middleware: [...] })` that: * * 1. Calls a caller-provided async resolver with the live * `RequestContext` (so the resolver can read headers, query * params, JWT claims, and the raw API Gateway event). * 2. Populates `ctx.tenant` with the resolved tenant. * 3. 404s with {@link TenantNotFoundError} on miss — protecting * multi-tenant Lambdas from leaking the platform's existence to * hostnames that aren't configured. * * A common case — derive the tenant from a host like `api.` * with a `domains` table lookup — is built on top of this primitive * via {@link hostPrefixDomainResolver}. Apps that need different rules * (path prefix, JWT claim, header, …) plug their own resolver in. */ import type { Middleware, RequestContext, TenantContext } from '@venturekit/runtime'; export interface RuntimeTenantResolver { (ctx: RequestContext): Promise | TenantContext | null; } export interface RuntimeTenantMiddlewareOptions { /** * Function that produces the tenant for the current request. Return * `null` to signal "no tenant matched"; the middleware will throw * {@link TenantNotFoundError} (404) unless `optional: true`. * * The resolver receives the full `RequestContext` so it can read * `rawEvent.headers`, `queryParams`, `user`, etc. */ resolver: RuntimeTenantResolver; /** * If `true`, missing tenants pass through with `ctx.tenant = null` * instead of 404-ing. Use for routes that legitimately work * un-scoped (e.g. health checks, platform-level admin). * * Default: `false`. */ optional?: boolean; } /** * Build a `@venturekit/runtime`-compatible middleware that resolves * the current request's tenant and stashes it on `ctx.tenant`. * * Place this **before** any tenant-scoped logic in your middleware * stack. * * @example * ```ts * import { handler } from '@venturekit/runtime'; * import { * createRuntimeTenantMiddleware, * hostPrefixDomainResolver, * } from '@venturekit-pro/tenancy'; * * const tenancy = createRuntimeTenantMiddleware({ * resolver: hostPrefixDomainResolver({ * prefix: 'api.', * lookup: async (domain) => loadCommunityByDomain(domain), * }), * }); * * export const main = handler(async (_b, ctx) => ({ * tenant: ctx.tenant!.slug, * }), { middleware: [tenancy] }); * ``` */ export declare function createRuntimeTenantMiddleware(options: RuntimeTenantMiddlewareOptions): Middleware; export interface HostPrefixDomainResolverOptions { /** * Prefix to strip from the request's `Host` header before looking * up. Defaults to `'api.'` — so `api.insea.pro` resolves the tenant * whose domain is `insea.pro`. */ prefix?: string; /** * Caller-supplied DB lookup. Receives the post-strip hostname * (without port) and returns the tenant or `null`. */ lookup: (domain: string, ctx: RequestContext) => Promise | T | null; /** * Dev-only overrides honored when `process.env.VENTURE_LOCAL === 'true'` * — the marker `vk dev` injects and nothing deployed ever carries * (the same switch the repo's other dev-only surfaces key on). Lets * `vk dev` serve tenanted routes from `localhost:3001` by accepting an * explicit slug from a header or query param. * * Deliberately NOT keyed on a stage name: `dev` is a deployable stage, * and a deployed API that let any caller pick the tenant with a header * would be a cross-tenant read. (Historically this read `STAGE=dev`, * which no deploy path set — the override was dead locally and one * `STAGE=dev` env var away from live in the cloud.) * * Defaults: header `X-Tenant-Slug`, query `tenant`. Set to `false` * to disable. */ devOverrides?: false | { headerName?: string; queryName?: string; lookupBySlug: (slug: string, ctx: RequestContext) => Promise | T | null; }; } /** * Build a {@link RuntimeTenantResolver} that derives the tenant from * the request's `Host` header by stripping a prefix (default `api.`) * and looking the remainder up in a caller-supplied store. * * Encapsulates the "every API hostname is `api.`" * convention many multi-tenant VentureKit apps share, including the * dev-only `X-Tenant-Slug` / `?tenant=` overrides for `vk dev`. */ export declare function hostPrefixDomainResolver(options: HostPrefixDomainResolverOptions): RuntimeTenantResolver; //# sourceMappingURL=runtime-tenant-middleware.d.ts.map