Skip to content

Commit 40655f7

Browse files
committed
feat!: modernize - use fetch, WebStreams, TypeScript, ESM (#330)
BREAKING CHANGE: Drop support for Node.js versions below v18 BREAKING CHANGE: The module now uses a named export instead of a default export. BREAKING CHANGE: UMD bundle dropped. Use a bundler. BREAKING CHANGE: `headers` in init dict dropped, pass a custom `fetch` function instead. BREAKING CHANGE: HTTP/HTTPS proxy support dropped. Pass a custom `fetch` function instead. BREAKING CHANGE: `https.*` options dropped. Pass a custom `fetch` function that provides an agent/dispatcher instead. BREAKING CHANGE: New default reconnect delay: 3 seconds instead of 1 second. BREAKING CHANGE: Reconnecting after a redirect will now always use the original URL, even if the status code was HTTP 307.
1 parent 372d387 commit 40655f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+17573
-26314
lines changed

.editorconfig

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
1-
# http://editorconfig.org
1+
; editorconfig.org
22
root = true
3+
charset= utf8
34

45
[*]
5-
# Use hard or soft tabs
6-
indent_style = space
7-
8-
# Size of a single indent
9-
indent_size = tab
10-
11-
# Number of columns representing a tab character
12-
tab_width = 2
13-
14-
# Use line-feed as EOL indicator
156
end_of_line = lf
16-
17-
# Use UTF-8 character encoding for all files
18-
charset = utf-8
19-
20-
# Remove any whitespace characters preceding newline characters
21-
trim_trailing_whitespace = true
22-
23-
# Ensure file ends with a newline when saving
247
insert_final_newline = true
8+
trim_trailing_whitespace = true
9+
indent_style = space
10+
indent_size = 2
2511

2612
[*.md]
2713
trim_trailing_whitespace = false

.eslintignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/dist
2+
/coverage
3+
/demo/dist
4+
.nyc_output

.github/workflows/build.yml

Lines changed: 0 additions & 34 deletions
This file was deleted.

.github/workflows/release.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Release
2+
3+
# Workflow name based on selected inputs.
4+
# Fallback to default GitHub naming when expression evaluates to empty string
5+
run-name: >-
6+
${{
7+
inputs.release && 'Release ➤ Publish to NPM' ||
8+
''
9+
}}
10+
on:
11+
pull_request:
12+
push:
13+
branches: [main]
14+
workflow_dispatch:
15+
inputs:
16+
release:
17+
description: 'Publish new release'
18+
required: true
19+
default: false
20+
type: boolean
21+
22+
concurrency:
23+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
24+
cancel-in-progress: true
25+
26+
jobs:
27+
release:
28+
# only run if opt-in during workflow_dispatch
29+
name: 'Release: Publish to NPM'
30+
if: always() && github.event.inputs.release == 'true'
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
with:
35+
# Need to fetch entire commit history to
36+
# analyze every commit since last release
37+
fetch-depth: 0
38+
- uses: actions/setup-node@v4
39+
with:
40+
node-version: lts/*
41+
cache: npm
42+
- run: npm ci
43+
# Branches that will release new versions are defined in .releaserc.json
44+
- run: npx semantic-release
45+
# Don't allow interrupting the release step if the job is cancelled, as it can lead to an inconsistent state
46+
# e.g. git tags were pushed but it exited before `npm publish`
47+
if: always()
48+
env:
49+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50+
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

.github/workflows/test.yml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: Test
2+
on:
3+
push:
4+
workflow_dispatch:
5+
6+
jobs:
7+
testBrowser:
8+
name: 'Test: Browsers'
9+
timeout-minutes: 15
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: actions/setup-node@v4
14+
with:
15+
node-version: 20
16+
- name: Cache node modules
17+
id: cache-node-modules
18+
uses: actions/cache@v4
19+
env:
20+
cache-name: cache-node-modules
21+
with:
22+
path: '**/node_modules'
23+
key: ${{ runner.os }}-modules-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
24+
restore-keys: |
25+
${{ runner.os }}-modules-${{ env.cache-name }}-
26+
${{ runner.os }}-modules-
27+
${{ runner.os }}-
28+
- name: Install dependencies
29+
if: steps.cache-node-modules.outputs.cache-hit != 'true'
30+
run: npx playwright install && npm ci
31+
- name: Install Playwright Browsers
32+
run: npx playwright install --with-deps
33+
- name: Run browser tests
34+
run: npm run test:browser
35+
36+
testNode:
37+
name: 'Test: Node.js ${{ matrix.node-version }}'
38+
timeout-minutes: 15
39+
runs-on: ubuntu-latest
40+
strategy:
41+
matrix:
42+
node-version: ['18.x', '20.x', '22.x']
43+
steps:
44+
- uses: actions/checkout@v4
45+
- uses: actions/setup-node@v4
46+
with:
47+
node-version: ${{ matrix.node-version }}
48+
- name: Cache node modules
49+
id: cache-node-modules
50+
uses: actions/cache@v4
51+
env:
52+
cache-name: cache-node-modules
53+
with:
54+
path: '**/node_modules'
55+
key: ${{ runner.os }}-modules-${{ env.cache-name }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
56+
restore-keys: |
57+
${{ runner.os }}-modules-${{ env.cache-name }}--node-${{ matrix.node-version }}-
58+
${{ runner.os }}-modules-${{ env.cache-name }}
59+
${{ runner.os }}-modules-
60+
${{ runner.os }}-
61+
- name: Install dependencies
62+
if: steps.cache-node-modules.outputs.cache-hit != 'true'
63+
run: npm ci
64+
- name: Run tests
65+
run: npm run test:node
66+
67+
testDeno:
68+
name: 'Test: Deno'
69+
timeout-minutes: 15
70+
runs-on: ubuntu-latest
71+
steps:
72+
- uses: actions/checkout@v4
73+
- uses: denoland/setup-deno@v2
74+
with:
75+
deno-version: v2.x
76+
- name: Install dependencies
77+
run: deno install
78+
- name: Run tests
79+
run: npm run test:deno
80+
81+
testBun:
82+
name: 'Test: Bun'
83+
timeout-minutes: 15
84+
runs-on: ubuntu-latest
85+
steps:
86+
- uses: actions/checkout@v4
87+
- uses: oven-sh/setup-bun@v2
88+
with:
89+
bun-version: latest
90+
- name: Install Dependencies
91+
run: bun install --frozen-lockfile
92+
- name: Run tests
93+
run: npm run test:bun

.gitignore

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,50 @@
1-
/node_modules/
2-
npm-debug.log
3-
.DS_Store
4-
yarn.lock
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
6+
# Runtime data
7+
pids
8+
*.pid
9+
*.seed
10+
11+
# Directory for instrumented libs generated by jscoverage/JSCover
12+
lib-cov
13+
14+
# Coverage directory used by tools like istanbul
15+
coverage
16+
17+
# nyc test coverage
518
.nyc_output
6-
/coverage
19+
20+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21+
.grunt
22+
23+
# node-waf configuration
24+
.lock-wscript
25+
26+
# Compiled binary addons (http://nodejs.org/api/addons.html)
27+
build/Release
28+
29+
# Dependency directories
30+
node_modules
31+
jspm_packages
32+
33+
# Optional npm cache directory
34+
.npm
35+
36+
# Optional REPL history
37+
.node_repl_history
38+
39+
# macOS finder cache file
40+
.DS_Store
41+
42+
# VS Code settings
43+
.vscode
44+
45+
# Cache
46+
.cache
47+
48+
# Compiled output
49+
/dist
50+

.npmignore

Lines changed: 0 additions & 4 deletions
This file was deleted.

.releaserc.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "@sanity/semantic-release-preset",
3+
"branches": ["main"]
4+
}

MIGRATION.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Migration guide
2+
3+
## v2 to v3
4+
5+
### Code changes
6+
7+
#### Named export
8+
9+
The module now uses named exports instead of a default export. This means you need to change your import statements from:
10+
11+
**ESM:**
12+
13+
```diff
14+
-import EventSource from 'eventsource'
15+
import {EventSource} from 'eventsource'
16+
```
17+
18+
**CommonJS:**
19+
20+
```diff
21+
-const EventSource = require('eventsource')
22+
const {EventSource} = require('eventsource')
23+
```
24+
25+
#### UMD bundle dropped
26+
27+
If you were previously importing/using the `eventsource-polyfill.js` file/module, you should instead use a bundler like Vite, Rollup or similar. You can theoretically also use something like [esm.sh](https://esm.sh/) to load the module directly in the browser - eg:
28+
29+
```ts
30+
import {EventSource} from 'https://esm.sh/[email protected]'
31+
```
32+
33+
#### Custom headers dropped
34+
35+
In v2 you could specify custom headers through the `headers` property in the options/init object to the constructor. In v3, the same can be achieved by passing a custom `fetch` function:
36+
37+
```diff
38+
const es = new EventSource('https://my-server.com/sse', {
39+
- headers: {Authorization: 'Bearer foobar'}
40+
+ fetch: (input, init) => fetch(input, {
41+
+ ...init,
42+
+ headers: {...init.headers, Authorization: 'Bearer foobar'},
43+
+ }),
44+
})
45+
```
46+
47+
#### HTTP/HTTPS proxy dropped
48+
49+
Use a package like [`node-fetch-native`](https://github.com/unjs/node-fetch-native) to add proxy support, either through environment variables or explicit configuration.
50+
51+
```ts
52+
// npm install node-fetch-native --save
53+
import {fetch} from 'node-fetch-native/proxy'
54+
55+
const es = new EventSource('https://my-server.com/sse', {
56+
fetch: (input, init) => fetch(input, init),
57+
})
58+
```
59+
60+
#### Custom HTTPS/connection options dropped
61+
62+
Use a package like [`undici`](https://github.com/nodejs/undici) for more control of fetch options through the use of an [`Agent`](https://undici.nodejs.org/#/docs/api/Agent.md).
63+
64+
```ts
65+
// npm install undici --save
66+
import {fetch, Agent} from 'undici'
67+
68+
await fetch('https://my-server.com/sse', {
69+
dispatcher: new Agent({
70+
connect: {
71+
rejectUnauthorized: false,
72+
},
73+
}),
74+
})
75+
```
76+
77+
### Behavior changes
78+
79+
#### New default reconnect timeout
80+
81+
The default reconnect timeout is now 3 seconds - up from 1 second in v1/v2. This aligns better with browsers (Chrome and Safari, Firefox uses 5 seconds). Servers are (as always) free to set their own reconnect timeout through the `retry` field.
82+
83+
#### Redirect handling
84+
85+
Redirect handling now matches Chrome/Safari. On disconnects, we will always reconnect to the _original_ URL. In v1/v2, only HTTP 307 would reconnect to the original, while 301 and 302 would both redirect to the _destination_.
86+
87+
While the _ideal_ behavior would be for 301 and 308 to reconnect to the redirect _destination_, and 302/307 to reconnect to the _original_ URL, this is not possible to do cross-platform (cross-origin requests in browsers do not allow reading location headers, and redirect handling will have to be done manually).

0 commit comments

Comments
 (0)