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:
@@ -29,11 +29,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "src/in-memory-event-bus.test.ts",
|
"path": "src/in-memory-event-bus.test.ts",
|
||||||
"sha256": "56f704de153f7676bf243d6350f4bebe28cb9ffec1eb3afef8f5f2bb08561c21"
|
"sha256": "d75a7694cbf08f6afdc0b5ffe80c37e2c61e00b94f61231200e1c26becb9f26f"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "src/in-memory-event-bus.ts",
|
"path": "src/in-memory-event-bus.ts",
|
||||||
"sha256": "d7de709049bd864bdcd8f02316d981cf0407efbc5b6f13fd2ab86094ecaddb8a"
|
"sha256": "c629e0733ab23ef808be7cba04ccd4d31792846dc81d02c3722a210a47aa5035"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "src/index.ts",
|
"path": "src/index.ts",
|
||||||
@@ -41,11 +41,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "src/payload-jobs-event-bus.test.ts",
|
"path": "src/payload-jobs-event-bus.test.ts",
|
||||||
"sha256": "cda493839ed9af7d48a4c240392933af7167805a94ebaf30efd64f8a374b7441"
|
"sha256": "dc091425978d4e02881ccdcb2a46b716273d848ebdd87f72131abdf419a40161"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "src/payload-jobs-event-bus.ts",
|
"path": "src/payload-jobs-event-bus.ts",
|
||||||
"sha256": "5fbeb611af7f4ba4302828de6a0262a74d9b4bb6a11cbbb8ed40712aae731ded"
|
"sha256": "2aae98fcc2b3d4c77d7b0f80e1aedb56bbce737e296b9681b663fd22380ae3ab"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "src/symbols.ts",
|
"path": "src/symbols.ts",
|
||||||
|
|||||||
@@ -46,4 +46,19 @@ describe("InMemoryEventBus", () => {
|
|||||||
const bus = new InMemoryEventBus();
|
const bus = new InMemoryEventBus();
|
||||||
await expect(bus.publish(evt, { id: "x" })).resolves.toBeUndefined();
|
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>>,
|
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||||
payload: T,
|
payload: T,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
descriptor.schema.parse(payload);
|
const parsed = descriptor.schema.parse(payload);
|
||||||
const subscribers = this.handlers.get(descriptor.name) ?? [];
|
const subscribers = this.handlers.get(descriptor.name) ?? [];
|
||||||
if (subscribers.length === 0) return;
|
if (subscribers.length === 0) return;
|
||||||
const settled = await Promise.allSettled(
|
const settled = await Promise.allSettled(subscribers.map((h) => h(parsed)));
|
||||||
subscribers.map((h) => h(payload)),
|
|
||||||
);
|
|
||||||
if (this.options.failFast) {
|
if (this.options.failFast) {
|
||||||
const failure = settled.find((s) => s.status === "rejected");
|
const failure = settled.find((s) => s.status === "rejected");
|
||||||
// Only the first rejection is rethrown. Other failures are intentionally
|
// Only the first rejection is rethrown. Other failures are intentionally
|
||||||
|
|||||||
@@ -42,6 +42,21 @@ describe("PayloadJobsEventBus", () => {
|
|||||||
expect(queue.enqueued[0]!.input).toEqual({ userId: "u1" });
|
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 () => {
|
it("enqueues nothing when no subscribers are registered", async () => {
|
||||||
const queue = recordingQueue();
|
const queue = recordingQueue();
|
||||||
const bus = new PayloadJobsEventBus(queue);
|
const bus = new PayloadJobsEventBus(queue);
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ export class PayloadJobsEventBus implements IEventBus {
|
|||||||
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
descriptor: EventDescriptor<string, z.ZodType<T>>,
|
||||||
payload: T,
|
payload: T,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
descriptor.schema.parse(payload);
|
const parsed = descriptor.schema.parse(payload);
|
||||||
const consumers = this.subscribers.get(descriptor.name) ?? [];
|
const consumers = this.subscribers.get(descriptor.name) ?? [];
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
consumers.map((consumerFeature) =>
|
consumers.map((consumerFeature) =>
|
||||||
this.queue.enqueue(
|
this.queue.enqueue(
|
||||||
`__events.${descriptor.name}.${consumerFeature}`,
|
`__events.${descriptor.name}.${consumerFeature}`,
|
||||||
payload,
|
parsed,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user