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,186 @@
# OpenSpec Change: enterprise-events-metrics
- **Change ID**: enterprise-events-metrics
- **Package**: `@suaveplan/events-metrics`
- **Category**: universal
- **Target path**: `universal/events/events-metrics`
- **Status**: PROPOSED
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/events-metrics.md`
---
## Summary
Deliver `@suaveplan/events-metrics` on the genesis branch from scratch at enterprise quality. The package is an opt-in metrics companion for `@suaveplan/events`: it attaches to the event bus via the public interceptor/companion API (never monkey-patching), records dispatch totals, listener error counts, dispatch latency histograms, queue depth gauges, and active subscriber counts. All metric names follow the `suaveplan.events.*` convention with stable attribute keys. Sub-microsecond hot-path overhead is a hard requirement.
---
## 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/opentelemetry` (`workspace:^`) | `MeterProvider` factory, counter/histogram/gauge helpers |
| `@suaveplan/error` (`workspace:^`) | Base class for `EventsMetricsError`; `defineCodes` for `EVENTS_METRICS.*` codes |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock` injection interfaces |
| `zod` (`catalog:forms`) | Zod schemas for all public option types |
| `@opentelemetry/api` (`catalog:observability`) | `Meter`, `Counter`, `Histogram`, `ObservableGauge` |
| `tslib` (`catalog:build`) | Runtime helpers |
Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata` (all `catalog:testing`).
---
## Public API surface
### Setup
```ts
// Attaches metrics companion to an existing EventBus
function attachEventMetrics(bus: EventBusLike, options?: EventMetricsOptions): EventMetricsHandle
interface EventMetricsOptions {
meter?: Meter // default: global meter from @opentelemetry/api
eventFilter?: (name: string) => boolean // optional: record only matching events
clock?: Clock
logger?: LoggerLike
}
interface EventMetricsHandle {
detach(): void
snapshot(): MetricsSnapshot
}
```
### Metric definitions
```ts
// All metric names and attribute keys as stable constants
const METRIC_NAMES = {
DISPATCH_TOTAL: "suaveplan.events.dispatch.total",
LISTENER_ERROR_TOTAL: "suaveplan.events.listener.error.total",
DISPATCH_LATENCY_MS: "suaveplan.events.dispatch.latency_ms",
QUEUE_DEPTH: "suaveplan.events.queue.depth",
ACTIVE_SUBSCRIBERS: "suaveplan.events.subscribers.active",
} as const
const ATTRIBUTE_KEYS = {
EVENT_NAME: "event.name",
EVENT_CATEGORY: "event.category",
LISTENER_ID: "listener.id",
BUS_ID: "bus.id",
} as const
```
### Snapshot
```ts
interface MetricsSnapshot {
dispatchTotal: Record<string, number> // keyed by event.name
listenerErrorTotal: Record<string, number>
avgDispatchLatencyMs: Record<string, number>
queueDepth: number
activeSubscribers: number
}
```
### Error handling
```ts
class EventsMetricsError extends SuaveplanError<EventsMetricsErrorCode>
type EventsMetricsErrorCode =
| "EVENTS_METRICS_ATTACH_FAILED"
| "EVENTS_METRICS_ALREADY_ATTACHED"
| "EVENTS_METRICS_DETACHED"
function registerEventsMetricsErrorCodes(): void
```
### Zod schemas
```ts
const EventMetricsOptionsSchema: ZodSchema
```
---
## New additions for genesis
### 1. Companion-API only
`attachEventMetrics` uses only the public `EventBus` interceptor/companion surface from `@suaveplan/events`. No access to private fields, no prototype patching. If the bus is replaced or re-created, callers must call `detach()` and `attachEventMetrics` again.
### 2. Low-overhead hot path
The dispatch interceptor is a thin closure: one array lookup for the counter and one `performance.now()` call for the histogram. Allocation on the critical path is zero when the event name was previously seen (name-to-attributes map is pre-cached).
### 3. Observable gauge for queue depth and subscribers
Queue depth and active subscriber count are reported as OTel `ObservableGauge` (polled at collection time), not recorded on every dispatch, to avoid per-dispatch overhead.
### 4. Property-based tests (fast-check)
- Counter monotonicity: `dispatchTotal[name]` never decreases after more dispatches
- Error counter accuracy: `listenerErrorTotal` equals the number of listener throws injected
- Histogram latency: average latency is within 1ms of a synthetic injected delay
### 5. Benchmark suite (`src/__benchmarks__/`)
- Overhead of `attachEventMetrics` on dispatch hot path: compare instrumented vs uninstrumented bus at 1M events/sec
- Snapshot retrieval latency at 1k distinct event names
---
## File layout
```text
universal/events/events-metrics/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
core/
attach-event-metrics/
attach-event-metrics.ts
attach-event-metrics.md
attach-event-metrics.types.ts
attach-event-metrics.test.ts
index.ts
metrics/
metric-names/
metric-names.ts
metric-names.md
index.ts
snapshot/
snapshot.ts
snapshot.md
snapshot.test.ts
index.ts
__benchmarks__/
events-metrics.bench.ts
tests/
integration.test.ts
property.test.ts
```
---
## Impact
**Downstream packages**: any application wiring `@suaveplan/events` that needs production observability without code changes to event publishers or subscribers.
**Workspace dep prerequisites**: `@suaveplan/events`, `@suaveplan/opentelemetry`, `@suaveplan/error`, `@suaveplan/types` must be published to Verdaccio.
**Breaking changes**: none (new package).

