From 4aba86f5aff7935881993715f718e3959065a66f Mon Sep 17 00:00:00 2001 From: Blaine Heffron Date: Tue, 24 Sep 2024 18:13:46 -0400 Subject: [PATCH] Add a way to build without axios (#1038) * replace axios with feaxios * make sure env is properly set for all builds and tests * add github test and changelog * update readme * update yarn lock --- .github/workflows/tests.yml | 3 + .gitignore | 2 + CHANGELOG.md | 4 + README.md | 7 + babel.config.js | 40 ++++ babel.config.json | 26 --- config/build.config.js | 4 + config/karma.conf.js | 10 +- config/set-output-dir.js | 14 ++ config/webpack.config.browser.js | 40 +++- package.json | 58 ++++- src/browser.ts | 5 +- src/federation/server.ts | 4 +- src/horizon/horizon_axios_client.ts | 27 ++- src/horizon/server.ts | 4 +- src/http-client/axios-client.ts | 4 + src/http-client/fetch-client.ts | 212 ++++++++++++++++++ src/http-client/index.ts | 27 +++ src/http-client/types.ts | 92 ++++++++ src/index.ts | 8 + src/rpc/axios.ts | 13 +- src/rpc/browser.ts | 2 - src/rpc/jsonrpc.ts | 4 +- src/rpc/server.ts | 2 + src/stellartoml/index.ts | 6 +- .../src/test-contract-client-constructor.js | 2 +- test/e2e/src/test-custom-types.js | 2 +- test/e2e/src/test-swap.js | 2 +- test/e2e/src/util.js | 2 +- test/test-browser.js | 2 +- test/test-nodejs.js | 2 +- yarn.lock | 154 +++++++++---- 32 files changed, 671 insertions(+), 113 deletions(-) create mode 100644 babel.config.js delete mode 100644 babel.config.json create mode 100644 config/build.config.js create mode 100644 config/set-output-dir.js create mode 100644 src/http-client/axios-client.ts create mode 100644 src/http-client/fetch-client.ts create mode 100644 src/http-client/index.ts create mode 100644 src/http-client/types.ts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4cae745b6..3047b8afa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,5 +41,8 @@ jobs: - name: Browser Tests run: yarn test:browser + - name: Browser No Axios Tests + run: yarn test:browser:no-axios + - name: Linter Tests run: yarn _prettier && (git diff-index --quiet HEAD; git diff) \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7d07cee8f..099bc6b21 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ test/unit/out/ target .soroban + +config/tsconfig.tmp.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 514599abb..51b3e2c63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,10 @@ export interface BalanceResponse { } ``` +- New build target for creating a browser bundle without Axios dependency. Set USE_AXIOS=false environment variable to build stellar-sdk-no-axios.js and stellar-sdk-no-axios.min.js in the dist/ directory. Use yarn build:browser:no-axios to generate these files. +- Similarly, a new import path for the node package without the Axios dependency can be used. Import the SDK using `@stellar/stellar-sdk/no-axios`. For Node.js environments that don't support the package.json `exports` configuration, use `@stellar/stellar-sdk/lib/no-axios/index`. +## [Version Number] - YYYY-MM-DD +- Updated the Node.js target in the Babel configuration from 16 to 18 for production builds. ## [v12.3.0](https://github.com/stellar/js-stellar-sdk/compare/v12.2.0...v12.3.0) diff --git a/README.md b/README.md index 1b9bf6c06..16d4c81ba 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,13 @@ If you don't want to use or install Bower, you can copy the packaged JS files fr | Always make sure that you are using the latest version number. They can be found on the [releases page](https://github.com/stellar/js-stellar-sdk/releases) in GitHub. | |----| +### Custom Installation + +You can configure whether or not to build the browser bundle with the axios dependency. In order to turn off the axios dependency, set the USE_AXIOS environment variable to false. + +#### Build without Axios +USE_AXIOS=false npm run build:browser + ## Usage The usage documentation for this library lives in a handful of places: diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 000000000..2b92da158 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,40 @@ +const buildConfig = require('./config/build.config'); +const fs = require('fs'); +const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')); +const version = packageJson.version; + +module.exports = function(api) { + api.cache(true); + + const presets = [ + "@babel/preset-env", + "@babel/typescript" + ]; + + const plugins = []; + + if (process.env.NODE_ENV === 'development') { + plugins.push('istanbul'); + } + + // Add the define plugin + plugins.push([ + 'babel-plugin-transform-define', + { + __USE_AXIOS__: buildConfig.useAxios, + __USE_EVENTSOURCE__: buildConfig.useEventSource, + __PACKAGE_VERSION__: version, + } + ]); + + const config = { + comments: process.env.NODE_ENV !== 'production', + presets, + plugins, + targets: process.env.NODE_ENV === 'production' + ? { node: 18, browsers: ["> 2%", "ie 11", "not op_mini all"] } + : { browsers: ["> 2%"] } + }; + + return config; +}; diff --git a/babel.config.json b/babel.config.json deleted file mode 100644 index ddf498d1b..000000000 --- a/babel.config.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "comments": true, // for debugging & jsdocs - "presets": [ - "@babel/preset-env", - "@babel/typescript" - ], - "targets": { - "browsers": [ "> 2%" ] // target modern browsers and ES6 - }, - "env": { - "development": { - "plugins": [ "istanbul" ] // for code coverage - }, - "production": { - "comments": false, - "targets": { - "node": 16, - "browsers": [ - "> 2%", - "ie 11", - "not op_mini all" - ] - } - } - } -} \ No newline at end of file diff --git a/config/build.config.js b/config/build.config.js new file mode 100644 index 000000000..67f5f573a --- /dev/null +++ b/config/build.config.js @@ -0,0 +1,4 @@ +module.exports = { + useAxios: process.env.USE_AXIOS !== 'false', + useEventSource: process.env.USE_EVENTSOURCE !== 'false', + }; \ No newline at end of file diff --git a/config/karma.conf.js b/config/karma.conf.js index bc54a9a87..3e87fa762 100644 --- a/config/karma.conf.js +++ b/config/karma.conf.js @@ -1,16 +1,24 @@ const webpackConfig = require('./webpack.config.browser.js'); +const buildConfig = require('./build.config'); delete webpackConfig.output; delete webpackConfig.entry; // karma fills these in webpackConfig.plugins.shift(); // drop eslinter plugin +function getStellarSdkFileName() { + let name = 'stellar-sdk'; + if (!buildConfig.useAxios) name += '-no-axios'; + if (!buildConfig.useEventSource) name += '-no-eventsource'; + return name + '.js'; +} + module.exports = function (config) { config.set({ frameworks: ['mocha', 'sinon-chai'], browsers: ['FirefoxHeadless', 'ChromeHeadless'], files: [ - '../dist/stellar-sdk.js', // webpack should build this first + '../dist/' + getStellarSdkFileName(), '../test/test-browser.js', '../test/unit/**/*.js' ], diff --git a/config/set-output-dir.js b/config/set-output-dir.js new file mode 100644 index 000000000..799a45cc1 --- /dev/null +++ b/config/set-output-dir.js @@ -0,0 +1,14 @@ +const fs = require('fs'); +const path = require('path'); + +const OUTPUT_DIR = process.env.OUTPUT_DIR || 'lib'; +const tsconfigPath = path.resolve(__dirname, 'tsconfig.json'); +const tempTsconfigPath = path.resolve(__dirname, 'tsconfig.tmp.json'); + +const tsconfig = require(tsconfigPath); +tsconfig.compilerOptions.outDir = path.resolve(__dirname, '..', OUTPUT_DIR); +tsconfig.compilerOptions.declarationDir = path.resolve(__dirname, '..', OUTPUT_DIR); + +fs.writeFileSync(tempTsconfigPath, JSON.stringify(tsconfig, null, 2), 'utf8'); + +console.log(`Set TypeScript outDir to ${OUTPUT_DIR} in temporary tsconfig file.`); \ No newline at end of file diff --git a/config/webpack.config.browser.js b/config/webpack.config.browser.js index bcb8f1c62..5854e3533 100644 --- a/config/webpack.config.browser.js +++ b/config/webpack.config.browser.js @@ -1,9 +1,15 @@ var path = require('path'); var webpack = require('webpack'); +var fs = require('fs'); var ESLintPlugin = require('eslint-webpack-plugin'); var TerserPlugin = require('terser-webpack-plugin'); var NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); +var buildConfig = require('./build.config'); + +// Read the version from package.json +const packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf8')); +const version = packageJson.version; const config = { target: 'web', @@ -16,17 +22,34 @@ const config = { fallback: { crypto: require.resolve('crypto-browserify'), stream: require.resolve('stream-browserify'), - buffer: require.resolve('buffer') + buffer: require.resolve('buffer'), }, - extensions: ['.ts', '.js'] + extensions: ['.ts', '.js'], }, output: { - clean: true, + clean: process.env.no_clean ? false: true, library: { name: 'StellarSdk', type: 'umd', umdNamedDefine: true }, + filename: (pathData) => { + let name = pathData.chunk.name; + let suffix = ''; + + if (name.endsWith('.min')) { + name = name.slice(0, -4); // Remove .min + suffix = '.min.js'; + } else { + suffix = '.js'; + } + + if (!buildConfig.useAxios && !buildConfig.useEventSource) name += '-minimal'; + else if (!buildConfig.useAxios) name += '-no-axios'; + else if (!buildConfig.useEventSource) name += '-no-eventsource'; + + return name + suffix; + }, path: path.resolve(__dirname, '../dist') }, mode: process.env.NODE_ENV ?? 'development', @@ -42,7 +65,11 @@ const config = { cacheDirectory: true } } - } + }, + { + test: /node_modules\/https-proxy-agent\//, + use: 'null-loader', + }, ] }, optimization: { @@ -70,6 +97,11 @@ const config = { }), new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] + }), + new webpack.DefinePlugin({ + __USE_AXIOS__: JSON.stringify(buildConfig.useAxios), + __USE_EVENTSOURCE__: JSON.stringify(buildConfig.useEventSource), + __PACKAGE_VERSION__: version, }) ], watchOptions: { diff --git a/package.json b/package.json index 30b0d48e1..5f48a2bf8 100644 --- a/package.json +++ b/package.json @@ -36,15 +36,63 @@ "./rpc": { "types": "./lib/rpc/index.d.ts", "default": "./lib/rpc/index.js" + }, + "./no-axios": { + "browser": "./dist/stellar-sdk-no-axios.min.js", + "types": "./lib/no-axios/index.d.ts", + "default": "./lib/no-axios/index.js" + }, + "./no-axios/contract": { + "types": "./lib/no-axios/contract/index.d.ts", + "default": "./lib/no-axios/contract/index.js" + }, + "./no-axios/rpc": { + "types": "./lib/no-axios/rpc/index.d.ts", + "default": "./lib/no-axios/rpc/index.js" + }, + "./no-eventsource": { + "browser": "./dist/stellar-sdk-no-eventsource.min.js", + "types": "./lib/no-eventsource/index.d.ts", + "default": "./lib/no-eventsource/index.js" + }, + "./no-eventsource/contract": { + "types": "./lib/no-eventsource/contract/index.d.ts", + "default": "./lib/no-eventsource/contract/index.js" + }, + "./no-eventsource/rpc": { + "types": "./lib/no-eventsource/rpc/index.d.ts", + "default": "./lib/no-eventsource/rpc/index.js" + }, + "./minimal": { + "browser": "./dist/stellar-sdk-minimal.min.js", + "types": "./lib/minimal/index.d.ts", + "default": "./lib/minimal/index.js" + }, + "./minimal/contract": { + "types": "./lib/minimal/contract/index.d.ts", + "default": "./lib/minimal/contract/index.js" + }, + "./minimal/rpc": { + "types": "./lib/minimal/rpc/index.d.ts", + "default": "./lib/minimal/rpc/index.js" } }, "scripts": { "build": "cross-env NODE_ENV=development yarn _build", "build:prod": "cross-env NODE_ENV=production yarn _build", "build:node": "yarn _babel && yarn build:ts", - "build:ts": "tsc -p ./config/tsconfig.json", + "build:node:no-axios": "cross-env OUTPUT_DIR=lib/no-axios USE_AXIOS=false yarn build:node", + "build:node:no-eventsource": "cross-env OUTPUT_DIR=lib/no-eventsource USE_EVENTSOURCE=false yarn build:node", + "build:node:minimal": "cross-env OUTPUT_DIR=lib/minimal USE_AXIOS=false USE_EVENTSOURCE=false yarn build:node", + "build:node:all": "yarn build:node && yarn build:node:no-axios && yarn build:node:no-eventsource && yarn build:node:minimal", + "clean:temp": "rm -f ./config/tsconfig.tmp.json", + "build:ts": "node config/set-output-dir.js && tsc -p ./config/tsconfig.tmp.json && yarn clean:temp", "build:test": "tsc -p ./test/unit/tsconfig.json", "build:browser": "webpack -c config/webpack.config.browser.js", + "build:browser:no-axios": "cross-env USE_AXIOS=false webpack -c config/webpack.config.browser.js", + "build:browser:no-eventsource": "cross-env USE_EVENTSOURCE=false webpack -c config/webpack.config.browser.js", + "build:browser:minimal": "cross-env USE_AXIOS=false USE_EVENTSOURCE=false webpack -c config/webpack.config.browser.js", + "build:browser:all": "yarn build:browser && cross-env no_clean=true yarn build:browser:no-axios && cross-env no_clean=true yarn build:browser:no-eventsource && cross-env no_clean=true yarn build:browser:minimal", "build:docs": "cross-env NODE_ENV=docs yarn _babel", "clean": "rm -rf lib/ dist/ coverage/ .nyc_output/ jsdoc/ test/e2e/.soroban", "docs": "yarn build:docs && jsdoc -c ./config/.jsdoc.json --verbose", @@ -53,11 +101,12 @@ "test:node": "yarn _nyc mocha --recursive 'test/unit/**/*.js'", "test:integration": "yarn _nyc mocha --recursive 'test/integration/**/*.js'", "test:browser": "karma start config/karma.conf.js", + "test:browser:no-axios": "cross-env USE_AXIOS=false yarn build:browser && cross-env USE_AXIOS=false karma start config/karma.conf.js", "fmt": "yarn _prettier && yarn eslint -c .eslintrc.js src/ --fix", "preversion": "yarn clean && yarn _prettier && yarn build:prod && yarn test", "prepare": "yarn build:prod", - "_build": "yarn build:node && yarn build:test && yarn build:browser", - "_babel": "babel --extensions '.ts' --out-dir lib/ src/", + "_build": "yarn build:node:all && yarn build:test && yarn build:browser:all", + "_babel": "babel --extensions '.ts' --out-dir ${OUTPUT_DIR:-lib} src/", "_nyc": "nyc --nycrc-path config/.nycrc", "_prettier": "prettier --ignore-path config/.prettierignore --write './test/**/*.js'" }, @@ -116,6 +165,7 @@ "axios-mock-adapter": "^1.22.0", "babel-loader": "^9.1.3", "babel-plugin-istanbul": "^7.0.0", + "babel-plugin-transform-define": "^2.1.4", "buffer": "^6.0.3", "chai": "^4.3.10", "chai-as-promised": "^7.1.1", @@ -149,6 +199,7 @@ "minami": "^1.1.1", "mocha": "^10.6.0", "node-polyfill-webpack-plugin": "^3.0.0", + "null-loader": "^4.0.1", "nyc": "^17.0.0", "prettier": "^3.3.3", "randombytes": "^2.1.0", @@ -166,6 +217,7 @@ "axios": "^1.7.7", "bignumber.js": "^9.1.2", "eventsource": "^2.0.2", + "feaxios": "^0.0.20", "randombytes": "^2.1.0", "toml": "^3.0.0", "urijs": "^1.19.1" diff --git a/src/browser.ts b/src/browser.ts index 2f6034b93..fea2654e9 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -1,10 +1,9 @@ /* tslint:disable:no-var-requires */ /* eslint import/no-import-module-exports: 0 */ - -import axios from "axios"; // idk why axios is weird +import { httpClient } from "./http-client"; export * from "./index"; export * as StellarBase from "@stellar/stellar-base"; -export { axios }; +export { httpClient }; export default module.exports; diff --git a/src/federation/server.ts b/src/federation/server.ts index 5f032c18e..e54aa4fb2 100644 --- a/src/federation/server.ts +++ b/src/federation/server.ts @@ -1,5 +1,4 @@ /* eslint-disable require-await */ -import axios from "axios"; import { StrKey } from "@stellar/stellar-base"; import URI from "urijs"; @@ -8,6 +7,7 @@ import { BadResponseError } from "../errors"; import { Resolver } from "../stellartoml"; import { Api } from "./api"; +import { httpClient } from "../http-client"; // FEDERATION_RESPONSE_MAX_SIZE is the maximum size of response from a federation server export const FEDERATION_RESPONSE_MAX_SIZE = 100 * 1024; @@ -218,7 +218,7 @@ export class FederationServer { private async _sendRequest(url: URI) { const timeout = this.timeout; - return axios + return httpClient .get(url.toString(), { maxContentLength: FEDERATION_RESPONSE_MAX_SIZE, timeout, diff --git a/src/horizon/horizon_axios_client.ts b/src/horizon/horizon_axios_client.ts index 7643b1db5..f0d8d004b 100644 --- a/src/horizon/horizon_axios_client.ts +++ b/src/horizon/horizon_axios_client.ts @@ -1,9 +1,10 @@ /* eslint-disable global-require */ -import axios, { AxiosResponse } from "axios"; import URI from "urijs"; +import { create, HttpResponseHeaders } from "../http-client"; -// eslint-disable-next-line prefer-import/prefer-import-over-require -export const version = require("../../package.json").version; +// eslint-disable-next-line prefer-import/prefer-import-over-require , @typescript-eslint/naming-convention +declare const __PACKAGE_VERSION__: string; +export const version = __PACKAGE_VERSION__; export interface ServerTime { serverTime: number; @@ -24,7 +25,7 @@ export interface ServerTime { */ export const SERVER_TIME_MAP: Record = {}; -export const AxiosClient = axios.create({ +export const AxiosClient = create({ headers: { "X-Client-Name": "js-stellar-sdk", "X-Client-Version": version, @@ -36,9 +37,20 @@ function toSeconds(ms: number): number { } AxiosClient.interceptors.response.use( - (response: AxiosResponse) => { + (response) => { const hostname = URI(response.config.url!).hostname(); - const serverTime = toSeconds(Date.parse(response.headers.date)); + let serverTime = 0; + if (response.headers instanceof Headers) { + const dateHeader = response.headers.get('date'); + if (dateHeader) { + serverTime = toSeconds(Date.parse(dateHeader)); + } + } else if (typeof response.headers === 'object' && 'date' in response.headers) { + const headers = response.headers as HttpResponseHeaders; // Cast response.headers to the correct type + if (typeof headers.date === 'string') { + serverTime = toSeconds(Date.parse(headers.date)); + } + } const localTimeRecorded = toSeconds(new Date().getTime()); if (!Number.isNaN(serverTime)) { @@ -46,8 +58,7 @@ AxiosClient.interceptors.response.use( serverTime, localTimeRecorded, }; - } - + } return response; }, ); diff --git a/src/horizon/server.ts b/src/horizon/server.ts index ffeefede1..d1d3e7813 100644 --- a/src/horizon/server.ts +++ b/src/horizon/server.ts @@ -100,6 +100,7 @@ export class Server { AxiosClient.interceptors.request.use((config) => { // merge the custom headers with an existing headers, where customs // override defaults + config.headers = config.headers || {}; config.headers = Object.assign(config.headers, customHeaders); return config; @@ -515,8 +516,7 @@ export class Server { * By default, this function calls {@link Server#checkMemoRequired}, you can * skip this check by setting the option `skipMemoRequiredCheck` to `true`. * - * @see [Submit - * Async Transaction](https://developers.stellar.org/docs/data/horizon/api-reference/resources/submit-async-transaction) + * @see [Submit-Async-Transaction](https://developers.stellar.org/docs/data/horizon/api-reference/resources/submit-async-transaction) * @param {Transaction|FeeBumpTransaction} transaction - The transaction to submit. * @param {object} [opts] Options object * @param {boolean} [opts.skipMemoRequiredCheck] - Allow skipping memo diff --git a/src/http-client/axios-client.ts b/src/http-client/axios-client.ts new file mode 100644 index 000000000..2a9107080 --- /dev/null +++ b/src/http-client/axios-client.ts @@ -0,0 +1,4 @@ +import axios from 'axios'; + +export const axiosClient = axios; +export const create = axios.create; \ No newline at end of file diff --git a/src/http-client/fetch-client.ts b/src/http-client/fetch-client.ts new file mode 100644 index 000000000..feed2fcc8 --- /dev/null +++ b/src/http-client/fetch-client.ts @@ -0,0 +1,212 @@ +import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, CreateAxiosDefaults, } from 'feaxios'; +import { CancelToken, HttpClient, HttpClientRequestConfig, HttpClientResponse, Interceptor, } from './types'; + +export interface HttpResponse extends AxiosResponse { + // You can add any additional properties here if needed +} + +export interface FetchClientConfig extends AxiosRequestConfig { + adapter?: (config: HttpClientRequestConfig) => Promise>; + // You can add any additional configuration options here + cancelToken?: CancelToken; +} + + +type InterceptorFulfilled = (value: V) => V | Promise; +type InterceptorRejected = (error: any) => any; + +class InterceptorManager { + handlers: Array | null> = []; + + use(fulfilled: InterceptorFulfilled, rejected?: InterceptorRejected): number { + this.handlers.push({ + fulfilled, + rejected, + }); + return this.handlers.length - 1; + } + + eject(id: number): void { + if (this.handlers[id]) { + this.handlers[id] = null; + } + } + + forEach(fn: (interceptor: Interceptor) => void): void { + this.handlers.forEach((h) => { + if (h !== null) { + fn(h); + } + }); + } +} +function getFormConfig(config?: HttpClientRequestConfig): HttpClientRequestConfig { + const formConfig = config || {}; + formConfig.headers = new Headers(formConfig.headers || {}); + formConfig.headers.set('Content-Type', 'application/x-www-form-urlencoded'); + return formConfig; +} + +function createFetchClient(fetchConfig: HttpClientRequestConfig = {}): HttpClient { + const defaults: CreateAxiosDefaults = { + ...fetchConfig, + headers: fetchConfig.headers || {} + }; + + const instance: AxiosInstance = axios.create(defaults); + const requestInterceptors = new InterceptorManager(); + const responseInterceptors = new InterceptorManager(); + + const httpClient: HttpClient = { + interceptors: { + request: requestInterceptors, + response: responseInterceptors + }, + + defaults: { + ...defaults, + adapter: (config: HttpClientRequestConfig) => instance.request(config), + }, + + create(config?: HttpClientRequestConfig): HttpClient { + return createFetchClient({ ...this.defaults, ...config }); + }, + + makeRequest(config: FetchClientConfig): Promise> { + return new Promise((resolve, reject) => { + const abortController = new AbortController(); + config.signal = abortController.signal; + + if (config.cancelToken) { + config.cancelToken.promise.then(() => { + abortController.abort(); + reject(new Error('Request canceled')); + }); + } + + // Apply request interceptors + let modifiedConfig = config; + if (requestInterceptors.handlers.length > 0) { + const chain = requestInterceptors.handlers + .filter((interceptor): interceptor is NonNullable => interceptor !== null) + .flatMap((interceptor) => [ + interceptor.fulfilled, + interceptor.rejected + ]); + for (let i = 0, len = chain.length; i < len; i += 2) { + const onFulfilled = chain[i]; + const onRejected = chain[i + 1]; + try { + if (onFulfilled) modifiedConfig = onFulfilled(modifiedConfig); + } catch (error) { + if (onRejected) (onRejected as InterceptorRejected)?.(error); + reject(error); + return; + } + } + } + + const adapter = modifiedConfig.adapter || this.defaults.adapter; + if (!adapter) { + throw new Error('No adapter available'); + } + let responsePromise = adapter(modifiedConfig).then((axiosResponse) => { + // Transform AxiosResponse to HttpClientResponse + const httpClientResponse: HttpClientResponse = { + data: axiosResponse.data, + headers: axiosResponse.headers as any, // You might want to transform headers more carefully + config: axiosResponse.config, + status: axiosResponse.status, + statusText: axiosResponse.statusText, + }; + return httpClientResponse; + }); + + // Apply response interceptors + if (responseInterceptors.handlers.length > 0) { + const chain = responseInterceptors.handlers + .filter((interceptor): interceptor is NonNullable => interceptor !== null) + .flatMap((interceptor) => [interceptor.fulfilled, interceptor.rejected]); + + for (let i = 0, len = chain.length; i < len; i += 2) { + responsePromise = responsePromise.then( + (response) => { + const fulfilledInterceptor = chain[i]; + if (typeof fulfilledInterceptor === 'function') { + return fulfilledInterceptor(response); + } + return response; + }, + (error) => { + const rejectedInterceptor = chain[i + 1]; + if (typeof rejectedInterceptor === 'function') { + return rejectedInterceptor(error); + } + throw error; + } + ).then((interceptedResponse) => interceptedResponse); + } + } + + // Resolve or reject the final promise + responsePromise + .then(resolve) + .catch(reject); + }); + }, + + + get(url: string, config?: HttpClientRequestConfig): Promise> { + return this.makeRequest({ ...this.defaults, ...config, url, method: 'get' }); + }, + + delete(url: string, config?: HttpClientRequestConfig): Promise> { + return this.makeRequest({ ...this.defaults, ...config, url, method: 'delete' }); + }, + + head(url: string, config?: HttpClientRequestConfig): Promise> { + return this.makeRequest({ ...this.defaults, ...config, url, method: 'head' }); + }, + + options(url: string, config?: HttpClientRequestConfig): Promise> { + return this.makeRequest({ ...this.defaults, ...config, url, method: 'options' }); + }, + + post(url: string, data?: any, config?: HttpClientRequestConfig): Promise> { + return this.makeRequest({ ...this.defaults, ...config, url, method: 'post', data }); + }, + + put(url: string, data?: any, config?: HttpClientRequestConfig): Promise> { + return this.makeRequest({ ...this.defaults, ...config, url, method: 'put', data }); + }, + + patch(url: string, data?: any, config?: HttpClientRequestConfig): Promise> { + return this.makeRequest({ ...this.defaults, ...config, url, method: 'patch', data }); + }, + + postForm(url: string, data?: any, config?: HttpClientRequestConfig): Promise> { + const formConfig = getFormConfig(config); + return this.makeRequest({ ...this.defaults, ...formConfig, url, method: 'post', data }); + }, + + putForm(url: string, data?: any, config?: HttpClientRequestConfig): Promise> { + const formConfig = getFormConfig(config); + return this.makeRequest({ ...this.defaults, ...formConfig, url, method: 'put', data }); + }, + + patchForm(url: string, data?: any, config?: HttpClientRequestConfig): Promise> { + const formConfig = getFormConfig(config); + return this.makeRequest({ ...this.defaults, ...formConfig, url, method: 'patch', data }); + }, + + CancelToken, + isCancel: (value: any): boolean => value instanceof Error && value.message === 'Request canceled', + }; + + + return httpClient; +} + +export const fetchClient = createFetchClient(); + +export { createFetchClient as create }; diff --git a/src/http-client/index.ts b/src/http-client/index.ts new file mode 100644 index 000000000..ad28ec7a0 --- /dev/null +++ b/src/http-client/index.ts @@ -0,0 +1,27 @@ +import { HttpClient, HttpClientRequestConfig } from "./types"; + +// eslint-disable-next-line import/no-mutable-exports +let httpClient: HttpClient; +// eslint-disable-next-line import/no-mutable-exports +let create: (config?: HttpClientRequestConfig) => HttpClient; + +// Declare a variable that will be set by the entrypoint +// eslint-disable-next-line @typescript-eslint/naming-convention +declare const __USE_AXIOS__: boolean; + +// Use the variable for the runtime check +// eslint-disable-next-line no-lonely-if +if (__USE_AXIOS__) { + // eslint-disable-next-line global-require, prefer-import/prefer-import-over-require + const axiosModule = require('./axios-client'); + httpClient = axiosModule.axiosClient; + create = axiosModule.create; +} else { + // eslint-disable-next-line global-require, prefer-import/prefer-import-over-require + const fetchModule = require('./fetch-client'); + httpClient = fetchModule.fetchClient; + create = fetchModule.create; +} + +export { httpClient, create }; +export * from "./types"; diff --git a/src/http-client/types.ts b/src/http-client/types.ts new file mode 100644 index 000000000..5d57f2e59 --- /dev/null +++ b/src/http-client/types.ts @@ -0,0 +1,92 @@ +export type HttpResponseHeaders = Record & { + 'set-cookie'?: string[]; +}; + +export interface HttpClientDefaults extends Omit { + headers?: [string, string][] | Record | Headers | undefined; +} + +export interface HttpClientResponse { + data: T; + headers: HttpResponseHeaders; + config: any; + status: number; + statusText: string; +} + +export interface CancelToken { + promise: Promise; + throwIfRequested(): void; + reason?: string; +} + +type HeadersInit = [string, string][] | Record | Headers; + +export interface HttpClientRequestConfig { + url?: string; + method?: string; + baseURL?: string; + data?: D; + timeout?: number; + fetchOptions?: Record; + headers?: HeadersInit; + params?: Record; + maxContentLength?: number; + cancelToken?: CancelToken; + adapter?: (config: HttpClientRequestConfig) => Promise; +} + +export interface HttpClient { + get: (url: string, config?: HttpClientRequestConfig) => Promise>; + delete: (url: string, config?: HttpClientRequestConfig) => Promise>; + head: (url: string, config?: HttpClientRequestConfig) => Promise>; + options: (url: string, config?: HttpClientRequestConfig) => Promise>; + post: (url: string, data?: any, config?: HttpClientRequestConfig) => Promise>; + put: (url: string, data?: any, config?: HttpClientRequestConfig) => Promise>; + patch: (url: string, data?: any, config?: HttpClientRequestConfig) => Promise>; + postForm: (url: string, data?: any, config?: HttpClientRequestConfig) => Promise>; + putForm: (url: string, data?: any, config?: HttpClientRequestConfig) => Promise>; + patchForm: (url: string, data?: any, config?: HttpClientRequestConfig) => Promise>; + interceptors: + { + request: InterceptorManager; + response: InterceptorManager; + }, + defaults: HttpClientDefaults; + CancelToken: typeof CancelToken; + isCancel: (value: any) => boolean; + makeRequest: (config: HttpClientRequestConfig) => Promise>; + create: (config?: HttpClientRequestConfig) => HttpClient; +} + +export interface Interceptor { + fulfilled: (value: V) => V | Promise; + rejected?: (error: any) => any; +} + +export interface InterceptorManager { + use(fulfilled: (value: V) => V | Promise, rejected?: (error: any) => any): number; + eject(id: number): void; + forEach(fn: (interceptor: Interceptor) => void): void; + handlers: Array | null>; +} + +export class CancelToken { + promise: Promise; + reason?: string; + throwIfRequested(): void { + if (this.reason) { + throw new Error(this.reason); + } + } + constructor(executor: (cancel: (reason?: string) => void) => void) { + let resolvePromise: (value?: any) => void; + this.promise = new Promise((resolve) => { + resolvePromise = resolve; + }); + executor((reason?: string) => { + this.reason = reason; + resolvePromise(); + }); + } +} diff --git a/src/index.ts b/src/index.ts index 83dfde51f..1147a414a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -49,3 +49,11 @@ export * as contract from './contract' export * from '@stellar/stellar-base'; export default module.exports; + +if (typeof (global as any).__USE_AXIOS__ === 'undefined') { + (global as any).__USE_AXIOS__ = true; +} + +if (typeof (global as any).__USE_EVENTSOURCE__ === 'undefined') { + (global as any).__USE_EVENTSOURCE__ = false; +} \ No newline at end of file diff --git a/src/rpc/axios.ts b/src/rpc/axios.ts index e33911202..578f0a90e 100644 --- a/src/rpc/axios.ts +++ b/src/rpc/axios.ts @@ -1,14 +1,15 @@ -import axios from 'axios'; -/* eslint-disable global-require */ +import { create, HttpClient } from "../http-client"; -// eslint-disable-next-line prefer-import/prefer-import-over-require -export const version = require('../../package.json').version; +// eslint-disable-next-line prefer-import/prefer-import-over-require, global-require, @typescript-eslint/naming-convention +declare const __PACKAGE_VERSION__: string; +export const version = __PACKAGE_VERSION__; -export const AxiosClient = axios.create({ + +export const AxiosClient: HttpClient = create({ headers: { 'X-Client-Name': 'js-soroban-client', 'X-Client-Version': version } }); -export default AxiosClient; +export default AxiosClient; \ No newline at end of file diff --git a/src/rpc/browser.ts b/src/rpc/browser.ts index d382124f1..94e2e8ff6 100644 --- a/src/rpc/browser.ts +++ b/src/rpc/browser.ts @@ -1,9 +1,7 @@ /* tslint:disable:no-var-requires */ /* eslint-disable import/no-import-module-exports */ -import axios from 'axios'; // idk why axios is weird export * from './index'; export * as StellarBase from '@stellar/stellar-base'; -export { axios }; export default module.exports; diff --git a/src/rpc/jsonrpc.ts b/src/rpc/jsonrpc.ts index dc29a82c5..24b3375f4 100644 --- a/src/rpc/jsonrpc.ts +++ b/src/rpc/jsonrpc.ts @@ -1,4 +1,4 @@ -import axios from "./axios"; +import http from "./axios"; export type Id = string | number; @@ -42,7 +42,7 @@ export async function postObject( method: string, param: any = null, ): Promise { - const response = await axios.post>(url, { + const response = await http.post>(url, { jsonrpc: "2.0", // TODO: Generate a unique request id id: 1, diff --git a/src/rpc/server.ts b/src/rpc/server.ts index 833c8253d..395a2cc79 100644 --- a/src/rpc/server.ts +++ b/src/rpc/server.ts @@ -893,6 +893,7 @@ export class Server { * @returns {Promise} the fee stats * @see https://developers.stellar.org/docs/data/rpc/api-reference/methods/getFeeStats */ + // eslint-disable-next-line require-await public async getFeeStats(): Promise { return jsonrpc.postObject(this.serverURL.toString(), 'getFeeStats'); } @@ -903,6 +904,7 @@ export class Server { * @returns {Promise} the version info * @see https://developers.stellar.org/docs/data/rpc/api-reference/methods/getVersionInfo */ + // eslint-disable-next-line require-await public async getVersionInfo(): Promise { return jsonrpc.postObject(this.serverURL.toString(), 'getVersionInfo'); } diff --git a/src/stellartoml/index.ts b/src/stellartoml/index.ts index 96d4bb755..d9c8af037 100644 --- a/src/stellartoml/index.ts +++ b/src/stellartoml/index.ts @@ -1,6 +1,6 @@ -import axios from "axios"; import toml from "toml"; import { Networks } from "@stellar/stellar-base"; +import { httpClient } from "../http-client"; import { Config } from "../config"; @@ -9,7 +9,7 @@ export const STELLAR_TOML_MAX_SIZE = 100 * 1024; // axios timeout doesn't catch missing urls, e.g. those with no response // so we use the axios cancel token to ensure the timeout -const CancelToken = axios.CancelToken; +const CancelToken = httpClient.CancelToken; /** Resolver allows resolving `stellar.toml` files. */ export class Resolver { @@ -46,7 +46,7 @@ export class Resolver { const protocol = allowHttp ? "http" : "https"; - return axios + return httpClient .get(`${protocol}://${domain}/.well-known/stellar.toml`, { maxContentLength: STELLAR_TOML_MAX_SIZE, cancelToken: timeout diff --git a/test/e2e/src/test-contract-client-constructor.js b/test/e2e/src/test-contract-client-constructor.js index 50344791f..16756156e 100644 --- a/test/e2e/src/test-contract-client-constructor.js +++ b/test/e2e/src/test-contract-client-constructor.js @@ -6,7 +6,7 @@ const { rpcUrl, generateFundedKeypair, } = require("./util"); -const { Address, contract } = require("../../.."); +const { Address, contract } = require("../../../lib"); async function clientFromConstructor( name, diff --git a/test/e2e/src/test-custom-types.js b/test/e2e/src/test-custom-types.js index 5c4cc970e..96cdd6147 100644 --- a/test/e2e/src/test-custom-types.js +++ b/test/e2e/src/test-custom-types.js @@ -1,5 +1,5 @@ const { expect } = require("chai"); -const { Address, contract } = require("../../.."); +const { Address, contract } = require("../../../lib"); const { clientFor } = require("./util"); describe("Custom Types Tests", function () { diff --git a/test/e2e/src/test-swap.js b/test/e2e/src/test-swap.js index 6d6167319..22b180e80 100644 --- a/test/e2e/src/test-swap.js +++ b/test/e2e/src/test-swap.js @@ -6,7 +6,7 @@ const { SorobanDataBuilder, xdr, TransactionBuilder, -} = require("../../.."); +} = require("../../../lib"); const { clientFor, generateFundedKeypair, diff --git a/test/e2e/src/util.js b/test/e2e/src/util.js index 0a5e67173..090213ef7 100644 --- a/test/e2e/src/util.js +++ b/test/e2e/src/util.js @@ -1,5 +1,5 @@ const { spawnSync } = require("node:child_process"); -const { contract, Keypair } = require("../../.."); +const { contract, Keypair } = require("../../../lib"); const basePath = `${__dirname}/../test-contracts/target/wasm32-unknown-unknown/release`; const contracts = { diff --git a/test/test-browser.js b/test/test-browser.js index 52f962918..a636a5d92 100644 --- a/test/test-browser.js +++ b/test/test-browser.js @@ -1,6 +1,6 @@ /* eslint-disable no-undef */ chai.use(require("chai-as-promised")); -window.axios = StellarSdk.axios; +window.axios = StellarSdk.httpClient; window.HorizonAxiosClient = StellarSdk.Horizon.AxiosClient; window.SorobanAxiosClient = StellarSdk.Soroban.AxiosClient; window.serverUrl = "https://horizon-live.stellar.org:1337/api/v1/jsonrpc"; diff --git a/test/test-nodejs.js b/test/test-nodejs.js index a318048e5..2c34cc7ff 100644 --- a/test/test-nodejs.js +++ b/test/test-nodejs.js @@ -1,7 +1,7 @@ /* eslint-disable no-undef */ require("@babel/register"); -global.StellarSdk = require("../lib/"); +global.StellarSdk = require("../lib"); global.axios = require("axios"); global.serverUrl = "https://horizon-live.stellar.org:1337/api/v1/jsonrpc"; diff --git a/yarn.lock b/yarn.lock index 4fb5eb31a..993753247 100644 --- a/yarn.lock +++ b/yarn.lock @@ -280,7 +280,14 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.14.7", "@babel/parser@^7.20.15", "@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.6": +"@babel/parser@^7.14.7", "@babel/parser@^7.20.15", "@babel/parser@^7.25.0": + version "7.25.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.3.tgz#91fb126768d944966263f0657ab222a642b82065" + integrity sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw== + dependencies: + "@babel/types" "^7.25.2" + +"@babel/parser@^7.23.9", "@babel/parser@^7.25.6": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f" integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q== @@ -1426,7 +1433,12 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/chai@4", "@types/chai@^4.3.19": +"@types/chai@4": + version "4.3.16" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.16.tgz#b1572967f0b8b60bf3f87fe1d854a5604ea70c82" + integrity sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ== + +"@types/chai@^4.3.19": version "4.3.19" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.19.tgz#14519f437361d41e84102ed3fbc922ddace3e228" integrity sha512-2hHHvQBVE2FiSK4eN0Br6snX9MtolHaTo/batnLjlGRhoQzlCL61iVpxoqO7SfFyOw+P/pwv+0zNHzKoGWz9Cw== @@ -1528,27 +1540,20 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.8.tgz#a7eff5816e070c3b4d803f1d3cd780c4e42934a1" integrity sha512-HfMcUmy9hTMJh66VNcmeC9iVErIZJli2bszuXc6julh5YGuRb/W5OnkHjwLNYdFlMis0sY3If5SEAp+PktdJjw== -"@types/node@*", "@types/node@>=10.0.0": - version "22.5.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.4.tgz#83f7d1f65bc2ed223bdbf57c7884f1d5a4fa84e8" - integrity sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg== +"@types/node@*", "@types/node@>=10.0.0", "@types/node@^20.14.11": + version "20.14.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.11.tgz#09b300423343460455043ddd4d0ded6ac579b74b" + integrity sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA== dependencies: - undici-types "~6.19.2" + undici-types "~5.26.4" "@types/node@^18.19.7": - version "18.19.50" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.50.tgz#8652b34ee7c0e7e2004b3f08192281808d41bf5a" - integrity sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg== + version "18.19.41" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.41.tgz#27695cf2cac63f22c202b9217c0bcf3fb192a2f0" + integrity sha512-LX84pRJ+evD2e2nrgYCHObGWkiQJ1mL+meAgbvnwk/US6vmMY7S2ygBTGV2Jw91s9vUsLSXeDEkUHZIJGLrhsg== dependencies: undici-types "~5.26.4" -"@types/node@^20.14.11": - version "20.16.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.16.5.tgz#d43c7f973b32ffdf9aa7bd4f80e1072310fd7a53" - integrity sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA== - dependencies: - undici-types "~6.19.2" - "@types/randombytes@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/randombytes/-/randombytes-2.0.3.tgz#c83a107ef51ae7a8611a7b964f54b21cb782bbed" @@ -2220,6 +2225,14 @@ babel-plugin-polyfill-regenerator@^0.6.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.6.2" +babel-plugin-transform-define@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-2.1.4.tgz#8f7088211176a55a72788d584ceea9f691a021c1" + integrity sha512-NN9xFmyNvr4swPZkRWy+RZZoV0yHhPk/WoxpuIvcVkTyYf0xy/JTQeZVbVGX8hyJ0/NKKuxnt4BZz9No7BziVA== + dependencies: + lodash "^4.17.11" + traverse "0.6.6" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -2252,6 +2265,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + bignumber.js@^9.1.2: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" @@ -2479,9 +2497,9 @@ camelcase@^6.0.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001646: - version "1.0.30001660" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz#31218de3463fabb44d0b7607b652e56edf2e2355" - integrity sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg== + version "1.0.30001663" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz#1529a723505e429fdfd49532e9fc42273ba7fed7" + integrity sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA== caseless@~0.12.0: version "0.12.0" @@ -3170,9 +3188,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.5.4: - version "1.5.20" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.20.tgz#2914e42cfc5cc992cbee5538b500ddaf7c2c7091" - integrity sha512-74mdl6Fs1HHzK9SUX4CKFxAtAe3nUns48y79TskHNAG6fGOlLfyKA4j855x+0b5u8rWJIrlaG9tcTPstMlwjIw== + version "1.5.28" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.28.tgz#aee074e202c6ee8a0030a9c2ef0b3fe9f967d576" + integrity sha512-VufdJl+rzaKZoYVUijN13QcXVF5dWPZANeFTLNy+OSpHdDL5ynXTF35+60RSBbaQYB1ae723lQXHCrf4pyLsMw== elliptic@^6.5.3, elliptic@^6.5.5: version "6.5.7" @@ -3202,6 +3220,11 @@ emojilib@^2.4.0: resolved "https://registry.yarnpkg.com/emojilib/-/emojilib-2.4.0.tgz#ac518a8bb0d5f76dda57289ccb2fdf9d39ae721e" integrity sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw== +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -3421,9 +3444,9 @@ eslint-import-resolver-node@^0.3.9: resolve "^1.22.4" eslint-module-utils@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz#b99b211ca4318243f09661fae088f373ad5243c4" - integrity sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ== + version "2.11.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.11.1.tgz#6d5a05f09af98f4d238a819ae4c23626a75fa65b" + integrity sha512-EwcbfLOhwVMAfatfqLecR2yv3dE5+kQ8kx+Rrt0DvDXEVwW86KQ/xbMDQhtp5l42VXukD5SOF8mQQHbaNtO0CQ== dependencies: debug "^3.2.7" @@ -3772,6 +3795,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +feaxios@^0.0.20: + version "0.0.20" + resolved "https://registry.yarnpkg.com/feaxios/-/feaxios-0.0.20.tgz#04e976beb7345401fedeba764f0e9e1c4d01afd4" + integrity sha512-g3hm2YDNffNxA3Re3Hd8ahbpmDee9Fv1Pb1C/NoWsjY7mtD8nyNeJytUzn+DK0Hyl9o6HppeWOrtnqgmhOYfWA== + dependencies: + is-retry-allowed "^3.0.0" + fflate@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" @@ -4607,6 +4637,11 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-retry-allowed@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-3.0.0.tgz#ea79389fd350d156823c491bee9c69f485b1445c" + integrity sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A== + is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" @@ -4939,7 +4974,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.2.3: +json5@^2.1.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -5126,6 +5161,15 @@ loader-runner@^4.2.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -5192,7 +5236,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.15, lodash@^4.17.21: +lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5638,6 +5682,14 @@ npm-run-path@^5.1.0: gauge "~2.7.3" set-blocking "~2.0.0" +null-loader@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/null-loader/-/null-loader-4.0.1.tgz#8e63bd3a2dd3c64236a4679428632edd0a6dbc6a" + integrity sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -6143,13 +6195,20 @@ qjobs@^1.2.0: resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== -qs@6.13.0, qs@^6.11.0, qs@^6.11.2, qs@^6.12.3: +qs@6.13.0, qs@^6.12.3: version "6.13.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: side-channel "^1.0.6" +qs@^6.11.0, qs@^6.11.2: + version "6.12.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.3.tgz#e43ce03c8521b9c7fd7f1f13e514e5ca37727754" + integrity sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ== + dependencies: + side-channel "^1.0.6" + qs@~6.5.2: version "6.5.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" @@ -6470,7 +6529,7 @@ safe-regex-test@^1.0.3: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -schema-utils@^3.1.1, schema-utils@^3.2.0: +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -6825,9 +6884,9 @@ streamroller@^3.1.5: fs-extra "^8.1.0" streamx@^2.15.0: - version "2.20.1" - resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.20.1.tgz#471c4f8b860f7b696feb83d5b125caab2fdbb93c" - integrity sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA== + version "2.18.0" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.18.0.tgz#5bc1a51eb412a667ebfdcd4e6cf6a6fc65721ac7" + integrity sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ== dependencies: fast-fifo "^1.3.2" queue-tick "^1.0.1" @@ -7051,9 +7110,9 @@ terser-webpack-plugin@^5.3.10: terser "^5.26.0" terser@^5.26.0: - version "5.32.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.32.0.tgz#ee811c0d2d6b741c1cc34a2bc5bcbfc1b5b1f96c" - integrity sha512-v3Gtw3IzpBJ0ugkxEX8U0W6+TnPKRRCWGh1jC/iM/e3Ki5+qvO1L1EAZ56bZasc64aXHwRHNIQEzm6//i5cemQ== + version "5.31.3" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.3.tgz#b24b7beb46062f4653f049eea4f0cd165d0f0c38" + integrity sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -7137,6 +7196,11 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +traverse@0.6.6: + version "0.6.6" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" + integrity sha512-kdf4JKs8lbARxWdp7RKdNzoJBhGUcIalSYibuGyHJbmk40pOysQ0+QPvlkCOICOivDWU2IJo2rkrxyTK2AH4fw== + ts-api-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" @@ -7231,9 +7295,9 @@ type-fest@^0.8.0: integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== type-fest@^4.4.0: - version "4.26.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.26.1.tgz#a4a17fa314f976dd3e6d6675ef6c775c16d7955e" - integrity sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg== + version "4.22.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.22.1.tgz#cc493ba0c1fb7faecb80d13a70525a75afd9d8d6" + integrity sha512-9tHNEa0Ov81YOopiVkcCJVz5TM6AEQ+CHHjFIktqPnE3NV0AHIkx+gh9tiCl58m/66wWxkOC9eltpa75J4lQPA== type-is@~1.6.18: version "1.6.18" @@ -7340,14 +7404,14 @@ typedarray@^0.0.6: integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== "typescript-5.6@npm:typescript@~5.6.0-0": - version "5.6.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0" - integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw== + version "5.6.0-dev.20240719" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.0-dev.20240719.tgz#1bee8182303b6f4cdfb7a0b97d721aeea5ef7fc9" + integrity sha512-gwHt3fdLbe+LLgZJnPaGZ7v9MHTkjrqjf4NpxznO0SmFVSu7cEd8IwEsePtmQQZAX3FtPdgfwDmeNktid848Hw== "typescript-5.7@npm:typescript@~5.7.0-0": - version "5.7.0-dev.20240912" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.0-dev.20240912.tgz#18c62679bece831289c7dfc23cb9f170f011eb5d" - integrity sha512-mDTEqq6tLag5w75BeGd1g9t64z7dlhTl/3x0CP1bG/638MXiBbvCoI5sbkcEY0UoYUxP7EeIi7ZI6HBd9lDPFw== + version "5.7.0-dev.20240924" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.0-dev.20240924.tgz#bf97ed5c8c7bddcd8d178a9d74913d1e37b21b92" + integrity sha512-Ij5R6dGDoRwlwMv0YAF0DSKOp2gGIv0SZNybFpAm05zZ/tktjetR7IrwuurvRlDEz7v91AC3ehs2lF7lJ8F/QA== typescript@5.3.3: version "5.3.3"