first commit
This commit is contained in:
31
docs/networking/events/events-schema/README.md
Normal file
31
docs/networking/events/events-schema/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# @suaveplan/events-schema - Runtime schema-validation companion for @suaveplan/events: validates event payloads at dispatch boundaries via Zod schemas with strict/warn/sample modes and sensitive field redaction.
|
||||
|
||||
## Summary
|
||||
|
||||
Runtime schema-validation companion for `@suaveplan/events`. Validates event payloads at dispatch boundaries via Zod schemas with strict/warn/sample modes and sensitive field redaction.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [attach-event-schemas](./src/core/attach-event-schemas/attach-event-schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [attach-event-schemas](./src/core/attach-event-schemas/attach-event-schemas.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
<!-- end:auto-docs --> - [attach-event-schemas](./src/core/attach-event-schemas/attach-event-schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [attach-event-schemas](./src/core/attach-event-schemas/attach-event-schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [attach-event-schemas](./src/core/attach-event-schemas/attach-event-schemas.md) — <!-- begin:auto-docs --> _(no submodules)_- [attach-event-schemas](./src/core/attach-event-schemas/attach-event-schemas.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
## Quick Start
|
||||
|
||||
```ts
|
||||
import { EventBus } from "@suaveplan/events";
|
||||
import { attachEventSchemas } from "@suaveplan/events-schema";
|
||||
import z from "zod";
|
||||
|
||||
const bus = new EventBus();
|
||||
const handle = attachEventSchemas(bus, { mode: "strict" });
|
||||
handle.registerSchema("user.created", z.object({ id: z.string() }));
|
||||
|
||||
await bus.emitAsync("user.created", { id: "u-1" }); // passes
|
||||
await bus.emitAsync("user.created", { id: 123 }); // throws EventsSchemaError
|
||||
handle.detach();
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
- [`src/core/attach-event-schemas/attach-event-schemas`](src/core/attach-event-schemas/attach-event-schemas.md) — `attachEventSchemas`, `registerEventSchema`, `EventSchemasHandle`
|
||||
@@ -0,0 +1,97 @@
|
||||
# attach-event-schemas
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Attaches runtime schema-validation instrumentation to an `EventBus` via the public interceptor API from `@suaveplan/events`. On each dispatch, it validates the event payload against a Zod schema registered for that event name. Three validation modes provide flexibility across different deployment stages: `"strict"` rejects the dispatch with a structured error (suitable for CI and development), `"warn"` logs the rejection but lets the event through (suitable for canary rollout), and `"sample"` validates a configurable fraction of events (suitable for high-throughput production paths).
|
||||
|
||||
Validation statistics are tracked in-process and available without an OTel exporter via `EventSchemasHandle.stats()`. OTel counters and histograms are also emitted for dashboard visibility.
|
||||
|
||||
## Features
|
||||
|
||||
- `attachEventSchemas(bus, options?)` — installs a schema-validation interceptor and returns `EventSchemasHandle`
|
||||
- Three validation modes: `strict`, `warn`, `sample`
|
||||
- `EventSchemasHandle.registerSchema(eventName, schema, version?)` — registers/updates a Zod schema
|
||||
- `EventSchemasHandle.stats()` — returns in-process validation statistics
|
||||
- Sensitive field redaction in rejection output (opt-in via `redactSensitive: true`)
|
||||
- OTel counters: `events_schema.validations_total`, `events_schema.validations_failed_total`, `events_schema.unknown_schema_total`
|
||||
- OTel histogram: `events_schema.validation_duration_ms`
|
||||
- `registerEventSchema(registry, eventName, schema, options?)` — standalone registration helper
|
||||
- Injectable `registry`, `clock`, `rng`, `logger` for testability
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { EventBus } from "@suaveplan/events";
|
||||
import { attachEventSchemas } from "@suaveplan/events-schema";
|
||||
import z from "zod";
|
||||
|
||||
const bus = new EventBus<{ "user.created": { id: string } }>();
|
||||
const handle = attachEventSchemas(bus);
|
||||
|
||||
handle.registerSchema("user.created", z.object({ id: z.string() }));
|
||||
|
||||
bus.on("user.created", (p) => console.log("created", p.id));
|
||||
await bus.emitAsync("user.created", { id: "u-1" }); // passes
|
||||
|
||||
handle.detach();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { EventBus } from "@suaveplan/events";
|
||||
import { attachEventSchemas, registerEventSchema } from "@suaveplan/events-schema";
|
||||
import { SchemaRegistry } from "@suaveplan/schema-registry";
|
||||
import z from "zod";
|
||||
|
||||
const registry = new SchemaRegistry();
|
||||
const bus = new EventBus();
|
||||
|
||||
const handle = attachEventSchemas(bus, {
|
||||
registry,
|
||||
mode: "warn",
|
||||
onRejection: (r) => console.warn("REJECTED", r.eventName, r.errors),
|
||||
redactSensitive: true,
|
||||
});
|
||||
|
||||
handle.registerSchema("order.placed", z.object({
|
||||
orderId: z.string(),
|
||||
amount: z.number().positive(),
|
||||
}));
|
||||
|
||||
await bus.emitAsync("order.placed", { orderId: "o-1", amount: -5 });
|
||||
// Logs a warning but does NOT throw (warn mode)
|
||||
|
||||
console.log(handle.stats().rejectionsTotal["order.placed"]); // 1
|
||||
handle.detach();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `attachEventSchemas<TMap>(bus, options?): EventSchemasHandle`
|
||||
|
||||
| Param | Type | Description |
|
||||
|---|---|---|
|
||||
| `bus` | `EventBus<TMap>` | The event bus to instrument |
|
||||
| `options` | `EventSchemasOptions?` | Registry, mode, sampleRate, onRejection, redact, clock, rng, logger |
|
||||
|
||||
**Returns**: `EventSchemasHandle`
|
||||
|
||||
### `registerEventSchema(registry, eventName, schema, options?): void`
|
||||
|
||||
Standalone helper for registering schemas in a `SchemaRegistry` before attaching.
|
||||
|
||||
### `EventSchemasHandle`
|
||||
|
||||
| Method | Returns | Description |
|
||||
|---|---|---|
|
||||
| `detach()` | `void` | Remove the interceptor |
|
||||
| `registerSchema(eventName, schema, version?)` | `void` | Register a Zod schema |
|
||||
| `stats()` | `SchemaValidationStats` | In-process statistics |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Schemas registered via `handle.registerSchema` are stored in an in-memory `Map` in addition to the registry. This avoids the registry's compatibility enforcement on the first registration (which would fail with `SchemaRegistryError` for new subjects). Subsequent registrations via `registerSchema` go through the registry's compatibility check. The `sample` mode evaluates the random gate before schema lookup to avoid the lookup cost for unsampled dispatches. The `rng.next()` call uses the injectable `Rng` interface from `@suaveplan/types` for deterministic testing.
|
||||
Reference in New Issue
Block a user