Files
2026-05-27 23:29:53 +01:00

226 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenSpec Change: enterprise-events-schema
- **Change ID**: enterprise-events-schema
- **Package**: `@suaveplan/events-schema`
- **Category**: universal
- **Target path**: `universal/events/events-schema`
- **Status**: PROPOSED
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/events-schema.md`
---
## Summary
Deliver `@suaveplan/events-schema` on the genesis branch from scratch at enterprise quality. The package is a runtime schema-validation companion for `@suaveplan/events`: it validates event envelopes and payloads at publish and subscribe boundaries against Zod schemas registered in `@suaveplan/schema-registry`, surfaces structured rejections with redacted field values, and supports three validation modes (strict, warn, sample) for production rollout. Attaches via the public `@suaveplan/events` interceptor API.
---
## Current-state audit (genesis)
The package does not exist on genesis yet — fresh build. No prior source, no test results, no coverage data.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/events` (`workspace:^`) | Event bus and interceptor API |
| `@suaveplan/schema-registry` (`workspace:^`) | Schema registration, version history, compatibility checking |
| `@suaveplan/error` (`workspace:^`) | Base class for `EventsSchemaError`; `defineCodes` for `EVENTS_SCHEMA.*` codes |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock`, `Rng` injection interfaces |
| `zod` (`catalog:forms`) | Zod schemas for event envelope contract and option types |
| `@opentelemetry/api` (`catalog:observability`) | Rejection counters, validation latency histograms |
| `tslib` (`catalog:build`) | Runtime helpers |
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`).
---
## Public API surface
### Setup
```ts
// Attaches schema-validation companion to an existing EventBus
function attachEventSchemas(bus: EventBusLike, options?: EventSchemasOptions): EventSchemasHandle
interface EventSchemasOptions {
registry?: SchemaRegistry // default: global registry
mode?: ValidationMode // default: "strict"
sampleRate?: number // used when mode === "sample"; 0.0–1.0
onRejection?: RejectionHandler
redactSensitive?: boolean // default: true
clock?: Clock
rng?: Rng
logger?: LoggerLike
}
type ValidationMode = "strict" | "warn" | "sample"
type RejectionHandler = (rejection: ValidationRejection) => void
interface EventSchemasHandle {
detach(): void
registerSchema(eventName: string, schema: ZodSchema, version?: string): void
stats(): SchemaValidationStats
}
```
### Schema registration
```ts
// Register a Zod schema for an event name (standalone, outside attachEventSchemas)
function registerEventSchema(
registry: SchemaRegistry,
eventName: string,
schema: ZodSchema,
options?: SchemaRegistrationOptions
): void
interface SchemaRegistrationOptions {
version?: string // default: "1.0.0"
description?: string
compatibility?: "BACKWARD" | "FORWARD" | "FULL" | "NONE"
}
```
### Validation result
```ts
interface ValidationRejection {
eventName: string
eventId?: string
payload: unknown
errors: ValidationError[]
mode: ValidationMode
timestamp: Date
}
interface ValidationError {
path: string
message: string
expected?: string
received?: string // redacted if field is marked @sensitive
}
```
### Stats
```ts
interface SchemaValidationStats {
validationsTotal: Record<string, number> // keyed by event.name
rejectionsTotal: Record<string, number>
avgValidationMs: Record<string, number>
unknownSchemaTotal: number
}
```
### Error handling
```ts
class EventsSchemaError extends SuaveplanError<EventsSchemaErrorCode>
type EventsSchemaErrorCode =
| "EVENTS_SCHEMA_VALIDATION_FAILED"
| "EVENTS_SCHEMA_UNKNOWN"
| "EVENTS_SCHEMA_COMPATIBILITY_BREAK"
| "EVENTS_SCHEMA_ATTACH_FAILED"
function registerEventsSchemaErrorCodes(): void
```
### Zod schemas
```ts
const EventSchemasOptionsSchema: ZodSchema
const ValidationRejectionSchema: ZodSchema
const SchemaRegistrationOptionsSchema: ZodSchema
```
---
## New additions for genesis
### 1. Three validation modes
- `"strict"` — reject the dispatch and throw `EventsSchemaError` if validation fails
- `"warn"` — log the rejection via `LoggerLike` but let the event through; useful for canary rollouts
- `"sample"` — validate a random `sampleRate` fraction of events; the rest pass-through unchecked; useful for high-volume events where full validation is too expensive
### 2. Sensitive field redaction
When `redactSensitive: true`, field values that fail validation are replaced with `"[REDACTED]"` in the `ValidationError.received` output. Fields are marked sensitive via a Zod `.meta({ sensitive: true })` convention.
### 3. Schema compatibility CI hook
`EventSchemasHandle.registerSchema` calls `registry.checkCompatibility` under the hood. An incompatible schema registration throws `EventsSchemaError("EVENTS_SCHEMA_COMPATIBILITY_BREAK")` synchronously.
### 4. OTel instrumentation
- Counter: `events_schema.validations_total` (attribute: `event.name`, `validation.result: "pass"|"fail"`)
- Counter: `events_schema.unknown_schema_total`
- Histogram: `events_schema.validation_duration_ms` (attribute: `event.name`)
### 5. Property-based tests (fast-check)
- Schema round-trip: valid payloads generated by the schema always pass validation
- Rejection completeness: every field-level Zod error surfaces as a `ValidationError` entry
- Mode behaviour: `"warn"` mode never throws; `"strict"` always throws on invalid payloads; `"sample"` at 0.0 never validates
- Compatibility check: re-registering a schema with a removed required field always throws under `BACKWARD` compatibility
---
## File layout
```text
universal/events/events-schema/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
core/
attach-event-schemas/
attach-event-schemas.ts
attach-event-schemas.md
attach-event-schemas.types.ts
attach-event-schemas.test.ts
index.ts
schema/
register-event-schema/
register-event-schema.ts
register-event-schema.md
register-event-schema.test.ts
index.ts
validation/
validator/
validator.ts
validator.md
validator.types.ts
validator.test.ts
index.ts
redaction/
redaction.ts
redaction.md
redaction.test.ts
index.ts
tests/
integration.test.ts
property.test.ts
```
---
## Impact
**Downstream packages**: any event-driven application needing runtime contract enforcement without deploying a separate message broker schema registry. Works alongside `@suaveplan/events-otel` and `@suaveplan/events-metrics`.
**Workspace dep prerequisites**: `@suaveplan/events`, `@suaveplan/schema-registry`, `@suaveplan/error`, `@suaveplan/types` must be published to Verdaccio.
**Breaking changes**: none (new package).