feat: isomorphic sockets and use eventemitter3 (#2514)

Reduces filesize by 3kb minified and gzipped or 2.8% and reduces bundler
config steps.

- Move `WSWrapper` to `@xrpl/isomorphic` to remove the need to add
mapping of `ws` to `WSWrapper` file in bundler configs
- Switch to `eventemitter3` which is smaller than `events` by almost
1kb and will not require a mapping to node's `events` in `vite`
bundling. `webpack` always automatically maps it.
    - max listeners is not a thing for `eventemitter3` so we do not
need to set it to `Infinity`. `ws` uses the native event emitter which
does still need that to be set.
- Remove `eventemitter2` which was only used in tests and was replaced
with `eventemitter3`

BREAKING CHANGE: Config for frontend bundlers has changed for `ws`.
This commit is contained in:
Caleb Kniffen
2023-10-17 21:00:29 -05:00
parent 3c8a990e6a
commit 294509cf79
16 changed files with 188 additions and 116 deletions

View File

@@ -37,11 +37,6 @@ To use `xrpl.js` with React, you need to install shims for core NodeJS modules.
const webpack = require("webpack"); const webpack = require("webpack");
module.exports = function override(config) { module.exports = function override(config) {
const fallback = config.resolve.fallback || {};
Object.assign(fallback, {
ws: require.resolve("xrpl/dist/npm/client/WSWrapper"),
});
config.resolve.fallback = fallback;
config.plugins = (config.plugins || []).concat([ config.plugins = (config.plugins || []).concat([
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
process: "process/browser", process: "process/browser",
@@ -137,20 +132,15 @@ export default defineConfig({
}), }),
], ],
}, },
}, },
build: { build: {
rollupOptions: { rollupOptions: {
plugins: [ plugins: [
polyfillNode(), polyfillNode(),
] ]
} }
},
resolve: {
alias: {
events: 'events',
ws: 'xrpl/dist/npm/client/WSWrapper',
}, },
}}) })
``` ```
3. Install the config dependencies and xrpl (e.g. using this command) 3. Install the config dependencies and xrpl (e.g. using this command)
@@ -158,9 +148,7 @@ resolve: {
```shell ```shell
npm install --save-dev @esbuild-plugins/node-globals-polyfill \ npm install --save-dev @esbuild-plugins/node-globals-polyfill \
rollup-plugin-polyfill-node \ rollup-plugin-polyfill-node \
&& npm install && npm install xrpl
events \
xrpl
``` ```
### Using xrpl.js with Deno ### Using xrpl.js with Deno

111
package-lock.json generated
View File

@@ -39,7 +39,6 @@
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-tsdoc": "^0.2.16", "eslint-plugin-tsdoc": "^0.2.16",
"eventemitter2": "^6.0.0",
"expect": "^29.3.1", "expect": "^29.3.1",
"jest": "^29.3.1", "jest": "^29.3.1",
"jest-mock": "^29.3.1", "jest-mock": "^29.3.1",
@@ -6668,17 +6667,10 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/eventemitter2": {
"version": "6.4.9",
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz",
"integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==",
"dev": true
},
"node_modules/eventemitter3": { "node_modules/eventemitter3": {
"version": "4.0.7", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
"dev": true
}, },
"node_modules/events": { "node_modules/events": {
"version": "3.3.0", "version": "3.3.0",
@@ -7794,6 +7786,12 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/http-proxy/node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"dev": true
},
"node_modules/http-signature": { "node_modules/http-signature": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -11607,6 +11605,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/p-queue/node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"dev": true
},
"node_modules/p-reduce": { "node_modules/p-reduce": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz",
@@ -15399,15 +15403,38 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@noble/hashes": "^1.0.0" "@noble/hashes": "^1.0.0",
"eventemitter3": "5.0.1",
"ws": "^8.13.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.18.38" "@types/node": "^16.18.38",
"@types/ws": "^8.5.6"
}, },
"engines": { "engines": {
"node": ">=16.0.0" "node": ">=16.0.0"
} }
}, },
"packages/isomorphic/node_modules/ws": {
"version": "8.14.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"packages/ripple-address-codec": { "packages/ripple-address-codec": {
"version": "4.3.1", "version": "4.3.1",
"license": "ISC", "license": "ISC",
@@ -15466,6 +15493,7 @@
"@xrplf/secret-numbers": "^1.0.0", "@xrplf/secret-numbers": "^1.0.0",
"bignumber.js": "^9.0.0", "bignumber.js": "^9.0.0",
"cross-fetch": "^4.0.0", "cross-fetch": "^4.0.0",
"eventemitter3": "^5.0.1",
"ripple-address-codec": "^4.3.1", "ripple-address-codec": "^4.3.1",
"ripple-binary-codec": "^1.11.0", "ripple-binary-codec": "^1.11.0",
"ripple-keypairs": "^1.3.1", "ripple-keypairs": "^1.3.1",
@@ -15473,6 +15501,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.18.38", "@types/node": "^16.18.38",
"eventemitter3": "^5.0.1",
"https-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.1",
"karma": "^6.4.1", "karma": "^6.4.1",
"karma-chrome-launcher": "^3.1.1", "karma-chrome-launcher": "^3.1.1",
@@ -15481,7 +15510,8 @@
"lodash": "^4.17.4", "lodash": "^4.17.4",
"react": "^18.2.0", "react": "^18.2.0",
"run-s": "^0.0.0", "run-s": "^0.0.0",
"typedoc": "0.25.0" "typedoc": "0.25.0",
"ws": "^8.14.2"
}, },
"engines": { "engines": {
"node": ">=16.0.0" "node": ">=16.0.0"
@@ -15511,9 +15541,10 @@
} }
}, },
"packages/xrpl/node_modules/ws": { "packages/xrpl/node_modules/ws": {
"version": "8.13.0", "version": "8.14.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
"dev": true,
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
}, },
@@ -18345,7 +18376,18 @@
"version": "file:packages/isomorphic", "version": "file:packages/isomorphic",
"requires": { "requires": {
"@noble/hashes": "^1.0.0", "@noble/hashes": "^1.0.0",
"@types/node": "^16.18.38" "@types/node": "^16.18.38",
"@types/ws": "^8.5.6",
"eventemitter3": "5.0.1",
"ws": "^8.13.0"
},
"dependencies": {
"ws": {
"version": "8.14.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
"requires": {}
}
} }
}, },
"@xrplf/prettier-config": { "@xrplf/prettier-config": {
@@ -20764,17 +20806,10 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true "dev": true
}, },
"eventemitter2": {
"version": "6.4.9",
"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz",
"integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==",
"dev": true
},
"eventemitter3": { "eventemitter3": {
"version": "4.0.7", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
"dev": true
}, },
"events": { "events": {
"version": "3.3.0", "version": "3.3.0",
@@ -21609,6 +21644,14 @@
"eventemitter3": "^4.0.0", "eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0", "follow-redirects": "^1.0.0",
"requires-port": "^1.0.0" "requires-port": "^1.0.0"
},
"dependencies": {
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"dev": true
}
} }
}, },
"http-proxy-agent": { "http-proxy-agent": {
@@ -24579,6 +24622,14 @@
"requires": { "requires": {
"eventemitter3": "^4.0.4", "eventemitter3": "^4.0.4",
"p-timeout": "^3.2.0" "p-timeout": "^3.2.0"
},
"dependencies": {
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"dev": true
}
} }
}, },
"p-reduce": { "p-reduce": {
@@ -27362,6 +27413,7 @@
"@xrplf/secret-numbers": "^1.0.0", "@xrplf/secret-numbers": "^1.0.0",
"bignumber.js": "^9.0.0", "bignumber.js": "^9.0.0",
"cross-fetch": "^4.0.0", "cross-fetch": "^4.0.0",
"eventemitter3": "^5.0.1",
"https-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.1",
"karma": "^6.4.1", "karma": "^6.4.1",
"karma-chrome-launcher": "^3.1.1", "karma-chrome-launcher": "^3.1.1",
@@ -27374,7 +27426,7 @@
"ripple-keypairs": "^1.3.1", "ripple-keypairs": "^1.3.1",
"run-s": "^0.0.0", "run-s": "^0.0.0",
"typedoc": "0.25.0", "typedoc": "0.25.0",
"ws": "^8.2.2" "ws": "^8.14.2"
}, },
"dependencies": { "dependencies": {
"agent-base": { "agent-base": {
@@ -27396,6 +27448,7 @@
"version": "8.16.0", "version": "8.16.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"dev": true,
"requires": {} "requires": {}
} }
} }

