Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
# OpenSpec Change: enterprise-events-otel
- **Change ID**: enterprise-events-otel
- **Package**: `@suaveplan/events-otel`
- **Category**: universal
- **Target path**: `universal/events/events-otel`
- **Status**: PROPOSED
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/events-otel.md`
---
## Summary
Deliver `@suaveplan/events-otel` on the genesis branch from scratch at enterprise quality. The package is an opt-in OpenTelemetry tracing companion for `@suaveplan/events`: it wraps each dispatch in a span, propagates W3C Trace Context across synchronous and async subscriber invocations, records event-level attributes on each span, and correctly marks listener exceptions as span errors. Attaches via the public `@suaveplan/events` interceptor API. Sampling-aware: honours the parent sampling decision and supports rule-based head sampling.
---
## 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/error` (`workspace:^`) | Base class for `EventsOtelError`; `defineCodes` for `EVENTS_OTEL.*` codes |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock` injection interfaces |
| `zod` (`catalog:forms`) | Zod schemas for all public option types |
| `@opentelemetry/api` (`catalog:observability`) | `Tracer`, `Span`, `SpanStatusCode`, W3C context propagation |
| `tslib` (`catalog:build`) | Runtime helpers |
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check` (all `catalog:testing`).
---
## Public API surface
### Setup
```ts
// Attaches OTel tracing companion to an existing EventBus
function attachEventTracing(bus: EventBusLike, options?: EventTracingOptions): EventTracingHandle
interface EventTracingOptions {
tracer?: Tracer // default: global tracer from @opentelemetry/api
propagator?: TextMapPropagator // default: W3CTraceContextPropagator
eventFilter?: (name: string) => boolean
samplingRule?: SamplingRule
clock?: Clock
logger?: LoggerLike
}
interface EventTracingHandle {
detach(): void
}
```
### Span attribute convention
```ts
// All span attribute keys as stable constants
const SPAN_ATTRIBUTES = {
EVENT_NAME: "suaveplan.event.name",
EVENT_ID: "suaveplan.event.id",
EVENT_CATEGORY: "suaveplan.event.category",
LISTENER_ID: "suaveplan.listener.id",
BUS_ID: "suaveplan.bus.id",
} as const
// Span names
const SPAN_NAMES = {
DISPATCH: "events.dispatch",
LISTENER: "events.listener",
} as const
```
### Sampling rule
```ts
type SamplingRule =
| { type: "always" }
| { type: "never" }
| { type: "rate"; rate: number } // 0.0–1.0
| { type: "event-name"; names: string[] } // sample only named events
```
### Trace context envelope (for transport interop)
```ts
// Serialize current trace context into a plain object for transport
function serializeTraceContext(): Record<string, string>
// Restore trace context from a serialized envelope
function deserializeTraceContext(headers: Record<string, string>): Con```text
### Error handling
```ts
class EventsOtelError extends SuaveplanError<EventsOtelErrorCode>
type EventsOtelErrorCode =
| "EVENTS_OTEL_ATTACH_FAILED"
| "EVENTS_OTEL_ALREADY_ATTACHED"
| "EVENTS_OTEL_PROPAGATION_FAILED"
function registerEventsOtelErrorCodes(): void
```
### Zod schemas
```ts
const EventTracingOptionsSchema: ZodSchema
const SamplingRuleSchema: ZodSchema
```
---
## New additions for genesis
### 1. W3C Trace Context propagation across async subscribers
The interceptor captures the active `Context` at dispatch time. Each listener invocation runs with that context restored as the active context, so any nested OTel operations inside listeners are correctly parented to the dispatch span.
### 2. Listener exception → span error
If a listener throws, the dispatch span's status is set to `SpanStatusCode.ERROR` with the exception recorded via `span.recordException(err)`. The error is re-thrown after recording.
### 3. Sampling-aware dispatch
When `samplingRule` is `"rate"` or `"event-name"`, spans are only created for the matching subset. The companion checks the parent sampling decision first; if the parent is sampled, the rule is bypassed to maintain trace completeness.
### 4. `serializeTraceContext` / `deserializeTraceContext`
These helpers allow `@suaveplan/events-transport` to inject and extract trace context from transport envelopes, enabling cross-context trace propagation without a direct package dependency on OTel internals.
### 5. Property-based tests (fast-check)
- Parent-child relationship: every listener span is a child of its dispatch span
- Context propagation under reentrancy: nested dispatches inside listeners produce correct span trees
- Sampling rate accuracy: at sampling rate 0.5, sampled span count is within 5% of 50% over 10k dispatches
---
## File layout
```text
universal/events/events-otel/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
core/
attach-event-tracing/
attach-event-tracing.ts
attach-event-tracing.md
attach-event-tracing.types.ts
attach-event-tracing.test.ts
index.ts
propagation/
trace-context/
trace-context.ts
trace-context.md
trace-context.test.ts
index.ts
sampling/
sampling-rule/
sampling-rule.ts
sampling-rule.md
sampling-rule.types.ts
sampling-rule.test.ts
index.ts
tests/
integration.test.ts
property.test.ts
```
---
## Impact
**Downstream packages**: `@suaveplan/events-transport` uses `serializeTraceContext`/`deserializeTraceContext` for cross-context propagation. Any application using `@suaveplan/events` with production tracing can add this companion without code changes to publishers or subscribers.
**Workspace dep prerequisites**: `@suaveplan/events`, `@suaveplan/error`, `@suaveplan/types` must be published to Verdaccio.
**Breaking changes**: none (new package).

