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,99 @@
# `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.