7.0 KiB
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
// 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
// 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
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
interface SchemaValidationStats {
validationsTotal: Record<string, number> // keyed by event.name
rejectionsTotal: Record<string, number>
avgValidationMs: Record<string, number>
unknownSchemaTotal: number
}
Error handling
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
const EventSchemasOptionsSchema: ZodSchema
const ValidationRejectionSchema: ZodSchema
const SchemaRegistrationOptionsSchema: ZodSchema
New additions for genesis
1. Three validation modes
"strict"— reject the dispatch and throwEventsSchemaErrorif validation fails"warn"— log the rejection viaLoggerLikebut let the event through; useful for canary rollouts"sample"— validate a randomsampleRatefraction 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
ValidationErrorentry - 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
BACKWARDcompatibility
File layout
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).