TDD: red (test-only), then green. PayloadJobsEventBus validates before enqueueing, names tasks __events.<event>.<consumer> deterministically, and enqueues one task per subscriber via Promise.all. 3 new tests (11 total). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
44 lines
1.5 KiB
TypeScript
44 lines
1.5 KiB
TypeScript
import type { z } from "zod";
|
|
import type { IJobQueue } from "@repo/core-shared/jobs";
|
|
import type { EventDescriptor } from "./event-descriptor";
|
|
import type { EventHandler, IEventBus } from "./event-bus.interface";
|
|
|
|
/**
|
|
* Production-grade bus: for each subscriber, enqueues one Payload task per
|
|
* `publish()` call. Subscribers register with their consumer-feature name so
|
|
* fan-out tasks are named deterministically: `__events.<event>.<consumer>`.
|
|
* The actual handler invocation happens inside Payload's job runner — see the
|
|
* matching task config generated by `gen event consume` (Task 39).
|
|
*/
|
|
export class PayloadJobsEventBus implements IEventBus {
|
|
private readonly subscribers = new Map<string, string[]>();
|
|
|
|
constructor(private readonly queue: IJobQueue) {}
|
|
|
|
async publish<T>(
|
|
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
|
payload: T,
|
|
): Promise<void> {
|
|
descriptor.schema.parse(payload);
|
|
const consumers = this.subscribers.get(descriptor.name) ?? [];
|
|
await Promise.all(
|
|
consumers.map((consumerFeature) =>
|
|
this.queue.enqueue(
|
|
`__events.${descriptor.name}.${consumerFeature}`,
|
|
payload,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
subscribe<T>(
|
|
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
|
consumerFeature: string,
|
|
_handler: EventHandler<T>,
|
|
): void {
|
|
const arr = this.subscribers.get(descriptor.name) ?? [];
|
|
if (!arr.includes(consumerFeature)) arr.push(consumerFeature);
|
|
this.subscribers.set(descriptor.name, arr);
|
|
}
|
|
}
|