fix(generators): event buses deliver the zod-parsed payload
InMemoryEventBus.publish and PayloadJobsEventBus.publish called descriptor.schema.parse(payload) but discarded the result, so handlers and enqueued jobs received the raw input — zod defaults, catches, transforms and strips never applied. Both bus templates now fan out the parsed value; regression tests added to both test templates and the events snapshot hashes regenerated (template-tree sha over hbs-stripped paths — only the four touched files differ). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -46,4 +46,19 @@ describe("InMemoryEventBus", () => {
|
||||
const bus = new InMemoryEventBus();
|
||||
await expect(bus.publish(evt, { id: "x" })).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("delivers the zod-parsed payload, not the raw input", async () => {
|
||||
const evtNormalized = defineEvent(
|
||||
"test.normalized",
|
||||
z.object({ id: z.string(), mode: z.string().catch("auto") }),
|
||||
);
|
||||
const bus = new InMemoryEventBus();
|
||||
const handler = vi.fn();
|
||||
bus.subscribe(evtNormalized, "consumer", handler);
|
||||
await bus.publish(evtNormalized, {
|
||||
id: "x",
|
||||
mode: 42 as unknown as string,
|
||||
});
|
||||
expect(handler).toHaveBeenCalledWith({ id: "x", mode: "auto" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,12 +16,10 @@ export class InMemoryEventBus implements IEventBus {
|
||||
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||
payload: T,
|
||||
): Promise<void> {
|
||||
descriptor.schema.parse(payload);
|
||||
const parsed = descriptor.schema.parse(payload);
|
||||
const subscribers = this.handlers.get(descriptor.name) ?? [];
|
||||
if (subscribers.length === 0) return;
|
||||
const settled = await Promise.allSettled(
|
||||
subscribers.map((h) => h(payload)),
|
||||
);
|
||||
const settled = await Promise.allSettled(subscribers.map((h) => h(parsed)));
|
||||
if (this.options.failFast) {
|
||||
const failure = settled.find((s) => s.status === "rejected");
|
||||
// Only the first rejection is rethrown. Other failures are intentionally
|
||||
|
||||
@@ -42,6 +42,21 @@ describe("PayloadJobsEventBus", () => {
|
||||
expect(queue.enqueued[0]!.input).toEqual({ userId: "u1" });
|
||||
});
|
||||
|
||||
it("enqueues the zod-parsed payload, not the raw input", async () => {
|
||||
const evtNormalized = defineEvent(
|
||||
"test.normalized",
|
||||
z.object({ userId: z.string(), mode: z.string().catch("auto") }),
|
||||
);
|
||||
const queue = recordingQueue();
|
||||
const bus = new PayloadJobsEventBus(queue);
|
||||
bus.subscribe(evtNormalized, "marketing-pages", vi.fn());
|
||||
await bus.publish(evtNormalized, {
|
||||
userId: "u1",
|
||||
mode: 42 as unknown as string,
|
||||
});
|
||||
expect(queue.enqueued[0]!.input).toEqual({ userId: "u1", mode: "auto" });
|
||||
});
|
||||
|
||||
it("enqueues nothing when no subscribers are registered", async () => {
|
||||
const queue = recordingQueue();
|
||||
const bus = new PayloadJobsEventBus(queue);
|
||||
|
||||
@@ -19,13 +19,13 @@ export class PayloadJobsEventBus implements IEventBus {
|
||||
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||
payload: T,
|
||||
): Promise<void> {
|
||||
descriptor.schema.parse(payload);
|
||||
const parsed = 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,
|
||||
parsed,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user