View File

@@ -44,7 +44,6 @@
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-tsdoc": "^0.2.16", "eslint-plugin-tsdoc": "^0.2.16",
"eventemitter2": "^6.0.0",
"expect": "^29.3.1", "expect": "^29.3.1",
"jest": "^29.3.1", "jest": "^29.3.1",
"jest-mock": "^29.3.1", "jest-mock": "^29.3.1",

View File

@@ -10,3 +10,4 @@ Initial release providing isomorphic and tree-shakable implementations of:
- bytesToHash - bytesToHash
- hashToBytes - hashToBytes
- randomBytes - randomBytes
- ws

View File

@@ -97,4 +97,8 @@ console.log(hexToBytes('DEADBEEF')) // [222, 173, 190, 239]
### `@xrplf/isomorphic/ws` ### `@xrplf/isomorphic/ws`
// TODO: Websocket Wrapper and `ws` ```typescript
import WebSocket from '@xrplf/isomorphic/ws'
const socket = new WebSocket('wss://localhost:8080')
```

View File

@@ -21,16 +21,20 @@
"sha512/*", "sha512/*",
"ripemd160/*", "ripemd160/*",
"src/*", "src/*",
"utils/*" "utils/*",
"ws/*"
], ],
"directories": { "directories": {
"test": "test" "test": "test"
}, },
"dependencies": { "dependencies": {
"@noble/hashes": "^1.0.0" "@noble/hashes": "^1.0.0",
"eventemitter3": "5.0.1",
"ws": "^8.13.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.18.38" "@types/node": "^16.18.38",
"@types/ws": "^8.5.6"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -1,5 +1,5 @@
/* eslint-disable max-classes-per-file -- Needs to be a wrapper for ws */ /* eslint-disable max-classes-per-file -- Needs to be a wrapper for ws */
import { EventEmitter } from 'events' import { EventEmitter } from 'eventemitter3'
// Define the global WebSocket class found on the native browser // Define the global WebSocket class found on the native browser
declare class WebSocket { declare class WebSocket {
@@ -31,7 +31,7 @@ export default class WSWrapper extends EventEmitter {
public static CONNECTING = 0 public static CONNECTING = 0
public static OPEN = 1 public static OPEN = 1
public static CLOSING = 2 public static CLOSING = 2
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- magic number is being defined here
public static CLOSED = 3 public static CLOSED = 3
private readonly ws: WebSocket private readonly ws: WebSocket
@@ -48,7 +48,6 @@ export default class WSWrapper extends EventEmitter {
_websocketOptions: WSWrapperOptions, _websocketOptions: WSWrapperOptions,
) { ) {
super() super()
this.setMaxListeners(Infinity)
this.ws = new WebSocket(url) this.ws = new WebSocket(url)

View File

@@ -0,0 +1,10 @@
import WebSocket from 'ws'
export default class Socket extends WebSocket {
constructor(...args) {
super(args[0], args[1], args[2])
this.setMaxListeners(Infinity)
}
}
export type ClientOptions = WebSocket.ClientOptions

View File

@@ -0,0 +1,11 @@
{
"name": "@xrplf/isomorphic/ws",
"private": true,
"main": "../dist/ws",
"types": "../dist/ws",
"browser": "../dist/ws/browser.js",
"dependencies": {
"ws": "^8.13.0",
"eventemitter3": "^5.0.1"
}
}

View File

@@ -17,10 +17,11 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
* Uses `@xrplf/secret-numbers` instead of `xrpl-secret-numbers` * Uses `@xrplf/secret-numbers` instead of `xrpl-secret-numbers`
* Improve key algorithm detection. It will now throw Errors with helpful messages * Improve key algorithm detection. It will now throw Errors with helpful messages
* Move `authorizeChannel` from `wallet/signer` to `wallet/authorizeChannel` to solve a circular dependency issue. * Move `authorizeChannel` from `wallet/signer` to `wallet/authorizeChannel` to solve a circular dependency issue.
* When using a bundler you must remove the mapping of `ws` to `WSWrapper`. ex. `ws: 'xrpl/dist/npm/client/WSWrapper'`. See [../UNIQUE_STEPS](Unique Steps) for the new, much smaller, configs.
### Bundling Changes ### Bundling Changes
* Bundler configurations are much more simplified. Bundler configurations are much more simplified. See [../UNIQUE_STEPS](Unique Steps) for the new, much smaller, configs.
* removed the following polyfills: * removed the following polyfills:
* `assert` * `assert`
* `crypto-browserify` * `crypto-browserify`
* `https-browserify` * `https-browserify`
@@ -29,7 +30,9 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
* `stream-http` * `stream-http`
* `url` * `url`
* `util` - previously added automatically by `webpack` * `util` - previously added automatically by `webpack`
* Removed mappings for: * `events` - previously added automatically by `webpack` but manual for `vite`
* Removed mappings for:
* `ws` to `WsWrapper`
* Excluding `https-proxy-agent` * Excluding `https-proxy-agent`
### Changed ### Changed

View File

@@ -28,6 +28,7 @@
"@xrplf/secret-numbers": "^1.0.0", "@xrplf/secret-numbers": "^1.0.0",
"bignumber.js": "^9.0.0", "bignumber.js": "^9.0.0",
"cross-fetch": "^4.0.0", "cross-fetch": "^4.0.0",
"eventemitter3": "^5.0.1",
"ripple-address-codec": "^4.3.1", "ripple-address-codec": "^4.3.1",
"ripple-binary-codec": "^1.11.0", "ripple-binary-codec": "^1.11.0",
"ripple-keypairs": "^1.3.1", "ripple-keypairs": "^1.3.1",
@@ -35,6 +36,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.18.38", "@types/node": "^16.18.38",
"eventemitter3": "^5.0.1",
"https-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.1",
"karma": "^6.4.1", "karma": "^6.4.1",
"karma-chrome-launcher": "^3.1.1", "karma-chrome-launcher": "^3.1.1",
@@ -43,7 +45,8 @@
"lodash": "^4.17.4", "lodash": "^4.17.4",
"run-s": "^0.0.0", "run-s": "^0.0.0",
"react": "^18.2.0", "react": "^18.2.0",
"typedoc": "0.25.0" "typedoc": "0.25.0",
"ws": "^8.14.2"
}, },
"resolutions": { "resolutions": {
"elliptic": "^6.5.4" "elliptic": "^6.5.4"

View File

@@ -1,8 +1,8 @@
/* eslint-disable max-lines -- Connection is a large file w/ lots of imports/exports */ /* eslint-disable max-lines -- Connection is a large file w/ lots of imports/exports */
import { EventEmitter } from 'events'
import type { Agent } from 'http' import type { Agent } from 'http'
import WebSocket from 'ws' import WebSocket, { ClientOptions } from '@xrplf/isomorphic/ws'
import { EventEmitter } from 'eventemitter3'
import { import {
DisconnectedError, DisconnectedError,
@@ -61,7 +61,7 @@ function createWebSocket(
url: string, url: string,
config: ConnectionOptions, config: ConnectionOptions,
): WebSocket | null { ): WebSocket | null {
const options: WebSocket.ClientOptions = { const options: ClientOptions = {
agent: config.agent, agent: config.agent,
} }
if (config.headers) { if (config.headers) {
@@ -75,15 +75,7 @@ function createWebSocket(
} }
} }
const websocketOptions = { ...options } const websocketOptions = { ...options }
const websocket = new WebSocket(url, websocketOptions) return new WebSocket(url, websocketOptions)
/*
* we will have a listener for each outstanding request,
* so we have to raise the limit (the default is 10)
*/
if (typeof websocket.setMaxListeners === 'function') {
websocket.setMaxListeners(Infinity)
}
return websocket
} }
/** /**
@@ -136,7 +128,6 @@ export class Connection extends EventEmitter {
*/ */
public constructor(url?: string, options: ConnectionUserOptions = {}) { public constructor(url?: string, options: ConnectionUserOptions = {}) {
super() super()
this.setMaxListeners(Infinity)
this.url = url this.url = url
this.config = { this.config = {
timeout: TIMEOUT * 1000, timeout: TIMEOUT * 1000,

View File

@@ -1,7 +1,7 @@
/* eslint-disable jsdoc/require-jsdoc -- Request has many aliases, but they don't need unique docs */ /* eslint-disable jsdoc/require-jsdoc -- Request has many aliases, but they don't need unique docs */
/* eslint-disable max-lines -- Client is a large file w/ lots of imports/exports */ /* eslint-disable max-lines -- Client is a large file w/ lots of imports/exports */
import { EventEmitter } from 'events' import { EventEmitter } from 'eventemitter3'
import { import {
RippledError, RippledError,
@@ -37,7 +37,10 @@ import type {
SubmitResponse, SubmitResponse,
} from '../models/methods' } from '../models/methods'
import type { BookOffer, BookOfferCurrency } from '../models/methods/bookOffers' import type { BookOffer, BookOfferCurrency } from '../models/methods/bookOffers'
import type { OnEventToListenerMap } from '../models/methods/subscribe' import type {
EventTypes,
OnEventToListenerMap,
} from '../models/methods/subscribe'
import type { Transaction } from '../models/transactions' import type { Transaction } from '../models/transactions'
import { setTransactionFlagsToNumber } from '../models/utils/flags' import { setTransactionFlagsToNumber } from '../models/utils/flags'
import { import {
@@ -161,7 +164,7 @@ const NORMAL_DISCONNECT_CODE = 1000
* *
* @category Clients * @category Clients
*/ */
class Client extends EventEmitter { class Client extends EventEmitter<EventTypes> {
/* /*
* Underlying connection to rippled. * Underlying connection to rippled.
*/ */
@@ -381,12 +384,12 @@ class Client extends EventEmitter {
* }) * })
* ``` * ```
*/ */
public on<T extends string, U = OnEventToListenerMap<T>>( public on<
eventName: T, T extends EventTypes,
listener: U, // eslint-disable-next-line @typescript-eslint/no-explicit-any -- needs to be any for overload
): this { U extends (...args: any[]) => void = OnEventToListenerMap<T>,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any -- Compatible >(eventName: T, listener: U): this {
return super.on(eventName, listener as (...args: any[]) => void) return super.on(eventName, listener)
} }
/** /**

View File

@@ -435,7 +435,19 @@ export type Stream =
| OrderBookStream | OrderBookStream
| ConsensusStream | ConsensusStream
export type OnEventToListenerMap<T> = T extends 'connected' export type EventTypes =
| 'connected'
| 'disconnected'
| 'ledgerClosed'
| 'validationReceived'
| 'transaction'
| 'peerStatusChange'
| 'consensusPhase'
| 'manifestReceived'
| 'path_find'
| 'error'
export type OnEventToListenerMap<T extends EventTypes> = T extends 'connected'
? () => void ? () => void
: T extends 'disconnected' : T extends 'disconnected'
? (code: number) => void ? (code: number) => void
@@ -456,7 +468,4 @@ export type OnEventToListenerMap<T> = T extends 'connected'
: T extends 'error' : T extends 'error'
? // eslint-disable-next-line @typescript-eslint/no-explicit-any -- needs to be any for overload ? // eslint-disable-next-line @typescript-eslint/no-explicit-any -- needs to be any for overload
(...err: any[]) => void (...err: any[]) => void
: T extends string : (...args: never[]) => void
? // eslint-disable-next-line @typescript-eslint/no-explicit-any -- needs to be any for overload
(...args: any[]) => void
: never

View File

@@ -1,4 +1,4 @@
import { EventEmitter2 } from 'eventemitter2' import { EventEmitter } from 'eventemitter3'
import { Server as WebSocketServer, type WebSocket } from 'ws' import { Server as WebSocketServer, type WebSocket } from 'ws'
import type { Request } from '../src' import type { Request } from '../src'
@@ -48,7 +48,7 @@ export interface PortResponse extends BaseResponse {
*/ */
export type MockedWebSocketServer = WebSocketServer & export type MockedWebSocketServer = WebSocketServer &
EventEmitter2 & { EventEmitter & {
responses: Record<string, unknown> responses: Record<string, unknown>
suppressOutput: boolean suppressOutput: boolean
socket: WebSocket socket: WebSocket
@@ -83,8 +83,8 @@ export function destroyMockRippled(server: MockedWebSocketServer): void {
} }
export default function createMockRippled(port: number): MockedWebSocketServer { export default function createMockRippled(port: number): MockedWebSocketServer {
Object.assign(WebSocketServer.prototype, EventEmitter.prototype)
const mock = new WebSocketServer({ port }) as MockedWebSocketServer const mock = new WebSocketServer({ port }) as MockedWebSocketServer
Object.assign(mock, EventEmitter2.prototype)
mock.responses = {} mock.responses = {}
mock.suppressOutput = false mock.suppressOutput = false

View File

@@ -10,11 +10,5 @@ module.exports = merge(getDefaultConfiguration(), {
library: 'xrpl', library: 'xrpl',
path: path.join(__dirname, 'build/'), path: path.join(__dirname, 'build/'),
filename: `xrpl.default.js`, filename: `xrpl.default.js`,
}, }
plugins: [new webpack.NormalModuleReplacementPlugin(/^ws$/, './WSWrapper')],
resolve: {
alias: {
ws: './dist/npm/client/WSWrapper.js',
},
},
}) })