from __future__ import annotations from dataclasses import dataclass from .routing import RoutedMessage, parse_address_line from .store import Store from .registry import RegistryError, validate_targets @dataclass(frozen=True) class BrokerIdentity: endpoint_id: str source_repo: str class InputBroker: """Convert tap input into durable, identity-bearing outbound messages.""" def __init__(self, store: Store, identity: BrokerIdentity): self.store = store self.identity = identity def inspect_line(self, line: str) -> RoutedMessage | None: routed = parse_address_line(line) if routed is None: return None try: validate_targets([routed.target_repo]) except RegistryError: return None self.store.add( self.identity.source_repo, routed.target_repo, routed.body, endpoint=self.identity.endpoint_id, ) return routed def deliver_pending(self, control, *, window_for_repo): """Inject pending messages through the authoritative control client.""" delivered = 0 for row in self.store.list(state="pending"): if row["endpoint_id"] not in (None, self.identity.endpoint_id): continue target = window_for_repo(row["target_repo"]) lease_id = self.store.claim(row["message_id"], self.identity.endpoint_id) if lease_id is None: continue try: control.inject(target, f"#{row['sender_repo']}: {row['body']}") except Exception: continue self.store.release(row["message_id"], lease_id, "injected") delivered += 1 return delivered