feat(deploy): notify clients of new releases
This commit is contained in:
@@ -38,10 +38,22 @@ describe("SSE stream", () => {
|
||||
expect(decoder.decode((await reader.read()).value)).toBe(
|
||||
'event: invalidation\ndata: {"type":"page.updated","id":"opaque","version":7}\n\n',
|
||||
);
|
||||
channel.sendNamed("deployment", "release-2");
|
||||
expect(decoder.decode((await reader.read()).value)).toBe(
|
||||
"event: deployment\ndata: release-2\n\n",
|
||||
);
|
||||
await vi.advanceTimersByTimeAsync(50);
|
||||
expect(decoder.decode((await reader.read()).value)).toMatch(/^: heartbeat /u);
|
||||
await vi.advanceTimersByTimeAsync(49);
|
||||
expect((await reader.read()).done).toBe(true);
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("rejects event names that could inject another SSE field", () => {
|
||||
const channel = createEventStream();
|
||||
expect(() => channel.sendNamed("deployment\ndata", "release-2")).toThrow(
|
||||
"SSE event names",
|
||||
);
|
||||
channel.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,20 @@ export const SSE_MAX_LIFETIME_MS = 30 * 60 * 1_000;
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
function namedEvent(eventName: string, data: string): string {
|
||||
if (!/^[a-z0-9_-]+$/iu.test(eventName)) {
|
||||
throw new Error(
|
||||
"SSE event names may contain only letters, numbers, underscores, and hyphens.",
|
||||
);
|
||||
}
|
||||
const lines = data
|
||||
.replace(/\r\n?/gu, "\n")
|
||||
.split("\n")
|
||||
.map((line) => `data: ${line}`)
|
||||
.join("\n");
|
||||
return `event: ${eventName}\n${lines}\n\n`;
|
||||
}
|
||||
|
||||
export interface EventStreamOptions {
|
||||
signal?: AbortSignal;
|
||||
heartbeatMs?: number;
|
||||
@@ -15,6 +29,7 @@ export interface EventStreamOptions {
|
||||
export interface EventStreamChannel {
|
||||
stream: ReadableStream<Uint8Array>;
|
||||
send(event: InvalidationEvent): boolean;
|
||||
sendNamed(eventName: string, data: string): boolean;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
@@ -87,6 +102,9 @@ export function createEventStream(
|
||||
send(event) {
|
||||
return enqueue(`event: invalidation\ndata: ${JSON.stringify(event)}\n\n`);
|
||||
},
|
||||
sendNamed(eventName, data) {
|
||||
return enqueue(namedEvent(eventName, data));
|
||||
},
|
||||
close() {
|
||||
close(true);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user