# `backpressure.ts` — bounded-queue accounting for the event-bus service ## Purpose Wraps `@suaveplan/events`' `BoundedQueue` with a small adapter that treats the queue as a pure accounting primitive: a publish reserves a slot, drains it immediately, and the adapter reports whether the publish counts as "dropped" under the configured policy. Keeping this logic in a focused module lets `event-bus-service.ts` stay under the 300-line file budget and makes the back-pressure semantics testable in isolation. ## Features - Constructs a `BoundedQueue` plus a public `capacity` / `policy` view (the queue itself keeps these private). - `tryEnqueue` translates `EVENTS.QUEUE.OVERFLOW` into `EventBusError(EVENT_BUS.BACKPRESSURE_DROPPED)`. - Reports `dropped: true` for `drop-oldest` / `drop-newest` policies when the queue was at capacity before enqueue. - Forwards `AbortSignal` through to the underlying queue. ## Usage: Basic ```ts import { createBackpressureState, tryEnqueue, } from "@suaveplan/api-plugin-event-bus/services/event-bus-service"; const state = createBackpressureState({ capacity: 8, policy: "drop-oldest", }); const outcome = await tryEnqueue(state, { hello: "world" }, undefined); console.log(outcome.dropped); ``` ## Usage: Advanced ```ts import { createBackpressureState, tryEnqueue, } from "@suaveplan/api-plugin-event-bus/services/event-bus-service"; const state = createBackpressureState({ capacity: 1, policy: "throw" }); await tryEnqueue(state, "a", undefined); try { await tryEnqueue(state, "b", undefined); } catch (err) { // EventBusError(EVENT_BUS.BACKPRESSURE_DROPPED) } ``` ## API Reference ### `createBackpressureState(opts: BackpressureOptions): BackpressureState` Build the queue + companion accounting struct used by the service. ### `tryEnqueue(state, payload, signal): Promise` Reserves a slot, drains it, and returns `{ dropped }`. Throws `EventBusError(EVENT_BUS.BACKPRESSURE_DROPPED)` under `policy: "throw"` when the queue is full. ### `interface BackpressureState` `{ queue: BoundedQueue; capacity: number; policy: QueuePolicy }`. ### `interface EnqueueOutcome` `{ dropped: boolean }`. ## Implementation Notes - We drain the queue immediately because we use it only for accounting — the actual dispatch is owned by `EventBus.emitAsync`. If the bus ever grows a queue-driven dispatch model, the `dequeue` call disappears and the queue starts carrying the payload. - "would-overflow" is computed **before** `enqueue` because under `drop-oldest` the queue swallows the oldest item synchronously and the post-enqueue size would not reveal the drop. - The thrown `EventBusError` carries `context.statusCode: 503` so the HTTP layer translates back-pressure rejections to "Service Unavailable" automatically.