first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
# common-schemas
## Purpose
Library of common Zod schemas decorated with OpenAPI `example` metadata.
Covers the universal HTTP query / path / pagination primitives that
every API plugin reaches for — UUID, email, URL, ISO datetime, signed
and unsigned integers, page/limit/sort order. Pulling these into a
single module ensures every plugin documents the same constraints,
prevents drift, and makes the OpenAPI spec consistent across the entire
multi-plugin server.
The schemas are exported as a single `CommonSchemas` const object so
consumers can spread, destructure, or `.openapi(...)`-extend any subset.
They are deliberately not pre-registered against any `SchemaRegistry` —
each consumer chooses whether (and under which name) to register them.
## Features
- `uuid`, `email`, `url`, `datetime` — string formats with examples.
- `positiveInt`, `nonNegativeInt` — integer constraints.
- `page` / `limit` — coercing pagination inputs with sane defaults.
- `sortOrder` — `"asc" | "desc"` enum, default `"desc"`.
- Every entry carries an OpenAPI `example` so the generated spec is rich.
## Usage: Basic
```ts
import z from "zod";
import { CommonSchemas } from "@suaveplan/api-openapi/schema-registry";
const ListQuery = z.object({
page: CommonSchemas.page,
limit: CommonSchemas.limit,
sort: CommonSchemas.sortOrder,
});
```
## Usage: Advanced
```ts
import z from "zod";
import {
CommonSchemas,
createSchemaRegistry,
} from "@suaveplan/api-openapi/schema-registry";
const registry = createSchemaRegistry();
// Register pagination primitives once under shared names so OpenAPI emits
// `#/components/schemas/pagination.page` references rather than inlining
// the same constraint in every operation.
registry.register("pagination.page", CommonSchemas.page);
registry.register("pagination.limit", CommonSchemas.limit);
registry.register("pagination.sortOrder", CommonSchemas.sortOrder);
const ListUsersQuery = z.object({
page: registry.get("pagination.page")!,
limit: registry.get("pagination.limit")!,
sort: registry.get("pagination.sortOrder")!,
q: z.string().optional(),
});
```
## API Reference
- `CommonSchemas.uuid: ZodString` — UUID string with example.
- `CommonSchemas.email: ZodString` — email string.
- `CommonSchemas.url: ZodString` — URL string.
- `CommonSchemas.datetime: ZodString` — ISO-8601 datetime.
- `CommonSchemas.positiveInt: ZodNumber` — `> 0`, integer.
- `CommonSchemas.nonNegativeInt: ZodNumber` — `≥ 0`, integer.
- `CommonSchemas.page: ZodCoerce<number>` — `min 1`, default `1`.
- `CommonSchemas.limit: ZodCoerce<number>` — `min 1, max 100`, default `20`.
- `CommonSchemas.sortOrder: ZodEnum<"asc" | "desc">` — default `"desc"`.
## Implementation Notes
`page` and `limit` use `z.coerce.number()` rather than `z.number()` so
they accept query-string inputs (always strings on the wire). Without
coercion every plugin would have to wrap them in `.transform(Number)`
manually, defeating the point of a shared catalogue.
The `.openapi({ example: ... })` annotation surfaces in the generated
spec without affecting validation. Consumers that want richer
descriptions (`{ description: "..." }`) call `.openapi(...)` on a copy
rather than mutating the shared schema.
`sortOrder` defaults to `"desc"` because the most common pagination
pattern is "latest first". Plugins that want a different default override
locally with `CommonSchemas.sortOrder.default("asc")`.
`CommonSchemas` is `as const` — TypeScript surfaces every field with its
narrowest literal type. Consumers can `typeof CommonSchemas` to derive
per-field types without importing each schema individually.