# `service/bus-map` ## Purpose `bus-map` declares `SmsBusMap` — the single-payload topic contract used by `SmsService.bus` (an `EventBus`). The map is split out from the service implementation so the legacy emitter and the service can both import the contract without a circular dependency, and so downstream consumers can type their own `EventBus`-shaped bridges against the same contract. `SmsBusMap` covers the eight live topics emitted by `SmsService`: `sms.sent`, `sms.delivered`, `sms.failed`, `sms.opted_out`, `sms.opted_in`, `sms.scheduled`, `sms.retry`, `sms.rate_limited`. ## Features - Single-payload `EventBus` contract — one value per topic (versus the legacy `SmsEventMap` tuple form which mirrors the upstream `on/off/emit` shape). - Open `Record` base so consumers can extend the map with custom topics in their own packages. - One-to-one mapping with the public type contracts in `src/types/sms.types.ts`. ## Usage: Basic ```ts import { EventBus } from "@suaveplan/events"; import type { SmsBusMap } from "@suaveplan/sms/service"; const bus = new EventBus(); bus.on("sms.sent", (report) => console.log("sent", report.messageId)); ``` ## Usage: Advanced ```ts import { EventBus } from "@suaveplan/events"; import type { SmsBusMap } from "@suaveplan/sms/service"; // Extend the map with a host-specific topic to share one bus. interface HostBusMap extends SmsBusMap { "host.audit": { actor: string; action: string }; } const bus = new EventBus(); bus.on("sms.failed", (report) => audit({ messageId: report.messageId })); bus.on("host.audit", (event) => console.log(event.actor, event.action)); ``` ## API Reference - `interface SmsBusMap extends Record` with keys: `sms.sent`, `sms.delivered`, `sms.failed`, `sms.opted_out`, `sms.opted_in`, `sms.scheduled`, `sms.retry`, `sms.rate_limited`, each mapped to the matching public type (`SmsDeliveryReport`, `OptOutEntry`, `{phoneNumber: string}`, `SmsScheduledEvent`, `SmsRetryEvent`, `SmsRateLimitedEvent`). ## Implementation Notes The map is a `type-only` export — no runtime symbol — so it adds zero bundle size. The decision to use single-payload topics on the bus (rather than the legacy tuple form) follows `@suaveplan/events` convention; the tuple form is preserved exclusively for the upstream `on/off/emit` surface and is mediated by `LegacyEmitter.mirror`. Consumers building new code should target the bus directly.