121 lines
4.6 KiB
Markdown
121 lines
4.6 KiB
Markdown
# OpenSpec Change: enterprise-timing
|
|
|
|
- **Change ID**: enterprise-timing
|
|
- **Package**: `@suaveplan/timing`
|
|
- **Category**: universal
|
|
- **Target path**: `packages/universal/timing`
|
|
- **Status**: IN PROGRESS
|
|
- **Date**: 2026-04-18
|
|
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/timing.md`
|
|
|
|
## Summary
|
|
|
|
Greenfield implementation of `@suaveplan/timing` for the genesis branch. The existing main-branch timing package provides the baseline public-API surface to preserve; this change delivers enterprise-grade timing and scheduling utilities — stopwatch, throttle/debounce, scheduler, exponential decay, and vector clock — with injectable clock, `AbortSignal` support, structured errors, Zod schemas, property-based tests, OTel instrumentation, and benchmarks.
|
|
|
|
## Current-state audit (genesis)
|
|
|
|
The package does not exist on genesis yet — this is a fresh build.
|
|
|
|
## Available workspace deps on genesis
|
|
|
|
| Package | Available |
|
|
|---------|-----------|
|
|
| `@suaveplan/error` | ✅ |
|
|
| `@suaveplan/types` | ✅ |
|
|
| `@suaveplan/events` | ✅ |
|
|
| `@suaveplan/streams` | ❌ not yet built — not required |
|
|
| `@suaveplan/opentelemetry` | ❌ not yet built — use `@opentelemetry/api` directly |
|
|
| `@suaveplan/feature-flags` | ❌ not yet built — skip |
|
|
| `@suaveplan/schema-registry` | ❌ not yet built — export Zod schemas directly |
|
|
|
|
External deps (via catalog):
|
|
|
|
- `zod` → `catalog:forms`
|
|
- `@opentelemetry/api` → `catalog:observability`
|
|
- `tslib` → `catalog:build`
|
|
|
|
Dev deps (via catalog):
|
|
|
|
- `vitest`, `@vitest/coverage-istanbul` → `catalog:testing`
|
|
- `fast-check` → `catalog:testing`
|
|
- `mitata` → `catalog:testing`
|
|
|
|
## Preserved public API surface (from main branch)
|
|
|
|
All of the following must be re-exported from the genesis build.
|
|
|
|
### Decay module (`src/decay/`)
|
|
|
|
- `createDecay(opts)` — returns a `Decay` instance with `.tick(elapsed)`, `.reset()`, `.value` getter
|
|
- `DecayOptions`, `DecayFunction`, `Decay` (types)
|
|
|
|
### Scheduler module (`src/scheduler/`)
|
|
|
|
- `createScheduler(opts?)` — returns a `Scheduler` with `.schedule(fn, delay)`, `.cancel(id)`, `.drain(signal?)`, `.dispose()`
|
|
- `SchedulerOptions`, `SchedulerHandle`, `Scheduler` (types)
|
|
|
|
### Stopwatch module (`src/stopwatch/`)
|
|
|
|
- `createStopwatch(opts?)` — returns a `Stopwatch` with `.start()`, `.stop()`, `.reset()`, `.lap()`, `.elapsed` getter, `.laps` getter
|
|
- `StopwatchOptions`, `StopwatchLap`, `Stopwatch` (types)
|
|
|
|
### Throttle module (`src/throttle/`)
|
|
|
|
- `throttle(fn, wait, opts?)` — leading/trailing edge configurable
|
|
- `debounce(fn, wait, opts?)` — with `.flush()` and `.cancel()` methods on the returned function
|
|
- `ThrottleOptions`, `DebounceOptions`, `ThrottledFunction`, `DebouncedFunction` (types)
|
|
|
|
### Timer module (`src/timer/`)
|
|
|
|
- `createTimer(opts)` — interval-based with `.start()`, `.stop()`, `.reset()`, `.tick` event
|
|
- `createCountdown(duration, opts?)` — countdown with `.start()`, `.pause()`, `.resume()`, `.stop()`, `.remaining` getter
|
|
- `TimerOptions`, `CountdownOptions`, `Timer`, `Countdown` (types)
|
|
|
|
### Vector clock module (`src/vector-clock/`)
|
|
|
|
- `createVectorClock(nodeId, opts?)` — returns a `VectorClock` with `.increment()`, `.update(remote)`, `.compare(other)`, `.toJSON()`, `.fromJSON(raw)`
|
|
- `vectorClockCompare(a, b)` → `'before' | 'after' | 'concurrent' | 'equal'`
|
|
- `VectorClockOptions`, `VectorClockState`, `VectorClock` (types)
|
|
|
|
## New additions for genesis
|
|
|
|
### Structured errors
|
|
|
|
- `TimingError` extends `SuaveplanError` with codes under `TIMING.*` namespace
|
|
- Codes: `TIMING.SCHEDULER.DISPOSED`, `TIMING.COUNTDOWN.INVALID_DURATION`, `TIMING.VECTOR_CLOCK.UNKNOWN_NODE`
|
|
|
|
### Zod schemas
|
|
|
|
- `SchedulerOptionsSchema`, `StopwatchOptionsSchema`, `TimerOptionsSchema`, `CountdownOptionsSchema`, `VectorClockOptionsSchema`
|
|
|
|
### Injectable clock
|
|
|
|
- All time-reading functions accept optional `{ clock?: Clock }` from `@suaveplan/types`
|
|
- Scheduler and timer use injected clock for deterministic tests
|
|
|
|
### AbortSignal support
|
|
|
|
- `Scheduler.drain(signal?)`, `Countdown` start/pause all honour `AbortSignal`
|
|
|
|
### OTel instrumentation
|
|
|
|
- Histograms for stopwatch elapsed, scheduler queue depth; counters for timer ticks
|
|
|
|
### Property-based tests (fast-check)
|
|
|
|
- Vector-clock ordering: transitivity, anti-symmetry
|
|
- Throttle: output count ≤ ceil(events / wait)
|
|
- Decay: monotonically decreasing (or increasing) toward target
|
|
|
|
### Benchmarks (mitata)
|
|
|
|
- `throttle` 10 000 events at 16ms window
|
|
- `createVectorClock` merge 1 000 nodes
|
|
- `createScheduler` drain 1 000 tasks
|
|
|
|
## Impact
|
|
|
|
- Adds one new package: `packages/universal/timing`
|
|
- Adds one new spec capability: `timing`
|
|
- No existing package consumers on genesis yet
|