Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/workerd-message-event-props.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'eventsource': patch
---

Fixed `origin` and `lastEventId` being empty on Cloudflare Workers

workerd accepts `data` from the `MessageEvent` constructor's init dictionary but silently drops `origin` and `lastEventId`, so message events dispatched on Cloudflare Workers arrived with `origin` set to `null` and `lastEventId` to an empty string. Both are now assigned explicitly when the constructor did not take them, which leaves every other runtime untouched.
62 changes: 59 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ jobs:
${{ runner.os }}-
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npx playwright install && npm ci
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
run: npx playwright install --with-deps chromium firefox webkit
- name: Run browser tests
run: npm run test:browser

Expand Down Expand Up @@ -62,7 +62,7 @@ jobs:
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci
- name: Run tests
run: npm run test:node
run: npm test

testTypes:
name: 'Test: Types'
Expand Down Expand Up @@ -105,3 +105,59 @@ jobs:
run: bun install --frozen-lockfile
- name: Run tests
run: npm run test:bun

# Known-failing environments, reported but never gating.
#
# happy-dom reports the test server's requests as cross-origin and blocks them. workerd's
# remaining failures all trace to cloudflare/workerd#6022, where its `EventTarget` dispatches
# `on<type>` handler properties itself and so fires our `on*` handlers twice.
#
# `continue-on-error` is deliberately on the *step* rather than the job. At job level it keeps
# the overall run green but the job still reports a check run with a `failure` conclusion, which
# is what puts a red X on the commit. At step level the job itself succeeds, so the commit stays
# green, and the outcome is written to the run summary to keep the result visible. Move
# `continue-on-error` up to the job (or drop it) once these are expected to pass.
testKnownFailing:
name: 'Test: ${{ matrix.suite }} (informational)'
timeout-minutes: 15
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
suite: ['happy-dom', 'workerd']
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
- name: Install dependencies
run: npm ci
- name: Run tests
id: tests
continue-on-error: true
run: |
# `pipefail` is not set in the default shell, so without it `tee` swallows the exit
# code and the step reports success no matter what the suite did.
set -o pipefail
npm run test:${{ matrix.suite }} 2>&1 | tee /tmp/${{ matrix.suite }}.log
- name: Summarise
if: always()
run: |
# Vitest forces colour on in CI, so the escape sequences have to come off before
# anything can be matched at the start of a line.
sed -E 's/\x1b\[[0-9;]*[mGKH]//g' '/tmp/${{ matrix.suite }}.log' > /tmp/plain.log
{
if [ '${{ steps.tests.outcome }}' = 'success' ]; then
echo '### ${{ matrix.suite }}: passing 🎉'
echo
echo 'This environment is no longer failing. It can be promoted to a gating job.'
else
echo '### ${{ matrix.suite }}: failing, as expected'
echo
echo '```'
grep -E '^ +(Tests|Test Files) ' /tmp/plain.log || true
echo '```'
echo
grep -E '^ FAIL ' /tmp/plain.log | sed 's/^ FAIL /- /' || true
fi
} | tee -a "$GITHUB_STEP_SUMMARY"
19 changes: 19 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ npm run build
npm test
```

## Running the tests

The suite in `test/client.test.ts` runs against a real HTTP server in every supported environment - there are no mocks and no simulated DOM. Each environment gets its own Vitest config, and `npm test` covers Node only:

- `npm test` - Node.js
- `npm run test:browser` - Chromium, Firefox and WebKit, via Playwright
- `npm run test:bun` - Bun
- `npm run test:deno` - Deno
- `npm run test:happy-dom` - happy-dom
- `npm run test:workerd` - workerd (Cloudflare Workers), via miniflare
- `npm run test:types` - type compatibility with the WhatWG `EventSource`
- `npm run test:all` - all of the above, in sequence

The browser tests need Playwright's browsers installed once, with `npx playwright install chromium firefox webkit`.

The happy-dom and workerd suites are expected to fail today and do not gate CI. happy-dom reports the test server's requests as cross-origin and blocks them. workerd's remaining failures all come from [cloudflare/workerd#6022](https://github.com/cloudflare/workerd/issues/6022): its `EventTarget` dispatches `on<type>` handler properties itself, on top of the `addEventListener` call our `on*` setters make, so those handlers fire twice, assigning `null` only removes one registration, and they fire ahead of listeners registered before them.

The browser suite is the one place where the endpoints are not served by a standalone server. Vitest serves the test page from its own Vite server, so the endpoints are mounted onto that same server (`test/helpers/ssePlugin.ts`) to keep the page and the endpoints same-origin. Serving them separately would make every request cross-origin and silently change what the CORS, cookie and redirect tests actually assert.

# Workflow guidelines

- Anything in the `main` branch is scheduled for the next release and should generally be ready to released, although there are exceptions when there are multiple features that are dependent on each other.
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ npm install --save eventsource
- Edge >= 84
- Deno >= 2
- Bun >= 1.1.23
- Cloudflare Workers: [partial](#cloudflare-workers)

Basically, any environment that supports:

Expand Down Expand Up @@ -97,6 +98,22 @@ error TS2304: Cannot find name 'EventTarget'.
error TS2304: Cannot find name 'MessageEvent'.
```

### Cloudflare Workers

Cloudflare Workers are supported, with one caveat: use `addEventListener()` rather than the `onmessage`, `onopen` and `onerror` properties.

workerd's `EventTarget` dispatches `on<type>` handler properties itself, on top of the listener this module registers, so a handler assigned that way is called twice per event - and assigning `null` only removes one of the two registrations, so it does not unsubscribe ([workerd#6022](https://github.com/cloudflare/workerd/issues/6022)).

```js
// Called twice per message on Cloudflare Workers
eventSource.onmessage = (event) => console.log(event.data)

// Use this instead
eventSource.addEventListener('message', (event) => console.log(event.data))
```

workerd also drops `origin` and `lastEventId` from the `MessageEvent` constructor. This module assigns them itself, so both are correct on Workers today; the workaround goes away once [workerd#6995](https://github.com/cloudflare/workerd/pull/6995) lands.

## Migrating from v1 / v2

See [MIGRATION.md](MIGRATION.md#v2-to-v3) for a detailed migration guide.
Expand Down
Loading
Loading