From 20621ec58e1286e7ab6280f3a67c6bc7c4f05b33 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Fri, 6 Dec 2024 09:40:17 +0100 Subject: [PATCH] Extracting formatter, error resonding by format. --- coverage.svg | 2 +- src/sse.ts | 57 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/coverage.svg b/coverage.svg index 1abdc489f..0f3b1eb96 100644 --- a/coverage.svg +++ b/coverage.svg @@ -1 +1 @@ -Coverage: 99.34%Coverage99.34% \ No newline at end of file +Coverage: 99.03%Coverage99.03% \ No newline at end of file diff --git a/src/sse.ts b/src/sse.ts index a8ee9b690..300affefb 100644 --- a/src/sse.ts +++ b/src/sse.ts @@ -19,6 +19,30 @@ interface Emitter extends FlatObject { emit: (event: K, data: z.input) => void; } +const makeEventSchema = (event: string, data: z.ZodTypeAny) => + z.object({ + data, + event: z.literal(event), + id: z.string().optional(), + retry: z.number().int().positive().optional(), + }); + +const formatEvent = ( + events: E, + event: keyof E, + data: unknown, +) => + makeEventSchema(String(event), events[event]) + .transform((props) => + [ + `event: ${props.event}`, + `data: ${JSON.stringify(props.data)}`, + "", + "", // empty line: events separator + ].join("\n"), + ) + .parse({ event, data }); + const makeMiddleware = (events: E) => new Middleware({ handler: async ({ response }): Promise> => { @@ -30,26 +54,16 @@ const makeMiddleware = (events: E) => return { isClosed: () => response.writableEnded || response.closed, emit: (event, data) => { - response.write( - `event: ${String(event)}\ndata: ${JSON.stringify(events[event].parse(data))}\n\n`, - "utf-8", - ); + response.write(formatEvent(events, event, data), "utf-8"); response.flush(); }, }; }, }); -const makeEventSchema = (event: string, data: z.ZodTypeAny) => - z.object({ - data, - event: z.literal(event), - id: z.string().optional(), - retry: z.number().int().positive().optional(), - }); - -const makeResultHandler = (events: E) => - new ResultHandler({ +const makeResultHandler = (events: E) => { + const negativeSchema = makeEventSchema("error", z.string()); + return new ResultHandler({ positive: { mimeType: contentTypes.sse, schema: Object.entries(events) @@ -58,21 +72,22 @@ const makeResultHandler = (events: E) => ) .reduce((agg, schema) => agg.or(schema)), }, - negative: { - mimeType: contentTypes.sse, - schema: makeEventSchema("error", z.string()), - }, + negative: { mimeType: contentTypes.sse, schema: negativeSchema }, handler: async ({ response, error, logger, request, input }) => { if (error) { const httpError = ensureHttpError(error); logServerError(httpError, logger, request, input); - return void response - .status(httpError.statusCode) - .end(getPublicErrorMessage(httpError)); + const output = formatEvent( + { error: negativeSchema }, + "error", + getPublicErrorMessage(httpError), + ); + return void response.status(httpError.statusCode).end(output); } response.status(200).end(); }, }); +}; export const unstable_createEventStream = < E extends EventsMap,