Skip to content

feat: support server-sent events (SSE) #2299

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 52 commits into
base: main
Choose a base branch
from
Open

feat: support server-sent events (SSE) #2299

wants to merge 52 commits into from

Conversation

kettanaito
Copy link
Member

@kettanaito kettanaito commented Sep 30, 2024

Todo

  • Accept an OutgoingEvents type argument at sse() to make type-safe event property on the mock payload.
  • Support passthrough(). How do you intercept an EventSource, interact with it from the mock, and then passthrough after that?
  • Support event augmentation. How do you listen to the actual server's events, modify them, and send them back to the client?
  • Add test for withCredentials affecting the request cookies propagation.
  • How to handle passthrough()? What if I want to let the EventSource receive whichever events the actual server sends from now on? Still manual?
  • Add tests for .use() with sse().
  • Support retry
  • Add an invariant for typeof EventSource !== 'undefined' to let the developer know when they are using sse() in the environment that doesn't support it.
  • Fix the [Error [TypeError]: Failed to fetch] error during test/browser/sse-api/sse.server.connect.test.ts tests. It seems to be originating from the actual fetch() in the Service Worker (the scenarios call server.connect() on the SSE).
    • This is caused by the missing logger!
  • Finish sse.client.send.test.ts (the last test case is unfinished).
  • ⚠️ Consider exporting sse from msw root. While EventSource doesn't exist in Node.js, it exists in Deno. It might also be added to Node.js in the future. Afaik, there's nothing specific to browsers in our implementation. Keep the global check, export from the root for consistency.
  • Documentation (document server-sent events mswjs.io#470)

References

@kettanaito
Copy link
Member Author

Augmenting actual server-sent events

sse(url, ({ source, server }) => {
  // This creates a new EventSource request
  // and propagates all the events from here
  // to the underlying pending stream for `source`.
  server.connect()
})

Modifying the event is similar to that in WebSockets: prevent its default and send a new event:

sse(url, ({ source, server }) => {
  server.connect()

  server.addEventListener('message', (event) => {
    // Prevent this server event from reaching the client.
    event.preventDefault()
    const newEvent = modify(event.data)
    source.send(newEvent)
  })
})

May be a good idea to rename source to client and have a consistent SSE/WebSocket experience.

@kettanaito
Copy link
Member Author

kettanaito commented Oct 2, 2024

The tests are failing likely due to Node.js bump to v20 in CI.

Edit: Looks like an issue specific to a particular version of Node.js. Reported here: nodejs/undici#3676

@kettanaito kettanaito changed the title feat: support server-sent events feat: support server-sent events (SSE) Dec 5, 2024
@kettanaito
Copy link
Member Author

kettanaito commented Feb 21, 2025

Test update

I've resolved the infinite request issue that failed the majority of SSE tests (was using legacy passthrough header; migrating to accept: msw/passthrough + respecting that later on helped).

Two tests are still failing, both seem to be related to errors.

There's an odd rogue TypeError: Network error being thrown right when the Playwright window closes. Odd. The SSE request is not considered a network error from what I can tell. The error test case also fails. Maybe the two are related?

@@ -1,6 +1,6 @@
/**
* Determines if the given value is an object.
*/
export function isObject(value: any): boolean {
export function isObject(value: any): value is Record<string, any> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw, this isn't exactly type-safe in a way you'd assume: TS playground

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch.

Copy link

pkg-pr-new bot commented Jun 5, 2025

Open in StackBlitz

npm i https://pkg.pr.new/msw@2299

commit: 5ed58db

@kettanaito
Copy link
Member Author

[Error [TypeError]: Failed to fetch] error

Root cause

The log method of the HttpHandler class that SSE is extending was consuming the response body in await serializeResponse(args.response), which resulted in the said error. The body of the SSE cannot be consumed since it's being streamed and it incomplete.

Solution

The SSE handler must implement its own logger to solve this issue and also be more contextual.

@kettanaito kettanaito marked this pull request as ready for review July 12, 2025 10:40
@kettanaito
Copy link
Member Author

Update

The sse API is ready. You can install a preview release by following the instructions here. I will let the folks use it and share their feedback for a bit and then merge it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

First-class Server-Sent Events (SSE) API
2 participants