Skip to content

Commit

Permalink
Replace axios with native fetch()
Browse files Browse the repository at this point in the history
Try to use browser APIs in Node.js as much as possible, now that Node
supports fetch(). This requires bumping our Node.js requirement to
version 18.2.0 (fetch() is available since Node 18.0.0, but the
dispatcher option needed by http-cookie-agent only since 18.2.0), so we
can also drop the second GitHub Actions job.

fetch.js is now the base implementation of both node.js and browser.js;
fetch-node.js adds cookie support, while fetch-browser.js renames the
user-agent header to api-user-agent because Chrome doesn’t care about
web standards that clearly say user-agent is allowed to be set.

Unfortunately, http-cookie-agent still requires undici to be installed
even when it’s also built into Node, because Node doesn’t expose the
necessary internals to use the dispatcher option mentioned above.

Related to # 26 (though we don’t actually support file uploads yet) as
well as # 25.
  • Loading branch information
lucaswerkmeister committed Jun 29, 2023
1 parent df72fcc commit 35a8ffe
Show file tree
Hide file tree
Showing 13 changed files with 147 additions and 256 deletions.
2 changes: 1 addition & 1 deletion .browserslistrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Node 16.0.0
Node 18.2.0
Chrome 63
ChromeAndroid 63
Firefox 60
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm publish
Expand All @@ -30,7 +30,7 @@ jobs:
path: gh-pages
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- name: Update old documentation
Expand Down
18 changes: 1 addition & 17 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,12 @@ jobs:
env:
MEDIAWIKI_USERNAME: ${{ secrets.MEDIAWIKI_USERNAME }}
MEDIAWIKI_PASSWORD: ${{ secrets.MEDIAWIKI_PASSWORD }}
test-most:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm ci
- run: npm run test:unit
- run: npm run test:node
env:
MEDIAWIKI_USERNAME: ${{ secrets.MEDIAWIKI_USERNAME }}
MEDIAWIKI_PASSWORD: ${{ secrets.MEDIAWIKI_PASSWORD }}
- run: npm run test:readme
# don’t run test:lint in Node 16 – no point running it twice
# don’t run test:browser in Node 16 – needs Node fetch() (see #23)
test-package-lock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- run: npm i --package-lock-only
- run: git diff --exit-code
19 changes: 17 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,25 @@ but this file may sometimes contain later improvements (e.g. typo fixes).
## next (not yet released)

