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

3.9 KiB

Capability Spec Delta: api-events-worker-proxy (api-events-worker-proxy)

Package: @suaveplan/api-events-worker-proxy Status: PROPOSED

Every requirement below carries a **Verified by:** line at authoring time.

ADDED Requirements

Requirement: Proxy preserves the EventBusLike surface from @suaveplan/events

The package SHALL expose createEventsProxyClient(port) returning a value that satisfies WorkerProxyOf<EventBusLike> where EventBusLike is the EXISTING type from @suaveplan/events. No new contract layer SHALL be introduced — worker-side plugin code that worked against the direct bus MUST also work against the proxy with no source change beyond async-await wrapping (which WorkerProxyOf already enforces by promise-wrapping return types).

Scenario: proxy type matches WorkerProxyOf

  • WHEN expectTypeOf<ReturnType<typeof createEventsProxyClient>>().toEqualTypeOf<WorkerProxyOf<EventBusLike>>() is checked
  • THEN the assertion passes

Verified by: src/client/client.spec-conformance.test.ts::"proxy type matches WorkerProxyOf<EventBusLike>"


Requirement: Disallowed-topic emit throws reconstructed SuaveplanError

The package SHALL marshal emit(topic, payload) calls to the main-process bus where topic-allow checks are enforced per design doc §5.2b. If the main side rejects with a disallow signal, the proxy MUST surface EVENTS_PROXY_TOPIC_NOT_ALLOWED as a SuaveplanError reconstructed via api-worker-rpc's error-reconstruction.

Scenario: allowed topic emit round-trips

  • WHEN a plugin manifests publishes: ["billing.invoice.created"] AND calls emit("billing.invoice.created", payload)
  • THEN the main-side bus receives the event AND the call resolves successfully

Scenario: disallowed topic throws TOPIC_NOT_ALLOWED

  • WHEN the plugin calls emit("foreign.topic", payload) with no matching manifest entry
  • THEN the proxy call rejects with SuaveplanError of code EVENTS_PROXY_TOPIC_NOT_ALLOWED

Verified by: src/publisher/publisher.test.ts::"allowed topic emit round-trips" AND src/publisher/publisher.test.ts::"disallowed topic throws TOPIC_NOT_ALLOWED"


Requirement: Subscription delivers events from main bus to worker handler with backpressure

The package SHALL marshal on(topic, handler) so that events emitted on the main-process bus are delivered to the worker-side handler. Worker-side handler throws MUST surface main-side as EVENTS_PROXY_HANDLER_THROW. The main-side dispatcher MUST respect api-worker-rpc's backpressure budget — when the worker is slow, main-side delivery throttles rather than buffering unbounded.

Scenario: event delivered to worker handler

  • WHEN the main bus emits topic AND the worker handler is registered
  • THEN the handler observes the event payload

Scenario: slow handler backpressures producer

  • WHEN the worker handler takes 100ms per call AND main emits 100 events in a burst
  • THEN the main-side dispatcher pauses at the backpressure budget rather than buffering all 100 in memory

Verified by: src/subscriber/subscriber.test.ts::"event delivered to handler" AND src/subscriber/subscriber.test.ts::"slow handler backpressures producer"


Requirement: Conformance suite passes against this proxy

The package SHALL be runnable as the surface argument to runEventsBusConformance from @suaveplan/events/testing. Every conformance scenario (FIFO ordering, interceptors, idempotent listeners) MUST pass identically to a direct in-process bus. This is the Stage-4-precondition gate.

Scenario: runEventsBusConformance passes

  • WHEN runEventsBusConformance("worker-proxy", makeFactory) runs
  • THEN every scenario passes

Verified by: tests/conformance/run-events-bus-conformance.test.ts::"runEventsBusConformance passes against proxy"