first commit
This commit is contained in:
106
docs/api-stack/api/api-plugin-event-bus/src/schemas/options.md
Normal file
106
docs/api-stack/api/api-plugin-event-bus/src/schemas/options.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# `options.ts` — Zod schema for `EventBusPluginOptions`
|
||||
|
||||
## Purpose
|
||||
|
||||
Boundary Zod schema validated by `createEventBusPlugin(options)` at
|
||||
the factory entry point. Invalid input produces an
|
||||
`EventBusError(EVENT_BUS.INVALID_INPUT)` carrying the underlying
|
||||
`ZodError` as `cause` — callers always see a typed failure, never an
|
||||
`unknown` parse error from upstream code paths. Schema is `.strict()`
|
||||
so unknown keys fail loudly (typos in option names surface immediately
|
||||
rather than producing silent no-ops).
|
||||
|
||||
## Features
|
||||
|
||||
- Single schema (`EventBusPluginOptionsSchema`) covering the full
|
||||
option surface with sane defaults applied.
|
||||
- Sub-schemas exported for downstream reuse:
|
||||
`BackpressureOptionsSchema`, `IdempotencyOptionsSchema`.
|
||||
- Schema produces a fully-resolved
|
||||
`EventBusPluginOptionsResolved` type (defaults applied) suitable for
|
||||
passing as `PluginDescriptor.defaults`.
|
||||
- Strict mode prevents typo classes like `applySchema: true` (the
|
||||
wrong package's option) from silently being ignored.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { EventBusPluginOptionsSchema } from "@suaveplan/api-plugin-event-bus/schemas/options";
|
||||
|
||||
const resolved = EventBusPluginOptionsSchema.parse({});
|
||||
console.log(resolved.routePrefix); // "/event-bus"
|
||||
console.log(resolved.backpressure.policy); // "drop-oldest"
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
EventBusPluginOptionsSchema,
|
||||
type EventBusPluginOptionsInput,
|
||||
} from "@suaveplan/api-plugin-event-bus/schemas/options";
|
||||
|
||||
const input: EventBusPluginOptionsInput = {
|
||||
backpressure: { capacity: 32, policy: "throw" },
|
||||
idempotency: { enabled: true, cacheCapacity: 8192 },
|
||||
routePrefix: "/bus",
|
||||
mountRoutes: false,
|
||||
requireAuthForRoutes: false,
|
||||
tenantIsolation: false,
|
||||
interceptors: [
|
||||
async (env, next) => {
|
||||
console.log("emit", env.type);
|
||||
return next();
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const resolved = EventBusPluginOptionsSchema.parse(input);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `EventBusPluginOptionsSchema`
|
||||
|
||||
Strict object schema producing `EventBusPluginOptionsResolved`. Fields:
|
||||
|
||||
- `interceptors: Interceptor[]` (default `[]`).
|
||||
- `backpressure: { capacity: number; policy: QueuePolicy }`.
|
||||
- `idempotency: { enabled: boolean; cacheCapacity: number }`.
|
||||
- `routePrefix: string` (default `"/event-bus"`).
|
||||
- `mountRoutes: boolean` (default `true`).
|
||||
- `requireAuthForRoutes: boolean` (default `true`).
|
||||
- `tenantIsolation: boolean` (default `true`).
|
||||
- `maxTopicLength: number` (default `256`).
|
||||
- `logger?: LoggerLike` (optional override).
|
||||
|
||||
### `BackpressureOptionsSchema`
|
||||
|
||||
Stand-alone sub-schema. Exported so consumers building custom plugin
|
||||
factories can reuse the same shape.
|
||||
|
||||
### `IdempotencyOptionsSchema`
|
||||
|
||||
Stand-alone sub-schema.
|
||||
|
||||
### `type EventBusPluginOptionsInput`
|
||||
|
||||
`z.input` of the top-level schema — what callers pass in.
|
||||
|
||||
### `type EventBusPluginOptionsResolved`
|
||||
|
||||
`z.output` of the top-level schema — what the descriptor stores as
|
||||
`defaults` after parsing.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `interceptors` array uses `z.custom` (`typeof val === "function"`)
|
||||
rather than `z.function(...)` because Zod 4's `z.function` is
|
||||
builder-style and does not return a `ZodType` directly compatible
|
||||
with `z.array`.
|
||||
- The `routePrefix` regex matches `/auth`'s rule verbatim — start with
|
||||
`/`, no trailing `/` (the literal root `/` is allowed). Sharing the
|
||||
rule across the plugin family means routes mount predictably.
|
||||
- The schema is `.strict()` because the cost of catching typos at the
|
||||
factory boundary far outweighs the convenience of forward-compatible
|
||||
passthrough.
|
||||
@@ -0,0 +1,99 @@
|
||||
# `routes.ts` — Zod schemas for the HTTP route surface
|
||||
|
||||
## Purpose
|
||||
|
||||
Boundary Zod schemas validated by the three OpenAPI routes the plugin
|
||||
optionally mounts: `POST /event-bus/publish`, `GET /event-bus/subscriptions`,
|
||||
and `DELETE /event-bus/subscriptions/:id`. The schemas double as the
|
||||
canonical wire contract — the OpenAPI doc served by the host is
|
||||
generated from them, and downstream client codegen consumes them via
|
||||
the `./schemas/routes` subpath export.
|
||||
|
||||
## Features
|
||||
|
||||
- Request body schemas (`PublishBodySchema`) constrain topic syntax
|
||||
AND length.
|
||||
- Response schemas (`PublishResultSchema`, `SubscriptionListSchema`,
|
||||
`UnsubscribeResultSchema`) describe every field the routes return.
|
||||
- All schemas are exported alongside their inferred TypeScript types
|
||||
(`PublishBody`, `PublishResult`, …) so handlers receive narrow,
|
||||
static types.
|
||||
- Error-report sub-schema (`ErrorReportSchema`) preserves the listener
|
||||
failure surface across the wire.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { PublishBodySchema } from "@suaveplan/api-plugin-event-bus/schemas/routes";
|
||||
|
||||
const parsed = PublishBodySchema.parse({
|
||||
topic: "order.created",
|
||||
payload: { id: "o1" },
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
PublishBodySchema,
|
||||
PublishResultSchema,
|
||||
type PublishResult,
|
||||
} from "@suaveplan/api-plugin-event-bus/schemas/routes";
|
||||
|
||||
async function publishViaHttp(req: Request): Promise<PublishResult> {
|
||||
const body = PublishBodySchema.parse(await req.json());
|
||||
const res = await fetch("/event-bus/publish", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return PublishResultSchema.parse(await res.json());
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `PublishBodySchema`
|
||||
|
||||
Strict shape: `{ topic, payload?, idempotencyKey?, timeoutMs?, traceId? }`.
|
||||
|
||||
### `PublishResultSchema`
|
||||
|
||||
`{ delivered: number; dropped: boolean; errors: ErrorReport[] }`.
|
||||
|
||||
### `ErrorReportSchema`
|
||||
|
||||
`{ code: string; message: string }`. Surfaces listener exceptions —
|
||||
the upstream `EventsError.code` and `.message` are passed through.
|
||||
|
||||
### `SubscriptionRecordSchema`
|
||||
|
||||
`{ id, topic, tenantId?, createdAt, once, idempotent }`.
|
||||
|
||||
### `SubscriptionListSchema`
|
||||
|
||||
`{ subscriptions: SubscriptionRecord[] }`.
|
||||
|
||||
### `UnsubscribeParamsSchema`
|
||||
|
||||
`{ id }` — used as the OpenAPI `params` schema for the DELETE route.
|
||||
|
||||
### `UnsubscribeResultSchema`
|
||||
|
||||
`{ unsubscribed: boolean }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `payload` is `z.unknown()` because the bus is intentionally
|
||||
payload-agnostic. Publishers / subscribers Zod-validate at their own
|
||||
layers (or fail loudly on the deserialise) when a typed contract is
|
||||
required.
|
||||
- `idempotencyKey` is capped at 256 chars to avoid pathological keys
|
||||
starving the in-memory cache.
|
||||
- `timeoutMs` is capped at 60_000 — beyond that, callers should set up
|
||||
durable retries through their own scheduler rather than parking an
|
||||
HTTP connection.
|
||||
- The route schemas project the topic returned to the client back
|
||||
through `unscopeTopic` at the service layer so HTTP responses never
|
||||
leak the `tenant:<id>:` prefix.
|
||||
Reference in New Issue
Block a user