first commit
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @wrnexus/pubsub — topic-based publish/subscribe with a pluggable driver.
|
||||
* The default is in-process; swap in a Redis/NATS driver for cross-instance
|
||||
* messaging (it also backs @wrnexus/core's realtime bridge).
|
||||
*
|
||||
* const bus = createPubSub();
|
||||
* const off = bus.subscribe("order:*", (msg, topic) => {...});
|
||||
* await bus.publish("order:created", { id: 7 });
|
||||
*
|
||||
* Subscriptions match exact topics, "ns:*" prefixes, and "*" (everything).
|
||||
*/
|
||||
|
||||
export type Handler<T = unknown> = (message: T, topic: string) => void | Promise<void>;
|
||||
|
||||
export interface PubSubDriver {
|
||||
publish(topic: string, message: unknown): void | Promise<void>;
|
||||
subscribe(pattern: string, handler: Handler): () => void;
|
||||
}
|
||||
|
||||
export interface PubSub {
|
||||
publish<T = unknown>(topic: string, message: T): Promise<void>;
|
||||
subscribe<T = unknown>(pattern: string, handler: Handler<T>): () => void;
|
||||
}
|
||||
|
||||
function patternMatches(pattern: string, topic: string): boolean {
|
||||
if (pattern === "*" || pattern === topic) return true;
|
||||
if (pattern.endsWith(":*")) return topic.startsWith(pattern.slice(0, -1)); // "post:" prefix
|
||||
return false;
|
||||
}
|
||||
|
||||
/** In-process pub/sub driver (default). */
|
||||
export function memoryDriver(): PubSubDriver {
|
||||
const subs = new Map<string, Set<Handler>>();
|
||||
return {
|
||||
publish(topic, message) {
|
||||
for (const [pattern, handlers] of subs) {
|
||||
if (!patternMatches(pattern, topic)) continue;
|
||||
for (const handler of handlers) void handler(message, topic);
|
||||
}
|
||||
},
|
||||
subscribe(pattern, handler) {
|
||||
let set = subs.get(pattern);
|
||||
if (!set) subs.set(pattern, (set = new Set()));
|
||||
set.add(handler);
|
||||
return () => {
|
||||
set!.delete(handler);
|
||||
if (!set!.size) subs.delete(pattern);
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Create a pub/sub bus over a driver (in-memory by default). */
|
||||
export function createPubSub(driver: PubSubDriver = memoryDriver()): PubSub {
|
||||
return {
|
||||
async publish(topic, message) {
|
||||
await driver.publish(topic, message);
|
||||
},
|
||||
subscribe(pattern, handler) {
|
||||
return driver.subscribe(pattern, handler as Handler);
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user