Task 3.7 of novox/hq ADR 0116, and the whole of the sdk's diff for the bus change. Three comments said AMQP where they meant 'message headers' and 'a broker client'; the code never spoke it, which is why no module is rebuilt for any of this (ADR 0039).
53 lines
2.2 KiB
TypeScript
53 lines
2.2 KiB
TypeScript
// 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<Req, Res>(key: string, body: Req): Promise<Res>;
|
|
/** Answer a question — the server side of request/reply. A tool runtime serves invocations this
|
|
* way. Returns an unregister. */
|
|
handle<Req, Res>(key: string, handler: (body: Req) => Promise<Res>): Promise<() => void>;
|
|
/** Emit an event onto the mesh. */
|
|
publish<T>(env: Envelope<T>): Promise<void>;
|
|
/** React to events matching a routing-key pattern. Returns an unsubscribe. */
|
|
subscribe<T>(pattern: string, handler: (env: Envelope<T>) => Promise<void>): Promise<() => void>;
|
|
close(): Promise<void>;
|
|
}
|
|
|
|
/**
|
|
* 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<T>(
|
|
pattern: string,
|
|
handler: (env: Envelope<T>) => Promise<void>,
|
|
): Promise<() => void> {
|
|
return broker().subscribe<T>(pattern, handler);
|
|
}
|