- BREAKING CHANGE:
m3api now requires at least Node.js version 16,
m3api now requires at least Node.js version 18.2.0,
up from Node 12.22.0 or Node 14.17.0 previously.
As part of this, `add-performance-global.js` has been removed,
The internal breaking changes below are also related to this.
- Internal Breaking Change:
The file `add-performance-global.js` has been removed,
as it is no longer needed.
- Internal Breaking Change:
The file `axios.js` has been removed,
as axios is no longer used.
Its role has been taken over by `fetch-node.js`.
- Internal Breaking Change:
The file `fetch.js` is no longer suitable for use in Chrome-like browsers.
The functionality of renaming the `user-agent` request header to `api-user-agent`
has been moved to `fetch-browser.js`.
- m3api now uses `fetch()` on all supported platforms.
The public interface, `browser.js` and `node.js`, can be used as before.
The internal interface has been rearranged,
with `fetch.js` now used for both backends,
augmented by `fetch-browser.js` and `fetch-node.js`.
- Improved `README.md` formatting for npmjs.com.
- Updated dependencies.

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ a library for interacting with the [MediaWiki Action API][] from JavaScript.
uses ES6 modules, and so on.
(See [§ compatibility](#Compatibility) below for details.)

It supports both Node.js (using axios) and browsers (using `fetch`).
It supports both Node.js and browsers.
The browser version has no external dependencies.

## Usage
Expand Down Expand Up @@ -395,7 +395,7 @@ Here are some guidelines or recommendations for creating m3api extension package

## Compatibility

In Node.js, m3api is compatible with Node 16 or later.
In Node.js, m3api is compatible with Node 18.2.0 or later.
Among major browsers, m3api is compatible with
Chrome 63, Firefox 60, Edge 79, Opera 50 (46 on Android), Safari 12, and Samsung Internet 8.0.
The relevant browser requirements of m3api are:
Expand All @@ -408,8 +408,8 @@ The relevant browser requirements of m3api are:
Supported since Chrome 63, Edge 79, Opera 50 (46 on Android), Safari 12, Samsung Internet 8.0.
(Firefox supported ES6 modules before async generators.)

The Node.js version requirement is based on the `performance` global being defined
(prior to Node 16, it had to be imported from `node:perf_hooks`).
The Node.js version requirement is based on `fetch()` being available
and supported by the `http-cookie-agent` package.
If you need support for earlier Node.js versions,
try using m3api v0.7.3.

Expand Down Expand Up @@ -444,7 +444,7 @@ The stable, public interface comprises the following items:

The internal interface additionally comprises the following items:

- The paths / existence of the `axios.js`, `fetch.js` and `combine.js` files.
- The paths / existence of the `fetch.js`, `fetch-browser.js`, `fetch-node.js` and `combine.js` files.

- All exports of those files, or of files in the public interface, that have not been marked `@private`.

Expand Down
56 changes: 0 additions & 56 deletions axios.js

This file was deleted.

4 changes: 2 additions & 2 deletions browser.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FetchSession } from './fetch.js';
import { FetchBrowserSession } from './fetch-browser.js';
import { mixCombiningSessionInto } from './combine.js';

export default class BrowserSession extends FetchSession {
export default class BrowserSession extends FetchBrowserSession {
}

mixCombiningSessionInto( BrowserSession );
Expand Down
20 changes: 20 additions & 0 deletions fetch-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { FetchSession } from './fetch.js';

class FetchBrowserSession extends FetchSession {

getFetchOptions( headers ) {
const { 'user-agent': userAgent, ...otherHeaders } = headers;
return {
...super.getFetchOptions( headers ),
headers: {
...otherHeaders,
'api-user-agent': userAgent,
},
};
}

}

export {
FetchBrowserSession,
};
26 changes: 26 additions & 0 deletions fetch-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { CookieAgent } from 'http-cookie-agent/undici';
import { CookieJar } from 'tough-cookie';
import { FetchSession } from './fetch.js';

class FetchNodeSession extends FetchSession {

constructor( apiUrl, defaultParams = {}, defaultOptions = {} ) {
super( apiUrl, defaultParams, defaultOptions );

this.agent = new CookieAgent( {
cookies: { jar: new CookieJar() },
} );
}

getFetchOptions( headers ) {
return {
...super.getFetchOptions( headers ),
dispatcher: this.agent,
};
}

}

export {
FetchNodeSession,
};
26 changes: 14 additions & 12 deletions fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,36 @@ class FetchSession extends Session {

constructor( apiUrl, defaultParams = {}, defaultOptions = {} ) {
super( apiUrl, defaultParams, defaultOptions );
this.fetchOptions = {};
}

/**
* Get the fetch() options for this request.
*
* @protected
* @param {Object} headers
* @return {Object}
*/
getFetchOptions( headers ) {
return { headers };
}

async internalGet( apiUrl, params, headers ) {
const url = new URL( apiUrl );
url.search = new URLSearchParams( params );
const { 'user-agent': userAgent, ...otherHeaders } = headers;
// eslint-disable-next-line compat/compat
const response = await fetch( url, {
headers: {
...otherHeaders,
'api-user-agent': userAgent,
},
...this.getFetchOptions( headers ),
} );
return transformResponse( response );
}

async internalPost( apiUrl, urlParams, bodyParams, headers ) {
const url = new URL( apiUrl );
url.search = new URLSearchParams( urlParams );
const { 'user-agent': userAgent, ...otherHeaders } = headers;
// eslint-disable-next-line compat/compat
const response = await fetch( url, {
...this.getFetchOptions( headers ),
method: 'POST',
body: new URLSearchParams( bodyParams ),
headers: {
...otherHeaders,
'api-user-agent': userAgent,
},
} );
return transformResponse( response );
}
Expand Down
4 changes: 2 additions & 2 deletions node.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AxiosSession } from './axios.js';
import { FetchNodeSession } from './fetch-node.js';
import { mixCombiningSessionInto } from './combine.js';

export default class NodeSession extends AxiosSession {
export default class NodeSession extends FetchNodeSession {
}

mixCombiningSessionInto( NodeSession );
Expand Down

0 comments on commit 35a8ffe

Please sign in to comment.