Add SSE id: and retry: field support to RiverEmitter#4
Conversation
…mitter Co-authored-by: Bewinxed <9145989+Bewinxed@users.noreply.github.com>
Co-authored-by: Bewinxed <9145989+Bewinxed@users.noreply.github.com>
id: and retry: field support to RiverEmitter
|
@copilot review this PR |
Co-authored-by: Bewinxed <9145989+Bewinxed@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds SSE id: and retry: field support to RiverEmitter, addressing the lack of two critical SSE spec fields that enable client reconnection with Last-Event-ID support and configurable retry intervals. The stream() method is also extended to accept and pass lastEventId to the callback for server-side reconnection catch-up logic.
Changes:
EmitPayloadtype extended with optionalid?: stringandretry?: numberfields, stripped before JSON serializationemitSingleEvent/emitStreamEventupdated to extract and prependid:/retry:SSE fields via newextractSSEFields()andbuildSSEPrefix()helpersstream()method extended with an optionallastEventIdoption, passed as a 3rd argument to the callback
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/types/core.ts |
Extends EmitPayload type to include optional id and retry SSE framing fields |
src/server/server.ts |
Adds helper methods and updates emit logic to write id: / retry: SSE fields; extends stream() to accept and forward lastEventId |
tests/core/sse-fields.test.ts |
New test file covering SSE field inclusion, backward compatibility, and lastEventId propagation |
package-lock.json |
New lock file added (inconsistent with project's use of bun.lockb and pnpm-lock.yaml) |
.gitignore |
Adds package-lock.json to ignored files (contradictory with the above file being committed) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| export type EmitPayload<T, K extends keyof T> = T[K] extends BaseEvent | ||
| ? Omit<T[K], 'type' | 'stream' | 'chunkSize'> | ||
| ? Omit<T[K], 'type' | 'stream' | 'chunkSize'> & { id?: string; retry?: number } |
There was a problem hiding this comment.
If a user-defined event schema includes an id (string) or retry (number) property as part of the event data, the extractSSEFields function will silently strip those properties from the serialized data and treat them as SSE framing fields instead. This would cause unexpected data loss without any warning or error. For example, a user_event defined with { data: { id: 'user-123', name: 'Alice' } } would have id removed from the JSON payload silently. Consider adding a check or warning, or renaming the SSE framing properties to something namespace-prefixed (e.g., sseId / sseRetry) to avoid collisions with user data.
| // Create payload with chunked data, preserving other properties (excluding SSE fields) | ||
| const chunkPayload = { ...dataPayload, data: chunk } as any; | ||
| const event_data = `${prefix}event: ${String( | ||
| event_type | ||
| )}\ndata: ${JSON.stringify(chunkPayload)}\n\n`; | ||
| writeSuccess = await this.writeChunk(writer, event_data); |
There was a problem hiding this comment.
In emitStreamEvent, when streaming events have multiple chunks, the SSE id: and retry: prefix is prepended to every chunk of the stream. This means a single emit call for a large dataset will produce many SSE messages each beginning with the same id: value. Repeating id: on every chunk updates the client's lastEventId repeatedly to the same value and sends unnecessary bytes per chunk. According to the SSE spec, id: should typically be emitted once per logical event, not per chunk. Consider only prepending the prefix on the first chunk of the stream.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
id:/retry:/lastEventIdchangespackage-lock.jsonto keep the PR scoped to library changesOriginal prompt
id:andretry:field support to RiverEmitter #3✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.