# `routes` ## Purpose Defines the Zod request/response schemas the five management routes mount when the plugin is configured with a `TenantStore`. Every body, query, and response shape flows through these schemas so the OpenAPI 3.1 document published at `/openapi.json` is in lock-step with the runtime behaviour — operators and SDKs generated from the spec see the same surface the runtime enforces. ## Features - `TenantResponseSchema` — wire-shape projection of the domain `Tenant` (Dates serialised as ISO strings). - `TenantListResponseSchema` — paginated list response (`items` + `total`). - `CreateTenantBodySchema` / `UpdateTenantBodySchema` — strict body schemas; unknown keys are rejected. - `TenantIdParamSchema` / `ListTenantsQuerySchema` — path-param and query schemas with coercion where appropriate (`limit`, `offset` are coerced from query string numerals). - Slug validation regex: lowercase alphanumeric + hyphen, starting with an alphanumeric. Keeps URLs predictable and prevents subtle case-folding bugs in path-prefix resolution. ## Usage: Basic Example ```ts import { CreateTenantBodySchema } from "@suaveplan/api-plugin-tenant"; const result = CreateTenantBodySchema.safeParse({ name: "Acme", slug: "acme", }); if (!result.success) throw new Error(result.error.message); ``` ## Usage: Advanced Example ```ts import { CreateTenantBodySchema, ListTenantsQuerySchema, TenantResponseSchema, } from "@suaveplan/api-plugin-tenant"; // Query coercion — limit/offset arrive as strings from the URL. const query = ListTenantsQuerySchema.parse({ status: "active", limit: "50", offset: "0", }); console.log(query.limit); // number, not string // Round-trip a Tenant through the response schema. const wire = TenantResponseSchema.parse({ id: "tenant_1", name: "Acme", slug: "acme", status: "active", settings: {}, enabledPlugins: ["billing"], domains: [], createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }); // Reject invalid slug at the boundary. const bad = CreateTenantBodySchema.safeParse({ name: "X", slug: "ACME!", // uppercase + punctuation }); console.assert(!bad.success); ``` ## API Reference - `TenantResponseSchema` — single-tenant wire shape. - `TenantListResponseSchema` — `{ items: TenantResponse[]; total: number }`. - `CreateTenantBodySchema` — strict object; `name`, `slug` required. - `UpdateTenantBodySchema` — strict object; all fields optional. - `TenantIdParamSchema` — `{ id: string }`. - `ListTenantsQuerySchema` — `{ status?, limit?, offset? }` with numeric coercion on `limit` and `offset`. - `DeleteTenantResponseSchema` — `{ success: true }`. - Type aliases (`CreateTenantBody`, `UpdateTenantBody`, etc.) are exported for handler signatures. ## Implementation Notes `strict()` is applied to `CreateTenantBodySchema`, `UpdateTenantBodySchema`, and the nested settings shape so unknown keys are rejected instead of silently dropped. This matters because the same shape round-trips through OpenAPI codegen on the client side — unknown keys on a strict schema imply the client has drifted, and the right behaviour is to fail loudly. The slug regex is intentionally conservative so URLs survive percent-encoding, CDN normalisation, and case-insensitive DNS lookups without ambiguity.