Skip to content
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

Rewrite module to use fetch, WebStreams, TypeScript and ESM #330

Merged
merged 11 commits into from
Dec 7, 2024
Merged
24 changes: 5 additions & 19 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
# http://editorconfig.org
; editorconfig.org
root = true
charset= utf8

[*]
# Use hard or soft tabs
indent_style = space

# Size of a single indent
indent_size = tab

# Number of columns representing a tab character
tab_width = 2

# Use line-feed as EOL indicator
end_of_line = lf

# Use UTF-8 character encoding for all files
charset = utf-8

# Remove any whitespace characters preceding newline characters
trim_trailing_whitespace = true

# Ensure file ends with a newline when saving
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false
4 changes: 4 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/dist
/coverage
/demo/dist
.nyc_output
34 changes: 0 additions & 34 deletions .github/workflows/build.yml

This file was deleted.

50 changes: 50 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Release

# Workflow name based on selected inputs.
# Fallback to default GitHub naming when expression evaluates to empty string
run-name: >-
${{
inputs.release && 'Release ➤ Publish to NPM' ||
''
}}
on:
pull_request:
push:
branches: [main]
workflow_dispatch:
inputs:
release:
description: 'Publish new release'
required: true
default: false
type: boolean

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
release:
# only run if opt-in during workflow_dispatch
name: 'Release: Publish to NPM'
if: always() && github.event.inputs.release == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Need to fetch entire commit history to
# analyze every commit since last release
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- run: npm ci
# Branches that will release new versions are defined in .releaserc.json
- run: npx semantic-release
# Don't allow interrupting the release step if the job is cancelled, as it can lead to an inconsistent state
# e.g. git tags were pushed but it exited before `npm publish`
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
93 changes: 93 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Test
on:
push:
workflow_dispatch:

jobs:
testBrowser:
name: 'Test: Browsers'
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Cache node modules
id: cache-node-modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-modules-${{ env.cache-name }}-
${{ runner.os }}-modules-
${{ runner.os }}-
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npx playwright install && npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run browser tests
run: npm run test:browser

testNode:
name: 'Test: Node.js ${{ matrix.node-version }}'
timeout-minutes: 15
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['18.x', '20.x', '22.x']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Cache node modules
id: cache-node-modules
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ env.cache-name }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-modules-${{ env.cache-name }}--node-${{ matrix.node-version }}-
${{ runner.os }}-modules-${{ env.cache-name }}
${{ runner.os }}-modules-
${{ runner.os }}-
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci
- name: Run tests
run: npm run test:node

testDeno:
name: 'Test: Deno'
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v2
with:
deno-version: v2.x
- name: Install dependencies
run: deno install
- name: Run tests
run: npm run test:deno

testBun:
name: 'Test: Bun'
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Run tests
run: npm run test:bun
54 changes: 49 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,50 @@
/node_modules/
npm-debug.log
.DS_Store
yarn.lock
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output
/coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# macOS finder cache file
.DS_Store

# VS Code settings
.vscode

# Cache
.cache

# Compiled output
/dist

4 changes: 0 additions & 4 deletions .npmignore

This file was deleted.

4 changes: 4 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@sanity/semantic-release-preset",
"branches": ["main"]
}
87 changes: 87 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Migration guide

## v2 to v3

### Code changes

#### Named export

The module now uses named exports instead of a default export. This means you need to change your import statements from:

**ESM:**

```diff
-import EventSource from 'eventsource'
import {EventSource} from 'eventsource'
```

**CommonJS:**

```diff
-const EventSource = require('eventsource')
const {EventSource} = require('eventsource')
```

#### UMD bundle dropped

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:

```ts
import {EventSource} from 'https://esm.sh/[email protected]'
```

#### Custom headers dropped

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:

```diff
const es = new EventSource('https://my-server.com/sse', {
- headers: {Authorization: 'Bearer foobar'}
+ fetch: (input, init) => fetch(input, {
+ ...init,
+ headers: {...init.headers, Authorization: 'Bearer foobar'},
+ }),
})
```

#### HTTP/HTTPS proxy dropped

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.

```ts
// npm install node-fetch-native --save
import {fetch} from 'node-fetch-native/proxy'

const es = new EventSource('https://my-server.com/sse', {
fetch: (input, init) => fetch(input, init),
})
```

#### Custom HTTPS/connection options dropped

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).

```ts
// npm install undici --save
import {fetch, Agent} from 'undici'

await fetch('https://my-server.com/sse', {
dispatcher: new Agent({
connect: {
rejectUnauthorized: false,
},
}),
})
```

### Behavior changes

#### New default reconnect timeout

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.

#### Redirect handling

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_.

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).
Loading
Loading