View File

@@ -0,0 +1,76 @@
# Tasks: enterprise-events-otel
- **Change ID**: enterprise-events-otel
- **Package**: `@suaveplan/events-otel`
- **Date**: 2026-04-24
## Prerequisites (must be published to Verdaccio first)
- [x] `@suaveplan/events`
- [x] `@suaveplan/error`
- [x] `@suaveplan/types`
## Tasks
### 1. Scaffold package
- [x] Create `universal/events/events-otel/` directory tree per file layout in `proposal.md`
- [x] Write `package.json` (name `@suaveplan/events-otel`, version `0.1.0`, `sideEffects: false`, correct workspace deps and `@opentelemetry/api` catalog dep)
- [x] Write `tsconfig.json` extending `@suaveplan/typescript-config/library.json`
- [x] Write `vitest.config.ts` using `createTestConfig()` from `@suaveplan/testing/config`
### 2. Error codes (`src/error-codes.ts`)
- [x] Define `EventsOtelErrorCode` union type
- [x] Call `defineCodes` to register all `EVENTS_OTEL.*` codes
- [x] Export `registerEventsOtelErrorCodes()` and `EventsOtelError`
### 3. Span attribute constants (`src/constants/index.ts`)
- [x] Export `SPAN_ATTRIBUTES` and `SPAN_NAMES` as `as const` objects
### 4. Sampling rule (`src/sampling/sampling-rule/`)
- [x] Implement `SamplingRule` type union and decision function
- [x] Write `.md`, `.types.ts`, `.test.ts`, `index.ts`
### 5. Trace context propagation (`src/propagation/trace-context/`)
- [x] Implement `serializeTraceContext()` using `@opentelemetry/api` W3C propagator
- [x] Implement `deserializeTraceContext(headers)` restoring context
- [x] Write `.md`, `.test.ts`, `index.ts`
### 6. Core `attachEventTracing` (`src/core/attach-event-tracing/`)
- [x] Implement `attachEventTracing(bus, options?)` via `@suaveplan/events` interceptor API only
- [x] Create `events.dispatch` span per dispatch; run listeners within that context
- [x] Create `events.listener` child span per listener invocation
- [x] Set `span.status = ERROR` and call `span.recordException(err)` on listener throw
- [x] Apply `SamplingRule` — check parent sampling decision first
- [x] Return `EventTracingHandle` with `detach()`
- [x] Write `.md`, `.types.ts`, `.test.ts`, `index.ts`
### 7. Tests
- [x] Unit tests co-located (100% coverage target)
- [x] `tests/integration.test.ts` — attach to real EventBus with in-memory OTel SDK, dispatch events, assert span tree structure and attributes
- [x] `tests/property.test.ts` — fast-check properties per `proposal.md` §5
### 8. Docs
- [x] Every `.ts` implementation file has a sibling `.md`
- [x] Root `README.md`
### 9. Pre-publish gate
- [x] `bunx turbo lint typecheck test --filter=@suaveplan/events-otel` green
- [x] `bunx turbo test:coverage --filter=@suaveplan/events-otel` ≥ 100% on touched modules
### 10. Publish
- [x] `bun scripts/publish.ts --package @suaveplan/events-otel`
### 11. Archive
- [x] Fold spec delta into `openspec/specs/events/spec.md`
- [x] `mv openspec/changes/enterprise-events-otel openspec/archive/2026-04-24-enterprise-events-otel`