first commit
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
# schema-registry
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Instance-based registry for reusable Zod schemas. Each schema is bound
|
||||
to a stable name and may be derived into related shapes via
|
||||
`.extend/.partial/.pick/.omit`. The registry is deliberately
|
||||
instance-only — there is no module-level singleton — so multi-tenant
|
||||
servers and parallel tests cannot accidentally share state. Calls into
|
||||
the derivation helpers raise typed `OpenAPIError`s when the base name
|
||||
is unknown or refers to a non-`ZodObject` schema, replacing upstream's
|
||||
silent `undefined` return.
|
||||
|
||||
## Features
|
||||
|
||||
- `register(name, schema, description?)` / `get(name)` / `has(name)` / `getDescription(name)`.
|
||||
- `getNames()`, `getAll()`, `remove(name)`, `clear()`.
|
||||
- `extend(baseName, name, extension)`, `partial(baseName, name)`,
|
||||
`pick(baseName, name, keys[])`, `omit(baseName, name, keys[])`.
|
||||
- `createSchemaRegistry()` factory — no global accessor.
|
||||
- `defineSchema(schema, meta, registry)` — registry argument is required.
|
||||
- Typed `OPENAPI.SCHEMA_NOT_FOUND` / `OPENAPI.INVALID_BASE_SCHEMA` errors from derivation helpers.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import z from "zod";
|
||||
import {
|
||||
createSchemaRegistry,
|
||||
defineSchema,
|
||||
} from "@suaveplan/api-openapi/schema-registry";
|
||||
|
||||
const registry = createSchemaRegistry();
|
||||
defineSchema(z.object({ id: z.string(), email: z.string() }), { name: "User" }, registry);
|
||||
const User = registry.get("User");
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import z from "zod";
|
||||
import {
|
||||
createSchemaRegistry,
|
||||
} from "@suaveplan/api-openapi/schema-registry";
|
||||
|
||||
const registry = createSchemaRegistry();
|
||||
registry.register(
|
||||
"User",
|
||||
z.object({
|
||||
id: z.string().uuid(),
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
}),
|
||||
"Internal user shape including hashed password"
|
||||
);
|
||||
|
||||
// Public shape — drop `password`.
|
||||
const UserPublic = registry.omit("User", "UserPublic", ["password"]);
|
||||
|
||||
// Patch shape — every field optional.
|
||||
const UserPatch = registry.partial("User", "UserPatch");
|
||||
|
||||
// Extended shape — add audit field.
|
||||
const UserAudit = registry.extend("User", "UserAudit", {
|
||||
lastSeenAt: z.string().datetime(),
|
||||
});
|
||||
|
||||
console.log(registry.getNames()); // ["User","UserPublic","UserPatch","UserAudit"]
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `class SchemaRegistry`
|
||||
- `register<T>(name, schema, description?): T`
|
||||
- `get<T>(name): T | undefined`
|
||||
- `has(name): boolean`
|
||||
- `getDescription(name): string | undefined`
|
||||
- `getNames(): string[]`
|
||||
- `getAll(): Map<string, z.ZodType>`
|
||||
- `remove(name): boolean`
|
||||
- `clear(): void`
|
||||
- `extend(baseName, name, extension): z.ZodObject` — throws `OPENAPI.SCHEMA_NOT_FOUND` or `OPENAPI.INVALID_BASE_SCHEMA`
|
||||
- `partial(baseName, name): z.ZodObject` — same throw semantics
|
||||
- `pick(baseName, name, keys[]): z.ZodObject` — same throw semantics
|
||||
- `omit(baseName, name, keys[]): z.ZodObject` — same throw semantics
|
||||
- `function createSchemaRegistry(): SchemaRegistry`
|
||||
- `function defineSchema<T>(schema: T, meta: {name, description?}, registry: SchemaRegistry): T`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`requireObjectBase` is the central guard. It throws
|
||||
`OPENAPI.SCHEMA_NOT_FOUND` when the name is unknown and
|
||||
`OPENAPI.INVALID_BASE_SCHEMA` when the registered schema is not a
|
||||
`ZodObject`. Both branches carry the base name in the error context so
|
||||
the call site can be diagnosed without a stack walk.
|
||||
|
||||
`pick` and `omit` take a `string[]` keys list rather than the Zod
|
||||
shape-mask object because OpenAPI consumers usually serialise key lists
|
||||
from configuration, not from compile-time literals. The shape mask is
|
||||
constructed internally to match Zod's expected input.
|
||||
|
||||
`defineSchema` requires the `registry` argument by position (third arg).
|
||||
Calling it without is a compile error. This is the deliberate behavioural
|
||||
divergence from upstream's `defineSchema(schema, meta)` which always
|
||||
wrote to a process-global registry — a footgun that turned every test
|
||||
file into a coupled neighbour.
|
||||
|
||||
The class uses `private readonly` Maps for `schemas` and `descriptions`.
|
||||
External mutation is impossible without going through `register` /
|
||||
`remove` / `clear`, which gives the registry well-defined ownership of
|
||||
both maps and lets `getAll()` safely return a fresh copy.
|
||||
Reference in New Issue
Block a user