View File

@@ -0,0 +1,78 @@
# Tasks: enterprise-events-metrics
- **Change ID**: enterprise-events-metrics
- **Package**: `@suaveplan/events-metrics`
- **Date**: 2026-04-24
## Prerequisites (must be published to Verdaccio first)
- [x] `@suaveplan/events`
- [x] `@suaveplan/opentelemetry`
- [x] `@suaveplan/error`
- [x] `@suaveplan/types`
## Tasks
### 1. Scaffold package
- [x] Create `universal/events/events-metrics/` directory tree per file layout in `proposal.md`
- [x] Write `package.json` (name `@suaveplan/events-metrics`, version `0.1.0`, `sideEffects: false`, correct workspace deps)
- [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 `EventsMetricsErrorCode` union type
- [x] Call `defineCodes` to register all `EVENTS_METRICS.*` codes
- [x] Export `registerEventsMetricsErrorCodes()` and `EventsMetricsError`
### 3. Metric name constants (`src/metrics/metric-names/`)
- [x] Export `METRIC_NAMES` and `ATTRIBUTE_KEYS` as `as const` objects
- [x] Write `metric-names.md` and `index.ts`
### 4. Core `attachEventMetrics` (`src/core/attach-event-metrics/`)
- [x] Implement `attachEventMetrics(bus, options?)` using `@suaveplan/events` interceptor API only
- [x] Zero-allocation hot path: pre-cache event-name → attribute set on first dispatch; no object creation on subsequent dispatches for the same event name
- [x] Counter: `DISPATCH_TOTAL` per event.name
- [x] Counter: `LISTENER_ERROR_TOTAL` per event.name + listener.id
- [x] Histogram: `DISPATCH_LATENCY_MS` using `performance.now()` or injectable clock
- [x] ObservableGauge: `QUEUE_DEPTH` and `ACTIVE_SUBSCRIBERS` — polled at collection time, not per-dispatch
- [x] Return `EventMetricsHandle` with `detach()` and `snapshot()`
- [x] Write `.md`, `.types.ts`, `.test.ts`, `index.ts`
### 5. Snapshot (`src/metrics/snapshot/`)
- [x] Implement `MetricsSnapshot` accumulator that mirrors the OTel metric state in-memory for test/introspection
- [x] Write `.md`, `.test.ts`, `index.ts`
### 6. Tests
- [x] Unit tests co-located (100% coverage target)
- [x] `tests/integration.test.ts` — attach to real EventBus, dispatch events, assert snapshot counts match
- [x] `tests/property.test.ts` — fast-check properties per `proposal.md` §4
### 7. Benchmarks (`src/__benchmarks__/events-metrics.bench.ts`)
- [x] Overhead benchmark: instrumented vs uninstrumented bus at high dispatch frequency
- [x] Snapshot retrieval benchmark
### 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-metrics` green
- [x] `bunx turbo test:coverage --filter=@suaveplan/events-metrics` ≥ 100% on touched modules
### 10. Publish
- [x] `bun scripts/publish.ts --package @suaveplan/events-metrics`
### 11. Archive
- [x] Fold spec delta into `openspec/specs/events/spec.md`
- [x] `mv openspec/changes/enterprise-events-metrics openspec/archive/2026-04-24-enterprise-events-metrics`