Contributions are welcome, no matter how large or small, but:
- Please open an issue before starting work on a feature or large change.
- We generally do not accept PRs that extend the API or surface of the library. The idea behind this module is to provide a (mostly) spec-compliant implementation of the EventSource API, and we want to keep it as simple as possible.
- Changes needs to be compatible with the EventSource specification as far as possible, as well as for all the supported environments.
Before contributing, please read our code of conduct.
Then make sure you have Node.js version 18 or newer.
git clone git@github.com:EventSource/eventsource.git
cd eventsource
npm install
npm run build
npm testThe 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.jsnpm run test:browser- Chromium, Firefox and WebKit, via Playwrightnpm run test:bun- Bunnpm run test:deno- Denonpm run test:happy-dom- happy-domnpm run test:workerd- workerd (Cloudflare Workers), via miniflarenpm run test:types- type compatibility with the WhatWGEventSourcenpm 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.
Every environment gates CI. Two of them deviate from the rest in ways that are the runtime's doing rather than ours, and those tests are asserted as known failures with test.fails rather than skipped or excluded, so each one turns red - prompting removal of the workaround - once the runtime is fixed:
- workerd, seven of the
on*handler tests. ItsEventTargetdispatcheson<type>handler properties itself, on top of theaddEventListenercall ouron*setters make, so those handlers fire more than once, assigningnullonly removes one registration, and they fire ahead of listeners registered before them (cloudflare/workerd#6022). The twoon*tests that register a single handler and check that it fired are unaffected and run normally. - happy-dom, the four cross-origin redirect tests. It does not strip
Authorizationwhen a request is redirected to a different origin, as the fetch spec requires.
The per-runtime message pattern in the extended-properties test is the other place runtimes differ: a refused connection is reported as fetch failed on Node, connect ECONNREFUSED … under happy-dom, Network connection lost. on workerd, and something different again in each browser engine. That one is only wording, so the pattern accepts all of 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.
- Anything in the
mainbranch 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. - To work on something new, create a descriptively named branch off of
main(ie:feat/some-new-feature). - Commit to that branch locally and regularly push your work to the same named branch on the remote.
- Rebase your feature branch regularly against
main. Make sure its even withmainwhile it is awaiting review. - Pull requests should be as ready as possible for merge. Unless stated otherwise, it should be safe to assume that:
- The changes/feature are reviewed and tested by you
- You think it's production ready
- The code is linted and the test suite is passing
We use Conventional Commits for our commit messages. This means that each commit should be prefixed with a type and a message. The message should be in the imperative mood. For example:
feat: allow specifying something
fix: double reconnect attempt on error
docs: clarify usage of `fetch` option
Releases are driven by changesets, not by commit messages. If your pull request changes anything under src/, add a changeset describing the change:
npm run changesetPick patch, minor or major, then write a sentence aimed at someone reading the changelog. Commit the generated file in .changeset/ along with the rest of your changes.
Changes that do not affect the published package - docs, tests, CI, internal refactors that leave behaviour untouched - do not need one. If a change touches src/ but genuinely should not trigger a release, run npx changeset add --empty.
Maintainers only. When changesets land on main, the release workflow opens a "Version Packages" pull request that applies the version bump and updates the changelog. Merging that pull request publishes to npm, pushes the git tag and creates the GitHub release.
Publishing uses npm trusted publishing over OIDC, so there is no npm token to rotate. The trusted publisher is configured on npm against the release.yml workflow in this repository - renaming that file will break publishing until the npm setting is updated to match. The npm configuration pins the workflow file but not the branch, which is what lets the same workflow release from maintenance branches.
Older majors that are still supported are patched from a long-lived vN branch - currently just v4, for the 4.x line. It sits at the latest release of that major and carries its own changesets configuration, so the release workflow behaves there exactly as it does on main:
- Branch off the maintenance branch, e.g.
git switch -c fix/some-backport v4. - Apply the fix and add a changeset (
npm run changeset). Usepatchorminor- a backport must never be amajor, since that would collide with a version that already exists on a newer line. - Open the pull request against the maintenance branch, not
main. - Merging it opens a "Version Packages" pull request against that same branch. Merging that one publishes.
Two things differ from a release off main:
- The npm dist-tag matches the branch, so a 4.x release publishes under
v4rather thanlatest. Users on that line install it withnpm install eventsource@v4, andnpm install eventsourcekeeps resolving to the current major. - The GitHub release is demoted from "Latest" after publishing, so the newest major keeps that badge.
Both are handled by the release workflow; there is nothing to pass by hand. If the fix also applies to the current major, land it on main separately - nothing is merged forward automatically.
The workflow's branch filter accepts any vN branch, so a v5 branch can be cut the same way once main moves on to 6.x. The archived v1.x and v2.x branches predate the convention, do not match the filter, and are not released from. 3.x is no longer supported - see SECURITY.md.
If you find a security vulnerability, do NOT open an issue. Use the [https://github.com/EventSource/eventsource/security/advisories/new](GitHub Security Advisory) page instead.
When filing an issue, make sure to answer these six questions:
- Which versions of the
eventsourcemodule are you using? - What operating system are you using?
- Which versions of Node.js/browser/runtime are you running?
- What happened that caused the issue?
- What did you expect to happen?
- What actually happend?
- What was the data sent from the server that caused the issue?