// The broker and event framework a module's code uses to talk to the mesh. The transport is the // mesh's one broker (novox/hq ADR 0001); this is the typed surface over it. The concrete broker // binding is provided by the runtime that hosts a module's code — the sdk defines the contract so // module code, the tool runtime and provisioners all speak it the same way. import type { Envelope, EventHeaders } from "../contracts/index.js"; export type { Envelope, EventHeaders }; /** A request/reply call and a publish/subscribe surface over the mesh broker. */ export interface Broker { /** Ask one question and await one answer — the client side; the shape mesh-controller's command * API is reached by. */ request(key: string, body: Req): Promise; /** Answer a question — the server side of request/reply. A tool runtime serves invocations this * way. Returns an unregister. */ handle(key: string, handler: (body: Req) => Promise): Promise<() => void>; /** Emit an event onto the mesh. */ publish(env: Envelope): Promise; /** React to events matching a routing-key pattern. Returns an unsubscribe. */ subscribe(pattern: string, handler: (env: Envelope) => Promise): Promise<() => void>; close(): Promise; } /** * How a module obtains its broker. The hosting runtime sets this once; module code calls broker() * without knowing the concrete binding. Keeping the binding out of the sdk is deliberate — the sdk * carries the contract, not a specific broker client build. */ let binding: (() => Broker) | undefined; export function useBroker(factory: () => Broker): void { binding = factory; } export function broker(): Broker { if (!binding) { throw new Error( "no broker is bound — the tool runtime or provisioner host must call useBroker() before " + "module code reaches the mesh", ); } return binding(); } /** A convenience for the common case: consume one event stream until stopped. */ export async function consume( pattern: string, handler: (env: Envelope) => Promise, ): Promise<() => void> { return broker().subscribe(pattern, handler); }