5.9 KiB
OpenSpec Change: enterprise-message-bridge
- Change ID: enterprise-message-bridge
- Package:
@suaveplan/message-bridge - Category: universal
- Target path:
universal/social/message-bridge - Status: IN PROGRESS
- Date: 2026-04-24
- Reference spec:
/mnt/s/web/suaveplan/enterprise/message-bridge.md
Summary
Deliver @suaveplan/message-bridge on the genesis branch from scratch at enterprise quality. The package provides a unified multi-platform message bridge: a MessageBridge facade that normalizes inbound and outbound messages across registered MessageAdapter implementations (Slack, Discord, Telegram, iMessage, SMS), a canonical UnifiedMessage model, platform-agnostic routing rules, deduplication of inbound messages by message ID, delivery confirmation tracking, and an event bus for inbound message propagation. This package defines the MessageAdapter interface that all platform adapters implement. Genesis adds structured errors, Zod schemas, OTel instrumentation, AbortSignal cancellation, and property-based tests.
Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build.
Available workspace deps on genesis
| Package | Role |
|---|---|
@suaveplan/error (workspace:^) |
MessageBridgeError; MSG_BRIDGE.* error codes |
@suaveplan/types (workspace:^) |
LoggerLike, Clock, EventBusLike |
@suaveplan/events (workspace:^) |
Inbound message event propagation |
@suaveplan/retry (workspace:^) |
Retry on send failures |
zod (catalog:forms) |
Schema validation |
@opentelemetry/api (catalog:observability) |
Spans + counters |
Dev deps: vitest, @vitest/coverage-istanbul, fast-check (all catalog:testing).
Public API surface
UnifiedMessage
interface UnifiedMessage {
id: string
platform: string // 'slack' | 'discord' | 'telegram' | 'imessage' | 'sms' | custom
channelId?: string
threadId?: string
senderId: string
senderName?: string
text: string
attachments: MessageAttachment[]
timestamp: Date
raw?: unknown // original platform payload
metadata: Record<string, unknown>
}
interface MessageAttachment {
id: string
type: 'image' | 'video' | 'audio' | 'file'
url?: string
content?: Uint8Array
mimeType?: string
filename?: string
size?: number
}
interface SendOptions {
platform: string
to: string // channel ID, chat ID, or phone number depending on platform
threadId?: string
attachments?: MessageAttachment[]
signal?: AbortSignal
}
MessageAdapter interface
interface MessageAdapter {
readonly platform: string
start(signal?: AbortSignal): AsyncGenerator<UnifiedMessage>
send(to: string, text: string, options?: AdapterSendOptions): Promise<void>
isAvailable(): boolean
}
MessageBridge
class MessageBridge {
constructor(options?: MessageBridgeOptions)
register(adapter: MessageAdapter): void
unregister(platform: string): void
send(options: SendOptions, text: string): Promise<void>
start(signal?: AbortSignal): void // starts all registered adapters
stop(): void
on(event: 'message', handler: (msg: UnifiedMessage) => void): () => void
platforms(): string[]
}
interface MessageBridgeOptions {
bus?: EventBusLike
logger?: LoggerLike
clock?: Clock
deduplicationWindowMs?: number // default 60_000; 0 disables
}
Routing
interface RouteRule {
fromPlatform?: string
toPlatform: string
transform?: (msg: UnifiedMessage) => Partial<UnifiedMessage>
}
function applyRoutes(bridge: MessageBridge, rules: RouteRule[]): () => void
Error codes
type MessageBridgeErrorCode =
| 'MSG_BRIDGE_ADAPTER_NOT_FOUND'
| 'MSG_BRIDGE_SEND_FAILED'
| 'MSG_BRIDGE_ADAPTER_UNAVAILABLE'
| 'MSG_BRIDGE_DEDUP_CONFLICT'
| 'MSG_BRIDGE_ROUTE_FAILED'
| 'MSG_BRIDGE_ABORTED'
Genesis additions
MessageBridgeErrorextendsSuaveplanErrorwith six stable codes.- Zod schemas for
UnifiedMessage,MessageAttachment,SendOptions,RouteRule,MessageBridgeOptions. - Message deduplication — a sliding-window set keyed by
${platform}:${id}prevents duplicate inbound delivery when multiple adapters or polling overlaps produce the same message. - Routing rules —
applyRouteswires cross-platform message forwarding (e.g. Slack → Telegram bridge) with optional transform. - OTel instrumentation — counter
message_bridge.inbound_totalwith attributemessage_bridge.platform; countermessage_bridge.outbound_total; histogrammessage_bridge.send_duration_ms. - Property-based tests — deduplication window correctly excludes duplicates and expires old entries for arbitrary sequences of message IDs.
File layout
universal/social/message-bridge/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
types/
common.ts
common.md
core/
message-bridge/
message-bridge.ts
message-bridge.md
message-bridge.types.ts
message-bridge.test.ts
index.ts
deduplicator/
deduplicator.ts
deduplicator.md
deduplicator.test.ts
index.ts
routing/
route-rules/
route-rules.ts
route-rules.md
route-rules.test.ts
index.ts
__benchmarks__/
message-bridge.bench.ts
tests/
integration.test.ts
property.test.ts
Impact
Downstream consumers: @suaveplan/imessage-adapter, @suaveplan/slack-client (adapter registration), @suaveplan/discord-client, @suaveplan/telegram-client, @suaveplan/sms-providers.
Note: This package must be built before the adapter packages since it defines the MessageAdapter interface they implement.
Workspace dep prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/retry.