Files
2026-05-27 23:29:53 +01:00

3.7 KiB

@suaveplan/events v1.1.0 — Port Missing Capabilities from Suaveplan

Status: Active
Target: @suaveplan/events 0.0.0 → additive capabilities, no breaking changes
Source: /home/ubuntu/web/suaveplan/openspec/changes/events-v1.1.0/


Why

The genesis EventBus was a cut-down redesign of suaveplan's EventEmitter. While it introduced improvements (interceptor API, BoundedQueue, idempotent listeners, deterministic injection), it was missing several capabilities that suaveplan had already proven necessary:

  • No event buffering for late subscribers — plugins attaching after lifecycle transitions miss historical events.
  • No wildcard pattern matching — observers must enumerate every event name.
  • No async iteration — consumers manually manage on/off instead of for await.
  • No prependListener — impossible to insert a listener at the head.
  • No listener introspection — listeners() / listenerCount() absent.
  • No once() → off() round-trip — off(type, originalListener) fails for once registrations.
  • No concurrency control on emitAsync — one slow listener blocks the chain.
  • No maxListeners warning — memory leaks go undetected.
  • No global errorSink — caller must inspect EmitResult.errors or errors are lost.
  • No Symbol.dispose — no RAII cleanup via using.

These gaps force consumers to reinvent the missing primitives downstream.


What changes

Core EventBus additions

  1. bufferSize option — replays the last N events to subscribers that attach after emission.
  2. wildcard option — subscribe to plugin:* and match all plugin:ready, plugin:error, etc.
  3. iter(type, signal?) — async iterable over events, compatible with for await.
  4. prepend(type, listener) — insert listener at the head of the dispatch queue.
  5. listeners(type) / listenerCount(type) — introspection without exposing internals.
  6. offAll(type?) — remove all listeners for a type, or everything.
  7. once() WeakMap tracking — off(type, originalListener) works for once registrations.
  8. concurrency option on emitAsync — sequential (default), parallel, allSettled.
  9. maxListeners + errorSink — warnings and centralised error routing.
  10. Symbol.dispose on EventBus and Unsubscribe handles.
  11. EVENTS.LISTENER.MAX_EXCEEDED error code.

Non-goals

  • No breaking changes to existing EventBus API.
  • No BoundedQueue integration into EventBus dispatch — remains a standalone utility.
  • No changes to companion packages (events-otel, events-schema, events-metrics, events-transport) unless required by API additions.

Impact

Affected dependents

Run bunx turbo ls --filter=...@suaveplan/events to enumerate. All additive surface, so no downstream code changes required. Verified by running downstream test suites.

  • @suaveplan/crypto-vault — no action needed (uses EventBus directly, doesn't touch changed surface)
  • @suaveplan/network — no action needed (uses EventBus + BoundedQueue, unchanged)
  • @suaveplan/storage — no action needed
  • @suaveplan/packer — no action needed
  • @suaveplan/fsm — no action needed
  • @suaveplan/retry — no action needed
  • @suaveplan/migration — no action needed (type-only import)
  • @suaveplan/config — no action needed
  • @suaveplan/events-schema — no action needed (type-only import)
  • @suaveplan/events-metrics — no action needed (type-only import)
  • @suaveplan/events-otel — no action needed (type-only import)
  • @suaveplan/events-transport — no action needed (type-only import)

Migration

No migration needed — purely additive.