Files
suaveplan/openspec/archive/2026-04-24-enterprise-events-schema/proposal.md
2026-05-27 23:29:53 +01:00

7.0 KiB
Raw Blame History

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 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

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).