diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000..fe5a185ee --- /dev/null +++ b/.babelrc @@ -0,0 +1,26 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "node": "current" + } + } + ], + "@babel/preset-typescript" + ], + "plugins": [ + "@babel/plugin-transform-runtime" + ], + "sourceType": "unambiguous", + "sourceMaps": "inline", + "retainLines": true, + "ignore": [ + "node_modules", + "build", + "browser_build", + "dist", + "configs" + ] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 726d7af1d..48a570cbe 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ test_products /test_wallets/ /build /browser_build/ +/dist/dist/ +/dist/src/test/ \ No newline at end of file diff --git a/.project b/.project index 1bad55ed9..2f7eec801 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - monero-javascript + monero-ts diff --git a/CMakeLists.txt b/CMakeLists.txt index 9728a9607..1750b04b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.4.1) -project(monero-javascript-wasm) +project(monero-ts-wasm) # build with exception whitelist from file file(STRINGS wasm_exception_whitelist.txt WASM_EXCEPTION_WHITELIST) @@ -151,7 +151,7 @@ set_target_properties( set( MONERO_WALLET_KEYS_SRC_FILES - # monero-javascript WASM bridge + # monero-ts WASM bridge src/main/cpp/index.cpp src/main/cpp/monero_wasm_bridge.cpp @@ -226,7 +226,7 @@ set( set( MONERO_WALLET_FULL_SRC_FILES - # monero-javascript WASM bridge + # monero-ts WASM bridge src/main/cpp/http_client_wasm.cpp # monero-cpp (modified for WASM) @@ -278,7 +278,8 @@ EMCC_LINKER_FLAGS_BASE # TODO? does EXPORT_NAME need to be the same for both targets? (or should it be set per-target with …_WASM, …_ASMJS?) "-Wall -Werror -Wl,--allow-undefined -std=c++14 -Oz \ ---bind -s MODULARIZE=1 \ +--bind \ +-s MODULARIZE=1 \ -s 'EXPORT_NAME=\"monero_javascript\"' \ -s ERROR_ON_UNDEFINED_SYMBOLS=0 \ -s ASSERTIONS=0 \ diff --git a/README.md b/README.md index bacd75204..050f02cf2 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Monero JavaScript Library +# Monero TypeScript Library -A JavaScript library for creating Monero applications using RPC and WebAssembly bindings to [monero v0.18.2.2 'Flourine Fermie'](https://github.com/monero-project/monero/tree/v0.18.2.2). +A TypeScript library for creating Monero applications using RPC and WebAssembly bindings to [monero v0.18.2.2 'Flourine Fermie'](https://github.com/monero-project/monero/tree/v0.18.2.2). * Supports client-side wallets in Node.js and the browser using WebAssembly. * Supports wallet and daemon RPC clients. * Supports multisig, view-only, and offline wallets. -* Wallet types are interchangeable by conforming to a [common interface](https://moneroecosystem.org/monero-javascript/MoneroWallet.html). +* Wallet types are interchangeable by conforming to a [common interface](https://moneroecosystem.org/monero-ts/MoneroWallet.html). * Uses a clearly defined [data model and API specification](https://moneroecosystem.org/monero-java/monero-spec.pdf) intended to be intuitive and robust. * [Query wallet transactions, transfers, and outputs](docs/developer_guide/query_data_model.md) by their properties. * Fetch and process binary data from the daemon (e.g. raw blocks). @@ -17,7 +17,7 @@ A JavaScript library for creating Monero applications using RPC and WebAssembly * [Architecture](#architecture) * [Sample code](#sample-code) * [Documentation](#documentation) -* [Using monero-javascript in your project](#using-monero-javascript-in-your-project) +* [Using monero-ts in your project](#using-monero-ts-in-your-project) * [Building WebAssembly binaries from source](#building-webassembly-binaries-from-source) * [Running tests](#running-tests) * [Related projects](#related-projects) @@ -28,66 +28,68 @@ A JavaScript library for creating Monero applications using RPC and WebAssembly


- Build browser or Node.js applications using RPC or WebAssembly bindings to monero-project/monero. Wallet implementations are interchangeable by conforming to a common interface, MoneroWallet.js. + Build browser or Node.js applications using RPC or WebAssembly bindings to monero-project/monero. Wallet implementations are interchangeable by conforming to a common interface, MoneroWallet.ts.

## Sample code -```js -// import library -const monerojs = require("monero-javascript"); +```typescript +// import monero-ts (or import types individually) +import * as moneroTs from "monero-ts"; // connect to daemon -let daemon = await monerojs.connectToDaemonRpc("http://localhost:38081", "superuser", "abctesting123"); -let height = await daemon.getHeight(); // 1523651 -let txsInPool = await daemon.getTxPool(); // get transactions in the pool +let daemon = await moneroTs.connectToDaemonRpc("http://localhost:28081"); +let height = await daemon.getHeight(); // 1523651 +let txsInPool = await daemon.getTxPool(); // get transactions in the pool -// open wallet on monero-wallet-rpc -let walletRpc = await monerojs.connectToWalletRpc("http://localhost:38084", "rpc_user", "abc123"); -await walletRpc.openWallet("sample_wallet_rpc", "supersecretpassword123"); -let primaryAddress = await walletRpc.getPrimaryAddress(); // 555zgduFhmKd2o8rPUz... -let balance = await walletRpc.getBalance(); // 533648366742 -let txs = await walletRpc.getTxs(); // get transactions containing transfers to/from the wallet - -// create wallet from seed phrase using WebAssembly bindings to monero-project -let walletFull = await monerojs.createWalletFull({ - path: "sample_wallet_full", +// create wallet from mnemonic phrase using WebAssembly bindings to monero-project +let walletFull = await moneroTs.createWalletFull({ + path: "sample_wallet_full" password: "supersecretpassword123", - networkType: "stagenet", - serverUri: "http://localhost:38081", - serverUsername: "superuser", - serverPassword: "abctesting123", + networkType: moneroTs.MoneroNetworkType.TESTNET, seed: "hefty value scenic...", restoreHeight: 573936, + server: { // provide url or MoneroRpcConnection + uri: "http://localhost:28081", + username: "superuser", + password: "abctesting123" + } }); // synchronize with progress notifications -await walletFull.sync(new class extends monerojs.MoneroWalletListener { - onSyncProgress(height, startHeight, endHeight, percentDone, message) { +await walletFull.sync(new class extends moneroTs.MoneroWalletListener { + async onSyncProgress(height: number, startHeight: number, endHeight: number, percentDone: number, message: string) { // feed a progress bar? } -}); +} as moneroTs.MoneroWalletListener); // synchronize in the background every 5 seconds await walletFull.startSyncing(5000); // receive notifications when funds are received, confirmed, and unlocked let fundsReceived = false; -await walletFull.addListener(new class extends monerojs.MoneroWalletListener { - onOutputReceived(output) { +await walletFull.addListener(new class extends moneroTs.MoneroWalletListener { + async onOutputReceived(output: moneroTs.MoneroOutputWallet) { let amount = output.getAmount(); let txHash = output.getTx().getHash(); - let isConfirmed = output.getTx().isConfirmed(); - let isLocked = output.getTx().isLocked(); + let isConfirmed = output.getTx().getIsConfirmed(); + let isLocked = output.getTx().getIsLocked(); fundsReceived = true; } }); +// connect to wallet RPC endpoint and open wallet +let walletRpc = await moneroTs.connectToWalletRpc("http://localhost:28084", "rpc_user", "abc123"); +await walletRpc.openWallet("sample_wallet_rpc", "supersecretpassword123"); +let primaryAddress = await walletRpc.getPrimaryAddress(); // 555zgduFhmKd2o8rPUz... +let balance = await walletRpc.getBalance(); // 533648366742 +let txs = await walletRpc.getTxs(); // get transactions containing transfers to/from the wallet + // send funds from RPC wallet to WebAssembly wallet let createdTx = await walletRpc.createTx({ accountIndex: 0, address: await walletFull.getAddress(1, 0), - amount: "250000000000", // send 0.25 XMR (denominated in atomic units) + amount: BigInt("250000000000"), // send 0.25 XMR (denominated in atomic units) relay: false // create transaction and relay to the network if true }); let fee = createdTx.getFee(); // "Are you sure you want to send... ?" @@ -103,12 +105,8 @@ await walletFull.close(true); ## Documentation -* [JSDocs](https://moneroecosystem.org/monero-javascript/MoneroWallet.html) +* [TypeDocs](https://moneroecosystem.org/monero-ts/MoneroWallet.html) * [API and model overview with visual diagrams](https://moneroecosystem.org/monero-java/monero-spec.pdf) -* [Mocha tests](src/test) -* [Installing prerequisites](docs/developer_guide/installing_prerequisites.md) -* [Getting started part 1: creating a Node.js application](docs/developer_guide/getting_started_p1.md) -* [Getting started part 2: creating a web application](docs/developer_guide/getting_started_p2.md) * [Creating wallets](docs/developer_guide/creating_wallets.md) * [The data model: blocks, transactions, transfers, and outputs](docs/developer_guide/data_model.md) * [Getting transactions, transfers, and outputs](docs/developer_guide/query_data_model.md) @@ -117,12 +115,16 @@ await walletFull.close(true); * [View-only and offline wallets](docs/developer_guide/view_only_offline.md) * [Connection manager](docs/developer_guide/connection_manager.md) * [HTTPS and self-signed certificates](./docs/developer_guide/https_and_self_signed_certificates.md) +* [Mocha tests](src/test) +* [Installing prerequisites](docs/developer_guide/installing_prerequisites.md) +* [Getting started part 1: creating a Node.js application](docs/developer_guide/getting_started_p1.md) +* [Getting started part 2: creating a web application](docs/developer_guide/getting_started_p2.md) -## Using monero-javascript in your project +## Using monero-ts in your project 1. `cd your_project` or `mkdir your_project && cd your_project && npm init` -2. `npm install monero-javascript@0.9.0` -3. Add `require("monero-javascript")` to your application code. +2. `npm install monero-ts@0.9.0` +3. Add `require("monero-ts")` to your application code. #### Running in Node.js @@ -148,8 +150,8 @@ Compiled WebAssembly binaries are committed to ./dist for convenience, but these 2. `cd emsdk` 3. `git pull && ./emsdk install 3.1.10 && ./emsdk activate 3.1.10 && source ./emsdk_env.sh` 4. `export EMSCRIPTEN=path/to/emsdk/upstream/emscripten` (change for your system) -2. Clone monero-javascript repository: `git clone --recursive https://github.com/monero-ecosystem/monero-javascript.git` -3. `cd monero-javascript` +2. Clone monero-ts repository: `git clone --recursive https://github.com/monero-ecosystem/monero-ts.git` +3. `cd monero-ts` 4. `./bin/update_submodules.sh` 5. Modify ./external/monero-cpp/external/monero-project/src/crypto/wallet/CMakeLists.txt from `set(MONERO_WALLET_CRYPTO_LIBRARY "auto" ...` to `set(MONERO_WALLET_CRYPTO_LIBRARY "cn" ...`. 6. [Download and install](https://unbound.docs.nlnetlabs.nl/en/latest/getting-started/installation.html) unbound 1.17.0 to your home directory (`~`). @@ -157,13 +159,13 @@ Compiled WebAssembly binaries are committed to ./dist for convenience, but these ## Running tests -1. Clone the project repository: `git clone https://github.com/monero-ecosystem/monero-javascript.git` -2. `cd monero-javascript` +1. Clone the project repository: `git clone https://github.com/monero-ecosystem/monero-ts.git` +2. `cd monero-ts` 3. Start RPC servers: 1. Download and install [Monero CLI](https://web.getmonero.org/downloads/). 2. Start monerod, e.g.: `./monerod --testnet` (or use a remote daemon). 3. Start monero-wallet-rpc, e.g.: `./monero-wallet-rpc --daemon-address http://localhost:38081 --testnet --rpc-bind-port 28084 --rpc-login rpc_user:abc123 --wallet-dir ./` -4. Configure the appropriate RPC endpoints, authentication, and other settings in [TestUtils.js](src/test/utils/TestUtils.js) (e.g. `WALLET_RPC_CONFIG` and `DAEMON_RPC_CONFIG`). +4. Configure the appropriate RPC endpoints, authentication, and other settings in [TestUtils.ts](src/test/utils/TestUtils.ts) (e.g. `WALLET_RPC_CONFIG` and `DAEMON_RPC_CONFIG`). #### Running tests in Node.js @@ -180,7 +182,7 @@ Compiled WebAssembly binaries are committed to ./dist for convenience, but these * [monero-java](https://github.com/monero-ecosystem/monero-java) * [monero-cpp](https://github.com/monero-ecosystem/monero-cpp) -* [xmr-sample-app](https://github.com/woodser/xmr-sample-app) - sample web application using monero-javascript +* [xmr-sample-app](https://github.com/woodser/xmr-sample-app) - sample web application using monero-ts * [monerostresstester.com](https://github.com/woodser/monerostresstester.com) - repeatedly sends txs to self to stress test the network (under development) * [monero-deposit-scanner](https://github.com/woodser/monero-deposit-scanner) - scan for incoming deposits to an address using a view key (under development) * [monerowebwallet.com](https://github.com/woodser/monerowebwallet.com) - open-source, client-side web wallet (under development) diff --git a/bin/build_all.sh b/bin/build_all.sh index 10c476018..db61813fa 100755 --- a/bin/build_all.sh +++ b/bin/build_all.sh @@ -18,18 +18,17 @@ HOST_NCORES=$(nproc 2>/dev/null || shell nproc 2>/dev/null || sysctl -n hw.ncpu make release-static -j$HOST_NCORES # don't exit because this will build translations directory even if build fails cd ../../../../ || exit 1 +# install node modules +npm install || exit 1 + # build boost ./bin/build_boost_emscripten.sh || exit 1 # build openssl ./bin/build_openssl_emscripten.sh || exit 1 -# build webassembly for distribution -./bin/build_wasm_emscripten.sh || exit 1 - -# build web worker -npm install --force || exit 1 -./bin/build_web_worker.sh || exit 1 +# build dist (wasm, commonjs , web worker) +./bin/build_dist.sh || exit 1 # build browser tests ./bin/build_browser_tests.sh || exit 1 \ No newline at end of file diff --git a/bin/build_browser_tests.sh b/bin/build_browser_tests.sh index 9a3831f85..1b2c1b87d 100755 --- a/bin/build_browser_tests.sh +++ b/bin/build_browser_tests.sh @@ -12,6 +12,7 @@ cp -R dist/* browser_build/ || exit 1 cp src/test/browser/tests.html browser_build/tests.html || exit 1 cp src/test/browser/favicon.ico browser_build/favicon.ico || exit 1 cp node_modules/mocha/mocha.js browser_build/mocha.js || exit 1 +cp node_modules/mocha/mocha.js.map browser_build/mocha.js.map || exit 1 cp node_modules/mocha/mocha.css browser_build/mocha.css || exit 1 # start server diff --git a/bin/build_dist.sh b/bin/build_dist.sh index 963ecc9db..b073bfed8 100755 --- a/bin/build_dist.sh +++ b/bin/build_dist.sh @@ -1,4 +1,5 @@ #!/bin/sh ./bin/build_wasm_emscripten.sh || exit 1 -./bin/build_web_worker.sh || exit 1 \ No newline at end of file +./bin/build_web_worker.sh || exit 1 +npm run build_commonjs || exit 1 \ No newline at end of file diff --git a/bin/start_wallet_rpc_test_servers.sh b/bin/start_wallet_rpc_test_servers.sh index a82279899..4da4f5517 100755 --- a/bin/start_wallet_rpc_test_servers.sh +++ b/bin/start_wallet_rpc_test_servers.sh @@ -1,4 +1,4 @@ #!/bin/sh -# run monero-wallet-rpc servers until terminated -node ./src/test/utils/RunWalletRpcTestServers.js \ No newline at end of file +# run monero-wallet-rpc servers until terminated +node ./dist/src/test/utils/RunWalletRpcTestServers.js \ No newline at end of file diff --git a/configs/jsdoc_config.js b/configs/jsdoc_config.js deleted file mode 100644 index 576c866d1..000000000 --- a/configs/jsdoc_config.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -module.exports = { - "plugins": ['./configs/jsdoc_plugins'], - "recurseDepth": 100, - "source": { - "include": ["./index.js", "./src/main/js/"], - "includePattern": ".+\\.js(doc|x)?$", - "excludePattern": "(^|\\/|\\\\)_" - }, - "sourceType": "module", - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc","closure"] - }, - "templates": { - "cleverLinks": false, - "monospaceLinks": false, - "default": { - "includeDate": false - } - } -}; \ No newline at end of file diff --git a/configs/jsdoc_plugins.js b/configs/jsdoc_plugins.js deleted file mode 100644 index 71eab344c..000000000 --- a/configs/jsdoc_plugins.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Plugin to generate jsdoc for undocumented methods. - */ -'use strict'; - -module.exports.handlers = { - newDoclet : function(e) { - if (e.doclet.name.startsWith("_")) e.doclet.undocumented = true; // hide private methods starting with "_" - else if (e.doclet.undocumented && e.doclet.kind === "function" && !e.doclet.comment && (e.doclet.scope === "instance" || e.doclet.scope === "static")) { - e.doclet.undocumented = false; // preserve doclet - } - }, - processingComplete: function(e) { - - // overwrite subclass comments and descriptions with superclass until no further changes - let changes; - do { - changes = false; - for (let doclet of e.doclets) { - if (!doclet.comment) { - let parentDoc = ""; - if (doclet.implements) parentDoc = doclet.implements[0]; - else if (doclet.overrides) parentDoc = doclet.overriddes; - if (parentDoc) { - for (let aDoclet of e.doclets) { - if (aDoclet.longname !== parentDoc) continue; - if (aDoclet.comment !== doclet.comment || aDoclet.description !== doclet.description) { - doclet.comment = aDoclet.comment; - doclet.description = aDoclet.description; - doclet.params = aDoclet.params; - doclet.returns = aDoclet.returns; - doclet.undocumented = false; - changes = true; - } - } - } - } - } - } while (changes); - -// for (let doclet of e.doclets) { -// console.log(doclet); -// } - } -}; \ No newline at end of file diff --git a/docs/developer_guide/connection_manager.md b/docs/developer_guide/connection_manager.md index 435cd347e..9778cd4a1 100644 --- a/docs/developer_guide/connection_manager.md +++ b/docs/developer_guide/connection_manager.md @@ -1,45 +1,39 @@ # Connection Manager -The following code demonstrates how to use monero-javascript's connection manager to manage daemon or wallet RPC endpoints. +The following code demonstrates how to use monero-ts's connection manager to manage daemon or wallet RPC endpoints. -See [MoneroConnectionManager](https://moneroecosystem.org/monero-javascript/MoneroConnectionManager.html) or [TestMoneroConnectionManager.js](https://github.com/monero-ecosystem/monero-javascript/blob/master/src/test/TestMoneroConnectionManager.js) for more detail. +See [MoneroConnectionManager](https://moneroecosystem.org/monero-ts/MoneroConnectionManager.html) or [TestMoneroConnectionManager.js](https://github.com/monero-ecosystem/monero-ts/blob/master/src/test/TestMoneroConnectionManager.js) for more detail. -```javascript -// imports -const monerojs = require("monero-javascript"); -const MoneroRpcConnection = monerojs.MoneroRpcConnection; -const MoneroConnectionManager = monerojs.MoneroConnectionManager; -const MoneroConnectionManagerListener = monerojs.MoneroConnectionManagerListener; +```typescript +// import monero-ts (or import types individually) +import * as moneroTs from "monero-ts"; // create connection manager -let connectionManager = new MoneroConnectionManager(); +let connectionManager = new moneroTs.MoneroConnectionManager(); // add managed connections with priorities -connectionManager.addConnection(new MoneroRpcConnection("http://localhost:38081").setPriority(1)); // use localhost as first priority -connectionManager.addConnection(new MoneroRpcConnection("http://example.com")); // default priority is prioritized last +await connectionManager.addConnection({uri: "http://localhost:28081", priority: 1}); // use localhost as first priority +await connectionManager.addConnection("http://example.com"); // default priority is prioritized last // set current connection -connectionManager.setConnection(new MoneroRpcConnection("http://foo.bar", "admin", "password")); // connection is added if new +await connectionManager.setConnection({uri: "http://foo.bar", username: "admin", password: "password"}); // connection is added if new -// create wallet with managed connections or set later -let walletFull = await monerojs.createWalletFull({ - path: "sample_wallet_full", +// create wallet governed by connection manager +let walletFull = await moneroTs.createWalletFull({ + path: "sample_wallet_full" password: "supersecretpassword123", - networkType: "stagenet", + networkType: moneroTs.MoneroNetworkType.TESTNET, connectionManager: connectionManager, seed: "hefty value scenic...", - restoreHeight: 573936, + restoreHeight: 573936 }); // check connection status await connectionManager.checkConnection(); -console.log("Connection manager is connected: " + connectionManager.isConnected()); -console.log("Connection is online: " + connectionManager.getConnection().isOnline()); -console.log("Connection is authenticated: " + connectionManager.getConnection().isAuthenticated()); // receive notifications of any changes to current connection -connectionManager.addListener(new class extends MoneroConnectionManagerListener { - onConnectionChanged(connection) { +connectionManager.addListener(new class extends moneroTs.MoneroConnectionManagerListener { + async onConnectionChanged(connection: moneroTs.MoneroRpcConnection) { console.log("Connection changed to: " + connection); } }); @@ -57,5 +51,5 @@ await connectionManager.checkConnections(); let connections = connectionManager.getConnections(); // clear connection manager -connectionManager.clear(); +await connectionManager.clear(); ``` \ No newline at end of file diff --git a/docs/developer_guide/creating_wallets.md b/docs/developer_guide/creating_wallets.md index 5b4446b71..0a9f7c8bb 100644 --- a/docs/developer_guide/creating_wallets.md +++ b/docs/developer_guide/creating_wallets.md @@ -10,14 +10,11 @@ Three types of wallets can be created: This example creates a client connected to monero-wallet-rpc then creates a wallet. -See [MoneroWalletRpc.createWallet()](https://moneroecosystem.org/monero-javascript/MoneroWalletRpc.html#createWallet) for all options. - -```javascript -// import library -let monerojs = require("monero-javascript"); +See [MoneroWalletRpc.createWallet()](https://moneroecosystem.org/monero-ts/MoneroWalletRpc.html#createWallet) for all options. +```typescript // create a client connected to monero-wallet-rpc -let walletRpc = await monerojs.connectToWalletRpc("http://localhost:38081", "superuser", "abctesting123"); +let walletRpc = await moneroTs.connectToWalletRpc("http://localhost:38081", "superuser", "abctesting123"); // create a wallet on monero-wallet-rpc await walletRpc.createWallet({ @@ -32,22 +29,21 @@ await walletRpc.createWallet({ This example creates a wallet using WebAssembly bindings to [wallet2.h](https://github.com/monero-project/monero/blob/master/src/wallet/wallet2.h). -See [MoneroWalletFull.createWallet()](https://moneroecosystem.org/monero-javascript/MoneroWalletFull.html#createWallet) for all options. - -```javascript -// import library -let monerojs = require("monero-javascript"); +See [MoneroWalletFull.createWallet()](https://moneroecosystem.org/monero-ts/MoneroWalletFull.html#createWallet) for all options. +```typescript // create wallet using WebAssembly -let wallet = await monerojs.createWalletFull({ +let wallet = await moneroTs.createWalletFull({ path: "./test_wallets/wallet1", // leave blank for in-memory wallet password: "supersecretpassword", - networkType: "stagenet", + networkType: moneroTs.MoneroNetworkType.STAGENET seed: "coexist igloo pamphlet lagoon...", restoreHeight: 1543218, - serverUri: "http://localhost:38081", - serverUsername: "daemon_user", - serverPassword: "daemon_password_123" + server: { + uri: "http://localhost:38081", + username: "daemon_user", + password: "daemon_password_123" + } }); ``` @@ -55,16 +51,13 @@ let wallet = await monerojs.createWalletFull({ This example creates a keys-only wallet using WebAssembly bindings to monero-project/monero. -See [MoneroWalletKeys.createWallet()](https://moneroecosystem.org/monero-javascript/MoneroWalletKeys.html#createWallet) for all options. +See [MoneroWalletKeys.createWallet()](https://moneroecosystem.org/monero-ts/MoneroWalletKeys.html#createWallet) for all options. ```javascript -// import library -let monerojs = require("monero-javascript"); - // create keys-only wallet -let wallet = await monerojs.createWalletKeys({ +let wallet = await moneroTs.createWalletKeys({ password: "abc123", - networkType: MoneroNetworkType.STAGENET, + networkType: moneroTs.MoneroNetworkType.STAGENET, seed: "coexist igloo pamphlet lagoon..." }); ``` \ No newline at end of file diff --git a/docs/developer_guide/data_model.md b/docs/developer_guide/data_model.md index 9f8d0caae..b66637a81 100644 --- a/docs/developer_guide/data_model.md +++ b/docs/developer_guide/data_model.md @@ -2,11 +2,11 @@ ## Overview -This section introduces the data model used in monero-javascript. Refer to the [API specification](https://moneroecosystem.org/monero-java/monero-spec.pdf) or [JSDocs](https://moneroecosystem.org/monero-javascript/MoneroTxWallet.html) for more information. +This section introduces the data model used in monero-ts. Refer to the [API specification](https://moneroecosystem.org/monero-java/monero-spec.pdf) or [JSDocs](https://moneroecosystem.org/monero-ts/MoneroTxWallet.html) for more information.


- Blocks have transactions which can have incoming transfers, an outgoing transfer, and outputs. + Blocks have transactions which can have incoming transfers, an outgoing transfer, and outputs.

## JSON diff --git a/docs/developer_guide/getting_started_p1.md b/docs/developer_guide/getting_started_p1.md index 7d3f197cf..e7a228310 100644 --- a/docs/developer_guide/getting_started_p1.md +++ b/docs/developer_guide/getting_started_p1.md @@ -1,44 +1,44 @@ # Getting started part 1: creating a Node.js application -## What is monero-javascript? +## What is monero-ts? -monero-javascript is a JavaScript library for producing Monero applications. The library conforms to a [model and API specification](https://moneroecosystem.org/monero-java/monero-spec.pdf) which aims to be an intuitive and robust interface to [monero-project/monero](https://github.com/monero-project/monero). +monero-ts is a TypeScript library for producing Monero applications. The library conforms to a [model and API specification](https://moneroecosystem.org/monero-java/monero-spec.pdf) which aims to be an intuitive and robust interface to [monero-project/monero](https://github.com/monero-project/monero). -In addition to RPC wallet and daemon server queries, monero-javascript can perform native wallet operations through WebAssembly (Wasm). The Wasm wallet enables developers to build trustless, client-side applications by eliminating the need to communicate with a RPC wallet server intermediary. +In addition to RPC wallet and daemon server queries, monero-ts can perform native wallet operations through WebAssembly (Wasm). The Wasm wallet enables developers to build trustless, client-side applications by eliminating the need to communicate with a RPC wallet server intermediary.


- monero-javascript uses Monero through three channels: RPC wallet servers, RPC daemon servers, and Wasm wallets. + monero-ts uses Monero through three channels: RPC wallet servers, RPC daemon servers, and Wasm wallets.

## Initial Setup ### Install Node.js and the Node package manager (npm) -Node.js and npm need to be installed before using the monero-javascript library. See the ["Node.js and npm"](installing_prerequisites.md#nodejs-and-npm) section of the prerequisite installation guide for instructions to download and install Node.js and npm. +Node.js and npm need to be installed before using the monero-ts library. See the ["Node.js and npm"](installing_prerequisites.md#nodejs-and-npm) section of the prerequisite installation guide for instructions to download and install Node.js and npm. ### Create a new Node.js project 1. Create project directory: `mkdir ~/git/offline_wallet_generator && cd ~/git/offline_wallet_generator` 2. Initialize the new project: `npm init -y` -3. Install the monero-javascript library: `npm install --save monero-javascript` +3. Install the monero-ts library: `npm install --save monero-ts` -## Write a monero-javascript program +## Write a monero-ts program ### The offline wallet generator An offline wallet generator creates and displays a new, random wallet address along with that address's associated view key, spend key, and seed phrase. An offline wallet generator only needs to produce and display these wallet attributes; it does not need to communicate with a Monero network, transfer XMR, or track a wallet's balance or outputs. -monero-javascript's keys-only wallet meets these requirements and is ideal for a monero-javascript-based offline wallet generator. The keys-only wallet is a minimal Wasm wallet implementation that only provides a wallet's _static_ attributes. It cannot initiate transfers, report its balance, or communicate with a Monero network. +monero-ts's keys-only wallet meets these requirements and is ideal for a monero-ts-based offline wallet generator. The keys-only wallet is a minimal Wasm wallet implementation that only provides a wallet's _static_ attributes. It cannot initiate transfers, report its balance, or communicate with a Monero network. The keys-only wallet has a file size just under 1/5 that of a standard Wasm wallet, allowing it to load more quickly. -### Write the essential monero-javascript code +### Write the essential monero-ts code Open your preferred text editor or IDE and copy the following code to a new, blank file: -``` -const monerojs = require("monero-javascript"); +```typescript +import * as moneroTs from "monero-ts"; main(); async function main() { @@ -47,21 +47,21 @@ async function main() { ``` Note the program's two components: -1. A "require" statement to import the monero-javascript library: -`const monerojs = require("monero-javascript");` -2. An asynchronous "main" function so that calls to monero-javascript can be "awaited" (most calls are asynchronous): +1. An "import" statement to import the monero-ts library: +`import * as moneroTs from "monero-ts";` +2. An asynchronous "main" function so that calls to monero-ts can be "awaited" (most calls are asynchronous): `async function main() {}` ### Building a keys-only wallet -monero-javscript implements keys-only wallets in the [MoneroWalletKeys](https://moneroecosystem.org/monero-javascript/MoneroWalletKeys.html) class. You can create a random keys-only wallet as follows: -``` +monero-javscript implements keys-only wallets in the [MoneroWalletKeys](https://moneroecosystem.org/monero-ts/MoneroWalletKeys.html) class. You can create a random keys-only wallet as follows: +```typescript // create a random keys-only (offline) stagenet wallet -let monerojs = require("monero-javascript"); -let keysOnlyWallet = await monerojs.createWalletKeys({networkType: "stagenet", language: "English"}); +import * as moneroTs from "monero-ts"; +let keysOnlyWallet = await moneroTs.createWalletKeys({networkType: moneroTs.MoneroNetworkType.STAGENET, language: "English"}); ``` -Wallets are created with a [MoneroWalletConfig](https://moneroecosystem.org/monero-javascript/MoneroWalletConfig) or equivalent JSON object. The monero-javascript library will create or restore a wallet based on attributes defined in the configuration or throw an error if any attributes are invalid. For example, a configuration that defines a view key but not a spend key will prompt the library to create a view-only wallet. The configuration object in the offline wallet generator code above contains no keys, so monero-javascript generates a new, random wallet rather than restoring an existing wallet. +Wallets are created with a [MoneroWalletConfig](https://moneroecosystem.org/monero-ts/MoneroWalletConfig) or equivalent JSON object. The monero-ts library will create or restore a wallet based on attributes defined in the configuration or throw an error if any attributes are invalid. For example, a configuration that defines a view key but not a spend key will prompt the library to create a view-only wallet. The configuration object in the offline wallet generator code above contains no keys, so monero-ts generates a new, random wallet rather than restoring an existing wallet. The offline wallet generator displays four basic wallet attributes: * The seed phrase @@ -70,26 +70,26 @@ The offline wallet generator displays four basic wallet attributes: * The view key Wallet getters are used to obtain wallet attributes and log them to the console: -``` +```typescript console.log("Seed phrase: " + await walletKeys.getSeed()); -console.log("Address: " + await walletKeys.getAddress(0,0)); // get address of account 0, subaddress 0 +console.log("Address: " + await walletKeys.getAddress(0, 0)); // get address of account 0, subaddress 0 console.log("Spend key: " + await walletKeys.getPrivateSpendKey()); console.log("View key: " + await walletKeys.getPrivateViewKey()); ``` The finished program should match the following: -``` -const monerojs = require("monero-javascript"); +```typescript +import * as moneroTs from "monero-ts"; main(); async function main() { // create a random keys-only (offline) stagenet wallet - let walletKeys = await monerojs.createWalletKeys({networkType: "stagenet", language: "English"}); + let walletKeys = await moneroTs.createWalletKeys({networkType: moneroTs.MoneroNetworkType.STAGENET, language: "English"}); // print wallet attributes console.log("Seed phrase: " + await walletKeys.getSeed()); - console.log("Address: " + await walletKeys.getAddress(0,0)); // get address of account 0, subaddress 0 + console.log("Address: " + await walletKeys.getAddress(0, 0)); // get address of account 0, subaddress 0 console.log("Spend key: " + await walletKeys.getPrivateSpendKey()); console.log("View key: " + await walletKeys.getPrivateViewKey()); } @@ -108,4 +108,4 @@ View key: b4e167b76888bf6ad4c1ab23b4d1bb2e57e7c082ac96478bcda4a9af7fd19507 ## The next step -Continue to [Getting started part 2: creating a web application](getting_started_p2.md) to learn how to write client-side web browser applications with monero-javascript. +Continue to [Getting started part 2: creating a web application](getting_started_p2.md) to learn how to write client-side web browser applications with monero-ts. diff --git a/docs/developer_guide/getting_started_p2.md b/docs/developer_guide/getting_started_p2.md index b578e7bab..6e343d545 100644 --- a/docs/developer_guide/getting_started_p2.md +++ b/docs/developer_guide/getting_started_p2.md @@ -1,17 +1,17 @@ # Getting started part 2: creating a web application -_Note: Though it is not strictly necessary, we recommend reading [part 1 of the monero-javascript getting started guide](getting_started_p1.md) before learning about monero-javascript web application development._ +_Note: Though it is not strictly necessary, we recommend reading [part 1 of the monero-ts getting started guide](getting_started_p1.md) before learning about monero-ts web application development._ ## Overview -This guide describes a convenient method for downloading, building, and launching monero-javascript sample web applications in an application server. The guide also explains how to convert the offline wallet generator from [part 1 of the getting started guide](getting_started_p1.md) to a web browser application. +This guide describes a convenient method for downloading, building, and launching monero-ts sample web applications in an application server. The guide also explains how to convert the offline wallet generator from [part 1 of the getting started guide](getting_started_p1.md) to a web browser application. This guide is divided into three sections: -1. [Creating a monero-javascript web application project](#creating-a-monero-javascript-web-application-project): A walkthrough of how to download and run the monero-javascript web application starter script, which automatically downloads, builds, and launches monero-javascript sample web applications in an application server. +1. [Creating a monero-ts web application project](#creating-a-monero-ts-web-application-project): A walkthrough of how to download and run the monero-ts web application starter script, which automatically downloads, builds, and launches monero-ts sample web applications in an application server. 2. [Modifying the offline wallet generator to display in a browser](#modifying-the-offline-wallet-generator-to-display-in-a-browser): Explains how to modify the offline wallet generator to display wallet attributes to an HTML page rather than the console. 3. [Running the application on your own application server](#running-the-application-on-your-own-application-server): Describes how to run the web application on your own application server rather than the built-in Python server. -## Creating a monero-javascript web application project +## Creating a monero-ts web application project ### Required software @@ -23,17 +23,17 @@ _Note: These are already installed if you followed [part 1 of the getting starte ### Configuring the project directory -A script is available to automatically download, build, and launch sample monero-javascript web applications in an application server. To download and run the script: +A script is available to automatically download, build, and launch sample monero-ts web applications in an application server. To download and run the script: -1. Create a new folder to contain the project: `mkdir ~/monero-javascript-sample-web-apps` -2. Enter the new directory: `cd ~/monero-javascript-sample-web-apps` +1. Create a new folder to contain the project: `mkdir ~/monero-ts-sample-web-apps` +2. Enter the new directory: `cd ~/monero-ts-sample-web-apps` 3. Download and run the web app starter script: `bash <(curl -sL https://raw.githubusercontent.com/woodser/xmr-sample-app/master/bin/web_template_script.sh)` Alternatively, you can [manually download](https://raw.githubusercontent.com/woodser/xmr-sample-app/master/bin/web_template_script.sh) the script then run it. The script configures a project folder and serves sample web applications on port 9100. Open a web browser and navigate to http://localhost:9100 for links to the applications: * "Offline wallet generator" shows off the final result of following this guide. To view the complete offline wallet generator code as a functioning web application, see "src/offline_wallet_generator.html" and "src/offline_wallet_generator.js". -* "Sample code" demonstrates a handful of common monero-javascript operations. Open the developer console to see the application's output. +* "Sample code" demonstrates a handful of common monero-ts operations. Open the developer console to see the application's output. _Note: In order for the server to reflect changes to source files, you need to stop it by pressing "ctrl-c" in the terminal where the server is running and then rebuild the application and restart the server by typing: `./bin/build_browser_app.sh`._ @@ -42,7 +42,7 @@ _Note: In order for the server to reflect changes to source files, you need to s 1. Navigate to the "./src/" directory. 2. Delete the files "offline_wallet_generator.html" and "offline_wallet_generator.js". We'll be rewriting them from scratch. 3. Create the file "offline_wallet_generator.html" and insert the following: - ``` + ```html @@ -60,18 +60,18 @@ _Note: In order for the server to reflect changes to source files, you need to s This line will tell the browser to run your offline wallet generator program. 4. Save the file. 5. While still in the "src" directory, create the file "offline_wallet_generator.js" and insert the following from [part 1 of this guide](getting_started_p1.md): - ``` - const monerojs = require("monero-javascript"); + ```typescript + import * as moneroTs from "monero-ts"; main(); async function main() { // create a random keys-only (offline) stagenet wallet - let walletKeys = await monerojs.createWalletKeys({networkType: "stagenet", language: "English"}); + let walletKeys = await moneroTs.createWalletKeys({networkType: moneroTs.MoneroNetworkType.STAGENET, language: "English"}); // print wallet attributes console.log("Seed phrase: " + await walletKeys.getSeed()); - console.log("Address: " + await walletKeys.getAddress(0,0)); // get address of account 0, subaddress 0 + console.log("Address: " + await walletKeys.getAddress(0, 0)); // get address of account 0, subaddress 0 console.log("Spend key: " + await walletKeys.getPrivateSpendKey()); console.log("View key: " + await walletKeys.getPrivateViewKey()); } @@ -91,7 +91,7 @@ The wallet generator is now _technically_ running on a server, but the typical e Add "div" elements between the opening and closing "body" tags to display each wallet attribute: -``` +```html
@@ -102,16 +102,16 @@ Add "div" elements between the opening and closing "body" tags to display each w Open offline_wallet_generator.js and find the lines that the print the wallet attributes to the console: -``` +```typescript console.log("Seed phrase: " + await walletKeys.getSeed()); -console.log("Address: " + await walletKeys.getAddress(0,0)); // get address of account 0, subaddress 0 +console.log("Address: " + await walletKeys.getAddress(0, 0)); // get address of account 0, subaddress 0 console.log("Spend key: " + await walletKeys.getPrivateSpendKey()); console.log("View key: " + await walletKeys.getPrivateViewKey()); ``` Modify these lines to assign each string to its corresponding div element in index.html instead: -``` +```html // print the wallet's attributes in the browser window document.getElementById("wallet_seed_phrase").innerHTML = "Seed phrase: " + await walletKeys.getSeed(); document.getElementById("wallet_address").innerHTML = "Address: " + await walletKeys.getAddress(0, 0); // get address of account 0, subaddress 0 @@ -123,7 +123,7 @@ The final HTML and JavaScript files should match the following: ### offline_wallet_generator.html -``` +```html @@ -143,14 +143,14 @@ The final HTML and JavaScript files should match the following: ### offline_wallet_generator.js -``` -const monerojs = require("monero-javascript"); +```typescript +import * as moneroTs from "monero-ts"; main(); async function main() { // create a random keys-only (offline) stagenet wallet - let walletKeys = await monerojs.createWalletKeys({networkType: "stagenet", language: "English"}); + let walletKeys = await moneroTs.createWalletKeys({networkType: moneroTs.MoneroNetworkType.STAGENET, language: "English"}); // print the wallet's attributes in the browser window document.getElementById("wallet_seed_phrase").innerHTML = "Seed phrase: " + await walletKeys.getSeed(); @@ -170,7 +170,7 @@ For example, to host the application on a standard apache server: `cp ./browser_ ## Additional resources -Read through the rest of the developer guides to learn more about using monero-javascript: +Read through the rest of the developer guides to learn more about using monero-ts: * [Creating wallets](./creating_wallets.md) * [The data model: blocks, transactions, transfers, and outputs](./data_model.md) diff --git a/docs/developer_guide/installing_prerequisites.md b/docs/developer_guide/installing_prerequisites.md index dac962b17..2630ddd3f 100644 --- a/docs/developer_guide/installing_prerequisites.md +++ b/docs/developer_guide/installing_prerequisites.md @@ -4,9 +4,9 @@ JavaScript is designed to run client-side in web browsers, so JavaScript programs cannot run as standalone or server-side applications out of the box. Node.js solves this limitation by providing a runtime environment, allowing JavaScript programs to execute outside of a web browser. -[Part 1 of the monero-javascript "getting started" guide](getting_started_p1.md) illustrates the use of Node.js to run a monero-javascript program in a unix terminal, so you need to install it to follow the guide. +[Part 1 of the monero-ts "getting started" guide](getting_started_p1.md) illustrates the use of Node.js to run a monero-ts program in a unix terminal, so you need to install it to follow the guide. -In addition, Node.js includes the Node Package Manger (npm). npm installs Node modules including the monero-javascript module, which is required to use the monero-javascript library. +In addition, Node.js includes the Node Package Manger (npm). npm installs Node modules including the monero-ts module, which is required to use the monero-ts library. To install Node.js and npm: ### Debian/Ubuntu: diff --git a/docs/developer_guide/multisig_wallets.md b/docs/developer_guide/multisig_wallets.md index 15e274e4f..0b4dae20a 100644 --- a/docs/developer_guide/multisig_wallets.md +++ b/docs/developer_guide/multisig_wallets.md @@ -2,21 +2,22 @@ The following is an example of creating multisig wallets. -For a full example of creating and sending funds from multisig wallets, see "Supports multisig wallets" in [TestMoneroWalletCommon.js](https://github.com/monero-ecosystem/monero-javascript/blob/master/src/test/TestMoneroWalletCommon.js). +For a full example of creating and sending funds from multisig wallets, see "Supports multisig wallets" in [TestMoneroWalletCommon.js](https://github.com/monero-ecosystem/monero-ts/blob/master/src/test/TestMoneroWalletCommon.js). + +```typescript +import * as moneroTs from "monero-ts"; -```javascript // create multisig wallets which require 3 out of 5 participants to send funds let M = 3; let N = 5; // create participating wallets using RPC or WebAssembly -const monerojs = require("monero-javascript"); let wallets = [] for (let i = 0; i < N; i++) { - wallets.push(await monerojs.createWalletFull({ + wallets.push(await moneroTs.createWalletFull({ path: "./test_wallets/multisig_participant_" + (i + 1), password: "supersecretpassword123", - networkType: "stagenet" + networkType: moneroTs.MoneroNetworkType.STAGENET })); } @@ -63,6 +64,6 @@ for (let wallet of wallets) { assert(info.isReady()); assert.equal(info.getThreshold(), M); assert.equal(info.getNumParticipants(), N); - await wallet.close(); + await wallet.close(true); } ``` \ No newline at end of file diff --git a/docs/developer_guide/query_data_model.md b/docs/developer_guide/query_data_model.md index af9e4e04a..ea2d61b52 100644 --- a/docs/developer_guide/query_data_model.md +++ b/docs/developer_guide/query_data_model.md @@ -4,21 +4,21 @@ Wallet [transactions, transfers, and outputs](data_model.md) can be queried by t ## Getting transactions with queries -See [MoneroWallet.getTxs()](https://moneroecosystem.org/monero-javascript/MoneroWallet.html#getTxs) for all query options. +See [MoneroWallet.getTxs()](https://moneroecosystem.org/monero-ts/MoneroWallet.html#getTxs) for all query options. -```javascript +```typescript // get a transaction by hash let tx = await wallet.getTx("48db7afb1e9eecb11303d4f49c955ffdee2ffc2fa513b8f05da35ff537744096"); ``` -```javascript +```typescript // get unconfirmed transactions let txs = await wallet.getTxs({ isConfirmed: false }); ``` -```javascript +```typescript // get transactions since height 582106 with incoming transfers to // account 0, subaddress 0 let txs = await wallet.getTxs({ @@ -31,7 +31,7 @@ let txs = await wallet.getTxs({ }); ``` -```javascript +```typescript // get transactions with available outputs let txs = await wallet.getTxs({ isLocked: false, @@ -43,14 +43,14 @@ let txs = await wallet.getTxs({ ## Getting transfers with queries -See [MoneroWallet.getTransfers()](https://moneroecosystem.org/monero-javascript/MoneroWallet.html#getTransfers) for all query options. +See [MoneroWallet.getTransfers()](https://moneroecosystem.org/monero-ts/MoneroWallet.html#getTransfers) for all query options. -```javascript +```typescript // get all transfers let transfers = await wallet.getTransfers(); ``` -```javascript +```typescript // get incoming transfers to account 0, subaddress 1 let transfers = await wallet.getTransfers({ isIncoming: true, @@ -59,7 +59,7 @@ let transfers = await wallet.getTransfers({ }); ``` -```javascript +```typescript // get transfers in the tx pool let transfers = await wallet.getTransfers({ txQuery: { @@ -68,10 +68,10 @@ let transfers = await wallet.getTransfers({ }); ``` -```javascript +```typescript // get confirmed outgoing transfers since a block height let transfers = await wallet.getTransfers({ - isOutgoing: true, + isIncoming: false, txQuery: { isConfirmed: true, minHeight: 582106, @@ -81,14 +81,14 @@ let transfers = await wallet.getTransfers({ ## Getting outputs with queries -See [MoneroWallet.getOutputs()](https://moneroecosystem.org/monero-javascript/MoneroWallet.html#getOutputs) for all query options. +See [MoneroWallet.getOutputs()](https://moneroecosystem.org/monero-ts/MoneroWallet.html#getOutputs) for all query options. -```javascript +```typescript // get all outputs let outputs = await wallet.getOutputs(); ``` -```javascript +```typescript // get outputs available to be spent let outputs = await wallet.getOutputs({ isSpent: false, @@ -98,14 +98,14 @@ let outputs = await wallet.getOutputs({ }); ``` -```javascript +```typescript // get outputs by amount outputs = await wallet.getOutputs({ - amount: "250000000000" + amount: BigInt()"250000000000") }); ``` -```javascript +```typescript // get outputs received to a specific subaddress let outputs = await wallet.getOutputs({ accountIndex: 0, @@ -113,7 +113,7 @@ let outputs = await wallet.getOutputs({ }); ``` -```javascript +```typescript // get outputs by their key image hex let keyImage = outputs[0].getKeyImage().getHex(); outputs = await wallet.getOutputs({ diff --git a/docs/developer_guide/sending_funds.md b/docs/developer_guide/sending_funds.md index bed294ef7..c694c6e80 100644 --- a/docs/developer_guide/sending_funds.md +++ b/docs/developer_guide/sending_funds.md @@ -1,13 +1,13 @@ # Sending funds -The following are examples of sending funds using monero-javascript. +The following are examples of sending funds using monero-ts. -```javascript +```typescript // create a transaction to send funds to an address, but do not relay let tx = await wallet.createTx({ accountIndex: 0, // source account to send funds from address: "55bcxMRhBWea6xxsot8moF1rdPprjJR2x4mfnNnTGgBJFgXa4gWXmWAYdUBKiRcJxy9AUAGJEg28DejvWdJU2VgUDrUvCHG", - amount: "1000000000000" // send 1 XMR (denominated in atomic units) + amount: BigInt("1000000000000") // send 1 XMR (denominated in atomic units) }); // can confirm with the user @@ -17,17 +17,17 @@ let fee = tx.getFee(); // "Are you sure you want to send... ?" let hash = await wallet.relayTx(tx); ``` -```javascript +```typescript // send funds to a single destination let tx = await wallet.createTx({ accountIndex: 0, // source account to send funds from address: "55bcxMRhBWea6xxsot8moF1rdPprjJR2x4mfnNnTGgBJFgXa4gWXmWAYdUBKiRcJxy9AUAGJEg28DejvWdJU2VgUDrUvCHG", - amount: "1000000000000", // send 1 XMR (denominated in atomic units) + amount: BigInt("1000000000000"), // send 1 XMR (denominated in atomic units) relay: true // relay the transaction to the network }); ``` -```javascript +```typescript // send funds from a specific subaddress to multiple destinations, // allowing transfers to be split across multiple transactions if needed let txs = await wallet.createTxs({ @@ -35,17 +35,17 @@ let txs = await wallet.createTxs({ subaddressIndex: 1, // source subaddress to send funds from destinations: [{ address: "55bcxMRhBWea6xxsot8moF1rdPprjJR2x4mfnNnTGgBJFgXa4gWXmWAYdUBKiRcJxy9AUAGJEg28DejvWdJU2VgUDrUvCHG", - amount: "500000000000", // send 0.5 XMR (denominated in atomic units) + amount: BigInt("500000000000"), // send 0.5 XMR (denominated in atomic units) }, { address: "52f7hei1UMrbvYUNtDMKZJMQjcfVyufYnezER8wVK271VmGbzE2kN7cMMG6qFjrb6Ub6qPkNt815a98kJmo874qG9GYZKD5", - amount: "500000000000", // send 0.5 XMR (denominated in atomic units) + amount: BigInt("500000000000"), // send 0.5 XMR (denominated in atomic units) }], priority: MoneroTxPriority.IMPORTANT, relay: true // relay the transaction to the network }); ``` -```javascript +```typescript // sweep an output let tx = await wallet.sweepOutput({ address: "55bcxMRhBWea6xxsot8moF1rdPprjJR2x4mfnNnTGgBJFgXa4gWXmWAYdUBKiRcJxy9AUAGJEg28DejvWdJU2VgUDrUvCHG", @@ -54,7 +54,7 @@ let tx = await wallet.sweepOutput({ }); ``` -```javascript +```typescript // sweep all unlocked funds in a wallet let txs = await wallet.sweepUnlocked({ address: "55bcxMRhBWea6xxsot8moF1rdPprjJR2x4mfnNnTGgBJFgXa4gWXmWAYdUBKiRcJxy9AUAGJEg28DejvWdJU2VgUDrUvCHG", @@ -62,7 +62,7 @@ let txs = await wallet.sweepUnlocked({ }); ``` -```javascript +```typescript // sweep unlocked funds in an account let txs = await wallet.sweepUnlocked({ accountIndex: 0, @@ -71,7 +71,7 @@ let txs = await wallet.sweepUnlocked({ }); ``` -```javascript +```typescript // sweep unlocked funds in a subaddress let txs = await wallet.sweepUnlocked({ accountIndex: 0, diff --git a/docs/developer_guide/view_only_offline.md b/docs/developer_guide/view_only_offline.md index 6b0ede213..3a0cbcebe 100644 --- a/docs/developer_guide/view_only_offline.md +++ b/docs/developer_guide/view_only_offline.md @@ -17,13 +17,13 @@ To sign and submit transactions using view-only and offline wallets: The following code demonstrates creating, signing, and submitting transactions using view-only and offline wallets: -```javascript -const monerojs = require("monero-javascript"); +```typescript +import * as moneroTs from "monero-ts"; // create and sync view-only wallet without spend key -let viewOnlyWallet = await monerojs.createWalletFull({ +let viewOnlyWallet = await moneroTs.createWalletFull({ path: "my_view_only_wallet", - networkType: "stagenet", + networkType: moneroTs.MoneroNetworkType.STAGENET, primaryAddress: "55bcxMRhBWea6xxsot8moF1rdPprjJR2x4mfnNnTGgBJFgXa4gWXmWAYdUBKiRcJxy9AUAGJEg28DejvWdJU2VgUDrUvCHG", privateViewKey: "03d463f03ae547b11dfdf194a07ff82c14a3c6a3de559bd89c9a5e8dc5e9ae02", restoreHeight: 573936, @@ -32,9 +32,9 @@ let viewOnlyWallet = await monerojs.createWalletFull({ await viewOnlyWallet.sync(); // create offline wallet -let offlineWallet = await monerojs.createWalletFull({ +let offlineWallet = await moneroTs.createWalletFull({ path: "my_offline_wallet", - networkType: "stagenet", + networkType: moneroTs.MoneroNetworkType.STAGENET, seed: "spying swept ashtray going hence jester swagger cease spying unusual..." }); @@ -54,7 +54,7 @@ await viewOnlyWallet.importKeyImages(keyImages); let unsignedTx = await viewOnlyWallet.createTx({ accountIndex: 0, address: "56j5AskbiNeeb2UAnS85qpey93GYs4VWB78hazZKGdsKCGHvEXUD6nuMQqXaiiY8SwMWsmtAEXS9kA2ko7hgNtGHKsEWyhv", - amount: "250000000000" // 0.25 XMR + amount: BigInt("250000000000") // 0.25 XMR }); // describe unsigned tx set to confirm details diff --git a/docs/img/architecture.drawio b/docs/img/architecture.drawio index c8eb54684..02c8f1e97 100644 --- a/docs/img/architecture.drawio +++ b/docs/img/architecture.drawio @@ -1 +1 @@ -7X1Zk5s49/enSdX7XiTFbrhkB2N2sI1vpjC7AYNZDPjT/0Vv6W47Sc9MuiczT5xKNwhJRuf8zqKjI/UnlC1HsfHrVK3CqPiEQOH4CeU+IQgMQwT4NZdM9yWfUQxC74uSJgsfqn0tsLNL9FAIPZT2WRi1Lyp2VVV0Wf2yMKiOxyjoXpT5TVMNL6vFVfHyW2s/ia4K7MAvrks3Wdil96UkDn0tl6IsSbunIT88Kf3Hyg8FbeqH1fCsCOU/oWxTVd39VTmyUTGT75Eu9+2Ebzx9erEmOnZvaSC6YfhH+gceUPIOnY6IWab7zzD1wI6zX/QPQ3543W56pEFT9ccwmruBPqHMkGZdZNd+MD8dAN9BWdqVBbiDwWWcFQVbFVVz1xYVBJ7jGVDedk2VR8+eoARKoeHTk0fqInMf1bET/DIrZtjQ5+iYgTbM9YAfaHCOmi4anxU9EECMqjLqmglUeXhKLL4s8PtGD4jEcIq8Lxi+sneBPfAsfc5aYvEAqwdIJU/df6U6uHgg/J9iAva/xATkUXgeWUCi2BUL0MUNFlDQu3EA/1/iwA0xoIhrHtwWAwh5NyYQVzSPQqCNH26rpkurpDr6Bf+1lHnJla91VlVVP/DiEHXd9GBa/L6rXnIqGrNuOzf/QgKi3N97d/ePd9z40PvdzfTsxoiaDAw+ah7LjoAQ2+c3z3qab792dXf32Nef5HRb9U0QfYeUyCPnOr9Jou67RH9QfzOlv4udJir8Lju/tIvvAILFPwkC+E8A4Gcz7R1Y8dDUqDLwMl/FH0FfCD8OPRY89nEPm4dmrzj69B5/h8nklbod/KKIup+rdUM/IuPgltYlAjLax9/Uug84gdH31MLo4koN49Cje/xMDcPIDTWMvZ8ppK54o1bHqKlA2dI/+3bQZPWMplW2b/yHb3zOMTD87iVbmqjNLv7+rsLMxVn+2gcSz7dFlhzBdQBoOatSZqZhBnxw+uFBmYXhnZzXMyLvRowzn3DuFceP4DWvmP1Q+BJGdzx+eGP4XXmMv83jhIkbPEbei8fI4yTle+5OdAzpeQI1M6bw2zYLvmU3UeqF1iSBo/xnDeff0KWdPnbEgKi1KXuL8oTjExZ+ht+oSZ+xAL/Bgceyv6twKejLo9v0JOkY/uW1K3U/rCu1e9UfRr30oHEYfQWVd1bgCAT/GEBZeTe3fo6Zwt9HBeMHeXInj88Ucnz3AVUeWnFZmYBXK7I9+BkUWf2H33TzZVXWPcBNC661qBuqJv8D+QNGyBH8/1Ifk+/43u8n5RSQcoz6+sFeseeRXM/gRt6AG/l+Av+Gaf6dco0aHhDmTsfODHsKWsxCGvpt+qRCn3H1psJ9xQQMxTBs9qf8tr6P08TZOHd2jwkD2IMuq25agdWrCvuq66ryhpnoZmfv2ppUfVdkR/B+jxEi6H5g9Tzuckzm0NWX8zn8MkT7P/ZzyOiu2TuiBf/yyiRgj1Of5wDBHms9hwjxfhD52VPgK6dLuPu8J2kx6Ia5ha9pe8vaoui7UZb4MWUf8fioNH8I/jeo0m/Ix2sBvPtK+rEUeqaDQ7/zP6H0/S0izNoVYbM1o1sDpIhJRYOPZrsp7ybgajffiiZLq+A3i3oBUN3MXMaIIeO4PE2vRGMm4Zgy5lwuXNw8ZGlloUt6PhfgLj+61vyMhzXHZWiTFxhzzZiuQPeSQbU0V+3nHpiU5vKAW9QSbTAxbcAVbVAlzY+qY4w5zdeKfaY6GjR21ZwEV3dvGgpL3R04K/cXkA164YuQ0ZxaE6EddJxrmKNb8ObawpAeCbOt3CjmKAC+nU+gNoCHIOHnaKGlfT/2Jk8veSdcu5fgjC13Y7D3WHdpEJ2ag4rOKTuc+pLwl/15X1DrI4aSPign6Op0cIwJsjdbi1mvvcFsux5DKhWOA1iuJ+Jkt2zSXIaQCSW1RC9Ngbr8zkAADpktVS8nvF6de7nCKmv0K3lpLeOenZTJ9toq9b1V37SyV216wcc7oQgzwVdTTqq9DpWFsuyOl/Vpt7OV/Uoh1FOz2wKGImJdUGy/Llly7VuORjTpPkM6IGoMD6VGCV61WB9JhICX4pmMILPqsb1yrI++G9SVJJ5HRQB1ocDXnTH3qoULQ1hR7TeANM7Sm/sZYFQVcDjv1mtKlz2PaCLDg6T8cuyzogoAyRitEn1UOAZmwxUYeDSKZeAcna0cHXsd1cj80MLb7mBHWoyQ9gG0cE8K5OsxG9jnw9qS2XLXV1h8ySxSEo6rbYqclbZkqZ68HDr/qMWLPlV627KQbjd1wfaiLMhuzwm2Z+9Rkc9s+7wHU2Nd82WFYNzWH6h1G63sXF72yqmlrIVU1UrLthbeiWI77ba5d0L5fVXCSLPeS224j9aHKZIbHkazhmU4KWmtFY5j6H5HY0JzLo5QyhZB6CwlYi2pPcFZB+mgW5totPr1+pjoxmVa6tIK+A9MsbTPUnmRZJtDxwUU+xa5PBj80I5rREFGYcT9hX/aICZWJKy331vrbpSmUk5cR4MladoY0z68hKyF81Opi+YMSwtWYD7WPaygysPJxsZg4o6TXiNkijuGcB6CLGQxErfA9ztsNfLRcbVCDL0XGEJvjYDkxwt4ZF+40iIyj9y2WgwT4WaotO3O7bA9t83y6jBgimthgbjKDC6xwCVpyIeh0rdWZYLBQTIHYz5vYYqYicDnddgEXodAIpnTMju6QL/VpOIMbu6YuaXWYqSkp5rlcQOPL4rkZowSEcGR6E3SAW0uXshGKzqTk1xuBiCwKDLYeZBj3HonVFrMblM8XZ9gkmMuMu4F0eQuWfYsrdK1QC+FQMn2DAyzudeuVq3bi5esO1wgN3ZlNMdyBuM3Rz5xWMdm+R3LqVmxs4dy6e7sqdyR7NK2SWc1Ifu0ENh8p65Unz/y09Slq8lhh/ziKRfBS6y8s80CF20E33lLjWF2teJHfJKDb0bobVy42rrwVry6hcS147iHtVcRvWJI5xYhli5KS3Lk4yf2sAyBP+NPhtU048mgW/xSOCgRc+3Zy1aXyGempORamNqszmoHT5tsudKjC5GWe99RsnMrNQAG4VrO/MsRt2r6Qi88bbuCRmybKXJOdkV4PFONvciOMont0JS3ozOgMkZYtu/EAnS0ZmuIMHdu+axEtRFJVeVAlN7MQWcDaLLaZyTf+/Ww5o1ZGa6VUqGM/LS0M/28KMpmdcJiULmsQnuBUHx1kieh3Vk6KEvMIsiiBg1QnOqEmFcZu1cdDIOW/hIno/ggraE87u2DoyRLiyezch5RfhzIfUVxwOyejtHePy0tKSxRcV8QnsVrZY4sThLqVDYUosYiNGJMLtppBC19UzOL6gjDlNrtFyq7jNlCAoVWgR2QfjmeZ02jc8uz0hlKShlQGMpdvcUWOKJMKSsdmdgTJs+tN/L8HoizmFXhIcm203k79jRvWXKOGPRkufJdV+ow0SeAEYxtNs3gNebGl88EoYnaZVwvdXnY9OZxhM9058izsKmzGgZN8EkuC3QmeTfRipMBYgkN7O72uwvbu5nGZiZ0iIYgxVtG2QzybhUu1zDtI/sO8WlnVrG0okAb0AzadxGUcPYUwSi/EGKcVEUZ2vb1zKwdMmzS/I7JwklH3N6vyMN2YRyBJZ5Nr8lZU7BEFV7NGEu9RPg4wFEysjIUb5FhYhY88BWYdJ69edyhOzMn1hpASS+uz36vaj2KwX6Fpip+PuPzNzKnyyCRbBOye/ioZmwXjgePBx4aw5LKelcJEQTz+PYEnYKjP1E7rcSrKpzHgSArtTFc9bIOUr0rFwHlRt1x76NiISyMkmnbI34k4HUjeNkZlJF1meu4GFjnxlNHfjY6J444q2Y8uwN5KgColXRQOTR3cTNDvuDpNMT2eoKHPZybQLxchwKtrJyeaPB7vwsxzCk1MS0DH9omOiseHHIBk/S5oA8+X2zLgsfRvPQNSrkw6SrATqo1NlaUYSs6VmBcaB06FiQZbYwCEmi/HTwbPet5FvLachYnnKC1lcAvPHCT6+ezwmb64hJXKTubiF5B6nEbjcqOgWwImRBaWK2oCeLC2WNZS8NCQhdG3TUuFyr7sLts6LieQdUcFoXIaSdrkHaDSiKZTO/ZzaSW+5BKTM6DI4Fd7BStLqoB9jq7Z4Z9DpAosEIXBbAIvBFBKap0B/qanEqod5U5+zfjWYhKS5/QeX5en1pYgSotJGmWWsmn/aXHaGrQQBtKOHJiYQj8Tt7xBz2qTU8ktw7GHHoXkWBJO1QHHGi1Kl8FfUCyOlaxOyyYZFr3NLMSyeN6tb5U3CAnCngHYUhEt2QK2aGTo5zR+FFgGbQn9VQadpBIkyzPlhhAU5A2y1ommEARWImJVnEG3Domc50kwmSeTgy54/BhSyfMeumzdMxWoSxLFaRIyQIGKkuI8ot0Ap6/mPBxSu73zOyOdmQxaCWb7JMyOXDAtBCrC2UPXsLxK0ucBTjWBnkmn9vqND2yrsUxmGfSp8BgoGXGtZnCDUOimVjkVBteCPlASpKDzpC4XuNjYsvitJQNjqWbVUFEvcqOtGut5j60XXCOaC5dYrKYUKpssDGbQLIGVKOUpYcjfa66bUjKK4vnpSJRRJtiwbvR+XLJLFUpy1elqvT8LO07MwmZtdhKsCXZwkXkN3RRBLJg+mboSpfDQof9SZEs2pfbhHRlToXtCTj8mewZBk3D8sCIq3hWchzBXcyEcXc1ZHomMtYMDFyLnJ5nBDnc0l4dyOcDPiTqdOz5iUFYKzMtEouC1F1M0KXOuZbfSyw9FHIWq/i64jy5VCSGqsX6ONcXsswM9JzpzjLny4iuMAzrCBN97uiAP3oVJIk1zWJEBAGLsgD/rMUks37YK/rIBIfw4DCjb8qnckeTfO4VfMybEiMjS0eCU9pPZm3pGbk1y1FD09RqYDDlwO/PREa3MpNKe2PJ0pqE0Ymlj5dE1WzdE3g2mV2nCxi8MV/QMm/JEcvQhGPknefIDJ0J3GQKumzxkpotuSFjxKWSyp7E11y0nJhS9FdFrM32VRaL5SkVgOgLuOKKebriZuUDKcBtoLulFO1rntatyaCEmlF8A5pVe0DjTC9rquwyFCcMG3N+Hxqw0K4OCJGsocOZSRKNt1jDnU05F8gUL4eoyVs0Q5C6YB5kdObpIJUYHcoLOlzuOrimpdYgnX1F503OeDWgQUJuTaY+mvror3ygDoUFtTtD5sYcU9csAc9JhrToSjjhUiSy6iD14FUC1J6r8oOzSeKJRRrGt41M5r08OGyME7hINAOT7e0l59yOibsjxiUn0jIklGdNay8jM76GATE30HY2MXrAsSxmBRwyBkOdRw3r9InPips+JTLgykspn88jWa4cAwOS0orTpjqMhd0mGOTb2rAALLQllquAKAK5DlwNKHfPkrhT5hPMEpjewYDk7kwN9MaqjdwIj32+4DnaiiUIozkF3U01BjhtD2EkKRO9BoCmAbyJgBZzLDF5QissbOq3piQj5nKs8FUAxiDR7A4hLVuDVxEdKbS9WnDJ2MhInu36botJKTOdYDBGpiuCXlgt1VDJd7QtbUDNVQJjKWXrIj7ofKFm/XmrWCcrXhTDJqBZEQLUJSohlvWOFSLdsQB3J7kKyC3Np1JSI6xsyzbQB8040JF5GfZ1wsgxPxPX4Q0v46iANKnFhlkyNvBhGAcyzUWur7PJo2NeT/l1HNJcBJwbBcXpFaNZUBJQOd101WwlmI2xbf1zldzhOEAhaIEJmJXxbpbNpWd5wjiiFu2GP3ZrGzYj3Owbwx/PaiIcbFsqPe5S25M+z7ohRvdPcMt6uXvoncpwwdvJECUlm3aZ0RvtpLVNUK1zoHRPA+ymCG9DkUafagWOjWTyTnDnsKudGOrDWttxe2KB7E69qzDLtXzyXcOv1m4z+4KiMGTZjiThyAJji6F6wrwzz5mAvOCdO4WM140m0pkNhytImmWRRk9UNPSnVVGJCZjSxBJh0dtGNdPZu7gAkDDyImc4WE6QIDCQFQ2a77IqoRRZp3askZnTATMrzZ0A/epc6AHBmKOEJB4NeSfP2FRrcpFx1cnmBZ6mrdmtyhSJz2YrHhanRc6qmXXwgZ1NUGpxMM1lLGasaKkjztMKvt+d1dlnnBiR4RmSpTmonD0rtCtK2t57PM2PsiCzgF4MYtpUS6KnixWseR2tBEI2tjY7y2vUNw3maMKYm44tJ/EmaZjVzNvtbD9Lg1vHrOWaK78rpNmL92k2ZxAouFRqovM0otHGJmUDZ8+Ym8yhHdXE2ISvpAvPsp7FMBjLMPwFWzAmEHfeLhly59KDvWEHSKcJKbMMW7mcLUXI2Tq/LPQZpzTJJKUP5/q0pnnJZpcAeQxDJwrvmhGdOIzfSztlENnKEtTYTEydEYBZBYKa2Lt0oBm+lUqgJpNjioR3966ogRmkDObQgpSs+LO28o4h1xUmy6hR0cZKRdPrrHMqZqDLZcECjSnnNJ8Bvsj0kk/oxKTtJShMaN6mWVAIfAo+A4UybeRabajgNQOF97Zt011YMq9SYWwXNM1M5149x8ApttixBfIG49stvcVhaSIVg0PaqAwJCjmQBq1RTkW3UUpnTc/OkUl2FNRkfWEkX3LAo4TNbE2ppYssX8SJ3/LNRZ02Yp8v3dSzhMT2bZ4bgNzhQpAObTgXCOtToZLeSd1NUmZka/KElOEopgxn7SdT3fVA9SZSCk8KnI5gmsPgTijxwIQics/wPIOsDroQ2OFZdVG3OpN705F77MKDOitQR1i5CdN3WUaGJA4Br12ExV6FjJ1HwjZWb1LMAO6J2XNnVGi9Tg7ZqiyOhXxS3WJYLlccPOIbPcuFiTBsSAHAtrcmyS1M10YQ9Egr6Fo8H4ZcWguKyWdHyzxZMooG0GSmy2wpiwZvHqJJ7Accy/hdIDSzkPmQbQaaKG1YeDZMKasFmi75sr03KlM+oOnWy6CespR5jhI3tDfJ2QHME3EKWdImibdxdQQ+jSMAVeJSiL7ZR5dgnkChnbkr0SjWczPcL1ot7K3GKMnA2/OQEVUedgyCgaSz8whcWJYA340cMksPyA1SRmNgjUWmwjGJqlEjY0dtL0nLyzkIJgtYtqjRd8FyMVrO7Ew2wAyyzERky3GpXTQ41NpxLcilrzTqWTjUlhiAWUuouQi5ifOC1E6buZ0iA/s62iNmAGlubG9XH/hthg4NDB626jxZYyMj4ZBlDDSKEQ7KZZ5+qL0b+dHqorQyNs+Xdnswb+ENpi3lAeqOc1u452wpIbXRj8OsG7ZtcIzjDZGhEVm0hYnO9MGhQvZRr0iIeYLJqha+X3KYaOoGkRyEaDthfDrPEMB/wtZViSe5uSYpZpddaEqHktb70+UuAsHsBAFYCpua2s2WMyaY9y9zsEL2+R46iYOESmMdBnrnCcVOEucAmGwcDsXamN3d+Y1PHmfCgNrFeRvgC747ry44P2HExiHSbDXgHNnBKNLzCUyOmgDDZNrCaw8IB+4skHk0RlUdnGkZE1GqicusJ6wWO4YaD6emtB39HacTkOqF63rPYfNL02G/U4uTfuLHFDhdp7s5HpjLMosDgpHFQ5x535z1GsMqoSAKNc41KPCiOVZKZEOz223nkWwdPfV8hdyd+DZ3OELIRG1PhDUrinauVjLuNHGIzN+4Vrc5pZqislP69Sid7KmXWq4g5wiMIVHr8DLInFJvV6fldkGQ/olpo+bc++fzrFCJXh7aUYwTX9yy6hkRz2RaoNG5mMm3IcNtth11G1vCharw5lbrzCnYHaE2S8s9BMfBBeqWvLmmWlH11cVxgpzjxrxb1b24Yy/PZleTcGaRbWQDX3WZPOb9KrsL2NN8ITi53Zsly77rqhGC3Fg1Wjwuvz3PSr21bvR+K3KLq3Wjp0wc9l4AfqfhvDHV6prDt7KO4VsMfsc0nOs0uE8I4ZfzMl0xM69vo/apJOmeiPEqOfJukfAlr9/OzNeouGbuN5YaHxf6/ybTnvZuPLzDp+fbI24yE7pKX8S/zaHbGTQIjr5ag/8MYy87qeK4jd4pe+Y6xa68E+zPdVMd5nXY38L8Hf6Tb0tff9qK8zHCDN/KqfstzD8QZuzfLsvYtZG+ZusPUimfpxwjL7YRfCHgxfOtBJ+hLxBM/mA3wd3dT8ywvD1w5GHgb9hi8MCdXyQbEyNfZk9iFP5KKbw1DxNHX/cEfXAeJnatdB4syX0+/eemDq7hWBRZ3UbPsuCCourD/1h6/XV2PUZRN7LrbyVivl+WHQZfs+P9t7f8/P1F2BuF/wmh772pBSZe5eC+ZP0Ce2VXviHjP08ykSvJlBzH+H/2//83OnffzjZ8kdT5IYINo9eSvSCv91R87JYK7DrDenOngkGZNac9QmyRRcdr1/6X2OD0qH/+gqfwdsZR15Nw4nG733ONfMv2v9t+pydVdhVluWefVQdfDu1/TWaf7Yh4gAL11KJqwqj5yfOCbwszfEOYb0VmPnYyh96azP0Fr/6nGl30rftDP8aPRjHiC/Law3oMaf1ZVxp93AHxdSfk21xpwAJ/elbtQXr+3Fvj5CvI3Pf6cz0C9FrRPFkIeVYK8az1f2nz8L7nQizgV8YBuY7A3zQO6HudzIGg15tiVhVQ2qBI6O/aP7LwV2bbu1r1a7b98zYdvd5x89ymz6z7bdTfERKvlPkvYdG/tZh2D4nfcHjHgD2EXplcAqeuEHErEvN+gEDesCX2h3vgvwZqn4548T49j8l+0Hkvj0tibwjGor+UE4k9aoYnVUG8LVBzI6z7uif4VU/vHYxFvu0sKNHUftaPxfTbY7jb7vzLeQzI9z2GmX+/TcQ7QoJ4Ha/95z0G+GcYiKdDUjDs5dlSFP6hp6R8Y4i/VkRhASEvYvjkK0y8MYZ/u9+rtaC/G1/46fYDvrYfv5cMftKBh9cA+OeXDOBrm8P5UVkdP/1eMng6qu46PPwLeAvw9WTyiXO/Q3nfYNuj+fwH2Xadb3nv5N0z7/dSz7v6eASOvW2pB/5YLfyt8w7vQfEbEe+JiBtq4kZgiPpQQGC3srJfIWCmav0TSPOn8yWvou0kca1WUYS8JhhOvBfBkGsJ+hctkryFfT8Ayo8B/w8uhVyj+T5D8I/7DME/4nkxJP2t4z4ICU9Jyy/EGL0+o+6Dox1PKurfG7z8SP7BixuH+H5wyPJ6EvJSsvM5aPlbsj9asl9tT/pI0XZX2qoTSWFfCWz+WTjYnt1//gfXuZ5OjP0L+VC/SExyzheCSOjr59UqE7H4i0FJFMO/kOh3OqZeZbX8vOWrDhNdWXMwPt3FlNa2R6fQb8CEeTgLF4Gq+YdWhdHdbAii67oA8n93JOl/TLt8JzL5MZvb0JfaA8OI6yQo5L1myDeB8a382HmoUdR87qrP829we7w/kPs3Jn42Jl4uhODw44T4OSY+1l3E3nCm8ZsXxz59XRh7LL+9KPbCnvwyyRePMaw3bIb5tZIvEAj5Aj23QK+3Ub7OwX2rbYNBx69mOJ8Xb1tv+3kbcN5wnvmH+D23/rbQ92H+DyIU+cX2alL4l+ebul4Gvyhk8Wqj71sBSkDQqz3DFAq96uu9AQpfG9Z/BqB/Em4/BtHjwuYvAqLFqxnZX/fZf5CgQFDIx0LoETH/ghzGN6ieX2vihyOv8wlx5K+hZp7poc8+yKvsaJIkPxY26Bv+es/H724i3gwU6pcCCoq9znb/q3lKGPl6cQf9aGTc2MUa7em2jcp98a88Y+pXSkO63hdxIwfpo+P+N3al/eb4T8trXbw2Ih/J8e/9abyrVYPw2gT8T5wTQsC3kkM/8KCQm1y6duwe/kDql6Cu/2tS+cskpGDQq0gb9Hhey89PEgW3TVV1zw34/Jfo1Cqcj//i/w8= \ No newline at end of file +7X1Zd5u6+ven6Vrve9EuZuNLZjBmBtv4Zi/MbMBgBgP+9H+RxGliu226d5Pdc07dJjFCEuL5PbOE+IQyxSDUXpUoZRDmnxAoGD6h7CcEQXAEBX+mkvGx5DOKzqHHorhOg8dC+GuBlZ7Dp8JLtS4NwuZVxbYs8zatXhf65eEQ+u2rMq+uy/51tajMX1+18uLwpsDyvfy2dJ0GbfJYSuLQ13IxTOPkcmUYejqz8/wsrsvu8HS9TwgaPXweTxfepa+n+k3iBWX/ogjlPqFMXZbt47diYMJ8ou6FbI/t+G+cfR53HR7atzQoPBLScSfklhu+XrhkXSPLz0/wnby8Cy+38TDYdrwQqE/SNrQqz5+Oe8AGn1A6aYscHMHgq9dUj7BE6RCCS9G5twtz+pk4TJmX9UNPKPTwAVWiNM9flHPE9A+UN21dZuHlzKE8gEvSTyMM6zYcvnnr8DNBAauGZRG29QiqPDX4/NTiiUkx7IJhfwfx5AXaz4XeE5fFz31/pTT48kTs+4QXnCD4K/kL9+fSFh0PiFEku8/w/A2kfyDgRFR2otoPgLgiKs9zLEffEBWcQQl0jgbPZy5cj0x9lIeW94o0nyhFncJDWj8jcEPuO6B8EwFi9mWGvwYBn5M3IMywOyDAxOzdQMD+l0BALtx8gYBEsRsI0NkdCObvJwb4/xICd8RgTtxicF8MIOTdQCBuaB4GwEo+HZZ1m5RxefBy7msp/RqVr3WWZVk9YbEP23Z8Mvle15avkQqHtN1Mzb+QgCiPx+7D8eWIHZ56fzgYXxzoYZ2Cmw/rS9kBEGLz8uBFT9Ph164eji59/STSTdnVfvgdUiIX5FqvjsP2u0R/Un8Tpb/LO3WYe216eu2vvAMTzP5NJoB/ggF+NWjvAMVTU71MwWC+iv/FXX4Sfhy6FFz6eGSbp2ZXiD6P45+ATN6o297L87D9tVo38EIy8u9pXcInw130Ta37xCcw+p5aGJ3dqGEcgtAbNQwjd9Qw9n6mcH6DjQJc4LoEZfZYgaClTquJm5bprvaervgSMXD77WtY6rBJz97uocKE4iR/zROJp8M8jQ/guw9oOalSeqJhCmIj6ulEkQbBg5xXE0c+3DFOf8LZK8SfPPW77vtrNnrA+GnE8LtijL/N44SJOxgj74Uxcgk8vufuhIeAmgLbCZjca5rU/5bdROevtCYJHOWfNZz/QJe22tASPaJUhuTOiiOOj1hwibR+qElfQHAv8LqU/VOFO4e+XNymZ0nH8C/XrtTjbd2o3Zv+sPlrDxqH0StWeWcFjkDwjxkoLR5yHi955ttB+VPOAqWfWrFpEYOh5ekO/PbztPrLq9vpa1lUHeCbBnxXw7Yv6+wv5C8YIQfw86U6xN/xvd9PyudAyrH51w92Bc+FXC8D/Xtx/vsJ/OzbSh10ST/8/6PR32S1kRuNfi+AfRbJD9Lotx7VJ4TwislByifwuiZsnkvi9pkYV372cpLQq9Tam8G85opbcL+hAJ4g/aegPadnn8bw6WWK8y6Y0I0njP+sckdw9MtrfvgMY687KaOoCd9JEd96a8WDYH+u6nI/pUT/CPN38Cfflgl5zrZ/jDDD99yzP8L8A2HG/tNlGbs10rew/sArfxm9Iq8yUl8IePYyK/UZ+KQw+YPE1MPRL3TW79848nTjb8hWPaHzmzj2GHmVyp5fz9S81aXH0eueoA926bFbpfNkSR5TM5/ryr9lxxy45s2DWk+8KnzgyrIL/ssyNbeJGuDs30nU3PPpiXezEhh8C8f7Z0p/faoae6PwP3Poe+dHYeIqnHsN/Qy7sivfkPFfJ5nIjWSKtq3/P+v//yc6d9/OATxPwH+YYMPorWTPyNv03Mdm57DbOfn1gwoGZabOTCF7noaHW9f+t8iVX/TP3/AU3g7c/DYIJy4zRy818j3b/26p82dVdpNleYTPrPwvQIL+q2T2VXLtiRXmzy3KOgjrXxwXfFuY4TvCfC8z87HBHPoLcu2/3Oiib51q/Bg/GsWIL8i1h3VJaf2sK41e1hF8nVR7mysNIPDGF9WepOfnRo2TVyzz2Ouv9QjQW0XzbCGkSSlEk9b/rc3D+y4xml0ttSMuOP3IOKDvtcgLQW+XGC1LoLRBEd89tL9A+DvD9q5W/Ra2f9+mo8R3bfoE3R+j/o4scaXMfwuL/q3JtEeW+MMO75iwh9Abk0vg8xuOuJeJeT+GQN6wiPqHyym+JmqfVwu6n17mZD9o6eBlSuwNyVj0t3IiMfT1tDs2I96WqLmT1r3uCb7q6b2Tsci3nQU5HJvP2iEf/3gM0zMUV8m538BjQL7vMUz4/TER78gSxHW+9t/3GOBfYSCe19th2OtlynP8Vy24uwPXA7jT559ZFfj3SjjMIORVip+8Ypk3pvjv93szVfRP0w+/3LzAt+blz4zCL3q05pYB/v0ZBfjWJLFeWJSHT39mFJ4firjNHv8GzgR8G2s+I/cn0/cN2C7W9V+E7XY55qMP+Ajen5mgd3UBCRx720wQ/LFa+FtP1jwyxR+OeE+OuKMm7uSN5h/KENi9RdtXHDBRtfoFpPnp5ZQ3yXiSuFWrKELeEgwn3otgyK0E/QfNobwFvh8wyo8Z/l+cKbnl5scFhH89LiD8K5rmSpI/Ou6DOOF5TfMrMb7MZ/17yZBnFfWfm9v8SPzg2Z3HRT84o3kbhLyW7GzKaf6R7I+W7Kunlz5StJ2lumwFkt+VPJN95veWa3Vv2cbpvabBAq9JHkD9G8ulfpOc5LScCCKhr5+rSShi9jeTkiiGfyHR73Q8v1r08utmt1pMcCTVxrhkG83VpjnYuXaHTehpAzUg8AhUTr/UMgi/7EE0BFFVlQP5b9Mp4fFvape/r0peKIdvZUi+zbPf2qLs7X7/t6fS0dfaA8OI2zVSyHtFyHcZ41vLZ6dbDcP6c1t+nv6Cw8Pjo99/eOJX88TriRAcvgTEL3niY91F7DZ3/fNW5TJ39unrvNml/P6c2St78tuszbjksN7wrMzvtTYDgZAv0EsLdP2U5fUS3bfaNhh0fBXhfJ69bb7t1z2f84bN4z7E77m3i9X32fxf5FDkN3uUc45/efnM1+vk1xyZXT0H/FYGJSDo6pHiOQpd9fXeDArfGtZ/h0F/kt1+zESXic3fhIlmVxHZ3/fZf7BAgZgjH8tCF475D1ji+AbV83sFfjhyvdwQR/4e10yRHvrig1wtniZJ8mPZBn3DPlEf//AT8WZGmf9WjIJi14vh/+46JYy8ntxBP5oz7jzkGu6opgmLXf4fuQXV77QM6faxiTtrkD4673/nobU/iP+yZa+zayPykYh/bxPGm1mD4NYE/E9sI0LA9xaHfuA+IndRunXsnrbi/eJX1X+XVP5GC1Iw6CrTBl22c/nXFolCv3wP/O+sMf9WSvQuvjcbev5oPfpPbt15JZPYnQ16ya9ppo9Z6gK9YTLtQWLCmgOkfRAc+FGBPr7f5VXwDb1G5i1UxlAMw6Y47f6rVXQg5A/TMndEe3lVYVe2bVnckf122nToVkWUXZunBzC+y7t2oBeWoRji6SVAX06n4Esf7v7aPc0dvev+rviXGx65nWwlsS93WORXKO67L815S1Ln2a7+otfn/H0p/UdvzsEu93YhPw4TX+74NyhyS/9L2TuI6BtmJi5ce9kM+Yci8oYtkr8hRdcQPlySupRCn77urRx4rfcJpR4PEX7aNRlh0hWtmT0kC3FJgY9qOQnnxODbdjoUDIZSwF8Gdf3dJ4SeymghoG2Ho6ilMD1DwA8JbUzl/NnJAoaSZ5qoZVMB7nCDY07nOFi1HZoyOJ42VrTh8FQn6vOGYsvd1AOdUGzms7NKpHQ6onS4pPR5QXGDYutDRnGVbJ3mLQUaO0pGgm8PIw34heb0rJl5M8gCvXB5QKt2pQrQFjpMNYzByTljZWJIhwTpRqplY+ABbqcjqA30LC/ip3CmJl03dAZHLTg7WDln/4QttoO/cxlnoROtkoGK9jHdH7uC8BbdaZfPVwcMJT1QTlDlcW/rI2StNya9Wrm90bQdhpQKHPmwVI3E0WqYuD73AR2ISoGe6xx1uK2OAD6kN/NqMeLV8tRJJVaag1dKC3MRdcwoj5bblInnLru6kdxy3fEe3vJ5kPKekrBi5baoxBdFezivjtutJe+WMqEc6+0GAIoIVT5nulXBkCvPtFWiTnYp0gJRozko0Qsw1Hx1IIFfuhBOZAgZZYft5EN18By/KkXhNMg8qAv5nmYPmVvOHBjC8nK3BqSxF+7UTw+jCo/DWbtazTXJdYk61F1IzM6HLs1LH5CMVkvBQ/mDb9RsjoFTg1D49sHeSOGh01CVzPYNvGn3VqhGCGntQQvnKEOeFjG+ddqvTIkptl2JRefUJEX+sNwkyEluCmbeked96x3UaNYlcmeZJtJux9bfnOUZ2e5Y3nKtHSpwqWWddgSCaqonyQTtNF4/XzXh0sqkRScfm7k5E8tKbpjGxFtBaMbtJnOPKLcrCxipVzuxCXbhaj+GUs3BaFozNCvGjbnEcQzdbSmMr0/5AUqY3A/shUisRKUjWHMv7jVzHQ5mt1odYk0/jwtNXAKVTecL6yQWZ1GyWHSYQZFnkou9zvXNsEJkZOAH3Jt5xzViYHnMuLuduWoHcSyk2LFVWBTHtT7ugnPAmDg3FppgTGxpwjLMRZqL5fNif7SwwR/Zw6hVCJngts6fej8NGIzETXB9mykHLjwsl4iudTxNaI3uk9xwBqesM1uYROqSm0aNYCJY96W62TottmM3aVbue0x2TMwXlqnOxib4SurSvi+1jVka4OYgiYUxjzMxWUiFpuxsJoZXAZBI+rhIDw7QbxUp272T2UZmKpUQysmxYjhcx6OzLDopLYeEfyA6g7RBm7MbMOGSSqU4k+oeCCyK9FbmZxi72vKlGjGbBE9WR5hk6bOEu344OguGOYnLZMVTC96X0x0Nw0zmNstl43TCOW33Z8iJHAnNsIzGuPWBi23Gthhuy7BKmm+tvlg4W2sstiSzsCzSXo7ILsl5JtsqS8XjDtw4tslytJk+O7vymXdjM2stI8cFC8G37kKl6W0leyEXZ+DKCLWJckdd5e6SUzaQsLJtZ79yS6KTdfHUIMTCQSlRCj38yOwXAfB6vFE363o46lSDn3MbJSK2Obnp8hx69BgXbAPP18uT0sLjOl0stfBMJMXOs+X01Ig1YINgJaXe+YCbFXWmZq66WUIDtkllKSPbPDic5rU1Sw8SiW3RhLPCE6AyRpiWZ0c8dDAnazhtA8/7+aRE1QFJFHlPFO6EoL0GNFnuUpLrvKpfcfqkDFdyIc/17LiwUu00y4t6ecQiULkoA2uGzLnyKI18szU1UBYbuZ+GNeqj+LzlI06hrU6xMQxaeAucDKO9uIKyqLP2thwvTI5Mi+mOskNP7so5C8zu8RDuvOPCFIMCFXY54ZqcWmTI7CiidmlBAarPAj3CpLwZB9DSM1QjLw8wPFfa3UxhFhGTi6DQzLE90i2G06RpNHZxkltdTuY6FARSW22m3Ls8Jox4oCOXH12nWkvTOBB7NqnCfZxuxtNm6CjONKUM0anRdKSHrpR+pI6ARzCmXte9WxtrTzoRhCqo52G10KR+3RmHAT5RrS1NwqZMahg0wUepyNGJ5O1IyXYKiMXXsLPdbc9M56QqkxrQPuz9BG9oed1L22WwWMGUh+xaxKPsScVSsgytQTNo14ZQzFpjCKPcjI9wUhEkaNNVE1hbpF8n2QPI/FFDnM4ryf1mph+AJZ5Mr8Gao79AZU5JaVM5h/jQw2E8MBIUbZB+pGcc8BXoZHorg8vu2xN9ZMwelHTC6uR1itqhGOyVaKLgpxM+XZE+nnuRZOqA2cEHJWXaYNi7HPCGaYaUV9uSDyGYwzdH6OgfvHG+VQu8LIPpPhBkqdS6o5xXfqK1xcyfO2F72HmokPMzvaCb5oAfCHhV8256AmVkVWQaLvjmqXaVgZuMzpElTooRTe5AlvCA1QrKL22KPTupLp3xZOwjazXC/Q7ODCBejj0HrcyMGinwd7cNMMwuVCEpfA/axBoj7G1yBpPUKaf2HpdvipzD0azw9Ll8ppOljx0Vc6jNMMWWVCTDON/YVMSLElrrOcRTXtO7FnrSsjTg1MUkTjhBqUuem7ngINNOJ5lJtdk5KpPpbQx5JyPVsAkHeUtDFoSMCMUvl/MRYoPJY1mJ/UxEZ3rV1g4byLugPa+pqJqYqt7PcoFVj2YvbnuFRFKJ2jHrUSl2wTw2WBcOeWa2ldUqL3vYba2O7ncZ4ESe4dvQhwXgjfByXiZb0Ndol3y1LY3JvxlOfFiY2ohO792ojg0sQ6UakBQzX0rH3bnDqHmvgjZz/sAKuc5zW2nL7bWwMlyB3NgYve8cRIRFdV/ucaDVymzpdz7JaFjJbDF/lCjNVY1SIA+r5epcsr0Uy2AMfB8LTkHnkk3FByml8APP0GhHaonYbyGBIhmOKTDATX5SLyqJoH2ZZ0Q6XEYpcOvo1LHjEJM4KtallsX7DRXTq4XHUBFTBpIklpAsxjMYqCw+zM7iEXj+QsxFCbnb0ZM72pJ5rxZMvIuLeM8C00Isz3Ord2OWW5rCJMCR2ksT+ZxGo6iBcUyWxlyDOvo6DS1Stklltu9j1cBCu1xzfMD5YhzvNZrEtQofYksSxoWkswxVL3Mi7BRmoBxzOfWhbv1TSLHJApOEeK5IOhMxMSSpQDWKabI/UKey3QSktDQ5TsxjWbDmDBgblS0W9EIR02xZKHLHTdK+NeKAXgmNCJuixZ8Fbk3luS/xhmcEjnjezzTYG2XRpDypiUlHYhXYGoHDn0qurlMULPW0sIwmJccS7NmIaWdbQYZrIENFw8C1yKgpIsjghnIrXzrt8T5WxkPHjTTCmKlhkljoJ85shM5VxjbcTmSoPpfSSMFXJetKhSzS80qoDlN9Pk0NX8vo9iSxnoRoMk0zNj9Sp5byuYNbQqJQUQxGhBCwKDPwz5yNEuMFnawNtL8P9jY9eIZ0LLYUyWVuzkWcIdISsrBFOKG8eNKWrp6ZkxzVFDVf9jQm77ndiUipRqITcacvGEoVMSo2teEcK6qluTzHxJPrdAY3r09fKIkzpZChKcLWs9a1JZpKeXY0eE0yOVFJF2yf0sJCTiRX5Co2XIx0IXjLPFIn+yoJ+eKY8ED0eVx2hCxZspPygWTgNlDtQgx3FUdp5qjP+YqWPR2aVLtP4XQnqYrk0HOW79fGNB4KQGiVe4SIV9D+RMexypmM7kymnPWlOScFqMGZFE2QGm/sJXTCtBcLjAqkGRUsti1cUWKjk/aupLI6o90K0CAmNwZdHQxt8JYeUIf8bL49QcbaGBLHKADmJE2aVMkfcTEUGKUXOzAUH7Wmqlxvr+NoZJCa9iw9lTg38/dr/Qi+xKqOSdbmnLFOS0ftAWPjI2nqIsoxhrmTkIm/+h4x1tBmMjGazzIMZvosMvh9lYU1Y3exxwjrLiFS4MqLCZdNd7JY2joGJKURxnW5H3KriTHIs9R+BiC0RIYtgSgCufYdFSh31xTZY+oR9AKY3l6HpPY076m1WemZHhy6bMaxlBmJEEaxMrodKwwgbfVBKMojtQIMTQH2JnxKyLDY4Ag1N7Gx2xiihBiLocSXPrgHkWK2CGlaKrwMqVCmrOWMjYdaQrJ027UbTEzo8QiDe6Tb3O/45UIJ5GxLWeIa1FzGMJbMLU3Ae43LlbQ7bWTzaEazvF/7FCNAgLpEyUeS1jJ8qNkmQHeUSp/cUFwixhXCSJZkAX1QDz0VGud+V8W0FHETcW1Od1N27pPGfLamF7QFfBjahgxjlmmrdHSpiNMSbhUFFBsC50ZGcWpJqyYU+/OMqttyshL0Wt803qmMH/jYRyFohvGYmXJOmk6lJ2nEWKISrJo7tCsLNkLc6GrdG05KzO8tSyxc9lxZozZF3RCteUe4YdzM2Xd2qTtgdBI0F+N1s0iptXpUm9ovVxlQuscedhKEs6BQpY6VDEd6PLpHuLWZ5VYItH6lbtkdMUO2x86R6cVKOnqO7pUrp558QYHv03RLknBognuLoGrE3BPHGoC8YMytTEarWhWo1IKDJSROskihx3nYd8dlXgoxCGkikTCpTa0YyeRdnAGT0NIso1lYihHf15ElBZpv0zKey5I23zJ6aox7zChVZwT0qzK+m16xdBCR2KUg9+jq63JFzlK2PFocz1GUOblVqSxy6WTFg/w4yxglNfcesLMxOp/tDWMRCSkjmMqAc5SM77YnZfIZR1qgOZpkKBYqJs8KbfOCsnYuR3GDxEsMoBeNGNa8IdHj2fRXnIaWPCHpG4uZ5DXs6hqzVX7IDNuS4mgd1/RywnYz2c9CZ1cRYzrG0mtzcfLiPYrJaATyz6USaxyFqJS+Thjf3tHGOrUpWzEwJuZK8cwxjGvSNMbQNHfGZrQBxJ2zCprcOlRvrZke0ihCTE3dks8nU+YzpsrOM23iU4qk48KDM21cUZxoMQvAeTRNxTLnGCEV27TXiVu5F5jS5JXIiA2N5oFZBYIaW9ukp2iuEQugJuNDggQPx46ggghSAjE0L8ZL7qQu3UPAtrnB0EqYN5FcUtQqbe2S7qlikTNAY0oZxaUAF4lacDEVG5S1AIUxxVkUAwqBT8GloFCi9EytdAUM05c5d9PU7ZkhszLhh2ZGUfR46pRTBJxikxkaIG8wvtlQGxwWR1LWWaQJi4CYI3tSp9S5XVJNmFBp3TFTZpIZeCVenWnRE21wKmZSS5Ur8SxJZ2HkNlx9Vsa10GULJ3FNPrY8i2N7IHc47yd9E0wF/OqYK6R7VLajmOrpijwiRTAICc2au9FQth1QvbGYwKMMJwMIc2jcDkQOmFBE6miOo5HlXuN9KzgpDuqUJ3Jn2FKHnTlQZwnq8Esnprs2TcmAxCHgtQuw0CmQvnVJ2MKqdYLpwD0xOvaE8o3bSgFTFvkhl46Kk/eLxZKFB3ytpRk/EroFyYCxrY1BsjPDsRAEPVAyuhJO+z4TV7xscOnBNI6mhKI+NBrJIl1Igs4Z+3AUuh7HUm7r8/UkZB5kGb4qiGsGngxTwqi+qomeZO300pD2aLJxU6ibm/IUo0Q15Y5SugdxIj5HFpRB4k1UHoBPY/NAlThzRFvvwrM/BVBoa2wLNIy0zAh2s0YNOrPWC9J3dxykh6WLHXy/J6n0NAAXliHAtZF9amo+uUaKcPDNIU8VOCJRJawl7KDuRHFxPvn+aALLFtba1l/MBtOenMkamEGGHol0MSzUswoHajOseKnw5Fo58fvKFHwQtQSqg5DrKMtJ9bie2skSsK+DNWA6kObacrfVntukaF/D4GSjTMEaE+oxiywioFH0oJfPU/ihdE7ohcuz3EjYFC9tdyBu4XS6KaQeag9TW7hjLTEm1cGLgrTtN41/iKI1kaIhmTe5gU70waFc8lA3j4kpwGQUE98tWEwwNJ2I93y4GTEumSIE8ENYmiJyJDvVJIX0vA0McV9QWnc8P76LbsvzwFJY87FZb1h9hDnvPCUrJI/roKPQi6g4VIGvtS6fb0VhSoBJ+n6fr/TJ3Z1GfHRZAwbUzk8bH59x7Wl5xrkRI9Y2kaTLHmfJFkaRjothclB5GCaTBl65QDhwe4ZMd6OX5d4eFxERJqqwSDvCbLBDoHJwYoibwduyGgEpbrCqdiw2DZoKuq2SH7UjNyTA6To+xHgglqVnewQj86c8864+aRWGlXxO5EqUqZDvhlOulEj7ervdTHeysbXE9WRye+SazGYJPhXUHRFUjCBYmVJKuF1HATJdcaVssrliCPJW7laDeLTGTmzYnJwyMLo4XwXnXmLlarM8LjYzgvSOdBPWp847nSaFSnRS3wxCFHvChlFOiHAikxwNT/lEvjUZbNLNoFnYAs4VmTM2amuM/vYANWlS7CA48s9Qu+CM1bwRFE+ZHUbIPqyNh7c1np2hkyazq4o4PUvXko4v21Qasm6ZPiTsKS7n7czqjIJh3nWO/eo9xxgOz+7OGt3bB/rnZ+2miKIs25fr5KaJSqUMppdwcf8H \ No newline at end of file diff --git a/docs/img/architecture.png b/docs/img/architecture.png index 17f757510..8333e400f 100644 Binary files a/docs/img/architecture.png and b/docs/img/architecture.png differ diff --git a/index.js b/index.js deleted file mode 100644 index e18975751..000000000 --- a/index.js +++ /dev/null @@ -1,287 +0,0 @@ -'use strict' - -/** - * Export all library models. - * - * See the full model specification: http://moneroecosystem.org/monero-java/monero-spec.pdf - */ -module.exports = {}; - -// export common models -module.exports.GenUtils = require("./src/main/js/common/GenUtils"); -module.exports.BigInteger = require("./src/main/js/common/biginteger").BigInteger; -module.exports.Filter = require("./src/main/js/common/Filter"); -module.exports.MoneroError = require("./src/main/js/common/MoneroError"); -module.exports.HttpClient = require("./src/main/js/common/HttpClient"); -module.exports.LibraryUtils = require("./src/main/js/common/LibraryUtils"); -module.exports.MoneroRpcConnection = require("./src/main/js/common/MoneroRpcConnection"); -module.exports.MoneroRpcError = require("./src/main/js/common/MoneroRpcError"); -module.exports.SslOptions = require("./src/main/js/common/SslOptions"); -module.exports.TaskLooper = require("./src/main/js/common/TaskLooper"); -module.exports.ThreadPool = require("./src/main/js/common/ThreadPool"); - -// export daemon models -module.exports.ConnectionType = require("./src/main/js/daemon/model/ConnectionType"); -module.exports.MoneroAltChain = require("./src/main/js/daemon/model/MoneroAltChain"); -module.exports.MoneroBan = require("./src/main/js/daemon/model/MoneroBan"); -module.exports.MoneroBlockHeader = require("./src/main/js/daemon/model/MoneroBlockHeader"); -module.exports.MoneroBlock = require("./src/main/js/daemon/model/MoneroBlock"); -module.exports.MoneroBlockTemplate = require("./src/main/js/daemon/model/MoneroBlockTemplate"); -module.exports.MoneroConnectionSpan = require("./src/main/js/daemon/model/MoneroConnectionSpan"); -module.exports.MoneroDaemonInfo = require("./src/main/js/daemon/model/MoneroDaemonInfo"); -module.exports.MoneroDaemonListener = require("./src/main/js/daemon/model/MoneroDaemonListener"); -module.exports.MoneroDaemonSyncInfo = require("./src/main/js/daemon/model/MoneroDaemonSyncInfo"); -module.exports.MoneroDaemonUpdateCheckResult = require("./src/main/js/daemon/model/MoneroDaemonUpdateCheckResult"); -module.exports.MoneroDaemonUpdateDownloadResult = require("./src/main/js/daemon/model/MoneroDaemonUpdateDownloadResult"); -module.exports.MoneroFeeEstimate = require("./src/main/js/daemon/model/MoneroFeeEstimate"); -module.exports.MoneroHardForkInfo = require("./src/main/js/daemon/model/MoneroHardForkInfo"); -module.exports.MoneroKeyImage = require("./src/main/js/daemon/model/MoneroKeyImage"); -module.exports.MoneroKeyImageSpentStatus = require("./src/main/js/daemon/model/MoneroKeyImageSpentStatus"); -module.exports.MoneroMinerTxSum = require("./src/main/js/daemon/model/MoneroMinerTxSum"); -module.exports.MoneroMiningStatus = require("./src/main/js/daemon/model/MoneroMiningStatus"); -module.exports.MoneroNetworkType = require("./src/main/js/daemon/model/MoneroNetworkType"); -module.exports.MoneroOutput = require("./src/main/js/daemon/model/MoneroOutput"); -module.exports.MoneroOutputHistogramEntry = require("./src/main/js/daemon/model/MoneroOutputHistogramEntry"); -module.exports.MoneroPruneResult = require("./src/main/js/daemon/model/MoneroPruneResult"); -module.exports.MoneroSubmitTxResult = require("./src/main/js/daemon/model/MoneroSubmitTxResult"); -module.exports.MoneroTx = require("./src/main/js/daemon/model/MoneroTx"); -module.exports.MoneroTxPoolStats = require("./src/main/js/daemon/model/MoneroTxPoolStats"); -module.exports.MoneroVersion = require("./src/main/js/daemon/model/MoneroVersion"); -module.exports.MoneroPeer = require("./src/main/js/daemon/model/MoneroPeer"); - -// export wallet models -module.exports.MoneroAccount = require("./src/main/js/wallet/model/MoneroAccount"); -module.exports.MoneroAccountTag = require("./src/main/js/wallet/model/MoneroAccountTag"); -module.exports.MoneroAddressBookEntry = require("./src/main/js/wallet/model/MoneroAddressBookEntry"); -module.exports.MoneroCheck = require("./src/main/js/wallet/model/MoneroCheck"); -module.exports.MoneroCheckReserve = require("./src/main/js/wallet/model/MoneroCheckReserve"); -module.exports.MoneroCheckTx = require("./src/main/js/wallet/model/MoneroCheckTx"); -module.exports.MoneroDestination = require("./src/main/js/wallet/model/MoneroDestination"); -module.exports.MoneroIntegratedAddress = require("./src/main/js/wallet/model/MoneroIntegratedAddress"); -module.exports.MoneroKeyImageImportResult = require("./src/main/js/wallet/model/MoneroKeyImageImportResult"); -module.exports.MoneroMultisigInfo = require("./src/main/js/wallet/model/MoneroMultisigInfo"); -module.exports.MoneroMultisigInitResult = require("./src/main/js/wallet/model/MoneroMultisigInitResult"); -module.exports.MoneroMultisigSignResult = require("./src/main/js/wallet/model/MoneroMultisigSignResult"); -module.exports.MoneroOutputWallet = require("./src/main/js/wallet/model/MoneroOutputWallet"); -module.exports.MoneroOutputQuery = require("./src/main/js/wallet/model/MoneroOutputQuery"); -module.exports.MoneroTxPriority = require("./src/main/js/wallet/model/MoneroTxPriority"); -module.exports.MoneroTxConfig = require("./src/main/js/wallet/model/MoneroTxConfig"); -module.exports.MoneroSubaddress = require("./src/main/js/wallet/model/MoneroSubaddress"); -module.exports.MoneroSyncResult = require("./src/main/js/wallet/model/MoneroSyncResult"); -module.exports.MoneroTransfer = require("./src/main/js/wallet/model/MoneroTransfer"); -module.exports.MoneroIncomingTransfer = require("./src/main/js/wallet/model/MoneroIncomingTransfer"); -module.exports.MoneroOutgoingTransfer = require("./src/main/js/wallet/model/MoneroOutgoingTransfer"); -module.exports.MoneroTransferQuery = require("./src/main/js/wallet/model/MoneroTransferQuery"); -module.exports.MoneroTxSet = require("./src/main/js/wallet/model/MoneroTxSet"); -module.exports.MoneroTxWallet = require("./src/main/js/wallet/model/MoneroTxWallet"); -module.exports.MoneroTxQuery = require("./src/main/js/wallet/model/MoneroTxQuery"); -module.exports.MoneroWalletListener = require("./src/main/js/wallet/model/MoneroWalletListener"); -module.exports.MoneroWalletConfig = require("./src/main/js/wallet/model/MoneroWalletConfig"); -module.exports.MoneroMessageSignatureType = require("./src/main/js/wallet/model/MoneroMessageSignatureType"); -module.exports.MoneroMessageSignatureResult = require("./src/main/js/wallet/model/MoneroMessageSignatureResult"); - -// export connection manager -module.exports.MoneroConnectionManager = require("./src/main/js/common/MoneroConnectionManager"); -module.exports.MoneroConnectionManagerListener = require("./src/main/js/common/MoneroConnectionManagerListener"); - -// export daemon, wallet, and utils classes -module.exports.MoneroUtils = require("./src/main/js/common/MoneroUtils"); -module.exports.MoneroDaemon = require("./src/main/js/daemon/MoneroDaemon"); -module.exports.MoneroWallet = require("./src/main/js/wallet/MoneroWallet"); -module.exports.MoneroDaemonRpc = require("./src/main/js/daemon/MoneroDaemonRpc"); -module.exports.MoneroWalletRpc = require("./src/main/js/wallet/MoneroWalletRpc"); -module.exports.MoneroWalletKeys = require("./src/main/js/wallet/MoneroWalletKeys"); -module.exports.MoneroWalletFull = require("./src/main/js/wallet/MoneroWalletFull"); - -// ---------------------------- GLOBAL FUNCTIONS ------------------------------ - -/** - *

Get the version of the monero-javascript library.

- * - * @return {string} the version of this monero-javascript library - */ -module.exports.getVersion = function() { - return module.exports.MoneroUtils.getVersion(); -} - -/** - *

Create a client connected to monerod.

- * - *

Examples:

- * - * - * let daemon = await monerojs.connectToDaemonRpc("http://localhost:38081", "superuser", "abctesting123");

- * - * let daemon = await monerojs.connectToDaemonRpc({
- *    uri: "http://localhost:38081",
- *    username: "superuser",
- *    password: "abctesting123"
- * }); - *
- * - * @param {string|object|MoneroRpcConnection} uriOrConfig - uri of monerod or JS config object or MoneroRpcConnection - * @param {string} uriOrConfig.uri - uri of monerod - * @param {string} uriOrConfig.username - username to authenticate with monerod (optional) - * @param {string} uriOrConfig.password - password to authenticate with monerod (optional) - * @param {boolean} uriOrConfig.rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {number} uriOrConfig.pollInterval - poll interval to query for updates in ms (default 5000) - * @param {boolean} uriOrConfig.proxyToWorker - run the daemon client in a web worker if true (default true) - * @param {string} username - username to authenticate with monerod (optional) - * @param {string} password - password to authenticate with monerod (optional) - * @param {boolean} rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {number} pollInterval - poll interval to query for updates in ms (default 5000) - * @param {boolean} proxyToWorker - runs the daemon client in a web worker if true (default true) - * @return {MoneroDaemonRpc} the daemon RPC client - */ -module.exports.connectToDaemonRpc = function() { return module.exports.MoneroDaemonRpc._connectToDaemonRpc(...arguments); } - -/** - *

Create a client connected to monero-wallet-rpc.

- * - *

Examples:

- * - * - * let walletRpc = await monerojs.connectToWalletRpc("http://localhost:38081", "superuser", "abctesting123");

- * - * let walletRpc = await monerojs.connectToWalletRpc({
- *    uri: "http://localhost:38081",
- *    username: "superuser",
- *    password: "abctesting123",
- *    rejectUnauthorized: false // e.g. local development
- * });

- * - * // connect to monero-wallet-rpc running as internal process
- * let walletRpc = await monerojs.connectToWalletRpc([
- *    "/path/to/monero-wallet-rpc",
- *    "--stagenet",
- *    "--daemon-address", "http://localhost:38081",
- *    "--daemon-login", "superuser:abctesting123",
- *    "--rpc-bind-port", "38085",
- *    "--rpc-login", "rpc_user:abc123",
- *    "--wallet-dir", "/path/to/wallets", // defaults to monero-wallet-rpc directory
- *    "--rpc-access-control-origins", "http://localhost:8080"
- *   ]); - * - *
- * - * @param {string|string[]|object|MoneroRpcConnection} uriOrConfig - uri of monero-wallet-rpc or terminal parameters or JS config object or MoneroRpcConnection - * @param {string} uriOrConfig.uri - uri of monero-wallet-rpc - * @param {string} uriOrConfig.username - username to authenticate with monero-wallet-rpc (optional) - * @param {string} uriOrConfig.password - password to authenticate with monero-wallet-rpc (optional) - * @param {boolean} uriOrConfig.rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {string} username - username to authenticate with monero-wallet-rpc (optional) - * @param {string} password - password to authenticate with monero-wallet-rpc (optional) - * @param {boolean} rejectUnauthorized - rejects self-signed certificates if true (default true) - * @return {MoneroWalletRpc} the wallet RPC client - */ -module.exports.connectToWalletRpc = function() { return module.exports.MoneroWalletRpc._connectToWalletRpc(...arguments); } - -/** - *

Create a Monero wallet using fully client-side WebAssembly bindings to monero-project's wallet2 in C++.

- * - *

Example:

- * - * - * let wallet = await monerojs.createWalletFull({
- *    path: "./test_wallets/wallet1", // leave blank for in-memory wallet
- *    password: "supersecretpassword",
- *    networkType: MoneroNetworkType.STAGENET,
- *    seed: "coexist igloo pamphlet lagoon...",
- *    restoreHeight: 1543218,
- *    server: new monerojs.MoneroRpcConnection("http://localhost:38081", "daemon_user", "daemon_password_123"),
- * }); - *
- * - * @param {object|MoneroWalletConfig} config - MoneroWalletConfig or equivalent config object - * @param {string} config.path - path of the wallet to create (optional, in-memory wallet if not given) - * @param {string} config.password - password of the wallet to create - * @param {string|number} config.networkType - network type of the wallet to create (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) - * @param {string} config.seed - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) - * @param {string} config.seedOffset - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed phrase - * @param {string} config.primaryAddress - primary address of the wallet to create (only provide if restoring from keys) - * @param {string} config.privateViewKey - private view key of the wallet to create (optional) - * @param {string} config.privateSpendKey - private spend key of the wallet to create (optional) - * @param {number} config.restoreHeight - block height to start scanning frsom (defaults to 0 unless generating random wallet) - * @param {string} config.language - language of the wallet's seed (defaults to "English" or auto-detected) - * @param {number} config.accountLookahead - number of accounts to scan (optional) - * @param {number} config.subaddressLookahead - number of subaddresses to scan per account (optional) - * @param {MoneroRpcConnection|object} config.server - MoneroRpcConnection or equivalent JS object providing daemon configuration (optional) - * @param {string} config.serverUri - uri of the wallet's daemon (optional) - * @param {string} config.serverUsername - username to authenticate with the daemon (optional) - * @param {string} config.serverPassword - password to authenticate with the daemon (optional) - * @param {boolean} config.rejectUnauthorized - reject self-signed server certificates if true (defaults to true) - * @param {boolean} config.proxyToWorker - proxies wallet operations to a web worker in order to not block the main thread (default true) - * @param {fs} config.fs - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) - * @return {MoneroWalletFull} the created wallet - */ -module.exports.createWalletFull = function() { return module.exports.MoneroWalletFull.createWallet(...arguments); } - -/** - *

Open an existing Monero wallet using fully client-side WebAssembly bindings to monero-project's wallet2 in C++.

- * - *

Examples:

- * - * - * let wallet1 = await monerojs.openWalletFull(
- *    "./wallets/wallet1",
- *    "supersecretpassword",
- *    MoneroNetworkType.STAGENET,
- *    "http://localhost:38081" // daemon uri
- * );

- * - * let wallet2 = await monerojs.openWalletFull({
- *    path: "./wallets/wallet2",
- *    password: "supersecretpassword",
- *    networkType: MoneroNetworkType.STAGENET,
- *    serverUri: "http://localhost:38081", // daemon configuration
- *    serverUsername: "superuser",
- *    serverPassword: "abctesting123"
- * }); - *
- * - * @param {MoneroWalletConfig|object|string} configOrPath - MoneroWalletConfig or equivalent config object or a path to a wallet to open - * @param {string} configOrPath.path - path of the wallet to open (optional if 'keysData' provided) - * @param {string} configOrPath.password - password of the wallet to open - * @param {string|number} configOrPath.networkType - network type of the wallet to open (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) - * @param {Uint8Array} configOrPath.keysData - wallet keys data to open (optional if path provided) - * @param {Uint8Array} configOrPath.cacheData - wallet cache data to open (optional) - * @param {MoneroRpcConnection|object} configOrPath.server - MoneroRpcConnection or equivalent JS object configuring the daemon connection (optional) - * @param {string} configOrPath.serverUri - uri of the wallet's daemon (optional) - * @param {string} configOrPath.serverUsername - username to authenticate with the daemon (optional) - * @param {string} configOrPath.serverPassword - password to authenticate with the daemon (optional) - * @param {boolean} configOrPath.rejectUnauthorized - reject self-signed server certificates if true (defaults to true) - * @param {boolean} configOrPath.proxyToWorker - proxies wallet operations to a web worker in order to not block the main thread (default true) - * @param {fs} configOrPath.fs - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) - * @param {string} password - password of the wallet to open - * @param {string|number} networkType - network type of the wallet to open - * @param {string|MoneroRpcConnection} daemonUriOrConnection - daemon URI or MoneroRpcConnection - * @param {boolean} proxyToWorker - proxies wallet operations to a web worker in order to not block the main thread (default true) - * @param {fs} fs - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) - * @return {MoneroWalletFull} the opened wallet - */ -module.exports.openWalletFull = function() { return module.exports.MoneroWalletFull.openWallet(...arguments); } - -/** - *

Create a wallet using WebAssembly bindings to monero-project.

- * - *

Example:

- * - * - * let wallet = await monerojs.createWalletKeys({
- *    password: "abc123",
- *    networkType: MoneroNetworkType.STAGENET,
- *    seed: "coexist igloo pamphlet lagoon..."
- * }); - *
- * - * @param {MoneroWalletConfig|object} config - MoneroWalletConfig or equivalent config object - * @param {string|number} config.networkType - network type of the wallet to create (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) - * @param {string} config.seed - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) - * @param {string} config.seedOffset - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed phrase - * @param {string} config.primaryAddress - primary address of the wallet to create (only provide if restoring from keys) - * @param {string} config.privateViewKey - private view key of the wallet to create (optional) - * @param {string} config.privateSpendKey - private spend key of the wallet to create (optional) - * @param {string} config.language - language of the wallet's seed phrase (defaults to "English" or auto-detected) - * @return {MoneroWalletKeys} the created wallet - */ -module.exports.createWalletKeys = function() { return module.exports.MoneroWalletKeys.createWallet(...arguments); } diff --git a/index.ts b/index.ts new file mode 100644 index 000000000..cc0ac49fd --- /dev/null +++ b/index.ts @@ -0,0 +1,371 @@ +'use strict' + +/** + * Import and export all library types. + * + * See the full model specification: http://moneroecosystem.org/monero-java/monero-spec.pdf + */ + +// import common models +import GenUtils from "./src/main/ts/common/GenUtils"; +import Filter from "./src/main/ts/common/Filter"; +import MoneroError from "./src/main/ts/common/MoneroError"; +import HttpClient from "./src/main/ts/common/HttpClient"; +import LibraryUtils from "./src/main/ts/common/LibraryUtils"; +import MoneroRpcConnection from "./src/main/ts/common/MoneroRpcConnection"; +import MoneroRpcError from "./src/main/ts/common/MoneroRpcError"; +import SslOptions from "./src/main/ts/common/SslOptions"; +import TaskLooper from "./src/main/ts/common/TaskLooper"; + +// import daemon models +import ConnectionType from "./src/main/ts/daemon/model/ConnectionType"; +import MoneroAltChain from "./src/main/ts/daemon/model/MoneroAltChain"; +import MoneroBan from "./src/main/ts/daemon/model/MoneroBan"; +import MoneroBlockHeader from "./src/main/ts/daemon/model/MoneroBlockHeader"; +import MoneroBlock from "./src/main/ts/daemon/model/MoneroBlock"; +import MoneroBlockTemplate from "./src/main/ts/daemon/model/MoneroBlockTemplate"; +import MoneroConnectionSpan from "./src/main/ts/daemon/model/MoneroConnectionSpan"; +import MoneroDaemonConfig from "./src/main/ts/daemon/model/MoneroDaemonConfig"; +import MoneroDaemonInfo from "./src/main/ts/daemon/model/MoneroDaemonInfo"; +import MoneroDaemonListener from "./src/main/ts/daemon/model/MoneroDaemonListener"; +import MoneroDaemonSyncInfo from "./src/main/ts/daemon/model/MoneroDaemonSyncInfo"; +import MoneroDaemonUpdateCheckResult from "./src/main/ts/daemon/model/MoneroDaemonUpdateCheckResult"; +import MoneroDaemonUpdateDownloadResult from "./src/main/ts/daemon/model/MoneroDaemonUpdateDownloadResult"; +import MoneroFeeEstimate from "./src/main/ts/daemon/model/MoneroFeeEstimate"; +import MoneroHardForkInfo from "./src/main/ts/daemon/model/MoneroHardForkInfo"; +import MoneroKeyImage from "./src/main/ts/daemon/model/MoneroKeyImage"; +import MoneroKeyImageSpentStatus from "./src/main/ts/daemon/model/MoneroKeyImageSpentStatus"; +import MoneroMinerTxSum from "./src/main/ts/daemon/model/MoneroMinerTxSum"; +import MoneroMiningStatus from "./src/main/ts/daemon/model/MoneroMiningStatus"; +import MoneroNetworkType from "./src/main/ts/daemon/model/MoneroNetworkType"; +import MoneroOutput from "./src/main/ts/daemon/model/MoneroOutput"; +import MoneroOutputHistogramEntry from "./src/main/ts/daemon/model/MoneroOutputHistogramEntry"; +import MoneroSubmitTxResult from "./src/main/ts/daemon/model/MoneroSubmitTxResult"; +import MoneroTx from "./src/main/ts/daemon/model/MoneroTx"; +import MoneroTxPoolStats from "./src/main/ts/daemon/model/MoneroTxPoolStats"; +import MoneroVersion from "./src/main/ts/daemon/model/MoneroVersion"; +import MoneroPeer from "./src/main/ts/daemon/model/MoneroPeer"; +import MoneroPruneResult from "./src/main/ts/daemon/model/MoneroPruneResult"; + +// import wallet models +import MoneroAccount from "./src/main/ts/wallet/model/MoneroAccount"; +import MoneroAccountTag from "./src/main/ts/wallet/model/MoneroAccountTag"; +import MoneroAddressBookEntry from "./src/main/ts/wallet/model/MoneroAddressBookEntry"; +import MoneroCheck from "./src/main/ts/wallet/model/MoneroCheck"; +import MoneroCheckReserve from "./src/main/ts/wallet/model/MoneroCheckReserve"; +import MoneroCheckTx from "./src/main/ts/wallet/model/MoneroCheckTx"; +import MoneroDestination from "./src/main/ts/wallet/model/MoneroDestination"; +import MoneroIntegratedAddress from "./src/main/ts/wallet/model/MoneroIntegratedAddress"; +import MoneroKeyImageImportResult from "./src/main/ts/wallet/model/MoneroKeyImageImportResult"; +import MoneroMultisigInfo from "./src/main/ts/wallet/model/MoneroMultisigInfo"; +import MoneroMultisigInitResult from "./src/main/ts/wallet/model/MoneroMultisigInitResult"; +import MoneroMultisigSignResult from "./src/main/ts/wallet/model/MoneroMultisigSignResult"; +import MoneroOutputWallet from "./src/main/ts/wallet/model/MoneroOutputWallet"; +import MoneroOutputQuery from "./src/main/ts/wallet/model/MoneroOutputQuery"; +import MoneroTxPriority from "./src/main/ts/wallet/model/MoneroTxPriority"; +import MoneroTxConfig from "./src/main/ts/wallet/model/MoneroTxConfig"; +import MoneroSubaddress from "./src/main/ts/wallet/model/MoneroSubaddress"; +import MoneroSyncResult from "./src/main/ts/wallet/model/MoneroSyncResult"; +import MoneroTransfer from "./src/main/ts/wallet/model/MoneroTransfer"; +import MoneroIncomingTransfer from "./src/main/ts/wallet/model/MoneroIncomingTransfer"; +import MoneroOutgoingTransfer from "./src/main/ts/wallet/model/MoneroOutgoingTransfer"; +import MoneroTransferQuery from "./src/main/ts/wallet/model/MoneroTransferQuery"; +import MoneroTxSet from "./src/main/ts/wallet/model/MoneroTxSet"; +import MoneroTxWallet from "./src/main/ts/wallet/model/MoneroTxWallet"; +import MoneroTxQuery from "./src/main/ts/wallet/model/MoneroTxQuery"; +import MoneroWalletListener from "./src/main/ts/wallet/model/MoneroWalletListener"; +import MoneroWalletConfig from "./src/main/ts/wallet/model/MoneroWalletConfig"; +import MoneroMessageSignatureType from "./src/main/ts/wallet/model/MoneroMessageSignatureType"; +import MoneroMessageSignatureResult from "./src/main/ts/wallet/model/MoneroMessageSignatureResult"; + +// import connection manager +import MoneroConnectionManager from "./src/main/ts/common/MoneroConnectionManager"; +import MoneroConnectionManagerListener from "./src/main/ts/common/MoneroConnectionManagerListener"; + +// import daemon, wallet, and utils classes +import MoneroDaemon from "./src/main/ts/daemon/MoneroDaemon"; +import MoneroWallet from "./src/main/ts/wallet/MoneroWallet"; +import MoneroDaemonRpc from "./src/main/ts/daemon/MoneroDaemonRpc"; +import MoneroWalletRpc from "./src/main/ts/wallet/MoneroWalletRpc"; +import { MoneroWalletKeys } from "./src/main/ts/wallet/MoneroWalletKeys"; +import MoneroWalletFull from "./src/main/ts/wallet/MoneroWalletFull"; +import MoneroUtils from "./src/main/ts/common/MoneroUtils"; +import ThreadPool from "./src/main/ts/common/ThreadPool"; + +// export types +export { + GenUtils, + Filter, + MoneroError, + HttpClient, + LibraryUtils, + MoneroRpcConnection, + MoneroRpcError, + SslOptions, + TaskLooper, + ConnectionType, + MoneroAltChain, + MoneroBan, + MoneroBlockHeader, + MoneroBlock, + MoneroBlockTemplate, + MoneroConnectionSpan, + MoneroDaemonConfig, + MoneroDaemonInfo, + MoneroDaemonListener, + MoneroDaemonSyncInfo, + MoneroDaemonUpdateCheckResult, + MoneroDaemonUpdateDownloadResult, + MoneroFeeEstimate, + MoneroHardForkInfo, + MoneroKeyImage, + MoneroKeyImageSpentStatus, + MoneroMinerTxSum, + MoneroMiningStatus, + MoneroNetworkType, + MoneroOutput, + MoneroOutputHistogramEntry, + MoneroSubmitTxResult, + MoneroTx, + MoneroTxPoolStats, + MoneroVersion, + MoneroPeer, + MoneroPruneResult, + MoneroAccount, + MoneroAccountTag, + MoneroAddressBookEntry, + MoneroCheck, + MoneroCheckReserve, + MoneroCheckTx, + MoneroDestination, + MoneroIntegratedAddress, + MoneroKeyImageImportResult, + MoneroMultisigInfo, + MoneroMultisigInitResult, + MoneroMultisigSignResult, + MoneroOutputWallet, + MoneroOutputQuery, + MoneroTxPriority, + MoneroTxConfig, + MoneroSubaddress, + MoneroSyncResult, + MoneroTransfer, + MoneroIncomingTransfer, + MoneroOutgoingTransfer, + MoneroTransferQuery, + MoneroTxSet, + MoneroTxWallet, + MoneroTxQuery, + MoneroWalletListener, + MoneroWalletConfig, + MoneroMessageSignatureType, + MoneroMessageSignatureResult, + MoneroConnectionManagerListener, + MoneroConnectionManager, + MoneroDaemon, + MoneroWallet, + MoneroDaemonRpc, + MoneroWalletRpc, + MoneroWalletKeys, + MoneroWalletFull, + MoneroUtils, + ThreadPool +}; + +// ---------------------------- GLOBAL FUNCTIONS ------------------------------ + +/** + *

Get the version of the monero-ts library.

+ * + * @return {string} the version of this monero-ts library + */ +export function getVersion() { + return MoneroUtils.getVersion(); +} + +/** + *

Create a client connected to monerod.

+ * + *

Examples:

+ * + * + * let daemon = await moneroTs.connectToDaemonRpc("http://localhost:38081");
+ *

+ *
+ * + * let daemon = await moneroTs.connectToDaemonRpc({
+ *    uri: "http://localhost:38081",
+ *    username: "superuser",
+ *    password: "abctesting123"
+ * }); + *

+ *
+ * + * // start monerod as an internal process
+ * let daemon = await moneroTs.connectToDaemonRpc({
+ *    cmd: ["path/to/monerod", ...params...],
+ * }); + *
+ * + * @param {string|Partial|Partial|string[]} uriOrConfig - uri or rpc connection or config or terminal parameters to connect to monerod + * @param {string} [username] - username to authenticate with monerod + * @param {string} [password] - password to authenticate with monerod + * @return {Promise} the daemon RPC client + */ +export function connectToDaemonRpc(uriOrConfig: string | Partial | Partial | string[], username?: string, password?: string): Promise { + return MoneroDaemonRpc.connectToDaemonRpc(uriOrConfig, username, password); +} + +/** + *

Create a client connected to monero-wallet-rpc.

+ * + *

Examples:

+ * + * + * let walletRpc = await moneroTs.connectToWalletRpc({
+ *    uri: "http://localhost:38081",
+ *    username: "superuser",
+ *    password: "abctesting123",
+ *    rejectUnauthorized: false // e.g. local development
+ * });
+ *

+ *
+ * + * // connect to monero-wallet-rpc running as internal process
+ * let walletRpc = await moneroTs.connectToWalletRpc({cmd: [
+ *    "/path/to/monero-wallet-rpc",
+ *    "--stagenet",
+ *    "--daemon-address", "http://localhost:38081",
+ *    "--daemon-login", "superuser:abctesting123",
+ *    "--rpc-bind-port", "38085",
+ *    "--rpc-login", "rpc_user:abc123",
+ *    "--wallet-dir", "/path/to/wallets", // defaults to monero-wallet-rpc directory
+ *    "--rpc-access-control-origins", "http://localhost:8080"
+ *  ]}); + *
+ * + * @param {string|Partial|Partial|string[]} uriOrConfig - uri or rpc connection or config or terminal parameters to connect to monero-wallet-rpc + * @param {string} [username] - username to authenticate with monero-wallet-rpc + * @param {string} [password] - password to authenticate with monero-wallet-rpc + * @return {Promise} the wallet RPC client + */ +export function connectToWalletRpc(uriOrConfig: string | Partial | Partial | string[], username?: string, password?: string): Promise { + return MoneroWalletRpc.connectToWalletRpc(uriOrConfig, username, password); +} + +/** + *

Create a Monero wallet using client-side WebAssembly bindings to monero-project's wallet2 in C++.

+ * + *

Example:

+ * + * + * const wallet = await moneroTs.createWalletFull({
+ *    path: "./test_wallets/wallet1", // leave blank for in-memory wallet
+ *    password: "supersecretpassword",
+ *    networkType: moneroTs.MoneroNetworkType.STAGENET,
+ *    seed: "coexist igloo pamphlet lagoon...",
+ *    restoreHeight: 1543218,
+ *    server: "http://localhost:38081"
+ * }); + *

+ *
+ * + * const wallet = await moneroTs.createWalletFull({
+ *    path: "./test_wallets/wallet1", // leave blank for in-memory wallet
+ *    password: "supersecretpassword",
+ *    networkType: moneroTs.MoneroNetworkType.STAGENET,
+ *    seed: "coexist igloo pamphlet lagoon...",
+ *    restoreHeight: 1543218,
+ *    proxyToWorker: false, // override default
+ *    server: {
+ *      uri: "http://localhost:38081",
+ *      username: "daemon_user",
+ *      password: "daemon_password_123"
+ *    }
+ * }); + *
+ * + * @param {Partial} config - MoneroWalletConfig or equivalent config object + * @param {string} [config.path] - path of the wallet to create (optional, in-memory wallet if not given) + * @param {string} [config.password] - password of the wallet to create + * @param {MoneroNetworkType|string} [config.networkType] - network type of the wallet to create (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) + * @param {string} [config.seed] - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) + * @param {string} [config.seedOffset] - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed phrase + * @param {boolean} [config.isMultisig] - restore multisig wallet from seed + * @param {string} [config.primaryAddress] - primary address of the wallet to create (only provide if restoring from keys) + * @param {string} [config.privateViewKey] - private view key of the wallet to create (optional) + * @param {string} [config.privateSpendKey] - private spend key of the wallet to create (optional) + * @param {number} [config.restoreHeight] - block height to start scanning from (defaults to 0 unless generating random wallet) + * @param {string} [config.language] - language of the wallet's seed phrase (defaults to "English" or auto-detected) + * @param {number} [config.accountLookahead] - number of accounts to scan (optional) + * @param {number} [config.subaddressLookahead] - number of subaddresses to scan per account (optional) + * @param {string|Partial} [config.server] - connection to monero daemon (optional) + * @param {MoneroConnectionManager} [config.connectionManager] - manage connections to monerod (optional) + * @param {boolean} [config.rejectUnauthorized] - reject self-signed server certificates if true (defaults to true) + * @param {boolean} [config.proxyToWorker] - proxies wallet operations to a worker in order to not block the main thread (default true) + * @param {any} [config.fs] - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) + * @return {Promise} the created wallet + */ +export function createWalletFull(config: Partial): Promise { + return MoneroWalletFull.createWallet(new MoneroWalletConfig(config)); +} + +/** + *

Open an existing Monero wallet using client-side WebAssembly bindings to monero-project's wallet2 in C++.

+ * + *

Example:

+ * + * + * const wallet = await moneroTs.openWalletFull({
+ *    path: "./wallets/wallet1",
+ *    password: "supersecretpassword",
+ *    networkType: moneroTs.MoneroNetworkType.STAGENET,
+*    server: { // daemon configuration
+ *      uri: "http://localhost:38081",
+ *      username: "superuser",
+ *      password: "abctesting123"
+ *    }
+ * }); + *
+ * + * @param {Partial} config - config to open a full wallet + * @param {string} [config.path] - path of the wallet to open (optional if 'keysData' provided) + * @param {string} [config.password] - password of the wallet to open + * @param {string|number} [config.networkType] - network type of the wallet to open (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) + * @param {string|MoneroRpcConnection} [config.server] - uri or connection to monero daemon (optional) + * @param {Uint8Array} [config.keysData] - wallet keys data to open (optional if path provided) + * @param {Uint8Array} [config.cacheData] - wallet cache data to open (optional) + * @param {boolean} [config.proxyToWorker] - proxies wallet operations to a worker in order to not block the main thread (default true) + * @param {any} [config.fs] - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) + * @return {Promise} the opened wallet + */ +export function openWalletFull(config: Partial): Promise { + return MoneroWalletFull.openWallet(new MoneroWalletConfig(config)); +} + +/** + *

Create a wallet using WebAssembly bindings to monero-project.

+ * + *

Example:

+ * + * + * const wallet = await moneroTs.createWalletKeys({
+ *    password: "abc123",
+ *    networkType: moneroTs.MoneroNetworkType.STAGENET,
+ *    seed: "coexist igloo pamphlet lagoon..."
+ * }); + *
+ * + * @param {Partial} config - MoneroWalletConfig or equivalent config object + * @param {string|number} config.networkType - network type of the wallet to create (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) + * @param {string} [config.seed] - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) + * @param {string} [config.seedOffset] - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed phrase + * @param {string} [config.primaryAddress] - primary address of the wallet to create (only provide if restoring from keys) + * @param {string} [config.privateViewKey] - private view key of the wallet to create (optional) + * @param {string} [config.privateSpendKey] - private spend key of the wallet to create (optional) + * @param {string} [config.language] - language of the wallet's seed (defaults to "English" or auto-detected) + * @return {Promise} the created wallet + */ +export function createWalletKeys(config: Partial): Promise { + return MoneroWalletKeys.createWallet(new MoneroWalletConfig(config)); +} diff --git a/package-lock.json b/package-lock.json index c5bd33cf4..49f5c6fdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { - "name": "monero-javascript", + "name": "monero-ts", "version": "0.9.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "monero-javascript", + "name": "monero-ts", "version": "0.9.0", "license": "MIT", "dependencies": { + "@types/node": "^20.6.0", "ajv": "^6.12.6", "async": "2.6.4", "crypto-js": "^4.0.0", @@ -20,20 +21,34 @@ "serialize-javascript": "^3.1.0", "text-encoding": "^0.7.0", "tls": "0.0.1", - "web-worker": "^1.0.0" + "uuid": "3.3.2", + "web-worker": "^1.2.0" }, "devDependencies": { - "@babel/core": "^7.15.0", - "assert": "^2.0.0", - "babel-loader": "8.0.2", + "@babel/cli": "^7.18.10", + "@babel/core": "^7.22.17", + "@babel/node": "^7.18.10", + "@babel/plugin-transform-runtime": "^7.18.10", + "@babel/preset-env": "^7.22.15", + "@babel/preset-typescript": "^7.22.15", + "@babel/register": "^7.22.15", + "@types/jquery": "^3.5.19", + "@types/mocha": "^9.1.1", + "@typescript-eslint/eslint-plugin": "5.53.0", + "@typescript-eslint/parser": "^5.53.0", + "assert": "^2.1.0", + "babel-loader": "^9.1.3", "browserify-zlib": "^0.2.0", "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", - "eslint": "^8.0.1", + "eslint": "^8.35.0", + "eslint-config-prettier": "^8.6.0", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-jsx-a11y": "^6.7.1", "https-browserify": "^1.0.0", - "jsdoc": "^4.0.0", - "memfs": "^3.2.0", - "minimist": "^1.2.5", + "jquery-csv": "^1.0.21", + "memfs": "^4.2.1", "mocha": "^9.1.3", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", @@ -41,73 +56,114 @@ "querystring-es3": "^0.2.1", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", - "url": "^0.11.0", - "util": "^0.12.4", - "webpack": "^5.76.0", - "webpack-cli": "^4.8.0", - "webpack-merge": "4.1.4", - "worker-loader": "2.0.0", - "yargs-parser": "^18.1.3" + "ts-loader": "^9.4.4", + "ts-node": "^10.9.1", + "typedoc": "^0.25.1", + "typescript": "^5.2.2", + "url": "^0.11.2", + "util": "^0.12.5", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4", + "webpack-merge": "^5.9.0" }, "engines": { "node": ">=10.0.0" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" } }, + "node_modules/@babel/cli": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.23.0.tgz", + "integrity": "sha512-17E1oSkGk2IwNILM4jtfAvgjt+ohmpfBky8aLerUfYZhiPNg7ca+CRCxZn8QDxwNhV/upsc2VHBCqGFIR+iBfA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "commander": "^4.0.1", + "convert-source-map": "^2.0.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" + }, + "bin": { + "babel": "bin/babel.js", + "babel-external-helpers": "bin/babel-external-helpers.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "optionalDependencies": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", - "convert-source-map": "^1.7.0", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", + "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -118,43 +174,92 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@babel/types": "^7.22.5" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", - "semver": "^6.3.0" + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", + "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -163,3910 +268,4113 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, - "bin": { - "parser": "bin/babel-parser.js" + "dependencies": { + "@babel/types": "^7.22.5" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", - "to-fast-properties": "^2.0.0" + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "node_modules/@babel/helpers": { + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", + "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0" + }, "engines": { - "node": ">=10.0.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "node_modules/@babel/node": { + "version": "7.22.19", + "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.22.19.tgz", + "integrity": "sha512-VsKSO9aEHdO16NdtqkJfrXZ9Sxlna1BVnBbToWr1KGdI3cyIk6KqOoa8mWvpK280lJDOwJqxvnl994KmLhq1Yw==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "@babel/register": "^7.22.15", + "commander": "^4.0.1", + "core-js": "^3.30.2", + "node-environment-flags": "^1.0.5", + "regenerator-runtime": "^0.14.0", + "v8flags": "^3.1.1" + }, + "bin": { + "babel-node": "bin/babel-node.js" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", - "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=10.10.0" + "node": ">=6.0.0" } }, - "node_modules/@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", + "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", + "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.15" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "engines": { - "node": ">=6.0.0" + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jsdoc/salty": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.3.tgz", - "integrity": "sha512-bbtCxCkxcnWhi50I+4Lj6mdz9w3pOXOgEQrID8TCZ/DF51fW7M9GCQW2y45SpBDdHd1Eirm1X/Cf6CkAAe8HPg==", + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, "dependencies": { - "lodash": "^4.17.21" + "@babel/helper-plugin-utils": "^7.8.3" }, - "engines": { - "node": ">=v12.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/eslint": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", - "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", "dev": true, "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true - }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.6.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.4.tgz", - "integrity": "sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==", - "dev": true - }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "dependencies": { - "@xtuc/long": "4.2.2" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz", + "integrity": "sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w==", "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", "dev": true, "dependencies": { - "envinfo": "^7.7.3" + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { - "webpack-cli": "4.x.x" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", "dev": true, - "peerDependencies": { - "webpack-cli": "4.x.x" + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz", + "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">=0.4.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { - "acorn": "^8" + "@babel/core": "^7.0.0-0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", + "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@babel/core": "^7.12.0" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@babel/plugin-transform-classes": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", + "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", + "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { - "ajv": "^6.9.1" + "@babel/core": "^7.0.0-0" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz", + "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">=6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", + "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", "dev": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/array.prototype.reduce": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", - "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", + "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", + "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", + "dev": true, "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", "dev": true, "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", + "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", "dev": true, "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">=0.8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", + "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", + "dev": true, "dependencies": { - "lodash": "^4.17.14" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">= 0.4" + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz", + "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": "*" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" - }, - "node_modules/babel-loader": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.2.tgz", - "integrity": "sha512-Law0PGtRV1JL8Y9Wpzc0d6EE0GD7LzXWCfaeWwboUMcBWNG6gvaWTK1/+BK7a4X5EmeJiGEuDDFxUsOa8RSWCw==", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", + "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", "dev": true, "dependencies": { - "find-cache-dir": "^1.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "util.promisify": "^1.0.0" + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" }, "engines": { - "node": ">= 6.9" + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" + "@babel/core": "^7.0.0-0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz", + "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dependencies": { - "tweetnacl": "^0.14.3" + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": "*" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", + "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { - "node": ">=8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", + "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", "dev": true, "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", + "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", "dev": true, "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", "dev": true, "dependencies": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", + "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", "dev": true, "dependencies": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", + "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", "dev": true, "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/browserify-sign/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", + "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", "dev": true, "dependencies": { - "pako": "~1.0.5" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" - }, - "bin": { - "browserslist": "cli.js" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.22.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", + "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true - }, - "node_modules/builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", - "dev": true - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "@babel/helper-plugin-utils": "^7.22.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", + "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + }, "engines": { - "node": ">=6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">=6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001374", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", - "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "node_modules/catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.15.tgz", + "integrity": "sha512-tEVLhk8NRZSmwQ0DJtxxhTrCht1HVo8VaMzYT4w6lwyKBuHsgoioAUA7/6eT2fRfc5/23fuGdlwIxXhRVgWr4g==", "dev": true, "dependencies": { - "lodash": "^4.17.15" + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.5", + "babel-plugin-polyfill-corejs3": "^0.8.3", + "babel-plugin-polyfill-regenerator": "^0.5.2", + "semver": "^6.3.1" }, "engines": { - "node": ">= 10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/@babel/plugin-transform-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { - "node": ">= 8.10.0" + "node": ">=6.9.0" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">= 6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, "engines": { - "node": ">=6.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz", + "integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", + "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", "dev": true, "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">=6" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", "dev": true, "dependencies": { - "color-name": "1.1.3" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "dev": true, "dependencies": { - "delayed-stream": "~1.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { - "node": ">= 0.8" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "node_modules/@babel/preset-env": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.20.tgz", + "integrity": "sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.20", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.15", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.15", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.11", + "@babel/plugin-transform-classes": "^7.22.15", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.15", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.11", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-for-of": "^7.22.15", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.11", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.15", + "@babel/plugin-transform-modules-systemjs": "^7.22.11", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", + "@babel/plugin-transform-numeric-separator": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.22.15", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.11", + "@babel/plugin-transform-optional-chaining": "^7.22.15", + "@babel/plugin-transform-parameters": "^7.22.15", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.22.19", + "babel-plugin-polyfill-corejs2": "^0.4.5", + "babel-plugin-polyfill-corejs3": "^0.8.3", + "babel-plugin-polyfill-regenerator": "^0.5.2", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.1" + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + "node_modules/@babel/preset-typescript": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.0.tgz", + "integrity": "sha512-6P6VVa/NM/VlAYj5s2Aq/gdVg8FSENCg3wlZ6Qau9AcPaoF5LbN1nyGlR9DTRIw9PpxI94e+ReydsJHcjwAweg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.23.0", + "@babel/plugin-transform-typescript": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "node_modules/@babel/register": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", + "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", "dev": true, "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.5", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", "dev": true }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/@babel/runtime": { + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", + "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==", "dev": true, "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/@babel/traverse": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", + "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" }, "engines": { - "node": ">= 8" + "node": ">=6.9.0" } }, - "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { - "node": "*" + "node": ">=12" } }, - "node_modules/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, "dependencies": { - "assert-plus": "^1.0.0" - }, + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, "engines": { - "node": ">=0.10" + "node": ">=10.0.0" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=6.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "node_modules/@eslint-community/regexpp": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.0.tgz", + "integrity": "sha512-zJmuCWj2VLBt4c25CfBIbMZLGLyhkvs7LznyVX5HfpzeocThgIj5XQK4L+g3U36mMcx8bPMhGyPpwCATamC4jQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">= 0.4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.22.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", + "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=0.4.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "node_modules/@eslint/js": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz", + "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==", "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, "engines": { - "node": ">=0.3.1" + "node": ">=10.10.0" } }, - "node_modules/diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "path-type": "^4.0.0" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, "engines": { "node": ">=6.0.0" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/electron-to-chromium": { - "version": "1.4.211", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz", - "integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==", - "dev": true - }, - "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dev": true, - "engines": { - "node": ">= 4" + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "dev": true, + "optional": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node": ">= 8" } }, - "node_modules/envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "bin": { - "envinfo": "dist/cli.js" - }, "engines": { - "node": ">=4" + "node": ">= 8" } }, - "node_modules/es-abstract": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", - "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.4.3", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 8" } }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", "dev": true }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "8.44.3", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz", + "integrity": "sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==", "dev": true, "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/@types/eslint-scope": { + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.5.tgz", + "integrity": "sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/@types/estree": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz", + "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==", + "dev": true + }, + "node_modules/@types/jquery": { + "version": "3.5.20", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.20.tgz", + "integrity": "sha512-UI+EGhgYD4LdSZ8gaiziFqXYIIB38VQSDsnAs8jL/div7FGrzrShx4HKCykVzk3tPfiIlusdNP9Wi3G60LCF2Q==", "dev": true, - "engines": { - "node": ">=0.8.0" + "dependencies": { + "@types/sizzle": "*" } }, - "node_modules/eslint": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.21.0.tgz", - "integrity": "sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==", + "node_modules/@types/json-schema": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.8.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.0.tgz", + "integrity": "sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==" + }, + "node_modules/@types/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", + "dev": true + }, + "node_modules/@types/sizzle": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.4.tgz", + "integrity": "sha512-jA2llq2zNkg8HrALI7DtWzhALcVH0l7i89yhY3iBdOz6cBPeACoFq+fkQrjHA39t1hnSFOboZ7A/AY5MMZSlag==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.53.0.tgz", + "integrity": "sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.10.4", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.3", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/type-utils": "5.53.0", + "@typescript-eslint/utils": "5.53.0", + "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "natural-compare-lite": "^1.4.0", "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "yallist": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + "lru-cache": "^6.0.0" }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "bin": { + "semver": "bin/semver.js" }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, "engines": { "node": ">=10" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.53.0.tgz", + "integrity": "sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0" }, "engines": { - "node": ">=7.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", "dev": true, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "node_modules/@typescript-eslint/type-utils": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.53.0.tgz", + "integrity": "sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "@typescript-eslint/typescript-estree": "5.53.0", + "@typescript-eslint/utils": "5.53.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" }, "engines": { - "node": ">=8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/espree": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", - "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", + "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", "dev": true, "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/@typescript-eslint/type-utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "estraverse": "^5.1.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=0.10" + "node": ">=10" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=4.0" + "node": ">=10" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } + "node_modules/@typescript-eslint/type-utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, "engines": { - "node": ">=0.8.x" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "yallist": "^4.0.0" }, "engines": { - "node": ">=8.6.0" + "node": ">=10" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 6" + "node": ">=10" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "node_modules/@typescript-eslint/utils": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.53.0.tgz", + "integrity": "sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==", "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.53.0", + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, "engines": { - "node": ">= 4.9.1" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.53.0.tgz", + "integrity": "sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "@typescript-eslint/types": "5.53.0", + "@typescript-eslint/visitor-keys": "5.53.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "yallist": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha512-46TFiBOzX7xq/PcSWfFwkyjpemdRnMe31UQF+os0y+1W3k95f6R4SEt02Hj4p3X0Mir9gfrkmOtshFidS0VPUg==", + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.53.0.tgz", + "integrity": "sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "@typescript-eslint/types": "5.53.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": { + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.53.0.tgz", + "integrity": "sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==", "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/flatted": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", - "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", "dev": true, "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "engines": { - "node": "*" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true }, - "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, - "engines": { - "node": ">=6.9.0" + "dependencies": { + "@xtuc/long": "4.2.2" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, - "node_modules/get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "dev": true, "dependencies": { - "assert-plus": "^1.0.0" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, "engines": { - "node": ">=10.13.0" + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, "engines": { - "node": ">=4" + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=14.15.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } } }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=4.x" + "node": ">=0.4.0" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=0.4.0" } }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=6" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1" + "ajv": "^8.0.0" }, - "engines": { - "node": ">= 0.4.0" + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "engines": { - "node": ">=4" + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "color-convert": "^1.9.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=4" + "node": ">= 8" } }, - "node_modules/hash-base/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, - "bin": { - "he": "bin/he" + "dependencies": { + "dequal": "^2.0.3" } }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", "dev": true, "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/html5-fs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/html5-fs/-/html5-fs-0.1.1.tgz", - "integrity": "sha512-FqL7S02QEbAZ/xvp+BqurAolHvPQqOKPaWn1SsbP5fwpOLVcMGu12NPqr4mW5dRpdh2/UFbuxFDAhOw/LCDDvw==" - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" }, "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", - "dev": true - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, "engines": { - "node": ">= 4" + "node": ">=8" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", "dev": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-local/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-local/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/array.prototype.reduce": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.6.tgz", + "integrity": "sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-local/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-local/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" + "safer-buffer": "~2.1.0" } }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", "dev": true, "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "engines": { - "node": ">=0.8.19" + "node": ">=0.8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "dev": true + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "lodash": "^4.17.14" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "engines": { - "node": ">= 0.10" + "node": "*" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, + "node_modules/axe-core": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.2.tgz", + "integrity": "sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", "dev": true, "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dequal": "^2.0.3" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", "dev": true, "dependencies": { - "binary-extensions": "^2.0.0" + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/babel-loader/node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "node_modules/babel-loader/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "node_modules/babel-loader/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "dependencies": { - "has": "^1.0.3" + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/babel-loader/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "node_modules/babel-loader/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "p-limit": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/babel-loader/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, "engines": { - "node": ">=0.10.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "node_modules/babel-loader/node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" + "find-up": "^6.3.0" }, "engines": { - "node": ">= 0.4" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "node_modules/babel-loader/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=12.20" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.4.tgz", + "integrity": "sha512-9l//BZZsPR+5XjyJMPtZSK4jv0BsTO1zDac2GC6ygx9WLGlcsnRd1Co0B2zT5fF5Ic6BZy+9m3HNZ3QcOeDKfg==", "dev": true, "dependencies": { - "isobject": "^3.0.1" + "@babel/helper-define-polyfill-provider": "^0.4.2", + "core-js-compat": "^3.32.2" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "@babel/helper-define-polyfill-provider": "^0.4.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/is-shared-array-buffer": { + "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "tweetnacl": "^0.14.3" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/is-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", - "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0" + "fill-range": "^7.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "pako": "~1.0.5" } }, - "node_modules/js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "xmlcreate": "^2.0.4" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "node_modules/jsdoc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.0.tgz", - "integrity": "sha512-tzTgkklbWKrlaQL2+e3NNgLcZu3NaK2vsHRx7tyHQ+H5jcB9Gx0txSd2eJWlMC/xU1+7LQu4s58Ry0RkuaEQVg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.9.4", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/jsdoc/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jsdoc/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "bin": { - "mkdirp": "bin/cmd.js" + "browserslist": "cli.js" }, "engines": { - "node": ">=10" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", "dev": true }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "dev": true }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dev": true, - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { - "node": ">=0.6.0" + "node": ">=6" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "node_modules/caniuse-lite": { + "version": "1.0.30001541", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001541.tgz", + "integrity": "sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw==", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.9" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=4" } }, - "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { - "uc.micro": "^1.0.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true, "engines": { - "node": ">=6.11.5" + "node": ">=6.0" } }, - "node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, - "node_modules/loader-utils/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "color-name": "1.1.3" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 0.8" } }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">= 6" } }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "dev": true }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-js": { + "version": "3.32.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz", + "integrity": "sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==", "dev": true, - "engines": { - "node": ">=8" + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/core-js-compat": { + "version": "3.32.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.2.tgz", + "integrity": "sha512-+GjlguTDINOijtVRUxrQOv3kfu9rl+qPNdX2LTbJ/ZyVTuxK+ksVSAGX1nHstu4hrv1En/uPTtWgq2gI5wt4AQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "browserslist": "^4.21.10" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", "dev": true, "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" } }, - "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" } }, - "node_modules/markdown-it-anchor": { - "version": "8.6.4", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.4.tgz", - "integrity": "sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img==", + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, - "peerDependencies": { - "@types/markdown-it": "*", - "markdown-it": "*" + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, - "node_modules/marked": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.18.tgz", - "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "bin": { - "marked": "bin/marked.js" + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">= 12" + "node": ">= 8" } }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "dependencies": { - "hash-base": "^3.0.0", + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" } }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "node_modules/crypto-js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", + "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, - "node_modules/memfs": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", - "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", - "dev": true, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dependencies": { - "fs-monkey": "^1.0.3" + "assert-plus": "^1.0.0" }, "engines": { - "node": ">= 4.0.0" + "node": ">=0.10" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "ms": "2.1.2" }, "engines": { - "node": ">=8.6" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "engines": { + "node": ">=10" }, - "bin": { - "miller-rabin": "bin/miller-rabin" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/define-data-property": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", + "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", "dev": true, "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, - "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "node": ">=0.3.1" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "path-type": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "esutils": "^2.0.2" }, "engines": { - "node": ">=10" + "node": ">=6.0.0" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.537", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.537.tgz", + "integrity": "sha512-W1+g9qs9hviII0HAwOdehGYkr+zt7KKdmCcJcjH0mYg6oL8+ioT3Skjmt7BLoAQqXhjf40AXd+HlR4oAWMlXjA==", "dev": true }, - "node_modules/mocha/node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "dependencies": { - "randombytes": "^2.1.0" + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=10.13.0" } }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "node_modules/envinfo": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.10.0.tgz", + "integrity": "sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==", "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "node_modules/es-abstract": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", + "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.11" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", "dev": true }, - "node_modules/net": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz", - "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==" - }, - "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "node_modules/es-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", + "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", "dev": true }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" + "node": ">= 0.4" } }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "has": "^1.0.3" } }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4075,808 +4383,919 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=6" } }, - "node_modules/object.assign": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.3.tgz", - "integrity": "sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA==", + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.50.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz", + "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.50.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">= 0.4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", - "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", + "node_modules/eslint-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", "dev": true, - "dependencies": { - "array.prototype.reduce": "^1.0.4", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.1" - }, - "engines": { - "node": ">= 0.8" + "bin": { + "eslint-config-prettier": "bin/cli.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "dependencies": { - "wrappy": "1" + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" + "ms": "^2.1.1" } }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", - "dev": true - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/eslint-import-resolver-typescript": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz", + "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "debug": "^4.3.4", + "enhanced-resolve": "^5.12.0", + "eslint-module-utils": "^2.7.4", + "fast-glob": "^3.3.1", + "get-tsconfig": "^4.5.0", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3" }, "engines": { - "node": ">=10" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "debug": "^3.2.7" }, "engines": { - "node": ">=10" + "node": ">=4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "ms": "^2.1.1" } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/eslint-plugin-import": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", + "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", "dev": true, "dependencies": { - "callsites": "^3.0.0" + "array-includes": "^3.1.6", + "array.prototype.findlastindex": "^1.2.2", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.8.0", + "has": "^1.0.3", + "is-core-module": "^2.13.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.6", + "object.groupby": "^1.0.0", + "object.values": "^1.1.6", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" }, "engines": { - "node": ">=6" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, - "node_modules/parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "dependencies": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" + "ms": "^2.1.1" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", + "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", "dev": true, + "dependencies": { + "@babel/runtime": "^7.20.7", + "aria-query": "^5.1.3", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.6.2", + "axobject-query": "^3.1.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.3", + "language-tags": "=1.0.5", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "semver": "^6.3.0" + }, "engines": { - "node": ">=8" + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, "engines": { - "node": ">=8" + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=0.12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "engines": { - "node": ">=8.6" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==", + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, "engines": { - "node": ">=4" + "node": ">=4.0" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "locate-path": "^2.0.0" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=4" + "node": ">=10.13.0" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "node_modules/eslint/node_modules/globals": { + "version": "13.22.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz", + "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==", "dev": true, "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "p-limit": "^1.1.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, "engines": { - "node": ">=4" + "node": ">=0.10" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { - "node": ">= 0.8.0" + "node": ">=4.0" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, "engines": { - "node": ">= 0.6.0" + "node": ">=4.0" } }, - "node_modules/promise-throttle": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/promise-throttle/-/promise-throttle-1.1.2.tgz", - "integrity": "sha512-dij7vjyXNewuuN/gyr+TX2KRjw48mbV5FEtgyXaIoJjGYAKT0au23/voNvy9eS4UNJjx2KUdEcO5Yyfc1h7vWQ==" - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "engines": { - "node": ">=6" + "node": ">=4.0" } }, - "node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, "engines": { - "node": ">=0.6" + "node": ">=4.0" } }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "engines": { - "node": ">=0.4.x" + "node": ">=0.10.0" } }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "engines": { - "node": ">=0.4.x" + "node": ">=0.8.x" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" ] }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, "dependencies": { - "safe-buffer": "^5.1.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" } }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "reusify": "^1.0.4" } }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "flat-cache": "^3.0.4" }, "engines": { - "node": ">= 6" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { - "picomatch": "^2.2.1" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=8.10.0" + "node": ">=8" } }, - "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", "dev": true, "dependencies": { - "resolve": "^1.9.0" + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=6" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "bin": { + "flat": "cli.js" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "node_modules/flat-cache": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", + "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "dev": true, "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "flatted": "^3.2.7", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">= 6" + "node": ">=12.0.0" } }, - "node_modules/request-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", - "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", - "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, "dependencies": { - "bluebird": "^3.5.0", - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, + "is-callable": "^1.1.3" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" + "node": "*" } }, - "node_modules/request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dependencies": { - "lodash": "^4.17.19" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" + "node": ">= 0.12" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=0.10.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "engines": { - "node": ">=4" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", "dev": true, "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dependencies": { - "queue-microtask": "^1.2.2" + "assert-plus": "^1.0.0" } }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": "*" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/serialize-javascript": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", - "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", - "dependencies": { - "randombytes": "^2.1.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "is-glob": "^4.0.1" }, - "bin": { - "sha.js": "bin.js" + "engines": { + "node": ">= 6" } }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, "dependencies": { - "shebang-regex": "^3.0.0" + "define-properties": "^1.1.3" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/slash": { + "node_modules/globby/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", @@ -4885,191 +5304,140 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=4.x" } }, - "node_modules/stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/stream-browserify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", - "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", - "dev": true, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", "dependencies": { - "inherits": "~2.0.4", - "readable-stream": "^3.5.0" + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/stream-http": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", - "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "xtend": "^4.0.2" + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "get-intrinsic": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "has-symbols": "^1.0.2" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -5077,524 +5445,470 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", - "dev": true, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" + "he": "bin/he" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", - "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.7", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.7.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" } }, - "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "dependencies": { - "randombytes": "^2.1.0" + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/text-encoding": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", - "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", - "deprecated": "no longer maintained" + "node_modules/html5-fs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/html5-fs/-/html5-fs-0.1.1.tgz", + "integrity": "sha512-FqL7S02QEbAZ/xvp+BqurAolHvPQqOKPaWn1SsbP5fwpOLVcMGu12NPqr4mW5dRpdh2/UFbuxFDAhOw/LCDDvw==" }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", "dev": true }, - "node_modules/tls": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tls/-/tls-0.0.1.tgz", - "integrity": "sha512-GzHpG+hwupY8VMR6rYsnAhTHqT/97zT45PG8WD5eTT1lq+dFE0nN+1PYpsoBcHJgSmTz5ceK2Cv88IkPmIPOtQ==" + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "engines": { + "node": ">=10.18" + } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { - "node": ">=4" + "node": ">= 4" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { - "is-number": "^7.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=8.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": ">=0.8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "node_modules/import-local/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, "dependencies": { - "safe-buffer": "^5.0.1" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/import-local/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1" + "p-locate": "^4.1.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/import-local/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "node_modules/import-local/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "p-limit": "^2.2.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/underscore": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", - "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", - "dev": true - }, - "node_modules/update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" + "find-up": "^4.0.0" }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "engines": { + "node": ">=8" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" } }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", - "which-typed-array": "^1.1.2" + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } }, - "node_modules/util.promisify": { + "node_modules/is-arguments": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.1.1.tgz", - "integrity": "sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw==", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "for-each": "^0.3.3", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "bin": { - "uuid": "bin/uuid" + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=8" } }, - "node_modules/web-worker": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", - "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" - }, - "node_modules/webpack": { - "version": "5.77.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", - "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", - "colorette": "^2.0.14", - "commander": "^7.0.0", - "cross-spawn": "^7.0.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "4.x.x || 5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "@webpack-cli/migrate": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, - "engines": { - "node": ">= 10" + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/webpack-cli/node_modules/webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "dependencies": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/webpack-merge": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.4.tgz", - "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "dependencies": { - "lodash": "^4.17.5" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { - "node": ">=10.13.0" + "node": ">=8" } }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, "engines": { - "node": ">=4.0" + "node": ">=0.10.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", "dev": true, "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", - "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.9" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5603,4312 +5917,3065 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/worker-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-2.0.0.tgz", - "integrity": "sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==", + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "dependencies": { - "loader-utils": "^1.0.0", - "schema-utils": "^0.4.0" + "isobject": "^3.0.1" }, "engines": { - "node": ">= 6.9.0 || >= 8.9.0" - }, - "peerDependencies": { - "webpack": "^3.0.0 || ^4.0.0-alpha.0 || ^4.0.0" + "node": ">=0.10.0" } }, - "node_modules/worker-loader/node_modules/schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "which-typed-array": "^1.1.11" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, - "node_modules/wrappy": { + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, - "node_modules/xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, "engines": { - "node": ">=0.4" + "node": ">=0.10.0" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">=10" + "node": ">= 10.13.0" } }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "has-flag": "^4.0.0" }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/yargs-unparser/node_modules/decamelize": { + "node_modules/jquery-csv": { + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/jquery-csv/-/jquery-csv-1.0.21.tgz", + "integrity": "sha512-uDIFsJcC4xZEofK9pk3hwFQlyqf3FFwkQq6JIa882lf0pu/S0H4LqjCAEaULGM0uOcdCbGbMBh3b3LmPtF1a9A==", + "dev": true + }, + "node_modules/js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "argparse": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-joy": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/json-joy/-/json-joy-9.6.0.tgz", + "integrity": "sha512-vJtJD89T0OOZFMaENe95xKCOdibMev/lELkclTdhZxLplwbBPxneWNuctUPizk2nLqtGfBxwCXVO42G9LBoFBA==", "dev": true, + "dependencies": { + "arg": "^5.0.2", + "hyperdyperid": "^1.2.0" + }, + "bin": { + "jj": "bin/jj.js", + "json-pack": "bin/json-pack.js", + "json-pack-test": "bin/json-pack-test.js", + "json-patch": "bin/json-patch.js", + "json-patch-test": "bin/json-patch-test.js", + "json-pointer": "bin/json-pointer.js", + "json-pointer-test": "bin/json-pointer-test.js", + "json-unpack": "bin/json-unpack.js" + }, "engines": { - "node": ">=10" + "node": ">=10.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "quill-delta": "^5", + "rxjs": "7", + "tslib": "2" } }, - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, - "@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, - "@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" } }, - "@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, - "requires": { - "@babel/types": "^7.18.10", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } + "json-buffer": "3.0.1" } }, - "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, - "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", - "semver": "^6.3.0" + "engines": { + "node": ">=0.10.0" } }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", "dev": true }, - "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", "dev": true, - "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "dependencies": { + "language-subtag-registry": "~0.3.2" } }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "requires": { - "@babel/types": "^7.18.6" + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, - "requires": { - "@babel/types": "^7.18.6" + "engines": { + "node": ">=6.11.5" } }, - "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } + "peer": true }, - "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, - "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", - "dev": true + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true, + "peer": true }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, - "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", - "dev": true - }, - "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" - } - }, - "@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", - "debug": "^4.1.0", - "globals": "^11.1.0" + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", - "to-fast-properties": "^2.0.0" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - } + "engines": { + "node": ">=8" } }, - "@humanwhocodes/config-array": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", - "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "dependencies": { + "yallist": "^3.0.2" } }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, - "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" } }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" } }, - "@jsdoc/salty": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.3.tgz", - "integrity": "sha512-bbtCxCkxcnWhi50I+4Lj6mdz9w3pOXOgEQrID8TCZ/DF51fW7M9GCQW2y45SpBDdHd1Eirm1X/Cf6CkAAe8HPg==", + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, - "requires": { - "lodash": "^4.17.21" + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/memfs": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.5.0.tgz", + "integrity": "sha512-8QePW5iXi/ZCySFTo39h3ujKGT0rYVnZywuSo5AzR7POAuy4uBEFZKziYkkrlGdWuxACUxKAJ0L/sry3DSG+TA==", "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "dependencies": { + "json-joy": "^9.2.0", + "thingies": "^1.11.1" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "engines": { + "node": ">= 8" } }, - "@types/eslint": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", - "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" } }, - "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, - "@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "requires": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" } }, - "@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, - "@types/node": { - "version": "18.6.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.4.tgz", - "integrity": "sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==", - "dev": true - }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", "dev": true }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "node_modules/mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" } }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "node_modules/mocha/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, - "requires": { - "@xtuc/long": "4.2.2" + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", - "dev": true, - "requires": {} - }, - "@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "requires": { - "envinfo": "^7.7.3" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, - "requires": {} - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true - }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { - "color-convert": "^1.9.0" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "engines": { + "node": ">=8" } }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.reduce": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", - "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" } }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "requires": { - "safer-buffer": "~2.1.0" - } + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, - "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "node_modules/mocha/node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - }, "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } + "randombytes": "^2.1.0" } }, - "assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "requires": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - }, - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "requires": { - "lodash": "^4.17.14" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" - }, - "aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" - }, - "babel-loader": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.2.tgz", - "integrity": "sha512-Law0PGtRV1JL8Y9Wpzc0d6EE0GD7LzXWCfaeWwboUMcBWNG6gvaWTK1/+BK7a4X5EmeJiGEuDDFxUsOa8RSWCw==", + "node_modules/nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, - "requires": { - "find-cache-dir": "^1.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1", - "util.promisify": "^1.0.0" + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true + "node_modules/net": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz", + "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==" }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/node-environment-flags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/node-environment-flags/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "bin": { + "semver": "bin/semver" } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "engines": { + "node": ">=0.10.0" } }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" } }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, - "requires": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dev": true, - "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "requires": { - "pako": "~1.0.5" + "engines": { + "node": ">= 0.4" } }, - "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" } }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001374", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz", - "integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.7.tgz", + "integrity": "sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g==", "dev": true, - "requires": { - "lodash": "^4.17.15" + "dependencies": { + "array.prototype.reduce": "^1.0.6", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "safe-array-concat": "^1.0.0" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" } }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "dependencies": { + "wrappy": "1" } }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "dev": true + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { - "color-name": "1.1.3" + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" } }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, - "commondir": { + "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { - "safe-buffer": "~5.1.1" + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, - "create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - }, "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" } }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "engines": { + "node": ">=0.10.0" } }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "engines": { + "node": ">=8" } }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "engines": { + "node": ">=0.10.0" } }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "engines": { + "node": ">=8" } }, - "crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "requires": { - "assert-plus": "^1.0.0" + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" } }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dev": true, - "requires": { - "ms": "2.1.2" + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "engines": { + "node": ">=6" } }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - }, "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, - "requires": { - "path-type": "^4.0.0" + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "doctrine": { + "node_modules/pkg-dir/node_modules/locate-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, - "requires": { - "esutils": "^2.0.2" + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "electron-to-chromium": { - "version": "1.4.211", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz", - "integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==", - "dev": true - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "emojis-list": { + "node_modules/pkg-dir/node_modules/path-exists": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "engines": { + "node": ">=4" } }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true - }, - "envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", - "dev": true + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "es-abstract": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", - "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.4.3", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" + "engines": { + "node": ">= 0.6.0" } }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true + "node_modules/promise-throttle": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/promise-throttle/-/promise-throttle-1.1.2.tgz", + "integrity": "sha512-dij7vjyXNewuuN/gyr+TX2KRjw48mbV5FEtgyXaIoJjGYAKT0au23/voNvy9eS4UNJjx2KUdEcO5Yyfc1h7vWQ==" }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } }, - "eslint": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.21.0.tgz", - "integrity": "sha512-/XJ1+Qurf1T9G2M5IHrsjp+xrGT73RZf23xA1z5wB1ZzzEAWSZKvRwhWxTFp1rvkvCfwcvAUNAP31bhKTTGfDA==", + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", "dev": true, - "requires": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.10.4", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.3", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + { + "type": "patreon", + "url": "https://www.patreon.com/feross" }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + { + "type": "consulting", + "url": "https://feross.org/support" } - } + ] }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "node_modules/quill-delta": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz", + "integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==", "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "peer": true, + "dependencies": { + "fast-diff": "^1.3.0", + "lodash.clonedeep": "^4.5.0", + "lodash.isequal": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } + "safe-buffer": "^5.1.0" } }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", - "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "requires": { - "estraverse": "^5.1.0" + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "requires": { - "estraverse": "^5.2.0" + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" } }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "dev": true }, - "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } + "@babel/runtime": "^7.8.4" } }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, - "requires": { - "reusify": "^1.0.4" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true, - "requires": { - "flat-cache": "^3.0.4" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, - "requires": { - "to-regex-range": "^5.0.1" + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" } }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha512-46TFiBOzX7xq/PcSWfFwkyjpemdRnMe31UQF+os0y+1W3k95f6R4SEt02Hj4p3X0Mir9gfrkmOtshFidS0VPUg==", + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" } }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "bin": { + "jsesc": "bin/jsesc" } }, - "flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "node_modules/request-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", + "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", + "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", + "dependencies": { + "bluebird": "^3.5.0", + "request-promise-core": "1.1.4", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" } }, - "flatted": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", - "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", - "dev": true + "node_modules/request-promise-core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", + "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", + "dependencies": { + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "request": "^2.34" + } }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "requires": { - "is-callable": "^1.1.3" + "engines": { + "node": ">=0.10.0" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/resolve": { + "version": "1.22.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", + "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "engines": { + "node": ">=8" } }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "engines": { + "node": ">=4" } }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "requires": { - "assert-plus": "^1.0.0" + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, - "requires": { - "is-glob": "^4.0.3" + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, - "requires": { - "function-bind": "^1.1.1" + "peer": true, + "dependencies": { + "tslib": "^2.1.0" } }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "has-tostringtag": { + "node_modules/safe-regex-test": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", "dev": true, - "requires": { - "has-symbols": "^1.0.2" + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "html5-fs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/html5-fs/-/html5-fs-0.1.1.tgz", - "integrity": "sha512-FqL7S02QEbAZ/xvp+BqurAolHvPQqOKPaWn1SsbP5fwpOLVcMGu12NPqr4mW5dRpdh2/UFbuxFDAhOw/LCDDvw==" - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" } }, - "https-browserify": { + "node_modules/schema-utils/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", - "dev": true - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "bin": { + "semver": "bin/semver.js" } }, - "import-local": { + "node_modules/serialize-javascript": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - } + "randombytes": "^2.1.0" } }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" } }, - "interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", - "dev": true - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" } }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { - "has-bigints": "^1.0.1" + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "requires": { - "binary-extensions": "^2.0.0" + "engines": { + "node": ">=8" } }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/shiki": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz", + "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, - "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "dev": true + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "is-core-module": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", - "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true, - "requires": { - "has": "^1.0.3" + "engines": { + "node": ">=6" } }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "engines": { + "node": ">=0.10.0" } }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "node_modules/stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", + "engines": { + "node": ">=0.10.0" } }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", "dev": true, - "requires": { - "is-extglob": "^2.1.1" + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" } }, - "is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" } }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, - "requires": { - "isobject": "^3.0.1" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, - "requires": { + "dependencies": { "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, - "requires": { - "call-bind": "^1.0.2" + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "requires": { - "has-symbols": "^1.0.2" + "engines": { + "node": ">=4" } }, - "is-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", - "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "requires": { - "xmlcreate": "^2.0.4" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "jsdoc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.0.tgz", - "integrity": "sha512-tzTgkklbWKrlaQL2+e3NNgLcZu3NaK2vsHRx7tyHQ+H5jcB9Gx0txSd2eJWlMC/xU1+7LQu4s58Ry0RkuaEQVg==", - "dev": true, - "requires": { - "@babel/parser": "^7.9.4", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.9" + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, - "requires": { - "uc.micro": "^1.0.1" - } - }, - "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true - }, - "loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } + "engines": { + "node": ">=6" } }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/terser": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.20.0.tgz", + "integrity": "sha512-e56ETryaQDyebBwJIWYB2TT6f2EZ0fL0sW/JRXNMN26zZdKi2u/E/5my5lG6jNxym6qsrVXfFRmOdV42zlAgLQ==", "dev": true, - "requires": { - "p-locate": "^5.0.0" + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "esbuild": { + "optional": true }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, - "requires": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - }, - "markdown-it-anchor": { - "version": "8.6.4", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.4.tgz", - "integrity": "sha512-Ul4YVYZNxMJYALpKtu+ZRdrryYt/GlQ5CK+4l1bp/gWXOG2QWElt6AqF3Mih/wfUKdZbNAZVXGR73/n6U/8img==", - "dev": true, - "requires": {} - }, - "marked": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.18.tgz", - "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==", - "dev": true - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, - "memfs": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", - "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", - "dev": true, - "requires": { - "fs-monkey": "^1.0.3" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - }, - "mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "net": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz", - "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==" - }, - "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.3.tgz", - "integrity": "sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", - "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", - "dev": true, - "requires": { - "array.prototype.reduce": "^1.0.4", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, - "requires": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==", - "dev": true, - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true - }, - "promise-throttle": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/promise-throttle/-/promise-throttle-1.1.2.tgz", - "integrity": "sha512-dij7vjyXNewuuN/gyr+TX2KRjw48mbV5FEtgyXaIoJjGYAKT0au23/voNvy9eS4UNJjx2KUdEcO5Yyfc1h7vWQ==" - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, - "requires": { - "resolve": "^1.9.0" - } - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "request-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", - "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", - "requires": { - "bluebird": "^3.5.0", - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, - "request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "requires": { - "lodash": "^4.17.19" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", - "dev": true, - "requires": { - "lodash": "^4.17.14" + "uglify-js": { + "optional": true + } } }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } + "randombytes": "^2.1.0" } }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/text-encoding": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", + "deprecated": "no longer maintained" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/thingies": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.12.0.tgz", + "integrity": "sha512-AiGqfYC1jLmJagbzQGuoZRM48JPsr9yB734a7K6wzr34NMhjUPrWSQrkF7ZBybf3yCerCL2Gcr02kMv4NmaZfA==", "dev": true, - "requires": { - "glob": "^7.1.3" + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" } }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } + "node_modules/tls": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tls/-/tls-0.0.1.tgz", + "integrity": "sha512-GzHpG+hwupY8VMR6rYsnAhTHqT/97zT45PG8WD5eTT1lq+dFE0nN+1PYpsoBcHJgSmTz5ceK2Cv88IkPmIPOtQ==" }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, - "requires": { - "queue-microtask": "^1.2.2" + "engines": { + "node": ">=4" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "serialize-javascript": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", - "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", - "requires": { - "randombytes": "^2.1.0" + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" } }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "node_modules/ts-loader": { + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz", + "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==", "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" } }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "kind-of": "^6.0.2" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "requires": { - "shebang-regex": "^3.0.0" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/ts-loader/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/ts-loader/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==" - }, - "stream-browserify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", - "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", - "dev": true, - "requires": { - "inherits": "~2.0.4", - "readable-stream": "^3.5.0" - } - }, - "stream-http": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", - "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "xtend": "^4.0.2" + "engines": { + "node": ">=8" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/ts-loader/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "node_modules/ts-loader/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" + "node_modules/ts-loader/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "requires": { - "has-flag": "^3.0.0" + "engines": { + "node": ">=0.3.1" } }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" } }, - "terser-webpack-plugin": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", - "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.7", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.7.2" - }, - "dependencies": { - "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - } + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" } }, - "text-encoding": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", - "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "tls": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tls/-/tls-0.0.1.tgz", - "integrity": "sha512-GzHpG+hwupY8VMR6rYsnAhTHqT/97zT45PG8WD5eTT1lq+dFE0nN+1PYpsoBcHJgSmTz5ceK2Cv88IkPmIPOtQ==" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true, + "peer": true }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, - "requires": { - "is-number": "^7.0.0" + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, - "tunnel-agent": { + "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "requires": { + "dependencies": { "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" } }, - "tweetnacl": { + "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, - "type-check": { + "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "type-fest": { + "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedoc": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", + "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "unbox-primitive": { + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, - "requires": { + "dependencies": { "call-bind": "^1.0.2", "has-bigints": "^1.0.2", "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "underscore": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", - "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", - "dev": true + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, - "requires": { + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { + "dependencies": { "punycode": "^2.1.0" } }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", + "node_modules/url": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", + "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", + "dev": true, + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.11.2" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/url/node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", - "dev": true - } + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dev": true, - "requires": { + "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", "which-typed-array": "^1.1.2" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "util.promisify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.1.1.tgz", - "integrity": "sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "for-each": "^0.3.3", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.1" + "node_modules/uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" } }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, - "verror": { + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "requires": { + "engines": [ + "node >=0.6.0" + ], + "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, - "watchpack": { + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, + "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, - "requires": { + "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" } }, - "web-worker": { + "node_modules/web-worker": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" }, - "webpack": { - "version": "5.77.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", - "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "node_modules/webpack": { + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "dev": true, - "requires": { + "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", + "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -9917,227 +8984,270 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, - "dependencies": { - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true } } }, - "webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, - "requires": { + "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", - "commander": "^7.0.0", + "commander": "^10.0.1", "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", "webpack-merge": "^5.7.3" }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true }, - "webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - } + "webpack-dev-server": { + "optional": true } } }, - "webpack-merge": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.4.tgz", - "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==", + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, - "requires": { - "lodash": "^4.17.5" + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-merge": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", + "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" } }, - "webpack-sources": { + "node_modules/webpack-sources": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "which-boxed-primitive": { + "node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, - "requires": { + "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", "is-number-object": "^1.0.4", "is-string": "^1.0.5", "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "which-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", - "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "dev": true, - "requires": { + "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, - "worker-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-2.0.0.tgz", - "integrity": "sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==", - "dev": true, - "requires": { - "loader-utils": "^1.0.0", - "schema-utils": "^0.4.0" - }, - "dependencies": { - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "workerpool": { + "node_modules/workerpool": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "wrappy": { + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true - }, - "xtend": { + "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.4" + } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "yargs": { + "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "requires": { + "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -10146,56 +9256,54 @@ "y18n": "^5.0.5", "yargs-parser": "^20.2.2" }, - "dependencies": { - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - } + "engines": { + "node": ">=10" } }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "engines": { + "node": ">=10" } }, - "yargs-unparser": { + "node_modules/yargs-unparser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, - "requires": { + "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - } + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" } }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index bdb458544..893568dc7 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,27 @@ { - "name": "monero-javascript", - "description": "A JavaScript library for using Monero", + "name": "monero-ts", + "description": "A TypeScript library for using Monero", "version": "0.9.0", "license": "MIT", - "repository": "https://github.com/monero-ecosystem/monero-javascript", + "repository": "https://github.com/monero-ecosystem/monero-ts", "private": false, - "main": "index.js", + "main": "dist/index.js", + "types": "dist/index.d.ts", "scripts": { "start": "todo", "build_web_worker": "webpack --config ./webpack.worker.js", "build_web_tests": "webpack --config ./webpack.tests.js", - "test": "node --no-experimental-fetch --experimental-wasm-threads node_modules/mocha/bin/_mocha 'src/test/TestAll' --timeout 900000000 --exit", - "jsdoc": "jsdoc -r -c ./configs/jsdoc_config.js -d ./docs" + "test": "npm run build_commonjs && node --enable-source-maps --no-experimental-fetch --experimental-wasm-threads node_modules/mocha/bin/_mocha --require @babel/register 'dist/src/test/TestAll' --timeout 900000000 --exit", + "typedoc": "typedoc ./index.ts --out ./docs/typedocs --excludePrivate", + "build_commonjs": "babel ./src --extensions '.js,.ts' --out-dir ./dist/src && babel ./index.ts --extensions '.ts' --out-dir ./dist && mkdir -p dist/dist && cp dist/*.js dist/dist && cp dist/*.js.map dist/dist && cp dist/*.wasm dist/dist", + "check_babel_version": "babel -V" }, "build": { "publish": [ { "provider": "github", "owner": "woodser", - "repo": "monero-javascript" + "repo": "monero-ts" } ] }, @@ -26,6 +29,7 @@ "node": ">=10.0.0" }, "dependencies": { + "@types/node": "^20.6.0", "ajv": "^6.12.6", "async": "2.6.4", "crypto-js": "^4.0.0", @@ -37,20 +41,34 @@ "serialize-javascript": "^3.1.0", "text-encoding": "^0.7.0", "tls": "0.0.1", - "web-worker": "^1.0.0" + "uuid": "3.3.2", + "web-worker": "^1.2.0" }, "devDependencies": { - "@babel/core": "^7.15.0", - "assert": "^2.0.0", - "babel-loader": "8.0.2", + "@babel/cli": "^7.18.10", + "@babel/core": "^7.22.17", + "@babel/node": "^7.18.10", + "@babel/plugin-transform-runtime": "^7.18.10", + "@babel/preset-env": "^7.22.15", + "@babel/preset-typescript": "^7.22.15", + "@babel/register": "^7.22.15", + "@types/jquery": "^3.5.19", + "@types/mocha": "^9.1.1", + "@typescript-eslint/eslint-plugin": "5.53.0", + "@typescript-eslint/parser": "^5.53.0", + "assert": "^2.1.0", + "babel-loader": "^9.1.3", "browserify-zlib": "^0.2.0", "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", - "eslint": "^8.0.1", + "eslint": "^8.35.0", + "eslint-config-prettier": "^8.6.0", + "eslint-import-resolver-typescript": "^3.5.3", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-jsx-a11y": "^6.7.1", "https-browserify": "^1.0.0", - "jsdoc": "^4.0.0", - "memfs": "^3.2.0", - "minimist": "^1.2.5", + "jquery-csv": "^1.0.21", + "memfs": "^4.2.1", "mocha": "^9.1.3", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", @@ -58,12 +76,14 @@ "querystring-es3": "^0.2.1", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", - "url": "^0.11.0", - "util": "^0.12.4", - "webpack": "^5.76.0", - "webpack-cli": "^4.8.0", - "webpack-merge": "4.1.4", - "worker-loader": "2.0.0", - "yargs-parser": "^18.1.3" + "ts-loader": "^9.4.4", + "ts-node": "^10.9.1", + "typedoc": "^0.25.1", + "typescript": "^5.2.2", + "url": "^0.11.2", + "util": "^0.12.5", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4", + "webpack-merge": "^5.9.0" } } diff --git a/src/main/cpp/http_client_wasm.cpp b/src/main/cpp/http_client_wasm.cpp index e64e1d7d1..be19a9353 100644 --- a/src/main/cpp/http_client_wasm.cpp +++ b/src/main/cpp/http_client_wasm.cpp @@ -12,10 +12,10 @@ using namespace std; EM_JS(const char*, js_send_json_request, (const char* uri, const char* username, const char* password, const char* reject_unauthorized_fn_id, const char* method, const char* body, std::chrono::milliseconds timeout), { //console.log("EM_JS js_send_json_request(" + UTF8ToString(uri) + ", " + UTF8ToString(username) + ", " + UTF8ToString(password) + ", " + UTF8ToString(method) + ")"); - const monerojs = require("../index"); - const HttpClient = monerojs.HttpClient; - const LibraryUtils = monerojs.LibraryUtils; - const GenUtils = monerojs.GenUtils; + const moneroTs = require("../index"); + const HttpClient = moneroTs.HttpClient; + const LibraryUtils = moneroTs.LibraryUtils; + const GenUtils = moneroTs.GenUtils; // use asyncify to synchronously return to C++ return Asyncify.handleSleep(function(wakeUp) { @@ -67,10 +67,10 @@ EM_JS(const char*, js_send_json_request, (const char* uri, const char* username, EM_JS(const char*, js_send_binary_request, (const char* uri, const char* username, const char* password, const char* reject_unauthorized_fn_id, const char* method, const char* body, int body_length, std::chrono::milliseconds timeout), { //console.log("EM_JS js_send_binary_request(" + UTF8ToString(uri) + ", " + UTF8ToString(username) + ", " + UTF8ToString(password) + ", " + UTF8ToString(method) + ")"); - const monerojs = require("../index"); - const HttpClient = monerojs.HttpClient; - const LibraryUtils = monerojs.LibraryUtils; - const GenUtils = monerojs.GenUtils; + const moneroTs = require("../index"); + const HttpClient = moneroTs.HttpClient; + const LibraryUtils = moneroTs.LibraryUtils; + const GenUtils = moneroTs.GenUtils; // use asyncify to synchronously return to C++ return Asyncify.handleSleep(function(wakeUp) { diff --git a/src/main/js/common/HttpClient.js b/src/main/js/common/HttpClient.js deleted file mode 100644 index ec8559446..000000000 --- a/src/main/js/common/HttpClient.js +++ /dev/null @@ -1,496 +0,0 @@ -const GenUtils = require("../common/GenUtils"); -const LibraryUtils = require("./LibraryUtils"); -const MoneroUtils = require("./MoneroUtils"); -const ThreadPool = require("./ThreadPool"); -const PromiseThrottle = require("promise-throttle"); -const Request = require("request-promise"); - -/** - * Handle HTTP requests with a uniform interface. - * - * @hideconstructor - */ -class HttpClient { - - /** - *

Make a HTTP request.

- * - * @param {object} request - configures the request to make - * @param {string} request.method - HTTP method ("GET", "PUT", "POST", "DELETE", etc) - * @param {string} request.uri - uri to request - * @param {string|object|Uint8Array} request.body - request body - * @param {string} request.username - username to authenticate the request (optional) - * @param {string} request.password - password to authenticate the request (optional) - * @param {object} request.headers - headers to add to the request (optional) - * @param {string} request.requestApi - one of "fetch" or "xhr" (default "fetch") - * @param {boolean} request.resolveWithFullResponse - return full response if true, else body only (default false) - * @param {boolean} request.rejectUnauthorized - whether or not to reject self-signed certificates (default true) - * @param {number} request.timeout - maximum time allowed in milliseconds - * @param {number} request.proxyToWorker - proxy request to worker thread - * @returns {object} response - the response object - * @returns {string|object|Uint8Array} response.body - the response body - * @returns {number} response.statusCode - the response code - * @returns {String} response.statusText - the response message - * @returns {object} response.headers - the response headers - */ - static async request(request) { - - // proxy to worker if configured - if (request.proxyToWorker) { - try { - return await LibraryUtils.invokeWorker(undefined, "httpRequest", request); - } catch (err) { - if (err.message.length > 0 && err.message.charAt(0) === "{") { - let parsed = JSON.parse(err.message); - err.message = parsed.statusMessage; - err.statusCode = parsed.statusCode; - } - throw err; - } - } - - // assign defaults - request = Object.assign({}, HttpClient._DEFAULT_REQUEST, request); - - // validate request - try { request.host = new URL(request.uri).host; } // hostname:port - catch (err) { throw new Error("Invalid request URL: " + request.uri); } - if (request.body && !(typeof request.body === "string" || typeof request.body === "object")) { - throw new Error("Request body type is not string or object"); - } - - // initialize one task queue per host - if (!HttpClient._TASK_QUEUES[request.host]) HttpClient._TASK_QUEUES[request.host] = new ThreadPool(1); - - // initialize one promise throttle per host - if (!HttpClient._PROMISE_THROTTLES[request.host]) { - HttpClient._PROMISE_THROTTLES[request.host] = new PromiseThrottle({ - requestsPerSecond: MoneroUtils.MAX_REQUESTS_PER_SECOND, // TODO: HttpClient should not depend on MoneroUtils for configuration - promiseImplementation: Promise - }); - } - - // request using fetch or xhr with timeout - let timeout = request.timeout === undefined ? HttpClient._DEFAULT_TIMEOUT : request.timeout === 0 ? HttpClient.MAX_TIMEOUT : request.timeout; - let requestPromise = request.requestApi === "fetch" ? HttpClient._requestFetch(request) : HttpClient._requestXhr(request); - let timeoutPromise = new Promise((resolve, reject) => { - let id = setTimeout(() => { - clearTimeout(id); - reject('Request timed out in '+ timeout + ' milliseconds') - }, timeout); - }); - return Promise.race([requestPromise, timeoutPromise]); - } - - // ----------------------------- PRIVATE HELPERS ---------------------------- - - static async _requestFetch(req) { - - // build request options - let opts = { - method: req.method, - uri: req.uri, - body: req.body, - agent: req.uri.startsWith("https") ? HttpClient._getHttpsAgent() : HttpClient._getHttpAgent(), - rejectUnauthorized: req.rejectUnauthorized, - resolveWithFullResponse: req.resolveWithFullResponse, - requestCert: true // TODO: part of config? - }; - if (req.username) { - opts.forever = true; - opts.auth = { - user: req.username, - pass: req.password, - sendImmediately: false - } - } - if (req.body instanceof Uint8Array) opts.encoding = null; - - // queue and throttle request to execute in serial and rate limited - let host = req.host; - let resp = await HttpClient._TASK_QUEUES[host].submit(async function() { - return HttpClient._PROMISE_THROTTLES[host].add(function(opts) { return Request(opts); }.bind(this, opts)); - }); - - // normalize response - let normalizedResponse = {}; - if (req.resolveWithFullResponse) { - normalizedResponse.statusCode = resp.statusCode; - normalizedResponse.statusText = resp.statusMessage; - normalizedResponse.headers = resp.headers; - normalizedResponse.body = resp.body; - } else { - normalizedResponse.body = resp; - } - return normalizedResponse; - } - - static async _requestXhr(req) { - if (req.headers) throw new Error("Custom headers not implemented in XHR request"); // TODO - - // collect params from request which change on await - let method = req.method; - let uri = req.uri; - let host = req.host; - let username = req.username; - let password = req.password; - let body = req.body; - let isBinary = body instanceof Uint8Array; - - // queue and throttle requests to execute in serial and rate limited per host - let resp = await HttpClient._TASK_QUEUES[host].submit(async function() { - return HttpClient._PROMISE_THROTTLES[host].add(function() { - return new Promise(function(resolve, reject) { - let digestAuthRequest = new HttpClient.digestAuthRequest(method, uri, username, password); - digestAuthRequest.request(function(resp) { - resolve(resp); - }, function(resp) { - if (resp.status) resolve(resp); - else reject(new Error("Request failed without response: " + method + " " + uri)); - }, body); - }); - }.bind(this)); - }); - - // normalize response - let normalizedResponse = {}; - normalizedResponse.statusCode = resp.status; - normalizedResponse.statusText = resp.statusText; - normalizedResponse.headers = HttpClient._parseXhrResponseHeaders(resp.getAllResponseHeaders()); - normalizedResponse.body = isBinary ? new Uint8Array(resp.response) : resp.response; - if (normalizedResponse.body instanceof ArrayBuffer) normalizedResponse.body = new Uint8Array(normalizedResponse.body); // handle empty binary request - return normalizedResponse; - } - - /** - * Get a singleton instance of an HTTP client to share. - * - * @return {http.Agent} a shared agent for network requests among library instances - */ - static _getHttpAgent() { - if (!HttpClient.HTTP_AGENT) { - let http = require('http'); - HttpClient.HTTP_AGENT = new http.Agent({keepAlive: true, family: 4}); // use IPv4 - } - return HttpClient.HTTP_AGENT; - } - - /** - * Get a singleton instance of an HTTPS client to share. - * - * @return {https.Agent} a shared agent for network requests among library instances - */ - static _getHttpsAgent() { - if (!HttpClient.HTTPS_AGENT) { - let https = require('https'); - HttpClient.HTTPS_AGENT = new https.Agent({keepAlive: true, family: 4}); // use IPv4 - } - return HttpClient.HTTPS_AGENT; - } - - - static _parseXhrResponseHeaders(headersStr) { - let headerMap = {}; - let headers = headersStr.trim().split(/[\r\n]+/); - for (let header of headers) { - let headerVals = header.split(": "); - headerMap[headerVals[0]] = headerVals[1]; - } - return headerMap; - } -} - -/** - * Modification of digest auth request by @inorganik. - * - * Dependent on CryptoJS MD5 hashing: http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/md5.js - * - * MIT licensed. - */ -HttpClient.digestAuthRequest = function(method, url, username, password) { - var self = this; - - if (typeof CryptoJS === 'undefined' && typeof require === 'function') { - var CryptoJS = require('crypto-js'); - } - - this.scheme = null; // we just echo the scheme, to allow for 'Digest', 'X-Digest', 'JDigest' etc - this.nonce = null; // server issued nonce - this.realm = null; // server issued realm - this.qop = null; // "quality of protection" - '' or 'auth' or 'auth-int' - this.response = null; // hashed response to server challenge - this.opaque = null; // hashed response to server challenge - this.nc = 1; // nonce count - increments with each request used with the same nonce - this.cnonce = null; // client nonce - - // settings - this.timeout = 60000; // timeout - this.loggingOn = false; // toggle console logging - - // determine if a post, so that request will send data - this.post = false; - if (method.toLowerCase() === 'post' || method.toLowerCase() === 'put') { - this.post = true; - } - - // start here - // successFn - will be passed JSON data - // errorFn - will be passed the failed authenticatedRequest - // data - optional, for POSTS - this.request = function(successFn, errorFn, data) { - - // stringify json - if (data) { - try { - self.data = data instanceof Uint8Array || typeof data === "string" ? data : JSON.stringify(data); - } catch (err) { - console.error(err); - throw err; - } - } - self.successFn = successFn; - self.errorFn = errorFn; - - if (!self.nonce) { - self.makeUnauthenticatedRequest(self.data); - } else { - self.makeAuthenticatedRequest(); - } - } - this.makeUnauthenticatedRequest = function(data) { - self.firstRequest = new XMLHttpRequest(); - self.firstRequest.open(method, url, true); - self.firstRequest.timeout = self.timeout; - // if we are posting, add appropriate headers - if (self.post && data) { - if (typeof data === "string") { - self.firstRequest.setRequestHeader('Content-type', 'text/plain'); - } else { - self.firstRequest.responseType = "arraybuffer"; - } - } - - self.firstRequest.onreadystatechange = function() { - - // 2: received headers, 3: loading, 4: done - if (self.firstRequest.readyState === 2) { - - var responseHeaders = self.firstRequest.getAllResponseHeaders(); - responseHeaders = responseHeaders.split('\n'); - // get authenticate header - var digestHeaders; - for(var i = 0; i < responseHeaders.length; i++) { - if (responseHeaders[i].match(/www-authenticate/i) != null) { - digestHeaders = responseHeaders[i]; - } - } - - if (digestHeaders != null) { - // parse auth header and get digest auth keys - digestHeaders = digestHeaders.slice(digestHeaders.indexOf(':') + 1, -1); - digestHeaders = digestHeaders.split(','); - self.scheme = digestHeaders[0].split(/\s/)[1]; - for (var i = 0; i < digestHeaders.length; i++) { - var equalIndex = digestHeaders[i].indexOf('='), - key = digestHeaders[i].substring(0, equalIndex), - val = digestHeaders[i].substring(equalIndex + 1); - val = val.replace(/['"]+/g, ''); - // find realm - if (key.match(/realm/i) != null) { - self.realm = val; - } - // find nonce - if (key.match(/nonce/i) != null) { - self.nonce = val; - } - // find opaque - if (key.match(/opaque/i) != null) { - self.opaque = val; - } - // find QOP - if (key.match(/qop/i) != null) { - self.qop = val; - } - } - // client generated keys - self.cnonce = self.generateCnonce(); - self.nc++; - // if logging, show headers received: - self.log('received headers:'); - self.log(' realm: '+self.realm); - self.log(' nonce: '+self.nonce); - self.log(' opaque: '+self.opaque); - self.log(' qop: '+self.qop); - // now we can make an authenticated request - self.makeAuthenticatedRequest(); - } - } - if (self.firstRequest.readyState === 4) { - if (self.firstRequest.status === 200) { - self.log('Authentication not required for '+url); - if (data instanceof Uint8Array) { - self.successFn(self.firstRequest); - } else { - if (self.firstRequest.responseText !== 'undefined') { - if (self.firstRequest.responseText.length > 0) { - // If JSON, parse and return object - if (self.isJson(self.firstRequest.responseText)) { // TODO: redundant - self.successFn(self.firstRequest); - } else { - self.successFn(self.firstRequest); - } - } - } else { - self.successFn(); - } - } - } - } - } - // send - if (self.post) { - // in case digest auth not required - self.firstRequest.send(self.data); - } else { - self.firstRequest.send(); - } - self.log('Unauthenticated request to '+url); - - // handle error - self.firstRequest.onerror = function() { - if (self.firstRequest.status !== 401) { - self.log('Error ('+self.firstRequest.status+') on unauthenticated request to '+url); - self.errorFn(self.firstRequest); - } - } - } - this.makeAuthenticatedRequest= function() { - - self.response = self.formulateResponse(); - self.authenticatedRequest = new XMLHttpRequest(); - self.authenticatedRequest.open(method, url, true); - self.authenticatedRequest.timeout = self.timeout; - var digestAuthHeader = self.scheme+' '+ - 'username="'+username+'", '+ - 'realm="'+self.realm+'", '+ - 'nonce="'+self.nonce+'", '+ - 'uri="'+url+'", '+ - 'response="'+self.response+'", '+ - 'opaque="'+self.opaque+'", '+ - 'qop='+self.qop+', '+ - 'nc='+('00000000' + self.nc).slice(-8)+', '+ - 'cnonce="'+self.cnonce+'"'; - self.authenticatedRequest.setRequestHeader('Authorization', digestAuthHeader); - self.log('digest auth header response to be sent:'); - self.log(digestAuthHeader); - // if we are posting, add appropriate headers - if (self.post && self.data) { - if (typeof self.data === "string") { - self.authenticatedRequest.setRequestHeader('Content-type', 'text/plain'); - } else { - self.authenticatedRequest.responseType = "arraybuffer"; - } - } - self.authenticatedRequest.onload = function() { - // success - if (self.authenticatedRequest.status >= 200 && self.authenticatedRequest.status < 400) { - // increment nonce count - self.nc++; - // return data - if (self.data instanceof Uint8Array) { - self.successFn(self.authenticatedRequest); - } else { - if (self.authenticatedRequest.responseText !== 'undefined' && self.authenticatedRequest.responseText.length > 0 ) { - // If JSON, parse and return object - if (self.isJson(self.authenticatedRequest.responseText)) { // TODO: redundant from not parsing - self.successFn(self.authenticatedRequest); - } else { - self.successFn(self.authenticatedRequest); - } - } else { - self.successFn(); - } - } - } - // failure - else { - self.nonce = null; - self.errorFn(self.authenticatedRequest); - } - } - // handle errors - self.authenticatedRequest.onerror = function() { - self.log('Error ('+self.authenticatedRequest.status+') on authenticated request to '+url); - self.nonce = null; - self.errorFn(self.authenticatedRequest); - }; - // send - if (self.post) { - self.authenticatedRequest.send(self.data); - } else { - self.authenticatedRequest.send(); - } - self.log('Authenticated request to '+url); - } - // hash response based on server challenge - this.formulateResponse = function() { - var HA1 = CryptoJS.MD5(username+':'+self.realm+':'+password).toString(); - var HA2 = CryptoJS.MD5(method+':'+url).toString(); - var response = CryptoJS.MD5(HA1+':'+ - self.nonce+':'+ - ('00000000' + self.nc).slice(-8)+':'+ - self.cnonce+':'+ - self.qop+':'+ - HA2).toString(); - return response; - } - // generate 16 char client nonce - this.generateCnonce = function() { - var characters = 'abcdef0123456789'; - var token = ''; - for (var i = 0; i < 16; i++) { - var randNum = Math.round(Math.random() * characters.length); - token += characters.substr(randNum, 1); - } - return token; - } - this.abort = function() { - self.log('[digestAuthRequest] Aborted request to '+url); - if (self.firstRequest != null) { - if (self.firstRequest.readyState != 4) self.firstRequest.abort(); - } - if (self.authenticatedRequest != null) { - if (self.authenticatedRequest.readyState != 4) self.authenticatedRequest.abort(); - } - } - this.isJson = function(str) { - try { - JSON.parse(str); - } catch (err) { - return false; - } - return true; - } - this.log = function(str) { - if (self.loggingOn) { - console.log('[digestAuthRequest] '+str); - } - } - this.version = function() { return '0.8.0' } -} - -// default request config -HttpClient._DEFAULT_REQUEST = { - method: "GET", - requestApi: "fetch", - resolveWithFullResponse: false, - rejectUnauthorized: true -} - -// rate limit requests per host -HttpClient._PROMISE_THROTTLES = []; -HttpClient._TASK_QUEUES = []; -HttpClient._DEFAULT_TIMEOUT = 60000; -HttpClient.MAX_TIMEOUT = 2147483647; // max 32-bit signed number - -module.exports = HttpClient; \ No newline at end of file diff --git a/src/main/js/common/MoneroConnectionManagerListener.js b/src/main/js/common/MoneroConnectionManagerListener.js deleted file mode 100644 index c0123db9c..000000000 --- a/src/main/js/common/MoneroConnectionManagerListener.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Default connection manager listener which takes no action on notifications. - */ -class MoneroConnectionManagerListener { - - /** - * Notified on connection change events. - * - * @param {MoneroRpcConnection} connection - the connection manager's current connection - */ - async onConnectionChanged(connection) { } -} - -module.exports = MoneroConnectionManagerListener; \ No newline at end of file diff --git a/src/main/js/common/SslOptions.js b/src/main/js/common/SslOptions.js deleted file mode 100644 index 5c82b0505..000000000 --- a/src/main/js/common/SslOptions.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * SSL options for remote endpoints. - */ -class SslOptions { - - constructor(state) { - this.state = Object.assign({}, state); - } - - getPrivateKeyPath() { - return this.state.privateKeyPath; - } - - setPrivateKeyPath(privateKeyPath) { - this.state.privateKeyPath = privateKeyPath; - return this; - } - - getCertificatePath() { - return this.state.certificatePath; - } - - setCertificatePath(certificatePath) { - this.state.certificatePath = certificatePath; - return this; - } - - getCertificateAuthorityFile() { - return this.state.certificateAuthorityFile; - } - - setCertificateAuthorityFile(certificateAuthorityFile) { - this.state.certificateAuthorityFile = certificateAuthorityFile; - return this; - } - - getAllowedFingerprints() { - return this.state.allowedFingerprints; - } - - setAllowedFingerprints(allowedFingerprints) { - this.state.allowedFingerprints = allowedFingerprints; - return this; - } - - getAllowAnyCert() { - return this.state.allowAnyCert; - } - - setAllowAnyCert(allowAnyCert) { - this.state.allowAnyCert = allowAnyCert; - return this; - } -} - -module.exports = SslOptions; \ No newline at end of file diff --git a/src/main/js/common/biginteger.js b/src/main/js/common/biginteger.js deleted file mode 100644 index 5082ef0a6..000000000 --- a/src/main/js/common/biginteger.js +++ /dev/null @@ -1,1636 +0,0 @@ -/* - JavaScript BigInteger library version 0.9.1 - http://silentmatt.com/biginteger/ - - Copyright (c) 2009 Matthew Crumley - Copyright (c) 2010,2011 by John Tobey - Licensed under the MIT license. - - Support for arbitrary internal representation base was added by - Vitaly Magerya. -*/ -/* - -This file has been modified by Paul Shapiro: - -1. to bring in the function lowVal which was written by Lucas Jones -2. to expose CONSTRUCT - -*/ -/* - File: biginteger.js - - Exports: - - -*/ -(function(exports) { -"use strict"; -/* - Class: BigInteger - An arbitrarily-large integer. - - objects should be considered immutable. None of the "built-in" - methods modify *this* or their arguments. All properties should be - considered private. - - All the methods of instances can be called "statically". The - static versions are convenient if you don't already have a - object. - - As an example, these calls are equivalent. - - > BigInteger(4).multiply(5); // returns BigInteger(20); - > BigInteger.multiply(4, 5); // returns BigInteger(20); - - > var a = 42; - > var a = BigInteger.toJSValue("0b101010"); // Not completely useless... -*/ - -var CONSTRUCT = {}; // Unique token to call "private" version of constructor - -/* - Constructor: BigInteger() - Convert a value to a . - - Although is the constructor for objects, it is - best not to call it as a constructor. If *n* is a object, it is - simply returned as-is. Otherwise, is equivalent to - without a radix argument. - - > var n0 = BigInteger(); // Same as - > var n1 = BigInteger("123"); // Create a new with value 123 - > var n2 = BigInteger(123); // Create a new with value 123 - > var n3 = BigInteger(n2); // Return n2, unchanged - - The constructor form only takes an array and a sign. *n* must be an - array of numbers in little-endian order, where each digit is between 0 - and BigInteger.base. The second parameter sets the sign: -1 for - negative, +1 for positive, or 0 for zero. The array is *not copied and - may be modified*. If the array contains only zeros, the sign parameter - is ignored and is forced to zero. - - > new BigInteger([5], -1): create a new BigInteger with value -5 - - Parameters: - - n - Value to convert to a . - - Returns: - - A value. - - See Also: - - , -*/ -function BigInteger(n, s, token) { - if (token !== CONSTRUCT) { - if (n instanceof BigInteger) { - return n; - } - else if (typeof n === "undefined") { - return ZERO; - } - return BigInteger.parse(n); - } - - n = n || []; // Provide the nullary constructor for subclasses. - while (n.length && !n[n.length - 1]) { - --n.length; - } - this._d = n; - this._s = n.length ? (s || 1) : 0; -} -BigInteger.CONSTRUCT = CONSTRUCT; // added by PS to actually use the constructor - -BigInteger._construct = function(n, s) { - return new BigInteger(n, s, CONSTRUCT); -}; - -// Base-10 speedup hacks in parse, toString, exp10 and log functions -// require base to be a power of 10. 10^7 is the largest such power -// that won't cause a precision loss when digits are multiplied. -var BigInteger_base = 10000000; -var BigInteger_base_log10 = 7; - -BigInteger.base = BigInteger_base; -BigInteger.base_log10 = BigInteger_base_log10; - -var ZERO = new BigInteger([], 0, CONSTRUCT); -// Constant: ZERO -// 0. -BigInteger.ZERO = ZERO; - -var ONE = new BigInteger([1], 1, CONSTRUCT); -// Constant: ONE -// 1. -BigInteger.ONE = ONE; - -var M_ONE = new BigInteger(ONE._d, -1, CONSTRUCT); -// Constant: M_ONE -// -1. -BigInteger.M_ONE = M_ONE; - -// Constant: _0 -// Shortcut for . -BigInteger._0 = ZERO; - -// Constant: _1 -// Shortcut for . -BigInteger._1 = ONE; - -/* - Constant: small - Array of from 0 to 36. - - These are used internally for parsing, but useful when you need a "small" - . - - See Also: - - , , <_0>, <_1> -*/ -BigInteger.small = [ - ZERO, - ONE, - /* Assuming BigInteger_base > 36 */ - new BigInteger( [2], 1, CONSTRUCT), - new BigInteger( [3], 1, CONSTRUCT), - new BigInteger( [4], 1, CONSTRUCT), - new BigInteger( [5], 1, CONSTRUCT), - new BigInteger( [6], 1, CONSTRUCT), - new BigInteger( [7], 1, CONSTRUCT), - new BigInteger( [8], 1, CONSTRUCT), - new BigInteger( [9], 1, CONSTRUCT), - new BigInteger([10], 1, CONSTRUCT), - new BigInteger([11], 1, CONSTRUCT), - new BigInteger([12], 1, CONSTRUCT), - new BigInteger([13], 1, CONSTRUCT), - new BigInteger([14], 1, CONSTRUCT), - new BigInteger([15], 1, CONSTRUCT), - new BigInteger([16], 1, CONSTRUCT), - new BigInteger([17], 1, CONSTRUCT), - new BigInteger([18], 1, CONSTRUCT), - new BigInteger([19], 1, CONSTRUCT), - new BigInteger([20], 1, CONSTRUCT), - new BigInteger([21], 1, CONSTRUCT), - new BigInteger([22], 1, CONSTRUCT), - new BigInteger([23], 1, CONSTRUCT), - new BigInteger([24], 1, CONSTRUCT), - new BigInteger([25], 1, CONSTRUCT), - new BigInteger([26], 1, CONSTRUCT), - new BigInteger([27], 1, CONSTRUCT), - new BigInteger([28], 1, CONSTRUCT), - new BigInteger([29], 1, CONSTRUCT), - new BigInteger([30], 1, CONSTRUCT), - new BigInteger([31], 1, CONSTRUCT), - new BigInteger([32], 1, CONSTRUCT), - new BigInteger([33], 1, CONSTRUCT), - new BigInteger([34], 1, CONSTRUCT), - new BigInteger([35], 1, CONSTRUCT), - new BigInteger([36], 1, CONSTRUCT) -]; - -// Used for parsing/radix conversion -BigInteger.digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); - -/* - Method: toString - Convert a to a string. - - When *base* is greater than 10, letters are upper case. - - Parameters: - - base - Optional base to represent the number in (default is base 10). - Must be between 2 and 36 inclusive, or an Error will be thrown. - - Returns: - - The string representation of the . -*/ -BigInteger.prototype.toString = function(base) { - base = +base || 10; - if (base < 2 || base > 36) { - throw new Error("illegal radix " + base + "."); - } - if (this._s === 0) { - return "0"; - } - if (base === 10) { - var str = this._s < 0 ? "-" : ""; - str += this._d[this._d.length - 1].toString(); - for (var i = this._d.length - 2; i >= 0; i--) { - var group = this._d[i].toString(); - while (group.length < BigInteger_base_log10) group = '0' + group; - str += group; - } - return str; - } - else { - var numerals = BigInteger.digits; - base = BigInteger.small[base]; - var sign = this._s; - - var n = this.abs(); - var digits = []; - var digit; - - while (n._s !== 0) { - var divmod = n.divRem(base); - n = divmod[0]; - digit = divmod[1]; - // TODO: This could be changed to unshift instead of reversing at the end. - // Benchmark both to compare speeds. - digits.push(numerals[digit.valueOf()]); - } - return (sign < 0 ? "-" : "") + digits.reverse().join(""); - } -}; - -// Verify strings for parsing -BigInteger.radixRegex = [ - /^$/, - /^$/, - /^[01]*$/, - /^[012]*$/, - /^[0-3]*$/, - /^[0-4]*$/, - /^[0-5]*$/, - /^[0-6]*$/, - /^[0-7]*$/, - /^[0-8]*$/, - /^[0-9]*$/, - /^[0-9aA]*$/, - /^[0-9abAB]*$/, - /^[0-9abcABC]*$/, - /^[0-9a-dA-D]*$/, - /^[0-9a-eA-E]*$/, - /^[0-9a-fA-F]*$/, - /^[0-9a-gA-G]*$/, - /^[0-9a-hA-H]*$/, - /^[0-9a-iA-I]*$/, - /^[0-9a-jA-J]*$/, - /^[0-9a-kA-K]*$/, - /^[0-9a-lA-L]*$/, - /^[0-9a-mA-M]*$/, - /^[0-9a-nA-N]*$/, - /^[0-9a-oA-O]*$/, - /^[0-9a-pA-P]*$/, - /^[0-9a-qA-Q]*$/, - /^[0-9a-rA-R]*$/, - /^[0-9a-sA-S]*$/, - /^[0-9a-tA-T]*$/, - /^[0-9a-uA-U]*$/, - /^[0-9a-vA-V]*$/, - /^[0-9a-wA-W]*$/, - /^[0-9a-xA-X]*$/, - /^[0-9a-yA-Y]*$/, - /^[0-9a-zA-Z]*$/ -]; - -/* - Function: parse - Parse a string into a . - - *base* is optional but, if provided, must be from 2 to 36 inclusive. If - *base* is not provided, it will be guessed based on the leading characters - of *s* as follows: - - - "0x" or "0X": *base* = 16 - - "0c" or "0C": *base* = 8 - - "0b" or "0B": *base* = 2 - - else: *base* = 10 - - If no base is provided, or *base* is 10, the number can be in exponential - form. For example, these are all valid: - - > BigInteger.parse("1e9"); // Same as "1000000000" - > BigInteger.parse("1.234*10^3"); // Same as 1234 - > BigInteger.parse("56789 * 10 ** -2"); // Same as 567 - - If any characters fall outside the range defined by the radix, an exception - will be thrown. - - Parameters: - - s - The string to parse. - base - Optional radix (default is to guess based on *s*). - - Returns: - - a instance. -*/ -BigInteger.parse = function(s, base) { - // Expands a number in exponential form to decimal form. - // expandExponential("-13.441*10^5") === "1344100"; - // expandExponential("1.12300e-1") === "0.112300"; - // expandExponential(1000000000000000000000000000000) === "1000000000000000000000000000000"; - function expandExponential(str) { - str = str.replace(/\s*[*xX]\s*10\s*(\^|\*\*)\s*/, "e"); - - return str.replace(/^([+\-])?(\d+)\.?(\d*)[eE]([+\-]?\d+)$/, function(x, s, n, f, c) { - c = +c; - var l = c < 0; - var i = n.length + c; - x = (l ? n : f).length; - c = ((c = Math.abs(c)) >= x ? c - x + l : 0); - var z = (new Array(c + 1)).join("0"); - var r = n + f; - return (s || "") + (l ? r = z + r : r += z).substr(0, i += l ? z.length : 0) + (i < r.length ? "." + r.substr(i) : ""); - }); - } - - s = s.toString(); - if (typeof base === "undefined" || +base === 10) { - s = expandExponential(s); - } - - var prefixRE; - if (typeof base === "undefined") { - prefixRE = '0[xcb]'; - } - else if (base == 16) { - prefixRE = '0x'; - } - else if (base == 8) { - prefixRE = '0c'; - } - else if (base == 2) { - prefixRE = '0b'; - } - else { - prefixRE = ''; - } - var parts = new RegExp('^([+\\-]?)(' + prefixRE + ')?([0-9a-z]*)(?:\\.\\d*)?$', 'i').exec(s); - if (parts) { - var sign = parts[1] || "+"; - var baseSection = parts[2] || ""; - var digits = parts[3] || ""; - - if (typeof base === "undefined") { - // Guess base - if (baseSection === "0x" || baseSection === "0X") { // Hex - base = 16; - } - else if (baseSection === "0c" || baseSection === "0C") { // Octal - base = 8; - } - else if (baseSection === "0b" || baseSection === "0B") { // Binary - base = 2; - } - else { - base = 10; - } - } - else if (base < 2 || base > 36) { - throw new Error("Illegal radix " + base + "."); - } - - base = +base; - - // Check for digits outside the range - if (!(BigInteger.radixRegex[base].test(digits))) { - throw new Error("Bad digit for radix " + base); - } - - // Strip leading zeros, and convert to array - digits = digits.replace(/^0+/, "").split(""); - if (digits.length === 0) { - return ZERO; - } - - // Get the sign (we know it's not zero) - sign = (sign === "-") ? -1 : 1; - - // Optimize 10 - if (base == 10) { - var d = []; - while (digits.length >= BigInteger_base_log10) { - d.push(parseInt(digits.splice(digits.length-BigInteger.base_log10, BigInteger.base_log10).join(''), 10)); - } - d.push(parseInt(digits.join(''), 10)); - return new BigInteger(d, sign, CONSTRUCT); - } - - // Do the conversion - var d = ZERO; - base = BigInteger.small[base]; - var small = BigInteger.small; - for (var i = 0; i < digits.length; i++) { - d = d.multiply(base).add(small[parseInt(digits[i], 36)]); - } - return new BigInteger(d._d, sign, CONSTRUCT); - } - else { - throw new Error("Invalid BigInteger format: " + s); - } -}; - -/* - Function: add - Add two . - - Parameters: - - n - The number to add to *this*. Will be converted to a . - - Returns: - - The numbers added together. - - See Also: - - , , , -*/ -BigInteger.prototype.add = function(n) { - if (this._s === 0) { - return BigInteger(n); - } - - n = BigInteger(n); - if (n._s === 0) { - return this; - } - if (this._s !== n._s) { - n = n.negate(); - return this.subtract(n); - } - - var a = this._d; - var b = n._d; - var al = a.length; - var bl = b.length; - var sum = new Array(Math.max(al, bl) + 1); - var size = Math.min(al, bl); - var carry = 0; - var digit; - - for (var i = 0; i < size; i++) { - digit = a[i] + b[i] + carry; - sum[i] = digit % BigInteger_base; - carry = (digit / BigInteger_base) | 0; - } - if (bl > al) { - a = b; - al = bl; - } - for (i = size; carry && i < al; i++) { - digit = a[i] + carry; - sum[i] = digit % BigInteger_base; - carry = (digit / BigInteger_base) | 0; - } - if (carry) { - sum[i] = carry; - } - - for ( ; i < al; i++) { - sum[i] = a[i]; - } - - return new BigInteger(sum, this._s, CONSTRUCT); -}; - -/* - Function: negate - Get the additive inverse of a . - - Returns: - - A with the same magnatude, but with the opposite sign. - - See Also: - - -*/ -BigInteger.prototype.negate = function() { - return new BigInteger(this._d, (-this._s) | 0, CONSTRUCT); -}; - -/* - Function: abs - Get the absolute value of a . - - Returns: - - A with the same magnatude, but always positive (or zero). - - See Also: - - -*/ -BigInteger.prototype.abs = function() { - return (this._s < 0) ? this.negate() : this; -}; - -/* - Function: subtract - Subtract two . - - Parameters: - - n - The number to subtract from *this*. Will be converted to a . - - Returns: - - The *n* subtracted from *this*. - - See Also: - - , , , -*/ -BigInteger.prototype.subtract = function(n) { - if (this._s === 0) { - return BigInteger(n).negate(); - } - - n = BigInteger(n); - if (n._s === 0) { - return this; - } - if (this._s !== n._s) { - n = n.negate(); - return this.add(n); - } - - var m = this; - // negative - negative => -|a| - -|b| => -|a| + |b| => |b| - |a| - if (this._s < 0) { - m = new BigInteger(n._d, 1, CONSTRUCT); - n = new BigInteger(this._d, 1, CONSTRUCT); - } - - // Both are positive => a - b - var sign = m.compareAbs(n); - if (sign === 0) { - return ZERO; - } - else if (sign < 0) { - // swap m and n - var t = n; - n = m; - m = t; - } - - // a > b - var a = m._d; - var b = n._d; - var al = a.length; - var bl = b.length; - var diff = new Array(al); // al >= bl since a > b - var borrow = 0; - var i; - var digit; - - for (i = 0; i < bl; i++) { - digit = a[i] - borrow - b[i]; - if (digit < 0) { - digit += BigInteger_base; - borrow = 1; - } - else { - borrow = 0; - } - diff[i] = digit; - } - for (i = bl; i < al; i++) { - digit = a[i] - borrow; - if (digit < 0) { - digit += BigInteger_base; - } - else { - diff[i++] = digit; - break; - } - diff[i] = digit; - } - for ( ; i < al; i++) { - diff[i] = a[i]; - } - - return new BigInteger(diff, sign, CONSTRUCT); -}; - -(function() { - function addOne(n, sign) { - var a = n._d; - var sum = a.slice(); - var carry = true; - var i = 0; - - while (true) { - var digit = (a[i] || 0) + 1; - sum[i] = digit % BigInteger_base; - if (digit <= BigInteger_base - 1) { - break; - } - ++i; - } - - return new BigInteger(sum, sign, CONSTRUCT); - } - - function subtractOne(n, sign) { - var a = n._d; - var sum = a.slice(); - var borrow = true; - var i = 0; - - while (true) { - var digit = (a[i] || 0) - 1; - if (digit < 0) { - sum[i] = digit + BigInteger_base; - } - else { - sum[i] = digit; - break; - } - ++i; - } - - return new BigInteger(sum, sign, CONSTRUCT); - } - - /* - Function: next - Get the next (add one). - - Returns: - - *this* + 1. - - See Also: - - , - */ - BigInteger.prototype.next = function() { - switch (this._s) { - case 0: - return ONE; - case -1: - return subtractOne(this, -1); - // case 1: - default: - return addOne(this, 1); - } - }; - - /* - Function: prev - Get the previous (subtract one). - - Returns: - - *this* - 1. - - See Also: - - , - */ - BigInteger.prototype.prev = function() { - switch (this._s) { - case 0: - return M_ONE; - case -1: - return addOne(this, -1); - // case 1: - default: - return subtractOne(this, 1); - } - }; -})(); - -/* - Function: compareAbs - Compare the absolute value of two . - - Calling is faster than calling twice, then . - - Parameters: - - n - The number to compare to *this*. Will be converted to a . - - Returns: - - -1, 0, or +1 if *|this|* is less than, equal to, or greater than *|n|*. - - See Also: - - , -*/ -BigInteger.prototype.compareAbs = function(n) { - if (this === n) { - return 0; - } - - if (!(n instanceof BigInteger)) { - if (!isFinite(n)) { - return(isNaN(n) ? n : -1); - } - n = BigInteger(n); - } - - if (this._s === 0) { - return (n._s !== 0) ? -1 : 0; - } - if (n._s === 0) { - return 1; - } - - var l = this._d.length; - var nl = n._d.length; - if (l < nl) { - return -1; - } - else if (l > nl) { - return 1; - } - - var a = this._d; - var b = n._d; - for (var i = l-1; i >= 0; i--) { - if (a[i] !== b[i]) { - return a[i] < b[i] ? -1 : 1; - } - } - - return 0; -}; - -/* - Function: compare - Compare two . - - Parameters: - - n - The number to compare to *this*. Will be converted to a . - - Returns: - - -1, 0, or +1 if *this* is less than, equal to, or greater than *n*. - - See Also: - - , , , -*/ -BigInteger.prototype.compare = function(n) { - if (this === n) { - return 0; - } - - n = BigInteger(n); - - if (this._s === 0) { - return -n._s; - } - - if (this._s === n._s) { // both positive or both negative - var cmp = this.compareAbs(n); - return cmp * this._s; - } - else { - return this._s; - } -}; - -/* - Function: isUnit - Return true iff *this* is either 1 or -1. - - Returns: - - true if *this* compares equal to or . - - See Also: - - , , , , , - , -*/ -BigInteger.prototype.isUnit = function() { - return this === ONE || - this === M_ONE || - (this._d.length === 1 && this._d[0] === 1); -}; - -/* - Function: multiply - Multiply two . - - Parameters: - - n - The number to multiply *this* by. Will be converted to a - . - - Returns: - - The numbers multiplied together. - - See Also: - - , , , -*/ -BigInteger.prototype.multiply = function(n) { - // TODO: Consider adding Karatsuba multiplication for large numbers - if (this._s === 0) { - return ZERO; - } - - n = BigInteger(n); - if (n._s === 0) { - return ZERO; - } - if (this.isUnit()) { - if (this._s < 0) { - return n.negate(); - } - return n; - } - if (n.isUnit()) { - if (n._s < 0) { - return this.negate(); - } - return this; - } - if (this === n) { - return this.square(); - } - - var r = (this._d.length >= n._d.length); - var a = (r ? this : n)._d; // a will be longer than b - var b = (r ? n : this)._d; - var al = a.length; - var bl = b.length; - - var pl = al + bl; - var partial = new Array(pl); - var i; - for (i = 0; i < pl; i++) { - partial[i] = 0; - } - - for (i = 0; i < bl; i++) { - var carry = 0; - var bi = b[i]; - var jlimit = al + i; - var digit; - for (var j = i; j < jlimit; j++) { - digit = partial[j] + bi * a[j - i] + carry; - carry = (digit / BigInteger_base) | 0; - partial[j] = (digit % BigInteger_base) | 0; - } - if (carry) { - digit = partial[j] + carry; - carry = (digit / BigInteger_base) | 0; - partial[j] = digit % BigInteger_base; - } - } - return new BigInteger(partial, this._s * n._s, CONSTRUCT); -}; - -// Multiply a BigInteger by a single-digit native number -// Assumes that this and n are >= 0 -// This is not really intended to be used outside the library itself -BigInteger.prototype.multiplySingleDigit = function(n) { - if (n === 0 || this._s === 0) { - return ZERO; - } - if (n === 1) { - return this; - } - - var digit; - if (this._d.length === 1) { - digit = this._d[0] * n; - if (digit >= BigInteger_base) { - return new BigInteger([(digit % BigInteger_base)|0, - (digit / BigInteger_base)|0], 1, CONSTRUCT); - } - return new BigInteger([digit], 1, CONSTRUCT); - } - - if (n === 2) { - return this.add(this); - } - if (this.isUnit()) { - return new BigInteger([n], 1, CONSTRUCT); - } - - var a = this._d; - var al = a.length; - - var pl = al + 1; - var partial = new Array(pl); - for (var i = 0; i < pl; i++) { - partial[i] = 0; - } - - var carry = 0; - for (var j = 0; j < al; j++) { - digit = n * a[j] + carry; - carry = (digit / BigInteger_base) | 0; - partial[j] = (digit % BigInteger_base) | 0; - } - if (carry) { - partial[j] = carry; - } - - return new BigInteger(partial, 1, CONSTRUCT); -}; - -/* - Function: square - Multiply a by itself. - - This is slightly faster than regular multiplication, since it removes the - duplicated multiplcations. - - Returns: - - > this.multiply(this) - - See Also: - -*/ -BigInteger.prototype.square = function() { - // Normally, squaring a 10-digit number would take 100 multiplications. - // Of these 10 are unique diagonals, of the remaining 90 (100-10), 45 are repeated. - // This procedure saves (N*(N-1))/2 multiplications, (e.g., 45 of 100 multiplies). - // Based on code by Gary Darby, Intellitech Systems Inc., www.DelphiForFun.org - - if (this._s === 0) { - return ZERO; - } - if (this.isUnit()) { - return ONE; - } - - var digits = this._d; - var length = digits.length; - var imult1 = new Array(length + length + 1); - var product, carry, k; - var i; - - // Calculate diagonal - for (i = 0; i < length; i++) { - k = i * 2; - product = digits[i] * digits[i]; - carry = (product / BigInteger_base) | 0; - imult1[k] = product % BigInteger_base; - imult1[k + 1] = carry; - } - - // Calculate repeating part - for (i = 0; i < length; i++) { - carry = 0; - k = i * 2 + 1; - for (var j = i + 1; j < length; j++, k++) { - product = digits[j] * digits[i] * 2 + imult1[k] + carry; - carry = (product / BigInteger_base) | 0; - imult1[k] = product % BigInteger_base; - } - k = length + i; - var digit = carry + imult1[k]; - carry = (digit / BigInteger_base) | 0; - imult1[k] = digit % BigInteger_base; - imult1[k + 1] += carry; - } - - return new BigInteger(imult1, 1, CONSTRUCT); -}; - -/* - Function: quotient - Divide two and truncate towards zero. - - throws an exception if *n* is zero. - - Parameters: - - n - The number to divide *this* by. Will be converted to a . - - Returns: - - The *this* / *n*, truncated to an integer. - - See Also: - - , , , , -*/ -BigInteger.prototype.quotient = function(n) { - return this.divRem(n)[0]; -}; - -/* - Function: divide - Deprecated synonym for . -*/ -BigInteger.prototype.divide = BigInteger.prototype.quotient; - -/* - Function: remainder - Calculate the remainder of two . - - throws an exception if *n* is zero. - - Parameters: - - n - The remainder after *this* is divided *this* by *n*. Will be - converted to a . - - Returns: - - *this* % *n*. - - See Also: - - , -*/ -BigInteger.prototype.remainder = function(n) { - return this.divRem(n)[1]; -}; - -/* - Function: divRem - Calculate the integer quotient and remainder of two . - - throws an exception if *n* is zero. - - Parameters: - - n - The number to divide *this* by. Will be converted to a . - - Returns: - - A two-element array containing the quotient and the remainder. - - > a.divRem(b) - - is exactly equivalent to - - > [a.quotient(b), a.remainder(b)] - - except it is faster, because they are calculated at the same time. - - See Also: - - , -*/ -BigInteger.prototype.divRem = function(n) { - n = BigInteger(n); - if (n._s === 0) { - throw new Error("Divide by zero"); - } - if (this._s === 0) { - return [ZERO, ZERO]; - } - if (n._d.length === 1) { - return this.divRemSmall(n._s * n._d[0]); - } - - // Test for easy cases -- |n1| <= |n2| - switch (this.compareAbs(n)) { - case 0: // n1 == n2 - return [this._s === n._s ? ONE : M_ONE, ZERO]; - case -1: // |n1| < |n2| - return [ZERO, this]; - } - - var sign = this._s * n._s; - var a = n.abs(); - var b_digits = this._d; - var b_index = b_digits.length; - var digits = n._d.length; - var quot = []; - var guess; - - var part = new BigInteger([], 0, CONSTRUCT); - - while (b_index) { - part._d.unshift(b_digits[--b_index]); - part = new BigInteger(part._d, 1, CONSTRUCT); - - if (part.compareAbs(n) < 0) { - quot.push(0); - continue; - } - if (part._s === 0) { - guess = 0; - } - else { - var xlen = part._d.length, ylen = a._d.length; - var highx = part._d[xlen-1]*BigInteger_base + part._d[xlen-2]; - var highy = a._d[ylen-1]*BigInteger_base + a._d[ylen-2]; - if (part._d.length > a._d.length) { - // The length of part._d can either match a._d length, - // or exceed it by one. - highx = (highx+1)*BigInteger_base; - } - guess = Math.ceil(highx/highy); - } - do { - var check = a.multiplySingleDigit(guess); - if (check.compareAbs(part) <= 0) { - break; - } - guess--; - } while (guess); - - quot.push(guess); - if (!guess) { - continue; - } - var diff = part.subtract(check); - part._d = diff._d.slice(); - } - - return [new BigInteger(quot.reverse(), sign, CONSTRUCT), - new BigInteger(part._d, this._s, CONSTRUCT)]; -}; - -// Throws an exception if n is outside of (-BigInteger.base, -1] or -// [1, BigInteger.base). It's not necessary to call this, since the -// other division functions will call it if they are able to. -BigInteger.prototype.divRemSmall = function(n) { - var r; - n = +n; - if (n === 0) { - throw new Error("Divide by zero"); - } - - var n_s = n < 0 ? -1 : 1; - var sign = this._s * n_s; - n = Math.abs(n); - - if (n < 1 || n >= BigInteger_base) { - throw new Error("Argument out of range"); - } - - if (this._s === 0) { - return [ZERO, ZERO]; - } - - if (n === 1 || n === -1) { - return [(sign === 1) ? this.abs() : new BigInteger(this._d, sign, CONSTRUCT), ZERO]; - } - - // 2 <= n < BigInteger_base - - // divide a single digit by a single digit - if (this._d.length === 1) { - var q = new BigInteger([(this._d[0] / n) | 0], 1, CONSTRUCT); - r = new BigInteger([(this._d[0] % n) | 0], 1, CONSTRUCT); - if (sign < 0) { - q = q.negate(); - } - if (this._s < 0) { - r = r.negate(); - } - return [q, r]; - } - - var digits = this._d.slice(); - var quot = new Array(digits.length); - var part = 0; - var diff = 0; - var i = 0; - var guess; - - while (digits.length) { - part = part * BigInteger_base + digits[digits.length - 1]; - if (part < n) { - quot[i++] = 0; - digits.pop(); - diff = BigInteger_base * diff + part; - continue; - } - if (part === 0) { - guess = 0; - } - else { - guess = (part / n) | 0; - } - - var check = n * guess; - diff = part - check; - quot[i++] = guess; - if (!guess) { - digits.pop(); - continue; - } - - digits.pop(); - part = diff; - } - - r = new BigInteger([diff], 1, CONSTRUCT); - if (this._s < 0) { - r = r.negate(); - } - return [new BigInteger(quot.reverse(), sign, CONSTRUCT), r]; -}; - -/* - Function: isEven - Return true iff *this* is divisible by two. - - Note that is even. - - Returns: - - true if *this* is even, false otherwise. - - See Also: - - -*/ -BigInteger.prototype.isEven = function() { - var digits = this._d; - return this._s === 0 || digits.length === 0 || (digits[0] % 2) === 0; -}; - -/* - Function: isOdd - Return true iff *this* is not divisible by two. - - Returns: - - true if *this* is odd, false otherwise. - - See Also: - - -*/ -BigInteger.prototype.isOdd = function() { - return !this.isEven(); -}; - -/* - Function: sign - Get the sign of a . - - Returns: - - * -1 if *this* < 0 - * 0 if *this* == 0 - * +1 if *this* > 0 - - See Also: - - , , , , -*/ -BigInteger.prototype.sign = function() { - return this._s; -}; - -/* - Function: isPositive - Return true iff *this* > 0. - - Returns: - - true if *this*.compare() == 1. - - See Also: - - , , , , , -*/ -BigInteger.prototype.isPositive = function() { - return this._s > 0; -}; - -/* - Function: isNegative - Return true iff *this* < 0. - - Returns: - - true if *this*.compare() == -1. - - See Also: - - , , , , , -*/ -BigInteger.prototype.isNegative = function() { - return this._s < 0; -}; - -/* - Function: isZero - Return true iff *this* == 0. - - Returns: - - true if *this*.compare() == 0. - - See Also: - - , , , , -*/ -BigInteger.prototype.isZero = function() { - return this._s === 0; -}; - -/* - Function: exp10 - Multiply a by a power of 10. - - This is equivalent to, but faster than - - > if (n >= 0) { - > return this.multiply(BigInteger("1e" + n)); - > } - > else { // n <= 0 - > return this.quotient(BigInteger("1e" + -n)); - > } - - Parameters: - - n - The power of 10 to multiply *this* by. *n* is converted to a - javascipt number and must be no greater than - (0x7FFFFFFF), or an exception will be thrown. - - Returns: - - *this* * (10 ** *n*), truncated to an integer if necessary. - - See Also: - - , -*/ -BigInteger.prototype.exp10 = function(n) { - n = +n; - if (n === 0) { - return this; - } - if (Math.abs(n) > Number(MAX_EXP)) { - throw new Error("exponent too large in BigInteger.exp10"); - } - // Optimization for this == 0. This also keeps us from having to trim zeros in the positive n case - if (this._s === 0) { - return ZERO; - } - if (n > 0) { - var k = new BigInteger(this._d.slice(), this._s, CONSTRUCT); - - for (; n >= BigInteger_base_log10; n -= BigInteger_base_log10) { - k._d.unshift(0); - } - if (n == 0) - return k; - k._s = 1; - k = k.multiplySingleDigit(Math.pow(10, n)); - return (this._s < 0 ? k.negate() : k); - } else if (-n >= this._d.length*BigInteger_base_log10) { - return ZERO; - } else { - var k = new BigInteger(this._d.slice(), this._s, CONSTRUCT); - - for (n = -n; n >= BigInteger_base_log10; n -= BigInteger_base_log10) { - k._d.shift(); - } - return (n == 0) ? k : k.divRemSmall(Math.pow(10, n))[0]; - } -}; - -/* - Function: pow - Raise a to a power. - - In this implementation, 0**0 is 1. - - Parameters: - - n - The exponent to raise *this* by. *n* must be no greater than - (0x7FFFFFFF), or an exception will be thrown. - - Returns: - - *this* raised to the *nth* power. - - See Also: - - -*/ -BigInteger.prototype.pow = function(n) { - if (this.isUnit()) { - if (this._s > 0) { - return this; - } - else { - return BigInteger(n).isOdd() ? this : this.negate(); - } - } - - n = BigInteger(n); - if (n._s === 0) { - return ONE; - } - else if (n._s < 0) { - if (this._s === 0) { - throw new Error("Divide by zero"); - } - else { - return ZERO; - } - } - if (this._s === 0) { - return ZERO; - } - if (n.isUnit()) { - return this; - } - - if (n.compareAbs(MAX_EXP) > 0) { - throw new Error("exponent too large in BigInteger.pow"); - } - var x = this; - var aux = ONE; - var two = BigInteger.small[2]; - - while (n.isPositive()) { - if (n.isOdd()) { - aux = aux.multiply(x); - if (n.isUnit()) { - return aux; - } - } - x = x.square(); - n = n.quotient(two); - } - - return aux; -}; - -/* - Function: modPow - Raise a to a power (mod m). - - Because it is reduced by a modulus, is not limited by - like . - - Parameters: - - exponent - The exponent to raise *this* by. Must be positive. - modulus - The modulus. - - Returns: - - *this* ^ *exponent* (mod *modulus*). - - See Also: - - , -*/ -BigInteger.prototype.modPow = function(exponent, modulus) { - var result = ONE; - var base = this; - - while (exponent.isPositive()) { - if (exponent.isOdd()) { - result = result.multiply(base).remainder(modulus); - } - - exponent = exponent.quotient(BigInteger.small[2]); - if (exponent.isPositive()) { - base = base.square().remainder(modulus); - } - } - - return result; -}; - -/* - Function: log - Get the natural logarithm of a as a native JavaScript number. - - This is equivalent to - - > Math.log(this.toJSValue()) - - but handles values outside of the native number range. - - Returns: - - log( *this* ) - - See Also: - - -*/ -BigInteger.prototype.log = function() { - switch (this._s) { - case 0: return -Infinity; - case -1: return NaN; - default: // Fall through. - } - - var l = this._d.length; - - if (l*BigInteger_base_log10 < 30) { - return Math.log(this.valueOf()); - } - - var N = Math.ceil(30/BigInteger_base_log10); - var firstNdigits = this._d.slice(l - N); - return Math.log((new BigInteger(firstNdigits, 1, CONSTRUCT)).valueOf()) + (l - N) * Math.log(BigInteger_base); -}; - -/* - Function: valueOf - Convert a to a native JavaScript integer. - - This is called automatically by JavaScipt to convert a to a - native value. - - Returns: - - > parseInt(this.toString(), 10) - - See Also: - - , -*/ -BigInteger.prototype.valueOf = function() { - return parseInt(this.toString(), 10); -}; - -/* - Function: toJSValue - Convert a to a native JavaScript integer. - - This is the same as valueOf, but more explicitly named. - - Returns: - - > parseInt(this.toString(), 10) - - See Also: - - , -*/ -BigInteger.prototype.toJSValue = function() { - return parseInt(this.toString(), 10); -}; - -/* - Function: lowVal - Author: Lucas Jones -*/ -BigInteger.prototype.lowVal = function () { - return this._d[0] || 0; -}; - -var MAX_EXP = BigInteger(0x7FFFFFFF); -// Constant: MAX_EXP -// The largest exponent allowed in and (0x7FFFFFFF or 2147483647). -BigInteger.MAX_EXP = MAX_EXP; - -(function() { - function makeUnary(fn) { - return function(a) { - return fn.call(BigInteger(a)); - }; - } - - function makeBinary(fn) { - return function(a, b) { - return fn.call(BigInteger(a), BigInteger(b)); - }; - } - - function makeTrinary(fn) { - return function(a, b, c) { - return fn.call(BigInteger(a), BigInteger(b), BigInteger(c)); - }; - } - - (function() { - var i, fn; - var unary = "toJSValue,isEven,isOdd,sign,isZero,isNegative,abs,isUnit,square,negate,isPositive,toString,next,prev,log".split(","); - var binary = "compare,remainder,divRem,subtract,add,quotient,divide,multiply,pow,compareAbs".split(","); - var trinary = ["modPow"]; - - for (i = 0; i < unary.length; i++) { - fn = unary[i]; - BigInteger[fn] = makeUnary(BigInteger.prototype[fn]); - } - - for (i = 0; i < binary.length; i++) { - fn = binary[i]; - BigInteger[fn] = makeBinary(BigInteger.prototype[fn]); - } - - for (i = 0; i < trinary.length; i++) { - fn = trinary[i]; - BigInteger[fn] = makeTrinary(BigInteger.prototype[fn]); - } - - BigInteger.exp10 = function(x, n) { - return BigInteger(x).exp10(n); - }; - })(); -})(); - -exports.BigInteger = BigInteger; -})(typeof exports !== 'undefined' ? exports : this); \ No newline at end of file diff --git a/src/main/js/daemon/MoneroDaemon.js b/src/main/js/daemon/MoneroDaemon.js deleted file mode 100644 index 8333f94b1..000000000 --- a/src/main/js/daemon/MoneroDaemon.js +++ /dev/null @@ -1,711 +0,0 @@ -const MoneroError = require("../common/MoneroError"); - -/** - * Copyright (c) woodser - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * Monero daemon interface and default implementations. - * - * @interface - */ -class MoneroDaemon { - - /** - * Register a listener to receive daemon notifications. - * - * @param {MoneroDaemonListener} listener - listener to receive daemon notifications - */ - async addListener(listener) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Unregister a listener to receive daemon notifications. - * - * @param {MoneroDaemonListener} listener - listener to unregister - */ - async removeListener(listener) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get the listeners registered with the daemon. - * - * @return {MoneroDaemonListener[]} the registered listeners - */ - getListeners() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Indicates if the client is connected to the daemon via RPC. - * - * @return {boolean} true if the client is connected to the daemon, false otherwise - */ - async isConnected() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Gets the version of the daemon. - * - * @return {MoneroVersion} the version of the daemon - */ - async getVersion() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Indicates if the daemon is trusted xor untrusted. - * - * @return {boolean} true if the daemon is trusted, false otherwise - */ - async isTrusted() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get the number of blocks in the longest chain known to the node. - * - * @return {int} the number of blocks - */ - async getHeight() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get a block's hash by its height. - * - * @param {int} height - height of the block hash to get - * @return {string} the block's hash at the given height - */ - async getBlockHash(height) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get a block template for mining a new block. - * - * @param {string} walletAddress - address of the wallet to receive miner transactions if block is successfully mined - * @param {int} reserveSize - reserve size (optional) - * @return {MoneroBlockTemplate} is a block template for mining a new block - */ - async getBlockTemplate(walletAddress, reserveSize) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get the last block's header. - * - * @return {MoneroBlockHeader} last block's header - */ - async getLastBlockHeader() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get a block header by its hash. - * - * @param {string} blockHash - hash of the block to get the header of - * @return {MoneroBlockHeader} block's header - */ - async getBlockHeaderByHash(blockHash) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get a block header by its height. - * - * @param {int} height - height of the block to get the header of - * @return {MoneroBlockHeader} block's header - */ - async getBlockHeaderByHeight(height) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get block headers for the given range. - * - * @param {int} startHeight - start height lower bound inclusive (optional) - * @param {int} endHeight - end height upper bound inclusive (optional) - * @return {MoneroBlockHeader[]} for the given range - */ - async getBlockHeadersByRange(startHeight, endHeight) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get a block by hash. - * - * @param {string} blockHash - hash of the block to get - * @return {MoneroBlock} with the given hash - */ - async getBlockByHash(blockHash) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get blocks by hash. - * - * @param {string[]} blockHashes - array of hashes; first 10 blocks hashes goes sequential, - * next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, - * and the last one is always genesis block - * @param {int} startHeight - start height to get blocks by hash - * @param {boolean} prune - specifies if returned blocks should be pruned (defaults to false) // TODO: test default - * @return {MoneroBlock[]} retrieved blocks - */ - async getBlocksByHash(blockHashes, startHeight, prune) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get a block by height. - * - * @param {int} height - height of the block to get - * @return {MoneroBlock} with the given height - */ - async getBlockByHeight(height) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get blocks at the given heights. - * - * @param {int[]} heights - heights of the blocks to get - * @return {MoneroBlock[]} are blocks at the given heights - */ - async getBlocksByHeight(heights) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get blocks in the given height range. - * - * @param {int} startHeight - start height lower bound inclusive (optional) - * @param {int} endHeight - end height upper bound inclusive (optional) - * @return {MoneroBlock[]} are blocks in the given height range - */ - async getBlocksByRange(startHeight, endHeight) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get blocks in the given height range as chunked requests so that each request is - * not too big. - * - * @param {int} startHeight - start height lower bound inclusive (optional) - * @param {int} endHeight - end height upper bound inclusive (optional) - * @param {int} maxChunkSize - maximum chunk size in any one request (default 3,000,000 bytes) - * @return {MoneroBlock[]} blocks in the given height range - */ - async getBlocksByRangeChunked(startHeight, endHeight, maxChunkSize) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get block hashes as a binary request to the daemon. - * - * @param {string[]} blockHashes - specify block hashes to fetch; first 10 blocks hash goes - * sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 - * and so on, and the last one is always genesis block - * @param {int} startHeight - starting height of block hashes to return - * @return {string[]} requested block hashes - */ - async getBlockHashes(blockHashes, startHeight) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get a transaction by hash. - * - * @param {string} txHash - hash of the transaction to get - * @param {boolean} prune - specifies if the returned tx should be pruned (defaults to false) - * @return {MoneroTx} transaction with the given hash or undefined if not found - */ - async getTx(txHash, prune = false) { - return (await this.getTxs([txHash], prune))[0]; - } - - /** - * Get transactions by hashes. - * - * @param {string[]} txHashes - hashes of transactions to get - * @param {boolean} prune - specifies if the returned txs should be pruned (defaults to false) - * @return {MoneroTx[]} found transactions with the given hashes - */ - async getTxs(txHashes, prune = false) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get a transaction hex by hash. - * - * @param {string} txHash - hash of the transaction to get hex from - * @param {boolean} prune - specifies if the returned tx hex should be pruned (defaults to false) - * @return {string} tx hex with the given hash - */ - async getTxHex(txHash, prune = false) { - return (await this.getTxHexes([txHash], prune))[0]; - } - - /** - * Get transaction hexes by hashes. - * - * @param {string[]} txHashes - hashes of transactions to get hexes from - * @param {boolean} prune - specifies if the returned tx hexes should be pruned (defaults to false) - * @return {string[]} tx hexes - */ - async getTxHexes(txHashes, prune = false) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Gets the total emissions and fees from the genesis block to the current height. - * - * @param {int} height - height to start computing the miner sum - * @param {int} numBlocks - number of blocks to include in the sum - * @return {MoneroMinerTxSum} encapsulates the total emissions and fees since the genesis block - */ - async getMinerTxSum(height, numBlocks) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get mining fee estimates per kB. - * - * @param {number} graceBlocks TODO - * @return {MoneroFeeEstimate} mining fee estimates per kB - */ - async getFeeEstimate(graceBlocks) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Submits a transaction to the daemon's pool. - * - * @param {string} txHex - raw transaction hex to submit - * @param {boolean} doNotRelay specifies if the tx should be relayed (optional) - * @return {MoneroSubmitTxResult} contains submission results - */ - async submitTxHex(txHex, doNotRelay) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Relays a transaction by hash. - * - * @param {string} txHash - hash of the transaction to relay - */ - async relayTxByHash(txHash) { - const assert = require("assert"); - assert.equal(typeof txHash, "string", "Must provide a transaction hash"); - await this.relayTxsByHash([txHash]); - } - - /** - * Relays transactions by hash. - * - * @param {string[]} txHashes - hashes of the transactinos to relay - */ - async relayTxsByHash(txHashes) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get valid transactions seen by the node but not yet mined into a block, as well - * as spent key image information for the tx pool. - * - * @return {MoneroTx[]} are transactions in the transaction pool - */ - async getTxPool() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get hashes of transactions in the transaction pool. - * - * @return {string[]} are hashes of transactions in the transaction pool - */ - async getTxPoolHashes() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get all transaction pool backlog. - * - * @return {MoneroTxBacklogEntry[]} backlog entries - */ - async getTxPoolBacklog() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get transaction pool statistics. - * - * @return {MoneroTxPoolStats} contains statistics about the transaction pool - */ - async getTxPoolStats() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Flush transactions from the tx pool. - * - * @param {(string|string[])} hashes - specific transactions to flush (defaults to all) - */ - async flushTxPool(hashes) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get the spent status of the given key image. - * - * @param {string} keyImage - key image hex to get the status of - * @return {MoneroKeyImageSpentStatus} status of the key image - */ - async getKeyImageSpentStatus(keyImage) { - return (await this.getKeyImageSpentStatuses([keyImage]))[0]; - } - - /** - * Get the spent status of each given key image. - * - * @param {string[]} keyImages are hex key images to get the statuses of - * @return {MoneroKeyImageSpentStatus[]} status for each key image - */ - async getKeyImageSpentStatuses(keyImages) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get outputs identified by a list of output amounts and indices as a binary - * request. - * - * @param {MoneroOutput[]} outputs - identify each output by amount and index - * @return {MoneroOutput[]} identified outputs - */ - async getOutputs(outputs) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get a histogram of output amounts. For all amounts (possibly filtered by - * parameters), gives the number of outputs on the chain for that amount. - * RingCT outputs counts as 0 amount. - * - * @param {BigInteger[]} amounts - amounts of outputs to make the histogram with - * @param {int} minCount - TODO - * @param {int} maxCount - TODO - * @param {boolean} isUnlocked - makes a histogram with outputs with the specified lock state - * @param {int} recentCutoff - TODO - * @return {MoneroOutputHistogramEntry[]} are entries meeting the parameters - */ - async getOutputHistogram(amounts, minCount, maxCount, isUnlocked, recentCutoff) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Creates an output distribution. - * - * @param {BigInteger[]} amounts - amounts of outputs to make the distribution with - * @param {boolean} cumulative - specifies if the results should be cumulative (defaults to TODO) - * @param {int} startHeight - start height lower bound inclusive (optional) - * @param {int} endHeight - end height upper bound inclusive (optional) - * @return {MoneroOutputDistributionEntry[]} are entries meeting the parameters - */ - async getOutputDistribution(amounts, cumulative, startHeight, endHeight) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get general information about the state of the node and the network. - * - * @return {MoneroDaemonInfo} is general information about the node and network - */ - async getInfo() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get synchronization information. - * - * @return {MoneroDaemonSyncInfo} contains sync information - */ - async getSyncInfo() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Look up information regarding hard fork voting and readiness. - * - * @return {MoneroHardForkInfo} contains hard fork information - */ - async getHardForkInfo() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get alternative chains seen by the node. - * - * @return {MoneroAltChain[]} alternative chains - */ - async getAltChains() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get known block hashes which are not on the main chain. - * - * @return {string[]} known block hashes which are not on the main chain - */ - async getAltBlockHashes() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get the download bandwidth limit. - * - * @return {int} download bandwidth limit - */ - async getDownloadLimit() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Set the download bandwidth limit. - * - * @param {int} limit - download limit to set (-1 to reset to default) - * @return {int} new download limit after setting - */ - async setDownloadLimit(limit) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Reset the download bandwidth limit. - * - * @return {int} download bandwidth limit after resetting - */ - async resetDownloadLimit() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get the upload bandwidth limit. - * - * @return {int} upload bandwidth limit - */ - async getUploadLimit() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Set the upload bandwidth limit. - * - * @param limit - upload limit to set (-1 to reset to default) - * @return {int} new upload limit after setting - */ - async setUploadLimit(limit) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Reset the upload bandwidth limit. - * - * @return {int} upload bandwidth limit after resetting - */ - async resetUploadLimit() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get peers with active incoming or outgoing connections to the node. - * - * @return {MoneroPeer[]} the daemon's peers - */ - async getPeers() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get known peers including their last known online status. - * - * @return {MoneroPeer[]} the daemon's known peers - */ - async getKnownPeers() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Limit number of outgoing peers. - * - * @param {int} limit - maximum number of outgoing peers - */ - async setOutgoingPeerLimit(limit) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Limit number of incoming peers. - * - * @param {int} limit - maximum number of incoming peers - */ - async setIncomingPeerLimit(limit) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get peer bans. - * - * @return {MoneroBan[]} entries about banned peers - */ - async getPeerBans() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Ban a peer node. - * - * @param {MoneroBan} ban - contains information about a node to ban - */ - async setPeerBan(ban) { - return await this.setPeerBans([ban]); - } - - /** - * Ban peers nodes. - * - * @param {MoneroBan[]} bans - specify which peers to ban - */ - async setPeerBans(bans) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Start mining. - * - * @param {string} address - address given miner rewards if the daemon mines a block - * @param {integer} numThreads - number of mining threads to run - * @param {boolean} isBackground - specifies if the miner should run in the background or not - * @param {boolean} ignoreBattery - specifies if the battery state (e.g. on laptop) should be ignored or not - */ - async startMining(address, numThreads, isBackground, ignoreBattery) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Stop mining. - */ - async stopMining() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get the daemon's mining status. - * - * @return {MoneroMiningStatus} daemon's mining status - */ - async getMiningStatus() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Submit a mined block to the network. - * - * @param {string} blockBlob - mined block to submit - */ - async submitBlock(blockBlob) { - await this.submitBlocks([blockBlob]); - } - - /** - * Prune the blockchain. - * - * @param {boolean} check specifies to check the pruning (default false) - * @return {MoneroPruneResult} the prune result - */ - async pruneBlockchain(check) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Submit mined blocks to the network. - * - * @param {string[]} blockBlobs - mined blocks to submit - */ - async submitBlocks(blockBlobs) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Check for update. - * - * @return {MoneroDaemonUpdateCheckResult} the result - */ - async checkForUpdate() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Download an update. - * - * @param {string} path - path to download the update (optional) - * @return {MoneroDaemonUpdateDownloadResult} the result - */ - async downloadUpdate(path) { - throw new MoneroError("Subclass must implement"); - } - - /** - * Safely disconnect and shut down the daemon. - */ - async stop() { - throw new MoneroError("Subclass must implement"); - } - - /** - * Get the header of the next block added to the chain. - * - * @return {MoneroBlockHeader} header of the next block added to the chain - */ - async waitForNextBlockHeader() { - throw new MoneroError("Subclass must implement"); - } - - // ----------------------------- STATIC UTILITIES --------------------------- - - /** - * Parses a network string to an enumerated type. - * - * @param {string} network - network string to parse - * @return {MoneroNetworkType} enumerated network type - */ - static parseNetworkType(network) { - const MoneroNetworkType = require("./model/MoneroNetworkType"); - if (network === "mainnet") return MoneroNetworkType.MAINNET; - if (network === "testnet") return MoneroNetworkType.TESTNET; - if (network === "stagenet") return MoneroNetworkType.STAGENET; - throw new MoneroError("Invalid network type to parse: " + network); - } -} - -module.exports = MoneroDaemon; \ No newline at end of file diff --git a/src/main/js/daemon/model/ConnectionType.js b/src/main/js/daemon/model/ConnectionType.js deleted file mode 100644 index 9d0d83689..000000000 --- a/src/main/js/daemon/model/ConnectionType.js +++ /dev/null @@ -1,52 +0,0 @@ -const assert = require("assert"); - -/** - * Enumerates connection types. - * - * Based on enums.h in monero-project. - * - * @hideconstructor - */ -class ConnectionType { - - /** - * Asserts that the given connection type is valid. - */ - static validate(type) { - assert(type === 0 || type === 1 || type === 2 || type === 3, "Connection type is invalid: " + type); - } - - /** - * Indicates if the given connection type is valid or not. - */ - static isValid(type) { - return type === 0 || type === 1 || type === 2 || 3; - } -} - -/** - * Invalid connection type (value=0). - */ -ConnectionType.INVALID = 0; - -/** - * IPV4 connection type (value=1). - */ -ConnectionType.IPV4 = 1; - -/** - * IPV6 connection type (value=2). - */ -ConnectionType.IPV6 = 2; - -/** - * TOR connection type (value=3). - */ -ConnectionType.TOR = 3; - -/** - * I2P connection type (value=4). - */ -ConnectionType.I2P = 4; - -module.exports = ConnectionType; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroAltChain.js b/src/main/js/daemon/model/MoneroAltChain.js deleted file mode 100644 index 5ca7b6058..000000000 --- a/src/main/js/daemon/model/MoneroAltChain.js +++ /dev/null @@ -1,66 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Models an alternative chain seen by the node. - */ -class MoneroAltChain { - - constructor(state) { - state = Object.assign({}, state); - if (state.difficulty !== undefined && !(state.difficulty instanceof BigInteger)) state.difficulty = BigInteger.parse(state.difficulty); - this.state = state; - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getDifficulty()) json.difficulty = this.getDifficulty().toString(); - return json; - } - - getBlockHashes(blockHashes) { - return this.state.blockHashes; - } - - setBlockHashes(blockHashes) { - this.state.blockHashes = blockHashes; - return this; - } - - getDifficulty() { - return this.state.difficulty; - } - - setDifficulty(difficulty) { - this.state.difficulty = difficulty; - return this; - } - - getHeight() { - return this.state.height; - } - - setHeight(height) { - this.state.height = height; - return this; - } - - getLength() { - return this.state.length; - } - - setLength(length) { - this.state.length = length; - return this; - } - - getMainChainParentBlockHash() { - return this.state.mainChainParentBlockHash; - } - - setMainChainParentBlockHash(mainChainParentBlockHash) { - this.state.mainChainParentBlockHash = mainChainParentBlockHash; - return this; - } -} - -module.exports = MoneroAltChain; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroBan.js b/src/main/js/daemon/model/MoneroBan.js deleted file mode 100644 index b71152bf7..000000000 --- a/src/main/js/daemon/model/MoneroBan.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Monero banhammer. - */ -class MoneroBan { - - constructor(state) { - this.state = Object.assign({}, state); - } - - toJson() { - return Object.assign({}, this.state); - } - - getHost() { - return this.state.host; - } - - setHost(host) { - this.state.host = host; - return this; - } - - getIp() { - return this.state.ip; - } - - setIp(ip) { - this.state.ip = ip; - return this; - } - - isBanned() { - return this.state.isBanned; - } - - setIsBanned(isBanned) { - this.state.isBanned = isBanned; - return this; - } - - getSeconds() { - return this.state.seconds; - } - - setSeconds(seconds) { - this.state.seconds = seconds; - return this; - } -} - -module.exports = MoneroBan; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroBlock.js b/src/main/js/daemon/model/MoneroBlock.js deleted file mode 100644 index 3b7c94ae5..000000000 --- a/src/main/js/daemon/model/MoneroBlock.js +++ /dev/null @@ -1,158 +0,0 @@ -const assert = require("assert"); -const GenUtils = require("../../common/GenUtils"); -const MoneroBlockHeader = require("./MoneroBlockHeader"); -const MoneroTx = require("./MoneroTx"); -const MoneroTxQuery = require("../../wallet/model/MoneroTxQuery"); -const MoneroTxWallet = require("../../wallet/model/MoneroTxWallet"); - -/** - * Models a Monero block in the blockchain. - * - * @extends {MoneroBlockHeader} - */ -class MoneroBlock extends MoneroBlockHeader { - - /** - * Construct the model. - * - * @param {MoneroBlock|MoneroBlockHeader|object} state is existing state to initialize from (optional) - * @param {MoneroBlock.DeserializationType} txType informs the tx deserialization type (MoneroTx, MoneroTxWallet, MoneroTxQuery) - */ - constructor(state, txType) { - super(state); - state = this.state; - - // deserialize miner tx - if (state.minerTx && !(state.minerTx instanceof MoneroTx)) state.minerTx = new MoneroTx(state.minerTx).setBlock(this); - - // deserialize non-miner txs - if (state.txs) { - for (let i = 0; i < state.txs.length; i++) { - if (txType === MoneroBlock.DeserializationType.TX || txType === undefined) { - if (!(state.txs[i] instanceof MoneroTx)) state.txs[i] = new MoneroTx(state.txs[i]).setBlock(this); - } else if (txType === MoneroBlock.DeserializationType.TX_WALLET) { - if (!(state.txs[i] instanceof MoneroTxWallet)) state.txs[i] = new MoneroTxWallet(state.txs[i]).setBlock(this); - } else if (txType === MoneroBlock.DeserializationType.TX_QUERY) { - if (!(state.txs[i] instanceof MoneroTxQuery)) state.txs[i] = new MoneroTxQuery(state.txs[i]).setBlock(this); - } else { - throw new Error("Unrecognized tx deserialization type: " + txType); - } - } - } - } - - getHex() { - return this.state.hex; - } - - setHex(hex) { - this.state.hex = hex; - return this; - } - - getMinerTx() { - return this.state.minerTx; - } - - setMinerTx(minerTx) { - this.state.minerTx = minerTx; - return this; - } - - getTxs() { - return this.state.txs; - } - - setTxs(txs) { - this.state.txs = txs; - return this; - } - - getTxHashes() { - return this.state.txHashes; - } - - setTxHashes(txHashes) { - this.state.txHashes = txHashes; - return this; - } - - copy() { - return new MoneroBlock(this); - } - - toJson() { - let json = super.toJson(); - if (this.getMinerTx()) json.minerTx = this.getMinerTx().toJson(); - if (this.getTxs()) { - json.txs = []; - for (let tx of this.getTxs()) json.txs.push(tx.toJson()); - } - return json; - } - - merge(block) { - assert(block instanceof MoneroBlock); - if (this === block) return this; - - // merge header fields - super.merge(block); - - // merge reconcilable block extensions - this.setHex(GenUtils.reconcile(this.getHex(), block.getHex())); - this.setTxHashes(GenUtils.reconcile(this.getTxHashes(), block.getTxHashes())); - - // merge miner tx - if (this.getMinerTx() === undefined) this.setMinerTx(block.getMinerTx()); - if (block.getMinerTx() !== undefined) { - block.getMinerTx().setBlock(this); - this.getMinerTx().merge(block.getMinerTx()); - } - - // merge non-miner txs - if (block.getTxs() !== undefined) { - for (let tx of block.getTxs()) { - tx.setBlock(this); - MoneroBlock._mergeTx(this.getTxs(), tx); - } - } - - return this; - } - - toString(indent = 0) { - let str = super.toString(indent) + "\n"; - str += GenUtils.kvLine("Hex", this.getHex(), indent); - if (this.getTxs()) { - str += GenUtils.kvLine("Txs", "", indent); - for (let tx of this.getTxs()) { - str += tx.toString(indent + 1) + "\n"; - } - } - if (this.getMinerTx()) { - str += GenUtils.kvLine("Miner tx", "", indent); - str += this.getMinerTx().toString(indent + 1) + "\n"; - } - str += GenUtils.kvLine("Txs hashes", this.getTxHashes(), indent); - return str[str.length - 1] === "\n" ? str.slice(0, str.length - 1) : str // strip last newline - } - - // private helper to merge txs - static _mergeTx(txs, tx) { - for (let aTx of txs) { - if (aTx.getHash() === tx.getHash()) { - aTx.merge(tx); - return; - } - } - txs.push(tx); - } -} - -MoneroBlock.DeserializationType = { - TX: 0, - TX_WALLET: 1, - TX_QUERY: 2 -} - -module.exports = MoneroBlock; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroBlockTemplate.js b/src/main/js/daemon/model/MoneroBlockTemplate.js deleted file mode 100644 index 0c89d83cf..000000000 --- a/src/main/js/daemon/model/MoneroBlockTemplate.js +++ /dev/null @@ -1,122 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Monero block template to mine. - */ -class MoneroBlockTemplate { - - constructor(state) { - state = Object.assign({}, state); - this.state = state; - - // deserialize BigIntegers - if (state.expectedReward !== undefined && !(state.expectedReward instanceof BigInteger)) state.expectedReward = BigInteger.parse(state.expectedReward); - if (state.difficulty !== undefined && !(state.difficulty instanceof BigInteger)) state.difficulty = BigInteger.parse(state.difficulty); - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getExpectedReward()) json.expectedReward = this.getExpectedReward().toString(); - if (this.getDifficulty()) json.difficulty = this.getDifficulty().toString(); - return json; - } - - toJson() { - let json = Object.assign({}, this.state); - if (json.expectedReward) json.expectedReward = json.expectedReward.toString(); - if (json.difficulty) json.difficulty = json.difficulty.toString(); - return json; - } - - getBlockTemplateBlob() { - return this.state.blockTemplateBlob; - } - - setBlockTemplateBlob(blockTemplateBlob) { - this.state.blockTemplateBlob = blockTemplateBlob; - return this; - } - - getBlockHashingBlob() { - return this.state.blockHashingBlob; - } - - setBlockHashingBlob(blockHashingBlob) { - this.state.blockHashingBlob = blockHashingBlob; - return this; - } - - getDifficulty() { - return this.state.difficulty; - } - - setDifficulty(difficulty) { - this.state.difficulty = difficulty; - return this; - } - - getExpectedReward() { - return this.state.expectedReward; - } - - setExpectedReward(expectedReward) { - this.state.expectedReward = expectedReward; - return this; - } - - getHeight() { - return this.state.height; - } - - setHeight(height) { - this.state.height = height; - return this; - } - - getPrevHash() { - return this.state.prevId; - } - - setPrevHash(prevId) { - this.state.prevId = prevId; - return this; - } - - getReservedOffset() { - return this.state.reservedOffset; - } - - setReservedOffset(reservedOffset) { - this.state.reservedOffset = reservedOffset; - return this; - } - - getSeedHeight() { - return this.state.height; - } - - setSeedHeight(seedHeight) { - this.state.seedHeight = seedHeight; - return this; - } - - getSeedHash() { - return this.state.seedHash; - } - - setSeedHash(seedHash) { - this.state.seedHash = seedHash; - return this; - } - - getNextSeedHash() { - return this.state.nextSeedHash - } - - setNextSeedHash(nextSeedHash) { - this.state.nextSeedHash = nextSeedHash; - return this; - } -} - -module.exports = MoneroBlockTemplate; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroConnectionSpan.js b/src/main/js/daemon/model/MoneroConnectionSpan.js deleted file mode 100644 index 627feed26..000000000 --- a/src/main/js/daemon/model/MoneroConnectionSpan.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Monero daemon connection span. - */ -class MoneroConnectionSpan { - - constructor(state) { - this.state = Object.assign({}, state); - } - - toJson() { - return Object.assign({}, this.state); - } - - getConnectionId() { - return this.state.connectionId; - } - - setConnectionId(connectionId) { - this.state.connectionId = connectionId; - return this; - } - - getNumBlocks() { - return this.state.numBlocks; - } - - setNumBlocks(numBlocks) { - this.state.numBlocks = numBlocks; - return this; - } - - getRemoteAddress() { - return this.state.remoteAddress; - } - - setRemoteAddress(remoteAddress) { - this.state.remoteAddress = remoteAddress; - return this; - } - - getRate() { - return this.state.rate; - } - - setRate(rate) { - this.state.rate = rate; - return this; - } - - getSpeed() { - return this.state.speed; - } - - setSpeed(speed) { - this.state.speed = speed; - return this; - } - - getSize() { - return this.state.size; - } - - setSize(size) { - this.state.size = size; - return this; - } - - getStartHeight() { - return this.state.startHeight; - } - - setStartHeight(startHeight) { - this.state.startHeight = startHeight; - return this; - } -} - -module.exports = MoneroConnectionSpan; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroDaemonInfo.js b/src/main/js/daemon/model/MoneroDaemonInfo.js deleted file mode 100644 index 2b47de5ef..000000000 --- a/src/main/js/daemon/model/MoneroDaemonInfo.js +++ /dev/null @@ -1,324 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Monero daemon info. - */ -class MoneroDaemonInfo { - - constructor(state) { - state = Object.assign({}, state); - this.state = state; - - // deserialize BigIntegers - if (state.difficulty !== undefined && !(state.difficulty instanceof BigInteger)) state.difficulty = BigInteger.parse(state.difficulty); - if (state.cumulativeDifficulty !== undefined && !(state.cumulativeDifficulty instanceof BigInteger)) state.cumulativeDifficulty = BigInteger.parse(state.cumulativeDifficulty); - if (state.credits !== undefined && !(state.credits instanceof BigInteger)) state.credits = BigInteger.parse(state.credits); - } - - toJson() { - let json = Object.assign([], this.state); - if (json.difficulty) json.difficulty = json.difficulty.toString(); - if (json.cumulativeDifficulty) json.cumulativeDifficulty = json.cumulativeDifficulty.toString(); - if (json.credits) json.credits = json.credits.toString(); - return json; - } - - getVersion() { - return this.state.version; - } - - setVersion(version) { - this.state.version = version; - return this; - } - - getNumAltBlocks() { - return this.state.numAltBlocks; - } - - setNumAltBlocks(numAltBlocks) { - this.state.numAltBlocks = numAltBlocks; - return this; - } - - getBlockSizeLimit() { - return this.state.blockSizeLimit; - } - - setBlockSizeLimit(blockSizeLimit) { - this.state.blockSizeLimit = blockSizeLimit; - return this; - } - - getBlockSizeMedian() { - return this.state.blockSizeMedian; - } - - setBlockSizeMedian(blockSizeMedian) { - this.state.blockSizeMedian = blockSizeMedian; - return this; - } - - getBlockWeightLimit() { - return this.state.blockWeightLimit; - } - - setBlockWeightLimit(blockWeightLimit) { - this.state.blockWeightLimit = blockWeightLimit; - return this; - } - - getBlockWeightMedian() { - return this.state.blockWeightMedian; - } - - setBlockWeightMedian(blockWeightMedian) { - this.state.blockWeightMedian = blockWeightMedian; - return this; - } - - getBootstrapDaemonAddress() { - return this.state.bootstrapDaemonAddress; - } - - setBootstrapDaemonAddress(bootstrapDaemonAddress) { - this.state.bootstrapDaemonAddress = bootstrapDaemonAddress; - return this; - } - - getDifficulty() { - return this.state.difficulty; - } - - setDifficulty(difficulty) { - this.state.difficulty = difficulty; - return this; - } - - getCumulativeDifficulty() { - return this.state.cumulativeDifficulty; - } - - setCumulativeDifficulty(cumulativeDifficulty) { - this.state.cumulativeDifficulty = cumulativeDifficulty; - return this; - } - - getFreeSpace() { - return this.state.freeSpace; - } - - setFreeSpace(freeSpace) { - this.state.freeSpace = freeSpace; - return this; - } - - getNumOfflinePeers() { - return this.state.numOfflinePeers; - } - - setNumOfflinePeers(numOfflinePeers) { - this.state.numOfflinePeers = numOfflinePeers; - return this; - } - - getNumOnlinePeers() { - return this.state.numOnlinePeers; - } - - setNumOnlinePeers(numOnlinePeers) { - this.state.numOnlinePeers = numOnlinePeers; - return this; - } - - getHeight() { - return this.state.height; - } - - setHeight(height) { - this.state.height = height; - return this; - } - - getHeightWithoutBootstrap() { - return this.state.heightWithoutBootstrap; - } - - setHeightWithoutBootstrap(heightWithoutBootstrap) { - this.state.heightWithoutBootstrap = heightWithoutBootstrap; - return this; - } - - getNetworkType() { - return this.state.networkType; - } - - setNetworkType(networkType) { - this.state.networkType = networkType; - return this; - } - - isOffline() { - return this.state.isOffline; - } - - setIsOffline(isOffline) { - this.state.isOffline = isOffline; - return this; - } - - getNumIncomingConnections() { - return this.state.numIncomingConnections; - } - - setNumIncomingConnections(numIncomingConnections) { - this.state.numIncomingConnections = numIncomingConnections; - return this; - } - - getNumOutgoingConnections() { - return this.state.numOutgoingConnections; - } - - setNumOutgoingConnections(numOutgoingConnections) { - this.state.numOutgoingConnections = numOutgoingConnections; - return this; - } - - getNumRpcConnections() { - return this.state.numRpcConnections; - } - - setNumRpcConnections(numRpcConnections) { - this.state.numRpcConnections = numRpcConnections; - return this; - } - - getStartTimestamp() { - return this.state.startTimestamp; - } - - setStartTimestamp(startTimestamp) { - this.state.startTimestamp = startTimestamp; - return this; - } - - getAdjustedTimestamp() { - return this.state.adjustedTimestamp; - } - - setAdjustedTimestamp(adjustedTimestamp) { - this.state.adjustedTimestamp = adjustedTimestamp; - return this; - } - - getTarget() { - return this.state.target; - } - - setTarget(target) { - this.state.target = target; - return this; - } - - getTargetHeight() { - return this.state.targetHeight; - } - - setTargetHeight(targetHeight) { - this.state.targetHeight = targetHeight; - return this; - } - - getTopBlockHash() { - return this.state.topBlockHash; - } - - setTopBlockHash(topBlockHash) { - this.state.topBlockHash = topBlockHash; - return this; - } - - getNumTxs() { - return this.state.numTxs; - } - - setNumTxs(numTxs) { - this.state.numTxs = numTxs; - return this; - } - - getNumTxsPool() { - return this.state.numTxsPool; - } - - setNumTxsPool(numTxsPool) { - this.state.numTxsPool = numTxsPool; - return this; - } - - getWasBootstrapEverUsed() { - return this.state.wasBootstrapEverUsed; - } - - setWasBootstrapEverUsed(wasBootstrapEverUsed) { - this.state.wasBootstrapEverUsed = wasBootstrapEverUsed; - return this; - } - - getDatabaseSize() { - return this.state.databaseSize; - } - - setDatabaseSize(databaseSize) { - this.state.databaseSize = databaseSize; - return this; - } - - getUpdateAvailable() { - return this.state.updateAvailable; - } - - setUpdateAvailable(updateAvailable) { - this.state.updateAvailable = updateAvailable; - return this; - } - - getCredits() { - return this.state.credits; - } - - setCredits(credits) { - this.state.credits = credits; - return this; - } - - isBusySyncing() { - return this.state.isBusySyncing; - } - - setIsBusySyncing(isBusySyncing) { - this.state.isBusySyncing = isBusySyncing; - return this; - } - - isSynchronized() { - return this.state.isSynchronized; - } - - setIsSynchronized(isSynchronized) { - this.state.isSynchronized = isSynchronized; - return this; - } - - isRestricted() { - return this.state.isRestricted; - } - - setIsRestricted(isRestricted) { - this.state.isRestricted = isRestricted; - return this; - } -} - -module.exports = MoneroDaemonInfo; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroDaemonSyncInfo.js b/src/main/js/daemon/model/MoneroDaemonSyncInfo.js deleted file mode 100644 index d6ac4098a..000000000 --- a/src/main/js/daemon/model/MoneroDaemonSyncInfo.js +++ /dev/null @@ -1,125 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; -const MoneroConnectionSpan = require("./MoneroConnectionSpan"); -const MoneroPeer = require("./MoneroPeer"); - -/** - * Models daemon synchronization information. - */ -class MoneroDaemonSyncInfo { - - constructor(state) { - - // copy state - state = Object.assign({}, state); - - // deserialize if necessary - if (state.peers) { - for (let i = 0; i < state.peers.length; i++) { - if (!(state.peers[i] instanceof MoneroPeer)) { - state.peers[i] = new MoneroPeer(state.peers[i]); - } - } - } - if (state.spans) { - for (let i = 0; i < state.spans.length; i++) { - if (!(state.spans[i] instanceof MoneroConnectionSpan)) { - state.spans[i] = new MoneroConnectionSpan(state.spans[i]); - } - } - } - if (state.credits !== undefined && !(state.credits instanceof BigInteger)) state.credits = BigInteger.parse(state.credits); - - // assign internal state - this.state = state; - } - - toJson() { - let json = Object.assign({}, this.state); - if (json.peers) { - for (let i = 0; i < json.peers.length; i++) { - json.peers[i] = json.peers[i].toJson(); - } - } - if (json.spans) { - for (let i = 0; i < json.spans.length; i++) { - json.spans[i] = json.spans[i].toJson(); - } - } - if (json.credits) json.credits = json.credits.toString(); - return json; - } - - getHeight() { - return this.state.height; - } - - setHeight(height) { - this.state.height = height; - return this; - } - - getPeers() { - return this.state.peers; - } - - setPeers(peers) { - this.state.peers = peers; - return this; - } - - getSpans() { - return this.state.spans; - } - - setSpans(spans) { - this.state.spans = spans; - return this; - } - - getTargetHeight() { - return this.state.targetHeight; - } - - setTargetHeight(targetHeight) { - this.state.targetHeight = targetHeight; - return this; - } - - getNextNeededPruningSeed() { - return this.state.nextNeededPruningSeed; - } - - setNextNeededPruningSeed(nextNeededPruningSeed) { - this.state.nextNeededPruningSeed = nextNeededPruningSeed; - return this; - } - - getOverview() { - return this.state.overview; - } - - setOverview(overview) { - this.state.overview = overview; - return this; - } - - getCredits() { - return this.state.credits; - } - - setCredits(credits) { - this.state.credits = credits; - return this; - } - - getTopBlockHash() { - return this.state.topBlockHash; - } - - setTopBlockHash(topBlockHash) { - this.state.topBlockHash = topBlockHash; - return this; - } -} - -module.exports = MoneroDaemonSyncInfo; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroDaemonUpdateCheckResult.js b/src/main/js/daemon/model/MoneroDaemonUpdateCheckResult.js deleted file mode 100644 index 62cb767c6..000000000 --- a/src/main/js/daemon/model/MoneroDaemonUpdateCheckResult.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Models the result of checking for a daemon update. - */ -class MoneroDaemonUpdateCheckResult { - - /** - * Deep copy constructor. - * - * @param {MoneroDaemonUpdateCheckResult} is an existing result to deep copy from - */ - constructor(result) { - this.state = {}; - if (result !== undefined) { - assert(result instanceof MoneroDaemonUpdateCheckResult); - this.setIsUpdateAvailable(result.isUpdateAvailable()); - this.setVersion(result.getVersion()); - this.setHash(result.getHash()); - this.setAutoUri(result.getAutoUri()); - this.setUserUri(result.getUserUri()); - } - } - - /** - * Indicates if an update is available. - * - * @return {boolean} true if an update is available, false otherwise - */ - isUpdateAvailable() { - return this.state.isUpdateAvailable; - } - - setIsUpdateAvailable(isUpdateAvailable) { - this.state.isUpdateAvailable = isUpdateAvailable; - return this; - } - - /** - * Get the update's version. - * - * @return {string} is the update's version - */ - getVersion() { - return this.state.version; - } - - setVersion(version) { - this.state.version = version; - return this; - } - - /** - * Get the update's hash. - * - * @return {string} is the update's hash - */ - getHash() { - return this.state.hash; - } - - setHash(hash) { - this.state.hash = hash; - return this; - } - - /** - * Get the uri to automatically download the update. - * - * @return {string} is the uri to automatically download the update - */ - getAutoUri() { - return this.state.autoUri; - } - - setAutoUri(autoUri) { - this.state.autoUri = autoUri; - return this; - } - - /** - * Get the uri to manually download the update. - * - * @return {string} is the uri to manually download the update - */ - getUserUri() { - return this.state.userUri; - } - - setUserUri(userUri) { - this.state.userUri = userUri; - return this; - } -} - -module.exports = MoneroDaemonUpdateCheckResult; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroDaemonUpdateDownloadResult.js b/src/main/js/daemon/model/MoneroDaemonUpdateDownloadResult.js deleted file mode 100644 index e77cef0f9..000000000 --- a/src/main/js/daemon/model/MoneroDaemonUpdateDownloadResult.js +++ /dev/null @@ -1,32 +0,0 @@ -const MoneroDaemonUpdateCheckResult = require("./MoneroDaemonUpdateCheckResult"); - -/** - * Models the result of downloading an update. - */ -class MoneroDaemonUpdateDownloadResult extends MoneroDaemonUpdateCheckResult { - - /** - * Construct a download result. - * - * @param {MoneroDaemonUpdateCheckResult} is an existing result to copy from - */ - constructor(result) { - super(result); - } - - /** - * Get the path the update was downloaded to. - * - * @return {string} is the path the update was downloaded to - */ - getDownloadPath() { - return this.state.downloadPath; - } - - setDownloadPath(downloadPath) { - this.state.downloadPath = downloadPath; - return this; - } -} - -module.exports = MoneroDaemonUpdateDownloadResult; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroFeeEstimate.js b/src/main/js/daemon/model/MoneroFeeEstimate.js deleted file mode 100644 index 39b42ea0f..000000000 --- a/src/main/js/daemon/model/MoneroFeeEstimate.js +++ /dev/null @@ -1,79 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; -const GenUtils = require("../../common/GenUtils"); - -/** - * Models a Monero fee estimate. - */ -class MoneroFeeEstimate { - - /** - * Construct the model. - * - * @param {MoneroFeeEstimate|object} state - MoneroFeeEstimate or JS object - */ - constructor(state) { - if (!state) this.state = {}; - else if (state instanceof MoneroFeeEstimate) this.state = state.toJson(); - else if (typeof state === "object") this.state = Object.assign({}, state); - else throw new MoneroError("state must be a MoneroFeeEstimate or JavaScript object"); - - // deserialize - if (this.state.fee !== undefined && !(this.state.fee instanceof BigInteger)) this.state.fee = BigInteger.parse(this.state.fee); - if (this.state.fees !== undefined) { - for (let i = 0; i < this.state.fees.length; i++) { - if (!(this.state.fees[i] instanceof BigInteger)) this.state.fees[i] = BigInteger.parse(this.state.fees[i]); - } - } - if (this.state.quantizationMask !== undefined && !(this.state.quantizationMask instanceof BigInteger)) this.state.quantizationMask = BigInteger.parse(this.state.quantizationMask); - } - - getFee() { - return this.state.fee; - } - - setFee(fee) { - this.state.fee = fee; - return this; - } - - getFees() { - return this.state.fees; - } - - setFees(fees) { - this.state.fees = fees; - return this; - } - - getQuantizationMask() { - return this.state.quantizationMask; - } - - setQuantizationMask(quantizationMask) { - this.state.quantizationMask = quantizationMask; - return this; - } - - copy() { - return new MoneroFeeEstimate(this); - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getFee()) json.fee = this.getFee().toString(); - if (this.getFees()) for (let i = 0; i < this.getFees().length; i++) json.fees[i] = this.getFees()[i].toString(); - if (this.getQuantizationMask()) json.quantizationMask = this.getQuantizationMask().toString(); - return json; - } - - toString(indent = 0) { - let str = ""; - let json = this.toJson(); - str += GenUtils.kvLine("Fee", json.fee, indent); - str += GenUtils.kvLine("Fees", json.fees, indent); - str += GenUtils.kvLine("Quantization mask", json.quantizationMask, indent); - return str.slice(0, str.length - 1); // strip last newline - } -} - -module.exports = MoneroFeeEstimate; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroHardForkInfo.js b/src/main/js/daemon/model/MoneroHardForkInfo.js deleted file mode 100644 index b0f58ce26..000000000 --- a/src/main/js/daemon/model/MoneroHardForkInfo.js +++ /dev/null @@ -1,110 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Monero hard fork info. - */ -class MoneroHardForkInfo { - - constructor(state) { - this.state = Object.assign({}, state); - if (this.state.credits !== undefined && !(this.state.credits instanceof BigInteger)) this.state.credits = BigInteger.parse(this.state.credits); - } - - toJson() { - let json = Object.assign({}, this.state); - if (json.credits) json.credits = json.credits.toString(); - return json; - } - - getEarliestHeight() { - return this.state.earliestHeight; - } - - setEarliestHeight(earliestHeight) { - this.state.earliestHeight = earliestHeight; - return this; - } - - isEnabled() { - return this.state.isEnabled; - } - - setIsEnabled(isEnabled) { - this.state.isEnabled = isEnabled; - return this; - } - - getState() { - return this.state.state; - } - - setState(state) { - this.state.state = state; - return this; - } - - getThreshold() { - return this.state.threshold; - } - - setThreshold(threshold) { - this.state.threshold = threshold; - return this; - } - - getVersion() { - return this.state.version; - } - - setVersion(version) { - this.state.version = version; - return this; - } - - getNumVotes() { - return this.state.numVotes; - } - - setNumVotes(numVotes) { - this.state.numVotes = numVotes; - return this; - } - - getWindow() { - return this.state.window; - } - - setWindow(window) { - this.state.window = window; - return this; - } - - getVoting() { - return this.state.voting; - } - - setVoting(voting) { - this.state.voting = voting; - return this; - } - - getCredits() { - return this.state.credits; - } - - setCredits(credits) { - this.state.credits = credits; - return this; - } - - getTopBlockHash() { - return this.state.topBlockHash; - } - - setTopBlockHash(topBlockHash) { - this.state.topBlockHash = topBlockHash; - return this; - } -} - -module.exports = MoneroHardForkInfo; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroKeyImage.js b/src/main/js/daemon/model/MoneroKeyImage.js deleted file mode 100644 index 466126ad4..000000000 --- a/src/main/js/daemon/model/MoneroKeyImage.js +++ /dev/null @@ -1,70 +0,0 @@ -const assert = require("assert"); -const GenUtils = require("../../common/GenUtils"); - -/** - * Models a Monero key image. - */ -class MoneroKeyImage { - - /** - * Construct the model. - * - * @param {MoneroKeyImage|object|string} stateOrHex is a MoneroKeyImage, JS object, or hex string to initialize from (optional) - * @param {string} signature is the key image's signature - */ - constructor(stateOrHex, signature) { - if (!stateOrHex) this.state = {}; - else if (stateOrHex instanceof MoneroKeyImage) this.state = stateOrHex.toJson(); - else if (typeof stateOrHex === "object") this.state = Object.assign({}, stateOrHex); - else if (typeof stateOrHex === "string") { - this.state = {}; - this.setHex(stateOrHex); - this.setSignature(signature); - } else { - throw new MoneroError("stateOrHex must be a MoneroKeyImage, JavaScript object, or string"); - } - } - - getHex() { - return this.state.hex; - } - - setHex(hex) { - this.state.hex = hex; - return this; - } - - getSignature() { - return this.state.signature; - } - - setSignature(signature) { - this.state.signature = signature; - return this; - } - - copy() { - return new MoneroKeyImage(this); - } - - toJson() { - return Object.assign({}, this.state); - } - - merge(keyImage) { - assert(keyImage instanceof MoneroKeyImage); - if (keyImage === this) return this; - this.setHex(GenUtils.reconcile(this.getHex(), keyImage.getHex())); - this.setSignature(GenUtils.reconcile(this.getSignature(), keyImage.getSignature())); - return this; - } - - toString(indent = 0) { - let str = ""; - str += GenUtils.kvLine("Hex", this.getHex(), indent); - str += GenUtils.kvLine("Signature", this.getSignature(), indent); - return str.slice(0, str.length - 1); // strip last newline - } -} - -module.exports = MoneroKeyImage; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroKeyImageSpentStatus.js b/src/main/js/daemon/model/MoneroKeyImageSpentStatus.js deleted file mode 100644 index 894377d4b..000000000 --- a/src/main/js/daemon/model/MoneroKeyImageSpentStatus.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Enumerate key image spent statuses. - * - * @hideconstructor - */ -class MoneroKeyImageSpentStatus {} - -/** - * Key image is not spent (value=0). - */ -MoneroKeyImageSpentStatus.NOT_SPENT = 0; - -/** - * Key image is confirmed (value=1). - */ -MoneroKeyImageSpentStatus.CONFIRMED = 1; - -/** - * Key image is in the pool (value=2). - */ -MoneroKeyImageSpentStatus.TX_POOL = 2; - -module.exports = MoneroKeyImageSpentStatus; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroMinerTxSum.js b/src/main/js/daemon/model/MoneroMinerTxSum.js deleted file mode 100644 index e04ef2c0b..000000000 --- a/src/main/js/daemon/model/MoneroMinerTxSum.js +++ /dev/null @@ -1,43 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Model for the summation of miner emissions and fees. - */ -class MoneroMinerTxSum { - - constructor(state) { - state = Object.assign({}, state); - this.state = state; - - // deserialize BigIntegers - if (state.emissionSum !== undefined && !(state.emissionSum instanceof BigInteger)) state.emissionSum = BigInteger.parse(state.emissionSum); - if (state.feeSum !== undefined && !(state.feeSum instanceof BigInteger)) state.feeSum = BigInteger.parse(state.feeSum); - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getEmissionSum()) json.emissionSum = this.getEmissionSum().toString(); - if (this.getFeeSum()) json.feeSum = this.getFeeSum().toString(); - return json; - } - - getEmissionSum() { - return this.state.emissionSum; - } - - setEmissionSum(emissionSum) { - this.state.emissionSum = emissionSum; - return this; - } - - getFeeSum() { - return this.state.feeSum; - } - - setFeeSum(feeSum) { - this.state.feeSum = feeSum; - return this; - } -} - -module.exports = MoneroMinerTxSum; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroMiningStatus.js b/src/main/js/daemon/model/MoneroMiningStatus.js deleted file mode 100644 index c7c99fdd7..000000000 --- a/src/main/js/daemon/model/MoneroMiningStatus.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Models daemon mining status. - */ -class MoneroMiningStatus { - - constructor(state) { - if (!state) state = {}; - else if (state instanceof MoneroMiningStatus) state = state.toJson(); - else if (typeof state === "object") state = Object.assign({}, state); - else throw new MoneroError("state must be a MoneroMiningStatus or JavaScript object"); - this.state = state; - } - - toJson() { - return Object.assign({}, this.state); - } - - isActive() { - return this.state.isActive; - } - - setIsActive(isActive) { - this.state.isActive = isActive; - return this; - } - - getAddress() { - return this.state.address; - } - - setAddress(address) { - this.state.address = address; - return this; - } - - getSpeed() { - return this.state.speed; - } - - setSpeed(speed) { - this.state.speed = speed; - return this; - } - - getNumThreads() { - return this.state.numThreads; - } - - setNumThreads(numThreads) { - this.state.numThreads = numThreads; - return this; - } - - isBackground() { - return this.state.isBackground; - } - - setIsBackground(isBackground) { - this.state.isBackground = isBackground; - return this; - } -} - -module.exports = MoneroMiningStatus; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroNetworkType.js b/src/main/js/daemon/model/MoneroNetworkType.js deleted file mode 100644 index 9c1edd0c0..000000000 --- a/src/main/js/daemon/model/MoneroNetworkType.js +++ /dev/null @@ -1,73 +0,0 @@ -const MoneroError = require("../../common/MoneroError"); - -/** - * Defines the Monero network types (mainnet, testnet, and stagenet). - * - * @hideconstructor - */ -class MoneroNetworkType { - - /** - * Validates the given network type. - * - * @param {int} networkType - the network type to validate as a numeric - */ - static validate(networkType) { - if (networkType !== 0 && networkType !== 1 && networkType !== 2) throw new MoneroError("Network type is invalid: " + networkType); - } - - /** - * Indicates if the given network type is valid or not. - * - * @param {int} networkType - the network type to validate as a numeric - * @return {boolean} true if the network type is valid, false otherwise - */ - static isValid(networkType) { - return networkType === 0 || networkType === 1 || networkType === 2; - } - - /** - * Parse the given string as a network type. - * - * @param {string} networkTypeStr - "mainnet", "testnet", or "stagenet" (case insensitive) - * @return {int} the network type as a numeric - */ - static parse(networkTypeStr) { - let str = ("" + networkTypeStr).toLowerCase(); - switch (str) { - case "mainnet": return MoneroNetworkType.MAINNET; - case "testnet": return MoneroNetworkType.TESTNET; - case "stagenet": return MoneroNetworkType.STAGENET; - default: throw new MoneroError("Invalid network type to parse: '" + networkTypeStr + "'"); - } - } - - /** - * Get the network type in human-readable form. - * - * @return {string} the network type in human-readable form - */ - static toString(networkType) { - if (networkType === 0) return "mainnet"; - if (networkType === 1) return "testnet"; - if (networkType === 2) return "stagenet"; - throw new MoneroError("Invalid network type: " + networkType); - } -} - -/** - * Mainnet (value=0). - */ -MoneroNetworkType.MAINNET = 0; - -/** - * Testnet (value=1). - */ -MoneroNetworkType.TESTNET = 1; - -/** - * Stagnet (value=2). - */ -MoneroNetworkType.STAGENET = 2; - -module.exports = MoneroNetworkType; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroOutput.js b/src/main/js/daemon/model/MoneroOutput.js deleted file mode 100644 index 2b5dccd1b..000000000 --- a/src/main/js/daemon/model/MoneroOutput.js +++ /dev/null @@ -1,131 +0,0 @@ -const assert = require("assert"); -const BigInteger = require("../../common/biginteger").BigInteger; -const GenUtils = require("../../common/GenUtils"); -const MoneroKeyImage = require("./MoneroKeyImage"); - -/** - * Models a Monero transaction output. - * - * @class - */ -class MoneroOutput { - - /** - * Construct the model. - * - * @param {MoneroOutput|object} state is existing state to initialize from (optional) - */ - constructor(state) { - - // initialize internal state - if (!state) state = {}; - else if (state instanceof MoneroOutput) state = state.toJson(); - else if (typeof state === "object") state = Object.assign({}, state); - else throw new MoneroError("state must be a MoneroOutput or JavaScript object"); - this.state = state; - - // deserialize fields if necessary - if (state.amount !== undefined && !(state.amount instanceof BigInteger)) state.amount = BigInteger.parse(state.amount); - if (state.keyImage && !(state.keyImage instanceof MoneroKeyImage)) state.keyImage = new MoneroKeyImage(state.keyImage); - } - - getTx() { - return this.state.tx; - } - - setTx(tx) { - this.state.tx = tx; - return this; - } - - getKeyImage() { - return this.state.keyImage; - } - - setKeyImage(keyImage) { - assert(keyImage === undefined || keyImage instanceof MoneroKeyImage); - this.state.keyImage = keyImage; - return this; - } - - getAmount() { - return this.state.amount; - } - - setAmount(amount) { - this.state.amount = amount; - return this; - } - - getIndex() { - return this.state.index; - } - - setIndex(index) { - this.state.index = index; - return this; - } - - getRingOutputIndices() { - return this.state.ringOutputIndices; - } - - setRingOutputIndices(ringOutputIndices) { - this.state.ringOutputIndices = ringOutputIndices; - return this; - } - - getStealthPublicKey() { - return this.state.stealthPublicKey; - } - - setStealthPublicKey(stealthPublicKey) { - this.state.stealthPublicKey = stealthPublicKey; - return this; - } - - copy() { - return new MoneroOutput(this); - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getAmount()) json.amount = this.getAmount() ? this.getAmount().toString() : undefined; - if (this.getKeyImage()) json.keyImage = this.getKeyImage() ? this.getKeyImage().toJson() : undefined; - delete json.tx; - return json; - } - - merge(output) { - assert(output instanceof MoneroOutput); - if (this === output) return this; - - // merge txs if they're different which comes back to merging outputs - if (this.getTx() !== output.getTx()) this.getTx().merge(output.getTx()); - - // otherwise merge output fields - else { - if (this.getKeyImage() === undefined) this.setKeyImage(output.getKeyImage()); - else if (output.getKeyImage() !== undefined) this.getKeyImage().merge(output.getKeyImage()); - this.setAmount(GenUtils.reconcile(this.getAmount(), output.getAmount())); - this.setIndex(GenUtils.reconcile(this.getIndex(), output.getIndex())); - } - - return this; - } - - toString(indent = 0) { - let str = ""; - if (this.getKeyImage()) { - str += GenUtils.kvLine("Key image", "", indent); - str += this.getKeyImage().toString(indent + 1) + "\n"; - } - str += GenUtils.kvLine("Amount", this.getAmount(), indent); - str += GenUtils.kvLine("Index", this.getIndex(), indent); - str += GenUtils.kvLine("Ring output indices", this.getRingOutputIndices(), indent); - str += GenUtils.kvLine("Stealth public key", this.getStealthPublicKey(), indent); - return str === "" ? str : str.slice(0, str.length - 1); // strip last newline - } -} - -module.exports = MoneroOutput; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroOutputHistogramEntry.js b/src/main/js/daemon/model/MoneroOutputHistogramEntry.js deleted file mode 100644 index 300133385..000000000 --- a/src/main/js/daemon/model/MoneroOutputHistogramEntry.js +++ /dev/null @@ -1,56 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Entry in a Monero output histogram (see get_output_histogram of Daemon RPC documentation). - */ -class MoneroOutputHistogramEntry { - - constructor(state) { - this.state = Object.assign({}, state); - if (this.state.amount !== undefined && !(this.state.amount instanceof BigInteger)) this.state.amount = BigInteger.parse(this.state.amount); - } - - toJson() { - let json = Object.assign({}, this.state); - if (json.amount) json.amount = json.amount.toString(); - return json; - } - - getAmount() { - return this.state.amount; - } - - setAmount(amount) { - this.state.amount = amount; - return this; - } - - getNumInstances() { - return this.state.numInstances; - } - - setNumInstances(numInstances) { - this.state.numInstances = numInstances; - return this; - } - - getNumUnlockedInstances() { - return this.state.numUnlockedInstances; - } - - setNumUnlockedInstances(numUnlockedInstances) { - this.state.numUnlockedInstances = numUnlockedInstances; - return this; - } - - getNumRecentInstances() { - return this.state.numRecentInstances; - } - - setNumRecentInstances(numRecentInstances) { - this.state.numRecentInstances = numRecentInstances; - return this; - } -} - -module.exports = MoneroOutputHistogramEntry; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroPeer.js b/src/main/js/daemon/model/MoneroPeer.js deleted file mode 100644 index dd271c07d..000000000 --- a/src/main/js/daemon/model/MoneroPeer.js +++ /dev/null @@ -1,260 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Models a peer to the daemon. - */ -class MoneroPeer { - - constructor(state) { - this.state = Object.assign({}, state); - if (this.state.rpcCreditsPerHash !== undefined && !(this.state.rpcCreditsPerHash instanceof BigInteger)) this.state.rpcCreditsPerHash = BigInteger.parse(this.state.rpcCreditsPerHash); - } - - toJson() { - let json = Object.assign({}, this.state); - if (json.rpcCreditsPerHash) json.rpcCreditsPerHash = json.rpcCreditsPerHash.toString(); - return json; - } - - getId() { - return this.state.id; - } - - setId(id) { - this.state.id = id; - return this; - } - - getAddress() { - return this.state.address; - } - - setAddress(address) { - this.state.address = address; - return this; - } - - getHost() { - return this.state.host; - } - - setHost(host) { - this.state.host = host; - return this; - } - - getPort() { - return this.state.port; - } - - setPort(port) { - this.state.port = port; - return this; - } - - /** - * Indicates if the peer was online when last checked (aka "white listed" as - * opposed to "gray listed"). - * - * @return {boolean} true if peer was online when last checked, false otherwise - */ - isOnline() { - return this.state.isOnline; - } - - setIsOnline(isOnline) { - this.state.isOnline = isOnline; - return this; - } - - getLastSeenTimestamp() { - return this.state.lastSeenTimestamp; - } - - setLastSeenTimestamp(lastSeenTimestamp) { - this.state.lastSeenTimestamp = lastSeenTimestamp; - return this; - } - - getPruningSeed() { - return this.state.pruningSeed; - } - - setPruningSeed(pruningSeed) { - this.state.pruningSeed = pruningSeed; - return this; - } - - getRpcPort() { - return this.state.rpcPort; - } - - setRpcPort(rpcPort) { - this.state.rpcPort = rpcPort; - return this; - } - - getRpcCreditsPerHash() { - return this.state.rpcCreditsPerHash; - } - - setRpcCreditsPerHash(rpcCreditsPerHash) { - this.state.rpcCreditsPerHash = rpcCreditsPerHash; - return this; - } - - getId() { - return this.state.id; - } - - setId(id) { - this.state.id = id; - return this; - } - - getAvgDownload() { - return this.state.avgDownload; - } - - setAvgDownload(avgDownload) { - this.state.avgDownload = avgDownload; - return this; - } - - getAvgUpload() { - return this.state.avgUpload; - } - - setAvgUpload(avgUpload) { - this.state.avgUpload = avgUpload; - return this; - } - - getCurrentDownload() { - return this.state.currentDownload; - } - - setCurrentDownload(currentDownload) { - this.state.currentDownload = currentDownload; - return this; - } - - getCurrentUpload() { - return this.state.currentUpload; - } - - setCurrentUpload(currentUpload) { - this.state.currentUpload = currentUpload; - return this; - } - - getHeight() { - return this.state.height; - } - - setHeight(height) { - this.state.height = height; - return this; - } - - isIncoming() { - return this.state.isIncoming; - } - - setIsIncoming(isIncoming) { - this.state.isIncoming = isIncoming; - return this; - } - - getLiveTime() { - return this.state.liveTime; - } - - setLiveTime(liveTime) { - this.state.liveTime = liveTime; - return this; - } - - isLocalIp() { - return this.state.isLocalIp; - } - - setIsLocalIp(isLocalIp) { - this.state.isLocalIp = isLocalIp; - return this; - } - - isLocalHost() { - return this.state.isLocalHost; - } - - setIsLocalHost(isLocalHost) { - this.state.isLocalHost = isLocalHost; - return this; - } - - getNumReceives() { - return this.state.numReceives; - } - - setNumReceives(numReceives) { - this.state.numReceives = numReceives; - return this; - } - - getNumSends() { - return this.state.numSends; - } - - setNumSends(numSends) { - this.state.numSends = numSends; - return this; - } - - getReceiveIdleTime() { - return this.state.receiveIdleTime; - } - - setReceiveIdleTime(receiveIdleTime) { - this.state.receiveIdleTime = receiveIdleTime; - return this; - } - - getSendIdleTime() { - return this.state.sendIdleTime; - } - - setSendIdleTime(sendIdleTime) { - this.state.sendIdleTime = sendIdleTime; - return this; - } - - getState() { - return this.state.state; - } - - setState(state) { - this.state.state = state; - return this; - } - - getNumSupportFlags() { - return this.state.numSupportFlags; - } - - setNumSupportFlags(numSupportFlags) { - this.state.numSupportFlags = numSupportFlags; - return this; - } - - getType() { - return this.state.type; - } - - setType(type) { - this.state.type = type; - return this; - } -} - -module.exports = MoneroPeer; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroPruneResult.js b/src/main/js/daemon/model/MoneroPruneResult.js deleted file mode 100644 index 3c54cb331..000000000 --- a/src/main/js/daemon/model/MoneroPruneResult.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Result of pruning the blockchain. - */ -class MoneroPruneResult { - - constructor(state) { - state = Object.assign({}, state); - this.state = state; - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.isPruned()) json.isPruned = this.isPruned(); - if (this.getPruningSeed()) json.pruningSeed = this.getPruningSeed(); - return json; - } - - isPruned() { - return this.state.isPruned; - } - - setIsPruned(isPruned) { - this.state.isPruned = isPruned; - return this; - } - - getPruningSeed() { - return this.state.pruningSeed; - } - - setPruningSeed(pruningSeed) { - this.state.pruningSeed = pruningSeed; - return this; - } -} - -module.exports = MoneroPruneResult; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroSubmitTxResult.js b/src/main/js/daemon/model/MoneroSubmitTxResult.js deleted file mode 100644 index fc926b84b..000000000 --- a/src/main/js/daemon/model/MoneroSubmitTxResult.js +++ /dev/null @@ -1,158 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Models the result from submitting a tx to a daemon. - */ -class MoneroSubmitTxResult { - - constructor(state) { - state = Object.assign({}, state); - this.state = state; - - // deserialize BigIntegers - if (state.credits !== undefined && !(state.credits instanceof BigInteger)) state.credits = BigInteger.parse(state.credits); - } - - toJson() { - let json = Object.assign({}, this.state); - if (json.credits) json.credits = json.credits.toString(); - return json; - } - - isGood() { - return this.state.isGood; - } - - setIsGood(isGood) { - this.state.isGood = isGood; - return this; - } - - isRelayed() { - return this.state.isRelayed; - } - - setIsRelayed(isRelayed) { - this.state.isRelayed = isRelayed; - return this; - } - - isDoubleSpendSeen() { - return this.state.isDoubleSpendSeen; - } - - setIsDoubleSpend(isDoubleSpendSeen) { - this.state.isDoubleSpendSeen = isDoubleSpendSeen - return this; - } - - isFeeTooLow() { - return this.state.isFeeTooLow; - } - - setIsFeeTooLow(isFeeTooLow) { - this.state.isFeeTooLow = isFeeTooLow; - return this; - } - - isMixinTooLow() { - return this.state.isMixinTooLow; - } - - setIsMixinTooLow(isMixinTooLow) { - this.state.isMixinTooLow = isMixinTooLow; - return this; - } - - hasInvalidInput() { - return this.state.hasInvalidInput; - } - - setHasInvalidInput(hasInvalidInput) { - this.state.hasInvalidInput = hasInvalidInput; - return this; - } - - hasInvalidOutput() { - return this.state.hasInvalidOutput; - } - - setHasInvalidOutput(hasInvalidOutput) { - this.state.hasInvalidOutput = hasInvalidOutput; - return this; - } - - hasTooFewOutputs() { - return this.state.hasTooFewOutputs; - } - - setHasTooFewOutputs(hasTooFewOutputs) { - this.state.hasTooFewOutputs = hasTooFewOutputs; - return this; - } - - isOverspend() { - return this.state.isOverspend; - } - - setIsOverspend(isOverspend) { - this.state.isOverspend = isOverspend; - return this; - } - - getReason() { - return this.state.reason; - } - - setReason(reason) { - this.state.reason = reason; - return this; - } - - isTooBig() { - return this.state.isTooBig; - } - - setIsTooBig(isTooBig) { - this.state.isTooBig = isTooBig; - return this; - } - - getSanityCheckFailed() { - return this.state.sanityCheckFailed; - } - - setSanityCheckFailed(sanityCheckFailed) { - this.state.sanityCheckFailed = sanityCheckFailed; - return this; - } - - getCredits() { - return this.state.credits; - } - - setCredits(credits) { - this.state.credits = credits; - return this; - } - - getTopBlockHash() { - return this.state.topBlockHash; - } - - setTopBlockHash(topBlockHash) { - this.state.topBlockHash = topBlockHash; - return this; - } - - isTxExtraTooBig() { - return this.state.isTxExtraTooBig; - } - - setIsTxExtraTooBig(isTxExtraTooBig) { - this.state.isTxExtraTooBig = isTxExtraTooBig; - return this; - } -} - -module.exports = MoneroSubmitTxResult; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroTx.js b/src/main/js/daemon/model/MoneroTx.js deleted file mode 100644 index a6cdb35af..000000000 --- a/src/main/js/daemon/model/MoneroTx.js +++ /dev/null @@ -1,606 +0,0 @@ -const assert = require("assert"); -const BigInteger = require("../../common/biginteger").BigInteger; -const GenUtils = require("../../common/GenUtils"); -const MoneroOutput = require("./MoneroOutput"); - -/** - * Represents a transaction on the Monero network. - * - * @class - */ -class MoneroTx { - - /** - * Construct the model. - * - * @param {MoneroTx|object} state is existing state to initialize from (optional) - */ - constructor(state) { - - // initialize internal state - if (!state) state = {}; - else if (state instanceof MoneroTx) state = state.toJson(); - else if (typeof state === "object") state = Object.assign({}, state); - else throw new MoneroError("state must be a MoneroTx or JavaScript object"); - this.state = state; - - // deserialize BigIntegers - if (state.fee !== undefined && !(state.fee instanceof BigInteger)) state.fee = BigInteger.parse(state.fee); - if (state.unlockTime !== undefined && !(state.unlockTime instanceof BigInteger)) state.unlockTime = BigInteger.parse(state.unlockTime); - - // deserialize inputs - if (state.inputs) { - for (let i = 0; i < state.inputs.length; i++) { - if (!(state.inputs[i] instanceof MoneroOutput)) { - state.inputs[i] = new MoneroOutput(Object.assign(state.inputs[i], {tx: this})); - } - } - } - - // deserialize outputs - if (state.outputs) { - for (let i = 0; i < state.outputs.length; i++) { - if (!(state.outputs[i] instanceof MoneroOutput)) { - state.outputs[i] = new MoneroOutput(Object.assign(state.outputs[i], {tx: this})); - } - } - } - } - - getBlock() { - return this.state.block; - } - - setBlock(block) { - this.state.block = block; - return this; - } - - getHeight() { - return this.getBlock() === undefined ? undefined : this.getBlock().getHeight(); - } - - getHash() { - return this.state.hash; - } - - setHash(hash) { - this.state.hash = hash; - return this; - } - - getVersion() { - return this.state.version; - } - - setVersion(version) { - this.state.version = version; - return this; - } - - isMinerTx() { - return this.state.isMinerTx; - } - - setIsMinerTx(miner) { - this.state.isMinerTx = miner; - return this; - } - - getPaymentId() { - return this.state.paymentId; - } - - setPaymentId(paymentId) { - this.state.paymentId = paymentId; - return this; - } - - getFee() { - return this.state.fee; - } - - setFee(fee) { - this.state.fee = fee; - return this; - } - - getRingSize() { - return this.state.ringSize; - } - - setRingSize(ringSize) { - this.state.ringSize = ringSize; - return this; - } - - getRelay() { - return this.state.relay; - } - - setRelay(relay) { - this.state.relay = relay; - return this; - } - - isRelayed() { - return this.state.isRelayed; - } - - setIsRelayed(isRelayed) { - this.state.isRelayed = isRelayed; - return this; - } - - isConfirmed() { - return this.state.isConfirmed; - } - - setIsConfirmed(isConfirmed) { - this.state.isConfirmed = isConfirmed; - return this; - } - - inTxPool() { - return this.state.inTxPool; - } - - setInTxPool(inTxPool) { - this.state.inTxPool = inTxPool; - return this; - } - - getNumConfirmations() { - return this.state.numConfirmations; - } - - setNumConfirmations(numConfirmations) { - this.state.numConfirmations = numConfirmations; - return this; - } - - /** - * Get the minimum height or timestamp for the transactions to unlock. - * - * @return {BigInteger} the minimum height or timestamp for the transactin to unlock - */ - getUnlockTime() { - return this.state.unlockTime; - } - - setUnlockTime(unlockTime) { - if (unlockTime !== undefined) { - if (typeof unlockTime === "number") unlockTime = "" + unlockTime; - if (!(unlockTime instanceof BigInteger)) { - try { unlockTime = BigInteger.parse(unlockTime); } - catch (err) { throw new MoneroError("Invalid unlock time: " + unlockTime); } - } - } - this.state.unlockTime = unlockTime; - return this; - } - - getLastRelayedTimestamp() { - return this.state.lastRelayedTimestamp; - } - - setLastRelayedTimestamp(lastRelayedTimestamp) { - this.state.lastRelayedTimestamp = lastRelayedTimestamp; - return this; - } - - getReceivedTimestamp() { - return this.state.receivedTimestamp; - } - - setReceivedTimestamp(receivedTimestamp) { - this.state.receivedTimestamp = receivedTimestamp; - return this; - } - - isDoubleSpendSeen() { - return this.state.isDoubleSpendSeen; - } - - setIsDoubleSpend(isDoubleSpendSeen) { - this.state.isDoubleSpendSeen = isDoubleSpendSeen; - return this; - } - - getKey() { - return this.state.key; - } - - setKey(key) { - this.state.key = key; - return this; - } - - /** - * Get full transaction hex. Full hex = pruned hex + prunable hex. - * - * @return {string} is full transaction hex - */ - getFullHex() { - return this.state.fullHex; - } - - setFullHex(fullHex) { - this.state.fullHex = fullHex; - return this; - } - - /** - * Get pruned transaction hex. Full hex = pruned hex + prunable hex. - * - * @return {string} is pruned transaction hex - */ - getPrunedHex() { - return this.state.prunedHex; - } - - setPrunedHex(prunedHex) { - this.state.prunedHex = prunedHex; - return this; - } - - /** - * Get prunable transaction hex which is hex that is removed from a pruned - * transaction. Full hex = pruned hex + prunable hex. - * - * @return {string} is the prunable transaction hex - */ - getPrunableHex() { - return this.state.prunableHex; - } - - setPrunableHex(prunableHex) { - this.state.prunableHex = prunableHex; - return this; - } - - getPrunableHash() { - return this.state.prunableHash; - } - - setPrunableHash(prunableHash) { - this.state.prunableHash = prunableHash; - return this; - } - - getSize() { - return this.state.size; - } - - setSize(size) { - this.state.size = size; - return this; - } - - getWeight() { - return this.state.weight; - } - - setWeight(weight) { - this.state.weight = weight; - return this; - } - - getInputs() { - return this.state.inputs; - } - - setInputs(inputs) { - this.state.inputs = inputs; - return this; - } - - getOutputs() { - return this.state.outputs; - } - - setOutputs(outputs) { - this.state.outputs = outputs; - return this; - } - - getOutputIndices() { - return this.state.outputIndices; - } - - setOutputIndices(outputIndices) { - this.state.outputIndices = outputIndices; - return this; - } - - getMetadata() { - return this.state.metadata; - } - - setMetadata(metadata) { - this.state.metadata = metadata; - return this; - } - - getExtra() { - return this.state.extra; - } - - setExtra(extra) { - this.state.extra = extra; - return this; - } - - getRctSignatures() { - return this.state.rctSignatures; - } - - setRctSignatures(rctSignatures) { - this.state.rctSignatures = rctSignatures; - return this; - } - - getRctSigPrunable() { - return this.state.rctSigPrunable; - } - - setRctSigPrunable(rctSigPrunable) { - this.state.rctSigPrunable = rctSigPrunable; - return this; - } - - isKeptByBlock() { - return this.state.isKeptByBlock; - } - - setIsKeptByBlock(isKeptByBlock) { - this.state.isKeptByBlock = isKeptByBlock; - return this; - } - - isFailed() { - return this.state.isFailed; - } - - setIsFailed(isFailed) { - this.state.isFailed = isFailed; - return this; - } - - getLastFailedHeight() { - return this.state.lastFailedHeight; - } - - setLastFailedHeight(lastFailedHeight) { - this.state.lastFailedHeight = lastFailedHeight; - return this; - } - - getLastFailedHash() { - return this.state.lastFailedHash; - } - - setLastFailedHash(lastFailedHash) { - this.state.lastFailedHash = lastFailedHash; - return this; - } - - getMaxUsedBlockHeight() { - return this.state.maxUsedBlockHeight; - } - - setMaxUsedBlockHeight(maxUsedBlockHeight) { - this.state.maxUsedBlockHeight = maxUsedBlockHeight; - return this; - } - - getMaxUsedBlockHash() { - return this.state.maxUsedBlockHash; - } - - setMaxUsedBlockHash(maxUsedBlockHash) { - this.state.maxUsedBlockHash = maxUsedBlockHash; - return this; - } - - getSignatures() { - return this.state.signatures; - } - - setSignatures(signatures) { - this.state.signatures = signatures; - return this; - } - - copy() { - return new MoneroTx(this); - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getFee()) json.fee = this.getFee().toString(); - if (this.getUnlockTime()) json.unlockTime = this.getUnlockTime().toString(); - if (this.getInputs()) { - json.inputs = []; - for (let input of this.getInputs()) json.inputs.push(input.toJson()); - } - if (this.getOutputs()) { - json.outputs = []; - for (let output of this.getOutputs()) json.outputs.push(output.toJson()); - } - if (this.getExtra()) json.extra = this.getExtra().slice(); - delete json.block; // do not serialize parent block - return json; - } - - /** - * Updates this transaction by merging the latest information from the given - * transaction. - * - * @param tx is the transaction to update this transaction with - * @return {MoneroTx} this for method chaining - */ - merge(tx) { - assert(tx instanceof MoneroTx); - if (this === tx) return this; - - // merge blocks if they're different - if (this.getBlock() !== tx.getBlock()) { - if (this.getBlock() === undefined) { - this.setBlock(tx.getBlock()); - this.getBlock().getTxs[this.getBlock().getTxs().indexOf(tx)] = this; // update block to point to this tx - } else if (tx.getBlock() !== undefined) { - this.getBlock().merge(tx.getBlock()); // comes back to merging txs - return this; - } - } - - // otherwise merge tx fields - this.setHash(GenUtils.reconcile(this.getHash(), tx.getHash())); - this.setVersion(GenUtils.reconcile(this.getVersion(), tx.getVersion())); - this.setPaymentId(GenUtils.reconcile(this.getPaymentId(), tx.getPaymentId())); - this.setFee(GenUtils.reconcile(this.getFee(), tx.getFee())); - this.setRingSize(GenUtils.reconcile(this.getRingSize(), tx.getRingSize())); - this.setIsConfirmed(GenUtils.reconcile(this.isConfirmed(), tx.isConfirmed(), {resolveTrue: true})); // tx can become confirmed - this.setIsMinerTx(GenUtils.reconcile(this.isMinerTx(), tx.isMinerTx())); - this.setRelay(GenUtils.reconcile(this.getRelay(), tx.getRelay(), {resolveTrue: true})); // tx can become relayed - this.setIsRelayed(GenUtils.reconcile(this.isRelayed(), tx.isRelayed(), {resolveTrue: true})); // tx can become relayed - this.setIsDoubleSpend(GenUtils.reconcile(this.isDoubleSpendSeen(), tx.isDoubleSpendSeen(), {resolveTrue: true})); // double spend can become seen - this.setKey(GenUtils.reconcile(this.getKey(), tx.getKey())); - this.setFullHex(GenUtils.reconcile(this.getFullHex(), tx.getFullHex())); - this.setPrunedHex(GenUtils.reconcile(this.getPrunedHex(), tx.getPrunedHex())); - this.setPrunableHex(GenUtils.reconcile(this.getPrunableHex(), tx.getPrunableHex())); - this.setPrunableHash(GenUtils.reconcile(this.getPrunableHash(), tx.getPrunableHash())); - this.setSize(GenUtils.reconcile(this.getSize(), tx.getSize())); - this.setWeight(GenUtils.reconcile(this.getWeight(), tx.getWeight())); - this.setOutputIndices(GenUtils.reconcile(this.getOutputIndices(), tx.getOutputIndices())); - this.setMetadata(GenUtils.reconcile(this.getMetadata(), tx.getMetadata())); - this.setExtra(GenUtils.reconcile(this.getExtra(), tx.getExtra())); - this.setRctSignatures(GenUtils.reconcile(this.getRctSignatures(), tx.getRctSignatures())); - this.setRctSigPrunable(GenUtils.reconcile(this.getRctSigPrunable(), tx.getRctSigPrunable())); - this.setIsKeptByBlock(GenUtils.reconcile(this.isKeptByBlock(), tx.isKeptByBlock())); - this.setIsFailed(GenUtils.reconcile(this.isFailed(), tx.isFailed(), {resolveTrue: true})); - this.setLastFailedHeight(GenUtils.reconcile(this.getLastFailedHeight(), tx.getLastFailedHeight())); - this.setLastFailedHash(GenUtils.reconcile(this.getLastFailedHash(), tx.getLastFailedHash())); - this.setMaxUsedBlockHeight(GenUtils.reconcile(this.getMaxUsedBlockHeight(), tx.getMaxUsedBlockHeight())); - this.setMaxUsedBlockHash(GenUtils.reconcile(this.getMaxUsedBlockHash(), tx.getMaxUsedBlockHash())); - this.setSignatures(GenUtils.reconcile(this.getSignatures(), tx.getSignatures())); - this.setUnlockTime(GenUtils.reconcile(this.getUnlockTime(), tx.getUnlockTime())); - this.setNumConfirmations(GenUtils.reconcile(this.getNumConfirmations(), tx.getNumConfirmations(), {resolveMax: true})); // num confirmations can increase - - // merge inputs - if (tx.getInputs()) { - for (let merger of tx.getInputs()) { - let merged = false; - merger.setTx(this); - if (!this.getInputs()) this.setInputs([]); - for (let mergee of this.getInputs()) { - if (mergee.getKeyImage().getHex() === merger.getKeyImage().getHex()) { - mergee.merge(merger); - merged = true; - break; - } - } - if (!merged) this.getInputs().push(merger); - } - } - - // merge outputs - if (tx.getOutputs()) { - for (let output of tx.getOutputs()) output.setTx(this); - if (!this.getOutputs()) this.setOutputs(tx.getOutputs()); - else { - - // merge outputs if key image or stealth public key present, otherwise append - for (let merger of tx.getOutputs()) { - let merged = false; - merger.setTx(this); - for (let mergee of this.getOutputs()) { - if ((merger.getKeyImage() && mergee.getKeyImage().getHex() === merger.getKeyImage().getHex()) || - (merger.getStealthPublicKey() && mergee.getStealthPublicKey() === merger.getStealthPublicKey())) { - mergee.merge(merger); - merged = true; - break; - } - } - if (!merged) this.getOutputs().push(merger); // append output - } - } - } - - // handle unrelayed -> relayed -> confirmed - if (this.isConfirmed()) { - this.setInTxPool(false); - this.setReceivedTimestamp(undefined); - this.setLastRelayedTimestamp(undefined); - } else { - this.setInTxPool(GenUtils.reconcile(this.inTxPool(), tx.inTxPool(), {resolveTrue: true})); // unrelayed -> tx pool - this.setReceivedTimestamp(GenUtils.reconcile(this.getReceivedTimestamp(), tx.getReceivedTimestamp(), {resolveMax: false})); // take earliest receive time - this.setLastRelayedTimestamp(GenUtils.reconcile(this.getLastRelayedTimestamp(), tx.getLastRelayedTimestamp(), {resolveMax: true})); // take latest relay time - } - - return this; // for chaining - } - - toString(indent = 0) { - let str = ""; - str += GenUtils.getIndent(indent) + "=== TX ===\n"; - str += GenUtils.kvLine("Tx hash", this.getHash(), indent); - str += GenUtils.kvLine("Height", this.getHeight(), indent); - str += GenUtils.kvLine("Version", this.getVersion(), indent); - str += GenUtils.kvLine("Is miner tx", this.isMinerTx(), indent); - str += GenUtils.kvLine("Payment ID", this.getPaymentId(), indent); - str += GenUtils.kvLine("Fee", this.getFee(), indent); - str += GenUtils.kvLine("Ring size", this.getRingSize(), indent); - str += GenUtils.kvLine("Relay", this.getRelay(), indent); - str += GenUtils.kvLine("Is relayed", this.isRelayed(), indent); - str += GenUtils.kvLine("Is confirmed", this.isConfirmed(), indent); - str += GenUtils.kvLine("In tx pool", this.inTxPool(), indent); - str += GenUtils.kvLine("Num confirmations", this.getNumConfirmations(), indent); - str += GenUtils.kvLine("Unlock time", this.getUnlockTime(), indent); - str += GenUtils.kvLine("Last relayed time", this.getLastRelayedTimestamp(), indent); - str += GenUtils.kvLine("Received time", this.getReceivedTimestamp(), indent); - str += GenUtils.kvLine("Is double spend", this.isDoubleSpendSeen(), indent); - str += GenUtils.kvLine("Key", this.getKey(), indent); - str += GenUtils.kvLine("Full hex", this.getFullHex(), indent); - str += GenUtils.kvLine("Pruned hex", this.getPrunedHex(), indent); - str += GenUtils.kvLine("Prunable hex", this.getPrunableHex(), indent); - str += GenUtils.kvLine("Prunable hash", this.getPrunableHash(), indent); - str += GenUtils.kvLine("Size", this.getSize(), indent); - str += GenUtils.kvLine("Weight", this.getWeight(), indent); - str += GenUtils.kvLine("Output indices", this.getOutputIndices(), indent); - str += GenUtils.kvLine("Metadata", this.getMetadata(), indent); - str += GenUtils.kvLine("Extra", this.getExtra(), indent); - str += GenUtils.kvLine("RCT signatures", this.getRctSignatures(), indent); - str += GenUtils.kvLine("RCT sig prunable", this.getRctSigPrunable(), indent); - str += GenUtils.kvLine("Kept by block", this.isKeptByBlock(), indent); - str += GenUtils.kvLine("Is failed", this.isFailed(), indent); - str += GenUtils.kvLine("Last failed height", this.getLastFailedHeight(), indent); - str += GenUtils.kvLine("Last failed hash", this.getLastFailedHash(), indent); - str += GenUtils.kvLine("Max used block height", this.getMaxUsedBlockHeight(), indent); - str += GenUtils.kvLine("Max used block hash", this.getMaxUsedBlockHash(), indent); - str += GenUtils.kvLine("Signatures", this.getSignatures(), indent); - if (this.getInputs()) { - str += GenUtils.kvLine("Inputs", "", indent); - for (let i = 0; i < this.getInputs().length; i++) { - str += GenUtils.kvLine(i + 1, "", indent + 1); - str += this.getInputs()[i].toString(indent + 2); - str += '\n' - } - } - if (this.getOutputs()) { - str += GenUtils.kvLine("Outputs", "", indent); - for (let i = 0; i < this.getOutputs().length; i++) { - str += GenUtils.kvLine(i + 1, "", indent + 1); - str += this.getOutputs()[i].toString(indent + 2); - str += '\n' - } - } - return str.slice(0, str.length - 1); // strip last newline - } -} - -// default payment id -MoneroTx.DEFAULT_PAYMENT_ID = "0000000000000000"; - -module.exports = MoneroTx; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroTxPoolStats.js b/src/main/js/daemon/model/MoneroTxPoolStats.js deleted file mode 100644 index 6495e7c85..000000000 --- a/src/main/js/daemon/model/MoneroTxPoolStats.js +++ /dev/null @@ -1,140 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Models transaction pool statistics. - */ -class MoneroTxPoolStats { - - constructor(state) { - this.state = Object.assign({}, state); - if (this.state.feeTotal !== undefined && !(this.state.feeTotal instanceof BigInteger)) this.state.feeTotal = BigInteger.parse(this.state.feeTotal); - if (this.state.histo !== undefined && !(this.state.histo instanceof Map)) this.state.histo = new Map(JSON.parse(this.state.histo)); - } - - toJson() { - let json = Object.assign({}, this.state); - if (json.feeTotal) json.feeTotal = json.feeTotal.toString(); - if (json.histo) json.histo = JSON.stringify([...json.histo]); // convert map to array of key-value pairs then stringify - return json; - } - - getNumTxs() { - return this.state.numTxs; - } - - setNumTxs(numTxs) { - this.state.numTxs = numTxs; - return this; - } - - getNumNotRelayed() { - return this.state.numNotRelayed; - } - - setNumNotRelayed(numNotRelayed) { - this.state.numNotRelayed = numNotRelayed; - return this; - } - - getNumFailing() { - return this.state.numFailing; - } - - setNumFailing(numFailing) { - this.state.numFailing = numFailing; - return this; - } - - getNumDoubleSpends() { - return this.state.numDoubleSpends; - } - - setNumDoubleSpends(numDoubleSpends) { - this.state.numDoubleSpends = numDoubleSpends; - return this; - } - - getNum10m() { - return this.state.num10m; - } - - setNum10m(num10m) { - this.state.num10m = num10m; - return this; - } - - getFeeTotal() { - return this.state.feeTotal; - } - - setFeeTotal(feeTotal) { - this.state.feeTotal = feeTotal; - return this; - } - - getBytesMax() { - return this.state.bytesMax; - } - - setBytesMax(bytesMax) { - this.state.bytesMax = bytesMax; - return this; - } - - getBytesMed() { - return this.state.bytesMed; - } - - setBytesMed(bytesMed) { - this.state.bytesMed = bytesMed; - return this; - } - - getBytesMin() { - return this.state.bytesMin; - } - - setBytesMin(bytesMin) { - this.state.bytesMin = bytesMin; - return this; - } - - getBytesTotal() { - return this.state.bytesTotal; - } - - setBytesTotal(bytesTotal) { - this.state.bytesTotal = bytesTotal; - return this; - } - - // TODO: histo... what? - getHisto() { - return this.state.histo; - } - - setHisto(histo) { - this.state.histo = histo; - return this; - } - - getHisto98pc() { - return this.state.histo98pc; - } - - setHisto98pc(histo98pc) { - this.state.histo98pc = histo98pc; - return this; - } - - getOldestTimestamp() { - return this.state.oldestTimestamp; - } - - setOldestTimestamp(oldestTimestamp) { - this.state.oldestTimestamp = oldestTimestamp; - return this; - } -} - -module.exports = MoneroTxPoolStats; \ No newline at end of file diff --git a/src/main/js/daemon/model/MoneroVersion.js b/src/main/js/daemon/model/MoneroVersion.js deleted file mode 100644 index 83bf52b44..000000000 --- a/src/main/js/daemon/model/MoneroVersion.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Models a Monero version. - */ -class MoneroVersion { - - /** - * Construct the model. - * - * @param number is the version number - * @param isRelease indicates if this version is a release - */ - constructor(number, isRelease) { - this.state = {}; - this.state.number = number; - this.state.isRelease = isRelease; - } - - getNumber() { - return this.state.number; - } - - setNumber(number) { - this.state.number = number; - return this; - } - - isRelease() { - return this.state.isRelease; - } - - setIsRelease(isRelease) { - this.state.isRelease = isRelease; - return this; - } - - copy() { - return new MoneroKeyImage(this); - } - - toJson() { - return Object.assign({}, this.state); - } -} - -module.exports = MoneroVersion; \ No newline at end of file diff --git a/src/main/js/wallet/MoneroWallet.js b/src/main/js/wallet/MoneroWallet.js deleted file mode 100644 index 2c2415264..000000000 --- a/src/main/js/wallet/MoneroWallet.js +++ /dev/null @@ -1,1418 +0,0 @@ -const assert = require("assert"); -const MoneroBlock = require("../daemon/model/MoneroBlock"); -const BigInteger = require("../common/biginteger").BigInteger; -const MoneroConnectionManagerListener = require("../common/MoneroConnectionManagerListener") -const MoneroError = require("../common/MoneroError"); -const MoneroOutputQuery = require("./model/MoneroOutputQuery"); -const MoneroTransferQuery = require("./model/MoneroTransferQuery"); -const MoneroTxConfig = require("./model/MoneroTxConfig"); -const MoneroTxQuery = require("./model/MoneroTxQuery"); -const MoneroTxSet = require("./model/MoneroTxSet"); - -/** - * Copyright (c) woodser - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * Monero wallet interface and default implementations. - * - * @interface - */ -class MoneroWallet { - - /** - * Register a listener to receive wallet notifications. - * - * @param {MoneroWalletListener} listener - listener to receive wallet notifications - */ - async addListener(listener) { - throw new Error("Not supported"); - } - - /** - * Unregister a listener to receive wallet notifications. - * - * @param {MoneroWalletListener} listener - listener to unregister - */ - async removeListener(listener) { - throw new Error("Not supported"); - } - - /** - * Get the listeners registered with the wallet. - * - * @return {MoneroWalletListener[]} the registered listeners - */ - getListeners() { - throw new Error("Not supported"); - } - - /** - * Indicates if the wallet is view-only, meaning it does not have the private - * spend key and can therefore only observe incoming outputs. - * - * @return {bool} true if the wallet is view-only, false otherwise - */ - async isViewOnly() { - throw new MoneroError("Not supported"); - } - - /** - * Set the wallet's daemon connection. - * - * @param {string|MoneroRpcConnection} uriOrConnection - daemon's URI or connection (defaults to offline) - * @param {string} username - username to authenticate with the daemon (optional) - * @param {string} password - password to authenticate with the daemon (optional) - */ - async setDaemonConnection(uriOrConnection, username, password) { - throw new MoneroError("Not supported"); - } - - /** - * Get the wallet's daemon connection. - * - * @return {MoneroRpcConnection} the wallet's daemon connection - */ - async getDaemonConnection() { - throw new MoneroError("Not supported"); - } - - /** - * Set the wallet's daemon connection manager. - * - * @param {MoneroConnectionManager} connectionManager manages connections to monerod - */ - async setConnectionManager(connectionManager) { - if (this._connectionManager) this._connectionManager.removeListener(this._connectionManagerListener); - this._connectionManager = connectionManager; - if (!connectionManager) return; - let that = this; - if (!this._connectionManagerListener) this._connectionManagerListener = new class extends MoneroConnectionManagerListener { - async onConnectionChanged(connection) { - await that.setDaemonConnection(connection); - } - }; - connectionManager.addListener(this._connectionManagerListener); - await this.setDaemonConnection(connectionManager.getConnection()); - } - - /** - * Get the wallet's daemon connection manager. - * - * @return {MoneroConnectionManager} the wallet's daemon connection manager - */ - getConnectionManager() { - return this._connectionManager; - } - - /** - * Indicates if the wallet is connected to daemon. - * - * @return {boolean} true if the wallet is connected to a daemon, false otherwise - */ - async isConnectedToDaemon() { - throw new MoneroError("Not supported"); - } - - /** - * Gets the version of the wallet. - * - * @return {MoneroVersion} the version of the wallet - */ - async getVersion() { - throw new MoneroError("Not supported"); - } - - /** - * Get the wallet's path. - * - * @return {string} the path the wallet can be opened with - */ - async getPath() { - throw new MoneroError("Not supported"); - } - - /** - * Get the wallet's mnemonic phrase or seed. - * - * @return {string} the wallet's mnemonic phrase or seed. - */ - async getSeed() { - throw new MoneroError("Not supported"); - } - - /** - * Get the language of the wallet's mnemonic phrase or seed. - * - * @return {string} the language of the wallet's mnemonic phrase or seed. - */ - async getSeedLanguage() { - throw new MoneroError("Not supported"); - } - - /** - * Get the wallet's private view key. - * - * @return {string} the wallet's private view key - */ - async getPrivateViewKey() { - throw new MoneroError("Not supported"); - } - - /** - * Get the wallet's private spend key. - * - * @return {string} the wallet's private spend key - */ - async getPrivateSpendKey() { - throw new MoneroError("Not supported"); - } - - /** - * Get the wallet's public view key. - * - * @return {string} the wallet's public view key - */ - async getPublicViewKey() { - throw new MoneroError("Not supported"); - } - - /** - * Get the wallet's public spend key. - * - * @return {string} the wallet's public spend key - */ - async getPublicSpendKey() { - throw new MoneroError("Not supported"); - } - - /** - * Get the wallet's primary address. - * - * @return {string} the wallet's primary address - */ - async getPrimaryAddress() { - return await this.getAddress(0, 0); - } - - /** - * Get the address of a specific subaddress. - * - * @param {int} accountIdx - the account index of the address's subaddress - * @param {int} subaddressIdx - the subaddress index within the account - * @return {string} the receive address of the specified subaddress - */ - async getAddress(accountIdx, subaddressIdx) { - throw new MoneroError("Not supported"); - } - - /** - * Get the account and subaddress index of the given address. - * - * @param {string} address - address to get the account and subaddress index from - * @return {MoneroSubaddress} the account and subaddress indices - */ - async getAddressIndex(address) { - throw new MoneroError("Not supported"); - } - - /** - * Get an integrated address based on the given standard address and payment - * ID. Uses the wallet's primary address if an address is not given. - * Generates a random payment ID if a payment ID is not given. - * - * @param {string} standardAddress is the standard address to generate the integrated address from (wallet's primary address if undefined) - * @param {string} paymentId is the payment ID to generate an integrated address from (randomly generated if undefined) - * @return {MoneroIntegratedAddress} the integrated address - */ - async getIntegratedAddress(standardAddress, paymentId) { - throw new MoneroError("Not supported"); - } - - /** - * Decode an integrated address to get its standard address and payment id. - * - * @param {string} integratedAddress - integrated address to decode - * @return {MoneroIntegratedAddress} the decoded integrated address including standard address and payment id - */ - async decodeIntegratedAddress(integratedAddress) { - throw new MoneroError("Not supported"); - } - - /** - * Get the block height that the wallet is synced to. - * - * @return {int} the block height that the wallet is synced to - */ - async getHeight() { - throw new MoneroError("Not supported"); - } - - /** - * Get the blockchain's height. - * - * @return {int} the blockchain's height - */ - async getDaemonHeight() { - throw new MoneroError("Not supported"); - } - - /** - * Get the blockchain's height by date as a conservative estimate for scanning. - * - * @param {int} year - year of the height to get - * @param {int} month - month of the height to get as a number between 1 and 12 - * @param {int} day - day of the height to get as a number between 1 and 31 - * @return the blockchain's approximate height at the given date - */ - async getHeightByDate(year, month, day) { - throw new MoneroError("Not supported"); - } - - /** - * Synchronize the wallet with the daemon as a one-time synchronous process. - * - * @param {MoneroWalletListener|number} listenerOrStartHeight - listener xor start height (defaults to no sync listener, the last synced block) - * @param {number} startHeight - startHeight if not given in first arg (defaults to last synced block) - */ - async sync(listenerOrStartHeight, startHeight) { - throw new MoneroError("Not supported"); - } - - /** - * Start background synchronizing with a maximum period between syncs. - * - * @param {int} syncPeriodInMs - maximum period between syncs in milliseconds (default is wallet-specific) - */ - async startSyncing(syncPeriodInMs) { - throw new MoneroError("Not supported"); - } - - /** - * Stop synchronizing the wallet with the daemon. - */ - async stopSyncing() { - throw new MoneroError("Not supported"); - } - - /** - * Scan transactions by their hash/id. - * - * @param {string[]} txHashes - tx hashes to scan - */ - async scanTxs(txHashes) { - throw new MoneroError("Not supported"); - } - - /** - *

Rescan the blockchain for spent outputs.

- * - *

Note: this can only be called with a trusted daemon.

- * - *

Example use case: peer multisig hex is import when connected to an untrusted daemon, - * so the wallet will not rescan spent outputs. Then the wallet connects to a trusted - * daemon. This method should be manually invoked to rescan outputs.

- */ - async rescanSpent() { - throw new MoneroError("Not supported"); - } - - /** - *

Rescan the blockchain from scratch, losing any information which cannot be recovered from - * the blockchain itself.

- * - *

WARNING: This method discards local wallet data like destination addresses, tx secret keys, - * tx notes, etc.

- */ - async rescanBlockchain() { - throw new MoneroError("Not supported"); - } - - /** - * Get the balance of the wallet, account, or subaddress. - * - * @param {int} accountIdx - index of the account to get the balance of (default all accounts) - * @param {int} subaddressIdx - index of the subaddress to get the balance of (default all subaddresses) - * @return {BigInteger} the balance of the wallet, account, or subaddress - */ - async getBalance(accountIdx, subaddressIdx) { - throw new MoneroError("Not supported"); - } - - /** - * Get the unlocked balance of the wallet, account, or subaddress. - * - * @param {int} accountIdx - index of the account to get the unlocked balance of (optional) - * @param {int} subaddressIdx - index of the subaddress to get the unlocked balance of (optional) - * @return {BigInteger} the unlocked balance of the wallet, account, or subaddress - */ - async getUnlockedBalance(accountIdx, subaddressIdx) { - throw new MoneroError("Not supported"); - } - - /** - * Get the number of blocks until the next and last funds unlock. - * - * @return {int[]} the number of blocks until the next and last funds unlock in elements 0 and 1, respectively, or undefined if no balance - */ - async getNumBlocksToUnlock() { - - // get balances - let balance = await this.getBalance(); - if (balance.compare(new BigInteger(0)) === 0) return [undefined, undefined]; // skip if no balance - let unlockedBalance = await this.getUnlockedBalance(); - - // compute number of blocks until next funds available - let txs; - let height; - let numBlocksToNextUnlock = undefined; - if (unlockedBalance.compare(new BigInteger(0)) > 0) numBlocksToNextUnlock = 0; - else { - txs = await this.getTxs({isLocked: true}); // get locked txs - height = await this.getHeight(); // get most recent height - for (let tx of txs) { - let numBlocksToUnlock = Math.max((tx.isConfirmed() ? tx.getHeight() : height) + 10, tx.getUnlockTime()) - height; - numBlocksToNextUnlock = numBlocksToNextUnlock === undefined ? numBlocksToUnlock : Math.min(numBlocksToNextUnlock, numBlocksToUnlock); - } - } - - // compute number of blocks until all funds available - let numBlocksToLastUnlock = undefined; - if (balance.compare(unlockedBalance) === 0) { - if (unlockedBalance.compare(new BigInteger(0)) > 0) numBlocksToLastUnlock = 0; - } else { - if (!txs) { - txs = await this.getTxs({isLocked: true}); // get locked txs - height = await this.getHeight(); // get most recent height - } - for (let tx of txs) { - let numBlocksToUnlock = Math.max((tx.isConfirmed() ? tx.getHeight() : height) + 10, tx.getUnlockTime()) - height; - numBlocksToLastUnlock = numBlocksToLastUnlock === undefined ? numBlocksToUnlock : Math.max(numBlocksToLastUnlock, numBlocksToUnlock); - } - } - - return [numBlocksToNextUnlock, numBlocksToLastUnlock]; - } - - /** - * Get accounts with a given tag. - * - * @param {boolean} includeSubaddresses - include subaddresses if true - * @param {string} tag - tag for filtering accounts, all accounts if undefined - * @return {MoneroAccount[]} all accounts with the given tag - */ - async getAccounts(includeSubaddresses, tag) { - throw new MoneroError("Not supported"); - } - - /** - * Get an account. - * - * @param {int} accountIdx - index of the account to get - * @param {boolean} includeSubaddresses - include subaddresses if true - * @return {MoneroAccount} the retrieved account - */ - async getAccount(accountIdx, includeSubaddresses) { - throw new MoneroError("Not supported"); - } - - /** - * Create a new account with a label for the first subaddress. - * - * @param {string} label - label for account's first subaddress (optional) - * @return {MoneroAccount} the created account - */ - async createAccount(label) { - throw new MoneroError("Not supported"); - } - - /** - * Set an account label. - * - * @param {number} accountIdx - index of the account to set the label for - * @param {string} label - the label to set - */ - async setAccountLabel(accountIdx, label) { - await this.setSubaddressLabel(accountIdx, 0, label); - } - - /** - * Get subaddresses in an account. - * - * @param {int} accountIdx - account to get subaddresses within - * @param {int[]} subaddressIndices - indices of subaddresses to get (optional) - * @return {MoneroSubaddress[]} the retrieved subaddresses - */ - async getSubaddresses(accountIdx, subaddressIndices) { - throw new MoneroError("Not supported"); - } - - /** - * Get a subaddress. - * - * @param {int} accountIdx - index of the subaddress's account - * @param {int} subaddressIdx - index of the subaddress within the account - * @return {MoneroSubaddress} the retrieved subaddress - */ - async getSubaddress(accountIdx, subaddressIdx) { - assert(accountIdx >= 0); - assert(subaddressIdx >= 0); - return (await this.getSubaddresses(accountIdx, subaddressIdx))[0]; - } - - /** - * Create a subaddress within an account. - * - * @param {int} accountIdx - index of the account to create the subaddress within - * @param {string} label - the label for the subaddress (optional) - * @return {MoneroSubaddress} the created subaddress - */ - async createSubaddress(accountIdx, label) { - throw new MoneroError("Not supported"); - } - - /** - * Set a subaddress label. - * - * @param {number} accountIdx - index of the account to set the label for - * @param {number} subaddressIdx - index of the subaddress to set the label for - * @param {string} label - the label to set - */ - async setSubaddressLabel(accountIdx, subaddressIdx, label) { - throw new MoneroError("Not supported"); - } - - /** - * Get a wallet transaction by hash. - * - * @param {string} txHash - hash of a transaction to get - * @return {MoneroTxWallet} the identified transaction or undefined if not found - */ - async getTx(txHash) { - let txs = await this.getTxs([txHash]); - return txs.length === 0 ? undefined : txs[0]; - } - - /** - *

Get wallet transactions. Wallet transactions contain one or more - * transfers that are either incoming or outgoing to the wallet.

- * - *

Results can be filtered by passing a query object. Transactions must - * meet every criteria defined in the query in order to be returned. All - * criteria are optional and no filtering is applied when not defined.

- * - * @param {(MoneroTxQuery|string[]|object)} query - configures the query (optional) - * @param {boolean} query.isConfirmed - get txs that are confirmed or not (optional) - * @param {boolean} query.inTxPool - get txs that are in the tx pool or not (optional) - * @param {boolean} query.isRelayed - get txs that are relayed or not (optional) - * @param {boolean} query.isFailed - get txs that are failed or not (optional) - * @param {boolean} query.isMinerTx - get miner txs or not (optional) - * @param {string} query.hash - get a tx with the hash (optional) - * @param {string[]} query.hashes - get txs with the hashes (optional) - * @param {string} query.paymentId - get transactions with the payment id (optional) - * @param {string[]} query.paymentIds - get transactions with the payment ids (optional) - * @param {boolean} query.hasPaymentId - get transactions with a payment id or not (optional) - * @param {int} query.minHeight - get txs with height >= the given height (optional) - * @param {int} query.maxHeight - get txs with height <= the given height (optional) - * @param {boolean} query.isOutgoing - get txs with an outgoing transfer or not (optional) - * @param {boolean} query.isIncoming - get txs with an incoming transfer or not (optional) - * @param {MoneroTransferQuery} query.transferQuery - get txs that have a transfer that meets this query (optional) - * @param {boolean} query.includeOutputs - specifies that tx outputs should be returned with tx results (optional) - * @return {MoneroTxWallet[]} wallet transactions per the configuration - */ - async getTxs(query) { - throw new MoneroError("Not supported"); - } - - /** - *

Get incoming and outgoing transfers to and from this wallet. An outgoing - * transfer represents a total amount sent from one or more subaddresses - * within an account to individual destination addresses, each with their - * own amount. An incoming transfer represents a total amount received into - * a subaddress within an account. Transfers belong to transactions which - * are stored on the blockchain.

- * - *

Results can be filtered by passing a query object. Transfers must - * meet every criteria defined in the query in order to be returned. All - * criteria are optional and no filtering is applied when not defined.

- * - * @param {(MoneroTransferQuery|object)} query - configures the query (optional) - * @param {boolean} query.isOutgoing - get transfers that are outgoing or not (optional) - * @param {boolean} query.isIncoming - get transfers that are incoming or not (optional) - * @param {string} query.address - wallet's address that a transfer either originated from (if outgoing) or is destined for (if incoming) (optional) - * @param {int} query.accountIndex - get transfers that either originated from (if outgoing) or are destined for (if incoming) a specific account index (optional) - * @param {int} query.subaddressIndex - get transfers that either originated from (if outgoing) or are destined for (if incoming) a specific subaddress index (optional) - * @param {int[]} query.subaddressIndices - get transfers that either originated from (if outgoing) or are destined for (if incoming) specific subaddress indices (optional) - * @param {BigInteger} query.amount - amount being transferred (optional) - * @param {MoneroDestination[]} query.destinations - individual destinations of an outgoing transfer, which is local wallet data and NOT recoverable from the blockchain (optional) - * @param {boolean} query.hasDestinations - get transfers that have destinations or not (optional) - * @param {MoneroTxQuery} query.txQuery - get transfers whose transaction meets this query (optional) - * @return {MoneroTransfer[]} wallet transfers that meet the query - */ - async getTransfers(query) { - throw new MoneroError("Not supported"); - } - - /** - * Get incoming transfers. - * - * @param {(MoneroTransferQuery|object)} query - configures the query (optional) - * @param {string} query.address - get incoming transfers to a specific address in the wallet (optional) - * @param {int} query.accountIndex - get incoming transfers to a specific account index (optional) - * @param {int} query.subaddressIndex - get incoming transfers to a specific subaddress index (optional) - * @param {int[]} query.subaddressIndices - get transfers destined for specific subaddress indices (optional) - * @param {BigInteger} query.amount - amount being transferred (optional) - * @param {MoneroTxQuery} query.txQuery - get transfers whose transaction meets this query (optional) - * @return {MoneroIncomingTransfer[]} incoming transfers that meet the query - */ - async getIncomingTransfers(query) { - query = MoneroWallet._normalizeTransferQuery(query); - if (query.isIncoming() === false) throw new MoneroError("Transfer query contradicts getting incoming transfers"); - query.setIsIncoming(true); - return this.getTransfers(query); - } - - /** - * Get outgoing transfers. - * - * @param {(MoneroTransferQuery|object)} query - configures the query (optional) - * @param {string} query.address - get outgoing transfers from a specific address in the wallet (optional) - * @param {int} query.accountIndex - get outgoing transfers from a specific account index (optional) - * @param {int} query.subaddressIndex - get outgoing transfers from a specific subaddress index (optional) - * @param {int[]} query.subaddressIndices - get outgoing transfers from specific subaddress indices (optional) - * @param {BigInteger} query.amount - amount being transferred (optional) - * @param {MoneroDestination[]} query.destinations - individual destinations of an outgoing transfer, which is local wallet data and NOT recoverable from the blockchain (optional) - * @param {boolean} query.hasDestinations - get transfers that have destinations or not (optional) - * @param {MoneroTxQuery} query.txQuery - get transfers whose transaction meets this query (optional) - * @return {MoneroOutgoingTransfer[]} outgoing transfers that meet the query - */ - async getOutgoingTransfers(query) { - query = MoneroWallet._normalizeTransferQuery(query); - if (query.isOutgoing() === false) throw new MoneroError("Transfer query contradicts getting outgoing transfers"); - query.setIsOutgoing(true); - return this.getTransfers(query); - } - - /** - *

Get outputs created from previous transactions that belong to the wallet - * (i.e. that the wallet can spend one time). Outputs are part of - * transactions which are stored in blocks on the blockchain.

- * - *

Results can be filtered by passing a query object. Outputs must - * meet every criteria defined in the query in order to be returned. All - * filtering is optional and no filtering is applied when not defined.

- * - * @param {(MoneroOutputQuery|object)} query - configures the query (optional) - * @param {int} query.accountIndex - get outputs associated with a specific account index (optional) - * @param {int} query.subaddressIndex - get outputs associated with a specific subaddress index (optional) - * @param {int[]} query.subaddressIndices - get outputs associated with specific subaddress indices (optional) - * @param {BigInteger} query.amount - get outputs with a specific amount (optional) - * @param {BigInteger} query.minAmount - get outputs greater than or equal to a minimum amount (optional) - * @param {BigInteger} query.maxAmount - get outputs less than or equal to a maximum amount (optional) - * @param {boolean} query.isSpent - get outputs that are spent or not (optional) - * @param {string|MoneroKeyImage} query.keyImage - get output with a key image or which matches fields defined in a MoneroKeyImage (optional) - * @param {MoneroTxQuery} query.txQuery - get outputs whose transaction meets this filter (optional) - * @return {MoneroOutputWallet[]} the queried outputs - */ - async getOutputs(query) { - throw new MoneroError("Not supported"); - } - - /** - * Export outputs in hex format. - * - * @param {boolean} all - export all outputs if true, else export the outputs since the last export - * @return {string} outputs in hex format - */ - async exportOutputs(all) { - throw new MoneroError("Not supported"); - } - - /** - * Import outputs in hex format. - * - * @param {string} outputsHex - outputs in hex format - * @return {int} the number of outputs imported - */ - async importOutputs(outputsHex) { - throw new MoneroError("Not supported"); - } - - /** - * Export signed key images. - * - * @param {boolean} all - export all key images if true, else export the key images since the last export - * @return {MoneroKeyImage[]} the wallet's signed key images - */ - async exportKeyImages(all) { - throw new MoneroError("Not supported"); - } - - /** - * Import signed key images and verify their spent status. - * - * @param {MoneroKeyImage[]} keyImages - images to import and verify (requires hex and signature) - * @return {MoneroKeyImageImportResult} results of the import - */ - async importKeyImages(keyImages) { - throw new MoneroError("Not supported"); - } - - /** - * Get new key images from the last imported outputs. - * - * @return {MoneroKeyImage[]} the key images from the last imported outputs - */ - async getNewKeyImagesFromLastImport() { - throw new MoneroError("Not supported"); - } - - /** - * Freeze an output. - * - * @param {string} keyImage - key image of the output to freeze - */ - async freezeOutput(keyImage) { - throw new MoneroError("Not supported"); - } - - /** - * Thaw a frozen output. - * - * @param {string} keyImage - key image of the output to thaw - */ - async thawOutput(keyImage) { - throw new MoneroError("Not supported"); - } - - /** - * Check if an output is frozen. - * - * @param {string} keyImage - key image of the output to check if frozen - * @return {boolean} true if the output is frozen, false otherwise - */ - async isOutputFrozen(keyImage) { - throw new MoneroError("Not supported"); - } - - /** - * Create a transaction to transfer funds from this wallet. - * - * @param {MoneroTxConfig|object} config - configures the transaction to create (required) - * @param {string} config.address - single destination address (required unless `destinations` provided) - * @param {BigInteger|string} config.amount - single destination amount (required unless `destinations` provided) - * @param {int} config.accountIndex - source account index to transfer funds from (required) - * @param {int} config.subaddressIndex - source subaddress index to transfer funds from (optional) - * @param {int[]} config.subaddressIndices - source subaddress indices to transfer funds from (optional) - * @param {boolean} config.relay - relay the transaction to peers to commit to the blockchain (default false) - * @param {MoneroTxPriority} config.priority - transaction priority (default MoneroTxPriority.NORMAL) - * @param {MoneroDestination[]} config.destinations - addresses and amounts in a multi-destination tx (required unless `address` and `amount` provided) - * @param {int[]} config.subtractFeeFrom - list of destination indices to split the transaction fee (optional) - * @param {string} config.paymentId - transaction payment ID (optional) - * @param {BigInteger|string} config.unlockTime - minimum height or timestamp for the transaction to unlock (default 0) - * @return {MoneroTxWallet} the created transaction - */ - async createTx(config) { - config = MoneroWallet._normalizeCreateTxsConfig(config); - if (config.getCanSplit() !== undefined) assert.equal(config.getCanSplit(), false, "Cannot split transactions using createTx(); use createTxs()"); - config.setCanSplit(false); - return (await this.createTxs(config))[0]; - } - - /** - * Create one or more transactions to transfer funds from this wallet. - * - * @param {MoneroTxConfig|object} config - configures the transactions to create (required) - * @param {string} config.address - single destination address (required unless `destinations` provided) - * @param {BigInteger|string} config.amount - single destination amount (required unless `destinations` provided) - * @param {int} config.accountIndex - source account index to transfer funds from (required) - * @param {int} config.subaddressIndex - source subaddress index to transfer funds from (optional) - * @param {int[]} config.subaddressIndices - source subaddress indices to transfer funds from (optional) - * @param {boolean} config.relay - relay the transactions to peers to commit to the blockchain (default false) - * @param {MoneroTxPriority} config.priority - transaction priority (default MoneroTxPriority.NORMAL) - * @param {MoneroDestination[]} config.destinations - addresses and amounts in a multi-destination tx (required unless `address` and `amount` provided) - * @param {string} config.paymentId - transaction payment ID (optional) - * @param {BigInteger|string} config.unlockTime - minimum height or timestamp for the transactions to unlock (default 0) - * @param {boolean} config.canSplit - allow funds to be transferred using multiple transactions (default true) - * @return {MoneroTxWallet[]} the created transactions - */ - async createTxs(config) { - throw new MoneroError("Not supported"); - } - - /** - * Sweep an output by key image. - * - * @param {MoneroTxConfig} config - configures the transaction to create (required) - * @param {string} config.address - single destination address (required) - * @param {string} config.keyImage - key image to sweep (required) - * @param {boolean} config.relay - relay the transaction to peers to commit to the blockchain (default false) - * @param {BigInteger|string} config.unlockTime - minimum height or timestamp for the transaction to unlock (default 0) - * @param {MoneroTxPriority} config.priority - transaction priority (default MoneroTxPriority.NORMAL) - * @return {MoneroTxWallet} the created transaction - */ - async sweepOutput(config) { - throw new MoneroError("Not supported"); - } - - /** - * Sweep all unlocked funds according to the given configuration. - * - * @param {MoneroTxConfig|object} config - configures the transactions to create (required) - * @param {string} config.address - single destination address (required) - * @param {int} config.accountIndex - source account index to sweep from (optional, defaults to all accounts) - * @param {int} config.subaddressIndex - source subaddress index to sweep from (optional, defaults to all subaddresses) - * @param {int[]} config.subaddressIndices - source subaddress indices to sweep from (optional) - * @param {boolean} config.relay - relay the transactions to peers to commit to the blockchain (default false) - * @param {MoneroTxPriority} config.priority - transaction priority (default MoneroTxPriority.NORMAL) - * @param {BigInteger|string} config.unlockTime - minimum height or timestamp for the transactions to unlock (default 0) - * @param {boolean} config.sweepEachSubaddress - sweep each subaddress individually if true (default false) - * @return {MoneroTxWallet[]} the created transactions - */ - async sweepUnlocked(config) { - throw new MoneroError("Not supported"); - } - - /** - *

Sweep all unmixable dust outputs back to the wallet to make them easier to spend and mix.

- * - *

NOTE: Dust only exists pre RCT, so this method will throw "no dust to sweep" on new wallets.

- * - * @param {boolean} relay - specifies if the resulting transaction should be relayed (default false) - * @return {MoneroTxWallet[]} the created transactions - */ - async sweepDust(relay) { - throw new MoneroError("Not supported"); - } - - /** - * Relay a previously created transaction. - * - * @param {(MoneroTxWallet|string)} txOrMetadata - transaction or its metadata to relay - * @return {string} the hash of the relayed tx - */ - async relayTx(txOrMetadata) { - return (await this.relayTxs([txOrMetadata]))[0]; - } - - /** - * Relay previously created transactions. - * - * @param {(MoneroTxWallet[]|string[])} txsOrMetadatas - transactions or their metadata to relay - * @return {string[]} the hashes of the relayed txs - */ - async relayTxs(txsOrMetadatas) { - throw new MoneroError("Not supported"); - } - - /** - * Describe a tx set from unsigned tx hex. - * - * @param {string} unsignedTxHex - unsigned tx hex - * @return {MoneroTxSet} the tx set containing structured transactions - */ - async describeUnsignedTxSet(unsignedTxHex) { - return this.describeTxSet(new MoneroTxSet().setUnsignedTxHex(unsignedTxHex)); - } - - /** - * Describe a tx set from multisig tx hex. - * - * @param {string} multisigTxHex - multisig tx hex - * @return {MoneroTxSet} the tx set containing structured transactions - */ - async describeMultisigTxSet(multisigTxHex) { - return this.describeTxSet(new MoneroTxSet().setMultisigTxHex(multisigTxHex)); - } - - /** - * Describe a tx set containing unsigned or multisig tx hex to a new tx set containing structured transactions. - * - * @param {MoneroTxSet} txSet - a tx set containing unsigned or multisig tx hex - * @return {MoneroTxSet} the tx set containing structured transactions - */ - async describeTxSet(txSet) { - throw new MoneroError("Not supported"); - } - - /** - * Sign unsigned transactions from a view-only wallet. - * - * @param {string} unsignedTxHex - unsigned transaction hex from when the transactions were created - * @return {string} the signed transaction hex - */ - async signTxs(unsignedTxHex) { - throw new MoneroError("Not supported"); - } - - /** - * Submit signed transactions from a view-only wallet. - * - * @param {string} signedTxHex - signed transaction hex from signTxs() - * @return {string[]} the resulting transaction hashes - */ - async submitTxs(signedTxHex) { - throw new MoneroError("Not supported"); - } - - /** - * Sign a message. - * - * @param {string} message - the message to sign - * @param {MoneroMessageSignatureType} signatureType - sign with spend key or view key (default spend key) - * @param {int} accountIdx - the account index of the message signature (default 0) - * @param {int} subaddressIdx - the subaddress index of the message signature (default 0) - * @return {string} the signature - */ - async signMessage(message, signatureType, accountIdx, subaddressIdx) { - throw new MoneroError("Not supported"); - } - - /** - * Verify a signature on a message. - * - * @param {string} message - signed message - * @param {string} address - signing address - * @param {string} signature - signature - * @return {MoneroMessageSignatureResult} true if the signature is good, false otherwise - */ - async verifyMessage(message, address, signature) { - throw new MoneroError("Not supported"); - } - - /** - * Get a transaction's secret key from its hash. - * - * @param {string} txHash - transaction's hash - * @return {string} - transaction's secret key - */ - async getTxKey(txHash) { - throw new MoneroError("Not supported"); - } - - /** - * Check a transaction in the blockchain with its secret key. - * - * @param {string} txHash - transaction to check - * @param {string} txKey - transaction's secret key - * @param {string} address - destination public address of the transaction - * @return {MoneroCheckTx} the result of the check - */ - async checkTxKey(txHash, txKey, address) { - throw new MoneroError("Not supported"); - } - - /** - * Get a transaction signature to prove it. - * - * @param {string} txHash - transaction to prove - * @param {string} address - destination public address of the transaction - * @param {string} message - message to include with the signature to further authenticate the proof (optional) - * @return {string} the transaction signature - */ - async getTxProof(txHash, address, message) { - throw new MoneroError("Not supported"); - } - - /** - * Prove a transaction by checking its signature. - * - * @param {string} txHash - transaction to prove - * @param {string} address - destination public address of the transaction - * @param {string} message - message included with the signature to further authenticate the proof (optional) - * @param {string} signature - transaction signature to confirm - * @return {MoneroCheckTx} the result of the check - */ - async checkTxProof(txHash, address, message, signature) { - throw new MoneroError("Not supported"); - } - - /** - * Generate a signature to prove a spend. Unlike proving a transaction, it does not require the destination public address. - * - * @param {string} txHash - transaction to prove - * @param {string} message - message to include with the signature to further authenticate the proof (optional) - * @return {string} the transaction signature - */ - async getSpendProof(txHash, message) { - throw new MoneroError("Not supported"); - } - - /** - * Prove a spend using a signature. Unlike proving a transaction, it does not require the destination public address. - * - * @param {string} txHash - transaction to prove - * @param {string} message - message included with the signature to further authenticate the proof (optional) - * @param {string} signature - transaction signature to confirm - * @return {boolean} true if the signature is good, false otherwise - */ - async checkSpendProof(txHash, message, signature) { - throw new MoneroError("Not supported"); - } - - /** - * Generate a signature to prove the entire balance of the wallet. - * - * @param message - message included with the signature to further authenticate the proof (optional) - * @return the reserve proof signature - */ - async getReserveProofWallet(message) { - throw new MoneroError("Not supported"); - } - - /** - * Generate a signature to prove an available amount in an account. - * - * @param {int} accountIdx - account to prove ownership of the amount - * @param {BigInteger} amount - minimum amount to prove as available in the account - * @param {string} message - message to include with the signature to further authenticate the proof (optional) - * @return {string} the reserve proof signature - */ - async getReserveProofAccount(accountIdx, amount, message) { - throw new MoneroError("Not supported"); - } - - /** - * Proves a wallet has a disposable reserve using a signature. - * - * @param {string} address - public wallet address - * @param {string} message - message included with the signature to further authenticate the proof (optional) - * @param {string} signature - reserve proof signature to check - * @return {MoneroCheckReserve} the result of checking the signature proof - */ - async checkReserveProof(address, message, signature) { - throw new MoneroError("Not supported"); - } - - /** - * Get a transaction note. - * - * @param {string} txHash - transaction to get the note of - * @return {string} the tx note - */ - async getTxNote(txHash) { - return (await this.getTxNotes([txHash]))[0]; - } - - /** - * Get notes for multiple transactions. - * - * @param {string[]} txHashes - hashes of the transactions to get notes for - * @return {string[]} notes for the transactions - */ - async getTxNotes(txHashes) { - throw new MoneroError("Not supported"); - } - - /** - * Set a note for a specific transaction. - * - * @param {string} txHash - hash of the transaction to set a note for - * @param {string} note - the transaction note - */ - async setTxNote(txHash, note) { - await this.setTxNotes([txHash], [note]); - } - - /** - * Set notes for multiple transactions. - * - * @param {string[]} txHashes - transactions to set notes for - * @param {string[]} notes - notes to set for the transactions - */ - async setTxNotes(txHashes, notes) { - throw new MoneroError("Not supported"); - } - - /** - * Get address book entries. - * - * @param {int[]} entryIndices - indices of the entries to get - * @return {MoneroAddressBookEntry[]} the address book entries - */ - async getAddressBookEntries(entryIndices) { - throw new MoneroError("Not supported"); - } - - /** - * Add an address book entry. - * - * @param {string} address - entry address - * @param {string} description - entry description (optional) - * @return {int} the index of the added entry - */ - async addAddressBookEntry(address, description) { - throw new MoneroError("Not supported"); - } - - /** - * Edit an address book entry. - * - * @param {number} index - index of the address book entry to edit - * @param {boolean} setAddress - specifies if the address should be updated - * @param {string} address - updated address - * @param {boolean} setDescription - specifies if the description should be updated - * @param {string} description - updated description - */ - async editAddressBookEntry(index, setAddress, address, setDescription, description) { - throw new MoneroError("Not supported"); - } - - /** - * Delete an address book entry. - * - * @param {int} entryIdx - index of the entry to delete - */ - async deleteAddressBookEntry(entryIdx) { - throw new MoneroError("Not supported"); - } - - /** - * Tag accounts. - * - * @param {string} tag - tag to apply to the specified accounts - * @param {int[]} accountIndices - indices of the accounts to tag - */ - async tagAccounts(tag, accountIndices) { - throw new MoneroError("Not supported"); - } - - /** - * Untag accounts. - * - * @param {int[]} accountIndices - indices of the accounts to untag - */ - async untagAccounts(accountIndices) { - throw new MoneroError("Not supported"); - } - - /** - * Return all account tags. - * - * @return {MoneroAccountTag[]} the wallet's account tags - */ - async getAccountTags() { - throw new MoneroError("Not supported"); - } - - /** - * Sets a human-readable description for a tag. - * - * @param {string} tag - tag to set a description for - * @param {string} label - label to set for the tag - */ - async setAccountTagLabel(tag, label) { - throw new MoneroError("Not supported"); - } - - /** - * Creates a payment URI from a send configuration. - * - * @param {MoneroTxConfig} config - specifies configuration for a potential tx - * @return {string} the payment uri - */ - async getPaymentUri(config) { - throw new MoneroError("Not supported"); - } - - /** - * Parses a payment URI to a tx config. - * - * @param {string} uri - payment uri to parse - * @return {MoneroTxConfig} the send configuration parsed from the uri - */ - async parsePaymentUri(uri) { - throw new MoneroError("Not supported"); - } - - /** - * Get an attribute. - * - * @param {string} key - attribute to get the value of - * @return {string} the attribute's value - */ - async getAttribute(key) { - throw new MoneroError("Not supported"); - } - - /** - * Set an arbitrary attribute. - * - * @param {string} key - attribute key - * @param {string} val - attribute value - */ - async setAttribute(key, val) { - throw new MoneroError("Not supported"); - } - - /** - * Start mining. - * - * @param {int} numThreads - number of threads created for mining (optional) - * @param {boolean} backgroundMining - specifies if mining should occur in the background (optional) - * @param {boolean} ignoreBattery - specifies if the battery should be ignored for mining (optional) - */ - async startMining(numThreads, backgroundMining, ignoreBattery) { - throw new MoneroError("Not supported"); - } - - /** - * Stop mining. - */ - async stopMining() { - throw new MoneroError("Not supported"); - } - - /** - * Indicates if importing multisig data is needed for returning a correct balance. - * - * @return {boolean} true if importing multisig data is needed for returning a correct balance, false otherwise - */ - async isMultisigImportNeeded() { - throw new MoneroError("Not supported"); - } - - /** - * Indicates if this wallet is a multisig wallet. - * - * @return {boolean} true if this is a multisig wallet, false otherwise - */ - async isMultisig() { - return (await this.getMultisigInfo()).isMultisig(); - } - - /** - * Get multisig info about this wallet. - * - * @return {MoneroMultisigInfo} multisig info about this wallet - */ - async getMultisigInfo() { - throw new MoneroError("Not supported"); - } - - /** - * Get multisig info as hex to share with participants to begin creating a - * multisig wallet. - * - * @return {string} this wallet's multisig hex to share with participants - */ - async prepareMultisig() { - throw new MoneroError("Not supported"); - } - - /** - * Make this wallet multisig by importing multisig hex from participants. - * - * @param {String[]} multisigHexes - multisig hex from each participant - * @param {int} threshold - number of signatures needed to sign transfers - * @param {string} password - wallet password - * @return {string} this wallet's multisig hex to share with participants - */ - async makeMultisig(multisigHexes, threshold, password) { - throw new MoneroError("Not supported"); - } - - /** - * Exchange multisig hex with participants in a M/N multisig wallet. - * - * This process must be repeated with participants exactly N-M times. - * - * @param {string[]} multisigHexes are multisig hex from each participant - * @param {string} password - wallet's password // TODO monero-project: redundant? wallet is created with password - * @return {MoneroMultisigInitResult} the result which has the multisig's address xor this wallet's multisig hex to share with participants iff not done - */ - async exchangeMultisigKeys(multisigHexes, password) { - throw new MoneroError("Not supported"); - } - - /** - * Export this wallet's multisig info as hex for other participants. - * - * @return {string} this wallet's multisig info as hex for other participants - */ - async exportMultisigHex() { - throw new MoneroError("Not supported?"); - } - - /** - * Import multisig info as hex from other participants. - * - * @param {string[]} multisigHexes - multisig hex from each participant - * @return {int} the number of outputs signed with the given multisig hex - */ - async importMultisigHex(multisigHexes) { - throw new MoneroError("Not supported"); - } - - /** - * Sign multisig transactions from a multisig wallet. - * - * @param {string} multisigTxHex - unsigned multisig transactions as hex - * @return {MoneroMultisigSignResult} the result of signing the multisig transactions - */ - async signMultisigTxHex(multisigTxHex) { - throw new MoneroError("Not supported"); - } - - /** - * Submit signed multisig transactions from a multisig wallet. - * - * @param {string} signedMultisigTxHex - signed multisig hex returned from signMultisigTxHex() - * @return {string[]} the resulting transaction hashes - */ - async submitMultisigTxHex(signedMultisigTxHex) { - throw new MoneroError("Not supported"); - } - - /** - * Change the wallet password. - * - * @param {string} oldPassword - the wallet's old password - * @param {string} newPassword - the wallet's new password - */ - async changePassword(oldPassword, newPassword) { - throw new MoneroError("Not supported"); - } - - /** - * Save the wallet at its current path. - */ - save() { - throw new MoneroError("Not supported"); - } - - /** - * Optionally save then close the wallet. - * - * @param {boolean} save - specifies if the wallet should be saved before being closed (default false) - */ - async close(save) { - if (this._connectionManager) this._connectionManager.removeListener(this._connectionManagerListener); - this._connectionManager = undefined; - this._connectionManagerListener = undefined; - } - - /** - * Indicates if this wallet is closed or not. - * - * @return {boolean} true if the wallet is closed, false otherwise - */ - async isClosed() { - throw new MoneroError("Not supported"); - } - - // -------------------------------- PRIVATE --------------------------------- - - static _normalizeTxQuery(query) { - if (query instanceof MoneroTxQuery) query = query.copy(); - else if (Array.isArray(query)) query = new MoneroTxQuery().setHashes(query); - else { - query = Object.assign({}, query); - query = new MoneroTxQuery(query); - } - if (query.getBlock() === undefined) query.setBlock(new MoneroBlock().setTxs([query])); - if (query.getInputQuery()) query.getInputQuery().setTxQuery(query); - if (query.getOutputQuery()) query.getOutputQuery().setTxQuery(query); - return query; - } - - static _normalizeTransferQuery(query) { - if (query === undefined) query = new MoneroTransferQuery(); - else if (query instanceof MoneroTransferQuery) { - if (query.getTxQuery() === undefined) query = query.copy(); - else { - let txQuery = query.getTxQuery().copy(); - if (query.getTxQuery().getTransferQuery() === query) query = txQuery.getTransferQuery(); - else { - assert.equal(query.getTxQuery().getTransferQuery(), undefined, "Transfer query's tx query must be circular reference or null"); - query = query.copy(); - query.setTxQuery(txQuery); - } - } - } else { - query = Object.assign({}, query); - query = new MoneroTransferQuery(query); - } - if (query.getTxQuery() === undefined) query.setTxQuery(new MoneroTxQuery()); - query.getTxQuery().setTransferQuery(query); - if (query.getTxQuery().getBlock() === undefined) query.getTxQuery().setBlock(new MoneroBlock().setTxs([query.getTxQuery()])); - return query; - } - - static _normalizeOutputQuery(query) { - if (query === undefined) query = new MoneroOutputQuery(); - else if (query instanceof MoneroOutputQuery) { - if (query.getTxQuery() === undefined) query = query.copy(); - else { - let txQuery = query.getTxQuery().copy(); - if (query.getTxQuery().getOutputQuery() === query) query = txQuery.getOutputQuery(); - else { - assert.equal(query.getTxQuery().getOutputQuery(), undefined, "Output query's tx query must be circular reference or null"); - query = query.copy(); - query.setTxQuery(txQuery); - } - } - } else { - query = Object.assign({}, query); - query = new MoneroOutputQuery(query); - } - if (query.getTxQuery() === undefined) query.setTxQuery(new MoneroTxQuery()); - query.getTxQuery().setOutputQuery(query); - if (query.getTxQuery().getBlock() === undefined) query.getTxQuery().setBlock(new MoneroBlock().setTxs([query.getTxQuery()])); - return query; - } - - static _normalizeCreateTxsConfig(config) { - if (config === undefined || !(config instanceof Object)) throw new MoneroError("Must provide MoneroTxConfig or equivalent JS object"); - config = new MoneroTxConfig(config); - assert(config.getDestinations() && config.getDestinations().length > 0, "Must provide destinations"); - assert.equal(config.getSweepEachSubaddress(), undefined); - assert.equal(config.getBelowAmount(), undefined); - return config; - } - - static _normalizeSweepOutputConfig(config) { - if (config === undefined || !(config instanceof Object)) throw new MoneroError("Must provide MoneroTxConfig or equivalent JS object"); - config = new MoneroTxConfig(config); - assert.equal(config.getSweepEachSubaddress(), undefined); - assert.equal(config.getBelowAmount(), undefined); - assert.equal(config.getCanSplit(), undefined, "Cannot split transactions when sweeping an output"); - if (!config.getDestinations() || config.getDestinations().length !== 1 || !config.getDestinations()[0].getAddress()) throw new MoneroError("Must provide exactly one destination address to sweep output to"); - if (config.getSubtractFeeFrom() && config.getSubtractFeeFrom().length > 0) throw new MoneroError("Sweep transfers do not support subtracting fees from destinations"); - return config; - } - - static _normalizeSweepUnlockedConfig(config) { - if (config === undefined || !(config instanceof Object)) throw new MoneroError("Must provide MoneroTxConfig or equivalent JS object"); - config = new MoneroTxConfig(config); - if (config.getDestinations() === undefined || config.getDestinations().length != 1) throw new MoneroError("Must provide exactly one destination to sweep to"); - if (config.getDestinations()[0].getAddress() === undefined) throw new MoneroError("Must provide destination address to sweep to"); - if (config.getDestinations()[0].getAmount() !== undefined) throw new MoneroError("Cannot provide amount in sweep config"); - if (config.getKeyImage() !== undefined) throw new MoneroError("Key image defined; use sweepOutput() to sweep an output by its key image"); - if (config.getSubaddressIndices() !== undefined && config.getSubaddressIndices().length === 0) config.setSubaddressIndices(undefined); - if (config.getAccountIndex() === undefined && config.getSubaddressIndices() !== undefined) throw new MoneroError("Must provide account index if subaddress indices are provided"); - return config; - } -} - -MoneroWallet.DEFAULT_LANGUAGE = "English"; - -module.exports = MoneroWallet; \ No newline at end of file diff --git a/src/main/js/wallet/MoneroWalletFull.js b/src/main/js/wallet/MoneroWalletFull.js deleted file mode 100644 index bcce206da..000000000 --- a/src/main/js/wallet/MoneroWalletFull.js +++ /dev/null @@ -1,2690 +0,0 @@ -const assert = require("assert"); -const BigInteger = require("../common/biginteger").BigInteger; -const GenUtils = require("../common/GenUtils"); -const LibraryUtils = require("../common/LibraryUtils"); -const TaskLooper = require("../common/TaskLooper"); -const MoneroAccount = require("./model/MoneroAccount"); -const MoneroAddressBookEntry = require("./model/MoneroAddressBookEntry"); -const MoneroBlock = require("../daemon/model/MoneroBlock"); -const MoneroCheckTx = require("./model/MoneroCheckTx"); -const MoneroCheckReserve = require("./model/MoneroCheckReserve"); -const MoneroDaemonRpc = require("../daemon/MoneroDaemonRpc"); -const MoneroError = require("../common/MoneroError"); -const MoneroIntegratedAddress = require("./model/MoneroIntegratedAddress"); -const MoneroKeyImage = require("../daemon/model/MoneroKeyImage"); -const MoneroKeyImageImportResult = require("./model/MoneroKeyImageImportResult"); -const MoneroMultisigInfo = require("./model/MoneroMultisigInfo"); -const MoneroMultisigInitResult = require("./model/MoneroMultisigInitResult"); -const MoneroMultisigSignResult = require("./model/MoneroMultisigSignResult"); -const MoneroNetworkType = require("../daemon/model/MoneroNetworkType"); -const MoneroOutputWallet = require("./model/MoneroOutputWallet"); -const MoneroRpcConnection = require("../common/MoneroRpcConnection"); -const MoneroSubaddress = require("./model/MoneroSubaddress"); -const MoneroSyncResult = require("./model/MoneroSyncResult"); -const MoneroTxConfig = require("./model/MoneroTxConfig"); -const MoneroTxSet = require("./model/MoneroTxSet"); -const MoneroTxWallet = require("./model/MoneroTxWallet"); -const MoneroWallet = require("./MoneroWallet"); -const MoneroWalletConfig = require("./model/MoneroWalletConfig"); -const MoneroWalletKeys = require("./MoneroWalletKeys"); -const MoneroWalletListener = require("./model/MoneroWalletListener"); -const MoneroMessageSignatureType = require("./model/MoneroMessageSignatureType"); -const MoneroMessageSignatureResult = require("./model/MoneroMessageSignatureResult"); - -/** - * Implements a Monero wallet using fully client-side WebAssembly bindings to monero-project's wallet2 in C++. - * - * @extends {MoneroWalletKeys} - * @implements {MoneroWallet} - * @hideconstructor - */ -class MoneroWalletFull extends MoneroWalletKeys { - - // --------------------------- STATIC UTILITIES ----------------------------- - - /** - * Check if a wallet exists at a given path. - * - * @param {string} path - path of the wallet on the file system - * @param {fs} - Node.js compatible file system to use (optional, defaults to disk if nodejs) - * @return {boolean} true if a wallet exists at the given path, false otherwise - */ - static walletExists(path, fs) { - assert(path, "Must provide a path to look for a wallet"); - if (!fs) fs = MoneroWalletFull._getFs(); - if (!fs) throw new MoneroError("Must provide file system to check if wallet exists"); - let exists = fs.existsSync(path + ".keys"); - LibraryUtils.log(1, "Wallet exists at " + path + ": " + exists); - return exists; - } - - /** - *

Open an existing wallet using WebAssembly bindings to wallet2.h.

- * - *

Examples:

- * - * - * let wallet1 = await MoneroWalletFull.openWallet(
- *    "./wallets/wallet1",
- *    "supersecretpassword",
- *    MoneroNetworkType.STAGENET,
- *    "http://localhost:38081" // daemon uri
- * );

- * - * let wallet2 = await MoneroWalletFull.openWallet({
- *    path: "./wallets/wallet2",
- *    password: "supersecretpassword",
- *    networkType: MoneroNetworkType.STAGENET,
- *    serverUri: "http://localhost:38081", // daemon configuration
- *    serverUsername: "superuser",
- *    serverPassword: "abctesting123"
- * }); - *
- * - * @param {MoneroWalletConfig|object|string} configOrPath - MoneroWalletConfig or equivalent config object or a path to a wallet to open - * @param {string} configOrPath.path - path of the wallet to open (optional if 'keysData' provided) - * @param {string} configOrPath.password - password of the wallet to open - * @param {string|number} configOrPath.networkType - network type of the wallet to open (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) - * @param {Uint8Array} configOrPath.keysData - wallet keys data to open (optional if path provided) - * @param {Uint8Array} configOrPath.cacheData - wallet cache data to open (optional) - * @param {MoneroRpcConnection|object} configOrPath.server - MoneroRpcConnection or equivalent JS object configuring the daemon connection (optional) - * @param {string} configOrPath.serverUri - uri of the wallet's daemon (optional) - * @param {string} configOrPath.serverUsername - username to authenticate with the daemon (optional) - * @param {string} configOrPath.serverPassword - password to authenticate with the daemon (optional) - * @param {boolean} configOrPath.rejectUnauthorized - reject self-signed server certificates if true (default true) - * @param {boolean} configOrPath.proxyToWorker - proxies wallet operations to a worker in order to not block the main thread (default true) - * @param {fs} configOrPath.fs - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) - * @param {string} password - password of the wallet to open - * @param {string|number} networkType - network type of the wallet to open - * @param {string|MoneroRpcConnection} daemonUriOrConnection - daemon URI or MoneroRpcConnection - * @param {boolean} proxyToWorker - proxies wallet operations to a worker in order to not block the main thread (default true) - * @param {fs} fs - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) - * @return {MoneroWalletFull} the opened wallet - */ - static async openWallet(configOrPath, password, networkType, daemonUriOrConnection, proxyToWorker, fs) { - - // normalize and validate config - let config; - if (typeof configOrPath === "object") { - config = configOrPath instanceof MoneroWalletConfig ? configOrPath : new MoneroWalletConfig(configOrPath); - if (password !== undefined || networkType !== undefined || daemonUriOrConnection !== undefined || proxyToWorker !== undefined || fs !== undefined) throw new MoneroError("Can specify config object or params but not both when opening WASM wallet") - } else { - config = new MoneroWalletConfig().setPath(configOrPath).setPassword(password).setNetworkType(networkType).setProxyToWorker(proxyToWorker).setFs(fs); - if (typeof daemonUriOrConnection === "object") config.setServer(daemonUriOrConnection); - else config.setServerUri(daemonUriOrConnection); - } - if (config.getProxyToWorker() === undefined) config.setProxyToWorker(true); - if (config.getSeed() !== undefined) throw new MoneroError("Cannot specify seed when opening wallet"); - if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot specify seed offset when opening wallet"); - if (config.getPrimaryAddress() !== undefined) throw new MoneroError("Cannot specify primary address when opening wallet"); - if (config.getPrivateViewKey() !== undefined) throw new MoneroError("Cannot specify private view key when opening wallet"); - if (config.getPrivateSpendKey() !== undefined) throw new MoneroError("Cannot specify private spend key when opening wallet"); - if (config.getRestoreHeight() !== undefined) throw new MoneroError("Cannot specify restore height when opening wallet"); - if (config.getLanguage() !== undefined) throw new MoneroError("Cannot specify language when opening wallet"); - if (config.getSaveCurrent() === true) throw new MoneroError("Cannot save current wallet when opening JNI wallet"); - - // read wallet data from disk if not provided - if (!config.getKeysData()) { - let fs = config.getFs() ? config.getFs() : MoneroWalletFull._getFs(); - if (!fs) throw new MoneroError("Must provide file system to read wallet data from"); - if (!this.walletExists(config.getPath(), fs)) throw new MoneroError("Wallet does not exist at path: " + config.getPath()); - config.setKeysData(fs.readFileSync(config.getPath() + ".keys")); - config.setCacheData(fs.existsSync(config.getPath()) ? fs.readFileSync(config.getPath()) : ""); - } - - // open wallet from data - return MoneroWalletFull._openWalletData(config.getPath(), config.getPassword(), config.getNetworkType(), config.getKeysData(), config.getCacheData(), config.getServer(), config.getProxyToWorker(), config.getFs()); - } - - /** - *

Create a wallet using WebAssembly bindings to wallet2.h.

- * - *

Example:

- * - * - * let wallet = await MoneroWalletFull.createWallet({
- *    path: "./test_wallets/wallet1", // leave blank for in-memory wallet
- *    password: "supersecretpassword",
- *    networkType: MoneroNetworkType.STAGENET,
- *    seed: "coexist igloo pamphlet lagoon...",
- *    restoreHeight: 1543218,
- *    server: new MoneroRpcConnection("http://localhost:38081", "daemon_user", "daemon_password_123"),
- * }); - *
- * - * @param {object|MoneroWalletConfig} config - MoneroWalletConfig or equivalent config object - * @param {string} config.path - path of the wallet to create (optional, in-memory wallet if not given) - * @param {string} config.password - password of the wallet to create - * @param {string|number} config.networkType - network type of the wallet to create (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) - * @param {string} config.seed - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) - * @param {string} config.seedOffset - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed phrase - * @param {boolean} config.isMultisig - restore multisig wallet from seed - * @param {string} config.primaryAddress - primary address of the wallet to create (only provide if restoring from keys) - * @param {string} config.privateViewKey - private view key of the wallet to create (optional) - * @param {string} config.privateSpendKey - private spend key of the wallet to create (optional) - * @param {number} config.restoreHeight - block height to start scanning from (defaults to 0 unless generating random wallet) - * @param {string} config.language - language of the wallet's seed phrase (defaults to "English" or auto-detected) - * @param {number} config.accountLookahead - number of accounts to scan (optional) - * @param {number} config.subaddressLookahead - number of subaddresses to scan per account (optional) - * @param {MoneroRpcConnection|object} config.server - MoneroRpcConnection or equivalent JS object providing daemon configuration (optional) - * @param {string} config.serverUri - uri of the wallet's daemon (optional) - * @param {string} config.serverUsername - username to authenticate with the daemon (optional) - * @param {string} config.serverPassword - password to authenticate with the daemon (optional) - * @param {MoneroConnectionManager} config.connectionManager - manage connections to monerod (optional) - * @param {boolean} config.rejectUnauthorized - reject self-signed server certificates if true (defaults to true) - * @param {boolean} config.proxyToWorker - proxies wallet operations to a worker in order to not block the main thread (default true) - * @param {fs} config.fs - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) - * @return {MoneroWalletFull} the created wallet - */ - static async createWallet(config) { - - // normalize and validate config - if (config === undefined) throw new MoneroError("Must provide config to create wallet"); - config = config instanceof MoneroWalletConfig ? config : new MoneroWalletConfig(config); - if (config.getSeed() !== undefined && (config.getPrimaryAddress() !== undefined || config.getPrivateViewKey() !== undefined || config.getPrivateSpendKey() !== undefined)) { - throw new MoneroError("Wallet may be initialized with a seed or keys but not both"); - } // TODO: factor this much out to common - if (config.getNetworkType() === undefined) throw new MoneroError("Must provide a networkType: 'mainnet', 'testnet' or 'stagenet'"); - MoneroNetworkType.validate(config.getNetworkType()); - if (config.getSaveCurrent() === true) throw new MoneroError("Cannot save current wallet when creating full WASM wallet"); - if (config.getPath() === undefined) config.setPath(""); - if (config.getPath() && MoneroWalletFull.walletExists(config.getPath(), config.getFs())) throw new MoneroError("Wallet already exists: " + config.getPath()); - if (config.getPassword() === undefined) config.setPassword(""); - - // set server from connection manager if provided - if (config.getConnectionManager()) { - if (config.getServer()) throw new MoneroError("Wallet can be initialized with a server or connection manager but not both"); - config.setServer(config.getConnectionManager().getConnection()); - } - - // create wallet - let wallet; - if (config.getSeed() !== undefined) { - if (config.getLanguage() !== undefined) throw new MoneroError("Cannot provide language when creating wallet from seed"); - wallet = await MoneroWalletFull._createWalletFromSeed(config); - } else if (config.getPrivateSpendKey() !== undefined || config.getPrimaryAddress() !== undefined) { - if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating wallet from keys"); - wallet = await MoneroWalletFull._createWalletFromKeys(config); - } else { - if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating random wallet"); - if (config.getRestoreHeight() !== undefined) throw new MoneroError("Cannot provide restoreHeight when creating random wallet"); - wallet = await MoneroWalletFull._createWalletRandom(config); - } - await wallet.setConnectionManager(config.getConnectionManager()); - return wallet; - } - - static async _createWalletFromSeed(config) { - if (config.getProxyToWorker() === undefined) config.setProxyToWorker(true); - if (config.getProxyToWorker()) return MoneroWalletFullProxy._createWallet(config); - - // validate and normalize params - let daemonConnection = config.getServer(); - let rejectUnauthorized = daemonConnection ? daemonConnection.getRejectUnauthorized() : true; - if (config.getRestoreHeight() === undefined) config.setRestoreHeight(0); - if (config.getSeedOffset() === undefined) config.setSeedOffset(""); - - // load full wasm module - let module = await LibraryUtils.loadFullModule(); - - // create wallet in queue - let wallet = await module.queueTask(async function() { - return new Promise(function(resolve, reject) { - - // register fn informing if unauthorized reqs should be rejected - let rejectUnauthorizedFnId = GenUtils.getUUID(); - LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, function() { return rejectUnauthorized }); - - // define callback for wasm - let callbackFn = async function(cppAddress) { - if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); - else resolve(new MoneroWalletFull(cppAddress, config.getPath(), config.getPassword(), config.getFs(), config.getRejectUnauthorized(), rejectUnauthorizedFnId)); - }; - - // create wallet in wasm and invoke callback when done - module.create_full_wallet(JSON.stringify(config.toJson()), rejectUnauthorizedFnId, callbackFn); - }); - }); - - // save wallet - if (config.getPath()) await wallet.save(); - return wallet; - } - - static async _createWalletFromKeys(config) { - if (config.getProxyToWorker() === undefined) config.setProxyToWorker(true); - if (config.getProxyToWorker()) return MoneroWalletFullProxy._createWallet(config); - - // validate and normalize params - MoneroNetworkType.validate(config.getNetworkType()); - if (config.getPrimaryAddress() === undefined) config.setPrimaryAddress(""); - if (config.getPrivateViewKey() === undefined) config.setPrivateViewKey(""); - if (config.getPrivateSpendKey() === undefined) config.setPrivateSpendKey(""); - let daemonConnection = config.getServer(); - let rejectUnauthorized = daemonConnection ? daemonConnection.getRejectUnauthorized() : true; - if (config.getRestoreHeight() === undefined) config.setRestoreHeight(0); - if (config.getLanguage() === undefined) config.setLanguage("English"); - - // load full wasm module - let module = await LibraryUtils.loadFullModule(); - - // create wallet in queue - let wallet = await module.queueTask(async function() { - return new Promise(function(resolve, reject) { - - // register fn informing if unauthorized reqs should be rejected - let rejectUnauthorizedFnId = GenUtils.getUUID(); - LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, function() { return rejectUnauthorized }); - - // define callback for wasm - let callbackFn = async function(cppAddress) { - if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); - else resolve(new MoneroWalletFull(cppAddress, config.getPath(), config.getPassword(), config.getFs(), config.getRejectUnauthorized(), rejectUnauthorizedFnId)); - }; - - // create wallet in wasm and invoke callback when done - module.create_full_wallet(JSON.stringify(config.toJson()), rejectUnauthorizedFnId, callbackFn); - }); - }); - - // save wallet - if (config.getPath()) await wallet.save(); - return wallet; - } - - static async _createWalletRandom(config) { - if (config.getProxyToWorker() === undefined) config.setProxyToWorker(true); - if (config.getProxyToWorker()) return MoneroWalletFullProxy._createWallet(config); - - // validate and normalize params - if (config.getLanguage() === undefined) config.setLanguage("English"); - let daemonConnection = config.getServer(); - let rejectUnauthorized = daemonConnection ? daemonConnection.getRejectUnauthorized() : true; - - // load wasm module - let module = await LibraryUtils.loadFullModule(); - - // create wallet in queue - let wallet = await module.queueTask(async function() { - return new Promise(function(resolve, reject) { - - // register fn informing if unauthorized reqs should be rejected - let rejectUnauthorizedFnId = GenUtils.getUUID(); - LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, function() { return rejectUnauthorized }); - - // define callback for wasm - let callbackFn = async function(cppAddress) { - if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); - else resolve(new MoneroWalletFull(cppAddress, config.getPath(), config.getPassword(), config.getFs(), config.getRejectUnauthorized(), rejectUnauthorizedFnId)); - }; - - // create wallet in wasm and invoke callback when done - module.create_full_wallet(JSON.stringify(config.toJson()), rejectUnauthorizedFnId, callbackFn); - }); - }); - - // save wallet - if (config.getPath()) await wallet.save(); - return wallet; - } - - static async getSeedLanguages() { - let module = await LibraryUtils.loadFullModule(); - return module.queueTask(async function() { - return JSON.parse(module.get_keys_wallet_seed_languages()).languages; - }); - } - - // --------------------------- INSTANCE METHODS ----------------------------- - - /** - * Internal constructor which is given the memory address of a C++ wallet - * instance. - * - * This method should not be called externally but should be called through - * static wallet creation utilities in this class. - * - * @param {int} cppAddress - address of the wallet instance in C++ - * @param {string} path - path of the wallet instance - * @param {string} password - password of the wallet instance - * @param {FileSystem} fs - node.js-compatible file system to read/write wallet files - * @param {boolean} rejectUnauthorized - specifies if unauthorized requests (e.g. self-signed certificates) should be rejected - * @param {string} rejectUnauthorizedFnId - unique identifier for http_client_wasm to query rejectUnauthorized - */ - constructor(cppAddress, path, password, fs, rejectUnauthorized, rejectUnauthorizedFnId) { - super(cppAddress); - this._path = path; - this._password = password; - this._listeners = []; - this._fs = fs ? fs : (path ? MoneroWalletFull._getFs() : undefined); - this._isClosed = false; - this._fullListener = new WalletFullListener(this); // receives notifications from wasm c++ - this._fullListenerHandle = 0; // memory address of the wallet listener in c++ - this._rejectUnauthorized = rejectUnauthorized; - this._rejectUnauthorizedConfigId = rejectUnauthorizedFnId; - this._syncPeriodInMs = MoneroWalletFull.DEFAULT_SYNC_PERIOD_IN_MS; - let that = this; - LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, function() { return that._rejectUnauthorized }); // register fn informing if unauthorized reqs should be rejected - } - - // ------------ WALLET METHODS SPECIFIC TO WASM IMPLEMENTATION -------------- - - /** - * Get the maximum height of the peers the wallet's daemon is connected to. - * - * @return {number} the maximum height of the peers the wallet's daemon is connected to - */ - async getDaemonMaxPeerHeight() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(resp) { - resolve(resp); - } - - // call wasm and invoke callback when done - that._module.get_daemon_max_peer_height(that._cppAddress, callbackFn); - }); - }); - } - - /** - * Indicates if the wallet's daemon is synced with the network. - * - * @return {boolean} true if the daemon is synced with the network, false otherwise - */ - async isDaemonSynced() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(resp) { - resolve(resp); - } - - // call wasm and invoke callback when done - that._module.is_daemon_synced(that._cppAddress, callbackFn); - }); - }); - } - - /** - * Indicates if the wallet is synced with the daemon. - * - * @return {boolean} true if the wallet is synced with the daemon, false otherwise - */ - async isSynced() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(resp) { - resolve(resp); - } - - // call wasm and invoke callback when done - that._module.is_synced(that._cppAddress, callbackFn); - }); - }); - } - - /** - * Get the wallet's network type (mainnet, testnet, or stagenet). - * - * @return {MoneroNetworkType} the wallet's network type - */ - async getNetworkType() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.get_network_type(that._cppAddress); - }); - } - - /** - * Get the height of the first block that the wallet scans. - * - * @return {number} the height of the first block that the wallet scans - */ - async getRestoreHeight() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.get_restore_height(that._cppAddress); - }); - } - - /** - * Set the height of the first block that the wallet scans. - * - * @param {number} restoreHeight - height of the first block that the wallet scans - */ - async setRestoreHeight(restoreHeight) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.set_restore_height(that._cppAddress, restoreHeight); - }); - } - - /** - * Move the wallet from its current path to the given path. - * - * @param {string} path - the wallet's destination path - */ - async moveTo(path) { - return MoneroWalletFull._moveTo(path, this); - } - - // -------------------------- COMMON WALLET METHODS ------------------------- - - async addListener(listener) { - this._assertNotClosed(); - assert(listener instanceof MoneroWalletListener, "Listener must be instance of MoneroWalletListener"); - this._listeners.push(listener); - await this._refreshListening(); - } - - async removeListener(listener) { - this._assertNotClosed(); - let idx = this._listeners.indexOf(listener); - if (idx > -1) this._listeners.splice(idx, 1); - else throw new MoneroError("Listener is not registered with wallet"); - await this._refreshListening(); - } - - getListeners() { - this._assertNotClosed(); - return this._listeners; - } - - async setDaemonConnection(uriOrRpcConnection) { - this._assertNotClosed(); - - // normalize connection - let connection = !uriOrRpcConnection ? undefined : uriOrRpcConnection instanceof MoneroRpcConnection ? uriOrRpcConnection : new MoneroRpcConnection(uriOrRpcConnection); - let uri = connection && connection.getUri() ? connection.getUri() : ""; - let username = connection && connection.getUsername() ? connection.getUsername() : ""; - let password = connection && connection.getPassword() ? connection.getPassword() : ""; - let rejectUnauthorized = connection ? connection.getRejectUnauthorized() : undefined; - this._rejectUnauthorized = rejectUnauthorized; // persist locally - - // set connection in queue - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(resp) { resolve(); } - - // call wasm and invoke callback when done - that._module.set_daemon_connection(that._cppAddress, uri, username, password, callbackFn); - }); - }); - } - - async getDaemonConnection() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let connectionContainerStr = that._module.get_daemon_connection(that._cppAddress); - if (!connectionContainerStr) resolve(); - else { - let jsonConnection = JSON.parse(connectionContainerStr); - resolve(new MoneroRpcConnection(jsonConnection.uri, jsonConnection.username, jsonConnection.password, that._rejectUnauthorized)); - } - }); - }); - } - - async isConnectedToDaemon() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(resp) { - resolve(resp); - } - - // call wasm and invoke callback when done - that._module.is_connected_to_daemon(that._cppAddress, callbackFn); - }); - }); - } - - async getVersion() { - this._assertNotClosed(); - throw new MoneroError("Not implemented"); - } - - async getPath() { - this._assertNotClosed(); - return this._path; - } - - async getIntegratedAddress(standardAddress, paymentId) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - try { - let result = that._module.get_integrated_address(that._cppAddress, standardAddress ? standardAddress : "", paymentId ? paymentId : ""); - if (result.charAt(0) !== "{") throw new MoneroError(result); - return new MoneroIntegratedAddress(JSON.parse(result)); - } catch (err) { - if (err.message.includes("Invalid payment ID")) throw new MoneroError("Invalid payment ID: " + paymentId); - throw new MoneroError(err.message); - } - }); - } - - async decodeIntegratedAddress(integratedAddress) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - try { - let result = that._module.decode_integrated_address(that._cppAddress, integratedAddress); - if (result.charAt(0) !== "{") throw new MoneroError(result); - return new MoneroIntegratedAddress(JSON.parse(result)); - } catch (err) { - throw new MoneroError(err.message); - } - }); - } - - async getHeight() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(resp) { - resolve(resp); - } - - // call wasm and invoke callback when done - that._module.get_height(that._cppAddress, callbackFn); - }); - }); - } - - async getDaemonHeight() { - this._assertNotClosed(); - if (!(await this.isConnectedToDaemon())) throw new MoneroError("Wallet is not connected to daemon"); - - // schedule task - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(resp) { - resolve(resp); - } - - // call wasm and invoke callback when done - that._module.get_daemon_height(that._cppAddress, callbackFn); - }); - }); - } - - async getHeightByDate(year, month, day) { - this._assertNotClosed(); - if (!(await this.isConnectedToDaemon())) throw new MoneroError("Wallet is not connected to daemon"); - - // schedule task - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(resp) { - if (typeof resp === "string") reject(new MoneroError(resp)); - else resolve(resp); - } - - // call wasm and invoke callback when done - that._module.get_height_by_date(that._cppAddress, year, month, day, callbackFn); - }); - }); - } - - /** - * Synchronize the wallet with the daemon as a one-time synchronous process. - * - * @param {MoneroWalletListener|number} listenerOrStartHeight - listener xor start height (defaults to no sync listener, the last synced block) - * @param {number} startHeight - startHeight if not given in first arg (defaults to last synced block) - * @param {bool} allowConcurrentCalls - allow other wallet methods to be processed simultaneously during sync (default false)

WARNING: enabling this option will crash wallet execution if another call makes a simultaneous network request. TODO: possible to sync wasm network requests in http_client_wasm.cpp? - */ - async sync(listenerOrStartHeight, startHeight, allowConcurrentCalls) { - this._assertNotClosed(); - if (!(await this.isConnectedToDaemon())) throw new MoneroError("Wallet is not connected to daemon"); - - // normalize params - startHeight = listenerOrStartHeight === undefined || listenerOrStartHeight instanceof MoneroWalletListener ? startHeight : listenerOrStartHeight; - let listener = listenerOrStartHeight instanceof MoneroWalletListener ? listenerOrStartHeight : undefined; - if (startHeight === undefined) startHeight = Math.max(await this.getHeight(), await this.getRestoreHeight()); - - // register listener if given - if (listener) await this.addListener(listener); - - // sync wallet - let err; - let result; - try { - let that = this; - result = await (allowConcurrentCalls ? syncWasm() : that._module.queueTask(async function() { return syncWasm(); })); - function syncWasm() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = async function(resp) { - if (resp.charAt(0) !== "{") reject(new MoneroError(resp)); - else { - let respJson = JSON.parse(resp); - resolve(new MoneroSyncResult(respJson.numBlocksFetched, respJson.receivedMoney)); - } - } - - // sync wallet in wasm and invoke callback when done - that._module.sync(that._cppAddress, startHeight, callbackFn); - }); - } - } catch (e) { - err = e; - } - - // unregister listener - if (listener) await this.removeListener(listener); - - // throw error or return - if (err) throw err; - return result; - } - - async startSyncing(syncPeriodInMs) { - this._assertNotClosed(); - if (!(await this.isConnectedToDaemon())) throw new MoneroError("Wallet is not connected to daemon"); - this._syncPeriodInMs = syncPeriodInMs === undefined ? MoneroWalletFull.DEFAULT_SYNC_PERIOD_IN_MS : syncPeriodInMs; - let that = this; - if (!this._syncLooper) this._syncLooper = new TaskLooper(async function() { await that._backgroundSync(); }) - this._syncLooper.start(this._syncPeriodInMs); - } - - async stopSyncing() { - this._assertNotClosed(); - if (this._syncLooper) this._syncLooper.stop(); - this._module.stop_syncing(this._cppAddress); // task is not queued so wallet stops immediately - } - - async scanTxs(txHashes) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = function(err) { - if (err) reject(new MoneroError(msg)); - else resolve(); - } - that._module.scan_txs(that._cppAddress, JSON.stringify({txHashes: txHashes}), callbackFn); - }); - }); - } - - async rescanSpent() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = function() { resolve(); } - that._module.rescan_spent(that._cppAddress, callbackFn); - }); - }); - } - - async rescanBlockchain() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = function() { resolve(); } - that._module.rescan_blockchain(that._cppAddress, callbackFn); - }); - }); - } - - async getBalance(accountIdx, subaddressIdx) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - - // get balance encoded in json string - let balanceStr; - if (accountIdx === undefined) { - assert(subaddressIdx === undefined, "Subaddress index must be undefined if account index is undefined"); - balanceStr = that._module.get_balance_wallet(that._cppAddress); - } else if (subaddressIdx === undefined) { - balanceStr = that._module.get_balance_account(that._cppAddress, accountIdx); - } else { - balanceStr = that._module.get_balance_subaddress(that._cppAddress, accountIdx, subaddressIdx); - } - - // parse json string to BigInteger - return BigInteger.parse(JSON.parse(GenUtils.stringifyBIs(balanceStr)).balance); - }); - } - - async getUnlockedBalance(accountIdx, subaddressIdx) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - - // get balance encoded in json string - let unlockedBalanceStr; - if (accountIdx === undefined) { - assert(subaddressIdx === undefined, "Subaddress index must be undefined if account index is undefined"); - unlockedBalanceStr = that._module.get_unlocked_balance_wallet(that._cppAddress); - } else if (subaddressIdx === undefined) { - unlockedBalanceStr = that._module.get_unlocked_balance_account(that._cppAddress, accountIdx); - } else { - unlockedBalanceStr = that._module.get_unlocked_balance_subaddress(that._cppAddress, accountIdx, subaddressIdx); - } - - // parse json string to BigInteger - return BigInteger.parse(JSON.parse(GenUtils.stringifyBIs(unlockedBalanceStr)).unlockedBalance); - }); - } - - async getAccounts(includeSubaddresses, tag) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let accountsStr = that._module.get_accounts(that._cppAddress, includeSubaddresses ? true : false, tag ? tag : ""); - let accounts = []; - for (let accountJson of JSON.parse(GenUtils.stringifyBIs(accountsStr)).accounts) { - accounts.push(MoneroWalletFull._sanitizeAccount(new MoneroAccount(accountJson))); - } - return accounts; - }); - } - - async getAccount(accountIdx, includeSubaddresses) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let accountStr = that._module.get_account(that._cppAddress, accountIdx, includeSubaddresses ? true : false); - let accountJson = JSON.parse(GenUtils.stringifyBIs(accountStr)); - return MoneroWalletFull._sanitizeAccount(new MoneroAccount(accountJson)); - }); - - } - - async createAccount(label) { - if (label === undefined) label = ""; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let accountStr = that._module.create_account(that._cppAddress, label); - let accountJson = JSON.parse(GenUtils.stringifyBIs(accountStr)); - return MoneroWalletFull._sanitizeAccount(new MoneroAccount(accountJson)); - }); - } - - async getSubaddresses(accountIdx, subaddressIndices) { - let args = {accountIdx: accountIdx, subaddressIndices: subaddressIndices === undefined ? [] : GenUtils.listify(subaddressIndices)}; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let subaddressesJson = JSON.parse(GenUtils.stringifyBIs(that._module.get_subaddresses(that._cppAddress, JSON.stringify(args)))).subaddresses; - let subaddresses = []; - for (let subaddressJson of subaddressesJson) subaddresses.push(MoneroWalletFull._sanitizeSubaddress(new MoneroSubaddress(subaddressJson))); - return subaddresses; - }); - } - - async createSubaddress(accountIdx, label) { - if (label === undefined) label = ""; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let subaddressStr = that._module.create_subaddress(that._cppAddress, accountIdx, label); - let subaddressJson = JSON.parse(GenUtils.stringifyBIs(subaddressStr)); - return MoneroWalletFull._sanitizeSubaddress(new MoneroSubaddress(subaddressJson)); - }); - } - - async setSubaddressLabel(accountIdx, subaddressIdx, label) { - if (label === undefined) label = ""; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - that._module.set_subaddress_label(that._cppAddress, accountIdx, subaddressIdx, label); - }); - } - - async getTxs(query) { - this._assertNotClosed(); - - // copy and normalize query up to block - query = MoneroWallet._normalizeTxQuery(query); - - // schedule task - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(blocksJsonStr) { - - // check for error - if (blocksJsonStr.charAt(0) !== "{") { - reject(new MoneroError(blocksJsonStr)); - return; - } - - // resolve with deserialized txs - try { - resolve(MoneroWalletFull._deserializeTxs(query, blocksJsonStr)); - } catch (err) { - reject(err); - } - } - - // call wasm and invoke callback when done - that._module.get_txs(that._cppAddress, JSON.stringify(query.getBlock().toJson()), callbackFn); - }); - }); - } - - async getTransfers(query) { - this._assertNotClosed(); - - // copy and normalize query up to block - query = MoneroWallet._normalizeTransferQuery(query); - - // return promise which resolves on callback - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(blocksJsonStr) { - - // check for error - if (blocksJsonStr.charAt(0) !== "{") { - reject(new MoneroError(blocksJsonStr)); - return; - } - - // resolve with deserialized transfers - try { - resolve(MoneroWalletFull._deserializeTransfers(query, blocksJsonStr)); - } catch (err) { - reject(err); - } - } - - // call wasm and invoke callback when done - that._module.get_transfers(that._cppAddress, JSON.stringify(query.getTxQuery().getBlock().toJson()), callbackFn); - }); - }); - } - - async getOutputs(query) { - this._assertNotClosed(); - - // copy and normalize query up to block - query = MoneroWallet._normalizeOutputQuery(query); - - // return promise which resolves on callback - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(blocksJsonStr) { - - // check for error - if (blocksJsonStr.charAt(0) !== "{") { - reject(new MoneroError(blocksJsonStr)); - return; - } - - // resolve with deserialized outputs - try { - resolve(MoneroWalletFull._deserializeOutputs(query, blocksJsonStr)); - } catch (err) { - reject(err); - } - } - - // call wasm and invoke callback when done - that._module.get_outputs(that._cppAddress, JSON.stringify(query.getTxQuery().getBlock().toJson()), callbackFn); - }); - }); - } - - async exportOutputs(all) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.export_outputs(that._cppAddress, all, function(outputsHex) { resolve(outputsHex); }); - }); - }); - } - - async importOutputs(outputsHex) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.import_outputs(that._cppAddress, outputsHex, function(numImported) { resolve(numImported); }); - }); - }); - } - - async exportKeyImages(all) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callback = function(keyImagesStr) { - if (keyImagesStr.charAt(0) !== '{') reject(new MoneroError(keyImagesStr)); // json expected, else error - let keyImages = []; - for (let keyImageJson of JSON.parse(GenUtils.stringifyBIs(keyImagesStr)).keyImages) keyImages.push(new MoneroKeyImage(keyImageJson)); - resolve(keyImages); - } - that._module.export_key_images(that._cppAddress, all, callback); - }); - }); - } - - async importKeyImages(keyImages) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callback = function(keyImageImportResultStr) { - resolve(new MoneroKeyImageImportResult(JSON.parse(GenUtils.stringifyBIs(keyImageImportResultStr)))); - } - that._module.import_key_images(that._cppAddress, JSON.stringify({keyImages: keyImages.map(keyImage => keyImage.toJson())}), callback); - }); - }); - } - - async getNewKeyImagesFromLastImport() { - this._assertNotClosed(); - throw new MoneroError("Not implemented"); - } - - async freezeOutput(keyImage) { - if (!keyImage) throw new MoneroError("Must specify key image to freeze"); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = function() { resolve(); } - that._module.freeze_output(that._cppAddress, keyImage, callbackFn); - }); - }); - } - - async thawOutput(keyImage) { - if (!keyImage) throw new MoneroError("Must specify key image to thaw"); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = function() { resolve(); } - that._module.thaw_output(that._cppAddress, keyImage, callbackFn); - }); - }); - } - - async isOutputFrozen(keyImage) { - if (!keyImage) throw new MoneroError("Must specify key image to check if frozen"); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = function(result) { resolve(result); } - that._module.is_output_frozen(that._cppAddress, keyImage, callbackFn); - }); - }); - } - - async createTxs(config) { - this._assertNotClosed(); - - // validate, copy, and normalize config - config = MoneroWallet._normalizeCreateTxsConfig(config); - if (config.getCanSplit() === undefined) config.setCanSplit(true); - - // return promise which resolves on callback - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(txSetJsonStr) { - if (txSetJsonStr.charAt(0) !== '{') reject(new MoneroError(txSetJsonStr)); // json expected, else error - else resolve(new MoneroTxSet(JSON.parse(GenUtils.stringifyBIs(txSetJsonStr))).getTxs()); - } - - // create txs in wasm and invoke callback when done - that._module.create_txs(that._cppAddress, JSON.stringify(config.toJson()), callbackFn); - }); - }); - } - - async sweepOutput(config) { - this._assertNotClosed(); - - // normalize and validate config - config = MoneroWallet._normalizeSweepOutputConfig(config); - - // return promise which resolves on callback - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(txSetJsonStr) { - if (txSetJsonStr.charAt(0) !== '{') reject(new MoneroError(txSetJsonStr)); // json expected, else error - else resolve(new MoneroTxSet(JSON.parse(GenUtils.stringifyBIs(txSetJsonStr))).getTxs()[0]); - } - - // sweep output in wasm and invoke callback when done - that._module.sweep_output(that._cppAddress, JSON.stringify(config.toJson()), callbackFn); - }); - }); - } - - async sweepUnlocked(config) { - this._assertNotClosed(); - - // validate and normalize config - config = MoneroWallet._normalizeSweepUnlockedConfig(config); - - // return promise which resolves on callback - let that = this; - return that._module.queueTask(async function() { // TODO: could factor this pattern out, invoked with module params and callback handler - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(txSetsJson) { - if (txSetsJson.charAt(0) !== '{') reject(new MoneroError(txSetsJson)); // json expected, else error - else { - let txSets = []; - for (let txSetJson of JSON.parse(GenUtils.stringifyBIs(txSetsJson)).txSets) txSets.push(new MoneroTxSet(txSetJson)); - let txs = []; - for (let txSet of txSets) for (let tx of txSet.getTxs()) txs.push(tx); - resolve(txs); - } - } - - // sweep unlocked in wasm and invoke callback when done - that._module.sweep_unlocked(that._cppAddress, JSON.stringify(config.toJson()), callbackFn); - }); - }); - } - - async sweepDust(relay) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = function(txSetJsonStr) { - if (txSetJsonStr.charAt(0) !== '{') reject(new MoneroError(txSetJsonStr)); // json expected, else error - else { - let txSet = new MoneroTxSet(JSON.parse(GenUtils.stringifyBIs(txSetJsonStr))); - if (txSet.getTxs() === undefined) txSet.setTxs([]); - resolve(txSet.getTxs()); - } - } - - // call wasm and invoke callback when done - that._module.sweep_dust(that._cppAddress, relay, callbackFn); - }); - }); - } - - async relayTxs(txsOrMetadatas) { - this._assertNotClosed(); - assert(Array.isArray(txsOrMetadatas), "Must provide an array of txs or their metadata to relay"); - let txMetadatas = []; - for (let txOrMetadata of txsOrMetadatas) txMetadatas.push(txOrMetadata instanceof MoneroTxWallet ? txOrMetadata.getMetadata() : txOrMetadata); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callback = function(txHashesJson) { - if (txHashesJson.charAt(0) !== "{") reject(new MoneroError(txHashesJson)); - else resolve(JSON.parse(txHashesJson).txHashes); - } - that._module.relay_txs(that._cppAddress, JSON.stringify({txMetadatas: txMetadatas}), callback); - }); - }); - } - - async describeTxSet(txSet) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - txSet = new MoneroTxSet() - .setUnsignedTxHex(txSet.getUnsignedTxHex()) - .setSignedTxHex(txSet.getSignedTxHex()) - .setMultisigTxHex(txSet.getMultisigTxHex()); - try { return new MoneroTxSet(JSON.parse(GenUtils.stringifyBIs(that._module.describe_tx_set(that._cppAddress, JSON.stringify(txSet.toJson()))))); } - catch (err) { throw new MoneroError(that._module.get_exception_message(err)); } - }); - } - - async signTxs(unsignedTxHex) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - try { return that._module.sign_txs(that._cppAddress, unsignedTxHex); } - catch (err) { throw new MoneroError(that._module.get_exception_message(err)); } - }); - } - - async submitTxs(signedTxHex) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = function(resp) { - if (resp.charAt(0) !== "{") reject(new MoneroError(resp)); - else resolve(JSON.parse(resp).txHashes); - } - that._module.submit_txs(that._cppAddress, signedTxHex, callbackFn); - }); - }); - } - - async signMessage(message, signatureType, accountIdx, subaddressIdx) { - - // assign defaults - signatureType = signatureType || MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY; - accountIdx = accountIdx || 0; - subaddressIdx = subaddressIdx || 0; - - // queue task to sign message - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - try { return that._module.sign_message(that._cppAddress, message, signatureType === MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY ? 0 : 1, accountIdx, subaddressIdx); } - catch (err) { throw new MoneroError(that._module.get_exception_message(err)); } - }); - } - - async verifyMessage(message, address, signature) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let resultJson; - try { - resultJson = JSON.parse(that._module.verify_message(that._cppAddress, message, address, signature)); - } catch (err) { - resultJson = {isGood: false}; - } - let result = new MoneroMessageSignatureResult( - resultJson.isGood, - !resultJson.isGood ? undefined : resultJson.isOld, - !resultJson.isGood ? undefined : resultJson.signatureType === "spend" ? MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY : MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY, - !resultJson.isGood ? undefined : resultJson.version); - return result; - }); - } - - async getTxKey(txHash) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - try { return that._module.get_tx_key(that._cppAddress, txHash); } - catch (err) { throw new MoneroError(that._module.get_exception_message(err)); } - }); - } - - async checkTxKey(txHash, txKey, address) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.check_tx_key(that._cppAddress, txHash, txKey, address, function(respJsonStr) { - if (respJsonStr.charAt(0) !== "{") reject(new MoneroError(respJsonStr)); - else resolve(new MoneroCheckTx(JSON.parse(GenUtils.stringifyBIs(respJsonStr)))); - }); - }); - }); - } - - async getTxProof(txHash, address, message) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.get_tx_proof(that._cppAddress, txHash || "", address || "", message || "", function(signature) { - let errorKey = "error: "; - if (signature.indexOf(errorKey) === 0) reject(new MoneroError(signature.substring(errorKey.length))); - else resolve(signature); - }); - }); - }); - } - - async checkTxProof(txHash, address, message, signature) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.check_tx_proof(that._cppAddress, txHash || "", address || "", message || "", signature || "", function(respJsonStr) { - if (respJsonStr.charAt(0) !== "{") reject(new MoneroError(respJsonStr)); - else resolve(new MoneroCheckTx(JSON.parse(GenUtils.stringifyBIs(respJsonStr)))); - }); - }); - }); - } - - async getSpendProof(txHash, message) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.get_spend_proof(that._cppAddress, txHash || "", message || "", function(signature) { - let errorKey = "error: "; - if (signature.indexOf(errorKey) === 0) reject(new MoneroError(signature.substring(errorKey.length))); - else resolve(signature); - }); - }); - }); - } - - async checkSpendProof(txHash, message, signature) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.check_spend_proof(that._cppAddress, txHash || "", message || "", signature || "", function(resp) { - typeof resp === "string" ? reject(new MoneroError(resp)) : resolve(resp); - }); - }); - }); - } - - async getReserveProofWallet(message) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.get_reserve_proof_wallet(that._cppAddress, message, function(signature) { - let errorKey = "error: "; - if (signature.indexOf(errorKey) === 0) reject(new MoneroError(signature.substring(errorKey.length), -1)); - else resolve(signature); - }); - }); - }); - } - - async getReserveProofAccount(accountIdx, amount, message) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.get_reserve_proof_account(that._cppAddress, accountIdx, amount.toString(), message, function(signature) { - let errorKey = "error: "; - if (signature.indexOf(errorKey) === 0) reject(new MoneroError(signature.substring(errorKey.length), -1)); - else resolve(signature); - }); - }); - }); - } - - async checkReserveProof(address, message, signature) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.check_reserve_proof(that._cppAddress, address, message, signature, function(respJsonStr) { - if (respJsonStr.charAt(0) !== "{") reject(new MoneroError(respJsonStr, -1)); - else resolve(new MoneroCheckReserve(JSON.parse(GenUtils.stringifyBIs(respJsonStr)))); - }); - }); - }); - } - - async getTxNotes(txHashes) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - try { return JSON.parse(that._module.get_tx_notes(that._cppAddress, JSON.stringify({txHashes: txHashes}))).txNotes; } - catch (err) { throw new MoneroError(that._module.get_exception_message(err)); } - }); - } - - async setTxNotes(txHashes, notes) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - try { that._module.set_tx_notes(that._cppAddress, JSON.stringify({txHashes: txHashes, txNotes: notes})); } - catch (err) { throw new MoneroError(that._module.get_exception_message(err)); } - }); - } - - async getAddressBookEntries(entryIndices) { - if (!entryIndices) entryIndices = []; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let entries = []; - for (let entryJson of JSON.parse(that._module.get_address_book_entries(that._cppAddress, JSON.stringify({entryIndices: entryIndices}))).entries) { - entries.push(new MoneroAddressBookEntry(entryJson)); - } - return entries; - }); - } - - async addAddressBookEntry(address, description) { - if (!address) address = ""; - if (!description) description = ""; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.add_address_book_entry(that._cppAddress, address, description); - }); - } - - async editAddressBookEntry(index, setAddress, address, setDescription, description) { - if (!setAddress) setAddress = false; - if (!address) address = ""; - if (!setDescription) setDescription = false; - if (!description) description = ""; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - that._module.edit_address_book_entry(that._cppAddress, index, setAddress, address, setDescription, description); - }); - } - - async deleteAddressBookEntry(entryIdx) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - that._module.delete_address_book_entry(that._cppAddress, entryIdx); - }); - } - - async tagAccounts(tag, accountIndices) { - if (!tag) tag = ""; - if (!accountIndices) accountIndices = []; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - that._module.tag_accounts(that._cppAddress, JSON.stringify({tag: tag, accountIndices: accountIndices})); - }); - } - - async untagAccounts(accountIndices) { - if (!accountIndices) accountIndices = []; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - that._module.tag_accounts(that._cppAddress, JSON.stringify({accountIndices: accountIndices})); - }); - } - - async getAccountTags() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let accountTags = []; - for (let accountTagJson of JSON.parse(that._module.get_account_tags(that._cppAddress)).accountTags) accountTags.push(new MoneroAccountTag(accountTagJson)); - return accountTags; - }); - } - - async setAccountTagLabel(tag, label) { - if (!tag) tag = ""; - if (!llabel) label = ""; - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - that._module.set_account_tag_label(that._cppAddress, tag, label); - }); - } - - async getPaymentUri(config) { - config = MoneroWallet._normalizeCreateTxsConfig(config); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - try { - return that._module.get_payment_uri(that._cppAddress, JSON.stringify(config.toJson())); - } catch (err) { - throw new MoneroError("Cannot make URI from supplied parameters"); - } - }); - } - - async parsePaymentUri(uri) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - try { - return new MoneroTxConfig(JSON.parse(GenUtils.stringifyBIs(that._module.parse_payment_uri(that._cppAddress, uri))), true); // relax validation for unquoted big integers - } catch (err) { - throw new MoneroError(err.message); - } - }); - } - - async getAttribute(key) { - this._assertNotClosed(); - assert(typeof key === "string", "Attribute key must be a string"); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let value = that._module.get_attribute(that._cppAddress, key); - return value === "" ? null : value; - }); - } - - async setAttribute(key, val) { - this._assertNotClosed(); - assert(typeof key === "string", "Attribute key must be a string"); - assert(typeof val === "string", "Attribute value must be a string"); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - that._module.set_attribute(that._cppAddress, key, val); - }); - } - - async startMining(numThreads, backgroundMining, ignoreBattery) { - this._assertNotClosed(); - let daemon = new MoneroDaemonRpc(Object.assign((await this.getDaemonConnection()).getConfig(), {proxyToWorker: false})); - await daemon.startMining(await this.getPrimaryAddress(), numThreads, backgroundMining, ignoreBattery); - } - - async stopMining() { - this._assertNotClosed(); - let daemon = new MoneroDaemonRpc(Object.assign((await this.getDaemonConnection()).getConfig(), {proxyToWorker: false})); - await daemon.stopMining(); - } - - async isMultisigImportNeeded() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.is_multisig_import_needed(that._cppAddress); - }); - } - - async isMultisig() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.is_multisig(that._cppAddress); - }); - } - - async getMultisigInfo() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new MoneroMultisigInfo(JSON.parse(that._module.get_multisig_info(that._cppAddress))); - }); - } - - async prepareMultisig() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.prepare_multisig(that._cppAddress); - }); - } - - async makeMultisig(multisigHexes, threshold, password) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.make_multisig(that._cppAddress, JSON.stringify({multisigHexes: multisigHexes, threshold: threshold, password: password}), (resp) => { - let errorKey = "error: "; - if (resp.indexOf(errorKey) === 0) reject(new MoneroError(resp.substring(errorKey.length))); - else resolve(resp); - }); - }); - }); - } - - async exchangeMultisigKeys(multisigHexes, password) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.exchange_multisig_keys(that._cppAddress, JSON.stringify({multisigHexes: multisigHexes, password: password}), (resp) => { - let errorKey = "error: "; - if (resp.indexOf(errorKey) === 0) reject(new MoneroError(resp.substring(errorKey.length))); - else resolve(new MoneroMultisigInitResult(JSON.parse(resp))); - }); - }); - }); - } - - async exportMultisigHex() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.export_multisig_hex(that._cppAddress); - }); - } - - async importMultisigHex(multisigHexes) { - if (!GenUtils.isArray(multisigHexes)) throw new MoneroError("Must provide string[] to importMultisigHex()") - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = function(resp) { - if (typeof resp === "string") reject(new MoneroError(resp)); - else resolve(resp); - } - that._module.import_multisig_hex(that._cppAddress, JSON.stringify({multisigHexes: multisigHexes}), callbackFn); - }); - }); - } - - async signMultisigTxHex(multisigTxHex) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = async function(resp) { - if (resp.charAt(0) !== "{") reject(new MoneroError(resp)); - else resolve(new MoneroMultisigSignResult(JSON.parse(resp))); - } - that._module.sign_multisig_tx_hex(that._cppAddress, multisigTxHex, callbackFn) - }); - }); - } - - async submitMultisigTxHex(signedMultisigTxHex) { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - let callbackFn = function(resp) { - if (resp.charAt(0) !== "{") reject(new MoneroError(resp)); - else resolve(JSON.parse(resp).txHashes); - } - that._module.submit_multisig_tx_hex(that._cppAddress, signedMultisigTxHex, callbackFn); - }); - }); - } - - /** - * Get the wallet's keys and cache data. - * - * @return {DataView[]} is the keys and cache data, respectively - */ - async getData() { - this._assertNotClosed(); - - // queue call to wasm module - let viewOnly = await this.isViewOnly(); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - - // store views in array - let views = []; - - // malloc cache buffer and get buffer location in c++ heap - let cacheBufferLoc = JSON.parse(that._module.get_cache_file_buffer(that._cppAddress)); - - // read binary data from heap to DataView - let view = new DataView(new ArrayBuffer(cacheBufferLoc.length)); - for (let i = 0; i < cacheBufferLoc.length; i++) { - view.setInt8(i, that._module.HEAPU8[cacheBufferLoc.pointer / Uint8Array.BYTES_PER_ELEMENT + i]); - } - - // free binary on heap - that._module._free(cacheBufferLoc.pointer); - - // write cache file - views.push(Buffer.from(view.buffer)); - - // malloc keys buffer and get buffer location in c++ heap - let keysBufferLoc = JSON.parse(that._module.get_keys_file_buffer(that._cppAddress, that._password, viewOnly)); - - // read binary data from heap to DataView - view = new DataView(new ArrayBuffer(keysBufferLoc.length)); - for (let i = 0; i < keysBufferLoc.length; i++) { - view.setInt8(i, that._module.HEAPU8[keysBufferLoc.pointer / Uint8Array.BYTES_PER_ELEMENT + i]); - } - - // free binary on heap - that._module._free(keysBufferLoc.pointer); - - // prepend keys file - views.unshift(Buffer.from(view.buffer)); - return views; - }); - } - - async changePassword(oldPassword, newPassword) { - if (oldPassword !== this._password) throw new MoneroError("Invalid original password."); // wallet2 verify_password loads from disk so verify password here - if (newPassword === undefined) newPassword = ""; - let that = this; - await that._module.queueTask(async function() { - that._assertNotClosed(); - return new Promise(function(resolve, reject) { - that._module.change_wallet_password(that._cppAddress, oldPassword, newPassword, async function(errMsg) { - if (errMsg) reject(new MoneroError(errMsg)); - else resolve(); - }); - }); - }); - this._password = newPassword; - if (this._path) await this.save(); // auto save - } - - async save() { - return MoneroWalletFull._save(this); - } - - async close(save) { - if (this._isClosed) return; // no effect if closed - await this._refreshListening(); - await this.stopSyncing(); - await super.close(save); - delete this._path; - delete this._password; - delete this._listeners; - delete this._fullListener; - LibraryUtils.setRejectUnauthorizedFn(this._rejectUnauthorizedConfigId, undefined); // unregister fn informing if unauthorized reqs should be rejected - } - - // ----------- ADD JSDOC FOR SUPPORTED DEFAULT IMPLEMENTATIONS -------------- - - async getNumBlocksToUnlock() { return super.getNumBlocksToUnlock(...arguments); } - async getTx() { return super.getTx(...arguments); } - async getIncomingTransfers() { return super.getIncomingTransfers(...arguments); } - async getOutgoingTransfers() { return super.getOutgoingTransfers(...arguments); } - async createTx() { return super.createTx(...arguments); } - async relayTx() { return super.relayTx(...arguments); } - async getTxNote() { return super.getTxNote(...arguments); } - async setTxNote() { return super.setTxNote(...arguments); } - - // ---------------------------- PRIVATE HELPERS ---------------------------- - - static _getFs() { - if (!MoneroWalletFull.FS) MoneroWalletFull.FS = GenUtils.isBrowser() ? undefined : require('fs'); - return MoneroWalletFull.FS; - } - - static async _openWalletData(path, password, networkType, keysData, cacheData, daemonUriOrConnection, proxyToWorker, fs) { - if (proxyToWorker) return MoneroWalletFullProxy.openWalletData(path, password, networkType, keysData, cacheData, daemonUriOrConnection, fs); - - // validate and normalize parameters - if (networkType === undefined) throw new MoneroError("Must provide the wallet's network type"); - MoneroNetworkType.validate(networkType); - let daemonConnection = typeof daemonUriOrConnection === "string" ? new MoneroRpcConnection(daemonUriOrConnection) : daemonUriOrConnection; - let daemonUri = daemonConnection && daemonConnection.getUri() ? daemonConnection.getUri() : ""; - let daemonUsername = daemonConnection && daemonConnection.getUsername() ? daemonConnection.getUsername() : ""; - let daemonPassword = daemonConnection && daemonConnection.getPassword() ? daemonConnection.getPassword() : ""; - let rejectUnauthorized = daemonConnection ? daemonConnection.getRejectUnauthorized() : true; - - // load wasm module - let module = await LibraryUtils.loadFullModule(); - - // open wallet in queue - return module.queueTask(async function() { - return new Promise(function(resolve, reject) { - - // register fn informing if unauthorized reqs should be rejected - let rejectUnauthorizedFnId = GenUtils.getUUID(); - LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, function() { return rejectUnauthorized }); - - // define callback for wasm - let callbackFn = async function(cppAddress) { - if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); - else resolve(new MoneroWalletFull(cppAddress, path, password, fs, rejectUnauthorized, rejectUnauthorizedFnId)); - }; - - // create wallet in wasm and invoke callback when done - module.open_wallet_full(password, networkType, keysData, cacheData, daemonUri, daemonUsername, daemonPassword, rejectUnauthorizedFnId, callbackFn); - }); - }); - } - - async _backgroundSync() { - let label = this._path ? this._path : (this._browserMainPath ? this._browserMainPath : "in-memory wallet"); // label for log - LibraryUtils.log(1, "Background synchronizing " + label); - try { await this.sync(); } - catch (err) { if (!this._isClosed) console.error("Failed to background synchronize " + label + ": " + err.message); } - } - - async _refreshListening() { - let isEnabled = this._listeners.length > 0; - let that = this; - if (that._fullListenerHandle === 0 && !isEnabled || that._fullListenerHandle > 0 && isEnabled) return; // no difference - return that._module.queueTask(async function() { - return new Promise(function(resolve, reject) { - that._module.set_listener( - that._cppAddress, - that._fullListenerHandle, - newListenerHandle => { - if (typeof newListenerHandle === "string") reject(new MoneroError(newListenerHandle)); - else { - that._fullListenerHandle = newListenerHandle; - resolve(); - } - }, - isEnabled ? async function(height, startHeight, endHeight, percentDone, message) { await that._fullListener.onSyncProgress(height, startHeight, endHeight, percentDone, message); } : undefined, - isEnabled ? async function(height) { await that._fullListener.onNewBlock(height); } : undefined, - isEnabled ? async function(newBalanceStr, newUnlockedBalanceStr) { await that._fullListener.onBalancesChanged(newBalanceStr, newUnlockedBalanceStr); } : undefined, - isEnabled ? async function(height, txHash, amountStr, accountIdx, subaddressIdx, version, unlockTime, isLocked) { await that._fullListener.onOutputReceived(height, txHash, amountStr, accountIdx, subaddressIdx, version, unlockTime, isLocked); } : undefined, - isEnabled ? async function(height, txHash, amountStr, accountIdxStr, subaddressIdxStr, version, unlockTime, isLocked) { await that._fullListener.onOutputSpent(height, txHash, amountStr, accountIdxStr, subaddressIdxStr, version, unlockTime, isLocked); } : undefined, - ); - }); - }); - } - - static _sanitizeBlock(block) { - for (let tx of block.getTxs()) MoneroWalletFull._sanitizeTxWallet(tx); - return block; - } - - static _sanitizeTxWallet(tx) { - assert(tx instanceof MoneroTxWallet); - return tx; - } - - static _sanitizeAccount(account) { - if (account.getSubaddresses()) { - for (let subaddress of account.getSubaddresses()) MoneroWalletFull._sanitizeSubaddress(subaddress); - } - return account; - } - - static _sanitizeSubaddress(subaddress) { - if (subaddress.getLabel() === "") subaddress.setLabel(undefined); - return subaddress - } - - static _deserializeBlocks(blocksJsonStr) { - let blocksJson = JSON.parse(GenUtils.stringifyBIs(blocksJsonStr)); - let deserializedBlocks = {}; - deserializedBlocks.blocks = []; - if (blocksJson.blocks) for (let blockJson of blocksJson.blocks) deserializedBlocks.blocks.push(MoneroWalletFull._sanitizeBlock(new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX_WALLET))); - return deserializedBlocks; - } - - static _deserializeTxs(query, blocksJsonStr) { - - // deserialize blocks - let deserializedBlocks = MoneroWalletFull._deserializeBlocks(blocksJsonStr); - let blocks = deserializedBlocks.blocks; - - // collect txs - let txs = []; - for (let block of blocks) { - MoneroWalletFull._sanitizeBlock(block); - for (let tx of block.getTxs()) { - if (block.getHeight() === undefined) tx.setBlock(undefined); // dereference placeholder block for unconfirmed txs - txs.push(tx); - } - } - - // re-sort txs which is lost over wasm serialization // TODO: confirm that order is lost - if (query.getHashes() !== undefined) { - let txMap = new Map(); - for (let tx of txs) txMap[tx.getHash()] = tx; - let txsSorted = []; - for (let txHash of query.getHashes()) if (txMap[txHash] !== undefined) txsSorted.push(txMap[txHash]); - txs = txsSorted; - } - - return txs; - } - - static _deserializeTransfers(query, blocksJsonStr) { - - // deserialize blocks - let deserializedBlocks = MoneroWalletFull._deserializeBlocks(blocksJsonStr); - let blocks = deserializedBlocks.blocks; - - // collect transfers - let transfers = []; - for (let block of blocks) { - for (let tx of block.getTxs()) { - if (block.getHeight() === undefined) tx.setBlock(undefined); // dereference placeholder block for unconfirmed txs - if (tx.getOutgoingTransfer() !== undefined) transfers.push(tx.getOutgoingTransfer()); - if (tx.getIncomingTransfers() !== undefined) { - for (let transfer of tx.getIncomingTransfers()) transfers.push(transfer); - } - } - } - - return transfers; - } - - static _deserializeOutputs(query, blocksJsonStr) { - - // deserialize blocks - let deserializedBlocks = MoneroWalletFull._deserializeBlocks(blocksJsonStr); - let blocks = deserializedBlocks.blocks; - - // collect outputs - let outputs = []; - for (let block of blocks) { - for (let tx of block.getTxs()) { - for (let output of tx.getOutputs()) outputs.push(output); - } - } - - return outputs; - } - - /** - * Set the path of the wallet on the browser main thread if run as a worker. - * - * @param {string} browserMainPath - path of the wallet on the browser main thread - */ - _setBrowserMainPath(browserMainPath) { - this._browserMainPath = browserMainPath; - } - - static async _moveTo(path, wallet) { - if (await wallet.isClosed()) throw new MoneroError("Wallet is closed"); - if (!path) throw new MoneroError("Must provide path of destination wallet"); - - // save and return if same path - const Path = require("path"); - if (Path.normalize(wallet._path) === Path.normalize(path)) { - await wallet.save(); - return; - } - - // create destination directory if it doesn't exist - let walletDir = Path.dirname(path); - if (!wallet._fs.existsSync(walletDir)) { - try { wallet._fs.mkdirSync(walletDir); } - catch (err) { throw new MoneroError("Destination path " + path + " does not exist and cannot be created: " + err.message); } - } - - // write wallet files - let data = await wallet.getData(); - wallet._fs.writeFileSync(path + ".keys", data[0], "binary"); - wallet._fs.writeFileSync(path, data[1], "binary"); - wallet._fs.writeFileSync(path + ".address.txt", await wallet.getPrimaryAddress()); - let oldPath = wallet._path; - wallet._path = path; - - // delete old wallet files - if (oldPath) { - wallet._fs.unlinkSync(oldPath + ".address.txt"); - wallet._fs.unlinkSync(oldPath + ".keys"); - wallet._fs.unlinkSync(oldPath); - } - } - - static async _save(wallet) { - if (await wallet.isClosed()) throw new MoneroError("Wallet is closed"); - - // path must be set - let path = await wallet.getPath(); - if (!path) throw new MoneroError("Cannot save wallet because path is not set"); - - // write wallet files to *.new - let pathNew = path + ".new"; - let data = await wallet.getData(); - wallet._fs.writeFileSync(pathNew + ".keys", data[0], "binary"); - wallet._fs.writeFileSync(pathNew, data[1], "binary"); - wallet._fs.writeFileSync(pathNew + ".address.txt", await wallet.getPrimaryAddress()); - - // replace old wallet files with new - wallet._fs.renameSync(pathNew + ".keys", path + ".keys"); - wallet._fs.renameSync(pathNew, path, path + ".keys"); - wallet._fs.renameSync(pathNew + ".address.txt", path + ".address.txt", path + ".keys"); - } -} - -/** - * Implements a MoneroWallet by proxying requests to a worker which runs a full wallet. - * - * TODO: sort these methods according to master sort in MoneroWallet.js - * TODO: probably only allow one listener to worker then propogate to registered listeners for performance - * TODO: ability to recycle worker for use in another wallet - * TODO: using LibraryUtils.WORKER_OBJECTS directly breaks encapsulation - * - * @private - */ -class MoneroWalletFullProxy extends MoneroWallet { - - // -------------------------- WALLET STATIC UTILS --------------------------- - - static async openWalletData(path, password, networkType, keysData, cacheData, daemonUriOrConnection, fs) { - let walletId = GenUtils.getUUID(); - if (password === undefined) password = ""; - let daemonUriOrConfig = daemonUriOrConnection instanceof MoneroRpcConnection ? daemonUriOrConnection.getConfig() : daemonUriOrConnection; - await LibraryUtils.invokeWorker(walletId, "openWalletData", [path, password, networkType, keysData, cacheData, daemonUriOrConfig]); - let wallet = new MoneroWalletFullProxy(walletId, await LibraryUtils.getWorker(), path, fs); - if (path) await wallet.save(); - return wallet; - } - - static async _createWallet(config) { - if (config.getPath() && MoneroWalletFull.walletExists(config.getPath(), config.getFs())) throw new MoneroError("Wallet already exists: " + path); - let walletId = GenUtils.getUUID(); - await LibraryUtils.invokeWorker(walletId, "_createWallet", [config.toJson()]); - let wallet = new MoneroWalletFullProxy(walletId, await LibraryUtils.getWorker(), config.getPath(), config.getFs()); - if (config.getPath()) await wallet.save(); - return wallet; - } - - // --------------------------- INSTANCE METHODS ---------------------------- - - /** - * Internal constructor which is given a worker to communicate with via messages. - * - * This method should not be called externally but should be called through - * static wallet creation utilities in this class. - * - * @param {string} walletId - identifies the wallet with the worker - * @param {Worker} worker - worker to communicate with via messages - */ - constructor(walletId, worker, path, fs) { - super(); - this._walletId = walletId; - this._worker = worker; - this._path = path; - this._fs = fs ? fs : (path ? MoneroWalletFull._getFs() : undefined); - this._wrappedListeners = []; - } - - async isViewOnly() { - return this._invokeWorker("isViewOnly"); - } - - async getNetworkType() { - return this._invokeWorker("getNetworkType"); - } - - async getVersion() { - throw new MoneroError("Not implemented"); - } - - getPath() { - return this._path; - } - - async getSeed() { - return this._invokeWorker("getSeed"); - } - - async getSeedLanguage() { - return this._invokeWorker("getSeedLanguage"); - } - - async getSeedLanguages() { - return this._invokeWorker("getSeedLanguages"); - } - - async getPrivateSpendKey() { - return this._invokeWorker("getPrivateSpendKey"); - } - - async getPrivateViewKey() { - return this._invokeWorker("getPrivateViewKey"); - } - - async getPublicViewKey() { - return this._invokeWorker("getPublicViewKey"); - } - - async getPublicSpendKey() { - return this._invokeWorker("getPublicSpendKey"); - } - - async getAddress(accountIdx, subaddressIdx) { - return this._invokeWorker("getAddress", Array.from(arguments)); - } - - async getAddressIndex(address) { - let subaddressJson = await this._invokeWorker("getAddressIndex", Array.from(arguments)); - return MoneroWalletFull._sanitizeSubaddress(new MoneroSubaddress(subaddressJson)); - } - - async setSubaddressLabel(accountIdx, subaddressIdx, label) { - return this._invokeWorker("setSubaddressLabel", Array.from(arguments)); - } - - async getIntegratedAddress(standardAddress, paymentId) { - return new MoneroIntegratedAddress(await this._invokeWorker("getIntegratedAddress", Array.from(arguments))); - } - - async decodeIntegratedAddress(integratedAddress) { - return new MoneroIntegratedAddress(await this._invokeWorker("decodeIntegratedAddress", Array.from(arguments))); - } - - async setDaemonConnection(uriOrRpcConnection) { - if (!uriOrRpcConnection) await this._invokeWorker("setDaemonConnection"); - else { - let connection = !uriOrRpcConnection ? undefined : uriOrRpcConnection instanceof MoneroRpcConnection ? uriOrRpcConnection : new MoneroRpcConnection(uriOrRpcConnection); - await this._invokeWorker("setDaemonConnection", connection ? connection.getConfig() : undefined); - } - } - - async getDaemonConnection() { - let rpcConfig = await this._invokeWorker("getDaemonConnection"); - return rpcConfig ? new MoneroRpcConnection(rpcConfig) : undefined; - } - - async isConnectedToDaemon() { - return this._invokeWorker("isConnectedToDaemon"); - } - - async getRestoreHeight() { - return this._invokeWorker("getRestoreHeight"); - } - - async setRestoreHeight(restoreHeight) { - return this._invokeWorker("setRestoreHeight", [restoreHeight]); - } - - async getDaemonHeight() { - return this._invokeWorker("getDaemonHeight"); - } - - async getDaemonMaxPeerHeight() { - return this._invokeWorker("getDaemonMaxPeerHeight"); - } - - async getHeightByDate(year, month, day) { - return this._invokeWorker("getHeightByDate", [year, month, day]); - } - - async isDaemonSynced() { - return this._invokeWorker("isDaemonSynced"); - } - - async getHeight() { - return this._invokeWorker("getHeight"); - } - - async addListener(listener) { - let wrappedListener = new WalletWorkerListener(listener); - let listenerId = wrappedListener.getId(); - LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onSyncProgress_" + listenerId] = [wrappedListener.onSyncProgress, wrappedListener]; - LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onNewBlock_" + listenerId] = [wrappedListener.onNewBlock, wrappedListener]; - LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onBalancesChanged_" + listenerId] = [wrappedListener.onBalancesChanged, wrappedListener]; - LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onOutputReceived_" + listenerId] = [wrappedListener.onOutputReceived, wrappedListener]; - LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onOutputSpent_" + listenerId] = [wrappedListener.onOutputSpent, wrappedListener]; - this._wrappedListeners.push(wrappedListener); - return this._invokeWorker("addListener", [listenerId]); - } - - async removeListener(listener) { - for (let i = 0; i < this._wrappedListeners.length; i++) { - if (this._wrappedListeners[i].getListener() === listener) { - let listenerId = this._wrappedListeners[i].getId(); - await this._invokeWorker("removeListener", [listenerId]); - delete LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onSyncProgress_" + listenerId]; - delete LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onNewBlock_" + listenerId]; - delete LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onBalancesChanged_" + listenerId]; - delete LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onOutputReceived_" + listenerId]; - delete LibraryUtils.WORKER_OBJECTS[this._walletId].callbacks["onOutputSpent_" + listenerId]; - this._wrappedListeners.splice(i, 1); - return; - } - } - throw new MoneroError("Listener is not registered with wallet"); - } - - getListeners() { - let listeners = []; - for (let wrappedListener of this._wrappedListeners) listeners.push(wrappedListener.getListener()); - return listeners; - } - - async isSynced() { - return this._invokeWorker("isSynced"); - } - - async sync(listenerOrStartHeight, startHeight, allowConcurrentCalls) { - - // normalize params - startHeight = listenerOrStartHeight instanceof MoneroWalletListener ? startHeight : listenerOrStartHeight; - let listener = listenerOrStartHeight instanceof MoneroWalletListener ? listenerOrStartHeight : undefined; - if (startHeight === undefined) startHeight = Math.max(await this.getHeight(), await this.getRestoreHeight()); - - // register listener if given - if (listener) await this.addListener(listener); - - // sync wallet in worker - let err; - let result; - try { - let resultJson = await this._invokeWorker("sync", [startHeight, allowConcurrentCalls]); - result = new MoneroSyncResult(resultJson.numBlocksFetched, resultJson.receivedMoney); - } catch (e) { - err = e; - } - - // unregister listener - if (listener) await this.removeListener(listener); - - // throw error or return - if (err) throw err; - return result; - } - - async startSyncing(syncPeriodInMs) { - return this._invokeWorker("startSyncing", Array.from(arguments)); - } - - async stopSyncing() { - return this._invokeWorker("stopSyncing"); - } - - async scanTxs(txHashes) { - assert(Array.isArray(txHashes), "Must provide an array of txs hashes to scan"); - return this._invokeWorker("scanTxs", [txHashes]); - } - - async rescanSpent() { - return this._invokeWorker("rescanSpent"); - } - - async rescanBlockchain() { - return this._invokeWorker("rescanBlockchain"); - } - - async getBalance(accountIdx, subaddressIdx) { - return BigInteger.parse(await this._invokeWorker("getBalance", Array.from(arguments))); - } - - async getUnlockedBalance(accountIdx, subaddressIdx) { - let unlockedBalanceStr = await this._invokeWorker("getUnlockedBalance", Array.from(arguments)); - return BigInteger.parse(unlockedBalanceStr); - } - - async getAccounts(includeSubaddresses, tag) { - let accounts = []; - for (let accountJson of (await this._invokeWorker("getAccounts", Array.from(arguments)))) { - accounts.push(MoneroWalletFull._sanitizeAccount(new MoneroAccount(accountJson))); - } - return accounts; - } - - async getAccount(accountIdx, includeSubaddresses) { - let accountJson = await this._invokeWorker("getAccount", Array.from(arguments)); - return MoneroWalletFull._sanitizeAccount(new MoneroAccount(accountJson)); - } - - async createAccount(label) { - let accountJson = await this._invokeWorker("createAccount", Array.from(arguments)); - return MoneroWalletFull._sanitizeAccount(new MoneroAccount(accountJson)); - } - - async getSubaddresses(accountIdx, subaddressIndices) { - let subaddresses = []; - for (let subaddressJson of (await this._invokeWorker("getSubaddresses", Array.from(arguments)))) { - subaddresses.push(MoneroWalletFull._sanitizeSubaddress(new MoneroSubaddress(subaddressJson))); - } - return subaddresses; - } - - async createSubaddress(accountIdx, label) { - let subaddressJson = await this._invokeWorker("createSubaddress", Array.from(arguments)); - return MoneroWalletFull._sanitizeSubaddress(new MoneroSubaddress(subaddressJson)); - } - - async getTxs(query) { - query = MoneroWallet._normalizeTxQuery(query); - let respJson = await this._invokeWorker("getTxs", [query.getBlock().toJson()]); - return MoneroWalletFull._deserializeTxs(query, JSON.stringify({blocks: respJson.blocks})); // initialize txs from blocks json string TODO: this stringifies then utility parses, avoid - } - - async getTransfers(query) { - query = MoneroWallet._normalizeTransferQuery(query); - let blockJsons = await this._invokeWorker("getTransfers", [query.getTxQuery().getBlock().toJson()]); - return MoneroWalletFull._deserializeTransfers(query, JSON.stringify({blocks: blockJsons})); // initialize transfers from blocks json string TODO: this stringifies then utility parses, avoid - } - - async getOutputs(query) { - query = MoneroWallet._normalizeOutputQuery(query); - let blockJsons = await this._invokeWorker("getOutputs", [query.getTxQuery().getBlock().toJson()]); - return MoneroWalletFull._deserializeOutputs(query, JSON.stringify({blocks: blockJsons})); // initialize transfers from blocks json string TODO: this stringifies then utility parses, avoid - } - - async exportOutputs(all) { - return this._invokeWorker("exportOutputs", [all]); - } - - async importOutputs(outputsHex) { - return this._invokeWorker("importOutputs", [outputsHex]); - } - - async exportKeyImages(all) { - let keyImages = []; - for (let keyImageJson of await this._invokeWorker("getKeyImages", [all])) keyImages.push(new MoneroKeyImage(keyImageJson)); - return keyImages; - } - - async importKeyImages(keyImages) { - let keyImagesJson = []; - for (let keyImage of keyImages) keyImagesJson.push(keyImage.toJson()); - return new MoneroKeyImageImportResult(await this._invokeWorker("importKeyImages", [keyImagesJson])); - } - - async getNewKeyImagesFromLastImport() { - throw new MoneroError("MoneroWalletFull.getNewKeyImagesFromLastImport() not implemented"); - } - - async freezeOutput(keyImage) { - return this._invokeWorker("freezeOutput", [keyImage]); - } - - async thawOutput(keyImage) { - return this._invokeWorker("thawOutput", [keyImage]); - } - - async isOutputFrozen(keyImage) { - return this._invokeWorker("isOutputFrozen", [keyImage]); - } - - async createTxs(config) { - config = MoneroWallet._normalizeCreateTxsConfig(config); - let txSetJson = await this._invokeWorker("createTxs", [config.toJson()]); - return new MoneroTxSet(txSetJson).getTxs(); - } - - async sweepOutput(config) { - config = MoneroWallet._normalizeSweepOutputConfig(config); - let txSetJson = await this._invokeWorker("sweepOutput", [config.toJson()]); - return new MoneroTxSet(txSetJson).getTxs()[0]; - } - - async sweepUnlocked(config) { - config = MoneroWallet._normalizeSweepUnlockedConfig(config); - let txSetsJson = await this._invokeWorker("sweepUnlocked", [config.toJson()]); - let txs = []; - for (let txSetJson of txSetsJson) for (let tx of new MoneroTxSet(txSetJson).getTxs()) txs.push(tx); - return txs; - } - - async sweepDust(relay) { - return new MoneroTxSet(await this._invokeWorker("sweepDust", [relay])).getTxs() || []; - } - - async relayTxs(txsOrMetadatas) { - assert(Array.isArray(txsOrMetadatas), "Must provide an array of txs or their metadata to relay"); - let txMetadatas = []; - for (let txOrMetadata of txsOrMetadatas) txMetadatas.push(txOrMetadata instanceof MoneroTxWallet ? txOrMetadata.getMetadata() : txOrMetadata); - return this._invokeWorker("relayTxs", [txMetadatas]); - } - - async describeTxSet(txSet) { - return new MoneroTxSet(await this._invokeWorker("describeTxSet", [txSet.toJson()])); - } - - async signTxs(unsignedTxHex) { - return this._invokeWorker("signTxs", Array.from(arguments)); - } - - async submitTxs(signedTxHex) { - return this._invokeWorker("submitTxs", Array.from(arguments)); - } - - async signMessage(message, signatureType, accountIdx, subaddressIdx) { - return this._invokeWorker("signMessage", Array.from(arguments)); - } - - async verifyMessage(message, address, signature) { - return new MoneroMessageSignatureResult(await this._invokeWorker("verifyMessage", Array.from(arguments))); - } - - async getTxKey(txHash) { - return this._invokeWorker("getTxKey", Array.from(arguments)); - } - - async checkTxKey(txHash, txKey, address) { - return new MoneroCheckTx(await this._invokeWorker("checkTxKey", Array.from(arguments))); - } - - async getTxProof(txHash, address, message) { - return this._invokeWorker("getTxProof", Array.from(arguments)); - } - - async checkTxProof(txHash, address, message, signature) { - return new MoneroCheckTx(await this._invokeWorker("checkTxProof", Array.from(arguments))); - } - - async getSpendProof(txHash, message) { - return this._invokeWorker("getSpendProof", Array.from(arguments)); - } - - async checkSpendProof(txHash, message, signature) { - return this._invokeWorker("checkSpendProof", Array.from(arguments)); - } - - async getReserveProofWallet(message) { - return this._invokeWorker("getReserveProofWallet", Array.from(arguments)); - } - - async getReserveProofAccount(accountIdx, amount, message) { - try { return await this._invokeWorker("getReserveProofAccount", [accountIdx, amount.toString(), message]); } - catch (e) { throw new MoneroError(e.message, -1); } - } - - async checkReserveProof(address, message, signature) { - try { return new MoneroCheckReserve(await this._invokeWorker("checkReserveProof", Array.from(arguments))); } - catch (e) { throw new MoneroError(e.message, -1); } - } - - async getTxNotes(txHashes) { - return this._invokeWorker("getTxNotes", Array.from(arguments)); - } - - async setTxNotes(txHashes, notes) { - return this._invokeWorker("setTxNotes", Array.from(arguments)); - } - - async getAddressBookEntries(entryIndices) { - if (!entryIndices) entryIndices = []; - let entries = []; - for (let entryJson of await this._invokeWorker("getAddressBookEntries", Array.from(arguments))) { - entries.push(new MoneroAddressBookEntry(entryJson)); - } - return entries; - } - - async addAddressBookEntry(address, description) { - return this._invokeWorker("addAddressBookEntry", Array.from(arguments)); - } - - async editAddressBookEntry(index, setAddress, address, setDescription, description) { - return this._invokeWorker("editAddressBookEntry", Array.from(arguments)); - } - - async deleteAddressBookEntry(entryIdx) { - return this._invokeWorker("deleteAddressBookEntry", Array.from(arguments)); - } - - async tagAccounts(tag, accountIndices) { - return this._invokeWorker("tagAccounts", Array.from(arguments)); - } - - async untagAccounts(accountIndices) { - return this._invokeWorker("untagAccounts", Array.from(arguments)); - } - - async getAccountTags() { - return this._invokeWorker("getAccountTags", Array.from(arguments)); - } - - async setAccountTagLabel(tag, label) { - return this._invokeWorker("setAccountTagLabel", Array.from(arguments)); - } - - async getPaymentUri(config) { - config = MoneroWallet._normalizeCreateTxsConfig(config); - return this._invokeWorker("getPaymentUri", [config.toJson()]); - } - - async parsePaymentUri(uri) { - return new MoneroTxConfig(await this._invokeWorker("parsePaymentUri", Array.from(arguments))); - } - - async getAttribute(key) { - return this._invokeWorker("getAttribute", Array.from(arguments)); - } - - async setAttribute(key, val) { - return this._invokeWorker("setAttribute", Array.from(arguments)); - } - - async startMining(numThreads, backgroundMining, ignoreBattery) { - return this._invokeWorker("startMining", Array.from(arguments)); - } - - async stopMining() { - return this._invokeWorker("stopMining", Array.from(arguments)); - } - - async isMultisigImportNeeded() { - return this._invokeWorker("isMultisigImportNeeded"); - } - - async isMultisig() { - return this._invokeWorker("isMultisig"); - } - - async getMultisigInfo() { - return new MoneroMultisigInfo(await this._invokeWorker("getMultisigInfo")); - } - - async prepareMultisig() { - return this._invokeWorker("prepareMultisig"); - } - - async makeMultisig(multisigHexes, threshold, password) { - return await this._invokeWorker("makeMultisig", Array.from(arguments)); - } - - async exchangeMultisigKeys(multisigHexes, password) { - return new MoneroMultisigInitResult(await this._invokeWorker("exchangeMultisigKeys", Array.from(arguments))); - } - - async exportMultisigHex() { - return this._invokeWorker("exportMultisigHex"); - } - - async importMultisigHex(multisigHexes) { - return this._invokeWorker("importMultisigHex", Array.from(arguments)); - } - - async signMultisigTxHex(multisigTxHex) { - return new MoneroMultisigSignResult(await this._invokeWorker("signMultisigTxHex", Array.from(arguments))); - } - - async submitMultisigTxHex(signedMultisigTxHex) { - return this._invokeWorker("submitMultisigTxHex", Array.from(arguments)); - } - - async getData() { - return this._invokeWorker("getData"); - } - - async moveTo(path) { - return MoneroWalletFull._moveTo(path, this); - } - - async changePassword(oldPassword, newPassword) { - await this._invokeWorker("changePassword", Array.from(arguments)); - if (this._path) await this.save(); // auto save - } - - async save() { - return MoneroWalletFull._save(this); - } - - async close(save) { - if (save) await this.save(); - while (this._wrappedListeners.length) await this.removeListener(this._wrappedListeners[0].getListener()); - await this._invokeWorker("close"); - delete LibraryUtils.WORKER_OBJECTS[this._walletId]; - } - - async isClosed() { - return this._invokeWorker("isClosed"); - } - - // --------------------------- PRIVATE HELPERS ------------------------------ - - async _invokeWorker(fnName, args) { - return await LibraryUtils.invokeWorker(this._walletId, fnName, args); - } -} - -// -------------------------------- LISTENING --------------------------------- - -/** - * Receives notifications directly from wasm c++. - * - * @private - */ -class WalletFullListener { - - constructor(wallet) { - this._wallet = wallet; - } - - async onSyncProgress(height, startHeight, endHeight, percentDone, message) { - for (let listener of this._wallet.getListeners()) await listener.onSyncProgress(height, startHeight, endHeight, percentDone, message); - } - - async onNewBlock(height) { - for (let listener of this._wallet.getListeners()) await listener.onNewBlock(height); - } - - async onBalancesChanged(newBalanceStr, newUnlockedBalanceStr) { - for (let listener of this._wallet.getListeners()) await listener.onBalancesChanged(BigInteger.parse(newBalanceStr), BigInteger.parse(newUnlockedBalanceStr)); - } - - async onOutputReceived(height, txHash, amountStr, accountIdx, subaddressIdx, version, unlockTime, isLocked) { - - // build received output - let output = new MoneroOutputWallet(); - output.setAmount(BigInteger.parse(amountStr)); - output.setAccountIndex(accountIdx); - output.setSubaddressIndex(subaddressIdx); - let tx = new MoneroTxWallet(); - tx.setHash(txHash); - tx.setVersion(version); - tx.setUnlockTime(unlockTime); - output.setTx(tx); - tx.setOutputs([output]); - tx.setIsIncoming(true); - tx.setIsLocked(isLocked); - if (height > 0) { - let block = new MoneroBlock().setHeight(height); - block.setTxs([tx]); - tx.setBlock(block); - tx.setIsConfirmed(true); - tx.setInTxPool(false); - tx.setIsFailed(false); - } else { - tx.setIsConfirmed(false); - tx.setInTxPool(true); - } - - // announce output - for (let listener of this._wallet.getListeners()) await listener.onOutputReceived(tx.getOutputs()[0]); - } - - async onOutputSpent(height, txHash, amountStr, accountIdxStr, subaddressIdxStr, version, unlockTime, isLocked) { - - // build spent output - let output = new MoneroOutputWallet(); - output.setAmount(BigInteger.parse(amountStr)); - if (accountIdxStr) output.setAccountIndex(parseInt(accountIdxStr)); - if (subaddressIdxStr) output.setSubaddressIndex(parseInt(subaddressIdxStr)); - let tx = new MoneroTxWallet(); - tx.setHash(txHash); - tx.setVersion(version); - tx.setUnlockTime(unlockTime); - tx.setIsLocked(isLocked); - output.setTx(tx); - tx.setInputs([output]); - if (height > 0) { - let block = new MoneroBlock().setHeight(height); - block.setTxs([tx]); - tx.setBlock(block); - tx.setIsConfirmed(true); - tx.setInTxPool(false); - tx.setIsFailed(false); - } else { - tx.setIsConfirmed(false); - tx.setInTxPool(true); - } - - // notify wallet listeners - for (let listener of this._wallet.getListeners()) await listener.onOutputSpent(tx.getInputs()[0]); - } -} - -/** - * Internal listener to bridge notifications to external listeners. - * - * @private - */ -class WalletWorkerListener { - - constructor(listener) { - this._id = GenUtils.getUUID(); - this._listener = listener; - } - - getId() { - return this._id; - } - - getListener() { - return this._listener; - } - - onSyncProgress(height, startHeight, endHeight, percentDone, message) { - this._listener.onSyncProgress(height, startHeight, endHeight, percentDone, message); - } - - async onNewBlock(height) { - await this._listener.onNewBlock(height); - } - - async onBalancesChanged(newBalanceStr, newUnlockedBalanceStr) { - await this._listener.onBalancesChanged(BigInteger.parse(newBalanceStr), BigInteger.parse(newUnlockedBalanceStr)); - } - - async onOutputReceived(blockJson) { - let block = new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX_WALLET); - await this._listener.onOutputReceived(block.getTxs()[0].getOutputs()[0]); - } - - async onOutputSpent(blockJson) { - let block = new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX_WALLET); - await this._listener.onOutputSpent(block.getTxs()[0].getInputs()[0]); - } -} - -MoneroWalletFull.DEFAULT_SYNC_PERIOD_IN_MS = 10000; // 10 second sync period by default - -module.exports = MoneroWalletFull; \ No newline at end of file diff --git a/src/main/js/wallet/MoneroWalletKeys.js b/src/main/js/wallet/MoneroWalletKeys.js deleted file mode 100644 index fc0c3476e..000000000 --- a/src/main/js/wallet/MoneroWalletKeys.js +++ /dev/null @@ -1,348 +0,0 @@ -const assert = require("assert"); -const LibraryUtils = require("../common/LibraryUtils"); -const MoneroError = require("../common/MoneroError"); -const MoneroNetworkType = require("../daemon/model/MoneroNetworkType"); -const MoneroSubaddress = require("./model/MoneroSubaddress"); -const MoneroUtils = require("../common/MoneroUtils"); -const MoneroVersion = require("../daemon/model/MoneroVersion"); -const MoneroWallet = require("./MoneroWallet"); -const MoneroWalletConfig = require("./model/MoneroWalletConfig"); - -/** - * Implements a MoneroWallet which only manages keys using WebAssembly. - * - * @implements {MoneroWallet} - * @hideconstructor - */ -class MoneroWalletKeys extends MoneroWallet { - - // --------------------------- STATIC UTILITIES ----------------------------- - - /** - *

Create a wallet using WebAssembly bindings to monero-project.

- * - *

Example:

- * - * - * let wallet = await MoneroWalletKeys.createWallet({
- *    password: "abc123",
- *    networkType: MoneroNetworkType.STAGENET,
- *    seed: "coexist igloo pamphlet lagoon..."
- * }); - *
- * - * @param {MoneroWalletConfig|object} config - MoneroWalletConfig or equivalent config object - * @param {string|number} config.networkType - network type of the wallet to create (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) - * @param {string} config.seed - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) - * @param {string} config.seedOffset - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed phrase - * @param {string} config.primaryAddress - primary address of the wallet to create (only provide if restoring from keys) - * @param {string} config.privateViewKey - private view key of the wallet to create (optional) - * @param {string} config.privateSpendKey - private spend key of the wallet to create (optional) - * @param {string} config.language - language of the wallet's seed (defaults to "English" or auto-detected) - * @return {MoneroWalletKeys} the created wallet - */ - static async createWallet(config) { - - // normalize and validate config - if (config === undefined) throw new MoneroError("Must provide config to create wallet"); - config = config instanceof MoneroWalletConfig ? config : new MoneroWalletConfig(config); - if (config.getSeed() !== undefined && (config.getPrimaryAddress() !== undefined || config.getPrivateViewKey() !== undefined || config.getPrivateSpendKey() !== undefined)) { - throw new MoneroError("Wallet may be initialized with a seed or keys but not both"); - } - if (config.getNetworkType() === undefined) throw new MoneroError("Must provide a networkType: 'mainnet', 'testnet' or 'stagenet'"); - if (config.getSaveCurrent() === true) throw new MoneroError("Cannot save current wallet when creating keys-only wallet"); - - // create wallet - if (config.getSeed() !== undefined) return MoneroWalletKeys._createWalletFromSeed(config); - else if (config.getPrivateSpendKey() !== undefined || config.getPrimaryAddress() !== undefined) return MoneroWalletKeys._createWalletFromKeys(config); - else return MoneroWalletKeys._createWalletRandom(config); - } - - static async _createWalletRandom(config) { - - // validate and sanitize params - config = config.copy(); - if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating random wallet"); - if (config.getRestoreHeight() !== undefined) throw new MoneroError("Cannot provide restoreHeight when creating random wallet"); - MoneroNetworkType.validate(config.getNetworkType()); - if (config.getLanguage() === undefined) config.setLanguage("English"); - - // load wasm module - let module = await LibraryUtils.loadKeysModule(); - - // queue call to wasm module - return module.queueTask(async function() { - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = async function(cppAddress) { - if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); - else resolve(new MoneroWalletKeys(cppAddress)); - }; - - // create wallet in wasm and invoke callback when done - module.create_keys_wallet_random(JSON.stringify(config.toJson()), callbackFn); - }); - }); - } - - static async _createWalletFromSeed(config) { - - // validate and sanitize params - MoneroNetworkType.validate(config.getNetworkType()); - if (config.getSeed() === undefined) throw Error("Must define seed to create wallet from"); - if (config.getSeedOffset() === undefined) config.setSeedOffset(""); - if (config.getLanguage() !== undefined) throw new MoneroError("Cannot provide language when creating wallet from seed"); - - // load wasm module - let module = await LibraryUtils.loadKeysModule(); - - // queue call to wasm module - return module.queueTask(async function() { - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = async function(cppAddress) { - if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); - else resolve(new MoneroWalletKeys(cppAddress)); - }; - - // create wallet in wasm and invoke callback when done - module.create_keys_wallet_from_seed(JSON.stringify(config.toJson()), callbackFn); - }); - }); - } - - static async _createWalletFromKeys(config) { - - // validate and sanitize params - if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating wallet from keys"); - MoneroNetworkType.validate(config.getNetworkType()); - if (config.getPrimaryAddress() === undefined) config.setPrimaryAddress(""); - if (config.getPrivateViewKey() === undefined) config.setPrivateViewKey(""); - if (config.getPrivateSpendKey() === undefined) config.setPrivateSpendKey(""); - if (config.getLanguage() === undefined) config.setLanguage("English"); - - // load wasm module - let module = await LibraryUtils.loadKeysModule(); - - // queue call to wasm module - return module.queueTask(async function() { - return new Promise(function(resolve, reject) { - - // define callback for wasm - let callbackFn = async function(cppAddress) { - if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); - else resolve(new MoneroWalletKeys(cppAddress)); - }; - - // create wallet in wasm and invoke callback when done - module.create_keys_wallet_from_keys(JSON.stringify(config.toJson()), callbackFn); - }); - }); - } - - static async getSeedLanguages() { - let module = await LibraryUtils.loadKeysModule(); - return module.queueTask(async function() { - return JSON.parse(module.get_keys_wallet_seed_languages()).languages; - }); - } - - // --------------------------- INSTANCE METHODS ----------------------------- - - /** - * Internal constructor which is given the memory address of a C++ wallet - * instance. - * - * This method should not be called externally but should be called through - * static wallet creation utilities in this class. - * - * @param {int} cppAddress - address of the wallet instance in C++ - */ - constructor(cppAddress) { - super(); - this._cppAddress = cppAddress; - this._module = LibraryUtils.getWasmModule(); - if (!this._module.create_full_wallet) throw new MoneroError("WASM module not loaded - create wallet instance using static utilities"); // static utilites pre-load wasm module - } - - async addListener(listener) { - throw new MoneroError("MoneroWalletKeys does not support adding listeners"); - } - - async removeListener(listener) { - throw new MoneroError("MoneroWalletKeys does not support removing listeners"); - } - - async isViewOnly() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.is_view_only(that._cppAddress); - }); - } - - async isConnectedToDaemon() { - return false; - } - - async getVersion() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let versionStr = that._module.get_version(that._cppAddress); - let versionJson = JSON.parse(versionStr); - return new MoneroVersion(versionJson.number, versionJson.isRelease); - }); - } - - /** - * @ignore - */ - getPath() { - this._assertNotClosed(); - throw new MoneroError("MoneroWalletKeys does not support a persisted path"); - } - - async getSeed() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let resp = that._module.get_seed(that._cppAddress); - const errorStr = "error: "; - if (resp.indexOf(errorStr) === 0) throw new MoneroError(resp.substring(errorStr.length)); - return resp ? resp : undefined; - }); - } - - async getSeedLanguage() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let resp = that._module.get_seed_language(that._cppAddress); - let errorKey = "error: "; - if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorStr.length)); - return resp ? resp : undefined; - }); - } - - async getPrivateSpendKey() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let resp = that._module.get_private_spend_key(that._cppAddress); - let errorKey = "error: "; - if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorStr.length)); - return resp ? resp : undefined; - }); - } - - async getPrivateViewKey() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let resp = that._module.get_private_view_key(that._cppAddress); - let errorKey = "error: "; - if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorStr.length)); - return resp ? resp : undefined; - }); - } - - async getPublicViewKey() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let resp = that._module.get_public_view_key(that._cppAddress); - let errorKey = "error: "; - if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorStr.length)); - return resp ? resp : undefined; - }); - } - - async getPublicSpendKey() { - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let resp = that._module.get_public_spend_key(that._cppAddress); - let errorKey = "error: "; - if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorStr.length)); - return resp ? resp : undefined; - }); - } - - async getAddress(accountIdx, subaddressIdx) { - this._assertNotClosed(); - assert(typeof accountIdx === "number"); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - return that._module.get_address(that._cppAddress, accountIdx, subaddressIdx); - }); - } - - async getAddressIndex(address) { - this._assertNotClosed(); - let that = this; - return that._module.queueTask(async function() { - that._assertNotClosed(); - let resp = that._module.get_address_index(that._cppAddress, address); - if (resp.charAt(0) !== '{') throw new MoneroError(resp); - return new MoneroSubaddress(JSON.parse(resp)); - }); - } - - getAccounts() { - this._assertNotClosed(); - throw new MoneroError("MoneroWalletKeys does not support getting an enumerable set of accounts; query specific accounts"); - } - - // getIntegratedAddress(paymentId) // TODO - // decodeIntegratedAddress - - async close(save) { - await super.close(save); - if (this._isClosed) return; // closing a closed wallet has no effect - - // save wallet if requested - if (save) await this.save(); - - // queue task to use wasm module - let that = this; - return that._module.queueTask(async function() { - return new Promise(function(resolve, reject) { - if (that._isClosed) { - resolve(); - return; - } - - // define callback for wasm - let callbackFn = async function() { - delete that._cppAddress; - that._isClosed = true; - resolve(); - }; - - // close wallet in wasm and invoke callback when done - that._module.close(that._cppAddress, false, callbackFn); // saving handled external to webassembly - }); - }); - } - - async isClosed() { - return this._isClosed; - } - - // ----------- ADD JSDOC FOR SUPPORTED DEFAULT IMPLEMENTATIONS -------------- - - async getPrimaryAddress() { return super.getPrimaryAddress(...arguments); } - async getSubaddress() { return super.getSubaddress(...arguments); } - - // ----------------------------- PRIVATE HELPERS ---------------------------- - - _assertNotClosed() { - if (this._isClosed) throw new MoneroError("Wallet is closed"); - } -} - -module.exports = MoneroWalletKeys; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroAccount.js b/src/main/js/wallet/model/MoneroAccount.js deleted file mode 100644 index 253d704cd..000000000 --- a/src/main/js/wallet/model/MoneroAccount.js +++ /dev/null @@ -1,131 +0,0 @@ -const assert = require("assert"); -const BigInteger = require("../../common/biginteger").BigInteger; -const MoneroSubaddress = require("./MoneroSubaddress"); - -/** - * Monero account model. - */ -class MoneroAccount { - - constructor(stateOrIndex, primaryAddress, balance, unlockedBalance, subaddresses) { - - // construct from json - if (typeof stateOrIndex === "object") { - this.state = stateOrIndex; - - // deserialize balances - if (this.state.balance !== undefined && !(this.state.balance instanceof BigInteger)) this.state.balance = BigInteger.parse(this.state.balance); - if (this.state.unlockedBalance !== undefined && !(this.state.unlockedBalance instanceof BigInteger)) this.state.unlockedBalance = BigInteger.parse(this.state.unlockedBalance); - - // deserialize subaddresses - if (this.state.subaddresses) { - for (let i = 0; i < this.state.subaddresses.length; i++) { - if (!(this.state.subaddresses[i] instanceof MoneroSubaddress)) { - this.state.subaddresses[i] = new MoneroSubaddress(this.state.subaddresses[i]); - } - } - } - } - - // construct from individual params - else { - this.state = {}; - this.setIndex(stateOrIndex); - this.setPrimaryAddress(primaryAddress); - this.setBalance(balance); - this.setUnlockedBalance(unlockedBalance); - this.setSubaddresses(subaddresses); - } - } - - toJson() { - let json = Object.assign({}, this.state); - if (json.balance) json.balance = json.balance.toString(); - if (json.unlockedBalance) json.unlockedBalance = json.unlockedBalance.toString(); - if (json.subaddresses) { - for (let i = 0; i < json.subaddresses.length; i++) { - json.subaddresses[i] = json.subaddresses[i].toJson(); - } - } - return json; - } - - getIndex() { - return this.state.index; - } - - setIndex(index) { - this.state.index = index; - return this; - } - - getPrimaryAddress() { - return this.state.primaryAddress; - } - - setPrimaryAddress(primaryAddress) { - this.state.primaryAddress = primaryAddress; - return this; - } - - getBalance() { - return this.state.balance; - } - - setBalance(balance) { - this.state.balance = balance; - return this; - } - - getUnlockedBalance() { - return this.state.unlockedBalance; - } - - setUnlockedBalance(unlockedBalance) { - this.state.unlockedBalance = unlockedBalance; - return this; - } - - getTag() { - return this.state.tag; - } - - setTag(tag) { - this.state.tag = tag; - return this; - } - - getSubaddresses() { - return this.state.subaddresses; - } - - setSubaddresses(subaddresses) { - assert(subaddresses === undefined || Array.isArray(subaddresses), "Given subaddresses must be undefined or an array of subaddresses"); - this.state.subaddresses = subaddresses; - if (subaddresses) { - for (let subaddress of subaddresses) { - subaddress.setAccountIndex(this.state.index); - } - } - return this; - } - - toString(indent = 0) { - let str = ""; - str += GenUtils.kvLine("Index", this.getIndex(), indent); - str += GenUtils.kvLine("Primary address", this.getPrimaryAddress(), indent); - str += GenUtils.kvLine("Balance", this.getBalance(), indent); - str += GenUtils.kvLine("Unlocked balance", this.getUnlockedBalance(), indent); - str += GenUtils.kvLine("Tag", this.getTag(), indent); - if (this.getSubaddresses() != null) { - sb += GenUtils.kvLine("Subaddresses", "", indent) - for (let i = 0; i < this.getSubaddresses().size(); i++) { - str += GenUtils.kvLine(i + 1, "", indent + 1); - str += this.getSubaddresses()[i].toString(indent + 2) + "\n"; - } - } - return str.slice(0, str.length - 1); // strip last newline - } -} - -module.exports = MoneroAccount; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroAccountTag.js b/src/main/js/wallet/model/MoneroAccountTag.js deleted file mode 100644 index ca50ae452..000000000 --- a/src/main/js/wallet/model/MoneroAccountTag.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Represents an account tag. - */ -class MoneroAccountTag { - - constructor(tag, label, accountIndices) { - this.tag = tag; - this.label = label; - this.accountIndices = accountIndices; - } - - getTag() { - return this.tag; - } - - setTag(tag) { - this.tag = tag; - return this; - } - - getLabel() { - return this.label; - } - - setLabel(label) { - this.label = label; - return this; - } - - getAccountIndices() { - return this.accountIndices; - } - - setAccountIndices(accountIndices) { - this.accoutIndices = accountIndices; - return this; - } -} - -module.exports = MoneroAccountTag; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroAddressBookEntry.js b/src/main/js/wallet/model/MoneroAddressBookEntry.js deleted file mode 100644 index a3d943c96..000000000 --- a/src/main/js/wallet/model/MoneroAddressBookEntry.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Monero address book entry model - */ -class MoneroAddressBookEntry { - - constructor(state) { - this.state = Object.assign({}, state); - } - - toJson() { - return Object.assign({}, this.state); - } - - getIndex() { - return this.state.index; - } - - setIndex(index) { - this.state.index = index; - return this; - } - - getAddress() { - return this.state.address; - } - - setAddress(address) { - this.state.address = address; - return this; - } - - getDescription() { - return this.state.description; - } - - setDescription(description) { - this.state.description = description; - return this; - } - - getPaymentId() { - return this.state.paymentId; - } - - setPaymentId(paymentId) { - this.state.paymentId = paymentId; - return this; - } -} - -module.exports = MoneroAddressBookEntry; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroCheck.js b/src/main/js/wallet/model/MoneroCheck.js deleted file mode 100644 index 50a1139c1..000000000 --- a/src/main/js/wallet/model/MoneroCheck.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Base class for results from checking a transaction or reserve proof. - * - * @class - */ -class MoneroCheck { - - constructor(state) { - this.state = Object.assign({}, state); - } - - isGood() { - return this.state.isGood; - } - - setIsGood(isGood) { - this.state.isGood = isGood; - return this; - } -} - -module.exports = MoneroCheck; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroCheckReserve.js b/src/main/js/wallet/model/MoneroCheckReserve.js deleted file mode 100644 index 84465b1b9..000000000 --- a/src/main/js/wallet/model/MoneroCheckReserve.js +++ /dev/null @@ -1,43 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; -const MoneroCheck = require("./MoneroCheck"); - -/** - * Results from checking a reserve proof. - * - * @extends {MoneroCheck} - */ -class MoneroCheckReserve extends MoneroCheck { - - constructor(state) { - super(state); - if (this.state.totalAmount !== undefined && !(this.state.totalAmount instanceof BigInteger)) this.state.totalAmount = BigInteger.parse(this.state.totalAmount); - if (this.state.unconfirmedSpentAmount !== undefined && !(this.state.unconfirmedSpentAmount instanceof BigInteger)) this.state.unconfirmedSpentAmount = BigInteger.parse(this.state.unconfirmedSpentAmount); - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getTotalAmount()) json.totalAmount = this.getTotalAmount().toString(); - if (this.getUnconfirmedSpentAmount()) json.unconfirmedSpentAmount = this.getUnconfirmedSpentAmount().toString(); - return json; - } - - getTotalAmount() { - return this.state.totalAmount; - } - - setTotalAmount(totalAmount) { - this.state.totalAmount = totalAmount; - return this; - } - - getUnconfirmedSpentAmount() { - return this.state.unconfirmedSpentAmount; - } - - setUnconfirmedSpentAmount(unconfirmedSpentAmount) { - this.state.unconfirmedSpentAmount = unconfirmedSpentAmount; - return this; - } -} - -module.exports = MoneroCheckReserve; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroCheckTx.js b/src/main/js/wallet/model/MoneroCheckTx.js deleted file mode 100644 index 37f1851f8..000000000 --- a/src/main/js/wallet/model/MoneroCheckTx.js +++ /dev/null @@ -1,50 +0,0 @@ -const MoneroCheck = require("./MoneroCheck"); -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Results from checking a transaction key. - * - * @extends {MoneroCheck} - */ -class MoneroCheckTx extends MoneroCheck { - - constructor(state) { - super(state); - if (this.state.receivedAmount !== undefined && !(this.state.receivedAmount instanceof BigInteger)) this.state.receivedAmount = BigInteger.parse(this.state.receivedAmount); - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getReceivedAmount()) json.receivedAmount = this.getReceivedAmount().toString(); - return json; - } - - inTxPool() { - return this.state.inTxPool; - } - - setInTxPool(inTxPool) { - this.state.inTxPool = inTxPool; - return this; - } - - getNumConfirmations() { - return this.state.numConfirmations; - } - - setNumConfirmations(numConfirmations) { - this.state.numConfirmations = numConfirmations; - return this; - } - - getReceivedAmount() { - return this.state.receivedAmount; - } - - setReceivedAmount(receivedAmount) { - this.state.receivedAmount = receivedAmount; - return this; - } -} - -module.exports = MoneroCheckTx; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroDestination.js b/src/main/js/wallet/model/MoneroDestination.js deleted file mode 100644 index 0759f7c48..000000000 --- a/src/main/js/wallet/model/MoneroDestination.js +++ /dev/null @@ -1,71 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; -const GenUtils = require("../../common/GenUtils"); -const MoneroError = require("../../common/MoneroError"); - -/** - * Models an outgoing transfer destination. - */ -class MoneroDestination { - - /** - * Construct the model. - * - * @param {MoneroDestination|object|string} stateOrAddress is a MoneroDestination, JS object, or hex string to initialize from (optional) - * @param {BigInteger|string} amount - the destination amount - */ - constructor(stateOrAddress, amount) { - if (!stateOrAddress) this.state = {}; - else if (stateOrAddress instanceof MoneroDestination) this.state = stateOrAddress.toJson(); - else if (typeof stateOrAddress === "object") { - this.state = Object.assign({}, stateOrAddress); - if (typeof this.state.amount === "number") this.state.amount = BigInteger.parse(this.state.amount); - } else if (typeof stateOrAddress === "string") { - this.state = {}; - this.setAddress(stateOrAddress); - } - else throw new MoneroError("stateOrAddress must be a MoneroDestination, JavaScript object, or hex string"); - if (amount) this.state.amount = amount; - this.setAmount(this.state.amount); - } - - getAddress() { - return this.state.address; - } - - setAddress(address) { - this.state.address = address; - return this; - } - - getAmount() { - return this.state.amount; - } - - setAmount(amount) { - if (amount !== undefined && !(this.state.amount instanceof BigInteger)) { - if (typeof amount === "number") throw new MoneroError("Destination amount must be BigInteger or string"); - try { amount = BigInteger.parse(amount); } - catch (err) { throw new MoneroError("Invalid destination amount: " + amount); } - } - this.state.amount = amount; - return this; - } - - copy() { - return new MoneroDestination(this); - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getAmount()) json.amount = this.getAmount().toString(); - return json; - } - - toString(indent = 0) { - let str = GenUtils.kvLine("Address", this.getAddress(), indent); - str += GenUtils.kvLine("Amount", this.getAmount() ? this.getAmount().toString() : undefined, indent); - return str.slice(0, str.length - 1); // strip last newline - } -} - -module.exports = MoneroDestination; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroIntegratedAddress.js b/src/main/js/wallet/model/MoneroIntegratedAddress.js deleted file mode 100644 index 57fff0cce..000000000 --- a/src/main/js/wallet/model/MoneroIntegratedAddress.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Monero integrated address model. - */ -class MoneroIntegratedAddress { - - constructor(state) { - this.state = Object.assign({}, state); - } - - toJson() { - return Object.assign({}, this.state); - } - - getStandardAddress() { - return this.state.standardAddress; - } - - setStandardAddress(standardAddress) { - this.state.standardAddress = standardAddress; - return this; - } - - getPaymentId() { - return this.state.paymentId; - } - - setPaymentId(paymentId) { - this.state.paymentId = paymentId; - return this; - } - - getIntegratedAddress() { - return this.state.integratedAddress; - } - - setIntegratedAddress(integratedAddress) { - this.state.integratedAddress = integratedAddress; - return this; - } - - toString() { - return this.state.integratedAddress; - } -} - -module.exports = MoneroIntegratedAddress; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroKeyImageImportResult.js b/src/main/js/wallet/model/MoneroKeyImageImportResult.js deleted file mode 100644 index 64f8ad60b..000000000 --- a/src/main/js/wallet/model/MoneroKeyImageImportResult.js +++ /dev/null @@ -1,50 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; - -/** - * Models results from importing key images. - */ -class MoneroKeyImageImportResult { - - constructor(state) { - state = Object.assign({}, state); - if (state.spentAmount !== undefined && !(state.spentAmount instanceof BigInteger)) state.spentAmount = BigInteger.parse(state.spentAmount); - if (state.unspentAmount !== undefined && !(state.unspentAmount instanceof BigInteger)) state.unspentAmount = BigInteger.parse(state.unspentAmount); - this.state = state; - } - - toJson() { - let json = Object.assign({}, this.state); - if (this.getSpentAmount()) json.spentAmount = this.getSpentAmount().toString(); - if (this.getUnspentAmount()) json.unspentAmount = this.getUnspentAmount().toString(); - return json; - } - - getHeight() { - return this.state.height; - } - - setHeight(height) { - this.state.height = height; - return this; - } - - getSpentAmount() { - return this.state.spentAmount; - } - - setSpentAmount(spentAmount) { - this.state.spentAmount = spentAmount; - return this; - } - - getUnspentAmount() { - return this.state.unspentAmount; - } - - setUnspentAmount(unspentAmount) { - this.state.unspentAmount = unspentAmount; - return this; - } -} - -module.exports = MoneroKeyImageImportResult; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroMessageSignatureResult.js b/src/main/js/wallet/model/MoneroMessageSignatureResult.js deleted file mode 100644 index 035871be6..000000000 --- a/src/main/js/wallet/model/MoneroMessageSignatureResult.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Message signature verification result. - * - * @class - */ -class MoneroMessageSignatureResult { - - constructor(stateOrIsGood, isOld, signatureType, version) { - if (typeof stateOrIsGood === "boolean") { - this.state = {}; - this.state.isGood = stateOrIsGood; - this.state.isOld = isOld; - this.state.signatureType = signatureType; - this.state.version = version; - } else { - this.state = stateOrIsGood; - } - } - - toJson() { - return Object.assign({}, this.state); - } - - isGood() { - return this.state.isGood; - } - - setIsGood(isGood) { - this.state.isGood = isGood; - return this; - } - - isOld() { - return this.state.isOld; - } - - setIsOld(isOld) { - this.state.isOld = isOld; - return this; - } - - getSignatureType() { - return this.state.signatureType; - } - - setSignatureType(signatureType) { - this.state.signatureType = signatureType; - return this; - } - - getVersion() { - return this.state.version; - } - - setVersion(version) { - this.state.version = version; - return this; - } -} - -module.exports = MoneroMessageSignatureResult; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroMessageSignatureType.js b/src/main/js/wallet/model/MoneroMessageSignatureType.js deleted file mode 100644 index 162ace349..000000000 --- a/src/main/js/wallet/model/MoneroMessageSignatureType.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Enumerate message signature types. - * - * @hideconstructor - */ -class MoneroMessageSignatureType {} - -/** - * Sign with spend key (value=0). - */ -MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY = 0; - -/** - * Sign with the view key (value=1). - */ -MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY = 1; - -module.exports = MoneroMessageSignatureType; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroMultisigInfo.js b/src/main/js/wallet/model/MoneroMultisigInfo.js deleted file mode 100644 index 89f401599..000000000 --- a/src/main/js/wallet/model/MoneroMultisigInfo.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Models information about a multisig wallet. - */ -class MoneroMultisigInfo { - - constructor(state) { - this.state = Object.assign({}, state); - } - - toJson() { - return Object.assign({}, this.state); - } - - isMultisig() { - return this.state.isMultisig; - } - - setIsMultisig(isMultisig) { - this.state.isMultisig = isMultisig; - return this; - } - - isReady() { - return this.state.isReady; - } - - setIsReady(isReady) { - this.state.isReady = isReady; - } - - getThreshold() { - return this.state.threshold; - } - - setThreshold(threshold) { - this.state.threshold = threshold; - } - - getNumParticipants() { - return this.state.numParticipants; - } - - setNumParticipants(numParticipants) { - this.state.numParticipants = numParticipants; - } -} - -module.exports = MoneroMultisigInfo; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroMultisigInitResult.js b/src/main/js/wallet/model/MoneroMultisigInitResult.js deleted file mode 100644 index c49c80aac..000000000 --- a/src/main/js/wallet/model/MoneroMultisigInitResult.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Models the result of initializing a multisig wallet which results in the - * multisig wallet's address xor another multisig hex to share with - * participants to create the wallet. - */ -class MoneroMultisigInitResult { - - constructor(state) { - this.state = Object.assign({}, state); - } - - toJson() { - return Object.assign({}, this.state); - } - - getAddress() { - return this.state.address; - } - - setAddress(address) { - this.state.address = address; - return this; - } - - getMultisigHex() { - return this.state.multisigHex; - } - - setMultisigHex(multisigHex) { - this.state.multisigHex = multisigHex; - return this; - } -} - -module.exports = MoneroMultisigInitResult; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroMultisigSignResult.js b/src/main/js/wallet/model/MoneroMultisigSignResult.js deleted file mode 100644 index 1abdecd61..000000000 --- a/src/main/js/wallet/model/MoneroMultisigSignResult.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Models the result of signing multisig tx hex. - */ -class MoneroMultisigSignResult { - - constructor(state) { - this.state = Object.assign({}, state); - } - - toJson() { - return Object.assign({}, this.state); - } - - getSignedMultisigTxHex() { - return this.state.signedMultisigTxHex; - } - - setSignedMultisigTxHex(signedTxMultisigHex) { - this.state.signedMultisigTxHex = signedTxMultisigHex; - } - - getTxHashes() { - return this.state.txHashes; - } - - setTxHashes(txHashes) { - this.state.txHashes = txHashes; - } -} - -module.exports = MoneroMultisigSignResult; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroOutgoingTransfer.js b/src/main/js/wallet/model/MoneroOutgoingTransfer.js deleted file mode 100644 index f747ac49e..000000000 --- a/src/main/js/wallet/model/MoneroOutgoingTransfer.js +++ /dev/null @@ -1,109 +0,0 @@ -const assert = require("assert"); -const GenUtils = require("../../common/GenUtils"); -const MoneroDestination = require("./MoneroDestination"); -const MoneroTransfer = require("./MoneroTransfer"); - -/** - * Models an outgoing transfer of funds from the wallet. - * - * @extends {MoneroTransfer} - */ -class MoneroOutgoingTransfer extends MoneroTransfer { - - /** - * Construct the model. - * - * @param {MoneroOutgoingTranser|object} state is existing state to initialize from (optional) - */ - constructor(state) { - super(state); - state = this.state; - - // deserialize destinations - if (state.destinations) { - for (let i = 0; i < state.destinations.length; i++) { - if (!(state.destinations[i] instanceof MoneroDestination)) state.destinations[i] = new MoneroDestination(state.destinations[i]); - } - } - } - - isIncoming() { - return false; - } - - getSubaddressIndices() { - return this.state.subaddressIndices; - } - - setSubaddressIndices(subaddressIndices) { - this.state.subaddressIndices = subaddressIndices; - return this; - } - - getAddresses() { - return this.state.addresses; - } - - setAddresses(addresses) { - this.state.addresses = addresses; - return this; - } - - getDestinations() { - return this.state.destinations; - } - - setDestinations(destinations) { - this.state.destinations = destinations; - return this; - } - - copy() { - return new MoneroOutgoingTransfer(this); - } - - toJson() { - let json = Object.assign({}, this.state, super.toJson()); // merge json onto inherited state - if (this.getDestinations()) { - json.destinations = []; - for (let destination of this.getDestinations()) json.destinations.push(destination.toJson()); - } - delete json.tx; // parent tx is not serialized - return json; - } - - /** - * Updates this transaction by merging the latest information from the given - * transaction. - * - * Merging can modify or build references to the transfer given so it - * should not be re-used or it should be copied before calling this method. - * - * @param transfer is the transfer to merge into this one - */ - merge(transfer) { - super.merge(transfer); - assert(transfer instanceof MoneroOutgoingTransfer); - if (this === transfer) return this; - this.setSubaddressIndices(GenUtils.reconcile(this.getSubaddressIndices(), transfer.getSubaddressIndices())); - this.setAddresses(GenUtils.reconcile(this.getAddresses(), transfer.getAddresses())); - this.setDestinations(GenUtils.reconcile(this.getDestinations(), transfer.getDestinations())); - return this; - } - - toString(indent = 0) { - let str = super.toString(indent) + "\n"; - str += GenUtils.kvLine("Subaddress indices", this.getSubaddressIndices(), indent); - str += GenUtils.kvLine("Addresses", this.getAddresses(), indent); - if (this.getDestinations()) { - str += GenUtils.kvLine("Destinations", "", indent); - for (let i = 0; i < this.getDestinations().length; i++) { - str += GenUtils.kvLine(i + 1, "", indent + 1); - str += this.getDestinations()[i].toString(indent + 2) + "\n"; - } - } - return str.slice(0, str.length - 1); // strip last newline - } -} - -module.exports = MoneroOutgoingTransfer; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroOutputQuery.js b/src/main/js/wallet/model/MoneroOutputQuery.js deleted file mode 100644 index 51422b2e3..000000000 --- a/src/main/js/wallet/model/MoneroOutputQuery.js +++ /dev/null @@ -1,139 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; -const MoneroError = require("../../common/MoneroError"); -const MoneroOutputWallet = require("./MoneroOutputWallet"); - -/** - * Configuration to query wallet outputs. - * - * @extends {MoneroOutputWallet} - */ -class MoneroOutputQuery extends MoneroOutputWallet { - - /** - *

Construct the output query.

- * - *

Example:

- * - * - * // get available outputs in account 0 with a minimum amount
- * let outputs = await wallet.getOutputs({
- *    isSpent: false,
- *    isLocked: false,
- *    accountIndex: 0,
- *    minAmount: new BigInteger("750000")
- * }); - *
- * - *

All configuration is optional. All outputs are returned except those that don't meet criteria defined in this query.

- * - * @param {object} config - output query configuration (optional) - * @param {int} config.accountIndex - get outputs in this account index - * @param {int} config.subaddressIndex - get outputs in this subaddress index - * @param {int[]} config.subaddressIndices - get outputs in these subaddress indices - * @param {BigInteger} config.amount - get outputs with this amount - * @param {BigInteger} config.minAmount - get outputs with amount greater than or equal to this amount - * @param {BigInteger} config.maxAmount - get outputs with amount less than or equal to this amount - * @param {boolean} config.isSpent - get spent xor unspent outputs - * @param {boolean} config.isFrozen - get frozen xor thawed outputs - * @param {object|MoneroKeyImage} config.keyImage - get outputs with a key image matching fields defined in this key image - * @param {string} config.keyImage.hex - get outputs with this key image hex - * @param {string} config.keyImage.signature - get outputs with this key image signature - * @param {object|MoneroTxQuery} config.txQuery - get outputs whose tx match this tx query - */ - constructor(config) { - super(config); - - // deserialize if necessary - const MoneroTxQuery = require("./MoneroTxQuery"); - if (this.state.minAmount !== undefined && !(this.state.minAmount instanceof BigInteger)) this.state.minAmount = BigInteger.parse(this.state.minAmount); - if (this.state.maxAmount !== undefined && !(this.state.maxAmount instanceof BigInteger)) this.state.maxAmount = BigInteger.parse(this.state.maxAmount); - if (this.state.txQuery && !(this.state.txQuery instanceof MoneroTxQuery)) this.state.txQuery = new MoneroTxQuery(this.state.txQuery); - if (this.state.txQuery) this.state.txQuery.setOutputQuery(this); - if (this.state.isLocked !== undefined) throw new MoneroError("isLocked must be part of tx query, not output query"); - } - - copy() { - return new MoneroOutputQuery(this); - } - - toJson() { - let json = Object.assign({}, this.state, super.toJson()); - if (this.getMinAmount()) json.minAmount = this.getMinAmount().toString(); - if (this.getMaxAmount()) json.maxAmount = this.getMaxAmount().toString(); - delete json.txQuery; - return json; - } - - getMinAmount() { - return this.state.minAmount; - } - - setMinAmount(minAmount) { - this.state.minAmount = minAmount; - return this; - } - - getMaxAmount() { - return this.state.maxAmount; - } - - setMaxAmount(maxAmount) { - this.state.maxAmount = maxAmount; - return this; - } - - getTxQuery() { - return this.state.txQuery; - } - - setTxQuery(txQuery) { - this.state.txQuery = txQuery; - if (txQuery) txQuery.state.outputQuery = this; - return this; - } - - getSubaddressIndices() { - return this.state.subaddressIndices; - } - - setSubaddressIndices(subaddressIndices) { - this.state.subaddressIndices = subaddressIndices; - return this; - } - - meetsCriteria(output, queryParent) { - if (!(output instanceof MoneroOutputWallet)) throw new Error("Output not given to MoneroOutputQuery.meetsCriteria(output)"); - if (queryParent === undefined) queryParent = true; - - // filter on output - if (this.getAccountIndex() !== undefined && this.getAccountIndex() !== output.getAccountIndex()) return false; - if (this.getSubaddressIndex() !== undefined && this.getSubaddressIndex() !== output.getSubaddressIndex()) return false; - if (this.getAmount() !== undefined && this.getAmount().compare(output.getAmount()) !== 0) return false; - if (this.isSpent() !== undefined && this.isSpent() !== output.isSpent()) return false; - if (this.isFrozen() !== undefined && this.isFrozen() !== output.isFrozen()) return false; - - // filter on output's key image - if (this.getKeyImage() !== undefined) { - if (output.getKeyImage() === undefined) return false; - if (this.getKeyImage().getHex() !== undefined && this.getKeyImage().getHex() !== output.getKeyImage().getHex()) return false; - if (this.getKeyImage().getSignature() !== undefined && this.getKeyImage().getSignature() !== output.getKeyImage().getSignature()) return false; - } - - // filter on extensions - if (this.getSubaddressIndices() !== undefined && !this.getSubaddressIndices().includes(output.getSubaddressIndex())) return false; - - // filter with tx query - if (this.getTxQuery() && !this.getTxQuery().meetsCriteria(output.getTx(), false)) return false; - - // filter on remaining fields - if (this.getMinAmount() !== undefined && (output.getAmount() === undefined || output.getAmount().compare(this.getMinAmount()) < 0)) return false; - if (this.getMaxAmount() !== undefined && (output.getAmount() === undefined || output.getAmount().compare(this.getMaxAmount()) > 0)) return false; - - // output meets query - return true; - } -} - -MoneroOutputQuery._EMPTY_OUTPUT = new MoneroOutputWallet(); - -module.exports = MoneroOutputQuery; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroOutputWallet.js b/src/main/js/wallet/model/MoneroOutputWallet.js deleted file mode 100644 index 54c883328..000000000 --- a/src/main/js/wallet/model/MoneroOutputWallet.js +++ /dev/null @@ -1,109 +0,0 @@ -const assert = require("assert"); -const GenUtils = require("../../common/GenUtils"); -const MoneroOutput = require("../../daemon/model/MoneroOutput"); - -/** - * Models a Monero output with wallet extensions. - * - * @class - * @extends {MoneroOutput} - */ -class MoneroOutputWallet extends MoneroOutput { - - /** - * Construct the model. - * - * @param {MoneroOutputWallet|object} state is existing state to initialize from (optional) - */ - constructor(state) { - super(state); - } - - getAccountIndex() { - return this.state.accountIndex; - } - - setAccountIndex(accountIndex) { - this.state.accountIndex = accountIndex; - return this; - } - - getSubaddressIndex() { - return this.state.subaddressIndex; - } - - setSubaddressIndex(subaddressIndex) { - this.state.subaddressIndex = subaddressIndex; - return this; - } - - isSpent() { - return this.state.isSpent; - } - - setIsSpent(isSpent) { - this.state.isSpent = isSpent; - return this; - } - - /** - * Indicates if this output has been deemed 'malicious' and will therefore - * not be spent by the wallet. - * - * @return Boolean is whether or not this output is frozen - */ - isFrozen() { - return this.state.isFrozen; - } - - setIsFrozen(isFrozen) { - this.state.isFrozen = isFrozen; - return this; - } - - isLocked() { - if (this.getTx() === undefined) return undefined; - return this.getTx().isLocked(); - } - - copy() { - return new MoneroOutputWallet(this.toJson()); - } - - toJson() { - let json = Object.assign({}, this.state, super.toJson()); - delete json.tx; - return json; - } - - /** - * Updates this output by merging the latest information from the given - * output. - * - * Merging can modify or build references to the output given so it - * should not be re-used or it should be copied before calling this method. - * - * @param output is the output to merge into this one - */ - merge(output) { - assert(output instanceof MoneroOutputWallet); - if (this === output) return; - super.merge(output); - this.setAccountIndex(GenUtils.reconcile(this.getAccountIndex(), output.getAccountIndex())); - this.setSubaddressIndex(GenUtils.reconcile(this.getSubaddressIndex(), output.getSubaddressIndex())); - this.setIsSpent(GenUtils.reconcile(this.isSpent(), output.isSpent(), {resolveTrue: true})); // output can become spent - this.setIsFrozen(GenUtils.reconcile(this.isFrozen(), output.isFrozen())); - return this; - } - - toString(indent) { - let str = super.toString(indent) + "\n" - str += GenUtils.kvLine("Account index", this.getAccountIndex(), indent); - str += GenUtils.kvLine("Subaddress index", this.getSubaddressIndex(), indent); - str += GenUtils.kvLine("Is spent", this.isSpent(), indent); - str += GenUtils.kvLine("Is frozen", this.isFrozen(), indent); - return str.slice(0, str.length - 1); // strip last newline - } -} - -module.exports = MoneroOutputWallet; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroSubaddress.js b/src/main/js/wallet/model/MoneroSubaddress.js deleted file mode 100644 index f0bb10040..000000000 --- a/src/main/js/wallet/model/MoneroSubaddress.js +++ /dev/null @@ -1,127 +0,0 @@ -const BigInteger = require("../../common/biginteger").BigInteger; -const GenUtils = require("../../common/GenUtils"); -const assert = require("assert"); - -/** - * Monero subaddress model. - */ -class MoneroSubaddress { - - constructor(stateOrAddress, accountIndex, index) { - if (stateOrAddress === undefined || typeof stateOrAddress === "string") { - this.state = {}; - this.setAddress(stateOrAddress); - this.setAccountIndex(accountIndex); - this.setIndex(index); - } else { - this.state = stateOrAddress; - assert(accountIndex === undefined && index === undefined, "Can construct MoneroSubaddress with object or params but not both"); - if (this.state.balance !== undefined && !(this.state.balance instanceof BigInteger)) this.state.balance = BigInteger.parse(this.state.balance); - if (this.state.unlockedBalance !== undefined && !(this.state.unlockedBalance instanceof BigInteger)) this.state.unlockedBalance = BigInteger.parse(this.state.unlockedBalance); - } - } - - toJson() { - let json = Object.assign({}, this.state); - if (json.balance) json.balance = json.balance.toString(); - if (json.unlockedBalance) json.unlockedBalance = json.unlockedBalance.toString(); - return json; - } - - getAccountIndex() { - return this.state.accountIndex; - } - - setAccountIndex(accountIndex) { - this.state.accountIndex = accountIndex; - return this; - } - - getIndex() { - return this.state.index; - } - - setIndex(index) { - this.state.index = index; - return this; - } - - getAddress() { - return this.state.address; - } - - setAddress(address) { - this.state.address = address; - return this; - } - - getLabel() { - return this.state.label; - } - - setLabel(label) { - this.state.label = label; - return this; - } - - getBalance() { - return this.state.balance; - } - - setBalance(balance) { - this.state.balance = balance; - return this; - } - - getUnlockedBalance() { - return this.state.unlockedBalance; - } - - setUnlockedBalance(unlockedBalance) { - this.state.unlockedBalance = unlockedBalance; - return this; - } - - getNumUnspentOutputs() { - return this.state.numUnspentOutputs; - } - - setNumUnspentOutputs(numUnspentOutputs) { - this.state.numUnspentOutputs = numUnspentOutputs; - return this; - } - - isUsed() { - return this.state.isUsed; - } - - setIsUsed(isUsed) { - this.state.isUsed = isUsed; - return this; - } - - getNumBlocksToUnlock() { - return this.state.numBlocksToUnlock; - } - - setNumBlocksToUnlock(numBlocksToUnlock) { - this.state.numBlocksToUnlock = numBlocksToUnlock; - return this; - } - - toString(indent) { - let str = ""; - str += GenUtils.kvLine("Account index", this.getAccountIndex(), indent); - str += GenUtils.kvLine("Subaddress index", this.getIndex(), indent); - str += GenUtils.kvLine("Address", this.getAddress(), indent); - str += GenUtils.kvLine("Label", this.getLabel(), indent); - str += GenUtils.kvLine("Balance", this.getBalance(), indent); - str += GenUtils.kvLine("Unlocked balance", this.getUnlockedBalance(), indent); - str += GenUtils.kvLine("Num unspent outputs", this.getNumUnspentOutputs(), indent); - str += GenUtils.kvLine("Is used", this.isUsed(), indent); - str += GenUtils.kvLine("Num blocks to unlock", this.getNumBlocksToUnlock(), indent); - return str.slice(0, str.length - 1); // strip last newline - } -} - -module.exports = MoneroSubaddress; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroTransferQuery.js b/src/main/js/wallet/model/MoneroTransferQuery.js deleted file mode 100644 index 899d7d1d8..000000000 --- a/src/main/js/wallet/model/MoneroTransferQuery.js +++ /dev/null @@ -1,221 +0,0 @@ -const MoneroIncomingTransfer = require("./MoneroIncomingTransfer"); -const MoneroOutgoingTransfer = require("./MoneroOutgoingTransfer"); -const MoneroTransfer = require("./MoneroTransfer"); -const MoneroError = require("../../common/MoneroError") - -/** - * Configuration to query wallet transfers. - * - * @extends {MoneroTransfer} - */ -class MoneroTransferQuery extends MoneroTransfer { - - /** - *

Construct the transfer query.

- * - *

Example:

- * - * - * // get incoming transfers to account 0, subaddress 1
- * let transfers = await wallet.getTransfers({
- *    accountIndex: 0,
- *    subaddressIndex: 0
- * }); - *
- * - *

All configuration is optional. All transfers are returned except those that don't meet criteria defined in this query.

- * - * @param {object} config - transfer query configuration (optional) - * @param {BigInteger} config.amount - get transfers with this amount - * @param {int} config.accountIndex - get transfers to/from this account index - * @param {int} config.subaddressIndex - get transfers to/from this subaddress index - * @param {int[]} config.subaddressIndices - get transfers to/from these subaddress indices - * @param {string} config.address - get transfers to/from this wallet address - * @param {string[]} config.addresses - get transfers to/from these wallet addresses - * @param {boolean} config.isIncoming - get transfers which are incoming if true - * @param {boolean} config.isOutgoing - get transfers which are outgoing if true - * @param {boolean} config.hasDestinations - get transfers with known destinations if true (destinations are only stored locally with the wallet) - * @param {object|MoneroTxQuery} config.txQuery - get transfers whose tx match this tx query - */ - constructor(config) { - super(config); - - // deserialize if necessary - const MoneroTxQuery = require("./MoneroTxQuery"); - if (this.state.txQuery && !(this.state.txQuery instanceof MoneroTxQuery)) this.state.txQuery = new MoneroTxQuery(this.state.txQuery); - if (this.state.txQuery) this.state.txQuery.setTransferQuery(this); - - // alias isOutgoing to isIncoming - if (this.state.isOutgoing !== undefined) this.state.isIncoming = !this.state.isOutgoing; - - // validate state - this._validate(); - } - - copy() { - return new MoneroTransferQuery(this); - } - - toJson() { - let json = Object.assign({}, this.state, super.toJson()); - delete json.txQuery; - return json; - } - - getTxQuery() { - return this.state.txQuery; - } - - setTxQuery(txQuery) { - this.state.txQuery = txQuery; - if (txQuery) txQuery.state.transferQuery = this; - return this; - } - - isIncoming() { - return this.state.isIncoming; - } - - setIsIncoming(isIncoming) { - this.state.isIncoming = isIncoming; - return this; - } - - isOutgoing() { - return this.state.isIncoming === undefined ? undefined : !this.state.isIncoming; - } - - setIsOutgoing(isOutgoing) { - this.state.isIncoming = isOutgoing === undefined ? undefined : !isOutgoing; - return this; - } - - getAddress() { - return this.state.address; - } - - setAddress(address) { - this.state.address = address; - return this; - } - - getAddresses() { - return this.state.addresses; - } - - setAddresses(addresses) { - this.state.addresses = addresses; - return this; - } - - getSubaddressIndex() { - return this.state.subaddressIndex; - } - - setSubaddressIndex(subaddressIndex) { - this.state.subaddressIndex = subaddressIndex; - this._validate(); - return this; - } - - getSubaddressIndices() { - return this.state.subaddressIndices; - } - - setSubaddressIndices(subaddressIndices) { - this.state.subaddressIndices = subaddressIndices; - this._validate(); - return this; - } - - getDestinations() { - return this.state.destinations; - } - - setDestinations(destinations) { - this.state.destinations = destinations; - return this; - } - - hasDestinations() { - return this.state.hasDestinations; - } - - setHasDestinations(hasDestinations) { - this.state.hasDestinations = hasDestinations; - return this; - } - - /** - * Convenience method to query outputs by the locked state of their tx. - * - * @param isLocked specifies if the output's tx must be locked or unlocked (optional) - * @return {MoneroOutputQuery} this query for chaining - */ - setIsLocked(isLocked) { - if (this.state.txQuery === undefined) this.state.txQuery = new MoneroTxQuery(); - this.state.txQuery.setIsLocked(isLocked); - return this; - } - - meetsCriteria(transfer, queryParent) { - if (!(transfer instanceof MoneroTransfer)) throw new Error("Transfer not given to MoneroTransferQuery.meetsCriteria(transfer)"); - if (queryParent === undefined) queryParent = true; - - // filter on common fields - if (this.isIncoming() !== undefined && this.isIncoming() !== transfer.isIncoming()) return false; - if (this.isOutgoing() !== undefined && this.isOutgoing() !== transfer.isOutgoing()) return false; - if (this.getAmount() !== undefined && this.getAmount().compare(transfer.getAmount()) !== 0) return false; - if (this.getAccountIndex() !== undefined && this.getAccountIndex() !== transfer.getAccountIndex()) return false; - - // filter on incoming fields - if (transfer instanceof MoneroIncomingTransfer) { - if (this.hasDestinations()) return false; - if (this.getAddress() !== undefined && this.getAddress() !== transfer.getAddress()) return false; - if (this.getAddresses() !== undefined && !this.getAddresses().includes(transfer.getAddress())) return false; - if (this.getSubaddressIndex() !== undefined && this.getSubaddressIndex() !== transfer.getSubaddressIndex()) return false; - if (this.getSubaddressIndices() !== undefined && !this.getSubaddressIndices().includes(transfer.getSubaddressIndex())) return false; - } - - // filter on outgoing fields - else if (transfer instanceof MoneroOutgoingTransfer) { - - // filter on addresses which must have overlap - if (this.getAddress() !== undefined && (transfer.getAddresses() === undefined || !transfer.getAddresses().includes(this.getAddress()))) return false; // TODO: will filter all transfers that don't contain addresses (outgoing txs might not have this field initialized) - if (this.getAddresses() !== undefined) { - if (!transfer.getAddresses()) return false; - if (!this.getAddresses().some(address => transfer.getAddresses().includes(address))) return false; - } - - // filter on subaddress indices - if (this.getSubaddressIndex() !== undefined && (transfer.getSubaddressIndices() === undefined || !transfer.getSubaddressIndices().includes(this.getSubaddressIndex()))) return false; - if (this.getSubaddressIndices() !== undefined) { - if (!transfer.getSubaddressIndices()) return false; - if (!this.getSubaddressIndices().some(subaddressIdx => transfer.getSubaddressIndices().includes(subaddressIdx))) return false; - } - - // filter on having destinations - if (this.hasDestinations() !== undefined) { - if (this.hasDestinations() && transfer.getDestinations() === undefined) return false; - if (!this.hasDestinations() && transfer.getDestinations() !== undefined) return false; - } - - // filter on destinations TODO: start with test for this -// if (this.getDestionations() !== undefined && this.getDestionations() !== transfer.getDestionations()) return false; - } - - // otherwise invalid type - else throw new Error("Transfer must be MoneroIncomingTransfer or MoneroOutgoingTransfer"); - - // filter with tx filter - if (queryParent && this.getTxQuery() !== undefined && !this.getTxQuery().meetsCriteria(transfer.getTx())) return false; - return true; - } - - _validate() { - if (this.getSubaddressIndex() !== undefined && this.getSubaddressIndex() < 0) throw new MoneroError("Subaddress index must be >= 0"); - if (this.getSubaddressIndices() !== undefined) for (let subaddressIdx of this.getSubaddressIndices()) if (subaddressIdx < 0) throw new MoneroError("Subaddress indices must be >= 0"); - } -} - -module.exports = MoneroTransferQuery; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroTxConfig.js b/src/main/js/wallet/model/MoneroTxConfig.js deleted file mode 100644 index 8d2e8816f..000000000 --- a/src/main/js/wallet/model/MoneroTxConfig.js +++ /dev/null @@ -1,340 +0,0 @@ -const assert = require("assert"); -const BigInteger = require("../../common/biginteger").BigInteger; -const GenUtils = require("../../common/GenUtils"); -const MoneroDestination = require("./MoneroDestination"); -const MoneroError = require("../../common/MoneroError"); - -/** - * Configures a transaction to send, sweep, or create a payment URI. - */ -class MoneroTxConfig { - - /** - *

Generic request to transfer funds from a wallet.

- * - *

Examples:

- * - * - * let config1 = new MoneroTxConfig({
- *    accountIndex: 0,
- *    address: "59aZULsUF3YN...",
- *    amount: new BigInteger("500000"),
- *    priority: MoneroTxPriority.NORMAL,
- *    relay: true
- * });

- *
- * - * @param {MoneroTxConfig|object} config - configures the transaction to create (optional) - * @param {string} config.address - single destination address - * @param {BigInteger} config.amount - single destination amount - * @param {int} config.accountIndex - source account index to transfer funds from - * @param {int} config.subaddressIndex - source subaddress index to transfer funds from - * @param {int[]} config.subaddressIndices - source subaddress indices to transfer funds from - * @param {boolean} config.relay - relay the transaction to peers to commit to the blockchain - * @param {MoneroTxPriority} config.priority - transaction priority (default MoneroTxPriority.NORMAL) - * @param {MoneroDestination[]} config.destinations - addresses and amounts in a multi-destination tx - * @param {int[]} config.subtractFeeFrom - list of destination indices to split the transaction fee - * @param {string} config.paymentId - transaction payment ID - * @param {BigInteger} config.unlockTime - minimum height or timestamp for the transaction to unlock (default 0) - * @param {string} config.note - transaction note saved locally with the wallet - * @param {string} config.recipientName - recipient name saved locally with the wallet - * @param {boolean} config.canSplit - allow funds to be transferred using multiple transactions - * @param {BigInteger} config.belowAmount - for sweep requests, include outputs below this amount when sweeping wallet, account, subaddress, or all unlocked funds - * @param {boolean} config.sweepEachSubaddress - for sweep requests, sweep each subaddress individually instead of together if true - * @param {string} config.keyImage - key image to sweep (ignored except in sweepOutput() requests) - */ - constructor(config, relaxValidation) { // relax validation for internal use to process json from rpc or cpp - if (arguments.length > 2) throw new MoneroError("MoneroTxConfig can be constructed with only two parameters but was given " + arguments.length) - - // initialize internal state - if (!config) this.state = {}; - else if (config instanceof MoneroTxConfig) this.state = config.toJson(); - else if (typeof config === "object") { - this.state = Object.assign({}, config); - if (relaxValidation) { - if (typeof this.state.amount === "number") this.state.amount = BigInteger.parse(this.state.amount); - if (typeof this.state.unlockTime === "number") this.state.unlockTime = BigInteger.parse(this.state.unlockTime); - if (typeof this.state.belowAmount === "number") this.state.belowAmount = BigInteger.parse(this.state.belowAmount); - } - - // check for unsupported fields - for (let key of Object.keys(config)) { - if (!GenUtils.arrayContains(MoneroTxConfig.SUPPORTED_FIELDS, key)) { - throw new MoneroError("Unsupported field in MoneroTxConfig: '" + key + "'"); - } - } - } - else throw new MoneroError("Invalid argument given to MoneroTxConfig: " + typeof config); - - // deserialize BigIntegers - if (this.state.fee !== undefined && !(this.state.fee instanceof BigInteger)) this.state.fee = BigInteger.parse(this.state.fee); - if (this.state.unlockTime !== undefined && !(this.state.unlockTime instanceof BigInteger)) this.state.unlockTime = BigInteger.parse(this.state.unlockTime); - if (this.state.belowAmount !== undefined && !(this.state.belowAmount instanceof BigInteger)) this.state.belowAmount = BigInteger.parse(this.state.belowAmount); - - // deserialize destinations - if (this.state.destinations) { - assert(this.state.address === undefined && this.state.amount === undefined, "Tx configuration may specify destinations or an address/amount but not both"); - this.setDestinations(this.state.destinations.map(destination => destination instanceof MoneroDestination ? destination : new MoneroDestination(destination))); - } - - // alias 'address' and 'amount' to single destination to support e.g. createTx({address: "..."}) - if (this.state.address || this.state.amount) { - assert(!this.state.destinations, "Tx configuration may specify destinations or an address/amount but not both"); - this.setAddress(this.state.address); - this.setAmount(this.state.amount); - delete this.state.address; - delete this.state.amount; - } - - // alias 'subaddressIndex' to subaddress indices - if (this.state.subaddressIndex !== undefined) { - this.setSubaddressIndices([this.state.subaddressIndex]); - delete this.state.subaddressIndex; - } - } - - copy() { - return new MoneroTxConfig(this); - } - - toJson() { - let json = Object.assign({}, this.state); // copy state - if (this.getDestinations()) { - json.destinations = []; - for (let destination of this.getDestinations()) json.destinations.push(destination.toJson()); - } - if (this.getFee()) json.fee = this.getFee().toString(); - if (this.getUnlockTime()) json.unlockTime = this.getUnlockTime().toString(); - if (this.getBelowAmount()) json.belowAmount = this.getBelowAmount().toString(); - return json; - } - - /** - * Set the address of a single-destination configuration. - * - * @param {string} address - the address to set for the single destination - * @return {MoneroTxConfig} this configuration for chaining - */ - setAddress(address) { - if (this.state.destinations !== undefined && this.state.destinations.length > 1) throw new MoneroError("Cannot set address because MoneroTxConfig already has multiple destinations"); - if (this.state.destinations === undefined || this.state.destinations.length === 0) this.addDestination(new MoneroDestination(address)); - else this.state.destinations[0].setAddress(address); - return this; - } - - /** - * Get the address of a single-destination configuration. - * - * @return {string} the address of the single destination - */ - getAddress() { - if (this.state.destinations === undefined || this.state.destinations.length !== 1) throw new MoneroError("Cannot get address because MoneroTxConfig does not have exactly one destination"); - return this.state.destinations[0].getAddress(); - } - - /** - * Set the amount of a single-destination configuration. - * - * @param {BigInteger|string} amount - the amount to set for the single destination - * @return {MoneroTxConfig} this configuration for chaining - */ - setAmount(amount) { - if (amount !== undefined && !(this.state.amount instanceof BigInteger)) { - if (typeof amount === "number") throw new MoneroError("Destination amount must be BigInteger or string"); - try { amount = BigInteger.parse(amount); } - catch (err) { throw new MoneroError("Invalid destination amount: " + amount); } - } - if (this.state.destinations !== undefined && this.state.destinations.length > 1) throw new MoneroError("Cannot set amount because MoneroTxConfig already has multiple destinations"); - if (this.state.destinations === undefined || this.state.destinations.length === 0) this.addDestination(new MoneroDestination(undefined, amount)); - else this.state.destinations[0].setAmount(amount); - return this; - } - - /** - * Get the amount of a single-destination configuration. - * - * @return {BigInteger} the amount of the single destination - */ - getAmount() { - if (this.state.destinations === undefined || this.state.destinations.length !== 1) throw new MoneroError("Cannot get amount because MoneroTxConfig does not have exactly one destination"); - return this.state.destinations[0].getAmount(); - } - - addDestination(destinationOrAddress, amount) { - if (typeof destinationOrAddress === "string") return this.addDestination(new MoneroDestination(destinationOrAddress, amount)); - assert(destinationOrAddress instanceof MoneroDestination); - if (this.state.destinations === undefined) this.state.destinations = []; - this.state.destinations.push(destinationOrAddress); - return this; - } - - getDestinations() { - return this.state.destinations; - } - - setDestinations(destinations) { - if (arguments.length > 1) destinations = Array.from(arguments); - this.state.destinations = destinations; - return this; - } - - setDestination(destination) { - return this.setDestinations(destination ? [destination] : destination); - } - - getSubtractFeeFrom() { - return this.state.subtractFeeFrom; - } - - setSubtractFeeFrom(destinationIndices) { - if (arguments.length > 1) destinationIndices = Array.from(arguments); - this.state.subtractFeeFrom = destinationIndices; - return this; - } - - getPaymentId() { - return this.state.paymentId; - } - - setPaymentId(paymentId) { - this.state.paymentId = paymentId; - return this; - } - - getPriority() { - return this.state.priority; - } - - setPriority(priority) { - this.state.priority = priority; - return this; - } - - getFee() { - return this.state.fee; - } - - setFee(fee) { - this.state.fee = fee; - return this; - } - - getAccountIndex() { - return this.state.accountIndex; - } - - setAccountIndex(accountIndex) { - this.state.accountIndex = accountIndex; - return this; - } - - setSubaddressIndex(subaddressIndex) { - this.setSubaddressIndices([subaddressIndex]); - return this; - } - - getSubaddressIndices() { - return this.state.subaddressIndices; - } - - setSubaddressIndices(subaddressIndices) { - if (arguments.length > 1) subaddressIndices = Array.from(arguments); - this.state.subaddressIndices = subaddressIndices; - return this; - } - - getUnlockTime() { - return this.state.unlockTime; - } - - setUnlockTime(unlockTime) { - if (unlockTime !== undefined) { - if (typeof unlockTime === "number") unlockTime = "" + unlockTime; - if (!(unlockTime instanceof BigInteger)) { - try { unlockTime = BigInteger.parse(unlockTime); } - catch (err) { throw new MoneroError("Invalid unlock time: " + unlockTime); } - } - } - this.state.unlockTime = unlockTime; - return this; - } - - getRelay() { - return this.state.relay; - } - - setRelay(relay) { - this.state.relay = relay; - return this; - } - - getCanSplit() { - return this.state.canSplit; - } - - setCanSplit(canSplit) { - this.state.canSplit = canSplit; - return this; - } - - getNote() { - return this.state.note; - } - - setNote(note) { - this.state.note = note; - return this; - } - - getRecipientName() { - return this.state.recipientName; - } - - setRecipientName(recipientName) { - this.state.recipientName = recipientName; - return this; - } - - // --------------------------- SPECIFIC TO SWEEP ---------------------------- - - getBelowAmount() { - return this.state.belowAmount; - } - - setBelowAmount(belowAmount) { - this.state.belowAmount = belowAmount; - return this; - } - - getSweepEachSubaddress() { - return this.state.sweepEachSubaddress; - } - - setSweepEachSubaddress(sweepEachSubaddress) { - this.state.sweepEachSubaddress = sweepEachSubaddress; - return this; - } - - /** - * Get the key image hex of the output to sweep. - * - * return {string} is the key image hex of the output to sweep - */ - getKeyImage() { - return this.state.keyImage; - } - - /** - * Set the key image hex of the output to sweep. - * - * @param {string} keyImage is the key image hex of the output to sweep - */ - setKeyImage(keyImage) { - this.state.keyImage = keyImage; - return this; - } -} - -MoneroTxConfig.SUPPORTED_FIELDS = ["address", "amount", "accountIndex", "subaddressIndex", "subaddressIndices", "relay", "priority", "destinations", "subtractFeeFrom", "paymentId", "unlockTime", "note", "recipientName", "canSplit", "belowAmount", "sweepEachSubaddress", "keyImage"]; - -module.exports = MoneroTxConfig \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroTxPriority.js b/src/main/js/wallet/model/MoneroTxPriority.js deleted file mode 100644 index 48869b365..000000000 --- a/src/main/js/wallet/model/MoneroTxPriority.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Enumerates send priorities. - * - * @hideconstructor - */ -class MoneroTxPriority {} - -/** - * Default priority (i.e. normal) (value=0). - */ -MoneroTxPriority.DEFAULT = 0; - -/** - * Unimportant priority (value=1). - */ -MoneroTxPriority.UNIMPORTANT = 1; - -/** - * Normal priority (value=2). - */ -MoneroTxPriority.NORMAL = 2; - -/** - * Elevated priority (value=3). - */ -MoneroTxPriority.ELEVATED = 3; - -module.exports = MoneroTxPriority; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroTxQuery.js b/src/main/js/wallet/model/MoneroTxQuery.js deleted file mode 100644 index 118ac4cc1..000000000 --- a/src/main/js/wallet/model/MoneroTxQuery.js +++ /dev/null @@ -1,300 +0,0 @@ -const assert = require("assert"); -const MoneroOutputQuery = require("./MoneroOutputQuery"); -const MoneroTransferQuery = require("./MoneroTransferQuery"); -const MoneroTxWallet = require("./MoneroTxWallet"); - -/** - *

Configuration to query transactions.

- * - * @class - * @extends {MoneroTxWallet} - */ -class MoneroTxQuery extends MoneroTxWallet { - - /** - *

Construct the transaction query.

- * - *

Example:

- * - * - * // get transactions with unlocked incoming transfers to account 0
- * let txs = await wallet.getTxs({
- *    isLocked: false,
- *    transferQuery: {
- *      isIncoming: true,
- *      accountIndex: 0
- *    }
- * }); - *
- * - *

All configuration is optional. All transactions are returned except those that don't meet criteria defined in this query.

- * - * @param {object} config - tx query configuration - * @param {string} config.hash - get a tx with this hash - * @param {string[]} config.txHashes - get txs with these hashes - * @param {int} config.height - get txs with this height - * @param {int} config.minHeight - get txs with height greater than or equal to this height - * @param {int} config.maxHeight - get txs with height less than or equal to this height - * @param {boolean} config.isConfirmed - get confirmed or unconfirmed txs - * @param {boolean} config.inTxPool - get txs in or out of the tx pool - * @param {boolean} config.relay - get txs with the same relay status - * @param {boolean} config.isRelayed - get relayed or non-relayed txs - * @param {boolean} config.isFailed - get failed or non-failed txs - * @param {boolean} config.isMinerTx - get miner or non-miner txs - * @param {boolean} config.isLocked - get locked or unlocked txs - * @param {boolean} config.isIncoming - get txs with or without incoming transfers - * @param {boolean} config.isOutgoing - get txs with or without outgoing transfers - * @param {string} config.paymentId - get txs with this payment ID - * @param {string} config.paymentIds - get txs with a payment ID among these payment IDs - * @param {boolean} config.hasPaymentId - get txs with or without payment IDs - * @param {object|MoneroTransferQuery} config.transferQuery - get txs with transfers matching this transfer query - * @param {object|MoneroOutputQuery} config.inputQuery - get txs with inputs matching this input query - * @param {object|MoneroOutputQuery} config.outputQuery - get txs with outputs matching this output query - */ - constructor(config) { - super(config); - - // deserialize if necessary - if (this.state.transferQuery && !(this.state.transferQuery instanceof MoneroTransferQuery)) this.state.transferQuery = new MoneroTransferQuery(this.state.transferQuery); - if (this.state.inputQuery && !(this.state.inputQuery instanceof MoneroOutputQuery)) this.state.inputQuery = new MoneroOutputQuery(this.state.inputQuery); - if (this.state.outputQuery && !(this.state.outputQuery instanceof MoneroOutputQuery)) this.state.outputQuery = new MoneroOutputQuery(this.state.outputQuery); - - // link cycles - if (this.state.transferQuery) this.state.transferQuery.setTxQuery(this); - if (this.state.inputQuery) this.state.inputQuery.setTxQuery(this); - if (this.state.outputQuery) this.state.outputQuery.setTxQuery(this); - - // alias 'hash' to hashes - if (this.state.hash) { - this.setHashes([this.state.hash]); - delete this.state.hash; - } - } - - copy() { - return new MoneroTxQuery(this); - } - - toJson() { - let json = Object.assign({}, this.state, super.toJson()); // merge json onto inherited state - if (this.getTransferQuery()) json.transferQuery = this.getTransferQuery().toJson(); - if (this.getInputQuery()) json.inputQuery = this.getInputQuery().toJson(); - if (this.getOutputQuery()) json.outputQuery = this.getOutputQuery().toJson(); - delete json.block; // do not serialize parent block - return json; - } - - isIncoming() { - return this.state.isIncoming; - } - - setIsIncoming(isIncoming) { - this.state.isIncoming = isIncoming; - return this; - } - - isOutgoing() { - return this.state.isOutgoing; - } - - setIsOutgoing(isOutgoing) { - this.state.isOutgoing = isOutgoing; - return this; - } - - getHashes() { - return this.state.hashes; - } - - setHashes(hashes) { - this.state.hashes = hashes; - return this; - } - - setHash(hash) { - if (hash === undefined) return this.setHashes(undefined); - assert(typeof hash === "string"); - return this.setHashes([hash]); - } - - hasPaymentId() { - return this.state.hasPaymentId; - } - - setHasPaymentId() { - this.state.hasPaymentId = hasPaymentId; - return this; - } - - getPaymentIds() { - return this.state.paymentIds; - } - - setPaymentIds(paymentIds) { - this.state.paymentIds = paymentIds; - return this; - } - - setPaymentId(paymentId) { - if (paymentId === undefined) return this.setPaymentIds(undefined); - assert(typeof paymentId === "string"); - return this.setPaymentIds([paymentId]); - } - - getHeight() { - return this.state.height; - } - - setHeight(height) { - this.state.height = height; - return this; - } - - getMinHeight() { - return this.state.minHeight; - } - - setMinHeight(minHeight) { - this.state.minHeight = minHeight; - return this; - } - - getMaxHeight() { - return this.state.maxHeight; - } - - setMaxHeight(maxHeight) { - this.state.maxHeight = maxHeight; - return this; - } - - getIncludeOutputs() { - return this.state.includeOutputs; - } - - setIncludeOutputs(includeOutputs) { - this.state.includeOutputs = includeOutputs; - return this; - } - - getTransferQuery() { - return this.state.transferQuery; - } - - setTransferQuery(transferQuery) { - this.state.transferQuery = transferQuery; - if (transferQuery) transferQuery.state.txQuery = this; - return this; - } - - getInputQuery() { - return this.state.inputQuery; - } - - setInputQuery(inputQuery) { - this.state.inputQuery = inputQuery; - if (inputQuery) inputQuery.state.txQuery = this; - return this; - } - - getOutputQuery() { - return this.state.outputQuery; - } - - setOutputQuery(outputQuery) { - this.state.outputQuery = outputQuery; - if (outputQuery) outputQuery.state.txQuery = this; - return this; - } - - meetsCriteria(tx, queryChildren) { - if (!(tx instanceof MoneroTxWallet)) throw new Error("Tx not given to MoneroTxQuery.meetsCriteria(tx)"); - if (queryChildren === undefined) queryChildren = true; - - // filter on tx - if (this.getHash() !== undefined && this.getHash() !== tx.getHash()) return false; - if (this.getPaymentId() !== undefined && this.getPaymentId() !== tx.getPaymentId()) return false; - if (this.isConfirmed() !== undefined && this.isConfirmed() !== tx.isConfirmed()) return false; - if (this.inTxPool() !== undefined && this.inTxPool() !== tx.inTxPool()) return false; - if (this.getRelay() !== undefined && this.getRelay() !== tx.getRelay()) return false; - if (this.isRelayed() !== undefined && this.isRelayed() !== tx.isRelayed()) return false; - if (this.isFailed() !== undefined && this.isFailed() !== tx.isFailed()) return false; - if (this.isMinerTx() !== undefined && this.isMinerTx() !== tx.isMinerTx()) return false; - if (this.isLocked() !== undefined && this.isLocked() !== tx.isLocked()) return false; - - // filter on having a payment id - if (this.hasPaymentId() !== undefined) { - if (this.hasPaymentId() && tx.getPaymentId() === undefined) return false; - if (!this.hasPaymentId() && tx.getPaymentId() !== undefined) return false; - } - - // filter on incoming - if (this.isIncoming() !== undefined) { - if (this.isIncoming() && !tx.isIncoming()) return false; - if (!this.isIncoming() && tx.isIncoming()) return false; - } - - // filter on outgoing - if (this.isOutgoing() !== undefined) { - if (this.isOutgoing() && !tx.isOutgoing()) return false; - if (!this.isOutgoing() && tx.isOutgoing()) return false; - } - - // filter on remaining fields - let txHeight = tx.getBlock() === undefined ? undefined : tx.getBlock().getHeight(); - if (this.getHashes() !== undefined && !this.getHashes().includes(tx.getHash())) return false; - if (this.getPaymentIds() !== undefined && !this.getPaymentIds().includes(tx.getPaymentId())) return false; - if (this.getHeight() !== undefined && (txHeight === undefined || txHeight !== this.getHeight())) return false; - if (this.getMinHeight() !== undefined && txHeight !== undefined && txHeight < this.getMinHeight()) return false; // do not filter unconfirmed - if (this.getMaxHeight() !== undefined && (txHeight === undefined || txHeight > this.getMaxHeight())) return false; - // TODO: filtering not complete - - // done if not querying transfers or outputs - if (!queryChildren) return true; - - // at least one transfer must meet transfer filter if defined - if (this.getTransferQuery()) { - let matchFound = false; - if (tx.getOutgoingTransfer() && this.getTransferQuery().meetsCriteria(tx.getOutgoingTransfer(), false)) matchFound = true; - else if (tx.getIncomingTransfers()) { - for (let incomingTransfer of tx.getIncomingTransfers()) { - if (this.getTransferQuery().meetsCriteria(incomingTransfer, false)) { - matchFound = true; - break; - } - } - } - if (!matchFound) return false; - } - - // at least one input must meet input query if defined - if (this.getInputQuery() !== undefined) { - if (tx.getInputs() === undefined || tx.getInputs().length === 0) return false; - let matchFound = false; - for (let input of tx.getInputs()) { - if (this.getInputQuery().meetsCriteria(input, false)) { - matchFound = true; - break; - } - } - if (!matchFound) return false; - } - - // at least one output must meet output query if defined - if (this.getOutputQuery() !== undefined) { - if (tx.getOutputs() === undefined || tx.getOutputs().length === 0) return false; - let matchFound = false; - for (let output of tx.getOutputs()) { - if (this.getOutputQuery().meetsCriteria(output, false)) { - matchFound = true; - break; - } - } - if (!matchFound) return false; - } - - return true; // transaction meets filter criteria - } -} - -module.exports = MoneroTxQuery; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroTxWallet.js b/src/main/js/wallet/model/MoneroTxWallet.js deleted file mode 100644 index 49d1c9460..000000000 --- a/src/main/js/wallet/model/MoneroTxWallet.js +++ /dev/null @@ -1,414 +0,0 @@ -const assert = require("assert"); -const BigInteger = require("../../common/biginteger").BigInteger; -const GenUtils = require("../../common/GenUtils"); -const MoneroIncomingTransfer = require("./MoneroIncomingTransfer"); -const MoneroOutgoingTransfer = require("./MoneroOutgoingTransfer"); -const MoneroOutputWallet = require("./MoneroOutputWallet"); -const MoneroTx = require("../../daemon/model/MoneroTx"); - -/** - * Models a Monero transaction with wallet extensions. - * - * @class - * @extends {MoneroTx} - */ -class MoneroTxWallet extends MoneroTx { - - /** - * Construct the model. - * - * @param {MoneroTxWallet|object} state is existing state to initialize from (optional) - */ - constructor(state) { - super(state); - if (state instanceof MoneroTxWallet && state.getTxSet()) this.setTxSet(state.getTxSet()); // preserve reference to tx set - state = this.state; - - // deserialize incoming transfers - if (state.incomingTransfers) { - for (let i = 0; i < state.incomingTransfers.length; i++) { - if (!(state.incomingTransfers[i] instanceof MoneroIncomingTransfer)) { - state.incomingTransfers[i] = new MoneroIncomingTransfer(Object.assign(state.incomingTransfers[i], {tx: this})); - } - } - } - - // deserialize outgoing transfer - if (state.outgoingTransfer && !(state.outgoingTransfer instanceof MoneroOutgoingTransfer)) { - this.setOutgoingTransfer(new MoneroOutgoingTransfer(Object.assign(state.outgoingTransfer, {tx: this}))); - } - - // deserialize inputs - if (state.inputs) { - for (let i = 0; i < state.inputs.length; i++) { - if (!(state.inputs[i] instanceof MoneroOutputWallet)) { - state.inputs[i] = new MoneroOutputWallet(Object.assign(state.inputs[i].toJson(), {tx: this})); - } - } - } - - // deserialize outputs - if (state.outputs) { - for (let i = 0; i < state.outputs.length; i++) { - if (!(state.outputs[i] instanceof MoneroOutputWallet)) { - state.outputs[i] = new MoneroOutputWallet(Object.assign(state.outputs[i].toJson(), {tx: this})); - } - } - } - - // deserialize BigIntegers - if (state.inputSum !== undefined && !(state.inputSum instanceof BigInteger)) state.inputSum = BigInteger.parse(state.inputSum); - if (state.outputSum !== undefined && !(state.outputSum instanceof BigInteger)) state.outputSum = BigInteger.parse(state.outputSum); - if (state.changeAmount !== undefined && !(state.changeAmount instanceof BigInteger)) state.changeAmount = BigInteger.parse(state.changeAmount); - } - - toJson() { - let json = Object.assign({}, this.state, super.toJson()); // merge json onto inherited state - if (this.getIncomingTransfers()) { - json.incomingTransfers = []; - for (let incomingTransfer of this.getIncomingTransfers()) json.incomingTransfers.push(incomingTransfer.toJson()); - } - if (this.getOutgoingTransfer()) json.outgoingTransfer = this.getOutgoingTransfer().toJson(); - if (this.getInputSum()) json.inputSum = this.getInputSum().toString(); - if (this.getOutputSum()) json.outputSum = this.getOutputSum().toString(); - if (this.getChangeAmount()) json.changeAmount = this.getChangeAmount().toString(); - delete json.block; // do not serialize parent block - delete json.txSet; // do not serialize parent tx set - return json; - } - - getTxSet() { - return this.state.txSet; - } - - setTxSet(txSet) { - this.state.txSet = txSet; - return this; - } - - isIncoming() { - return this.state.isIncoming; - } - - setIsIncoming(isIncoming) { - this.state.isIncoming = isIncoming; - return this; - } - - isOutgoing() { - return this.state.isOutgoing; - } - - setIsOutgoing(isOutgoing) { - this.state.isOutgoing = isOutgoing; - return this; - } - - getIncomingAmount() { - if (this.getIncomingTransfers() === undefined) return undefined; - let incomingAmt = BigInteger.parse("0"); - for (let transfer of this.getIncomingTransfers()) incomingAmt = incomingAmt.add(transfer.getAmount()); - return incomingAmt; - } - - getOutgoingAmount() { - return this.getOutgoingTransfer() ? this.getOutgoingTransfer().getAmount() : undefined; - } - - getTransfers(transferQuery) { - let transfers = []; - if (this.getOutgoingTransfer() && (!transferQuery || transferQuery.meetsCriteria(this.getOutgoingTransfer()))) transfers.push(this.getOutgoingTransfer()); - if (this.getIncomingTransfers()) { - for (let transfer of this.getIncomingTransfers()) { - if (!transferQuery || transferQuery.meetsCriteria(transfer)) transfers.push(transfer); - } - } - return transfers; - } - - filterTransfers(transferQuery) { - let transfers = []; - - // collect outgoing transfer or erase if filtered - if (this.getOutgoingTransfer() && (!transferQuery || transferQuery.meetsCriteria(this.getOutgoingTransfer()))) transfers.push(this.getOutgoingTransfer()); - else this.setOutgoingTransfer(undefined); - - // collect incoming transfers or erase if filtered - if (this.getIncomingTransfers()) { - let toRemoves = []; - for (let transfer of this.getIncomingTransfers()) { - if (transferQuery.meetsCriteria(transfer)) transfers.push(transfer); - else toRemoves.push(transfer); - } - this.setIncomingTransfers(this.getIncomingTransfers().filter(function(transfer) { - return !toRemoves.includes(transfer); - })); - if (this.getIncomingTransfers().length === 0) this.setIncomingTransfers(undefined); - } - - return transfers; - } - - getIncomingTransfers() { - return this.state.incomingTransfers; - } - - setIncomingTransfers(incomingTransfers) { - this.state.incomingTransfers = incomingTransfers; - return this; - } - - getOutgoingTransfer() { - return this.state.outgoingTransfer; - } - - setOutgoingTransfer(outgoingTransfer) { - this.state.outgoingTransfer = outgoingTransfer; - return this; - } - - getInputs(outputQuery) { - if (!outputQuery || !super.getInputs()) return super.getInputs(); - let inputs = []; - for (let output of super.getInputs()) if (!outputQuery || outputQuery.meetsCriteria(output)) inputs.push(output); - return inputs; - } - - setInputs(inputs) { - - // validate that all inputs are wallet inputs - if (inputs) { - for (let output of inputs) { - if (!(output instanceof MoneroOutputWallet)) throw new MoneroError("Wallet transaction inputs must be of type MoneroOutputWallet"); - } - } - super.setInputs(inputs); - return this; - } - - getOutputs(outputQuery) { - if (!outputQuery || !super.getOutputs()) return super.getOutputs(); - let outputs = []; - for (let output of super.getOutputs()) if (!outputQuery || outputQuery.meetsCriteria(output)) outputs.push(output); - return outputs; - } - - setOutputs(outputs) { - - // validate that all outputs are wallet outputs - if (outputs) { - for (let output of outputs) { - if (!(output instanceof MoneroOutputWallet)) throw new MoneroError("Wallet transaction outputs must be of type MoneroOutputWallet"); - } - } - super.setOutputs(outputs); - return this; - } - - filterOutputs(outputQuery) { - let outputs = []; - if (super.getOutputs()) { - let toRemoves = []; - for (let output of super.getOutputs()) { - if (!outputQuery || outputQuery.meetsCriteria(output)) outputs.push(output); - else toRemoves.push(output); - } - this.setOutputs(super.getOutputs().filter(function(output) { - return !toRemoves.includes(output); - })); - if (this.getOutputs().length === 0) this.setOutputs(undefined); - } - return outputs; - } - - getNote() { - return this.state.note; - } - - setNote(note) { - this.state.note = note; - return this; - } - - isLocked() { - return this.state.isLocked; - } - - setIsLocked(isLocked) { - this.state.isLocked = isLocked; - return this; - } - - getInputSum() { - return this.state.inputSum; - } - - setInputSum(inputSum) { - this.state.inputSum = inputSum; - return this; - } - - getOutputSum() { - return this.state.outputSum; - } - - setOutputSum(outputSum) { - this.state.outputSum = outputSum; - return this; - } - - getChangeAddress() { - return this.state.changeAddress; - } - - setChangeAddress(changeAddress) { - this.state.changeAddress = changeAddress; - return this; - } - - getChangeAmount() { - return this.state.changeAmount; - } - - setChangeAmount(changeAmount) { - this.state.changeAmount = changeAmount; - return this; - } - - getNumDummyOutputs() { - return this.state.numDummyOutputs; - } - - setNumDummyOutputs(numDummyOutputs) { - this.state.numDummyOutputs = numDummyOutputs; - return this; - } - - getExtraHex() { - return this.state.extraHex; - } - - setExtraHex(extraHex) { - this.state.extraHex = extraHex; - return this; - } - - copy() { - return new MoneroTxWallet(this); - } - - /** - * Updates this transaction by merging the latest information from the given - * transaction. - * - * Merging can modify or build references to the transaction given so it - * should not be re-used or it should be copied before calling this method. - * - * @param tx is the transaction to merge into this transaction - */ - merge(tx) { - assert(tx instanceof MoneroTxWallet); - if (this === tx) return this; - - // merge base classes - super.merge(tx); - - // merge tx set if they're different which comes back to merging txs - const MoneroTxSet = require("./MoneroTxSet"); - if (this.getTxSet() !== tx.getTxSet()) { - if (this.getTxSet() == undefined) { - this.setTxSet(new MoneroTxSet().setTxs([this])); - } - if (tx.getTxSet() === undefined) { - tx.setTxSet(new MoneroTxSet().setTxs([tx])); - } - this.getTxSet().merge(tx.getTxSet()); - return this; - } - - // merge incoming transfers - if (tx.getIncomingTransfers()) { - if (this.getIncomingTransfers() === undefined) this.setIncomingTransfers([]); - for (let transfer of tx.getIncomingTransfers()) { - transfer.setTx(this); - MoneroTxWallet._mergeIncomingTransfer(this.getIncomingTransfers(), transfer); - } - } - - // merge outgoing transfer - if (tx.getOutgoingTransfer()) { - tx.getOutgoingTransfer().setTx(this); - if (this.getOutgoingTransfer() === undefined) this.setOutgoingTransfer(tx.getOutgoingTransfer()); - else this.getOutgoingTransfer().merge(tx.getOutgoingTransfer()); - } - - // merge simple extensions - this.setIsIncoming(GenUtils.reconcile(this.isIncoming(), tx.isIncoming(), {resolveTrue: true})); // outputs seen on confirmation - this.setIsOutgoing(GenUtils.reconcile(this.isOutgoing(), tx.isOutgoing())); - this.setNote(GenUtils.reconcile(this.getNote(), tx.getNote())); - this.setIsLocked(GenUtils.reconcile(this.isLocked(), tx.isLocked(), {resolveTrue: false})); // tx can become unlocked - this.setInputSum(GenUtils.reconcile(this.getInputSum(), tx.getInputSum())); - this.setOutputSum(GenUtils.reconcile(this.getOutputSum(), tx.getOutputSum())); - this.setChangeAddress(GenUtils.reconcile(this.getChangeAddress(), tx.getChangeAddress())); - this.setChangeAmount(GenUtils.reconcile(this.getChangeAmount(), tx.getChangeAmount())); - this.setNumDummyOutputs(GenUtils.reconcile(this.getNumDummyOutputs(), tx.getNumDummyOutputs())); - this.setExtraHex(GenUtils.reconcile(this.getExtraHex(), tx.getExtraHex())); - - return this; // for chaining - } - - toString(indent = 0, oneLine) { - let str = ""; - - // represent tx with one line string - // TODO: proper csv export - if (oneLine) { - str += this.getHash() + ", "; - str += (this.isConfirmed() ? this.getBlock().getTimestamp() : this.getReceivedTimestamp()) + ", "; - str += this.isConfirmed() + ", "; - str += (this.getOutgoingAmount() ? this.getOutgoingAmount().toString() : "") + ", "; - str += this.getIncomingAmount() ? this.getIncomingAmount().toString() : ""; - return str; - } - - // otherwise stringify all fields - str += super.toString(indent) + "\n"; - str += GenUtils.kvLine("Is incoming", this.isIncoming(), indent); - str += GenUtils.kvLine("Incoming amount", this.getIncomingAmount(), indent); - if (this.getIncomingTransfers()) { - str += GenUtils.kvLine("Incoming transfers", "", indent); - for (let i = 0; i < this.getIncomingTransfers().length; i++) { - str += GenUtils.kvLine(i + 1, "", indent + 1); - str += this.getIncomingTransfers()[i].toString(indent + 2) + "\n"; - } - } - str += GenUtils.kvLine("Is outgoing", this.isOutgoing(), indent); - str += GenUtils.kvLine("Outgoing amount", this.getOutgoingAmount(), indent); - if (this.getOutgoingTransfer()) { - str += GenUtils.kvLine("Outgoing transfer", "", indent); - str += this.getOutgoingTransfer().toString(indent + 1) + "\n"; - } - str += GenUtils.kvLine("Note", this.getNote(), indent); - str += GenUtils.kvLine("Is locked", this.isLocked(), indent); - str += GenUtils.kvLine("Input sum", this.getInputSum(), indent); - str += GenUtils.kvLine("Output sum", this.getOutputSum(), indent); - str += GenUtils.kvLine("Change address", this.getChangeAddress(), indent); - str += GenUtils.kvLine("Change amount", this.getChangeAmount(), indent); - str += GenUtils.kvLine("Num dummy outputs", this.getNumDummyOutputs(), indent); - str += GenUtils.kvLine("Extra hex", this.getExtraHex(), indent); - return str.slice(0, str.length - 1); // strip last newline - } - - // private helper to merge transfers - static _mergeIncomingTransfer(transfers, transfer) { - for (let aTransfer of transfers) { - if (aTransfer.getAccountIndex() === transfer.getAccountIndex() && aTransfer.getSubaddressIndex() === transfer.getSubaddressIndex()) { - aTransfer.merge(transfer); - return; - } - } - transfers.push(transfer); - } -} - -module.exports = MoneroTxWallet; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroWalletConfig.js b/src/main/js/wallet/model/MoneroWalletConfig.js deleted file mode 100644 index 8e02b5740..000000000 --- a/src/main/js/wallet/model/MoneroWalletConfig.js +++ /dev/null @@ -1,310 +0,0 @@ -const GenUtils = require("../../common/GenUtils"); -const MoneroError = require("../../common/MoneroError"); -const MoneroNetworkType = require("../../daemon/model/MoneroNetworkType"); -const MoneroRpcConnection = require("../../common/MoneroRpcConnection"); - -/** - * Configuration to create a Monero wallet. - */ -class MoneroWalletConfig { - - /** - * Construct a configuration to open or create a wallet. - * - * @param {object|MoneroWalletConfig} config - MoneroWalletConfig or equivalent config object - * @param {string} config.path - path of the wallet to open or create - * @param {string} config.password - password of the wallet to open - * @param {string|number} config.networkType - network type of the wallet to open (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) - * @param {string} config.seed - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) - * @param {string} config.seedOffset - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed phrase - * @param {boolean} config.isMultisig - restore multisig wallet from seed - * @param {string} config.primaryAddress - primary address of the wallet to create (only provide if restoring from keys) - * @param {string} config.privateViewKey - private view key of the wallet to create (optional) - * @param {string} config.privateSpendKey - private spend key of the wallet to create (optional) - * @param {number} config.restoreHeight - block height to start scanning from (defaults to 0 unless generating random wallet) - * @param {string} config.language - language of the wallet's seed phrase (defaults to "English" or auto-detected) - * @param {number} config.accountLookahead - number of accounts to scan (optional) - * @param {number} config.subaddressLookahead - number of subaddresses to scan per account (optional) - * @param {MoneroRpcConnection|object} config.server - MoneroRpcConnection or equivalent JS object configuring the server connection (optional) - * @param {string} config.serverUri - uri of the wallet's server (optional) - * @param {string} config.serverUsername - username of the wallet's server (optional) - * @param {string} config.serverPassword - password of the wallet's server (optional) - * @param {MoneroConnectionManager} config.connectionManager - manage connections to monerod (optional) - * @param {boolean} config.rejectUnauthorized - reject self-signed server certificates if true (default true) - * @param {Uint8Array} config.keysData - wallet keys data to open (optional) - * @param {Uint8Array} config.cacheData - wallet cache data to open (optional) - * @param {boolean} config.proxyToWorker - proxies wallet operations to a worker in order to not block the main thread (default true) - * @param {fs} config.fs - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) - * @param {boolean} config.saveCurrent - specifies if the current RPC wallet should be saved before being closed - * @param {number} config.accountLookahead - number of accounts to scan (optional) - * @param {number} config.subaddressLookahead - number of subaddresses to scan per account (optional) - */ - constructor(config) { - - // initialize internal config - if (!config) config = {}; - else if (config instanceof MoneroWalletConfig) config = Object.assign({}, config.config); - else if (typeof config === "object") config = Object.assign({}, config); - else throw new MoneroError("config must be a MoneroWalletConfig or JavaScript object"); - this.config = config; - - // normalize config - this.setNetworkType(config.networkType); - if (config.server) this.setServer(config.server); - else if (config.serverUri) this.setServer({uri: config.serverUri, username: config.serverUsername, password: config.serverPassword, rejectUnauthorized: config.rejectUnauthorized}); - this.setProxyToWorker(config.proxyToWorker); - this.config.serverUri = undefined; - this.config.serverUsername = undefined; - this.config.serverPassword = undefined; - this.config.rejectUnauthorized = undefined; - - // check for unsupported fields - for (let key of Object.keys(this.config)) { - if (!GenUtils.arrayContains(MoneroWalletConfig.SUPPORTED_FIELDS, key)) { - throw new MoneroError("Unsupported field in MoneroWalletConfig: '" + key + "'"); - } - } - } - - copy() { - return new MoneroWalletConfig(this); - } - - toJson() { - let json = Object.assign({}, this.config); - if (json.server) json.server = json.server.toJson(); - json.fs = undefined; - json.connectionManager = undefined; - return json; - } - - getPath() { - return this.config.path; - } - - setPath(path) { - this.config.path = path; - return this; - } - - getPassword() { - return this.config.password; - } - - setPassword(password) { - this.config.password = password; - return this; - } - - getNetworkType() { - return this.config.networkType; - } - - setNetworkType(networkTypeOrStr) { - this.config.networkType = typeof networkTypeOrStr === "string" ? MoneroNetworkType.parse(networkTypeOrStr) : networkTypeOrStr; - return this; - } - - getServer() { - return this.config.server; - } - - setServer(server) { - if (server && !(server instanceof MoneroRpcConnection)) server = new MoneroRpcConnection(server); - this.config.server = server; - this.config.serverUsername = server === undefined ? undefined : server.getUsername(); - this.config.serverPassword = server === undefined ? undefined : server.getPassword(); - return this; - } - - getServerUri() { - return this.config.server ? this.config.server.getUri() : undefined; - } - - setServerUri(serverUri) { - if (!serverUri) this.setServer(undefined); - else { - if (!this.config.server) this.setServer(new MoneroRpcConnection(serverUri)); - else this.config.server.setUri(serverUri); - } - return this; - } - - getServerUsername() { - return this.server ? server.getUsername() : undefined; - } - - setServerUsername(serverUsername) { - this.config.serverUsername = serverUsername; - if (this.config.serverUsername && this.config.serverPassword) this.config.server.setCredentials(this.config.serverUsername, this.config.serverPassword); - return this; - } - - getServerPassword() { - return this.server ? server.getPassword() : undefined; - } - - setServerPassword(serverPassword) { - this.config.serverPassword = serverPassword; - if (this.config.serverUsername && this.config.serverPassword) this.config.server.setCredentials(this.config.serverUsername, this.config.serverPassword); - return this; - } - - getConnectionManager() { - return this.config.connectionManager; - } - - setConnectionManager(connectionManager) { - this.config.connectionManager = connectionManager; - return this; - } - - getRejectUnauthorized() { - return this.config.rejectUnauthorized; - } - - setRejectUnauthorized(rejectUnauthorized) { - this.config.rejectUnauthorized = rejectUnauthorized; - return this; - } - - getSeed() { - return this.config.seed; - } - - setSeed(seed) { - this.config.seed = seed; - return this; - } - - getSeedOffset() { - return this.config.seedOffset; - } - - setSeedOffset(seedOffset) { - this.config.seedOffset = seedOffset; - return this; - } - - isMultisig() { - return this.config.isMultisig; - } - - setIsMultisig(isMultisig) { - this.config.isMultisig = isMultisig; - return this; - } - - getPrimaryAddress() { - return this.config.primaryAddress; - } - - setPrimaryAddress(primaryAddress) { - this.config.primaryAddress = primaryAddress; - return this; - } - - getPrivateViewKey() { - return this.config.privateViewKey; - } - - setPrivateViewKey(privateViewKey) { - this.config.privateViewKey = privateViewKey; - return this; - } - - getPrivateSpendKey() { - return this.config.privateSpendKey; - } - - setPrivateSpendKey(privateSpendKey) { - this.config.privateSpendKey = privateSpendKey; - return this; - } - - getRestoreHeight() { - return this.config.restoreHeight; - } - - setRestoreHeight(restoreHeight) { - this.config.restoreHeight = restoreHeight; - return this; - } - - getLanguage() { - return this.config.language; - } - - setLanguage(language) { - this.config.language = language; - return this; - } - - getSaveCurrent() { - return this.config.saveCurrent; - } - - setSaveCurrent(saveCurrent) { - this.config.saveCurrent = saveCurrent; - return this; - } - - getProxyToWorker() { - return this.config.proxyToWorker; - } - - setProxyToWorker(proxyToWorker) { - this.config.proxyToWorker = proxyToWorker; - if (this.config.server) this.config.server.setProxyToWorker(proxyToWorker); - return this; - } - - getFs() { - return this.config.fs; - } - - setFs(fs) { - this.config.fs = fs; - return this; - } - - getKeysData() { - return this.config.keysData; - } - - setKeysData(keysData) { - this.config.keysData = keysData; - return this; - } - - getCacheData() { - return this.config.cacheData; - } - - setCacheData(cacheData) { - this.config.cacheData = cacheData; - return this; - } - - getAccountLookahead() { - return this.config.accountLookahead; - } - - setAccountLookahead(accountLookahead) { - this.config.accountLookahead = accountLookahead; - return this; - } - - getSubaddressLookahead() { - return this.config.subaddressLookahead; - } - - setSubaddressLookahead(subaddressLookahead) { - this.config.subaddressLookahead = subaddressLookahead; - return this; - } -} - -MoneroWalletConfig.SUPPORTED_FIELDS = ["path", "password", "networkType", "server", "serverUri", "serverUsername", "serverPassword", "connectionManager", "rejectUnauthorized", "seed", "seedOffset", "isMultisig", "primaryAddress", "privateViewKey", "privateSpendKey", "restoreHeight", "language", "saveCurrent", "proxyToWorker", "fs", "keysData", "cacheData", "accountLookahead", "subaddressLookahead"]; - -module.exports = MoneroWalletConfig; \ No newline at end of file diff --git a/src/main/js/common/Filter.js b/src/main/ts/common/Filter.ts similarity index 84% rename from src/main/js/common/Filter.js rename to src/main/ts/common/Filter.ts index e8e573bb8..d9e3ef85d 100644 --- a/src/main/js/common/Filter.js +++ b/src/main/ts/common/Filter.ts @@ -3,7 +3,7 @@ * * @private */ -class Filter { +export default class Filter { /** * Indicates if the given value meets the criteria of this filter. @@ -11,7 +11,7 @@ class Filter { * @param val is the value to test * @return true if the value meets the criteria of this filter, false otherwise */ - meetsCriteria(val) { + meetsCriteria(val: any): boolean { throw new Error("Subclass must implement"); } @@ -23,9 +23,7 @@ class Filter { * @param array is the array to apply the filter to * @return the new array of filtered elements */ - static apply(filter, array) { + static apply(filter: Filter, array: any[]): any[] { return array.filter(elem => !filter || filter.meetsCriteria(elem)); } } - -module.exports = Filter; \ No newline at end of file diff --git a/src/main/js/common/GenUtils.js b/src/main/ts/common/GenUtils.ts similarity index 81% rename from src/main/js/common/GenUtils.js rename to src/main/ts/common/GenUtils.ts index 79744a0cb..c096fea20 100644 --- a/src/main/js/common/GenUtils.js +++ b/src/main/ts/common/GenUtils.ts @@ -1,5 +1,7 @@ -const assert = require("assert"); -const BigInteger = require("./biginteger").BigInteger; +import assert from "assert"; +import async from "async"; +import * as csv from 'jquery-csv'; +import { ChildProcess } from "child_process"; /** * MIT License @@ -25,19 +27,16 @@ const BigInteger = require("./biginteger").BigInteger; /** * Collection of general purpose utilities. - * - * TODO: could pull in assert and remove these asserts - * TODO: needs cleanup as ES6+ utility class */ -class GenUtils { +export default class GenUtils { /** * Indicates if the given argument is defined. * - * @param arg is the arg to test - * @returns true if the given arg is defined, false otherwise + * @param {any} arg is the arg to test + * @return {boolean} true if the given arg is defined, false otherwise */ - static isDefined(arg) { + static isDefined(arg: any): boolean { return typeof arg !== 'undefined'; } @@ -45,19 +44,19 @@ class GenUtils { * Indicates if the given argument is undefined. * * @param arg is the arg to test - * @returns true if the given arg is undefined, false otherwise + * @return {boolean} true if the given arg is undefined, false otherwise */ - static isUndefined(arg) { + static isUndefined(arg): boolean { return typeof arg === 'undefined'; } /** * Indicates if the given arg is initialized. * - * @param arg is the arg to test - * @returns true if the given arg is initialized, false otherwise + * @param {any} arg is the arg to test + * @return {boolean} true if the given arg is initialized, false otherwise */ - static isInitialized(arg) { + static isInitialized(arg: any): boolean { return arg !== undefined && arg !== null; } @@ -65,9 +64,9 @@ class GenUtils { * Indicates if the given arg is uninitialized. * * @param arg is the arg to test - * @returns true if the given arg is uninitialized, false otherwise + * @return true if the given arg is uninitialized, false otherwise */ - static isUninitialized(arg) { + static isUninitialized(arg: any): boolean { if (!arg) return true; return false; } @@ -75,71 +74,71 @@ class GenUtils { /** * Indicates if the given argument is a number. * - * @param arg is the argument to test - * @returns true if the argument is a number, false otherwise + * @param {any} arg is the argument to test + * @return {boolean} true if the argument is a number, false otherwise */ - static isNumber(arg) { + static isNumber(arg: any): boolean { return !isNaN(parseFloat(arg)) && isFinite(arg); } /** * Indicates if the given argument is an integer. * - * @param arg is the argument to test - * @returns true if the given argument is an integer, false otherwise + * @param {any} arg is the argument to test + * @return {boolean} true if the given argument is an integer, false otherwise */ - static isInt(arg) { - return arg === parseInt(Number(arg)) && !isNaN(arg) && !isNaN(parseInt(arg, 10)); + static isInt(arg: any): boolean { + return arg === parseInt("" + Number(arg)) && !isNaN(arg) && !isNaN(parseInt(arg, 10)); } /** * Indicates if the given argument is an array. * - * @param arg is the argument to test as being an array - * @returns true if the argument is an array, false otherwise + * @param {any} arg is the argument to test as being an array + * @return {booolean} true if the argument is an array, false otherwise */ - static isArray(arg) { + static isArray(arg: any): boolean { return arg instanceof Array && Array.isArray(arg); } /** * Indicates if the given argument is a string. * - * @param arg is the argument to test as being a string - * @returns true if the argument is a string, false otherwise + * @param {any} arg is the argument to test as being a string + * @return {boolean} true if the argument is a string, false otherwise */ - static isString(arg) { + static isString(arg: any): boolean { return typeof arg === 'string'; } /** * Determines if the given argument is a boolean. * - * @param arg is the argument to test as being a boolean - * @returns true if the argument is a boolean, false otherwise + * @param {any} arg is the argument to test as being a boolean + * @return {boolean} true if the argument is a boolean, false otherwise */ - static isBoolean(arg) { + static isBoolean(arg: any): boolean { return typeof(arg) == typeof(true); } /** * Determines if the given argument is a static. * - * @param arg is the argument to test as being a static - * @returns true if the argument is a static, false otherwise + * @param {any} arg is the argument to test as being a static + * @return {boolean} true if the argument is a static, false otherwise */ - static isFunction(arg) { - return typeof arg === "static"; + static isFunction(arg: any): boolean { + return typeof arg === "function"; } /** * Indicates if the given argument is an object and optionally if it has the given constructor name. * - * @param arg is the argument to test - * @param obj is an object to test arg instanceof obj (optional) - * @returns true if the given argument is an object and optionally has the given constructor name + * @param {any} arg is the argument to test + * @param {any} obj is an object to test arg instanceof obj (optional) + * @return {boolean} true if the given argument is an object and optionally has the given constructor name */ - static isObject(arg, obj) { + static isObject(arg: any, obj?: any): boolean { if (!arg) return false; if (typeof arg !== 'object') return false; if (obj && !(arg instanceof obj)) return false; @@ -149,10 +148,10 @@ class GenUtils { /** * Determines if all alphabet characters in the given string are upper case. * - * @param str is the string to test - * @returns true if the string is upper case, false otherwise + * @param {string} str is the string to test + * @return {boolean} true if the string is upper case, false otherwise */ - static isUpperCase(str) { + static isUpperCase(str: string): boolean { return str.toUpperCase() === str; } @@ -173,7 +172,7 @@ class GenUtils { * @param msg is the message to throw if the argument is not hex */ static assertHex(str, msg) { - GenUtils.assertTrue(isHex(str), msg ? msg : "Argument asserted as hex but is not hex"); + GenUtils.assertTrue(GenUtils.isHex(str), msg ? msg : "Argument asserted as hex but is not hex"); } /** @@ -182,7 +181,7 @@ class GenUtils { * Credit: https://github.com/roryrjb/is-hex/blob/master/is-hex.js. * * @param str is the string to test - * @returns true if the given string is hexidecimal, false otherwise + * @return true if the given string is hexidecimal, false otherwise */ static isHex(arg) { if (typeof arg !== 'string') return false; @@ -206,7 +205,7 @@ class GenUtils { * @param msg is the message to throw if the argument is not base58 */ static assertBase58(str, msg) { - GenUtils.assertTrue(isBase58(str), msg ? msg : "Argument asserted as base58 but is not base58"); + GenUtils.assertTrue(GenUtils.isBase58(str), msg ? msg : "Argument asserted as base58 but is not base58"); } /** @@ -225,7 +224,7 @@ class GenUtils { * @param msg is the message to throw if the argument is not base64 */ static assertBase64(str, msg) { - GenUtils.assertTrue(isBase64(str), msg ? msg : "Argument asserted as base64 but is not base64"); + GenUtils.assertTrue(GenUtils.isBase64(str), msg ? msg : "Argument asserted as base64 but is not base64"); } /** @@ -246,19 +245,19 @@ class GenUtils { * * @param msg defines the message to throw the exception with (optional) */ - static fail(msg) { + static fail(msg?) { throw new Error(msg ? msg : "Failure (no message)"); } /** - * Asserts that the given boolean is true. Throws an exception if not a boolean or false. + * Asserts that the given condition is true. Throws an exception if not a boolean or false. * - * @param bool is the boolean to assert true - * @param msg is the message to throw if bool is false (optional) + * @param {boolean} condition is the boolean to assert true + * @param {string} [msg] is the message to throw if condition is false (optional) */ - static assertTrue(bool, msg) { - if (typeof bool !== 'boolean') throw new Error("Argument is not a boolean"); - if (!bool) throw new Error(msg ? msg : "Boolean asserted as true but was false"); + static assertTrue(condition, msg?) { + if (typeof condition !== 'boolean') throw new Error("Argument is not a boolean"); + if (!condition) throw new Error(msg ? msg : "Boolean asserted as true but was false"); } /** @@ -267,7 +266,7 @@ class GenUtils { * @param bool is the boolean to assert false * @param msg is the message to throw if bool is true (optional) */ - static assertFalse(bool, msg) { + static assertFalse(bool, msg?) { if (typeof bool !== 'boolean') throw new Error("Argument is not a boolean"); if (bool) throw new Error(msg ? msg : "Boolean asserted as false but was true"); } @@ -278,7 +277,7 @@ class GenUtils { * @param arg is the argument to assert null * @param msg is the message to throw if arg is not null (optional) */ - static assertNull(arg, msg) { + static assertNull(arg, msg?) { if (arg !== null) throw new Error(msg ? msg : "Argument asserted as null but was not null: " + arg); } @@ -288,7 +287,7 @@ class GenUtils { * @param arg is the argument to assert not null * @param msg is the message to throw if arg is null (optional) */ - static assertNotNull(arg, msg) { + static assertNotNull(arg, msg?) { if (arg === null) throw new Error(msg ? msg : "Argument asserted as not null but was null"); } @@ -298,7 +297,7 @@ class GenUtils { * @param arg is the argument to assert defined * @param msg is the message to throw if arg is undefined (optional) */ - static assertDefined(arg, msg) { + static assertDefined(arg, msg?) { if (GenUtils.isUndefined(arg)) throw new Error(msg ? msg : "Argument asserted as defined but was undefined"); } @@ -308,7 +307,7 @@ class GenUtils { * @param arg is the argument to assert undefined * @param msg is the message to throw if arg is defined (optional) */ - static assertUndefined(arg, msg) { + static assertUndefined(arg, msg?) { if (GenUtils.isDefined(arg)) throw new Error(msg ? msg : "Argument asserted as undefined but was defined: " + arg); } @@ -318,7 +317,7 @@ class GenUtils { * @param arg is the argument to assert as initialized * @param msg is the message to throw if arg is not initialized (optional) */ - static assertInitialized(arg, msg) { + static assertInitialized(arg, msg?) { if (GenUtils.isUninitialized(arg)) { throw new Error(msg ? msg : "Argument asserted as initialized but was " + arg); } @@ -330,7 +329,7 @@ class GenUtils { * @param arg is the argument to assert as uninitialized * @param msg is the message to throw if arg is initialized (optional) */ - static assertUninitialized(arg, msg) { + static assertUninitialized(arg, msg?) { if (GenUtils.isInitialized(arg)) throw new Error(msg ? msg : "Argument asserted as uninitialized but was initialized"); } @@ -341,7 +340,7 @@ class GenUtils { * @param arg2 is an argument to assert as equal * @param msg is the message to throw if the arguments are not equal */ - static assertEquals(arg1, arg2, msg) { + static assertEquals(arg1, arg2, msg?) { GenUtils.assertTrue(GenUtils.equals(arg1, arg2), msg ? msg : "Arguments asserted as equal but are not equal: " + arg1 + " vs " + arg2); } @@ -352,7 +351,7 @@ class GenUtils { * @param arg2 is an argument to assert as not equal * @param msg is the message to throw if the arguments are equal */ - static assertNotEquals(arg1, arg2, msg) { + static assertNotEquals(arg1, arg2, msg?) { if (arg1 === arg2) throw new Error(msg ? msg : "Arguments asserted as not equal but are equal: " + arg1 + " vs " + arg2); } @@ -362,7 +361,7 @@ class GenUtils { * @param arg is the argument to assert as an integer * @param msg is the message to throw if the argument is not an integer */ - static assertInt(arg, msg) { + static assertInt(arg, msg?) { if (!GenUtils.isInt(arg)) throw new Error(msg ? msg : "Argument asserted as an integer but is not an integer"); } @@ -372,7 +371,7 @@ class GenUtils { * @param arg is the argument to assert as a number * @param msg is the message to throw if the argument is not a number */ - static assertNumber(arg, msg) { + static assertNumber(arg, msg?) { if (!GenUtils.isNumber(arg)) throw new Error(msg ? msg : "Argument asserted as a number but is not a number"); } @@ -382,7 +381,7 @@ class GenUtils { * @param arg is the argument to assert as a boolean * @param msg is the message to throw if the argument is not a boolean */ - static assertBoolean(arg, msg) { + static assertBoolean(arg, msg?) { if (!GenUtils.isBoolean(arg)) throw new Error(msg ? msg : "Argument asserted as a boolean but is not a boolean"); } @@ -392,7 +391,7 @@ class GenUtils { * @param arg is the argument to assert as a string * @param msg is the message to throw if the argument is not a string */ - static assertString(arg, msg) { + static assertString(arg, msg?) { if (!GenUtils.isString(arg)) throw new Error(msg ? msg : "Argument asserted as a string but is not a string: " + arg); } @@ -402,7 +401,7 @@ class GenUtils { * @param arg is the argument to assert as an array * @param msg is the message to throw if the argument is not an array */ - static assertArray(arg, msg) { + static assertArray(arg, msg?) { if (!GenUtils.isArray(arg)) throw new Error(msg ? msg : "Argument asserted as an array but is not an array"); } @@ -412,7 +411,7 @@ class GenUtils { * @param arg is the argument to assert as a static * @param msg is the message to throw if the argument is not a static */ - static assertFunction(arg, msg) { + static assertFunction(arg, msg?) { if (!GenUtils.isFunction(arg)) throw new Error(msg ? msg : "Argument asserted as a static but is not a static"); } @@ -423,12 +422,12 @@ class GenUtils { * @param obj is an object to assert arg instanceof obj (optional) * @param msg is the message to throw if the argument is not the specified object */ - static assertObject(arg, obj, msg) { + static assertObject(arg, obj, msg?) { GenUtils.assertInitialized(arg, msg); if (obj) { - if (!isObject(arg, obj)) throw new Error(msg ? msg : "Argument asserted as object '" + obj.name + "' but was not"); + if (!GenUtils.isObject(arg, obj)) throw new Error(msg ? msg : "Argument asserted as object '" + obj.name + "' but was not"); } else { - if (!isObject(arg)) throw new Error(msg ? msg : "Argument asserted as object but was not"); + if (!GenUtils.isObject(arg)) throw new Error(msg ? msg : "Argument asserted as object but was not"); } } @@ -454,7 +453,7 @@ class GenUtils { let args = []; for (let i = 1; i < arguments.length; i++) args.push(arguments[i]); for (let i = 0; i < fns.length; i++) { - assertFunction(fns[i], "Functions[" + i + "] is not a static"); + GenUtils.assertFunction(fns[i], "Functions[" + i + "] is not a static"); fns[i].apply(null, args); } } @@ -463,7 +462,7 @@ class GenUtils { * Returns the power set of the given array. * * @param arr is the array to get the power set of - * @returns [][] is the power set of the given array + * @return [][] is the power set of the given array */ static getPowerSet(arr) { let fn = function(n, src, got, all) { @@ -495,10 +494,10 @@ class GenUtils { * returns [][] is the power set of the given array whose elements are the given size */ static getPowerSetOfLength(arr, size) { - assertInitialized(arr); - assertInitialized(size); + GenUtils.assertInitialized(arr); + GenUtils.assertInitialized(size); GenUtils.assertTrue(size >= 1); - let powerSet = getPowerSet(arr); + let powerSet = GenUtils.getPowerSet(arr); let powerSetOfLength = []; for (let i = 0; i < powerSet.length; i++) { if (powerSet[i].length === size) { @@ -512,7 +511,7 @@ class GenUtils { * Returns an array of indices of the given size. * * @param size specifies the size to get indices for - * @returns array of the given size with indices starting at 0 + * @return array of the given size with indices starting at 0 */ static getIndices(size) { let indices = []; @@ -526,7 +525,7 @@ class GenUtils { * Returns a new array containing unique elements of the given array. * * @param arr is the array to return unique elements from - * @returns a new array with the given array's unique elements + * @return a new array with the given array's unique elements */ static toUniqueArray(arr) { return arr.filter(function(value, index, self) { @@ -538,7 +537,7 @@ class GenUtils { * Copies the given array. * * @param arr is the array to copy - * @returns a copy of the given array + * @return a copy of the given array */ static copyArray(arr) { GenUtils.assertArray(arr); @@ -552,7 +551,7 @@ class GenUtils { * * @param arr is the array to remove the value from * @param val is the value to remove from the array - * @returns true if the value is found and removed, false otherwise + * @return true if the value is found and removed, false otherwise */ static remove(arr, val) { let found = false; @@ -570,7 +569,7 @@ class GenUtils { * Returns a copy of the given array where each element is lowercase. * * @param arr is the array to convert to lowercase - * @returns a copy of the given array where each element is lowercase + * @return a copy of the given array where each element is lowercase */ static toLowerCaseArray(arr) { let arr2 = []; @@ -584,7 +583,7 @@ class GenUtils { * Listifies the given argument. * * @param arrOrElem is an array or an element in the array - * @returns an array which is the given arg if it's an array or an array with the given arg as an element + * @return an array which is the given arg if it's an array or an array with the given arg as an element */ static listify(arrOrElem) { return GenUtils.isArray(arrOrElem) ? arrOrElem : [arrOrElem]; @@ -593,12 +592,12 @@ class GenUtils { /** * Indicates if the given array contains the given object. * - * @param {object[]} arr - array that may or may not contain the object - * @param {object} obj - object to check for inclusion in the array - * @param {boolean} compareByReference - compare strictly by reference, forgoing deep equality check - * @returns true if the array contains the object, false otherwise + * @param {any} arr - array that may or may not contain the object + * @param {any} obj - object to check for inclusion in the array + * @param {boolean} [compareByReference] - compare strictly by reference, forgoing deep equality check (default false) + * @return true if the array contains the object, false otherwise */ - static arrayContains(arr, obj, compareByReference) { + static arrayContains(arr, obj, compareByReference = false) { GenUtils.assertTrue(GenUtils.isArray(arr)); for (let i = 0; i < arr.length; i++) { if (arr[i] === obj) return true; @@ -612,7 +611,7 @@ class GenUtils { * * @param str is the string to search for a substring * @param substring is the substring to searchin within the string - * @returns true if the substring is within the string, false otherwise + * @return true if the substring is within the string, false otherwise */ static strContains(str, substring) { return str.indexOf(substring) > -1; @@ -623,7 +622,7 @@ class GenUtils { * * @param arr1 is an array to compare * @param arr2 is an array to compare - * @returns true if the arrays are equal, false otherwise + * @return true if the arrays are equal, false otherwise */ static arraysEqual(arr1, arr2) { if (arr1 === arr2) return true; @@ -645,7 +644,7 @@ class GenUtils { * * @param arg1 is an argument to compare * @param arg2 is an argument to compare - * @returns true if the arguments are deep equals, false otherwise + * @return true if the arguments are deep equals, false otherwise */ static equals(arg1, arg2) { if (GenUtils.isArray(arg1) && GenUtils.isArray(arg2)) return GenUtils.arraysEqual(arg1, arg2); @@ -660,7 +659,7 @@ class GenUtils { * * @param map1 is a map to compare * @param map2 is a map to compare - * @returns true if the maps have identical keys and values, false otherwise + * @return true if the maps have identical keys and values, false otherwise */ static objectsEqual(map1, map2) { let keys1 = Object.keys(map1); @@ -723,12 +722,12 @@ class GenUtils { static getCombinations(arr, combinationSize) { // validate input - assertInitialized(arr); - assertInitialized(combinationSize); + GenUtils.assertInitialized(arr); + GenUtils.assertInitialized(combinationSize); GenUtils.assertTrue(combinationSize >= 1); // get combinations of array indices of the given size - let indexCombinations = getPowerSetOfLength(getIndices(arr.length), combinationSize); + let indexCombinations = GenUtils.getPowerSetOfLength(GenUtils.getIndices(arr.length), combinationSize); // collect combinations from each combination of array indices let combinations = []; @@ -755,7 +754,7 @@ class GenUtils { * * @param name is the name of the file to download * @param contents are the string contents of the file to download - * @returns 'a' dom element with downloadable file + * @return 'a' dom element with downloadable file */ static getDownloadableA(name, contents) { let a = window.document.createElement('a'); @@ -766,21 +765,11 @@ class GenUtils { return a; } - /** - * Returns the given node's outer HTML. - * - * @param node is the node to get outer HTML for - * @returns the outer HTML of the given node - */ - static getOuterHtml(node) { - return $('
').append($(node).clone()).html(); - } - /** * Copies properties in the given object to a new object. * * @param obj is object to copy properties for - * @returns a new object with properties copied from the given object + * @return a new object with properties copied from the given object */ static copyProperties(obj) { return JSON.parse(JSON.stringify(obj)) @@ -794,34 +783,34 @@ class GenUtils { static deleteProperties(obj) { let props = []; for (let prop in obj) props.push(prop); // TODO: if (obj.hasOwnProperty(prop)) { ... - for (i = 0; i < props.length; i++) delete obj[props[i].toString()]; + for (let i = 0; i < props.length; i++) delete obj[props[i].toString()]; } /** * Converts a CSV string to a 2-dimensional array of strings. * * @param csv is the CSV string to convert - * @returns a 2-dimensional array of strings + * @return a 2-dimensional array of strings */ static csvToArr(csv) { - return $.csv.toArrays(csv); + return csv.toArrays(csv); } /** * Converts the given array to a CSV string. * * @param arr is a 2-dimensional array of strings - * @returns the CSV string + * @return the CSV string */ static arrToCsv(arr) { - return $.csv.fromObjects(arr, {headers: false}); + return csv.fromObjects(arr, {headers: false}); } /** * Indicates if the given string contains whitespace. * * @param str is the string to test - * @returns true if the string contains whitespace, false otherwise + * @return true if the string contains whitespace, false otherwise */ static hasWhitespace(str) { return /\s/g.test(str); @@ -831,7 +820,7 @@ class GenUtils { * Indicates if the given character is whitespace. * * @param char is the character to test - * @returns true if the given character is whitespace, false otherwise + * @return true if the given character is whitespace, false otherwise */ static isWhitespace(char) { return /\s/.test(char); @@ -841,7 +830,7 @@ class GenUtils { * Indicates if the given character is a newline. * * @param char is the character to test - * @returns true if the given character is a newline, false otherwise + * @return true if the given character is a newline, false otherwise */ static isNewline(char) { return char === '\n' || char === '\r'; @@ -851,12 +840,12 @@ class GenUtils { * Counts the number of non-whitespace characters in the given string. * * @param str is the string to count the number of non-whitespace characters in - * @returns int is the number of non-whitespace characters in the given string + * @return int is the number of non-whitespace characters in the given string */ static countNonWhitespaceCharacters(str) { let count = 0; for (let i = 0; i < str.length; i++) { - if (!isWhitespace(str.charAt(i))) count++; + if (!GenUtils.isWhitespace(str.charAt(i))) count++; } return count; } @@ -865,7 +854,7 @@ class GenUtils { * Returns tokens separated by whitespace from the given string. * * @param str is the string to get tokens from - * @returns string[] are the tokens separated by whitespace within the string + * @return string[] are the tokens separated by whitespace within the string */ static getWhitespaceTokens(str) { return str.match(/\S+/g); @@ -884,7 +873,7 @@ class GenUtils { /** * Returns the document's first stylesheet which has no href. * - * @returns StyleSheet is the internal stylesheet + * @return StyleSheet is the internal stylesheet */ static getInternalStyleSheet() { for (let i = 0; i < document.styleSheets.length; i++) { @@ -897,11 +886,11 @@ class GenUtils { /** * Returns the document's internal stylesheet as text. * - * @returns str is the document's internal stylesheet + * @return str is the document's internal stylesheet */ static getInternalStyleSheetText() { let internalCss = ""; - let internalStyleSheet = getInternalStyleSheet(); + let internalStyleSheet = GenUtils.getInternalStyleSheet(); if (!internalStyleSheet) return null; for (let i = 0; i < internalStyleSheet.cssRules.length; i++) { internalCss += internalStyleSheet.cssRules[i].cssText + "\n"; @@ -918,7 +907,7 @@ class GenUtils { * content.dependencyPaths specifies paths to js, css, or img paths * content.internalCss is css to embed in the html document * content.metas are meta elements with keys/values to include - * @returns str is the document string + * @return str is the document string */ static buildHtmlDocument(content) { let str = ""; @@ -926,7 +915,7 @@ class GenUtils { // add metas if (content.metas) { - let metas = listify(content.metas); + let metas = GenUtils.listify(content.metas); for (let i = 0; i < metas.length; i++) { let meta = metas[i]; let elem = document.createElement("meta"); @@ -945,7 +934,7 @@ class GenUtils { // add dependency paths if (content.dependencyPaths) { - let dependencyPaths = listify(content.dependencyPaths); + let dependencyPaths = GenUtils.listify(content.dependencyPaths); for (let i = 0; i < dependencyPaths.length; i++) { let dependencyPath = dependencyPaths[i]; if (dependencyPath.endsWith(".js")) str += ""; @@ -974,19 +963,19 @@ class GenUtils { static newWindow(content, onLoad) { let onLoadCalled = false; let w = window.open(); - if (!isInitialized(w) || !isInitialized(w.document)) { + if (!GenUtils.isInitialized(w) || !GenUtils.isInitialized(w.document)) { onLoadOnce(new Error("Could not get window reference")); return; } w.opener = null; - w.document.write(buildHtmlDocument(content)); + w.document.write(GenUtils.buildHtmlDocument(content)); w.addEventListener('load', function() { onLoadOnce(null, w); }); w.document.close(); // prevents onLoad() from being called multiple times - function onLoadOnce(err, window) { + function onLoadOnce(err, window?) { if (onLoadCalled) return; onLoadCalled = true; if (onLoad) onLoad(err, window); @@ -1052,7 +1041,7 @@ class GenUtils { * Determines if the given file is a zip file. * * @param file is a file - * @returns true if the given file is a zip file, false otherwise + * @return true if the given file is a zip file, false otherwise */ static isZipFile(file) { return file.name.endsWith(".zip") || file.type === 'application/zip'; @@ -1062,7 +1051,7 @@ class GenUtils { * Determines if the given file is a json file. * * @param file is a file - * @returns true if the given file is a json file, false otherwise + * @return true if the given file is a json file, false otherwise */ static isJsonFile(file) { return file.name.endsWith(".json") || file.type === 'application/json'; @@ -1072,7 +1061,7 @@ class GenUtils { * Determines if the given file is a csv file. * * @param file is a file - * @returns true if the given file is a csv file, false otherwise + * @return true if the given file is a csv file, false otherwise */ static isCsvFile(file) { return file.name.endsWith(".csv") || file.type === 'text/csv'; @@ -1082,7 +1071,7 @@ class GenUtils { * Determines if the given file is a txt file. * * @param file is a file - * @returns true if the given file is a txt file, false otherwise + * @return true if the given file is a txt file, false otherwise */ static isTxtFile(file) { return file.name.endsWith(".txt") || file.type === 'text/plain'; @@ -1100,7 +1089,7 @@ class GenUtils { // listify paths if (!GenUtils.isArray(paths)) { - GenUtils.assertTrue(isString(paths)); + GenUtils.assertTrue(GenUtils.isString(paths)); paths = [paths]; } @@ -1128,7 +1117,7 @@ class GenUtils { * Returns a string indentation of the given length; * * @param length is the length of the indentation - * @returns {string} is an indentation string of the given length + * @return {string} is an indentation string of the given length */ static getIndent(length) { let str = ""; @@ -1140,7 +1129,7 @@ class GenUtils { // Polyfill Object.assign() // Credit: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign - if (typeof Object.assign != 'static') { + if (typeof Object.assign != 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, "assign", { value: function assign(target, varArgs) { // .length of static is 2 @@ -1170,13 +1159,6 @@ class GenUtils { }); } - /** - * Polyfill str.replaceAt(idx, replacement). - */ - String.prototype.replaceAt=function(idx, replacement) { - return this.substr(0, idx) + replacement + this.substr(idx + replacement.length); - } - /** * Polyfill str.startsWith(searchString, position). * @@ -1196,23 +1178,6 @@ class GenUtils { else position |= 0; // round position return this.substr(position - searchString.length, searchString.length) === searchString; } - - /** - * Removes the given value from the array. - * - * @returns true if the value was found and removed, false otherwise - */ - Array.prototype.removeVal = function(val) { - var found = false; - for (var i = 0; i < this.length; i++) { - if (this[i] == val) { - found = true; - this.splice(i, 1); - i--; - } - } - return found;; - }; } /** @@ -1253,7 +1218,7 @@ class GenUtils { * * Credit: https://stackoverflow.com/questions/19999388/check-if-user-is-using-ie-with-jquery/21712356#21712356 * - * @returns the IE version number or null if not IE + * @return the IE version number or null if not IE */ static getIEVersion() { let ua = window.navigator.userAgent; @@ -1288,7 +1253,7 @@ class GenUtils { * * @param name is the name of the parameter to get the value of * @param url is a URL to get the parameter from, uses the window's current href if not given - * @returns the parameter's value + * @return the parameter's value */ static getParameterByName(name, url) { if (!url) url = window.location.href; @@ -1377,13 +1342,13 @@ class GenUtils { * @param getFn gets the current value * @param setFn sets the current value * @param val is the value to set iff it does not overwrite a previous value - * @param config specifies reconciliation configuration + * @param [config] specifies reconciliation configuration * config.resolveDefined uses defined value if true or undefined, undefined if false * config.resolveTrue uses true over false if true, false over true if false, must be equal if undefined * config.resolveMax uses max over min if true, min over max if false, must be equal if undefined - * @param errMsg is the error message to throw if the values cannot be reconciled (optional) + * @param [errMsg] is the error message to throw if the values cannot be reconciled (optional) */ - static safeSet(obj, getFn, setFn, val, config, errMsg) { + static safeSet(obj, getFn, setFn, val, config?, errMsg?) { let curVal = getFn.call(obj); let reconciledVal = GenUtils.reconcile(curVal, val, config, errMsg); if (curVal !== reconciledVal) setFn.call(obj, reconciledVal); @@ -1396,23 +1361,22 @@ class GenUtils { * * @param val1 is a value to reconcile * @param val2 is a value to reconcile - * @param config specifies reconciliation configuration + * @param [config] specifies reconciliation configuration * config.resolveDefined uses defined value if true or undefined, undefined if false * config.resolveTrue uses true over false if true, false over true if false, must be equal if undefined * config.resolveMax uses max over min if true, min over max if false, must be equal if undefined - * @param errMsg is the error message to throw if the values cannot be reconciled (optional) - * @returns the reconciled value if reconcilable, throws error otherwise + * @param [errMsg] is the error message to throw if the values cannot be reconciled (optional) + * @return the reconciled value if reconcilable, throws error otherwise */ - static reconcile(val1, val2, config, errMsg) { + static reconcile(val1, val2, config?, errMsg?) { // check for equality if (val1 === val2) return val1; - // check for BigInteger equality + // check for bigint equality let comparison; // save comparison for later if applicable - if (val1 instanceof BigInteger && val2 instanceof BigInteger) { - comparison = val1.compare(val2); - if (comparison === 0) return val1; + if (typeof val1 === "bigint" && typeof val2 === "bigint") { + if (val1 === val2) return val1; } // resolve one value defined @@ -1436,8 +1400,8 @@ class GenUtils { return config.resolveMax ? Math.max(val1, val2) : Math.min(val1, val2); } - // resolve BigIntegers - if (val1 instanceof BigInteger && val2 instanceof BigInteger) { + // resolve bigints + if (typeof val1 === "bigint" && typeof val2 === "bigint") { return config.resolveMax ? (comparison < 0 ? val2 : val1) : (comparison < 0 ? val1 : val2); } } @@ -1455,7 +1419,7 @@ class GenUtils { * @param indent indents the line * @param newline specifies if the string should be terminated with a newline or not * @param ignoreUndefined specifies if undefined values should return an empty string - * @returns {string} is the human-friendly key value line + * @return {string} is the human-friendly key value line */ static kvLine(key, value, indent = 0, newline = true, ignoreUndefined = true) { if (value === undefined && ignoreUndefined) return ""; @@ -1469,7 +1433,7 @@ class GenUtils { * @param {string} str is the string to be modified * @return {string} the modified string with big numbers converted to strings */ - static stringifyBIs(str) { + static stringifyBigInts(str) { return str.replace(/("[^"]*"\s*:\s*)(\d{16,})/g, '$1"$2"'); } @@ -1480,7 +1444,7 @@ class GenUtils { */ static printStackTrace(msg) { try { throw new Error(msg); } - catch (err) { console.error(err.stack); } + catch (err: any) { console.error(err.stack); } } /** @@ -1495,16 +1459,16 @@ class GenUtils { /** * Kill the given nodejs child process. * - * @param {process} process - the nodejs child process to kill - * @param {string|undefined} signal - the kill signal, e.g. SIGTERM, SIGKILL, SIGINT (default) - * @return {Promise} the exit code from killing the process + * @param {ChildProcess} process - the nodejs child process to kill + * @param {number | NodeJS.Signals} [signal] - the kill signal, e.g. SIGTERM, SIGKILL, SIGINT (default) + * @return {Promise} the exit code from killing the process */ - static async killProcess(process, signal) { + static async killProcess(process: ChildProcess, signal?: number | NodeJS.Signals): Promise { return new Promise((resolve, reject) => { process.on("exit", function(code, signal) { resolve(code); }); process.on("error", function(err) { reject(err); }); try { - if (!process.kill(signal ? signal : "SIGINT")) resolve(); // resolve immediately if not running + if (!process.kill(signal === undefined ? "SIGINT" : signal)) resolve(undefined); // resolve immediately if not running } catch (err) { reject(err); } @@ -1523,6 +1487,29 @@ class GenUtils { if (!new RegExp("^\\w+://.+").test(uri)) uri= "http://" + uri; // assume http if protocol not given return uri; } + + /** + * Get the absolute value of the given bigint or number. + * + * @param {bigint | number} bi - the bigint or number to get the absolute value of + * @return {bigint | number} the absolute value of the given bigint or number + */ + static abs(bi: bigint | number): bigint | number { + return bi < 0 ? -bi : bi; + } + + /** + * Get an enum key name by value. + * + * @param {any} enumType is the enum type to get the key from + * @param {any} enumValue is the enum value to get the key for + * @return {string | undefined} the enum key name + */ + static getEnumKeyByValue(enumType: any, enumValue: any): string | undefined { + for (let key in enumType) { + if (enumType[key] === enumValue) return key; + } + return undefined; + } } -module.exports = GenUtils; \ No newline at end of file diff --git a/src/main/ts/common/HttpClient.ts b/src/main/ts/common/HttpClient.ts new file mode 100644 index 000000000..9aaf1fa0d --- /dev/null +++ b/src/main/ts/common/HttpClient.ts @@ -0,0 +1,498 @@ +import GenUtils from "./GenUtils"; +import LibraryUtils from "./LibraryUtils"; +import MoneroUtils from "./MoneroUtils"; +import ThreadPool from "./ThreadPool"; +import PromiseThrottle from "promise-throttle"; +import Request from "request-promise"; +import http from "http"; +import https from "https"; + +/** + * Handle HTTP requests with a uniform interface. + */ +export default class HttpClient { + + static MAX_REQUESTS_PER_SECOND = 50 + + // default request config + protected static DEFAULT_REQUEST = { + method: "GET", + requestApi: "fetch", + resolveWithFullResponse: false, + rejectUnauthorized: true + } + + // rate limit requests per host + protected static PROMISE_THROTTLES = []; + protected static TASK_QUEUES = []; + protected static DEFAULT_TIMEOUT = 60000; + static MAX_TIMEOUT = 2147483647; // max 32-bit signed number + + protected static HTTP_AGENT: any; + protected static HTTPS_AGENT: any; + + /** + *

Make a HTTP request.

+ * + * @param {object} request - configures the request to make + * @param {string} request.method - HTTP method ("GET", "PUT", "POST", "DELETE", etc) + * @param {string} request.uri - uri to request + * @param {string|Uint8Array|object} request.body - request body + * @param {string} [request.username] - username to authenticate the request (optional) + * @param {string} [request.password] - password to authenticate the request (optional) + * @param {object} [request.headers] - headers to add to the request (optional) + * @param {string} [request.requestApi] - one of "fetch" or "xhr" (default "fetch") + * @param {boolean} [request.resolveWithFullResponse] - return full response if true, else body only (default false) + * @param {boolean} [request.rejectUnauthorized] - whether or not to reject self-signed certificates (default true) + * @param {number} request.timeout - maximum time allowed in milliseconds + * @param {number} request.proxyToWorker - proxy request to worker thread + * @return {object} response - the response object + * @return {string|Uint8Array|object} response.body - the response body + * @return {number} response.statusCode - the response code + * @return {String} response.statusText - the response message + * @return {object} response.headers - the response headers + */ + static async request(request) { + + // proxy to worker if configured + if (request.proxyToWorker) { + try { + return await LibraryUtils.invokeWorker(undefined, "httpRequest", request); + } catch (err: any) { + if (err.message.length > 0 && err.message.charAt(0) === "{") { + let parsed = JSON.parse(err.message); + err.message = parsed.statusMessage; + err.statusCode = parsed.statusCode; + } + throw err; + } + } + + // assign defaults + request = Object.assign({}, HttpClient.DEFAULT_REQUEST, request); + + // validate request + try { request.host = new URL(request.uri).host; } // hostname:port + catch (err) { throw new Error("Invalid request URL: " + request.uri); } + if (request.body && !(typeof request.body === "string" || typeof request.body === "object")) { + throw new Error("Request body type is not string or object"); + } + + // initialize one task queue per host + if (!HttpClient.TASK_QUEUES[request.host]) HttpClient.TASK_QUEUES[request.host] = new ThreadPool(1); + + // initialize one promise throttle per host + if (!HttpClient.PROMISE_THROTTLES[request.host]) { + HttpClient.PROMISE_THROTTLES[request.host] = new PromiseThrottle({ + requestsPerSecond: HttpClient.MAX_REQUESTS_PER_SECOND, // TODO: HttpClient should not depend on MoneroUtils for configuration + promiseImplementation: Promise + }); + } + + // request using fetch or xhr with timeout + let timeout = request.timeout === undefined ? HttpClient.DEFAULT_TIMEOUT : request.timeout === 0 ? HttpClient.MAX_TIMEOUT : request.timeout; + let requestPromise = request.requestApi === "fetch" ? HttpClient.requestFetch(request) : HttpClient.requestXhr(request); + let timeoutPromise = new Promise((resolve, reject) => { + let id = setTimeout(() => { + clearTimeout(id); + reject('Request timed out in '+ timeout + ' milliseconds') + }, timeout); + }); + return Promise.race([requestPromise, timeoutPromise]); + } + + // ----------------------------- PRIVATE HELPERS ---------------------------- + + protected static async requestFetch(req) { + + // build request options + let opts: any = { + method: req.method, + uri: req.uri, + body: req.body, + agent: req.uri.startsWith("https") ? HttpClient.getHttpsAgent() : HttpClient.getHttpAgent(), + rejectUnauthorized: req.rejectUnauthorized, + resolveWithFullResponse: req.resolveWithFullResponse, + requestCert: true // TODO: part of config? + }; + if (req.username) { + opts.forever = true; + opts.auth = { + user: req.username, + pass: req.password, + sendImmediately: false + } + } + if (req.body instanceof Uint8Array) opts.encoding = null; + + // queue and throttle request to execute in serial and rate limited + let host = req.host; + let resp = await HttpClient.TASK_QUEUES[host].submit(async function() { + return HttpClient.PROMISE_THROTTLES[host].add(function(opts) { return Request(opts); }.bind(this, opts)); + }); + + // normalize response + let normalizedResponse: any = {}; + if (req.resolveWithFullResponse) { + normalizedResponse.statusCode = resp.statusCode; + normalizedResponse.statusText = resp.statusMessage; + normalizedResponse.headers = resp.headers; + normalizedResponse.body = resp.body; + } else { + normalizedResponse.body = resp; + } + return normalizedResponse; + } + + protected static async requestXhr(req) { + if (req.headers) throw new Error("Custom headers not implemented in XHR request"); // TODO + + // collect params from request which change on await + let method = req.method; + let uri = req.uri; + let host = req.host; + let username = req.username; + let password = req.password; + let body = req.body; + let isBinary = body instanceof Uint8Array; + + // queue and throttle requests to execute in serial and rate limited per host + let resp = await HttpClient.TASK_QUEUES[host].submit(async function() { + return HttpClient.PROMISE_THROTTLES[host].add(function() { + return new Promise(function(resolve, reject) { + let digestAuthRequest = new HttpClient.digestAuthRequest(method, uri, username, password); + digestAuthRequest.request(function(resp) { + resolve(resp); + }, function(resp) { + if (resp.status) resolve(resp); + else reject(new Error("Request failed without response: " + method + " " + uri)); + }, body); + }); + }.bind(this)); + }); + + // normalize response + let normalizedResponse: any = {}; + normalizedResponse.statusCode = resp.status; + normalizedResponse.statusText = resp.statusText; + normalizedResponse.headers = HttpClient.parseXhrResponseHeaders(resp.getAllResponseHeaders()); + normalizedResponse.body = isBinary ? new Uint8Array(resp.response) : resp.response; + if (normalizedResponse.body instanceof ArrayBuffer) normalizedResponse.body = new Uint8Array(normalizedResponse.body); // handle empty binary request + return normalizedResponse; + } + + /** + * Get a singleton instance of an HTTP client to share. + * + * @return {http.Agent} a shared agent for network requests among library instances + */ + protected static getHttpAgent() { + if (!HttpClient.HTTP_AGENT) HttpClient.HTTP_AGENT = new http.Agent({ + keepAlive: true, + family: 4 // use IPv4 + }); + return HttpClient.HTTP_AGENT; + } + + /** + * Get a singleton instance of an HTTPS client to share. + * + * @return {https.Agent} a shared agent for network requests among library instances + */ + protected static getHttpsAgent() { + if (!HttpClient.HTTPS_AGENT) HttpClient.HTTPS_AGENT = new https.Agent({ + keepAlive: true, + family: 4 // use IPv4 + }); + return HttpClient.HTTPS_AGENT; + } + + protected static parseXhrResponseHeaders(headersStr) { + let headerMap = {}; + let headers = headersStr.trim().split(/[\r\n]+/); + for (let header of headers) { + let headerVals = header.split(": "); + headerMap[headerVals[0]] = headerVals[1]; + } + return headerMap; + } + + /** + * Modification of digest auth request by @inorganik. + * + * Dependent on CryptoJS MD5 hashing: http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/md5.js + * + * MIT licensed. + */ + protected static digestAuthRequest = function(method, url, username, password) { + var self = this; + + if (typeof CryptoJS === 'undefined' && typeof require === 'function') { + var CryptoJS = require('crypto-js'); + } + + this.scheme = null; // we just echo the scheme, to allow for 'Digest', 'X-Digest', 'JDigest' etc + this.nonce = null; // server issued nonce + this.realm = null; // server issued realm + this.qop = null; // "quality of protection" - '' or 'auth' or 'auth-int' + this.response = null; // hashed response to server challenge + this.opaque = null; // hashed response to server challenge + this.nc = 1; // nonce count - increments with each request used with the same nonce + this.cnonce = null; // client nonce + + // settings + this.timeout = 60000; // timeout + this.loggingOn = false; // toggle console logging + + // determine if a post, so that request will send data + this.post = false; + if (method.toLowerCase() === 'post' || method.toLowerCase() === 'put') { + this.post = true; + } + + // start here + // successFn - will be passed JSON data + // errorFn - will be passed the failed authenticatedRequest + // data - optional, for POSTS + this.request = function(successFn, errorFn, data) { + + // stringify json + if (data) { + try { + self.data = data instanceof Uint8Array || typeof data === "string" ? data : JSON.stringify(data); + } catch (err) { + console.error(err); + throw err; + } + } + self.successFn = successFn; + self.errorFn = errorFn; + + if (!self.nonce) { + self.makeUnauthenticatedRequest(self.data); + } else { + self.makeAuthenticatedRequest(); + } + } + this.makeUnauthenticatedRequest = function(data) { + self.firstRequest = new XMLHttpRequest(); + self.firstRequest.open(method, url, true); + self.firstRequest.timeout = self.timeout; + // if we are posting, add appropriate headers + if (self.post && data) { + if (typeof data === "string") { + self.firstRequest.setRequestHeader('Content-type', 'text/plain'); + } else { + self.firstRequest.responseType = "arraybuffer"; + } + } + + self.firstRequest.onreadystatechange = function() { + + // 2: received headers, 3: loading, 4: done + if (self.firstRequest.readyState === 2) { + + var responseHeaders = self.firstRequest.getAllResponseHeaders(); + responseHeaders = responseHeaders.split('\n'); + // get authenticate header + var digestHeaders; + for(var i = 0; i < responseHeaders.length; i++) { + if (responseHeaders[i].match(/www-authenticate/i) != null) { + digestHeaders = responseHeaders[i]; + } + } + + if (digestHeaders != null) { + // parse auth header and get digest auth keys + digestHeaders = digestHeaders.slice(digestHeaders.indexOf(':') + 1, -1); + digestHeaders = digestHeaders.split(','); + self.scheme = digestHeaders[0].split(/\s/)[1]; + for (var i = 0; i < digestHeaders.length; i++) { + var equalIndex = digestHeaders[i].indexOf('='), + key = digestHeaders[i].substring(0, equalIndex), + val = digestHeaders[i].substring(equalIndex + 1); + val = val.replace(/['"]+/g, ''); + // find realm + if (key.match(/realm/i) != null) { + self.realm = val; + } + // find nonce + if (key.match(/nonce/i) != null) { + self.nonce = val; + } + // find opaque + if (key.match(/opaque/i) != null) { + self.opaque = val; + } + // find QOP + if (key.match(/qop/i) != null) { + self.qop = val; + } + } + // client generated keys + self.cnonce = self.generateCnonce(); + self.nc++; + // if logging, show headers received: + self.log('received headers:'); + self.log(' realm: '+self.realm); + self.log(' nonce: '+self.nonce); + self.log(' opaque: '+self.opaque); + self.log(' qop: '+self.qop); + // now we can make an authenticated request + self.makeAuthenticatedRequest(); + } + } + if (self.firstRequest.readyState === 4) { + if (self.firstRequest.status === 200) { + self.log('Authentication not required for '+url); + if (data instanceof Uint8Array) { + self.successFn(self.firstRequest); + } else { + if (self.firstRequest.responseText !== 'undefined') { + if (self.firstRequest.responseText.length > 0) { + // If JSON, parse and return object + if (self.isJson(self.firstRequest.responseText)) { // TODO: redundant + self.successFn(self.firstRequest); + } else { + self.successFn(self.firstRequest); + } + } + } else { + self.successFn(); + } + } + } + } + } + // send + if (self.post) { + // in case digest auth not required + self.firstRequest.send(self.data); + } else { + self.firstRequest.send(); + } + self.log('Unauthenticated request to '+url); + + // handle error + self.firstRequest.onerror = function() { + if (self.firstRequest.status !== 401) { + self.log('Error ('+self.firstRequest.status+') on unauthenticated request to '+url); + self.errorFn(self.firstRequest); + } + } + } + this.makeAuthenticatedRequest= function() { + + self.response = self.formulateResponse(); + self.authenticatedRequest = new XMLHttpRequest(); + self.authenticatedRequest.open(method, url, true); + self.authenticatedRequest.timeout = self.timeout; + var digestAuthHeader = self.scheme+' '+ + 'username="'+username+'", '+ + 'realm="'+self.realm+'", '+ + 'nonce="'+self.nonce+'", '+ + 'uri="'+url+'", '+ + 'response="'+self.response+'", '+ + 'opaque="'+self.opaque+'", '+ + 'qop='+self.qop+', '+ + 'nc='+('00000000' + self.nc).slice(-8)+', '+ + 'cnonce="'+self.cnonce+'"'; + self.authenticatedRequest.setRequestHeader('Authorization', digestAuthHeader); + self.log('digest auth header response to be sent:'); + self.log(digestAuthHeader); + // if we are posting, add appropriate headers + if (self.post && self.data) { + if (typeof self.data === "string") { + self.authenticatedRequest.setRequestHeader('Content-type', 'text/plain'); + } else { + self.authenticatedRequest.responseType = "arraybuffer"; + } + } + self.authenticatedRequest.onload = function() { + // success + if (self.authenticatedRequest.status >= 200 && self.authenticatedRequest.status < 400) { + // increment nonce count + self.nc++; + // return data + if (self.data instanceof Uint8Array) { + self.successFn(self.authenticatedRequest); + } else { + if (self.authenticatedRequest.responseText !== 'undefined' && self.authenticatedRequest.responseText.length > 0 ) { + // If JSON, parse and return object + if (self.isJson(self.authenticatedRequest.responseText)) { // TODO: redundant from not parsing + self.successFn(self.authenticatedRequest); + } else { + self.successFn(self.authenticatedRequest); + } + } else { + self.successFn(); + } + } + } + // failure + else { + self.nonce = null; + self.errorFn(self.authenticatedRequest); + } + } + // handle errors + self.authenticatedRequest.onerror = function() { + self.log('Error ('+self.authenticatedRequest.status+') on authenticated request to '+url); + self.nonce = null; + self.errorFn(self.authenticatedRequest); + }; + // send + if (self.post) { + self.authenticatedRequest.send(self.data); + } else { + self.authenticatedRequest.send(); + } + self.log('Authenticated request to '+url); + } + // hash response based on server challenge + this.formulateResponse = function() { + var HA1 = CryptoJS.MD5(username+':'+self.realm+':'+password).toString(); + var HA2 = CryptoJS.MD5(method+':'+url).toString(); + var response = CryptoJS.MD5(HA1+':'+ + self.nonce+':'+ + ('00000000' + self.nc).slice(-8)+':'+ + self.cnonce+':'+ + self.qop+':'+ + HA2).toString(); + return response; + } + // generate 16 char client nonce + this.generateCnonce = function() { + var characters = 'abcdef0123456789'; + var token = ''; + for (var i = 0; i < 16; i++) { + var randNum = Math.round(Math.random() * characters.length); + token += characters.substr(randNum, 1); + } + return token; + } + this.abort = function() { + self.log('[digestAuthRequest] Aborted request to '+url); + if (self.firstRequest != null) { + if (self.firstRequest.readyState != 4) self.firstRequest.abort(); + } + if (self.authenticatedRequest != null) { + if (self.authenticatedRequest.readyState != 4) self.authenticatedRequest.abort(); + } + } + this.isJson = function(str) { + try { + JSON.parse(str); + } catch (err) { + return false; + } + return true; + } + this.log = function(str) { + if (self.loggingOn) { + console.log('[digestAuthRequest] '+str); + } + } + this.version = function() { return '0.8.0' } + } +} diff --git a/src/main/js/common/LibraryUtils.js b/src/main/ts/common/LibraryUtils.ts similarity index 67% rename from src/main/js/common/LibraryUtils.js rename to src/main/ts/common/LibraryUtils.ts index df7a67abb..b5bf065a9 100644 --- a/src/main/js/common/LibraryUtils.js +++ b/src/main/ts/common/LibraryUtils.ts @@ -1,19 +1,34 @@ -const assert = require("assert"); -const GenUtils = require("./GenUtils"); -const MoneroError = require("./MoneroError"); -const ThreadPool = require("./ThreadPool"); +import assert from "assert"; +import GenUtils from "./GenUtils"; +import MoneroError from "./MoneroError"; +import ThreadPool from "./ThreadPool"; +import path from "path"; /** * Collection of helper utilities for the library. - * - * @hideconstructor */ -class LibraryUtils { +export default class LibraryUtils { + + // static variables + static LOG_LEVEL = 0; + static WASM_MODULE: any; + static WORKER: any; + static WORKER_OBJECTS: any; + static FULL_LOADED: any; + static REJECT_UNAUTHORIZED_FNS: any; + static WORKER_DIST_PATH_DEFAULT = GenUtils.isBrowser() ? "/monero_web_worker.js" : function() { + + // get worker path in dist (assumes library is running from src or dist) + let curPath = __dirname; + if (curPath.indexOf("monero-ts/dist") === -1) curPath = path.join(curPath, "../../../../dist/src/main/js/common"); + return LibraryUtils.prefixWindowsPath(path.join(curPath, "./MoneroWebWorker.js")); + }(); + static WORKER_DIST_PATH = LibraryUtils.WORKER_DIST_PATH_DEFAULT; /** * Log a message. * - * @param {int} level - log level of the message + * @param {number} level - log level of the message * @param {string} msg - message to log */ static log(level, msg) { @@ -24,7 +39,7 @@ class LibraryUtils { /** * Set the library's log level with 0 being least verbose. * - * @param {int} level - the library's log level + * @param {number} level - the library's log level */ static async setLogLevel(level) { assert(level === parseInt(level, 10) && level >= 0, "Log level must be an integer >= 0"); @@ -36,20 +51,20 @@ class LibraryUtils { /** * Get the library's log level. * - * @return {int} the library's log level + * @return {number} the library's log level */ - static getLogLevel() { + static getLogLevel(): number { return LibraryUtils.LOG_LEVEL; } /** * Get the total memory used by WebAssembly. * - * @return {int} the total memory used by WebAssembly + * @return {Promise} the total memory used by WebAssembly */ - static async getWasmMemoryUsed() { + static async getWasmMemoryUsed(): Promise { let total = 0; - if (LibraryUtils.WORKER) total += await LibraryUtils.invokeWorker(undefined, "getWasmMemoryUsed", []); + if (LibraryUtils.WORKER) total += await LibraryUtils.invokeWorker(undefined, "getWasmMemoryUsed", []) as number; if (LibraryUtils.getWasmModule() && LibraryUtils.getWasmModule().HEAP8) total += LibraryUtils.getWasmModule().HEAP8.length; return total; } @@ -76,7 +91,7 @@ class LibraryUtils { LibraryUtils.WASM_MODULE.then(module => { LibraryUtils.WASM_MODULE = module delete LibraryUtils.WASM_MODULE.then; - LibraryUtils._initWasmModule(LibraryUtils.WASM_MODULE); + LibraryUtils.initWasmModule(LibraryUtils.WASM_MODULE); resolve(LibraryUtils.WASM_MODULE); }); }); @@ -102,7 +117,7 @@ class LibraryUtils { LibraryUtils.WASM_MODULE = module; delete LibraryUtils.WASM_MODULE.then; LibraryUtils.FULL_LOADED = true; - LibraryUtils._initWasmModule(LibraryUtils.WASM_MODULE); + LibraryUtils.initWasmModule(LibraryUtils.WASM_MODULE); resolve(LibraryUtils.WASM_MODULE); }); }); @@ -138,7 +153,7 @@ class LibraryUtils { * @param {string} workerDistPath - path to load the worker */ static setWorkerDistPath(workerDistPath) { - let path = LibraryUtils._prefixWindowsPath(workerDistPath ? workerDistPath : LibraryUtils.WORKER_DIST_PATH_DEFAULT); + let path = LibraryUtils.prefixWindowsPath(workerDistPath ? workerDistPath : LibraryUtils.WORKER_DIST_PATH_DEFAULT); if (path !== LibraryUtils.WORKER_DIST_PATH) delete LibraryUtils.WORKER; LibraryUtils.WORKER_DIST_PATH = path; } @@ -152,16 +167,17 @@ class LibraryUtils { // one time initialization if (!LibraryUtils.WORKER) { - if (GenUtils.isBrowser()) LibraryUtils.WORKER = new Worker(LibraryUtils.WORKER_DIST_PATH); - else { - const Worker = require("web-worker"); // import web worker if nodejs - LibraryUtils.WORKER = new Worker(LibraryUtils.WORKER_DIST_PATH); + if (GenUtils.isBrowser()) { + LibraryUtils.WORKER = new Worker(LibraryUtils.WORKER_DIST_PATH); + } else { + const Worker = require("web-worker"); // import web worker if nodejs + LibraryUtils.WORKER = new Worker(LibraryUtils.WORKER_DIST_PATH); } LibraryUtils.WORKER_OBJECTS = {}; // store per object running in the worker // receive worker errors LibraryUtils.WORKER.onerror = function(err) { - console.error("Error posting message to MoneroWebWorker.js; is it copied to the app's build directory (e.g. in the root)?"); + console.error("Error posting message to Monero web worker; is it built and copied to the app's build directory (e.g. in the root)?"); console.log(err); }; @@ -169,7 +185,7 @@ class LibraryUtils { LibraryUtils.WORKER.onmessage = function(e) { // lookup object id, callback function, and this arg - let thisArg = null; + let thisArg = undefined; let callbackFn = LibraryUtils.WORKER_OBJECTS[e.data[0]].callbacks[e.data[1]]; // look up by object id then by function name if (callbackFn === undefined) throw new Error("No worker callback function defined for key '" + e.data[1] + "'"); if (callbackFn instanceof Array) { // this arg may be stored with callback function @@ -183,9 +199,21 @@ class LibraryUtils { } return LibraryUtils.WORKER; } + + static addWorkerCallback(objectId, callbackId, callbackArgs) { + LibraryUtils.WORKER_OBJECTS[objectId].callbacks[callbackId] = callbackArgs; + } + + static removeWorkerCallback(objectId, callbackId) { + delete LibraryUtils.WORKER_OBJECTS[objectId].callbacks[callbackId]; + } + + static removeWorkerObject(objectId) { + delete LibraryUtils.WORKER_OBJECTS[objectId]; + } /** - * Terminate monero-javascript's singleton worker. + * Terminate monero-ts's singleton worker. */ static async terminateWorker() { if (LibraryUtils.WORKER) { @@ -194,13 +222,13 @@ class LibraryUtils { LibraryUtils.WORKER = undefined; } } - + /** * Invoke a worker function and get the result with error handling. * * @param {string} objectId identifies the worker object to invoke (default random id) * @param {string} fnName is the name of the function to invoke - * @param {any[]} args are function arguments to invoke with + * @param {any[]} [args] are function arguments to invoke with * @return {any} resolves with response payload from the worker or an error */ static async invokeWorker(objectId, fnName, args) { @@ -209,48 +237,43 @@ class LibraryUtils { let randomObject = objectId === undefined; if (randomObject) objectId = GenUtils.getUUID(); if (!LibraryUtils.WORKER_OBJECTS[objectId]) LibraryUtils.WORKER_OBJECTS[objectId] = {callbacks: {}}; - return await new Promise(function(resolve, reject) { - let callbackId = GenUtils.getUUID(); - LibraryUtils.WORKER_OBJECTS[objectId].callbacks[callbackId] = function(resp) { // TODO: this defines function once per callback - resp ? (resp.error ? reject(LibraryUtils.deserializeError(resp.error)) : resolve(resp.result)) : resolve(); - delete LibraryUtils.WORKER_OBJECTS[objectId].callbacks[callbackId]; - if (randomObject) delete LibraryUtils.WORKER_OBJECTS[objectId]; - }; - worker.postMessage([objectId, fnName, callbackId].concat(args === undefined ? [] : GenUtils.listify(args))); - }); + let callbackId = GenUtils.getUUID(); + try { + return await new Promise((resolve, reject) => { + LibraryUtils.WORKER_OBJECTS[objectId].callbacks[callbackId] = (resp) => { // TODO: this defines function once per callback + delete LibraryUtils.WORKER_OBJECTS[objectId].callbacks[callbackId]; + if (randomObject) delete LibraryUtils.WORKER_OBJECTS[objectId]; + resp ? (resp.error ? reject(new Error(JSON.stringify(resp.error))) : resolve(resp.result)) : resolve(undefined); + }; + worker.postMessage([objectId, fnName, callbackId].concat(args === undefined ? [] : GenUtils.listify(args))); + }); + } catch (e: any) { + throw LibraryUtils.deserializeError(JSON.parse(e.message)); + } } static serializeError(err) { - const serializedErr = { name: err.name, message: err.message, stack: err.stack }; + const serializedErr: any = { name: err.name, message: err.message, stack: err.stack }; if (err instanceof MoneroError) serializedErr.type = "MoneroError"; return serializedErr; } - static deserializeError(serializedErr) { + protected static deserializeError(serializedErr) { const err = serializedErr.type === "MoneroError" ? new MoneroError(serializedErr.message) : new Error(serializedErr.message); err.name = serializedErr.name; - err.stack = serializedErr.stack; + err.stack = err.stack + "\nWorker error: " + serializedErr.stack; return err; } // ------------------------------ PRIVATE HELPERS --------------------------- - static _initWasmModule(wasmModule) { + protected static initWasmModule(wasmModule) { wasmModule.taskQueue = new ThreadPool(1); wasmModule.queueTask = async function(asyncFn) { return wasmModule.taskQueue.submit(asyncFn); } } - static _prefixWindowsPath(path) { + protected static prefixWindowsPath(path) { if (/^[A-Z]:/.test(path) && path.indexOf("file://") == -1) path = "file://" + path; // prepend e.g. C: paths with file:// return path; } } - -LibraryUtils.LOG_LEVEL = 0; -LibraryUtils.WORKER_DIST_PATH_DEFAULT = GenUtils.isBrowser() ? "/monero_web_worker.js" : function() { - const path = require("path"); - return LibraryUtils._prefixWindowsPath(path.join(__dirname, "./MoneroWebWorker.js")); -}(); -LibraryUtils.WORKER_DIST_PATH = LibraryUtils.WORKER_DIST_PATH_DEFAULT; - -module.exports = LibraryUtils; \ No newline at end of file diff --git a/src/main/js/common/MoneroConnectionManager.js b/src/main/ts/common/MoneroConnectionManager.ts similarity index 57% rename from src/main/js/common/MoneroConnectionManager.js rename to src/main/ts/common/MoneroConnectionManager.ts index 75f6f5071..1fc3212a4 100644 --- a/src/main/js/common/MoneroConnectionManager.js +++ b/src/main/ts/common/MoneroConnectionManager.ts @@ -1,8 +1,9 @@ -const GenUtils = require("./GenUtils"); -const MoneroError = require("./MoneroError"); -const MoneroRpcConnection = require("./MoneroRpcConnection"); -const TaskLooper = require("./TaskLooper"); -const ThreadPool = require("./ThreadPool"); +import GenUtils from "./GenUtils"; +import TaskLooper from "./TaskLooper"; +import ThreadPool from "./ThreadPool"; +import MoneroConnectionManagerListener from "./MoneroConnectionManagerListener"; +import MoneroError from "./MoneroError"; +import MoneroRpcConnection from "./MoneroRpcConnection"; /** *

Manages a collection of prioritized connections to daemon or wallet RPC endpoints.

@@ -11,10 +12,9 @@ const ThreadPool = require("./ThreadPool"); * * * // imports
- * const monerojs = require("monero-javascript");
- * const MoneroRpcConnection = monerojs.MoneroRpcConnection;
- * const MoneroConnectionManager = monerojs.MoneroConnectionManager;
- * const MoneroConnectionManagerListener = monerojs.MoneroConnectionManagerListener;

+ * * const MoneroRpcConnection = MoneroRpcConnection;
+ * const MoneroConnectionManager = MoneroConnectionManager;
+ * const MoneroConnectionManagerListener = MoneroConnectionManagerListener;

* * // create connection manager
* let connectionManager = new MoneroConnectionManager();

@@ -29,8 +29,8 @@ const ThreadPool = require("./ThreadPool"); * // check connection status
* await connectionManager.checkConnection();
* console.log("Connection manager is connected: " + connectionManager.isConnected());
- * console.log("Connection is online: " + connectionManager.getConnection().isOnline());
- * console.log("Connection is authenticated: " + connectionManager.getConnection().isAuthenticated());

+ * console.log("Connection is online: " + connectionManager.getConnection().getIsOnline());
+ * console.log("Connection is authenticated: " + connectionManager.getConnection().getIsAuthenticated());

* * // receive notifications of any changes to current connection
* connectionManager.addListener(new class extends MoneroConnectionManagerListener {
@@ -58,7 +58,23 @@ const ThreadPool = require("./ThreadPool"); * connectionManager.clear(); * */ -class MoneroConnectionManager { +export default class MoneroConnectionManager { + + // static variables + static DEFAULT_TIMEOUT = 5000; + static DEFAULT_POLL_PERIOD = 20000; + static DEFAULT_AUTO_SWITCH = true; + static MIN_BETTER_RESPONSES = 3; + + // instance variables + protected proxyToWorker: any; + protected timeoutMs: any; + protected autoSwitch: any; + protected connections: any; + protected responseTimes: any; + protected listeners: any; + protected currentConnection: any; + protected poller: any; /** * Specify behavior when polling. @@ -74,15 +90,15 @@ class MoneroConnectionManager { /** * Construct a connection manager. * - * @param {boolean} proxyToWorker - configure all connections to proxy to worker (default true) + * @param {boolean} [proxyToWorker] - configure all connections to proxy to worker (default true) */ - constructor(proxyToWorker) { - this._proxyToWorker = proxyToWorker !== false; - this._timeoutMs = MoneroConnectionManager.DEFAULT_TIMEOUT; - this._autoSwitch = MoneroConnectionManager.DEFAULT_AUTO_SWITCH; - this._connections = []; - this._responseTimes = new Map(); - this._listeners = []; + constructor(proxyToWorker = true) { + this.proxyToWorker = proxyToWorker !== false; + this.timeoutMs = MoneroConnectionManager.DEFAULT_TIMEOUT; + this.autoSwitch = MoneroConnectionManager.DEFAULT_AUTO_SWITCH; + this.connections = []; + this.responseTimes = new Map(); + this.listeners = []; } /** @@ -91,8 +107,8 @@ class MoneroConnectionManager { * @param {MoneroConnectionManagerListener} listener - the listener to add * @return {MoneroConnectionManager} this connection manager for chaining */ - addListener(listener) { - this._listeners.push(listener); + addListener(listener: MoneroConnectionManagerListener): MoneroConnectionManager { + this.listeners.push(listener); return this; } @@ -102,8 +118,8 @@ class MoneroConnectionManager { * @param {MoneroConnectionManagerListener} listener - the listener to remove * @return {MoneroConnectionManager} this connection manager for chaining */ - removeListener(listener) { - if (!GenUtils.remove(this._listeners, listener)) throw new MoneroError("Monero connection manager does not contain listener to remove"); + removeListener(listener: MoneroConnectionManagerListener): MoneroConnectionManager { + if (!GenUtils.remove(this.listeners, listener)) throw new MoneroError("Monero connection manager does not contain listener to remove"); return this; } @@ -112,8 +128,8 @@ class MoneroConnectionManager { * * @return {MoneroConnectionManager} this connection manager for chaining */ - removeListeners() { - this._listeners.splice(0, this._listeners.length); + removeListeners(): MoneroConnectionManager { + this.listeners.splice(0, this.listeners.length); return this; } @@ -122,23 +138,23 @@ class MoneroConnectionManager { * * @return {MoneroConnectionManagerListener[]} all listeners */ - getListeners() { - return this._listeners + getListeners(): MoneroConnectionManagerListener[] { + return this.listeners } /** * Add a connection. The connection may have an elevated priority for this manager to use. * - * @param {string|MoneroRpcConnection} uriOrConnection - uri or connection to add + * @param {string|Partial} uriOrConnection - uri or connection to add * @return {Promise} this connection manager for chaining */ - async addConnection(uriOrConnection) { - let connection = typeof uriOrConnection === "string" ? new MoneroRpcConnection(uriOrConnection) : uriOrConnection; - for (let aConnection of this._connections) { + async addConnection(uriOrConnection: string | Partial): Promise { + let connection = uriOrConnection instanceof MoneroRpcConnection ? uriOrConnection : new MoneroRpcConnection(uriOrConnection); + for (let aConnection of this.connections) { if (aConnection.getUri() === connection.getUri()) throw new MoneroError("Connection URI already exists"); } - if (this._proxyToWorker !== undefined) connection.setProxyToWorker(this._proxyToWorker); - this._connections.push(connection); + if (this.proxyToWorker !== undefined) connection.setProxyToWorker(this.proxyToWorker); + this.connections.push(connection); return this; } @@ -148,14 +164,14 @@ class MoneroConnectionManager { * @param {string} uri - of the the connection to remove * @return {Promise} this connection manager for chaining */ - async removeConnection(uri) { + async removeConnection(uri: string): Promise { let connection = this.getConnectionByUri(uri); if (!connection) throw new MoneroError("No connection exists with URI: " + uri); - GenUtils.remove(this._connections, connection); - this._responseTimes.delete(connection.getUri()); - if (connection === this._currentConnection) { - this._currentConnection = undefined; - await this._onConnectionChanged(this._currentConnection); + GenUtils.remove(this.connections, connection); + this.responseTimes.delete(connection.getUri()); + if (connection === this.currentConnection) { + this.currentConnection = undefined; + await this.onConnectionChanged(this.currentConnection); } return this; } @@ -167,10 +183,10 @@ class MoneroConnectionManager { * Notify if current connection changes. * Does not check the connection. * - * @param {string|MoneroRpcConnection} uriOrConnection - is the uri of the connection or the connection to make current (default undefined for no current connection) + * @param {string|Partial} [uriOrConnection] - is the uri of the connection or the connection to make current (default undefined for no current connection) * @return {Promise} this connection manager for chaining */ - async setConnection(uriOrConnection) { + async setConnection(uriOrConnection?: string | Partial): Promise { // handle uri if (uriOrConnection && typeof uriOrConnection === "string") { @@ -180,25 +196,25 @@ class MoneroConnectionManager { // handle connection let connection = uriOrConnection; - if (this._currentConnection === connection) return this; + if (this.currentConnection === connection) return this; // check if setting undefined connection if (!connection) { - this._currentConnection = undefined; - await this._onConnectionChanged(undefined); + this.currentConnection = undefined; + await this.onConnectionChanged(undefined); return this; } // validate connection - if (!(connection instanceof MoneroRpcConnection)) throw new MoneroError("Must provide string or MoneroRpcConnection to set connection"); + if (!(connection instanceof MoneroRpcConnection)) connection = new MoneroRpcConnection(connection); if (!connection.getUri()) throw new MoneroError("Connection is missing URI"); // add or replace connection let prevConnection = this.getConnectionByUri(connection.getUri()); - if (prevConnection) GenUtils.remove(this._connections, prevConnection); + if (prevConnection) GenUtils.remove(this.connections, prevConnection); await this.addConnection(connection); - this._currentConnection = connection; - await this._onConnectionChanged(this._currentConnection); + this.currentConnection = connection; + await this.onConnectionChanged(this.currentConnection); return this; } @@ -208,8 +224,8 @@ class MoneroConnectionManager { * * @return {MoneroRpcConnection} the current connection or undefined if no connection set */ - getConnection() { - return this._currentConnection; + getConnection(): MoneroRpcConnection { + return this.currentConnection; } /** @@ -218,7 +234,7 @@ class MoneroConnectionManager { * @param {string} uri URI of the connection to check * @return {boolean} true if this manager has a connection with the given URI, false otherwise */ - hasConnection(uri) { + hasConnection(uri: string): boolean { return this.getConnectionByUri(uri) !== undefined; } @@ -228,8 +244,8 @@ class MoneroConnectionManager { * @param {string} uri is the URI of the connection to get * @return {MoneroRpcConnection} the connection with the URI or undefined if no connection with the URI exists */ - getConnectionByUri(uri) { - for (let connection of this._connections) if (connection.getUri() === uri) return connection; + getConnectionByUri(uri: string): MoneroRpcConnection { + for (let connection of this.connections) if (connection.getUri() === uri) return connection; return undefined; } @@ -238,9 +254,9 @@ class MoneroConnectionManager { * * @return {MoneroRpcConnection[]} the list of sorted connections */ - getConnections() { - let sortedConnections = GenUtils.copyArray(this._connections); - sortedConnections.sort(this._compareConnections.bind(this)); + getConnections(): MoneroRpcConnection[] { + let sortedConnections = GenUtils.copyArray(this.connections); + sortedConnections.sort(this.compareConnections.bind(this)); return sortedConnections; } @@ -249,22 +265,22 @@ class MoneroConnectionManager { * * @return {boolean|undefined} true if the current connection is set, online, and not unauthenticated, undefined if unknown, false otherwise */ - isConnected() { - if (!this._currentConnection) return false; - return this._currentConnection.isConnected(); + isConnected(): boolean | undefined { + if (!this.currentConnection) return false; + return this.currentConnection.isConnected(); } /** * Start polling connections. * - * @param {number} periodMs poll period in milliseconds (default 20s) - * @param {boolean} autoSwitch specifies to automatically switch to the best connection (default true unless changed) - * @param {number} timeoutMs specifies the timeout to poll a single connection (default 5s unless changed) - * @param {number} pollType one of PRIORITIZED (poll connections in order of priority until connected; default), CURRENT (poll current connection), or ALL (poll all connections) - * @param {MoneroRpcConnection[]} excludedConnections connections excluded from being polled + * @param {number} [periodMs] poll period in milliseconds (default 20s) + * @param {boolean} [autoSwitch] specifies to automatically switch to the best connection (default true unless changed) + * @param {number} [timeoutMs] specifies the timeout to poll a single connection (default 5s unless changed) + * @param {number} [pollType] one of PRIORITIZED (poll connections in order of priority until connected; default), CURRENT (poll current connection), or ALL (poll all connections) + * @param {MoneroRpcConnection[]} [excludedConnections] connections excluded from being polled * @return {MoneroConnectionManager} this connection manager for chaining */ - startPolling(periodMs, autoSwitch, timeoutMs, pollType, excludedConnections) { + startPolling(periodMs?: number, autoSwitch?: boolean, timeoutMs?: number, pollType?: number, excludedConnections?: MoneroRpcConnection[]): MoneroConnectionManager { // apply defaults if (periodMs == undefined) periodMs = MoneroConnectionManager.DEFAULT_POLL_PERIOD; @@ -278,14 +294,14 @@ class MoneroConnectionManager { // start polling switch (pollType) { case MoneroConnectionManager.PollType.CURRENT: - this._startPollingConnection(periodMs); + this.startPollingConnection(periodMs); break; case MoneroConnectionManager.PollType.ALL: - this._startPollingConnections(periodMs); + this.startPollingConnections(periodMs); break; case MoneroConnectionManager.PollType.PRIORITIZED: default: - this._startPollingPrioritizedConnections(periodMs, excludedConnections); + this.startPollingPrioritizedConnections(periodMs, excludedConnections); } return this; } @@ -295,9 +311,9 @@ class MoneroConnectionManager { * * @return {MoneroConnectionManager} this connection manager for chaining */ - stopPolling() { - if (this._poller) this._poller.stop(); - this._poller = undefined; + stopPolling(): MoneroConnectionManager { + if (this.poller) this.poller.stop(); + this.poller = undefined; return this; } @@ -306,21 +322,21 @@ class MoneroConnectionManager { * * @return {Promise} this connection manager for chaining */ - async checkConnection() { + async checkConnection(): Promise { let connectionChanged = false; let connection = this.getConnection(); if (connection) { - if (await connection.checkConnection(this._timeoutMs)) connectionChanged = true; - await this._processResponses([connection]); + if (await connection.checkConnection(this.timeoutMs)) connectionChanged = true; + await this.processResponses([connection]); } - if (this._autoSwitch && !this.isConnected()) { + if (this.autoSwitch && !this.isConnected()) { let bestConnection = await this.getBestAvailableConnection([connection]); if (bestConnection) { await this.setConnection(bestConnection); return this; } } - if (connectionChanged) await this._onConnectionChanged(connection); + if (connectionChanged) await this.onConnectionChanged(connection); return this; } @@ -329,8 +345,9 @@ class MoneroConnectionManager { * * @return {Promise} this connection manager for chaining */ - async checkConnections() { - return this._checkConnections(this.getConnections()); + async checkConnections(): Promise { + await this.checkConnectionsAux(this.getConnections()); + return this; } /** @@ -339,14 +356,13 @@ class MoneroConnectionManager { * * @return {Promise[]} a promise for each connection in the order of getConnections(). */ - checkConnectionPromises() { - let that = this; + checkConnectionPromises(): Promise[] { let checkPromises = []; - let pool = new ThreadPool(this._connections.length); + let pool = new ThreadPool(this.connections.length); for (let connection of this.getConnections()) { - checkPromises.push(pool.submit(async function() { + checkPromises.push(pool.submit(async () => { try { - if (await connection.checkConnection(that._timeoutMs) && connection === this._currentConnection) await that._onConnectionChanged(connection); + if (await connection.checkConnection(this.timeoutMs) && connection === this.currentConnection) await this.onConnectionChanged(connection); } catch (err) { // ignore error } @@ -359,13 +375,13 @@ class MoneroConnectionManager { /** * Get the best available connection in order of priority then response time. * - * @param {MoneroRpcConnection[]} excludedConnections - connections to be excluded from consideration (optional) + * @param {MoneroRpcConnection[]} [excludedConnections] - connections to be excluded from consideration (optional) * @return {Promise} the best available connection in order of priority then response time, undefined if no connections available */ - async getBestAvailableConnection(excludedConnections) { + async getBestAvailableConnection(excludedConnections?: MoneroRpcConnection[]): Promise { // try connections within each ascending priority - for (let prioritizedConnections of this._getConnectionsInAscendingPriority()) { + for (let prioritizedConnections of this.getConnectionsInAscendingPriority()) { try { // create promises to check connections @@ -374,7 +390,7 @@ class MoneroConnectionManager { for (let connection of prioritizedConnections) { if (excludedConnections && GenUtils.arrayContains(excludedConnections, connection)) continue; checkPromises.push(new Promise(async function(resolve, reject) { - await connection.checkConnection(that._timeoutMs); + await connection.checkConnection(that.timeoutMs); if (connection.isConnected()) resolve(connection); else reject(); })); @@ -396,8 +412,8 @@ class MoneroConnectionManager { * @param {boolean} autoSwitch specifies if the connection should auto switch to a better connection * @return {MoneroConnectionManager} this connection manager for chaining */ - setAutoSwitch(autoSwitch) { - this._autoSwitch = autoSwitch; + setAutoSwitch(autoSwitch: boolean): MoneroConnectionManager { + this.autoSwitch = autoSwitch; return this; } @@ -406,28 +422,28 @@ class MoneroConnectionManager { * * @return {boolean} true if auto switch enabled, false otherwise */ - getAutoSwitch() { - return this._autoSwitch; + getAutoSwitch(): boolean { + return this.autoSwitch; } /** * Set the maximum request time before its connection is considered offline. * - * @param {int} timeoutMs - the timeout before the connection is considered offline + * @param {number} timeoutMs - the timeout before the connection is considered offline * @return {MoneroConnectionManager} this connection manager for chaining */ - setTimeout(timeoutMs) { - this._timeoutMs = timeoutMs; + setTimeout(timeoutMs: number): MoneroConnectionManager { + this.timeoutMs = timeoutMs; return this; } /** * Get the request timeout. * - * @return {int} the request timeout before a connection is considered offline + * @return {number} the request timeout before a connection is considered offline */ - getTimeout() { - return this._timeoutMs; + getTimeout(): number { + return this.timeoutMs; } /** @@ -435,7 +451,7 @@ class MoneroConnectionManager { * * @return {Promise} connectable peers */ - async getPeerConnections() { + async getPeerConnections(): Promise { throw new MoneroError("Not implemented"); } @@ -444,7 +460,7 @@ class MoneroConnectionManager { * * @return {Promise} this connection manager for chaining */ - async disconnect() { + async disconnect(): Promise { await this.setConnection(undefined); return this; } @@ -452,13 +468,13 @@ class MoneroConnectionManager { /** * Remove all connections. * - * @return {Promise} this connection manager for chaining + * @return {Promise} this connection manager for chaining */ - async clear() { - this._connections.splice(0, this._connections.length); - if (this._currentConnection) { - this._currentConnection = undefined; - await this._onConnectionChanged(undefined); + async clear(): Promise { + this.connections.splice(0, this.connections.length); + if (this.currentConnection) { + this.currentConnection = undefined; + await this.onConnectionChanged(undefined); } return this; } @@ -466,28 +482,28 @@ class MoneroConnectionManager { /** * Reset to default state. * - * @return {MoneroConnectonManager} this connection manager for chaining + * @return {MoneroConnectionManager} this connection manager for chaining */ - reset() { + reset(): MoneroConnectionManager { this.removeListeners(); this.stopPolling(); this.clear(); - this._timeoutMs = MoneroConnectionManager.DEFAULT_TIMEOUT; - this._autoSwitch = MoneroConnectionManager.DEFAULT_AUTOSWITCH; + this.timeoutMs = MoneroConnectionManager.DEFAULT_TIMEOUT; + this.autoSwitch = MoneroConnectionManager.DEFAULT_AUTO_SWITCH; return this; } // ------------------------------ PRIVATE HELPERS --------------------------- - async _onConnectionChanged(connection) { + protected async onConnectionChanged(connection) { let promises = []; - for (let listener of this._listeners) promises.push(listener.onConnectionChanged(connection)); + for (let listener of this.listeners) promises.push(listener.onConnectionChanged(connection)); return Promise.all(promises); } - _getConnectionsInAscendingPriority() { + protected getConnectionsInAscendingPriority() { let connectionPriorities = new Map(); - for (let connection of this._connections) { + for (let connection of this.connections) { if (!connectionPriorities.has(connection.getPriority())) connectionPriorities.set(connection.getPriority(), []); connectionPriorities.get(connection.getPriority()).push(connection); } @@ -498,59 +514,59 @@ class MoneroConnectionManager { return ascendingPrioritiesList; } - _compareConnections(c1, c2) { + protected compareConnections(c1, c2) { // current connection is first - if (c1 === this._currentConnection) return -1; - if (c2 === this._currentConnection) return 1; + if (c1 === this.currentConnection) return -1; + if (c2 === this.currentConnection) return 1; // order by availability then priority then by name - if (c1.isOnline() === c2.isOnline()) { + if (c1.getIsOnline() === c2.getIsOnline()) { if (c1.getPriority() === c2.getPriority()) return c1.getUri().localeCompare(c2.getUri()); else return c1.getPriority() == 0 ? 1 : c2.getPriority() == 0 ? -1 : c1.getPriority() - c2.getPriority(); } else { - if (c1.isOnline()) return -1; - else if (c2.isOnline()) return 1; - else if (c1.isOnline() === undefined) return -1; + if (c1.getIsOnline()) return -1; + else if (c2.getIsOnline()) return 1; + else if (c1.getIsOnline() === undefined) return -1; else return 1; // c1 is offline } } - _startPollingConnection(periodMs) { - this._poller = new TaskLooper(async () => { + protected startPollingConnection(periodMs) { + this.poller = new TaskLooper(async () => { try { await this.checkConnection(); } - catch (err) { console.error("Error checking connection: " + err.message); } + catch (err) { console.log(err); } }); - this._poller.start(periodMs); + this.poller.start(periodMs); return this; } - _startPollingConnections(periodMs) { - this._poller = new TaskLooper(async () => { + protected startPollingConnections(periodMs) { + this.poller = new TaskLooper(async () => { try { await this.checkConnections(); } - catch (err) { console.error("Error checking connections: " + err.message); } + catch (err) { console.log(err); } }); - this._poller.start(periodMs); + this.poller.start(periodMs); return this; } - _startPollingPrioritizedConnections(periodMs, excludedConnections) { - this._poller = new TaskLooper(async () => { - try { await this._checkPrioritizedConnections(excludedConnections); } - catch (err) { console.error("Error checking connections: " + err.message); } + protected startPollingPrioritizedConnections(periodMs, excludedConnections) { + this.poller = new TaskLooper(async () => { + try { await this.checkPrioritizedConnections(excludedConnections); } + catch (err) { console.log(err); } }); - this._poller.start(periodMs); + this.poller.start(periodMs); return this; } - async _checkPrioritizedConnections(excludedConnections) { - for (let prioritizedConnections of this._getConnectionsInAscendingPriority()) { - let hasConnection = await this._checkConnections(prioritizedConnections, excludedConnections); + async checkPrioritizedConnections(excludedConnections) { + for (let prioritizedConnections of this.getConnectionsInAscendingPriority()) { + let hasConnection = await this.checkConnectionsAux(prioritizedConnections, excludedConnections); if (hasConnection) return; } } - async _checkConnections(connections, excludedConnections) { + protected async checkConnectionsAux(connections, excludedConnections?) { try { // check connections in parallel @@ -561,13 +577,13 @@ class MoneroConnectionManager { if (excludedConnections && GenUtils.arrayContains(excludedConnections, connection)) continue; checkPromises.push(new Promise(async function(resolve, reject) { try { - let change = await connection.checkConnection(that._timeoutMs); - if (change && connection === that.getConnection()) await that._onConnectionChanged(connection); + let change = await connection.checkConnection(that.timeoutMs); + if (change && connection === that.getConnection()) await that.onConnectionChanged(connection); if (connection.isConnected() && !hasConnection) { hasConnection = true; - if (!that.isConnected() && that._autoSwitch) await that.setConnection(connection); // set first available connection if disconnected + if (!that.isConnected() && that.autoSwitch) await that.setConnection(connection); // set first available connection if disconnected } - resolve(); + resolve(undefined); } catch (err) { reject(err); } @@ -576,22 +592,22 @@ class MoneroConnectionManager { await Promise.all(checkPromises); // process responses - await this._processResponses(connections); + await this.processResponses(connections); return hasConnection; } catch (err) { throw new MoneroError(err); } } - async _processResponses(responses) { + protected async processResponses(responses) { // add non-existing connections for (let connection of responses) { - if (!this._responseTimes.has(connection.getUri())) this._responseTimes.set(connection.getUri(), []); + if (!this.responseTimes.has(connection.getUri())) this.responseTimes.set(connection.getUri(), []); } // insert response times or undefined - this._responseTimes.forEach((times, connection) => { + this.responseTimes.forEach((times, connection) => { times.unshift(GenUtils.arrayContains(responses, connection) ? connection.getResponseTime() : undefined); // remove old response times @@ -599,28 +615,28 @@ class MoneroConnectionManager { }); // update best connection based on responses and priority - await this._updateBestConnectionInPriority(); + await this.updateBestConnectionInPriority(); } - async _updateBestConnectionInPriority() { - if (!this._autoSwitch) return; - for (let prioritizedConnections of this._getConnectionsInAscendingPriority()) { - if (await this._updateBestConnectionFromResponses(prioritizedConnections)) break; + protected async updateBestConnectionInPriority() { + if (!this.autoSwitch) return; + for (let prioritizedConnections of this.getConnectionsInAscendingPriority()) { + if (await this.updateBestConnectionFromResponses(prioritizedConnections)) break; } } - async _updateBestConnectionFromResponses(responses) { + protected async updateBestConnectionFromResponses(responses) { let bestConnection = this.isConnected() ? this.getConnection() : undefined; - if (bestConnection && (this._responseTimes.has(bestConnection.getUri()) || this._responseTimes.get(bestConnection.getUri()).length < MoneroConnectionManager.MIN_BETTER_RESPONSES)) return bestConnection; + if (bestConnection && (!this.responseTimes.has(bestConnection.getUri()) || this.responseTimes.get(bestConnection.getUri()).length < MoneroConnectionManager.MIN_BETTER_RESPONSES)) return bestConnection; if (this.isConnected()) { // check if connection is consistently better for (let connection of responses) { if (connection === bestConnection) continue; - if (!this._responseTimes.has(connection.getUri()) || this._responseTimes.get(connection.getUri()).length < MIN_BETTER_RESPONSES) continue; + if (!this.responseTimes.has(connection.getUri()) || this.responseTimes.get(connection.getUri()).length < MoneroConnectionManager.MIN_BETTER_RESPONSES) continue; let better = true; - for (let i = 0; i < MIN_BETTER_RESPONSES; i++) { - if (this._responseTimes.get(connection.getUri())[i] === undefined || this._responseTimes.get(connection.getUri())[i] >= this._responseTimes.get(bestConnection.getUri())[i]) { + for (let i = 0; i < MoneroConnectionManager.MIN_BETTER_RESPONSES; i++) { + if (this.responseTimes.get(connection.getUri())[i] === undefined || this.responseTimes.get(connection.getUri())[i] >= this.responseTimes.get(bestConnection.getUri())[i]) { better = false; break; } @@ -636,10 +652,3 @@ class MoneroConnectionManager { return bestConnection; } } - -MoneroConnectionManager.DEFAULT_TIMEOUT = 5000; -MoneroConnectionManager.DEFAULT_POLL_PERIOD = 20000; -MoneroConnectionManager.DEFAULT_AUTO_SWITCH = true; -MoneroConnectionManager.MIN_BETTER_RESPONSES = 3; - -module.exports = MoneroConnectionManager; diff --git a/src/main/ts/common/MoneroConnectionManagerListener.ts b/src/main/ts/common/MoneroConnectionManagerListener.ts new file mode 100644 index 000000000..11075d170 --- /dev/null +++ b/src/main/ts/common/MoneroConnectionManagerListener.ts @@ -0,0 +1,15 @@ +import MoneroRpcConnection from "./MoneroRpcConnection"; + +/** + * Default connection manager listener which takes no action on notifications. + */ +export default class MoneroConnectionManagerListener { + + /** + * Notified on connection change events. + * + * @param {MoneroRpcConnection | undefined} connection - the connection manager's current connection + * @return {Promise} + */ + async onConnectionChanged(connection: MoneroRpcConnection | undefined): Promise { } +} diff --git a/src/main/js/common/MoneroError.js b/src/main/ts/common/MoneroError.ts similarity index 77% rename from src/main/js/common/MoneroError.js rename to src/main/ts/common/MoneroError.ts index 4e5d5a0d7..445f5a7ee 100644 --- a/src/main/js/common/MoneroError.js +++ b/src/main/ts/common/MoneroError.ts @@ -1,15 +1,17 @@ /** * Exception when interacting with a Monero wallet or daemon. */ -class MoneroError extends Error { +export default class MoneroError extends Error { + + code: number; /** * Constructs the error. * * @param {string} message is a human-readable message of the error - * @param {int} code is the error code (optional) + * @param {number} [code] is the error code (optional) */ - constructor(message, code) { + constructor(message, code?) { super(message); this.code = code; } @@ -26,5 +28,3 @@ class MoneroError extends Error { return str; } } - -module.exports = MoneroError; \ No newline at end of file diff --git a/src/main/js/common/MoneroRpcConnection.js b/src/main/ts/common/MoneroRpcConnection.ts similarity index 55% rename from src/main/js/common/MoneroRpcConnection.js rename to src/main/ts/common/MoneroRpcConnection.ts index 395b0430f..d3d2885ba 100644 --- a/src/main/js/common/MoneroRpcConnection.js +++ b/src/main/ts/common/MoneroRpcConnection.ts @@ -1,15 +1,41 @@ -const GenUtils = require("./GenUtils"); -const HttpClient = require("./HttpClient"); -const LibraryUtils = require("./LibraryUtils"); -const MoneroError = require("../common/MoneroError"); -const MoneroRpcError = require("../common/MoneroRpcError"); -const MoneroUtils = require("./MoneroUtils"); +import GenUtils from "./GenUtils"; +import HttpClient from "./HttpClient"; +import LibraryUtils from "./LibraryUtils"; +import MoneroError from "./MoneroError"; +import MoneroRpcError from "./MoneroRpcError"; +import MoneroUtils from "./MoneroUtils"; /** * Maintains a connection and sends requests to a Monero RPC API. */ -class MoneroRpcConnection { - +export default class MoneroRpcConnection { + + // public instance variables + uri: string; + username: string; + password: string; + rejectUnauthorized: boolean; + proxyToWorker: boolean; + priority: number; + + // private instance variables + protected isOnline: boolean; + protected isAuthenticated: boolean; + protected attributes: any; + protected fakeDisconnected: boolean; + protected responseTime: number; + + // default config + /** @private */ + static DEFAULT_CONFIG: Partial = { + uri: undefined, + username: undefined, + password: undefined, + rejectUnauthorized: true, // reject self-signed certificates if true + proxyToWorker: false, + priority: 0 + } + /** *

Construct a RPC connection.

* @@ -27,48 +53,30 @@ class MoneroRpcConnection { * }); *
* - * @param {string|object|MoneroRpcConnection} uriOrConfigOrConnection - RPC endpoint URI, MoneroRpcConnection, or equivalent JS object - * @param {string} uriOrConfigOrConnection.uri - URI of the RPC endpoint - * @param {string} uriOrConfigOrConnection.username - username to authenticate with the RPC endpoint (optional) - * @param {string} uriOrConfigOrConnection.password - password to authenticate with the RPC endpoint (optional) - * @param {boolean} uriOrConfigOrConnection.rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {boolean} uriOrConfigOrConnection.proxyToWorker - proxy requests to worker + * @param {string|Partial} uriOrConnection - MoneroRpcConnection or URI of the RPC endpoint + * @param {string} uriOrConnection.uri - URI of the RPC endpoint + * @param {string} [uriOrConnection.username] - username to authenticate with the RPC endpoint (optional) + * @param {string} [uriOrConnection.password] - password to authenticate with the RPC endpoint (optional) + * @param {boolean} [uriOrConnection.rejectUnauthorized] - rejects self-signed certificates if true (default true) + * @param {boolean} uriOrConnection.proxyToWorker - proxy requests to worker (default true) * @param {string} username - username to authenticate with the RPC endpoint (optional) * @param {string} password - password to authenticate with the RPC endpoint (optional) - * @param {boolean} rejectUnauthorized - reject self-signed certificates if true (default true) */ - constructor(uriOrConfigOrConnection, username, password, rejectUnauthorized, proxyToWorker) { - + constructor(uriOrConnection: string | Partial, username?: string, password?: string) { + // validate and normalize config - if (typeof uriOrConfigOrConnection === "string") { - this._config = {uri: uriOrConfigOrConnection}; + if (typeof uriOrConnection === "string") { + Object.assign(this, MoneroRpcConnection.DEFAULT_CONFIG); + this.uri = uriOrConnection; this.setCredentials(username, password); - if (rejectUnauthorized !== undefined) this._config.rejectUnauthorized = rejectUnauthorized; - if (proxyToWorker !== undefined) this._config.proxyToWorker = proxyToWorker; - } else if (typeof uriOrConfigOrConnection === "object") { - if (username !== undefined || password !== undefined || rejectUnauthorized !== undefined || proxyToWorker !== undefined) throw new MoneroError("Can provide config object or params but not both"); - if (uriOrConfigOrConnection instanceof MoneroRpcConnection) this._config = Object.assign({}, uriOrConfigOrConnection.getConfig()); - else this._config = Object.assign({}, uriOrConfigOrConnection); - this.setCredentials(this._config.username, this._config.password); - } else if (uriOrConfigOrConnection !== undefined) { - throw new MoneroError("Invalid configuration to MoneroRpcConnection; must be string or MoneroRpcConnection or equivalent JS object"); + } else { + if (username !== undefined || password !== undefined) throw new MoneroError("Can provide config object or params but not both"); + Object.assign(this, MoneroRpcConnection.DEFAULT_CONFIG, uriOrConnection); + this.setCredentials(this.username, this.password); } - // merge default config - this._config = Object.assign({}, MoneroRpcConnection.DEFAULT_CONFIG, this._config); - // normalize uri - if (this._config.uri) this._config.uri = GenUtils.normalizeUri(this._config.uri); - - // fail with friendly message if using old api - if (this._config.user || this._config.pass) throw new MoneroError("Authentication fields 'user' and 'pass' have been renamed to 'username' and 'password'. Please update to the new api"); - - // check for unsupported fields - for (let key of Object.keys(this._config)) { - if (!GenUtils.arrayContains(MoneroRpcConnection.SUPPORTED_FIELDS, key)) { - throw new MoneroError("RPC connection includes unsupported field: '" + key + "'"); - } - } + if (this.uri) this.uri = GenUtils.normalizeUri(this.uri); } setCredentials(username, password) { @@ -78,60 +86,56 @@ class MoneroRpcConnection { if (!username) throw new MoneroError("username must be defined because password is defined"); if (!password) throw new MoneroError("password must be defined because username is defined"); } - if (this._config.username === "") this._config.username = undefined; - if (this._config.password === "") this._config.password = undefined; - if (this._config.username !== username || this._config.password !== password) { - this._isOnline = undefined; - this._isAuthenticated = undefined; + if (this.username === "") this.username = undefined; + if (this.password === "") this.password = undefined; + if (this.username !== username || this.password !== password) { + this.isOnline = undefined; + this.isAuthenticated = undefined; } - this._config.username = username; - this._config.password = password; + this.username = username; + this.password = password; return this; } getUri() { - return this._config.uri; + return this.uri; } getUsername() { - return this._config.username ? this._config.username : ""; + return this.username ? this.username : ""; } getPassword() { - return this._config.password ? this._config.password : ""; + return this.password ? this.password : ""; } getRejectUnauthorized() { - return this._config.rejectUnauthorized; + return this.rejectUnauthorized; } setProxyToWorker(proxyToWorker) { - this._config.proxyToWorker = proxyToWorker; + this.proxyToWorker = proxyToWorker; return this; } getProxyToWorker() { - return this._config.proxyToWorker; - } - - getConfig() { - return this._config; + return this.proxyToWorker; } getPriority() { - return this._config.priority; + return this.priority; } /** * Set the connection's priority relative to other connections. Priority 1 is highest, * then priority 2, etc. The default priority of 0 is lowest priority. * - * @param {int} priority - the connection priority (default 0) + * @param {number} [priority] - the connection priority (default 0) * @return {MoneroRpcConnection} this connection */ setPriority(priority) { if (!(priority >= 0)) throw new MoneroError("Priority must be >= 0"); - this._config.priority = priority; + this.priority = priority; return this; } @@ -148,37 +152,37 @@ class MoneroRpcConnection { /** * Check the connection status to update isOnline, isAuthenticated, and response time. * - * @param {int} timeoutMs - maximum response time before considered offline + * @param {number} timeoutMs - maximum response time before considered offline * @return {Promise} true if there is a change in status, false otherwise */ async checkConnection(timeoutMs) { await LibraryUtils.loadKeysModule(); // cache wasm for binary request - let isOnlineBefore = this._isOnline; - let isAuthenticatedBefore = this._isAuthenticated; + let isOnlineBefore = this.isOnline; + let isAuthenticatedBefore = this.isAuthenticated; let startTime = Date.now(); try { - if (this._fakeDisconnected) throw new Error("Connection is fake disconnected"); + if (this.fakeDisconnected) throw new Error("Connection is fake disconnected"); let heights = []; for (let i = 0; i < 100; i++) heights.push(i); await this.sendBinaryRequest("get_blocks_by_height.bin", {heights: heights}); // assume daemon connection - this._isOnline = true; - this._isAuthenticated = true; + this.isOnline = true; + this.isAuthenticated = true; } catch (err) { - this._isOnline = false; - this._isAuthenticated = undefined; - this._responseTime = undefined; + this.isOnline = false; + this.isAuthenticated = undefined; + this.responseTime = undefined; if (err instanceof MoneroRpcError) { if (err.getCode() === 401) { - this._isOnline = true; - this._isAuthenticated = false; + this.isOnline = true; + this.isAuthenticated = false; } else if (err.getCode() === 404) { // fallback to latency check - this._isOnline = true; - this._isAuthenticated = true; + this.isOnline = true; + this.isAuthenticated = true; } } } - if (this._isOnline) this._responseTime = Date.now() - startTime; - return isOnlineBefore !== this._isOnline || isAuthenticatedBefore !== this._isAuthenticated; + if (this.isOnline) this.responseTime = Date.now() - startTime; + return isOnlineBefore !== this.isOnline || isAuthenticatedBefore !== this.isAuthenticated; } /** @@ -186,10 +190,10 @@ class MoneroRpcConnection { * * Note: must call checkConnection() manually unless using MoneroConnectionManager. * - * @return {boolean|undefined} true or false to indicate if connected, or undefined if checkConnection() has not been called + * @return {boolean} true or false to indicate if connected, or undefined if checkConnection() has not been called */ isConnected() { - return this._isOnline === undefined ? undefined : this._isOnline && this._isAuthenticated !== false; + return this.isOnline === undefined ? undefined : this.isOnline && this.isAuthenticated !== false; } /** @@ -197,10 +201,10 @@ class MoneroRpcConnection { * * Note: must call checkConnection() manually unless using MoneroConnectionManager. * - * @return {boolean|undefined} true or false to indicate if online, or undefined if checkConnection() has not been called + * @return {boolean} true or false to indicate if online, or undefined if checkConnection() has not been called */ - isOnline() { - return this._isOnline; + getIsOnline() { + return this.isOnline; } /** @@ -208,14 +212,14 @@ class MoneroRpcConnection { * * Note: must call checkConnection() manually unless using MoneroConnectionManager. * - * @return {boolean|undefined} true if authenticated or no authentication, false if not authenticated, or undefined if checkConnection() has not been called + * @return {boolean} true if authenticated or no authentication, false if not authenticated, or undefined if checkConnection() has not been called */ - isAuthenticated() { - return this._isAuthenticated; + getIsAuthenticated() { + return this.isAuthenticated; } getResponseTime() { - return this._responseTime; + return this.responseTime; } /** @@ -223,14 +227,14 @@ class MoneroRpcConnection { * * @param {string} method - JSON RPC method to invoke * @param {object} params - request parameters - * @param {int} timeoutInMs - request timeout in milliseconds + * @param {number} timeoutInMs - request timeout in milliseconds * @return {object} is the response map */ - async sendJsonRequest(method, params, timeoutInMs) { + async sendJsonRequest(method, params?, timeoutInMs?): Promise { try { // build request body - let body = JSON.stringify({ // body is stringified so text/plain is returned so BigIntegers are preserved + let body = JSON.stringify({ // body is stringified so text/plain is returned so bigints are preserved id: "0", jsonrpc: "2.0", method: method, @@ -249,26 +253,26 @@ class MoneroRpcConnection { password: this.getPassword(), body: body, timeout: timeoutInMs, - rejectUnauthorized: this._config.rejectUnauthorized, + rejectUnauthorized: this.rejectUnauthorized, requestApi: GenUtils.isFirefox() ? "xhr" : "fetch", // firefox issue: https://bugzilla.mozilla.org/show_bug.cgi?id=1491010 - proxyToWorker: this._config.proxyToWorker + proxyToWorker: this.proxyToWorker }); // validate response - MoneroRpcConnection._validateHttpResponse(resp); + MoneroRpcConnection.validateHttpResponse(resp); // deserialize response if (resp.body[0] != '{') throw resp.body; resp = JSON.parse(resp.body.replace(/("[^"]*"\s*:\s*)(\d{16,})/g, '$1"$2"')); // replace 16 or more digits with strings and parse if (LibraryUtils.getLogLevel() >= 3) { let respStr = JSON.stringify(resp); - LibraryUtils.log(3, "Received response from method='" + method + "', response=" + respStr.substring(0, Math.min(1000, respStr.length) + "(" + (new Date().getTime() - startTime) + " ms)")); + LibraryUtils.log(3, "Received response from method='" + method + "', response=" + respStr.substring(0, Math.min(1000, respStr.length)) + "(" + (new Date().getTime() - startTime) + " ms)"); } // check rpc response for errors - MoneroRpcConnection._validateRpcResponse(resp, method, params); + MoneroRpcConnection.validateRpcResponse(resp, method, params); return resp; - } catch (err) { + } catch (err: any) { if (err instanceof MoneroRpcError) throw err; else throw new MoneroRpcError(err, err.statusCode, method, params); } @@ -281,10 +285,10 @@ class MoneroRpcConnection { * * @param {string} path - JSON RPC path to invoke * @param {object} params - request parameters - * @param {int} timeoutInMs - request timeout in milliseconds + * @param {number} timeoutInMs - request timeout in milliseconds * @return {object} is the response map */ - async sendPathRequest(path, params, timeoutInMs) { + async sendPathRequest(path, params?, timeoutInMs?): Promise { try { // logging @@ -297,15 +301,15 @@ class MoneroRpcConnection { uri: this.getUri() + '/' + path, username: this.getUsername(), password: this.getPassword(), - body: JSON.stringify(params), // body is stringified so text/plain is returned so BigIntegers are preserved + body: JSON.stringify(params), // body is stringified so text/plain is returned so bigints are preserved timeout: timeoutInMs, - rejectUnauthorized: this._config.rejectUnauthorized, + rejectUnauthorized: this.rejectUnauthorized, requestApi: GenUtils.isFirefox() ? "xhr" : "fetch", - proxyToWorker: this._config.proxyToWorker + proxyToWorker: this.proxyToWorker }); // validate response - MoneroRpcConnection._validateHttpResponse(resp); + MoneroRpcConnection.validateHttpResponse(resp); // deserialize response if (resp.body[0] != '{') throw resp.body; @@ -313,13 +317,13 @@ class MoneroRpcConnection { if (typeof resp === "string") resp = JSON.parse(resp); // TODO: some responses returned as strings? if (LibraryUtils.getLogLevel() >= 3) { let respStr = JSON.stringify(resp); - LibraryUtils.log(3, "Received response from path='" + method + "', response=" + respStr.substring(0, Math.min(1000, respStr.length) + "(" + (new Date().getTime() - startTime) + " ms)")); + LibraryUtils.log(3, "Received response from path='" + path + "', response=" + respStr.substring(0, Math.min(1000, respStr.length)) + "(" + (new Date().getTime() - startTime) + " ms)"); } // check rpc response for errors - MoneroRpcConnection._validateRpcResponse(resp, path, params); + MoneroRpcConnection.validateRpcResponse(resp, path, params); return resp; - } catch (err) { + } catch (err: any) { if (err instanceof MoneroRpcError) throw err; else throw new MoneroRpcError(err, err.statusCode, path, params); } @@ -329,11 +333,11 @@ class MoneroRpcConnection { * Send a binary RPC request. * * @param {string} path - path of the binary RPC method to invoke - * @param {object} params - request parameters - * @param {int} timeoutInMs - request timeout in milliseconds + * @param {object} [params] - request parameters + * @param {number} [timeoutInMs] - request timeout in milliseconds * @return {Uint8Array} the binary response */ - async sendBinaryRequest(path, params, timeoutInMs) { + async sendBinaryRequest(path, params?, timeoutInMs?): Promise { // serialize params let paramsBin = await MoneroUtils.jsonToBinary(params); @@ -351,13 +355,13 @@ class MoneroRpcConnection { password: this.getPassword(), body: paramsBin, timeout: timeoutInMs, - rejectUnauthorized: this._config.rejectUnauthorized, + rejectUnauthorized: this.rejectUnauthorized, requestApi: GenUtils.isFirefox() ? "xhr" : "fetch", - proxyToWorker: this._config.proxyToWorker + proxyToWorker: this.proxyToWorker }); // validate response - MoneroRpcConnection._validateHttpResponse(resp); + MoneroRpcConnection.validateHttpResponse(resp); // process response resp = resp.body; @@ -367,23 +371,38 @@ class MoneroRpcConnection { } if (resp.error) throw new MoneroRpcError(resp.error.message, resp.error.code, path, params); return resp; - } catch (err) { + } catch (err: any) { if (err instanceof MoneroRpcError) throw err; else throw new MoneroRpcError(err, err.statusCode, path, params); } } + getConfig() { + return { + uri: this.uri, + username: this.username, + password: this.password, + rejectUnauthorized: this.rejectUnauthorized, + proxyToWorker: this.proxyToWorker, + priority: this.priority + }; + } + toJson() { - return this._config; + return Object.assign({}, this); } toString() { - return this.getUri() + " (username=" + this.getUsername() + ", password=" + (this.getPassword() ? "***" : this.getPassword()) + ", priority=" + this.getPriority() + ", isOnline=" + this.isOnline() + ", isAuthenticated=" + this.isAuthenticated() + ")"; + return this.getUri() + " (username=" + this.getUsername() + ", password=" + (this.getPassword() ? "***" : this.getPassword()) + ", priority=" + this.getPriority() + ", isOnline=" + this.getIsOnline() + ", isAuthenticated=" + this.getIsAuthenticated() + ")"; + } + + setFakeDisconnected(fakeDisconnected) { // used to test connection manager + this.fakeDisconnected = fakeDisconnected; } // ------------------------------ PRIVATE HELPERS -------------------------- - static _validateHttpResponse(resp) { + protected static validateHttpResponse(resp) { let code = resp.statusCode; if (code < 200 || code > 299) { let content = resp.body; @@ -391,28 +410,8 @@ class MoneroRpcConnection { } } - static _validateRpcResponse(resp, method, params) { + protected static validateRpcResponse(resp, method, params) { if (!resp.error) return; throw new MoneroRpcError(resp.error.message, resp.error.code, method, params); } - - _setFakeDisconnected(fakeDisconnected) { // used to test connection manager - this._fakeDisconnected = fakeDisconnected; - } -} - -/** - * Default RPC configuration. - */ -MoneroRpcConnection.DEFAULT_CONFIG = { - uri: undefined, - username: undefined, - password: undefined, - rejectUnauthorized: true, // reject self-signed certificates if true - proxyToWorker: false, - priority: 0 } - -MoneroRpcConnection.SUPPORTED_FIELDS = ["uri", "username", "password", "rejectUnauthorized", "priority", "proxyToWorker"]; - -module.exports = MoneroRpcConnection; \ No newline at end of file diff --git a/src/main/js/common/MoneroRpcError.js b/src/main/ts/common/MoneroRpcError.ts similarity index 59% rename from src/main/js/common/MoneroRpcError.js rename to src/main/ts/common/MoneroRpcError.ts index df022dca8..a75599976 100644 --- a/src/main/js/common/MoneroRpcError.js +++ b/src/main/ts/common/MoneroRpcError.ts @@ -1,19 +1,23 @@ -const MoneroError = require("./MoneroError"); +import MoneroError from "./MoneroError"; /** * Error when interacting with Monero RPC. */ -class MoneroRpcError extends MoneroError { +export default class MoneroRpcError extends MoneroError { + + // instance variables + protected rpcMethod: any; + protected rpcParams: any; /** * Constructs the error. * * @param {string} rpcDescription is a description of the error from rpc - * @param {int} rpcCode is the error code from rpc - * @param {string} rpcMethod is the rpc method invoked - * @param {object} rpcParams are parameters sent with the rpc request + * @param {number} rpcCode is the error code from rpc + * @param {string} [rpcMethod] is the rpc method invoked + * @param {object} [rpcParams] are parameters sent with the rpc request */ - constructor(rpcDescription, rpcCode, rpcMethod, rpcParams) { + constructor(rpcDescription, rpcCode, rpcMethod?, rpcParams?) { super(rpcDescription, rpcCode); this.rpcMethod = rpcMethod; this.rpcParams = rpcParams; @@ -33,5 +37,3 @@ class MoneroRpcError extends MoneroError { return str; } } - -module.exports = MoneroRpcError; \ No newline at end of file diff --git a/src/main/js/common/MoneroUtils.js b/src/main/ts/common/MoneroUtils.ts similarity index 85% rename from src/main/js/common/MoneroUtils.js rename to src/main/ts/common/MoneroUtils.ts index a83abaea1..f68d47991 100644 --- a/src/main/js/common/MoneroUtils.js +++ b/src/main/ts/common/MoneroUtils.ts @@ -1,22 +1,26 @@ -const assert = require("assert"); -const BigInteger = require("./biginteger").BigInteger; -const GenUtils = require("./GenUtils"); -const LibraryUtils = require("./LibraryUtils"); -const MoneroError = require("./MoneroError"); -const MoneroIntegratedAddress = require("../wallet/model/MoneroIntegratedAddress"); -const MoneroNetworkType = require("../daemon/model/MoneroNetworkType"); +import assert from "assert"; +import GenUtils from "./GenUtils"; +import LibraryUtils from "./LibraryUtils"; +import MoneroError from "./MoneroError"; +import MoneroIntegratedAddress from "../wallet/model/MoneroIntegratedAddress"; +import MoneroNetworkType from "../daemon/model/MoneroNetworkType"; +import ConnectionType from "../daemon/model/ConnectionType"; /** * Collection of Monero utilities. Runs in a worker thread by default. - * - * @hideconstructor */ -class MoneroUtils { - +export default class MoneroUtils { + + // static variables + static PROXY_TO_WORKER = false; + static NUM_MNEMONIC_WORDS = 25; + static AU_PER_XMR = 1000000000000n; + static RING_SIZE = 12; + /** - *

Get the version of the monero-javascript library.

+ *

Get the version of the monero-ts library.

* - * @return {string} the version of this monero-javascript library + * @return {string} the version of this monero-ts library */ static getVersion() { return "0.9.0"; @@ -30,7 +34,7 @@ class MoneroUtils { static setProxyToWorker(proxyToWorker) { MoneroUtils.PROXY_TO_WORKER = proxyToWorker || false; } - + /** * Validate the given mnemonic, throw an error if invalid. * @@ -110,7 +114,7 @@ class MoneroUtils { * @param {string} privateViewKey - private view key to validate */ static async validatePrivateViewKey(privateViewKey) { - if (!MoneroUtils._isHex64(privateViewKey)) throw new MoneroError("private view key expected to be 64 hex characters"); + if (!MoneroUtils.isHex64(privateViewKey)) throw new MoneroError("private view key expected to be 64 hex characters"); } /** @@ -119,7 +123,7 @@ class MoneroUtils { * @param {string} publicViewKey - public view key to validate */ static async validatePublicViewKey(publicViewKey) { - if (!MoneroUtils._isHex64(publicViewKey)) throw new MoneroError("public view key expected to be 64 hex characters"); + if (!MoneroUtils.isHex64(publicViewKey)) throw new MoneroError("public view key expected to be 64 hex characters"); } /** @@ -128,7 +132,7 @@ class MoneroUtils { * @param {string} privateSpendKey - private spend key to validate */ static async validatePrivateSpendKey(privateSpendKey) { - if (!MoneroUtils._isHex64(privateSpendKey)) throw new MoneroError("private spend key expected to be 64 hex characters"); + if (!MoneroUtils.isHex64(privateSpendKey)) throw new MoneroError("private spend key expected to be 64 hex characters"); } /** @@ -137,7 +141,7 @@ class MoneroUtils { * @param {string} publicSpendKey - public spend key to validate */ static async validatePublicSpendKey(publicSpendKey) { - if (!MoneroUtils._isHex64(publicSpendKey)) throw new MoneroError("public spend key expected to be 64 hex characters"); + if (!MoneroUtils.isHex64(publicSpendKey)) throw new MoneroError("public spend key expected to be 64 hex characters"); } /** @@ -145,10 +149,10 @@ class MoneroUtils { * * @param {MoneroNetworkType} networkType - network type of the integrated address * @param {string} standardAddress - address to derive the integrated address from - * @param {string} paymentId - optionally specifies the integrated address's payment id (defaults to random payment id) + * @param {string} [paymentId] - optionally specifies the integrated address's payment id (defaults to random payment id) * @return {Promise} the integrated address */ - static async getIntegratedAddress(networkType, standardAddress, paymentId) { + static async getIntegratedAddress(networkType: MoneroNetworkType, standardAddress: string, paymentId?: string) { if (MoneroUtils.PROXY_TO_WORKER) return new MoneroIntegratedAddress(await LibraryUtils.invokeWorker(undefined, "moneroUtilsGetIntegratedAddress", Array.from(arguments))); // validate inputs @@ -197,7 +201,7 @@ class MoneroUtils { assert(typeof address === "string", "Address is not string"); assert(address.length > 0, "Address is empty"); assert(GenUtils.isBase58(address), "Address is not base 58"); - MoneroNetworkType.validate(networkType); + networkType = MoneroNetworkType.from(networkType); // load keys module by default if (LibraryUtils.getWasmModule() === undefined) await LibraryUtils.loadKeysModule(); @@ -411,43 +415,36 @@ class MoneroUtils { /** * Convert XMR to atomic units. * - * @param {number|string} amountXmr - amount in XMR to convert to atomic units - * @return {BigInteger} amount in atomic units + * @param {number | string} amountXmr - amount in XMR to convert to atomic units + * @return {bigint} amount in atomic units */ - static xmrToAtomicUnits(amountXmr) { + static xmrToAtomicUnits(amountXmr: number | string): bigint { if (typeof amountXmr === "number") amountXmr = "" + amountXmr; - else if (typeof amountXmr !== "string") throw new MoneroError("Must provide XMR amount as a string or js number to convert to atomic units"); let decimalDivisor = 1; let decimalIdx = amountXmr.indexOf('.'); if (decimalIdx > -1) { decimalDivisor = Math.pow(10, amountXmr.length - decimalIdx - 1); amountXmr = amountXmr.slice(0, decimalIdx) + amountXmr.slice(decimalIdx + 1); } - return new BigInteger(amountXmr).multiply(new BigInteger(MoneroUtils.AU_PER_XMR)).divide(new BigInteger(decimalDivisor)); + return BigInt(amountXmr) * BigInt(MoneroUtils.AU_PER_XMR) / BigInt(decimalDivisor); } /** * Convert atomic units to XMR. * - * @param {BigInteger|string} amountAtomicUnits - amount in atomic units to convert to XMR + * @param {bigint | string} amountAtomicUnits - amount in atomic units to convert to XMR * @return {number} amount in XMR */ - static atomicUnitsToXmr(amountAtomicUnits) { - if (typeof amountAtomicUnits === "string") amountAtomicUnits = new BigInteger(amountAtomicUnits); - else if (!(amountAtomicUnits instanceof BigInteger)) throw new MoneroError("Must provide atomic units as BigInteger or string to convert to XMR"); - let quotientAndRemainder = amountAtomicUnits.divRem(new BigInteger(MoneroUtils.AU_PER_XMR)); - return Number(quotientAndRemainder[0].toJSValue() + quotientAndRemainder[1].toJSValue() / MoneroUtils.AU_PER_XMR); + static atomicUnitsToXmr(amountAtomicUnits: bigint | string) { + if (typeof amountAtomicUnits === "string") amountAtomicUnits = BigInt(amountAtomicUnits); + else if (typeof amountAtomicUnits !== "bigint") throw new Error("Must provide atomic units as bigint or string to convert to XMR"); + const quotient: bigint = amountAtomicUnits / MoneroUtils.AU_PER_XMR; + const remainder: bigint = amountAtomicUnits % MoneroUtils.AU_PER_XMR; + return Number(quotient) + Number(remainder) / Number(MoneroUtils.AU_PER_XMR); } - static _isHex64(str) { + protected static isHex64(str) { return typeof str === "string" && str.length === 64 && GenUtils.isHex(str); } } -MoneroUtils.PROXY_TO_WORKER = true; -MoneroUtils.NUM_MNEMONIC_WORDS = 25; -MoneroUtils.RING_SIZE = 12; -MoneroUtils.MAX_REQUESTS_PER_SECOND = 50; -MoneroUtils.AU_PER_XMR = 1000000000000; - -module.exports = MoneroUtils; \ No newline at end of file diff --git a/src/main/js/common/MoneroWebWorker.js b/src/main/ts/common/MoneroWebWorker.ts similarity index 93% rename from src/main/js/common/MoneroWebWorker.js rename to src/main/ts/common/MoneroWebWorker.ts index 7f5ae55f0..e750d0e55 100644 --- a/src/main/js/common/MoneroWebWorker.js +++ b/src/main/ts/common/MoneroWebWorker.ts @@ -1,20 +1,25 @@ -const assert = require("assert"); -const GenUtils = require("./GenUtils"); -const HttpClient = require("./HttpClient"); -const LibraryUtils = require("./LibraryUtils"); -const MoneroBan = require("../daemon/model/MoneroBan"); -const MoneroBlock = require("../daemon/model/MoneroBlock"); -const MoneroDaemonListener = require("../daemon/model/MoneroDaemonListener"); -const MoneroDaemonRpc = require("../daemon/MoneroDaemonRpc"); -const MoneroError = require("./MoneroError"); -const MoneroKeyImage = require("../daemon/model/MoneroKeyImage"); -const MoneroRpcConnection = require("./MoneroRpcConnection"); -const MoneroTxConfig = require("../wallet/model/MoneroTxConfig"); -const MoneroTxSet = require("../wallet/model/MoneroTxSet"); -const MoneroUtils = require("../common/MoneroUtils"); -const MoneroWalletConfig = require("../wallet/model/MoneroWalletConfig"); -const MoneroWalletListener = require("../wallet/model/MoneroWalletListener"); -const MoneroWalletFull = require("../wallet/MoneroWalletFull"); +import assert from "assert"; +import GenUtils from "./GenUtils"; +import HttpClient from "./HttpClient"; +import LibraryUtils from "./LibraryUtils"; +import MoneroBan from "../daemon/model/MoneroBan"; +import MoneroBlock from "../daemon/model/MoneroBlock"; +import MoneroDaemonConfig from "../daemon/model/MoneroDaemonConfig"; +import MoneroDaemonListener from "../daemon/model/MoneroDaemonListener"; +import MoneroDaemonRpc from "../daemon/MoneroDaemonRpc"; +import MoneroError from "./MoneroError"; +import MoneroKeyImage from "../daemon/model/MoneroKeyImage"; +import MoneroRpcConnection from "./MoneroRpcConnection"; +import MoneroTxConfig from "../wallet/model/MoneroTxConfig"; +import MoneroTxQuery from "../wallet/model/MoneroTxQuery"; +import MoneroTxSet from "../wallet/model/MoneroTxSet"; +import MoneroUtils from "./MoneroUtils"; +import MoneroWalletConfig from "../wallet/model/MoneroWalletConfig" +import MoneroWalletListener from "../wallet/model/MoneroWalletListener" +import {MoneroWalletKeys} from "../wallet/MoneroWalletKeys"; +import MoneroWalletFull from "../wallet/MoneroWalletFull"; + +declare const self: any; /** * Worker to manage a daemon and wasm wallet off the main thread using messages. @@ -25,7 +30,7 @@ const MoneroWalletFull = require("../wallet/MoneroWalletFull"); * * @private */ -onmessage = async function(e) { +self.onmessage = async function(e) { // initialize one time await self.initOneTime(); @@ -42,7 +47,7 @@ onmessage = async function(e) { // execute worker function and post result to callback try { postMessage([objectId, callbackId, {result: await self[fnName].apply(null, e.data)}]); - } catch (e) { + } catch (e: any) { if (!(e instanceof Error)) e = new Error(e); postMessage([objectId, callbackId, {error: LibraryUtils.serializeError(e)}]); } @@ -61,7 +66,7 @@ self.initOneTime = async function() { self.httpRequest = async function(objectId, opts) { try { return await HttpClient.request(Object.assign(opts, {proxyToWorker: false})); - } catch (err) { + } catch (err: any) { throw err.statusCode ? new Error(JSON.stringify({statusCode: err.statusCode, statusMessage: err.message})) : err; } } @@ -116,7 +121,7 @@ self.daemonRemoveListener = async function(daemonId, listenerId) { } self.connectDaemonRpc = async function(daemonId, config) { - self.WORKER_OBJECTS[daemonId] = new MoneroDaemonRpc(config); + self.WORKER_OBJECTS[daemonId] = await MoneroDaemonRpc.connectToDaemonRpc(new MoneroDaemonConfig(config)); } self.daemonGetRpcConnection = async function(daemonId) { @@ -414,16 +419,22 @@ self.daemonWaitForNextBlockHeader = async function(daemonId) { self.openWalletData = async function(walletId, path, password, networkType, keysData, cacheData, daemonUriOrConfig) { let daemonConnection = daemonUriOrConfig ? new MoneroRpcConnection(daemonUriOrConfig) : undefined; self.WORKER_OBJECTS[walletId] = await MoneroWalletFull.openWallet({path: "", password: password, networkType: networkType, keysData: keysData, cacheData: cacheData, server: daemonConnection, proxyToWorker: false}); - self.WORKER_OBJECTS[walletId]._setBrowserMainPath(path); + self.WORKER_OBJECTS[walletId].setBrowserMainPath(path); } -self._createWallet = async function(walletId, configJson) { +self.createWalletKeys = async function(walletId, configJson) { + let config = new MoneroWalletConfig(configJson); + config.setProxyToWorker(false); + self.WORKER_OBJECTS[walletId] = await MoneroWalletKeys.createWallet(config); +} + +self.createWalletFull = async function(walletId, configJson) { let config = new MoneroWalletConfig(configJson); let path = config.getPath(); config.setPath(""); config.setProxyToWorker(false); self.WORKER_OBJECTS[walletId] = await MoneroWalletFull.createWallet(config); - self.WORKER_OBJECTS[walletId]._setBrowserMainPath(path); + self.WORKER_OBJECTS[walletId].setBrowserMainPath(path); } self.isViewOnly = async function(walletId) { @@ -538,6 +549,10 @@ self.addListener = async function(walletId, listenerId) { * @private */ class WalletWorkerHelperListener extends MoneroWalletListener { + + protected walletId: string; + protected id: string; + protected worker: Worker; constructor(walletId, id, worker) { super(); @@ -550,25 +565,25 @@ self.addListener = async function(walletId, listenerId) { return this.id; } - onSyncProgress(height, startHeight, endHeight, percentDone, message) { + async onSyncProgress(height, startHeight, endHeight, percentDone, message) { this.worker.postMessage([this.walletId, "onSyncProgress_" + this.getId(), height, startHeight, endHeight, percentDone, message]); } - onNewBlock(height) { + async onNewBlock(height) { this.worker.postMessage([this.walletId, "onNewBlock_" + this.getId(), height]); } - onBalancesChanged(newBalance, newUnlockedBalance) { + async onBalancesChanged(newBalance, newUnlockedBalance) { this.worker.postMessage([this.walletId, "onBalancesChanged_" + this.getId(), newBalance.toString(), newUnlockedBalance.toString()]); } - onOutputReceived(output) { + async onOutputReceived(output) { let block = output.getTx().getBlock(); if (block === undefined) block = new MoneroBlock().setTxs([output.getTx()]); this.worker.postMessage([this.walletId, "onOutputReceived_" + this.getId(), block.toJson()]); // serialize from root block } - onOutputSpent(output) { + async onOutputSpent(output) { let block = output.getTx().getBlock(); if (block === undefined) block = new MoneroBlock().setTxs([output.getTx()]); this.worker.postMessage([this.walletId, "onOutputSpent_" + this.getId(), block.toJson()]); // serialize from root block @@ -596,7 +611,7 @@ self.isSynced = async function(walletId) { } self.sync = async function(walletId, startHeight, allowConcurrentCalls) { - return await self.WORKER_OBJECTS[walletId].sync(undefined, startHeight, allowConcurrentCalls); + return (await self.WORKER_OBJECTS[walletId].sync(undefined, startHeight, allowConcurrentCalls)); } self.startSyncing = async function(walletId, syncPeriodInMs) { @@ -684,7 +699,7 @@ self.getTxs = async function(walletId, blockJsonQuery) { self.getTransfers = async function(walletId, blockJsonQuery) { // deserialize query which is json string rooted at block - let query = new MoneroBlock(blockJsonQuery, MoneroBlock.DeserializationType.TX_QUERY).getTxs()[0].getTransferQuery(); + let query = (new MoneroBlock(blockJsonQuery, MoneroBlock.DeserializationType.TX_QUERY).getTxs()[0] as MoneroTxQuery).getTransferQuery(); // get transfers let transfers = await self.WORKER_OBJECTS[walletId].getTransfers(query); @@ -714,7 +729,7 @@ self.getTransfers = async function(walletId, blockJsonQuery) { self.getOutputs = async function(walletId, blockJsonQuery) { // deserialize query which is json string rooted at block - let query = new MoneroBlock(blockJsonQuery, MoneroBlock.DeserializationType.TX_QUERY).getTxs()[0].getOutputQuery(); + let query = (new MoneroBlock(blockJsonQuery, MoneroBlock.DeserializationType.TX_QUERY).getTxs()[0] as MoneroTxQuery).getOutputQuery(); // get outputs let outputs = await self.WORKER_OBJECTS[walletId].getOutputs(query); diff --git a/src/main/ts/common/SslOptions.ts b/src/main/ts/common/SslOptions.ts new file mode 100644 index 000000000..cb2b2e4bc --- /dev/null +++ b/src/main/ts/common/SslOptions.ts @@ -0,0 +1,60 @@ +/** + * SSL options for remote endpoints. + */ +export default class SslOptions { + + privateKeyPath: string; + certificatePath: string; + certificateAuthorityFile: string; + allowedFingerprints: string[]; + allowAnyCert: boolean; + + constructor(options?: Partial) { + Object.assign(this, options); + } + + getPrivateKeyPath() { + return this.privateKeyPath; + } + + setPrivateKeyPath(privateKeyPath) { + this.privateKeyPath = privateKeyPath; + return this; + } + + getCertificatePath() { + return this.certificatePath; + } + + setCertificatePath(certificatePath) { + this.certificatePath = certificatePath; + return this; + } + + getCertificateAuthorityFile() { + return this.certificateAuthorityFile; + } + + setCertificateAuthorityFile(certificateAuthorityFile) { + this.certificateAuthorityFile = certificateAuthorityFile; + return this; + } + + getAllowedFingerprints() { + return this.allowedFingerprints; + } + + setAllowedFingerprints(allowedFingerprints) { + this.allowedFingerprints = allowedFingerprints; + return this; + } + + getAllowAnyCert() { + return this.allowAnyCert; + } + + setAllowAnyCert(allowAnyCert) { + this.allowAnyCert = allowAnyCert; + return this; + } +} diff --git a/src/main/js/common/TaskLooper.js b/src/main/ts/common/TaskLooper.ts similarity index 60% rename from src/main/js/common/TaskLooper.js rename to src/main/ts/common/TaskLooper.ts index c88ad2727..1c888e75b 100644 --- a/src/main/js/common/TaskLooper.js +++ b/src/main/ts/common/TaskLooper.ts @@ -1,7 +1,13 @@ /** * Run a task in a fixed period loop. */ -class TaskLooper { +export default class TaskLooper { + + // instance variables + protected task: any; + protected periodInMs: any; + protected _isStarted: any; + protected isLooping: any; /** * Build the looper with a function to invoke on a fixed period loop. @@ -9,7 +15,7 @@ class TaskLooper { * @param {function} task - the task function to invoke */ constructor(task) { - this._task = task; + this.task = task; } /** @@ -18,22 +24,22 @@ class TaskLooper { * @return {function} the task function */ getTask() { - return this._task; + return this.task; } /** * Start the task loop. * - * @param {int} periodInMs the loop period in milliseconds + * @param {number} periodInMs the loop period in milliseconds * @return {TaskLooper} this class for chaining */ start(periodInMs) { - this._periodInMs = periodInMs; + this.setPeriodInMs(periodInMs); if (this._isStarted) return this; this._isStarted = true; // start looping - this._runLoop(); + this.runLoop(); return this; } @@ -56,23 +62,22 @@ class TaskLooper { /** * Set the loop period in milliseconds. * - * @param {int} periodInMs the loop period in milliseconds + * @param {number} periodInMs the loop period in milliseconds */ setPeriodInMs(periodInMs) { - this._periodInMs = periodInMs; + if (periodInMs <= 0) throw new Error("Looper period must be greater than 0 ms"); + this.periodInMs = periodInMs; } - async _runLoop() { - if (this._isLooping) return; - this._isLooping = true; + protected async runLoop() { + if (this.isLooping) return; + this.isLooping = true; let that = this; while (this._isStarted) { let startTime = Date.now(); - await this._task(); - if (this._isStarted) await new Promise(function(resolve) { setTimeout(resolve, that._periodInMs - (Date.now() - startTime)); }); + await this.task(); + if (this._isStarted) await new Promise(function(resolve) { setTimeout(resolve, that.periodInMs - (Date.now() - startTime)); }); } - this._isLooping = false; + this.isLooping = false; } } - -module.exports = TaskLooper; \ No newline at end of file diff --git a/src/main/js/common/ThreadPool.js b/src/main/ts/common/ThreadPool.ts similarity index 55% rename from src/main/js/common/ThreadPool.js rename to src/main/ts/common/ThreadPool.ts index 1675c7c52..5ddaa81f1 100644 --- a/src/main/js/common/ThreadPool.js +++ b/src/main/ts/common/ThreadPool.ts @@ -1,31 +1,34 @@ -const GenUtils = require("./GenUtils"); +import GenUtils from "./GenUtils"; +import async from "async"; /** * Simple thread pool using the async library. */ -class ThreadPool { +export default class ThreadPool { + + protected taskQueue: any; + protected drainListeners: any; /** * Construct the thread pool. * - * @param {int} maxConcurrency - maximum number of threads in the pool (default 1) + * @param {number} [maxConcurrency] - maximum number of threads in the pool (default 1) */ constructor(maxConcurrency) { if (maxConcurrency === undefined) maxConcurrency = 1; if (maxConcurrency < 1) throw new Error("Max concurrency must be greater than or equal to 1"); // manager concurrency with async queue - const async = require("async"); - this.taskQueue = async.queue(function(asyncFn, callback) { + //import async from "async"; + this.taskQueue = async.queue((asyncFn, callback) => { if (asyncFn.then) asyncFn.then(resp => { callback(resp); }).catch(err => { callback(undefined, err); }); else asyncFn().then(resp => { callback(resp); }).catch(err => { callback(undefined, err); }); }, maxConcurrency); // use drain listeners to support await all - let that = this; this.drainListeners = []; - this.taskQueue.drain = function() { - for (let listener of that.drainListeners) listener(); + this.taskQueue.drain = () => { + for (let listener of this.drainListeners) listener(); } } @@ -33,12 +36,11 @@ class ThreadPool { * Submit an asynchronous function to run using the thread pool. * * @param {function} asyncFn - asynchronous function to run with the thread pool - * @return {Promise} resolves when the function completes execution + * @return {Promise} resolves when the function completes execution */ - async submit(asyncFn) { - let that = this; - return new Promise(function(resolve, reject) { - that.taskQueue.push(asyncFn, function(resp, err) { + async submit(asyncFn): Promise { + return new Promise((resolve, reject) => { + this.taskQueue.push(asyncFn, (resp, err) => { if (err !== undefined) reject(err); else resolve(resp); }); @@ -48,18 +50,16 @@ class ThreadPool { /** * Await all functions to complete. * - * @return {Promise} resolves when all functions complete + * @return {Promise} resolves when all functions complete */ - async awaitAll() { + async awaitAll(): Promise { if (this.taskQueue.length === 0) return; - let that = this; - return new Promise(function(resolve) { - that.drainListeners.push(function() { - GenUtils.remove(that.drainListeners, this); + return new Promise((resolve) => { + this.drainListeners.push(() => { + GenUtils.remove(this.drainListeners, this); resolve(); }) }); } } -module.exports = ThreadPool; \ No newline at end of file diff --git a/src/main/ts/daemon/MoneroDaemon.ts b/src/main/ts/daemon/MoneroDaemon.ts new file mode 100644 index 000000000..822aadd3e --- /dev/null +++ b/src/main/ts/daemon/MoneroDaemon.ts @@ -0,0 +1,732 @@ +import assert from "assert"; +import MoneroAltChain from "./model/MoneroAltChain"; +import MoneroBan from "./model/MoneroBan"; +import MoneroBlock from "./model/MoneroBlock"; +import MoneroBlockHeader from "./model/MoneroBlockHeader"; +import MoneroBlockTemplate from "./model/MoneroBlockTemplate"; +import MoneroDaemonInfo from "./model/MoneroDaemonInfo"; +import MoneroDaemonListener from "./model/MoneroDaemonListener"; +import MoneroDaemonSyncInfo from "./model/MoneroDaemonSyncInfo"; +import MoneroDaemonUpdateCheckResult from "./model/MoneroDaemonUpdateCheckResult"; +import MoneroDaemonUpdateDownloadResult from "./model/MoneroDaemonUpdateDownloadResult"; +import MoneroError from "../common/MoneroError"; +import MoneroFeeEstimate from "./model/MoneroFeeEstimate"; +import MoneroHardForkInfo from "./model/MoneroHardForkInfo"; +import MoneroKeyImageSpentStatus from "./model/MoneroKeyImageSpentStatus"; +import MoneroMinerTxSum from "./model/MoneroMinerTxSum"; +import MoneroMiningStatus from "./model/MoneroMiningStatus"; +import MoneroOutput from "./model/MoneroOutput"; +//import MoneroOutputDistributionEntry from "./model/MoneroOutputDistributionEntry"; +import MoneroOutputHistogramEntry from "./model/MoneroOutputHistogramEntry"; +import MoneroNetworkType from "./model/MoneroNetworkType"; +import MoneroPeer from "./model/MoneroPeer"; +import MoneroPruneResult from "./model/MoneroPruneResult"; +import MoneroSubmitTxResult from "./model/MoneroSubmitTxResult"; +import MoneroTx from "./model/MoneroTx"; +import MoneroTxPoolStats from "./model/MoneroTxPoolStats"; +import MoneroVersion from "./model/MoneroVersion"; + +/** + * Copyright (c) woodser + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * Monero daemon interface and default implementations. + */ +export default class MoneroDaemon { + + /** + * Register a listener to receive daemon notifications. + * + * @param {MoneroDaemonListener} listener - listener to receive daemon notifications + * @return {Promise} + */ + async addListener(listener: MoneroDaemonListener): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Unregister a listener to receive daemon notifications. + * + * @param {MoneroDaemonListener} listener - listener to unregister + * @return {Promise} + */ + async removeListener(listener: MoneroDaemonListener): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get the listeners registered with the daemon. + * + * @return {MoneroDaemonListener[]} the registered listeners + */ + getListeners(): MoneroDaemonListener[] { + throw new MoneroError("Subclass must implement"); + } + + /** + * Indicates if the client is connected to the daemon via RPC. + * + * @return {Promise} true if the client is connected to the daemon, false otherwise + */ + async isConnected(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Gets the version of the daemon. + * + * @return {Promise} the version of the daemon + */ + async getVersion(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Indicates if the daemon is trusted xor untrusted. + * + * @return {Promise} true if the daemon is trusted, false otherwise + */ + async isTrusted(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get the number of blocks in the longest chain known to the node. + * + * @return {Promise} the number of blocks! + */ + async getHeight(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get a block's hash by its height. + * + * @param {number} height - height of the block hash to get + * @return {Promise} the block's hash at the given height + */ + async getBlockHash(height: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get a block template for mining a new block. + * + * @param {string} walletAddress - address of the wallet to receive miner transactions if block is successfully mined + * @param {number} [reserveSize] - reserve size (optional) + * @return {Promise} is a block template for mining a new block + */ + async getBlockTemplate(walletAddress: string, reserveSize?: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get the last block's header. + * + * @return {Promise} last block's header + */ + async getLastBlockHeader(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get a block header by its hash. + * + * @param {string} blockHash - hash of the block to get the header of + * @return {Promise} block's header + */ + async getBlockHeaderByHash(blockHash: string): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get a block header by its height. + * + * @param {number} height - height of the block to get the header of + * @return {Promise} block's header + */ + async getBlockHeaderByHeight(height: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get block headers for the given range. + * + * @param {number} [startHeight] - start height lower bound inclusive (optional) + * @param {number} [endHeight] - end height upper bound inclusive (optional) + * @return {Promise} for the given range + */ + async getBlockHeadersByRange(startHeight?: number, endHeight?: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get a block by hash. + * + * @param {string} blockHash - hash of the block to get + * @return {Promise} with the given hash + */ + async getBlockByHash(blockHash: string): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get blocks by hash. + * + * @param {string[]} blockHashes - array of hashes; first 10 blocks hashes goes sequential, + * next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, + * and the last one is always genesis block + * @param {number} startHeight - start height to get blocks by hash + * @param {boolean} [prune] - specifies if returned blocks should be pruned (defaults to false) // TODO: test default + * @return {Promise} retrieved blocks + */ + async getBlocksByHash(blockHashes: string[], startHeight: number, prune = false): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get a block by height. + * + * @param {number} height - height of the block to get + * @return {Promise} with the given height + */ + async getBlockByHeight(height: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get blocks at the given heights. + * + * @param {number[]} heights - heights of the blocks to get + * @return {Promise} are blocks at the given heights + */ + async getBlocksByHeight(heights: number[]): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get blocks in the given height range. + * + * @param {number} [startHeight] - start height lower bound inclusive (optional) + * @param {number} [endHeight] - end height upper bound inclusive (optional) + * @return {Promise} are blocks in the given height range + */ + async getBlocksByRange(startHeight?: number, endHeight?: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get blocks in the given height range as chunked requests so that each request is + * not too big. + * + * @param {number} [startHeight] - start height lower bound inclusive (optional) + * @param {number} [endHeight] - end height upper bound inclusive (optional) + * @param {number} [maxChunkSize] - maximum chunk size in any one request (default 3,000,000 bytes) + * @return {Promise} blocks in the given height range + */ + async getBlocksByRangeChunked(startHeight?: number, endHeight?: number, maxChunkSize?: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get block hashes as a binary request to the daemon. + * + * @param {string[]} blockHashes - specify block hashes to fetch; first 10 blocks hash goes + * sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 + * and so on, and the last one is always genesis block + * @param {number} startHeight - starting height of block hashes to return + * @return {Promise} requested block hashes + */ + async getBlockHashes(blockHashes: string[], startHeight: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get a transaction by hash. + * + * @param {string} txHash - hash of the transaction to get + * @param {boolean} [prune] - specifies if the returned tx should be pruned (defaults to false) + * @return {Promise} transaction with the given hash or undefined if not found + */ + async getTx(txHash?: string, prune = false): Promise { + return (await this.getTxs([txHash], prune))[0]; + } + + /** + * Get transactions by hashes. + * + * @param {string[]} txHashes - hashes of transactions to get + * @param {boolean} [prune] - specifies if the returned txs should be pruned (defaults to false) + * @return {Promise} found transactions with the given hashes + */ + async getTxs(txHashes: string[], prune = false): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get a transaction hex by hash. + * + * @param {string} txHash - hash of the transaction to get hex from + * @param {boolean} [prune] - specifies if the returned tx hex should be pruned (defaults to false) + * @return {Promise} tx hex with the given hash + */ + async getTxHex(txHash: string, prune = false): Promise { + return (await this.getTxHexes([txHash], prune))[0]; + } + + /** + * Get transaction hexes by hashes. + * + * @param {string[]} txHashes - hashes of transactions to get hexes from + * @param {boolean} [prune] - specifies if the returned tx hexes should be pruned (defaults to false) + * @return {Promise} tx hexes + */ + async getTxHexes(txHashes: string[], prune = false): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Gets the total emissions and fees from the genesis block to the current height. + * + * @param {number} height - height to start computing the miner sum + * @param {number} numBlocks - number of blocks to include in the sum + * @return {Promise} encapsulates the total emissions and fees since the genesis block + */ + async getMinerTxSum(height: number, numBlocks: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get mining fee estimates per kB. + * + * @param {number} graceBlocks TODO + * @return {Promise} mining fee estimates per kB + */ + async getFeeEstimate(graceBlocks?: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Submits a transaction to the daemon's pool. + * + * @param {string} txHex - raw transaction hex to submit + * @param {boolean} doNotRelay specifies if the tx should be relayed (default false, i.e. relay) + * @return {Promise} contains submission results + */ + async submitTxHex(txHex: string, doNotRelay = false): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Relays a transaction by hash. + * + * @param {string} txHash - hash of the transaction to relay + * @return {Promise} + */ + async relayTxByHash(txHash: string): Promise { + assert.equal(typeof txHash, "string", "Must provide a transaction hash"); + await this.relayTxsByHash([txHash]); + } + + /** + * Relays transactions by hash. + * + * @param {string[]} txHashes - hashes of the transactinos to relay + * @return {Promise} + */ + async relayTxsByHash(txHashes: string[]): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get valid transactions seen by the node but not yet mined into a block, as well + * as spent key image information for the tx pool. + * + * @return {Promise} are transactions in the transaction pool! + */ + async getTxPool(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get hashes of transactions in the transaction pool. + * + * @return {string[]} are hashes of transactions in the transaction pool + */ + async getTxPoolHashes(): Promise { + throw new MoneroError("Subclass must implement"); + } + + // /** + // * Get all transaction pool backlog. + // * + // * @return {Promise} backlog entries + // */ + // async getTxPoolBacklog(): Promise { + // throw new MoneroError("Subclass must implement"); + // } + + /** + * Get transaction pool statistics. + * + * @return {Promise} contains statistics about the transaction pool + */ + async getTxPoolStats(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Flush transactions from the tx pool. + * + * @param {(string | string[])} [hashes] - specific transactions to flush (defaults to all) + * @return {Promise} + */ + async flushTxPool(hashes?: string | string[]): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get the spent status of the given key image. + * + * @param {string} keyImage - key image hex to get the status of + * @return {Promise} status of the key image + */ + async getKeyImageSpentStatus(keyImage: string): Promise { + return (await this.getKeyImageSpentStatuses([keyImage]))[0]; + } + + /** + * Get the spent status of each given key image. + * + * @param {string[]} keyImages are hex key images to get the statuses of + * @return {Promise} status for each key image + */ + async getKeyImageSpentStatuses(keyImages: string[]): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get outputs identified by a list of output amounts and indices as a binary + * request. + * + * @param {MoneroOutput[]} outputs - identify each output by amount and index + * @return {Promise} identified outputs + */ + async getOutputs(outputs: MoneroOutput[]): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get a histogram of output amounts. For all amounts (possibly filtered by + * parameters), gives the number of outputs on the chain for that amount. + * RingCT outputs counts as 0 amount. + * + * @param {bigint[]} [amounts] - amounts of outputs to make the histogram with + * @param {number} [minCount] - TODO + * @param {number} [maxCount] - TODO + * @param {boolean} [isUnlocked] - makes a histogram with outputs with the specified lock state + * @param {number} [recentCutoff] - TODO + * @return {Promise} are entries meeting the parameters + */ + async getOutputHistogram(amounts?: bigint[], minCount?: number, maxCount?: number, isUnlocked?: boolean, recentCutoff?: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + // /** + // * Creates an output distribution. + // * + // * @param {bigint[]} amounts - amounts of outputs to make the distribution with + // * @param {boolean} [cumulative] - specifies if the results should be cumulative (defaults to TODO) + // * @param {number} [startHeight] - start height lower bound inclusive (optional) + // * @param {number} [endHeight] - end height upper bound inclusive (optional) + // * @return {Promise} are entries meeting the parameters + // */ + // async getOutputDistribution(amounts: bigint[], cumulative?: boolean, startHeight?: number, endHeight?: number): Promise { + // throw new MoneroError("Subclass must implement"); + // } + + /** + * Get general information about the state of the node and the network. + * + * @return {Promise} is general information about the node and network + */ + async getInfo(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get synchronization information. + * + * @return {Promise} contains sync information + */ + async getSyncInfo(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Look up information regarding hard fork voting and readiness. + * + * @return {Promise } contains hard fork information + */ + async getHardForkInfo(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get alternative chains seen by the node. + * + * @return {Promise} alternative chains + */ + async getAltChains(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get known block hashes which are not on the main chain. + * + * @return {Promise} known block hashes which are not on the main chain + */ + async getAltBlockHashes(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get the download bandwidth limit. + * + * @return {Promise} download bandwidth limit + */ + async getDownloadLimit(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Set the download bandwidth limit. + * + * @param {number} limit - download limit to set (-1 to reset to default) + * @return {number} new download limit after setting + */ + async setDownloadLimit(limit: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Reset the download bandwidth limit. + * + * @return {Promise} download bandwidth limit after resetting + */ + async resetDownloadLimit(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get the upload bandwidth limit. + * + * @return {Promise} upload bandwidth limit + */ + async getUploadLimit(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Set the upload bandwidth limit. + * + * @param limit - upload limit to set (-1 to reset to default) + * @return {Promise} new upload limit after setting + */ + async setUploadLimit(limit: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Reset the upload bandwidth limit. + * + * @return {Promise} upload bandwidth limit after resetting + */ + async resetUploadLimit(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get peers with active incoming or outgoing connections to the node. + * + * @return {Promise} the daemon's peers + */ + async getPeers(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get known peers including their last known online status. + * + * @return {MoneroPeer[]} the daemon's known peers + */ + async getKnownPeers(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Limit number of outgoing peers. + * + * @param {number} limit - maximum number of outgoing peers + * @return {Promise} + */ + async setOutgoingPeerLimit(limit: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Limit number of incoming peers. + * + * @param {number} limit - maximum number of incoming peers + * @return {Promise} + */ + async setIncomingPeerLimit(limit: number): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get peer bans. + * + * @return {Promise} entries about banned peers + */ + async getPeerBans(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Ban a peer node. + * + * @param {MoneroBan} ban - contains information about a node to ban + * @return {Promise} + */ + async setPeerBan(ban: MoneroBan): Promise { + return await this.setPeerBans([ban]); + } + + /** + * Ban peers nodes. + * + * @param {MoneroBan[]} bans - specify which peers to ban + * @return {Promise} + */ + async setPeerBans(bans: MoneroBan[]): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Start mining. + * + * @param {string} address - address given miner rewards if the daemon mines a block + * @param {integer} [numThreads] - number of mining threads to run (default 1) + * @param {boolean} [isBackground] - specifies if the miner should run in the background or not (default false) + * @param {boolean} [ignoreBattery] - specifies if the battery state (e.g. on laptop) should be ignored or not (default false) + * @return {Promise} + */ + async startMining(address: string, numThreads?: number, isBackground?: boolean, ignoreBattery?: boolean): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Stop mining. + * + * @return {Promise} + */ + async stopMining(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get the daemon's mining status. + * + * @return {Promise} daemon's mining status + */ + async getMiningStatus(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Submit a mined block to the network. + * + * @param {string} blockBlob - mined block to submit + * @return {Promise} + */ + async submitBlock(blockBlob: string): Promise { + await this.submitBlocks([blockBlob]); + } + + /** + * Prune the blockchain. + * + * @param {boolean} check specifies to check the pruning (default false) + * @return {Promise} the prune result + */ + async pruneBlockchain(check: boolean): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Submit mined blocks to the network. + * + * @param {string[]} blockBlobs - mined blocks to submit + * @return {Promise} + */ + async submitBlocks(blockBlobs: string[]): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Check for update. + * + * @return {Promise} the result + */ + async checkForUpdate(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Download an update. + * + * @param {string} [path] - path to download the update (optional) + * @return {Promise} the result + */ + async downloadUpdate(path?: string): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Safely disconnect and shut down the daemon. + * + * @return {Promise} + */ + async stop(): Promise { + throw new MoneroError("Subclass must implement"); + } + + /** + * Get the header of the next block added to the chain. + * + * @return {Promise} header of the next block added to the chain + */ + async waitForNextBlockHeader(): Promise { + throw new MoneroError("Subclass must implement"); + } +} \ No newline at end of file diff --git a/src/main/js/daemon/MoneroDaemonRpc.js b/src/main/ts/daemon/MoneroDaemonRpc.ts similarity index 57% rename from src/main/js/daemon/MoneroDaemonRpc.js rename to src/main/ts/daemon/MoneroDaemonRpc.ts index 1c45242a0..7840a2fed 100644 --- a/src/main/js/daemon/MoneroDaemonRpc.js +++ b/src/main/ts/daemon/MoneroDaemonRpc.ts @@ -1,34 +1,39 @@ -const assert = require("assert"); -const BigInteger = require("../common/biginteger").BigInteger; -const GenUtils = require("../common/GenUtils"); -const LibraryUtils = require("../common/LibraryUtils"); -const TaskLooper = require("../common/TaskLooper"); -const MoneroAltChain = require("./model/MoneroAltChain"); -const MoneroBan = require("./model/MoneroBan"); -const MoneroBlock = require("./model/MoneroBlock"); -const MoneroBlockHeader = require("./model/MoneroBlockHeader"); -const MoneroBlockTemplate = require("./model/MoneroBlockTemplate"); -const MoneroDaemon = require("./MoneroDaemon"); -const MoneroDaemonInfo = require("./model/MoneroDaemonInfo"); -const MoneroDaemonListener = require("./model/MoneroDaemonListener"); -const MoneroDaemonSyncInfo = require("./model/MoneroDaemonSyncInfo"); -const MoneroError = require("../common/MoneroError"); -const MoneroFeeEstimate = require("./model/MoneroFeeEstimate"); -const MoneroHardForkInfo = require("./model/MoneroHardForkInfo"); -const MoneroKeyImage = require("./model/MoneroKeyImage"); -const MoneroMinerTxSum = require("./model/MoneroMinerTxSum"); -const MoneroMiningStatus = require("./model/MoneroMiningStatus"); -const MoneroNetworkType = require("./model/MoneroNetworkType"); -const MoneroOutput = require("./model/MoneroOutput"); -const MoneroOutputHistogramEntry = require("./model/MoneroOutputHistogramEntry"); -const MoneroPeer = require("./model/MoneroPeer"); -const MoneroPruneResult = require("./model/MoneroPruneResult"); -const MoneroRpcConnection = require("../common/MoneroRpcConnection"); -const MoneroSubmitTxResult = require("./model/MoneroSubmitTxResult"); -const MoneroTx = require("./model/MoneroTx"); -const MoneroTxPoolStats = require("./model/MoneroTxPoolStats"); -const MoneroUtils = require("../common/MoneroUtils"); -const MoneroVersion = require("./model/MoneroVersion"); +import assert from "assert"; +import GenUtils from "../common/GenUtils"; +import LibraryUtils from "../common/LibraryUtils"; +import TaskLooper from "../common/TaskLooper"; +import MoneroAltChain from "./model/MoneroAltChain"; +import MoneroBan from "./model/MoneroBan"; +import MoneroBlock from "./model/MoneroBlock"; +import MoneroBlockHeader from "./model/MoneroBlockHeader"; +import MoneroBlockTemplate from "./model/MoneroBlockTemplate"; +import MoneroConnectionSpan from "./model/MoneroConnectionSpan"; +import MoneroDaemon from "./MoneroDaemon"; +import MoneroDaemonConfig from "./model/MoneroDaemonConfig"; +import MoneroDaemonInfo from "./model/MoneroDaemonInfo"; +import MoneroDaemonListener from "./model/MoneroDaemonListener"; +import MoneroDaemonSyncInfo from "./model/MoneroDaemonSyncInfo"; +import MoneroDaemonUpdateCheckResult from "./model/MoneroDaemonUpdateCheckResult"; +import MoneroDaemonUpdateDownloadResult from "./model/MoneroDaemonUpdateDownloadResult"; +import MoneroFeeEstimate from "./model/MoneroFeeEstimate"; +import MoneroError from "../common/MoneroError"; +import MoneroHardForkInfo from "./model/MoneroHardForkInfo"; +import MoneroKeyImage from "./model/MoneroKeyImage"; +import MoneroKeyImageSpentStatus from "./model/MoneroKeyImageSpentStatus"; +import MoneroMinerTxSum from "./model/MoneroMinerTxSum"; +import MoneroMiningStatus from "./model/MoneroMiningStatus"; +import MoneroNetworkType from "./model/MoneroNetworkType"; +import MoneroOutput from "./model/MoneroOutput"; +import MoneroOutputHistogramEntry from "./model/MoneroOutputHistogramEntry"; +import MoneroPeer from "./model/MoneroPeer"; +import MoneroPruneResult from "./model/MoneroPruneResult"; +import MoneroRpcConnection from "../common/MoneroRpcConnection"; +import MoneroSubmitTxResult from "./model/MoneroSubmitTxResult"; +import MoneroTx from "./model/MoneroTx"; +import MoneroTxPoolStats from "./model/MoneroTxPoolStats"; +import MoneroUtils from "../common/MoneroUtils"; +import MoneroVersion from "./model/MoneroVersion"; +import { ChildProcess } from "child_process"; /** * Copyright (c) woodser @@ -54,177 +59,73 @@ const MoneroVersion = require("./model/MoneroVersion"); /** * Implements a MoneroDaemon as a client of monerod. - * - * @implements {MoneroDaemon} - * @hideconstructor */ class MoneroDaemonRpc extends MoneroDaemon { - - /** - *

Construct a daemon RPC client (for internal use).

- * - * @param {string|object|MoneroRpcConnection} uriOrConfig - uri of monerod or JS config object or MoneroRpcConnection - * @param {string} uriOrConfig.uri - uri of monerod - * @param {string} uriOrConfig.username - username to authenticate with monerod (optional) - * @param {string} uriOrConfig.password - password to authenticate with monerod (optional) - * @param {boolean} uriOrConfig.rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {number} uriOrConfig.pollInterval - poll interval to query for updates in ms (default 5000) - * @param {string} username - username to authenticate with monerod (optional) - * @param {string} password - password to authenticate with monerod (optional) - * @param {boolean} rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {number} pollInterval - poll interval to query for updates in ms (default 5000) - * @param {boolean} proxyToWorker - runs the daemon client in a worker if true (default true) - */ - constructor(uriOrConfig, username, password, rejectUnauthorized, pollInterval, proxyToWorker) { + + // static variables + protected static readonly MAX_REQ_SIZE = "3000000"; + protected static readonly DEFAULT_ID = "0000000000000000000000000000000000000000000000000000000000000000"; // uninitialized tx or block hash from daemon rpc + protected static readonly NUM_HEADERS_PER_REQ = 750; // number of headers to fetch and cache per request + protected static readonly DEFAULT_POLL_PERIOD = 20000; // default interval between polling the daemon in ms + + // instance variables + protected config: Partial; + protected listeners: MoneroDaemonListener[]; + protected cachedHeaders: any; + protected process: any; + protected pollListener: any; + protected proxyDaemon: any; + + /** @private */ + constructor(config: MoneroDaemonConfig, proxyDaemon: MoneroDaemonRpcProxy) { super(); - if (GenUtils.isArray(uriOrConfig)) throw new Error("Use monerojs.connectToDaemonRpc(...) to use terminal parameters"); - this.config = MoneroDaemonRpc._normalizeConfig(uriOrConfig, username, password, rejectUnauthorized, pollInterval, proxyToWorker); - if (this.config.proxyToWorker) throw new Error("Use monerojs.connectToDaemonRpc(...) to proxy to worker"); - let rpcConfig = Object.assign({}, this.config); - delete rpcConfig.proxyToWorker; - delete rpcConfig.pollInterval; - this.rpc = new MoneroRpcConnection(rpcConfig); + this.config = config; + this.proxyDaemon = proxyDaemon; + if (config.proxyToWorker) return; this.listeners = []; // block listeners this.cachedHeaders = {}; // cached headers for fetching blocks in bound chunks } - /** - *

Create a client connected to monerod (for internal use).

- * - * @param {string|string[]|object|MoneroRpcConnection} uriOrConfig - uri of monerod or terminal parameters or JS config object or MoneroRpcConnection - * @param {string} uriOrConfig.uri - uri of monerod - * @param {string} uriOrConfig.username - username to authenticate with monerod (optional) - * @param {string} uriOrConfig.password - password to authenticate with monerod (optional) - * @param {boolean} uriOrConfig.rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {number} uriOrConfig.pollInterval - poll interval to query for updates in ms (default 5000) - * @param {boolean} uriOrConfig.proxyToWorker - run the daemon client in a worker if true (default true) - * @param {string} username - username to authenticate with monerod (optional) - * @param {string} password - password to authenticate with monerod (optional) - * @param {boolean} rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {number} pollInterval - poll interval to query for updates in ms (default 5000) - * @param {boolean} proxyToWorker - runs the daemon client in a worker if true (default true) - * @return {MoneroDaemonRpc} the daemon RPC client - */ - static async _connectToDaemonRpc(uriOrConfig, username, password, rejectUnauthorized, pollInterval, proxyToWorker) { - if (GenUtils.isArray(uriOrConfig)) return MoneroDaemonRpc._startMonerodProcess(uriOrConfig, rejectUnauthorized, pollInterval, proxyToWorker); // handle array as terminal command - let config = MoneroDaemonRpc._normalizeConfig(uriOrConfig, username, password, rejectUnauthorized, pollInterval, proxyToWorker); - if (config.proxyToWorker) return MoneroDaemonRpcProxy.connect(config); - else return new MoneroDaemonRpc(config); - } - - static async _startMonerodProcess(cmd, rejectUnauthorized, pollInterval, proxyToWorker) { - assert(GenUtils.isArray(cmd), "Must provide string array with command line parameters"); - - // start process - this.process = require('child_process').spawn(cmd[0], cmd.slice(1), {}); - this.process.stdout.setEncoding('utf8'); - this.process.stderr.setEncoding('utf8'); - - // return promise which resolves after starting monerod - let uri; - let that = this; - let output = ""; - return new Promise(function(resolve, reject) { - - // handle stdout - that.process.stdout.on('data', async function(data) { - let line = data.toString(); - LibraryUtils.log(2, line); - output += line + '\n'; // capture output in case of error - - // extract uri from e.g. "I Binding on 127.0.0.1 (IPv4):38085" - let uriLineContains = "Binding on "; - let uriLineContainsIdx = line.indexOf(uriLineContains); - if (uriLineContainsIdx >= 0) { - let host = line.substring(uriLineContainsIdx + uriLineContains.length, line.lastIndexOf(' ')); - let unformattedLine = line.replace(/\u001b\[.*?m/g, '').trim(); // remove color formatting - let port = unformattedLine.substring(unformattedLine.lastIndexOf(':') + 1); - let sslIdx = cmd.indexOf("--rpc-ssl"); - let sslEnabled = sslIdx >= 0 ? "enabled" == cmd[sslIdx + 1].toLowerCase() : false; - uri = (sslEnabled ? "https" : "http") + "://" + host + ":" + port; - } - - // read success message - if (line.indexOf("core RPC server started ok") >= 0) { - - // get username and password from params - let userPassIdx = cmd.indexOf("--rpc-login"); - let userPass = userPassIdx >= 0 ? cmd[userPassIdx + 1] : undefined; - let username = userPass === undefined ? undefined : userPass.substring(0, userPass.indexOf(':')); - let password = userPass === undefined ? undefined : userPass.substring(userPass.indexOf(':') + 1); - - // create client connected to internal process - let daemon = await that._connectToDaemonRpc(uri, username, password, rejectUnauthorized, pollInterval, proxyToWorker); - daemon.process = that.process; - - // resolve promise with client connected to internal process - this.isResolved = true; - resolve(daemon); - } - }); - - // handle stderr - that.process.stderr.on('data', function(data) { - if (LibraryUtils.getLogLevel() >= 2) console.error(data); - }); - - // handle exit - that.process.on("exit", function(code) { - if (!this.isResolved) reject(new Error("monerod process terminated with exit code " + code + (output ? ":\n\n" + output : ""))); - }); - - // handle error - that.process.on("error", function(err) { - if (err.message.indexOf("ENOENT") >= 0) reject(new Error("monerod does not exist at path '" + cmd[0] + "'")); - if (!this.isResolved) reject(err); - }); - - // handle uncaught exception - that.process.on("uncaughtException", function(err, origin) { - console.error("Uncaught exception in monerod process: " + err.message); - console.error(origin); - reject(err); - }); - }); - } - /** * Get the internal process running monerod. * - * @return the process running monerod, undefined if not created from new process + * @return {ChildProcess} the node process running monerod, undefined if not created from new process */ - getProcess() { + getProcess(): ChildProcess { return this.process; } /** * Stop the internal process running monerod, if applicable. * - * @param {boolean} force specifies if the process should be destroyed forcibly - * @return {Promise} the exit code from stopping the process + * @param {boolean} [force] specifies if the process should be destroyed forcibly (default false) + * @return {Promise} the exit code from stopping the process */ - async stopProcess(force) { + async stopProcess(force = false): Promise { if (this.process === undefined) throw new MoneroError("MoneroDaemonRpc instance not created from new process"); - let listenersCopy = GenUtils.copyArray(this.getListeners()); + let listenersCopy = GenUtils.copyArray(await this.getListeners()); for (let listener of listenersCopy) await this.removeListener(listener); - return GenUtils.killProcess(this.process, force ? "sigkill" : undefined); + return GenUtils.killProcess(this.process, force ? "SIGKILL" : undefined); } - async addListener(listener) { + async addListener(listener: MoneroDaemonListener): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.addListener(listener); assert(listener instanceof MoneroDaemonListener, "Listener must be instance of MoneroDaemonListener"); this.listeners.push(listener); - this._refreshListening(); + this.refreshListening(); } - async removeListener(listener) { + async removeListener(listener: MoneroDaemonListener): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.removeListener(listener); assert(listener instanceof MoneroDaemonListener, "Listener must be instance of MoneroDaemonListener"); let idx = this.listeners.indexOf(listener); if (idx > -1) this.listeners.splice(idx, 1); else throw new MoneroError("Listener is not registered with daemon"); - this._refreshListening(); + this.refreshListening(); } - getListeners() { + getListeners(): MoneroDaemonListener[] { + if (this.config.proxyToWorker) return this.proxyDaemon.getListeners(); return this.listeners; } @@ -234,102 +135,116 @@ class MoneroDaemonRpc extends MoneroDaemon { * @return {MoneroRpcConnection} the daemon's rpc connection */ async getRpcConnection() { - return this.rpc; + if (this.config.proxyToWorker) return this.proxyDaemon.getRpcConnection(); + return this.config.getServer(); } - async isConnected() { + async isConnected(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.isConnected(); try { await this.getVersion(); return true; - } catch (e) { + } catch (e: any) { return false; } } - async getVersion() { - let resp = await this.rpc.sendJsonRequest("get_version"); - MoneroDaemonRpc._checkResponseStatus(resp.result); + async getVersion(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getVersion(); + let resp = await this.config.getServer().sendJsonRequest("get_version"); + MoneroDaemonRpc.checkResponseStatus(resp.result); return new MoneroVersion(resp.result.version, resp.result.release); } - async isTrusted() { - let resp = await this.rpc.sendPathRequest("get_height"); - MoneroDaemonRpc._checkResponseStatus(resp); + async isTrusted(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.isTrusted(); + let resp = await this.config.getServer().sendPathRequest("get_height"); + MoneroDaemonRpc.checkResponseStatus(resp); return !resp.untrusted; } - async getHeight() { - let resp = await this.rpc.sendJsonRequest("get_block_count"); - MoneroDaemonRpc._checkResponseStatus(resp.result); + async getHeight(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getHeight(); + let resp = await this.config.getServer().sendJsonRequest("get_block_count"); + MoneroDaemonRpc.checkResponseStatus(resp.result); return resp.result.count; } - async getBlockHash(height) { - return (await this.rpc.sendJsonRequest("on_get_block_hash", [height])).result; // TODO monero-wallet-rpc: no status returned + async getBlockHash(height: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlockHash(height); + return (await this.config.getServer().sendJsonRequest("on_get_block_hash", [height])).result; // TODO monero-wallet-rpc: no status returned } - async getBlockTemplate(walletAddress, reserveSize) { + async getBlockTemplate(walletAddress: string, reserveSize?: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlockTemplate(walletAddress, reserveSize); assert(walletAddress && typeof walletAddress === "string", "Must specify wallet address to be mined to"); - let resp = await this.rpc.sendJsonRequest("get_block_template", {wallet_address: walletAddress, reserve_size: reserveSize}); - MoneroDaemonRpc._checkResponseStatus(resp.result); - return MoneroDaemonRpc._convertRpcBlockTemplate(resp.result); + let resp = await this.config.getServer().sendJsonRequest("get_block_template", {wallet_address: walletAddress, reserve_size: reserveSize}); + MoneroDaemonRpc.checkResponseStatus(resp.result); + return MoneroDaemonRpc.convertRpcBlockTemplate(resp.result); } - async getLastBlockHeader() { - let resp = await this.rpc.sendJsonRequest("get_last_block_header"); - MoneroDaemonRpc._checkResponseStatus(resp.result); - return MoneroDaemonRpc._convertRpcBlockHeader(resp.result.block_header); + async getLastBlockHeader(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getLastBlockHeader(); + let resp = await this.config.getServer().sendJsonRequest("get_last_block_header"); + MoneroDaemonRpc.checkResponseStatus(resp.result); + return MoneroDaemonRpc.convertRpcBlockHeader(resp.result.block_header); } - async getBlockHeaderByHash(blockHash) { - let resp = await this.rpc.sendJsonRequest("get_block_header_by_hash", {hash: blockHash}); - MoneroDaemonRpc._checkResponseStatus(resp.result); - return MoneroDaemonRpc._convertRpcBlockHeader(resp.result.block_header); + async getBlockHeaderByHash(blockHash: string): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlockHeaderByHash(blockHash); + let resp = await this.config.getServer().sendJsonRequest("get_block_header_by_hash", {hash: blockHash}); + MoneroDaemonRpc.checkResponseStatus(resp.result); + return MoneroDaemonRpc.convertRpcBlockHeader(resp.result.block_header); } - async getBlockHeaderByHeight(height) { - let resp = await this.rpc.sendJsonRequest("get_block_header_by_height", {height: height}); - MoneroDaemonRpc._checkResponseStatus(resp.result); - return MoneroDaemonRpc._convertRpcBlockHeader(resp.result.block_header); + async getBlockHeaderByHeight(height: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlockHeaderByHeight(height); + let resp = await this.config.getServer().sendJsonRequest("get_block_header_by_height", {height: height}); + MoneroDaemonRpc.checkResponseStatus(resp.result); + return MoneroDaemonRpc.convertRpcBlockHeader(resp.result.block_header); } - async getBlockHeadersByRange(startHeight, endHeight) { + async getBlockHeadersByRange(startHeight?: number, endHeight?: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlockHeadersByRange(startHeight, endHeight); // fetch block headers - let resp = await this.rpc.sendJsonRequest("get_block_headers_range", { + let resp = await this.config.getServer().sendJsonRequest("get_block_headers_range", { start_height: startHeight, end_height: endHeight }); - MoneroDaemonRpc._checkResponseStatus(resp.result); + MoneroDaemonRpc.checkResponseStatus(resp.result); // build headers let headers = []; for (let rpcHeader of resp.result.headers) { - headers.push(MoneroDaemonRpc._convertRpcBlockHeader(rpcHeader)); + headers.push(MoneroDaemonRpc.convertRpcBlockHeader(rpcHeader)); } return headers; } - async getBlockByHash(blockHash) { - let resp = await this.rpc.sendJsonRequest("get_block", {hash: blockHash}); - MoneroDaemonRpc._checkResponseStatus(resp.result); - return MoneroDaemonRpc._convertRpcBlock(resp.result); + async getBlockByHash(blockHash: string): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlockByHash(blockHash); + let resp = await this.config.getServer().sendJsonRequest("get_block", {hash: blockHash}); + MoneroDaemonRpc.checkResponseStatus(resp.result); + return MoneroDaemonRpc.convertRpcBlock(resp.result); } - async getBlockByHeight(height) { - let resp = await this.rpc.sendJsonRequest("get_block", {height: height}); - MoneroDaemonRpc._checkResponseStatus(resp.result); - return MoneroDaemonRpc._convertRpcBlock(resp.result); + async getBlockByHeight(height: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlockByHeight(height); + let resp = await this.config.getServer().sendJsonRequest("get_block", {height: height}); + MoneroDaemonRpc.checkResponseStatus(resp.result); + return MoneroDaemonRpc.convertRpcBlock(resp.result); } - async getBlocksByHeight(heights) { + async getBlocksByHeight(heights: number[]): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlocksByHeight(heights); // fetch blocks in binary - let respBin = await this.rpc.sendBinaryRequest("get_blocks_by_height.bin", {heights: heights}); + let respBin = await this.config.getServer().sendBinaryRequest("get_blocks_by_height.bin", {heights: heights}); // convert binary blocks to json let rpcBlocks = await MoneroUtils.binaryBlocksToJson(respBin); - MoneroDaemonRpc._checkResponseStatus(rpcBlocks); + MoneroDaemonRpc.checkResponseStatus(rpcBlocks); // build blocks with transactions assert.equal(rpcBlocks.txs.length, rpcBlocks.blocks.length); @@ -337,7 +252,7 @@ class MoneroDaemonRpc extends MoneroDaemon { for (let blockIdx = 0; blockIdx < rpcBlocks.blocks.length; blockIdx++) { // build block - let block = MoneroDaemonRpc._convertRpcBlock(rpcBlocks.blocks[blockIdx]); + let block = MoneroDaemonRpc.convertRpcBlock(rpcBlocks.blocks[blockIdx]); block.setHeight(heights[blockIdx]); blocks.push(block); @@ -353,8 +268,8 @@ class MoneroDaemonRpc extends MoneroDaemon { tx.setRelay(true); tx.setIsRelayed(true); tx.setIsFailed(false); - tx.setIsDoubleSpend(false); - MoneroDaemonRpc._convertRpcTx(rpcBlocks.txs[blockIdx][txIdx], tx); + tx.setIsDoubleSpendSeen(false); + MoneroDaemonRpc.convertRpcTx(rpcBlocks.txs[blockIdx][txIdx], tx); } // merge into one block @@ -368,7 +283,8 @@ class MoneroDaemonRpc extends MoneroDaemon { return blocks; } - async getBlocksByRange(startHeight, endHeight) { + async getBlocksByRange(startHeight?: number, endHeight?: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlocksByRange(startHeight, endHeight); if (startHeight === undefined) startHeight = 0; if (endHeight === undefined) endHeight = await this.getHeight() - 1; let heights = []; @@ -376,13 +292,14 @@ class MoneroDaemonRpc extends MoneroDaemon { return await this.getBlocksByHeight(heights); } - async getBlocksByRangeChunked(startHeight, endHeight, maxChunkSize) { + async getBlocksByRangeChunked(startHeight?: number, endHeight?: number, maxChunkSize?: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getBlocksByRangeChunked(startHeight, endHeight, maxChunkSize); if (startHeight === undefined) startHeight = 0; if (endHeight === undefined) endHeight = await this.getHeight() - 1; let lastHeight = startHeight - 1; let blocks = []; while (lastHeight < endHeight) { - for (let block of await this._getMaxBlocks(lastHeight + 1, endHeight, maxChunkSize)) { + for (let block of await this.getMaxBlocks(lastHeight + 1, endHeight, maxChunkSize)) { blocks.push(block); } lastHeight = blocks[blocks.length - 1].getHeight(); @@ -390,21 +307,22 @@ class MoneroDaemonRpc extends MoneroDaemon { return blocks; } - async getTxs(txHashes, prune) { + async getTxs(txHashes: string[], prune = false): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getTxs(txHashes, prune); // validate input assert(Array.isArray(txHashes) && txHashes.length > 0, "Must provide an array of transaction hashes"); assert(prune === undefined || typeof prune === "boolean", "Prune must be a boolean or undefined"); // fetch transactions - let resp = await this.rpc.sendPathRequest("get_transactions", { + let resp = await this.config.getServer().sendPathRequest("get_transactions", { txs_hashes: txHashes, decode_as_json: true, prune: prune }); try { - MoneroDaemonRpc._checkResponseStatus(resp); - } catch (e) { + MoneroDaemonRpc.checkResponseStatus(resp); + } catch (e: any) { if (e.message.indexOf("Failed to parse hex representation of transaction hash") >= 0) throw new MoneroError("Invalid transaction hash"); throw e; } @@ -415,68 +333,74 @@ class MoneroDaemonRpc extends MoneroDaemon { for (let txIdx = 0; txIdx < resp.txs.length; txIdx++) { let tx = new MoneroTx(); tx.setIsMinerTx(false); - txs.push(MoneroDaemonRpc._convertRpcTx(resp.txs[txIdx], tx)); + txs.push(MoneroDaemonRpc.convertRpcTx(resp.txs[txIdx], tx)); } } return txs; } - async getTxHexes(txHashes, prune) { + async getTxHexes(txHashes: string[], prune = false): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getTxHexes(txHashes, prune); let hexes = []; for (let tx of await this.getTxs(txHashes, prune)) hexes.push(prune ? tx.getPrunedHex() : tx.getFullHex()); return hexes; } - async getMinerTxSum(height, numBlocks) { + async getMinerTxSum(height: number, numBlocks: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getMinerTxSum(height, numBlocks); if (height === undefined) height = 0; else assert(height >= 0, "Height must be an integer >= 0"); if (numBlocks === undefined) numBlocks = await this.getHeight(); else assert(numBlocks >= 0, "Count must be an integer >= 0"); - let resp = await this.rpc.sendJsonRequest("get_coinbase_tx_sum", {height: height, count: numBlocks}); - MoneroDaemonRpc._checkResponseStatus(resp.result); + let resp = await this.config.getServer().sendJsonRequest("get_coinbase_tx_sum", {height: height, count: numBlocks}); + MoneroDaemonRpc.checkResponseStatus(resp.result); let txSum = new MoneroMinerTxSum(); - txSum.setEmissionSum(new BigInteger(resp.result.emission_amount)); - txSum.setFeeSum(new BigInteger(resp.result.fee_amount)); + txSum.setEmissionSum(BigInt(resp.result.emission_amount)); + txSum.setFeeSum(BigInt(resp.result.fee_amount)); return txSum; } - async getFeeEstimate(graceBlocks) { - let resp = await this.rpc.sendJsonRequest("get_fee_estimate", {grace_blocks: graceBlocks}); - MoneroDaemonRpc._checkResponseStatus(resp.result); + async getFeeEstimate(graceBlocks?: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getFeeEstimate(graceBlocks); + let resp = await this.config.getServer().sendJsonRequest("get_fee_estimate", {grace_blocks: graceBlocks}); + MoneroDaemonRpc.checkResponseStatus(resp.result); let feeEstimate = new MoneroFeeEstimate(); - feeEstimate.setFee(new BigInteger(resp.result.fee)); + feeEstimate.setFee(BigInt(resp.result.fee)); let fees = []; - for (let i = 0; i < resp.result.fees.length; i++) fees.push(new BigInteger(resp.result.fees[i])); + for (let i = 0; i < resp.result.fees.length; i++) fees.push(BigInt(resp.result.fees[i])); feeEstimate.setFees(fees); - feeEstimate.setQuantizationMask(new BigInteger(resp.result.quantization_mask)); + feeEstimate.setQuantizationMask(BigInt(resp.result.quantization_mask)); return feeEstimate; } - async submitTxHex(txHex, doNotRelay) { - let resp = await this.rpc.sendPathRequest("send_raw_transaction", {tx_as_hex: txHex, do_not_relay: doNotRelay}); - let result = MoneroDaemonRpc._convertRpcSubmitTxResult(resp); + async submitTxHex(txHex: string, doNotRelay: boolean): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.submitTxHex(txHex, doNotRelay); + let resp = await this.config.getServer().sendPathRequest("send_raw_transaction", {tx_as_hex: txHex, do_not_relay: doNotRelay}); + let result = MoneroDaemonRpc.convertRpcSubmitTxResult(resp); // set isGood based on status try { - MoneroDaemonRpc._checkResponseStatus(resp); + MoneroDaemonRpc.checkResponseStatus(resp); result.setIsGood(true); - } catch(e) { + } catch (e: any) { result.setIsGood(false); } return result; } - async relayTxsByHash(txHashes) { - let resp = await this.rpc.sendJsonRequest("relay_tx", {txids: txHashes}); - MoneroDaemonRpc._checkResponseStatus(resp.result); + async relayTxsByHash(txHashes: string[]): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.relayTxsByHash(txHashes); + let resp = await this.config.getServer().sendJsonRequest("relay_tx", {txids: txHashes}); + MoneroDaemonRpc.checkResponseStatus(resp.result); } - async getTxPool() { + async getTxPool(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getTxPool(); // send rpc request - let resp = await this.rpc.sendPathRequest("get_transaction_pool"); - MoneroDaemonRpc._checkResponseStatus(resp); + let resp = await this.config.getServer().sendPathRequest("get_transaction_pool"); + MoneroDaemonRpc.checkResponseStatus(resp); // build txs let txs = []; @@ -488,62 +412,67 @@ class MoneroDaemonRpc extends MoneroDaemon { tx.setIsMinerTx(false); tx.setInTxPool(true); tx.setNumConfirmations(0); - MoneroDaemonRpc._convertRpcTx(rpcTx, tx); + MoneroDaemonRpc.convertRpcTx(rpcTx, tx); } } return txs; } - async getTxPoolHashes() { + async getTxPoolHashes(): Promise { throw new MoneroError("Not implemented"); } - async getTxPoolBacklog() { - throw new MoneroError("Not implemented"); - } + // async getTxPoolBacklog(): Promise { + // throw new MoneroError("Not implemented"); + // } - async getTxPoolStats() { - let resp = await this.rpc.sendPathRequest("get_transaction_pool_stats"); - MoneroDaemonRpc._checkResponseStatus(resp); - return MoneroDaemonRpc._convertRpcTxPoolStats(resp.pool_stats); + async getTxPoolStats(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getTxPoolStats(); + let resp = await this.config.getServer().sendPathRequest("get_transaction_pool_stats"); + MoneroDaemonRpc.checkResponseStatus(resp); + return MoneroDaemonRpc.convertRpcTxPoolStats(resp.pool_stats); } - async flushTxPool(hashes) { + async flushTxPool(hashes?: string | string[]): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.flushTxPool(hashes); if (hashes) hashes = GenUtils.listify(hashes); - let resp = await this.rpc.sendJsonRequest("flush_txpool", {txids: hashes}); - MoneroDaemonRpc._checkResponseStatus(resp.result); + let resp = await this.config.getServer().sendJsonRequest("flush_txpool", {txids: hashes}); + MoneroDaemonRpc.checkResponseStatus(resp.result); } - async getKeyImageSpentStatuses(keyImages) { + async getKeyImageSpentStatuses(keyImages: string[]): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getKeyImageSpentStatuses(keyImages); if (keyImages === undefined || keyImages.length === 0) throw new MoneroError("Must provide key images to check the status of"); - let resp = await this.rpc.sendPathRequest("is_key_image_spent", {key_images: keyImages}); - MoneroDaemonRpc._checkResponseStatus(resp); + let resp = await this.config.getServer().sendPathRequest("is_key_image_spent", {key_images: keyImages}); + MoneroDaemonRpc.checkResponseStatus(resp); return resp.spent_status; } - async getOutputHistogram(amounts, minCount, maxCount, isUnlocked, recentCutoff) { + async getOutputHistogram(amounts?: bigint[], minCount?: number, maxCount?: number, isUnlocked?: boolean, recentCutoff?: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getOutputHistogram(amounts, minCount, maxCount, isUnlocked, recentCutoff); // send rpc request - let resp = await this.rpc.sendJsonRequest("get_output_histogram", { + let resp = await this.config.getServer().sendJsonRequest("get_output_histogram", { amounts: amounts, min_count: minCount, max_count: maxCount, unlocked: isUnlocked, recent_cutoff: recentCutoff }); - MoneroDaemonRpc._checkResponseStatus(resp.result); + MoneroDaemonRpc.checkResponseStatus(resp.result); // build histogram entries from response let entries = []; if (!resp.result.histogram) return entries; for (let rpcEntry of resp.result.histogram) { - entries.push(MoneroDaemonRpc._convertRpcOutputHistogramEntry(rpcEntry)); + entries.push(MoneroDaemonRpc.convertRpcOutputHistogramEntry(rpcEntry)); } return entries; } async getOutputDistribution(amounts, cumulative, startHeight, endHeight) { + if (this.config.proxyToWorker) return this.proxyDaemon.getOutputDistribution(amounts, cumulative, startHeight, endHeight); throw new MoneroError("Not implemented (response 'distribution' field is binary)"); // let amountStrs = []; @@ -556,7 +485,7 @@ class MoneroDaemonRpc extends MoneroDaemon { // // send rpc request // console.log("*********** SENDING REQUEST *************"); // if (startHeight === undefined) startHeight = 0; -// let resp = await this.rpc.sendJsonRequest("get_output_distribution", { +// let resp = await this.config.getServer().sendJsonRequest("get_output_distribution", { // amounts: amountStrs, // cumulative: cumulative, // from_height: startHeight, @@ -570,31 +499,35 @@ class MoneroDaemonRpc extends MoneroDaemon { // let entries = []; // if (!resp.result.distributions) return entries; // for (let rpcEntry of resp.result.distributions) { -// let entry = MoneroDaemonRpc._convertRpcOutputDistributionEntry(rpcEntry); +// let entry = MoneroDaemonRpc.convertRpcOutputDistributionEntry(rpcEntry); // entries.push(entry); // } // return entries; } - async getInfo() { - let resp = await this.rpc.sendJsonRequest("get_info"); - MoneroDaemonRpc._checkResponseStatus(resp.result); - return MoneroDaemonRpc._convertRpcInfo(resp.result); + async getInfo(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getInfo(); + let resp = await this.config.getServer().sendJsonRequest("get_info"); + MoneroDaemonRpc.checkResponseStatus(resp.result); + return MoneroDaemonRpc.convertRpcInfo(resp.result); } - async getSyncInfo() { - let resp = await this.rpc.sendJsonRequest("sync_info"); - MoneroDaemonRpc._checkResponseStatus(resp.result); - return MoneroDaemonRpc._convertRpcSyncInfo(resp.result); + async getSyncInfo(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getSyncInfo(); + let resp = await this.config.getServer().sendJsonRequest("sync_info"); + MoneroDaemonRpc.checkResponseStatus(resp.result); + return MoneroDaemonRpc.convertRpcSyncInfo(resp.result); } - async getHardForkInfo() { - let resp = await this.rpc.sendJsonRequest("hard_fork_info"); - MoneroDaemonRpc._checkResponseStatus(resp.result); - return MoneroDaemonRpc._convertRpcHardForkInfo(resp.result); + async getHardForkInfo(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getHardForkInfo(); + let resp = await this.config.getServer().sendJsonRequest("hard_fork_info"); + MoneroDaemonRpc.checkResponseStatus(resp.result); + return MoneroDaemonRpc.convertRpcHardForkInfo(resp.result); } - async getAltChains() { + async getAltChains(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getAltChains(); // // mocked response for test // let resp = { @@ -609,15 +542,16 @@ class MoneroDaemonRpc extends MoneroDaemon { // ] // } - let resp = await this.rpc.sendJsonRequest("get_alternate_chains"); - MoneroDaemonRpc._checkResponseStatus(resp.result); + let resp = await this.config.getServer().sendJsonRequest("get_alternate_chains"); + MoneroDaemonRpc.checkResponseStatus(resp.result); let chains = []; if (!resp.result.chains) return chains; - for (let rpcChain of resp.result.chains) chains.push(MoneroDaemonRpc._convertRpcAltChain(rpcChain)); + for (let rpcChain of resp.result.chains) chains.push(MoneroDaemonRpc.convertRpcAltChain(rpcChain)); return chains; } - async getAltBlockHashes() { + async getAltBlockHashes(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getAltBlockHashes(); // // mocked response for test // let resp = { @@ -626,69 +560,77 @@ class MoneroDaemonRpc extends MoneroDaemon { // blks_hashes: ["9c2277c5470234be8b32382cdf8094a103aba4fcd5e875a6fc159dc2ec00e011","637c0e0f0558e284493f38a5fcca3615db59458d90d3a5eff0a18ff59b83f46f","6f3adc174a2e8082819ebb965c96a095e3e8b63929ad9be2d705ad9c086a6b1c","697cf03c89a9b118f7bdf11b1b3a6a028d7b3617d2d0ed91322c5709acf75625"] // } - let resp = await this.rpc.sendPathRequest("get_alt_blocks_hashes"); - MoneroDaemonRpc._checkResponseStatus(resp); + let resp = await this.config.getServer().sendPathRequest("get_alt_blocks_hashes"); + MoneroDaemonRpc.checkResponseStatus(resp); if (!resp.blks_hashes) return []; return resp.blks_hashes; } - async getDownloadLimit() { - return (await this._getBandwidthLimits())[0]; + async getDownloadLimit(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getDownloadLimit(); + return (await this.getBandwidthLimits())[0]; } - async setDownloadLimit(limit) { + async setDownloadLimit(limit: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.setDownloadLimit(limit); if (limit == -1) return await this.resetDownloadLimit(); if (!(GenUtils.isInt(limit) && limit > 0)) throw new MoneroError("Download limit must be an integer greater than 0"); - return (await this._setBandwidthLimits(limit, 0))[0]; + return (await this.setBandwidthLimits(limit, 0))[0]; } - async resetDownloadLimit() { - return (await this._setBandwidthLimits(-1, 0))[0]; + async resetDownloadLimit(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.resetDownloadLimit(); + return (await this.setBandwidthLimits(-1, 0))[0]; } - async getUploadLimit() { - return (await this._getBandwidthLimits())[1]; + async getUploadLimit(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getUploadLimit(); + return (await this.getBandwidthLimits())[1]; } - async setUploadLimit(limit) { + async setUploadLimit(limit: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.setUploadLimit(limit); if (limit == -1) return await this.resetUploadLimit(); if (!(GenUtils.isInt(limit) && limit > 0)) throw new MoneroError("Upload limit must be an integer greater than 0"); - return (await this._setBandwidthLimits(0, limit))[1]; + return (await this.setBandwidthLimits(0, limit))[1]; } - async resetUploadLimit() { - return (await this._setBandwidthLimits(0, -1))[1]; + async resetUploadLimit(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.resetUploadLimit(); + return (await this.setBandwidthLimits(0, -1))[1]; } - async getPeers() { - let resp = await this.rpc.sendJsonRequest("get_connections"); - MoneroDaemonRpc._checkResponseStatus(resp.result); + async getPeers(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getPeers(); + let resp = await this.config.getServer().sendJsonRequest("get_connections"); + MoneroDaemonRpc.checkResponseStatus(resp.result); let peers = []; if (!resp.result.connections) return peers; for (let rpcConnection of resp.result.connections) { - peers.push(MoneroDaemonRpc._convertRpcConnection(rpcConnection)); + peers.push(MoneroDaemonRpc.convertRpcConnection(rpcConnection)); } return peers; } - async getKnownPeers() { + async getKnownPeers(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getKnownPeers(); // tx config - let resp = await this.rpc.sendPathRequest("get_peer_list"); - MoneroDaemonRpc._checkResponseStatus(resp); + let resp = await this.config.getServer().sendPathRequest("get_peer_list"); + MoneroDaemonRpc.checkResponseStatus(resp); // build peers let peers = []; if (resp.gray_list) { for (let rpcPeer of resp.gray_list) { - let peer = MoneroDaemonRpc._convertRpcPeer(rpcPeer); + let peer = MoneroDaemonRpc.convertRpcPeer(rpcPeer); peer.setIsOnline(false); // gray list means offline last checked peers.push(peer); } } if (resp.white_list) { for (let rpcPeer of resp.white_list) { - let peer = MoneroDaemonRpc._convertRpcPeer(rpcPeer); + let peer = MoneroDaemonRpc.convertRpcPeer(rpcPeer); peer.setIsOnline(true); // white list means online last checked peers.push(peer); } @@ -696,21 +638,24 @@ class MoneroDaemonRpc extends MoneroDaemon { return peers; } - async setOutgoingPeerLimit(limit) { + async setOutgoingPeerLimit(limit: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.setOutgoingPeerLimit(limit); if (!(GenUtils.isInt(limit) && limit >= 0)) throw new MoneroError("Outgoing peer limit must be >= 0"); - let resp = await this.rpc.sendPathRequest("out_peers", {out_peers: limit}); - MoneroDaemonRpc._checkResponseStatus(resp); + let resp = await this.config.getServer().sendPathRequest("out_peers", {out_peers: limit}); + MoneroDaemonRpc.checkResponseStatus(resp); } - async setIncomingPeerLimit(limit) { + async setIncomingPeerLimit(limit: number): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.setIncomingPeerLimit(limit); if (!(GenUtils.isInt(limit) && limit >= 0)) throw new MoneroError("Incoming peer limit must be >= 0"); - let resp = await this.rpc.sendPathRequest("in_peers", {in_peers: limit}); - MoneroDaemonRpc._checkResponseStatus(resp); + let resp = await this.config.getServer().sendPathRequest("in_peers", {in_peers: limit}); + MoneroDaemonRpc.checkResponseStatus(resp); } - async getPeerBans() { - let resp = await this.rpc.sendJsonRequest("get_bans"); - MoneroDaemonRpc._checkResponseStatus(resp.result); + async getPeerBans(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getPeerBans(); + let resp = await this.config.getServer().sendJsonRequest("get_bans"); + MoneroDaemonRpc.checkResponseStatus(resp.result); let bans = []; for (let rpcBan of resp.result.bans) { let ban = new MoneroBan(); @@ -722,71 +667,81 @@ class MoneroDaemonRpc extends MoneroDaemon { return bans; } - async setPeerBans(bans) { + async setPeerBans(bans: MoneroBan[]): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.setPeerBans(bans); let rpcBans = []; - for (let ban of bans) rpcBans.push(MoneroDaemonRpc._convertToRpcBan(ban)); - let resp = await this.rpc.sendJsonRequest("set_bans", {bans: rpcBans}); - MoneroDaemonRpc._checkResponseStatus(resp.result); + for (let ban of bans) rpcBans.push(MoneroDaemonRpc.convertToRpcBan(ban)); + let resp = await this.config.getServer().sendJsonRequest("set_bans", {bans: rpcBans}); + MoneroDaemonRpc.checkResponseStatus(resp.result); } - async startMining(address, numThreads, isBackground, ignoreBattery) { + async startMining(address: string, numThreads?: number, isBackground?: boolean, ignoreBattery?: boolean): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.startMining(address, numThreads, isBackground, ignoreBattery); assert(address, "Must provide address to mine to"); assert(GenUtils.isInt(numThreads) && numThreads > 0, "Number of threads must be an integer greater than 0"); assert(isBackground === undefined || typeof isBackground === "boolean"); assert(ignoreBattery === undefined || typeof ignoreBattery === "boolean"); - let resp = await this.rpc.sendPathRequest("start_mining", { + let resp = await this.config.getServer().sendPathRequest("start_mining", { miner_address: address, threads_count: numThreads, do_background_mining: isBackground, ignore_battery: ignoreBattery, }); - MoneroDaemonRpc._checkResponseStatus(resp); + MoneroDaemonRpc.checkResponseStatus(resp); } - async stopMining() { - let resp = await this.rpc.sendPathRequest("stop_mining"); - MoneroDaemonRpc._checkResponseStatus(resp); + async stopMining(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.stopMining(); + let resp = await this.config.getServer().sendPathRequest("stop_mining"); + MoneroDaemonRpc.checkResponseStatus(resp); } - async getMiningStatus() { - let resp = await this.rpc.sendPathRequest("mining_status"); - MoneroDaemonRpc._checkResponseStatus(resp); - return MoneroDaemonRpc._convertRpcMiningStatus(resp); + async getMiningStatus(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.getMiningStatus(); + let resp = await this.config.getServer().sendPathRequest("mining_status"); + MoneroDaemonRpc.checkResponseStatus(resp); + return MoneroDaemonRpc.convertRpcMiningStatus(resp); } - async submitBlocks(blockBlobs) { + async submitBlocks(blockBlobs: string[]): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.submitBlocks(); assert(Array.isArray(blockBlobs) && blockBlobs.length > 0, "Must provide an array of mined block blobs to submit"); - let resp = await this.rpc.sendJsonRequest("submit_block", blockBlobs); - MoneroDaemonRpc._checkResponseStatus(resp.result); + let resp = await this.config.getServer().sendJsonRequest("submit_block", blockBlobs); + MoneroDaemonRpc.checkResponseStatus(resp.result); } - async pruneBlockchain(check) { - let resp = await this.rpc.sendJsonRequest("prune_blockchain", {check: check}, 0); - MoneroDaemonRpc._checkResponseStatus(resp.result); + async pruneBlockchain(check: boolean): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.pruneBlockchain(); + let resp = await this.config.getServer().sendJsonRequest("prune_blockchain", {check: check}, 0); + MoneroDaemonRpc.checkResponseStatus(resp.result); let result = new MoneroPruneResult(); result.setIsPruned(resp.result.pruned); result.setPruningSeed(resp.result.pruning_seed); return result; } - async checkForUpdate() { - let resp = await this.rpc.sendPathRequest("update", {command: "check"}); - MoneroDaemonRpc._checkResponseStatus(resp); - return MoneroDaemonRpc._convertRpcUpdateCheckResult(resp); + async checkForUpdate(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.checkForUpdate(); + let resp = await this.config.getServer().sendPathRequest("update", {command: "check"}); + MoneroDaemonRpc.checkResponseStatus(resp); + return MoneroDaemonRpc.convertRpcUpdateCheckResult(resp); } - async downloadUpdate(path) { - let resp = await this.rpc.sendPathRequest("update", {command: "download", path: path}); - MoneroDaemonRpc._checkResponseStatus(resp); - return MoneroDaemonRpc._convertRpcUpdateDownloadResult(resp); + async downloadUpdate(path?: string): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.downloadUpdate(path); + let resp = await this.config.getServer().sendPathRequest("update", {command: "download", path: path}); + MoneroDaemonRpc.checkResponseStatus(resp); + return MoneroDaemonRpc.convertRpcUpdateDownloadResult(resp); } - async stop() { - let resp = await this.rpc.sendPathRequest("stop_daemon"); - MoneroDaemonRpc._checkResponseStatus(resp); + async stop(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.stop(); + let resp = await this.config.getServer().sendPathRequest("stop_daemon"); + MoneroDaemonRpc.checkResponseStatus(resp); } - async waitForNextBlockHeader() { + async waitForNextBlockHeader(): Promise { + if (this.config.proxyToWorker) return this.proxyDaemon.waitForNextBlockHeader(); let that = this; return new Promise(async function(resolve) { await that.addListener(new class extends MoneroDaemonListener { @@ -797,33 +752,36 @@ class MoneroDaemonRpc extends MoneroDaemon { }); }); } + + getPollInterval(): number { + return this.config.pollInterval; + } // ----------- ADD JSDOC FOR SUPPORTED DEFAULT IMPLEMENTATIONS -------------- - - async getTx() { return super.getTx(...arguments); } - async getTxHex() { return super.getTxHex(...arguments); } - async getKeyImageSpentStatus() { return super.getKeyImageSpentStatus(...arguments); } - async setPeerBan() { return super.setPeerBan(...arguments); } - async submitBlock() { return super.submitBlock(...arguments); } + async getTx(txHash?: string, prune = false): Promise { return super.getTx(txHash, prune); }; + async getTxHex(txHash: string, prune = false): Promise { return super.getTxHex(txHash, prune); }; + async getKeyImageSpentStatus(keyImage: string): Promise { return super.getKeyImageSpentStatus(keyImage); } + async setPeerBan(ban: MoneroBan): Promise { return super.setPeerBan(ban); } + async submitBlock(blockBlob: string): Promise { return super.submitBlock(blockBlob); } // ------------------------------- PRIVATE ---------------------------------- - - _refreshListening() { + + protected refreshListening() { if (this.pollListener == undefined && this.listeners.length) this.pollListener = new DaemonPoller(this); if (this.pollListener !== undefined) this.pollListener.setIsPolling(this.listeners.length > 0); } - async _getBandwidthLimits() { - let resp = await this.rpc.sendPathRequest("get_limit"); - MoneroDaemonRpc._checkResponseStatus(resp); + protected async getBandwidthLimits() { + let resp = await this.config.getServer().sendPathRequest("get_limit"); + MoneroDaemonRpc.checkResponseStatus(resp); return [resp.limit_down, resp.limit_up]; } - async _setBandwidthLimits(downLimit, upLimit) { + protected async setBandwidthLimits(downLimit, upLimit) { if (downLimit === undefined) downLimit = 0; if (upLimit === undefined) upLimit = 0; - let resp = await this.rpc.sendPathRequest("set_limit", {limit_down: downLimit, limit_up: upLimit}); - MoneroDaemonRpc._checkResponseStatus(resp); + let resp = await this.config.getServer().sendPathRequest("set_limit", {limit_down: downLimit, limit_up: upLimit}); + MoneroDaemonRpc.checkResponseStatus(resp); return [resp.limit_down, resp.limit_up]; } @@ -831,12 +789,12 @@ class MoneroDaemonRpc extends MoneroDaemon { * Get a contiguous chunk of blocks starting from a given height up to a maximum * height or amount of block data fetched from the blockchain, whichever comes first. * - * @param {number} startHeight - start height to retrieve blocks (default 0) - * @param {number} maxHeight - maximum end height to retrieve blocks (default blockchain height) - * @param {number} maxReqSize - maximum amount of block data to fetch from the blockchain in bytes (default 3,000,000 bytes) + * @param {number} [startHeight] - start height to retrieve blocks (default 0) + * @param {number} [maxHeight] - maximum end height to retrieve blocks (default blockchain height) + * @param {number} [maxReqSize] - maximum amount of block data to fetch from the blockchain in bytes (default 3,000,000 bytes) * @return {MoneroBlock[]} are the resulting chunk of blocks */ - async _getMaxBlocks(startHeight, maxHeight, maxReqSize) { + protected async getMaxBlocks(startHeight, maxHeight, maxReqSize) { if (startHeight === undefined) startHeight = 0; if (maxHeight === undefined) maxHeight = await this.getHeight() - 1; if (maxReqSize === undefined) maxReqSize = MoneroDaemonRpc.MAX_REQ_SIZE; @@ -847,7 +805,7 @@ class MoneroDaemonRpc extends MoneroDaemon { while (reqSize < maxReqSize && endHeight < maxHeight) { // get header of next block - let header = await this._getBlockHeaderByHeightCached(endHeight + 1, maxHeight); + let header = await this.getBlockHeaderByHeightCached(endHeight + 1, maxHeight); // block cannot be bigger than max request size assert(header.getSize() <= maxReqSize, "Block exceeds maximum request size: " + header.getSize()); @@ -869,7 +827,7 @@ class MoneroDaemonRpc extends MoneroDaemon { * @param {number} height - height of the header to retrieve from the cache * @param {number} maxHeight - maximum height of headers to cache */ - async _getBlockHeaderByHeightCached(height, maxHeight) { + protected async getBlockHeaderByHeightCached(height, maxHeight) { // get header from cache let cachedHeader = this.cachedHeaders[height]; @@ -887,30 +845,116 @@ class MoneroDaemonRpc extends MoneroDaemon { } // --------------------------------- STATIC --------------------------------- + + static async connectToDaemonRpc(uriOrConfig: string | Partial | Partial | string[], username?: string, password?: string): Promise { + let config = MoneroDaemonRpc.normalizeConfig(uriOrConfig, username, password); + if (config.cmd) return MoneroDaemonRpc.startMonerodProcess(config); + return new MoneroDaemonRpc(config, config.proxyToWorker ? await MoneroDaemonRpcProxy.connect(config) : undefined); + } - static _normalizeConfig(uriOrConfigOrConnection, username, password, rejectUnauthorized, pollInterval, proxyToWorker) { - let config; - if (typeof uriOrConfigOrConnection === "string") config = {uri: uriOrConfigOrConnection, username: username, password: password, proxyToWorker: proxyToWorker, rejectUnauthorized: rejectUnauthorized, pollInterval: pollInterval}; - else { - if (typeof uriOrConfigOrConnection !== "object") throw new MoneroError("Invalid configuration to create rpc client; must be string, object, or MoneroRpcConnection"); - if (username || password || rejectUnauthorized || pollInterval || proxyToWorker) throw new MoneroError("Can provide config object or params or new MoneroDaemonRpc(...) but not both"); - if (uriOrConfigOrConnection instanceof MoneroRpcConnection) config = Object.assign({}, uriOrConfigOrConnection.getConfig()); - else config = Object.assign({}, uriOrConfigOrConnection); - } - if (config.server) { - config = Object.assign(config, new MoneroRpcConnection(config.server).getConfig()); - delete config.server; + protected static async startMonerodProcess(config: MoneroDaemonConfig): Promise { + assert(GenUtils.isArray(config.cmd), "Must provide string array with command line parameters"); + + // start process + let process = require('child_process').spawn(config.cmd[0], config.cmd.slice(1), {}); + process.stdout.setEncoding('utf8'); + process.stderr.setEncoding('utf8'); + + // return promise which resolves after starting monerod + let uri; + let output = ""; + return new Promise(function(resolve, reject) { + + // handle stdout + process.stdout.on('data', async function(data) { + let line = data.toString(); + LibraryUtils.log(2, line); + output += line + '\n'; // capture output in case of error + + // extract uri from e.g. "I Binding on 127.0.0.1 (IPv4):38085" + let uriLineContains = "Binding on "; + let uriLineContainsIdx = line.indexOf(uriLineContains); + if (uriLineContainsIdx >= 0) { + let host = line.substring(uriLineContainsIdx + uriLineContains.length, line.lastIndexOf(' ')); + let unformattedLine = line.replace(/\u001b\[.*?m/g, '').trim(); // remove color formatting + let port = unformattedLine.substring(unformattedLine.lastIndexOf(':') + 1); + let sslIdx = config.cmd.indexOf("--rpc-ssl"); + let sslEnabled = sslIdx >= 0 ? "enabled" == config.cmd[sslIdx + 1].toLowerCase() : false; + uri = (sslEnabled ? "https" : "http") + "://" + host + ":" + port; + } + + // read success message + if (line.indexOf("core RPC server started ok") >= 0) { + + // get username and password from params + let userPassIdx = config.cmd.indexOf("--rpc-login"); + let userPass = userPassIdx >= 0 ? config.cmd[userPassIdx + 1] : undefined; + let username = userPass === undefined ? undefined : userPass.substring(0, userPass.indexOf(':')); + let password = userPass === undefined ? undefined : userPass.substring(userPass.indexOf(':') + 1); + + // create client connected to internal process + config = config.copy().setServer({uri: uri, username: username, password: password, rejectUnauthorized: config.getServer() ? config.getServer().getRejectUnauthorized() : undefined}); + config.setProxyToWorker(config.proxyToWorker); + config.cmd = undefined + let daemon = await MoneroDaemonRpc.connectToDaemonRpc(config); + daemon.process = process; + + // resolve promise with client connected to internal process + this.isResolved = true; + resolve(daemon); + } + }); + + // handle stderr + process.stderr.on('data', function(data) { + if (LibraryUtils.getLogLevel() >= 2) console.error(data); + }); + + // handle exit + process.on("exit", function(code) { + if (!this.isResolved) reject(new Error("monerod process terminated with exit code " + code + (output ? ":\n\n" + output : ""))); + }); + + // handle error + process.on("error", function(err) { + if (err.message.indexOf("ENOENT") >= 0) reject(new Error("monerod does not exist at path '" + config.cmd[0] + "'")); + if (!this.isResolved) reject(err); + }); + + // handle uncaught exception + process.on("uncaughtException", function(err, origin) { + console.error("Uncaught exception in monerod process: " + err.message); + console.error(origin); + reject(err); + }); + }); + } + + protected static normalizeConfig(uriOrConfig: string | Partial | Partial | string[], username?: string, password?: string): MoneroDaemonConfig { + let config: undefined | Partial = undefined; + if (typeof uriOrConfig === "string") { + config = new MoneroDaemonConfig({server: new MoneroRpcConnection(uriOrConfig as string, username, password)}); + } else if ((uriOrConfig as Partial).uri !== undefined) { + config = new MoneroDaemonConfig({server: new MoneroRpcConnection(uriOrConfig as Partial)}); + + // transfer worker proxy setting from rpc connection to daemon config + config.setProxyToWorker(config.proxyToWorker); + config.getServer().setProxyToWorker(MoneroRpcConnection.DEFAULT_CONFIG.proxyToWorker); + } else if (GenUtils.isArray(uriOrConfig)) { + config = new MoneroDaemonConfig({cmd: uriOrConfig as string[]}); + } else { + config = new MoneroDaemonConfig(uriOrConfig as Partial); } - if (config.pollInterval === undefined) config.pollInterval = 5000; // TODO: move to config if (config.proxyToWorker === undefined) config.proxyToWorker = true; - return config; + if (config.pollInterval === undefined) config.pollInterval = MoneroDaemonRpc.DEFAULT_POLL_PERIOD; + return config as MoneroDaemonConfig; } - static _checkResponseStatus(resp) { + protected static checkResponseStatus(resp) { if (resp.status !== "OK") throw new MoneroError(resp.status); } - static _convertRpcBlockHeader(rpcHeader) { + protected static convertRpcBlockHeader(rpcHeader) { if (!rpcHeader) return undefined; let header = new MoneroBlockHeader(); for (let key of Object.keys(rpcHeader)) { @@ -921,8 +965,8 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "cumulative_difficulty") { } // handled by wide_cumulative_difficulty else if (key === "difficulty_top64") { } // handled by wide_difficulty else if (key === "cumulative_difficulty_top64") { } // handled by wide_cumulative_difficulty - else if (key === "wide_difficulty") header.setDifficulty(GenUtils.reconcile(header.getDifficulty(), MoneroDaemonRpc._prefixedHexToBI(val))); - else if (key === "wide_cumulative_difficulty") header.setCumulativeDifficulty(GenUtils.reconcile(header.getCumulativeDifficulty(), MoneroDaemonRpc._prefixedHexToBI(val))); + else if (key === "wide_difficulty") header.setDifficulty(GenUtils.reconcile(header.getDifficulty(), MoneroDaemonRpc.prefixedHexToBI(val))); + else if (key === "wide_cumulative_difficulty") header.setCumulativeDifficulty(GenUtils.reconcile(header.getCumulativeDifficulty(), MoneroDaemonRpc.prefixedHexToBI(val))); else if (key === "hash") GenUtils.safeSet(header, header.getHash, header.setHash, val); else if (key === "height") GenUtils.safeSet(header, header.getHeight, header.setHeight, val); else if (key === "major_version") GenUtils.safeSet(header, header.getMajorVersion, header.setMajorVersion, val); @@ -931,7 +975,7 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "num_txes") GenUtils.safeSet(header, header.getNumTxs, header.setNumTxs, val); else if (key === "orphan_status") GenUtils.safeSet(header, header.getOrphanStatus, header.setOrphanStatus, val); else if (key === "prev_hash" || key === "prev_id") GenUtils.safeSet(header, header.getPrevHash, header.setPrevHash, val); - else if (key === "reward") GenUtils.safeSet(header, header.getReward, header.setReward, BigInteger.parse(val)); + else if (key === "reward") GenUtils.safeSet(header, header.getReward, header.setReward, BigInt(val)); else if (key === "timestamp") GenUtils.safeSet(header, header.getTimestamp, header.setTimestamp, val); else if (key === "block_weight") GenUtils.safeSet(header, header.getWeight, header.setWeight, val); else if (key === "long_term_weight") GenUtils.safeSet(header, header.getLongTermWeight, header.setLongTermWeight, val); @@ -944,10 +988,10 @@ class MoneroDaemonRpc extends MoneroDaemon { return header; } - static _convertRpcBlock(rpcBlock) { + protected static convertRpcBlock(rpcBlock) { // build block - let block = new MoneroBlock(MoneroDaemonRpc._convertRpcBlockHeader(rpcBlock.block_header ? rpcBlock.block_header : rpcBlock)); + let block = new MoneroBlock(MoneroDaemonRpc.convertRpcBlockHeader(rpcBlock.block_header ? rpcBlock.block_header : rpcBlock) as MoneroBlock); block.setHex(rpcBlock.blob); block.setTxHashes(rpcBlock.tx_hashes === undefined ? [] : rpcBlock.tx_hashes); @@ -956,8 +1000,9 @@ class MoneroDaemonRpc extends MoneroDaemon { let minerTx = new MoneroTx(); block.setMinerTx(minerTx); minerTx.setIsConfirmed(true); + minerTx.setInTxPool(false) minerTx.setIsMinerTx(true); - MoneroDaemonRpc._convertRpcTx(rpcMinerTx, minerTx); + MoneroDaemonRpc.convertRpcTx(rpcMinerTx, minerTx); return block; } @@ -969,16 +1014,12 @@ class MoneroDaemonRpc extends MoneroDaemon { * * @param rpcTx - RPC map containing transaction fields * @param tx - MoneroTx to populate with values (optional) - * @returns tx - same tx that was passed in or a new one if none given + * @return tx - same tx that was passed in or a new one if none given */ - static _convertRpcTx(rpcTx, tx) { + protected static convertRpcTx(rpcTx, tx) { if (rpcTx === undefined) return undefined; if (tx === undefined) tx = new MoneroTx(); -// console.log("******** BUILDING TX ***********"); -// console.log(rpcTx); -// console.log(tx.toString()); - // initialize from rpc map let header; for (let key of Object.keys(rpcTx)) { @@ -996,24 +1037,24 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "receive_time" || key === "received_timestamp") GenUtils.safeSet(tx, tx.getReceivedTimestamp, tx.setReceivedTimestamp, val); else if (key === "confirmations") GenUtils.safeSet(tx, tx.getNumConfirmations, tx.setNumConfirmations, val); else if (key === "in_pool") { - GenUtils.safeSet(tx, tx.isConfirmed, tx.setIsConfirmed, !val); - GenUtils.safeSet(tx, tx.inTxPool, tx.setInTxPool, val); + GenUtils.safeSet(tx, tx.getIsConfirmed, tx.setIsConfirmed, !val); + GenUtils.safeSet(tx, tx.getInTxPool, tx.setInTxPool, val); } - else if (key === "double_spend_seen") GenUtils.safeSet(tx, tx.isDoubleSpendSeen, tx.setIsDoubleSpend, val); + else if (key === "double_spend_seen") GenUtils.safeSet(tx, tx.getIsDoubleSpendSeen, tx.setIsDoubleSpendSeen, val); else if (key === "version") GenUtils.safeSet(tx, tx.getVersion, tx.setVersion, val); else if (key === "extra") { if (typeof val === "string") console.log("WARNING: extra field as string not being asigned to int[]: " + key + ": " + val); // TODO: how to set string to int[]? - or, extra is string which can encode int[] - else GenUtils.safeSet(tx, tx.getExtra, tx.setExtra, val); + else GenUtils.safeSet(tx, tx.getExtra, tx.setExtra, new Uint8Array(val)); } else if (key === "vin") { if (val.length !== 1 || !val[0].gen) { // ignore miner input TODO: why? - tx.setInputs(val.map(rpcVin => MoneroDaemonRpc._convertRpcOutput(rpcVin, tx))); + tx.setInputs(val.map(rpcVin => MoneroDaemonRpc.convertRpcOutput(rpcVin, tx))); } } - else if (key === "vout") tx.setOutputs(val.map(rpcOutput => MoneroDaemonRpc._convertRpcOutput(rpcOutput, tx))); + else if (key === "vout") tx.setOutputs(val.map(rpcOutput => MoneroDaemonRpc.convertRpcOutput(rpcOutput, tx))); else if (key === "rct_signatures") { GenUtils.safeSet(tx, tx.getRctSignatures, tx.setRctSignatures, val); - if (val.txnFee) GenUtils.safeSet(tx, tx.getFee, tx.setFee, BigInteger.parse(val.txnFee)); + if (val.txnFee) GenUtils.safeSet(tx, tx.getFee, tx.setFee, BigInt(val.txnFee)); } else if (key === "rctsig_prunable") GenUtils.safeSet(tx, tx.getRctSigPrunable, tx.setRctSigPrunable, val); else if (key === "unlock_time") GenUtils.safeSet(tx, tx.getUnlockTime, tx.setUnlockTime, val); @@ -1021,23 +1062,23 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "as_hex" || key === "tx_blob") GenUtils.safeSet(tx, tx.getFullHex, tx.setFullHex, val ? val : undefined); else if (key === "blob_size") GenUtils.safeSet(tx, tx.getSize, tx.setSize, val); else if (key === "weight") GenUtils.safeSet(tx, tx.getWeight, tx.setWeight, val); - else if (key === "fee") GenUtils.safeSet(tx, tx.getFee, tx.setFee, BigInteger.parse(val)); - else if (key === "relayed") GenUtils.safeSet(tx, tx.isRelayed, tx.setIsRelayed, val); + else if (key === "fee") GenUtils.safeSet(tx, tx.getFee, tx.setFee, BigInt(val)); + else if (key === "relayed") GenUtils.safeSet(tx, tx.getIsRelayed, tx.setIsRelayed, val); else if (key === "output_indices") GenUtils.safeSet(tx, tx.getOutputIndices, tx.setOutputIndices, val); else if (key === "do_not_relay") GenUtils.safeSet(tx, tx.getRelay, tx.setRelay, !val); - else if (key === "kept_by_block") GenUtils.safeSet(tx, tx.isKeptByBlock, tx.setIsKeptByBlock, val); + else if (key === "kept_by_block") GenUtils.safeSet(tx, tx.getIsKeptByBlock, tx.setIsKeptByBlock, val); else if (key === "signatures") GenUtils.safeSet(tx, tx.getSignatures, tx.setSignatures, val); else if (key === "last_failed_height") { - if (val === 0) GenUtils.safeSet(tx, tx.isFailed, tx.setIsFailed, false); + if (val === 0) GenUtils.safeSet(tx, tx.getIsFailed, tx.setIsFailed, false); else { - GenUtils.safeSet(tx, tx.isFailed, tx.setIsFailed, true); + GenUtils.safeSet(tx, tx.getIsFailed, tx.setIsFailed, true); GenUtils.safeSet(tx, tx.getLastFailedHeight, tx.setLastFailedHeight, val); } } else if (key === "last_failed_id_hash") { - if (val === MoneroDaemonRpc.DEFAULT_ID) GenUtils.safeSet(tx, tx.isFailed, tx.setIsFailed, false); + if (val === MoneroDaemonRpc.DEFAULT_ID) GenUtils.safeSet(tx, tx.getIsFailed, tx.setIsFailed, false); else { - GenUtils.safeSet(tx, tx.isFailed, tx.setIsFailed, true); + GenUtils.safeSet(tx, tx.getIsFailed, tx.setIsFailed, true); GenUtils.safeSet(tx, tx.getLastFailedHash, tx.setLastFailedHash, val); } } @@ -1052,47 +1093,47 @@ class MoneroDaemonRpc extends MoneroDaemon { // link block and tx if (header) tx.setBlock(new MoneroBlock(header).setTxs([tx])); - // TODO monerod: unconfirmed txs misreport block height and timestamp + // TODO monerod: unconfirmed txs misreport block height and timestamp? if (tx.getBlock() && tx.getBlock().getHeight() !== undefined && tx.getBlock().getHeight() === tx.getBlock().getTimestamp()) { tx.setBlock(undefined); tx.setIsConfirmed(false); } // initialize remaining known fields - if (tx.isConfirmed()) { - GenUtils.safeSet(tx, tx.isRelayed, tx.setIsRelayed, true); + if (tx.getIsConfirmed()) { + GenUtils.safeSet(tx, tx.getIsRelayed, tx.setIsRelayed, true); GenUtils.safeSet(tx, tx.getRelay, tx.setRelay, true); - GenUtils.safeSet(tx, tx.isFailed, tx.setIsFailed, false); + GenUtils.safeSet(tx, tx.getIsFailed, tx.setIsFailed, false); } else { tx.setNumConfirmations(0); } - if (tx.isFailed() === undefined) tx.setIsFailed(false); + if (tx.getIsFailed() === undefined) tx.setIsFailed(false); if (tx.getOutputIndices() && tx.getOutputs()) { assert.equal(tx.getOutputs().length, tx.getOutputIndices().length); for (let i = 0; i < tx.getOutputs().length; i++) { tx.getOutputs()[i].setIndex(tx.getOutputIndices()[i]); // transfer output indices to outputs } } - if (rpcTx.as_json) MoneroDaemonRpc._convertRpcTx(JSON.parse(rpcTx.as_json), tx); - if (rpcTx.tx_json) MoneroDaemonRpc._convertRpcTx(JSON.parse(rpcTx.tx_json), tx); - if (!tx.isRelayed()) tx.setLastRelayedTimestamp(undefined); // TODO monerod: returns last_relayed_timestamp despite relayed: false, self inconsistent + if (rpcTx.as_json) MoneroDaemonRpc.convertRpcTx(JSON.parse(rpcTx.as_json), tx); + if (rpcTx.tx_json) MoneroDaemonRpc.convertRpcTx(JSON.parse(rpcTx.tx_json), tx); + if (!tx.getIsRelayed()) tx.setLastRelayedTimestamp(undefined); // TODO monerod: returns last_relayed_timestamp despite relayed: false, self inconsistent // return built transaction return tx; } - static _convertRpcOutput(rpcOutput, tx) { + protected static convertRpcOutput(rpcOutput, tx) { let output = new MoneroOutput(); output.setTx(tx); for (let key of Object.keys(rpcOutput)) { let val = rpcOutput[key]; if (key === "gen") throw new MoneroError("Output with 'gen' from daemon rpc is miner tx which we ignore (i.e. each miner input is undefined)"); else if (key === "key") { - GenUtils.safeSet(output, output.getAmount, output.setAmount, new BigInteger(val.amount)); + GenUtils.safeSet(output, output.getAmount, output.setAmount, BigInt(val.amount)); GenUtils.safeSet(output, output.getKeyImage, output.setKeyImage, new MoneroKeyImage(val.k_image)); GenUtils.safeSet(output, output.getRingOutputIndices, output.setRingOutputIndices, val.key_offsets); } - else if (key === "amount") GenUtils.safeSet(output, output.getAmount, output.setAmount, BigInteger.parse(val)); + else if (key === "amount") GenUtils.safeSet(output, output.getAmount, output.setAmount, BigInt(val)); else if (key === "target") { let pubKey = val.key === undefined ? val.tagged_key.key : val.key; // TODO (monerod): rpc json uses {tagged_key={key=...}}, binary blocks use {key=...} GenUtils.safeSet(output, output.getStealthPublicKey, output.setStealthPublicKey, pubKey); @@ -1102,17 +1143,17 @@ class MoneroDaemonRpc extends MoneroDaemon { return output; } - static _convertRpcBlockTemplate(rpcTemplate) { + protected static convertRpcBlockTemplate(rpcTemplate) { let template = new MoneroBlockTemplate(); for (let key of Object.keys(rpcTemplate)) { let val = rpcTemplate[key]; if (key === "blockhashing_blob") template.setBlockTemplateBlob(val); else if (key === "blocktemplate_blob") template.setBlockHashingBlob(val); - else if (key === "difficulty") template.setDifficulty(BigInteger.parse(val)); + else if (key === "difficulty") template.setDifficulty(BigInt(val)); else if (key === "expected_reward") template.setExpectedReward(val); else if (key === "difficulty") { } // handled by wide_difficulty else if (key === "difficulty_top64") { } // handled by wide_difficulty - else if (key === "wide_difficulty") template.setDifficulty(GenUtils.reconcile(template.getDifficulty(), MoneroDaemonRpc._prefixedHexToBI(val))); + else if (key === "wide_difficulty") template.setDifficulty(GenUtils.reconcile(template.getDifficulty(), MoneroDaemonRpc.prefixedHexToBI(val))); else if (key === "height") template.setHeight(val); else if (key === "prev_hash") template.setPrevHash(val); else if (key === "reserved_offset") template.setReservedOffset(val); @@ -1127,7 +1168,7 @@ class MoneroDaemonRpc extends MoneroDaemon { return template; } - static _convertRpcInfo(rpcInfo) { + protected static convertRpcInfo(rpcInfo) { if (!rpcInfo) return undefined; let info = new MoneroDaemonInfo(); for (let key of Object.keys(rpcInfo)) { @@ -1143,9 +1184,9 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "cumulative_difficulty") { } // handled by wide_cumulative_difficulty else if (key === "difficulty_top64") { } // handled by wide_difficulty else if (key === "cumulative_difficulty_top64") { } // handled by wide_cumulative_difficulty - else if (key === "wide_difficulty") info.setDifficulty(GenUtils.reconcile(info.getDifficulty(), MoneroDaemonRpc._prefixedHexToBI(val))); - else if (key === "wide_cumulative_difficulty") info.setCumulativeDifficulty(GenUtils.reconcile(info.getCumulativeDifficulty(), MoneroDaemonRpc._prefixedHexToBI(val))); - else if (key === "free_space") info.setFreeSpace(BigInteger.parse(val)); + else if (key === "wide_difficulty") info.setDifficulty(GenUtils.reconcile(info.getDifficulty(), MoneroDaemonRpc.prefixedHexToBI(val))); + else if (key === "wide_cumulative_difficulty") info.setCumulativeDifficulty(GenUtils.reconcile(info.getCumulativeDifficulty(), MoneroDaemonRpc.prefixedHexToBI(val))); + else if (key === "free_space") info.setFreeSpace(BigInt(val)); else if (key === "database_size") info.setDatabaseSize(val); else if (key === "grey_peerlist_size") info.setNumOfflinePeers(val); else if (key === "height") info.setHeight(val); @@ -1166,11 +1207,11 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "was_bootstrap_ever_used") info.setWasBootstrapEverUsed(val); else if (key === "white_peerlist_size") info.setNumOnlinePeers(val); else if (key === "update_available") info.setUpdateAvailable(val); - else if (key === "nettype") GenUtils.safeSet(info, info.getNetworkType, info.setNetworkType, MoneroDaemon.parseNetworkType(val)); + else if (key === "nettype") GenUtils.safeSet(info, info.getNetworkType, info.setNetworkType, MoneroNetworkType.parse(val)); else if (key === "mainnet") { if (val) GenUtils.safeSet(info, info.getNetworkType, info.setNetworkType, MoneroNetworkType.MAINNET); } else if (key === "testnet") { if (val) GenUtils.safeSet(info, info.getNetworkType, info.setNetworkType, MoneroNetworkType.TESTNET); } else if (key === "stagenet") { if (val) GenUtils.safeSet(info, info.getNetworkType, info.setNetworkType, MoneroNetworkType.STAGENET); } - else if (key === "credits") info.setCredits(BigInteger.parse(val)); + else if (key === "credits") info.setCredits(BigInt(val)); else if (key === "top_block_hash" || key === "top_hash") info.setTopBlockHash(GenUtils.reconcile(info.getTopBlockHash(), "" === val ? undefined : val)) else if (key === "busy_syncing") info.setIsBusySyncing(val); else if (key === "synchronized") info.setIsSynchronized(val); @@ -1186,7 +1227,7 @@ class MoneroDaemonRpc extends MoneroDaemon { * @param rpcSyncInfo - rpc map to initialize the sync info from * @return {MoneroDaemonSyncInfo} is sync info initialized from the map */ - static _convertRpcSyncInfo(rpcSyncInfo) { + protected static convertRpcSyncInfo(rpcSyncInfo) { let syncInfo = new MoneroDaemonSyncInfo(); for (let key of Object.keys(rpcSyncInfo)) { let val = rpcSyncInfo[key]; @@ -1195,28 +1236,28 @@ class MoneroDaemonRpc extends MoneroDaemon { syncInfo.setPeers([]); let rpcConnections = val; for (let rpcConnection of rpcConnections) { - syncInfo.getPeers().push(MoneroDaemonRpc._convertRpcConnection(rpcConnection.info)); + syncInfo.getPeers().push(MoneroDaemonRpc.convertRpcConnection(rpcConnection.info)); } } else if (key === "spans") { syncInfo.setSpans([]); let rpcSpans = val; for (let rpcSpan of rpcSpans) { - syncInfo.getSpans().push(MoneroDaemonRpc._convertRpcConnectionSpan(rpcSpan)); + syncInfo.getSpans().push(MoneroDaemonRpc.convertRpcConnectionSpan(rpcSpan)); } } else if (key === "status") {} // handled elsewhere - else if (key === "target_height") syncInfo.setTargetHeight(BigInteger.parse(val)); + else if (key === "target_height") syncInfo.setTargetHeight(val); else if (key === "next_needed_pruning_seed") syncInfo.setNextNeededPruningSeed(val); else if (key === "overview") { // this returns [] without pruning let overview; try { overview = JSON.parse(val); if (overview !== undefined && overview.length > 0) console.error("Ignoring non-empty 'overview' field (not implemented): " + overview); // TODO - } catch (e) { + } catch (e: any) { console.error("Failed to parse 'overview' field: " + overview + ": " + e.message); } } - else if (key === "credits") syncInfo.setCredits(BigInteger.parse(val)); + else if (key === "credits") syncInfo.setCredits(BigInt(val)); else if (key === "top_hash") syncInfo.setTopBlockHash("" === val ? undefined : val); else if (key === "untrusted") {} // handled elsewhere else console.log("WARNING: ignoring unexpected field in sync info: " + key + ": " + val); @@ -1224,7 +1265,7 @@ class MoneroDaemonRpc extends MoneroDaemon { return syncInfo; } - static _convertRpcHardForkInfo(rpcHardForkInfo) { + protected static convertRpcHardForkInfo(rpcHardForkInfo) { let info = new MoneroHardForkInfo(); for (let key of Object.keys(rpcHardForkInfo)) { let val = rpcHardForkInfo[key]; @@ -1238,14 +1279,14 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "votes") info.setNumVotes(val); else if (key === "voting") info.setVoting(val); else if (key === "window") info.setWindow(val); - else if (key === "credits") info.setCredits(BigInteger.parse(val)); + else if (key === "credits") info.setCredits(BigInt(val)); else if (key === "top_hash") info.setTopBlockHash("" === val ? undefined : val); else console.log("WARNING: ignoring unexpected field in hard fork info: " + key + ": " + val); } return info; } - static _convertRpcConnectionSpan(rpcConnectionSpan) { + protected static convertRpcConnectionSpan(rpcConnectionSpan) { let span = new MoneroConnectionSpan(); for (let key of Object.keys(rpcConnectionSpan)) { let val = rpcConnectionSpan[key]; @@ -1261,11 +1302,11 @@ class MoneroDaemonRpc extends MoneroDaemon { return span; } - static _convertRpcOutputHistogramEntry(rpcEntry) { + protected static convertRpcOutputHistogramEntry(rpcEntry) { let entry = new MoneroOutputHistogramEntry(); for (let key of Object.keys(rpcEntry)) { let val = rpcEntry[key]; - if (key === "amount") entry.setAmount(BigInteger.parse(val)); + if (key === "amount") entry.setAmount(BigInt(val)); else if (key === "total_instances") entry.setNumInstances(val); else if (key === "unlocked_instances") entry.setNumUnlockedInstances(val); else if (key === "recent_instances") entry.setNumRecentInstances(val); @@ -1274,12 +1315,12 @@ class MoneroDaemonRpc extends MoneroDaemon { return entry; } - static _convertRpcSubmitTxResult(rpcResult) { + protected static convertRpcSubmitTxResult(rpcResult) { assert(rpcResult); let result = new MoneroSubmitTxResult(); for (let key of Object.keys(rpcResult)) { let val = rpcResult[key]; - if (key === "double_spend") result.setIsDoubleSpend(val); + if (key === "double_spend") result.setIsDoubleSpendSeen(val); else if (key === "fee_too_low") result.setIsFeeTooLow(val); else if (key === "invalid_input") result.setHasInvalidInput(val); else if (key === "invalid_output") result.setHasInvalidOutput(val); @@ -1290,7 +1331,7 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "reason") result.setReason(val === "" ? undefined : val); else if (key === "too_big") result.setIsTooBig(val); else if (key === "sanity_check_failed") result.setSanityCheckFailed(val); - else if (key === "credits") result.setCredits(BigInteger.parse(val)) + else if (key === "credits") result.setCredits(BigInt(val)) else if (key === "status" || key === "untrusted") {} // handled elsewhere else if (key === "top_hash") result.setTopBlockHash("" === val ? undefined : val); else if (key === "tx_extra_too_big") result.setIsTxExtraTooBig(val); @@ -1299,7 +1340,7 @@ class MoneroDaemonRpc extends MoneroDaemon { return result; } - static _convertRpcTxPoolStats(rpcStats) { + protected static convertRpcTxPoolStats(rpcStats) { assert(rpcStats); let stats = new MoneroTxPoolStats(); for (let key of Object.keys(rpcStats)) { @@ -1315,7 +1356,7 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "num_not_relayed") stats.setNumNotRelayed(val); else if (key === "oldest") stats.setOldestTimestamp(val); else if (key === "txs_total") stats.setNumTxs(val); - else if (key === "fee_total") stats.setFeeTotal(BigInteger.parse(val)); + else if (key === "fee_total") stats.setFeeTotal(BigInt(val)); else if (key === "histo") { stats.setHisto(new Map()); for (let elem of val) stats.getHisto().set(elem.bytes, elem.txs); @@ -1336,7 +1377,7 @@ class MoneroDaemonRpc extends MoneroDaemon { return stats; } - static _convertRpcAltChain(rpcChain) { + protected static convertRpcAltChain(rpcChain) { assert(rpcChain); let chain = new MoneroAltChain(); for (let key of Object.keys(rpcChain)) { @@ -1344,7 +1385,7 @@ class MoneroDaemonRpc extends MoneroDaemon { if (key === "block_hash") {} // using block_hashes instead else if (key === "difficulty") { } // handled by wide_difficulty else if (key === "difficulty_top64") { } // handled by wide_difficulty - else if (key === "wide_difficulty") chain.setDifficulty(GenUtils.reconcile(chain.getDifficulty(), MoneroDaemonRpc._prefixedHexToBI(val))); + else if (key === "wide_difficulty") chain.setDifficulty(GenUtils.reconcile(chain.getDifficulty(), MoneroDaemonRpc.prefixedHexToBI(val))); else if (key === "height") chain.setHeight(val); else if (key === "length") chain.setLength(val); else if (key === "block_hashes") chain.setBlockHashes(val); @@ -1354,25 +1395,25 @@ class MoneroDaemonRpc extends MoneroDaemon { return chain; } - static _convertRpcPeer(rpcPeer) { + protected static convertRpcPeer(rpcPeer) { assert(rpcPeer); let peer = new MoneroPeer(); for (let key of Object.keys(rpcPeer)) { let val = rpcPeer[key]; if (key === "host") peer.setHost(val); - else if (key === "id") peer.setId("" + val); // TODO monero-wallet-rpc: peer id is BigInteger but string in `get_connections` + else if (key === "id") peer.setId("" + val); // TODO monero-wallet-rpc: peer id is BigInt but string in `get_connections` else if (key === "ip") {} // host used instead which is consistently a string else if (key === "last_seen") peer.setLastSeenTimestamp(val); else if (key === "port") peer.setPort(val); else if (key === "rpc_port") peer.setRpcPort(val); else if (key === "pruning_seed") peer.setPruningSeed(val); - else if (key === "rpc_credits_per_hash") peer.setRpcCreditsPerHash(BigInteger.parse(val)); + else if (key === "rpc_credits_per_hash") peer.setRpcCreditsPerHash(BigInt(val)); else console.log("WARNING: ignoring unexpected field in rpc peer: " + key + ": " + val); } return peer; } - static _convertRpcConnection(rpcConnection) { + protected static convertRpcConnection(rpcConnection) { let peer = new MoneroPeer(); peer.setIsOnline(true); for (let key of Object.keys(rpcConnection)) { @@ -1400,23 +1441,23 @@ class MoneroDaemonRpc extends MoneroDaemon { else if (key === "state") peer.setState(val); else if (key === "support_flags") peer.setNumSupportFlags(val); else if (key === "pruning_seed") peer.setPruningSeed(val); - else if (key === "rpc_credits_per_hash") peer.setRpcCreditsPerHash(BigInteger.parse(val)); + else if (key === "rpc_credits_per_hash") peer.setRpcCreditsPerHash(BigInt(val)); else if (key === "address_type") peer.setType(val); else console.log("WARNING: ignoring unexpected field in peer: " + key + ": " + val); } return peer; } - static _convertToRpcBan(ban) { - let rpcBan = {}; + protected static convertToRpcBan(ban: MoneroBan) { + let rpcBan: any = {}; rpcBan.host = ban.getHost(); rpcBan.ip = ban.getIp(); - rpcBan.ban = ban.isBanned(); + rpcBan.ban = ban.getIsBanned(); rpcBan.seconds = ban.getSeconds(); return rpcBan; } - static _convertRpcMiningStatus(rpcStatus) { + protected static convertRpcMiningStatus(rpcStatus) { let status = new MoneroMiningStatus(); status.setIsActive(rpcStatus.active); status.setSpeed(rpcStatus.speed); @@ -1428,7 +1469,7 @@ class MoneroDaemonRpc extends MoneroDaemon { return status; } - static _convertRpcUpdateCheckResult(rpcResult) { + protected static convertRpcUpdateCheckResult(rpcResult) { assert(rpcResult); let result = new MoneroDaemonUpdateCheckResult(); for (let key of Object.keys(rpcResult)) { @@ -1450,36 +1491,43 @@ class MoneroDaemonRpc extends MoneroDaemon { return result; } - static _convertRpcUpdateDownloadResult(rpcResult) { - let result = new MoneroDaemonUpdateDownloadResult(MoneroDaemonRpc._convertRpcUpdateCheckResult(rpcResult)); + protected static convertRpcUpdateDownloadResult(rpcResult) { + let result = new MoneroDaemonUpdateDownloadResult(MoneroDaemonRpc.convertRpcUpdateCheckResult(rpcResult) as MoneroDaemonUpdateDownloadResult); result.setDownloadPath(rpcResult["path"]); if (result.getDownloadPath() === "") result.setDownloadPath(undefined); return result; } /** - * Converts a '0x' prefixed hexidecimal string to a BigInteger. + * Converts a '0x' prefixed hexidecimal string to a bigint. * - * @param hex is the '0x' prefixed hexidecimal string to convert - * @return BigInteger is the hexicedimal converted to decimal + * @param {string} hex is the '0x' prefixed hexidecimal string to convert + * @return {bigint} the hexicedimal converted to decimal */ - static _prefixedHexToBI(hex) { + protected static prefixedHexToBI(hex) { assert(hex.substring(0, 2) === "0x"); - return BigInteger.parse(hex, 16); + return BigInt(hex); } } -// static variables -MoneroDaemonRpc.DEFAULT_ID = "0000000000000000000000000000000000000000000000000000000000000000"; // uninitialized tx or block hash from daemon rpc -MoneroDaemonRpc.MAX_REQ_SIZE = "3000000"; // max request size when fetching blocks from daemon -MoneroDaemonRpc.NUM_HEADERS_PER_REQ = "750"; // number of headers to fetch and cache per request - /** * Implements a MoneroDaemon by proxying requests to a worker. * * @private */ -class MoneroDaemonRpcProxy extends MoneroDaemon { +class MoneroDaemonRpcProxy { + + // state variables + private daemonId: any; + private worker: any; + private wrappedListeners: any; + private process: any; + + constructor(daemonId, worker) { + this.daemonId = daemonId; + this.worker = worker; + this.wrappedListeners = []; + } // --------------------------- STATIC UTILITIES ----------------------------- @@ -1492,38 +1540,20 @@ class MoneroDaemonRpcProxy extends MoneroDaemon { // ---------------------------- INSTANCE METHODS ---------------------------- - constructor(daemonId, worker) { - super(); - this.daemonId = daemonId; - this.worker = worker; - this.wrappedListeners = []; - } - - async getProcess() { - return undefined; // proxy does not have access to process - } - - async stopProcess(force) { - if (this.process === undefined) throw new MoneroError("MoneroDaemonRpcProxy instance not created from new process"); - let listenersCopy = GenUtils.copyArray(this.getListeners()); - for (let listener of listenersCopy) await this.removeListener(listener); - return GenUtils.killProcess(this.process, force ? "sigkill" : undefined); - } - async addListener(listener) { let wrappedListener = new DaemonWorkerListener(listener); let listenerId = wrappedListener.getId(); - LibraryUtils.WORKER_OBJECTS[this.daemonId].callbacks["onBlockHeader_" + listenerId] = [wrappedListener.onBlockHeader, wrappedListener]; + LibraryUtils.addWorkerCallback(this.daemonId, "onBlockHeader_" + listenerId, [wrappedListener.onBlockHeader, wrappedListener]); this.wrappedListeners.push(wrappedListener); - return this._invokeWorker("daemonAddListener", [listenerId]); + return this.invokeWorker("daemonAddListener", [listenerId]); } async removeListener(listener) { for (let i = 0; i < this.wrappedListeners.length; i++) { if (this.wrappedListeners[i].getListener() === listener) { let listenerId = this.wrappedListeners[i].getId(); - await this._invokeWorker("daemonRemoveListener", [listenerId]); - delete LibraryUtils.WORKER_OBJECTS[this.daemonId].callbacks["onBlockHeader_" + listenerId]; + await this.invokeWorker("daemonRemoveListener", [listenerId]); + LibraryUtils.removeWorkerCallback(this.daemonId, "onBlockHeader_" + listenerId); this.wrappedListeners.splice(i, 1); return; } @@ -1531,114 +1561,114 @@ class MoneroDaemonRpcProxy extends MoneroDaemon { throw new MoneroError("Listener is not registered with daemon"); } - getListeners() { + async getListeners() { let listeners = []; for (let wrappedListener of this.wrappedListeners) listeners.push(wrappedListener.getListener()); return listeners; } async getRpcConnection() { - let config = await this._invokeWorker("daemonGetRpcConnection"); - return new MoneroRpcConnection(config); + let config = await this.invokeWorker("daemonGetRpcConnection"); + return new MoneroRpcConnection(config as Partial); } async isConnected() { - return this._invokeWorker("daemonIsConnected"); + return this.invokeWorker("daemonIsConnected"); } async getVersion() { - let versionJson = await this._invokeWorker("daemonGetVersion"); + let versionJson: any = await this.invokeWorker("daemonGetVersion"); return new MoneroVersion(versionJson.number, versionJson.isRelease); } async isTrusted() { - return this._invokeWorker("daemonIsTrusted"); + return this.invokeWorker("daemonIsTrusted"); } async getHeight() { - return this._invokeWorker("daemonGetHeight"); + return this.invokeWorker("daemonGetHeight"); } async getBlockHash(height) { - return this._invokeWorker("daemonGetBlockHash", Array.from(arguments)); + return this.invokeWorker("daemonGetBlockHash", Array.from(arguments)); } async getBlockTemplate(walletAddress, reserveSize) { - return new MoneroBlockTemplate(await this._invokeWorker("daemonGetBlockTemplate", Array.from(arguments))); + return new MoneroBlockTemplate(await this.invokeWorker("daemonGetBlockTemplate", Array.from(arguments))); } async getLastBlockHeader() { - return new MoneroBlockHeader(await this._invokeWorker("daemonGetLastBlockHeader")); + return new MoneroBlockHeader(await this.invokeWorker("daemonGetLastBlockHeader")); } async getBlockHeaderByHash(blockHash) { - return new MoneroBlockHeader(await this._invokeWorker("daemonGetBlockHeaderByHash", Array.from(arguments))); + return new MoneroBlockHeader(await this.invokeWorker("daemonGetBlockHeaderByHash", Array.from(arguments))); } async getBlockHeaderByHeight(height) { - return new MoneroBlockHeader(await this._invokeWorker("daemonGetBlockHeaderByHeight", Array.from(arguments))); + return new MoneroBlockHeader(await this.invokeWorker("daemonGetBlockHeaderByHeight", Array.from(arguments))); } async getBlockHeadersByRange(startHeight, endHeight) { - let blockHeadersJson = await this._invokeWorker("daemonGetBlockHeadersByRange", Array.from(arguments)); + let blockHeadersJson: any[] = await this.invokeWorker("daemonGetBlockHeadersByRange", Array.from(arguments)) as any[]; let headers = []; for (let blockHeaderJson of blockHeadersJson) headers.push(new MoneroBlockHeader(blockHeaderJson)); return headers; } async getBlockByHash(blockHash) { - return new MoneroBlock(await this._invokeWorker("daemonGetBlockByHash", Array.from(arguments))); + return new MoneroBlock(await this.invokeWorker("daemonGetBlockByHash", Array.from(arguments)), MoneroBlock.DeserializationType.TX); } async getBlocksByHash(blockHashes, startHeight, prune) { - let blocksJson = await this._invokeWorker("daemonGetBlocksByHash", Array.from(arguments)); + let blocksJson: any[] = await this.invokeWorker("daemonGetBlocksByHash", Array.from(arguments)) as any[]; let blocks = []; for (let blockJson of blocksJson) blocks.push(new MoneroBlock(blockJson)); return blocks; } async getBlockByHeight(height) { - return new MoneroBlock(await this._invokeWorker("daemonGetBlockByHeight", Array.from(arguments))); + return new MoneroBlock(await this.invokeWorker("daemonGetBlockByHeight", Array.from(arguments)), MoneroBlock.DeserializationType.TX); } async getBlocksByHeight(heights) { - let blocksJson = await this._invokeWorker("daemonGetBlocksByHeight", Array.from(arguments)); + let blocksJson: any[]= await this.invokeWorker("daemonGetBlocksByHeight", Array.from(arguments)) as any[]; let blocks = []; - for (let blockJson of blocksJson) blocks.push(new MoneroBlock(blockJson)); + for (let blockJson of blocksJson) blocks.push(new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX)); return blocks; } async getBlocksByRange(startHeight, endHeight) { - let blocksJson = await this._invokeWorker("daemonGetBlocksByRange", Array.from(arguments)); + let blocksJson: any[] = await this.invokeWorker("daemonGetBlocksByRange", Array.from(arguments)) as any[]; let blocks = []; - for (let blockJson of blocksJson) blocks.push(new MoneroBlock(blockJson)); + for (let blockJson of blocksJson) blocks.push(new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX)); return blocks; } async getBlocksByRangeChunked(startHeight, endHeight, maxChunkSize) { - let blocksJson = await this._invokeWorker("daemonGetBlocksByRangeChunked", Array.from(arguments)); + let blocksJson = await this.invokeWorker("daemonGetBlocksByRangeChunked", Array.from(arguments)) as any[]; let blocks = []; - for (let blockJson of blocksJson) blocks.push(new MoneroBlock(blockJson)); + for (let blockJson of blocksJson) blocks.push(new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX)); return blocks; } async getBlockHashes(blockHashes, startHeight) { - return this._invokeWorker("daemonGetBlockHashes", Array.from(arguments)); + return this.invokeWorker("daemonGetBlockHashes", Array.from(arguments)); } async getTxs(txHashes, prune = false) { // deserialize txs from blocks let blocks = []; - for (let blockJson of await this._invokeWorker("daemonGetTxs", Array.from(arguments))) { - blocks.push(new MoneroBlock(blockJson)); + for (let blockJson of await this.invokeWorker("daemonGetTxs", Array.from(arguments)) as any[]) { + blocks.push(new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX)); } // collect txs let txs = []; for (let block of blocks) { for (let tx of block.getTxs()) { - if (!tx.isConfirmed()) tx.setBlock(undefined); + if (!tx.getIsConfirmed()) tx.setBlock(undefined); txs.push(tx); } } @@ -1646,34 +1676,34 @@ class MoneroDaemonRpcProxy extends MoneroDaemon { } async getTxHexes(txHashes, prune = false) { - return this._invokeWorker("daemonGetTxHexes", Array.from(arguments)); + return this.invokeWorker("daemonGetTxHexes", Array.from(arguments)); } async getMinerTxSum(height, numBlocks) { - return new MoneroMinerTxSum(await this._invokeWorker("daemonGetMinerTxSum", Array.from(arguments))); + return new MoneroMinerTxSum(await this.invokeWorker("daemonGetMinerTxSum", Array.from(arguments))); } - async getFeeEstimate(graceBlocks) { - return new MoneroFeeEstimate(await this._invokeWorker("daemonGetFeeEstimate", Array.from(arguments))); + async getFeeEstimate(graceBlocks?) { + return new MoneroFeeEstimate(await this.invokeWorker("daemonGetFeeEstimate", Array.from(arguments))); } async submitTxHex(txHex, doNotRelay) { - return new MoneroSubmitTxResult(await this._invokeWorker("daemonSubmitTxHex", Array.from(arguments))); + return new MoneroSubmitTxResult(await this.invokeWorker("daemonSubmitTxHex", Array.from(arguments))); } async relayTxsByHash(txHashes) { - return this._invokeWorker("daemonRelayTxsByHash", Array.from(arguments)); + return this.invokeWorker("daemonRelayTxsByHash", Array.from(arguments)); } async getTxPool() { - let blockJson = await this._invokeWorker("daemonGetTxPool"); - let txs = new MoneroBlock(blockJson).getTxs(); + let blockJson = await this.invokeWorker("daemonGetTxPool"); + let txs = new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX).getTxs(); for (let tx of txs) tx.setBlock(undefined); return txs ? txs : []; } async getTxPoolHashes() { - return this._invokeWorker("daemonGetTxPoolHashes", Array.from(arguments)); + return this.invokeWorker("daemonGetTxPoolHashes", Array.from(arguments)); } async getTxPoolBacklog() { @@ -1681,24 +1711,24 @@ class MoneroDaemonRpcProxy extends MoneroDaemon { } async getTxPoolStats() { - return new MoneroTxPoolStats(await this._invokeWorker("daemonGetTxPoolStats")); + return new MoneroTxPoolStats(await this.invokeWorker("daemonGetTxPoolStats")); } async flushTxPool(hashes) { - return this._invokeWorker("daemonFlushTxPool", Array.from(arguments)); + return this.invokeWorker("daemonFlushTxPool", Array.from(arguments)); } async getKeyImageSpentStatuses(keyImages) { - return this._invokeWorker("daemonGetKeyImageSpentStatuses", Array.from(arguments)); + return this.invokeWorker("daemonGetKeyImageSpentStatuses", Array.from(arguments)); } - async getOutputs(outputs) { + async getOutputs(outputs): Promise { throw new MoneroError("Not implemented"); } async getOutputHistogram(amounts, minCount, maxCount, isUnlocked, recentCutoff) { let entries = []; - for (let entryJson of await this._invokeWorker("daemonGetOutputHistogram", [amounts, minCount, maxCount, isUnlocked, recentCutoff])) { + for (let entryJson of await this.invokeWorker("daemonGetOutputHistogram", [amounts, minCount, maxCount, isUnlocked, recentCutoff]) as any[]) { entries.push(new MoneroOutputHistogramEntry(entryJson)); } return entries; @@ -1709,93 +1739,93 @@ class MoneroDaemonRpcProxy extends MoneroDaemon { } async getInfo() { - return new MoneroDaemonInfo(await this._invokeWorker("daemonGetInfo")); + return new MoneroDaemonInfo(await this.invokeWorker("daemonGetInfo")); } async getSyncInfo() { - return new MoneroDaemonSyncInfo(await this._invokeWorker("daemonGetSyncInfo")); + return new MoneroDaemonSyncInfo(await this.invokeWorker("daemonGetSyncInfo")); } async getHardForkInfo() { - return new MoneroHardForkInfo(await this._invokeWorker("daemonGetHardForkInfo")); + return new MoneroHardForkInfo(await this.invokeWorker("daemonGetHardForkInfo")); } async getAltChains() { let altChains = []; - for (let altChainJson of await this._invokeWorker("daemonGetAltChains")) altChains.push(new MoneroAltChain(altChainJson)); + for (let altChainJson of await this.invokeWorker("daemonGetAltChains") as any) altChains.push(new MoneroAltChain(altChainJson)); return altChains; } async getAltBlockHashes() { - return this._invokeWorker("daemonGetAltBlockHashes"); + return this.invokeWorker("daemonGetAltBlockHashes"); } async getDownloadLimit() { - return this._invokeWorker("daemonGetDownloadLimit"); + return this.invokeWorker("daemonGetDownloadLimit"); } async setDownloadLimit(limit) { - return this._invokeWorker("daemonSetDownloadLimit", Array.from(arguments)); + return this.invokeWorker("daemonSetDownloadLimit", Array.from(arguments)); } async resetDownloadLimit() { - return this._invokeWorker("daemonResetDownloadLimit"); + return this.invokeWorker("daemonResetDownloadLimit"); } async getUploadLimit() { - return this._invokeWorker("daemonGetUploadLimit"); + return this.invokeWorker("daemonGetUploadLimit"); } async setUploadLimit(limit) { - return this._invokeWorker("daemonSetUploadLimit", Array.from(arguments)); + return this.invokeWorker("daemonSetUploadLimit", Array.from(arguments)); } async resetUploadLimit() { - return this._invokeWorker("daemonResetUploadLimit"); + return this.invokeWorker("daemonResetUploadLimit"); } async getPeers() { let peers = []; - for (let peerJson of await this._invokeWorker("daemonGetPeers")) peers.push(new MoneroPeer(peerJson)); + for (let peerJson of await this.invokeWorker("daemonGetPeers") as any) peers.push(new MoneroPeer(peerJson)); return peers; } async getKnownPeers() { let peers = []; - for (let peerJson of await this._invokeWorker("daemonGetKnownPeers")) peers.push(new MoneroPeer(peerJson)); + for (let peerJson of await this.invokeWorker("daemonGetKnownPeers") as any) peers.push(new MoneroPeer(peerJson)); return peers; } async setOutgoingPeerLimit(limit) { - return this._invokeWorker("daemonSetIncomingPeerLimit", Array.from(arguments)); + return this.invokeWorker("daemonSetIncomingPeerLimit", Array.from(arguments)); } async setIncomingPeerLimit(limit) { - return this._invokeWorker("daemonSetIncomingPeerLimit", Array.from(arguments)); + return this.invokeWorker("daemonSetIncomingPeerLimit", Array.from(arguments)); } async getPeerBans() { let bans = []; - for (let banJson of await this._invokeWorker("daemonGetPeerBans")) bans.push(new MoneroBan(banJson)); + for (let banJson of await this.invokeWorker("daemonGetPeerBans") as any) bans.push(new MoneroBan(banJson)); return bans; } async setPeerBans(bans) { let bansJson = []; for (let ban of bans) bansJson.push(ban.toJson()); - return this._invokeWorker("daemonSetPeerBans", [bansJson]); + return this.invokeWorker("daemonSetPeerBans", [bansJson]); } async startMining(address, numThreads, isBackground, ignoreBattery) { - return this._invokeWorker("daemonStartMining", Array.from(arguments)); + return this.invokeWorker("daemonStartMining", Array.from(arguments)); } async stopMining() { - await this._invokeWorker("daemonStopMining") + await this.invokeWorker("daemonStopMining") } async getMiningStatus() { - return new MoneroMiningStatus(await this._invokeWorker("daemonGetMiningStatus")); + return new MoneroMiningStatus(await this.invokeWorker("daemonGetMiningStatus")); } async submitBlocks(blockBlobs) { @@ -1803,30 +1833,30 @@ class MoneroDaemonRpcProxy extends MoneroDaemon { } async pruneBlockchain(check) { - return new MoneroPruneResult(await this._invokeWorker("daemonPruneBlockchain")); + return new MoneroPruneResult(await this.invokeWorker("daemonPruneBlockchain")); } - async checkForUpdate() { + async checkForUpdate(): Promise { throw new MoneroError("Not implemented"); } - async downloadUpdate(path) { + async downloadUpdate(path): Promise { throw new MoneroError("Not implemented"); } async stop() { - while (this.wrappedListeners.length) await this.removeBlockListener(this.wrappedListeners[0].getListener()); - return this._invokeWorker("daemonStop"); + while (this.wrappedListeners.length) await this.removeListener(this.wrappedListeners[0].getListener()); + return this.invokeWorker("daemonStop"); } async waitForNextBlockHeader() { - return new MoneroBlockHeader(await this._invokeWorker("daemonWaitForNextBlockHeader")); + return new MoneroBlockHeader(await this.invokeWorker("daemonWaitForNextBlockHeader")); } // --------------------------- PRIVATE HELPERS ------------------------------ // TODO: duplicated with MoneroWalletFullProxy - async _invokeWorker(fnName, args) { + protected async invokeWorker(fnName: string, args?: any) { return LibraryUtils.invokeWorker(this.daemonId, fnName, args); } } @@ -1834,39 +1864,43 @@ class MoneroDaemonRpcProxy extends MoneroDaemon { /** * Polls a Monero daemon for updates and notifies listeners as they occur. * - * @class - * @ignore + * @private */ class DaemonPoller { - + + protected daemon: MoneroDaemonRpc; + protected looper: TaskLooper; + protected isPolling: boolean; + protected lastHeader: MoneroBlockHeader; + constructor(daemon) { let that = this; - this._daemon = daemon; - this._looper = new TaskLooper(async function() { await that.poll(); }); + this.daemon = daemon; + this.looper = new TaskLooper(async function() { await that.poll(); }); } - setIsPolling(isPolling) { - this._isPolling = isPolling; - if (isPolling) this._looper.start(this._daemon.config.pollInterval); - else this._looper.stop(); + setIsPolling(isPolling: boolean) { + this.isPolling = isPolling; + if (isPolling) this.looper.start(this.daemon.getPollInterval()); + else this.looper.stop(); } async poll() { try { // get latest block header - let header = await this._daemon.getLastBlockHeader(); + let header = await this.daemon.getLastBlockHeader(); // save first header for comparison - if (!this._lastHeader) { - this._lastHeader = await this._daemon.getLastBlockHeader(); + if (!this.lastHeader) { + this.lastHeader = await this.daemon.getLastBlockHeader(); return; } // compare header to last - if (header.getHash() !== this._lastHeader.getHash()) { - this._lastHeader = header; - for (let listener of this._daemon.getListeners()) { + if (header.getHash() !== this.lastHeader.getHash()) { + this.lastHeader = header; + for (let listener of await this.daemon.getListeners()) { await listener.onBlockHeader(header); // notify listener } } @@ -1883,23 +1917,26 @@ class DaemonPoller { * @private */ class DaemonWorkerListener { - + + protected id: any; + protected listener: any; + constructor(listener) { - this._id = GenUtils.getUUID(); - this._listener = listener; + this.id = GenUtils.getUUID(); + this.listener = listener; } getId() { - return this._id; + return this.id; } getListener() { - return this._listener; + return this.listener; } async onBlockHeader(headerJson) { - return this._listener.onBlockHeader(new MoneroBlockHeader(headerJson)); + return this.listener.onBlockHeader(new MoneroBlockHeader(headerJson)); } } -module.exports = MoneroDaemonRpc; \ No newline at end of file +export default MoneroDaemonRpc; diff --git a/src/main/ts/daemon/model/ConnectionType.ts b/src/main/ts/daemon/model/ConnectionType.ts new file mode 100644 index 000000000..c258ca9f3 --- /dev/null +++ b/src/main/ts/daemon/model/ConnectionType.ts @@ -0,0 +1,36 @@ +import assert from "assert"; + +/** + * Enumerates connection types. + * + * Based on enums.h in monero-project. + */ +enum ConnectionType { + + /** + * Invalid connection type (value=0). + */ + INVALID = 0, + + /** + * IPV4 connection type (value=1). + */ + IPV4 = 1, + + /** + * IPV6 connection type (value=2). + */ + IPV6 = 2, + + /** + * TOR connection type (value=3). + */ + TOR = 3, + + /** + * I2P connection type (value=4). + */ + I2P = 4 +} + +export default ConnectionType; \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroAltChain.ts b/src/main/ts/daemon/model/MoneroAltChain.ts new file mode 100644 index 000000000..a1e9be47d --- /dev/null +++ b/src/main/ts/daemon/model/MoneroAltChain.ts @@ -0,0 +1,67 @@ +/** + * Models an alternative chain seen by the node. + */ +export default class MoneroAltChain { + + blockHashes: string[]; + difficulty: bigint; + height: number; + length: number; + mainChainParentBlockHash: string; + + constructor(altChain?: Partial) { + Object.assign(this, altChain); + if (this.difficulty !== undefined && typeof this.difficulty !== "bigint") this.difficulty = BigInt(this.difficulty); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getDifficulty() !== undefined) json.difficulty = this.getDifficulty().toString(); + return json; + } + + getBlockHashes(): string[] { + return this.blockHashes; + } + + setBlockHashes(blockHashes: string[]): MoneroAltChain { + this.blockHashes = blockHashes; + return this; + } + + getDifficulty(): bigint { + return this.difficulty; + } + + setDifficulty(difficulty: bigint): MoneroAltChain { + this.difficulty = difficulty; + return this; + } + + getHeight(): number { + return this.height; + } + + setHeight(height: number): MoneroAltChain { + this.height = height; + return this; + } + + getLength(): number { + return this.length; + } + + setLength(length: number): MoneroAltChain { + this.length = length; + return this; + } + + getMainChainParentBlockHash(): string { + return this.mainChainParentBlockHash; + } + + setMainChainParentBlockHash(mainChainParentBlockHash: string): MoneroAltChain { + this.mainChainParentBlockHash = mainChainParentBlockHash; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroBan.ts b/src/main/ts/daemon/model/MoneroBan.ts new file mode 100644 index 000000000..8b333b170 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroBan.ts @@ -0,0 +1,54 @@ +/** + * Monero banhammer. + */ +export default class MoneroBan { + + host: string; + ip: string; + isBanned: boolean; + seconds: number; + + constructor(ban?: Partial) { + Object.assign(this, ban); + } + + toJson(): any { + return Object.assign({}, this); + } + + getHost(): string { + return this.host; + } + + setHost(host: string): MoneroBan { + this.host = host; + return this; + } + + getIp(): string { + return this.ip; + } + + setIp(ip: string): MoneroBan { + this.ip = ip; + return this; + } + + getIsBanned(): boolean { + return this.isBanned; + } + + setIsBanned(isBanned: boolean): MoneroBan { + this.isBanned = isBanned; + return this; + } + + getSeconds(): number { + return this.seconds; + } + + setSeconds(seconds: number): MoneroBan { + this.seconds = seconds; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroBlock.ts b/src/main/ts/daemon/model/MoneroBlock.ts new file mode 100644 index 000000000..d07663d14 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroBlock.ts @@ -0,0 +1,254 @@ +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroBlockHeader from "./MoneroBlockHeader"; +import MoneroTx from "./MoneroTx"; +import MoneroTxQuery from "../../wallet/model/MoneroTxQuery"; +import MoneroTxWallet from "../../wallet/model/MoneroTxWallet"; + +/** + * Enumerates types to deserialize to. + */ +enum DeserializationType { + TX = 0, + TX_WALLET = 1, + TX_QUERY = 2 +} + +/** + * Models a Monero block in the blockchain. + */ +export default class MoneroBlock extends MoneroBlockHeader { + + static DeserializationType = DeserializationType; + + hex: string; + minerTx: MoneroTx; + txs: MoneroTx[]; + txHashes: string[]; + + constructor(block?: Partial, txType?: DeserializationType) { + super(block); + + // copy miner tx + if (this.minerTx) { + this.minerTx = this.deserializeTx(this.minerTx, txType).setBlock(this); + } + + // copy non-miner txs + if (this.txs) { + this.txs = this.txs.slice(); + for (let i = 0; i < this.txs.length; i++) { + this.txs[i] = this.deserializeTx(this.txs[i], txType).setBlock(this); + } + } + } + + getHex(): string { + return this.hex; + } + + setHex(hex: string): MoneroBlock { + this.hex = hex; + return this; + } + + getMinerTx(): MoneroTx { + return this.minerTx; + } + + setMinerTx(minerTx: MoneroTx): MoneroBlock { + this.minerTx = minerTx; + return this; + } + + getTxs(): MoneroTx[] { + return this.txs; + } + + setTxs(txs: MoneroTx[]): MoneroBlock { + this.txs = txs; + return this; + } + + getTxHashes(): string[] { + return this.txHashes; + } + + setTxHashes(txHashes: string[]): MoneroBlock { + this.txHashes = txHashes; + return this; + } + + copy(): MoneroBlock { + return new MoneroBlock(this); + } + + toJson(): any { + let json = super.toJson(); + if (this.getMinerTx() !== undefined) json.minerTx = this.getMinerTx().toJson(); + if (this.getTxs() !== undefined) { + json.txs = []; + for (let tx of this.getTxs()) json.txs.push(tx.toJson()); + } + return json; + } + + merge(block: MoneroBlock): MoneroBlock { + assert(block instanceof MoneroBlock); + if (this === block) return this; + + // merge header fields + super.merge(block); + + // merge reconcilable block extensions + this.setHex(GenUtils.reconcile(this.getHex(), block.getHex())); + this.setTxHashes(GenUtils.reconcile(this.getTxHashes(), block.getTxHashes())); + + // merge miner tx + if (this.getMinerTx() === undefined) this.setMinerTx(block.getMinerTx()); + if (block.getMinerTx() !== undefined) { + block.getMinerTx().setBlock(this); + this.getMinerTx().merge(block.getMinerTx()); + } + + // merge non-miner txs + if (block.getTxs() !== undefined) { + for (let tx of block.getTxs()) { + tx.setBlock(this); + MoneroBlock.mergeTx(this.getTxs(), tx); + } + } + + return this; + } + + toString(indent = 0): string { + let str = super.toString(indent) + "\n"; + str += GenUtils.kvLine("Hex", this.getHex(), indent); + if (this.getTxs() !== undefined) { + str += GenUtils.kvLine("Txs", "", indent); + for (let tx of this.getTxs()) { + str += tx.toString(indent + 1) + "\n"; + } + } + if (this.getMinerTx() !== undefined) { + str += GenUtils.kvLine("Miner tx", "", indent); + str += this.getMinerTx().toString(indent + 1) + "\n"; + } + str += GenUtils.kvLine("Txs hashes", this.getTxHashes(), indent); + return str[str.length - 1] === "\n" ? str.slice(0, str.length - 1) : str // strip last newline + } + + // helper to merge txs + protected static mergeTx(txs, tx) { + for (let aTx of txs) { + if (aTx.getHash() === tx.getHash()) { + aTx.merge(tx); + return; + } + } + txs.push(tx); + } + + // -------------------- OVERRIDE COVARIANT RETURN TYPES --------------------- + + setHeight(height: number): MoneroBlock { + super.setHeight(height); + return this; + } + + setTimestamp(timestamp: number): MoneroBlock { + super.setTimestamp(timestamp); + return this; + } + + setSize(size: number): MoneroBlock { + super.setSize(size); + return this; + } + + setWeight(weight: number): MoneroBlock { + super.setWeight(weight); + return this; + } + + setLongTermWeight(longTermWeight: number): MoneroBlock { + super.setLongTermWeight(longTermWeight); + return this; + } + + setDepth(depth: number): MoneroBlock { + super.setDepth(depth); + return this; + } + + setDifficulty(difficulty: bigint): MoneroBlock { + super.setDifficulty(difficulty); + return this; + } + + setCumulativeDifficulty(cumulativeDifficulty: bigint): MoneroBlock { + super.setCumulativeDifficulty(cumulativeDifficulty); + return this; + } + + setMajorVersion(majorVersion: number): MoneroBlock { + super.setMajorVersion(majorVersion); + return this; + } + + setMinorVersion(minorVersion: number): MoneroBlock { + super.setMinorVersion(minorVersion); + return this; + } + + setNonce(nonce: number): MoneroBlock { + super.setNonce(nonce); + return this; + } + + setMinerTxHash(minerTxHash: string): MoneroBlock { + super.setMinerTxHash(minerTxHash); + return this; + } + + setNumTxs(numTxs: number): MoneroBlock { + super.setNumTxs(numTxs); + return this; + } + + setOrphanStatus(orphanStatus: boolean): MoneroBlock { + super.setOrphanStatus(orphanStatus); + return this; + } + + setPrevHash(prevHash: string): MoneroBlock { + super.setPrevHash(prevHash); + return this; + } + + setReward(reward: bigint): MoneroBlock { + super.setReward(reward); + return this; + } + + setPowHash(powHash: string): MoneroBlock { + super.setPowHash(powHash); + return this; + } + + protected deserializeTx(tx: any, txType?: DeserializationType): MoneroTx { + if (txType === undefined) { + if (!(tx instanceof MoneroTx)) throw new Error("Must provide DeserializationType if tx is not instanceof MoneroTx"); + return tx.copy(); + } else if (txType === MoneroBlock.DeserializationType.TX || txType === undefined) { + return new MoneroTx(tx); + } else if (txType === MoneroBlock.DeserializationType.TX_WALLET) { + return new MoneroTxWallet(tx); + } else if (txType === MoneroBlock.DeserializationType.TX_QUERY) { + return new MoneroTxQuery(tx); + } else { + throw new Error("Unrecognized tx deserialization type: " + txType); + } + } +} diff --git a/src/main/js/daemon/model/MoneroBlockHeader.js b/src/main/ts/daemon/model/MoneroBlockHeader.ts similarity index 51% rename from src/main/js/daemon/model/MoneroBlockHeader.js rename to src/main/ts/daemon/model/MoneroBlockHeader.ts index 078c2658b..52d90dcc0 100644 --- a/src/main/js/daemon/model/MoneroBlockHeader.js +++ b/src/main/ts/daemon/model/MoneroBlockHeader.ts @@ -1,52 +1,57 @@ -const assert = require("assert"); -const BigInteger = require("../../common/biginteger").BigInteger; -const GenUtils = require("../../common/GenUtils"); +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; /** * Models a Monero block header which contains information about the block. - * - * @class */ -class MoneroBlockHeader { +export default class MoneroBlockHeader { + + hash: string; + height: number; + timestamp: number; + size: number; + weight: number; + longTermWeight: number; + depth: number; + difficulty: bigint; + cumulativeDifficulty: bigint; + majorVersion: number; + minorVersion: number; + nonce: number; + minerTxHash: string; + numTxs: number; + orphanStatus: boolean; + prevHash: string; + reward: bigint; + powHash: string; + + constructor(header?: Partial) { + Object.assign(this, header); + + // deserialize bigints + if (this.difficulty !== undefined && typeof this.difficulty !== "bigint") this.difficulty = BigInt(this.difficulty); + if (this.cumulativeDifficulty !== undefined && typeof this.cumulativeDifficulty !== "bigint") this.cumulativeDifficulty = BigInt(this.cumulativeDifficulty); + if (this.reward !== undefined && typeof this.reward !== "bigint") this.reward = BigInt(this.reward); + } - /** - * Construct the model. - * - * @param {MoneroBlockHeader|object} state is existing state to initialize from (optional) - */ - constructor(state) { - - // initialize internal state - if (!state) state = {}; - else if (state instanceof MoneroBlockHeader) state = state.toJson(); - else if (typeof state === "object") state = Object.assign({}, state); - else throw new MoneroError("state must be a MoneroBlockHeader or JavaScript object"); - this.state = state; - - // deserialize BigIntegers - if (state.difficulty !== undefined && !(state.difficulty instanceof BigInteger)) state.difficulty = BigInteger.parse(state.difficulty); - if (state.cumulativeDifficulty !== undefined && !(state.cumulativeDifficulty instanceof BigInteger)) state.cumulativeDifficulty = BigInteger.parse(state.cumulativeDifficulty); - if (state.reward !== undefined && !(state.reward instanceof BigInteger)) state.reward = BigInteger.parse(state.reward); - } - - copy() { + copy(): MoneroBlockHeader { return new MoneroBlockHeader(this); } toJson() { - let json = Object.assign({}, this.state); - if (this.getDifficulty()) json.difficulty = this.getDifficulty().toString(); - if (this.getCumulativeDifficulty()) json.cumulativeDifficulty = this.getCumulativeDifficulty().toString(); - if (this.getReward()) json.reward = this.getReward().toString(); + let json: any = Object.assign({}, this); + if (this.getDifficulty() !== undefined) json.difficulty = this.getDifficulty().toString(); + if (this.getCumulativeDifficulty() !== undefined) json.cumulativeDifficulty = this.getCumulativeDifficulty().toString(); + if (this.getReward() !== undefined) json.reward = this.getReward().toString(); return json; } getHash() { - return this.state.hash; + return this.hash; } - setHash(hash) { - this.state.hash = hash; + setHash(hash: string) { + this.hash = hash; return this; } @@ -55,8 +60,8 @@ class MoneroBlockHeader { * * @return {number} the block's height */ - getHeight() { - return this.state.height; + getHeight(): number { + return this.height; } /** @@ -65,156 +70,156 @@ class MoneroBlockHeader { * @param {number} height is the block's height to set * @return {MoneroBlockHeader} a reference to this header for chaining */ - setHeight(height) { - this.state.height = height; + setHeight(height: number): MoneroBlockHeader { + this.height = height; return this; } - getTimestamp() { - return this.state.timestamp; + getTimestamp(): number { + return this.timestamp; } - setTimestamp(timestamp) { - this.state.timestamp = timestamp; + setTimestamp(timestamp): MoneroBlockHeader { + this.timestamp = timestamp; return this; } - getSize() { - return this.state.size; + getSize(): number { + return this.size; } - setSize(size) { - this.state.size = size; + setSize(size: number): MoneroBlockHeader { + this.size = size; return this; } - getWeight() { - return this.state.weight; + getWeight(): number { + return this.weight; } - setWeight(weight) { - this.state.weight = weight; + setWeight(weight: number): MoneroBlockHeader { + this.weight = weight; return this; } - getLongTermWeight() { - return this.state.longTermWeight; + getLongTermWeight(): number { + return this.longTermWeight; } - setLongTermWeight(longTermWeight) { - this.state.longTermWeight = longTermWeight; + setLongTermWeight(longTermWeight: number): MoneroBlockHeader { + this.longTermWeight = longTermWeight; return this; } - getDepth() { - return this.state.depth; + getDepth(): number { + return this.depth; } - setDepth(depth) { - this.state.depth = depth; + setDepth(depth: number): MoneroBlockHeader { + this.depth = depth; return this; } - getDifficulty() { - return this.state.difficulty; + getDifficulty(): bigint { + return this.difficulty; } - setDifficulty(difficulty) { - this.state.difficulty = difficulty; + setDifficulty(difficulty: bigint): MoneroBlockHeader { + this.difficulty = difficulty; return this; } - getCumulativeDifficulty() { - return this.state.cumulativeDifficulty; + getCumulativeDifficulty(): bigint { + return this.cumulativeDifficulty; } - setCumulativeDifficulty(cumulativeDifficulty) { - this.state.cumulativeDifficulty = cumulativeDifficulty; + setCumulativeDifficulty(cumulativeDifficulty: bigint): MoneroBlockHeader { + this.cumulativeDifficulty = cumulativeDifficulty; return this; } - getMajorVersion() { - return this.state.majorVersion; + getMajorVersion(): number { + return this.majorVersion; } - setMajorVersion(majorVersion) { - this.state.majorVersion = majorVersion; + setMajorVersion(majorVersion: number): MoneroBlockHeader { + this.majorVersion = majorVersion; return this; } - getMinorVersion() { - return this.state.minorVersion; + getMinorVersion(): number { + return this.minorVersion; } - setMinorVersion(minorVersion) { - this.state.minorVersion = minorVersion; + setMinorVersion(minorVersion: number): MoneroBlockHeader { + this.minorVersion = minorVersion; return this; } - getNonce() { - return this.state.nonce; + getNonce(): number { + return this.nonce; } - setNonce(nonce) { - this.state.nonce = nonce; + setNonce(nonce: number): MoneroBlockHeader { + this.nonce = nonce; return this; } - getMinerTxHash() { - return this.state.minerTxHash; + getMinerTxHash(): string { + return this.minerTxHash; } - setMinerTxHash(minerTxHash) { - this.state.minerTxHash = minerTxHash; + setMinerTxHash(minerTxHash: string): MoneroBlockHeader { + this.minerTxHash = minerTxHash; return this; } - getNumTxs() { - return this.state.numTxs; + getNumTxs(): number { + return this.numTxs; } - setNumTxs(numTxs) { - this.state.numTxs = numTxs; + setNumTxs(numTxs: number): MoneroBlockHeader { + this.numTxs = numTxs; return this; } - getOrphanStatus() { - return this.state.orphanStatus; + getOrphanStatus(): boolean { + return this.orphanStatus; } - setOrphanStatus(orphanStatus) { - this.state.orphanStatus = orphanStatus; + setOrphanStatus(orphanStatus: boolean): MoneroBlockHeader { + this.orphanStatus = orphanStatus; return this; } - getPrevHash() { - return this.state.prevHash; + getPrevHash(): string { + return this.prevHash; } - setPrevHash(prevHash) { - this.state.prevHash = prevHash; + setPrevHash(prevHash: string): MoneroBlockHeader { + this.prevHash = prevHash; return this; } - getReward() { - return this.state.reward; + getReward(): bigint { + return this.reward; } - setReward(reward) { - this.state.reward = reward; + setReward(reward: bigint): MoneroBlockHeader { + this.reward = reward; return this; } - getPowHash() { - return this.state.powHash; + getPowHash(): string { + return this.powHash; } - setPowHash(powHash) { - this.state.powHash = powHash; + setPowHash(powHash: string): MoneroBlockHeader { + this.powHash = powHash; return this; } - merge(header) { + merge(header: MoneroBlockHeader): MoneroBlockHeader { assert(header instanceof MoneroBlockHeader); if (this === header) return this; this.setHash(GenUtils.reconcile(this.getHash(), header.getHash())); @@ -258,6 +263,4 @@ class MoneroBlockHeader { str += GenUtils.kvLine("Pow hash", this.getPowHash(), indent); return str[str.length - 1] === "\n" ? str.slice(0, str.length - 1) : str // strip last newline } -} - -module.exports = MoneroBlockHeader; \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroBlockTemplate.ts b/src/main/ts/daemon/model/MoneroBlockTemplate.ts new file mode 100644 index 000000000..7b630c1a3 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroBlockTemplate.ts @@ -0,0 +1,119 @@ +/** + * Monero block template to mine. + */ +export default class MoneroBlockTemplate { + + blockTemplateBlob: string; + blockHashingBlob: string; + difficulty: bigint; + expectedReward: bigint; + height: number; + prevId: string; + reservedOffset: number; + seedHeight: number; + seedHash: string; + nextSeedHash: string; + + constructor(template?: Partial) { + Object.assign(this, template); + if (this.expectedReward !== undefined && typeof this.expectedReward !== "bigint") this.expectedReward = BigInt(this.expectedReward); + if (this.difficulty !== undefined && typeof this.difficulty !== "bigint") this.difficulty = BigInt(this.difficulty); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getExpectedReward() !== undefined) json.expectedReward = this.getExpectedReward().toString(); + if (this.getDifficulty() !== undefined) json.difficulty = this.getDifficulty().toString(); + return json; + } + + getBlockTemplateBlob(): string { + return this.blockTemplateBlob; + } + + setBlockTemplateBlob(blockTemplateBlob: string): MoneroBlockTemplate { + this.blockTemplateBlob = blockTemplateBlob; + return this; + } + + getBlockHashingBlob(): string { + return this.blockHashingBlob; + } + + setBlockHashingBlob(blockHashingBlob: string): MoneroBlockTemplate { + this.blockHashingBlob = blockHashingBlob; + return this; + } + + getDifficulty(): bigint { + return this.difficulty; + } + + setDifficulty(difficulty: bigint): MoneroBlockTemplate { + this.difficulty = difficulty; + return this; + } + + getExpectedReward(): bigint { + return this.expectedReward; + } + + setExpectedReward(expectedReward: bigint): MoneroBlockTemplate { + this.expectedReward = expectedReward; + return this; + } + + getHeight(): number { + return this.height; + } + + setHeight(height: number): MoneroBlockTemplate { + this.height = height; + return this; + } + + getPrevHash(): string { + return this.prevId; + } + + setPrevHash(prevId: string): MoneroBlockTemplate { + this.prevId = prevId; + return this; + } + + getReservedOffset(): number { + return this.reservedOffset; + } + + setReservedOffset(reservedOffset: number): MoneroBlockTemplate { + this.reservedOffset = reservedOffset; + return this; + } + + getSeedHeight(): number { + return this.height; + } + + setSeedHeight(seedHeight: number): MoneroBlockTemplate { + this.seedHeight = seedHeight; + return this; + } + + getSeedHash(): string { + return this.seedHash; + } + + setSeedHash(seedHash: string): MoneroBlockTemplate { + this.seedHash = seedHash; + return this; + } + + getNextSeedHash(): string { + return this.nextSeedHash + } + + setNextSeedHash(nextSeedHash: string): MoneroBlockTemplate { + this.nextSeedHash = nextSeedHash; + return this; + } +} \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroConnectionSpan.ts b/src/main/ts/daemon/model/MoneroConnectionSpan.ts new file mode 100644 index 000000000..e523eaa3f --- /dev/null +++ b/src/main/ts/daemon/model/MoneroConnectionSpan.ts @@ -0,0 +1,84 @@ +/** + * Monero daemon connection span. + */ +export default class MoneroConnectionSpan { + + connectionId: string; + numBlocks: number; + remoteAddress: string; + rate: number; + speed: number; + size: number; + startHeight: number; + + constructor(span?: any) { + Object.assign(this, span); + } + + toJson(): any { + return Object.assign({}, this); + } + + getConnectionId(): string { + return this.connectionId; + } + + setConnectionId(connectionId: string): MoneroConnectionSpan { + this.connectionId = connectionId; + return this; + } + + getNumBlocks(): number { + return this.numBlocks; + } + + setNumBlocks(numBlocks: number): MoneroConnectionSpan { + this.numBlocks = numBlocks; + return this; + } + + getRemoteAddress(): string { + return this.remoteAddress; + } + + setRemoteAddress(remoteAddress: string): MoneroConnectionSpan { + this.remoteAddress = remoteAddress; + return this; + } + + getRate(): number { + return this.rate; + } + + setRate(rate: number): MoneroConnectionSpan { + this.rate = rate; + return this; + } + + getSpeed(): number { + return this.speed; + } + + setSpeed(speed: number): MoneroConnectionSpan { + this.speed = speed; + return this; + } + + getSize(): number { + return this.size; + } + + setSize(size: number): MoneroConnectionSpan { + this.size = size; + return this; + } + + getStartHeight(): number { + return this.startHeight; + } + + setStartHeight(startHeight: number): MoneroConnectionSpan { + this.startHeight = startHeight; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroDaemonConfig.ts b/src/main/ts/daemon/model/MoneroDaemonConfig.ts new file mode 100644 index 000000000..a4eb779ea --- /dev/null +++ b/src/main/ts/daemon/model/MoneroDaemonConfig.ts @@ -0,0 +1,74 @@ +import MoneroRpcConnection from "../../common/MoneroRpcConnection"; + +/** + * Configuration to connect to monerod. + */ +export default class MoneroDaemonConfig { + + server: string | Partial; + proxyToWorker: boolean; + cmd: string[]; + pollInterval: number; + + /** + * Construct a configuration to open or create a wallet. + * + * @param {Partial} [config] - MoneroDaemonConfig to construct from (optional) + * @param {string|Partial} [config.server] - uri or MoneroRpcConnection to the daemon (optional) + * @param {boolean} [config.proxyToWorker] - proxy daemon requests to a worker (default true) + * @param {string[]} [config.cmd] - command to start monerod (optional) + * @param {number} [config.pollInterval] - interval in milliseconds to poll the daemon for updates (default 20000) + */ + constructor(config?: Partial) { + Object.assign(this, config); + if (this.server) this.setServer(this.server); + this.setProxyToWorker(this.proxyToWorker); + } + + copy(): MoneroDaemonConfig { + return new MoneroDaemonConfig(this); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (json.server) json.server = json.server.toJson(); + return json; + } + + getServer(): MoneroRpcConnection { + return this.server as MoneroRpcConnection; + } + + setServer(server: Partial | string): MoneroDaemonConfig { + if (server && !(server instanceof MoneroRpcConnection)) server = new MoneroRpcConnection(server); + this.server = server as MoneroRpcConnection; + return this; + } + + getProxyToWorker(): boolean { + return this.proxyToWorker; + } + + setProxyToWorker(proxyToWorker: boolean): MoneroDaemonConfig { + this.proxyToWorker = proxyToWorker; + return this; + } + + getCmd(): string[] { + return this.cmd; + } + + setCmd(cmd: string[]): MoneroDaemonConfig { + this.cmd = cmd; + return this; + } + + getPollInterval(): number { + return this.pollInterval; + } + + setPollInterval(pollInterval: number): MoneroDaemonConfig { + this.pollInterval = pollInterval; + return this; + } +} \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroDaemonInfo.ts b/src/main/ts/daemon/model/MoneroDaemonInfo.ts new file mode 100644 index 000000000..512176d72 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroDaemonInfo.ts @@ -0,0 +1,353 @@ +/** + * Monero daemon info. + */ +export default class MoneroDaemonInfo { + + version: string; + numAltBlocks: number; + blockSizeLimit: number; + blockSizeMedian: number; + blockWeightLimit: number; + blockWeightMedian: number; + bootstrapDaemonAddress: string; + difficulty: bigint; + cumulativeDifficulty: bigint; + freeSpace: bigint; + numOfflinePeers: number; + numOnlinePeers: number; + height: number; + heightWithoutBootstrap: number; + networkType: string; + isOffline: boolean; + numIncomingConnections: number; + numOutgoingConnections: number; + numRpcConnections: number; + startTimestamp: number; + adjustedTimestamp: number; + target: number; + targetHeight: number; + topBlockHash: string; + numTxs: number; + numTxsPool: number; + wasBootstrapEverUsed: boolean; + databaseSize: number; + updateAvailable: boolean; + credits: bigint; + isBusySyncing: boolean; + isSynchronized: boolean; + isRestricted: boolean; + + constructor(info?: Partial) { + Object.assign(this, info); + + // deserialize bigints + if (this.difficulty !== undefined && typeof this.difficulty !== "bigint") this.difficulty = BigInt(this.difficulty); + if (this.cumulativeDifficulty !== undefined && typeof this.cumulativeDifficulty !== "bigint") this.cumulativeDifficulty = BigInt(this.cumulativeDifficulty); + if (this.credits !== undefined && typeof this.credits !== "bigint") this.credits = BigInt(this.credits); + } + + toJson(): any { + let json: any = Object.assign([], this); + if (json.difficulty !== undefined) json.difficulty = json.difficulty.toString(); + if (json.cumulativeDifficulty !== undefined) json.cumulativeDifficulty = json.cumulativeDifficulty.toString(); + if (json.credits !== undefined) json.credits = json.credits.toString(); + return json; + } + + getVersion(): string { + return this.version; + } + + setVersion(version: string): MoneroDaemonInfo { + this.version = version; + return this; + } + + getNumAltBlocks(): number { + return this.numAltBlocks; + } + + setNumAltBlocks(numAltBlocks: number): MoneroDaemonInfo { + this.numAltBlocks = numAltBlocks; + return this; + } + + getBlockSizeLimit(): number { + return this.blockSizeLimit; + } + + setBlockSizeLimit(blockSizeLimit: number): MoneroDaemonInfo { + this.blockSizeLimit = blockSizeLimit; + return this; + } + + getBlockSizeMedian(): number { + return this.blockSizeMedian; + } + + setBlockSizeMedian(blockSizeMedian: number): MoneroDaemonInfo { + this.blockSizeMedian = blockSizeMedian; + return this; + } + + getBlockWeightLimit(): number { + return this.blockWeightLimit; + } + + setBlockWeightLimit(blockWeightLimit: number): MoneroDaemonInfo { + this.blockWeightLimit = blockWeightLimit; + return this; + } + + getBlockWeightMedian(): number { + return this.blockWeightMedian; + } + + setBlockWeightMedian(blockWeightMedian: number): MoneroDaemonInfo { + this.blockWeightMedian = blockWeightMedian; + return this; + } + + getBootstrapDaemonAddress(): string { + return this.bootstrapDaemonAddress; + } + + setBootstrapDaemonAddress(bootstrapDaemonAddress): MoneroDaemonInfo { + this.bootstrapDaemonAddress = bootstrapDaemonAddress; + return this; + } + + getDifficulty(): bigint { + return this.difficulty; + } + + setDifficulty(difficulty: bigint): MoneroDaemonInfo { + this.difficulty = difficulty; + return this; + } + + getCumulativeDifficulty(): bigint { + return this.cumulativeDifficulty; + } + + setCumulativeDifficulty(cumulativeDifficulty: bigint): MoneroDaemonInfo { + this.cumulativeDifficulty = cumulativeDifficulty; + return this; + } + + getFreeSpace(): bigint { + return this.freeSpace; + } + + setFreeSpace(freeSpace: bigint): MoneroDaemonInfo { + this.freeSpace = freeSpace; + return this; + } + + getNumOfflinePeers(): number { + return this.numOfflinePeers; + } + + setNumOfflinePeers(numOfflinePeers: number): MoneroDaemonInfo { + this.numOfflinePeers = numOfflinePeers; + return this; + } + + getNumOnlinePeers(): number { + return this.numOnlinePeers; + } + + setNumOnlinePeers(numOnlinePeers: number): MoneroDaemonInfo { + this.numOnlinePeers = numOnlinePeers; + return this; + } + + getHeight(): number { + return this.height; + } + + setHeight(height: number): MoneroDaemonInfo { + this.height = height; + return this; + } + + getHeightWithoutBootstrap(): number { + return this.heightWithoutBootstrap; + } + + setHeightWithoutBootstrap(heightWithoutBootstrap: number): MoneroDaemonInfo { + this.heightWithoutBootstrap = heightWithoutBootstrap; + return this; + } + + getNetworkType(): string { + return this.networkType; + } + + setNetworkType(networkType: string) { + this.networkType = networkType; + return this; + } + + getIsOffline(): boolean { + return this.isOffline; + } + + setIsOffline(isOffline: boolean): MoneroDaemonInfo { + this.isOffline = isOffline; + return this; + } + + getNumIncomingConnections(): number { + return this.numIncomingConnections; + } + + setNumIncomingConnections(numIncomingConnections: number): MoneroDaemonInfo { + this.numIncomingConnections = numIncomingConnections; + return this; + } + + getNumOutgoingConnections(): number { + return this.numOutgoingConnections; + } + + setNumOutgoingConnections(numOutgoingConnections: number): MoneroDaemonInfo { + this.numOutgoingConnections = numOutgoingConnections; + return this; + } + + getNumRpcConnections(): number { + return this.numRpcConnections; + } + + setNumRpcConnections(numRpcConnections: number): MoneroDaemonInfo { + this.numRpcConnections = numRpcConnections; + return this; + } + + getStartTimestamp(): number { + return this.startTimestamp; + } + + setStartTimestamp(startTimestamp: number): MoneroDaemonInfo { + this.startTimestamp = startTimestamp; + return this; + } + + getAdjustedTimestamp(): number { + return this.adjustedTimestamp; + } + + setAdjustedTimestamp(adjustedTimestamp: number): MoneroDaemonInfo { + this.adjustedTimestamp = adjustedTimestamp; + return this; + } + + getTarget(): number { + return this.target; + } + + setTarget(target: number): MoneroDaemonInfo { + this.target = target; + return this; + } + + getTargetHeight(): number { + return this.targetHeight; + } + + setTargetHeight(targetHeight: number): MoneroDaemonInfo { + this.targetHeight = targetHeight; + return this; + } + + getTopBlockHash(): string { + return this.topBlockHash; + } + + setTopBlockHash(topBlockHash): MoneroDaemonInfo { + this.topBlockHash = topBlockHash; + return this; + } + + getNumTxs(): number { + return this.numTxs; + } + + setNumTxs(numTxs: number): MoneroDaemonInfo { + this.numTxs = numTxs; + return this; + } + + getNumTxsPool(): number { + return this.numTxsPool; + } + + setNumTxsPool(numTxsPool): MoneroDaemonInfo { + this.numTxsPool = numTxsPool; + return this; + } + + getWasBootstrapEverUsed(): boolean { + return this.wasBootstrapEverUsed; + } + + setWasBootstrapEverUsed(wasBootstrapEverUsed): MoneroDaemonInfo { + this.wasBootstrapEverUsed = wasBootstrapEverUsed; + return this; + } + + getDatabaseSize(): number { + return this.databaseSize; + } + + setDatabaseSize(databaseSize: number): MoneroDaemonInfo { + this.databaseSize = databaseSize; + return this; + } + + getUpdateAvailable(): boolean { + return this.updateAvailable; + } + + setUpdateAvailable(updateAvailable: boolean): MoneroDaemonInfo { + this.updateAvailable = updateAvailable; + return this; + } + + getCredits(): bigint { + return this.credits; + } + + setCredits(credits: bigint): MoneroDaemonInfo { + this.credits = credits; + return this; + } + + getIsBusySyncing(): boolean { + return this.isBusySyncing; + } + + setIsBusySyncing(isBusySyncing: boolean): MoneroDaemonInfo { + this.isBusySyncing = isBusySyncing; + return this; + } + + getIsSynchronized(): boolean { + return this.isSynchronized; + } + + setIsSynchronized(isSynchronized: boolean): MoneroDaemonInfo { + this.isSynchronized = isSynchronized; + return this; + } + + getIsRestricted(): boolean { + return this.isRestricted; + } + + setIsRestricted(isRestricted: boolean): MoneroDaemonInfo { + this.isRestricted = isRestricted; + return this; + } +} diff --git a/src/main/js/daemon/model/MoneroDaemonListener.js b/src/main/ts/daemon/model/MoneroDaemonListener.ts similarity index 63% rename from src/main/js/daemon/model/MoneroDaemonListener.js rename to src/main/ts/daemon/model/MoneroDaemonListener.ts index d80150b7c..2c0f84846 100644 --- a/src/main/js/daemon/model/MoneroDaemonListener.js +++ b/src/main/ts/daemon/model/MoneroDaemonListener.ts @@ -1,14 +1,18 @@ +import MoneroBlockHeader from "./MoneroBlockHeader"; + /** * Receives notifications as a daemon is updated. */ -class MoneroDaemonListener { +export default class MoneroDaemonListener { + + protected lastHeader: MoneroBlockHeader; /** * Called when a new block is added to the chain. * * @param {MoneroBlockHeader} header - the header of the block added to the chain */ - async onBlockHeader(header) { + async onBlockHeader(header: MoneroBlockHeader) { this.lastHeader = header; } @@ -17,9 +21,7 @@ class MoneroDaemonListener { * * @return {MoneroBlockHeader} the last notified block header */ - getLastBlockHeader() { + getLastBlockHeader(): MoneroBlockHeader { return this.lastHeader; } } - -module.exports = MoneroDaemonListener; diff --git a/src/main/ts/daemon/model/MoneroDaemonSyncInfo.ts b/src/main/ts/daemon/model/MoneroDaemonSyncInfo.ts new file mode 100644 index 000000000..f3921cc4a --- /dev/null +++ b/src/main/ts/daemon/model/MoneroDaemonSyncInfo.ts @@ -0,0 +1,126 @@ +import MoneroConnectionSpan from "./MoneroConnectionSpan"; +import MoneroPeer from "./MoneroPeer"; + +/** + * Models daemon synchronization information. + */ +export default class MoneroDaemonSyncInfo { + + height: number; + peers: MoneroPeer[]; + spans: MoneroConnectionSpan[]; + targetHeight: number; + nextNeededPruningSeed: number; + overview: string; + credits: bigint; + topBlockHash: string; + + constructor(info?: Partial) { + Object.assign(this, info); + + // deserialize bigints + if (this.credits !== undefined && typeof this.credits !== "bigint") this.credits = BigInt(this.credits); + + // copy peers + if (this.peers) { + for (let i = 0; i < this.peers.length; i++) { + this.peers[i] = new MoneroPeer(this.peers[i]); + } + } + + // copy spans + if (this.spans) { + for (let i = 0; i < this.spans.length; i++) { + this.spans[i] = new MoneroConnectionSpan(this.spans[i]); + } + } + } + + toJson() { + let json: any = Object.assign({}, this); + if (json.peers !== undefined) { + for (let i = 0; i < json.peers.length; i++) { + json.peers[i] = json.peers[i].toJson(); + } + } + if (json.spans !== undefined) { + for (let i = 0; i < json.spans.length; i++) { + json.spans[i] = json.spans[i].toJson(); + } + } + if (json.credits !== undefined) json.credits = json.credits.toString(); + return json; + } + + getHeight(): number { + return this.height; + } + + setHeight(height: number): MoneroDaemonSyncInfo { + this.height = height; + return this; + } + + getPeers(): MoneroPeer[] { + return this.peers; + } + + setPeers(peers: MoneroPeer[]): MoneroDaemonSyncInfo { + this.peers = peers; + return this; + } + + getSpans(): MoneroConnectionSpan[] { + return this.spans; + } + + setSpans(spans: MoneroConnectionSpan[]): MoneroDaemonSyncInfo { + this.spans = spans; + return this; + } + + getTargetHeight(): number { + return this.targetHeight; + } + + setTargetHeight(targetHeight: number): MoneroDaemonSyncInfo { + this.targetHeight = targetHeight; + return this; + } + + getNextNeededPruningSeed(): number { + return this.nextNeededPruningSeed; + } + + setNextNeededPruningSeed(nextNeededPruningSeed: number): MoneroDaemonSyncInfo { + this.nextNeededPruningSeed = nextNeededPruningSeed; + return this; + } + + getOverview(): string { + return this.overview; + } + + setOverview(overview: string): MoneroDaemonSyncInfo { + this.overview = overview; + return this; + } + + getCredits(): bigint { + return this.credits; + } + + setCredits(credits: bigint): MoneroDaemonSyncInfo { + this.credits = credits; + return this; + } + + getTopBlockHash(): string { + return this.topBlockHash; + } + + setTopBlockHash(topBlockHash: string): MoneroDaemonSyncInfo { + this.topBlockHash = topBlockHash; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroDaemonUpdateCheckResult.ts b/src/main/ts/daemon/model/MoneroDaemonUpdateCheckResult.ts new file mode 100644 index 000000000..e01e1ad9b --- /dev/null +++ b/src/main/ts/daemon/model/MoneroDaemonUpdateCheckResult.ts @@ -0,0 +1,85 @@ +/** + * Models the result of checking for a daemon update. + */ +export default class MoneroDaemonUpdateCheckResult { + + isUpdateAvailable: boolean; + version: string; + hash: string; + autoUri: string; + userUri: string; + + constructor(result?: MoneroDaemonUpdateCheckResult) { + Object.assign(this, result); + } + + /** + * Indicates if an update is available. + * + * @return {boolean} true if an update is available, false otherwise + */ + getIsUpdateAvailable(): boolean { + return this.isUpdateAvailable; + } + + setIsUpdateAvailable(isUpdateAvailable: boolean): MoneroDaemonUpdateCheckResult { + this.isUpdateAvailable = isUpdateAvailable; + return this; + } + + /** + * Get the update's version. + * + * @return {string} is the update's version + */ + getVersion(): string { + return this.version; + } + + setVersion(version: string): MoneroDaemonUpdateCheckResult { + this.version = version; + return this; + } + + /** + * Get the update's hash. + * + * @return {string} is the update's hash + */ + getHash(): string { + return this.hash; + } + + setHash(hash: string): MoneroDaemonUpdateCheckResult { + this.hash = hash; + return this; + } + + /** + * Get the uri to automatically download the update. + * + * @return {string} is the uri to automatically download the update + */ + getAutoUri(): string { + return this.autoUri; + } + + setAutoUri(autoUri: string): MoneroDaemonUpdateCheckResult { + this.autoUri = autoUri; + return this; + } + + /** + * Get the uri to manually download the update. + * + * @return {string} is the uri to manually download the update + */ + getUserUri(): string { + return this.userUri; + } + + setUserUri(userUri: string): MoneroDaemonUpdateCheckResult { + this.userUri = userUri; + return this; + } +} \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroDaemonUpdateDownloadResult.ts b/src/main/ts/daemon/model/MoneroDaemonUpdateDownloadResult.ts new file mode 100644 index 000000000..b7523132c --- /dev/null +++ b/src/main/ts/daemon/model/MoneroDaemonUpdateDownloadResult.ts @@ -0,0 +1,27 @@ +import MoneroDaemonUpdateCheckResult from "./MoneroDaemonUpdateCheckResult"; + +/** + * Models the result of downloading an update. + */ +export default class MoneroDaemonUpdateDownloadResult extends MoneroDaemonUpdateCheckResult { + + downloadPath: string; + + constructor(state: MoneroDaemonUpdateDownloadResult) { + super(state); + } + + /** + * Get the path the update was downloaded to. + * + * @return {string} is the path the update was downloaded to + */ + getDownloadPath(): string { + return this.downloadPath; + } + + setDownloadPath(downloadPath: string): MoneroDaemonUpdateDownloadResult { + this.downloadPath = downloadPath; + return this; + } +} \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroFeeEstimate.ts b/src/main/ts/daemon/model/MoneroFeeEstimate.ts new file mode 100644 index 000000000..3cea83323 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroFeeEstimate.ts @@ -0,0 +1,72 @@ +import GenUtils from "../../common/GenUtils"; + +/** + * Models a Monero fee estimate. + */ +export default class MoneroFeeEstimate { + + fee: bigint; + fees: bigint[]; + quantizationMask: bigint; + + constructor(feeEstimate?: Partial) { + Object.assign(this, feeEstimate); + + // deserialize + if (this.fee !== undefined && typeof this.fee !== "bigint") this.fee = BigInt(this.fee); + if (this.fees !== undefined) { + for (let i = 0; i < this.fees.length; i++) { + if (typeof this.fees[i] !== "bigint") this.fees[i] = BigInt(this.fees[i]); + } + } + if (this.quantizationMask !== undefined && typeof this.quantizationMask !== "bigint") this.quantizationMask = BigInt(this.quantizationMask); + } + + getFee(): bigint { + return this.fee; + } + + setFee(fee: bigint): MoneroFeeEstimate { + this.fee = fee; + return this; + } + + getFees(): bigint[] { + return this.fees; + } + + setFees(fees) { + this.fees = fees; + return this; + } + + getQuantizationMask(): bigint { + return this.quantizationMask; + } + + setQuantizationMask(quantizationMask): MoneroFeeEstimate { + this.quantizationMask = quantizationMask; + return this; + } + + copy(): MoneroFeeEstimate { + return new MoneroFeeEstimate(this); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getFee()) json.fee = this.getFee().toString(); + if (this.getFees()) for (let i = 0; i < this.getFees().length; i++) json.fees[i] = this.getFees()[i].toString(); + if (this.getQuantizationMask()) json.quantizationMask = this.getQuantizationMask().toString(); + return json; + } + + toString(indent = 0) { + let str = ""; + let json = this.toJson(); + str += GenUtils.kvLine("Fee", json.fee, indent); + str += GenUtils.kvLine("Fees", json.fees, indent); + str += GenUtils.kvLine("Quantization mask", json.quantizationMask, indent); + return str.slice(0, str.length - 1); // strip last newline + } +} \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroHardForkInfo.ts b/src/main/ts/daemon/model/MoneroHardForkInfo.ts new file mode 100644 index 000000000..16ba6ee4c --- /dev/null +++ b/src/main/ts/daemon/model/MoneroHardForkInfo.ts @@ -0,0 +1,117 @@ +/** + * Monero hard fork info. + */ +export default class MoneroHardForkInfo { + + earliestHeight: number; + isEnabled: boolean; + state: string; + threshold: number; + version: number; + numVotes: number; + window: number; + voting: number; + credits: bigint; + topBlockHash: string; + + constructor(info?: Partial) { + Object.assign(this, info); + if (this.credits !== undefined && typeof this.credits !== "bigint") this.credits = BigInt(this.credits); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (json.credits !== undefined) json.credits = json.credits.toString(); + return json; + } + + getEarliestHeight(): number { + return this.earliestHeight; + } + + setEarliestHeight(earliestHeight: number): MoneroHardForkInfo { + this.earliestHeight = earliestHeight; + return this; + } + + getIsEnabled(): boolean { + return this.isEnabled; + } + + setIsEnabled(isEnabled: boolean): MoneroHardForkInfo { + this.isEnabled = isEnabled; + return this; + } + + getState(): string { + return this.state; + } + + setState(state: string): MoneroHardForkInfo { + this.state = state; + return this; + } + + getThreshold(): number { + return this.threshold; + } + + setThreshold(threshold: number): MoneroHardForkInfo { + this.threshold = threshold; + return this; + } + + getVersion(): number { + return this.version; + } + + setVersion(version: number): MoneroHardForkInfo { + this.version = version; + return this; + } + + getNumVotes(): number { + return this.numVotes; + } + + setNumVotes(numVotes: number): MoneroHardForkInfo { + this.numVotes = numVotes; + return this; + } + + getWindow(): number { + return this.window; + } + + setWindow(window: number): MoneroHardForkInfo { + this.window = window; + return this; + } + + getVoting(): number { + return this.voting; + } + + setVoting(voting: number): MoneroHardForkInfo { + this.voting = voting; + return this; + } + + getCredits(): bigint{ + return this.credits; + } + + setCredits(credits: bigint): MoneroHardForkInfo { + this.credits = credits; + return this; + } + + getTopBlockHash(): string { + return this.topBlockHash; + } + + setTopBlockHash(topBlockHash: string): MoneroHardForkInfo { + this.topBlockHash = topBlockHash; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroKeyImage.ts b/src/main/ts/daemon/model/MoneroKeyImage.ts new file mode 100644 index 000000000..6ef6e272d --- /dev/null +++ b/src/main/ts/daemon/model/MoneroKeyImage.ts @@ -0,0 +1,68 @@ +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroError from "../../common/MoneroError"; + +/** + * Models a Monero key image. + */ +export default class MoneroKeyImage { + + hex: string; + signature: string; + + /** + * Construct the model. + * + * @param {string|Partial} [keyImageOrHex] is a MoneroKeyImage or hex string to initialize from (optional) + * @param {string} [signature] is the key image's signature + */ + constructor(hexOrKeyImage?: string | Partial, signature?: string) { + if (typeof hexOrKeyImage === "string") { + this.setHex(hexOrKeyImage); + this.setSignature(signature); + } else { + Object.assign(this, hexOrKeyImage); + } + } + + getHex(): string { + return this.hex; + } + + setHex(hex: string): MoneroKeyImage { + this.hex = hex; + return this; + } + + getSignature(): string { + return this.signature; + } + + setSignature(signature: string): MoneroKeyImage { + this.signature = signature; + return this; + } + + copy(): MoneroKeyImage { + return new MoneroKeyImage(this); + } + + toJson() { + return Object.assign({}, this); + } + + merge(keyImage: MoneroKeyImage): MoneroKeyImage { + assert(keyImage instanceof MoneroKeyImage); + if (keyImage === this) return this; + this.setHex(GenUtils.reconcile(this.getHex(), keyImage.getHex())); + this.setSignature(GenUtils.reconcile(this.getSignature(), keyImage.getSignature())); + return this; + } + + toString(indent = 0): string { + let str = ""; + str += GenUtils.kvLine("Hex", this.getHex(), indent); + str += GenUtils.kvLine("Signature", this.getSignature(), indent); + return str.slice(0, str.length - 1); // strip last newline + } +} diff --git a/src/main/ts/daemon/model/MoneroKeyImageSpentStatus.ts b/src/main/ts/daemon/model/MoneroKeyImageSpentStatus.ts new file mode 100644 index 000000000..d6d1eba76 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroKeyImageSpentStatus.ts @@ -0,0 +1,24 @@ +/** + * Enumerates connection types. + * + * Based on enums.h in monero-project. + */ +enum MoneroKeyImageSpentStatus { + + /** + * Key image is not spent (value=0). + */ + NOT_SPENT = 0, + + /** + * Key image is confirmed (value=1). + */ + CONFIRMED = 1, + + /** + * Key image is in the pool (value=2). + */ + TX_POOL = 2 + } + + export default MoneroKeyImageSpentStatus; \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroMinerTxSum.ts b/src/main/ts/daemon/model/MoneroMinerTxSum.ts new file mode 100644 index 000000000..fb9f6f078 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroMinerTxSum.ts @@ -0,0 +1,41 @@ +/** + * Model for the summation of miner emissions and fees. + */ +export default class MoneroMinerTxSum { + + emissionSum: bigint; + feeSum: bigint; + + constructor(txSum?: Partial) { + Object.assign(this, txSum); + + // deserialize bigints + if (this.emissionSum !== undefined && typeof this.emissionSum !== "bigint") this.emissionSum = BigInt(this.emissionSum); + if (this.feeSum !== undefined && typeof this.feeSum !== "bigint") this.feeSum = BigInt(this.feeSum); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getEmissionSum() !== undefined) json.emissionSum = this.getEmissionSum().toString(); + if (this.getFeeSum() !== undefined) json.feeSum = this.getFeeSum().toString(); + return json; + } + + getEmissionSum(): bigint { + return this.emissionSum; + } + + setEmissionSum(emissionSum: bigint): MoneroMinerTxSum { + this.emissionSum = emissionSum; + return this; + } + + getFeeSum(): bigint { + return this.feeSum; + } + + setFeeSum(feeSum: bigint): MoneroMinerTxSum { + this.feeSum = feeSum; + return this; + } +} \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroMiningStatus.ts b/src/main/ts/daemon/model/MoneroMiningStatus.ts new file mode 100644 index 000000000..59c27afe1 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroMiningStatus.ts @@ -0,0 +1,64 @@ +/** + * Models daemon mining status. + */ +export default class MoneroMiningStatus { + + isActive: boolean; + address: string; + speed: number; + numThreads: number; + isBackground: boolean; + + constructor(status?: Partial) { + Object.assign(this, status); + } + + toJson() { + return Object.assign({}, this); + } + + getIsActive(): boolean { + return this.isActive; + } + + setIsActive(isActive: boolean): MoneroMiningStatus { + this.isActive = isActive; + return this; + } + + getAddress(): string { + return this.address; + } + + setAddress(address: string): MoneroMiningStatus { + this.address = address; + return this; + } + + getSpeed(): number { + return this.speed; + } + + setSpeed(speed: number): MoneroMiningStatus { + this.speed = speed; + return this; + } + + getNumThreads(): number { + return this.numThreads; + } + + setNumThreads(numThreads: number): MoneroMiningStatus { + this.numThreads = numThreads; + return this; + } + + getIsBackground(): boolean { + return this.isBackground; + } + + setIsBackground(isBackground: boolean): MoneroMiningStatus { + this.isBackground = isBackground; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroNetworkType.ts b/src/main/ts/daemon/model/MoneroNetworkType.ts new file mode 100644 index 000000000..dfc26bf98 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroNetworkType.ts @@ -0,0 +1,87 @@ +import MoneroError from "../../common/MoneroError"; + +/** + * Defines the Monero network types (mainnet, testnet, and stagenet). + */ +export default class MoneroNetworkType { + + /** + * Mainnet (value=0). + */ + static readonly MAINNET = 0; + + /** + * Testnet (value=1). + */ + static readonly TESTNET = 1; + + /** + * Stagnet (value=2). + */ + static readonly STAGENET = 2; + + /** + * Validate and normalize the given network type. + * + * @param {MoneroNetworkType | number | string} networkType - the network type to validate and normalize + * @return {MoneroNetworkType} the given network type + */ + static from(networkType: MoneroNetworkType | number | string): MoneroNetworkType { + if (typeof networkType === "string") return MoneroNetworkType.parse(networkType); + MoneroNetworkType.validate(networkType); + return networkType; + } + + /** + * Validate the given network type. + * + * @param {MoneroNetworkType} networkType - the network type to validate as a numeric + */ + static validate(networkType: MoneroNetworkType | number | string) { + if (typeof networkType === "string") MoneroNetworkType.parse(networkType); + else if (networkType !== 0 && networkType !== 1 && networkType !== 2) throw new MoneroError("Network type is invalid: " + networkType); + } + + /** + * Indicates if the given network type is valid or not. + * + * @param {MoneroNetworkType | number} networkType - the network type to validate as a numeric + * @return {boolean} true if the network type is valid, false otherwise + */ + static isValid(networkType: MoneroNetworkType | number | string): boolean { + try { + MoneroNetworkType.validate(networkType); + return true; + } catch(err) { + return false; + } + } + + /** + * Parse the given string as a network type. + * + * @param {string} networkTypeStr - "mainnet", "testnet", or "stagenet" (case insensitive) + * @return {MoneroNetworkType} the network type as a numeric + */ + static parse(networkTypeStr: string): MoneroNetworkType { + let str = ("" + networkTypeStr).toLowerCase(); + switch (str) { + case "mainnet": return MoneroNetworkType.MAINNET; + case "testnet": return MoneroNetworkType.TESTNET; + case "stagenet": return MoneroNetworkType.STAGENET; + default: throw new MoneroError("Invalid network type to parse: '" + networkTypeStr + "'"); + } + } + + /** + * Get the network type in human-readable form. + * + * @return {string} the network type in human-readable form + */ + static toString(networkType: MoneroNetworkType | number): string { + if (networkType === 0) return "mainnet"; + if (networkType === 1) return "testnet"; + if (networkType === 2) return "stagenet"; + throw new MoneroError("Invalid network type: " + networkType); + } +} \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroOutput.ts b/src/main/ts/daemon/model/MoneroOutput.ts new file mode 100644 index 000000000..94655e19f --- /dev/null +++ b/src/main/ts/daemon/model/MoneroOutput.ts @@ -0,0 +1,128 @@ +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroError from "../../common/MoneroError"; +import MoneroKeyImage from "./MoneroKeyImage"; +import MoneroTx from "./MoneroTx"; + +/** + * Models a Monero transaction output. + */ +export default class MoneroOutput { + + tx: MoneroTx; + keyImage: Partial; + amount: bigint; + index: number; + ringOutputIndices: number[]; + stealthPublicKey: string; + + /** + * Construct the model. + * + * @param {MoneroOutput} [output] is existing state to initialize from (optional) + */ + constructor(output?: Partial) { + Object.assign(this, output); + + // deserialize fields if necessary + if (this.amount !== undefined && typeof this.amount !== "bigint") this.amount = BigInt(this.amount); + if (this.keyImage) this.keyImage = this.keyImage instanceof MoneroKeyImage ? this.keyImage.copy() : new MoneroKeyImage(this.keyImage); + } + + getTx(): MoneroTx { + return this.tx; + } + + setTx(tx: MoneroTx): MoneroOutput { + this.tx = tx; + return this; + } + + getKeyImage(): MoneroKeyImage { + return this.keyImage as MoneroKeyImage; + } + + setKeyImage(keyImage: MoneroKeyImage): MoneroOutput { + this.keyImage = keyImage === undefined ? undefined : keyImage instanceof MoneroKeyImage ? keyImage : new MoneroKeyImage(keyImage); + return this; + } + + getAmount(): bigint { + return this.amount; + } + + setAmount(amount: bigint): MoneroOutput { + this.amount = amount; + return this; + } + + getIndex(): number { + return this.index; + } + + setIndex(index: number): MoneroOutput { + this.index = index; + return this; + } + + getRingOutputIndices(): number[] { + return this.ringOutputIndices; + } + + setRingOutputIndices(ringOutputIndices: number[]): MoneroOutput { + this.ringOutputIndices = ringOutputIndices; + return this; + } + + getStealthPublicKey(): string { + return this.stealthPublicKey; + } + + setStealthPublicKey(stealthPublicKey: string): MoneroOutput { + this.stealthPublicKey = stealthPublicKey; + return this; + } + + copy(): MoneroOutput { + return new MoneroOutput(this); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getAmount() !== undefined) json.amount = this.getAmount().toString(); + if (this.getKeyImage() !== undefined) json.keyImage = this.getKeyImage() ? this.getKeyImage().toJson() : undefined; + delete json.tx; + return json; + } + + merge(output: MoneroOutput): MoneroOutput { + assert(output instanceof MoneroOutput); + if (this === output) return this; + + // merge txs if they're different which comes back to merging outputs + if (this.getTx() !== output.getTx()) this.getTx().merge(output.getTx()); + + // otherwise merge output fields + else { + if (this.getKeyImage() === undefined) this.setKeyImage(output.getKeyImage()); + else if (output.getKeyImage() !== undefined) this.getKeyImage().merge(output.getKeyImage()); + this.setAmount(GenUtils.reconcile(this.getAmount(), output.getAmount())); + this.setIndex(GenUtils.reconcile(this.getIndex(), output.getIndex())); + } + + return this; + } + + toString(indent = 0): string { + let str = ""; + if (this.getKeyImage() !== undefined) { + str += GenUtils.kvLine("Key image", "", indent); + str += this.getKeyImage().toString(indent + 1) + "\n"; + } + str += GenUtils.kvLine("Amount", this.getAmount(), indent); + str += GenUtils.kvLine("Index", this.getIndex(), indent); + str += GenUtils.kvLine("Ring output indices", this.getRingOutputIndices(), indent); + str += GenUtils.kvLine("Stealth public key", this.getStealthPublicKey(), indent); + return str === "" ? str : str.slice(0, str.length - 1); // strip last newline + } +} \ No newline at end of file diff --git a/src/main/ts/daemon/model/MoneroOutputHistogramEntry.ts b/src/main/ts/daemon/model/MoneroOutputHistogramEntry.ts new file mode 100644 index 000000000..a15939b47 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroOutputHistogramEntry.ts @@ -0,0 +1,57 @@ +/** + * Entry in a Monero output histogram (see get_output_histogram of Daemon RPC documentation). + */ +export default class MoneroOutputHistogramEntry { + + amount: bigint; + numInstances: number; + numUnlockedInstances: number; + numRecentInstances: number; + + constructor(entry?: MoneroOutputHistogramEntry) { + Object.assign(this, entry); + if (this.amount !== undefined && typeof this.amount !== "bigint") this.amount = BigInt(this.amount); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (json.amount !== undefined) json.amount = json.amount.toString(); + return json; + } + + getAmount(): bigint { + return this.amount; + } + + setAmount(amount: bigint): MoneroOutputHistogramEntry { + this.amount = amount; + return this; + } + + getNumInstances(): number { + return this.numInstances; + } + + setNumInstances(numInstances: number): MoneroOutputHistogramEntry { + this.numInstances = numInstances; + return this; + } + + getNumUnlockedInstances(): number { + return this.numUnlockedInstances; + } + + setNumUnlockedInstances(numUnlockedInstances: number) { + this.numUnlockedInstances = numUnlockedInstances; + return this; + } + + getNumRecentInstances(): number { + return this.numRecentInstances; + } + + setNumRecentInstances(numRecentInstances: number): MoneroOutputHistogramEntry { + this.numRecentInstances = numRecentInstances; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroPeer.ts b/src/main/ts/daemon/model/MoneroPeer.ts new file mode 100644 index 000000000..585818bf4 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroPeer.ts @@ -0,0 +1,275 @@ +import ConnectionType from "./ConnectionType"; + +/** + * Models a peer to the daemon. + */ +export default class MoneroPeer { + + id: string; + address: string; + host: string; + port: number; + isOnline: boolean; + lastSeenTimestamp: number; + pruningSeed: number; + rpcPort: number; + rpcCreditsPerHash: bigint; + avgDownload: number; + avgUpload: number; + currentDownload: number; + currentUpload: number; + height: number; + isIncoming: boolean; + liveTime: number; + isLocalIp: boolean; + isLocalHost: boolean; + numReceives: number; + numSends: number; + receiveIdleTime: number; + sendIdleTime: number; + state: string; + numSupportFlags: number; + type: ConnectionType; + + constructor(peer?: MoneroPeer) { + Object.assign(this, peer); + if (this.rpcCreditsPerHash !== undefined && typeof this.rpcCreditsPerHash !== "bigint") this.rpcCreditsPerHash = BigInt(this.rpcCreditsPerHash); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (json.rpcCreditsPerHash !== undefined) json.rpcCreditsPerHash = json.rpcCreditsPerHash.toString(); + return json; + } + + getId(): string { + return this.id; + } + + setId(id: string): MoneroPeer { + this.id = id; + return this; + } + + getAddress(): string { + return this.address; + } + + setAddress(address: string): MoneroPeer { + this.address = address; + return this; + } + + getHost(): string { + return this.host; + } + + setHost(host: string): MoneroPeer { + this.host = host; + return this; + } + + getPort(): number { + return this.port; + } + + setPort(port: number): MoneroPeer { + this.port = port; + return this; + } + + /** + * Indicates if the peer was online when last checked (aka "white listed" as + * opposed to "gray listed"). + * + * @return {boolean} true if peer was online when last checked, false otherwise + */ + getIsOnline(): boolean { + return this.isOnline; + } + + setIsOnline(isOnline: boolean): MoneroPeer { + this.isOnline = isOnline; + return this; + } + + getLastSeenTimestamp(): number { + return this.lastSeenTimestamp; + } + + setLastSeenTimestamp(lastSeenTimestamp: number): MoneroPeer { + this.lastSeenTimestamp = lastSeenTimestamp; + return this; + } + + getPruningSeed(): number { + return this.pruningSeed; + } + + setPruningSeed(pruningSeed: number): MoneroPeer { + this.pruningSeed = pruningSeed; + return this; + } + + getRpcPort(): number { + return this.rpcPort; + } + + setRpcPort(rpcPort: number): MoneroPeer { + this.rpcPort = rpcPort; + return this; + } + + getRpcCreditsPerHash(): bigint { + return this.rpcCreditsPerHash; + } + + setRpcCreditsPerHash(rpcCreditsPerHash: bigint): MoneroPeer { + this.rpcCreditsPerHash = rpcCreditsPerHash; + return this; + } + + getAvgDownload(): number { + return this.avgDownload; + } + + setAvgDownload(avgDownload: number): MoneroPeer { + this.avgDownload = avgDownload; + return this; + } + + getAvgUpload(): number { + return this.avgUpload; + } + + setAvgUpload(avgUpload: number): MoneroPeer { + this.avgUpload = avgUpload; + return this; + } + + getCurrentDownload(): number { + return this.currentDownload; + } + + setCurrentDownload(currentDownload: number): MoneroPeer { + this.currentDownload = currentDownload; + return this; + } + + getCurrentUpload(): number { + return this.currentUpload; + } + + setCurrentUpload(currentUpload: number): MoneroPeer { + this.currentUpload = currentUpload; + return this; + } + + getHeight(): number { + return this.height; + } + + setHeight(height: number): MoneroPeer { + this.height = height; + return this; + } + + getIsIncoming(): boolean { + return this.isIncoming; + } + + setIsIncoming(isIncoming: boolean): MoneroPeer { + this.isIncoming = isIncoming; + return this; + } + + getLiveTime(): number { + return this.liveTime; + } + + setLiveTime(liveTime: number) { + this.liveTime = liveTime; + return this; + } + + getIsLocalIp(): boolean { + return this.isLocalIp; + } + + setIsLocalIp(isLocalIp: boolean): MoneroPeer { + this.isLocalIp = isLocalIp; + return this; + } + + getIsLocalHost(): boolean { + return this.isLocalHost; + } + + setIsLocalHost(isLocalHost: boolean): MoneroPeer { + this.isLocalHost = isLocalHost; + return this; + } + + getNumReceives(): number { + return this.numReceives; + } + + setNumReceives(numReceives: number): MoneroPeer { + this.numReceives = numReceives; + return this; + } + + getNumSends(): number { + return this.numSends; + } + + setNumSends(numSends: number): MoneroPeer { + this.numSends = numSends; + return this; + } + + getReceiveIdleTime(): number { + return this.receiveIdleTime; + } + + setReceiveIdleTime(receiveIdleTime: number): MoneroPeer { + this.receiveIdleTime = receiveIdleTime; + return this; + } + + getSendIdleTime(): number { + return this.sendIdleTime; + } + + setSendIdleTime(sendIdleTime: number): MoneroPeer { + this.sendIdleTime = sendIdleTime; + return this; + } + + getState(): string { + return this.state; + } + + setState(state: string): MoneroPeer { + this.state = state; + return this; + } + + getNumSupportFlags(): number { + return this.numSupportFlags; + } + + setNumSupportFlags(numSupportFlags: number): MoneroPeer { + this.numSupportFlags = numSupportFlags; + return this; + } + + getType(): ConnectionType { + return this.type; + } + + setType(type: ConnectionType): MoneroPeer { + this.type = type; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroPruneResult.ts b/src/main/ts/daemon/model/MoneroPruneResult.ts new file mode 100644 index 000000000..8fc7a56f6 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroPruneResult.ts @@ -0,0 +1,37 @@ +/** + * Result of pruning the blockchain. + */ +export default class MoneroPruneResult { + + isPruned: boolean; + pruningSeed: number; + + constructor(result?: Partial) { + Object.assign(this, result); + } + + toJson(): any { + let json = Object.assign({}, this); + if (this.getIsPruned()) json.isPruned = this.getIsPruned(); + if (this.getPruningSeed()) json.pruningSeed = this.getPruningSeed(); + return json; + } + + getIsPruned(): boolean { + return this.isPruned; + } + + setIsPruned(isPruned: boolean): MoneroPruneResult { + this.isPruned = isPruned; + return this; + } + + getPruningSeed(): number { + return this.pruningSeed; + } + + setPruningSeed(pruningSeed: number): MoneroPruneResult { + this.pruningSeed = pruningSeed; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroSubmitTxResult.ts b/src/main/ts/daemon/model/MoneroSubmitTxResult.ts new file mode 100644 index 000000000..6c7420d2c --- /dev/null +++ b/src/main/ts/daemon/model/MoneroSubmitTxResult.ts @@ -0,0 +1,167 @@ +/** + * Models the result from submitting a tx to a daemon. + */ +export default class MoneroSubmitTxResult { + + isGood: boolean; + isRelayed: boolean; + isDoubleSpendSeen: boolean; + isFeeTooLow: boolean; + isMixinTooLow: boolean; + hasInvalidInput: boolean; + hasInvalidOutput: boolean; + hasTooFewOutputs: boolean; + isOverspend: boolean; + reason: string; + isTooBig: boolean; + sanityCheckFailed: boolean; + credits: bigint; + topBlockHash: string; + isTxExtraTooBig: boolean; + + constructor(result?: Partial) { + Object.assign(this, result); + if (this.credits !== undefined && typeof this.credits !== "bigint") this.credits = BigInt(this.credits); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (json.credits !== undefined) json.credits = json.credits.toString(); + return json; + } + + getIsGood(): boolean { + return this.isGood; + } + + setIsGood(isGood: boolean): MoneroSubmitTxResult { + this.isGood = isGood; + return this; + } + + getIsRelayed(): boolean { + return this.isRelayed; + } + + setIsRelayed(isRelayed: boolean) { + this.isRelayed = isRelayed; + return this; + } + + getIsDoubleSpendSeen(): boolean { + return this.isDoubleSpendSeen; + } + + setIsDoubleSpendSeen(isDoubleSpendSeen: boolean): MoneroSubmitTxResult { + this.isDoubleSpendSeen = isDoubleSpendSeen + return this; + } + + getIsFeeTooLow(): boolean { + return this.isFeeTooLow; + } + + setIsFeeTooLow(isFeeTooLow: boolean): MoneroSubmitTxResult { + this.isFeeTooLow = isFeeTooLow; + return this; + } + + getIsMixinTooLow(): boolean { + return this.isMixinTooLow; + } + + setIsMixinTooLow(isMixinTooLow: boolean): MoneroSubmitTxResult { + this.isMixinTooLow = isMixinTooLow; + return this; + } + + getHasInvalidInput(): boolean { + return this.hasInvalidInput; + } + + setHasInvalidInput(hasInvalidInput: boolean): MoneroSubmitTxResult { + this.hasInvalidInput = hasInvalidInput; + return this; + } + + getHasInvalidOutput(): boolean { + return this.hasInvalidOutput; + } + + setHasInvalidOutput(hasInvalidOutput: boolean): MoneroSubmitTxResult { + this.hasInvalidOutput = hasInvalidOutput; + return this; + } + + getHasTooFewOutputs(): boolean { + return this.hasTooFewOutputs; + } + + setHasTooFewOutputs(hasTooFewOutputs: boolean): MoneroSubmitTxResult { + this.hasTooFewOutputs = hasTooFewOutputs; + return this; + } + + getIsOverspend(): boolean { + return this.isOverspend; + } + + setIsOverspend(isOverspend: boolean): MoneroSubmitTxResult { + this.isOverspend = isOverspend; + return this; + } + + getReason(): string { + return this.reason; + } + + setReason(reason): MoneroSubmitTxResult { + this.reason = reason; + return this; + } + + getIsTooBig(): boolean { + return this.isTooBig; + } + + setIsTooBig(isTooBig: boolean) { + this.isTooBig = isTooBig; + return this; + } + + getSanityCheckFailed(): boolean { + return this.sanityCheckFailed; + } + + setSanityCheckFailed(sanityCheckFailed): MoneroSubmitTxResult { + this.sanityCheckFailed = sanityCheckFailed; + return this; + } + + getCredits(): bigint { + return this.credits; + } + + setCredits(credits): MoneroSubmitTxResult { + this.credits = credits; + return this; + } + + getTopBlockHash(): string { + return this.topBlockHash; + } + + setTopBlockHash(topBlockHash: string): MoneroSubmitTxResult { + this.topBlockHash = topBlockHash; + return this; + } + + getIsTxExtraTooBig(): boolean { + return this.isTxExtraTooBig; + } + + setIsTxExtraTooBig(isTxExtraTooBig: boolean): MoneroSubmitTxResult { + this.isTxExtraTooBig = isTxExtraTooBig; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroTx.ts b/src/main/ts/daemon/model/MoneroTx.ts new file mode 100644 index 000000000..5953c7567 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroTx.ts @@ -0,0 +1,880 @@ +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroBlock from "./MoneroBlock"; +import MoneroOutput from "./MoneroOutput"; + +/** + * Represents a transaction on the Monero network. + */ +export default class MoneroTx { + + static readonly DEFAULT_PAYMENT_ID = "0000000000000000"; + + block: MoneroBlock; + hash: string; + version: number; + isMinerTx: boolean; + paymentId: string; + fee: bigint; + ringSize: number; + relay: boolean; + isRelayed: boolean; + isConfirmed: boolean; + inTxPool: boolean; + numConfirmations: number; + unlockTime: bigint; + lastRelayedTimestamp: number; + receivedTimestamp: number; + isDoubleSpendSeen: boolean; + key: string; + fullHex: string; + prunedHex: string; + prunableHex: string; + prunableHash: string; + size: number; + weight: number; + inputs: MoneroOutput[]; + outputs: MoneroOutput[]; + outputIndices: number[]; + metadata: string; + extra: Uint8Array; + rctSignatures: any; + rctSigPrunable: any; + isKeptByBlock: boolean; + isFailed: boolean; + lastFailedHeight: number; + lastFailedHash: string; + maxUsedBlockHeight: number; + maxUsedBlockHash: string; + signatures: string[]; + + constructor(tx?: Partial) { + Object.assign(this, tx); + this.block = undefined; + + // deserialize extra + if (this.extra !== undefined) this.extra = new Uint8Array(this.extra); + + // deserialize bigints + if (this.fee !== undefined && typeof this.fee !== "bigint") this.fee = BigInt(this.fee); + if (this.unlockTime !== undefined && typeof this.unlockTime !== "bigint") this.unlockTime = BigInt(this.unlockTime); + + // copy inputs + if (this.inputs) { + this.inputs = this.inputs.slice(); + for (let i = 0; i < this.inputs.length; i++) { + this.inputs[i] = new MoneroOutput(this.inputs[i]).setTx(this); + } + } + + // copy outputs + if (this.outputs) { + this.outputs = this.outputs.slice(); + for (let i = 0; i < this.outputs.length; i++) { + this.outputs[i] = new MoneroOutput(this.outputs[i]).setTx(this); + } + } + } + + /** + * @return {MoneroBlock} tx block + */ + getBlock(): MoneroBlock { + return this.block; + } + + /** + * @param {MoneroBlock} block - tx block + * @return {MoneroTx} this tx for chaining + */ + setBlock(block: MoneroBlock): MoneroTx { + this.block = block; + return this; + } + + /** + * @return {number} tx height + */ + getHeight(): number { + return this.getBlock() === undefined ? undefined : this.getBlock().getHeight(); + } + + /** + * @return {string} tx hash + */ + getHash(): string { + return this.hash; + } + + /** + * @param {string} hash - tx hash + * @return {MoneroTx} this tx for chaining + */ + setHash(hash: string): MoneroTx { + this.hash = hash; + return this; + } + + /** + * @return {number} tx version + */ + getVersion(): number { + return this.version; + } + + /** + * @param {number} version - tx version + * @return {MoneroTx} this tx for chaining + */ + setVersion(version: number): MoneroTx { + this.version = version; + return this; + } + + /** + * @return {boolean} true if the tx is a miner tx, false otherwise + */ + getIsMinerTx(): boolean { + return this.isMinerTx; + } + + /** + * @param {boolean} miner - true if the tx is a miner tx, false otherwise + * @return {MoneroTx} this tx for chaining + */ + setIsMinerTx(miner: boolean): MoneroTx { + this.isMinerTx = miner; + return this; + } + + /** + * @return {string} tx payment id + */ + getPaymentId(): string { + return this.paymentId; + } + + /** + * @param {string} paymentId - tx payment id + * @return {MoneroTx} this tx for chaining + */ + setPaymentId(paymentId: string): MoneroTx { + this.paymentId = paymentId; + return this; + } + + /** + * @return {bigint} tx fee + */ + getFee(): bigint { + return this.fee; + } + + /** + * @param {bigint} fee - tx fee + * @return {MoneroTx} this tx for chaining + */ + setFee(fee: bigint): MoneroTx { + this.fee = fee; + return this; + } + + /** + * @return {number} tx ring size + */ + getRingSize(): number { + return this.ringSize; + } + + /** + * @param {number} ringSize - tx ring size + * @return {MoneroTx} this tx for chaining + */ + setRingSize(ringSize: number): MoneroTx { + this.ringSize = ringSize; + return this; + } + + /** + * @return {boolean} true if the tx is set to be relayed, false otherwise + */ + getRelay(): boolean { + return this.relay; + } + + /** + * @param {boolean} relay - true if the tx is set to be relayed, false otherwise + * @return {MoneroTx} this tx for chaining + */ + setRelay(relay: boolean): MoneroTx { + this.relay = relay; + return this; + } + + /** + * @return {boolean} true if the tx is relayed, false otherwise + */ + getIsRelayed(): boolean { + return this.isRelayed; + } + + /** + * @param {boolean} isRelayed - true if the tx is relayed, false otherwise + * @return {MoneroTx} this tx for chaining + */ + setIsRelayed(isRelayed: boolean): MoneroTx { + this.isRelayed = isRelayed; + return this; + } + + /** + * @return {boolean} true if the tx is confirmed, false otherwise + */ + getIsConfirmed(): boolean { + return this.isConfirmed; + } + + /** + * @param {boolean} isConfirmed - true if the tx is confirmed, false otherwise + * @return {MoneroTx} this tx for chaining + */ + setIsConfirmed(isConfirmed: boolean): MoneroTx { + this.isConfirmed = isConfirmed; + return this; + } + + /** + * @return {boolean} true if the tx is in the memory pool, false otherwise + */ + getInTxPool(): boolean { + return this.inTxPool; + } + + /** + * @param {boolean} inTxPool - true if the tx is in the memory pool, false otherwise + * @return {MoneroTx} this tx for chaining + */ + setInTxPool(inTxPool: boolean): MoneroTx { + this.inTxPool = inTxPool; + return this; + } + + /** + * @return {number} number of block confirmations + */ + getNumConfirmations(): number { + return this.numConfirmations; + } + + /** + * @param {number} numConfirmations - number of block confirmations + * @return {MoneroTx} this tx for chaining + */ + setNumConfirmations(numConfirmations: number): MoneroTx { + this.numConfirmations = numConfirmations; + return this; + } + + /** + * Get the minimum height or timestamp for the transactions to unlock. + * + * @return {bigint} the minimum height or timestamp for the transactin to unlock + */ + getUnlockTime(): bigint { + return this.unlockTime; + } + + setUnlockTime(unlockTime: bigint | string | number | undefined): MoneroTx { + if (unlockTime !== undefined && typeof unlockTime !== "bigint") unlockTime = BigInt(unlockTime); + this.unlockTime = unlockTime as bigint | undefined; + return this; + } + + /** + * @return {number} timestamp the tx was last relayed from the node + */ + getLastRelayedTimestamp(): number { + return this.lastRelayedTimestamp; + } + + /** + * @param {number} lastRelayedTimestamp - timestamp the tx was last relayed from the node + * @return {MoneroTx} this tx for chaining + */ + setLastRelayedTimestamp(lastRelayedTimestamp: number): MoneroTx { + this.lastRelayedTimestamp = lastRelayedTimestamp; + return this; + } + + /** + * @return {number} timestamp the tx was received at the node + */ + getReceivedTimestamp(): number { + return this.receivedTimestamp; + } + + /** + * @param {number} receivedTimestamp - timestamp the tx was received at the node + * @return {MoneroTx} this tx for chaining + */ + setReceivedTimestamp(receivedTimestamp: number): MoneroTx { + this.receivedTimestamp = receivedTimestamp; + return this; + } + + /** + * @return {boolean} true if a double spend has been seen, false otherwise + */ + getIsDoubleSpendSeen(): boolean { + return this.isDoubleSpendSeen; + } + + /** + * @param {boolean} isDoubleSpendSeen - true if a double spend has been seen, false otherwise + * @return {MoneroTx} this tx for chaining + */ + setIsDoubleSpendSeen(isDoubleSpendSeen: boolean ): MoneroTx { + this.isDoubleSpendSeen = isDoubleSpendSeen; + return this; + } + + /** + * @return {string} tx key + */ + getKey(): string { + return this.key; + } + + /** + * @param {string} key - tx key + * @return {MoneroTx} this tx for chaining + */ + setKey(key: string): MoneroTx { + this.key = key; + return this; + } + + /** + * Get full transaction hex. Full hex = pruned hex + prunable hex. + * + * @return {string} full tx hex + */ + getFullHex(): string { + return this.fullHex; + } + + /** + * @param {string} fullHex - full tx hex + * @return {MoneroTx} this tx for chaining + */ + setFullHex(fullHex: string): MoneroTx { + this.fullHex = fullHex; + return this; + } + + /** + * Get pruned transaction hex. Full hex = pruned hex + prunable hex. + * + * @return {string} pruned tx hex + */ + getPrunedHex(): string { + return this.prunedHex; + } + + /** + * @param {string} prunedHex - pruned tx hex + * @return {MoneroTx} this tx for chaining + */ + setPrunedHex(prunedHex: string): MoneroTx { + this.prunedHex = prunedHex; + return this; + } + + /** + * Get prunable transaction hex which is hex that is removed from a pruned + * transaction. Full hex = pruned hex + prunable hex. + * + * @return {string} prunable tx hex + */ + getPrunableHex(): string { + return this.prunableHex; + } + + /** + * @param {string} prunableHex - prunable tx hex + * @return {MoneroTx} this tx for chaining + */ + setPrunableHex(prunableHex: string): MoneroTx { + this.prunableHex = prunableHex; + return this; + } + + /** + * @return {string} prunable tx hash + */ + getPrunableHash(): string { + return this.prunableHash; + } + + /** + * @param {string} prunableHash - prunable tx hash + * @return {MoneroTx} this tx for chaining + */ + setPrunableHash(prunableHash: string): MoneroTx { + this.prunableHash = prunableHash; + return this; + } + + /** + * @return {number} tx size + */ + getSize(): number { + return this.size; + } + + /** + * @param {number} size - tx size + * @return {MoneroTx} this tx for chaining + */ + setSize(size: number): MoneroTx { + this.size = size; + return this; + } + + /** + * @return {number} tx weight + */ + getWeight(): number { + return this.weight; + } + + /** + * @param {number} weight - tx weight + * @return {MoneroTx} this tx for chaining + */ + setWeight(weight: number): MoneroTx { + this.weight = weight; + return this; + } + + /** + * @return {MoneroOutput[]} tx inputs + */ + getInputs(): MoneroOutput[] { + return this.inputs; + } + + /** + * @param {MoneroOutput[]} - tx inputs + * @return {MoneroTx} this tx for chaining + */ + setInputs(inputs: MoneroOutput[]): MoneroTx { + this.inputs = inputs; + return this; + } + + /** + * @return {MoneroOutput[]} tx outputs + */ + getOutputs(): MoneroOutput[] { + return this.outputs; + } + + /** + * @param {MoneroOutput[]} outputs - tx outputs + * @return {MoneroTx} this tx for chaining + */ + setOutputs(outputs: MoneroOutput[]): MoneroTx { + this.outputs = outputs; + return this; + } + + /** + * @return {number[]} tx output indices + */ + getOutputIndices(): number[] { + return this.outputIndices; + } + + /** + * @param {number[]} outputIndices - tx output indices + * @return {MoneroTx} this tx for chaining + */ + setOutputIndices(outputIndices: number[]): MoneroTx { + this.outputIndices = outputIndices; + return this; + } + + /** + * @return {string} tx metadata + */ + getMetadata(): string { + return this.metadata; + } + + /** + * @param {string} metadata - tx metadata + * @return {MoneroTx} this tx for chaining + */ + setMetadata(metadata: string): MoneroTx { + this.metadata = metadata; + return this; + } + + /** + * @return {Uint8Array} tx extra + */ + getExtra(): Uint8Array { + return this.extra; + } + + /** + * @param {Uint8Array} extra - tx extra + * @return {MoneroTx} this tx for chaining + */ + setExtra(extra: Uint8Array): MoneroTx { + this.extra = extra; + return this; + } + + /** + * @return {any} RCT signatures + */ + getRctSignatures(): any { + return this.rctSignatures; + } + + /** + * @param {any} rctSignatures - RCT signatures + * @return {MoneroTx} this tx for chaining + */ + setRctSignatures(rctSignatures: any): MoneroTx { + this.rctSignatures = rctSignatures; + return this; + } + + /** + * @return {any} prunable RCT signature data + */ + getRctSigPrunable(): object { + return this.rctSigPrunable; + } + + /** + * @param {any} rctSigPrunable - prunable RCT signature data + * @return {MoneroTx} this tx for chaining + */ + setRctSigPrunable(rctSigPrunable: any): MoneroTx { + this.rctSigPrunable = rctSigPrunable; + return this; + } + + /** + * @return {boolean} true if kept by a block, false otherwise + */ + getIsKeptByBlock(): boolean { + return this.isKeptByBlock; + } + + /** + * @param {boolean} isKeptByBlock - true if kept by a block, false otherwise + * @return {MoneroTx} this tx for chaining + */ + setIsKeptByBlock(isKeptByBlock: boolean): MoneroTx { + this.isKeptByBlock = isKeptByBlock; + return this; + } + + /** + * @return {boolean} true if the tx failed, false otherwise + */ + getIsFailed(): boolean { + return this.isFailed; + } + + /** + * @param {boolean} isFailed - true if the tx failed, false otherwise + * @return {MoneroTx} this tx for chaining + */ + setIsFailed(isFailed: boolean): MoneroTx { + this.isFailed = isFailed; + return this; + } + + /** + * @return {number} block height of the last tx failure + */ + getLastFailedHeight(): number { + return this.lastFailedHeight; + } + + /** + * @param {number} lastFailedHeight - block height of the last tx failure + * @return {MoneroTx} this tx for chaining + */ + setLastFailedHeight(lastFailedHeight: number): MoneroTx { + this.lastFailedHeight = lastFailedHeight; + return this; + } + + /** + * @return {string} block hash of the last tx failure + */ + getLastFailedHash(): string { + return this.lastFailedHash; + } + + /** + * @param {string} lastFailedHash - block hash of the last tx failure + * @return {MoneroTx} this tx for chaining + */ + setLastFailedHash(lastFailedHash: string): MoneroTx { + this.lastFailedHash = lastFailedHash; + return this; + } + + /** + * @return {number} max used block height + */ + getMaxUsedBlockHeight(): number { + return this.maxUsedBlockHeight; + } + + /** + * @param {number} maxUsedBlockHeight - max used block height + * @return {MoneroTx} this tx for chaining + */ + setMaxUsedBlockHeight(maxUsedBlockHeight: number): MoneroTx { + this.maxUsedBlockHeight = maxUsedBlockHeight; + return this; + } + + /** + * @return {string} max used block hash + */ + getMaxUsedBlockHash(): string { + return this.maxUsedBlockHash; + } + + /** + * @param {string} maxUsedBlockHash - max used block hash + * @return {MoneroTx} this tx for chaining + */ + setMaxUsedBlockHash(maxUsedBlockHash: string): MoneroTx { + this.maxUsedBlockHash = maxUsedBlockHash; + return this; + } + + /** + * @return {string[]} tx signatures + */ + getSignatures(): string[] { + return this.signatures; + } + + /** + * @param {string[]} signatures - tx signatures + * @return {MoneroTx} this tx for chaining + */ + setSignatures(signatures: string[]): MoneroTx { + this.signatures = signatures; + return this; + } + + /** + * @return {MoneroTx} a copy of this tx + */ + copy(): MoneroTx { + return new MoneroTx(this); + } + + /** + * @return {any} json representation of this tx + */ + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getFee() !== undefined) json.fee = this.getFee().toString(); + if (this.getUnlockTime() !== undefined) json.unlockTime = this.getUnlockTime().toString(); + if (this.getInputs()) { + json.inputs = []; + for (let input of this.getInputs()) json.inputs.push(input.toJson()); + } + if (this.getOutputs()) { + json.outputs = []; + for (let output of this.getOutputs()) json.outputs.push(output.toJson()); + } + if (this.getExtra() !== undefined) json.extra = Array.from(this.getExtra(), byte => byte); + delete json.block; // do not serialize parent block + return json; + } + + /** + * Updates this transaction by merging the latest information from the given + * transaction. + * + * @param {MoneroTx} tx - the transaction to update this transaction with + * @return {MoneroTx} this for method chaining + */ + merge(tx: MoneroTx): MoneroTx { + assert(tx instanceof MoneroTx); + if (this === tx) return this; + + // merge blocks if they're different + if (this.getBlock() !== tx.getBlock()) { + if (this.getBlock() === undefined) { + this.setBlock(tx.getBlock()); + this.getBlock().getTxs[this.getBlock().getTxs().indexOf(tx)] = this; // update block to point to this tx + } else if (tx.getBlock() !== undefined) { + this.getBlock().merge(tx.getBlock()); // comes back to merging txs + return this; + } + } + + // otherwise merge tx fields + this.setHash(GenUtils.reconcile(this.getHash(), tx.getHash())); + this.setVersion(GenUtils.reconcile(this.getVersion(), tx.getVersion())); + this.setPaymentId(GenUtils.reconcile(this.getPaymentId(), tx.getPaymentId())); + this.setFee(GenUtils.reconcile(this.getFee(), tx.getFee())); + this.setRingSize(GenUtils.reconcile(this.getRingSize(), tx.getRingSize())); + this.setIsConfirmed(GenUtils.reconcile(this.getIsConfirmed(), tx.getIsConfirmed(), {resolveTrue: true})); // tx can become confirmed + this.setIsMinerTx(GenUtils.reconcile(this.getIsMinerTx(), tx.getIsMinerTx())); + this.setRelay(GenUtils.reconcile(this.getRelay(), tx.getRelay(), {resolveTrue: true})); // tx can become relayed + this.setIsRelayed(GenUtils.reconcile(this.getIsRelayed(), tx.getIsRelayed(), {resolveTrue: true})); // tx can become relayed + this.setIsDoubleSpendSeen(GenUtils.reconcile(this.getIsDoubleSpendSeen(), tx.getIsDoubleSpendSeen(), {resolveTrue: true})); // double spend can become seen + this.setKey(GenUtils.reconcile(this.getKey(), tx.getKey())); + this.setFullHex(GenUtils.reconcile(this.getFullHex(), tx.getFullHex())); + this.setPrunedHex(GenUtils.reconcile(this.getPrunedHex(), tx.getPrunedHex())); + this.setPrunableHex(GenUtils.reconcile(this.getPrunableHex(), tx.getPrunableHex())); + this.setPrunableHash(GenUtils.reconcile(this.getPrunableHash(), tx.getPrunableHash())); + this.setSize(GenUtils.reconcile(this.getSize(), tx.getSize())); + this.setWeight(GenUtils.reconcile(this.getWeight(), tx.getWeight())); + this.setOutputIndices(GenUtils.reconcile(this.getOutputIndices(), tx.getOutputIndices())); + this.setMetadata(GenUtils.reconcile(this.getMetadata(), tx.getMetadata())); + this.setExtra(GenUtils.reconcile(this.getExtra(), tx.getExtra())); + this.setRctSignatures(GenUtils.reconcile(this.getRctSignatures(), tx.getRctSignatures())); + this.setRctSigPrunable(GenUtils.reconcile(this.getRctSigPrunable(), tx.getRctSigPrunable())); + this.setIsKeptByBlock(GenUtils.reconcile(this.getIsKeptByBlock(), tx.getIsKeptByBlock())); + this.setIsFailed(GenUtils.reconcile(this.getIsFailed(), tx.getIsFailed(), {resolveTrue: true})); + this.setLastFailedHeight(GenUtils.reconcile(this.getLastFailedHeight(), tx.getLastFailedHeight())); + this.setLastFailedHash(GenUtils.reconcile(this.getLastFailedHash(), tx.getLastFailedHash())); + this.setMaxUsedBlockHeight(GenUtils.reconcile(this.getMaxUsedBlockHeight(), tx.getMaxUsedBlockHeight())); + this.setMaxUsedBlockHash(GenUtils.reconcile(this.getMaxUsedBlockHash(), tx.getMaxUsedBlockHash())); + this.setSignatures(GenUtils.reconcile(this.getSignatures(), tx.getSignatures())); + this.setUnlockTime(GenUtils.reconcile(this.getUnlockTime(), tx.getUnlockTime())); + this.setNumConfirmations(GenUtils.reconcile(this.getNumConfirmations(), tx.getNumConfirmations(), {resolveMax: true})); // num confirmations can increase + + // merge inputs + if (tx.getInputs()) { + for (let merger of tx.getInputs()) { + let merged = false; + merger.setTx(this); + if (!this.getInputs()) this.setInputs([]); + for (let mergee of this.getInputs()) { + if (mergee.getKeyImage().getHex() === merger.getKeyImage().getHex()) { + mergee.merge(merger); + merged = true; + break; + } + } + if (!merged) this.getInputs().push(merger); + } + } + + // merge outputs + if (tx.getOutputs()) { + for (let output of tx.getOutputs()) output.setTx(this); + if (!this.getOutputs()) this.setOutputs(tx.getOutputs()); + else { + + // merge outputs if key image or stealth public key present, otherwise append + for (let merger of tx.getOutputs()) { + let merged = false; + merger.setTx(this); + for (let mergee of this.getOutputs()) { + if ((merger.getKeyImage() && mergee.getKeyImage().getHex() === merger.getKeyImage().getHex()) || + (merger.getStealthPublicKey() && mergee.getStealthPublicKey() === merger.getStealthPublicKey())) { + mergee.merge(merger); + merged = true; + break; + } + } + if (!merged) this.getOutputs().push(merger); // append output + } + } + } + + // handle unrelayed -> relayed -> confirmed + if (this.getIsConfirmed()) { + this.setInTxPool(false); + this.setReceivedTimestamp(undefined); + this.setLastRelayedTimestamp(undefined); + } else { + this.setInTxPool(GenUtils.reconcile(this.getInTxPool(), tx.getInTxPool(), {resolveTrue: true})); // unrelayed -> tx pool + this.setReceivedTimestamp(GenUtils.reconcile(this.getReceivedTimestamp(), tx.getReceivedTimestamp(), {resolveMax: false})); // take earliest receive time + this.setLastRelayedTimestamp(GenUtils.reconcile(this.getLastRelayedTimestamp(), tx.getLastRelayedTimestamp(), {resolveMax: true})); // take latest relay time + } + + return this; // for chaining + } + + /** + * @param {number} [indent] - starting indentation + * @return {string} string representation of this tx + */ + toString(indent = 0): string { + let str = ""; + str += GenUtils.getIndent(indent) + "=== TX ===\n"; + str += GenUtils.kvLine("Tx hash", this.getHash(), indent); + str += GenUtils.kvLine("Height", this.getHeight(), indent); + str += GenUtils.kvLine("Version", this.getVersion(), indent); + str += GenUtils.kvLine("Is miner tx", this.getIsMinerTx(), indent); + str += GenUtils.kvLine("Payment ID", this.getPaymentId(), indent); + str += GenUtils.kvLine("Fee", this.getFee(), indent); + str += GenUtils.kvLine("Ring size", this.getRingSize(), indent); + str += GenUtils.kvLine("Relay", this.getRelay(), indent); + str += GenUtils.kvLine("Is relayed", this.getIsRelayed(), indent); + str += GenUtils.kvLine("Is confirmed", this.getIsConfirmed(), indent); + str += GenUtils.kvLine("In tx pool", this.getInTxPool(), indent); + str += GenUtils.kvLine("Num confirmations", this.getNumConfirmations(), indent); + str += GenUtils.kvLine("Unlock time", this.getUnlockTime(), indent); + str += GenUtils.kvLine("Last relayed time", this.getLastRelayedTimestamp(), indent); + str += GenUtils.kvLine("Received time", this.getReceivedTimestamp(), indent); + str += GenUtils.kvLine("Is double spend", this.getIsDoubleSpendSeen(), indent); + str += GenUtils.kvLine("Key", this.getKey(), indent); + str += GenUtils.kvLine("Full hex", this.getFullHex(), indent); + str += GenUtils.kvLine("Pruned hex", this.getPrunedHex(), indent); + str += GenUtils.kvLine("Prunable hex", this.getPrunableHex(), indent); + str += GenUtils.kvLine("Prunable hash", this.getPrunableHash(), indent); + str += GenUtils.kvLine("Size", this.getSize(), indent); + str += GenUtils.kvLine("Weight", this.getWeight(), indent); + str += GenUtils.kvLine("Output indices", this.getOutputIndices(), indent); + str += GenUtils.kvLine("Metadata", this.getMetadata(), indent); + str += GenUtils.kvLine("Extra", this.getExtra(), indent); + str += GenUtils.kvLine("RCT signatures", this.getRctSignatures(), indent); + str += GenUtils.kvLine("RCT sig prunable", this.getRctSigPrunable(), indent); + str += GenUtils.kvLine("Kept by block", this.getIsKeptByBlock(), indent); + str += GenUtils.kvLine("Is failed", this.getIsFailed(), indent); + str += GenUtils.kvLine("Last failed height", this.getLastFailedHeight(), indent); + str += GenUtils.kvLine("Last failed hash", this.getLastFailedHash(), indent); + str += GenUtils.kvLine("Max used block height", this.getMaxUsedBlockHeight(), indent); + str += GenUtils.kvLine("Max used block hash", this.getMaxUsedBlockHash(), indent); + str += GenUtils.kvLine("Signatures", this.getSignatures(), indent); + if (this.getInputs() !== undefined) { + str += GenUtils.kvLine("Inputs", "", indent); + for (let i = 0; i < this.getInputs().length; i++) { + str += GenUtils.kvLine(i + 1, "", indent + 1); + str += this.getInputs()[i].toString(indent + 2); + str += '\n' + } + } + if (this.getOutputs() !== undefined) { + str += GenUtils.kvLine("Outputs", "", indent); + for (let i = 0; i < this.getOutputs().length; i++) { + str += GenUtils.kvLine(i + 1, "", indent + 1); + str += this.getOutputs()[i].toString(indent + 2); + str += '\n' + } + } + return str.slice(0, str.length - 1); // strip last newline + } +} diff --git a/src/main/ts/daemon/model/MoneroTxPoolStats.ts b/src/main/ts/daemon/model/MoneroTxPoolStats.ts new file mode 100644 index 000000000..ffa348684 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroTxPoolStats.ts @@ -0,0 +1,149 @@ +/** + * Models transaction pool statistics. + */ +export default class MoneroTxPoolStats { + + numTxs: number; + numNotRelayed: number; + numFailing: number; + numDoubleSpends: number; + num10m: number; + feeTotal: bigint; + bytesMax: number; + bytesMed: number; + bytesMin: number; + bytesTotal: number; + histo: Map; + histo98pc: number; + oldestTimestamp: number; + + constructor(stats?: Partial) { + Object.assign(this, stats); + if (this.feeTotal !== undefined && typeof this.feeTotal !== "bigint") this.feeTotal = BigInt(this.feeTotal); + if (this.histo !== undefined && !(this.histo instanceof Map)) this.histo = new Map(JSON.parse(this.histo)); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (json.feeTotal) json.feeTotal = json.feeTotal.toString(); + if (json.histo) json.histo = JSON.stringify([...json.histo]); // convert map to array of key-value pairs then stringify + return json; + } + + getNumTxs(): number { + return this.numTxs; + } + + setNumTxs(numTxs: number): MoneroTxPoolStats { + this.numTxs = numTxs; + return this; + } + + getNumNotRelayed(): number { + return this.numNotRelayed; + } + + setNumNotRelayed(numNotRelayed: number): MoneroTxPoolStats { + this.numNotRelayed = numNotRelayed; + return this; + } + + getNumFailing(): number { + return this.numFailing; + } + + setNumFailing(numFailing: number): MoneroTxPoolStats { + this.numFailing = numFailing; + return this; + } + + getNumDoubleSpends(): number { + return this.numDoubleSpends; + } + + setNumDoubleSpends(numDoubleSpends: number): MoneroTxPoolStats { + this.numDoubleSpends = numDoubleSpends; + return this; + } + + getNum10m(): number { + return this.num10m; + } + + setNum10m(num10m): MoneroTxPoolStats { + this.num10m = num10m; + return this; + } + + getFeeTotal(): bigint { + return this.feeTotal; + } + + setFeeTotal(feeTotal: bigint): MoneroTxPoolStats { + this.feeTotal = feeTotal; + return this; + } + + getBytesMax(): number { + return this.bytesMax; + } + + setBytesMax(bytesMax: number): MoneroTxPoolStats { + this.bytesMax = bytesMax; + return this; + } + + getBytesMed(): number { + return this.bytesMed; + } + + setBytesMed(bytesMed: number): MoneroTxPoolStats { + this.bytesMed = bytesMed; + return this; + } + + getBytesMin(): number { + return this.bytesMin; + } + + setBytesMin(bytesMin: number): MoneroTxPoolStats { + this.bytesMin = bytesMin; + return this; + } + + getBytesTotal(): number { + return this.bytesTotal; + } + + setBytesTotal(bytesTotal: number): MoneroTxPoolStats { + this.bytesTotal = bytesTotal; + return this; + } + + getHisto(): Map { + return this.histo; + } + + setHisto(histo: Map): MoneroTxPoolStats { + this.histo = histo; + return this; + } + + getHisto98pc(): number { + return this.histo98pc; + } + + setHisto98pc(histo98pc: number): MoneroTxPoolStats { + this.histo98pc = histo98pc; + return this; + } + + getOldestTimestamp(): number { + return this.oldestTimestamp; + } + + setOldestTimestamp(oldestTimestamp: number): MoneroTxPoolStats { + this.oldestTimestamp = oldestTimestamp; + return this; + } +} diff --git a/src/main/ts/daemon/model/MoneroVersion.ts b/src/main/ts/daemon/model/MoneroVersion.ts new file mode 100644 index 000000000..ce66d6102 --- /dev/null +++ b/src/main/ts/daemon/model/MoneroVersion.ts @@ -0,0 +1,39 @@ +/** + * Models a Monero version. + */ +export default class MoneroVersion { + + number: number; + isRelease: boolean; + + constructor(number: number, isRelease: boolean) { + this.number = number; + this.isRelease = isRelease; + } + + getNumber(): number { + return this.number; + } + + setNumber(number: number): MoneroVersion { + this.number = number; + return this; + } + + getIsRelease(): boolean { + return this.isRelease; + } + + setIsRelease(isRelease: boolean): MoneroVersion { + this.isRelease = isRelease; + return this; + } + + copy(): MoneroVersion { + return new MoneroVersion(this.number, this.isRelease); + } + + toJson(): any { + return Object.assign({}, this); + } +} diff --git a/src/main/ts/wallet/MoneroWallet.ts b/src/main/ts/wallet/MoneroWallet.ts new file mode 100644 index 000000000..3a589e52f --- /dev/null +++ b/src/main/ts/wallet/MoneroWallet.ts @@ -0,0 +1,1452 @@ +import assert from "assert"; +import GenUtils from "../common/GenUtils"; +import MoneroAccount from "./model/MoneroAccount"; +import MoneroAccountTag from "./model/MoneroAccountTag"; +import MoneroAddressBookEntry from "./model/MoneroAddressBookEntry"; +import MoneroBlock from "../daemon/model/MoneroBlock"; +import MoneroCheckReserve from "./model/MoneroCheckReserve"; +import MoneroCheckTx from "./model/MoneroCheckTx"; +import MoneroConnectionManager from "../common/MoneroConnectionManager"; +import MoneroConnectionManagerListener from "../common/MoneroConnectionManagerListener"; +import MoneroError from "../common/MoneroError"; +import MoneroIncomingTransfer from "./model/MoneroIncomingTransfer"; +import MoneroIntegratedAddress from "./model/MoneroIntegratedAddress"; +import MoneroKeyImage from "../daemon/model/MoneroKeyImage"; +import MoneroKeyImageImportResult from "./model/MoneroKeyImageImportResult"; +import MoneroMessageSignatureResult from "./model/MoneroMessageSignatureResult"; +import MoneroMessageSignatureType from "./model/MoneroMessageSignatureType"; +import MoneroMultisigInfo from "./model/MoneroMultisigInfo"; +import MoneroMultisigInitResult from "./model/MoneroMultisigInitResult"; +import MoneroMultisigSignResult from "./model/MoneroMultisigSignResult"; +import MoneroOutputQuery from "./model/MoneroOutputQuery"; +import MoneroOutputWallet from "./model/MoneroOutputWallet"; +import MoneroOutgoingTransfer from "./model/MoneroOutgoingTransfer"; +import MoneroRpcConnection from "../common/MoneroRpcConnection"; +import MoneroSubaddress from "./model/MoneroSubaddress"; +import MoneroSyncResult from "./model/MoneroSyncResult"; +import MoneroTransfer from "./model/MoneroTransfer"; +import MoneroTransferQuery from "./model/MoneroTransferQuery"; +import MoneroTxConfig from "./model/MoneroTxConfig"; +import MoneroTxQuery from "./model/MoneroTxQuery"; +import MoneroTxWallet from "./model/MoneroTxWallet"; +import MoneroTxSet from "./model/MoneroTxSet"; +import MoneroVersion from "../daemon/model/MoneroVersion"; +import MoneroWalletListener from "./model/MoneroWalletListener"; + +/** + * Copyright (c) woodser + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** + * Monero wallet interface and default implementations. + * + * @interface + */ +export default class MoneroWallet { + + // static variables + static readonly DEFAULT_LANGUAGE = "English"; + + // state variables + protected connectionManager: MoneroConnectionManager; + protected connectionManagerListener: MoneroConnectionManagerListener; + + /** + * Register a listener to receive wallet notifications. + * + * @param {MoneroWalletListener} listener - listener to receive wallet notifications + * @return {Promise} + */ + async addListener(listener: MoneroWalletListener): Promise { + throw new Error("Not supported"); + } + + /** + * Unregister a listener to receive wallet notifications. + * + * @param {MoneroWalletListener} listener - listener to unregister + * @return {Promise} + */ + async removeListener(listener): Promise { + throw new Error("Not supported"); + } + + /** + * Get the listeners registered with the wallet. + * + * @return {MoneroWalletListener[]} the registered listeners + */ + getListeners(): MoneroWalletListener[] { + throw new Error("Not supported"); + } + + /** + * Indicates if the wallet is view-only, meaning it does not have the private + * spend key and can therefore only observe incoming outputs. + * + * @return {Promise} true if the wallet is view-only, false otherwise + */ + async isViewOnly(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Set the wallet's daemon connection. + * + * @param {MoneroRpcConnection | string} [uriOrConnection] - daemon's URI or connection (defaults to offline) + * @return {Promise} + */ + async setDaemonConnection(uriOrConnection?: MoneroRpcConnection | string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the wallet's daemon connection. + * + * @return {Promise} the wallet's daemon connection + */ + async getDaemonConnection(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Set the wallet's daemon connection manager. + * + * @param {MoneroConnectionManager} connectionManager manages connections to monerod + * @return {Promise} + */ + async setConnectionManager(connectionManager?: MoneroConnectionManager): Promise { + if (this.connectionManager) this.connectionManager.removeListener(this.connectionManagerListener); + this.connectionManager = connectionManager; + if (!connectionManager) return; + let that = this; + if (!this.connectionManagerListener) this.connectionManagerListener = new class extends MoneroConnectionManagerListener { + async onConnectionChanged(connection: MoneroRpcConnection | undefined) { + await that.setDaemonConnection(connection); + } + }; + connectionManager.addListener(this.connectionManagerListener); + await this.setDaemonConnection(connectionManager.getConnection()); + } + + /** + * Get the wallet's daemon connection manager. + * + * @return {Promise} the wallet's daemon connection manager + */ + async getConnectionManager(): Promise { + return this.connectionManager; + } + + /** + * Indicates if the wallet is connected to daemon. + * + * @return {Promise} true if the wallet is connected to a daemon, false otherwise + */ + async isConnectedToDaemon(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Gets the version of the wallet. + * + * @return {Promise} the version of the wallet + */ + async getVersion(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the wallet's path. + * + * @return {Promise} the path the wallet can be opened with + */ + async getPath(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the wallet's mnemonic phrase or seed. + * + * @return {Promise} the wallet's mnemonic phrase or seed. + */ + async getSeed(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the language of the wallet's mnemonic phrase or seed. + * + * @return {Promise} the language of the wallet's mnemonic phrase or seed. + */ + async getSeedLanguage(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the wallet's private view key. + * + * @return {Promise} the wallet's private view key + */ + async getPrivateViewKey(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the wallet's private spend key. + * + * @return {Promise} the wallet's private spend key + */ + async getPrivateSpendKey(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the wallet's public view key. + * + * @return {Promise} the wallet's public view key + */ + async getPublicViewKey(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the wallet's public spend key. + * + * @return {Promise} the wallet's public spend key + */ + async getPublicSpendKey(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the wallet's primary address. + * + * @return {Promise} the wallet's primary address + */ + async getPrimaryAddress(): Promise { + return await this.getAddress(0, 0); + } + + /** + * Get the address of a specific subaddress. + * + * @param {number} accountIdx - the account index of the address's subaddress + * @param {number} subaddressIdx - the subaddress index within the account + * @return {Promise} the receive address of the specified subaddress + */ + async getAddress(accountIdx: number, subaddressIdx: number): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the account and subaddress index of the given address. + * + * @param {string} address - address to get the account and subaddress index from + * @return {Promise} the account and subaddress indices + */ + async getAddressIndex(address: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get an integrated address based on the given standard address and payment + * ID. Uses the wallet's primary address if an address is not given. + * Generates a random payment ID if a payment ID is not given. + * + * @param {string} standardAddress is the standard address to generate the integrated address from (wallet's primary address if undefined) + * @param {string} paymentId is the payment ID to generate an integrated address from (randomly generated if undefined) + * @return {Promise} the integrated address + */ + async getIntegratedAddress(standardAddress?: string, paymentId?: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Decode an integrated address to get its standard address and payment id. + * + * @param {string} integratedAddress - integrated address to decode + * @return {Promise} the decoded integrated address including standard address and payment id + */ + async decodeIntegratedAddress(integratedAddress: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the block height that the wallet is synced to. + * + * @return {Promise} the block height that the wallet is synced to + */ + async getHeight(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the blockchain's height. + * + * @return {Promise} the blockchain's height + */ + async getDaemonHeight(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the blockchain's height by date as a conservative estimate for scanning. + * + * @param {number} year - year of the height to get + * @param {number} month - month of the height to get as a number between 1 and 12 + * @param {number} day - day of the height to get as a number between 1 and 31 + * @return {Promise} the blockchain's approximate height at the given date + */ + async getHeightByDate(year: number, month: number, day: number): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Synchronize the wallet with the daemon as a one-time synchronous process. + * + * @param {MoneroWalletListener|number} [listenerOrStartHeight] - listener xor start height (defaults to no sync listener, the last synced block) + * @param {number} [startHeight] - startHeight if not given in first arg (defaults to last synced block) + * @return {Promise} + */ + async sync(listenerOrStartHeight?: MoneroWalletListener | number, startHeight?: number): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Start background synchronizing with a maximum period between syncs. + * + * @param {number} [syncPeriodInMs] - maximum period between syncs in milliseconds (default is wallet-specific) + * @return {Promise} + */ + async startSyncing(syncPeriodInMs?: number): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Stop synchronizing the wallet with the daemon. + * + * @return {Promise} + */ + async stopSyncing(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Scan transactions by their hash/id. + * + * @param {string[]} txHashes - tx hashes to scan + * @return {Promise} + */ + async scanTxs(txHashes: string[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + *

Rescan the blockchain for spent outputs.

+ * + *

Note: this can only be called with a trusted daemon.

+ * + *

Example use case: peer multisig hex is import when connected to an untrusted daemon, + * so the wallet will not rescan spent outputs. Then the wallet connects to a trusted + * daemon. This method should be manually invoked to rescan outputs.

+ * + * @return {Promise} + */ + async rescanSpent(): Promise { + throw new MoneroError("Not supported"); + } + + /** + *

Rescan the blockchain from scratch, losing any information which cannot be recovered from + * the blockchain itself.

+ * + *

WARNING: This method discards local wallet data like destination addresses, tx secret keys, + * tx notes, etc.

+ * + * @return {Promise} + */ + async rescanBlockchain(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the balance of the wallet, account, or subaddress. + * + * @param {number} [accountIdx] - index of the account to get the balance of (default all accounts) + * @param {number} [subaddressIdx] - index of the subaddress to get the balance of (default all subaddresses) + * @return {Promise} the balance of the wallet, account, or subaddress + */ + async getBalance(accountIdx?: number, subaddressIdx?: number): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the unlocked balance of the wallet, account, or subaddress. + * + * @param {number} [accountIdx] - index of the account to get the unlocked balance of (optional) + * @param {number} [subaddressIdx] - index of the subaddress to get the unlocked balance of (optional) + * @return {Promise} the unlocked balance of the wallet, account, or subaddress + */ + async getUnlockedBalance(accountIdx?: number, subaddressIdx?: number): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get the number of blocks until the next and last funds unlock. + * + * @return {Promise} the number of blocks until the next and last funds unlock in elements 0 and 1, respectively, or undefined if no balance + */ + async getNumBlocksToUnlock(): Promise { + + // get balances + let balance = await this.getBalance(); + if (balance === 0n) return [undefined, undefined]; // skip if no balance + let unlockedBalance = await this.getUnlockedBalance(); + + // compute number of blocks until next funds available + let txs; + let height; + let numBlocksToNextUnlock = undefined; + if (unlockedBalance > 0n) numBlocksToNextUnlock = 0; + else { + txs = await this.getTxs({isLocked: true}); // get locked txs + height = await this.getHeight(); // get most recent height + for (let tx of txs) { + let numBlocksToUnlock = Math.max((tx.getIsConfirmed() ? tx.getHeight() : height) + 10, tx.getUnlockTime()) - height; + numBlocksToNextUnlock = numBlocksToNextUnlock === undefined ? numBlocksToUnlock : Math.min(numBlocksToNextUnlock, numBlocksToUnlock); + } + } + + // compute number of blocks until all funds available + let numBlocksToLastUnlock = undefined; + if (balance === unlockedBalance) { + if (unlockedBalance > 0n) numBlocksToLastUnlock = 0; + } else { + if (!txs) { + txs = await this.getTxs({isLocked: true}); // get locked txs + height = await this.getHeight(); // get most recent height + } + for (let tx of txs) { + let numBlocksToUnlock = Math.max((tx.getIsConfirmed() ? tx.getHeight() : height) + 10, tx.getUnlockTime()) - height; + numBlocksToLastUnlock = numBlocksToLastUnlock === undefined ? numBlocksToUnlock : Math.max(numBlocksToLastUnlock, numBlocksToUnlock); + } + } + + return [numBlocksToNextUnlock, numBlocksToLastUnlock]; + } + + /** + * Get accounts with a given tag. + * + * @param {boolean} includeSubaddresses - include subaddresses if true + * @param {string} tag - tag for filtering accounts, all accounts if undefined + * @return {Promise} all accounts with the given tag + */ + async getAccounts(includeSubaddresses?: boolean, tag?: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get an account. + * + * @param {number} accountIdx - index of the account to get + * @param {boolean} includeSubaddresses - include subaddresses if true + * @return {Promise} the retrieved account + */ + async getAccount(accountIdx: number, includeSubaddresses?: boolean): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Create a new account with a label for the first subaddress. + * + * @param {string} [label] - label for account's first subaddress (optional) + * @return {Promise} the created account + */ + async createAccount(label?: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Set an account label. + * + * @param {number} accountIdx - index of the account to set the label for + * @param {string} label - the label to set + * @return {Promise} + */ + async setAccountLabel(accountIdx: number, label: string): Promise { + await this.setSubaddressLabel(accountIdx, 0, label); + } + + /** + * Get subaddresses in an account. + * + * @param {number} accountIdx - account to get subaddresses within + * @param {number[]} [subaddressIndices] - indices of subaddresses to get (optional) + * @return {Promise} the retrieved subaddresses + */ + async getSubaddresses(accountIdx: number, subaddressIndices?: number[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get a subaddress. + * + * @param {number} accountIdx - index of the subaddress's account + * @param {number} subaddressIdx - index of the subaddress within the account + * @return {Promise} the retrieved subaddress + */ + async getSubaddress(accountIdx: number, subaddressIdx: number): Promise { + assert(accountIdx >= 0); + assert(subaddressIdx >= 0); + return (await this.getSubaddresses(accountIdx, [subaddressIdx]))[0]; + } + + /** + * Create a subaddress within an account. + * + * @param {number} accountIdx - index of the account to create the subaddress within + * @param {string} [label] - the label for the subaddress (optional) + * @return {Promise} the created subaddress + */ + async createSubaddress(accountIdx: number, label?: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Set a subaddress label. + * + * @param {number} accountIdx - index of the account to set the label for + * @param {number} subaddressIdx - index of the subaddress to set the label for + * @param {Promise} label - the label to set + */ + async setSubaddressLabel(accountIdx: number, subaddressIdx: number, label: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get a wallet transaction by hash. + * + * @param {string} txHash - hash of a transaction to get + * @return {Promise } the identified transaction or undefined if not found + */ + async getTx(txHash: string): Promise { + let txs = await this.getTxs([txHash]); + return txs.length === 0 ? undefined : txs[0]; + } + + /** + *

Get wallet transactions. Wallet transactions contain one or more + * transfers that are either incoming or outgoing to the wallet.

+ * + *

Results can be filtered by passing a query object. Transactions must + * meet every criteria defined in the query in order to be returned. All + * criteria are optional and no filtering is applied when not defined.

+ * + * @param {string[] | MoneroTxQuery} [query] - configures the query (optional) + * @param {boolean} [query.isConfirmed] - get txs that are confirmed or not (optional) + * @param {boolean} [query.inTxPool] - get txs that are in the tx pool or not (optional) + * @param {boolean} [query.isRelayed] - get txs that are relayed or not (optional) + * @param {boolean} [query.isFailed] - get txs that are failed or not (optional) + * @param {boolean} [query.isMinerTx] - get miner txs or not (optional) + * @param {string} [query.hash] - get a tx with the hash (optional) + * @param {string[]} [query.hashes] - get txs with the hashes (optional) + * @param {string} [query.paymentId] - get transactions with the payment id (optional) + * @param {string[]} [query.paymentIds] - get transactions with the payment ids (optional) + * @param {boolean} [query.hasPaymentId] - get transactions with a payment id or not (optional) + * @param {number} [query.minHeight] - get txs with height >= the given height (optional) + * @param {number} [query.maxHeight] - get txs with height <= the given height (optional) + * @param {boolean} [query.isOutgoing] - get txs with an outgoing transfer or not (optional) + * @param {boolean} [query.isIncoming] - get txs with an incoming transfer or not (optional) + * @param {MoneroTransferQuery} [query.transferQuery] - get txs that have a transfer that meets this query (optional) + * @param {boolean} [query.includeOutputs] - specifies that tx outputs should be returned with tx results (optional) + * @return {Promise} wallet transactions per the configuration + */ + async getTxs(query?: string[] | Partial): Promise { + throw new MoneroError("Not supported"); + } + + /** + *

Get incoming and outgoing transfers to and from this wallet. An outgoing + * transfer represents a total amount sent from one or more subaddresses + * within an account to individual destination addresses, each with their + * own amount. An incoming transfer represents a total amount received into + * a subaddress within an account. Transfers belong to transactions which + * are stored on the blockchain.

+ * + *

Results can be filtered by passing a query object. Transfers must + * meet every criteria defined in the query in order to be returned. All + * criteria are optional and no filtering is applied when not defined.

+ * + * @param {MoneroTransferQuery} [query] - configures the query (optional) + * @param {boolean} [query.isOutgoing] - get transfers that are outgoing or not (optional) + * @param {boolean} [query.isIncoming] - get transfers that are incoming or not (optional) + * @param {string} [query.address] - wallet's address that a transfer either originated from (if outgoing) or is destined for (if incoming) (optional) + * @param {number} [query.accountIndex] - get transfers that either originated from (if outgoing) or are destined for (if incoming) a specific account index (optional) + * @param {number} [query.subaddressIndex] - get transfers that either originated from (if outgoing) or are destined for (if incoming) a specific subaddress index (optional) + * @param {int[]} [query.subaddressIndices] - get transfers that either originated from (if outgoing) or are destined for (if incoming) specific subaddress indices (optional) + * @param {bigint} [query.amount] - amount being transferred (optional) + * @param {MoneroDestination[] | MoneroDestinationModel[]} [query.destinations] - individual destinations of an outgoing transfer, which is local wallet data and NOT recoverable from the blockchain (optional) + * @param {boolean} [query.hasDestinations] - get transfers that have destinations or not (optional) + * @param {MoneroTxQuery} [query.txQuery] - get transfers whose transaction meets this query (optional) + * @return {Promise} wallet transfers that meet the query + */ + async getTransfers(query?: Partial): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get incoming transfers. + * + * @param {Partial} [query] - configures the query (optional) + * @param {string} [query.address] - get incoming transfers to a specific address in the wallet (optional) + * @param {number} [query.accountIndex] - get incoming transfers to a specific account index (optional) + * @param {number} [query.subaddressIndex] - get incoming transfers to a specific subaddress index (optional) + * @param {int[]} [query.subaddressIndices] - get transfers destined for specific subaddress indices (optional) + * @param {bigint} [query.amount] - amount being transferred (optional) + * @param {MoneroTxQuery} [query.txQuery] - get transfers whose transaction meets this query (optional) + * @return {Promise} incoming transfers that meet the query + */ + async getIncomingTransfers(query?: Partial): Promise { + const queryNormalized: MoneroTransferQuery = MoneroWallet.normalizeTransferQuery(query); + if (queryNormalized.getIsIncoming() === false) throw new MoneroError("Transfer query contradicts getting incoming transfers"); + queryNormalized.setIsIncoming(true); + return this.getTransfers(queryNormalized) as unknown as MoneroIncomingTransfer[]; + } + + /** + * Get outgoing transfers. + * + * @param {Partial} [query] - configures the query (optional) + * @param {string} [query.address] - get outgoing transfers from a specific address in the wallet (optional) + * @param {number} [query.accountIndex] - get outgoing transfers from a specific account index (optional) + * @param {number} [query.subaddressIndex] - get outgoing transfers from a specific subaddress index (optional) + * @param {int[]} [query.subaddressIndices] - get outgoing transfers from specific subaddress indices (optional) + * @param {bigint} [query.amount] - amount being transferred (optional) + * @param {MoneroDestination[] | MoneroDestinationModel[]} [query.destinations] - individual destinations of an outgoing transfer, which is local wallet data and NOT recoverable from the blockchain (optional) + * @param {boolean} [query.hasDestinations] - get transfers that have destinations or not (optional) + * @param {MoneroTxQuery} [query.txQuery] - get transfers whose transaction meets this query (optional) + * @return {Promise} outgoing transfers that meet the query + */ + async getOutgoingTransfers(query?: Partial): Promise { + const queryNormalized: MoneroTransferQuery = MoneroWallet.normalizeTransferQuery(query); + if (queryNormalized.getIsOutgoing() === false) throw new MoneroError("Transfer query contradicts getting outgoing transfers"); + queryNormalized.setIsOutgoing(true); + return this.getTransfers(queryNormalized) as unknown as MoneroOutgoingTransfer[]; + } + + /** + *

Get outputs created from previous transactions that belong to the wallet + * (i.e. that the wallet can spend one time). Outputs are part of + * transactions which are stored in blocks on the blockchain.

+ * + *

Results can be filtered by passing a query object. Outputs must + * meet every criteria defined in the query in order to be returned. All + * filtering is optional and no filtering is applied when not defined.

+ * + * @param {Parital} [query] - configures the query (optional) + * @param {number} [query.accountIndex] - get outputs associated with a specific account index (optional) + * @param {number} [query.subaddressIndex] - get outputs associated with a specific subaddress index (optional) + * @param {int[]} [query.subaddressIndices] - get outputs associated with specific subaddress indices (optional) + * @param {bigint} [query.amount] - get outputs with a specific amount (optional) + * @param {bigint} [query.minAmount] - get outputs greater than or equal to a minimum amount (optional) + * @param {bigint} [query.maxAmount] - get outputs less than or equal to a maximum amount (optional) + * @param {boolean} [query.isSpent] - get outputs that are spent or not (optional) + * @param {string|MoneroKeyImage} [query.keyImage] - get output with a key image or which matches fields defined in a MoneroKeyImage (optional) + * @param {MoneroTxQuery} [query.txQuery] - get outputs whose transaction meets this filter (optional) + * @return {Promise} the queried outputs + */ + async getOutputs(query?: Partial): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Export outputs in hex format. + * + * @param {boolean} [all] - export all outputs if true, else export the outputs since the last export (default false) + * @return {Promise} outputs in hex format + */ + async exportOutputs(all = false): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Import outputs in hex format. + * + * @param {string} outputsHex - outputs in hex format + * @return {Promise} the number of outputs imported + */ + async importOutputs(outputsHex: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Export signed key images. + * + * @param {boolean} [all] - export all key images if true, else export the key images since the last export (default false) + * @return {Promise} the wallet's signed key images + */ + async exportKeyImages(all = false): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Import signed key images and verify their spent status. + * + * @param {MoneroKeyImage[]} keyImages - images to import and verify (requires hex and signature) + * @return {Promise} results of the import + */ + async importKeyImages(keyImages: MoneroKeyImage[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get new key images from the last imported outputs. + * + * @return {Promise} the key images from the last imported outputs + */ + async getNewKeyImagesFromLastImport(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Freeze an output. + * + * @param {string} keyImage - key image of the output to freeze + * @return {Promise} + */ + async freezeOutput(keyImage: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Thaw a frozen output. + * + * @param {string} keyImage - key image of the output to thaw + * @return {Promise} + */ + async thawOutput(keyImage: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Check if an output is frozen. + * + * @param {string} keyImage - key image of the output to check if frozen + * @return {Promise} true if the output is frozen, false otherwise + */ + async isOutputFrozen(keyImage: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Create a transaction to transfer funds from this wallet. + * + * @param {MoneroTxConfig} config - configures the transaction to create (required) + * @param {string} config.address - single destination address (required unless `destinations` provided) + * @param {bigint|string} config.amount - single destination amount (required unless `destinations` provided) + * @param {number} config.accountIndex - source account index to transfer funds from (required) + * @param {number} [config.subaddressIndex] - source subaddress index to transfer funds from (optional) + * @param {number[]} [config.subaddressIndices] - source subaddress indices to transfer funds from (optional) + * @param {boolean} [config.relay] - relay the transaction to peers to commit to the blockchain (default false) + * @param {MoneroTxPriority} [config.priority] - transaction priority (default MoneroTxPriority.NORMAL) + * @param {MoneroDestination[] | MoneroDestinationModel[]} config.destinations - addresses and amounts in a multi-destination tx (required unless `address` and `amount` provided) + * @param {number[]} [config.subtractFeeFrom] - list of destination indices to split the transaction fee (optional) + * @param {string} [config.paymentId] - transaction payment ID (optional) + * @param {bigint|string} [config.unlockTime] - minimum height or timestamp for the transaction to unlock (default 0) + * @return {Promise} the created transaction + */ + async createTx(config: Partial): Promise { + const configNormalized: MoneroTxConfig = MoneroWallet.normalizeCreateTxsConfig(config); + if (configNormalized.getCanSplit() !== undefined) assert.equal(configNormalized.getCanSplit(), false, "Cannot split transactions using createTx(); use createTxs()"); + configNormalized.setCanSplit(false); + return (await this.createTxs(configNormalized))[0]; + } + + /** + * Create one or more transactions to transfer funds from this wallet. + * + * @param {Partial} config - configures the transactions to create (required) + * @param {string} config.address - single destination address (required unless `destinations` provided) + * @param {bigint|string} config.amount - single destination amount (required unless `destinations` provided) + * @param {number} config.accountIndex - source account index to transfer funds from (required) + * @param {number} [config.subaddressIndex] - source subaddress index to transfer funds from (optional) + * @param {int[]} [config.subaddressIndices] - source subaddress indices to transfer funds from (optional) + * @param {boolean} [config.relay] - relay the transactions to peers to commit to the blockchain (default false) + * @param {MoneroTxPriority} [config.priority] - transaction priority (default MoneroTxPriority.NORMAL) + * @param {MoneroDestination[] | MoneroDestinationModel[]} config.destinations - addresses and amounts in a multi-destination tx (required unless `address` and `amount` provided) + * @param {string} [config.paymentId] - transaction payment ID (optional) + * @param {bigint|string} [config.unlockTime] - minimum height or timestamp for the transactions to unlock (default 0) + * @param {boolean} [config.canSplit] - allow funds to be transferred using multiple transactions (default true) + * @return {Promise} the created transactions + */ + async createTxs(config: Partial): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Sweep an output by key image. + * + * @param {Partial} config - configures the transaction to create (required) + * @param {string} config.address - single destination address (required) + * @param {string} config.keyImage - key image to sweep (required) + * @param {boolean} [config.relay] - relay the transaction to peers to commit to the blockchain (default false) + * @param {bigint|string} [config.unlockTime] - minimum height or timestamp for the transaction to unlock (default 0) + * @param {MoneroTxPriority} [config.priority] - transaction priority (default MoneroTxPriority.NORMAL) + * @return {Promise} the created transaction + */ + async sweepOutput(config: Partial): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Sweep all unlocked funds according to the given configuration. + * + * @param {Partial} config - configures the transactions to create (required) + * @param {string} config.address - single destination address (required) + * @param {number} [config.accountIndex] - source account index to sweep from (optional, defaults to all accounts) + * @param {number} [config.subaddressIndex] - source subaddress index to sweep from (optional, defaults to all subaddresses) + * @param {number[]} [config.subaddressIndices] - source subaddress indices to sweep from (optional) + * @param {boolean} [config.relay] - relay the transactions to peers to commit to the blockchain (default false) + * @param {MoneroTxPriority} [config.priority] - transaction priority (default MoneroTxPriority.NORMAL) + * @param {bigint|string} [config.unlockTime] - minimum height or timestamp for the transactions to unlock (default 0) + * @param {boolean} [config.sweepEachSubaddress] - sweep each subaddress individually if true (default false) + * @return {Promise} the created transactions + */ + async sweepUnlocked(config: Partial): Promise { + throw new MoneroError("Not supported"); + } + + /** + *

Sweep all unmixable dust outputs back to the wallet to make them easier to spend and mix.

+ * + *

NOTE: Dust only exists pre RCT, so this method will throw "no dust to sweep" on new wallets.

+ * + * @param {boolean} [relay] - specifies if the resulting transaction should be relayed (default false) + * @return {Promise} the created transactions + */ + async sweepDust(relay?: boolean): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Relay a previously created transaction. + * + * @param {(MoneroTxWallet | string)} txOrMetadata - transaction or its metadata to relay + * @return {Promise} the hash of the relayed tx + */ + async relayTx(txOrMetadata: MoneroTxWallet | string): Promise { + return (await this.relayTxs([txOrMetadata]))[0]; + } + + /** + * Relay previously created transactions. + * + * @param {(MoneroTxWallet[] | string[])} txsOrMetadatas - transactions or their metadata to relay + * @return {Promise} the hashes of the relayed txs + */ + async relayTxs(txsOrMetadatas: (MoneroTxWallet | string)[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Describe a tx set from unsigned tx hex. + * + * @param {string} unsignedTxHex - unsigned tx hex + * @return {Promise} the tx set containing structured transactions + */ + async describeUnsignedTxSet(unsignedTxHex: string): Promise { + return this.describeTxSet(new MoneroTxSet().setUnsignedTxHex(unsignedTxHex)); + } + + /** + * Describe a tx set from multisig tx hex. + * + * @param {string} multisigTxHex - multisig tx hex + * @return {Promise} the tx set containing structured transactions + */ + async describeMultisigTxSet(multisigTxHex: string): Promise { + return this.describeTxSet(new MoneroTxSet().setMultisigTxHex(multisigTxHex)); + } + + /** + * Describe a tx set containing unsigned or multisig tx hex to a new tx set containing structured transactions. + * + * @param {MoneroTxSet} txSet - a tx set containing unsigned or multisig tx hex + * @return {Promise} txSet - the tx set containing structured transactions + */ + async describeTxSet(txSet: MoneroTxSet): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Sign unsigned transactions from a view-only wallet. + * + * @param {string} unsignedTxHex - unsigned transaction hex from when the transactions were created + * @return {Promise} the signed transaction hex + */ + async signTxs(unsignedTxHex: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Submit signed transactions from a view-only wallet. + * + * @param {string} signedTxHex - signed transaction hex from signTxs() + * @return {Promise} the resulting transaction hashes + */ + async submitTxs(signedTxHex: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Sign a message. + * + * @param {string} message - the message to sign + * @param {MoneroMessageSignatureType} [signatureType] - sign with spend key or view key (default spend key) + * @param {number} [accountIdx] - the account index of the message signature (default 0) + * @param {number} [subaddressIdx] - the subaddress index of the message signature (default 0) + * @return {Promise} the signature + */ + async signMessage(message: string, signatureType = MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY, accountIdx = 0, subaddressIdx = 0): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Verify a signature on a message. + * + * @param {string} message - signed message + * @param {string} address - signing address + * @param {string} signature - signature + * @return {Promise} true if the signature is good, false otherwise + */ + async verifyMessage(message: string, address: string, signature: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get a transaction's secret key from its hash. + * + * @param {string} txHash - transaction's hash + * @return {Promise} - transaction's secret key + */ + async getTxKey(txHash: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Check a transaction in the blockchain with its secret key. + * + * @param {string} txHash - transaction to check + * @param {string} txKey - transaction's secret key + * @param {string} address - destination public address of the transaction + * @return {romise} the result of the check + */ + async checkTxKey(txHash: string, txKey: string, address: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get a transaction signature to prove it. + * + * @param {string} txHash - transaction to prove + * @param {string} address - destination public address of the transaction + * @param {string} [message] - message to include with the signature to further authenticate the proof (optional) + * @return {Promise} the transaction signature + */ + async getTxProof(txHash: string, address: string, message?: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Prove a transaction by checking its signature. + * + * @param {string} txHash - transaction to prove + * @param {string} address - destination public address of the transaction + * @param {string | undefined} message - message included with the signature to further authenticate the proof + * @param {string} signature - transaction signature to confirm + * @return {Promise} the result of the check + */ + async checkTxProof(txHash: string, address: string, message: string | undefined, signature: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Generate a signature to prove a spend. Unlike proving a transaction, it does not require the destination public address. + * + * @param {string} txHash - transaction to prove + * @param {string} [message] - message to include with the signature to further authenticate the proof (optional) + * @return {Promise} the transaction signature + */ + async getSpendProof(txHash: string, message?: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Prove a spend using a signature. Unlike proving a transaction, it does not require the destination public address. + * + * @param {string} txHash - transaction to prove + * @param {string | undefined} message - message included with the signature to further authenticate the proof (optional) + * @param {string} signature - transaction signature to confirm + * @return {Promise} true if the signature is good, false otherwise + */ + async checkSpendProof(txHash: string, message: string | undefined, signature: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Generate a signature to prove the entire balance of the wallet. + * + * @param {string} [message] - message included with the signature to further authenticate the proof (optional) + * @return {Promise} the reserve proof signature + */ + async getReserveProofWallet(message?: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Generate a signature to prove an available amount in an account. + * + * @param {number} accountIdx - account to prove ownership of the amount + * @param {bigint} amount - minimum amount to prove as available in the account + * @param {string} [message] - message to include with the signature to further authenticate the proof (optional) + * @return {Promise} the reserve proof signature + */ + async getReserveProofAccount(accountIdx: number, amount: bigint, message?: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Proves a wallet has a disposable reserve using a signature. + * + * @param {string} address - public wallet address + * @param {string | undefined} message - message included with the signature to further authenticate the proof (optional) + * @param {string} signature - reserve proof signature to check + * @return {Promise} the result of checking the signature proof + */ + async checkReserveProof(address: string, message: string | undefined, signature: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get a transaction note. + * + * @param {string} txHash - transaction to get the note of + * @return {Promise} the tx note + */ + async getTxNote(txHash: string): Promise { + return (await this.getTxNotes([txHash]))[0]; + } + + /** + * Get notes for multiple transactions. + * + * @param {string[]} txHashes - hashes of the transactions to get notes for + * @return {Promise} notes for the transactions + */ + async getTxNotes(txHashes: string[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Set a note for a specific transaction. + * + * @param {string} txHash - hash of the transaction to set a note for + * @param {string} note - the transaction note + * @return {Promise} + */ + async setTxNote(txHash: string, note: string): Promise { + await this.setTxNotes([txHash], [note]); + } + + /** + * Set notes for multiple transactions. + * + * @param {string[]} txHashes - transactions to set notes for + * @param {string[]} notes - notes to set for the transactions + * @return {Promise} + */ + async setTxNotes(txHashes: string[], notes: string[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get address book entries. + * + * @param {number[]} [entryIndices] - indices of the entries to get + * @return {Promise} the address book entries + */ + async getAddressBookEntries(entryIndices?: number[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Add an address book entry. + * + * @param {string} address - entry address + * @param {string} [description] - entry description (optional) + * @return {Promise} the index of the added entry + */ + async addAddressBookEntry(address: string, description?: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Edit an address book entry. + * + * @param {number} index - index of the address book entry to edit + * @param {boolean} setAddress - specifies if the address should be updated + * @param {string | undefined} address - updated address + * @param {boolean} setDescription - specifies if the description should be updated + * @param {string | undefined} description - updated description + * @return {Promise} + */ + async editAddressBookEntry(index: number, setAddress: boolean, address: string | undefined, setDescription: boolean, description: string | undefined): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Delete an address book entry. + * + * @param {number} entryIdx - index of the entry to delete + * @return {Promise} + */ + async deleteAddressBookEntry(entryIdx: number): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Tag accounts. + * + * @param {string} tag - tag to apply to the specified accounts + * @param {number[]} accountIndices - indices of the accounts to tag + * @return {Promise} + */ + async tagAccounts(tag: string, accountIndices: number[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Untag accounts. + * + * @param {number[]} accountIndices - indices of the accounts to untag + * @return {Promise} + */ + async untagAccounts(accountIndices: number[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Return all account tags. + * + * @return {Promise} the wallet's account tags + */ + async getAccountTags(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Sets a human-readable description for a tag. + * + * @param {string} tag - tag to set a description for + * @param {string} label - label to set for the tag + * @return {Promise} + */ + async setAccountTagLabel(tag: string, label: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Creates a payment URI from a send configuration. + * + * @param {MoneroTxConfig} config - specifies configuration for a potential tx + * @return {Promise} the payment uri + */ + async getPaymentUri(config: MoneroTxConfig): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Parses a payment URI to a tx config. + * + * @param {string} uri - payment uri to parse + * @return {Promise} the send configuration parsed from the uri + */ + async parsePaymentUri(uri: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get an attribute. + * + * @param {string} key - attribute to get the value of + * @return {Promise} the attribute's value + */ + async getAttribute(key: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Set an arbitrary attribute. + * + * @param {string} key - attribute key + * @param {string} val - attribute value + * @return {Promise} + */ + async setAttribute(key: string, val: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Start mining. + * + * @param {number} [numThreads] - number of threads created for mining (optional) + * @param {boolean} [backgroundMining] - specifies if mining should occur in the background (optional) + * @param {boolean} [ignoreBattery] - specifies if the battery should be ignored for mining (optional) + * @return {Promise} + */ + async startMining(numThreads: number, backgroundMining?: boolean, ignoreBattery?: boolean): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Stop mining. + * + * @return {Promise} + */ + async stopMining(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Indicates if importing multisig data is needed for returning a correct balance. + * + * @return {Promise} true if importing multisig data is needed for returning a correct balance, false otherwise + */ + async isMultisigImportNeeded(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Indicates if this wallet is a multisig wallet. + * + * @return {Promise} true if this is a multisig wallet, false otherwise + */ + async isMultisig(): Promise { + return (await this.getMultisigInfo()).getIsMultisig(); + } + + /** + * Get multisig info about this wallet. + * + * @return {Promise} multisig info about this wallet + */ + async getMultisigInfo(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Get multisig info as hex to share with participants to begin creating a + * multisig wallet. + * + * @return {Promise} this wallet's multisig hex to share with participants + */ + async prepareMultisig(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Make this wallet multisig by importing multisig hex from participants. + * + * @param {string[]} multisigHexes - multisig hex from each participant + * @param {number} threshold - number of signatures needed to sign transfers + * @param {string} password - wallet password + * @return {Promise} this wallet's multisig hex to share with participants + */ + async makeMultisig(multisigHexes: string[], threshold: number, password: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Exchange multisig hex with participants in a M/N multisig wallet. + * + * This process must be repeated with participants exactly N-M times. + * + * @param {string[]} multisigHexes are multisig hex from each participant + * @param {string} password - wallet's password // TODO monero-project: redundant? wallet is created with password + * @return {Promise} the result which has the multisig's address xor this wallet's multisig hex to share with participants iff not done + */ + async exchangeMultisigKeys(multisigHexes: string[], password: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Export this wallet's multisig info as hex for other participants. + * + * @return {Promise} this wallet's multisig info as hex for other participants + */ + async exportMultisigHex(): Promise { + throw new MoneroError("Not supported?"); + } + + /** + * Import multisig info as hex from other participants. + * + * @param {string[]} multisigHexes - multisig hex from each participant + * @return {Promise} the number of outputs signed with the given multisig hex + */ + async importMultisigHex(multisigHexes: string[]): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Sign multisig transactions from a multisig wallet. + * + * @param {string} multisigTxHex - unsigned multisig transactions as hex + * @return {MoneroMultisigSignResult} the result of signing the multisig transactions + */ + async signMultisigTxHex(multisigTxHex: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Submit signed multisig transactions from a multisig wallet. + * + * @param {string} signedMultisigTxHex - signed multisig hex returned from signMultisigTxHex() + * @return {Promise} the resulting transaction hashes + */ + async submitMultisigTxHex(signedMultisigTxHex: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Change the wallet password. + * + * @param {string} oldPassword - the wallet's old password + * @param {string} newPassword - the wallet's new password + * @return {Promise} + */ + async changePassword(oldPassword: string, newPassword: string): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Save the wallet at its current path. + * + * @return {Promise} + */ + async save(): Promise { + throw new MoneroError("Not supported"); + } + + /** + * Optionally save then close the wallet. + * + * @param {boolean} [save] - specifies if the wallet should be saved before being closed (default false) + * @return {Promise} + */ + async close(save = false): Promise { + if (this.connectionManager) this.connectionManager.removeListener(this.connectionManagerListener); + this.connectionManager = undefined; + this.connectionManagerListener = undefined; + } + + /** + * Indicates if this wallet is closed or not. + * + * @return {Promise} true if the wallet is closed, false otherwise + */ + async isClosed(): Promise { + throw new MoneroError("Not supported"); + } + + // -------------------------------- PRIVATE --------------------------------- + + protected static normalizeTxQuery(query) { + if (query instanceof MoneroTxQuery) query = query.copy(); + else if (Array.isArray(query)) query = new MoneroTxQuery().setHashes(query); + else { + query = Object.assign({}, query); + query = new MoneroTxQuery(query); + } + if (query.getBlock() === undefined) query.setBlock(new MoneroBlock().setTxs([query])); + if (query.getInputQuery()) query.getInputQuery().setTxQuery(query); + if (query.getOutputQuery()) query.getOutputQuery().setTxQuery(query); + return query; + } + + protected static normalizeTransferQuery(query) { + query = new MoneroTransferQuery(query); + if (query.getTxQuery() !== undefined) { + let txQuery = query.getTxQuery().copy(); + query = txQuery.getTransferQuery(); + } + if (query.getTxQuery() === undefined) query.setTxQuery(new MoneroTxQuery()); + query.getTxQuery().setTransferQuery(query); + if (query.getTxQuery().getBlock() === undefined) query.getTxQuery().setBlock(new MoneroBlock().setTxs([query.getTxQuery()])); + return query; + } + + protected static normalizeOutputQuery(query) { + query = new MoneroOutputQuery(query); + if (query.getTxQuery() !== undefined) { + let txQuery = query.getTxQuery().copy(); + query = txQuery.getOutputQuery(); + } + if (query.getTxQuery() === undefined) query.setTxQuery(new MoneroTxQuery()); + query.getTxQuery().setOutputQuery(query); + if (query.getTxQuery().getBlock() === undefined) query.getTxQuery().setBlock(new MoneroBlock().setTxs([query.getTxQuery()])); + return query; + } + + protected static normalizeCreateTxsConfig(config) { + if (config === undefined || !(config instanceof Object)) throw new MoneroError("Must provide MoneroTxConfig or equivalent JS object"); + config = new MoneroTxConfig(config); + assert(config.getDestinations() && config.getDestinations().length > 0, "Must provide destinations"); + assert.equal(config.getSweepEachSubaddress(), undefined); + assert.equal(config.getBelowAmount(), undefined); + return config; + } + + protected static normalizeSweepOutputConfig(config) { + if (config === undefined || !(config instanceof Object)) throw new MoneroError("Must provide MoneroTxConfig or equivalent JS object"); + config = new MoneroTxConfig(config); + assert.equal(config.getSweepEachSubaddress(), undefined); + assert.equal(config.getBelowAmount(), undefined); + assert.equal(config.getCanSplit(), undefined, "Cannot split transactions when sweeping an output"); + if (!config.getDestinations() || config.getDestinations().length !== 1 || !config.getDestinations()[0].getAddress()) throw new MoneroError("Must provide exactly one destination address to sweep output to"); + if (config.getSubtractFeeFrom() && config.getSubtractFeeFrom().length > 0) throw new MoneroError("Sweep transfers do not support subtracting fees from destinations"); + return config; + } + + protected static normalizeSweepUnlockedConfig(config) { + if (config === undefined || !(config instanceof Object)) throw new MoneroError("Must provide MoneroTxConfig or equivalent JS object"); + config = new MoneroTxConfig(config); + if (config.getDestinations() === undefined || config.getDestinations().length != 1) throw new MoneroError("Must provide exactly one destination to sweep to"); + if (config.getDestinations()[0].getAddress() === undefined) throw new MoneroError("Must provide destination address to sweep to"); + if (config.getDestinations()[0].getAmount() !== undefined) throw new MoneroError("Cannot provide amount in sweep config"); + if (config.getKeyImage() !== undefined) throw new MoneroError("Key image defined; use sweepOutput() to sweep an output by its key image"); + if (config.getSubaddressIndices() !== undefined && config.getSubaddressIndices().length === 0) config.setSubaddressIndices(undefined); + if (config.getAccountIndex() === undefined && config.getSubaddressIndices() !== undefined) throw new MoneroError("Must provide account index if subaddress indices are provided"); + return config; + } +} diff --git a/src/main/ts/wallet/MoneroWalletFull.ts b/src/main/ts/wallet/MoneroWalletFull.ts new file mode 100644 index 000000000..878d16f87 --- /dev/null +++ b/src/main/ts/wallet/MoneroWalletFull.ts @@ -0,0 +1,2461 @@ +import assert from "assert"; +import Path from "path"; +import GenUtils from "../common/GenUtils"; +import LibraryUtils from "../common/LibraryUtils"; +import TaskLooper from "../common/TaskLooper"; +import MoneroAccount from "./model/MoneroAccount"; +import MoneroAccountTag from "./model/MoneroAccountTag"; +import MoneroAddressBookEntry from "./model/MoneroAddressBookEntry"; +import MoneroBlock from "../daemon/model/MoneroBlock"; +import MoneroCheckTx from "./model/MoneroCheckTx"; +import MoneroCheckReserve from "./model/MoneroCheckReserve"; +import MoneroDaemonRpc from "../daemon/MoneroDaemonRpc"; +import MoneroError from "../common/MoneroError"; +import MoneroIncomingTransfer from "./model/MoneroIncomingTransfer"; +import MoneroIntegratedAddress from "./model/MoneroIntegratedAddress"; +import MoneroKeyImage from "../daemon/model/MoneroKeyImage"; +import MoneroKeyImageImportResult from "./model/MoneroKeyImageImportResult"; +import MoneroMultisigInfo from "./model/MoneroMultisigInfo"; +import MoneroMultisigInitResult from "./model/MoneroMultisigInitResult"; +import MoneroMultisigSignResult from "./model/MoneroMultisigSignResult"; +import MoneroNetworkType from "../daemon/model/MoneroNetworkType"; +import MoneroOutputQuery from "./model/MoneroOutputQuery"; +import MoneroOutputWallet from "./model/MoneroOutputWallet"; +import MoneroRpcConnection from "../common/MoneroRpcConnection"; +import MoneroSubaddress from "./model/MoneroSubaddress"; +import MoneroSyncResult from "./model/MoneroSyncResult"; +import MoneroTransfer from "./model/MoneroTransfer"; +import MoneroTransferQuery from "./model/MoneroTransferQuery"; +import MoneroTxConfig from "./model/MoneroTxConfig"; +import MoneroTxQuery from "./model/MoneroTxQuery"; +import MoneroTxSet from "./model/MoneroTxSet"; +import MoneroTx from "../daemon/model/MoneroTx"; +import MoneroTxWallet from "./model/MoneroTxWallet"; +import MoneroWallet from "./MoneroWallet"; +import MoneroWalletConfig from "./model/MoneroWalletConfig"; +import { MoneroWalletKeys, MoneroWalletKeysProxy } from "./MoneroWalletKeys"; +import MoneroWalletListener from "./model/MoneroWalletListener"; +import MoneroMessageSignatureType from "./model/MoneroMessageSignatureType"; +import MoneroMessageSignatureResult from "./model/MoneroMessageSignatureResult"; +import MoneroVersion from "../daemon/model/MoneroVersion"; +import fs from "fs"; + +/** + * Implements a Monero wallet using client-side WebAssembly bindings to monero-project's wallet2 in C++. + */ +export default class MoneroWalletFull extends MoneroWalletKeys { + + // static variables + protected static readonly DEFAULT_SYNC_PERIOD_IN_MS = 20000; + protected static FS; + + // instance variables + protected path: string; + protected password: string; + protected listeners: MoneroWalletListener[]; + protected fs: any; + protected fullListener: WalletFullListener; + protected fullListenerHandle: number; + protected rejectUnauthorized: boolean; + protected rejectUnauthorizedConfigId: string; + protected syncPeriodInMs: number; + protected syncLooper: TaskLooper; + protected browserMainPath: string; + + /** + * Internal constructor which is given the memory address of a C++ wallet instance. + * + * This constructor should not be called through static wallet creation utilities in this class. + * + * @param {number} cppAddress - address of the wallet instance in C++ + * @param {string} path - path of the wallet instance + * @param {string} password - password of the wallet instance + * @param {FileSystem} fs - node.js-compatible file system to read/write wallet files + * @param {boolean} rejectUnauthorized - specifies if unauthorized requests (e.g. self-signed certificates) should be rejected + * @param {string} rejectUnauthorizedFnId - unique identifier for http_client_wasm to query rejectUnauthorized + * @param {MoneroWalletFullProxy} walletProxy - proxy to invoke wallet operations in a web worker + * + * @private + */ + constructor(cppAddress, path, password, fs, rejectUnauthorized, rejectUnauthorizedFnId, walletProxy?: MoneroWalletFullProxy) { + super(cppAddress, walletProxy); + if (walletProxy) return; + this.path = path; + this.password = password; + this.listeners = []; + this.fs = fs ? fs : (path ? MoneroWalletFull.getFs() : undefined); + this._isClosed = false; + this.fullListener = new WalletFullListener(this); // receives notifications from wasm c++ + this.fullListenerHandle = 0; // memory address of the wallet listener in c++ + this.rejectUnauthorized = rejectUnauthorized; + this.rejectUnauthorizedConfigId = rejectUnauthorizedFnId; + this.syncPeriodInMs = MoneroWalletFull.DEFAULT_SYNC_PERIOD_IN_MS; + LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, () => this.rejectUnauthorized); // register fn informing if unauthorized reqs should be rejected + } + + // --------------------------- STATIC UTILITIES ----------------------------- + + /** + * Check if a wallet exists at a given path. + * + * @param {string} path - path of the wallet on the file system + * @param {fs} - Node.js compatible file system to use (optional, defaults to disk if nodejs) + * @return {boolean} true if a wallet exists at the given path, false otherwise + */ + static walletExists(path, fs) { + assert(path, "Must provide a path to look for a wallet"); + if (!fs) fs = MoneroWalletFull.getFs(); + if (!fs) throw new MoneroError("Must provide file system to check if wallet exists"); + let exists = fs.existsSync(path + ".keys"); + LibraryUtils.log(1, "Wallet exists at " + path + ": " + exists); + return exists; + } + + static async openWallet(config: Partial) { + + // validate config + config = new MoneroWalletConfig(config); + if (config.getProxyToWorker() === undefined) config.setProxyToWorker(true); + if (config.getSeed() !== undefined) throw new MoneroError("Cannot specify seed when opening wallet"); + if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot specify seed offset when opening wallet"); + if (config.getPrimaryAddress() !== undefined) throw new MoneroError("Cannot specify primary address when opening wallet"); + if (config.getPrivateViewKey() !== undefined) throw new MoneroError("Cannot specify private view key when opening wallet"); + if (config.getPrivateSpendKey() !== undefined) throw new MoneroError("Cannot specify private spend key when opening wallet"); + if (config.getRestoreHeight() !== undefined) throw new MoneroError("Cannot specify restore height when opening wallet"); + if (config.getLanguage() !== undefined) throw new MoneroError("Cannot specify language when opening wallet"); + if (config.getSaveCurrent() === true) throw new MoneroError("Cannot save current wallet when opening full wallet"); + + // read wallet data from disk if not given + if (!config.getKeysData()) { + let fs = config.getFs() ? config.getFs() : MoneroWalletFull.getFs(); + if (!fs) throw new MoneroError("Must provide file system to read wallet data from"); + if (!this.walletExists(config.getPath(), fs)) throw new MoneroError("Wallet does not exist at path: " + config.getPath()); + config.setKeysData(fs.readFileSync(config.getPath() + ".keys")); + config.setCacheData(fs.existsSync(config.getPath()) ? fs.readFileSync(config.getPath()) : ""); + } + + // open wallet from data + return MoneroWalletFull.openWalletData(config); + } + + static async createWallet(config: MoneroWalletConfig): Promise { + + // validate config + if (config === undefined) throw new MoneroError("Must provide config to create wallet"); + if (config.getSeed() !== undefined && (config.getPrimaryAddress() !== undefined || config.getPrivateViewKey() !== undefined || config.getPrivateSpendKey() !== undefined)) throw new MoneroError("Wallet may be initialized with a seed or keys but not both"); + if (config.getNetworkType() === undefined) throw new MoneroError("Must provide a networkType: 'mainnet', 'testnet' or 'stagenet'"); + MoneroNetworkType.validate(config.getNetworkType()); + if (config.getSaveCurrent() === true) throw new MoneroError("Cannot save current wallet when creating full WASM wallet"); + if (config.getPath() === undefined) config.setPath(""); + if (config.getPath() && MoneroWalletFull.walletExists(config.getPath(), config.getFs())) throw new MoneroError("Wallet already exists: " + config.getPath()); + if (config.getPassword() === undefined) config.setPassword(""); + + // set server from connection manager if provided + if (config.getConnectionManager()) { + if (config.getServer()) throw new MoneroError("Wallet can be initialized with a server or connection manager but not both"); + config.setServer(config.getConnectionManager().getConnection()); + } + + // create proxied or local wallet + let wallet; + if (config.getProxyToWorker() === undefined) config.setProxyToWorker(true); + if (config.getProxyToWorker()) { + let walletProxy = await MoneroWalletFullProxy.createWallet(config); + wallet = new MoneroWalletFull(undefined, undefined, undefined, undefined, undefined, undefined, walletProxy); + } else { + if (config.getSeed() !== undefined) { + if (config.getLanguage() !== undefined) throw new MoneroError("Cannot provide language when creating wallet from seed"); + wallet = await MoneroWalletFull.createWalletFromSeed(config); + } else if (config.getPrivateSpendKey() !== undefined || config.getPrimaryAddress() !== undefined) { + if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating wallet from keys"); + wallet = await MoneroWalletFull.createWalletFromKeys(config); + } else { + if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating random wallet"); + if (config.getRestoreHeight() !== undefined) throw new MoneroError("Cannot provide restoreHeight when creating random wallet"); + wallet = await MoneroWalletFull.createWalletRandom(config); + } + } + + // set wallet's connection manager + await wallet.setConnectionManager(config.getConnectionManager()); + return wallet; + } + + protected static async createWalletFromSeed(config: MoneroWalletConfig): Promise { + + // validate and normalize params + let daemonConnection = config.getServer(); + let rejectUnauthorized = daemonConnection ? daemonConnection.getRejectUnauthorized() : true; + if (config.getRestoreHeight() === undefined) config.setRestoreHeight(0); + if (config.getSeedOffset() === undefined) config.setSeedOffset(""); + + // load full wasm module + let module = await LibraryUtils.loadFullModule(); + + // create wallet in queue + let wallet = await module.queueTask(async () => { + return new Promise((resolve, reject) => { + + // register fn informing if unauthorized reqs should be rejected + let rejectUnauthorizedFnId = GenUtils.getUUID(); + LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, () => rejectUnauthorized); + + // create wallet in wasm which invokes callback when done + module.create_full_wallet(JSON.stringify(config.toJson()), rejectUnauthorizedFnId, async (cppAddress) => { + if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); + else resolve(new MoneroWalletFull(cppAddress, config.getPath(), config.getPassword(), config.getFs(), config.getServer() ? config.getServer().getRejectUnauthorized() : undefined, rejectUnauthorizedFnId)); + }); + }); + }); + + // save wallet + if (config.getPath()) await wallet.save(); + return wallet; + } + + protected static async createWalletFromKeys(config: MoneroWalletConfig): Promise { + + // validate and normalize params + MoneroNetworkType.validate(config.getNetworkType()); + if (config.getPrimaryAddress() === undefined) config.setPrimaryAddress(""); + if (config.getPrivateViewKey() === undefined) config.setPrivateViewKey(""); + if (config.getPrivateSpendKey() === undefined) config.setPrivateSpendKey(""); + let daemonConnection = config.getServer(); + let rejectUnauthorized = daemonConnection ? daemonConnection.getRejectUnauthorized() : true; + if (config.getRestoreHeight() === undefined) config.setRestoreHeight(0); + if (config.getLanguage() === undefined) config.setLanguage("English"); + + // load full wasm module + let module = await LibraryUtils.loadFullModule(); + + // create wallet in queue + let wallet = await module.queueTask(async () => { + return new Promise((resolve, reject) => { + + // register fn informing if unauthorized reqs should be rejected + let rejectUnauthorizedFnId = GenUtils.getUUID(); + LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, () => rejectUnauthorized); + + // create wallet in wasm which invokes callback when done + module.create_full_wallet(JSON.stringify(config.toJson()), rejectUnauthorizedFnId, async (cppAddress) => { + if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); + else resolve(new MoneroWalletFull(cppAddress, config.getPath(), config.getPassword(), config.getFs(), config.getServer() ? config.getServer().getRejectUnauthorized() : undefined, rejectUnauthorizedFnId)); + }); + }); + }); + + // save wallet + if (config.getPath()) await wallet.save(); + return wallet; + } + + protected static async createWalletRandom(config: MoneroWalletConfig): Promise { + + // validate and normalize params + if (config.getLanguage() === undefined) config.setLanguage("English"); + let daemonConnection = config.getServer(); + let rejectUnauthorized = daemonConnection ? daemonConnection.getRejectUnauthorized() : true; + + // load wasm module + let module = await LibraryUtils.loadFullModule(); + + // create wallet in queue + let wallet = await module.queueTask(async () => { + return new Promise((resolve, reject) => { + + // register fn informing if unauthorized reqs should be rejected + let rejectUnauthorizedFnId = GenUtils.getUUID(); + LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, () => rejectUnauthorized); + + // create wallet in wasm which invokes callback when done + module.create_full_wallet(JSON.stringify(config.toJson()), rejectUnauthorizedFnId, async (cppAddress) => { + if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); + else resolve(new MoneroWalletFull(cppAddress, config.getPath(), config.getPassword(), config.getFs(), config.getServer() ? config.getServer().getRejectUnauthorized() : undefined, rejectUnauthorizedFnId)); + }); + }); + }); + + // save wallet + if (config.getPath()) await wallet.save(); + return wallet; + } + + static async getSeedLanguages() { + let module = await LibraryUtils.loadFullModule(); + return module.queueTask(async () => { + return JSON.parse(module.get_keys_wallet_seed_languages()).languages; + }); + } + + static getFs() { + if (!MoneroWalletFull.FS) MoneroWalletFull.FS = GenUtils.isBrowser() ? undefined : fs; + return MoneroWalletFull.FS; + } + + // ------------ WALLET METHODS SPECIFIC TO WASM IMPLEMENTATION -------------- + + // TODO: move these to MoneroWallet.ts, others can be unsupported + + /** + * Get the maximum height of the peers the wallet's daemon is connected to. + * + * @return {Promise} the maximum height of the peers the wallet's daemon is connected to + */ + async getDaemonMaxPeerHeight(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getDaemonMaxPeerHeight(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + + // call wasm which invokes callback when done + this.module.get_daemon_max_peer_height(this.cppAddress, (resp) => { + resolve(resp); + }); + }); + }); + } + + /** + * Indicates if the wallet's daemon is synced with the network. + * + * @return {Promise} true if the daemon is synced with the network, false otherwise + */ + async isDaemonSynced(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().isDaemonSynced(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + + // call wasm which invokes callback when done + this.module.is_daemon_synced(this.cppAddress, (resp) => { + resolve(resp); + }); + }); + }); + } + + /** + * Indicates if the wallet is synced with the daemon. + * + * @return {Promise} true if the wallet is synced with the daemon, false otherwise + */ + async isSynced(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().isSynced(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.is_synced(this.cppAddress, (resp) => { + resolve(resp); + }); + }); + }); + } + + /** + * Get the wallet's network type (mainnet, testnet, or stagenet). + * + * @return {Promise} the wallet's network type + */ + async getNetworkType(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getNetworkType(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return this.module.get_network_type(this.cppAddress); + }); + } + + /** + * Get the height of the first block that the wallet scans. + * + * @return {Promise} the height of the first block that the wallet scans + */ + async getRestoreHeight(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getRestoreHeight(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return this.module.get_restore_height(this.cppAddress); + }); + } + + /** + * Set the height of the first block that the wallet scans. + * + * @param {number} restoreHeight - height of the first block that the wallet scans + * @return {Promise} + */ + async setRestoreHeight(restoreHeight: number): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().setRestoreHeight(restoreHeight); + return this.module.queueTask(async () => { + this.assertNotClosed(); + this.module.set_restore_height(this.cppAddress, restoreHeight); + }); + } + + /** + * Move the wallet from its current path to the given path. + * + * @param {string} path - the wallet's destination path + * @return {Promise} + */ + async moveTo(path: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().moveTo(path); + return MoneroWalletFull.moveTo(path, this); + } + + // -------------------------- COMMON WALLET METHODS ------------------------- + + async addListener(listener: MoneroWalletListener): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().addListener(listener); + assert(listener instanceof MoneroWalletListener, "Listener must be instance of MoneroWalletListener"); + this.listeners.push(listener); + await this.refreshListening(); + } + + async removeListener(listener): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().removeListener(listener); + let idx = this.listeners.indexOf(listener); + if (idx > -1) this.listeners.splice(idx, 1); + else throw new MoneroError("Listener is not registered with wallet"); + await this.refreshListening(); + } + + getListeners(): MoneroWalletListener[] { + if (this.getWalletProxy()) return this.getWalletProxy().getListeners(); + return this.listeners; + } + + async setDaemonConnection(uriOrConnection?: MoneroRpcConnection | string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().setDaemonConnection(uriOrConnection); + + // normalize connection + let connection = !uriOrConnection ? undefined : uriOrConnection instanceof MoneroRpcConnection ? uriOrConnection : new MoneroRpcConnection(uriOrConnection); + let uri = connection && connection.getUri() ? connection.getUri() : ""; + let username = connection && connection.getUsername() ? connection.getUsername() : ""; + let password = connection && connection.getPassword() ? connection.getPassword() : ""; + let rejectUnauthorized = connection ? connection.getRejectUnauthorized() : undefined; + this.rejectUnauthorized = rejectUnauthorized; // persist locally + + // set connection in queue + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.set_daemon_connection(this.cppAddress, uri, username, password, (resp) => { + resolve(); + }); + }); + }); + } + + async getDaemonConnection(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getDaemonConnection(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + let connectionContainerStr = this.module.get_daemon_connection(this.cppAddress); + if (!connectionContainerStr) resolve(undefined); + else { + let jsonConnection = JSON.parse(connectionContainerStr); + resolve(new MoneroRpcConnection({uri: jsonConnection.uri, username: jsonConnection.username, password: jsonConnection.password, rejectUnauthorized: this.rejectUnauthorized})); + } + }); + }); + } + + async isConnectedToDaemon(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().isConnectedToDaemon(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.is_connected_to_daemon(this.cppAddress, (resp) => { + resolve(resp); + }); + }); + }); + } + + async getVersion(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getVersion(); + throw new MoneroError("Not implemented"); + } + + async getPath(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getPath(); + return this.path; + } + + async getIntegratedAddress(standardAddress?: string, paymentId?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getIntegratedAddress(standardAddress, paymentId); + return this.module.queueTask(async () => { + this.assertNotClosed(); + try { + let result = this.module.get_integrated_address(this.cppAddress, standardAddress ? standardAddress : "", paymentId ? paymentId : ""); + if (result.charAt(0) !== "{") throw new MoneroError(result); + return new MoneroIntegratedAddress(JSON.parse(result)); + } catch (err: any) { + if (err.message.includes("Invalid payment ID")) throw new MoneroError("Invalid payment ID: " + paymentId); + throw new MoneroError(err.message); + } + }); + } + + async decodeIntegratedAddress(integratedAddress: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().decodeIntegratedAddress(integratedAddress); + return this.module.queueTask(async () => { + this.assertNotClosed(); + try { + let result = this.module.decode_integrated_address(this.cppAddress, integratedAddress); + if (result.charAt(0) !== "{") throw new MoneroError(result); + return new MoneroIntegratedAddress(JSON.parse(result)); + } catch (err: any) { + throw new MoneroError(err.message); + } + }); + } + + async getHeight(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getHeight(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.get_height(this.cppAddress, (resp) => { + resolve(resp); + }); + }); + }); + } + + async getDaemonHeight(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getDaemonHeight(); + if (!(await this.isConnectedToDaemon())) throw new MoneroError("Wallet is not connected to daemon"); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.get_daemon_height(this.cppAddress, (resp) => { + resolve(resp); + }); + }); + }); + } + + async getHeightByDate(year: number, month: number, day: number): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getHeightByDate(year, month, day); + if (!(await this.isConnectedToDaemon())) throw new MoneroError("Wallet is not connected to daemon"); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.get_height_by_date(this.cppAddress, year, month, day, (resp) => { + if (typeof resp === "string") reject(new MoneroError(resp)); + else resolve(resp); + }); + }); + }); + } + + /** + * Synchronize the wallet with the daemon as a one-time synchronous process. + * + * @param {MoneroWalletListener|number} [listenerOrStartHeight] - listener xor start height (defaults to no sync listener, the last synced block) + * @param {number} [startHeight] - startHeight if not given in first arg (defaults to last synced block) + * @param {boolean} [allowConcurrentCalls] - allow other wallet methods to be processed simultaneously during sync (default false)

WARNING: enabling this option will crash wallet execution if another call makes a simultaneous network request. TODO: possible to sync wasm network requests in http_client_wasm.cpp? + */ + async sync(listenerOrStartHeight?: MoneroWalletListener | number, startHeight?: number, allowConcurrentCalls = false): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().sync(listenerOrStartHeight, startHeight, allowConcurrentCalls); + if (!(await this.isConnectedToDaemon())) throw new MoneroError("Wallet is not connected to daemon"); + + // normalize params + startHeight = listenerOrStartHeight === undefined || listenerOrStartHeight instanceof MoneroWalletListener ? startHeight : listenerOrStartHeight; + let listener = listenerOrStartHeight instanceof MoneroWalletListener ? listenerOrStartHeight : undefined; + if (startHeight === undefined) startHeight = Math.max(await this.getHeight(), await this.getRestoreHeight()); + + // register listener if given + if (listener) await this.addListener(listener); + + // sync wallet + let err; + let result; + try { + let that = this; + result = await (allowConcurrentCalls ? syncWasm() : this.module.queueTask(async () => syncWasm())); + function syncWasm() { + that.assertNotClosed(); + return new Promise((resolve, reject) => { + + // sync wallet in wasm which invokes callback when done + that.module.sync(that.cppAddress, startHeight, async (resp) => { + if (resp.charAt(0) !== "{") reject(new MoneroError(resp)); + else { + let respJson = JSON.parse(resp); + resolve(new MoneroSyncResult(respJson.numBlocksFetched, respJson.receivedMoney)); + } + }); + }); + } + } catch (e) { + err = e; + } + + // unregister listener + if (listener) await this.removeListener(listener); + + // throw error or return + if (err) throw err; + return result; + } + + async startSyncing(syncPeriodInMs?: number): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().startSyncing(syncPeriodInMs); + if (!(await this.isConnectedToDaemon())) throw new MoneroError("Wallet is not connected to daemon"); + this.syncPeriodInMs = syncPeriodInMs === undefined ? MoneroWalletFull.DEFAULT_SYNC_PERIOD_IN_MS : syncPeriodInMs; + if (!this.syncLooper) this.syncLooper = new TaskLooper(async () => await this.backgroundSync()) + this.syncLooper.start(this.syncPeriodInMs); + } + + async stopSyncing(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().stopSyncing(); + this.assertNotClosed(); + if (this.syncLooper) this.syncLooper.stop(); + this.module.stop_syncing(this.cppAddress); // task is not queued so wallet stops immediately + } + + async scanTxs(txHashes: string[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().scanTxs(txHashes); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.scan_txs(this.cppAddress, JSON.stringify({txHashes: txHashes}), (err) => { + if (err) reject(new MoneroError(err)); + else resolve(); + }); + }); + }); + } + + async rescanSpent(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().rescanSpent(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.rescan_spent(this.cppAddress, () => resolve()); + }); + }); + } + + async rescanBlockchain(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().rescanBlockchain(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.rescan_blockchain(this.cppAddress, () => resolve()); + }); + }); + } + + async getBalance(accountIdx?: number, subaddressIdx?: number): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getBalance(accountIdx, subaddressIdx); + return this.module.queueTask(async () => { + this.assertNotClosed(); + + // get balance encoded in json string + let balanceStr; + if (accountIdx === undefined) { + assert(subaddressIdx === undefined, "Subaddress index must be undefined if account index is undefined"); + balanceStr = this.module.get_balance_wallet(this.cppAddress); + } else if (subaddressIdx === undefined) { + balanceStr = this.module.get_balance_account(this.cppAddress, accountIdx); + } else { + balanceStr = this.module.get_balance_subaddress(this.cppAddress, accountIdx, subaddressIdx); + } + + // parse json string to bigint + return BigInt(JSON.parse(GenUtils.stringifyBigInts(balanceStr)).balance); + }); + } + + async getUnlockedBalance(accountIdx?: number, subaddressIdx?: number): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getUnlockedBalance(accountIdx, subaddressIdx); + return this.module.queueTask(async () => { + this.assertNotClosed(); + + // get balance encoded in json string + let unlockedBalanceStr; + if (accountIdx === undefined) { + assert(subaddressIdx === undefined, "Subaddress index must be undefined if account index is undefined"); + unlockedBalanceStr = this.module.get_unlocked_balance_wallet(this.cppAddress); + } else if (subaddressIdx === undefined) { + unlockedBalanceStr = this.module.get_unlocked_balance_account(this.cppAddress, accountIdx); + } else { + unlockedBalanceStr = this.module.get_unlocked_balance_subaddress(this.cppAddress, accountIdx, subaddressIdx); + } + + // parse json string to bigint + return BigInt(JSON.parse(GenUtils.stringifyBigInts(unlockedBalanceStr)).unlockedBalance); + }); + } + + async getAccounts(includeSubaddresses?: boolean, tag?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getAccounts(includeSubaddresses, tag); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let accountsStr = this.module.get_accounts(this.cppAddress, includeSubaddresses ? true : false, tag ? tag : ""); + let accounts = []; + for (let accountJson of JSON.parse(GenUtils.stringifyBigInts(accountsStr)).accounts) { + accounts.push(MoneroWalletFull.sanitizeAccount(new MoneroAccount(accountJson))); + } + return accounts; + }); + } + + async getAccount(accountIdx: number, includeSubaddresses?: boolean): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getAccount(accountIdx, includeSubaddresses); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let accountStr = this.module.get_account(this.cppAddress, accountIdx, includeSubaddresses ? true : false); + let accountJson = JSON.parse(GenUtils.stringifyBigInts(accountStr)); + return MoneroWalletFull.sanitizeAccount(new MoneroAccount(accountJson)); + }); + + } + + async createAccount(label?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().createAccount(label); + if (label === undefined) label = ""; + return this.module.queueTask(async () => { + this.assertNotClosed(); + let accountStr = this.module.create_account(this.cppAddress, label); + let accountJson = JSON.parse(GenUtils.stringifyBigInts(accountStr)); + return MoneroWalletFull.sanitizeAccount(new MoneroAccount(accountJson)); + }); + } + + async getSubaddresses(accountIdx: number, subaddressIndices?: number[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getSubaddresses(accountIdx, subaddressIndices); + let args = {accountIdx: accountIdx, subaddressIndices: subaddressIndices === undefined ? [] : GenUtils.listify(subaddressIndices)}; + return this.module.queueTask(async () => { + this.assertNotClosed(); + let subaddressesJson = JSON.parse(GenUtils.stringifyBigInts(this.module.get_subaddresses(this.cppAddress, JSON.stringify(args)))).subaddresses; + let subaddresses = []; + for (let subaddressJson of subaddressesJson) subaddresses.push(MoneroWalletKeys.sanitizeSubaddress(new MoneroSubaddress(subaddressJson))); + return subaddresses; + }); + } + + async createSubaddress(accountIdx: number, label?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().createSubaddress(accountIdx, label); + if (label === undefined) label = ""; + return this.module.queueTask(async () => { + this.assertNotClosed(); + let subaddressStr = this.module.create_subaddress(this.cppAddress, accountIdx, label); + let subaddressJson = JSON.parse(GenUtils.stringifyBigInts(subaddressStr)); + return MoneroWalletKeys.sanitizeSubaddress(new MoneroSubaddress(subaddressJson)); + }); + } + + async setSubaddressLabel(accountIdx: number, subaddressIdx: number, label: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().setSubaddressLabel(accountIdx, subaddressIdx, label); + if (label === undefined) label = ""; + return this.module.queueTask(async () => { + this.assertNotClosed(); + this.module.set_subaddress_label(this.cppAddress, accountIdx, subaddressIdx, label); + }); + } + + async getTxs(query?: string[] | Partial): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getTxs(query); + + // copy and normalize query up to block + const queryNormalized = query = MoneroWallet.normalizeTxQuery(query); + + // schedule task + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + + // call wasm which invokes callback + this.module.get_txs(this.cppAddress, JSON.stringify(queryNormalized.getBlock().toJson()), (blocksJsonStr) => { + + // check for error + if (blocksJsonStr.charAt(0) !== "{") { + reject(new MoneroError(blocksJsonStr)); + return; + } + + // resolve with deserialized txs + try { + resolve(MoneroWalletFull.deserializeTxs(queryNormalized, blocksJsonStr)); + } catch (err) { + reject(err); + } + }); + }); + }); + } + + async getTransfers(query?: Partial): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getTransfers(query); + + // copy and normalize query up to block + const queryNormalized = MoneroWallet.normalizeTransferQuery(query); + + // return promise which resolves on callback + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + + // call wasm which invokes callback + this.module.get_transfers(this.cppAddress, JSON.stringify(queryNormalized.getTxQuery().getBlock().toJson()), (blocksJsonStr) => { + + // check for error + if (blocksJsonStr.charAt(0) !== "{") { + reject(new MoneroError(blocksJsonStr)); + return; + } + + // resolve with deserialized transfers + try { + resolve(MoneroWalletFull.deserializeTransfers(queryNormalized, blocksJsonStr)); + } catch (err) { + reject(err); + } + }); + }); + }); + } + + async getOutputs(query?: Partial): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getOutputs(query); + + // copy and normalize query up to block + const queryNormalized = MoneroWallet.normalizeOutputQuery(query); + + // return promise which resolves on callback + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) =>{ + + // call wasm which invokes callback + this.module.get_outputs(this.cppAddress, JSON.stringify(queryNormalized.getTxQuery().getBlock().toJson()), (blocksJsonStr) => { + + // check for error + if (blocksJsonStr.charAt(0) !== "{") { + reject(new MoneroError(blocksJsonStr)); + return; + } + + // resolve with deserialized outputs + try { + resolve(MoneroWalletFull.deserializeOutputs(queryNormalized, blocksJsonStr)); + } catch (err) { + reject(err); + } + }); + }); + }); + } + + async exportOutputs(all = false): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().exportOutputs(all); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.export_outputs(this.cppAddress, all, (outputsHex) => resolve(outputsHex)); + }); + }); + } + + async importOutputs(outputsHex: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().importOutputs(outputsHex); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.import_outputs(this.cppAddress, outputsHex, (numImported) => resolve(numImported)); + }); + }); + } + + async exportKeyImages(all = false): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().exportKeyImages(all); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.export_key_images(this.cppAddress, all, (keyImagesStr) => { + if (keyImagesStr.charAt(0) !== '{') reject(new MoneroError(keyImagesStr)); // json expected, else error + let keyImages = []; + for (let keyImageJson of JSON.parse(GenUtils.stringifyBigInts(keyImagesStr)).keyImages) keyImages.push(new MoneroKeyImage(keyImageJson)); + resolve(keyImages); + }); + }); + }); + } + + async importKeyImages(keyImages: MoneroKeyImage[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().importKeyImages(keyImages); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.import_key_images(this.cppAddress, JSON.stringify({keyImages: keyImages.map(keyImage => keyImage.toJson())}), (keyImageImportResultStr) => { + resolve(new MoneroKeyImageImportResult(JSON.parse(GenUtils.stringifyBigInts(keyImageImportResultStr)))); + }); + }); + }); + } + + async getNewKeyImagesFromLastImport(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getNewKeyImagesFromLastImport(); + throw new MoneroError("Not implemented"); + } + + async freezeOutput(keyImage: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().freezeOutput(keyImage); + if (!keyImage) throw new MoneroError("Must specify key image to freeze"); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.freeze_output(this.cppAddress, keyImage, () => resolve()); + }); + }); + } + + async thawOutput(keyImage: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().thawOutput(keyImage); + if (!keyImage) throw new MoneroError("Must specify key image to thaw"); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.thaw_output(this.cppAddress, keyImage, () => resolve()); + }); + }); + } + + async isOutputFrozen(keyImage: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().isOutputFrozen(keyImage); + if (!keyImage) throw new MoneroError("Must specify key image to check if frozen"); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.is_output_frozen(this.cppAddress, keyImage, (result) => resolve(result)); + }); + }); + } + + async createTxs(config: Partial): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().createTxs(config); + + // validate, copy, and normalize config + const configNormalized = MoneroWallet.normalizeCreateTxsConfig(config); + if (configNormalized.getCanSplit() === undefined) configNormalized.setCanSplit(true); + + // create txs in queue + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + + // create txs in wasm which invokes callback when done + this.module.create_txs(this.cppAddress, JSON.stringify(configNormalized.toJson()), (txSetJsonStr) => { + if (txSetJsonStr.charAt(0) !== '{') reject(new MoneroError(txSetJsonStr)); // json expected, else error + else resolve(new MoneroTxSet(JSON.parse(GenUtils.stringifyBigInts(txSetJsonStr))).getTxs()); + }); + }); + }); + } + + async sweepOutput(config: Partial): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().sweepOutput(config); + + // normalize and validate config + const configNormalized = MoneroWallet.normalizeSweepOutputConfig(config); + + // sweep output in queue + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + + // sweep output in wasm which invokes callback when done + this.module.sweep_output(this.cppAddress, JSON.stringify(configNormalized.toJson()), (txSetJsonStr) => { + if (txSetJsonStr.charAt(0) !== '{') reject(new MoneroError(txSetJsonStr)); // json expected, else error + else resolve(new MoneroTxSet(JSON.parse(GenUtils.stringifyBigInts(txSetJsonStr))).getTxs()[0]); + }); + }); + }); + } + + async sweepUnlocked(config: Partial): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().sweepUnlocked(config); + + // validate and normalize config + const configNormalized = MoneroWallet.normalizeSweepUnlockedConfig(config); + + // sweep unlocked in queue + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + + // sweep unlocked in wasm which invokes callback when done + this.module.sweep_unlocked(this.cppAddress, JSON.stringify(configNormalized.toJson()), (txSetsJson) => { + if (txSetsJson.charAt(0) !== '{') reject(new MoneroError(txSetsJson)); // json expected, else error + else { + let txSets = []; + for (let txSetJson of JSON.parse(GenUtils.stringifyBigInts(txSetsJson)).txSets) txSets.push(new MoneroTxSet(txSetJson)); + let txs = []; + for (let txSet of txSets) for (let tx of txSet.getTxs()) txs.push(tx); + resolve(txs); + } + }); + }); + }); + } + + async sweepDust(relay?: boolean): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().sweepDust(relay); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + + // call wasm which invokes callback when done + this.module.sweep_dust(this.cppAddress, relay, (txSetJsonStr) => { + if (txSetJsonStr.charAt(0) !== '{') reject(new MoneroError(txSetJsonStr)); // json expected, else error + else { + let txSet = new MoneroTxSet(JSON.parse(GenUtils.stringifyBigInts(txSetJsonStr))); + if (txSet.getTxs() === undefined) txSet.setTxs([]); + resolve(txSet.getTxs()); + } + }); + }); + }); + } + + async relayTxs(txsOrMetadatas: (MoneroTxWallet | string)[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().relayTxs(txsOrMetadatas); + assert(Array.isArray(txsOrMetadatas), "Must provide an array of txs or their metadata to relay"); + let txMetadatas = []; + for (let txOrMetadata of txsOrMetadatas) txMetadatas.push(txOrMetadata instanceof MoneroTxWallet ? txOrMetadata.getMetadata() : txOrMetadata); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.relay_txs(this.cppAddress, JSON.stringify({txMetadatas: txMetadatas}), (txHashesJson) => { + if (txHashesJson.charAt(0) !== "{") reject(new MoneroError(txHashesJson)); + else resolve(JSON.parse(txHashesJson).txHashes); + }); + }); + }); + } + + async describeTxSet(txSet: MoneroTxSet): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().describeTxSet(txSet); + return this.module.queueTask(async () => { + this.assertNotClosed(); + txSet = new MoneroTxSet({unsignedTxHex: txSet.getUnsignedTxHex(), signedTxHex: txSet.getSignedTxHex(), multisigTxHex: txSet.getMultisigTxHex()}); + try { return new MoneroTxSet(JSON.parse(GenUtils.stringifyBigInts(this.module.describe_tx_set(this.cppAddress, JSON.stringify(txSet.toJson()))))); } + catch (err) { throw new MoneroError(this.module.get_exception_message(err)); } + }); + } + + async signTxs(unsignedTxHex: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().signTxs(unsignedTxHex); + return this.module.queueTask(async () => { + this.assertNotClosed(); + try { return this.module.sign_txs(this.cppAddress, unsignedTxHex); } + catch (err) { throw new MoneroError(this.module.get_exception_message(err)); } + }); + } + + async submitTxs(signedTxHex: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().submitTxs(signedTxHex); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.submit_txs(this.cppAddress, signedTxHex, (resp) => { + if (resp.charAt(0) !== "{") reject(new MoneroError(resp)); + else resolve(JSON.parse(resp).txHashes); + }); + }); + }); + } + + async signMessage(message: string, signatureType = MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY, accountIdx = 0, subaddressIdx = 0): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().signMessage(message, signatureType, accountIdx, subaddressIdx); + + // assign defaults + signatureType = signatureType || MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY; + accountIdx = accountIdx || 0; + subaddressIdx = subaddressIdx || 0; + + // queue task to sign message + return this.module.queueTask(async () => { + this.assertNotClosed(); + try { return this.module.sign_message(this.cppAddress, message, signatureType === MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY ? 0 : 1, accountIdx, subaddressIdx); } + catch (err) { throw new MoneroError(this.module.get_exception_message(err)); } + }); + } + + async verifyMessage(message: string, address: string, signature: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().verifyMessage(message, address, signature); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let result; + try { + result = JSON.parse(this.module.verify_message(this.cppAddress, message, address, signature)); + } catch (err) { + result = {isGood: false}; + } + return new MoneroMessageSignatureResult(result.isGood ? + {isGood: result.isGood, isOld: result.isOld, signatureType: result.signatureType === "spend" ? MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY : MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY, version: result.version} : + {isGood: false} + ); + }); + } + + async getTxKey(txHash: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getTxKey(txHash); + return this.module.queueTask(async () => { + this.assertNotClosed(); + try { return this.module.get_tx_key(this.cppAddress, txHash); } + catch (err) { throw new MoneroError(this.module.get_exception_message(err)); } + }); + } + + async checkTxKey(txHash: string, txKey: string, address: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().checkTxKey(txHash, txKey, address); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.check_tx_key(this.cppAddress, txHash, txKey, address, (respJsonStr) => { + if (respJsonStr.charAt(0) !== "{") reject(new MoneroError(respJsonStr)); + else resolve(new MoneroCheckTx(JSON.parse(GenUtils.stringifyBigInts(respJsonStr)))); + }); + }); + }); + } + + async getTxProof(txHash: string, address: string, message?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getTxProof(txHash, address, message); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.get_tx_proof(this.cppAddress, txHash || "", address || "", message || "", (signature) => { + let errorKey = "error: "; + if (signature.indexOf(errorKey) === 0) reject(new MoneroError(signature.substring(errorKey.length))); + else resolve(signature); + }); + }); + }); + } + + async checkTxProof(txHash: string, address: string, message: string | undefined, signature: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().checkTxProof(txHash, address, message, signature); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.check_tx_proof(this.cppAddress, txHash || "", address || "", message || "", signature || "", (respJsonStr) => { + if (respJsonStr.charAt(0) !== "{") reject(new MoneroError(respJsonStr)); + else resolve(new MoneroCheckTx(JSON.parse(GenUtils.stringifyBigInts(respJsonStr)))); + }); + }); + }); + } + + async getSpendProof(txHash: string, message?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getSpendProof(txHash, message); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.get_spend_proof(this.cppAddress, txHash || "", message || "", (signature) => { + let errorKey = "error: "; + if (signature.indexOf(errorKey) === 0) reject(new MoneroError(signature.substring(errorKey.length))); + else resolve(signature); + }); + }); + }); + } + + async checkSpendProof(txHash: string, message: string | undefined, signature: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().checkSpendProof(txHash, message, signature); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.check_spend_proof(this.cppAddress, txHash || "", message || "", signature || "", (resp) => { + typeof resp === "string" ? reject(new MoneroError(resp)) : resolve(resp); + }); + }); + }); + } + + async getReserveProofWallet(message?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getReserveProofWallet(message); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.get_reserve_proof_wallet(this.cppAddress, message, (signature) => { + let errorKey = "error: "; + if (signature.indexOf(errorKey) === 0) reject(new MoneroError(signature.substring(errorKey.length), -1)); + else resolve(signature); + }); + }); + }); + } + + async getReserveProofAccount(accountIdx: number, amount: bigint, message?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getReserveProofAccount(accountIdx, amount, message); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.get_reserve_proof_account(this.cppAddress, accountIdx, amount.toString(), message, (signature) => { + let errorKey = "error: "; + if (signature.indexOf(errorKey) === 0) reject(new MoneroError(signature.substring(errorKey.length), -1)); + else resolve(signature); + }); + }); + }); + } + + async checkReserveProof(address: string, message: string | undefined, signature: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().checkReserveProof(address, message, signature); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.check_reserve_proof(this.cppAddress, address, message, signature, (respJsonStr) => { + if (respJsonStr.charAt(0) !== "{") reject(new MoneroError(respJsonStr, -1)); + else resolve(new MoneroCheckReserve(JSON.parse(GenUtils.stringifyBigInts(respJsonStr)))); + }); + }); + }); + } + + async getTxNotes(txHashes: string[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getTxNotes(txHashes); + return this.module.queueTask(async () => { + this.assertNotClosed(); + try { return JSON.parse(this.module.get_tx_notes(this.cppAddress, JSON.stringify({txHashes: txHashes}))).txNotes; } + catch (err) { throw new MoneroError(this.module.get_exception_message(err)); } + }); + } + + async setTxNotes(txHashes: string[], notes: string[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().setTxNotes(txHashes, notes); + return this.module.queueTask(async () => { + this.assertNotClosed(); + try { this.module.set_tx_notes(this.cppAddress, JSON.stringify({txHashes: txHashes, txNotes: notes})); } + catch (err) { throw new MoneroError(this.module.get_exception_message(err)); } + }); + } + + async getAddressBookEntries(entryIndices?: number[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getAddressBookEntries(entryIndices); + if (!entryIndices) entryIndices = []; + return this.module.queueTask(async () => { + this.assertNotClosed(); + let entries = []; + for (let entryJson of JSON.parse(this.module.get_address_book_entries(this.cppAddress, JSON.stringify({entryIndices: entryIndices}))).entries) { + entries.push(new MoneroAddressBookEntry(entryJson)); + } + return entries; + }); + } + + async addAddressBookEntry(address: string, description?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().addAddressBookEntry(address, description); + if (!address) address = ""; + if (!description) description = ""; + return this.module.queueTask(async () => { + this.assertNotClosed(); + return this.module.add_address_book_entry(this.cppAddress, address, description); + }); + } + + async editAddressBookEntry(index: number, setAddress: boolean, address: string | undefined, setDescription: boolean, description: string | undefined): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().editAddressBookEntry(index, setAddress, address, setDescription, description); + if (!setAddress) setAddress = false; + if (!address) address = ""; + if (!setDescription) setDescription = false; + if (!description) description = ""; + return this.module.queueTask(async () => { + this.assertNotClosed(); + this.module.edit_address_book_entry(this.cppAddress, index, setAddress, address, setDescription, description); + }); + } + + async deleteAddressBookEntry(entryIdx: number): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().deleteAddressBookEntry(entryIdx); + return this.module.queueTask(async () => { + this.assertNotClosed(); + this.module.delete_address_book_entry(this.cppAddress, entryIdx); + }); + } + + async tagAccounts(tag: string, accountIndices: number[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().tagAccounts(tag, accountIndices); + if (!tag) tag = ""; + if (!accountIndices) accountIndices = []; + return this.module.queueTask(async () => { + this.assertNotClosed(); + this.module.tag_accounts(this.cppAddress, JSON.stringify({tag: tag, accountIndices: accountIndices})); + }); + } + + async untagAccounts(accountIndices: number[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().untagAccounts(accountIndices); + if (!accountIndices) accountIndices = []; + return this.module.queueTask(async () => { + this.assertNotClosed(); + this.module.tag_accounts(this.cppAddress, JSON.stringify({accountIndices: accountIndices})); + }); + } + + async getAccountTags(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getAccountTags(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let accountTags = []; + for (let accountTagJson of JSON.parse(this.module.get_account_tags(this.cppAddress)).accountTags) accountTags.push(new MoneroAccountTag(accountTagJson)); + return accountTags; + }); + } + + async setAccountTagLabel(tag: string, label: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().setAccountTagLabel(tag, label); + if (!tag) tag = ""; + if (!label) label = ""; + return this.module.queueTask(async () => { + this.assertNotClosed(); + this.module.set_account_tag_label(this.cppAddress, tag, label); + }); + } + + async getPaymentUri(config: MoneroTxConfig): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getPaymentUri(config); + config = MoneroWallet.normalizeCreateTxsConfig(config); + return this.module.queueTask(async () => { + this.assertNotClosed(); + try { + return this.module.get_payment_uri(this.cppAddress, JSON.stringify(config.toJson())); + } catch (err) { + throw new MoneroError("Cannot make URI from supplied parameters"); + } + }); + } + + async parsePaymentUri(uri: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().parsePaymentUri(uri); + return this.module.queueTask(async () => { + this.assertNotClosed(); + try { + return new MoneroTxConfig(JSON.parse(GenUtils.stringifyBigInts(this.module.parse_payment_uri(this.cppAddress, uri)))); + } catch (err: any) { + throw new MoneroError(err.message); + } + }); + } + + async getAttribute(key: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getAttribute(key); + this.assertNotClosed(); + assert(typeof key === "string", "Attribute key must be a string"); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let value = this.module.get_attribute(this.cppAddress, key); + return value === "" ? null : value; + }); + } + + async setAttribute(key: string, val: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().setAttribute(key, val); + this.assertNotClosed(); + assert(typeof key === "string", "Attribute key must be a string"); + assert(typeof val === "string", "Attribute value must be a string"); + return this.module.queueTask(async () => { + this.assertNotClosed(); + this.module.set_attribute(this.cppAddress, key, val); + }); + } + + async startMining(numThreads: number, backgroundMining?: boolean, ignoreBattery?: boolean): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().startMining(numThreads, backgroundMining, ignoreBattery); + this.assertNotClosed(); + let daemon = await MoneroDaemonRpc.connectToDaemonRpc(await this.getDaemonConnection()); + await daemon.startMining(await this.getPrimaryAddress(), numThreads, backgroundMining, ignoreBattery); + } + + async stopMining(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().stopMining(); + this.assertNotClosed(); + let daemon = await MoneroDaemonRpc.connectToDaemonRpc(await this.getDaemonConnection()); + await daemon.stopMining(); + } + + async isMultisigImportNeeded(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().isMultisigImportNeeded(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return this.module.is_multisig_import_needed(this.cppAddress); + }); + } + + async isMultisig(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().isMultisig(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return this.module.is_multisig(this.cppAddress); + }); + } + + async getMultisigInfo(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getMultisigInfo(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new MoneroMultisigInfo(JSON.parse(this.module.get_multisig_info(this.cppAddress))); + }); + } + + async prepareMultisig(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().prepareMultisig(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return this.module.prepare_multisig(this.cppAddress); + }); + } + + async makeMultisig(multisigHexes: string[], threshold: number, password: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().makeMultisig(multisigHexes, threshold, password); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.make_multisig(this.cppAddress, JSON.stringify({multisigHexes: multisigHexes, threshold: threshold, password: password}), (resp) => { + let errorKey = "error: "; + if (resp.indexOf(errorKey) === 0) reject(new MoneroError(resp.substring(errorKey.length))); + else resolve(resp); + }); + }); + }); + } + + async exchangeMultisigKeys(multisigHexes: string[], password: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().exchangeMultisigKeys(multisigHexes, password); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.exchange_multisig_keys(this.cppAddress, JSON.stringify({multisigHexes: multisigHexes, password: password}), (resp) => { + let errorKey = "error: "; + if (resp.indexOf(errorKey) === 0) reject(new MoneroError(resp.substring(errorKey.length))); + else resolve(new MoneroMultisigInitResult(JSON.parse(resp))); + }); + }); + }); + } + + async exportMultisigHex(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().exportMultisigHex(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return this.module.export_multisig_hex(this.cppAddress); + }); + } + + async importMultisigHex(multisigHexes: string[]): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().importMultisigHex(multisigHexes); + if (!GenUtils.isArray(multisigHexes)) throw new MoneroError("Must provide string[] to importMultisigHex()") + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.import_multisig_hex(this.cppAddress, JSON.stringify({multisigHexes: multisigHexes}), (resp) => { + if (typeof resp === "string") reject(new MoneroError(resp)); + else resolve(resp); + }); + }); + }); + } + + async signMultisigTxHex(multisigTxHex: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().signMultisigTxHex(multisigTxHex); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.sign_multisig_tx_hex(this.cppAddress, multisigTxHex, (resp) => { + if (resp.charAt(0) !== "{") reject(new MoneroError(resp)); + else resolve(new MoneroMultisigSignResult(JSON.parse(resp))); + }); + }); + }); + } + + async submitMultisigTxHex(signedMultisigTxHex: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().submitMultisigTxHex(signedMultisigTxHex); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.submit_multisig_tx_hex(this.cppAddress, signedMultisigTxHex, (resp) => { + if (resp.charAt(0) !== "{") reject(new MoneroError(resp)); + else resolve(JSON.parse(resp).txHashes); + }); + }); + }); + } + + /** + * Get the wallet's keys and cache data. + * + * @return {Promise} is the keys and cache data, respectively + */ + async getData(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getData(); + + // queue call to wasm module + let viewOnly = await this.isViewOnly(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + + // store views in array + let views = []; + + // malloc cache buffer and get buffer location in c++ heap + let cacheBufferLoc = JSON.parse(this.module.get_cache_file_buffer(this.cppAddress)); + + // read binary data from heap to DataView + let view = new DataView(new ArrayBuffer(cacheBufferLoc.length)); + for (let i = 0; i < cacheBufferLoc.length; i++) { + view.setInt8(i, this.module.HEAPU8[cacheBufferLoc.pointer / Uint8Array.BYTES_PER_ELEMENT + i]); + } + + // free binary on heap + this.module._free(cacheBufferLoc.pointer); + + // write cache file + views.push(Buffer.from(view.buffer)); + + // malloc keys buffer and get buffer location in c++ heap + let keysBufferLoc = JSON.parse(this.module.get_keys_file_buffer(this.cppAddress, this.password, viewOnly)); + + // read binary data from heap to DataView + view = new DataView(new ArrayBuffer(keysBufferLoc.length)); + for (let i = 0; i < keysBufferLoc.length; i++) { + view.setInt8(i, this.module.HEAPU8[keysBufferLoc.pointer / Uint8Array.BYTES_PER_ELEMENT + i]); + } + + // free binary on heap + this.module._free(keysBufferLoc.pointer); + + // prepend keys file + views.unshift(Buffer.from(view.buffer)); + return views; + }); + } + + async changePassword(oldPassword: string, newPassword: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().changePassword(oldPassword, newPassword); + if (oldPassword !== this.password) throw new MoneroError("Invalid original password."); // wallet2 verify_password loads from disk so verify password here + if (newPassword === undefined) newPassword = ""; + await this.module.queueTask(async () => { + this.assertNotClosed(); + return new Promise((resolve, reject) => { + this.module.change_wallet_password(this.cppAddress, oldPassword, newPassword, (errMsg) => { + if (errMsg) reject(new MoneroError(errMsg)); + else resolve(); + }); + }); + }); + this.password = newPassword; + if (this.path) await this.save(); // auto save + } + + async save(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().save(); + return MoneroWalletFull.save(this); + } + + async close(save = false): Promise { + if (this._isClosed) return; // no effect if closed + if (this.getWalletProxy()) { + await this.getWalletProxy().close(save); + this._isClosed = true; + return; + } + await this.refreshListening(); + await this.stopSyncing(); + await super.close(save); + delete this.path; + delete this.password; + delete this.listeners; + delete this.fullListener; + LibraryUtils.setRejectUnauthorizedFn(this.rejectUnauthorizedConfigId, undefined); // unregister fn informing if unauthorized reqs should be rejected + } + + // ----------- ADD JSDOC FOR SUPPORTED DEFAULT IMPLEMENTATIONS -------------- + + async getNumBlocksToUnlock(): Promise { return super.getNumBlocksToUnlock(); } + async getTx(txHash: string): Promise { return super.getTx(txHash); } + async getIncomingTransfers(query: Partial): Promise { return super.getIncomingTransfers(query); } + async getOutgoingTransfers(query: Partial) { return super.getOutgoingTransfers(query); } + async createTx(config: Partial): Promise { return super.createTx(config); } + async relayTx(txOrMetadata: MoneroTxWallet | string): Promise { return super.relayTx(txOrMetadata); } + async getTxNote(txHash: string): Promise { return super.getTxNote(txHash); } + async setTxNote(txHash: string, note: string): Promise { return super.setTxNote(txHash, note); } + + // ---------------------------- PRIVATE HELPERS ---------------------------- + + protected static async openWalletData(config: Partial) { + if (config.proxyToWorker) return MoneroWalletFullProxy.openWalletData(config); + + // validate and normalize parameters + if (config.networkType === undefined) throw new MoneroError("Must provide the wallet's network type"); + config.networkType = MoneroNetworkType.from(config.networkType); + let daemonConnection = config.getServer(); + let daemonUri = daemonConnection && daemonConnection.getUri() ? daemonConnection.getUri() : ""; + let daemonUsername = daemonConnection && daemonConnection.getUsername() ? daemonConnection.getUsername() : ""; + let daemonPassword = daemonConnection && daemonConnection.getPassword() ? daemonConnection.getPassword() : ""; + let rejectUnauthorized = daemonConnection ? daemonConnection.getRejectUnauthorized() : true; + + // load wasm module + let module = await LibraryUtils.loadFullModule(); + + // open wallet in queue + return module.queueTask(async () => { + return new Promise((resolve, reject) => { + + // register fn informing if unauthorized reqs should be rejected + let rejectUnauthorizedFnId = GenUtils.getUUID(); + LibraryUtils.setRejectUnauthorizedFn(rejectUnauthorizedFnId, () => rejectUnauthorized); + + // create wallet in wasm which invokes callback when done + module.open_wallet_full(config.password, config.networkType, config.keysData, config.cacheData, daemonUri, daemonUsername, daemonPassword, rejectUnauthorizedFnId, (cppAddress) => { + if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); + else resolve(new MoneroWalletFull(cppAddress, config.path, config.password, fs, rejectUnauthorized, rejectUnauthorizedFnId)); + }); + }); + }); + } + + protected getWalletProxy(): MoneroWalletFullProxy { + return super.getWalletProxy() as MoneroWalletFullProxy; + } + + protected async backgroundSync() { + let label = this.path ? this.path : (this.browserMainPath ? this.browserMainPath : "in-memory wallet"); // label for log + LibraryUtils.log(1, "Background synchronizing " + label); + try { await this.sync(); } + catch (err: any) { if (!this._isClosed) console.error("Failed to background synchronize " + label + ": " + err.message); } + } + + protected async refreshListening() { + let isEnabled = this.listeners.length > 0; + if (this.fullListenerHandle === 0 && !isEnabled || this.fullListenerHandle > 0 && isEnabled) return; // no difference + return this.module.queueTask(async () => { + return new Promise((resolve, reject) => { + this.module.set_listener( + this.cppAddress, + this.fullListenerHandle, + newListenerHandle => { + if (typeof newListenerHandle === "string") reject(new MoneroError(newListenerHandle)); + else { + this.fullListenerHandle = newListenerHandle; + resolve(); + } + }, + isEnabled ? async (height, startHeight, endHeight, percentDone, message) => await this.fullListener.onSyncProgress(height, startHeight, endHeight, percentDone, message) : undefined, + isEnabled ? async (height) => await this.fullListener.onNewBlock(height) : undefined, + isEnabled ? async (newBalanceStr, newUnlockedBalanceStr) => await this.fullListener.onBalancesChanged(newBalanceStr, newUnlockedBalanceStr) : undefined, + isEnabled ? async (height, txHash, amountStr, accountIdx, subaddressIdx, version, unlockTime, isLocked) => await this.fullListener.onOutputReceived(height, txHash, amountStr, accountIdx, subaddressIdx, version, unlockTime, isLocked) : undefined, + isEnabled ? async (height, txHash, amountStr, accountIdxStr, subaddressIdxStr, version, unlockTime, isLocked) => await this.fullListener.onOutputSpent(height, txHash, amountStr, accountIdxStr, subaddressIdxStr, version, unlockTime, isLocked) : undefined, + ); + }); + }); + } + + static sanitizeBlock(block) { + for (let tx of block.getTxs()) MoneroWalletFull.sanitizeTxWallet(tx); + return block; + } + + static sanitizeTxWallet(tx) { + assert(tx instanceof MoneroTxWallet); + return tx; + } + + static sanitizeAccount(account) { + if (account.getSubaddresses()) { + for (let subaddress of account.getSubaddresses()) MoneroWalletKeys.sanitizeSubaddress(subaddress); + } + return account; + } + + static deserializeBlocks(blocksJsonStr) { + let blocksJson = JSON.parse(GenUtils.stringifyBigInts(blocksJsonStr)); + let deserializedBlocks: any = {}; + deserializedBlocks.blocks = []; + if (blocksJson.blocks) for (let blockJson of blocksJson.blocks) deserializedBlocks.blocks.push(MoneroWalletFull.sanitizeBlock(new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX_WALLET))); + return deserializedBlocks; + } + + static deserializeTxs(query, blocksJsonStr) { + + // deserialize blocks + let deserializedBlocks = MoneroWalletFull.deserializeBlocks(blocksJsonStr); + let blocks = deserializedBlocks.blocks; + + // collect txs + let txs = []; + for (let block of blocks) { + MoneroWalletFull.sanitizeBlock(block); + for (let tx of block.getTxs()) { + if (block.getHeight() === undefined) tx.setBlock(undefined); // dereference placeholder block for unconfirmed txs + txs.push(tx); + } + } + + // re-sort txs which is lost over wasm serialization // TODO: confirm that order is lost + if (query.getHashes() !== undefined) { + let txMap = new Map(); + for (let tx of txs) txMap[tx.getHash()] = tx; + let txsSorted = []; + for (let txHash of query.getHashes()) if (txMap[txHash] !== undefined) txsSorted.push(txMap[txHash]); + txs = txsSorted; + } + + return txs; + } + + static deserializeTransfers(query, blocksJsonStr) { + + // deserialize blocks + let deserializedBlocks = MoneroWalletFull.deserializeBlocks(blocksJsonStr); + let blocks = deserializedBlocks.blocks; + + // collect transfers + let transfers = []; + for (let block of blocks) { + for (let tx of block.getTxs()) { + if (block.getHeight() === undefined) tx.setBlock(undefined); // dereference placeholder block for unconfirmed txs + if (tx.getOutgoingTransfer() !== undefined) transfers.push(tx.getOutgoingTransfer()); + if (tx.getIncomingTransfers() !== undefined) { + for (let transfer of tx.getIncomingTransfers()) transfers.push(transfer); + } + } + } + + return transfers; + } + + static deserializeOutputs(query, blocksJsonStr) { + + // deserialize blocks + let deserializedBlocks = MoneroWalletFull.deserializeBlocks(blocksJsonStr); + let blocks = deserializedBlocks.blocks; + + // collect outputs + let outputs = []; + for (let block of blocks) { + for (let tx of block.getTxs()) { + for (let output of tx.getOutputs()) outputs.push(output); + } + } + + return outputs; + } + + /** + * Set the path of the wallet on the browser main thread if run as a worker. + * + * @param {string} browserMainPath - path of the wallet on the browser main thread + */ + protected setBrowserMainPath(browserMainPath) { + this.browserMainPath = browserMainPath; + } + + static async moveTo(path, wallet) { + if (await wallet.isClosed()) throw new MoneroError("Wallet is closed"); + if (!path) throw new MoneroError("Must provide path of destination wallet"); + + // save and return if same path + if (Path.normalize(wallet.path) === Path.normalize(path)) { + await wallet.save(); + return; + } + + // create destination directory if it doesn't exist + let walletDir = Path.dirname(path); + if (!wallet.fs.existsSync(walletDir)) { + try { wallet.fs.mkdirSync(walletDir); } + catch (err: any) { throw new MoneroError("Destination path " + path + " does not exist and cannot be created: " + err.message); } + } + + // write wallet files + let data = await wallet.getData(); + wallet.fs.writeFileSync(path + ".keys", data[0], "binary"); + wallet.fs.writeFileSync(path, data[1], "binary"); + wallet.fs.writeFileSync(path + ".address.txt", await wallet.getPrimaryAddress()); + let oldPath = wallet.path; + wallet.path = path; + + // delete old wallet files + if (oldPath) { + wallet.fs.unlinkSync(oldPath + ".address.txt"); + wallet.fs.unlinkSync(oldPath + ".keys"); + wallet.fs.unlinkSync(oldPath); + } + } + + static async save(wallet: any) { + if (await wallet.isClosed()) throw new MoneroError("Wallet is closed"); + + // path must be set + let path = await wallet.getPath(); + if (!path) throw new MoneroError("Cannot save wallet because path is not set"); + + // write wallet files to *.new + let pathNew = path + ".new"; + let data = await wallet.getData(); + wallet.fs.writeFileSync(pathNew + ".keys", data[0], "binary"); + wallet.fs.writeFileSync(pathNew, data[1], "binary"); + wallet.fs.writeFileSync(pathNew + ".address.txt", await wallet.getPrimaryAddress()); + + // replace old wallet files with new + wallet.fs.renameSync(pathNew + ".keys", path + ".keys"); + wallet.fs.renameSync(pathNew, path, path + ".keys"); + wallet.fs.renameSync(pathNew + ".address.txt", path + ".address.txt", path + ".keys"); + } +} + +/** + * Implements a MoneroWallet by proxying requests to a worker which runs a full wallet. + * + * @private + */ +class MoneroWalletFullProxy extends MoneroWalletKeysProxy { + + // instance variables + protected path: any; + protected fs: any; + protected wrappedListeners: any; + + // -------------------------- WALLET STATIC UTILS --------------------------- + + static async openWalletData(config: Partial) { + let walletId = GenUtils.getUUID(); + if (config.password === undefined) config.password = ""; + let daemonConnection = config.getServer(); + await LibraryUtils.invokeWorker(walletId, "openWalletData", [config.path, config.password, config.networkType, config.keysData, config.cacheData, daemonConnection ? daemonConnection.toJson() : undefined]); + let wallet = new MoneroWalletFullProxy(walletId, await LibraryUtils.getWorker(), config.path, config.getFs()); + if (config.path) await wallet.save(); + return wallet; + } + + static async createWallet(config) { + if (config.getPath() && MoneroWalletFull.walletExists(config.getPath(), config.getFs())) throw new MoneroError("Wallet already exists: " + config.getPath()); + let walletId = GenUtils.getUUID(); + await LibraryUtils.invokeWorker(walletId, "createWalletFull", [config.toJson()]); + let wallet = new MoneroWalletFullProxy(walletId, await LibraryUtils.getWorker(), config.getPath(), config.getFs()); + if (config.getPath()) await wallet.save(); + return wallet; + } + + // --------------------------- INSTANCE METHODS ---------------------------- + + /** + * Internal constructor which is given a worker to communicate with via messages. + * + * This method should not be called externally but should be called through + * static wallet creation utilities in this class. + * + * @param {string} walletId - identifies the wallet with the worker + * @param {Worker} worker - worker to communicate with via messages + */ + constructor(walletId, worker, path, fs) { + super(walletId, worker); + this.path = path; + this.fs = fs ? fs : (path ? MoneroWalletFull.getFs() : undefined); + this.wrappedListeners = []; + } + + getPath() { + return this.path; + } + + async getNetworkType() { + return this.invokeWorker("getNetworkType"); + } + + async setSubaddressLabel(accountIdx, subaddressIdx, label) { + return this.invokeWorker("setSubaddressLabel", Array.from(arguments)) as Promise; + } + + async setDaemonConnection(uriOrRpcConnection) { + if (!uriOrRpcConnection) await this.invokeWorker("setDaemonConnection"); + else { + let connection = !uriOrRpcConnection ? undefined : uriOrRpcConnection instanceof MoneroRpcConnection ? uriOrRpcConnection : new MoneroRpcConnection(uriOrRpcConnection); + await this.invokeWorker("setDaemonConnection", connection ? connection.getConfig() : undefined); + } + } + + async getDaemonConnection() { + let rpcConfig = await this.invokeWorker("getDaemonConnection"); + return rpcConfig ? new MoneroRpcConnection(rpcConfig) : undefined; + } + + async isConnectedToDaemon() { + return this.invokeWorker("isConnectedToDaemon"); + } + + async getRestoreHeight() { + return this.invokeWorker("getRestoreHeight"); + } + + async setRestoreHeight(restoreHeight) { + return this.invokeWorker("setRestoreHeight", [restoreHeight]); + } + + async getDaemonHeight() { + return this.invokeWorker("getDaemonHeight"); + } + + async getDaemonMaxPeerHeight() { + return this.invokeWorker("getDaemonMaxPeerHeight"); + } + + async getHeightByDate(year, month, day) { + return this.invokeWorker("getHeightByDate", [year, month, day]); + } + + async isDaemonSynced() { + return this.invokeWorker("isDaemonSynced"); + } + + async getHeight() { + return this.invokeWorker("getHeight"); + } + + async addListener(listener) { + let wrappedListener = new WalletWorkerListener(listener); + let listenerId = wrappedListener.getId(); + LibraryUtils.addWorkerCallback(this.walletId, "onSyncProgress_" + listenerId, [wrappedListener.onSyncProgress, wrappedListener]); + LibraryUtils.addWorkerCallback(this.walletId, "onNewBlock_" + listenerId, [wrappedListener.onNewBlock, wrappedListener]); + LibraryUtils.addWorkerCallback(this.walletId, "onBalancesChanged_" + listenerId, [wrappedListener.onBalancesChanged, wrappedListener]); + LibraryUtils.addWorkerCallback(this.walletId, "onOutputReceived_" + listenerId, [wrappedListener.onOutputReceived, wrappedListener]); + LibraryUtils.addWorkerCallback(this.walletId, "onOutputSpent_" + listenerId, [wrappedListener.onOutputSpent, wrappedListener]); + this.wrappedListeners.push(wrappedListener); + return this.invokeWorker("addListener", [listenerId]); + } + + async removeListener(listener) { + for (let i = 0; i < this.wrappedListeners.length; i++) { + if (this.wrappedListeners[i].getListener() === listener) { + let listenerId = this.wrappedListeners[i].getId(); + await this.invokeWorker("removeListener", [listenerId]); + LibraryUtils.removeWorkerCallback(this.walletId, "onSyncProgress_" + listenerId); + LibraryUtils.removeWorkerCallback(this.walletId, "onNewBlock_" + listenerId); + LibraryUtils.removeWorkerCallback(this.walletId, "onBalancesChanged_" + listenerId); + LibraryUtils.removeWorkerCallback(this.walletId, "onOutputReceived_" + listenerId); + LibraryUtils.removeWorkerCallback(this.walletId, "onOutputSpent_" + listenerId); + this.wrappedListeners.splice(i, 1); + return; + } + } + throw new MoneroError("Listener is not registered with wallet"); + } + + getListeners() { + let listeners = []; + for (let wrappedListener of this.wrappedListeners) listeners.push(wrappedListener.getListener()); + return listeners; + } + + async isSynced() { + return this.invokeWorker("isSynced"); + } + + async sync(listenerOrStartHeight?: MoneroWalletListener | number, startHeight?: number, allowConcurrentCalls = false): Promise { + + // normalize params + startHeight = listenerOrStartHeight instanceof MoneroWalletListener ? startHeight : listenerOrStartHeight; + let listener = listenerOrStartHeight instanceof MoneroWalletListener ? listenerOrStartHeight : undefined; + if (startHeight === undefined) startHeight = Math.max(await this.getHeight(), await this.getRestoreHeight()); + + // register listener if given + if (listener) await this.addListener(listener); + + // sync wallet in worker + let err; + let result; + try { + let resultJson = await this.invokeWorker("sync", [startHeight, allowConcurrentCalls]); + result = new MoneroSyncResult(resultJson.numBlocksFetched, resultJson.receivedMoney); + } catch (e) { + err = e; + } + + // unregister listener + if (listener) await this.removeListener(listener); + + // throw error or return + if (err) throw err; + return result; + } + + async startSyncing(syncPeriodInMs) { + return this.invokeWorker("startSyncing", Array.from(arguments)); + } + + async stopSyncing() { + return this.invokeWorker("stopSyncing"); + } + + async scanTxs(txHashes) { + assert(Array.isArray(txHashes), "Must provide an array of txs hashes to scan"); + return this.invokeWorker("scanTxs", [txHashes]); + } + + async rescanSpent() { + return this.invokeWorker("rescanSpent"); + } + + async rescanBlockchain() { + return this.invokeWorker("rescanBlockchain"); + } + + async getBalance(accountIdx, subaddressIdx) { + return BigInt(await this.invokeWorker("getBalance", Array.from(arguments))); + } + + async getUnlockedBalance(accountIdx, subaddressIdx) { + let unlockedBalanceStr = await this.invokeWorker("getUnlockedBalance", Array.from(arguments)); + return BigInt(unlockedBalanceStr); + } + + async getAccounts(includeSubaddresses, tag) { + let accounts = []; + for (let accountJson of (await this.invokeWorker("getAccounts", Array.from(arguments)))) { + accounts.push(MoneroWalletFull.sanitizeAccount(new MoneroAccount(accountJson))); + } + return accounts; + } + + async getAccount(accountIdx, includeSubaddresses) { + let accountJson = await this.invokeWorker("getAccount", Array.from(arguments)); + return MoneroWalletFull.sanitizeAccount(new MoneroAccount(accountJson)); + } + + async createAccount(label) { + let accountJson = await this.invokeWorker("createAccount", Array.from(arguments)); + return MoneroWalletFull.sanitizeAccount(new MoneroAccount(accountJson)); + } + + async getSubaddresses(accountIdx, subaddressIndices) { + let subaddresses = []; + for (let subaddressJson of (await this.invokeWorker("getSubaddresses", Array.from(arguments)))) { + subaddresses.push(MoneroWalletKeys.sanitizeSubaddress(new MoneroSubaddress(subaddressJson))); + } + return subaddresses; + } + + async createSubaddress(accountIdx, label) { + let subaddressJson = await this.invokeWorker("createSubaddress", Array.from(arguments)); + return MoneroWalletKeys.sanitizeSubaddress(new MoneroSubaddress(subaddressJson)); + } + + async getTxs(query) { + query = MoneroWallet.normalizeTxQuery(query); + let respJson = await this.invokeWorker("getTxs", [query.getBlock().toJson()]); + return MoneroWalletFull.deserializeTxs(query, JSON.stringify({blocks: respJson.blocks})); // initialize txs from blocks json string TODO: this stringifies then utility parses, avoid + } + + async getTransfers(query) { + query = MoneroWallet.normalizeTransferQuery(query); + let blockJsons = await this.invokeWorker("getTransfers", [query.getTxQuery().getBlock().toJson()]); + return MoneroWalletFull.deserializeTransfers(query, JSON.stringify({blocks: blockJsons})); // initialize transfers from blocks json string TODO: this stringifies then utility parses, avoid + } + + async getOutputs(query) { + query = MoneroWallet.normalizeOutputQuery(query); + let blockJsons = await this.invokeWorker("getOutputs", [query.getTxQuery().getBlock().toJson()]); + return MoneroWalletFull.deserializeOutputs(query, JSON.stringify({blocks: blockJsons})); // initialize transfers from blocks json string TODO: this stringifies then utility parses, avoid + } + + async exportOutputs(all) { + return this.invokeWorker("exportOutputs", [all]); + } + + async importOutputs(outputsHex) { + return this.invokeWorker("importOutputs", [outputsHex]); + } + + async exportKeyImages(all) { + let keyImages = []; + for (let keyImageJson of await this.invokeWorker("getKeyImages", [all])) keyImages.push(new MoneroKeyImage(keyImageJson)); + return keyImages; + } + + async importKeyImages(keyImages) { + let keyImagesJson = []; + for (let keyImage of keyImages) keyImagesJson.push(keyImage.toJson()); + return new MoneroKeyImageImportResult(await this.invokeWorker("importKeyImages", [keyImagesJson])); + } + + async getNewKeyImagesFromLastImport(): Promise { + throw new MoneroError("MoneroWalletFull.getNewKeyImagesFromLastImport() not implemented"); + } + + async freezeOutput(keyImage) { + return this.invokeWorker("freezeOutput", [keyImage]); + } + + async thawOutput(keyImage) { + return this.invokeWorker("thawOutput", [keyImage]); + } + + async isOutputFrozen(keyImage) { + return this.invokeWorker("isOutputFrozen", [keyImage]); + } + + async createTxs(config) { + config = MoneroWallet.normalizeCreateTxsConfig(config); + let txSetJson = await this.invokeWorker("createTxs", [config.toJson()]); + return new MoneroTxSet(txSetJson).getTxs(); + } + + async sweepOutput(config) { + config = MoneroWallet.normalizeSweepOutputConfig(config); + let txSetJson = await this.invokeWorker("sweepOutput", [config.toJson()]); + return new MoneroTxSet(txSetJson).getTxs()[0]; + } + + async sweepUnlocked(config) { + config = MoneroWallet.normalizeSweepUnlockedConfig(config); + let txSetsJson = await this.invokeWorker("sweepUnlocked", [config.toJson()]); + let txs = []; + for (let txSetJson of txSetsJson) for (let tx of new MoneroTxSet(txSetJson).getTxs()) txs.push(tx); + return txs; + } + + async sweepDust(relay) { + return new MoneroTxSet(await this.invokeWorker("sweepDust", [relay])).getTxs() || []; + } + + async relayTxs(txsOrMetadatas) { + assert(Array.isArray(txsOrMetadatas), "Must provide an array of txs or their metadata to relay"); + let txMetadatas = []; + for (let txOrMetadata of txsOrMetadatas) txMetadatas.push(txOrMetadata instanceof MoneroTxWallet ? txOrMetadata.getMetadata() : txOrMetadata); + return this.invokeWorker("relayTxs", [txMetadatas]); + } + + async describeTxSet(txSet) { + return new MoneroTxSet(await this.invokeWorker("describeTxSet", [txSet.toJson()])); + } + + async signTxs(unsignedTxHex) { + return this.invokeWorker("signTxs", Array.from(arguments)); + } + + async submitTxs(signedTxHex) { + return this.invokeWorker("submitTxs", Array.from(arguments)); + } + + async signMessage(message, signatureType, accountIdx, subaddressIdx) { + return this.invokeWorker("signMessage", Array.from(arguments)); + } + + async verifyMessage(message, address, signature) { + return new MoneroMessageSignatureResult(await this.invokeWorker("verifyMessage", Array.from(arguments))); + } + + async getTxKey(txHash) { + return this.invokeWorker("getTxKey", Array.from(arguments)); + } + + async checkTxKey(txHash, txKey, address) { + return new MoneroCheckTx(await this.invokeWorker("checkTxKey", Array.from(arguments))); + } + + async getTxProof(txHash, address, message) { + return this.invokeWorker("getTxProof", Array.from(arguments)); + } + + async checkTxProof(txHash, address, message, signature) { + return new MoneroCheckTx(await this.invokeWorker("checkTxProof", Array.from(arguments))); + } + + async getSpendProof(txHash, message) { + return this.invokeWorker("getSpendProof", Array.from(arguments)); + } + + async checkSpendProof(txHash, message, signature) { + return this.invokeWorker("checkSpendProof", Array.from(arguments)); + } + + async getReserveProofWallet(message) { + return this.invokeWorker("getReserveProofWallet", Array.from(arguments)); + } + + async getReserveProofAccount(accountIdx, amount, message) { + try { return await this.invokeWorker("getReserveProofAccount", [accountIdx, amount.toString(), message]); } + catch (e: any) { throw new MoneroError(e.message, -1); } + } + + async checkReserveProof(address, message, signature) { + try { return new MoneroCheckReserve(await this.invokeWorker("checkReserveProof", Array.from(arguments))); } + catch (e: any) { throw new MoneroError(e.message, -1); } + } + + async getTxNotes(txHashes) { + return this.invokeWorker("getTxNotes", Array.from(arguments)); + } + + async setTxNotes(txHashes, notes) { + return this.invokeWorker("setTxNotes", Array.from(arguments)); + } + + async getAddressBookEntries(entryIndices) { + if (!entryIndices) entryIndices = []; + let entries = []; + for (let entryJson of await this.invokeWorker("getAddressBookEntries", Array.from(arguments))) { + entries.push(new MoneroAddressBookEntry(entryJson)); + } + return entries; + } + + async addAddressBookEntry(address, description) { + return this.invokeWorker("addAddressBookEntry", Array.from(arguments)); + } + + async editAddressBookEntry(index, setAddress, address, setDescription, description) { + return this.invokeWorker("editAddressBookEntry", Array.from(arguments)); + } + + async deleteAddressBookEntry(entryIdx) { + return this.invokeWorker("deleteAddressBookEntry", Array.from(arguments)); + } + + async tagAccounts(tag, accountIndices) { + return this.invokeWorker("tagAccounts", Array.from(arguments)); + } + + async untagAccounts(accountIndices) { + return this.invokeWorker("untagAccounts", Array.from(arguments)); + } + + async getAccountTags() { + return this.invokeWorker("getAccountTags", Array.from(arguments)); + } + + async setAccountTagLabel(tag, label) { + return this.invokeWorker("setAccountTagLabel", Array.from(arguments)); + } + + async getPaymentUri(config) { + config = MoneroWallet.normalizeCreateTxsConfig(config); + return this.invokeWorker("getPaymentUri", [config.toJson()]); + } + + async parsePaymentUri(uri) { + return new MoneroTxConfig(await this.invokeWorker("parsePaymentUri", Array.from(arguments))); + } + + async getAttribute(key) { + return this.invokeWorker("getAttribute", Array.from(arguments)); + } + + async setAttribute(key, val) { + return this.invokeWorker("setAttribute", Array.from(arguments)); + } + + async startMining(numThreads, backgroundMining, ignoreBattery) { + return this.invokeWorker("startMining", Array.from(arguments)); + } + + async stopMining() { + return this.invokeWorker("stopMining", Array.from(arguments)); + } + + async isMultisigImportNeeded() { + return this.invokeWorker("isMultisigImportNeeded"); + } + + async isMultisig() { + return this.invokeWorker("isMultisig"); + } + + async getMultisigInfo() { + return new MoneroMultisigInfo(await this.invokeWorker("getMultisigInfo")); + } + + async prepareMultisig() { + return this.invokeWorker("prepareMultisig"); + } + + async makeMultisig(multisigHexes, threshold, password) { + return await this.invokeWorker("makeMultisig", Array.from(arguments)); + } + + async exchangeMultisigKeys(multisigHexes, password) { + return new MoneroMultisigInitResult(await this.invokeWorker("exchangeMultisigKeys", Array.from(arguments))); + } + + async exportMultisigHex() { + return this.invokeWorker("exportMultisigHex"); + } + + async importMultisigHex(multisigHexes) { + return this.invokeWorker("importMultisigHex", Array.from(arguments)); + } + + async signMultisigTxHex(multisigTxHex) { + return new MoneroMultisigSignResult(await this.invokeWorker("signMultisigTxHex", Array.from(arguments))); + } + + async submitMultisigTxHex(signedMultisigTxHex) { + return this.invokeWorker("submitMultisigTxHex", Array.from(arguments)); + } + + async getData() { + return this.invokeWorker("getData"); + } + + async moveTo(path) { + return MoneroWalletFull.moveTo(path, this); + } + + async changePassword(oldPassword, newPassword) { + await this.invokeWorker("changePassword", Array.from(arguments)); + if (this.path) await this.save(); // auto save + } + + async save() { + return MoneroWalletFull.save(this); + } + + async close(save) { + if (save) await this.save(); + while (this.wrappedListeners.length) await this.removeListener(this.wrappedListeners[0].getListener()); + await super.close(false); + } +} + +// -------------------------------- LISTENING --------------------------------- + +/** + * Receives notifications directly from wasm c++. + * + * @private + */ +class WalletFullListener { + + protected wallet: any; + + constructor(wallet) { + this.wallet = wallet; + } + + async onSyncProgress(height, startHeight, endHeight, percentDone, message) { + for (let listener of this.wallet.getListeners()) await listener.onSyncProgress(height, startHeight, endHeight, percentDone, message); + } + + async onNewBlock(height) { + for (let listener of this.wallet.getListeners()) await listener.onNewBlock(height); + } + + async onBalancesChanged(newBalanceStr, newUnlockedBalanceStr) { + for (let listener of this.wallet.getListeners()) await listener.onBalancesChanged(BigInt(newBalanceStr), BigInt(newUnlockedBalanceStr)); + } + + async onOutputReceived(height, txHash, amountStr, accountIdx, subaddressIdx, version, unlockTime, isLocked) { + + // build received output + let output = new MoneroOutputWallet(); + output.setAmount(BigInt(amountStr)); + output.setAccountIndex(accountIdx); + output.setSubaddressIndex(subaddressIdx); + let tx = new MoneroTxWallet(); + tx.setHash(txHash); + tx.setVersion(version); + tx.setUnlockTime(unlockTime); + output.setTx(tx); + tx.setOutputs([output]); + tx.setIsIncoming(true); + tx.setIsLocked(isLocked); + if (height > 0) { + let block = new MoneroBlock().setHeight(height); + block.setTxs([tx as MoneroTx]); + tx.setBlock(block); + tx.setIsConfirmed(true); + tx.setInTxPool(false); + tx.setIsFailed(false); + } else { + tx.setIsConfirmed(false); + tx.setInTxPool(true); + } + + // announce output + for (let listener of this.wallet.getListeners()) await listener.onOutputReceived(tx.getOutputs()[0]); + } + + async onOutputSpent(height, txHash, amountStr, accountIdxStr, subaddressIdxStr, version, unlockTime, isLocked) { + + // build spent output + let output = new MoneroOutputWallet(); + output.setAmount(BigInt(amountStr)); + if (accountIdxStr) output.setAccountIndex(parseInt(accountIdxStr)); + if (subaddressIdxStr) output.setSubaddressIndex(parseInt(subaddressIdxStr)); + let tx = new MoneroTxWallet(); + tx.setHash(txHash); + tx.setVersion(version); + tx.setUnlockTime(unlockTime); + tx.setIsLocked(isLocked); + output.setTx(tx); + tx.setInputs([output]); + if (height > 0) { + let block = new MoneroBlock().setHeight(height); + block.setTxs([tx]); + tx.setBlock(block); + tx.setIsConfirmed(true); + tx.setInTxPool(false); + tx.setIsFailed(false); + } else { + tx.setIsConfirmed(false); + tx.setInTxPool(true); + } + + // notify wallet listeners + for (let listener of this.wallet.getListeners()) await listener.onOutputSpent(tx.getInputs()[0]); + } +} + +/** + * Internal listener to bridge notifications to external listeners. + * + * @private + */ +class WalletWorkerListener { + + protected id: any; + protected listener: any; + + constructor(listener) { + this.id = GenUtils.getUUID(); + this.listener = listener; + } + + getId() { + return this.id; + } + + getListener() { + return this.listener; + } + + onSyncProgress(height, startHeight, endHeight, percentDone, message) { + this.listener.onSyncProgress(height, startHeight, endHeight, percentDone, message); + } + + async onNewBlock(height) { + await this.listener.onNewBlock(height); + } + + async onBalancesChanged(newBalanceStr, newUnlockedBalanceStr) { + await this.listener.onBalancesChanged(BigInt(newBalanceStr), BigInt(newUnlockedBalanceStr)); + } + + async onOutputReceived(blockJson) { + let block = new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX_WALLET); + await this.listener.onOutputReceived(block.getTxs()[0].getOutputs()[0]); + } + + async onOutputSpent(blockJson) { + let block = new MoneroBlock(blockJson, MoneroBlock.DeserializationType.TX_WALLET); + await this.listener.onOutputSpent(block.getTxs()[0].getInputs()[0]); + } +} diff --git a/src/main/ts/wallet/MoneroWalletKeys.ts b/src/main/ts/wallet/MoneroWalletKeys.ts new file mode 100644 index 000000000..0827bb29c --- /dev/null +++ b/src/main/ts/wallet/MoneroWalletKeys.ts @@ -0,0 +1,478 @@ +import assert from "assert"; +import GenUtils from "../common/GenUtils"; +import LibraryUtils from "../common/LibraryUtils"; +import MoneroAccount from "./model/MoneroAccount"; +import MoneroError from "../common/MoneroError"; +import MoneroIntegratedAddress from "./model/MoneroIntegratedAddress"; +import MoneroNetworkType from "../daemon/model/MoneroNetworkType"; +import MoneroSubaddress from "./model/MoneroSubaddress"; +import MoneroVersion from "../daemon/model/MoneroVersion"; +import MoneroWallet from "./MoneroWallet"; +import MoneroWalletConfig from "./model/MoneroWalletConfig"; +import MoneroWalletListener from "./model/MoneroWalletListener"; + +/** + * Implements a MoneroWallet which only manages keys using WebAssembly. + */ +export class MoneroWalletKeys extends MoneroWallet { + + // instance variables + protected cppAddress: string; + protected module: any; + protected walletProxy: MoneroWalletKeysProxy; + protected _isClosed: boolean; + + // --------------------------- STATIC UTILITIES ----------------------------- + + /** + *

Create a wallet using WebAssembly bindings to monero-project.

+ * + *

Example:

+ * + * + * let wallet = await MoneroWalletKeys.createWallet({
+ *    password: "abc123",
+ *    networkType: MoneroNetworkType.STAGENET,
+ *    seed: "coexist igloo pamphlet lagoon..."
+ * }); + *
+ * + * @param {MoneroWalletConfig} config - MoneroWalletConfig or equivalent config object + * @param {string|number} config.networkType - network type of the wallet to create (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) + * @param {string} [config.seed] - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) + * @param {string} [config.seedOffset] - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed phrase + * @param {string} [config.primaryAddress] - primary address of the wallet to create (only provide if restoring from keys) + * @param {string} [config.privateViewKey] - private view key of the wallet to create (optional) + * @param {string} [config.privateSpendKey] - private spend key of the wallet to create (optional) + * @param {string} [config.language] - language of the wallet's seed (defaults to "English" or auto-detected) + * @return {MoneroWalletKeys} the created wallet + */ + static async createWallet(config: Partial) { + + // normalize and validate config + if (config === undefined) throw new MoneroError("Must provide config to create wallet"); + config = config instanceof MoneroWalletConfig ? config : new MoneroWalletConfig(config); + if (config.getSeed() !== undefined && (config.getPrimaryAddress() !== undefined || config.getPrivateViewKey() !== undefined || config.getPrivateSpendKey() !== undefined)) { + throw new MoneroError("Wallet may be initialized with a seed or keys but not both"); + } + if (config.getNetworkType() === undefined) throw new MoneroError("Must provide a networkType: 'mainnet', 'testnet' or 'stagenet'"); + if (config.getSaveCurrent() === true) throw new MoneroError("Cannot save current wallet when creating keys-only wallet"); + + // initialize proxied wallet if configured + if (config.getProxyToWorker() === undefined) config.setProxyToWorker(true); + if (config.getProxyToWorker()) { + let walletProxy = await MoneroWalletKeysProxy.createWallet(config);; + return new MoneroWalletKeys(undefined, walletProxy); + } + + // create wallet + if (config.getSeed() !== undefined) return MoneroWalletKeys.createWalletFromSeed(config); + else if (config.getPrivateSpendKey() !== undefined || config.getPrimaryAddress() !== undefined) return MoneroWalletKeys.createWalletFromKeys(config); + else return MoneroWalletKeys.createWalletRandom(config); + } + + protected static async createWalletRandom(config: Partial) { + + // validate and sanitize params + config = config.copy(); + if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating random wallet"); + if (config.getRestoreHeight() !== undefined) throw new MoneroError("Cannot provide restoreHeight when creating random wallet"); + MoneroNetworkType.validate(config.getNetworkType()); + if (config.getLanguage() === undefined) config.setLanguage("English"); + + // load wasm module + let module = await LibraryUtils.loadKeysModule(); + + // queue call to wasm module + return module.queueTask(async () => { + return new Promise((resolve, reject) => { + + // create wallet in wasm which invokes callback when done + module.create_keys_wallet_random(JSON.stringify(config.toJson()), (cppAddress) => { + if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); + else resolve(new MoneroWalletKeys(cppAddress)); + }); + }); + }); + } + + protected static async createWalletFromSeed(config: Partial) { + + // validate and sanitize params + MoneroNetworkType.validate(config.getNetworkType()); + if (config.getSeed() === undefined) throw Error("Must define seed to create wallet from"); + if (config.getSeedOffset() === undefined) config.setSeedOffset(""); + if (config.getLanguage() !== undefined) throw new MoneroError("Cannot provide language when creating wallet from seed"); + + // load wasm module + let module = await LibraryUtils.loadKeysModule(); + + // queue call to wasm module + return module.queueTask(async () => { + return new Promise((resolve, reject) => { + + // create wallet in wasm which invokes callback when done + module.create_keys_wallet_from_seed(JSON.stringify(config.toJson()), (cppAddress) => { + if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); + else resolve(new MoneroWalletKeys(cppAddress)); + }); + }); + }); + } + + protected static async createWalletFromKeys(config: Partial) { + + // validate and sanitize params + if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating wallet from keys"); + MoneroNetworkType.validate(config.getNetworkType()); + if (config.getPrimaryAddress() === undefined) config.setPrimaryAddress(""); + if (config.getPrivateViewKey() === undefined) config.setPrivateViewKey(""); + if (config.getPrivateSpendKey() === undefined) config.setPrivateSpendKey(""); + if (config.getLanguage() === undefined) config.setLanguage("English"); + + // load wasm module + let module = await LibraryUtils.loadKeysModule(); + + // queue call to wasm module + return module.queueTask(async () => { + return new Promise((resolve, reject) => { + + // create wallet in wasm which invokes callback when done + module.create_keys_wallet_from_keys(JSON.stringify(config.toJson()), (cppAddress) => { + if (typeof cppAddress === "string") reject(new MoneroError(cppAddress)); + else resolve(new MoneroWalletKeys(cppAddress)); + }); + }); + }); + } + + static async getSeedLanguages(): Promise { + let module = await LibraryUtils.loadKeysModule(); + return module.queueTask(async () => { + return JSON.parse(module.get_keys_wallet_seed_languages()).languages; + }); + } + + // --------------------------- INSTANCE METHODS ----------------------------- + + /** + * Internal constructor which is given the memory address of a C++ wallet + * instance. + * + * This method should not be called externally but should be called through + * static wallet creation utilities in this class. + * + * @param {number} cppAddress - address of the wallet instance in C++ + * @param {MoneroWalletKeysProxy} walletProxy - proxy + * + * @private + */ + constructor(cppAddress, walletProxy?: MoneroWalletKeysProxy) { + super(); + this._isClosed = false; + if (!cppAddress && !walletProxy) throw new MoneroError("Must provide cppAddress or walletProxy"); + if (walletProxy) this.walletProxy = walletProxy; + else { + this.cppAddress = cppAddress; + this.module = LibraryUtils.getWasmModule(); + if (!this.module.create_full_wallet) throw new MoneroError("WASM module not loaded - create wallet instance using static utilities"); // static utilites pre-load wasm module + } + } + + async addListener(listener: MoneroWalletListener): Promise { + throw new MoneroError("MoneroWalletKeys does not support adding listeners"); + } + + async removeListener(listener): Promise { + throw new MoneroError("MoneroWalletKeys does not support removing listeners"); + } + + async isViewOnly(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().isViewOnly(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return this.module.is_view_only(this.cppAddress); + }); + } + + async isConnectedToDaemon(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().isConnectedToDaemon(); + return false; + } + + async getVersion(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getVersion(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let versionStr = this.module.get_version(this.cppAddress); + let versionJson = JSON.parse(versionStr); + return new MoneroVersion(versionJson.number, versionJson.isRelease); + }); + } + + /** + * @ignore + */ + getPath(): Promise { + throw new MoneroError("MoneroWalletKeys does not support a persisted path"); + } + + async getSeed(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getSeed(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let resp = this.module.get_seed(this.cppAddress); + const errorStr = "error: "; + if (resp.indexOf(errorStr) === 0) throw new MoneroError(resp.substring(errorStr.length)); + return resp ? resp : undefined; + }); + } + + async getSeedLanguage(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getSeedLanguage(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let resp = this.module.get_seed_language(this.cppAddress); + let errorKey = "error: "; + if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorKey.length)); + return resp ? resp : undefined; + }); + } + + async getPrivateSpendKey(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getPrivateSpendKey(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let resp = this.module.get_private_spend_key(this.cppAddress); + let errorKey = "error: "; + if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorKey.length)); + return resp ? resp : undefined; + }); + } + + async getPrivateViewKey(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getPrivateViewKey(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let resp = this.module.get_private_view_key(this.cppAddress); + let errorKey = "error: "; + if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorKey.length)); + return resp ? resp : undefined; + }); + } + + async getPublicViewKey(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getPublicViewKey(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let resp = this.module.get_public_view_key(this.cppAddress); + let errorKey = "error: "; + if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorKey.length)); + return resp ? resp : undefined; + }); + } + + async getPublicSpendKey(): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getPublicSpendKey(); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let resp = this.module.get_public_spend_key(this.cppAddress); + let errorKey = "error: "; + if (resp.indexOf(errorKey) === 0) throw new MoneroError(resp.substring(errorKey.length)); + return resp ? resp : undefined; + }); + } + + async getAddress(accountIdx: number, subaddressIdx: number): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getAddress(accountIdx, subaddressIdx); + assert(typeof accountIdx === "number"); + return this.module.queueTask(async () => { + this.assertNotClosed(); + return this.module.get_address(this.cppAddress, accountIdx, subaddressIdx); + }); + } + + async getAddressIndex(address: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getAddressIndex(address); + return this.module.queueTask(async () => { + this.assertNotClosed(); + let resp = this.module.get_address_index(this.cppAddress, address); + if (resp.charAt(0) !== '{') throw new MoneroError(resp); + return new MoneroSubaddress(JSON.parse(resp)); + }); + } + + async getAccounts(includeSubaddresses?: boolean, tag?: string): Promise { + if (this.getWalletProxy()) return this.getWalletProxy().getAccounts(); + throw new MoneroError("MoneroWalletKeys does not support getting an enumerable set of accounts; query specific accounts"); + } + + // getIntegratedAddress(paymentId) // TODO + // decodeIntegratedAddress + + async close(save = false): Promise { + if (this._isClosed) return; // no effect if closed + if (this.getWalletProxy()) { + await this.getWalletProxy().close(save); + this._isClosed = true; + return; + } + + // save wallet if requested + if (save) await this.save(); + + // close super + await super.close(); + + // queue task to use wasm module + return this.module.queueTask(async () => { + return new Promise((resolve, reject) => { + if (this._isClosed) { + resolve(undefined); + return; + } + + // close wallet in wasm and invoke callback when done + this.module.close(this.cppAddress, false, async () => { // saving handled external to webassembly + delete this.cppAddress; + this._isClosed = true; + resolve(); + }); + }); + }); + } + + async isClosed(): Promise { + return this._isClosed; + } + + // ----------- ADD JSDOC FOR SUPPORTED DEFAULT IMPLEMENTATIONS -------------- + + async getPrimaryAddress(): Promise { return super.getPrimaryAddress(); } + async getSubaddress(accountIdx: number, subaddressIdx: number): Promise { return super.getSubaddress(accountIdx, subaddressIdx); } + + // ----------------------------- PRIVATE HELPERS ---------------------------- + + static sanitizeSubaddress(subaddress) { + if (subaddress.getLabel() === "") subaddress.setLabel(undefined); + return subaddress + } + + protected assertNotClosed() { + if (this._isClosed) throw new MoneroError("Wallet is closed"); + } + + protected getWalletProxy(): MoneroWalletKeysProxy { + this.assertNotClosed(); + return this.walletProxy; + } +} + +/** + * Implements a MoneroWallet by proxying requests to a worker which runs a keys-only wallet. + * + * TODO: sort these methods according to master sort in MoneroWallet.ts + * TODO: probably only allow one listener to worker then propogate to registered listeners for performance + * + * @private + */ +export class MoneroWalletKeysProxy extends MoneroWallet { + + // state variables + protected walletId: string; + protected worker: Worker; + + // -------------------------- WALLET STATIC UTILS --------------------------- + + static async createWallet(config) { + let walletId = GenUtils.getUUID(); + await LibraryUtils.invokeWorker(walletId, "createWalletKeys", [config.toJson()]); + return new MoneroWalletKeysProxy(walletId, await LibraryUtils.getWorker()); + } + + // --------------------------- INSTANCE METHODS ---------------------------- + + /** + * Internal constructor which is given a worker to communicate with via messages. + * + * This method should not be called externally but should be called through + * static wallet creation utilities in this class. + * + * @param {string} walletId - identifies the wallet with the worker + * @param {Worker} worker - worker to communicate with via messages + * + * @protected + */ + constructor(walletId, worker) { + super(); + this.walletId = walletId; + this.worker = worker; + } + + async isViewOnly(): Promise { + return this.invokeWorker("isViewOnly"); + } + + async getVersion(): Promise { + throw new MoneroError("Not implemented"); + } + + async getSeed() { + return this.invokeWorker("getSeed") as Promise; + } + + async getSeedLanguage() { + return this.invokeWorker("getSeedLanguage") as Promise; + } + + async getSeedLanguages() { + return this.invokeWorker("getSeedLanguages"); + } + + async getPrivateSpendKey() { + return this.invokeWorker("getPrivateSpendKey") as Promise; + } + + async getPrivateViewKey() { + return this.invokeWorker("getPrivateViewKey") as Promise; + } + + async getPublicViewKey() { + return this.invokeWorker("getPublicViewKey") as Promise; + } + + async getPublicSpendKey() { + return this.invokeWorker("getPublicSpendKey") as Promise; + } + + async getAddress(accountIdx, subaddressIdx) { + return this.invokeWorker("getAddress", Array.from(arguments)) as Promise; + } + + async getAddressIndex(address) { + let subaddressJson = await this.invokeWorker("getAddressIndex", Array.from(arguments)); + return MoneroWalletKeys.sanitizeSubaddress(new MoneroSubaddress(subaddressJson)); + } + + async getIntegratedAddress(standardAddress, paymentId) { + return new MoneroIntegratedAddress(await this.invokeWorker("getIntegratedAddress", Array.from(arguments))); + } + + async decodeIntegratedAddress(integratedAddress) { + return new MoneroIntegratedAddress(await this.invokeWorker("decodeIntegratedAddress", Array.from(arguments))); + } + + async close(save) { + if (save) throw new MoneroError("Cannot save keys-only wallet"); + await this.invokeWorker("close"); + LibraryUtils.removeWorkerObject(this.walletId); + } + + async isClosed() { + return this.invokeWorker("isClosed"); + } + + protected async invokeWorker(fnName: string, args?: any): Promise { + return await LibraryUtils.invokeWorker(this.walletId, fnName, args); + } +} diff --git a/src/main/js/wallet/MoneroWalletRpc.js b/src/main/ts/wallet/MoneroWalletRpc.ts similarity index 59% rename from src/main/js/wallet/MoneroWalletRpc.js rename to src/main/ts/wallet/MoneroWalletRpc.ts index 298ee2c4d..7af4592f0 100644 --- a/src/main/js/wallet/MoneroWalletRpc.js +++ b/src/main/ts/wallet/MoneroWalletRpc.ts @@ -1,45 +1,47 @@ -const assert = require("assert"); -const BigInteger = require("../common/biginteger").BigInteger; -const GenUtils = require("../common/GenUtils"); -const LibraryUtils = require("../common/LibraryUtils"); -const TaskLooper = require("../common/TaskLooper"); -const MoneroAccount = require("./model/MoneroAccount"); -const MoneroAccountTag = require("./model/MoneroAccountTag"); -const MoneroAddressBookEntry = require("./model/MoneroAddressBookEntry"); -const MoneroBlock = require("../daemon/model/MoneroBlock"); -const MoneroBlockHeader = require("../daemon/model/MoneroBlockHeader"); -const MoneroCheckReserve = require("./model/MoneroCheckReserve"); -const MoneroCheckTx = require("./model/MoneroCheckTx"); -const MoneroDestination = require("./model/MoneroDestination"); -const MoneroError = require("../common/MoneroError"); -const MoneroIncomingTransfer = require("./model/MoneroIncomingTransfer"); -const MoneroIntegratedAddress = require("./model/MoneroIntegratedAddress"); -const MoneroKeyImage = require("../daemon/model/MoneroKeyImage"); -const MoneroKeyImageImportResult = require("./model/MoneroKeyImageImportResult"); -const MoneroMultisigInfo = require("./model/MoneroMultisigInfo"); -const MoneroMultisigInitResult = require("./model/MoneroMultisigInitResult"); -const MoneroMultisigSignResult = require("./model/MoneroMultisigSignResult"); -const MoneroOutgoingTransfer = require("./model/MoneroOutgoingTransfer"); -const MoneroOutputQuery = require("./model/MoneroOutputQuery"); -const MoneroOutputWallet = require("./model/MoneroOutputWallet"); -const MoneroRpcConnection = require("../common/MoneroRpcConnection"); -const MoneroRpcError = require("../common/MoneroRpcError"); -const MoneroSubaddress = require("./model/MoneroSubaddress"); -const MoneroSyncResult = require("./model/MoneroSyncResult"); -const MoneroTransferQuery = require("./model/MoneroTransferQuery"); -const MoneroTxConfig = require("./model/MoneroTxConfig"); -const MoneroTxQuery = require("./model/MoneroTxQuery"); -const MoneroTxSet = require("./model/MoneroTxSet"); -const MoneroTxWallet = require("./model/MoneroTxWallet"); -const MoneroUtils = require("../common/MoneroUtils"); -const MoneroVersion = require("../daemon/model/MoneroVersion"); -const MoneroWallet = require("./MoneroWallet"); -const MoneroWalletConfig = require("./model/MoneroWalletConfig"); -const MoneroWalletListener = require("./model/MoneroWalletListener"); -const MoneroMessageSignatureType = require("./model/MoneroMessageSignatureType"); -const MoneroMessageSignatureResult = require("./model/MoneroMessageSignatureResult"); -const ThreadPool = require("../common/ThreadPool"); -const SslOptions = require("../common/SslOptions"); +import assert from "assert"; +import GenUtils from "../common/GenUtils"; +import LibraryUtils from "../common/LibraryUtils"; +import TaskLooper from "../common/TaskLooper"; +import MoneroAccount from "./model/MoneroAccount"; +import MoneroAccountTag from "./model/MoneroAccountTag"; +import MoneroAddressBookEntry from "./model/MoneroAddressBookEntry"; +import MoneroBlock from "../daemon/model/MoneroBlock"; +import MoneroBlockHeader from "../daemon/model/MoneroBlockHeader"; +import MoneroCheckReserve from "./model/MoneroCheckReserve"; +import MoneroCheckTx from "./model/MoneroCheckTx"; +import MoneroDestination from "./model/MoneroDestination"; +import MoneroError from "../common/MoneroError"; +import MoneroIncomingTransfer from "./model/MoneroIncomingTransfer"; +import MoneroIntegratedAddress from "./model/MoneroIntegratedAddress"; +import MoneroKeyImage from "../daemon/model/MoneroKeyImage"; +import MoneroKeyImageImportResult from "./model/MoneroKeyImageImportResult"; +import MoneroMultisigInfo from "./model/MoneroMultisigInfo"; +import MoneroMultisigInitResult from "./model/MoneroMultisigInitResult"; +import MoneroMultisigSignResult from "./model/MoneroMultisigSignResult"; +import MoneroOutgoingTransfer from "./model/MoneroOutgoingTransfer"; +import MoneroOutputQuery from "./model/MoneroOutputQuery"; +import MoneroOutputWallet from "./model/MoneroOutputWallet"; +import MoneroRpcConnection from "../common/MoneroRpcConnection"; +import MoneroRpcError from "../common/MoneroRpcError"; +import MoneroSubaddress from "./model/MoneroSubaddress"; +import MoneroSyncResult from "./model/MoneroSyncResult"; +import MoneroTransfer from "./model/MoneroTransfer"; +import MoneroTransferQuery from "./model/MoneroTransferQuery"; +import MoneroTx from "../daemon/model/MoneroTx"; +import MoneroTxConfig from "./model/MoneroTxConfig"; +import MoneroTxQuery from "./model/MoneroTxQuery"; +import MoneroTxSet from "./model/MoneroTxSet"; +import MoneroTxWallet from "./model/MoneroTxWallet"; +import MoneroUtils from "../common/MoneroUtils"; +import MoneroVersion from "../daemon/model/MoneroVersion"; +import MoneroWallet from "./MoneroWallet"; +import MoneroWalletConfig from "./model/MoneroWalletConfig"; +import MoneroWalletListener from "./model/MoneroWalletListener"; +import MoneroMessageSignatureType from "./model/MoneroMessageSignatureType"; +import MoneroMessageSignatureResult from "./model/MoneroMessageSignatureResult"; +import ThreadPool from "../common/ThreadPool"; +import SslOptions from "../common/SslOptions"; +import { ChildProcess } from "child_process"; /** * Copyright (c) woodser @@ -67,157 +69,62 @@ const SslOptions = require("../common/SslOptions"); * Implements a MoneroWallet as a client of monero-wallet-rpc. * * @implements {MoneroWallet} - * @hideconstructor */ -class MoneroWalletRpc extends MoneroWallet { - - /** - *

Construct a wallet RPC client (for internal use).

- * - * @param {string|object|MoneroRpcConnection|string[]} uriOrConfig - uri of monero-wallet-rpc or JS config object or MoneroRpcConnection or command line parameters to run a monero-wallet-rpc process internally - * @param {string} uriOrConfig.uri - uri of monero-wallet-rpc - * @param {string} uriOrConfig.username - username to authenticate with monero-wallet-rpc (optional) - * @param {string} uriOrConfig.password - password to authenticate with monero-wallet-rpc (optional) - * @param {boolean} uriOrConfig.rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {string} username - username to authenticate with monero-wallet-rpc (optional) - * @param {string} password - password to authenticate with monero-wallet-rpc (optional) - * @param {boolean} rejectUnauthorized - rejects self-signed certificates if true (default true) - */ - constructor(uriOrConfig, username, password, rejectUnauthorized) { +export default class MoneroWalletRpc extends MoneroWallet { + + // static variables + protected static readonly DEFAULT_SYNC_PERIOD_IN_MS = 20000; // default period between syncs in ms (defined by DEFAULT_AUTO_REFRESH_PERIOD in wallet_rpc_server.cpp) + + // instance variables + protected config: Partial; + protected addressCache: any; + protected syncPeriodInMs: number; + protected listeners: MoneroWalletListener[]; + protected process: any; + protected path: string; + protected daemonConnection: MoneroRpcConnection; + protected walletPoller: WalletPoller; + + /** @private */ + constructor(config: MoneroWalletConfig) { super(); - if (GenUtils.isArray(uriOrConfig)) throw new MoneroError("Array with command parameters is invalid first parameter, use `await monerojs.connectToWalletRpc(...)`"); - this.config = MoneroWalletRpc._normalizeConfig(uriOrConfig, username, password, rejectUnauthorized); - this.rpc = new MoneroRpcConnection(this.config); + this.config = config; this.addressCache = {}; // avoid unecessary requests for addresses this.syncPeriodInMs = MoneroWalletRpc.DEFAULT_SYNC_PERIOD_IN_MS; this.listeners = []; } - /** - *

Create a client connected to monero-wallet-rpc (for internal use).

- * - * @param {string|string[]|object|MoneroRpcConnection} uriOrConfig - uri of monero-wallet-rpc or terminal parameters or JS config object or MoneroRpcConnection - * @param {string} uriOrConfig.uri - uri of monero-wallet-rpc - * @param {string} uriOrConfig.username - username to authenticate with monero-wallet-rpc (optional) - * @param {string} uriOrConfig.password - password to authenticate with monero-wallet-rpc (optional) - * @param {boolean} uriOrConfig.rejectUnauthorized - rejects self-signed certificates if true (default true) - * @param {string} username - username to authenticate with monero-wallet-rpc (optional) - * @param {string} password - password to authenticate with monero-wallet-rpc (optional) - * @param {boolean} rejectUnauthorized - rejects self-signed certificates if true (default true) - * @return {MoneroWalletRpc} the wallet RPC client - */ - static async _connectToWalletRpc(uriOrConfig, username, password, rejectUnauthorized) { - if (GenUtils.isArray(uriOrConfig)) return MoneroWalletRpc._startWalletRpcProcess(uriOrConfig); // handle array as terminal command - else return new MoneroWalletRpc(...arguments); // otherwise connect to server - } - - static async _startWalletRpcProcess(cmd) { - assert(GenUtils.isArray(cmd), "Must provide string array with command line parameters"); - - // start process - this.process = require('child_process').spawn(cmd[0], cmd.slice(1), {}); - this.process.stdout.setEncoding('utf8'); - this.process.stderr.setEncoding('utf8'); - - // return promise which resolves after starting monero-wallet-rpc - let uri; - let that = this; - let output = ""; - return new Promise(function(resolve, reject) { - - // handle stdout - that.process.stdout.on('data', function(data) { - let line = data.toString(); - LibraryUtils.log(2, line); - output += line + '\n'; // capture output in case of error - - // extract uri from e.g. "I Binding on 127.0.0.1 (IPv4):38085" - let uriLineContains = "Binding on "; - let uriLineContainsIdx = line.indexOf(uriLineContains); - if (uriLineContainsIdx >= 0) { - let host = line.substring(uriLineContainsIdx + uriLineContains.length, line.lastIndexOf(' ')); - let unformattedLine = line.replace(/\u001b\[.*?m/g, '').trim(); // remove color formatting - let port = unformattedLine.substring(unformattedLine.lastIndexOf(':') + 1); - let sslIdx = cmd.indexOf("--rpc-ssl"); - let sslEnabled = sslIdx >= 0 ? "enabled" == cmd[sslIdx + 1].toLowerCase() : false; - uri = (sslEnabled ? "https" : "http") + "://" + host + ":" + port; - } - - // read success message - if (line.indexOf("Starting wallet RPC server") >= 0) { - - // get username and password from params - let userPassIdx = cmd.indexOf("--rpc-login"); - let userPass = userPassIdx >= 0 ? cmd[userPassIdx + 1] : undefined; - let username = userPass === undefined ? undefined : userPass.substring(0, userPass.indexOf(':')); - let password = userPass === undefined ? undefined : userPass.substring(userPass.indexOf(':') + 1); - - // create client connected to internal process - let wallet = new MoneroWalletRpc(uri, username, password); - wallet.process = that.process; - - // resolve promise with client connected to internal process - this.isResolved = true; - resolve(wallet); - } - }); - - // handle stderr - that.process.stderr.on('data', function(data) { - if (LibraryUtils.getLogLevel() >= 2) console.error(data); - }); - - // handle exit - that.process.on("exit", function(code) { - if (!this.isResolved) reject(new MoneroError("monero-wallet-rpc process terminated with exit code " + code + (output ? ":\n\n" + output : ""))); - }); - - // handle error - that.process.on("error", function(err) { - if (err.message.indexOf("ENOENT") >= 0) reject(new MoneroError("monero-wallet-rpc does not exist at path '" + cmd[0] + "'")); - if (!this.isResolved) reject(err); - }); - - // handle uncaught exception - that.process.on("uncaughtException", function(err, origin) { - console.error("Uncaught exception in monero-wallet-rpc process: " + err.message); - console.error(origin); - reject(err); - }); - }); - } - // --------------------------- RPC WALLET METHODS --------------------------- /** * Get the internal process running monero-wallet-rpc. * - * @return the process running monero-wallet-rpc, undefined if not created from new process + * @return {ChildProcess} the process running monero-wallet-rpc, undefined if not created from new process */ - getProcess() { + getProcess(): ChildProcess { return this.process; } /** * Stop the internal process running monero-wallet-rpc, if applicable. * - * @param {boolean} force specifies if the process should be destroyed forcibly - * @return {Promise} the exit code from stopping the process + * @param {boolean} force specifies if the process should be destroyed forcibly (default false) + * @return {Promise} the exit code from stopping the process */ - async stopProcess(force) { + async stopProcess(force = false): Promise { if (this.process === undefined) throw new MoneroError("MoneroWalletRpc instance not created from new process"); let listenersCopy = GenUtils.copyArray(this.getListeners()); for (let listener of listenersCopy) await this.removeListener(listener); - return GenUtils.killProcess(this.process, force ? "sigkill" : undefined); + return GenUtils.killProcess(this.process, force ? "SIGKILL" : undefined); } /** * Get the wallet's RPC connection. * - * @return {MoneroWalletRpc} the wallet's rpc connection + * @return {MoneroRpcConnection | undefined} the wallet's rpc connection */ - getRpcConnection() { - return this.rpc; + getRpcConnection(): MoneroRpcConnection | undefined { + return this.config.getServer(); } /** @@ -228,39 +135,36 @@ class MoneroWalletRpc extends MoneroWallet { * * let wallet = new MoneroWalletRpc("http://localhost:38084", "rpc_user", "abc123");
* await wallet.openWallet("mywallet1", "supersecretpassword");
+ *
* await wallet.openWallet({
*    path: "mywallet2",
*    password: "supersecretpassword",
- *    serverUri: "http://locahost:38081",
+ *    server: "http://locahost:38081", // or object with uri, username, password, etc
*    rejectUnauthorized: false
* });
*
* - * @param {string|object|MoneroWalletConfig} pathOrConfig - the wallet's name or configuration to open + * @param {string|MoneroWalletConfig} pathOrConfig - the wallet's name or configuration to open * @param {string} pathOrConfig.path - path of the wallet to create (optional, in-memory wallet if not given) * @param {string} pathOrConfig.password - password of the wallet to create - * @param {string} pathOrConfig.serverUri - uri of a daemon to use (optional, monero-wallet-rpc usually started with daemon config) - * @param {string} pathOrConfig.serverUsername - username to authenticate with the daemon (optional) - * @param {string} pathOrConfig.serverPassword - password to authenticate with the daemon (optional) - * @param {boolean} pathOrConfig.rejectUnauthorized - reject self-signed server certificates if true (defaults to true) - * @param {MoneroRpcConnection|object} pathOrConfig.server - MoneroRpcConnection or equivalent JS object providing daemon configuration (optional) - * @param {string} password is the wallet's password - * @return {MoneroWalletRpc} this wallet client + * @param {string|Partial} pathOrConfig.server - uri or MoneroRpcConnection of a daemon to use (optional, monero-wallet-rpc usually started with daemon config) + * @param {string} [password] the wallet's password + * @return {Promise} this wallet client */ - async openWallet(pathOrConfig, password) { + async openWallet(pathOrConfig: string | Partial, password?: string): Promise { // normalize and validate config let config = new MoneroWalletConfig(typeof pathOrConfig === "string" ? {path: pathOrConfig, password: password ? password : ""} : pathOrConfig); - // TODO: ensure other fields are uninitialized? + // TODO: ensure other fields uninitialized? // open wallet on rpc server if (!config.getPath()) throw new MoneroError("Must provide name of wallet to open"); - await this.rpc.sendJsonRequest("open_wallet", {filename: config.getPath(), password: config.getPassword()}); - await this._clear(); + await this.config.getServer().sendJsonRequest("open_wallet", {filename: config.getPath(), password: config.getPassword()}); + await this.clear(); this.path = config.getPath(); // set daemon if provided - if (config.getServer()) return this.setDaemonConnection(config.getServer()); + if (config.getServer()) await this.setDaemonConnection(config.getServer()); return this; } @@ -282,62 +186,56 @@ class MoneroWalletRpc extends MoneroWallet { * }); *
* - * @param {object|MoneroWalletConfig} config - MoneroWalletConfig or equivalent JS object - * @param {string} config.path - path of the wallet to create (optional, in-memory wallet if not given) - * @param {string} config.password - password of the wallet to create - * @param {string} config.seed - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) - * @param {string} config.seedOffset - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed - * @param {boolean} config.isMultisig - restore multisig wallet from seed - * @param {string} config.primaryAddress - primary address of the wallet to create (only provide if restoring from keys) - * @param {string} config.privateViewKey - private view key of the wallet to create (optional) - * @param {string} config.privateSpendKey - private spend key of the wallet to create (optional) - * @param {number} config.restoreHeight - block height to start scanning from (defaults to 0 unless generating random wallet) - * @param {string} config.language - language of the wallet's mnemonic phrase or seed (defaults to "English" or auto-detected) - * @param {MoneroRpcConnection} config.server - MoneroRpcConnection to a monero daemon (optional)
- * @param {string} config.serverUri - uri of a daemon to use (optional, monero-wallet-rpc usually started with daemon config) - * @param {string} config.serverUsername - username to authenticate with the daemon (optional) - * @param {string} config.serverPassword - password to authenticate with the daemon (optional) - * @param {MoneroConnectionManager} config.connectionManager - manage connections to monerod (optional) - * @param {boolean} config.rejectUnauthorized - reject self-signed server certificates if true (defaults to true) - * @param {MoneroRpcConnection|object} config.server - MoneroRpcConnection or equivalent JS object providing daemon configuration (optional) - * @param {boolean} config.saveCurrent - specifies if the current RPC wallet should be saved before being closed (default true) + * @param {Partial} config - MoneroWalletConfig or equivalent JS object + * @param {string} [config.path] - path of the wallet to create (optional, in-memory wallet if not given) + * @param {string} [config.password] - password of the wallet to create + * @param {string} [config.seed] - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) + * @param {string} [config.seedOffset] - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed + * @param {boolean} [config.isMultisig] - restore multisig wallet from seed + * @param {string} [config.primaryAddress] - primary address of the wallet to create (only provide if restoring from keys) + * @param {string} [config.privateViewKey] - private view key of the wallet to create (optional) + * @param {string} [config.privateSpendKey] - private spend key of the wallet to create (optional) + * @param {number} [config.restoreHeight] - block height to start scanning from (defaults to 0 unless generating random wallet) + * @param {string} [config.language] - language of the wallet's mnemonic phrase or seed (defaults to "English" or auto-detected) + * @param {MoneroRpcConnection} [config.server] - MoneroRpcConnection to a monero daemon (optional)
+ * @param {string} [config.serverUri] - uri of a daemon to use (optional, monero-wallet-rpc usually started with daemon config) + * @param {string} [config.serverUsername] - username to authenticate with the daemon (optional) + * @param {string} [config.serverPassword] - password to authenticate with the daemon (optional) + * @param {MoneroConnectionManager} [config.connectionManager] - manage connections to monerod (optional) + * @param {boolean} [config.rejectUnauthorized] - reject self-signed server certificates if true (defaults to true) + * @param {MoneroRpcConnection} [config.server] - MoneroRpcConnection or equivalent JS object providing daemon configuration (optional) + * @param {boolean} [config.saveCurrent] - specifies if the current RPC wallet should be saved before being closed (default true) * @return {MoneroWalletRpc} this wallet client */ - async createWallet(config) { + async createWallet(config: Partial): Promise { // normalize and validate config if (config === undefined) throw new MoneroError("Must provide config to create wallet"); - config = new MoneroWalletConfig(config); - if (config.getSeed() !== undefined && (config.getPrimaryAddress() !== undefined || config.getPrivateViewKey() !== undefined || config.getPrivateSpendKey() !== undefined)) { + const configNormalized = new MoneroWalletConfig(config); + if (configNormalized.getSeed() !== undefined && (configNormalized.getPrimaryAddress() !== undefined || configNormalized.getPrivateViewKey() !== undefined || configNormalized.getPrivateSpendKey() !== undefined)) { throw new MoneroError("Wallet can be initialized with a seed or keys but not both"); } - if (config.getNetworkType() !== undefined) throw new MoneroError("Cannot provide networkType when creating RPC wallet because server's network type is already set"); - if (config.getAccountLookahead() !== undefined || config.getSubaddressLookahead() !== undefined) throw new MoneroError("monero-wallet-rpc does not support creating wallets with subaddress lookahead over rpc"); - if (config.getPassword() === undefined) config.setPassword(""); + if (configNormalized.getNetworkType() !== undefined) throw new MoneroError("Cannot provide networkType when creating RPC wallet because server's network type is already set"); + if (configNormalized.getAccountLookahead() !== undefined || configNormalized.getSubaddressLookahead() !== undefined) throw new MoneroError("monero-wallet-rpc does not support creating wallets with subaddress lookahead over rpc"); + if (configNormalized.getPassword() === undefined) configNormalized.setPassword(""); // create wallet - if (config.getSeed() !== undefined) await this._createWalletFromSeed(config); - else if (config.getPrivateSpendKey() !== undefined || config.getPrimaryAddress() !== undefined) await this._createWalletFromKeys(config); - else await this._createWalletRandom(config); + if (configNormalized.getSeed() !== undefined) await this.createWalletFromSeed(configNormalized); + else if (configNormalized.getPrivateSpendKey() !== undefined || configNormalized.getPrimaryAddress() !== undefined) await this.createWalletFromKeys(configNormalized); + else await this.createWalletRandom(configNormalized); // set daemon or connection manager - if (config.getConnectionManager()) { - if (config.getServer()) throw new MoneroError("Wallet can be initialized with a server or connection manager but not both"); - await this.setConnectionManager(config.getConnectionManager()); - } else if (config.getServer()) { - await this.setDaemonConnection(config.getServer()); + if (configNormalized.getConnectionManager()) { + if (configNormalized.getServer()) throw new MoneroError("Wallet can be initialized with a server or connection manager but not both"); + await this.setConnectionManager(configNormalized.getConnectionManager()); + } else if (configNormalized.getServer()) { + await this.setDaemonConnection(configNormalized.getServer()); } return this; } - /** - * Create and open a new wallet with a randomly generated seed on the RPC server. - * - * @param {MoneroWalletConfig} config - the wallet configuration - * @return {MoneroWalletRpc} this wallet client - */ - async _createWalletRandom(config) { + protected async createWalletRandom(config: MoneroWalletConfig) { if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating random wallet"); if (config.getRestoreHeight() !== undefined) throw new MoneroError("Cannot provide restoreHeight when creating random wallet"); if (config.getSaveCurrent() === false) throw new MoneroError("Current wallet is saved automatically when creating random wallet"); @@ -345,54 +243,41 @@ class MoneroWalletRpc extends MoneroWallet { if (!config.getLanguage()) config.setLanguage(MoneroWallet.DEFAULT_LANGUAGE); let params = { filename: config.getPath(), password: config.getPassword(), language: config.getLanguage() }; try { - await this.rpc.sendJsonRequest("create_wallet", params); - } catch (err) { - this._handleCreateWalletError(config.getPath(), err); + await this.config.getServer().sendJsonRequest("create_wallet", params); + } catch (err: any) { + this.handleCreateWalletError(config.getPath(), err); } - await this._clear(); + await this.clear(); this.path = config.getPath(); return this; } - /** - * Create and open a wallet from an existing seed on the RPC server, - * closing the currently open wallet if applicable. - * - * @param {MoneroWalletConfig} config - the wallet configuration - * @return {MoneroWalletRpc} this wallet client - */ - async _createWalletFromSeed(config) { + protected async createWalletFromSeed(config: MoneroWalletConfig) { try { - await this.rpc.sendJsonRequest("restore_deterministic_wallet", { + await this.config.getServer().sendJsonRequest("restore_deterministic_wallet", { filename: config.getPath(), password: config.getPassword(), seed: config.getSeed(), seed_offset: config.getSeedOffset(), - enable_multisig_experimental: config.isMultisig(), + enable_multisig_experimental: config.getIsMultisig(), restore_height: config.getRestoreHeight(), language: config.getLanguage(), autosave_current: config.getSaveCurrent() }); - } catch (err) { - this._handleCreateWalletError(config.getPath(), err); + } catch (err: any) { + this.handleCreateWalletError(config.getPath(), err); } - await this._clear(); + await this.clear(); this.path = config.getPath(); return this; } - /** - * Create a wallet on the RPC server from an address, view key, and (optionally) spend key. - * - * @param {MoneroWalletConfig} config - the wallet configuration - * @return {MoneroWalletRpc} this wallet client - */ - async _createWalletFromKeys(config) { + protected async createWalletFromKeys(config: MoneroWalletConfig) { if (config.getSeedOffset() !== undefined) throw new MoneroError("Cannot provide seedOffset when creating wallet from keys"); if (config.getRestoreHeight() === undefined) config.setRestoreHeight(0); if (config.getLanguage() === undefined) config.setLanguage(MoneroWallet.DEFAULT_LANGUAGE); try { - await this.rpc.sendJsonRequest("generate_from_keys", { + await this.config.getServer().sendJsonRequest("generate_from_keys", { filename: config.getPath(), password: config.getPassword(), address: config.getPrimaryAddress(), @@ -401,25 +286,25 @@ class MoneroWalletRpc extends MoneroWallet { restore_height: config.getRestoreHeight(), autosave_current: config.getSaveCurrent() }); - } catch (err) { - this._handleCreateWalletError(config.getPath(), err); + } catch (err: any) { + this.handleCreateWalletError(config.getPath(), err); } - await this._clear(); + await this.clear(); this.path = config.getPath(); return this; } - _handleCreateWalletError(name, err) { + protected handleCreateWalletError(name, err) { if (err.message === "Cannot create wallet. Already exists.") throw new MoneroRpcError("Wallet already exists: " + name, err.getCode(), err.getRpcMethod(), err.getRpcParams()); if (err.message === "Electrum-style word list failed verification") throw new MoneroRpcError("Invalid mnemonic", err.getCode(), err.getRpcMethod(), err.getRpcParams()); throw err; } - async isViewOnly() { + async isViewOnly(): Promise { try { - await this.rpc.sendJsonRequest("query_key", {key_type: "mnemonic"}); + await this.config.getServer().sendJsonRequest("query_key", {key_type: "mnemonic"}); return false; // key retrieval succeeds if not view only - } catch (e) { + } catch (e: any) { if (e.getCode() === -29) return true; // wallet is view only if (e.getCode() === -1) return false; // wallet is offline but not view only throw e; @@ -429,15 +314,14 @@ class MoneroWalletRpc extends MoneroWallet { /** * Set the wallet's daemon connection. * - * @param {string|MoneroRpcConnection} uriOrConnection - the daemon's URI or connection (defaults to offline) + * @param {string|MoneroRpcConnection} [uriOrConnection] - the daemon's URI or connection (defaults to offline) * @param {boolean} isTrusted - indicates if the daemon in trusted * @param {SslOptions} sslOptions - custom SSL configuration */ - async setDaemonConnection(uriOrRpcConnection, isTrusted, sslOptions) { - let connection = !uriOrRpcConnection ? undefined : uriOrRpcConnection instanceof MoneroRpcConnection ? uriOrRpcConnection : new MoneroRpcConnection(uriOrRpcConnection); - if (connection) connection.setProxyToWorker(false); + async setDaemonConnection(uriOrConnection?: MoneroRpcConnection | string, isTrusted?: boolean, sslOptions?: SslOptions): Promise { + let connection = !uriOrConnection ? undefined : uriOrConnection instanceof MoneroRpcConnection ? uriOrConnection : new MoneroRpcConnection(uriOrConnection); if (!sslOptions) sslOptions = new SslOptions(); - let params = {}; + let params: any = {}; params.address = connection ? connection.getUri() : "bad_uri"; // TODO monero-wallet-rpc: bad daemon uri necessary for offline? params.username = connection ? connection.getUsername() : ""; params.password = connection ? connection.getPassword() : ""; @@ -448,57 +332,82 @@ class MoneroWalletRpc extends MoneroWallet { params.ssl_ca_file = sslOptions.getCertificateAuthorityFile(); params.ssl_allowed_fingerprints = sslOptions.getAllowedFingerprints(); params.ssl_allow_any_cert = sslOptions.getAllowAnyCert(); - await this.rpc.sendJsonRequest("set_daemon", params); + await this.config.getServer().sendJsonRequest("set_daemon", params); this.daemonConnection = connection; } - async getDaemonConnection() { + async getDaemonConnection(): Promise { return this.daemonConnection; } + + /** + * Get the locked and unlocked balances in a single request. + * + * @param {number} [accountIdx] account index + * @param {number} [subaddressIdx] subaddress index + * @return {Promise} is the locked and unlocked balances in an array, respectively + */ + async getBalances(accountIdx?: number, subaddressIdx?: number): Promise { + if (accountIdx === undefined) { + assert.equal(subaddressIdx, undefined, "Must provide account index with subaddress index"); + let balance = BigInt(0); + let unlockedBalance = BigInt(0); + for (let account of await this.getAccounts()) { + balance = balance + account.getBalance(); + unlockedBalance = unlockedBalance + account.getUnlockedBalance(); + } + return [balance, unlockedBalance]; + } else { + let params = {account_index: accountIdx, address_indices: subaddressIdx === undefined ? undefined : [subaddressIdx]}; + let resp = await this.config.getServer().sendJsonRequest("get_balance", params); + if (subaddressIdx === undefined) return [BigInt(resp.result.balance), BigInt(resp.result.unlocked_balance)]; + else return [BigInt(resp.result.per_subaddress[0].balance), BigInt(resp.result.per_subaddress[0].unlocked_balance)]; + } + } // -------------------------- COMMON WALLET METHODS ------------------------- - async addListener(listener) { + async addListener(listener: MoneroWalletListener): Promise { assert(listener instanceof MoneroWalletListener, "Listener must be instance of MoneroWalletListener"); this.listeners.push(listener); - this._refreshListening(); + this.refreshListening(); } - async removeListener(listener) { + async removeListener(listener): Promise { let idx = this.listeners.indexOf(listener); if (idx > -1) this.listeners.splice(idx, 1); else throw new MoneroError("Listener is not registered with wallet"); - this._refreshListening(); + this.refreshListening(); } - getListeners() { + getListeners(): MoneroWalletListener[] { return this.listeners; } - async isConnectedToDaemon() { + async isConnectedToDaemon(): Promise { try { await this.checkReserveProof(await this.getPrimaryAddress(), "", ""); // TODO (monero-project): provide better way to know if wallet rpc is connected to daemon throw new MoneroError("check reserve expected to fail"); - } catch (e) { + } catch (e: any) { return e.message.indexOf("Failed to connect to daemon") < 0; } } - async getVersion() { - let resp = await this.rpc.sendJsonRequest("get_version"); + async getVersion(): Promise { + let resp = await this.config.getServer().sendJsonRequest("get_version"); return new MoneroVersion(resp.result.version, resp.result.release); } - async getPath() { + async getPath(): Promise { return this.path; } - async getSeed() { - let resp = await this.rpc.sendJsonRequest("query_key", { key_type: "mnemonic" }); + async getSeed(): Promise { + let resp = await this.config.getServer().sendJsonRequest("query_key", { key_type: "mnemonic" }); return resp.result.key; } - async getSeedLanguage() { + async getSeedLanguage(): Promise { if (await this.getSeed() === undefined) return undefined; throw new MoneroError("MoneroWalletRpc.getSeedLanguage() not supported"); } @@ -509,20 +418,20 @@ class MoneroWalletRpc extends MoneroWallet { * @return {string[]} the available languages for the wallet's seed. */ async getSeedLanguages() { - return (await this.rpc.sendJsonRequest("get_languages")).result.languages; + return (await this.config.getServer().sendJsonRequest("get_languages")).result.languages; } - async getPrivateViewKey() { - let resp = await this.rpc.sendJsonRequest("query_key", { key_type: "view_key" }); + async getPrivateViewKey(): Promise { + let resp = await this.config.getServer().sendJsonRequest("query_key", { key_type: "view_key" }); return resp.result.key; } - async getPrivateSpendKey() { - let resp = await this.rpc.sendJsonRequest("query_key", { key_type: "spend_key" }); + async getPrivateSpendKey(): Promise { + let resp = await this.config.getServer().sendJsonRequest("query_key", { key_type: "spend_key" }); return resp.result.key; } - async getAddress(accountIdx, subaddressIdx) { + async getAddress(accountIdx: number, subaddressIdx: number): Promise { let subaddressMap = this.addressCache[accountIdx]; if (!subaddressMap) { await this.getSubaddresses(accountIdx, undefined, true); // cache's all addresses at this account @@ -537,70 +446,70 @@ class MoneroWalletRpc extends MoneroWallet { } // TODO: use cache - async getAddressIndex(address) { + async getAddressIndex(address: string): Promise { // fetch result and normalize error if address does not belong to the wallet let resp; try { - resp = await this.rpc.sendJsonRequest("get_address_index", {address: address}); - } catch (e) { + resp = await this.config.getServer().sendJsonRequest("get_address_index", {address: address}); + } catch (e: any) { if (e.getCode() === -2) throw new MoneroError(e.message); throw e; } // convert rpc response - let subaddress = new MoneroSubaddress(address); + let subaddress = new MoneroSubaddress({address: address}); subaddress.setAccountIndex(resp.result.index.major); subaddress.setIndex(resp.result.index.minor); return subaddress; } - async getIntegratedAddress(standardAddress, paymentId) { + async getIntegratedAddress(standardAddress?: string, paymentId?: string): Promise { try { - let integratedAddressStr = (await this.rpc.sendJsonRequest("make_integrated_address", {standard_address: standardAddress, payment_id: paymentId})).result.integrated_address; + let integratedAddressStr = (await this.config.getServer().sendJsonRequest("make_integrated_address", {standard_address: standardAddress, payment_id: paymentId})).result.integrated_address; return await this.decodeIntegratedAddress(integratedAddressStr); - } catch (e) { + } catch (e: any) { if (e.message.includes("Invalid payment ID")) throw new MoneroError("Invalid payment ID: " + paymentId); throw e; } } - async decodeIntegratedAddress(integratedAddress) { - let resp = await this.rpc.sendJsonRequest("split_integrated_address", {integrated_address: integratedAddress}); + async decodeIntegratedAddress(integratedAddress: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("split_integrated_address", {integrated_address: integratedAddress}); return new MoneroIntegratedAddress().setStandardAddress(resp.result.standard_address).setPaymentId(resp.result.payment_id).setIntegratedAddress(integratedAddress); } - async getHeight() { - return (await this.rpc.sendJsonRequest("get_height")).result.height; + async getHeight(): Promise { + return (await this.config.getServer().sendJsonRequest("get_height")).result.height; } - async getDaemonHeight() { + async getDaemonHeight(): Promise { throw new MoneroError("monero-wallet-rpc does not support getting the chain height"); } - async getHeightByDate(year, month, day) { + async getHeightByDate(year: number, month: number, day: number): Promise { throw new MoneroError("monero-wallet-rpc does not support getting a height by date"); } - async sync(startHeight, onProgress) { - assert(onProgress === undefined, "Monero Wallet RPC does not support reporting sync progress"); + async sync(listenerOrStartHeight?: MoneroWalletListener | number, startHeight?: number): Promise { + assert(!(listenerOrStartHeight instanceof MoneroWalletListener), "Monero Wallet RPC does not support reporting sync progress"); try { - let resp = await this.rpc.sendJsonRequest("refresh", {start_height: startHeight}, 0); - await this._poll(); + let resp = await this.config.getServer().sendJsonRequest("refresh", {start_height: startHeight}, 0); + await this.poll(); return new MoneroSyncResult(resp.result.blocks_fetched, resp.result.received_money); - } catch (err) { + } catch (err: any) { if (err.message === "no connection to daemon") throw new MoneroError("Wallet is not connected to daemon"); throw err; } } - async startSyncing(syncPeriodInMs) { + async startSyncing(syncPeriodInMs?: number): Promise { // convert ms to seconds for rpc parameter let syncPeriodInSeconds = Math.round((syncPeriodInMs === undefined ? MoneroWalletRpc.DEFAULT_SYNC_PERIOD_IN_MS : syncPeriodInMs) / 1000); // send rpc request - await this.rpc.sendJsonRequest("auto_refresh", { + await this.config.getServer().sendJsonRequest("auto_refresh", { enable: true, period: syncPeriodInSeconds }); @@ -610,45 +519,49 @@ class MoneroWalletRpc extends MoneroWallet { if (this.walletPoller !== undefined) this.walletPoller.setPeriodInMs(syncPeriodInMs); // poll if listening - await this._poll(); + await this.poll(); + } + + getSyncPeriodInMs(): number { + return this.syncPeriodInMs; } - async stopSyncing() { - return this.rpc.sendJsonRequest("auto_refresh", { enable: false }); + async stopSyncing(): Promise { + return this.config.getServer().sendJsonRequest("auto_refresh", { enable: false }); } - async scanTxs(txHashes) { + async scanTxs(txHashes: string[]): Promise { if (!txHashes || !txHashes.length) throw new MoneroError("No tx hashes given to scan"); - await this.rpc.sendJsonRequest("scan_tx", {txids: txHashes}); - await this._poll(); + await this.config.getServer().sendJsonRequest("scan_tx", {txids: txHashes}); + await this.poll(); } - async rescanSpent() { - await this.rpc.sendJsonRequest("rescan_spent", undefined, 0); + async rescanSpent(): Promise { + await this.config.getServer().sendJsonRequest("rescan_spent", undefined, 0); } - async rescanBlockchain() { - await this.rpc.sendJsonRequest("rescan_blockchain", undefined, 0); + async rescanBlockchain(): Promise { + await this.config.getServer().sendJsonRequest("rescan_blockchain", undefined, 0); } - async getBalance(accountIdx, subaddressIdx) { - return (await this._getBalances(accountIdx, subaddressIdx))[0]; + async getBalance(accountIdx?: number, subaddressIdx?: number): Promise { + return (await this.getBalances(accountIdx, subaddressIdx))[0]; } - async getUnlockedBalance(accountIdx, subaddressIdx) { - return (await this._getBalances(accountIdx, subaddressIdx))[1]; + async getUnlockedBalance(accountIdx?: number, subaddressIdx?: number): Promise { + return (await this.getBalances(accountIdx, subaddressIdx))[1]; } - async getAccounts(includeSubaddresses, tag, skipBalances) { + async getAccounts(includeSubaddresses?: boolean, tag?: string, skipBalances?: boolean): Promise { // fetch accounts from rpc - let resp = await this.rpc.sendJsonRequest("get_accounts", {tag: tag}); + let resp = await this.config.getServer().sendJsonRequest("get_accounts", {tag: tag}); // build account objects and fetch subaddresses per account using get_address // TODO monero-wallet-rpc: get_address should support all_accounts so not called once per account - let accounts = []; + let accounts: MoneroAccount[] = []; for (let rpcAccount of resp.result.subaddress_accounts) { - let account = MoneroWalletRpc._convertRpcAccount(rpcAccount); + let account = MoneroWalletRpc.convertRpcAccount(rpcAccount); if (includeSubaddresses) account.setSubaddresses(await this.getSubaddresses(account.getIndex(), undefined, true)); accounts.push(account); } @@ -659,18 +572,18 @@ class MoneroWalletRpc extends MoneroWallet { // these fields are not initialized if subaddress is unused and therefore not returned from `get_balance` for (let account of accounts) { for (let subaddress of account.getSubaddresses()) { - subaddress.setBalance(new BigInteger(0)); - subaddress.setUnlockedBalance(new BigInteger(0)); + subaddress.setBalance(BigInt(0)); + subaddress.setUnlockedBalance(BigInt(0)); subaddress.setNumUnspentOutputs(0); subaddress.setNumBlocksToUnlock(0); } } // fetch and merge info from get_balance - resp = await this.rpc.sendJsonRequest("get_balance", {all_accounts: true}); + resp = await this.config.getServer().sendJsonRequest("get_balance", {all_accounts: true}); if (resp.result.per_subaddress) { for (let rpcSubaddress of resp.result.per_subaddress) { - let subaddress = MoneroWalletRpc._convertRpcSubaddress(rpcSubaddress); + let subaddress = MoneroWalletRpc.convertRpcSubaddress(rpcSubaddress); // merge info let account = accounts[subaddress.getAccountIndex()]; @@ -684,12 +597,11 @@ class MoneroWalletRpc extends MoneroWallet { } } - // return accounts return accounts; } // TODO: getAccountByIndex(), getAccountByTag() - async getAccount(accountIdx, includeSubaddresses, skipBalances) { + async getAccount(accountIdx: number, includeSubaddresses?: boolean, skipBalances?: boolean): Promise { assert(accountIdx >= 0); for (let account of await this.getAccounts()) { if (account.getIndex() === accountIdx) { @@ -697,27 +609,33 @@ class MoneroWalletRpc extends MoneroWallet { return account; } } - throw new Exception("Account with index " + accountIdx + " does not exist"); + throw new Error("Account with index " + accountIdx + " does not exist"); } - async createAccount(label) { + async createAccount(label?: string): Promise { label = label ? label : undefined; - let resp = await this.rpc.sendJsonRequest("create_account", {label: label}); - return new MoneroAccount(resp.result.account_index, resp.result.address, new BigInteger(0), new BigInteger(0)); + let resp = await this.config.getServer().sendJsonRequest("create_account", {label: label}); + return new MoneroAccount({ + index: resp.result.account_index, + primaryAddress: resp.result.address, + label: label, + balance: BigInt(0), + unlockedBalance: BigInt(0) + }); } - async getSubaddresses(accountIdx, subaddressIndices, skipBalances) { + async getSubaddresses(accountIdx: number, subaddressIndices?: number[], skipBalances?: boolean): Promise { // fetch subaddresses - let params = {}; + let params: any = {}; params.account_index = accountIdx; if (subaddressIndices) params.address_index = GenUtils.listify(subaddressIndices); - let resp = await this.rpc.sendJsonRequest("get_address", params); + let resp = await this.config.getServer().sendJsonRequest("get_address", params); // initialize subaddresses let subaddresses = []; for (let rpcSubaddress of resp.result.addresses) { - let subaddress = MoneroWalletRpc._convertRpcSubaddress(rpcSubaddress); + let subaddress = MoneroWalletRpc.convertRpcSubaddress(rpcSubaddress); subaddress.setAccountIndex(accountIdx); subaddresses.push(subaddress); } @@ -727,17 +645,17 @@ class MoneroWalletRpc extends MoneroWallet { // these fields are not initialized if subaddress is unused and therefore not returned from `get_balance` for (let subaddress of subaddresses) { - subaddress.setBalance(new BigInteger(0)); - subaddress.setUnlockedBalance(new BigInteger(0)); + subaddress.setBalance(BigInt(0)); + subaddress.setUnlockedBalance(BigInt(0)); subaddress.setNumUnspentOutputs(0); subaddress.setNumBlocksToUnlock(0); } // fetch and initialize balances - resp = await this.rpc.sendJsonRequest("get_balance", params); + resp = await this.config.getServer().sendJsonRequest("get_balance", params); if (resp.result.per_subaddress) { for (let rpcSubaddress of resp.result.per_subaddress) { - let subaddress = MoneroWalletRpc._convertRpcSubaddress(rpcSubaddress); + let subaddress = MoneroWalletRpc.convertRpcSubaddress(rpcSubaddress); // transfer info to existing subaddress object for (let tgtSubaddress of subaddresses) { @@ -765,16 +683,16 @@ class MoneroWalletRpc extends MoneroWallet { return subaddresses; } - async getSubaddress(accountIdx, subaddressIdx, skipBalances) { + async getSubaddress(accountIdx: number, subaddressIdx: number, skipBalances?: boolean): Promise { assert(accountIdx >= 0); assert(subaddressIdx >= 0); - return (await this.getSubaddresses(accountIdx, subaddressIdx, skipBalances))[0]; + return (await this.getSubaddresses(accountIdx, [subaddressIdx], skipBalances))[0]; } - async createSubaddress(accountIdx, label) { + async createSubaddress(accountIdx: number, label?: string): Promise { // send request - let resp = await this.rpc.sendJsonRequest("create_address", {account_index: accountIdx, label: label}); + let resp = await this.config.getServer().sendJsonRequest("create_address", {account_index: accountIdx, label: label}); // build subaddress object let subaddress = new MoneroSubaddress(); @@ -782,33 +700,33 @@ class MoneroWalletRpc extends MoneroWallet { subaddress.setIndex(resp.result.address_index); subaddress.setAddress(resp.result.address); subaddress.setLabel(label ? label : undefined); - subaddress.setBalance(new BigInteger(0)); - subaddress.setUnlockedBalance(new BigInteger(0)); + subaddress.setBalance(BigInt(0)); + subaddress.setUnlockedBalance(BigInt(0)); subaddress.setNumUnspentOutputs(0); subaddress.setIsUsed(false); subaddress.setNumBlocksToUnlock(0); return subaddress; } - async setSubaddressLabel(accountIdx, subaddressIdx, label) { - await this.rpc.sendJsonRequest("label_address", {index: {major: accountIdx, minor: subaddressIdx}, label: label}); + async setSubaddressLabel(accountIdx: number, subaddressIdx: number, label: string): Promise { + await this.config.getServer().sendJsonRequest("label_address", {index: {major: accountIdx, minor: subaddressIdx}, label: label}); } - async getTxs(query) { + async getTxs(query?: string[] | Partial): Promise { // copy query - query = MoneroWallet._normalizeTxQuery(query); + const queryNormalized = MoneroWallet.normalizeTxQuery(query); // temporarily disable transfer and output queries in order to collect all tx information - let transferQuery = query.getTransferQuery(); - let inputQuery = query.getInputQuery(); - let outputQuery = query.getOutputQuery(); - query.setTransferQuery(undefined); - query.setInputQuery(undefined); - query.setOutputQuery(undefined); + let transferQuery = queryNormalized.getTransferQuery(); + let inputQuery = queryNormalized.getInputQuery(); + let outputQuery = queryNormalized.getOutputQuery(); + queryNormalized.setTransferQuery(undefined); + queryNormalized.setInputQuery(undefined); + queryNormalized.setOutputQuery(undefined); // fetch all transfers that meet tx query - let transfers = await this._getTransfersAux(new MoneroTransferQuery().setTxQuery(MoneroWalletRpc._decontextualize(query.copy()))); + let transfers = await this.getTransfersAux(new MoneroTransferQuery().setTxQuery(MoneroWalletRpc.decontextualize(queryNormalized.copy()))); // collect unique txs from transfers while retaining order let txs = []; @@ -824,70 +742,70 @@ class MoneroWalletRpc extends MoneroWallet { let txMap = {}; let blockMap = {}; for (let tx of txs) { - MoneroWalletRpc._mergeTx(tx, txMap, blockMap); + MoneroWalletRpc.mergeTx(tx, txMap, blockMap); } // fetch and merge outputs if requested - if (query.getIncludeOutputs() || outputQuery) { + if (queryNormalized.getIncludeOutputs() || outputQuery) { // fetch outputs - let outputQueryAux = (outputQuery ? outputQuery.copy() : new MoneroOutputQuery()).setTxQuery(MoneroWalletRpc._decontextualize(query.copy())); - let outputs = await this._getOutputsAux(outputQueryAux); + let outputQueryAux = (outputQuery ? outputQuery.copy() : new MoneroOutputQuery()).setTxQuery(MoneroWalletRpc.decontextualize(queryNormalized.copy())); + let outputs = await this.getOutputsAux(outputQueryAux); // merge output txs one time while retaining order let outputTxs = []; for (let output of outputs) { if (!outputTxs.includes(output.getTx())) { - MoneroWalletRpc._mergeTx(output.getTx(), txMap, blockMap); + MoneroWalletRpc.mergeTx(output.getTx(), txMap, blockMap); outputTxs.push(output.getTx()); } } } // restore transfer and output queries - query.setTransferQuery(transferQuery); - query.setInputQuery(inputQuery); - query.setOutputQuery(outputQuery); + queryNormalized.setTransferQuery(transferQuery); + queryNormalized.setInputQuery(inputQuery); + queryNormalized.setOutputQuery(outputQuery); // filter txs that don't meet transfer query let txsQueried = []; for (let tx of txs) { - if (query.meetsCriteria(tx)) txsQueried.push(tx); + if (queryNormalized.meetsCriteria(tx)) txsQueried.push(tx); else if (tx.getBlock() !== undefined) tx.getBlock().getTxs().splice(tx.getBlock().getTxs().indexOf(tx), 1); } txs = txsQueried; // special case: re-fetch txs if inconsistency caused by needing to make multiple rpc calls for (let tx of txs) { - if (tx.isConfirmed() && tx.getBlock() === undefined || !tx.isConfirmed() && tx.getBlock() !== undefined) { + if (tx.getIsConfirmed() && tx.getBlock() === undefined || !tx.getIsConfirmed() && tx.getBlock() !== undefined) { console.error("Inconsistency detected building txs from multiple rpc calls, re-fetching txs"); - return this.getTxs(query); + return this.getTxs(queryNormalized); } } // order txs if tx hashes given then return - if (query.getHashes() && query.getHashes().length > 0) { + if (queryNormalized.getHashes() && queryNormalized.getHashes().length > 0) { let txsById = new Map() // store txs in temporary map for sorting for (let tx of txs) txsById.set(tx.getHash(), tx); let orderedTxs = []; - for (let hash of query.getHashes()) if (txsById.get(hash)) orderedTxs.push(txsById.get(hash)); + for (let hash of queryNormalized.getHashes()) if (txsById.get(hash)) orderedTxs.push(txsById.get(hash)); txs = orderedTxs; } return txs; } - async getTransfers(query) { + async getTransfers(query?: Partial): Promise { // copy and normalize query up to block - query = MoneroWallet._normalizeTransferQuery(query); + const queryNormalized = MoneroWallet.normalizeTransferQuery(query); // get transfers directly if query does not require tx context (other transfers, outputs) - if (!MoneroWalletRpc._isContextual(query)) return this._getTransfersAux(query); + if (!MoneroWalletRpc.isContextual(queryNormalized)) return this.getTransfersAux(queryNormalized); // otherwise get txs with full models to fulfill query let transfers = []; - for (let tx of await this.getTxs(query.getTxQuery())) { - for (let transfer of tx.filterTransfers(query)) { + for (let tx of await this.getTxs(queryNormalized.getTxQuery())) { + for (let transfer of tx.filterTransfers(queryNormalized)) { transfers.push(transfer); } } @@ -895,18 +813,18 @@ class MoneroWalletRpc extends MoneroWallet { return transfers; } - async getOutputs(query) { + async getOutputs(query?: Partial): Promise { // copy and normalize query up to block - query = MoneroWallet._normalizeOutputQuery(query); + const queryNormalized = MoneroWallet.normalizeOutputQuery(query); // get outputs directly if query does not require tx context (other outputs, transfers) - if (!MoneroWalletRpc._isContextual(query)) return this._getOutputsAux(query); + if (!MoneroWalletRpc.isContextual(queryNormalized)) return this.getOutputsAux(queryNormalized); // otherwise get txs with full models to fulfill query let outputs = []; - for (let tx of await this.getTxs(query.getTxQuery())) { - for (let output of tx.filterOutputs(query)) { + for (let tx of await this.getTxs(queryNormalized.getTxQuery())) { + for (let output of tx.filterOutputs(queryNormalized)) { outputs.push(output); } } @@ -914,128 +832,128 @@ class MoneroWalletRpc extends MoneroWallet { return outputs; } - async exportOutputs(all) { - return (await this.rpc.sendJsonRequest("export_outputs", {all: all})).result.outputs_data_hex; + async exportOutputs(all = false): Promise { + return (await this.config.getServer().sendJsonRequest("export_outputs", {all: all})).result.outputs_data_hex; } - async importOutputs(outputsHex) { - let resp = await this.rpc.sendJsonRequest("import_outputs", {outputs_data_hex: outputsHex}); + async importOutputs(outputsHex: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("import_outputs", {outputs_data_hex: outputsHex}); return resp.result.num_imported; } - async exportKeyImages(all) { - return await this._rpcExportKeyImages(all); + async exportKeyImages(all = false): Promise { + return await this.rpcExportKeyImages(all); } - async importKeyImages(keyImages) { + async importKeyImages(keyImages: MoneroKeyImage[]): Promise { // convert key images to rpc parameter let rpcKeyImages = keyImages.map(keyImage => ({key_image: keyImage.getHex(), signature: keyImage.getSignature()})); // send request - let resp = await this.rpc.sendJsonRequest("import_key_images", {signed_key_images: rpcKeyImages}); + let resp = await this.config.getServer().sendJsonRequest("import_key_images", {signed_key_images: rpcKeyImages}); // build and return result let importResult = new MoneroKeyImageImportResult(); importResult.setHeight(resp.result.height); - importResult.setSpentAmount(new BigInteger(resp.result.spent)); - importResult.setUnspentAmount(new BigInteger(resp.result.unspent)); + importResult.setSpentAmount(BigInt(resp.result.spent)); + importResult.setUnspentAmount(BigInt(resp.result.unspent)); return importResult; } - async getNewKeyImagesFromLastImport() { - return await this._rpcExportKeyImages(false); + async getNewKeyImagesFromLastImport(): Promise { + return await this.rpcExportKeyImages(false); } - async freezeOutput(keyImage) { - return this.rpc.sendJsonRequest("freeze", {key_image: keyImage}); + async freezeOutput(keyImage: string): Promise { + return this.config.getServer().sendJsonRequest("freeze", {key_image: keyImage}); } - async thawOutput(keyImage) { - return this.rpc.sendJsonRequest("thaw", {key_image: keyImage}); + async thawOutput(keyImage: string): Promise { + return this.config.getServer().sendJsonRequest("thaw", {key_image: keyImage}); } - async isOutputFrozen(keyImage) { - let resp = await this.rpc.sendJsonRequest("frozen", {key_image: keyImage}); + async isOutputFrozen(keyImage: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("frozen", {key_image: keyImage}); return resp.result.frozen === true; } - async createTxs(config) { + async createTxs(config: Partial): Promise { // validate, copy, and normalize config - config = MoneroWallet._normalizeCreateTxsConfig(config); - if (config.getCanSplit() === undefined) config.setCanSplit(true); - if (config.getRelay() === true && await this.isMultisig()) throw new MoneroError("Cannot relay multisig transaction until co-signed"); + const configNormalized = MoneroWallet.normalizeCreateTxsConfig(config); + if (configNormalized.getCanSplit() === undefined) configNormalized.setCanSplit(true); + if (configNormalized.getRelay() === true && await this.isMultisig()) throw new MoneroError("Cannot relay multisig transaction until co-signed"); // determine account and subaddresses to send from - let accountIdx = config.getAccountIndex(); + let accountIdx = configNormalized.getAccountIndex(); if (accountIdx === undefined) throw new MoneroError("Must provide the account index to send from"); - let subaddressIndices = config.getSubaddressIndices() === undefined ? undefined : config.getSubaddressIndices().slice(0); // fetch all or copy given indices + let subaddressIndices = configNormalized.getSubaddressIndices() === undefined ? undefined : configNormalized.getSubaddressIndices().slice(0); // fetch all or copy given indices // build config parameters - let params = {}; + let params: any = {}; params.destinations = []; - for (let destination of config.getDestinations()) { + for (let destination of configNormalized.getDestinations()) { assert(destination.getAddress(), "Destination address is not defined"); assert(destination.getAmount(), "Destination amount is not defined"); params.destinations.push({ address: destination.getAddress(), amount: destination.getAmount().toString() }); } - if (config.getSubtractFeeFrom()) params.subtract_fee_from_outputs = config.getSubtractFeeFrom(); + if (configNormalized.getSubtractFeeFrom()) params.subtract_fee_from_outputs = configNormalized.getSubtractFeeFrom(); params.account_index = accountIdx; params.subaddr_indices = subaddressIndices; - params.payment_id = config.getPaymentId(); - if (config.getUnlockTime() !== undefined) params.unlock_time = config.getUnlockTime().toString() - params.do_not_relay = config.getRelay() !== true; - assert(config.getPriority() === undefined || config.getPriority() >= 0 && config.getPriority() <= 3); - params.priority = config.getPriority(); + params.payment_id = configNormalized.getPaymentId(); + if (configNormalized.getUnlockTime() !== undefined) params.unlock_time = configNormalized.getUnlockTime().toString() + params.do_not_relay = configNormalized.getRelay() !== true; + assert(configNormalized.getPriority() === undefined || configNormalized.getPriority() >= 0 && configNormalized.getPriority() <= 3); + params.priority = configNormalized.getPriority(); params.get_tx_hex = true; params.get_tx_metadata = true; - if (config.getCanSplit()) params.get_tx_keys = true; // param to get tx key(s) depends if split + if (configNormalized.getCanSplit()) params.get_tx_keys = true; // param to get tx key(s) depends if split else params.get_tx_key = true; // cannot apply subtractFeeFrom with `transfer_split` call - if (config.getCanSplit() && config.getSubtractFeeFrom() && config.getSubtractFeeFrom().length > 0) { + if (configNormalized.getCanSplit() && configNormalized.getSubtractFeeFrom() && configNormalized.getSubtractFeeFrom().length > 0) { throw new MoneroError("subtractfeefrom transfers cannot be split over multiple transactions yet"); } // send request let result; try { - let resp = await this.rpc.sendJsonRequest(config.getCanSplit() ? "transfer_split" : "transfer", params); + let resp = await this.config.getServer().sendJsonRequest(configNormalized.getCanSplit() ? "transfer_split" : "transfer", params); result = resp.result; - } catch (err) { + } catch (err: any) { if (err.message.indexOf("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS") > -1) throw new MoneroError("Invalid destination address"); throw err; } // pre-initialize txs iff present. multisig and view-only wallets will have tx set without transactions let txs; - let numTxs = config.getCanSplit() ? (result.fee_list !== undefined ? result.fee_list.length : 0) : (result.fee !== undefined ? 1 : 0); + let numTxs = configNormalized.getCanSplit() ? (result.fee_list !== undefined ? result.fee_list.length : 0) : (result.fee !== undefined ? 1 : 0); if (numTxs > 0) txs = []; let copyDestinations = numTxs === 1; for (let i = 0; i < numTxs; i++) { let tx = new MoneroTxWallet(); - MoneroWalletRpc._initSentTxWallet(config, tx, copyDestinations); + MoneroWalletRpc.initSentTxWallet(configNormalized, tx, copyDestinations); tx.getOutgoingTransfer().setAccountIndex(accountIdx); if (subaddressIndices !== undefined && subaddressIndices.length === 1) tx.getOutgoingTransfer().setSubaddressIndices(subaddressIndices); txs.push(tx); } // notify of changes - if (config.getRelay()) await this._poll(); + if (configNormalized.getRelay()) await this.poll(); // initialize tx set from rpc response with pre-initialized txs - if (config.getCanSplit()) return MoneroWalletRpc._convertRpcSentTxsToTxSet(result, txs, config).getTxs(); - else return MoneroWalletRpc._convertRpcTxToTxSet(result, txs === undefined ? undefined : txs[0], true, config).getTxs(); + if (configNormalized.getCanSplit()) return MoneroWalletRpc.convertRpcSentTxsToTxSet(result, txs, configNormalized).getTxs(); + else return MoneroWalletRpc.convertRpcTxToTxSet(result, txs === undefined ? undefined : txs[0], true, configNormalized).getTxs(); } - async sweepOutput(config) { + async sweepOutput(config: Partial): Promise { // normalize and validate config - config = MoneroWallet._normalizeSweepOutputConfig(config); + config = MoneroWallet.normalizeSweepOutputConfig(config); // build request parameters - let params = {}; + let params: any = {}; params.address = config.getDestinations()[0].getAddress(); params.account_index = config.getAccountIndex(); params.subaddr_indices = config.getSubaddressIndices(); @@ -1050,44 +968,44 @@ class MoneroWalletRpc extends MoneroWallet { params.get_tx_metadata = true; // send request - let resp = await this.rpc.sendJsonRequest("sweep_single", params); + let resp = await this.config.getServer().sendJsonRequest("sweep_single", params); let result = resp.result; // notify of changes - if (config.getRelay()) await this._poll(); + if (config.getRelay()) await this.poll(); // build and return tx - let tx = MoneroWalletRpc._initSentTxWallet(config, undefined, true); - MoneroWalletRpc._convertRpcTxToTxSet(result, tx, true, config); + let tx = MoneroWalletRpc.initSentTxWallet(config, undefined, true); + MoneroWalletRpc.convertRpcTxToTxSet(result, tx, true, config); tx.getOutgoingTransfer().getDestinations()[0].setAmount(tx.getOutgoingTransfer().getAmount()); // initialize destination amount return tx; } - async sweepUnlocked(config) { + async sweepUnlocked(config: Partial): Promise { // validate and normalize config - config = MoneroWallet._normalizeSweepUnlockedConfig(config); + const configNormalized = MoneroWallet.normalizeSweepUnlockedConfig(config); // determine account and subaddress indices to sweep; default to all with unlocked balance if not specified let indices = new Map(); // maps each account index to subaddress indices to sweep - if (config.getAccountIndex() !== undefined) { - if (config.getSubaddressIndices() !== undefined) { - indices.set(config.getAccountIndex(), config.getSubaddressIndices()); + if (configNormalized.getAccountIndex() !== undefined) { + if (configNormalized.getSubaddressIndices() !== undefined) { + indices.set(configNormalized.getAccountIndex(), configNormalized.getSubaddressIndices()); } else { let subaddressIndices = []; - indices.set(config.getAccountIndex(), subaddressIndices); - for (let subaddress of await this.getSubaddresses(config.getAccountIndex())) { - if (subaddress.getUnlockedBalance().compare(new BigInteger(0)) > 0) subaddressIndices.push(subaddress.getIndex()); + indices.set(configNormalized.getAccountIndex(), subaddressIndices); + for (let subaddress of await this.getSubaddresses(configNormalized.getAccountIndex())) { + if (subaddress.getUnlockedBalance() > 0n) subaddressIndices.push(subaddress.getIndex()); } } } else { let accounts = await this.getAccounts(true); for (let account of accounts) { - if (account.getUnlockedBalance().compare(new BigInteger(0)) > 0) { + if (account.getUnlockedBalance() > 0n) { let subaddressIndices = []; indices.set(account.getIndex(), subaddressIndices); for (let subaddress of account.getSubaddresses()) { - if (subaddress.getUnlockedBalance().compare(new BigInteger(0)) > 0) subaddressIndices.push(subaddress.getIndex()); + if (subaddress.getUnlockedBalance() > 0n) subaddressIndices.push(subaddress.getIndex()); } } } @@ -1098,83 +1016,83 @@ class MoneroWalletRpc extends MoneroWallet { for (let accountIdx of indices.keys()) { // copy and modify the original config - let copy = config.copy(); + let copy = configNormalized.copy(); copy.setAccountIndex(accountIdx); copy.setSweepEachSubaddress(false); // sweep all subaddresses together // TODO monero-project: can this reveal outputs belong to the same wallet? if (copy.getSweepEachSubaddress() !== true) { copy.setSubaddressIndices(indices.get(accountIdx)); - for (let tx of await this._rpcSweepAccount(copy)) txs.push(tx); + for (let tx of await this.rpcSweepAccount(copy)) txs.push(tx); } // otherwise sweep each subaddress individually else { for (let subaddressIdx of indices.get(accountIdx)) { copy.setSubaddressIndices([subaddressIdx]); - for (let tx of await this._rpcSweepAccount(copy)) txs.push(tx); + for (let tx of await this.rpcSweepAccount(copy)) txs.push(tx); } } } // notify of changes - if (config.getRelay()) await this._poll(); + if (configNormalized.getRelay()) await this.poll(); return txs; } - async sweepDust(relay) { + async sweepDust(relay?: boolean): Promise { if (relay === undefined) relay = false; - let resp = await this.rpc.sendJsonRequest("sweep_dust", {do_not_relay: !relay}); - if (relay) await this._poll(); + let resp = await this.config.getServer().sendJsonRequest("sweep_dust", {do_not_relay: !relay}); + if (relay) await this.poll(); let result = resp.result; - let txSet = MoneroWalletRpc._convertRpcSentTxsToTxSet(result); + let txSet = MoneroWalletRpc.convertRpcSentTxsToTxSet(result); if (txSet.getTxs() === undefined) return []; for (let tx of txSet.getTxs()) { tx.setIsRelayed(!relay); - tx.setInTxPool(tx.isRelayed()); + tx.setInTxPool(tx.getIsRelayed()); } return txSet.getTxs(); } - async relayTxs(txsOrMetadatas) { + async relayTxs(txsOrMetadatas: (MoneroTxWallet | string)[]): Promise { assert(Array.isArray(txsOrMetadatas), "Must provide an array of txs or their metadata to relay"); let txHashes = []; for (let txOrMetadata of txsOrMetadatas) { let metadata = txOrMetadata instanceof MoneroTxWallet ? txOrMetadata.getMetadata() : txOrMetadata; - let resp = await this.rpc.sendJsonRequest("relay_tx", { hex: metadata }); + let resp = await this.config.getServer().sendJsonRequest("relay_tx", { hex: metadata }); txHashes.push(resp.result.tx_hash); } - await this._poll(); // notify of changes + await this.poll(); // notify of changes return txHashes; } - async describeTxSet(txSet) { - let resp = await this.rpc.sendJsonRequest("describe_transfer", { + async describeTxSet(txSet: MoneroTxSet): Promise { + let resp = await this.config.getServer().sendJsonRequest("describe_transfer", { unsigned_txset: txSet.getUnsignedTxHex(), multisig_txset: txSet.getMultisigTxHex() }); - return MoneroWalletRpc._convertRpcDescribeTransfer(resp.result); + return MoneroWalletRpc.convertRpcDescribeTransfer(resp.result); } - async signTxs(unsignedTxHex) { - let resp = await this.rpc.sendJsonRequest("sign_transfer", { + async signTxs(unsignedTxHex: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("sign_transfer", { unsigned_txset: unsignedTxHex, export_raw: false }); - await this._poll(); + await this.poll(); return resp.result.signed_txset } - async submitTxs(signedTxHex) { - let resp = await this.rpc.sendJsonRequest("submit_transfer", { + async submitTxs(signedTxHex: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("submit_transfer", { tx_data_hex: signedTxHex }); - await this._poll(); + await this.poll(); return resp.result.tx_hash_list; } - async signMessage(message, signatureType, accountIdx, subaddressIdx) { - let resp = await this.rpc.sendJsonRequest("sign", { + async signMessage(message: string, signatureType = MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY, accountIdx = 0, subaddressIdx = 0): Promise { + let resp = await this.config.getServer().sendJsonRequest("sign", { data: message, signature_type: signatureType === MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY ? "spend" : "view", account_index: accountIdx, @@ -1183,64 +1101,62 @@ class MoneroWalletRpc extends MoneroWallet { return resp.result.signature; } - async verifyMessage(message, address, signature) { + async verifyMessage(message: string, address: string, signature: string): Promise { try { - let resp = await this.rpc.sendJsonRequest("verify", {data: message, address: address, signature: signature}); - let result = new MoneroMessageSignatureResult( - resp.result.good, - !resp.result.good ? undefined : resp.result.old, - !resp.result.good ? undefined : !resp.result.signature_type ? undefined : resp.result.signature_type === "view" ? MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY : MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY, - !resp.result.good ? undefined : resp.result.version); - return result; - } catch (e) { - if (e.getCode() === -2) return new MoneroMessageSignatureResult(false); + let resp = await this.config.getServer().sendJsonRequest("verify", {data: message, address: address, signature: signature}); + let result = resp.result; + return new MoneroMessageSignatureResult( + result.good ? {isGood: result.good, isOld: result.old, signatureType: result.signature_type === "view" ? MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY : MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY, version: result.version} : {isGood: false} + ); + } catch (e: any) { + if (e.getCode() === -2) return new MoneroMessageSignatureResult({isGood: false}); throw e; } } - async getTxKey(txHash) { + async getTxKey(txHash: string): Promise { try { - return (await this.rpc.sendJsonRequest("get_tx_key", {txid: txHash})).result.tx_key; - } catch (e) { + return (await this.config.getServer().sendJsonRequest("get_tx_key", {txid: txHash})).result.tx_key; + } catch (e: any) { if (e instanceof MoneroRpcError && e.getCode() === -8 && e.message.includes("TX ID has invalid format")) e = new MoneroRpcError("TX hash has invalid format", e.getCode(), e.getRpcMethod(), e.getRpcParams()); // normalize error message throw e; } } - async checkTxKey(txHash, txKey, address) { + async checkTxKey(txHash: string, txKey: string, address: string): Promise { try { // send request - let resp = await this.rpc.sendJsonRequest("check_tx_key", {txid: txHash, tx_key: txKey, address: address}); + let resp = await this.config.getServer().sendJsonRequest("check_tx_key", {txid: txHash, tx_key: txKey, address: address}); // interpret result let check = new MoneroCheckTx(); check.setIsGood(true); check.setNumConfirmations(resp.result.confirmations); check.setInTxPool(resp.result.in_pool); - check.setReceivedAmount(new BigInteger(resp.result.received)); + check.setReceivedAmount(BigInt(resp.result.received)); return check; - } catch (e) { + } catch (e: any) { if (e instanceof MoneroRpcError && e.getCode() === -8 && e.message.includes("TX ID has invalid format")) e = new MoneroRpcError("TX hash has invalid format", e.getCode(), e.getRpcMethod(), e.getRpcParams()); // normalize error message throw e; } } - async getTxProof(txHash, address, message) { + async getTxProof(txHash: string, address: string, message?: string): Promise { try { - let resp = await this.rpc.sendJsonRequest("get_tx_proof", {txid: txHash, address: address, message: message}); + let resp = await this.config.getServer().sendJsonRequest("get_tx_proof", {txid: txHash, address: address, message: message}); return resp.result.signature; - } catch (e) { + } catch (e: any) { if (e instanceof MoneroRpcError && e.getCode() === -8 && e.message.includes("TX ID has invalid format")) e = new MoneroRpcError("TX hash has invalid format", e.getCode(), e.getRpcMethod(), e.getRpcParams()); // normalize error message throw e; } } - async checkTxProof(txHash, address, message, signature) { + async checkTxProof(txHash: string, address: string, message: string | undefined, signature: string): Promise { try { // send request - let resp = await this.rpc.sendJsonRequest("check_tx_proof", { + let resp = await this.config.getServer().sendJsonRequest("check_tx_proof", { txid: txHash, address: address, message: message, @@ -1254,50 +1170,50 @@ class MoneroWalletRpc extends MoneroWallet { if (isGood) { check.setNumConfirmations(resp.result.confirmations); check.setInTxPool(resp.result.in_pool); - check.setReceivedAmount(new BigInteger(resp.result.received)); + check.setReceivedAmount(BigInt(resp.result.received)); } return check; - } catch (e) { + } catch (e: any) { if (e instanceof MoneroRpcError && e.getCode() === -1 && e.message === "basic_string") e = new MoneroRpcError("Must provide signature to check tx proof", -1); if (e instanceof MoneroRpcError && e.getCode() === -8 && e.message.includes("TX ID has invalid format")) e = new MoneroRpcError("TX hash has invalid format", e.getCode(), e.getRpcMethod(), e.getRpcParams()); throw e; } } - async getSpendProof(txHash, message) { + async getSpendProof(txHash: string, message?: string): Promise { try { - let resp = await this.rpc.sendJsonRequest("get_spend_proof", {txid: txHash, message: message}); + let resp = await this.config.getServer().sendJsonRequest("get_spend_proof", {txid: txHash, message: message}); return resp.result.signature; - } catch (e) { + } catch (e: any) { if (e instanceof MoneroRpcError && e.getCode() === -8 && e.message.includes("TX ID has invalid format")) e = new MoneroRpcError("TX hash has invalid format", e.getCode(), e.getRpcMethod(), e.getRpcParams()); // normalize error message throw e; } } - async checkSpendProof(txHash, message, signature) { + async checkSpendProof(txHash: string, message: string | undefined, signature: string): Promise { try { - let resp = await this.rpc.sendJsonRequest("check_spend_proof", { + let resp = await this.config.getServer().sendJsonRequest("check_spend_proof", { txid: txHash, message: message, signature: signature }); return resp.result.good; - } catch (e) { + } catch (e: any) { if (e instanceof MoneroRpcError && e.getCode() === -8 && e.message.includes("TX ID has invalid format")) e = new MoneroRpcError("TX hash has invalid format", e.getCode(), e.getRpcMethod(), e.getRpcParams()); // normalize error message throw e; } } - async getReserveProofWallet(message) { - let resp = await this.rpc.sendJsonRequest("get_reserve_proof", { + async getReserveProofWallet(message?: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("get_reserve_proof", { all: true, message: message }); return resp.result.signature; } - async getReserveProofAccount(accountIdx, amount, message) { - let resp = await this.rpc.sendJsonRequest("get_reserve_proof", { + async getReserveProofAccount(accountIdx: number, amount: bigint, message?: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("get_reserve_proof", { account_index: accountIdx, amount: amount.toString(), message: message @@ -1305,10 +1221,10 @@ class MoneroWalletRpc extends MoneroWallet { return resp.result.signature; } - async checkReserveProof(address, message, signature) { + async checkReserveProof(address: string, message: string | undefined, signature: string): Promise { // send request - let resp = await this.rpc.sendJsonRequest("check_reserve_proof", { + let resp = await this.config.getServer().sendJsonRequest("check_reserve_proof", { address: address, message: message, signature: signature @@ -1319,22 +1235,22 @@ class MoneroWalletRpc extends MoneroWallet { let check = new MoneroCheckReserve(); check.setIsGood(isGood); if (isGood) { - check.setUnconfirmedSpentAmount(new BigInteger(resp.result.spent)); - check.setTotalAmount(new BigInteger(resp.result.total)); + check.setUnconfirmedSpentAmount(BigInt(resp.result.spent)); + check.setTotalAmount(BigInt(resp.result.total)); } return check; } - async getTxNotes(txHashes) { - return (await this.rpc.sendJsonRequest("get_tx_notes", {txids: txHashes})).result.notes; + async getTxNotes(txHashes: string[]): Promise { + return (await this.config.getServer().sendJsonRequest("get_tx_notes", {txids: txHashes})).result.notes; } - async setTxNotes(txHashes, notes) { - await this.rpc.sendJsonRequest("set_tx_notes", {txids: txHashes, notes: notes}); + async setTxNotes(txHashes: string[], notes: string[]): Promise { + await this.config.getServer().sendJsonRequest("set_tx_notes", {txids: txHashes, notes: notes}); } - async getAddressBookEntries(entryIndices) { - let resp = await this.rpc.sendJsonRequest("get_address_book", {entries: entryIndices}); + async getAddressBookEntries(entryIndices?: number[]): Promise { + let resp = await this.config.getServer().sendJsonRequest("get_address_book", {entries: entryIndices}); if (!resp.result.entries) return []; let entries = []; for (let rpcEntry of resp.result.entries) { @@ -1343,13 +1259,13 @@ class MoneroWalletRpc extends MoneroWallet { return entries; } - async addAddressBookEntry(address, description) { - let resp = await this.rpc.sendJsonRequest("add_address_book", {address: address, description: description}); + async addAddressBookEntry(address: string, description?: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("add_address_book", {address: address, description: description}); return resp.result.index; } - async editAddressBookEntry(index, setAddress, address, setDescription, description) { - let resp = await this.rpc.sendJsonRequest("edit_address_book", { + async editAddressBookEntry(index: number, setAddress: boolean, address: string | undefined, setDescription: boolean, description: string | undefined): Promise { + let resp = await this.config.getServer().sendJsonRequest("edit_address_book", { index: index, set_address: setAddress, address: address, @@ -1358,36 +1274,40 @@ class MoneroWalletRpc extends MoneroWallet { }); } - async deleteAddressBookEntry(entryIdx) { - await this.rpc.sendJsonRequest("delete_address_book", {index: entryIdx}); + async deleteAddressBookEntry(entryIdx: number): Promise { + await this.config.getServer().sendJsonRequest("delete_address_book", {index: entryIdx}); } async tagAccounts(tag, accountIndices) { - await this.rpc.sendJsonRequest("tag_accounts", {tag: tag, accounts: accountIndices}); + await this.config.getServer().sendJsonRequest("tag_accounts", {tag: tag, accounts: accountIndices}); } - async untagAccounts(accountIndices) { - await this.rpc.sendJsonRequest("untag_accounts", {accounts: accountIndices}); + async untagAccounts(accountIndices: number[]): Promise { + await this.config.getServer().sendJsonRequest("untag_accounts", {accounts: accountIndices}); } - async getAccountTags() { + async getAccountTags(): Promise { let tags = []; - let resp = await this.rpc.sendJsonRequest("get_account_tags"); + let resp = await this.config.getServer().sendJsonRequest("get_account_tags"); if (resp.result.account_tags) { for (let rpcAccountTag of resp.result.account_tags) { - tags.push(new MoneroAccountTag(rpcAccountTag.tag ? rpcAccountTag.tag : undefined, rpcAccountTag.label ? rpcAccountTag.label : undefined, rpcAccountTag.accounts)); + tags.push(new MoneroAccountTag({ + tag: rpcAccountTag.tag ? rpcAccountTag.tag : undefined, + label: rpcAccountTag.label ? rpcAccountTag.label : undefined, + accountIndices: rpcAccountTag.accounts + })); } } return tags; } - async setAccountTagLabel(tag, label) { - await this.rpc.sendJsonRequest("set_account_tag_description", {tag: tag, description: label}); + async setAccountTagLabel(tag: string, label: string): Promise { + await this.config.getServer().sendJsonRequest("set_account_tag_description", {tag: tag, description: label}); } - async getPaymentUri(config) { - config = MoneroWallet._normalizeCreateTxsConfig(config); - let resp = await this.rpc.sendJsonRequest("make_uri", { + async getPaymentUri(config: MoneroTxConfig): Promise { + config = MoneroWallet.normalizeCreateTxsConfig(config); + let resp = await this.config.getServer().sendJsonRequest("make_uri", { address: config.getDestinations()[0].getAddress(), amount: config.getDestinations()[0].getAmount() ? config.getDestinations()[0].getAmount().toString() : undefined, payment_id: config.getPaymentId(), @@ -1397,10 +1317,10 @@ class MoneroWalletRpc extends MoneroWallet { return resp.result.uri; } - async parsePaymentUri(uri) { + async parsePaymentUri(uri: string): Promise { assert(uri, "Must provide URI to parse"); - let resp = await this.rpc.sendJsonRequest("parse_uri", {uri: uri}); - let config = new MoneroTxConfig({address: resp.result.uri.address, amount: new BigInteger(resp.result.uri.amount)}); + let resp = await this.config.getServer().sendJsonRequest("parse_uri", {uri: uri}); + let config = new MoneroTxConfig({address: resp.result.uri.address, amount: BigInt(resp.result.uri.amount)}); config.setPaymentId(resp.result.uri.payment_id); config.setRecipientName(resp.result.uri.recipient_name); config.setNote(resp.result.uri.tx_description); @@ -1411,39 +1331,39 @@ class MoneroWalletRpc extends MoneroWallet { return config; } - async getAttribute(key) { + async getAttribute(key: string): Promise { try { - let resp = await this.rpc.sendJsonRequest("get_attribute", {key: key}); + let resp = await this.config.getServer().sendJsonRequest("get_attribute", {key: key}); return resp.result.value === "" ? undefined : resp.result.value; - } catch (e) { + } catch (e: any) { if (e instanceof MoneroRpcError && e.getCode() === -45) return undefined; throw e; } } - async setAttribute(key, val) { - await this.rpc.sendJsonRequest("set_attribute", {key: key, value: val}); + async setAttribute(key: string, val: string): Promise { + await this.config.getServer().sendJsonRequest("set_attribute", {key: key, value: val}); } - async startMining(numThreads, backgroundMining, ignoreBattery) { - await this.rpc.sendJsonRequest("start_mining", { + async startMining(numThreads: number, backgroundMining?: boolean, ignoreBattery?: boolean): Promise { + await this.config.getServer().sendJsonRequest("start_mining", { threads_count: numThreads, do_background_mining: backgroundMining, ignore_battery: ignoreBattery }); } - async stopMining() { - await this.rpc.sendJsonRequest("stop_mining"); + async stopMining(): Promise { + await this.config.getServer().sendJsonRequest("stop_mining"); } - async isMultisigImportNeeded() { - let resp = await this.rpc.sendJsonRequest("get_balance"); + async isMultisigImportNeeded(): Promise { + let resp = await this.config.getServer().sendJsonRequest("get_balance"); return resp.result.multisig_import_needed === true; } - async getMultisigInfo() { - let resp = await this.rpc.sendJsonRequest("is_multisig"); + async getMultisigInfo(): Promise { + let resp = await this.config.getServer().sendJsonRequest("is_multisig"); let result = resp.result; let info = new MoneroMultisigInfo(); info.setIsMultisig(result.multisig); @@ -1453,15 +1373,15 @@ class MoneroWalletRpc extends MoneroWallet { return info; } - async prepareMultisig() { - let resp = await this.rpc.sendJsonRequest("prepare_multisig", {enable_multisig_experimental: true}); + async prepareMultisig(): Promise { + let resp = await this.config.getServer().sendJsonRequest("prepare_multisig", {enable_multisig_experimental: true}); this.addressCache = {}; let result = resp.result; return result.multisig_info; } - async makeMultisig(multisigHexes, threshold, password) { - let resp = await this.rpc.sendJsonRequest("make_multisig", { + async makeMultisig(multisigHexes: string[], threshold: number, password: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("make_multisig", { multisig_info: multisigHexes, threshold: threshold, password: password @@ -1470,8 +1390,8 @@ class MoneroWalletRpc extends MoneroWallet { return resp.result.multisig_info; } - async exchangeMultisigKeys(multisigHexes, password) { - let resp = await this.rpc.sendJsonRequest("exchange_multisig_keys", {multisig_info: multisigHexes, password: password}); + async exchangeMultisigKeys(multisigHexes: string[], password: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("exchange_multisig_keys", {multisig_info: multisigHexes, password: password}); this.addressCache = {}; let msResult = new MoneroMultisigInitResult(); msResult.setAddress(resp.result.address); @@ -1481,19 +1401,19 @@ class MoneroWalletRpc extends MoneroWallet { return msResult; } - async exportMultisigHex() { - let resp = await this.rpc.sendJsonRequest("export_multisig_info"); + async exportMultisigHex(): Promise { + let resp = await this.config.getServer().sendJsonRequest("export_multisig_info"); return resp.result.info; } - async importMultisigHex(multisigHexes) { + async importMultisigHex(multisigHexes: string[]): Promise { if (!GenUtils.isArray(multisigHexes)) throw new MoneroError("Must provide string[] to importMultisigHex()") - let resp = await this.rpc.sendJsonRequest("import_multisig_info", {info: multisigHexes}); + let resp = await this.config.getServer().sendJsonRequest("import_multisig_info", {info: multisigHexes}); return resp.result.n_outputs; } - async signMultisigTxHex(multisigTxHex) { - let resp = await this.rpc.sendJsonRequest("sign_multisig", {tx_data_hex: multisigTxHex}); + async signMultisigTxHex(multisigTxHex: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("sign_multisig", {tx_data_hex: multisigTxHex}); let result = resp.result; let signResult = new MoneroMultisigSignResult(); signResult.setSignedMultisigTxHex(result.tx_data_hex); @@ -1501,30 +1421,30 @@ class MoneroWalletRpc extends MoneroWallet { return signResult; } - async submitMultisigTxHex(signedMultisigTxHex) { - let resp = await this.rpc.sendJsonRequest("submit_multisig", {tx_data_hex: signedMultisigTxHex}); + async submitMultisigTxHex(signedMultisigTxHex: string): Promise { + let resp = await this.config.getServer().sendJsonRequest("submit_multisig", {tx_data_hex: signedMultisigTxHex}); return resp.result.tx_hash_list; } - async changePassword(oldPassword, newPassword) { - return this.rpc.sendJsonRequest("change_wallet_password", {old_password: oldPassword || "", new_password: newPassword || ""}); + async changePassword(oldPassword: string, newPassword: string): Promise { + return this.config.getServer().sendJsonRequest("change_wallet_password", {old_password: oldPassword || "", new_password: newPassword || ""}); } - async save() { - await this.rpc.sendJsonRequest("store"); + async save(): Promise { + await this.config.getServer().sendJsonRequest("store"); } - async close(save) { + async close(save = false): Promise { await super.close(save); if (save === undefined) save = false; - await this._clear(); - await this.rpc.sendJsonRequest("close_wallet", {autosave_current: save}); + await this.clear(); + await this.config.getServer().sendJsonRequest("close_wallet", {autosave_current: save}); } - async isClosed() { + async isClosed(): Promise { try { await this.getPrimaryAddress(); - } catch (e) { + } catch (e: any) { return e instanceof MoneroRpcError && e.getCode() === -13 && e.message.indexOf("No wallet file") > -1; } return false; @@ -1532,86 +1452,155 @@ class MoneroWalletRpc extends MoneroWallet { /** * Save and close the current wallet and stop the RPC server. + * + * @return {Promise} */ - async stop() { - await this._clear(); - await this.rpc.sendJsonRequest("stop_wallet"); + async stop(): Promise { + await this.clear(); + await this.config.getServer().sendJsonRequest("stop_wallet"); } // ----------- ADD JSDOC FOR SUPPORTED DEFAULT IMPLEMENTATIONS -------------- - - async getNumBlocksToUnlock() { return super.getNumBlocksToUnlock(...arguments); } - async getTx() { return super.getTx(...arguments); } - async getIncomingTransfers() { return super.getIncomingTransfers(...arguments); } - async getOutgoingTransfers() { return super.getOutgoingTransfers(...arguments); } - async createTx() { return super.createTx(...arguments); } - async relayTx() { return super.relayTx(...arguments); } - async getTxNote() { return super.getTxNote(...arguments); } - async setTxNote() { return super.setTxNote(...arguments); } + + async getNumBlocksToUnlock(): Promise { return super.getNumBlocksToUnlock(); } + async getTx(txHash: string): Promise { return super.getTx(txHash); } + async getIncomingTransfers(query: Partial): Promise { return super.getIncomingTransfers(query); } + async getOutgoingTransfers(query: Partial) { return super.getOutgoingTransfers(query); } + async createTx(config: Partial): Promise { return super.createTx(config); } + async relayTx(txOrMetadata: MoneroTxWallet | string): Promise { return super.relayTx(txOrMetadata); } + async getTxNote(txHash: string): Promise { return super.getTxNote(txHash); } + async setTxNote(txHash: string, note: string): Promise { return super.setTxNote(txHash, note); } // -------------------------------- PRIVATE --------------------------------- + + static async connectToWalletRpc(uriOrConfig: string | Partial | Partial | string[], username?: string, password?: string): Promise { + let config = MoneroWalletRpc.normalizeConfig(uriOrConfig, username, password); + if (config.cmd) return MoneroWalletRpc.startWalletRpcProcess(config); + else return new MoneroWalletRpc(config); + } + + protected static async startWalletRpcProcess(config: Partial): Promise { + assert(GenUtils.isArray(config.cmd), "Must provide string array with command line parameters"); + + // start process + let child_process = await import("child_process"); + const process = child_process.spawn(config.cmd[0], config.cmd.slice(1), {}); + process.stdout.setEncoding('utf8'); + process.stderr.setEncoding('utf8'); + + // return promise which resolves after starting monero-wallet-rpc + let uri; + let that = this; + let output = ""; + return new Promise(function(resolve, reject) { + + // handle stdout + process.stdout.on('data', async function(data) { + let line = data.toString(); + LibraryUtils.log(2, line); + output += line + '\n'; // capture output in case of error + + // extract uri from e.g. "I Binding on 127.0.0.1 (IPv4):38085" + let uriLineContains = "Binding on "; + let uriLineContainsIdx = line.indexOf(uriLineContains); + if (uriLineContainsIdx >= 0) { + let host = line.substring(uriLineContainsIdx + uriLineContains.length, line.lastIndexOf(' ')); + let unformattedLine = line.replace(/\u001b\[.*?m/g, '').trim(); // remove color formatting + let port = unformattedLine.substring(unformattedLine.lastIndexOf(':') + 1); + let sslIdx = config.cmd.indexOf("--rpc-ssl"); + let sslEnabled = sslIdx >= 0 ? "enabled" == config.cmd[sslIdx + 1].toLowerCase() : false; + uri = (sslEnabled ? "https" : "http") + "://" + host + ":" + port; + } + + // read success message + if (line.indexOf("Starting wallet RPC server") >= 0) { + + // get username and password from params + let userPassIdx = config.cmd.indexOf("--rpc-login"); + let userPass = userPassIdx >= 0 ? config.cmd[userPassIdx + 1] : undefined; + let username = userPass === undefined ? undefined : userPass.substring(0, userPass.indexOf(':')); + let password = userPass === undefined ? undefined : userPass.substring(userPass.indexOf(':') + 1); + + // create client connected to internal process + config = config.copy().setServer({uri: uri, username: username, password: password, rejectUnauthorized: config.getServer() ? config.getServer().getRejectUnauthorized() : undefined}); + config.cmd = undefined; + let wallet = await MoneroWalletRpc.connectToWalletRpc(config); + wallet.process = process; + + // resolve promise with client connected to internal process + this.isResolved = true; + resolve(wallet); + } + }); + + // handle stderr + process.stderr.on('data', function(data) { + if (LibraryUtils.getLogLevel() >= 2) console.error(data); + }); + + // handle exit + process.on("exit", function(code) { + if (!this.isResolved) reject(new MoneroError("monero-wallet-rpc process terminated with exit code " + code + (output ? ":\n\n" + output : ""))); + }); + + // handle error + process.on("error", function(err) { + if (err.message.indexOf("ENOENT") >= 0) reject(new MoneroError("monero-wallet-rpc does not exist at path '" + config.cmd[0] + "'")); + if (!this.isResolved) reject(err); + }); + + // handle uncaught exception + process.on("uncaughtException", function(err, origin) { + console.error("Uncaught exception in monero-wallet-rpc process: " + err.message); + console.error(origin); + reject(err); + }); + }); + } - async _clear() { + protected async clear() { this.listeners.splice(0, this.listeners.length); - this._refreshListening(); + this.refreshListening(); delete this.addressCache; this.addressCache = {}; this.path = undefined; } - async _getBalances(accountIdx, subaddressIdx) { - if (accountIdx === undefined) { - assert.equal(subaddressIdx, undefined, "Must provide account index with subaddress index"); - let balance = new BigInteger(0); - let unlockedBalance = new BigInteger(0); - for (let account of await this.getAccounts()) { - balance = balance.add(account.getBalance()); - unlockedBalance = unlockedBalance.add(account.getUnlockedBalance()); - } - return [balance, unlockedBalance]; - } else { - let params = {account_index: accountIdx, address_indices: subaddressIdx === undefined ? undefined : [subaddressIdx]}; - let resp = await this.rpc.sendJsonRequest("get_balance", params); - if (subaddressIdx === undefined) return [new BigInteger(resp.result.balance), new BigInteger(resp.result.unlocked_balance)]; - else return [new BigInteger(resp.result.per_subaddress[0].balance), new BigInteger(resp.result.per_subaddress[0].unlocked_balance)]; - } - } - - async _getAccountIndices(getSubaddressIndices) { + protected async getAccountIndices(getSubaddressIndices?: any) { let indices = new Map(); for (let account of await this.getAccounts()) { - indices.set(account.getIndex(), getSubaddressIndices ? await this._getSubaddressIndices(account.getIndex()) : undefined); + indices.set(account.getIndex(), getSubaddressIndices ? await this.getSubaddressIndices(account.getIndex()) : undefined); } return indices; } - async _getSubaddressIndices(accountIdx) { + protected async getSubaddressIndices(accountIdx) { let subaddressIndices = []; - let resp = await this.rpc.sendJsonRequest("get_address", {account_index: accountIdx}); + let resp = await this.config.getServer().sendJsonRequest("get_address", {account_index: accountIdx}); for (let address of resp.result.addresses) subaddressIndices.push(address.address_index); return subaddressIndices; } - async _getTransfersAux(query) { + protected async getTransfersAux(query: MoneroTransferQuery) { // build params for get_transfers rpc call let txQuery = query.getTxQuery(); - let canBeConfirmed = txQuery.isConfirmed() !== false && txQuery.inTxPool() !== true && txQuery.isFailed() !== true && txQuery.isRelayed() !== false; - let canBeInTxPool = txQuery.isConfirmed() !== true && txQuery.inTxPool() !== false && txQuery.isFailed() !== true && txQuery.getHeight() === undefined && txQuery.getMaxHeight() === undefined && txQuery.isLocked() !== false; - let canBeIncoming = query.isIncoming() !== false && query.isOutgoing() !== true && query.hasDestinations() !== true; - let canBeOutgoing = query.isOutgoing() !== false && query.isIncoming() !== true; + let canBeConfirmed = txQuery.getIsConfirmed() !== false && txQuery.getInTxPool() !== true && txQuery.getIsFailed() !== true && txQuery.getIsRelayed() !== false; + let canBeInTxPool = txQuery.getIsConfirmed() !== true && txQuery.getInTxPool() !== false && txQuery.getIsFailed() !== true && txQuery.getHeight() === undefined && txQuery.getMaxHeight() === undefined && txQuery.getIsLocked() !== false; + let canBeIncoming = query.getIsIncoming() !== false && query.getIsOutgoing() !== true && query.getHasDestinations() !== true; + let canBeOutgoing = query.getIsOutgoing() !== false && query.getIsIncoming() !== true; // check if fetching pool txs contradicted by configuration - if (txQuery.inTxPool() === true && !canBeInTxPool) { + if (txQuery.getInTxPool() === true && !canBeInTxPool) { throw new MoneroError("Cannot fetch pool transactions because it contradicts configuration"); } - let params = {}; + let params: any = {}; params.in = canBeIncoming && canBeConfirmed; params.out = canBeOutgoing && canBeConfirmed; params.pool = canBeIncoming && canBeInTxPool; params.pending = canBeOutgoing && canBeInTxPool; - params.failed = txQuery.isFailed() !== false && txQuery.isConfirmed() !== true && txQuery.inTxPool() != true; + params.failed = txQuery.getIsFailed() !== false && txQuery.getIsConfirmed() !== true && txQuery.getInTxPool() != true; if (txQuery.getMinHeight() !== undefined) { if (txQuery.getMinHeight() > 0) params.min_height = txQuery.getMinHeight() - 1; // TODO monero-project: wallet2::get_payments() min_height is exclusive, so manually offset to match intended range (issues #5751, #5598) else params.min_height = txQuery.getMinHeight(); @@ -1636,42 +1625,42 @@ class MoneroWalletRpc extends MoneroWallet { let blockMap = {}; // build txs using `get_transfers` - let resp = await this.rpc.sendJsonRequest("get_transfers", params); + let resp = await this.config.getServer().sendJsonRequest("get_transfers", params); for (let key of Object.keys(resp.result)) { for (let rpcTx of resp.result[key]) { //if (rpcTx.txid === query.debugTxId) console.log(rpcTx); - let tx = MoneroWalletRpc._convertRpcTxWithTransfer(rpcTx); - if (tx.isConfirmed()) assert(tx.getBlock().getTxs().indexOf(tx) > -1); + let tx = MoneroWalletRpc.convertRpcTxWithTransfer(rpcTx); + if (tx.getIsConfirmed()) assert(tx.getBlock().getTxs().indexOf(tx) > -1); // replace transfer amount with destination sum // TODO monero-wallet-rpc: confirmed tx from/to same account has amount 0 but cached transfers - if (tx.getOutgoingTransfer() !== undefined && tx.isRelayed() && !tx.isFailed() && - tx.getOutgoingTransfer().getDestinations() && tx.getOutgoingAmount().compare(new BigInteger(0)) === 0) { + if (tx.getOutgoingTransfer() !== undefined && tx.getIsRelayed() && !tx.getIsFailed() && + tx.getOutgoingTransfer().getDestinations() && tx.getOutgoingAmount() === 0n) { let outgoingTransfer = tx.getOutgoingTransfer(); - let transferTotal = new BigInteger(0); - for (let destination of outgoingTransfer.getDestinations()) transferTotal = transferTotal.add(destination.getAmount()); + let transferTotal = BigInt(0); + for (let destination of outgoingTransfer.getDestinations()) transferTotal = transferTotal + destination.getAmount(); tx.getOutgoingTransfer().setAmount(transferTotal); } // merge tx - MoneroWalletRpc._mergeTx(tx, txMap, blockMap); + MoneroWalletRpc.mergeTx(tx, txMap, blockMap); } } // sort txs by block height - let txs = Object.values(txMap); - txs.sort(MoneroWalletRpc._compareTxsByHeight); + let txs: MoneroTxWallet[] = Object.values(txMap); + txs.sort(MoneroWalletRpc.compareTxsByHeight); // filter and return transfers let transfers = []; for (let tx of txs) { // tx is not incoming/outgoing unless already set - if (tx.isIncoming() === undefined) tx.setIsIncoming(false); - if (tx.isOutgoing() === undefined) tx.setIsOutgoing(false); + if (tx.getIsIncoming() === undefined) tx.setIsIncoming(false); + if (tx.getIsOutgoing() === undefined) tx.setIsOutgoing(false); // sort incoming transfers - if (tx.getIncomingTransfers() !== undefined) tx.getIncomingTransfers().sort(MoneroWalletRpc._compareIncomingTransfers); + if (tx.getIncomingTransfers() !== undefined) tx.getIncomingTransfers().sort(MoneroWalletRpc.compareIncomingTransfers); // collect queried transfers, erase if excluded for (let transfer of tx.filterTransfers(query)) { @@ -1687,7 +1676,7 @@ class MoneroWalletRpc extends MoneroWallet { return transfers; } - async _getOutputsAux(query) { + protected async getOutputsAux(query) { // determine account and subaddress indices to be queried let indices = new Map(); @@ -1699,7 +1688,7 @@ class MoneroWalletRpc extends MoneroWallet { } else { assert.equal(query.getSubaddressIndex(), undefined, "Query specifies a subaddress index but not an account index") assert(query.getSubaddressIndices() === undefined || query.getSubaddressIndices().length === 0, "Query specifies subaddress indices but not an account index"); - indices = await this._getAccountIndices(); // fetch all account indices without subaddresses + indices = await this.getAccountIndices(); // fetch all account indices without subaddresses } // cache unique txs and blocks @@ -1707,34 +1696,34 @@ class MoneroWalletRpc extends MoneroWallet { let blockMap = {}; // collect txs with outputs for each indicated account using `incoming_transfers` rpc call - let params = {}; - params.transfer_type = query.isSpent() === true ? "unavailable" : query.isSpent() === false ? "available" : "all"; + let params: any = {}; + params.transfer_type = query.getIsSpent() === true ? "unavailable" : query.getIsSpent() === false ? "available" : "all"; params.verbose = true; for (let accountIdx of indices.keys()) { // send request params.account_index = accountIdx; params.subaddr_indices = indices.get(accountIdx); - let resp = await this.rpc.sendJsonRequest("incoming_transfers", params); + let resp = await this.config.getServer().sendJsonRequest("incoming_transfers", params); // convert response to txs with outputs and merge if (resp.result.transfers === undefined) continue; for (let rpcOutput of resp.result.transfers) { - let tx = MoneroWalletRpc._convertRpcTxWalletWithOutput(rpcOutput); - MoneroWalletRpc._mergeTx(tx, txMap, blockMap); + let tx = MoneroWalletRpc.convertRpcTxWalletWithOutput(rpcOutput); + MoneroWalletRpc.mergeTx(tx, txMap, blockMap); } } // sort txs by block height - let txs = Object.values(txMap); - txs.sort(MoneroWalletRpc._compareTxsByHeight); + let txs: MoneroTxWallet[] = Object.values(txMap); + txs.sort(MoneroWalletRpc.compareTxsByHeight); // collect queried outputs let outputs = []; for (let tx of txs) { // sort outputs - if (tx.getOutputs() !== undefined) tx.getOutputs().sort(MoneroWalletRpc._compareOutputs); + if (tx.getOutputs() !== undefined) tx.getOutputs().sort(MoneroWalletRpc.compareOutputs); // collect queried outputs, erase if excluded for (let output of tx.filterOutputs(query)) outputs.push(output); @@ -1753,13 +1742,13 @@ class MoneroWalletRpc extends MoneroWallet { * @param all - pecifies to get all xor only new images from last import * @return {MoneroKeyImage[]} are the key images */ - async _rpcExportKeyImages(all) { - let resp = await this.rpc.sendJsonRequest("export_key_images", {all: all}); + protected async rpcExportKeyImages(all) { + let resp = await this.config.getServer().sendJsonRequest("export_key_images", {all: all}); if (!resp.result.signed_key_images) return []; return resp.result.signed_key_images.map(rpcImage => new MoneroKeyImage(rpcImage.key_image, rpcImage.signature)); } - async _rpcSweepAccount(config) { + protected async rpcSweepAccount(config) { // validate config if (config === undefined) throw new MoneroError("Must provide sweep config"); @@ -1782,7 +1771,7 @@ class MoneroWalletRpc extends MoneroWallet { if (config.getSubaddressIndices().length === 0) throw new MoneroError("No subaddresses to sweep from"); // common config params - let params = {}; + let params: any = {}; let relay = config.getRelay() === true; params.account_index = config.getAccountIndex(); params.subaddr_indices = config.getSubaddressIndices(); @@ -1798,11 +1787,11 @@ class MoneroWalletRpc extends MoneroWallet { params.get_tx_metadata = true; // invoke wallet rpc `sweep_all` - let resp = await this.rpc.sendJsonRequest("sweep_all", params); + let resp = await this.config.getServer().sendJsonRequest("sweep_all", params); let result = resp.result; // initialize txs from response - let txSet = MoneroWalletRpc._convertRpcSentTxsToTxSet(result, undefined, config); + let txSet = MoneroWalletRpc.convertRpcSentTxsToTxSet(result, undefined, config); // initialize remaining known fields for (let tx of txSet.getTxs()) { @@ -1814,24 +1803,23 @@ class MoneroWalletRpc extends MoneroWallet { tx.setIsRelayed(relay); tx.setIsMinerTx(false); tx.setIsFailed(false); - tx.setRingSize(MoneroUtils.RING_SIZE); let transfer = tx.getOutgoingTransfer(); transfer.setAccountIndex(config.getAccountIndex()); if (config.getSubaddressIndices().length === 1) transfer.setSubaddressIndices(config.getSubaddressIndices()); - let destination = new MoneroDestination(config.getDestinations()[0].getAddress(), new BigInteger(transfer.getAmount())); + let destination = new MoneroDestination(config.getDestinations()[0].getAddress(), BigInt(transfer.getAmount())); transfer.setDestinations([destination]); tx.setOutgoingTransfer(transfer); tx.setPaymentId(config.getPaymentId()); if (tx.getUnlockTime() === undefined) tx.setUnlockTime(config.getUnlockTime() === undefined ? 0 : config.getUnlockTime()); if (tx.getRelay()) { if (tx.getLastRelayedTimestamp() === undefined) tx.setLastRelayedTimestamp(+new Date().getTime()); // TODO (monero-wallet-rpc): provide timestamp on response; unconfirmed timestamps vary - if (tx.isDoubleSpendSeen() === undefined) tx.setIsDoubleSpend(false); + if (tx.getIsDoubleSpendSeen() === undefined) tx.setIsDoubleSpendSeen(false); } } return txSet.getTxs(); } - _refreshListening() { + protected refreshListening() { if (this.walletPoller == undefined && this.listeners.length) this.walletPoller = new WalletPoller(this); if (this.walletPoller !== undefined) this.walletPoller.setIsPolling(this.listeners.length > 0); } @@ -1839,26 +1827,19 @@ class MoneroWalletRpc extends MoneroWallet { /** * Poll if listening. */ - async _poll() { - if (this.walletPoller !== undefined && this.walletPoller._isPolling) await this.walletPoller.poll(); + protected async poll() { + if (this.walletPoller !== undefined && this.walletPoller.isPolling) await this.walletPoller.poll(); } // ---------------------------- PRIVATE STATIC ------------------------------ - static _normalizeConfig(uriOrConfigOrConnection, username, password, rejectUnauthorized) { - let config; - if (typeof uriOrConfigOrConnection === "string") config = {uri: uriOrConfigOrConnection, username: username, password: password, rejectUnauthorized: rejectUnauthorized}; - else { - if (typeof uriOrConfigOrConnection !== "object") throw new MoneroError("Invalid configuration to create rpc client; must be string, object, or MoneroRpcConnection"); - if (username || password || rejectUnauthorized) throw new MoneroError("Can provide config object or params or new MoneroDaemonRpc(...) but not both"); - if (uriOrConfigOrConnection instanceof MoneroRpcConnection) config = Object.assign({}, uriOrConfigOrConnection.getConfig()); - else config = Object.assign({}, uriOrConfigOrConnection); - } - if (config.server) { - config = Object.assign(config, new MoneroRpcConnection(config.server).getConfig()); - delete config.server; - } - return config; + protected static normalizeConfig(uriOrConfig: string | Partial | Partial | string[], username?: string, password?: string): MoneroWalletConfig { + let config: undefined | Partial = undefined; + if (typeof uriOrConfig === "string" || (uriOrConfig as Partial).uri) config = new MoneroWalletConfig({server: new MoneroRpcConnection(uriOrConfig as string | Partial, username, password)}); + else if (GenUtils.isArray(uriOrConfig)) config = new MoneroWalletConfig({cmd: uriOrConfig as string[]}); + else config = new MoneroWalletConfig(uriOrConfig as Partial); + if (config.proxyToWorker === undefined) config.proxyToWorker = true; + return config as MoneroWalletConfig; } /** @@ -1868,7 +1849,7 @@ class MoneroWalletRpc extends MoneroWallet { * @param {MoneroTxQuery} query - the query to decontextualize * @return {MoneroTxQuery} a reference to the query for convenience */ - static _decontextualize(query) { + protected static decontextualize(query) { query.setIsIncoming(undefined); query.setIsOutgoing(undefined); query.setTransferQuery(undefined); @@ -1877,11 +1858,11 @@ class MoneroWalletRpc extends MoneroWallet { return query; } - static _isContextual(query) { + protected static isContextual(query) { if (!query) return false; if (!query.getTxQuery()) return false; - if (query.getTxQuery().isIncoming() !== undefined) return true; // requires getting other transfers - if (query.getTxQuery().isOutgoing() !== undefined) return true; + if (query.getTxQuery().getIsIncoming() !== undefined) return true; // requires getting other transfers + if (query.getTxQuery().getIsOutgoing() !== undefined) return true; if (query instanceof MoneroTransferQuery) { if (query.getTxQuery().getOutputQuery() !== undefined) return true; // requires getting other outputs } else if (query instanceof MoneroOutputQuery) { @@ -1892,13 +1873,13 @@ class MoneroWalletRpc extends MoneroWallet { return false; } - static _convertRpcAccount(rpcAccount) { + protected static convertRpcAccount(rpcAccount) { let account = new MoneroAccount(); for (let key of Object.keys(rpcAccount)) { let val = rpcAccount[key]; if (key === "account_index") account.setIndex(val); - else if (key === "balance") account.setBalance(new BigInteger(val)); - else if (key === "unlocked_balance") account.setUnlockedBalance(new BigInteger(val)); + else if (key === "balance") account.setBalance(BigInt(val)); + else if (key === "unlocked_balance") account.setUnlockedBalance(BigInt(val)); else if (key === "base_address") account.setPrimaryAddress(val); else if (key === "tag") account.setTag(val); else if (key === "label") { } // label belongs to first subaddress @@ -1908,15 +1889,15 @@ class MoneroWalletRpc extends MoneroWallet { return account; } - static _convertRpcSubaddress(rpcSubaddress) { + protected static convertRpcSubaddress(rpcSubaddress) { let subaddress = new MoneroSubaddress(); for (let key of Object.keys(rpcSubaddress)) { let val = rpcSubaddress[key]; if (key === "account_index") subaddress.setAccountIndex(val); else if (key === "address_index") subaddress.setIndex(val); else if (key === "address") subaddress.setAddress(val); - else if (key === "balance") subaddress.setBalance(new BigInteger(val)); - else if (key === "unlocked_balance") subaddress.setUnlockedBalance(new BigInteger(val)); + else if (key === "balance") subaddress.setBalance(BigInt(val)); + else if (key === "unlocked_balance") subaddress.setUnlockedBalance(BigInt(val)); else if (key === "num_unspent_outputs") subaddress.setNumUnspentOutputs(val); else if (key === "label") { if (val) subaddress.setLabel(val); } else if (key === "used") subaddress.setIsUsed(val); @@ -1933,11 +1914,11 @@ class MoneroWalletRpc extends MoneroWallet { * TODO: remove copyDestinations after >18.2.2 when subtractFeeFrom fully supported * * @param {MoneroTxConfig} config - send config - * @param {MoneroTxWallet} tx - existing transaction to initialize (optional) + * @param {MoneroTxWallet} [tx] - existing transaction to initialize (optional) * @param {boolean} copyDestinations - copies config destinations if true * @return {MoneroTxWallet} is the initialized send tx */ - static _initSentTxWallet(config, tx, copyDestinations) { + protected static initSentTxWallet(config, tx, copyDestinations) { if (!tx) tx = new MoneroTxWallet(); let relay = config.getRelay() === true; tx.setIsOutgoing(true); @@ -1950,7 +1931,8 @@ class MoneroWalletRpc extends MoneroWallet { tx.setIsFailed(false); tx.setIsLocked(true); tx.setRingSize(MoneroUtils.RING_SIZE); - let transfer = new MoneroOutgoingTransfer().setTx(tx); + let transfer = new MoneroOutgoingTransfer(); + transfer.setTx(tx); if (config.getSubaddressIndices() && config.getSubaddressIndices().length === 1) transfer.setSubaddressIndices(config.getSubaddressIndices().slice(0)); // we know src subaddress indices iff config specifies 1 if (copyDestinations) { let destCopies = []; @@ -1962,7 +1944,7 @@ class MoneroWalletRpc extends MoneroWallet { if (tx.getUnlockTime() === undefined) tx.setUnlockTime(config.getUnlockTime() === undefined ? 0 : config.getUnlockTime()); if (config.getRelay()) { if (tx.getLastRelayedTimestamp() === undefined) tx.setLastRelayedTimestamp(+new Date().getTime()); // TODO (monero-wallet-rpc): provide timestamp on response; unconfirmed timestamps vary - if (tx.isDoubleSpendSeen() === undefined) tx.setIsDoubleSpend(false); + if (tx.getIsDoubleSpendSeen() === undefined) tx.setIsDoubleSpendSeen(false); } return tx; } @@ -1974,7 +1956,7 @@ class MoneroWalletRpc extends MoneroWallet { * @return MoneroTxSet - initialized tx set * @return the resulting tx set */ - static _convertRpcTxSet(rpcMap) { + protected static convertRpcTxSet(rpcMap) { let txSet = new MoneroTxSet(); txSet.setMultisigTxHex(rpcMap.multisig_txset); txSet.setUnsignedTxHex(rpcMap.unsigned_txset); @@ -1993,10 +1975,10 @@ class MoneroWalletRpc extends MoneroWallet { * @param config - tx config * @return the converted tx set */ - static _convertRpcSentTxsToTxSet(rpcTxs, txs, config) { + protected static convertRpcSentTxsToTxSet(rpcTxs: any, txs?: any, config?: any) { // build shared tx set - let txSet = MoneroWalletRpc._convertRpcTxSet(rpcTxs); + let txSet = MoneroWalletRpc.convertRpcTxSet(rpcTxs); // get number of txs let numTxs = rpcTxs.fee_list ? rpcTxs.fee_list.length : 0; @@ -2026,12 +2008,12 @@ class MoneroWalletRpc extends MoneroWallet { else if (key === "tx_key_list") for (let i = 0; i < val.length; i++) txs[i].setKey(val[i]); else if (key === "tx_blob_list") for (let i = 0; i < val.length; i++) txs[i].setFullHex(val[i]); else if (key === "tx_metadata_list") for (let i = 0; i < val.length; i++) txs[i].setMetadata(val[i]); - else if (key === "fee_list") for (let i = 0; i < val.length; i++) txs[i].setFee(new BigInteger(val[i])); + else if (key === "fee_list") for (let i = 0; i < val.length; i++) txs[i].setFee(BigInt(val[i])); else if (key === "weight_list") for (let i = 0; i < val.length; i++) txs[i].setWeight(val[i]); else if (key === "amount_list") { for (let i = 0; i < val.length; i++) { if (txs[i].getOutgoingTransfer() == undefined) txs[i].setOutgoingTransfer(new MoneroOutgoingTransfer().setTx(txs[i])); - txs[i].getOutgoingTransfer().setAmount(new BigInteger(val[i])); + txs[i].getOutgoingTransfer().setAmount(BigInt(val[i])); } } else if (key === "multisig_txset" || key === "unsigned_txset" || key === "signed_txset") {} // handled elsewhere @@ -2053,8 +2035,8 @@ class MoneroWalletRpc extends MoneroWallet { if (txs[txIdx].getOutgoingTransfer() === undefined) txs[txIdx].setOutgoingTransfer(new MoneroOutgoingTransfer().setTx(txs[txIdx])); txs[txIdx].getOutgoingTransfer().setDestinations([]); for (let amount of amountsByDest) { - if (config.getDestinations().length === 1) txs[txIdx].getOutgoingTransfer().getDestinations().push(new MoneroDestination(config.getDestinations()[0].getAddress(), new BigInteger(amount))); // sweeping can create multiple txs with one address - else txs[txIdx].getOutgoingTransfer().getDestinations().push(new MoneroDestination(config.getDestinations()[destinationIdx++].getAddress(), new BigInteger(amount))); + if (config.getDestinations().length === 1) txs[txIdx].getOutgoingTransfer().getDestinations().push(new MoneroDestination(config.getDestinations()[0].getAddress(), BigInt(amount))); // sweeping can create multiple txs with one address + else txs[txIdx].getOutgoingTransfer().getDestinations().push(new MoneroDestination(config.getDestinations()[destinationIdx++].getAddress(), BigInt(amount))); } } } @@ -2071,11 +2053,11 @@ class MoneroWalletRpc extends MoneroWallet { * @param tx - existing tx to continue initializing (optional) * @param isOutgoing - specifies if the tx is outgoing if true, incoming if false, or decodes from type if undefined * @param config - tx config - * @returns the initialized tx set with a tx + * @return the initialized tx set with a tx */ - static _convertRpcTxToTxSet(rpcTx, tx, isOutgoing, config) { - let txSet = MoneroWalletRpc._convertRpcTxSet(rpcTx); - txSet.setTxs([MoneroWalletRpc._convertRpcTxWithTransfer(rpcTx, tx, isOutgoing, config).setTxSet(txSet)]); + protected static convertRpcTxToTxSet(rpcTx, tx, isOutgoing, config) { + let txSet = MoneroWalletRpc.convertRpcTxSet(rpcTx); + txSet.setTxs([MoneroWalletRpc.convertRpcTxWithTransfer(rpcTx, tx, isOutgoing, config).setTxSet(txSet)]); return txSet; } @@ -2086,26 +2068,26 @@ class MoneroWalletRpc extends MoneroWallet { * @param tx - existing tx to continue initializing (optional) * @param isOutgoing - specifies if the tx is outgoing if true, incoming if false, or decodes from type if undefined * @param config - tx config - * @returns {MoneroTxWallet} is the initialized tx + * @return {MoneroTxWallet} is the initialized tx */ - static _convertRpcTxWithTransfer(rpcTx, tx, isOutgoing, config) { // TODO: change everything to safe set + protected static convertRpcTxWithTransfer(rpcTx: any, tx?: any, isOutgoing?: any, config?: any) { // TODO: change everything to safe set // initialize tx to return if (!tx) tx = new MoneroTxWallet(); // initialize tx state from rpc type - if (rpcTx.type !== undefined) isOutgoing = MoneroWalletRpc._decodeRpcType(rpcTx.type, tx); + if (rpcTx.type !== undefined) isOutgoing = MoneroWalletRpc.decodeRpcType(rpcTx.type, tx); else assert.equal(typeof isOutgoing, "boolean", "Must indicate if tx is outgoing (true) xor incoming (false) since unknown"); // TODO: safe set - // initialize remaining fields TODO: seems this should be part of common function with DaemonRpc._convertRpcTx + // initialize remaining fields TODO: seems this should be part of common function with DaemonRpc.convertRpcTx let header; let transfer; for (let key of Object.keys(rpcTx)) { let val = rpcTx[key]; if (key === "txid") tx.setHash(val); else if (key === "tx_hash") tx.setHash(val); - else if (key === "fee") tx.setFee(new BigInteger(val)); + else if (key === "fee") tx.setFee(BigInt(val)); else if (key === "note") { if (val) tx.setNote(val); } else if (key === "tx_key") tx.setKey(val); else if (key === "type") { } // type already handled @@ -2115,15 +2097,15 @@ class MoneroWalletRpc extends MoneroWallet { else if (key === "locked") tx.setIsLocked(val); else if (key === "tx_blob") tx.setFullHex(val); else if (key === "tx_metadata") tx.setMetadata(val); - else if (key === "double_spend_seen") tx.setIsDoubleSpend(val); + else if (key === "double_spend_seen") tx.setIsDoubleSpendSeen(val); else if (key === "block_height" || key === "height") { - if (tx.isConfirmed()) { + if (tx.getIsConfirmed()) { if (!header) header = new MoneroBlockHeader(); header.setHeight(val); } } else if (key === "timestamp") { - if (tx.isConfirmed()) { + if (tx.getIsConfirmed()) { if (!header) header = new MoneroBlockHeader(); header.setTimestamp(val); } else { @@ -2137,7 +2119,7 @@ class MoneroWalletRpc extends MoneroWallet { } else if (key === "amount") { if (transfer === undefined) transfer = (isOutgoing ? new MoneroOutgoingTransfer() : new MoneroIncomingTransfer()).setTx(tx); - transfer.setAmount(new BigInteger(val)); + transfer.setAmount(BigInt(val)); } else if (key === "amounts") {} // ignoring, amounts sum to amount else if (key === "address") { @@ -2171,7 +2153,7 @@ class MoneroWalletRpc extends MoneroWallet { destinations.push(destination); for (let destinationKey of Object.keys(rpcDestination)) { if (destinationKey === "address") destination.setAddress(rpcDestination[destinationKey]); - else if (destinationKey === "amount") destination.setAmount(new BigInteger(rpcDestination[destinationKey])); + else if (destinationKey === "amount") destination.setAmount(BigInt(rpcDestination[destinationKey])); else throw new MoneroError("Unrecognized transaction destination field: " + destinationKey); } } @@ -2180,10 +2162,10 @@ class MoneroWalletRpc extends MoneroWallet { } else if (key === "multisig_txset" && val !== undefined) {} // handled elsewhere; this method only builds a tx wallet else if (key === "unsigned_txset" && val !== undefined) {} // handled elsewhere; this method only builds a tx wallet - else if (key === "amount_in") tx.setInputSum(new BigInteger(val)); - else if (key === "amount_out") tx.setOutputSum(new BigInteger(val)); + else if (key === "amount_in") tx.setInputSum(BigInt(val)); + else if (key === "amount_out") tx.setOutputSum(BigInt(val)); else if (key === "change_address") tx.setChangeAddress(val === "" ? undefined : val); - else if (key === "change_amount") tx.setChangeAmount(new BigInteger(val)); + else if (key === "change_amount") tx.setChangeAmount(BigInt(val)); else if (key === "dummy_outputs") tx.setNumDummyOutputs(val); else if (key === "extra") tx.setExtraHex(val); else if (key === "ring_size") tx.setRingSize(val); @@ -2202,7 +2184,7 @@ class MoneroWalletRpc extends MoneroWallet { if (transfer === undefined) transfer = new MoneroOutgoingTransfer().setTx(tx); transfer.setDestinations([]); for (let i = 0; i < config.getDestinations().length; i++) { - transfer.getDestinations().push(new MoneroDestination(config.getDestinations()[i].getAddress(), new BigInteger(amountsByDest[i]))); + transfer.getDestinations().push(new MoneroDestination(config.getDestinations()[i].getAddress(), BigInt(amountsByDest[i]))); } } else console.log("WARNING: ignoring unexpected transaction field with transfer: " + key + ": " + val); @@ -2213,8 +2195,8 @@ class MoneroWalletRpc extends MoneroWallet { // initialize final fields if (transfer) { - if (tx.isConfirmed() === undefined) tx.setIsConfirmed(false); - if (!transfer.getTx().isConfirmed()) tx.setNumConfirmations(0); + if (tx.getIsConfirmed() === undefined) tx.setIsConfirmed(false); + if (!transfer.getTx().getIsConfirmed()) tx.setNumConfirmations(0); if (isOutgoing) { tx.setIsOutgoing(true); if (tx.getOutgoingTransfer()) { @@ -2232,7 +2214,7 @@ class MoneroWalletRpc extends MoneroWallet { return tx; } - static _convertRpcTxWalletWithOutput(rpcOutput) { + protected static convertRpcTxWalletWithOutput(rpcOutput) { // initialize tx let tx = new MoneroTxWallet(); @@ -2244,7 +2226,7 @@ class MoneroWalletRpc extends MoneroWallet { let output = new MoneroOutputWallet({tx: tx}); for (let key of Object.keys(rpcOutput)) { let val = rpcOutput[key]; - if (key === "amount") output.setAmount(new BigInteger(val)); + if (key === "amount") output.setAmount(BigInt(val)); else if (key === "spent") output.setIsSpent(val); else if (key === "key_image") { if ("" !== val) output.setKeyImage(new MoneroKeyImage(val)); } else if (key === "global_index") output.setIndex(val); @@ -2256,7 +2238,7 @@ class MoneroWalletRpc extends MoneroWallet { output.setAccountIndex(val.major); output.setSubaddressIndex(val.minor); } - else if (key === "block_height") tx.setBlock(new MoneroBlock().setHeight(val).setTxs([tx])); + else if (key === "block_height") tx.setBlock((new MoneroBlock().setHeight(val) as MoneroBlock).setTxs([tx as MoneroTx])); else console.log("WARNING: ignoring unexpected transaction field: " + key + ": " + val); } @@ -2265,14 +2247,14 @@ class MoneroWalletRpc extends MoneroWallet { return tx; } - static _convertRpcDescribeTransfer(rpcDescribeTransferResult) { + protected static convertRpcDescribeTransfer(rpcDescribeTransferResult) { let txSet = new MoneroTxSet(); for (let key of Object.keys(rpcDescribeTransferResult)) { let val = rpcDescribeTransferResult[key]; if (key === "desc") { txSet.setTxs([]); for (let txMap of val) { - let tx = MoneroWalletRpc._convertRpcTxWithTransfer(txMap, undefined, true); + let tx = MoneroWalletRpc.convertRpcTxWithTransfer(txMap, undefined, true); tx.setTxSet(txSet); txSet.getTxs().push(tx); } @@ -2293,7 +2275,7 @@ class MoneroWalletRpc extends MoneroWallet { * @param tx is the transaction to decode known fields to * @return {boolean} true if the rpc type indicates outgoing xor incoming */ - static _decodeRpcType(rpcType, tx) { + protected static decodeRpcType(rpcType, tx) { let isOutgoing; if (rpcType === "in") { isOutgoing = false; @@ -2356,7 +2338,7 @@ class MoneroWalletRpc extends MoneroWallet { * @param {Object} txMap - maps tx hashes to txs * @param {Object} blockMap - maps block heights to blocks */ - static _mergeTx(tx, txMap, blockMap) { + protected static mergeTx(tx, txMap, blockMap) { assert(tx.getHash() !== undefined); // merge tx @@ -2375,7 +2357,7 @@ class MoneroWalletRpc extends MoneroWallet { /** * Compares two transactions by their height. */ - static _compareTxsByHeight(tx1, tx2) { + protected static compareTxsByHeight(tx1, tx2) { if (tx1.getHeight() === undefined && tx2.getHeight() === undefined) return 0; // both unconfirmed else if (tx1.getHeight() === undefined) return 1; // tx1 is unconfirmed else if (tx2.getHeight() === undefined) return -1; // tx2 is unconfirmed @@ -2387,7 +2369,7 @@ class MoneroWalletRpc extends MoneroWallet { /** * Compares two transfers by ascending account and subaddress indices. */ - static _compareIncomingTransfers(t1, t2) { + static compareIncomingTransfers(t1, t2) { if (t1.getAccountIndex() < t2.getAccountIndex()) return -1; else if (t1.getAccountIndex() === t2.getAccountIndex()) return t1.getSubaddressIndex() - t2.getSubaddressIndex(); return 1; @@ -2396,10 +2378,10 @@ class MoneroWalletRpc extends MoneroWallet { /** * Compares two outputs by ascending account and subaddress indices. */ - static _compareOutputs(o1, o2) { + protected static compareOutputs(o1, o2) { // compare by height - let heightComparison = MoneroWalletRpc._compareTxsByHeight(o1.getTx(), o2.getTx()); + let heightComparison = MoneroWalletRpc.compareTxsByHeight(o1.getTx(), o2.getTx()); if (heightComparison !== 0) return heightComparison; // compare by account index, subaddress index, output index, then key image hex @@ -2416,131 +2398,142 @@ class MoneroWalletRpc extends MoneroWallet { /** * Polls monero-wallet-rpc to provide listener notifications. * - * @class - * @ignore + * @private */ class WalletPoller { + + // instance variables + isPolling: boolean; + protected wallet: MoneroWalletRpc; + protected looper: TaskLooper; + protected prevLockedTxs: any; + protected prevUnconfirmedNotifications: any; + protected prevConfirmedNotifications: any; + protected threadPool: any; + protected numPolling: any; + protected prevHeight: any; + protected prevBalances: any; constructor(wallet) { let that = this; - this._wallet = wallet; - this._looper = new TaskLooper(async function() { await that.poll(); }); - this._prevLockedTxs = []; - this._prevUnconfirmedNotifications = new Set(); // tx hashes of previous notifications - this._prevConfirmedNotifications = new Set(); // tx hashes of previously confirmed but not yet unlocked notifications - this._threadPool = new ThreadPool(1); // synchronize polls - this._numPolling = 0; + this.wallet = wallet; + this.looper = new TaskLooper(async function() { await that.poll(); }); + this.prevLockedTxs = []; + this.prevUnconfirmedNotifications = new Set(); // tx hashes of previous notifications + this.prevConfirmedNotifications = new Set(); // tx hashes of previously confirmed but not yet unlocked notifications + this.threadPool = new ThreadPool(1); // synchronize polls + this.numPolling = 0; } setIsPolling(isPolling) { - this._isPolling = isPolling; - if (isPolling) this._looper.start(this._wallet.syncPeriodInMs); - else this._looper.stop(); + this.isPolling = isPolling; + if (isPolling) this.looper.start(this.wallet.getSyncPeriodInMs()); + else this.looper.stop(); } setPeriodInMs(periodInMs) { - this._looper.setPeriodInMs(periodInMs); + this.looper.setPeriodInMs(periodInMs); } async poll() { // skip if next poll is queued - if (this._numPolling > 1) return; - this._numPolling++; + if (this.numPolling > 1) return; + this.numPolling++; // synchronize polls let that = this; - return this._threadPool.submit(async function() { + return this.threadPool.submit(async function() { try { // skip if wallet is closed - if (await that._wallet.isClosed()) { - that._numPolling--; + if (await that.wallet.isClosed()) { + that.numPolling--; return; } // take initial snapshot - if (that._prevHeight === undefined) { - that._prevHeight = await that._wallet.getHeight(); - that._prevLockedTxs = await that._wallet.getTxs(new MoneroTxQuery().setIsLocked(true)); - that._prevBalances = await that._wallet._getBalances(); - that._numPolling--; + if (that.prevHeight === undefined) { + that.prevHeight = await that.wallet.getHeight(); + that.prevLockedTxs = await that.wallet.getTxs(new MoneroTxQuery().setIsLocked(true)); + that.prevBalances = await that.wallet.getBalances(); + that.numPolling--; return; } // announce height changes - let height = await that._wallet.getHeight(); - if (that._prevHeight !== height) { - for (let i = that._prevHeight; i < height; i++) await that._onNewBlock(i); - that._prevHeight = height; + let height = await that.wallet.getHeight(); + if (that.prevHeight !== height) { + for (let i = that.prevHeight; i < height; i++) await that.onNewBlock(i); + that.prevHeight = height; } // get locked txs for comparison to previous let minHeight = Math.max(0, height - 70); // only monitor recent txs - let lockedTxs = await that._wallet.getTxs(new MoneroTxQuery().setIsLocked(true).setMinHeight(minHeight).setIncludeOutputs(true)); + let lockedTxs = await that.wallet.getTxs(new MoneroTxQuery().setIsLocked(true).setMinHeight(minHeight).setIncludeOutputs(true)); // collect hashes of txs no longer locked let noLongerLockedHashes = []; - for (let prevLockedTx of that._prevLockedTxs) { - if (that._getTx(lockedTxs, prevLockedTx.getHash()) === undefined) { + for (let prevLockedTx of that.prevLockedTxs) { + if (that.getTx(lockedTxs, prevLockedTx.getHash()) === undefined) { noLongerLockedHashes.push(prevLockedTx.getHash()); } } // save locked txs for next comparison - that._prevLockedTxs = lockedTxs; + that.prevLockedTxs = lockedTxs; // fetch txs which are no longer locked - let unlockedTxs = noLongerLockedHashes.length === 0 ? [] : await that._wallet.getTxs(new MoneroTxQuery().setIsLocked(false).setMinHeight(minHeight).setHashes(noLongerLockedHashes).setIncludeOutputs(true)); + let unlockedTxs = noLongerLockedHashes.length === 0 ? [] : await that.wallet.getTxs(new MoneroTxQuery().setIsLocked(false).setMinHeight(minHeight).setHashes(noLongerLockedHashes).setIncludeOutputs(true)); // announce new unconfirmed and confirmed outputs for (let lockedTx of lockedTxs) { - let searchSet = lockedTx.isConfirmed() ? that._prevConfirmedNotifications : that._prevUnconfirmedNotifications; + let searchSet = lockedTx.getIsConfirmed() ? that.prevConfirmedNotifications : that.prevUnconfirmedNotifications; let unannounced = !searchSet.has(lockedTx.getHash()); searchSet.add(lockedTx.getHash()); - if (unannounced) await that._notifyOutputs(lockedTx); + if (unannounced) await that.notifyOutputs(lockedTx); } // announce new unlocked outputs for (let unlockedTx of unlockedTxs) { - that._prevUnconfirmedNotifications.delete(unlockedTx.getHash()); - that._prevConfirmedNotifications.delete(unlockedTx.getHash()); - await that._notifyOutputs(unlockedTx); + that.prevUnconfirmedNotifications.delete(unlockedTx.getHash()); + that.prevConfirmedNotifications.delete(unlockedTx.getHash()); + await that.notifyOutputs(unlockedTx); } // announce balance changes - await that._checkForChangedBalances(); - that._numPolling--; - } catch (err) { - that._numPolling--; - console.error("Failed to background poll " + await that._wallet.getPath()); + await that.checkForChangedBalances(); + that.numPolling--; + } catch (err: any) { + that.numPolling--; + console.error("Failed to background poll " + await that.wallet.getPath()); } }); } - async _onNewBlock(height) { - for (let listener of this._wallet.getListeners()) await listener.onNewBlock(height); + protected async onNewBlock(height) { + for (let listener of this.wallet.getListeners()) await listener.onNewBlock(height); } - async _notifyOutputs(tx) { + protected async notifyOutputs(tx) { // notify spent outputs // TODO (monero-project): monero-wallet-rpc does not allow scrape of tx inputs so providing one input with outgoing amount if (tx.getOutgoingTransfer() !== undefined) { assert(tx.getInputs() === undefined); let output = new MoneroOutputWallet() - .setAmount(tx.getOutgoingTransfer().getAmount().add(tx.getFee())) + .setAmount(tx.getOutgoingTransfer().getAmount() + tx.getFee()) .setAccountIndex(tx.getOutgoingTransfer().getAccountIndex()) .setSubaddressIndex(tx.getOutgoingTransfer().getSubaddressIndices().length === 1 ? tx.getOutgoingTransfer().getSubaddressIndices()[0] : undefined) // initialize if transfer sourced from single subaddress .setTx(tx); tx.setInputs([output]); - for (let listener of this._wallet.getListeners()) await listener.onOutputSpent(output); + for (let listener of this.wallet.getListeners()) await listener.onOutputSpent(output); } // notify received outputs if (tx.getIncomingTransfers() !== undefined) { if (tx.getOutputs() !== undefined && tx.getOutputs().length > 0) { // TODO (monero-project): outputs only returned for confirmed txs for (let output of tx.getOutputs()) { - for (let listener of this._wallet.getListeners()) await listener.onOutputReceived(output); + for (let listener of this.wallet.getListeners()) await listener.onOutputReceived(output); } } else { // TODO (monero-project): monero-wallet-rpc does not allow scrape of unconfirmed received outputs so using incoming transfer values let outputs = []; @@ -2552,29 +2545,25 @@ class WalletPoller { .setTx(tx)); } tx.setOutputs(outputs); - for (let listener of this._wallet.getListeners()) { + for (let listener of this.wallet.getListeners()) { for (let output of tx.getOutputs()) await listener.onOutputReceived(output); } } } } - _getTx(txs, txHash) { + protected getTx(txs, txHash) { for (let tx of txs) if (txHash === tx.getHash()) return tx; return undefined; } - async _checkForChangedBalances() { - let balances = await this._wallet._getBalances(); - if (balances[0].compare(this._prevBalances[0]) !== 0 || balances[1].compare(this._prevBalances[1]) !== 0) { - this._prevBalances = balances; - for (let listener of await this._wallet.getListeners()) await listener.onBalancesChanged(balances[0], balances[1]); + protected async checkForChangedBalances() { + let balances = await this.wallet.getBalances(); + if (balances[0] !== this.prevBalances[0] || balances[1] !== this.prevBalances[1]) { + this.prevBalances = balances; + for (let listener of await this.wallet.getListeners()) await listener.onBalancesChanged(balances[0], balances[1]); return true; } return false; } } - -MoneroWalletRpc.DEFAULT_SYNC_PERIOD_IN_MS = 20000; // default period between syncs in ms (defined by DEFAULT_AUTO_REFRESH_PERIOD in wallet_rpc_server.cpp) - -module.exports = MoneroWalletRpc; \ No newline at end of file diff --git a/src/main/ts/wallet/model/MoneroAccount.ts b/src/main/ts/wallet/model/MoneroAccount.ts new file mode 100644 index 000000000..04ffd886b --- /dev/null +++ b/src/main/ts/wallet/model/MoneroAccount.ts @@ -0,0 +1,131 @@ +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroSubaddress from "./MoneroSubaddress"; + +/** + * Monero account model. + */ +export default class MoneroAccount { + + index: number; + primaryAddress: string; + balance: bigint; + unlockedBalance: bigint; + label: string; + tag: string; + subaddresses: MoneroSubaddress[]; + + constructor(account?: Partial) { + Object.assign(this, account); + + // deserialize balances + if (this.balance !== undefined && typeof this.balance !== "bigint") this.balance = BigInt(this.balance); + if (this.unlockedBalance !== undefined && typeof this.unlockedBalance !== "bigint") this.unlockedBalance = BigInt(this.unlockedBalance); + + // copy subaddresses + if (this.subaddresses) { + for (let i = 0; i < this.subaddresses.length; i++) { + this.subaddresses[i] = new MoneroSubaddress(this.subaddresses[i]); + } + } + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (json.balance !== undefined) json.balance = json.balance.toString(); + if (json.unlockedBalance !== undefined) json.unlockedBalance = json.unlockedBalance.toString(); + if (json.subaddresses !== undefined) { + for (let i = 0; i < json.subaddresses.length; i++) { + json.subaddresses[i] = json.subaddresses[i].toJson(); + } + } + return json; + } + + getIndex(): number { + return this.index; + } + + setIndex(index: number): MoneroAccount { + this.index = index; + return this; + } + + getPrimaryAddress(): string { + return this.primaryAddress; + } + + setPrimaryAddress(primaryAddress: string): MoneroAccount { + this.primaryAddress = primaryAddress; + return this; + } + + getBalance(): bigint { + return this.balance; + } + + setBalance(balance: bigint): MoneroAccount { + this.balance = balance; + return this; + } + + getUnlockedBalance(): bigint { + return this.unlockedBalance; + } + + setUnlockedBalance(unlockedBalance: bigint): MoneroAccount { + this.unlockedBalance = unlockedBalance; + return this; + } + + getLabel(): string { + return this.label; + } + + setLabel(label: string): MoneroAccount { + this.label = label; + return this; + } + + getTag(): string { + return this.tag; + } + + setTag(tag: string): MoneroAccount { + this.tag = tag; + return this; + } + + getSubaddresses(): MoneroSubaddress[] { + return this.subaddresses; + } + + setSubaddresses(subaddresses: MoneroSubaddress[]): MoneroAccount { + assert(subaddresses === undefined || Array.isArray(subaddresses), "Given subaddresses must be undefined or an array of subaddresses"); + this.subaddresses = subaddresses; + if (subaddresses) { + for (let subaddress of subaddresses) { + subaddress.setAccountIndex(this.index); + } + } + return this; + } + + toString(indent = 0): string { + let str = ""; + str += GenUtils.kvLine("Index", this.getIndex(), indent); + str += GenUtils.kvLine("Primary address", this.getPrimaryAddress(), indent); + str += GenUtils.kvLine("Balance", this.getBalance(), indent); + str += GenUtils.kvLine("Unlocked balance", this.getUnlockedBalance(), indent); + str += GenUtils.kvLine("Label", this.getTag(), indent); + str += GenUtils.kvLine("Tag", this.getTag(), indent); + if (this.getSubaddresses() !== undefined) { + str += GenUtils.kvLine("Subaddresses", "", indent) + for (let i = 0; i < this.getSubaddresses().length; i++) { + str += GenUtils.kvLine(i + 1, "", indent + 1); + str += this.getSubaddresses()[i].toString(indent + 2) + "\n"; + } + } + return str.slice(0, str.length - 1); // strip last newline + } +} diff --git a/src/main/ts/wallet/model/MoneroAccountTag.ts b/src/main/ts/wallet/model/MoneroAccountTag.ts new file mode 100644 index 000000000..7b27d65f0 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroAccountTag.ts @@ -0,0 +1,40 @@ +/** + * Represents an account tag. + */ +export default class MoneroAccountTag { + + tag: string; + label: string; + accountIndices: number[]; + + constructor(accountTag?: Partial) { + Object.assign(this, accountTag); + } + + getTag(): string { + return this.tag; + } + + setTag(tag: string): MoneroAccountTag { + this.tag = tag; + return this; + } + + getLabel(): string { + return this.label; + } + + setLabel(label: string): MoneroAccountTag { + this.label = label; + return this; + } + + getAccountIndices(): number[] { + return this.accountIndices; + } + + setAccountIndices(accountIndices: number[]): MoneroAccountTag { + this.accountIndices = accountIndices; + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroAddressBookEntry.ts b/src/main/ts/wallet/model/MoneroAddressBookEntry.ts new file mode 100644 index 000000000..01c29f9dd --- /dev/null +++ b/src/main/ts/wallet/model/MoneroAddressBookEntry.ts @@ -0,0 +1,54 @@ +/** + * Monero address book entry model + */ +export default class MoneroAddressBookEntry { + + index: number; + address: string; + description: string; + paymentId: string; + + constructor(entry?: Partial) { + Object.assign(this, entry); + } + + toJson(): any { + return Object.assign({}, this); + } + + getIndex(): number { + return this.index; + } + + setIndex(index: number): MoneroAddressBookEntry { + this.index = index; + return this; + } + + getAddress(): string { + return this.address; + } + + setAddress(address: string): MoneroAddressBookEntry { + this.address = address; + return this; + } + + getDescription(): string { + return this.description; + } + + setDescription(description: string): MoneroAddressBookEntry { + this.description = description; + return this; + } + + getPaymentId(): string { + return this.paymentId; + } + + setPaymentId(paymentId: string): MoneroAddressBookEntry { + this.paymentId = paymentId; + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroCheck.ts b/src/main/ts/wallet/model/MoneroCheck.ts new file mode 100644 index 000000000..f43418ca5 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroCheck.ts @@ -0,0 +1,20 @@ +/** + * Base class for results from checking a transaction or reserve proof. + */ +export default class MoneroCheck { + + isGood?: boolean; + + constructor(check?: Partial) { + Object.assign(this, check); + } + + getIsGood(): boolean { + return this.isGood; + } + + setIsGood(isGood: boolean): MoneroCheck { + this.isGood = isGood; + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroCheckReserve.ts b/src/main/ts/wallet/model/MoneroCheckReserve.ts new file mode 100644 index 000000000..b8996c23c --- /dev/null +++ b/src/main/ts/wallet/model/MoneroCheckReserve.ts @@ -0,0 +1,41 @@ +import MoneroCheck from "./MoneroCheck"; + +/** + * Results from checking a reserve proof. + */ +export default class MoneroCheckReserve extends MoneroCheck { + + totalAmount: bigint; + unconfirmedSpentAmount: bigint; + + constructor(check?: Partial) { + super(check); + if (this.totalAmount !== undefined && typeof this.totalAmount !== "bigint") this.totalAmount = BigInt(this.totalAmount); + if (this.unconfirmedSpentAmount !== undefined && typeof this.unconfirmedSpentAmount !== "bigint") this.unconfirmedSpentAmount = BigInt(this.unconfirmedSpentAmount); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getTotalAmount() !== undefined) json.totalAmount = this.getTotalAmount().toString(); + if (this.getUnconfirmedSpentAmount() !== undefined) json.unconfirmedSpentAmount = this.getUnconfirmedSpentAmount().toString(); + return json; + } + + getTotalAmount(): bigint { + return this.totalAmount; + } + + setTotalAmount(totalAmount: bigint): MoneroCheckReserve { + this.totalAmount = totalAmount; + return this; + } + + getUnconfirmedSpentAmount(): bigint { + return this.unconfirmedSpentAmount; + } + + setUnconfirmedSpentAmount(unconfirmedSpentAmount: bigint): MoneroCheckReserve { + this.unconfirmedSpentAmount = unconfirmedSpentAmount; + return this; + } +} \ No newline at end of file diff --git a/src/main/ts/wallet/model/MoneroCheckTx.ts b/src/main/ts/wallet/model/MoneroCheckTx.ts new file mode 100644 index 000000000..22b904f2c --- /dev/null +++ b/src/main/ts/wallet/model/MoneroCheckTx.ts @@ -0,0 +1,49 @@ +import MoneroCheck from "./MoneroCheck"; + +/** + * Results from checking a transaction key. + */ +export default class MoneroCheckTx extends MoneroCheck { + + inTxPool: boolean; + numConfirmations: number; + receivedAmount: bigint; + + constructor(check?: Partial) { + super(check); + if (this.receivedAmount !== undefined && typeof this.receivedAmount !== "bigint") this.receivedAmount = BigInt(this.receivedAmount); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getReceivedAmount() !== undefined) json.receivedAmount = this.getReceivedAmount().toString(); + return json; + } + + getInTxPool(): boolean { + return this.inTxPool; + } + + setInTxPool(inTxPool: boolean): MoneroCheckTx { + this.inTxPool = inTxPool; + return this; + } + + getNumConfirmations(): number { + return this.numConfirmations; + } + + setNumConfirmations(numConfirmations: number): MoneroCheckTx { + this.numConfirmations = numConfirmations; + return this; + } + + getReceivedAmount(): bigint { + return this.receivedAmount; + } + + setReceivedAmount(receivedAmount: bigint): MoneroCheckTx { + this.receivedAmount = receivedAmount; + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroDestination.ts b/src/main/ts/wallet/model/MoneroDestination.ts new file mode 100644 index 000000000..2bffdc3c6 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroDestination.ts @@ -0,0 +1,67 @@ +import GenUtils from "../../common/GenUtils"; +import MoneroError from "../../common/MoneroError"; + +/** + * Models an outgoing transfer destination. + */ +export default class MoneroDestination { + + address: string; + amount: bigint; + + /** + * Construct a destination to send funds to. + * + * @param {Partial|string} destinationOrAddress is a MoneroDestination or hex string to initialize from (optional) + * @param {bigint} [amount] - the destination amount + */ + constructor(destinationOrAddress?: Partial | string, amount?: bigint) { + if (typeof destinationOrAddress === "string") { + this.setAddress(destinationOrAddress); + this.setAmount(amount); + } else { + if (amount !== undefined) throw new Error("Amount parameter must be undefined when initializing a MoneroDestination from a MoneroDestination") + Object.assign(this, destinationOrAddress); + if (this.amount && typeof this.amount !== "bigint") this.amount = BigInt(this.amount); + } + } + + getAddress(): string { + return this.address; + } + + setAddress(address: string | undefined): MoneroDestination { + this.address = address; + return this; + } + + getAmount(): bigint { + return this.amount; + } + + setAmount(amount: bigint): MoneroDestination { + if (amount !== undefined && typeof amount !== "bigint") { + if (typeof amount === "number") throw new MoneroError("Destination amount must be BigInt or string"); + try { amount = BigInt(amount); } + catch (err) { throw new MoneroError("Invalid destination amount: " + amount); } + } + this.amount = amount; + return this; + } + + copy(): MoneroDestination { + return new MoneroDestination(this); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getAmount() !== undefined) json.amount = this.getAmount().toString(); + return json; + } + + toString(indent = 0): string { + let str = GenUtils.kvLine("Address", this.getAddress(), indent); + str += GenUtils.kvLine("Amount", this.getAmount() ? this.getAmount().toString() : undefined, indent); + return str.slice(0, str.length - 1); // strip last newline + } +} \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroIncomingTransfer.js b/src/main/ts/wallet/model/MoneroIncomingTransfer.ts similarity index 53% rename from src/main/js/wallet/model/MoneroIncomingTransfer.js rename to src/main/ts/wallet/model/MoneroIncomingTransfer.ts index 8516f3131..463be73ba 100644 --- a/src/main/js/wallet/model/MoneroIncomingTransfer.js +++ b/src/main/ts/wallet/model/MoneroIncomingTransfer.ts @@ -1,42 +1,45 @@ -const assert = require("assert"); -const GenUtils = require("../../common/GenUtils"); -const MoneroTransfer = require("./MoneroTransfer"); +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroTransfer from "./MoneroTransfer"; +import MoneroTxWallet from "./MoneroTxWallet"; /** * Models an incoming transfer of funds to the wallet. - * - * @extends {MoneroTransfer} */ -class MoneroIncomingTransfer extends MoneroTransfer { +export default class MoneroIncomingTransfer extends MoneroTransfer { + + subaddressIndex: number; + address: string; + numSuggestedConfirmations: number; /** - * Construct the model. + * Construct the transfer. * - * @param {MoneroTransfer|object} state is existing state to initialize from (optional) + * @param {MoneroTransfer} [transfer] is existing state to initialize from (optional) */ - constructor(state) { - super(state); + constructor(transfer?: Partial) { + super(transfer); } - isIncoming() { + getIsIncoming(): boolean { return true; } - getSubaddressIndex() { - return this.state.subaddressIndex; + getSubaddressIndex(): number { + return this.subaddressIndex; } - setSubaddressIndex(subaddressIndex) { - this.state.subaddressIndex = subaddressIndex; + setSubaddressIndex(subaddressIndex: number): MoneroIncomingTransfer { + this.subaddressIndex = subaddressIndex; return this; } - getAddress() { - return this.state.address; + getAddress(): string { + return this.address; } - setAddress(address) { - this.state.address = address; + setAddress(address: string): MoneroIncomingTransfer { + this.address = address; return this; } @@ -48,16 +51,16 @@ class MoneroIncomingTransfer extends MoneroTransfer { * * @return {number} is the number of confirmations before it's not worth rewriting the chain */ - getNumSuggestedConfirmations() { - return this.state.numSuggestedConfirmations; + getNumSuggestedConfirmations(): number { + return this.numSuggestedConfirmations; } - setNumSuggestedConfirmations(numSuggestedConfirmations) { - this.state.numSuggestedConfirmations = numSuggestedConfirmations; + setNumSuggestedConfirmations(numSuggestedConfirmations: number): MoneroIncomingTransfer { + this.numSuggestedConfirmations = numSuggestedConfirmations; return this; } - copy() { + copy(): MoneroIncomingTransfer { return new MoneroIncomingTransfer(this.toJson()); } @@ -69,8 +72,9 @@ class MoneroIncomingTransfer extends MoneroTransfer { * should not be re-used or it should be copied before calling this method. * * @param {MoneroIncomingTransfer} transfer is the transfer to merge into this one + * @return {MoneroIncomingTransfer} */ - merge(transfer) { + merge(transfer: MoneroIncomingTransfer): MoneroIncomingTransfer { super.merge(transfer); assert(transfer instanceof MoneroIncomingTransfer); if (this === transfer) return this; @@ -80,17 +84,28 @@ class MoneroIncomingTransfer extends MoneroTransfer { return this; } - toString() { - return this.toString(0); - } - - toString(indent) { + toString(indent = 0) { let str = super.toString(indent) + "\n"; str += GenUtils.kvLine("Subaddress index", this.getSubaddressIndex(), indent); str += GenUtils.kvLine("Address", this.getAddress(), indent); str += GenUtils.kvLine("Num suggested confirmations", this.getNumSuggestedConfirmations(), indent); return str.slice(0, str.length - 1); // strip last newline } -} -module.exports = MoneroIncomingTransfer; \ No newline at end of file + // -------------------- OVERRIDE COVARIANT RETURN TYPES --------------------- + + setTx(tx: MoneroTxWallet): MoneroIncomingTransfer { + super.setTx(tx); + return this; + } + + setAmount(amount: bigint): MoneroIncomingTransfer { + super.setAmount(amount); + return this; + } + + setAccountIndex(accountIndex: number): MoneroIncomingTransfer { + super.setAccountIndex(accountIndex); + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroIntegratedAddress.ts b/src/main/ts/wallet/model/MoneroIntegratedAddress.ts new file mode 100644 index 000000000..7481916a0 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroIntegratedAddress.ts @@ -0,0 +1,48 @@ +/** + * Monero integrated address model. + */ +export default class MoneroIntegratedAddress { + + standardAddress: string; + paymentId: string; + integratedAddress: string; + + constructor(integratedAddress?: Partial) { + Object.assign(this, integratedAddress); + } + + toJson(): any { + return Object.assign({}, this); + } + + getStandardAddress(): string { + return this.standardAddress; + } + + setStandardAddress(standardAddress: string): MoneroIntegratedAddress { + this.standardAddress = standardAddress; + return this; + } + + getPaymentId(): string { + return this.paymentId; + } + + setPaymentId(paymentId: string): MoneroIntegratedAddress { + this.paymentId = paymentId; + return this; + } + + getIntegratedAddress(): string { + return this.integratedAddress; + } + + setIntegratedAddress(integratedAddress: string): MoneroIntegratedAddress { + this.integratedAddress = integratedAddress; + return this; + } + + toString(): string { + return this.integratedAddress; + } +} diff --git a/src/main/ts/wallet/model/MoneroKeyImageImportResult.ts b/src/main/ts/wallet/model/MoneroKeyImageImportResult.ts new file mode 100644 index 000000000..5bd07f23c --- /dev/null +++ b/src/main/ts/wallet/model/MoneroKeyImageImportResult.ts @@ -0,0 +1,49 @@ +/** + * Models results from importing key images. + */ +export default class MoneroKeyImageImportResult { + + height: number; + spentAmount: bigint; + unspentAmount: bigint; + + constructor(result?: Partial) { + Object.assign(this, result); + if (this.spentAmount !== undefined && typeof this.spentAmount !== "bigint") this.spentAmount = BigInt(this.spentAmount); + if (this.unspentAmount !== undefined && typeof this.unspentAmount !== "bigint") this.unspentAmount = BigInt(this.unspentAmount); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getSpentAmount() !== undefined) json.spentAmount = this.getSpentAmount().toString(); + if (this.getUnspentAmount() !== undefined) json.unspentAmount = this.getUnspentAmount().toString(); + return json; + } + + getHeight(): number { + return this.height; + } + + setHeight(height: number): MoneroKeyImageImportResult { + this.height = height; + return this; + } + + getSpentAmount(): bigint { + return this.spentAmount; + } + + setSpentAmount(spentAmount: bigint): MoneroKeyImageImportResult { + this.spentAmount = spentAmount; + return this; + } + + getUnspentAmount(): bigint { + return this.unspentAmount; + } + + setUnspentAmount(unspentAmount: bigint): MoneroKeyImageImportResult { + this.unspentAmount = unspentAmount; + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroMessageSignatureResult.ts b/src/main/ts/wallet/model/MoneroMessageSignatureResult.ts new file mode 100644 index 000000000..36b32a755 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroMessageSignatureResult.ts @@ -0,0 +1,57 @@ +import MoneroMessageSignatureType from "./MoneroMessageSignatureType"; + +/** + * Message signature verification result. + */ +export default class MoneroMessageSignatureResult { + + isGood: boolean; + isOld: boolean; + signatureType: MoneroMessageSignatureType; + version: number; + + constructor(result?: Partial) { + Object.assign(this, result); + } + + toJson(): any { + return Object.assign({}, this); + } + + getIsGood(): boolean { + return this.isGood; + } + + setIsGood(isGood: boolean): MoneroMessageSignatureResult { + this.isGood = isGood; + return this; + } + + getIsOld(): boolean { + return this.isOld; + } + + setIsOld(isOld: boolean): MoneroMessageSignatureResult { + this.isOld = isOld; + return this; + } + + getSignatureType(): MoneroMessageSignatureType { + return this.signatureType; + } + + setSignatureType(signatureType: MoneroMessageSignatureType): MoneroMessageSignatureResult { + this.signatureType = signatureType; + return this; + } + + getVersion(): number { + return this.version; + } + + setVersion(version: number): MoneroMessageSignatureResult { + this.version = version; + return this; + } + +} \ No newline at end of file diff --git a/src/main/ts/wallet/model/MoneroMessageSignatureType.ts b/src/main/ts/wallet/model/MoneroMessageSignatureType.ts new file mode 100644 index 000000000..eca670804 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroMessageSignatureType.ts @@ -0,0 +1,17 @@ +/** + * Enumerate message signature types. + */ +enum MoneroMessageSignatureType { + + /** + * Sign with spend key (value=0). + */ + SIGN_WITH_SPEND_KEY = 0, + + /** + * Sign with the view key (value=1). + */ + SIGN_WITH_VIEW_KEY = 1 +} + +export default MoneroMessageSignatureType; \ No newline at end of file diff --git a/src/main/ts/wallet/model/MoneroMultisigInfo.ts b/src/main/ts/wallet/model/MoneroMultisigInfo.ts new file mode 100644 index 000000000..4446e0f00 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroMultisigInfo.ts @@ -0,0 +1,54 @@ +/** + * Models information about a multisig wallet. + */ +export default class MoneroMultisigInfo { + + isMultisig: boolean; + isReady: boolean; + threshold: number; + numParticipants: number; + + constructor(multisigInfo?: Partial) { + Object.assign(this, multisigInfo); + } + + toJson(): any { + return Object.assign({}, this); + } + + getIsMultisig(): boolean { + return this.isMultisig; + } + + setIsMultisig(isMultisig: boolean): MoneroMultisigInfo { + this.isMultisig = isMultisig; + return this; + } + + getIsReady(): boolean { + return this.isReady; + } + + setIsReady(isReady: boolean): MoneroMultisigInfo { + this.isReady = isReady; + return this; + } + + getThreshold(): number { + return this.threshold; + } + + setThreshold(threshold: number): MoneroMultisigInfo { + this.threshold = threshold; + return this; + } + + getNumParticipants(): number { + return this.numParticipants; + } + + setNumParticipants(numParticipants: number): MoneroMultisigInfo { + this.numParticipants = numParticipants; + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroMultisigInitResult.ts b/src/main/ts/wallet/model/MoneroMultisigInitResult.ts new file mode 100644 index 000000000..c7f2b01dd --- /dev/null +++ b/src/main/ts/wallet/model/MoneroMultisigInitResult.ts @@ -0,0 +1,36 @@ +/** + * Models the result of initializing a multisig wallet which results in the + * multisig wallet's address xor another multisig hex to share with + * participants to create the wallet. + */ +export default class MoneroMultisigInitResult { + + address: string; + multisigHex: string; + + constructor(result?: Partial) { + Object.assign(this, result); + } + + toJson(): any { + return Object.assign({}, this); + } + + getAddress(): string { + return this.address; + } + + setAddress(address: string): MoneroMultisigInitResult { + this.address = address; + return this; + } + + getMultisigHex(): string { + return this.multisigHex; + } + + setMultisigHex(multisigHex: string): MoneroMultisigInitResult { + this.multisigHex = multisigHex; + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroMultisigSignResult.ts b/src/main/ts/wallet/model/MoneroMultisigSignResult.ts new file mode 100644 index 000000000..a1c17fc78 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroMultisigSignResult.ts @@ -0,0 +1,34 @@ +/** + * Models the result of signing multisig tx hex. + */ +export default class MoneroMultisigSignResult { + + signedMultisigTxHex: string; + txHashes: string[]; + + constructor(result?: Partial) { + Object.assign(this, result); + } + + toJson(): any { + return Object.assign({}, this); + } + + getSignedMultisigTxHex(): string { + return this.signedMultisigTxHex; + } + + setSignedMultisigTxHex(signedTxMultisigHex: string): MoneroMultisigSignResult { + this.signedMultisigTxHex = signedTxMultisigHex; + return this; + } + + getTxHashes(): string[] { + return this.txHashes; + } + + setTxHashes(txHashes: string[]): MoneroMultisigSignResult { + this.txHashes = txHashes; + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroOutgoingTransfer.ts b/src/main/ts/wallet/model/MoneroOutgoingTransfer.ts new file mode 100644 index 000000000..b111c1fa2 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroOutgoingTransfer.ts @@ -0,0 +1,127 @@ +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroDestination from "./MoneroDestination"; +import MoneroTransfer from "./MoneroTransfer"; +import MoneroTxWallet from "./MoneroTxWallet"; + +/** + * Models an outgoing transfer of funds from the wallet. + */ +export default class MoneroOutgoingTransfer extends MoneroTransfer { + + subaddressIndices: number[]; + addresses: string[]; + destinations: MoneroDestination[]; + + /** + * Construct the model. + * + * @param {MoneroOutgoingTranser [transfer] existing state to initialize from (optional) + */ + constructor(transfer?: Partial) { + super(transfer); + + // copy destinations + if (this.destinations) { + this.destinations = this.destinations.slice(); + for (let i = 0; i < this.destinations.length; i++) { + this.destinations[i] = new MoneroDestination(this.destinations[i]); + } + } + } + + getIsIncoming(): boolean { + return false; + } + + getSubaddressIndices(): number[] { + return this.subaddressIndices; + } + + setSubaddressIndices(subaddressIndices: number[]): MoneroOutgoingTransfer { + this.subaddressIndices = subaddressIndices; + return this; + } + + getAddresses(): string[] { + return this.addresses; + } + + setAddresses(addresses: string[]): MoneroOutgoingTransfer { + this.addresses = addresses; + return this; + } + + getDestinations(): MoneroDestination[] { + return this.destinations; + } + + setDestinations(destinations: MoneroDestination[]): MoneroOutgoingTransfer { + this.destinations = destinations; + return this; + } + + copy(): MoneroOutgoingTransfer { + return new MoneroOutgoingTransfer(this); + } + + toJson(): any { + let json = Object.assign({}, this, super.toJson()); // merge json onto inherited state + if (this.getDestinations() !== undefined) { + json.destinations = []; + for (let destination of this.getDestinations()) json.destinations.push(destination.toJson()); + } + delete json.tx; // parent tx is not serialized + return json; + } + + /** + * Updates this transaction by merging the latest information from the given + * transaction. + * + * Merging can modify or build references to the transfer given so it + * should not be re-used or it should be copied before calling this method. + * + * @param transfer is the transfer to merge into this one + */ + merge(transfer: MoneroOutgoingTransfer): MoneroOutgoingTransfer { + super.merge(transfer); + assert(transfer instanceof MoneroOutgoingTransfer); + if (this === transfer) return this; + this.setSubaddressIndices(GenUtils.reconcile(this.getSubaddressIndices(), transfer.getSubaddressIndices())); + this.setAddresses(GenUtils.reconcile(this.getAddresses(), transfer.getAddresses())); + this.setDestinations(GenUtils.reconcile(this.getDestinations(), transfer.getDestinations())); + return this; + } + + toString(indent = 0): string { + let str = super.toString(indent) + "\n"; + str += GenUtils.kvLine("Subaddress indices", this.getSubaddressIndices(), indent); + str += GenUtils.kvLine("Addresses", this.getAddresses(), indent); + if (this.getDestinations() !== undefined) { + str += GenUtils.kvLine("Destinations", "", indent); + for (let i = 0; i < this.getDestinations().length; i++) { + str += GenUtils.kvLine(i + 1, "", indent + 1); + str += this.getDestinations()[i].toString(indent + 2) + "\n"; + } + } + return str.slice(0, str.length - 1); // strip last newline + } + + // -------------------- OVERRIDE COVARIANT RETURN TYPES --------------------- + + setTx(tx: MoneroTxWallet): MoneroOutgoingTransfer { + super.setTx(tx); + return this; + } + + setAmount(amount: bigint): MoneroOutgoingTransfer { + super.setAmount(amount); + return this; + } + + setAccountIndex(accountIndex: number): MoneroOutgoingTransfer { + super.setAccountIndex(accountIndex); + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroOutputQuery.ts b/src/main/ts/wallet/model/MoneroOutputQuery.ts new file mode 100644 index 000000000..6df54497a --- /dev/null +++ b/src/main/ts/wallet/model/MoneroOutputQuery.ts @@ -0,0 +1,189 @@ +import MoneroError from "../../common/MoneroError"; +import MoneroKeyImage from "../../daemon/model/MoneroKeyImage"; +import MoneroOutputWallet from "./MoneroOutputWallet"; +import MoneroTx from "../../daemon/model/MoneroTx"; +import MoneroTxWallet from "./MoneroTxWallet"; +import MoneroTxQuery from "./MoneroTxQuery"; + +/** + * Configuration to query wallet outputs. + */ +export default class MoneroOutputQuery extends MoneroOutputWallet { + + minAmount: bigint; + maxAmount: bigint; + txQuery: Partial; + subaddressIndices: number[]; + + /** + *

Construct the output query.

+ * + *

Example:

+ * + * + * // get available outputs in account 0 with a minimum amount
+ * let outputs = await wallet.getOutputs({
+ *    isSpent: false,
+ *    isLocked: false,
+ *    accountIndex: 0,
+ *    minAmount: BigInt("750000")
+ * }); + *
+ * + *

All configuration is optional. All outputs are returned except those that don't meet criteria defined in this query.

+ * + * @param {MoneroOutputQuery} [config] - output query configuration (optional) + * @param {number} config.accountIndex - get outputs in this account index + * @param {number} config.subaddressIndex - get outputs in this subaddress index + * @param {number[]} config.subaddressIndices - get outputs in these subaddress indices + * @param {bigint} config.amount - get outputs with this amount + * @param {bigint} config.minAmount - get outputs with amount greater than or equal to this amount + * @param {bigint} config.maxAmount - get outputs with amount less than or equal to this amount + * @param {boolean} config.isSpent - get spent xor unspent outputs + * @param {boolean} config.isFrozen - get frozen xor thawed outputs + * @param {MoneroKeyImage} config.keyImage - get outputs with a key image matching fields defined in this key image + * @param {string} config.keyImage.hex - get outputs with this key image hex + * @param {string} config.keyImage.signature - get outputs with this key image signature + * @param {MoneroTxQuery} config.txQuery - get outputs whose tx match this tx query + */ + constructor(query?: Partial) { + super(query); + if (this.minAmount !== undefined && typeof this.minAmount !== "bigint") this.minAmount = BigInt(this.minAmount); + if (this.maxAmount !== undefined && typeof this.maxAmount !== "bigint") this.maxAmount = BigInt(this.maxAmount); + if (this.txQuery && !(this.txQuery instanceof MoneroTxQuery)) this.txQuery = new MoneroTxQuery(this.txQuery); + if (this.txQuery) this.txQuery.setOutputQuery(this); + if (this.isLocked !== undefined) throw new MoneroError("isLocked must be part of tx query, not output query"); + } + + copy(): MoneroOutputQuery { + return new MoneroOutputQuery(this); + } + + toJson(): any { + let json = Object.assign({}, this, super.toJson()); + if (this.getMinAmount() !== undefined) json.minAmount = this.getMinAmount().toString(); + if (this.getMaxAmount() !== undefined) json.maxAmount = this.getMaxAmount().toString(); + delete json.txQuery; + return json; + } + + getMinAmount(): bigint { + return this.minAmount; + } + + setMinAmount(minAmount: bigint): MoneroOutputQuery { + this.minAmount = minAmount; + return this; + } + + getMaxAmount(): bigint { + return this.maxAmount; + } + + setMaxAmount(maxAmount: bigint): MoneroOutputQuery { + this.maxAmount = maxAmount; + return this; + } + + getTxQuery(): MoneroTxQuery { + return this.txQuery as MoneroTxQuery; + } + + setTxQuery(txQuery: MoneroTxQuery): MoneroOutputQuery { + this.txQuery = txQuery === undefined ? undefined : txQuery instanceof MoneroTxQuery ? txQuery : new MoneroTxQuery(txQuery); + if (txQuery) this.txQuery.outputQuery = this; + return this; + } + + getSubaddressIndices(): number[] { + return this.subaddressIndices; + } + + setSubaddressIndices(subaddressIndices: number[]): MoneroOutputQuery { + this.subaddressIndices = subaddressIndices; + return this; + } + + meetsCriteria(output: MoneroOutputWallet, queryParent = true): boolean { + if (!(output instanceof MoneroOutputWallet)) throw new Error("Output not given to MoneroOutputQuery.meetsCriteria(output)"); + + // filter on output + if (this.getAccountIndex() !== undefined && this.getAccountIndex() !== output.getAccountIndex()) return false; + if (this.getSubaddressIndex() !== undefined && this.getSubaddressIndex() !== output.getSubaddressIndex()) return false; + if (this.getAmount() !== undefined && this.getAmount() !== output.getAmount()) return false; + if (this.getIsSpent() !== undefined && this.getIsSpent() !== output.getIsSpent()) return false; + if (this.getIsFrozen() !== undefined && this.getIsFrozen() !== output.getIsFrozen()) return false; + + // filter on output's key image + if (this.getKeyImage() !== undefined) { + if (output.getKeyImage() === undefined) return false; + if (this.getKeyImage().getHex() !== undefined && this.getKeyImage().getHex() !== output.getKeyImage().getHex()) return false; + if (this.getKeyImage().getSignature() !== undefined && this.getKeyImage().getSignature() !== output.getKeyImage().getSignature()) return false; + } + + // filter on extensions + if (this.getSubaddressIndices() !== undefined && !this.getSubaddressIndices().includes(output.getSubaddressIndex())) return false; + + // filter with tx query + if (this.getTxQuery() && !this.getTxQuery().meetsCriteria(output.getTx() as MoneroTxWallet, false)) return false; + + // filter on remaining fields + if (this.getMinAmount() !== undefined && (output.getAmount() === undefined || output.getAmount() < this.getMinAmount())) return false; + if (this.getMaxAmount() !== undefined && (output.getAmount() === undefined || output.getAmount() > this.getMaxAmount())) return false; + + // output meets query + return true; + } + + // -------------------- OVERRIDE COVARIANT RETURN TYPES --------------------- + + setTx(tx: MoneroTx): MoneroOutputQuery { + super.setTx(tx); + return this; + } + + setAccountIndex(accountIndex: number): MoneroOutputQuery { + super.setAccountIndex(accountIndex); + return this; + } + + setSubaddressIndex(subaddressIndex: number): MoneroOutputQuery { + super.setSubaddressIndex(subaddressIndex); + return this; + } + + setIsSpent(isSpent: boolean): MoneroOutputQuery { + super.setIsSpent(isSpent); + return this; + } + + setIsFrozen(isFrozen: boolean): MoneroOutputQuery { + super.setIsFrozen(isFrozen); + return this; + } + + setKeyImage(keyImage: MoneroKeyImage): MoneroOutputQuery { + super.setKeyImage(keyImage); + return this; + } + + setAmount(amount: bigint): MoneroOutputQuery { + super.setAmount(amount); + return this; + } + + setIndex(index: number): MoneroOutputQuery { + super.setIndex(index); + return this; + } + + setRingOutputIndices(ringOutputIndices: number[]): MoneroOutputQuery { + super.setRingOutputIndices(ringOutputIndices); + return this; + } + + setStealthPublicKey(stealthPublicKey: string): MoneroOutputQuery { + super.setStealthPublicKey(stealthPublicKey); + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroOutputWallet.ts b/src/main/ts/wallet/model/MoneroOutputWallet.ts new file mode 100644 index 000000000..ff8590f46 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroOutputWallet.ts @@ -0,0 +1,151 @@ +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroError from "../../common/MoneroError"; +import MoneroKeyImage from "../../daemon/model/MoneroKeyImage"; +import MoneroOutput from "../../daemon/model/MoneroOutput"; +import MoneroTx from "../../daemon/model/MoneroTx"; +import MoneroTxWallet from "./MoneroTxWallet"; + +/** + * Models a Monero output with wallet extensions. + */ +export default class MoneroOutputWallet extends MoneroOutput { + + accountIndex: number; + subaddressIndex: number; + isSpent: boolean; + isFrozen: boolean; + isLocked: boolean; + + /** + * Construct the model. + * + * @param {MoneroOutputWallet} [output] is existing state to initialize from (optional) + */ + constructor(output?: Partial) { + super(output); + } + + getTx(): MoneroTxWallet { + return super.getTx() as MoneroTxWallet; + } + + setTx(tx: MoneroTx): MoneroOutputWallet { + if (tx !== undefined && !(tx instanceof MoneroTxWallet)) throw new MoneroError("Wallet output's transaction must be of type MoneroTxWallet"); + super.setTx(tx); + return this; + } + + getAccountIndex(): number { + return this.accountIndex; + } + + setAccountIndex(accountIndex: number): MoneroOutputWallet { + this.accountIndex = accountIndex; + return this; + } + + getSubaddressIndex(): number { + return this.subaddressIndex; + } + + setSubaddressIndex(subaddressIndex: number): MoneroOutputWallet { + this.subaddressIndex = subaddressIndex; + return this; + } + + getIsSpent(): boolean { + return this.isSpent; + } + + setIsSpent(isSpent: boolean): MoneroOutputWallet { + this.isSpent = isSpent; + return this; + } + + /** + * Indicates if this output has been deemed 'malicious' and will therefore + * not be spent by the wallet. + * + * @return Boolean is whether or not this output is frozen + */ + getIsFrozen(): boolean { + return this.isFrozen; + } + + setIsFrozen(isFrozen: boolean): MoneroOutputWallet { + this.isFrozen = isFrozen; + return this; + } + + getIsLocked(): boolean { + if (this.getTx() === undefined) return undefined; + return (this.getTx() as MoneroTxWallet).getIsLocked(); + } + + copy(): MoneroOutputWallet { + return new MoneroOutputWallet(this.toJson()); + } + + toJson(): any { + let json: any = Object.assign({}, this, super.toJson()); + delete json.tx; + return json; + } + + /** + * Updates this output by merging the latest information from the given + * output. + * + * Merging can modify or build references to the output given so it + * should not be re-used or it should be copied before calling this method. + * + * @param output is the output to merge into this one + */ + merge(output: MoneroOutputWallet): MoneroOutputWallet { + assert(output instanceof MoneroOutputWallet); + if (this === output) return; + super.merge(output); + this.setAccountIndex(GenUtils.reconcile(this.getAccountIndex(), output.getAccountIndex())); + this.setSubaddressIndex(GenUtils.reconcile(this.getSubaddressIndex(), output.getSubaddressIndex())); + this.setIsSpent(GenUtils.reconcile(this.getIsSpent(), output.getIsSpent(), {resolveTrue: true})); // output can become spent + this.setIsFrozen(GenUtils.reconcile(this.getIsFrozen(), output.getIsFrozen())); + return this; + } + + toString(indent = 0): string { + let str = super.toString(indent) + "\n" + str += GenUtils.kvLine("Account index", this.getAccountIndex(), indent); + str += GenUtils.kvLine("Subaddress index", this.getSubaddressIndex(), indent); + str += GenUtils.kvLine("Is spent", this.getIsSpent(), indent); + str += GenUtils.kvLine("Is frozen", this.getIsFrozen(), indent); + return str.slice(0, str.length - 1); // strip last newline + } + + // -------------------- OVERRIDE COVARIANT RETURN TYPES --------------------- + + setKeyImage(keyImage: MoneroKeyImage): MoneroOutputWallet { + super.setKeyImage(keyImage); + return this; + } + + setAmount(amount: bigint): MoneroOutputWallet { + super.setAmount(amount); + return this; + } + + setIndex(index: number): MoneroOutputWallet { + super.setIndex(index); + return this; + } + + setRingOutputIndices(ringOutputIndices: number[]): MoneroOutputWallet { + super.setRingOutputIndices(ringOutputIndices); + return this; + } + + setStealthPublicKey(stealthPublicKey: string): MoneroOutputWallet { + super.setStealthPublicKey(stealthPublicKey); + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroSubaddress.ts b/src/main/ts/wallet/model/MoneroSubaddress.ts new file mode 100644 index 000000000..883512fb9 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroSubaddress.ts @@ -0,0 +1,125 @@ +import GenUtils from "../../common/GenUtils"; + +/** + * Monero subaddress model. + */ +export default class MoneroSubaddress { + + accountIndex: number; + index: number; + address: string; + label: string; + balance: bigint; + unlockedBalance: bigint; + numUnspentOutputs: number; + isUsed: boolean; + numBlocksToUnlock: number; + + constructor(subaddress?: Partial) { + Object.assign(this, subaddress); + if (this.balance !== undefined && typeof this.balance !== "bigint") this.balance = BigInt(this.balance); + if (this.unlockedBalance !== undefined && typeof this.unlockedBalance !== "bigint") this.unlockedBalance = BigInt(this.unlockedBalance); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (json.balance !== undefined) json.balance = json.balance.toString(); + if (json.unlockedBalance !== undefined) json.unlockedBalance = json.unlockedBalance.toString(); + return json; + } + + getAccountIndex(): number { + return this.accountIndex; + } + + setAccountIndex(accountIndex: number): MoneroSubaddress { + this.accountIndex = accountIndex; + return this; + } + + getIndex(): number { + return this.index; + } + + setIndex(index: number): MoneroSubaddress { + this.index = index; + return this; + } + + getAddress(): string { + return this.address; + } + + setAddress(address: string): MoneroSubaddress { + this.address = address; + return this; + } + + getLabel(): string { + return this.label; + } + + setLabel(label: string): MoneroSubaddress { + this.label = label; + return this; + } + + getBalance(): bigint { + return this.balance; + } + + setBalance(balance: bigint): MoneroSubaddress { + this.balance = balance; + return this; + } + + getUnlockedBalance(): bigint { + return this.unlockedBalance; + } + + setUnlockedBalance(unlockedBalance: bigint): MoneroSubaddress { + this.unlockedBalance = unlockedBalance; + return this; + } + + getNumUnspentOutputs(): number { + return this.numUnspentOutputs; + } + + setNumUnspentOutputs(numUnspentOutputs: number): MoneroSubaddress { + this.numUnspentOutputs = numUnspentOutputs; + return this; + } + + getIsUsed(): boolean { + return this.isUsed; + } + + setIsUsed(isUsed: boolean): MoneroSubaddress { + this.isUsed = isUsed; + return this; + } + + getNumBlocksToUnlock(): number { + return this.numBlocksToUnlock; + } + + setNumBlocksToUnlock(numBlocksToUnlock: number): MoneroSubaddress { + this.numBlocksToUnlock = numBlocksToUnlock; + return this; + } + + toString(indent = 0): string { + let str = ""; + str += GenUtils.kvLine("Account index", this.getAccountIndex(), indent); + str += GenUtils.kvLine("Subaddress index", this.getIndex(), indent); + str += GenUtils.kvLine("Address", this.getAddress(), indent); + str += GenUtils.kvLine("Label", this.getLabel(), indent); + str += GenUtils.kvLine("Balance", this.getBalance(), indent); + str += GenUtils.kvLine("Unlocked balance", this.getUnlockedBalance(), indent); + str += GenUtils.kvLine("Num unspent outputs", this.getNumUnspentOutputs(), indent); + str += GenUtils.kvLine("Is used", this.getIsUsed(), indent); + str += GenUtils.kvLine("Num blocks to unlock", this.getNumBlocksToUnlock(), indent); + return str.slice(0, str.length - 1); // strip last newline + } +} diff --git a/src/main/js/wallet/model/MoneroSyncResult.js b/src/main/ts/wallet/model/MoneroSyncResult.ts similarity index 50% rename from src/main/js/wallet/model/MoneroSyncResult.js rename to src/main/ts/wallet/model/MoneroSyncResult.ts index c9c52edf3..72dfcec33 100644 --- a/src/main/js/wallet/model/MoneroSyncResult.js +++ b/src/main/ts/wallet/model/MoneroSyncResult.ts @@ -1,30 +1,31 @@ /** * Result from syncing a Monero wallet. */ -class MoneroSyncResult { +export default class MoneroSyncResult { + + numBlocksFetched: number; + receivedMoney: bigint; - constructor(numBlocksFetched, receivedMoney) { + constructor(numBlocksFetched: number, receivedMoney: bigint) { this.setNumBlocksFetched(numBlocksFetched); this.setReceivedMoney(receivedMoney); } - getNumBlocksFetched() { + getNumBlocksFetched(): number { return this.numBlocksFetched; } - setNumBlocksFetched(numBlocksFetched) { + setNumBlocksFetched(numBlocksFetched: number): MoneroSyncResult { this.numBlocksFetched = numBlocksFetched; return this; } - getReceivedMoney() { + getReceivedMoney(): bigint { return this.receivedMoney; } - setReceivedMoney(receivedMoney) { + setReceivedMoney(receivedMoney: bigint): MoneroSyncResult { this.receivedMoney = receivedMoney; return this; } } - -module.exports = MoneroSyncResult; \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroTransfer.js b/src/main/ts/wallet/model/MoneroTransfer.ts similarity index 55% rename from src/main/js/wallet/model/MoneroTransfer.js rename to src/main/ts/wallet/model/MoneroTransfer.ts index ce6dbb4ea..81e18f5ec 100644 --- a/src/main/js/wallet/model/MoneroTransfer.js +++ b/src/main/ts/wallet/model/MoneroTransfer.ts @@ -1,81 +1,74 @@ -const assert = require("assert"); -const BigInteger = require("../../common/biginteger").BigInteger; -const GenUtils = require("../../common/GenUtils"); +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroError from "../../common/MoneroError"; +import MoneroTxWallet from "./MoneroTxWallet"; /** * Models a base transfer of funds to or from the wallet. - * - * @class */ -class MoneroTransfer { +export default class MoneroTransfer { + + tx: MoneroTxWallet; + accountIndex: number; + amount: bigint; /** - * Construct the model. + * Construct the transfer. * - * @param {MoneroTransfer|object} state is existing state to initialize from (optional) + * @param {Partial} transfer existing state to initialize from (optional) */ - constructor(state) { - - // initialize internal state - if (!state) state = {}; - else if (state instanceof MoneroTransfer) state = state.toJson(); - else if (typeof state === "object") state = Object.assign({}, state); - else throw new MoneroError("state must be a MoneroTransfer or JavaScript object"); - this.state = state; - - // deserialize fields if necessary - if (state.amount !== undefined && !(state.amount instanceof BigInteger)) state.amount = BigInteger.parse(state.amount); - - // validate state - this._validate(); + constructor(transfer: Partial) { + Object.assign(this, transfer); + if (this.amount !== undefined && typeof this.amount !== "bigint") this.amount = BigInt(this.amount); + this.validate(); } - copy() { + copy(): MoneroTransfer { return new MoneroTransfer(this); } - toJson() { - let json = Object.assign({}, this.state); - if (this.getAmount()) json.amount = this.getAmount().toString() + toJson(): any { + let json: any = Object.assign({}, this); + if (this.getAmount() !== undefined) json.amount = this.getAmount().toString() delete json.tx; // parent tx is not serialized return json; } - getTx() { - return this.state.tx; + getTx(): MoneroTxWallet { + return this.tx; } - setTx(tx) { - this.state.tx = tx; + setTx(tx: MoneroTxWallet): MoneroTransfer { + this.tx = tx; return this; } - isOutgoing() { - let isIncoming = this.isIncoming(); + getIsOutgoing(): boolean { + let isIncoming = this.getIsIncoming(); assert(typeof isIncoming === "boolean"); return !isIncoming; } - isIncoming() { + getIsIncoming(): boolean { throw new Error("Subclass must implement"); } - getAccountIndex() { - return this.state.accountIndex; + getAccountIndex(): number { + return this.accountIndex; } - setAccountIndex(accountIndex) { - this.state.accountIndex = accountIndex; - this._validate(); + setAccountIndex(accountIndex: number): MoneroTransfer { + this.accountIndex = accountIndex; + this.validate(); return this; } - getAmount() { - return this.state.amount; + getAmount(): bigint { + return this.amount; } - setAmount(amount) { - this.state.amount = amount; + setAmount(amount: bigint): MoneroTransfer { + this.amount = amount; return this; } @@ -89,7 +82,7 @@ class MoneroTransfer { * @param transfer is the transfer to merge into this one * @return {MoneroTransfer} the merged transfer */ - merge(transfer) { + merge(transfer: MoneroTransfer): MoneroTransfer { assert(transfer instanceof MoneroTransfer); if (this === transfer) return this; @@ -103,7 +96,7 @@ class MoneroTransfer { this.setAccountIndex(GenUtils.reconcile(this.getAccountIndex(), transfer.getAccountIndex())); // TODO monero-project: failed tx in pool (after testUpdateLockedDifferentAccounts()) causes non-originating saved wallets to return duplicate incoming transfers but one has amount of 0 - if (this.getAmount() !== undefined && transfer.getAmount() !== undefined && this.getAmount().compare(transfer.getAmount()) !== 0 && (this.getAmount().compare(BigInteger.parse("0")) === 0 || transfer.getAmount().compare(BigInteger.parse("0")) === 0)) { + if (this.getAmount() !== undefined && transfer.getAmount() !== undefined && this.getAmount() !== transfer.getAmount() && (this.getAmount() === 0n || transfer.getAmount() === 0n)) { console.warn("monero-project returning transfers with 0 amount/numSuggestedConfirmations"); } else { this.setAmount(GenUtils.reconcile(this.getAmount(), transfer.getAmount())); @@ -112,17 +105,15 @@ class MoneroTransfer { return this; } - toString(indent = 0) { + toString(indent = 0): string { let str = ""; - str += GenUtils.kvLine("Is incoming", this.isIncoming(), indent); + str += GenUtils.kvLine("Is incoming", this.getIsIncoming(), indent); str += GenUtils.kvLine("Account index", this.getAccountIndex(), indent); str += GenUtils.kvLine("Amount", this.getAmount() ? this.getAmount().toString() : undefined, indent); return str === "" ? str : str.slice(0, str.length - 1); // strip last newline } - _validate() { + protected validate() { if (this.getAccountIndex() !== undefined && this.getAccountIndex() < 0) throw new MoneroError("Account index must be >= 0"); } } - -module.exports = MoneroTransfer; \ No newline at end of file diff --git a/src/main/ts/wallet/model/MoneroTransferQuery.ts b/src/main/ts/wallet/model/MoneroTransferQuery.ts new file mode 100644 index 000000000..2b3b7dd29 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroTransferQuery.ts @@ -0,0 +1,241 @@ +import GenUtils from "../../common/GenUtils"; +import MoneroDestination from "./MoneroDestination"; +import MoneroIncomingTransfer from "./MoneroIncomingTransfer"; +import MoneroOutgoingTransfer from "./MoneroOutgoingTransfer"; +import MoneroTransfer from "./MoneroTransfer"; +import MoneroTxWallet from "./MoneroTxWallet"; +import MoneroTxQuery from "./MoneroTxQuery"; +import MoneroError from "../../common/MoneroError"; + +/** + * Configuration to query wallet transfers. + */ +export default class MoneroTransferQuery extends MoneroTransfer { + + txQuery: Partial; + isIncoming: boolean; + address: string; + addresses: string[]; + subaddressIndex: number; + subaddressIndices: number[]; + destinations: MoneroDestination[]; + hasDestinations: boolean; + + /** + *

Construct the transfer query.

+ * + *

Example:

+ * + * + * // get incoming transfers to account 0, subaddress 1
+ * let transfers = await wallet.getTransfers({
+ *    accountIndex: 0,
+ *    subaddressIndex: 0
+ * }); + *
+ * + *

All configuration is optional. All transfers are returned except those that don't meet criteria defined in this query.

+ * + * @param {Partial} [query] - transfer query configuration (optional) + * @param {bigint} query.amount - get transfers with this amount + * @param {number} query.accountIndex - get transfers to/from this account index + * @param {number} query.subaddressIndex - get transfers to/from this subaddress index + * @param {number[]} query.subaddressIndices - get transfers to/from these subaddress indices + * @param {string} query.address - get transfers to/from this wallet address + * @param {string[]} query.addresses - get transfers to/from these wallet addresses + * @param {boolean} query.isIncoming - get transfers which are incoming if true + * @param {boolean} query.isOutgoing - get transfers which are outgoing if true + * @param {boolean} query.hasDestinations - get transfers with known destinations if true (destinations are only stored locally with the wallet) + * @param {MoneroTxQuery} query.txQuery - get transfers whose tx match this tx query + */ + constructor(query?: Partial) { + super(query); + if (this.txQuery && !(this.txQuery instanceof MoneroTxQuery)) this.txQuery = new MoneroTxQuery(this.txQuery); + if (this.txQuery) this.txQuery.setTransferQuery(this); + + // alias isOutgoing to isIncoming + if ((this as any).isOutgoing !== undefined) this.isIncoming = !(this as any).isOutgoing; + this.validate(); + } + + copy(): MoneroTransferQuery { + return new MoneroTransferQuery(this); + } + + toJson(): any { + let json = Object.assign({}, this, super.toJson()); + delete json.txQuery; + return json; + } + + getTxQuery(): MoneroTxQuery { + return this.txQuery as MoneroTxQuery; + } + + setTxQuery(txQuery: MoneroTxQuery): MoneroTransferQuery { + this.txQuery = txQuery; + if (txQuery) txQuery.setTransferQuery(this); + return this; + } + + getIsIncoming(): boolean { + return this.isIncoming; + } + + setIsIncoming(isIncoming: boolean): MoneroTransferQuery { + this.isIncoming = isIncoming; + return this; + } + + getIsOutgoing(): boolean { + return this.isIncoming === undefined ? undefined : !this.isIncoming; + } + + setIsOutgoing(isOutgoing: boolean): MoneroTransferQuery { + this.isIncoming = isOutgoing === undefined ? undefined : !isOutgoing; + return this; + } + + getAddress(): string { + return this.address; + } + + setAddress(address: string): MoneroTransferQuery { + this.address = address; + return this; + } + + getAddresses(): string[] { + return this.addresses; + } + + setAddresses(addresses: string[]): MoneroTransferQuery { + this.addresses = addresses; + return this; + } + + getSubaddressIndex(): number { + return this.subaddressIndex; + } + + setSubaddressIndex(subaddressIndex: number): MoneroTransferQuery { + this.subaddressIndex = subaddressIndex; + this.validate(); + return this; + } + + getSubaddressIndices(): number[] { + return this.subaddressIndices; + } + + setSubaddressIndices(subaddressIndices: number[]): MoneroTransferQuery { + this.subaddressIndices = subaddressIndices; + this.validate(); + return this; + } + + getDestinations(): MoneroDestination[] { + return this.destinations; + } + + setDestinations(destinations: MoneroDestination[]) { + this.destinations = destinations; + return this; + } + + getHasDestinations(): boolean { + return this.hasDestinations; + } + + setHasDestinations(hasDestinations: boolean): MoneroTransferQuery { + this.hasDestinations = hasDestinations; + return this; + } + + /** + * Convenience method to query outputs by the locked state of their tx. + * + * @param isLocked specifies if the output's tx must be locked or unlocked (optional) + * @return {MoneroOutputQuery} this query for chaining + */ + setIsLocked(isLocked: boolean): MoneroTransferQuery { + if (this.txQuery === undefined) this.txQuery = new MoneroTxQuery(); + this.getTxQuery().setIsLocked(isLocked); + return this; + } + + meetsCriteria(transfer: MoneroTransfer, queryParent = true): boolean { + if (!(transfer instanceof MoneroTransfer)) throw new Error("Transfer not given to MoneroTransferQuery.meetsCriteria(transfer)"); + + // filter on common fields + if (this.getIsIncoming() !== undefined && this.getIsIncoming() !== transfer.getIsIncoming()) return false; + if (this.getIsOutgoing() !== undefined && this.getIsOutgoing() !== transfer.getIsOutgoing()) return false; + if (this.getAmount() !== undefined && this.getAmount() !== transfer.getAmount()) return false; + if (this.getAccountIndex() !== undefined && this.getAccountIndex() !== transfer.getAccountIndex()) return false; + + // filter on incoming fields + if (transfer instanceof MoneroIncomingTransfer) { + if (this.getHasDestinations() !== undefined) return false; + if (this.getAddress() !== undefined && this.getAddress() !== transfer.getAddress()) return false; + if (this.getAddresses() !== undefined && !this.getAddresses().includes(transfer.getAddress())) return false; + if (this.getSubaddressIndex() !== undefined && this.getSubaddressIndex() !== transfer.getSubaddressIndex()) return false; + if (this.getSubaddressIndices() !== undefined && !this.getSubaddressIndices().includes(transfer.getSubaddressIndex())) return false; + } + + // filter on outgoing fields + else if (transfer instanceof MoneroOutgoingTransfer) { + + // filter on addresses which must have overlap + if (this.getAddress() !== undefined && (transfer.getAddresses() === undefined || !transfer.getAddresses().includes(this.getAddress()))) return false; // TODO: will filter all transfers that don't contain addresses (outgoing txs might not have this field initialized) + if (this.getAddresses() !== undefined) { + if (!transfer.getAddresses()) return false; + if (!this.getAddresses().some(address => transfer.getAddresses().includes(address))) return false; + } + + // filter on subaddress indices + if (this.getSubaddressIndex() !== undefined && (transfer.getSubaddressIndices() === undefined || !transfer.getSubaddressIndices().includes(this.getSubaddressIndex()))) return false; + if (this.getSubaddressIndices() !== undefined) { + if (!transfer.getSubaddressIndices()) return false; + if (!this.getSubaddressIndices().some(subaddressIdx => transfer.getSubaddressIndices().includes(subaddressIdx))) return false; + } + + // filter on having destinations + if (this.getHasDestinations() !== undefined) { + if (this.getHasDestinations() && transfer.getDestinations() === undefined) return false; + if (!this.getHasDestinations() && transfer.getDestinations() !== undefined) return false; + } + + // filter on destinations TODO: start with test for this +// if (this.getDestionations() !== undefined && this.getDestionations() !== transfer.getDestionations()) return false; + } + + // otherwise invalid type + else throw new Error("Transfer must be MoneroIncomingTransfer or MoneroOutgoingTransfer"); + + // filter with tx filter + if (queryParent && this.getTxQuery() !== undefined && !this.getTxQuery().meetsCriteria(transfer.getTx())) return false; + return true; + } + + validate() { + if (this.getSubaddressIndex() !== undefined && this.getSubaddressIndex() < 0) throw new MoneroError("Subaddress index must be >= 0"); + if (this.getSubaddressIndices() !== undefined) for (let subaddressIdx of this.getSubaddressIndices()) if (subaddressIdx < 0) throw new MoneroError("Subaddress indices must be >= 0"); + } + + // -------------------- OVERRIDE COVARIANT RETURN TYPES --------------------- + + setTx(tx: MoneroTxWallet): MoneroTransferQuery { + super.setTx(tx); + return this; + } + + setAmount(amount: bigint): MoneroTransferQuery { + super.setAmount(amount); + return this; + } + + setAccountIndex(accountIndex: number): MoneroTransferQuery { + super.setAccountIndex(accountIndex); + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroTxConfig.ts b/src/main/ts/wallet/model/MoneroTxConfig.ts new file mode 100644 index 000000000..54588de2e --- /dev/null +++ b/src/main/ts/wallet/model/MoneroTxConfig.ts @@ -0,0 +1,327 @@ +import assert from "assert"; +import MoneroDestination from "./MoneroDestination"; +import MoneroError from "../../common/MoneroError"; + +/** + * Configures a transaction to send, sweep, or create a payment URI. + */ +export default class MoneroTxConfig { + + address: string; + amount: bigint; + accountIndex: number; + subaddressIndex: number; + subaddressIndices: number[]; + relay: boolean; + priority: number; + destinations: Partial[]; + subtractFeeFrom: number[]; + paymentId: string; + unlockTime: bigint; + fee: bigint; + note: string; + recipientName: string; + canSplit: boolean; + belowAmount: bigint; + sweepEachSubaddress: boolean; + keyImage: string; + + /** + *

Generic request to transfer funds from a wallet.

+ * + *

Example:

+ * + * + * let config1 = new MoneroTxConfig({
+ *    accountIndex: 0,
+ *    address: "59aZULsUF3YN...",
+ *    amount: BigInt("500000"),
+ *    priority: MoneroTxPriority.NORMAL,
+ *    relay: true
+ * }); + *
+ * + * @param {Partial} [config] - configures the transaction to create (optional) + * @param {string} [config.address] - single destination address + * @param {bigint} [config.amount] - single destination amount + * @param {number} [config.accountIndex] - source account index to transfer funds from + * @param {number} [config.subaddressIndex] - source subaddress index to transfer funds from + * @param {number[]} [config.subaddressIndices] - source subaddress indices to transfer funds from + * @param {boolean} [config.relay] - relay the transaction to peers to commit to the blockchain + * @param {MoneroTxPriority} [config.priority] - transaction priority (default MoneroTxPriority.NORMAL) + * @param {MoneroDestination[]} [config.destinations] - addresses and amounts in a multi-destination tx + * @param {number[]} [config.subtractFeeFrom] - list of destination indices to split the transaction fee + * @param {string} [config.paymentId] - transaction payment ID + * @param {bigint} [config.unlockTime] - minimum height or timestamp for the transaction to unlock (default 0) + * @param {string} [config.note] - transaction note saved locally with the wallet + * @param {string} [config.recipientName] - recipient name saved locally with the wallet + * @param {boolean} [config.canSplit] - allow funds to be transferred using multiple transactions + * @param {bigint} [config.belowAmount] - for sweep requests, include outputs below this amount when sweeping wallet, account, subaddress, or all unlocked funds + * @param {boolean} [config.sweepEachSubaddress] - for sweep requests, sweep each subaddress individually instead of together if true + * @param {string} [config.keyImage] - key image to sweep (ignored except in sweepOutput() requests) + */ + constructor(config?: Partial) { + Object.assign(this, config); + + // deserialize bigints + if (this.amount !== undefined && typeof this.amount !== "bigint") this.amount = BigInt(this.amount); + if (this.fee !== undefined && typeof this.fee !== "bigint") this.fee = BigInt(this.fee); + if (this.unlockTime !== undefined && typeof this.unlockTime !== "bigint") this.unlockTime = BigInt(this.unlockTime); + if (this.belowAmount !== undefined && typeof this.belowAmount !== "bigint") this.belowAmount = BigInt(this.belowAmount); + + // copy destinations + if (this.destinations) { + assert(this.address === undefined && this.amount === undefined, "Tx configuration may specify destinations or an address/amount but not both"); + this.setDestinations(this.destinations.map(destination => new MoneroDestination(destination))); + } + + // alias 'address' and 'amount' to single destination to support e.g. createTx({address: "..."}) + if (this.address || this.amount) { + assert(!this.destinations, "Tx configuration may specify destinations or an address/amount but not both"); + this.setAddress(this.address); + this.setAmount(this.amount); + delete this.address; + delete this.amount; + } + + // alias 'subaddressIndex' to subaddress indices + if (this.subaddressIndex !== undefined) { + this.setSubaddressIndices([this.subaddressIndex]); + delete this.subaddressIndex; + } + } + + copy(): MoneroTxConfig { + return new MoneroTxConfig(this); + } + + toJson(): any { + let json: any = Object.assign({}, this); // copy state + if (this.getDestinations() !== undefined) { + json.destinations = []; + for (let destination of this.getDestinations()) json.destinations.push(destination.toJson()); + } + if (this.getFee()) json.fee = this.getFee().toString(); + if (this.getUnlockTime()) json.unlockTime = this.getUnlockTime().toString(); + if (this.getBelowAmount()) json.belowAmount = this.getBelowAmount().toString(); + return json; + } + + /** + * Set the address of a single-destination configuration. + * + * @param {string} address - the address to set for the single destination + * @return {MoneroTxConfig} this configuration for chaining + */ + setAddress(address: string): MoneroTxConfig { + if (this.destinations !== undefined && this.destinations.length > 1) throw new MoneroError("Cannot set address because MoneroTxConfig already has multiple destinations"); + if (this.destinations === undefined || this.destinations.length === 0) this.addDestination(new MoneroDestination(address)); + else (this.destinations[0] as MoneroDestination).setAddress(address); + return this; + } + + /** + * Get the address of a single-destination configuration. + * + * @return {string} the address of the single destination + */ + getAddress(): string { + if (this.destinations === undefined || this.destinations.length !== 1) throw new MoneroError("Cannot get address because MoneroTxConfig does not have exactly one destination"); + return (this.destinations[0] as MoneroDestination).getAddress(); + } + + /** + * Set the amount of a single-destination configuration. + * + * @param {bigint} amount - the amount to set for the single destination + * @return {MoneroTxConfig} this configuration for chaining + */ + setAmount(amount: bigint): MoneroTxConfig { + if (amount !== undefined && typeof this.amount !== "bigint") { + if (typeof amount === "number") throw new MoneroError("Destination amount must be bigint or string"); + try { amount = BigInt(amount); } + catch (err) { throw new MoneroError("Invalid destination amount: " + amount); } + } + if (this.destinations !== undefined && this.destinations.length > 1) throw new MoneroError("Cannot set amount because MoneroTxConfig already has multiple destinations"); + if (this.destinations === undefined || this.destinations.length === 0) this.addDestination(new MoneroDestination(undefined, amount)); + else (this.destinations[0] as MoneroDestination).setAmount(amount); + return this; + } + + /** + * Get the amount of a single-destination configuration. + * + * @return {bigint} the amount of the single destination + */ + getAmount(): bigint { + if (this.destinations === undefined || this.destinations.length !== 1) throw new MoneroError("Cannot get amount because MoneroTxConfig does not have exactly one destination"); + return (this.destinations[0] as MoneroDestination).getAmount(); + } + + addDestination(destinationOrAddress: MoneroDestination | string, amount?: bigint) { + if (typeof destinationOrAddress === "string") return this.addDestination(new MoneroDestination(destinationOrAddress, amount)); + assert(destinationOrAddress instanceof MoneroDestination); + if (this.destinations === undefined) this.destinations = []; + this.destinations.push(destinationOrAddress); + return this; + } + + getDestinations(): MoneroDestination[] { + return this.destinations as MoneroDestination[]; + } + + setDestinations(destinations: MoneroDestination[]): MoneroTxConfig { + if (arguments.length > 1) destinations = Array.from(arguments); + this.destinations = destinations; + return this; + } + + setDestination(destination: MoneroDestination): MoneroTxConfig { + return this.setDestinations(destination ? [destination] : undefined); + } + + getSubtractFeeFrom(): number[] { + return this.subtractFeeFrom; + } + + setSubtractFeeFrom(destinationIndices: number[]): MoneroTxConfig { + if (arguments.length > 1) destinationIndices = Array.from(arguments); + this.subtractFeeFrom = destinationIndices; + return this; + } + + getPaymentId(): string { + return this.paymentId; + } + + setPaymentId(paymentId: string): MoneroTxConfig { + this.paymentId = paymentId; + return this; + } + + getPriority(): number { + return this.priority; + } + + setPriority(priority: number): MoneroTxConfig { + this.priority = priority; + return this; + } + + getFee(): bigint { + return this.fee; + } + + setFee(fee: bigint): MoneroTxConfig { + this.fee = fee; + return this; + } + + getAccountIndex(): number { + return this.accountIndex; + } + + setAccountIndex(accountIndex: number): MoneroTxConfig { + this.accountIndex = accountIndex; + return this; + } + + setSubaddressIndex(subaddressIndex: number): MoneroTxConfig { + this.setSubaddressIndices([subaddressIndex]); + return this; + } + + getSubaddressIndices(): number[] { + return this.subaddressIndices; + } + + setSubaddressIndices(subaddressIndices: number[]): MoneroTxConfig { + if (arguments.length > 1) subaddressIndices = Array.from(arguments); + this.subaddressIndices = subaddressIndices; + return this; + } + + getUnlockTime(): bigint { + return this.unlockTime; + } + + setUnlockTime(unlockTime: bigint): MoneroTxConfig { + this.unlockTime = unlockTime; + return this; + } + + getRelay(): boolean { + return this.relay; + } + + setRelay(relay: boolean): MoneroTxConfig { + this.relay = relay; + return this; + } + + getCanSplit(): boolean { + return this.canSplit; + } + + setCanSplit(canSplit: boolean): MoneroTxConfig { + this.canSplit = canSplit; + return this; + } + + getNote(): string { + return this.note; + } + + setNote(note: string): MoneroTxConfig { + this.note = note; + return this; + } + + getRecipientName(): string { + return this.recipientName; + } + + setRecipientName(recipientName: string): MoneroTxConfig { + this.recipientName = recipientName; + return this; + } + + // --------------------------- SPECIFIC TO SWEEP ---------------------------- + + getBelowAmount() { + return this.belowAmount; + } + + setBelowAmount(belowAmount) { + this.belowAmount = belowAmount; + return this; + } + + getSweepEachSubaddress() { + return this.sweepEachSubaddress; + } + + setSweepEachSubaddress(sweepEachSubaddress) { + this.sweepEachSubaddress = sweepEachSubaddress; + return this; + } + + /** + * Get the key image hex of the output to sweep. + * + * return {string} is the key image hex of the output to sweep + */ + getKeyImage() { + return this.keyImage; + } + + /** + * Set the key image hex of the output to sweep. + * + * @param {string} keyImage is the key image hex of the output to sweep + */ + setKeyImage(keyImage) { + this.keyImage = keyImage; + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroTxPriority.ts b/src/main/ts/wallet/model/MoneroTxPriority.ts new file mode 100644 index 000000000..d49da09a7 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroTxPriority.ts @@ -0,0 +1,27 @@ +/** + * Enumerates send priorities. + */ +enum MoneroTxPriority { + + /** + * Default priority (i.e. normal) (value=0). + */ + DEFAULT = 0, + + /** + * Unimportant priority (value=1). + */ + UNIMPORTANT = 1, + + /** + * Normal priority (value=2). + */ + NORMAL = 2, + + /** + * Elevated priority (value=3). + */ + ELEVATED = 3 +} + +export default MoneroTxPriority; \ No newline at end of file diff --git a/src/main/ts/wallet/model/MoneroTxQuery.ts b/src/main/ts/wallet/model/MoneroTxQuery.ts new file mode 100644 index 000000000..ac0bea280 --- /dev/null +++ b/src/main/ts/wallet/model/MoneroTxQuery.ts @@ -0,0 +1,524 @@ +import assert from "assert"; +import MoneroBlock from "../../daemon/model/MoneroBlock"; +import MoneroOutput from "../../daemon/model/MoneroOutput"; +import MoneroIncomingTransfer from "./MoneroIncomingTransfer"; +import MoneroOutgoingTransfer from "./MoneroOutgoingTransfer"; +import MoneroOutputQuery from "./MoneroOutputQuery"; +import MoneroTransferQuery from "./MoneroTransferQuery"; +import MoneroTxSet from "./MoneroTxSet"; +import MoneroTxWallet from "./MoneroTxWallet"; + +/** + *

Configuration to query transactions.

+ */ +export default class MoneroTxQuery extends MoneroTxWallet { + + hash: string; + hashes: string[]; + height: number; + minHeight: number; + maxHeight: number; + includeOutputs: boolean; + isConfirmed: boolean; + inTxPool: boolean; + relay: boolean; + isRelayed: boolean; + isFailed: boolean; + isMinerTx: boolean; + isLocked: boolean; + isIncoming: boolean; + isOutgoing: boolean; + paymentId: string; + paymentIds: string[]; + hasPaymentId: boolean; + transferQuery: Partial; + inputQuery: Partial; + outputQuery: Partial; + + /** + *

Construct the transaction query.

+ * + *

Example:

+ * + * + * // get transactions with unlocked incoming transfers to account 0
+ * let txs = await wallet.getTxs({
+ *    isLocked: false,
+ *    transferQuery: {
+ *      isIncoming: true,
+ *      accountIndex: 0
+ *    }
+ * }); + *
+ * + *

All configuration is optional. All transactions are returned except those that don't meet criteria defined in this query.

+ * + * @param {MoneroTxQuery} [query] - tx query configuration + * @param {string} [query.hash] - get a tx with this hash + * @param {string[]} [query.txHashes] - get txs with these hashes + * @param {number} [query.height] - get txs with this height + * @param {number} [query.minHeight] - get txs with height greater than or equal to this height + * @param {number} [query.maxHeight] - get txs with height less than or equal to this height + * @param {boolean} [query.isConfirmed] - get confirmed or unconfirmed txs + * @param {boolean} [query.inTxPool] - get txs in or out of the tx pool + * @param {boolean} [query.relay] - get txs with the same relay status + * @param {boolean} [query.isRelayed] - get relayed or non-relayed txs + * @param {boolean} [query.isFailed] - get failed or non-failed txs + * @param {boolean} [query.isMinerTx] - get miner or non-miner txs + * @param {boolean} [query.isLocked] - get locked or unlocked txs + * @param {boolean} [query.isIncoming] - get txs with or without incoming transfers + * @param {boolean} [query.isOutgoing] - get txs with or without outgoing transfers + * @param {string} [query.paymentId] - get txs with this payment ID + * @param {string} [query.paymentIds] - get txs with a payment ID among these payment IDs + * @param {boolean} [query.hasPaymentId] - get txs with or without payment IDs + * @param {Partial} [query.transferQuery] - get txs with transfers matching this transfer query + * @param {Partial} [query.inputQuery] - get txs with inputs matching this input query + * @param {Partial} [query.outputQuery] - get txs with outputs matching this output query + */ + constructor(query?: Partial) { + super(query); + + // copy queries + if (this.transferQuery) this.transferQuery = new MoneroTransferQuery(this.transferQuery); + if (this.inputQuery) this.inputQuery = new MoneroOutputQuery(this.inputQuery); + if (this.outputQuery) this.outputQuery = new MoneroOutputQuery(this.outputQuery); + + // link cycles + if (this.transferQuery) this.getTransferQuery().setTxQuery(this); + if (this.inputQuery) this.getInputQuery().setTxQuery(this); + if (this.outputQuery) this.getOutputQuery().setTxQuery(this); + + // alias 'hash' to hashes + if (this.hash) { + this.setHashes([this.hash]); + delete this.hash; + } + } + + copy(): MoneroTxQuery { + return new MoneroTxQuery(this); + } + + toJson(): any { + let json = Object.assign({}, this, super.toJson()); // merge json onto inherited state + if (this.getTransferQuery() !== undefined) json.transferQuery = this.getTransferQuery().toJson(); + if (this.getInputQuery() !== undefined) json.inputQuery = this.getInputQuery().toJson(); + if (this.getOutputQuery() !== undefined) json.outputQuery = this.getOutputQuery().toJson(); + delete json.block; // do not serialize parent block + return json; + } + + getIsIncoming(): boolean { + return this.isIncoming; + } + + setIsIncoming(isIncoming: boolean): MoneroTxQuery { + this.isIncoming = isIncoming; + return this; + } + + getIsOutgoing(): boolean { + return this.isOutgoing; + } + + setIsOutgoing(isOutgoing: boolean): MoneroTxQuery { + this.isOutgoing = isOutgoing; + return this; + } + + getHashes(): string[] { + return this.hashes; + } + + setHashes(hashes: string[]): MoneroTxQuery { + this.hashes = hashes; + return this; + } + + setHash(hash: string): MoneroTxQuery { + if (hash === undefined) return this.setHashes(undefined); + assert(typeof hash === "string"); + return this.setHashes([hash]); + } + + getHasPaymentId(): boolean { + return this.hasPaymentId; + } + + setHasPaymentId(hasPaymentId: boolean): MoneroTxQuery { + this.hasPaymentId = hasPaymentId; + return this; + } + + getPaymentIds(): string[] { + return this.paymentIds; + } + + setPaymentIds(paymentIds: string[]): MoneroTxQuery { + this.paymentIds = paymentIds; + return this; + } + + setPaymentId(paymentId: string): MoneroTxQuery { + if (paymentId === undefined) return this.setPaymentIds(undefined); + assert(typeof paymentId === "string"); + return this.setPaymentIds([paymentId]); + } + + getHeight(): number { + return this.height; + } + + setHeight(height: number): MoneroTxQuery { + this.height = height; + return this; + } + + getMinHeight(): number { + return this.minHeight; + } + + setMinHeight(minHeight: number): MoneroTxQuery { + this.minHeight = minHeight; + return this; + } + + getMaxHeight(): number { + return this.maxHeight; + } + + setMaxHeight(maxHeight: number): MoneroTxQuery { + this.maxHeight = maxHeight; + return this; + } + + getIncludeOutputs(): boolean { + return this.includeOutputs; + } + + setIncludeOutputs(includeOutputs: boolean): MoneroTxQuery { + this.includeOutputs = includeOutputs; + return this; + } + + getTransferQuery(): MoneroTransferQuery { + return this.transferQuery as MoneroTransferQuery + } + + setTransferQuery(transferQuery: MoneroTransferQuery): MoneroTxQuery { + this.transferQuery = transferQuery === undefined ? undefined : transferQuery instanceof MoneroTransferQuery ? transferQuery : new MoneroTransferQuery(transferQuery); + if (transferQuery) this.transferQuery.txQuery = this; + return this; + } + + getInputQuery(): MoneroOutputQuery { + return this.inputQuery as MoneroOutputQuery; + } + + setInputQuery(inputQuery: MoneroOutputQuery): MoneroTxQuery { + this.inputQuery = inputQuery; + if (inputQuery) inputQuery.txQuery = this; + return this; + } + + getOutputQuery(): MoneroOutputQuery { + return this.outputQuery as MoneroOutputQuery; + } + + setOutputQuery(outputQuery: MoneroOutputQuery): MoneroTxQuery { + this.outputQuery = outputQuery === undefined ? undefined : outputQuery instanceof MoneroOutputQuery ? outputQuery : new MoneroOutputQuery(outputQuery); + if (outputQuery) this.outputQuery.txQuery = this; + return this; + } + + meetsCriteria(tx: MoneroTxWallet, queryChildren?: boolean): boolean { + if (!(tx instanceof MoneroTxWallet)) throw new Error("Tx not given to MoneroTxQuery.meetsCriteria(tx)"); + if (queryChildren === undefined) queryChildren = true; + + // filter on tx + if (this.getHash() !== undefined && this.getHash() !== tx.getHash()) return false; + if (this.getPaymentId() !== undefined && this.getPaymentId() !== tx.getPaymentId()) return false; + if (this.getIsConfirmed() !== undefined && this.getIsConfirmed() !== tx.getIsConfirmed()) return false; + if (this.getInTxPool() !== undefined && this.getInTxPool() !== tx.getInTxPool()) return false; + if (this.getRelay() !== undefined && this.getRelay() !== tx.getRelay()) return false; + if (this.getIsRelayed() !== undefined && this.getIsRelayed() !== tx.getIsRelayed()) return false; + if (this.getIsFailed() !== undefined && this.getIsFailed() !== tx.getIsFailed()) return false; + if (this.getIsMinerTx() !== undefined && this.getIsMinerTx() !== tx.getIsMinerTx()) return false; + if (this.getIsLocked() !== undefined && this.getIsLocked() !== tx.getIsLocked()) return false; + + // filter on having a payment id + if (this.getHasPaymentId() !== undefined) { + if (this.getHasPaymentId() && tx.getPaymentId() === undefined) return false; + if (!this.getHasPaymentId() && tx.getPaymentId() !== undefined) return false; + } + + // filter on incoming + if (this.getIsIncoming() !== undefined) { + if (this.getIsIncoming() && !tx.getIsIncoming()) return false; + if (!this.getIsIncoming() && tx.getIsIncoming()) return false; + } + + // filter on outgoing + if (this.getIsOutgoing() !== undefined) { + if (this.getIsOutgoing() && !tx.getIsOutgoing()) return false; + if (!this.getIsOutgoing() && tx.getIsOutgoing()) return false; + } + + // filter on remaining fields + let txHeight = tx.getBlock() === undefined ? undefined : tx.getBlock().getHeight(); + if (this.getHashes() !== undefined && !this.getHashes().includes(tx.getHash())) return false; + if (this.getPaymentIds() !== undefined && !this.getPaymentIds().includes(tx.getPaymentId())) return false; + if (this.getHeight() !== undefined && (txHeight === undefined || txHeight !== this.getHeight())) return false; + if (this.getMinHeight() !== undefined && txHeight !== undefined && txHeight < this.getMinHeight()) return false; // do not filter unconfirmed + if (this.getMaxHeight() !== undefined && (txHeight === undefined || txHeight > this.getMaxHeight())) return false; + // TODO: filtering not complete + + // done if not querying transfers or outputs + if (!queryChildren) return true; + + // at least one transfer must meet transfer filter if defined + if (this.getTransferQuery() !== undefined) { + let matchFound = false; + if (tx.getOutgoingTransfer() && this.getTransferQuery().meetsCriteria(tx.getOutgoingTransfer(), false)) matchFound = true; + else if (tx.getIncomingTransfers()) { + for (let incomingTransfer of tx.getIncomingTransfers()) { + if (this.getTransferQuery().meetsCriteria(incomingTransfer, false)) { + matchFound = true; + break; + } + } + } + if (!matchFound) return false; + } + + // at least one input must meet input query if defined + if (this.getInputQuery() !== undefined) { + if (tx.getInputs() === undefined || tx.getInputs().length === 0) return false; + let matchFound = false; + for (let input of tx.getInputsWallet()) { + if (this.getInputQuery().meetsCriteria(input, false)) { + matchFound = true; + break; + } + } + if (!matchFound) return false; + } + + // at least one output must meet output query if defined + if (this.getOutputQuery() !== undefined) { + if (tx.getOutputs() === undefined || tx.getOutputs().length === 0) return false; + let matchFound = false; + for (let output of tx.getOutputsWallet()) { + if (this.getOutputQuery().meetsCriteria(output, false)) { + matchFound = true; + break; + } + } + if (!matchFound) return false; + } + + return true; // transaction meets filter criteria + } + + // ------------------- OVERRIDE CO-VARIANT RETURN TYPES --------------------- + + setIncomingTransfers(incomingTransfers: MoneroIncomingTransfer[]): MoneroTxQuery { + super.setIncomingTransfers(incomingTransfers); + return this; + } + + setOutgoingTransfer(outgoingTransfer: MoneroOutgoingTransfer): MoneroTxQuery { + super.setOutgoingTransfer(outgoingTransfer); + return this; + } + + setOutputs(outputs: MoneroOutput[]): MoneroTxQuery { + super.setOutputs(outputs); + return this; + } + + setNote(note: string): MoneroTxQuery { + super.setNote(note); + return this; + } + + setIsLocked(isLocked: boolean): MoneroTxQuery { + super.setIsLocked(isLocked); + return this; + } + + setBlock(block: MoneroBlock): MoneroTxQuery { + super.setBlock(block); + return this; + } + + setVersion(version: number): MoneroTxQuery { + super.setVersion(version); + return this; + } + + setIsMinerTx(isMinerTx: boolean): MoneroTxQuery { + super.setIsMinerTx(isMinerTx); + return this; + } + + setFee(fee: bigint): MoneroTxQuery { + super.setFee(fee); + return this; + } + + setRingSize(ringSize: number): MoneroTxQuery { + super.setRingSize(ringSize); + return this; + } + + setRelay(relay: boolean): MoneroTxQuery { + super.setRelay(relay); + return this; + } + + setIsRelayed(isRelayed: boolean): MoneroTxQuery { + super.setIsRelayed(isRelayed); + return this; + } + + setIsConfirmed(isConfirmed: boolean): MoneroTxQuery { + super.setIsConfirmed(isConfirmed); + return this; + } + + setInTxPool(inTxPool: boolean): MoneroTxQuery { + super.setInTxPool(inTxPool); + return this; + } + + setNumConfirmations(numConfirmations: number): MoneroTxQuery { + super.setNumConfirmations(numConfirmations); + return this; + } + + setUnlockTime(unlockTime: bigint): MoneroTxQuery { + super.setUnlockTime(unlockTime); + return this; + } + + setLastRelayedTimestamp(lastRelayedTimestamp: number): MoneroTxQuery { + super.setLastRelayedTimestamp(lastRelayedTimestamp); + return this; + } + + setReceivedTimestamp(receivedTimestamp: number): MoneroTxQuery { + super.setReceivedTimestamp(receivedTimestamp); + return this; + } + + setIsDoubleSpendSeen(isDoubleSpendSeen: boolean): MoneroTxQuery { + super.setIsDoubleSpendSeen(isDoubleSpendSeen); + return this; + } + + setKey(key: string): MoneroTxQuery { + super.setKey(key); + return this; + } + + setFullHex(hex: string): MoneroTxQuery { + super.setFullHex(hex); + return this; + } + + setPrunedHex(prunedHex: string): MoneroTxQuery { + super.setPrunedHex(prunedHex); + return this; + } + + setPrunableHex(prunableHex: string): MoneroTxQuery { + super.setPrunableHex(prunableHex); + return this; + } + + setPrunableHash(prunableHash: string): MoneroTxQuery { + super.setPrunableHash(prunableHash); + return this; + } + + setSize(size: number): MoneroTxQuery { + super.setSize(size); + return this; + } + + setWeight(weight: number): MoneroTxQuery { + super.setWeight(weight); + return this; + } + + setInputs(inputs: MoneroOutput[]): MoneroTxQuery { + super.setInputs(inputs); + return this; + } + + setOutputIndices(outputIndices: number[]): MoneroTxQuery { + super.setOutputIndices(outputIndices); + return this; + } + + setMetadata(metadata: string): MoneroTxQuery { + super.setMetadata(metadata); + return this; + } + + setTxSet(txSet: MoneroTxSet): MoneroTxQuery { + super.setTxSet(txSet); + return this; + } + + setExtra(extra: Uint8Array): MoneroTxQuery { + super.setExtra(extra); + return this; + } + + setRctSignatures(rctSignatures: any): MoneroTxQuery { + super.setRctSignatures(rctSignatures) + return this; + } + + setRctSigPrunable(rctSigPrunable: any): MoneroTxQuery { + super.setRctSigPrunable(rctSigPrunable); + return this; + } + + setIsKeptByBlock(isKeptByBlock: boolean): MoneroTxQuery { + super.setIsKeptByBlock(isKeptByBlock) + return this; + } + + setIsFailed(isFailed: boolean): MoneroTxQuery { + super.setIsFailed(isFailed); + return this; + } + + setLastFailedHeight(lastFailedHeight: number): MoneroTxQuery { + super.setLastFailedHeight(lastFailedHeight); + return this; + } + + setLastFailedHash(lastFailedId: string): MoneroTxQuery { + super.setLastFailedHash(lastFailedId); + return this; + } + + setMaxUsedBlockHeight(maxUsedBlockHeight: number): MoneroTxQuery { + super.setMaxUsedBlockHeight(maxUsedBlockHeight); + return this; + } + + setMaxUsedBlockHash(maxUsedBlockId: string): MoneroTxQuery { + super.setMaxUsedBlockHash(maxUsedBlockId); + return this; + } + + setSignatures(signatures: string[]): MoneroTxQuery { + super.setSignatures(signatures); + return this; + } +} diff --git a/src/main/js/wallet/model/MoneroTxSet.js b/src/main/ts/wallet/model/MoneroTxSet.ts similarity index 62% rename from src/main/js/wallet/model/MoneroTxSet.js rename to src/main/ts/wallet/model/MoneroTxSet.ts index 7ee75f5f0..3c33e4e9d 100644 --- a/src/main/js/wallet/model/MoneroTxSet.js +++ b/src/main/ts/wallet/model/MoneroTxSet.ts @@ -1,7 +1,7 @@ -const assert = require("assert"); -const GenUtils = require("../../common/GenUtils"); -const MoneroTxWallet = require("./MoneroTxWallet"); -const MoneroUtils = require("../../common/MoneroUtils"); +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroTxWallet from "./MoneroTxWallet"; +import MoneroUtils from "../../common/MoneroUtils"; /** * Groups transactions who share common hex data which is needed in order to @@ -11,28 +11,28 @@ const MoneroUtils = require("../../common/MoneroUtils"); * hex string which is needed in order to sign and submit the multisig * transactions. */ -class MoneroTxSet { +export default class MoneroTxSet { + + txs: MoneroTxWallet[]; + multisigTxHex: string; + unsignedTxHex: string; + signedTxHex: string; - constructor(state) { - - // initialize internal state - if (!state) state = {}; - else if (typeof state === "object") state = Object.assign({}, state); - else throw new MoneroError("state must be JavaScript object"); - this.state = state; + constructor(txSet?: Partial) { + Object.assign(this, txSet); - // deserialize txs - if (state.txs) { - for (let i = 0; i < state.txs.length; i++) { - if (!(state.txs[i] instanceof MoneroTxWallet)) state.txs[i] = new MoneroTxWallet(state.txs[i]); - state.txs[i].setTxSet(this); + // copy txs + if (this.txs) { + for (let i = 0; i < this.txs.length; i++) { + this.txs[i] = new MoneroTxWallet(this.txs[i]); + this.txs[i].setTxSet(this); } } } toJson() { - let json = Object.assign({}, this.state); // copy state - if (this.getTxs()) { + let json = Object.assign({}, this); // copy state + if (this.getTxs() !== undefined) { json.txs = []; for (let tx of this.getTxs()) json.txs.push(tx.toJson()); } @@ -40,38 +40,38 @@ class MoneroTxSet { } getTxs() { - return this.state.txs; + return this.txs; } setTxs(txs) { - this.state.txs = txs; + this.txs = txs; return this; } getMultisigTxHex() { - return this.state.multisigTxHex; + return this.multisigTxHex; } setMultisigTxHex(multisigTxHex) { - this.state.multisigTxHex = multisigTxHex; + this.multisigTxHex = multisigTxHex; return this; } getUnsignedTxHex() { - return this.state.unsignedTxHex; + return this.unsignedTxHex; } setUnsignedTxHex(unsignedTxHex) { - this.state.unsignedTxHex = unsignedTxHex; + this.unsignedTxHex = unsignedTxHex; return this; } getSignedTxHex() { - return this.state.signedTxHex; + return this.signedTxHex; } setSignedTxHex(signedTxHex) { - this.state.signedTxHex = signedTxHex; + this.signedTxHex = signedTxHex; return this; } @@ -108,6 +108,4 @@ class MoneroTxSet { } return str; } -} - -module.exports = MoneroTxSet; \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/ts/wallet/model/MoneroTxWallet.ts b/src/main/ts/wallet/model/MoneroTxWallet.ts new file mode 100644 index 000000000..f2c349dff --- /dev/null +++ b/src/main/ts/wallet/model/MoneroTxWallet.ts @@ -0,0 +1,746 @@ +import assert from "assert"; +import GenUtils from "../../common/GenUtils"; +import MoneroBlock from "../../daemon/model/MoneroBlock"; +import MoneroError from "../../common/MoneroError"; +import MoneroIncomingTransfer from "./MoneroIncomingTransfer"; +import MoneroOutgoingTransfer from "./MoneroOutgoingTransfer"; +import MoneroOutputQuery from "./MoneroOutputQuery"; +import MoneroOutput from "../../daemon/model/MoneroOutput"; +import MoneroOutputWallet from "./MoneroOutputWallet"; +import MoneroTransfer from "./MoneroTransfer"; +import MoneroTransferQuery from "./MoneroTransferQuery"; +import MoneroTx from "../../daemon/model/MoneroTx"; +import MoneroTxSet from "./MoneroTxSet"; + +/** + * Models a Monero transaction with wallet extensions. + */ +export default class MoneroTxWallet extends MoneroTx { + + txSet: MoneroTxSet; + isIncoming: boolean; + isOutgoing: boolean; + incomingTransfers: MoneroIncomingTransfer[]; + outgoingTransfer: MoneroOutgoingTransfer; + note: string; + isLocked: boolean; + inputSum: bigint; + outputSum: bigint; + changeAddress: string; + changeAmount: bigint; + numDummyOutputs: number; + extraHex: string; + + /** + * Construct the model. + * + * @param {Partial} [tx] is existing state to initialize from (optional) + */ + constructor(tx?: Partial) { + super(tx); + this.setTxSet(this.getTxSet()); // preserve reference to tx set + + // copy incoming transfers + if (this.incomingTransfers) { + this.incomingTransfers = this.incomingTransfers.slice(); + for (let i = 0; i < this.incomingTransfers.length; i++) { + this.incomingTransfers[i] = new MoneroIncomingTransfer(this.incomingTransfers[i]).setTx(this); + } + } + + // copy outgoing transfer + if (this.outgoingTransfer) { + this.outgoingTransfer = new MoneroOutgoingTransfer(this.outgoingTransfer).setTx(this); + } + + // copy inputs + if (this.inputs) { + this.inputs = this.inputs.slice(); + for (let i = 0; i < this.inputs.length; i++) { + this.inputs[i] = new MoneroOutputWallet(this.inputs[i] as MoneroOutputWallet).setTx(this); + } + } + + // copy outputs + if (this.outputs) { + this.outputs = this.outputs.slice(); + for (let i = 0; i < this.outputs.length; i++) { + this.outputs[i] = new MoneroOutputWallet(this.outputs[i] as MoneroOutputWallet).setTx(this); + } + } + + // deserialize bigints + if (this.inputSum !== undefined && typeof this.inputSum !== "bigint") this.inputSum = BigInt(this.inputSum); + if (this.outputSum !== undefined && typeof this.outputSum !== "bigint") this.outputSum = BigInt(this.outputSum); + if (this.changeAmount !== undefined && typeof this.changeAmount !== "bigint") this.changeAmount = BigInt(this.changeAmount); + } + + /** + * @return {any} json representation of this tx + */ + toJson(): any { + let json = Object.assign({}, this, super.toJson()); // merge json onto inherited state + if (this.getIncomingTransfers() !== undefined) { + json.incomingTransfers = []; + for (let incomingTransfer of this.getIncomingTransfers()) json.incomingTransfers.push(incomingTransfer.toJson()); + } + if (this.getOutgoingTransfer() !== undefined) json.outgoingTransfer = this.getOutgoingTransfer().toJson(); + if (this.getInputSum() !== undefined) json.inputSum = this.getInputSum().toString(); + if (this.getOutputSum() !== undefined) json.outputSum = this.getOutputSum().toString(); + if (this.getChangeAmount() !== undefined) json.changeAmount = this.getChangeAmount().toString(); + delete json.block; // do not serialize parent block + delete json.txSet; // do not serialize parent tx set + return json; + } + + /** + * @return {MoneroTxSet} tx set containing txs + */ + getTxSet(): MoneroTxSet { + return this.txSet; + } + + /** + * @param {MoneroTxSet} txSet - tx set containing txs + * @return {MoneroTxWallet} this tx for chaining + */ + setTxSet(txSet: MoneroTxSet): MoneroTxWallet { + this.txSet = txSet; + return this; + } + + /** + * @return {boolean} true if the tx has incoming funds, false otherwise + */ + getIsIncoming(): boolean { + return this.isIncoming; + } + + /** + * @param {boolean} isIncoming - true if the tx has incoming funds, false otherwise + * @return {MoneroTxWallet} this tx for chaining + */ + setIsIncoming(isIncoming: boolean): MoneroTxWallet { + this.isIncoming = isIncoming; + return this; + } + + /** + * @return {boolean} true if the tx has outgoing funds, false otherwise + */ + getIsOutgoing(): boolean { + return this.isOutgoing; + } + + /** + * @param {boolean} isOutgoing - true if the tx has outgoing funds, false otherwise + * @return {MoneroTxWallet} this tx for chaining + */ + setIsOutgoing(isOutgoing: boolean): MoneroTxWallet { + this.isOutgoing = isOutgoing; + return this; + } + + /** + * @return {bigint} amount received in the tx + */ + getIncomingAmount(): bigint { + if (this.getIncomingTransfers() === undefined) return undefined; + let incomingAmt = BigInt("0"); + for (let transfer of this.getIncomingTransfers()) incomingAmt = incomingAmt + transfer.getAmount(); + return incomingAmt; + } + + /** + * @return {bigint} amount spent in the tx + */ + getOutgoingAmount(): bigint { + return this.getOutgoingTransfer() ? this.getOutgoingTransfer().getAmount() : undefined; + } + + /** + * @param {MoneroTransferQuery} [transferQuery] - query to get specific transfers + * @return {MoneroTransfer[]} transfers matching the query + */ + getTransfers(transferQuery?: MoneroTransferQuery): MoneroTransfer[] { + let transfers = []; + if (this.getOutgoingTransfer() && (!transferQuery || transferQuery.meetsCriteria(this.getOutgoingTransfer()))) transfers.push(this.getOutgoingTransfer()); + if (this.getIncomingTransfers() !== undefined) { + for (let transfer of this.getIncomingTransfers()) { + if (!transferQuery || transferQuery.meetsCriteria(transfer)) transfers.push(transfer); + } + } + return transfers; + } + + /** + * @param {MoneroTransferQuery} transferQuery - query to keep only specific transfers + * @return {MoneroTransfer[]} remaining transfers matching the query + */ + filterTransfers(transferQuery: MoneroTransferQuery): MoneroTransfer[] { + let transfers = []; + + // collect outgoing transfer or erase if filtered + if (this.getOutgoingTransfer() && (!transferQuery || transferQuery.meetsCriteria(this.getOutgoingTransfer()))) transfers.push(this.getOutgoingTransfer()); + else this.setOutgoingTransfer(undefined); + + // collect incoming transfers or erase if filtered + if (this.getIncomingTransfers() !== undefined) { + let toRemoves = []; + for (let transfer of this.getIncomingTransfers()) { + if (transferQuery.meetsCriteria(transfer)) transfers.push(transfer); + else toRemoves.push(transfer); + } + this.setIncomingTransfers(this.getIncomingTransfers().filter(function(transfer) { + return !toRemoves.includes(transfer); + })); + if (this.getIncomingTransfers().length === 0) this.setIncomingTransfers(undefined); + } + + return transfers; + } + + /** + * @return {MoneroIncomingTransfer[]} incoming transfers + */ + getIncomingTransfers(): MoneroIncomingTransfer[] { + return this.incomingTransfers; + } + + /** + * @param {MoneroIncomingTransfer[]} incomingTransfers - incoming transfers + * @return {MoneroTxWallet} this tx for chaining + */ + setIncomingTransfers(incomingTransfers: MoneroIncomingTransfer[]): MoneroTxWallet { + this.incomingTransfers = incomingTransfers; + return this; + } + + /** + * @return {MoneroOutgoingTransfer} outgoing transfers + */ + getOutgoingTransfer(): MoneroOutgoingTransfer { + return this.outgoingTransfer; + } + + /** + * @param {MoneroOutgoingTransfer} outgoingTransfer - outgoing transfer + * @return {MoneroTxWallet} this tx for chaining + */ + setOutgoingTransfer(outgoingTransfer: MoneroOutgoingTransfer): MoneroTxWallet { + this.outgoingTransfer = outgoingTransfer; + return this; + } + + /** + * @param {MoneroOutputWallet[]} outputQuery - query to get specific inputs + * @return {MoneroOutputWallet[]} inputs matching the query + */ + getInputsWallet(outputQuery?: MoneroOutputQuery): MoneroOutputWallet[] { + let inputs: MoneroOutputWallet[] = []; + for (let output of super.getInputs()) if (!outputQuery || outputQuery.meetsCriteria(output as MoneroOutputWallet)) inputs.push(output as MoneroOutputWallet); + return inputs; + } + + /** + * @param {MoneroOutputWallet[]} inputs - tx inputs + * @return {MoneroTxWallet} this tx for chaining + */ + setInputsWallet(inputs: MoneroOutputWallet[]): MoneroTxWallet { + + // validate that all inputs are wallet inputs + if (inputs) { + for (let output of inputs) { + if (!(output instanceof MoneroOutputWallet)) throw new MoneroError("Wallet transaction inputs must be of type MoneroOutputWallet"); + } + } + super.setInputs(inputs); + return this; + } + + /** + * @param {MoneroOutputQuery} [outputQuery] - query to get specific outputs + * @return {MoneroOutputWallet[]} outputs matching the query + */ + getOutputsWallet(outputQuery?: MoneroOutputQuery): MoneroOutputWallet[] { + let outputs = []; + for (let output of super.getOutputs()) if (!outputQuery || outputQuery.meetsCriteria(output as MoneroOutputWallet)) outputs.push(output); + return outputs; + } + + /** + * @param {MoneroOutputWallet[]} outputs - tx outputs + * @return {MoneroTxWallet} this tx for chaining + */ + setOutputsWallet(outputs: MoneroOutputWallet[]): MoneroTxWallet { + + // validate that all outputs are wallet outputs + if (outputs) { + for (let output of outputs) { + if (!(output instanceof MoneroOutputWallet)) throw new MoneroError("Wallet transaction outputs must be of type MoneroOutputWallet"); + } + } + super.setOutputs(outputs); + return this; + } + + /** + * @param {MoneroOutputQuery} outputQuery - query to keep only specific outputs + * @return {MoneroTransfer[]} remaining outputs matching the query + */ + filterOutputs(outputQuery: MoneroOutputQuery): MoneroTransfer[] { + let outputs = []; + if (super.getOutputs()) { + let toRemoves = []; + for (let output of super.getOutputs()) { + if (!outputQuery || outputQuery.meetsCriteria(output as MoneroOutputWallet)) outputs.push(output); + else toRemoves.push(output); + } + this.setOutputs(super.getOutputs().filter(function(output) { + return !toRemoves.includes(output); + })); + if (this.getOutputs().length === 0) this.setOutputs(undefined); + } + return outputs; + } + + /** + * @return {string} tx note + */ + getNote(): string { + return this.note; + } + + /** + * @param {string} note - tx note + * @return {MoneroTxWallet} this tx for chaining + */ + setNote(note: string): MoneroTxWallet { + this.note = note; + return this; + } + + /** + * @return {boolean} true if the tx is locked, false otherwise + */ + getIsLocked(): boolean { + return this.isLocked; + } + + /** + * @param {boolean} isLocked - true if the tx is locked, false otherwise + * @return {MoneroTxWallet} this tx for chaining + */ + setIsLocked(isLocked: boolean): MoneroTxWallet { + this.isLocked = isLocked; + return this; + } + + /** + * @return {bigint} sum of tx inputs + */ + getInputSum(): bigint { + return this.inputSum; + } + + /** + * @param {bigint} inputSum - sum of tx inputs + * @return {MoneroTxWallet} this tx for chaining + */ + setInputSum(inputSum: bigint): MoneroTxWallet { + this.inputSum = inputSum; + return this; + } + + /** + * @return {bigint} sum of tx outputs + */ + getOutputSum(): bigint { + return this.outputSum; + } + + /** + * @param {bigint} outputSum - sum of tx outputs + * @return {MoneroTxWallet} this tx for chaining + */ + setOutputSum(outputSum: bigint): MoneroTxWallet { + this.outputSum = outputSum; + return this; + } + + /** + * @return {string} change address + */ + getChangeAddress(): string { + return this.changeAddress; + } + + /** + * @param {string} changeAddress - change address + * @return {MoneroTxWallet} this tx for chaining + */ + setChangeAddress(changeAddress: string): MoneroTxWallet { + this.changeAddress = changeAddress; + return this; + } + + /** + * @return {bigint} change amount + */ + getChangeAmount(): bigint { + return this.changeAmount; + } + + /** + * @param {bigint} changeAmount - change amount + * @return {MoneroTxWallet} this tx for chaining + */ + setChangeAmount(changeAmount: bigint): MoneroTxWallet { + this.changeAmount = changeAmount; + return this; + } + + /** + * @return {number} number of dummy outputs + */ + getNumDummyOutputs(): number { + return this.numDummyOutputs; + } + + /** + * @param {number} numDummyOutputs - number of dummy outputs + * @return {MoneroTxWallet} this tx for chaining + */ + setNumDummyOutputs(numDummyOutputs: number): MoneroTxWallet { + this.numDummyOutputs = numDummyOutputs; + return this; + } + + /** + * @return {string} tx extra as hex + */ + getExtraHex(): string { + return this.extraHex; + } + + /** + * @param {string} extraHex - tx extra as hex + * @return {MoneroTxWallet} this tx for chaining + */ + setExtraHex(extraHex: string): MoneroTxWallet { + this.extraHex = extraHex; + return this; + } + + /** + * @return {MoneroTxWallet} a copy of this tx + */ + copy(): MoneroTxWallet { + return new MoneroTxWallet(this); + } + + /** + * Updates this transaction by merging the latest information from the given + * transaction. + * + * Merging can modify or build references to the transaction given so it + * should not be re-used or it should be copied before calling this method. + * + * @param {MoneroTxWallet} tx - the transaction to merge into this transaction + */ + merge(tx: MoneroTxWallet): MoneroTxWallet { + assert(tx instanceof MoneroTxWallet); + if (this === tx) return this; + + // merge base classes + super.merge(tx); + + // merge tx set if they're different which comes back to merging txs + //import MoneroTxSet from "./MoneroTxSet"; + if (this.getTxSet() !== tx.getTxSet()) { + if (this.getTxSet() == undefined) { + this.setTxSet(new MoneroTxSet().setTxs([this])); + } + if (tx.getTxSet() === undefined) { + tx.setTxSet(new MoneroTxSet().setTxs([tx])); + } + this.getTxSet().merge(tx.getTxSet()); + return this; + } + + // merge incoming transfers + if (tx.getIncomingTransfers()) { + if (this.getIncomingTransfers() === undefined) this.setIncomingTransfers([]); + for (let transfer of tx.getIncomingTransfers()) { + transfer.setTx(this); + MoneroTxWallet.mergeIncomingTransfer(this.getIncomingTransfers(), transfer); + } + } + + // merge outgoing transfer + if (tx.getOutgoingTransfer()) { + tx.getOutgoingTransfer().setTx(this); + if (this.getOutgoingTransfer() === undefined) this.setOutgoingTransfer(tx.getOutgoingTransfer()); + else this.getOutgoingTransfer().merge(tx.getOutgoingTransfer()); + } + + // merge simple extensions + this.setIsIncoming(GenUtils.reconcile(this.getIsIncoming(), tx.getIsIncoming(), {resolveTrue: true})); // outputs seen on confirmation + this.setIsOutgoing(GenUtils.reconcile(this.getIsOutgoing(), tx.getIsOutgoing())); + this.setNote(GenUtils.reconcile(this.getNote(), tx.getNote())); + this.setIsLocked(GenUtils.reconcile(this.getIsLocked(), tx.getIsLocked(), {resolveTrue: false})); // tx can become unlocked + this.setInputSum(GenUtils.reconcile(this.getInputSum(), tx.getInputSum())); + this.setOutputSum(GenUtils.reconcile(this.getOutputSum(), tx.getOutputSum())); + this.setChangeAddress(GenUtils.reconcile(this.getChangeAddress(), tx.getChangeAddress())); + this.setChangeAmount(GenUtils.reconcile(this.getChangeAmount(), tx.getChangeAmount())); + this.setNumDummyOutputs(GenUtils.reconcile(this.getNumDummyOutputs(), tx.getNumDummyOutputs())); + this.setExtraHex(GenUtils.reconcile(this.getExtraHex(), tx.getExtraHex())); + + return this; // for chaining + } + + /** + * @param {number} [indent] - starting indentation + * @param {boolean} [oneLine] - string is one line if true, multiple lines if false + * @return {string} string representation of this tx + */ + toString(indent = 0, oneLine = false): string { + let str = ""; + + // represent tx with one line string + // TODO: proper csv export + if (oneLine) { + str += this.getHash() + ", "; + str += (this.getIsConfirmed() ? this.getBlock().getTimestamp() : this.getReceivedTimestamp()) + ", "; + str += this.getIsConfirmed() + ", "; + str += (this.getOutgoingAmount() ? this.getOutgoingAmount().toString() : "") + ", "; + str += this.getIncomingAmount() ? this.getIncomingAmount().toString() : ""; + return str; + } + + // otherwise stringify all fields + str += super.toString(indent) + "\n"; + str += GenUtils.kvLine("Is incoming", this.getIsIncoming(), indent); + str += GenUtils.kvLine("Incoming amount", this.getIncomingAmount(), indent); + if (this.getIncomingTransfers() !== undefined) { + str += GenUtils.kvLine("Incoming transfers", "", indent); + for (let i = 0; i < this.getIncomingTransfers().length; i++) { + str += GenUtils.kvLine(i + 1, "", indent + 1); + str += this.getIncomingTransfers()[i].toString(indent + 2) + "\n"; + } + } + str += GenUtils.kvLine("Is outgoing", this.getIsOutgoing(), indent); + str += GenUtils.kvLine("Outgoing amount", this.getOutgoingAmount(), indent); + if (this.getOutgoingTransfer() !== undefined) { + str += GenUtils.kvLine("Outgoing transfer", "", indent); + str += this.getOutgoingTransfer().toString(indent + 1) + "\n"; + } + str += GenUtils.kvLine("Note", this.getNote(), indent); + str += GenUtils.kvLine("Is locked", this.getIsLocked(), indent); + str += GenUtils.kvLine("Input sum", this.getInputSum(), indent); + str += GenUtils.kvLine("Output sum", this.getOutputSum(), indent); + str += GenUtils.kvLine("Change address", this.getChangeAddress(), indent); + str += GenUtils.kvLine("Change amount", this.getChangeAmount(), indent); + str += GenUtils.kvLine("Num dummy outputs", this.getNumDummyOutputs(), indent); + str += GenUtils.kvLine("Extra hex", this.getExtraHex(), indent); + return str.slice(0, str.length - 1); // strip last newline + } + + // private helper to merge transfers + protected static mergeIncomingTransfer(transfers, transfer) { + for (let aTransfer of transfers) { + if (aTransfer.getAccountIndex() === transfer.getAccountIndex() && aTransfer.getSubaddressIndex() === transfer.getSubaddressIndex()) { + aTransfer.merge(transfer); + return; + } + } + transfers.push(transfer); + } + + // -------------------- OVERRIDE COVARIANT RETURN TYPES --------------------- + + setBlock(block: MoneroBlock): MoneroTxWallet { + super.setBlock(block); + return this; + } + + setHash(hash: string): MoneroTxWallet { + super.setHash(hash); + return this; + } + + setVersion(version: number): MoneroTxWallet { + super.setVersion(version); + return this; + } + + setIsMinerTx(isMinerTx: boolean): MoneroTxWallet { + super.setIsMinerTx(isMinerTx); + return this; + } + + setPaymentId(paymentId: string): MoneroTxWallet { + super.setPaymentId(paymentId); + return this; + } + + setFee(fee: bigint): MoneroTxWallet { + super.setFee(fee); + return this; + } + + setRingSize(ringSize: number): MoneroTxWallet { + super.setRingSize(ringSize); + return this; + } + + setRelay(relay: boolean): MoneroTxWallet { + super.setRelay(relay); + return this; + } + + setIsRelayed(isRelayed: boolean): MoneroTxWallet { + super.setIsRelayed(isRelayed); + return this; + } + + setIsConfirmed(isConfirmed: boolean): MoneroTxWallet { + super.setIsConfirmed(isConfirmed); + return this; + } + + setInTxPool(inTxPool: boolean): MoneroTxWallet { + super.setInTxPool(inTxPool); + return this; + } + + setNumConfirmations(numConfirmations: number): MoneroTxWallet { + super.setNumConfirmations(numConfirmations); + return this; + } + + setUnlockTime(unlockTime: bigint): MoneroTxWallet { + super.setUnlockTime(unlockTime); + return this; + } + + setLastRelayedTimestamp(lastRelayedTimestamp: number): MoneroTxWallet { + super.setLastRelayedTimestamp(lastRelayedTimestamp); + return this; + } + + setReceivedTimestamp(receivedTimestamp: number): MoneroTxWallet { + super.setReceivedTimestamp(receivedTimestamp); + return this; + } + + setIsDoubleSpendSeen(isDoubleSpendSeen: boolean): MoneroTxWallet { + super.setIsDoubleSpendSeen(isDoubleSpendSeen); + return this; + } + + setKey(key: string): MoneroTxWallet { + super.setKey(key); + return this; + } + + setFullHex(fullHex: string): MoneroTxWallet { + super.setFullHex(fullHex); + return this; + } + + setPrunedHex(prunedHex: string): MoneroTxWallet { + super.setPrunedHex(prunedHex); + return this; + } + + setPrunableHex(prunableHex: string): MoneroTxWallet { + super.setPrunableHex(prunableHex); + return this; + } + + setPrunableHash(prunableHash: string): MoneroTxWallet { + super.setPrunableHash(prunableHash); + return this; + } + + setSize(size: number): MoneroTxWallet { + super.setSize(size); + return this; + } + + setWeight(weight: number): MoneroTxWallet { + super.setWeight(weight); + return this; + } + + setInputs(inputs: MoneroOutput[]): MoneroTxWallet { + super.setInputs(inputs); + return this; + } + + setOutputs(outputs: MoneroOutput[]): MoneroTxWallet { + super.setOutputs(outputs); + return this; + } + + setOutputIndices(outputIndices: number[]): MoneroTxWallet { + super.setOutputIndices(outputIndices); + return this; + } + + setMetadata(metadata: string): MoneroTxWallet { + super.setMetadata(metadata); + return this; + } + + setExtra(extra: Uint8Array): MoneroTxWallet { + super.setExtra(extra); + return this; + } + + setRctSignatures(rctSignatures: any): MoneroTxWallet { + super.setRctSignatures(rctSignatures); + return this; + } + + setRctSigPrunable(rctSigPrunable: any): MoneroTxWallet { + super.setRctSigPrunable(rctSigPrunable); + return this; + } + + setIsKeptByBlock(isKeptByBlock: boolean): MoneroTxWallet { + super.setIsKeptByBlock(isKeptByBlock); + return this; + } + + setIsFailed(isFailed: boolean): MoneroTxWallet { + super.setIsFailed(isFailed); + return this; + } + + setLastFailedHeight(lastFailedHeight: number): MoneroTxWallet { + super.setLastFailedHeight(lastFailedHeight); + return this; + } + + setLastFailedHash(lastFailedHash: string): MoneroTxWallet { + super.setLastFailedHash(lastFailedHash); + return this; + } + + setMaxUsedBlockHeight(maxUsedBlockHeight: number): MoneroTxWallet { + super.setMaxUsedBlockHeight(maxUsedBlockHeight); + return this; + } + + setMaxUsedBlockHash(maxUsedBlockHash: string): MoneroTxWallet { + super.setMaxUsedBlockHash(maxUsedBlockHash); + return this; + } + + setSignatures(signatures: string[]): MoneroTxWallet { + super.setSignatures(signatures); + return this; + } +} diff --git a/src/main/ts/wallet/model/MoneroWalletConfig.ts b/src/main/ts/wallet/model/MoneroWalletConfig.ts new file mode 100644 index 000000000..9047dd89d --- /dev/null +++ b/src/main/ts/wallet/model/MoneroWalletConfig.ts @@ -0,0 +1,263 @@ +import MoneroConnectionManager from "../../common/MoneroConnectionManager"; +import MoneroNetworkType from "../../daemon/model/MoneroNetworkType"; +import MoneroRpcConnection from "../../common/MoneroRpcConnection"; +import MoneroUtils from "../../common/MoneroUtils"; + +/** + * Configuration to create a Monero wallet. + */ +export default class MoneroWalletConfig { + + path: string; + password: string; + networkType: MoneroNetworkType; + server: string | Partial; + connectionManager: MoneroConnectionManager; + seed: string; + seedOffset: string; + isMultisig: boolean; + primaryAddress: string; + privateViewKey: string; + privateSpendKey: string; + restoreHeight: number; + language: string; + saveCurrent: boolean; + proxyToWorker: boolean; + fs: any; + keysData: Uint8Array; + cacheData: Uint8Array; + accountLookahead: number; + subaddressLookahead: number; + cmd: string[]; + + /** + * Construct a configuration to open or create a wallet. + * + * @param {Partial} [config] - MoneroWalletConfig or equivalent config object + * @param {string} [config.path] - path of the wallet to open or create + * @param {string} [config.password] - password of the wallet to open + * @param {string|number} [config.networkType] - network type of the wallet to open (one of "mainnet", "testnet", "stagenet" or MoneroNetworkType.MAINNET|TESTNET|STAGENET) + * @param {string} [config.seed] - seed of the wallet to create (optional, random wallet created if neither seed nor keys given) + * @param {string} [config.seedOffset] - the offset used to derive a new seed from the given seed to recover a secret wallet from the seed phrase + * @param {boolean} [config.isMultisig] - restore multisig wallet from seed + * @param {string} [config.primaryAddress] - primary address of the wallet to create (only provide if restoring from keys) + * @param {string} [config.privateViewKey] - private view key of the wallet to create (optional) + * @param {string} [config.privateSpendKey] - private spend key of the wallet to create (optional) + * @param {number} [config.restoreHeight] - block height to start scanning from (defaults to 0 unless generating random wallet) + * @param {string} [config.language] - language of the wallet's seed phrase (defaults to "English" or auto-detected) + * @param {number} [config.accountLookahead] - number of accounts to scan (optional) + * @param {number} [config.subaddressLookahead] - number of subaddresses to scan per account (optional) + * @param {string|Partial} [config.server] - uri or MoneroRpcConnection to the wallet's server (optional) + * @param {MoneroConnectionManager} [config.connectionManager] - manage connections to monerod (optional) + * @param {boolean} [config.rejectUnauthorized] - reject self-signed server certificates if true (default true) + * @param {Uint8Array} [config.keysData] - wallet keys data to open (optional) + * @param {Uint8Array} [config.cacheData] - wallet cache data to open (optional) + * @param {boolean} [config.proxyToWorker] - proxies wallet operations to a worker in order to not block the main thread (default true) + * @param {fs} [config.fs] - Node.js compatible file system to use (defaults to disk or in-memory FS if browser) + * @param {boolean} [config.saveCurrent] - specifies if the current RPC wallet should be saved before being closed + * @param {number} [config.accountLookahead] - number of accounts to scan (optional) + * @param {number} [config.subaddressLookahead] - number of subaddresses to scan per account (optional) + * @param {string[]} [config.cmd] - command to start wallet daemon (optional) + */ + constructor(config?: Partial) { + Object.assign(this, config); + + // normalize config + if (this.server) this.setServer(this.server); + this.setProxyToWorker(this.proxyToWorker); + if (this.networkType !== undefined) this.networkType = MoneroNetworkType.from(this.networkType); + } + + copy(): MoneroWalletConfig { + return new MoneroWalletConfig(this); + } + + toJson(): any { + let json: any = Object.assign({}, this); + if (json.server) json.server = json.server.toJson(); + json.fs = undefined; + json.connectionManager = undefined; + return json; + } + + getPath(): string { + return this.path; + } + + setPath(path: string): MoneroWalletConfig { + this.path = path; + return this; + } + + getPassword(): string { + return this.password; + } + + setPassword(password): MoneroWalletConfig { + this.password = password; + return this; + } + + getNetworkType(): MoneroNetworkType { + return this.networkType; + } + + setNetworkType(networkTypeOrStr: MoneroNetworkType | string): MoneroWalletConfig { + this.networkType = networkTypeOrStr === undefined ? undefined : MoneroNetworkType.from(networkTypeOrStr); + return this; + } + + getServer(): MoneroRpcConnection { + return this.server as MoneroRpcConnection; + } + + setServer(server: Partial | string): MoneroWalletConfig { + if (server && !(server instanceof MoneroRpcConnection)) server = new MoneroRpcConnection(server); + this.server = server as MoneroRpcConnection; + return this; + } + + getConnectionManager(): MoneroConnectionManager { + return this.connectionManager; + } + + setConnectionManager(connectionManager: MoneroConnectionManager): MoneroWalletConfig { + this.connectionManager = connectionManager; + return this; + } + + getSeed(): string { + return this.seed; + } + + setSeed(seed: string): MoneroWalletConfig { + this.seed = seed; + return this; + } + + getSeedOffset(): string { + return this.seedOffset; + } + + setSeedOffset(seedOffset: string): MoneroWalletConfig { + this.seedOffset = seedOffset; + return this; + } + + getIsMultisig(): boolean { + return this.isMultisig; + } + + setIsMultisig(isMultisig: boolean): MoneroWalletConfig { + this.isMultisig = isMultisig; + return this; + } + + getPrimaryAddress(): string { + return this.primaryAddress; + } + + setPrimaryAddress(primaryAddress: string) { + this.primaryAddress = primaryAddress; + return this; + } + + getPrivateViewKey(): string { + return this.privateViewKey; + } + + setPrivateViewKey(privateViewKey): MoneroWalletConfig { + this.privateViewKey = privateViewKey; + return this; + } + + getPrivateSpendKey(): string { + return this.privateSpendKey; + } + + setPrivateSpendKey(privateSpendKey: string): MoneroWalletConfig { + this.privateSpendKey = privateSpendKey; + return this; + } + + getRestoreHeight(): number { + return this.restoreHeight; + } + + setRestoreHeight(restoreHeight: number): MoneroWalletConfig { + this.restoreHeight = restoreHeight; + return this; + } + + getLanguage(): string { + return this.language; + } + + setLanguage(language: string): MoneroWalletConfig { + this.language = language; + return this; + } + + getSaveCurrent(): boolean { + return this.saveCurrent; + } + + setSaveCurrent(saveCurrent: boolean): MoneroWalletConfig { + this.saveCurrent = saveCurrent; + return this; + } + + getProxyToWorker(): boolean { + return this.proxyToWorker; + } + + setProxyToWorker(proxyToWorker: boolean): MoneroWalletConfig { + this.proxyToWorker = proxyToWorker; + return this; + } + + getFs(): any { + return this.fs; + } + + setFs(fs: any) { + this.fs = fs; + return this; + } + + getKeysData(): Uint8Array { + return this.keysData; + } + + setKeysData(keysData: Uint8Array): MoneroWalletConfig { + this.keysData = keysData; + return this; + } + + getCacheData(): Uint8Array { + return this.cacheData; + } + + setCacheData(cacheData: Uint8Array): MoneroWalletConfig { + this.cacheData = cacheData; + return this; + } + + getAccountLookahead(): number { + return this.accountLookahead; + } + + setAccountLookahead(accountLookahead: number): MoneroWalletConfig { + this.accountLookahead = accountLookahead; + return this; + } + + getSubaddressLookahead(): number { + return this.subaddressLookahead; + } + + setSubaddressLookahead(subaddressLookahead: number): MoneroWalletConfig { + this.subaddressLookahead = subaddressLookahead; + return this; + } +} \ No newline at end of file diff --git a/src/main/js/wallet/model/MoneroWalletListener.js b/src/main/ts/wallet/model/MoneroWalletListener.ts similarity index 59% rename from src/main/js/wallet/model/MoneroWalletListener.js rename to src/main/ts/wallet/model/MoneroWalletListener.ts index be603b105..81c8ef209 100644 --- a/src/main/js/wallet/model/MoneroWalletListener.js +++ b/src/main/ts/wallet/model/MoneroWalletListener.ts @@ -1,7 +1,9 @@ +import MoneroOutputWallet from "./MoneroOutputWallet"; + /** * Default wallet listener which takes no action on notifications. */ -class MoneroWalletListener { +export default class MoneroWalletListener { /** * Invoked as the wallet is synchronized. @@ -11,23 +13,26 @@ class MoneroWalletListener { * @param {number} endHeight - ending height of the sync request * @param {number} percentDone - sync progress as a percentage * @param {string} message - human-readable description of the current progress + * @return {Promise} */ - async onSyncProgress(height, startHeight, endHeight, percentDone, message) { } + async onSyncProgress(height: number, startHeight: number, endHeight: number, percentDone: number, message: string) { } /** * Invoked when a new block is added to the chain. * - * @param {int} height - the height of the new block (i.e. the number of blocks before it). + * @param {number} height - the height of the new block (i.e. the number of blocks before it). + * @return {Promise} */ - async onNewBlock(height) { } + async onNewBlock(height: number): Promise { } /** * Invoked when the wallet's balances change. * - * @param {BigInteger} newBalance - new wallet balance - * @param {BigInteger} newUnlockedBalance - new unlocked wallet balance + * @param {bigint} newBalance - new wallet balance + * @param {bigint} newUnlockedBalance - new unlocked wallet balance + * @return {Promise} */ - async onBalancesChanged(newBalance, newUnlockedBalance) { } + async onBalancesChanged(newBalance: bigint, newUnlockedBalance: bigint): Promise { } /** * Invoked 3 times per received output: once when unconfirmed, once when confirmed, and @@ -36,8 +41,9 @@ class MoneroWalletListener { * The notified output includes basic fields only, so the output or its transaction should be fetched to get all available fields. * * @param {MoneroOutputWallet} output - the received output + * @return {Promise} */ - async onOutputReceived(output) { } + async onOutputReceived(output: MoneroOutputWallet): Promise { } /** * Invoked twice per spent output: once when confirmed and once when unlocked. @@ -45,8 +51,7 @@ class MoneroWalletListener { * The notified output includes basic fields only, so the output or its transaction should be fetched to get all available fields. * * @param {MoneroOutputWallet} output - the spent output + * @return {Promise} */ - async onOutputSpent(output) { } + async onOutputSpent(output): Promise { } } - -module.exports = MoneroWalletListener; \ No newline at end of file diff --git a/src/test/Scratchpad.js b/src/test/Scratchpad.ts similarity index 65% rename from src/test/Scratchpad.js rename to src/test/Scratchpad.ts index 1d0361699..eb112b552 100644 --- a/src/test/Scratchpad.js +++ b/src/test/Scratchpad.ts @@ -1,6 +1,9 @@ -const TestUtils = require("./utils/TestUtils"); -const WalletSyncPrinter = require("./utils/WalletSyncPrinter"); -const monerojs = require("../../index"); +import TestUtils from "./utils/TestUtils"; +import WalletSyncPrinter from "./utils/WalletSyncPrinter"; +import {connectToDaemonRpc, + connectToWalletRpc, + createWalletFull, + MoneroNetworkType} from "../../index"; describe("Scratchpad", function() { @@ -11,7 +14,7 @@ describe("Scratchpad", function() { // let walletFull = await TestUtils.getWalletFull(); // initialize daemon rpc client - let daemon = await monerojs.connectToDaemonRpc({ + let daemon = await connectToDaemonRpc({ uri: "http://localhost:28081", username: "superuser", password: "abctesting123", @@ -21,7 +24,7 @@ describe("Scratchpad", function() { console.log("Daemon height: " + await daemon.getHeight()); // initialize wallet rpc client - let walletRpc = await monerojs.connectToWalletRpc({ + let walletRpc = await connectToWalletRpc({ uri: "http://localhost:28084", username: "rpc_user", password: "abc123", @@ -31,20 +34,24 @@ describe("Scratchpad", function() { console.log("RPC wallet seed: " + await walletRpc.getSeed()); // create in-memory wallet with mnemonic - let walletFull = await monerojs.createWalletFull({ + let walletFull = await createWalletFull({ //path: "./test_wallets/" + GenUtils.getUUID(), // in-memory wallet if not given - password: "abctesting123", - networkType: "testnet", - serverUri: "http://localhost:28081", + password: "supersecretpassword123", + networkType: MoneroNetworkType.TESTNET, + server: { + uri: "http://localhost:28081", + username: "superuser", + password: "abctesting123", + rejectUnauthorized: false + }, seed: "silk mocked cucumber lettuce hope adrenalin aching lush roles fuel revamp baptism wrist long tender teardrop midst pastry pigment equip frying inbound pinched ravine frying", restoreHeight: 0, - proxyToWorker: TestUtils.PROXY_TO_WORKER, - rejectUnauthorized: false + proxyToWorker: TestUtils.PROXY_TO_WORKER, }); await walletFull.sync(new WalletSyncPrinter()); console.log("Full wallet daemon height: " + await walletFull.getDaemonHeight()); console.log("Full wallet seed: " + await walletFull.getSeed()); - + console.log("Wallet balance: " + (await walletFull.getUnlockedBalance()).toString()) await walletFull.close(); }); -}); \ No newline at end of file +}); diff --git a/src/test/TestAll.js b/src/test/TestAll.ts similarity index 66% rename from src/test/TestAll.js rename to src/test/TestAll.ts index 54b4c99ac..00f6bb823 100644 --- a/src/test/TestAll.js +++ b/src/test/TestAll.ts @@ -1,13 +1,12 @@ // import test types -const monerojs = require("../../index"); -const LibraryUtils = monerojs.LibraryUtils; -const TestSampleCode = require("./TestSampleCode"); -const TestMoneroUtils = require("./TestMoneroUtils"); -const TestMoneroDaemonRpc = require("./TestMoneroDaemonRpc"); -const TestMoneroWalletKeys = require("./TestMoneroWalletKeys"); -const TestMoneroWalletFull = require("./TestMoneroWalletFull"); -const TestMoneroWalletRpc = require("./TestMoneroWalletRpc"); -const TestMoneroConnectionManager = require("./TestMoneroConnectionManager"); +import {LibraryUtils} from "../../index.js"; +import TestSampleCode from "./TestSampleCode"; +import TestMoneroUtils from "./TestMoneroUtils"; +import TestMoneroDaemonRpc from "./TestMoneroDaemonRpc"; +import TestMoneroWalletKeys from "./TestMoneroWalletKeys"; +import TestMoneroWalletFull from "./TestMoneroWalletFull"; +import TestMoneroWalletRpc from "./TestMoneroWalletRpc"; +import TestMoneroConnectionManager from "./TestMoneroConnectionManager"; // set log level LibraryUtils.setLogLevel(1); // no need for await before worker used @@ -57,4 +56,4 @@ new TestMoneroWalletRpc({ new TestMoneroConnectionManager().runTests(); // test scratchpad -require("./Scratchpad"); \ No newline at end of file +require("./Scratchpad"); diff --git a/src/test/TestMoneroConnectionManager.js b/src/test/TestMoneroConnectionManager.ts similarity index 85% rename from src/test/TestMoneroConnectionManager.js rename to src/test/TestMoneroConnectionManager.ts index 2b23feca2..f5dd90454 100644 --- a/src/test/TestMoneroConnectionManager.js +++ b/src/test/TestMoneroConnectionManager.ts @@ -1,10 +1,12 @@ -const assert = require("assert"); -const TestUtils = require("./utils/TestUtils"); -const monerojs = require("../../index"); -const GenUtils = monerojs.GenUtils; -const MoneroRpcConnection = monerojs.MoneroRpcConnection; -const MoneroConnectionManager = monerojs.MoneroConnectionManager; -const MoneroConnectionManagerListener = monerojs.MoneroConnectionManagerListener; +import assert from "assert"; +import TestUtils from "./utils/TestUtils"; +import { + GenUtils, + MoneroWalletRpc, + MoneroConnectionManager, + MoneroConnectionManagerListener, + MoneroRpcConnection +} from "../../index.js"; /** * Test the Monero RPC connection manager. @@ -15,8 +17,8 @@ class TestMoneroConnectionManager { describe("Test connection manager", function() { it("Can manage connections", async function() { let err; - let walletRpcs = []; - let connectionManager; + let walletRpcs: MoneroWalletRpc[] = []; + let connectionManager: MoneroConnectionManager; try { // start monero-wallet-rpc instances as test server connections (can also use monerod servers) @@ -30,11 +32,11 @@ class TestMoneroConnectionManager { connectionManager.addListener(listener); // add prioritized connections - connectionManager.addConnection(walletRpcs[4].getRpcConnection().setPriority(1)); - connectionManager.addConnection(walletRpcs[2].getRpcConnection().setPriority(2)); - connectionManager.addConnection(walletRpcs[3].getRpcConnection().setPriority(2)); - connectionManager.addConnection(walletRpcs[0].getRpcConnection()); // default priority is lowest - connectionManager.addConnection(new MoneroRpcConnection(walletRpcs[1].getRpcConnection().getUri())); // test unauthenticated + await connectionManager.addConnection(walletRpcs[4].getRpcConnection()!.setPriority(1)); + await connectionManager.addConnection(walletRpcs[2].getRpcConnection()!.setPriority(2)); + await connectionManager.addConnection(walletRpcs[3].getRpcConnection()!.setPriority(2)); + await connectionManager.addConnection(walletRpcs[0].getRpcConnection()); // default priority is lowest + await connectionManager.addConnection(new MoneroRpcConnection(walletRpcs[1].getRpcConnection()!.getUri())); // test unauthenticated // test connections and order let orderedConnections = connectionManager.getConnections(); @@ -42,12 +44,12 @@ class TestMoneroConnectionManager { assert(orderedConnections[1] === walletRpcs[2].getRpcConnection()); assert(orderedConnections[2] === walletRpcs[3].getRpcConnection()); assert(orderedConnections[3] === walletRpcs[0].getRpcConnection()); - assert.equal(orderedConnections[4].getUri(), (walletRpcs[1].getRpcConnection()).getUri()); - for (let connection of orderedConnections) assert.equal(undefined, connection.isOnline()); + assert.equal(orderedConnections[4].getUri(), (walletRpcs[1].getRpcConnection()!).getUri()); + for (let connection of orderedConnections) assert.equal(undefined, connection.getIsOnline()); // test getting connection by uri - assert(connectionManager.hasConnection(walletRpcs[0].getRpcConnection().getUri())); - assert(connectionManager.getConnectionByUri(walletRpcs[0].getRpcConnection().getUri()) === walletRpcs[0].getRpcConnection()); + assert(connectionManager.hasConnection(walletRpcs[0].getRpcConnection()!.getUri())); + assert(connectionManager.getConnectionByUri(walletRpcs[0].getRpcConnection()!.getUri()) === walletRpcs[0].getRpcConnection()); // test unknown connection let numExpectedChanges = 0; @@ -60,7 +62,7 @@ class TestMoneroConnectionManager { await GenUtils.waitFor(TestUtils.AUTO_CONNECT_TIMEOUT_MS); assert(connectionManager.isConnected()); let connection = connectionManager.getConnection(); - assert(connection.isOnline()); + assert(connection.getIsOnline()); assert(connection === walletRpcs[4].getRpcConnection()); assert.equal(listener.changedConnections.length, ++numExpectedChanges); assert(listener.changedConnections[listener.changedConnections.length - 1] === connection); @@ -77,8 +79,8 @@ class TestMoneroConnectionManager { connection = await connectionManager.getBestAvailableConnection(); await connectionManager.setConnection(connection); assert(connection === walletRpcs[4].getRpcConnection()); - assert(connection.isOnline()); - assert(connection.isAuthenticated()); + assert(connection.getIsOnline()); + assert(connection.getIsAuthenticated()); assert.equal(listener.changedConnections.length, ++numExpectedChanges); assert(listener.changedConnections[listener.changedConnections.length - 1] === connection); @@ -88,17 +90,17 @@ class TestMoneroConnectionManager { assert(orderedConnections[1] === walletRpcs[2].getRpcConnection()); assert(orderedConnections[2] === walletRpcs[3].getRpcConnection()); assert(orderedConnections[3] === walletRpcs[0].getRpcConnection()); - assert.equal(orderedConnections[4].getUri(), walletRpcs[1].getRpcConnection().getUri()); - for (let i = 1; i < orderedConnections.length; i++) assert.equal(undefined, orderedConnections[i].isOnline()); + assert.equal(orderedConnections[4].getUri(), walletRpcs[1].getRpcConnection()!.getUri()); + for (let i = 1; i < orderedConnections.length; i++) assert.equal(undefined, orderedConnections[i].getIsOnline()); // shut down prioritized servers - walletRpcs[2].getRpcConnection()._setFakeDisconnected(true); // browser does not start or stop instances - walletRpcs[3].getRpcConnection()._setFakeDisconnected(true); - walletRpcs[4].getRpcConnection()._setFakeDisconnected(true); + walletRpcs[2].getRpcConnection()!.setFakeDisconnected(true); // browser does not start or stop instances + walletRpcs[3].getRpcConnection()!.setFakeDisconnected(true); + walletRpcs[4].getRpcConnection()!.setFakeDisconnected(true); await GenUtils.waitFor(TestUtils.SYNC_PERIOD_IN_MS + 100); assert.equal(false, connectionManager.isConnected()); - assert.equal(false, connectionManager.getConnection().isOnline()); - assert.equal(undefined, connectionManager.getConnection().isAuthenticated()); + assert.equal(false, connectionManager.getConnection().getIsOnline()); + assert.equal(undefined, connectionManager.getConnection().getIsAuthenticated()); assert.equal(listener.changedConnections.length, ++numExpectedChanges); assert(listener.changedConnections[listener.changedConnections.length - 1] === connectionManager.getConnection()); @@ -106,7 +108,7 @@ class TestMoneroConnectionManager { orderedConnections = connectionManager.getConnections(); assert(orderedConnections[0] === walletRpcs[4].getRpcConnection()); assert(orderedConnections[1] === walletRpcs[0].getRpcConnection()); - assert.equal(orderedConnections[2].getUri(), walletRpcs[1].getRpcConnection().getUri()); + assert.equal(orderedConnections[2].getUri(), walletRpcs[1].getRpcConnection()!.getUri()); assert(orderedConnections[3] === walletRpcs[2].getRpcConnection()); assert(orderedConnections[4] === walletRpcs[3].getRpcConnection()); @@ -117,14 +119,14 @@ class TestMoneroConnectionManager { orderedConnections = connectionManager.getConnections(); assert(orderedConnections[0] === walletRpcs[4].getRpcConnection()); assert(orderedConnections[1] === walletRpcs[0].getRpcConnection()); - assert(orderedConnections[2].getUri() === walletRpcs[1].getRpcConnection().getUri()); + assert(orderedConnections[2].getUri() === walletRpcs[1].getRpcConnection()!.getUri()); assert(orderedConnections[3] === walletRpcs[2].getRpcConnection()); assert(orderedConnections[4] === walletRpcs[3].getRpcConnection()); // test online and authentication status for (let i = 0; i < orderedConnections.length; i++) { - let isOnline = orderedConnections[i].isOnline(); - let isAuthenticated = orderedConnections[i].isAuthenticated(); + let isOnline = orderedConnections[i].getIsOnline(); + let isAuthenticated = orderedConnections[i].getIsAuthenticated(); if (i === 1 || i === 2) assert.equal(true, isOnline); else assert.equal(false, isOnline); if (i === 1) assert.equal(true, isAuthenticated); @@ -137,7 +139,7 @@ class TestMoneroConnectionManager { await GenUtils.waitFor(TestUtils.SYNC_PERIOD_IN_MS + 100); // allow time to poll assert(connectionManager.isConnected()); connection = connectionManager.getConnection(); - assert(connection.isOnline()); + assert(connection.getIsOnline()); assert(connection === walletRpcs[0].getRpcConnection()); assert.equal(listener.changedConnections.length, ++numExpectedChanges); assert(listener.changedConnections[listener.changedConnections.length - 1] === connection); @@ -146,14 +148,14 @@ class TestMoneroConnectionManager { orderedConnections = connectionManager.getConnections(); assert(orderedConnections[0] === connection); assert(orderedConnections[0] === walletRpcs[0].getRpcConnection()); - assert(orderedConnections[1].getUri() === walletRpcs[1].getRpcConnection().getUri()); + assert(orderedConnections[1].getUri() === walletRpcs[1].getRpcConnection()!.getUri()); assert(orderedConnections[2] === walletRpcs[4].getRpcConnection()); assert(orderedConnections[3] === walletRpcs[2].getRpcConnection()); assert(orderedConnections[4] === walletRpcs[3].getRpcConnection()); // connect to specific endpoint without authentication connection = orderedConnections[1]; - assert.equal(false, connection.isAuthenticated()); + assert.equal(false, connection.getIsAuthenticated()); await connectionManager.setConnection(connection); assert.equal(false, connectionManager.isConnected()); assert.equal(listener.changedConnections.length, ++numExpectedChanges); @@ -162,24 +164,24 @@ class TestMoneroConnectionManager { connectionManager.setAutoSwitch(false); orderedConnections[1].setCredentials("rpc_user", "abc123"); await connectionManager.checkConnection(); - assert.equal(connection.getUri(), walletRpcs[1].getRpcConnection().getUri()); - assert(connection.isOnline()); - assert(connection.isAuthenticated()); + assert.equal(connection.getUri(), walletRpcs[1].getRpcConnection()!.getUri()); + assert(connection.getIsOnline()); + assert(connection.getIsAuthenticated()); assert.equal(listener.changedConnections.length, ++numExpectedChanges); assert(listener.changedConnections[listener.changedConnections.length - 1] === connection); // test connection order orderedConnections = connectionManager.getConnections(); assert(orderedConnections[0] === connectionManager.getConnection()); - assert.equal(orderedConnections[0].getUri(), walletRpcs[1].getRpcConnection().getUri()); + assert.equal(orderedConnections[0].getUri(), walletRpcs[1].getRpcConnection()!.getUri()); assert(orderedConnections[1] === walletRpcs[0].getRpcConnection()); assert(orderedConnections[2] === walletRpcs[4].getRpcConnection()); assert(orderedConnections[3] === walletRpcs[2].getRpcConnection()); assert(orderedConnections[4] === walletRpcs[3].getRpcConnection()); - for (let i = 0; i < orderedConnections.length; i++) assert(i <= 1 ? orderedConnections[i].isOnline() : !orderedConnections[i].isOnline()); + for (let i = 0; i < orderedConnections.length; i++) assert(i <= 1 ? orderedConnections[i].getIsOnline() : !orderedConnections[i].getIsOnline()); // set connection to existing uri - await connectionManager.setConnection(walletRpcs[0].getRpcConnection().getUri()); + await connectionManager.setConnection(walletRpcs[0].getRpcConnection()!.getUri()); assert(connectionManager.isConnected()); assert(walletRpcs[0].getRpcConnection() === connectionManager.getConnection()); assert.equal(TestUtils.WALLET_RPC_CONFIG.username, connectionManager.getConnection().getUsername()); @@ -236,9 +238,9 @@ class TestMoneroConnectionManager { // shut down all connections connection = connectionManager.getConnection(); - for (let connection of orderedConnections) connection._setFakeDisconnected(true); + for (let connection of orderedConnections) connection.setFakeDisconnected(true); await GenUtils.waitFor(TestUtils.SYNC_PERIOD_IN_MS + 100); - assert.equal(false, connection.isOnline()); + assert.equal(false, connection.getIsOnline()); assert.equal(listener.changedConnections.length, ++numExpectedChanges); assert(listener.changedConnections[listener.changedConnections.length - 1] === connection); @@ -267,6 +269,9 @@ class TestMoneroConnectionManager { } class ConnectionChangeCollector extends MoneroConnectionManagerListener { + + changedConnections: MoneroRpcConnection[] = []; + constructor() { super(); this.changedConnections = []; @@ -276,4 +281,4 @@ class ConnectionChangeCollector extends MoneroConnectionManagerListener { } } -module.exports = TestMoneroConnectionManager; \ No newline at end of file +export default TestMoneroConnectionManager; diff --git a/src/test/TestMoneroDaemonRpc.js b/src/test/TestMoneroDaemonRpc.ts similarity index 84% rename from src/test/TestMoneroDaemonRpc.js rename to src/test/TestMoneroDaemonRpc.ts index 6d4c5852e..fefc2f6d1 100644 --- a/src/test/TestMoneroDaemonRpc.js +++ b/src/test/TestMoneroDaemonRpc.ts @@ -1,19 +1,25 @@ -const assert = require("assert"); -const TestUtils = require("./utils/TestUtils"); -const monerojs = require("../../index"); -const BigInteger = monerojs.BigInteger; -const ConnectionType = monerojs.ConnectionType; -const GenUtils = monerojs.GenUtils; -const MoneroOutput = monerojs.MoneroOutput; -const MoneroTxConfig = monerojs.MoneroTxConfig; -const MoneroBan = monerojs.MoneroBan; -const MoneroKeyImage = monerojs.MoneroKeyImage; -const MoneroTx = monerojs.MoneroTx; -const MoneroAltChain = monerojs.MoneroAltChain; -const MoneroDaemonListener = monerojs.MoneroDaemonListener; -const MoneroDaemonSyncInfo = monerojs.MoneroDaemonSyncInfo; -const MoneroPeer = monerojs.MoneroPeer; -const MoneroKeyImageSpentStatus = monerojs.MoneroKeyImageSpentStatus; +import assert from "assert"; +import TestUtils from "./utils/TestUtils"; +import {connectToDaemonRpc, + GenUtils, + MoneroDaemonInfo, + MoneroNetworkType, + MoneroTxWallet, + MoneroTx, + MoneroKeyImageSpentStatus, + MoneroWallet, + MoneroDaemon, + MoneroPeer, + MoneroDaemonUpdateCheckResult, + MoneroBan, + MoneroDaemonListener, + MoneroDaemonSyncInfo, + MoneroTxConfig, + MoneroKeyImage, + MoneroOutput, + MoneroAltChain, + MoneroSubmitTxResult, + MoneroTxPoolStats} from "../../index"; // context for testing binary blocks // TODO: binary blocks have inconsistent client-side pruning @@ -23,7 +29,17 @@ const BINARY_BLOCK_CTX = { hasHex: false, headerIsFull: false, hasTxs: true, ctx /** * Tests a Monero daemon. */ -class TestMoneroDaemonRpc { +export default class TestMoneroDaemonRpc { + + // static variables + static readonly MAX_REQ_SIZE = "3000000"; + static readonly DEFAULT_ID = "0000000000000000000000000000000000000000000000000000000000000000"; // uninitialized tx or block hash from daemon rpc + static readonly NUM_HEADERS_PER_REQ = 750; // number of headers to fetch and cache per request + + // state variables + testConfig: any; + wallet: MoneroWallet; + daemon: MoneroDaemon; constructor(testConfig) { this.testConfig = testConfig; @@ -59,7 +75,7 @@ class TestMoneroDaemonRpc { // create command to start monerod process let cmd = [ TestUtils.DAEMON_LOCAL_PATH, - "--" + monerojs.MoneroNetworkType.toString(TestUtils.NETWORK_TYPE).toLowerCase(), + "--" + GenUtils.getEnumKeyByValue(MoneroNetworkType, TestUtils.NETWORK_TYPE)!.toLowerCase(), "--no-igd", "--hide-my-port", "--data-dir", TestUtils.MONERO_BINS_DIR + "/node1", @@ -70,7 +86,7 @@ class TestMoneroDaemonRpc { ]; // start monerod process from command - let daemon = await monerojs.connectToDaemonRpc(cmd); + let daemon = await connectToDaemonRpc(cmd); // query daemon let connection = await daemon.getRpcConnection(); @@ -89,7 +105,7 @@ class TestMoneroDaemonRpc { it("Can get the daemon's version", async function() { let version = await that.daemon.getVersion(); assert(version.getNumber() > 0); - assert.equal(typeof version.isRelease(), "boolean"); + assert.equal(typeof version.getIsRelease(), "boolean"); }); if (testConfig.testNonRelays) @@ -233,12 +249,14 @@ class TestMoneroDaemonRpc { // select random heights // TODO: this is horribly inefficient way of computing last 100 blocks if not shuffling let currentHeight = await that.daemon.getHeight(); - let allHeights = []; + let allHeights: number[] = []; for (let i = 0; i < currentHeight - 1; i++) allHeights.push(i); //GenUtils.shuffle(allHeights); - let heights = []; + let heights: number[] = []; for (let i = allHeights.length - numBlocks; i < allHeights.length; i++) heights.push(allHeights[i]); + //heights.push(allHeights[i]); + // fetch blocks let blocks = await that.daemon.getBlocksByHeight(heights); @@ -342,7 +360,7 @@ class TestMoneroDaemonRpc { try { await that.daemon.getTx("invalid tx hash"); throw new Error("fail"); - } catch (e) { + } catch (e: any) { assert.equal("Invalid transaction hash", e.message); } }); @@ -381,7 +399,7 @@ class TestMoneroDaemonRpc { try { await that.daemon.getTxs(txHashes); throw new Error("fail"); - } catch (e) { + } catch (e: any) { assert.equal("Invalid transaction hash", e.message); } }); @@ -391,12 +409,12 @@ class TestMoneroDaemonRpc { await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(that.wallet); // wait for wallet's txs in the pool to clear to ensure reliable sync // submit txs to the pool but don't relay - let txHashes = []; + let txHashes: string[] = []; for (let i = 1; i < 3; i++) { let tx = await getUnrelayedTx(that.wallet, i); let result = await that.daemon.submitTxHex(tx.getFullHex(), true); testSubmitTxResultGood(result); - assert.equal(result.isRelayed(), false); + assert.equal(result.getIsRelayed(), false); txHashes.push(tx.getHash()); } @@ -421,8 +439,8 @@ class TestMoneroDaemonRpc { let txHashes = await getConfirmedTxHashes(that.daemon); // fetch each tx hex by hash with and without pruning - let hexes = [] - let hexesPruned = []; + let hexes: string[] = [] + let hexesPruned: string[] = []; for (let txHash of txHashes) { hexes.push(await that.daemon.getTxHex(txHash)); hexesPruned.push(await that.daemon.getTxHex(txHash, true)); @@ -442,7 +460,7 @@ class TestMoneroDaemonRpc { try { await that.daemon.getTxHex("invalid tx hash"); throw new Error("fail"); - } catch (e) { + } catch (e: any) { assert.equal("Invalid transaction hash", e.message); } }); @@ -472,7 +490,7 @@ class TestMoneroDaemonRpc { try { await that.daemon.getTxHexes(txHashes); throw new Error("fail"); - } catch (e) { + } catch (e: any) { assert.equal("Invalid transaction hash", e.message); } }); @@ -486,10 +504,10 @@ class TestMoneroDaemonRpc { if (testConfig.testNonRelays) it("Can get a fee estimate", async function() { let feeEstimate = await that.daemon.getFeeEstimate(); - TestUtils.testUnsignedBigInteger(feeEstimate.getFee(), true); + TestUtils.testUnsignedBigInt(feeEstimate.getFee(), true); assert(feeEstimate.getFees().length === 4); // slow, normal, fast, fastest - for (let i = 0; i < 4; i++) TestUtils.testUnsignedBigInteger(feeEstimate.getFees()[i], true); - TestUtils.testUnsignedBigInteger(feeEstimate.getQuantizationMask(), true); + for (let i = 0; i < 4; i++) TestUtils.testUnsignedBigInt(feeEstimate.getFees()[i], true); + TestUtils.testUnsignedBigInt(feeEstimate.getQuantizationMask(), true); }); if (testConfig.testNonRelays) @@ -500,7 +518,7 @@ class TestMoneroDaemonRpc { let tx = await getUnrelayedTx(that.wallet, 0); let result = await that.daemon.submitTxHex(tx.getFullHex(), true); testSubmitTxResultGood(result); - assert.equal(result.isRelayed(), false); + assert.equal(result.getIsRelayed(), false); // fetch txs in pool let txs = await that.daemon.getTxPool(); @@ -542,7 +560,7 @@ class TestMoneroDaemonRpc { // submit tx hex let tx = await getUnrelayedTx(that.wallet, i); let result = await that.daemon.submitTxHex(tx.getFullHex(), true); - assert.equal(result.isGood(), true, "Bad tx submit result: " + result.toJson()); + assert.equal(result.getIsGood(), true, "Bad tx submit result: " + result.toJson()); // get tx pool stats let stats = await that.daemon.getTxPoolStats(); @@ -579,7 +597,7 @@ class TestMoneroDaemonRpc { // re-submit original transactions for (let tx of txPoolBefore) { - let result = await that.daemon.submitTxHex(tx.getFullHex(), tx.isRelayed()); + let result = await that.daemon.submitTxHex(tx.getFullHex(), tx.getIsRelayed()); testSubmitTxResultGood(result); } @@ -598,7 +616,7 @@ class TestMoneroDaemonRpc { let txPoolBefore = await that.daemon.getTxPool(); // submit txs to the pool but don't relay - let txs = []; + let txs: MoneroTx[] = []; for (let i = 1; i < 3; i++) { let tx = await getUnrelayedTx(that.wallet, i); let result = await that.daemon.submitTxHex(tx.getFullHex(), true); @@ -632,7 +650,7 @@ class TestMoneroDaemonRpc { let txPoolBefore = await that.daemon.getTxPool(); // submit txs to the pool but don't relay - let txHashes = []; + let txHashes: string[] = []; for (let i = 1; i < 3; i++) { let tx = await getUnrelayedTx(that.wallet, i); let result = await that.daemon.submitTxHex(tx.getFullHex(), true); @@ -654,13 +672,13 @@ class TestMoneroDaemonRpc { await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(that.wallet); // submit txs to the pool to collect key images then flush them - let txs = []; + let txs: MoneroTx[] = []; for (let i = 1; i < 3; i++) { let tx = await getUnrelayedTx(that.wallet, i); await that.daemon.submitTxHex(tx.getFullHex(), true); txs.push(tx); } - let keyImages = []; + let keyImages: string[] = []; let txHashes = txs.map(tx => tx.getHash()); for (let tx of await that.daemon.getTxs(txHashes)) { for (let input of tx.getInputs()) keyImages.push(input.getKeyImage().getHex()); @@ -725,22 +743,22 @@ class TestMoneroDaemonRpc { } }); - if (testConfig.testNonRelays) - it("Can get an output distribution (binary)", async function() { - let amounts = []; - amounts.push(new BigInteger(0)); - amounts.push(new BigInteger(1)); - amounts.push(new BigInteger(10)); - amounts.push(new BigInteger(100)); - amounts.push(new BigInteger(1000)); - amounts.push(new BigInteger(10000)); - amounts.push(new BigInteger(100000)); - amounts.push(new BigInteger(1000000)); - let entries = await that.daemon.getOutputDistribution(amounts); - for (let entry of entries) { - testOutputDistributionEntry(entry); - } - }); + // if (testConfig.testNonRelays) + // it("Can get an output distribution (binary)", async function() { + // let amounts: bigint[] = []; + // amounts.push(BigInt(0)); + // amounts.push(BigInt(1)); + // amounts.push(BigInt(10)); + // amounts.push(BigInt(100)); + // amounts.push(BigInt(1000)); + // amounts.push(BigInt(10000)); + // amounts.push(BigInt(100000)); + // amounts.push(BigInt(1000000)); + // let entries = await that.daemon.getOutputDistribution(amounts); + // for (let entry of entries) { + // testOutputDistributionEntry(entry); + // } + // }); if (testConfig.testNonRelays) it("Can get general information", async function() { @@ -790,22 +808,16 @@ class TestMoneroDaemonRpc { assert.equal(resetVal, initVal); // test invalid limits - try { - await that.daemon.setDownloadLimit(); - fail("Should have thrown error on invalid input"); - } catch (e) { - assert.equal("Download limit must be an integer greater than 0", e.message); - } try { await that.daemon.setDownloadLimit(0); - fail("Should have thrown error on invalid input"); - } catch (e) { + throw new Error("Should have thrown error on invalid input"); + } catch (e: any) { assert.equal("Download limit must be an integer greater than 0", e.message); } try { await that.daemon.setDownloadLimit(1.2); - fail("Should have thrown error on invalid input"); - } catch (e) { + throw new Error("Should have thrown error on invalid input"); + } catch (e: any) { assert.equal("Download limit must be an integer greater than 0", e.message); } assert.equal(await that.daemon.getDownloadLimit(), initVal); @@ -822,22 +834,16 @@ class TestMoneroDaemonRpc { assert.equal(resetVal, initVal); // test invalid limits - try { - await that.daemon.setUploadLimit(); - fail("Should have thrown error on invalid input"); - } catch (e) { - assert.equal("Upload limit must be an integer greater than 0", e.message); - } try { await that.daemon.setUploadLimit(0); - fail("Should have thrown error on invalid input"); - } catch (e) { + throw new Error("Should have thrown error on invalid input"); + } catch (e: any) { assert.equal("Upload limit must be an integer greater than 0", e.message); } try { await that.daemon.setUploadLimit(1.2); - fail("Should have thrown error on invalid input"); - } catch (e) { + throw new Error("Should have thrown error on invalid input"); + } catch (e: any) { assert.equal("Upload limit must be an integer greater than 0", e.message); } assert.equal(await that.daemon.getUploadLimit(), initVal); @@ -867,12 +873,6 @@ class TestMoneroDaemonRpc { await that.daemon.setOutgoingPeerLimit(0); await that.daemon.setOutgoingPeerLimit(8); await that.daemon.setOutgoingPeerLimit(10); - try { - await that.daemon.setOutgoingPeerLimit("a"); - throw new Error("Should have failed on invalid input"); - } catch(e) { - assert.notEqual("Should have failed on invalid input", e.message); - } }); if (testConfig.testNonRelays) @@ -880,22 +880,17 @@ class TestMoneroDaemonRpc { await that.daemon.setIncomingPeerLimit(0); await that.daemon.setIncomingPeerLimit(8); await that.daemon.setIncomingPeerLimit(10); - try { - await that.daemon.setIncomingPeerLimit("a"); - throw new Error("Should have failed on invalid input"); - } catch(e) { - assert.notEqual("Should have failed on invalid input", e.message); - } }); if (testConfig.testNonRelays) it("Can ban a peer", async function() { // set ban - let ban = new MoneroBan(); - ban.setHost("192.168.1.56"); - ban.setIsBanned(true); - ban.setSeconds(60); + let ban = new MoneroBan({ + host: "192.168.1.56", + isBanned: true, + seconds: 60 + }); await that.daemon.setPeerBan(ban); // test ban @@ -920,7 +915,7 @@ class TestMoneroDaemonRpc { ban2.setHost("192.168.1.53"); ban2.setIsBanned(true); ban2.setSeconds(60); - let bans = []; + let bans: MoneroBan[] = []; bans.push(ban1); bans.push(ban2); await that.daemon.setPeerBans(bans); @@ -966,11 +961,11 @@ class TestMoneroDaemonRpc { // test status without mining let status = await that.daemon.getMiningStatus(); - assert.equal(status.isActive(), false); + assert.equal(status.getIsActive(), false); assert.equal(status.getAddress(), undefined); assert.equal(status.getSpeed(), 0); assert.equal(status.getNumThreads(), 0); - assert.equal(status.isBackground(), undefined); + assert.equal(status.getIsBackground(), undefined); // test status with mining let address = await that.wallet.getPrimaryAddress(); @@ -978,11 +973,11 @@ class TestMoneroDaemonRpc { let isBackground = false; await that.daemon.startMining(address, threadCount, isBackground, true); status = await that.daemon.getMiningStatus(); - assert.equal(status.isActive(), true); + assert.equal(status.getIsActive(), true); assert.equal(status.getAddress(), address); assert(status.getSpeed() >= 0); assert.equal(status.getNumThreads(), threadCount); - assert.equal(status.isBackground(), isBackground); + assert.equal(status.getIsBackground(), isBackground); } catch(e) { throw e; } finally { @@ -1004,8 +999,8 @@ class TestMoneroDaemonRpc { // try to submit block hashing blob without nonce try { await that.daemon.submitBlock(template.getBlockHashingBlob()); - fail("Should have thrown error"); - } catch (e) { + throw new Error("Should have thrown error"); + } catch (e: any) { assert.equal(e.getCode(), -7); assert.equal(e.message, "Block not accepted"); } @@ -1014,7 +1009,7 @@ class TestMoneroDaemonRpc { if (testConfig.testNonRelays) it("Can prune the blockchain", async function() { let result = await that.daemon.pruneBlockchain(true); - assert(result.isPruned()); + assert(result.getIsPruned()); assert(result.getPruningSeed() > 0); }); @@ -1037,11 +1032,11 @@ class TestMoneroDaemonRpc { testUpdateDownloadResult(result, path); // test invalid path - if (result.isUpdateAvailable()) { + if (result.getIsUpdateAvailable()) { try { result = await that.daemon.downloadUpdate("./ohhai/there"); throw new Error("Should have thrown error"); - } catch(e) { + } catch (e: any) { assert.notEqual("Should have thrown error", e.message); assert.equal(e.statusCode, 500); // TODO monerod: this causes a 500 in that.daemon rpc } @@ -1065,7 +1060,7 @@ class TestMoneroDaemonRpc { try { await that.daemon.getHeight(); throw new Error("Should have thrown error"); - } catch(e) { + } catch (e: any) { console.log(e); assert.notEqual("Should have thrown error", e.message); } @@ -1085,7 +1080,7 @@ class TestMoneroDaemonRpc { // submit and relay tx1 let result = await that.daemon.submitTxHex(tx1.getFullHex()); - assert.equal(result.isRelayed(), true); + assert.equal(result.getIsRelayed(), true); testSubmitTxResultGood(result); // tx1 is in the pool @@ -1093,7 +1088,7 @@ class TestMoneroDaemonRpc { let found = false; for (let aTx of txs) { if (aTx.getHash() === tx1.getHash()) { - assert.equal(aTx.isRelayed(), true); + assert.equal(aTx.getIsRelayed(), true); found = true; break; } @@ -1106,7 +1101,7 @@ class TestMoneroDaemonRpc { // submit and relay tx2 hex which double spends tx1 result = await that.daemon.submitTxHex(tx2.getFullHex()); - assert.equal(result.isRelayed(), true); + assert.equal(result.getIsRelayed(), true); testSubmitTxResultDoubleSpend(result); // tx2 is in not the pool @@ -1134,7 +1129,7 @@ class TestMoneroDaemonRpc { if (testConfig.testRelays && !testConfig.liteMode) it("Can submit txs in hex format to the pool then relay", async function() { await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(that.wallet); - let txs = []; + let txs: MoneroTxWallet[] = []; txs.push(await getUnrelayedTx(that.wallet, 1)); txs.push(await getUnrelayedTx(that.wallet, 2)); // TODO: accounts cannot be re-used across send tests else isRelayed is true; wallet needs to update? await testSubmitThenRelay(txs); @@ -1143,19 +1138,19 @@ class TestMoneroDaemonRpc { async function testSubmitThenRelay(txs) { // submit txs hex but don't relay - let txHashes = []; + let txHashes: string[] = []; for (let tx of txs) { txHashes.push(tx.getHash()); let result = await that.daemon.submitTxHex(tx.getFullHex(), true); testSubmitTxResultGood(result); - assert.equal(result.isRelayed(), false); + assert.equal(result.getIsRelayed(), false); // ensure tx is in pool let poolTxs = await that.daemon.getTxPool(); let found = false; for (let aTx of poolTxs) { if (aTx.getHash() === tx.getHash()) { - assert.equal(aTx.isRelayed(), false); + assert.equal(aTx.getIsRelayed(), false); found = true; break; } @@ -1164,7 +1159,7 @@ class TestMoneroDaemonRpc { // fetch tx by hash and ensure not relayed let fetchedTx = await that.daemon.getTx(tx.getHash()); - assert.equal(fetchedTx.isRelayed(), false); + assert.equal(fetchedTx.getIsRelayed(), false); } // relay the txs @@ -1184,7 +1179,7 @@ class TestMoneroDaemonRpc { let found = false; for (let aTx of poolTxs) { if (aTx.getHash() === tx.getHash()) { - assert.equal(aTx.isRelayed(), true); + assert.equal(aTx.getIsRelayed(), true); found = true; break; } @@ -1206,20 +1201,20 @@ class TestMoneroDaemonRpc { // start mining if possible to help push the network along let address = await that.wallet.getPrimaryAddress(); try { await that.daemon.startMining(address, 8, false, true); } - catch (e) { if ("BUSY" === e.message) throw e; } + catch (e: any) { if ("BUSY" === e.message) throw e; } // register a listener let listenerHeader; let listener = new class extends MoneroDaemonListener { async onBlockHeader(header) { listenerHeader = header; - await that.daemon.removeListener(listener); // otherwise daemon will keep polling } } await that.daemon.addListener(listener); // wait for next block notification let header = await that.daemon.waitForNextBlockHeader(); + await that.daemon.removeListener(listener); // otherwise daemon will keep polling testBlockHeader(header, true); // test that listener was called with equivalent header @@ -1253,8 +1248,8 @@ function testBlockHeader(header, isFull) { assert(header.getPowHash() === undefined); // never seen defined assert(!isFull ? undefined === header.getSize() : header.getSize()); assert(!isFull ? undefined === header.getDepth() : header.getDepth() >= 0); - assert(!isFull ? undefined === header.getDifficulty() : header.getDifficulty().toJSValue() > 0); - assert(!isFull ? undefined === header.getCumulativeDifficulty() : header.getCumulativeDifficulty().toJSValue() > 0); + assert(!isFull ? undefined === header.getDifficulty() : header.getDifficulty() > 0); + assert(!isFull ? undefined === header.getCumulativeDifficulty() : header.getCumulativeDifficulty() > 0); assert(!isFull ? undefined === header.getHash() : header.getHash().length === 64); assert(!isFull ? undefined === header.getMinerTxHash() : header.getMinerTxHash().length === 64); assert(!isFull ? undefined === header.getNumTxs() : header.getNumTxs() >= 0); @@ -1302,13 +1297,13 @@ function testBlock(block, ctx) { function testMinerTx(minerTx) { assert(minerTx); assert(minerTx instanceof MoneroTx); - assert.equal(typeof minerTx.isMinerTx(), "boolean"); - assert(minerTx.isMinerTx()); + assert.equal(typeof minerTx.getIsMinerTx(), "boolean"); + assert(minerTx.getIsMinerTx()); assert(minerTx.getVersion() >= 0); - assert(Array.isArray(minerTx.getExtra())); + assert(minerTx.getExtra() instanceof Uint8Array); assert(minerTx.getExtra().length > 0); - assert(minerTx.getUnlockTime().compare(new BigInteger(0)) >= 0); + assert(minerTx.getUnlockTime() >= BigInt(0)); // TODO: miner tx does not have hashes in binary requests so this will fail, need to derive using prunable data // testTx(minerTx, { @@ -1322,7 +1317,7 @@ function testMinerTx(minerTx) { } // TODO: how to test output indices? comes back with /get_transactions, maybe others -function testTx(tx, ctx) { +function testTx(tx: MoneroTx, ctx) { // check inputs assert(tx); @@ -1333,41 +1328,42 @@ function testTx(tx, ctx) { // standard across all txs assert(tx.getHash().length === 64); - if (tx.isRelayed() === undefined) assert(tx.inTxPool()); // TODO monerod: add relayed to get_transactions - else assert.equal(typeof tx.isRelayed(), "boolean"); - assert.equal(typeof tx.isConfirmed(), "boolean"); - assert.equal(typeof tx.inTxPool(), "boolean"); - assert.equal(typeof tx.isMinerTx(), "boolean"); - assert.equal(typeof tx.isDoubleSpendSeen(), "boolean"); + if (tx.getIsRelayed() === undefined) assert(tx.getInTxPool()); // TODO monerod: add relayed to get_transactions + else assert.equal(typeof tx.getIsRelayed(), "boolean"); + assert.equal(typeof tx.getIsConfirmed(), "boolean"); + assert.equal(typeof tx.getInTxPool(), "boolean"); + assert.equal(typeof tx.getIsMinerTx(), "boolean"); + assert.equal(typeof tx.getIsDoubleSpendSeen(), "boolean"); assert(tx.getVersion() >= 0); - assert(tx.getUnlockTime().compare(new BigInteger(0)) >= 0); + assert(tx.getUnlockTime() >= BigInt(0)); assert(tx.getInputs()); assert(tx.getOutputs()); + assert(tx.getExtra() instanceof Uint8Array); assert(tx.getExtra().length > 0); - TestUtils.testUnsignedBigInteger(tx.getFee(), true); + TestUtils.testUnsignedBigInt(tx.getFee(), true); // test presence of output indices // TODO: change this over to outputs only - if (tx.isMinerTx()) assert.equal(tx.getOutputIndices(), undefined); // TODO: how to get output indices for miner transactions? - if (tx.inTxPool() || ctx.fromGetTxPool || ctx.hasOutputIndices === false) assert.equal(tx.getOutputIndices(), undefined); + if (tx.getIsMinerTx()) assert.equal(tx.getOutputIndices(), undefined); // TODO: how to get output indices for miner transactions? + if (tx.getInTxPool() || ctx.fromGetTxPool || ctx.hasOutputIndices === false) assert.equal(tx.getOutputIndices(), undefined); else assert(tx.getOutputIndices()); if (tx.getOutputIndices()) assert(tx.getOutputIndices().length > 0); // test confirmed ctx - if (ctx.isConfirmed === true) assert.equal(tx.isConfirmed(), true); - if (ctx.isConfirmed === false) assert.equal(tx.isConfirmed(), false); + if (ctx.isConfirmed === true) assert.equal(tx.getIsConfirmed(), true); + if (ctx.isConfirmed === false) assert.equal(tx.getIsConfirmed(), false); // test confirmed - if (tx.isConfirmed()) { + if (tx.getIsConfirmed()) { assert(tx.getBlock()); assert(tx.getBlock().getTxs().includes(tx)); assert(tx.getBlock().getHeight() > 0); assert(tx.getBlock().getTimestamp() > 0); - assert.equal(tx.isRelayed(), true); - assert.equal(tx.isFailed(), false); - assert.equal(tx.inTxPool(), false); + assert.equal(tx.getIsRelayed(), true); + assert.equal(tx.getIsFailed(), false); + assert.equal(tx.getInTxPool(), false); assert.equal(tx.getRelay(), true); - assert.equal(tx.isDoubleSpendSeen(), false); + assert.equal(tx.getIsDoubleSpendSeen(), false); if (ctx.fromBinaryBlock) assert.equal(tx.getNumConfirmations(), undefined); else assert(tx.getNumConfirmations() > 0); } else { @@ -1376,16 +1372,16 @@ function testTx(tx, ctx) { } // test in tx pool - if (tx.inTxPool()) { - assert.equal(tx.isConfirmed(), false); - assert.equal(tx.isDoubleSpendSeen(), false); + if (tx.getInTxPool()) { + assert.equal(tx.getIsConfirmed(), false); + assert.equal(tx.getIsDoubleSpendSeen(), false); assert.equal(tx.getLastFailedHeight(), undefined); assert.equal(tx.getLastFailedHash(), undefined); assert(tx.getReceivedTimestamp() > 0); if (ctx.fromGetTxPool) { assert(tx.getSize() > 0); assert(tx.getWeight() > 0); - assert.equal(typeof tx.isKeptByBlock(), "boolean"); + assert.equal(typeof tx.getIsKeptByBlock(), "boolean"); assert(tx.getMaxUsedBlockHeight() >= 0); assert(tx.getMaxUsedBlockHash()); } @@ -1396,9 +1392,8 @@ function testTx(tx, ctx) { } // test miner tx - if (tx.isMinerTx()) { - assert.equal(tx.getFee().compare(new BigInteger(0)), 0); - assert(tx.getIncomingTransfers().length > 0); // TODO: MoneroTx does not have getIncomingTransfers() but this doesn't fail? + if (tx.getIsMinerTx()) { + assert.equal(tx.getFee(), 0n); assert.equal(tx.getInputs(), undefined); assert.equal(tx.getSignatures(), undefined); } else { @@ -1406,17 +1401,16 @@ function testTx(tx, ctx) { } // test failed // TODO: what else to test associated with failed - if (tx.isFailed()) { - assert(tx.getOutgoingTransfer() instanceof MoneroTransfer); // TODO: MoneroTx does not have getOutgoingTransfer() but this doesn't fail? + if (tx.getIsFailed()) { assert(tx.getReceivedTimestamp() > 0) } else { - if (tx.isRelayed() === undefined) assert.equal(tx.getRelay(), undefined); // TODO monerod: add relayed to get_transactions - else if (tx.isRelayed()) assert.equal(tx.isDoubleSpendSeen(), false); + if (tx.getIsRelayed() === undefined) assert.equal(tx.getRelay(), undefined); // TODO monerod: add relayed to get_transactions + else if (tx.getIsRelayed()) assert.equal(tx.getIsDoubleSpendSeen(), false); else { - assert.equal(tx.isRelayed(), false); + assert.equal(tx.getIsRelayed(), false); if (ctx.fromGetTxPool) { assert.equal(tx.getRelay(), false); - assert.equal(typeof tx.isDoubleSpendSeen(), "boolean"); + assert.equal(typeof tx.getIsDoubleSpendSeen(), "boolean"); } } } @@ -1425,13 +1419,13 @@ function testTx(tx, ctx) { // received time only for tx pool or failed txs if (tx.getReceivedTimestamp() !== undefined) { - assert(tx.inTxPool() || tx.isFailed()); + assert(tx.getInTxPool() || tx.getIsFailed()); } // test inputs and outputs assert(tx.getInputs() && Array.isArray(tx.getInputs()) && tx.getInputs().length >= 0); assert(tx.getOutputs() && Array.isArray(tx.getOutputs()) && tx.getOutputs().length >= 0); - if (!tx.isMinerTx()) assert(tx.getInputs().length > 0); + if (!tx.getIsMinerTx()) assert(tx.getInputs().length > 0); for (let input of tx.getInputs()) { assert(tx === input.getTx()); testVin(input, ctx); @@ -1455,24 +1449,25 @@ function testTx(tx, ctx) { } else { assert.equal(tx.getPrunedHex(), undefined); assert(tx.getVersion() >= 0); - assert(tx.getUnlockTime().compare(new BigInteger(0)) >= 0); - assert(Array.isArray(tx.getExtra()) && tx.getExtra().length > 0); + assert(tx.getUnlockTime() >= 0n); + assert(tx.getExtra() instanceof Uint8Array); + assert(tx.getExtra().length > 0); if (ctx.fromBinaryBlock) assert.equal(tx.getFullHex(), undefined); // TODO: getBlocksByHeight() has inconsistent client-side pruning else assert(tx.getFullHex().length > 0); if (ctx.fromBinaryBlock) assert.equal(tx.getRctSigPrunable(), undefined); // TODO: getBlocksByHeight() has inconsistent client-side pruning //else assert.equal(typeof tx.getRctSigPrunable().nbp, "number"); - assert.equal(tx.isDoubleSpendSeen(), false); - if (tx.isConfirmed()) { + assert.equal(tx.getIsDoubleSpendSeen(), false); + if (tx.getIsConfirmed()) { assert.equal(tx.getLastRelayedTimestamp(), undefined); assert.equal(tx.getReceivedTimestamp(), undefined); } else { - if (tx.isRelayed()) assert(tx.getLastRelayedTimestamp() > 0); + if (tx.getIsRelayed()) assert(tx.getLastRelayedTimestamp() > 0); else assert.equal(tx.getLastRelayedTimestamp(), undefined); assert(tx.getReceivedTimestamp() > 0); } } - if (tx.isFailed()) { + if (tx.getIsFailed()) { // TODO: implement this } @@ -1485,7 +1480,7 @@ function testBlockTemplate(template) { assert(template.getBlockTemplateBlob()); assert(template.getBlockHashingBlob()); assert(template.getDifficulty()); - assert(template.getDifficulty() instanceof BigInteger); + assert.equal(typeof template.getDifficulty(), "bigint"); assert(template.getExpectedReward()); assert(template.getHeight()); assert(template.getPrevHash()); @@ -1497,14 +1492,14 @@ function testBlockTemplate(template) { // next seed hash can be null or initialized // TODO: test circumstances for each } -function testInfo(info) { +function testInfo(info: MoneroDaemonInfo) { assert(info.getVersion()); assert(info.getNumAltBlocks() >= 0); assert(info.getBlockSizeLimit()); assert(info.getBlockSizeMedian()); assert(info.getBootstrapDaemonAddress() === undefined || (typeof info.getBootstrapDaemonAddress() === "string" && info.getBootstrapDaemonAddress().length > 0)); assert(info.getCumulativeDifficulty()); - assert(info.getCumulativeDifficulty() instanceof BigInteger) + assert.equal(typeof info.getCumulativeDifficulty(), "bigint"); assert(info.getFreeSpace()); assert(info.getNumOfflinePeers() >= 0); assert(info.getNumOnlinePeers() >= 0); @@ -1512,7 +1507,7 @@ function testInfo(info) { assert(info.getHeightWithoutBootstrap()); assert(info.getNumIncomingConnections() >= 0); assert(info.getNetworkType()); - assert.equal("boolean", typeof info.isOffline()); + assert.equal(typeof info.getIsOffline(), "boolean"); assert(info.getNumOutgoingConnections() >= 0); assert(info.getNumRpcConnections() >= 0); assert(info.getStartTimestamp()); @@ -1521,16 +1516,16 @@ function testInfo(info) { assert(info.getTargetHeight() >= 0); assert(info.getNumTxs() >= 0); assert(info.getNumTxsPool() >= 0); - assert(typeof info.getWasBootstrapEverUsed() === "boolean"); + assert.equal(typeof info.getWasBootstrapEverUsed(), "boolean"); assert(info.getBlockWeightLimit()); assert(info.getBlockWeightMedian()); assert(info.getDatabaseSize() > 0); assert(typeof info.getUpdateAvailable() === "boolean"); - TestUtils.testUnsignedBigInteger(info.getCredits(), false); + TestUtils.testUnsignedBigInt(info.getCredits(), false); assert.equal(typeof info.getTopBlockHash(), "string"); assert(info.getTopBlockHash()); - assert.equal("boolean", typeof info.isBusySyncing()); - assert.equal("boolean", typeof info.isSynchronized()); + assert.equal("boolean", typeof info.getIsBusySyncing()); + assert.equal("boolean", typeof info.getIsSynchronized()); } function testSyncInfo(syncInfo) { // TODO: consistent naming, daemon in name? @@ -1550,7 +1545,7 @@ function testSyncInfo(syncInfo) { // TODO: consistent naming, daemon in name? } assert(syncInfo.getNextNeededPruningSeed() >= 0); assert.equal(syncInfo.getOverview(), undefined); - TestUtils.testUnsignedBigInteger(syncInfo.getCredits(), false); + TestUtils.testUnsignedBigInt(syncInfo.getCredits(), false); assert.equal(syncInfo.getTopBlockHash(), undefined); } @@ -1568,14 +1563,14 @@ function testConnectionSpan(span) { function testHardForkInfo(hardForkInfo) { assert.notEqual(hardForkInfo.getEarliestHeight(), undefined); - assert.notEqual(hardForkInfo.isEnabled(), undefined); + assert.notEqual(hardForkInfo.getIsEnabled(), undefined); assert.notEqual(hardForkInfo.getState(), undefined); assert.notEqual(hardForkInfo.getThreshold(), undefined); assert.notEqual(hardForkInfo.getVersion(), undefined); assert.notEqual(hardForkInfo.getNumVotes(), undefined); assert.notEqual(hardForkInfo.getVoting(), undefined); assert.notEqual(hardForkInfo.getWindow(), undefined); - TestUtils.testUnsignedBigInteger(hardForkInfo.getCredits(), false); + TestUtils.testUnsignedBigInt(hardForkInfo.getCredits(), false); assert.equal(hardForkInfo.getTopBlockHash(), undefined); } @@ -1586,73 +1581,73 @@ function testMoneroBan(ban) { } function testMinerTxSum(txSum) { - TestUtils.testUnsignedBigInteger(txSum.getEmissionSum(), true); - TestUtils.testUnsignedBigInteger(txSum.getFeeSum(), true); + TestUtils.testUnsignedBigInt(txSum.getEmissionSum(), true); + TestUtils.testUnsignedBigInt(txSum.getFeeSum(), true); } function testOutputHistogramEntry(entry) { - TestUtils.testUnsignedBigInteger(entry.getAmount()); + TestUtils.testUnsignedBigInt(entry.getAmount()); assert(entry.getNumInstances() >= 0); assert(entry.getNumUnlockedInstances() >= 0); assert(entry.getNumRecentInstances() >= 0); } function testOutputDistributionEntry(entry) { - TestUtils.testUnsignedBigInteger(entry.getAmount()); + TestUtils.testUnsignedBigInt(entry.getAmount()); assert(entry.getBase() >= 0); assert(Array.isArray(entry.getDistribution()) && entry.getDistribution().length > 0); assert(entry.getStartHeight() >= 0); } -function testSubmitTxResultGood(result) { +function testSubmitTxResultGood(result: MoneroSubmitTxResult) { testSubmitTxResultCommon(result); try { - assert.equal(result.isDoubleSpendSeen(), false, "tx submission is double spend."); - assert.equal(result.isFeeTooLow(), false); - assert.equal(result.isMixinTooLow(), false); - assert.equal(result.hasInvalidInput(), false); - assert.equal(result.hasInvalidOutput(), false); - assert.equal(result.hasTooFewOutputs(), false); - assert.equal(result.isOverspend(), false); - assert.equal(result.isTooBig(), false); + assert.equal(result.getIsDoubleSpendSeen(), false, "tx submission is double spend."); + assert.equal(result.getIsFeeTooLow(), false); + assert.equal(result.getIsMixinTooLow(), false); + assert.equal(result.getHasInvalidInput(), false); + assert.equal(result.getHasInvalidOutput(), false); + assert.equal(result.getHasTooFewOutputs(), false); + assert.equal(result.getIsOverspend(), false); + assert.equal(result.getIsTooBig(), false); assert.equal(result.getSanityCheckFailed(), false); - TestUtils.testUnsignedBigInteger(result.getCredits(), false); // 0 credits + TestUtils.testUnsignedBigInt(result.getCredits(), false); // 0 credits assert.equal(result.getTopBlockHash(), undefined); - assert.equal(result.isTxExtraTooBig(), false); - assert.equal(result.isGood(), true); + assert.equal(result.getIsTxExtraTooBig(), false); + assert.equal(result.getIsGood(), true); } catch (e) { - console.log("Submit result is not good: " + JSON.stringify(result)); + console.log("Submit result is not good: " + JSON.stringify(result.toJson())); throw e; } } -function testSubmitTxResultDoubleSpend(result) { +function testSubmitTxResultDoubleSpend(result: MoneroSubmitTxResult) { testSubmitTxResultCommon(result); - assert.equal(result.isGood(), false); - assert.equal(result.isDoubleSpendSeen(), true); - assert.equal(result.isFeeTooLow(), false); - assert.equal(result.isMixinTooLow(), false); - assert.equal(result.hasInvalidInput(), false); - assert.equal(result.hasInvalidOutput(), false); - assert.equal(result.isOverspend(), false); - assert.equal(result.isTooBig(), false); + assert.equal(result.getIsGood(), false); + assert.equal(result.getIsDoubleSpendSeen(), true); + assert.equal(result.getIsFeeTooLow(), false); + assert.equal(result.getIsMixinTooLow(), false); + assert.equal(result.getHasInvalidInput(), false); + assert.equal(result.getHasInvalidOutput(), false); + assert.equal(result.getIsOverspend(), false); + assert.equal(result.getIsTooBig(), false); } -function testSubmitTxResultCommon(result) { - assert.equal(typeof result.isGood(), "boolean"); - assert.equal(typeof result.isRelayed(), "boolean"); - assert.equal(typeof result.isDoubleSpendSeen(), "boolean"); - assert.equal(typeof result.isFeeTooLow(), "boolean"); - assert.equal(typeof result.isMixinTooLow(), "boolean"); - assert.equal(typeof result.hasInvalidInput(), "boolean"); - assert.equal(typeof result.hasInvalidOutput(), "boolean"); - assert.equal(typeof result.isOverspend(), "boolean"); - assert.equal(typeof result.isTooBig(), "boolean"); +function testSubmitTxResultCommon(result: MoneroSubmitTxResult) { + assert.equal(typeof result.getIsGood(), "boolean"); + assert.equal(typeof result.getIsRelayed(), "boolean"); + assert.equal(typeof result.getIsDoubleSpendSeen(), "boolean"); + assert.equal(typeof result.getIsFeeTooLow(), "boolean"); + assert.equal(typeof result.getIsMixinTooLow(), "boolean"); + assert.equal(typeof result.getHasInvalidInput(), "boolean"); + assert.equal(typeof result.getHasInvalidOutput(), "boolean"); + assert.equal(typeof result.getIsOverspend(), "boolean"); + assert.equal(typeof result.getIsTooBig(), "boolean"); assert.equal(typeof result.getSanityCheckFailed(), "boolean"); assert(result.getReason() === undefined || result.getReason().length > 0); } -function testTxPoolStats(stats) { +function testTxPoolStats(stats: MoneroTxPoolStats) { assert(stats); assert(stats.getNumTxs() >= 0); if (stats.getNumTxs() > 0) { @@ -1716,18 +1711,18 @@ function testKeyImage(image, ctx) { } } -function testOutput(output, ctx) { +function testOutput(output, ctx?) { assert(output instanceof MoneroOutput); - TestUtils.testUnsignedBigInteger(output.getAmount()); + TestUtils.testUnsignedBigInt(output.getAmount()); if (ctx) { - if (output.getTx().inTxPool() || ctx.fromGetTxPool || ctx.hasOutputIndices === false) assert.equal(output.getIndex(), undefined); // TODO: get_blocks_by_height.bin (#5127), get_transaction_pool, and tx pool txs do not return output indices + if (output.getTx().getInTxPool() || ctx.fromGetTxPool || ctx.hasOutputIndices === false) assert.equal(output.getIndex(), undefined); // TODO: get_blocks_by_height.bin (#5127), get_transaction_pool, and tx pool txs do not return output indices else assert(output.getIndex() >= 0); assert(output.getStealthPublicKey() && output.getStealthPublicKey().length === 64); } } async function getConfirmedTxs(daemon, numTxs) { - let txs = []; + let txs: MoneroTx[] = []; let numBlocksPerReq = 50; for (let startIdx = await daemon.getHeight() - numBlocksPerReq - 1; startIdx >= 0; startIdx -= numBlocksPerReq) { let blocks = await daemon.getBlocksByRange(startIdx, startIdx + numBlocksPerReq); @@ -1745,7 +1740,7 @@ async function getConfirmedTxs(daemon, numTxs) { function testAltChain(altChain) { assert(altChain instanceof MoneroAltChain); assert(Array.isArray(altChain.getBlockHashes()) && altChain.getBlockHashes().length > 0); - TestUtils.testUnsignedBigInteger(altChain.getDifficulty(), true); + TestUtils.testUnsignedBigInt(altChain.getDifficulty(), true); assert(altChain.getHeight() > 0); assert(altChain.getLength() > 0); assert(altChain.getMainChainParentBlockHash().length === 64); @@ -1761,29 +1756,28 @@ function testPeer(peer) { assert(peer.getCurrentUpload() >= 0); assert(peer.getHeight() >= 0); assert(peer.getLiveTime() >= 0); - assert.equal(typeof peer.isLocalIp(), "boolean"); - assert.equal(typeof peer.isLocalHost(), "boolean"); + assert.equal(typeof peer.getIsLocalIp(), "boolean"); + assert.equal(typeof peer.getIsLocalHost(), "boolean"); assert(peer.getNumReceives() >= 0); assert(peer.getReceiveIdleTime() >= 0); assert(peer.getNumSends() >= 0); assert(peer.getSendIdleTime() >= 0); assert(peer.getState()); assert(peer.getNumSupportFlags() >= 0); - ConnectionType.validate(peer.getType()); } -function testKnownPeer(peer, fromConnection) { +function testKnownPeer(peer, fromConnection?) { assert(peer instanceof MoneroPeer); assert.equal(typeof peer.getId(), "string"); assert.equal(typeof peer.getHost(), "string"); assert(typeof peer.getPort() === "number"); assert(peer.getPort() > 0); assert(peer.getRpcPort() === undefined || (typeof peer.getRpcPort() === "number" && peer.getRpcPort() >= 0)); - assert.equal(typeof peer.isOnline(), "boolean"); - if (peer.getRpcCreditsPerHash() !== undefined) TestUtils.testUnsignedBigInteger(peer.getRpcCreditsPerHash()); + assert.equal(typeof peer.getIsOnline(), "boolean"); + if (peer.getRpcCreditsPerHash() !== undefined) TestUtils.testUnsignedBigInt(peer.getRpcCreditsPerHash()); if (fromConnection) assert.equal(undefined, peer.getLastSeenTimestamp()); else { - if (peer.getLastSeenTimestamp() < 0) console("Last seen timestamp is invalid: " + peer.getLastSeenTimestamp()); + if (peer.getLastSeenTimestamp() < 0) console.log("Last seen timestamp is invalid: " + peer.getLastSeenTimestamp()); assert(peer.getLastSeenTimestamp() >= 0); } assert(peer.getPruningSeed() === undefined || peer.getPruningSeed() >= 0); @@ -1791,8 +1785,8 @@ function testKnownPeer(peer, fromConnection) { function testUpdateCheckResult(result) { assert(result instanceof MoneroDaemonUpdateCheckResult); - assert.equal(typeof result.isUpdateAvailable(), "boolean"); - if (result.isUpdateAvailable()) { + assert.equal(typeof result.getIsUpdateAvailable(), "boolean"); + if (result.getIsUpdateAvailable()) { assert(result.getAutoUri(), "No auto uri; is daemon online?"); assert(result.getUserUri()); assert.equal(typeof result.getVersion(), "string"); @@ -1806,7 +1800,7 @@ function testUpdateCheckResult(result) { } } -function testUpdateDownloadResult(result, path) { +function testUpdateDownloadResult(result, path?) { testUpdateCheckResult(result); if (result.isUpdateAvailable()) { if (path) assert.equal(result.getDownloadPath(), path); @@ -1816,9 +1810,9 @@ function testUpdateDownloadResult(result, path) { } } -async function getConfirmedTxHashes(daemon) { +async function getConfirmedTxHashes(daemon): Promise { let numTxs = 5; - let txHashes = []; + let txHashes: string[] = []; let height = await daemon.getHeight(); while (txHashes.length < numTxs && height > 0) { let block = await daemon.getBlockByHeight(--height); @@ -1827,7 +1821,7 @@ async function getConfirmedTxHashes(daemon) { return txHashes; } -function testTxCopy(tx, ctx) { +function testTxCopy(tx: MoneroTx, ctx) { // copy tx and test let copy = tx.copy(); @@ -1841,7 +1835,7 @@ function testTxCopy(tx, ctx) { else { assert(copy.getInputs() !== tx.getInputs()); for (let i = 0; i < copy.getInputs().length; i++) { - assert.equal(0, tx.getInputs()[i].getAmount().compare(copy.getInputs()[i].getAmount())); + assert.equal(tx.getInputs()[i].getAmount(), copy.getInputs()[i].getAmount()); } } @@ -1850,7 +1844,7 @@ function testTxCopy(tx, ctx) { else { assert(copy.getOutputs() !== tx.getOutputs()); for (let i = 0; i < copy.getOutputs().length; i++) { - assert.equal(0, tx.getOutputs()[i].getAmount().compare(copy.getOutputs()[i].getAmount())); + assert.equal(tx.getOutputs()[i].getAmount(), copy.getOutputs()[i].getAmount()); } } @@ -1864,5 +1858,3 @@ function testTxCopy(tx, ctx) { let merged = copy.merge(copy.copy()); assert.equal(merged.toString(), tx.toString()); } - -module.exports = TestMoneroDaemonRpc; \ No newline at end of file diff --git a/src/test/TestMoneroUtils.js b/src/test/TestMoneroUtils.ts similarity index 89% rename from src/test/TestMoneroUtils.js rename to src/test/TestMoneroUtils.ts index 5de8b56c7..729c05940 100644 --- a/src/test/TestMoneroUtils.js +++ b/src/test/TestMoneroUtils.ts @@ -1,15 +1,13 @@ -const assert = require("assert"); -const monerojs = require("../../index"); -const BigInteger = monerojs.BigInteger; -const MoneroError = monerojs.MoneroError; -const MoneroUtils = monerojs.MoneroUtils; -const MoneroNetworkType = monerojs.MoneroNetworkType; -const LibraryUtils = monerojs.LibraryUtils; +import assert from "assert"; +import {MoneroError, + MoneroUtils, + MoneroNetworkType, + LibraryUtils} from "../../index"; /** * Test utilities including those implemented in WebAssembly. */ -class TestMoneroUtils { +export default class TestMoneroUtils { runTests() { describe("TEST MONERO UTILITIES", function() { @@ -199,28 +197,28 @@ class TestMoneroUtils { try { await MoneroUtils.validateAddress(address, networkType); throw new Error("Should have thrown exception"); - } catch (err) { + } catch (err: any) { assert.notEqual("Should have thrown exception", err.message); assert(err.message); } } - async function testInvalidPrivateViewKey(privateViewKey) { + async function testInvalidPrivateViewKey(privateViewKey?) { assert(!await MoneroUtils.isValidPrivateViewKey(privateViewKey)); try { await MoneroUtils.validatePrivateViewKey(privateViewKey); - fail("Should have thrown exception"); - } catch (e) { + throw new Error("Should have thrown exception"); + } catch (e: any) { assert(e.message.length > 0); } } - async function testInvalidPublicViewKey(publicViewKey) { + async function testInvalidPublicViewKey(publicViewKey?) { assert(!await MoneroUtils.isValidPublicViewKey(publicViewKey)); try { await MoneroUtils.validatePublicViewKey(publicViewKey); - fail("Should have thrown exception"); - } catch (e) { + throw new Error("Should have thrown exception"); + } catch (e: any) { assert(e.message.length > 0); } } @@ -229,42 +227,40 @@ class TestMoneroUtils { assert(!await MoneroUtils.isValidPrivateSpendKey(privateSpendKey)); try { await MoneroUtils.validatePrivateSpendKey(privateSpendKey); - fail("Should have thrown exception"); - } catch (e) { + throw new Error("Should have thrown exception"); + } catch (e: any) { assert(e.message.length > 0); } } - async function testInvalidPublicSpendKey(publicSpendKey) { + async function testInvalidPublicSpendKey(publicSpendKey?) { assert(!await MoneroUtils.isValidPublicSpendKey(publicSpendKey)); try { await MoneroUtils.validatePublicSpendKey(publicSpendKey); - fail("Should have thrown exception"); - } catch (e) { + throw new Error("Should have thrown exception"); + } catch (e: any) { assert(e.message.length > 0); } } it("Can convert between XMR and atomic units", function() { - assert.equal(MoneroUtils.xmrToAtomicUnits(1).toString(), new BigInteger("1000000000000").toString()); - assert.equal(MoneroUtils.atomicUnitsToXmr(new BigInteger("1000000000000")), 1); - assert.equal(MoneroUtils.xmrToAtomicUnits(0.001).toString(), new BigInteger("1000000000").toString()); - assert.equal(MoneroUtils.atomicUnitsToXmr(new BigInteger("1000000000")), .001); - assert.equal(MoneroUtils.xmrToAtomicUnits(.25).toString(), new BigInteger("250000000000").toString()); - assert.equal(MoneroUtils.atomicUnitsToXmr(new BigInteger("250000000000")), .25); - assert.equal(MoneroUtils.xmrToAtomicUnits(1.25).toString(), new BigInteger("1250000000000").toString()); - assert.equal(MoneroUtils.atomicUnitsToXmr(new BigInteger("1250000000000")), 1.25); - assert.equal(MoneroUtils.xmrToAtomicUnits("1").toString(), new BigInteger("1000000000000").toString()); - assert.equal(MoneroUtils.atomicUnitsToXmr(new BigInteger("1000000000000")), 1); - assert.equal(MoneroUtils.xmrToAtomicUnits("0.001").toString(), new BigInteger("1000000000").toString()); - assert.equal(MoneroUtils.atomicUnitsToXmr(new BigInteger("1000000000")), .001); - assert.equal(MoneroUtils.xmrToAtomicUnits(".25").toString(), new BigInteger("250000000000").toString()); - assert.equal(MoneroUtils.atomicUnitsToXmr(new BigInteger("250000000000")), .25); - assert.equal(MoneroUtils.xmrToAtomicUnits("1.25").toString(), new BigInteger("1250000000000").toString()); - assert.equal(MoneroUtils.atomicUnitsToXmr(new BigInteger("1250000000000")), 1.25); + assert.equal(MoneroUtils.xmrToAtomicUnits(1).toString(), BigInt("1000000000000").toString()); + assert.equal(MoneroUtils.atomicUnitsToXmr(BigInt("1000000000000")), 1); + assert.equal(MoneroUtils.xmrToAtomicUnits(0.001).toString(), BigInt("1000000000").toString()); + assert.equal(MoneroUtils.atomicUnitsToXmr(BigInt("1000000000")), .001); + assert.equal(MoneroUtils.xmrToAtomicUnits(.25).toString(), BigInt("250000000000").toString()); + assert.equal(MoneroUtils.atomicUnitsToXmr(BigInt("250000000000")), .25); + assert.equal(MoneroUtils.xmrToAtomicUnits(1.25).toString(), BigInt("1250000000000").toString()); + assert.equal(MoneroUtils.atomicUnitsToXmr(BigInt("1250000000000")), 1.25); + assert.equal(MoneroUtils.xmrToAtomicUnits("1").toString(), BigInt("1000000000000").toString()); + assert.equal(MoneroUtils.atomicUnitsToXmr(BigInt("1000000000000")), 1); + assert.equal(MoneroUtils.xmrToAtomicUnits("0.001").toString(), BigInt("1000000000").toString()); + assert.equal(MoneroUtils.atomicUnitsToXmr(BigInt("1000000000")), .001); + assert.equal(MoneroUtils.xmrToAtomicUnits(".25").toString(), BigInt("250000000000").toString()); + assert.equal(MoneroUtils.atomicUnitsToXmr(BigInt("250000000000")), .25); + assert.equal(MoneroUtils.xmrToAtomicUnits("1.25").toString(), BigInt("1250000000000").toString()); + assert.equal(MoneroUtils.atomicUnitsToXmr(BigInt("1250000000000")), 1.25); }); }) } } - -module.exports = TestMoneroUtils; \ No newline at end of file diff --git a/src/test/TestMoneroWalletCommon.js b/src/test/TestMoneroWalletCommon.ts similarity index 81% rename from src/test/TestMoneroWalletCommon.js rename to src/test/TestMoneroWalletCommon.ts index f227d8f01..a316804c7 100644 --- a/src/test/TestMoneroWalletCommon.js +++ b/src/test/TestMoneroWalletCommon.ts @@ -1,47 +1,57 @@ -const assert = require("assert"); -const StartMining = require("./utils/StartMining"); -const TestUtils = require("./utils/TestUtils"); -const monerojs = require("../../index"); -const Filter = monerojs.Filter; // TODO: don't export filter -const LibraryUtils = monerojs.LibraryUtils; -const MoneroConnectionManager = monerojs.MoneroConnectionManager; -const MoneroError = monerojs.MoneroError; -const MoneroTxPriority = monerojs.MoneroTxPriority; -const MoneroWalletRpc = monerojs.MoneroWalletRpc; -const MoneroWalletKeys = monerojs.MoneroWalletKeys; -const MoneroWallet = monerojs.MoneroWallet; -const MoneroWalletListener = monerojs.MoneroWalletListener; -const MoneroWalletConfig = monerojs.MoneroWalletConfig; -const MoneroUtils = monerojs.MoneroUtils; -const GenUtils = monerojs.GenUtils; -const MoneroSyncResult = monerojs.MoneroSyncResult; -const BigInteger = monerojs.BigInteger; -const MoneroRpcConnection = monerojs.MoneroRpcConnection; -const MoneroTxQuery = monerojs.MoneroTxQuery; -const MoneroTransfer = monerojs.MoneroTransfer; -const MoneroIncomingTransfer = monerojs.MoneroIncomingTransfer; -const MoneroTransferQuery = monerojs.MoneroTransferQuery; -const MoneroOutputQuery = monerojs.MoneroOutputQuery; -const MoneroOutputWallet = monerojs.MoneroOutputWallet; -const MoneroTxConfig = monerojs.MoneroTxConfig; -const MoneroTxWallet = monerojs.MoneroTxWallet; -const MoneroDestination = monerojs.MoneroDestination; -const MoneroSubaddress = monerojs.MoneroSubaddress; -const MoneroKeyImage = monerojs.MoneroKeyImage; -const MoneroTx = monerojs.MoneroTx; -const MoneroMessageSignatureType = monerojs.MoneroMessageSignatureType; -const MoneroMessageSignatureResult = monerojs.MoneroMessageSignatureResult; +import assert from "assert"; +import StartMining from "./utils/StartMining"; +import TestUtils from "./utils/TestUtils"; +import {Filter, + GenUtils, + LibraryUtils, + MoneroAccount, + MoneroBlock, + MoneroDaemonRpc, + MoneroError, + MoneroTxPriority, + MoneroWalletRpc, + MoneroWalletKeys, + MoneroWallet, + MoneroWalletListener, + MoneroWalletConfig, + MoneroUtils, + MoneroMultisigInfo, + MoneroSyncResult, + MoneroRpcConnection, + MoneroConnectionManager, + MoneroCheckTx, + MoneroTxQuery, + MoneroTransfer, + MoneroIncomingTransfer, + MoneroOutgoingTransfer, + MoneroTransferQuery, + MoneroOutputQuery, + MoneroOutputWallet, + MoneroTxConfig, + MoneroTxWallet, + MoneroDestination, + MoneroSubaddress, + MoneroKeyImage, + MoneroTx, + MoneroMessageSignatureType, + MoneroMessageSignatureResult, + MoneroCheckReserve} from "../../index.js"; // test constants -const SEND_DIVISOR = 10; -const SEND_MAX_DIFF = 60; +const SEND_DIVISOR = BigInt(10); +const SEND_MAX_DIFF = BigInt(60); const MAX_TX_PROOFS = 25; // maximum number of transactions to check for each proof, undefined to check all const NUM_BLOCKS_LOCKED = 10; /** * Test a wallet for common functionality. */ -class TestMoneroWalletCommon { +export default class TestMoneroWalletCommon { + + // instance variables + testConfig: any; + wallet: MoneroWallet; + daemon: MoneroDaemonRpc; /** * Construct the tester. @@ -80,7 +90,7 @@ class TestMoneroWalletCommon { // try to stop mining try { await this.daemon.stopMining(); } - catch (err) { } + catch (err: any) { } // close wallet await this.wallet.close(true); @@ -107,9 +117,9 @@ class TestMoneroWalletCommon { /** * Get the main wallet to test. * - * @return the wallet to test + * @return {Promise} the wallet to test */ - async getTestWallet() { + async getTestWallet(): Promise { throw new Error("Subclass must implement"); } @@ -119,17 +129,17 @@ class TestMoneroWalletCommon { * @param config - configures the wallet to open * @return MoneroWallet is the opened wallet */ - async openWallet(config) { + async openWallet(config): Promise { throw new Error("Subclass must implement"); } /** * Create a test wallet with default configuration for each wallet type. * - * @param config - configures the wallet to create - * @return MoneroWallet is the created wallet + * @param [config] - configures the wallet to create + * @return {Promise} is the created wallet */ - async createWallet(config) { + async createWallet(config?: Partial): Promise { throw new Error("Subclass must implement"); } @@ -137,9 +147,10 @@ class TestMoneroWalletCommon { * Close a test wallet with customization for each wallet type. * * @param {MoneroWallet} wallet - the wallet to close - * @param {bool} save - whether or not to save the wallet + * @param {boolean} [save] - whether or not to save the wallet + * @return {Promise} */ - async closeWallet(wallet, save) { + async closeWallet(wallet, save?) { throw new Error("Subclass must implement"); } @@ -147,17 +158,17 @@ class TestMoneroWalletCommon { * Get the wallet's supported languages for the seed phrase. This is an * instance method for wallet rpc and a static utility for other wallets. * - * @return {string[]} the wallet's supported languages + * @return {Promise} the wallet's supported languages */ - async getSeedLanguages() { + async getSeedLanguages(): Promise { throw new Error("Subclass must implement"); } // ------------------------------ BEGIN TESTS ------------------------------- - runCommonTests() { + runCommonTests(testConfig?) { let that = this; - let testConfig = this.testConfig; + testConfig = Object.assign({}, this.testConfig, testConfig) describe("Common Wallet Tests" + (testConfig.liteMode ? " (lite mode)" : ""), function() { // start tests by sending to multiple addresses @@ -175,7 +186,7 @@ class TestMoneroWalletCommon { let e1 = undefined; try { let wallet = await that.createWallet(); - let path; try { path = await wallet.getPath(); } catch(e) { } // TODO: factor out keys-only tests? + let path; try { path = await wallet.getPath(); } catch (e: any) { } // TODO: factor out keys-only tests? let e2 = undefined; try { await MoneroUtils.validateAddress(await wallet.getPrimaryAddress(), TestUtils.NETWORK_TYPE); @@ -183,7 +194,7 @@ class TestMoneroWalletCommon { await MoneroUtils.validatePrivateSpendKey(await wallet.getPrivateSpendKey()); await MoneroUtils.validateMnemonic(await wallet.getSeed()); if (!(wallet instanceof MoneroWalletRpc)) assert.equal(await wallet.getSeedLanguage(), MoneroWallet.DEFAULT_LANGUAGE); // TODO monero-wallet-rpc: get mnemonic language - } catch (e) { + } catch (e: any) { e2 = e; } await that.closeWallet(wallet); @@ -194,7 +205,7 @@ class TestMoneroWalletCommon { try { await that.createWallet({path: path}); throw new Error("Should have thrown error"); - } catch(e) { + } catch (e: any) { assert.equal(e.message, "Wallet already exists: " + path); } } @@ -203,10 +214,10 @@ class TestMoneroWalletCommon { try { await that.createWallet({language: "english"}); // TODO: support lowercase? throw new Error("Should have thrown error"); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Unknown language: english"); } - } catch (e) { + } catch (e: any) { e1 = e; } @@ -225,14 +236,14 @@ class TestMoneroWalletCommon { // recreate test wallet from seed let wallet = await that.createWallet({seed: TestUtils.SEED, restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT}); - let path; try { path = await wallet.getPath(); } catch(e) { } // TODO: factor out keys-only tests? + let path; try { path = await wallet.getPath(); } catch (e: any) { } // TODO: factor out keys-only tests? let e2 = undefined; try { assert.equal(await wallet.getPrimaryAddress(), primaryAddress); assert.equal(await wallet.getPrivateViewKey(), privateViewKey); assert.equal(await wallet.getPrivateSpendKey(), privateSpendKey); if (!(wallet instanceof MoneroWalletRpc)) assert.equal(await wallet.getSeedLanguage(), MoneroWallet.DEFAULT_LANGUAGE); - } catch (e) { + } catch (e: any) { e2 = e; } await that.closeWallet(wallet); @@ -242,7 +253,7 @@ class TestMoneroWalletCommon { try { let invalidMnemonic = "memoir desk algebra inbound innocent unplugs fully okay five inflamed giant factual ritual toyed topic snake unhappy guarded tweezers haunted inundate giant"; await that.createWallet(new MoneroWalletConfig().setSeed(invalidMnemonic).setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT)); - } catch(err) { + } catch (err: any) { assert.equal("Invalid mnemonic", err.message); } @@ -251,11 +262,11 @@ class TestMoneroWalletCommon { try { await that.createWallet({path: path}); throw new Error("Should have thrown error"); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Wallet already exists: " + path); } } - } catch (e) { + } catch (e: any) { e1 = e; } @@ -276,12 +287,12 @@ class TestMoneroWalletCommon { await MoneroUtils.validateAddress(await wallet.getPrimaryAddress(), TestUtils.NETWORK_TYPE); assert.notEqual(await wallet.getPrimaryAddress(), TestUtils.ADDRESS); if (!(wallet instanceof MoneroWalletRpc)) assert.equal(await wallet.getSeedLanguage(), MoneroWallet.DEFAULT_LANGUAGE); // TODO monero-wallet-rpc: support - } catch (e) { + } catch (e: any) { e2 = e; } await that.closeWallet(wallet); if (e2 !== undefined) throw e2; - } catch (e) { + } catch (e: any) { e1 = e; } @@ -300,7 +311,7 @@ class TestMoneroWalletCommon { // recreate test wallet from keys let wallet = await that.createWallet({primaryAddress: primaryAddress, privateViewKey: privateViewKey, privateSpendKey: privateSpendKey, restoreHeight: await that.daemon.getHeight()}); - let path; try { path = await wallet.getPath(); } catch(e) { } // TODO: factor out keys-only tests? + let path; try { path = await wallet.getPath(); } catch (e: any) { } // TODO: factor out keys-only tests? let e2 = undefined; try { assert.equal(await wallet.getPrimaryAddress(), primaryAddress); @@ -311,7 +322,7 @@ class TestMoneroWalletCommon { assert.equal(await wallet.getSeed(), TestUtils.SEED); // TODO monero-wallet-rpc: cannot get mnemonic from wallet created from keys? assert.equal(await wallet.getSeedLanguage(), MoneroWallet.DEFAULT_LANGUAGE); } - } catch (e) { + } catch (e: any) { e2 = e; } await that.closeWallet(wallet); @@ -320,7 +331,7 @@ class TestMoneroWalletCommon { // recreate test wallet from spend key if (!(wallet instanceof MoneroWalletRpc)) { // TODO monero-wallet-rpc: cannot create wallet from spend key? wallet = await that.createWallet({privateSpendKey: privateSpendKey, restoreHeight: await that.daemon.getHeight()}); - try { path = await wallet.getPath(); } catch(e) { } // TODO: factor out keys-only tests? + try { path = await wallet.getPath(); } catch (e: any) { } // TODO: factor out keys-only tests? e2 = undefined; try { assert.equal(await wallet.getPrimaryAddress(), primaryAddress); @@ -331,7 +342,7 @@ class TestMoneroWalletCommon { assert.equal(await wallet.getSeed(), TestUtils.SEED); // TODO monero-wallet-rpc: cannot get seed from wallet created from keys? assert.equal(await wallet.getSeedLanguage(), MoneroWallet.DEFAULT_LANGUAGE); } - } catch (e) { + } catch (e: any) { e2 = e; } await that.closeWallet(wallet); @@ -343,11 +354,11 @@ class TestMoneroWalletCommon { try { await that.createWallet({path: path}); throw new Error("Should have thrown error"); - } catch(e) { + } catch (e: any) { assert.equal(e.message, "Wallet already exists: " + path); } } - } catch (e) { + } catch (e: any) { e1 = e; } @@ -357,26 +368,26 @@ class TestMoneroWalletCommon { if (testConfig.testRelays) it("Can create wallets with subaddress lookahead", async function() { let err; - let receiver; + let receiver: MoneroWallet | undefined = undefined; try { - // create wallet with high subaddress lookahead - receiver = await that.createWallet({ + // create wallet with high subaddress lookahead + receiver = await that.createWallet({ accountLookahead: 1, subaddressLookahead: 100000 - }); + }); - // transfer funds to subaddress with high index - await that.wallet.createTx(new MoneroTxConfig() - .setAccountIndex(0) - .addDestination((await receiver.getSubaddress(0, 85000)).getAddress(), TestUtils.MAX_FEE) - .setRelay(true)); + // transfer funds to subaddress with high index + await that.wallet.createTx(new MoneroTxConfig() + .setAccountIndex(0) + .addDestination((await receiver.getSubaddress(0, 85000)).getAddress(), TestUtils.MAX_FEE) + .setRelay(true)); - // observe unconfirmed funds - await GenUtils.waitFor(1000); - await receiver.sync(); - assert((await receiver.getBalance()).compare(new BigInteger("0")) > 0); - } catch (e) { + // observe unconfirmed funds + await GenUtils.waitFor(1000); + await receiver.sync(); + assert(await receiver.getBalance() > 0n); + } catch (e: any) { err = e; } @@ -390,7 +401,7 @@ class TestMoneroWalletCommon { let version = await that.wallet.getVersion(); assert.equal(typeof version.getNumber(), "number"); assert(version.getNumber() > 0); - assert.equal(typeof version.isRelease(), "boolean"); + assert.equal(typeof version.getIsRelease(), "boolean"); }); if (testConfig.testNonRelays) @@ -468,10 +479,10 @@ class TestMoneroWalletCommon { try { await wallet.sync(); throw new Error("Exception expected"); - } catch (e1) { + } catch (e1: any) { assert.equal(e1.message, "Wallet is not connected to daemon"); } - } catch (e) { + } catch (e: any) { err = e; } @@ -483,7 +494,7 @@ class TestMoneroWalletCommon { if (testConfig.testNonRelays) it("Can use a connection manager", async function() { let err; - let wallet; + let wallet: MoneroWallet | undefined = undefined; try { // create connection manager with monerod connections @@ -494,7 +505,7 @@ class TestMoneroWalletCommon { await connectionManager.addConnection(connection2); // create wallet with connection manager - wallet = await that.createWallet(new MoneroWalletConfig().setServerUri("").setConnectionManager(connectionManager)); + wallet = await that.createWallet(new MoneroWalletConfig().setServer("").setConnectionManager(connectionManager)); assert.equal((await wallet.getDaemonConnection()).getUri(), (await that.daemon.getRpcConnection()).getUri()); assert(await wallet.isConnectedToDaemon()); @@ -504,7 +515,7 @@ class TestMoneroWalletCommon { assert.equal((await wallet.getDaemonConnection()).getUri(), connection2.getUri()); // disconnect - await connectionManager.setConnection(); + await connectionManager.setConnection(undefined); assert.equal(await wallet.getDaemonConnection(), undefined); assert(!await wallet.isConnectedToDaemon()); @@ -516,6 +527,15 @@ class TestMoneroWalletCommon { assert.equal((await wallet.getDaemonConnection()).getUri(), connection1.getUri()); assert(await wallet.isConnectedToDaemon()); + // test override with bad connection + wallet.addListener(new MoneroWalletListener()); + connectionManager.setAutoSwitch(false); + await connectionManager.setConnection("http://foo.bar.xyz"); + assert.equal((await wallet.getDaemonConnection()).getUri(), "http://foo.bar.xyz"); + assert.equal(false, await wallet.isConnectedToDaemon()); + await GenUtils.waitFor(5000); + assert.equal(false, await wallet.isConnectedToDaemon()); + // set to another connection manager let connectionManager2 = new MoneroConnectionManager(); await connectionManager2.setConnection(connection2); @@ -529,7 +549,7 @@ class TestMoneroWalletCommon { // stop polling and close connectionManager.stopPolling(); - } catch (e) { + } catch (e: any) { err = e; } @@ -602,7 +622,7 @@ class TestMoneroWalletCommon { if (testConfig.testNonRelays) it("Can get addresses out of range of used accounts and subaddresses", async function() { - await that._testGetSubaddressAddressOutOfRange(); + await that.testGetSubaddressAddressOutOfRange(); }); if (testConfig.testNonRelays) @@ -626,7 +646,7 @@ class TestMoneroWalletCommon { try { subaddress = await that.wallet.getAddressIndex(nonWalletAddress); throw new Error("fail"); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Address doesn't belong to the wallet"); } @@ -634,7 +654,7 @@ class TestMoneroWalletCommon { try { subaddress = await that.wallet.getAddressIndex("this is definitely not an address"); throw new Error("fail"); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Invalid address"); } }); @@ -668,7 +688,7 @@ class TestMoneroWalletCommon { try { integratedAddress = await that.wallet.getIntegratedAddress(subaddress); throw new Error("Getting integrated address from subaddress should have failed"); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Subaddress shouldn't be used"); } @@ -677,7 +697,7 @@ class TestMoneroWalletCommon { try { integratedAddress = await that.wallet.getIntegratedAddress(undefined, invalidPaymentId); throw new Error("Getting integrated address with invalid payment id " + invalidPaymentId + " should have thrown a RPC exception"); - } catch (e) { + } catch (e: any) { //assert.equal(e.getCode(), -5); // TODO: error codes part of rpc only? assert.equal(e.message, "Invalid payment ID: " + invalidPaymentId); } @@ -693,7 +713,7 @@ class TestMoneroWalletCommon { try { console.log(await that.wallet.decodeIntegratedAddress("bad address")); throw new Error("Should have failed decoding bad address"); - } catch (err) { + } catch (err: any) { assert.equal(err.message, "Invalid address"); } }); @@ -722,29 +742,29 @@ class TestMoneroWalletCommon { // collect dates to test starting 100 days ago const DAY_MS = 24 * 60 * 60 * 1000; let yesterday = new Date(new Date().getTime() - DAY_MS); // TODO monero-project: today's date can throw exception as "in future" so we test up to yesterday - let dates = []; + let dates: any = []; for (let i = 99; i >= 0; i--) { dates.push(new Date(yesterday.getTime() - DAY_MS * i)); // subtract i days } // test heights by date - let lastHeight = undefined; + let lastHeight: number | undefined = undefined; for (let date of dates) { let height = await that.wallet.getHeightByDate(date.getYear() + 1900, date.getMonth() + 1, date.getDate()); assert(height >= 0); if (lastHeight != undefined) assert(height >= lastHeight); lastHeight = height; } - assert(lastHeight >= 0); + assert(lastHeight! >= 0); let height = await that.wallet.getHeight(); assert(height >= 0); // test future date try { let tomorrow = new Date(yesterday.getTime() + DAY_MS * 2); - await that.wallet.getHeightByDate(tomorrow.getYear() + 1900, tomorrow.getMonth() + 1, tomorrow.getDate()); + await that.wallet.getHeightByDate(tomorrow.getFullYear(), tomorrow.getMonth() + 1, tomorrow.getDate()); throw new Error("Expected exception on future date"); - } catch (err) { + } catch (err: any) { assert.equal(err.message, "specified date is in the future"); } }); @@ -756,18 +776,18 @@ class TestMoneroWalletCommon { let accounts = await that.wallet.getAccounts(true); // test that balances add up between accounts and wallet - let accountsBalance = new BigInteger(0); - let accountsUnlockedBalance = new BigInteger(0); + let accountsBalance = BigInt(0); + let accountsUnlockedBalance = BigInt(0); for (let account of accounts) { - accountsBalance = accountsBalance.add(account.getBalance()); - accountsUnlockedBalance = accountsUnlockedBalance.add(account.getUnlockedBalance()); + accountsBalance = accountsBalance + (account.getBalance()); + accountsUnlockedBalance = accountsUnlockedBalance + (account.getUnlockedBalance()); // test that balances add up between subaddresses and accounts - let subaddressesBalance = new BigInteger(0); - let subaddressesUnlockedBalance = new BigInteger(0); + let subaddressesBalance = BigInt(0); + let subaddressesUnlockedBalance = BigInt(0); for (let subaddress of account.getSubaddresses()) { - subaddressesBalance = subaddressesBalance.add(subaddress.getBalance()); - subaddressesUnlockedBalance = subaddressesUnlockedBalance.add(subaddress.getUnlockedBalance()); + subaddressesBalance = subaddressesBalance + (subaddress.getBalance()); + subaddressesUnlockedBalance = subaddressesUnlockedBalance + (subaddress.getUnlockedBalance()); // test that balances are consistent with getAccounts() call assert.equal((await that.wallet.getBalance(subaddress.getAccountIndex(), subaddress.getIndex())).toString(), subaddress.getBalance().toString()); @@ -776,8 +796,8 @@ class TestMoneroWalletCommon { assert.equal((await that.wallet.getBalance(account.getIndex())).toString(), subaddressesBalance.toString()); assert.equal((await that.wallet.getUnlockedBalance(account.getIndex())).toString(), subaddressesUnlockedBalance.toString()); } - TestUtils.testUnsignedBigInteger(accountsBalance); - TestUtils.testUnsignedBigInteger(accountsUnlockedBalance); + TestUtils.testUnsignedBigInt(accountsBalance); + TestUtils.testUnsignedBigInt(accountsUnlockedBalance); assert.equal((await that.wallet.getBalance()).toString(), accountsBalance.toString()); assert.equal((await that.wallet.getUnlockedBalance()).toString(), accountsUnlockedBalance.toString()); @@ -785,7 +805,7 @@ class TestMoneroWalletCommon { try { await that.wallet.getBalance(undefined, 0); throw new Error("Should have failed"); - } catch(e) { + } catch (e: any) { assert.notEqual(e.message, "Should have failed"); } }); @@ -922,7 +942,7 @@ class TestMoneroWalletCommon { for (let subaddress of subaddresses) { testSubaddress(subaddress); assert.deepEqual(await that.wallet.getSubaddress(account.getIndex(), subaddress.getIndex()), subaddress); - assert.deepEqual((await that.wallet.getSubaddresses(account.getIndex(), subaddress.getIndex()))[0], subaddress); // test plural call with single subaddr number + assert.deepEqual((await that.wallet.getSubaddresses(account.getIndex(), [subaddress.getIndex()]))[0], subaddress); // test plural call with single subaddr number } } }); @@ -965,7 +985,7 @@ class TestMoneroWalletCommon { while ((await that.wallet.getSubaddresses(0)).length < 3) await that.wallet.createSubaddress(0); // set subaddress labels - for (const subaddressIdx = 0; subaddressIdx < (that.wallet.getSubaddresses(0)).length; subaddressIdx++) { + for (let subaddressIdx = 0; subaddressIdx < (await that.wallet.getSubaddresses(0)).length; subaddressIdx++) { const label = GenUtils.getUUID(); await that.wallet.setSubaddressLabel(0, subaddressIdx, label); assert.equal((await that.wallet.getSubaddress(0, subaddressIdx)).getLabel(), label); @@ -975,24 +995,24 @@ class TestMoneroWalletCommon { if (testConfig.testNonRelays) it("Can get transactions in the wallet", async function() { let nonDefaultIncoming = false; - let txs = await that._getAndTestTxs(that.wallet, undefined, true); + let txs = await that.getAndTestTxs(that.wallet, undefined, true); assert(txs.length > 0, "Wallet has no txs to test"); assert.equal(txs[0].getHeight(), TestUtils.FIRST_RECEIVE_HEIGHT, "First tx's restore height must match the restore height in TestUtils"); // test each tranasction let blocksPerHeight = {}; for (let i = 0; i < txs.length; i++) { - await that._testTxWallet(txs[i], {wallet: that.wallet}); - await that._testTxWallet(txs[i], {wallet: that.wallet}); + await that.testTxWallet(txs[i], {wallet: that.wallet}); + await that.testTxWallet(txs[i], {wallet: that.wallet}); assert.equal(txs[i].toString(), txs[i].toString()); // test merging equivalent txs let copy1 = txs[i].copy(); let copy2 = txs[i].copy(); - if (copy1.isConfirmed()) copy1.setBlock(txs[i].getBlock().copy().setTxs([copy1])); - if (copy2.isConfirmed()) copy2.setBlock(txs[i].getBlock().copy().setTxs([copy2])); + if (copy1.getIsConfirmed()) copy1.setBlock(txs[i].getBlock().copy().setTxs([copy1])); + if (copy2.getIsConfirmed()) copy2.setBlock(txs[i].getBlock().copy().setTxs([copy2])); let merged = copy1.merge(copy2); - await that._testTxWallet(merged, {wallet: that.wallet}); + await that.testTxWallet(merged, {wallet: that.wallet}); // find non-default incoming if (txs[i].getIncomingTransfers()) { @@ -1002,7 +1022,7 @@ class TestMoneroWalletCommon { } // ensure unique block reference per height - if (txs[i].isConfirmed()) { + if (txs[i].getIsConfirmed()) { let block = blocksPerHeight[txs[i].getHeight()]; if (block === undefined) blocksPerHeight[txs[i].getHeight()] = txs[i].getBlock(); else assert(block === txs[i].getBlock(), "Block references for same height must be same"); @@ -1029,7 +1049,7 @@ class TestMoneroWalletCommon { // test fetching by hash let fetchedTx = await that.wallet.getTx(txs[0].getHash()); assert.equal(fetchedTx.getHash(), txs[0].getHash()); - await that._testTxWallet(fetchedTx); + await that.testTxWallet(fetchedTx); // test fetching by hashes let txId1 = txs[0].getHash(); @@ -1038,13 +1058,13 @@ class TestMoneroWalletCommon { assert.equal(2, fetchedTxs.length); // test fetching by hashes as collection - let txHashes = []; + let txHashes: any = []; for (let tx of txs) txHashes.push(tx.getHash()); fetchedTxs = await that.wallet.getTxs(txHashes); assert.equal(fetchedTxs.length, txs.length); for (let i = 0; i < txs.length; i++) { assert.equal(fetchedTxs[i].getHash(), txs[i].getHash()); - await that._testTxWallet(fetchedTxs[i]); + await that.testTxWallet(fetchedTxs[i]); } // test fetching with missing tx hashes @@ -1054,7 +1074,7 @@ class TestMoneroWalletCommon { assert.equal(txs.length, fetchedTxs.length); for (let i = 0; i < txs.length; i++) { assert.equal(txs[i].getHash(), fetchedTxs[i].getHash()); - await that._testTxWallet(fetchedTxs[i]); + await that.testTxWallet(fetchedTxs[i]); } }); @@ -1063,44 +1083,44 @@ class TestMoneroWalletCommon { // get random transactions for testing let randomTxs = await getRandomTransactions(that.wallet, undefined, 3, 5); - for (let randomTx of randomTxs) await that._testTxWallet(randomTx); + for (let randomTx of randomTxs) await that.testTxWallet(randomTx); // get transactions by hash - let txHashes = []; + let txHashes: any = []; for (let randomTx of randomTxs) { txHashes.push(randomTx.getHash()); - let txs = await that._getAndTestTxs(that.wallet, {hash: randomTx.getHash()}, true); + let txs = await that.getAndTestTxs(that.wallet, {hash: randomTx.getHash()}, true); assert.equal(txs.length, 1); let merged = txs[0].merge(randomTx.copy()); // txs change with chain so check mergeability - await that._testTxWallet(merged); + await that.testTxWallet(merged); } // get transactions by hashes - let txs = await that._getAndTestTxs(that.wallet, {hashes: txHashes}); + let txs = await that.getAndTestTxs(that.wallet, {hashes: txHashes}); assert.equal(txs.length, randomTxs.length); for (let tx of txs) assert(txHashes.includes(tx.getHash())); // get transactions with an outgoing transfer - txs = await that._getAndTestTxs(that.wallet, {isOutgoing: true}, true); + txs = await that.getAndTestTxs(that.wallet, {isOutgoing: true}, true); for (let tx of txs) { - assert(tx.isOutgoing()); + assert(tx.getIsOutgoing()); assert(tx.getOutgoingTransfer() instanceof MoneroTransfer); await testTransfer(tx.getOutgoingTransfer()); } // get transactions without an outgoing transfer - txs = await that._getAndTestTxs(that.wallet, {isOutgoing: false}, true); + txs = await that.getAndTestTxs(that.wallet, {isOutgoing: false}, true); for (let tx of txs) assert.equal(tx.getOutgoingTransfer(), undefined); // get transactions with incoming transfers - txs = await that._getAndTestTxs(that.wallet, {isIncoming: true}, true); + txs = await that.getAndTestTxs(that.wallet, {isIncoming: true}, true); for (let tx of txs) { assert(tx.getIncomingTransfers().length > 0); for (let transfer of tx.getIncomingTransfers()) assert(transfer instanceof MoneroTransfer); } // get transactions without incoming transfers - txs = await that._getAndTestTxs(that.wallet, {isIncoming: false}, true); + txs = await that.getAndTestTxs(that.wallet, {isIncoming: false}, true); for (let tx of txs) assert.equal(tx.getIncomingTransfers(), undefined); // get transactions associated with an account @@ -1138,30 +1158,30 @@ class TestMoneroWalletCommon { let txQuery = new MoneroTxQuery(); txQuery.setIsConfirmed(true); txQuery.setTransferQuery(new MoneroTransferQuery().setAccountIndex(0).setIsOutgoing(true)); - txs = await that._getAndTestTxs(that.wallet, txQuery, true); + txs = await that.getAndTestTxs(that.wallet, txQuery, true); for (let tx of txs) { - if (!tx.isConfirmed()) console.log(tx.toString()); - assert.equal(tx.isConfirmed(), true); + if (!tx.getIsConfirmed()) console.log(tx.toString()); + assert.equal(tx.getIsConfirmed(), true); assert(tx.getOutgoingTransfer()); assert.equal(tx.getOutgoingTransfer().getAccountIndex(), 0); } // get txs with outgoing transfers that have destinations to account 1 - txs = await that._getAndTestTxs(that.wallet, {transferQuery: {hasDestinations: true, accountIndex: 0}}); + txs = await that.getAndTestTxs(that.wallet, {transferQuery: {hasDestinations: true, accountIndex: 0}}); for (let tx of txs) { assert(tx.getOutgoingTransfer()); assert(tx.getOutgoingTransfer().getDestinations().length > 0); } // include outputs with transactions - txs = await that._getAndTestTxs(that.wallet, {includeOutputs: true}, true); + txs = await that.getAndTestTxs(that.wallet, {includeOutputs: true}, true); let found = false; for (let tx of txs) { if (tx.getOutputs()) { assert(tx.getOutputs().length > 0); found = true; } else { - assert(tx.isOutgoing() || (tx.isIncoming() && !tx.isConfirmed())); // TODO: monero-wallet-rpc: return outputs for unconfirmed txs + assert(tx.getIsOutgoing() || (tx.getIsIncoming() && !tx.getIsConfirmed())); // TODO: monero-wallet-rpc: return outputs for unconfirmed txs } } assert(found, "No outputs found in txs"); @@ -1175,8 +1195,8 @@ class TestMoneroWalletCommon { for (let tx of txs) { assert(tx.getOutputs().length > 0); found = false; - for (let output of tx.getOutputs()) { - if (output.isSpent() === false && output.getAccountIndex() === 1 && output.getSubaddressIndex() === 2) { + for (let output of tx.getOutputsWallet()) { + if (output.getIsSpent() === false && output.getAccountIndex() === 1 && output.getSubaddressIndex() === 2) { found = true; break; } @@ -1188,15 +1208,15 @@ class TestMoneroWalletCommon { txs = await that.wallet.getTxs(new MoneroTxQuery().setIsLocked(false)); assert(txs.length > 0); for (let tx of txs) { - assert.equal(tx.isLocked(), false); + assert.equal(tx.getIsLocked(), false); } // get confirmed transactions sent from/to same wallet with a transfer with destinations txs = await that.wallet.getTxs({isIncoming: true, isOutgoing: true, isConfirmed: true, includeOutputs: true, transferQuery: { hasDestinations: true }}); for (let tx of txs) { - assert(tx.isIncoming()); - assert(tx.isOutgoing()); - assert(tx.isConfirmed()); + assert(tx.getIsIncoming()); + assert(tx.getIsOutgoing()); + assert(tx.getIsConfirmed()); assert(tx.getOutputs().length > 0); assert.notEqual(tx.getOutgoingTransfer(), undefined); assert.notEqual(tx.getOutgoingTransfer().getDestinations(), undefined); @@ -1208,11 +1228,11 @@ class TestMoneroWalletCommon { it("Can get transactions by height", async function() { // get all confirmed txs for testing - let txs = await that._getAndTestTxs(that.wallet, new MoneroTxQuery().setIsConfirmed(true)); + let txs = await that.getAndTestTxs(that.wallet, new MoneroTxQuery().setIsConfirmed(true)); assert(txs.length > 0, "Wallet has no confirmed txs; run send tests"); // collect all tx heights - let txHeights = []; + let txHeights: any = []; for (let tx of txs) txHeights.push(tx.getHeight()); // get height that most txs occur at @@ -1221,16 +1241,16 @@ class TestMoneroWalletCommon { let modeHeight = heightModes.values().next().value; // fetch txs at mode height - let modeTxs = await that._getAndTestTxs(that.wallet, new MoneroTxQuery().setHeight(modeHeight)); + let modeTxs = await that.getAndTestTxs(that.wallet, new MoneroTxQuery().setHeight(modeHeight)); assert.equal(modeTxs.length, heightCounts.get(modeHeight)); // fetch txs at mode height by range - let modeTxsByRange = await that._getAndTestTxs(that.wallet, new MoneroTxQuery().setMinHeight(modeHeight).setMaxHeight(modeHeight)); + let modeTxsByRange = await that.getAndTestTxs(that.wallet, new MoneroTxQuery().setMinHeight(modeHeight).setMaxHeight(modeHeight)); assert.equal(modeTxsByRange.length, modeTxs.length); assert.deepEqual(modeTxsByRange, modeTxs); // fetch all txs by range - let fetched = await that._getAndTestTxs(that.wallet, new MoneroTxQuery().setMinHeight(txs[0].getHeight()).setMaxHeight(txs[txs.length - 1].getHeight())); + let fetched = await that.getAndTestTxs(that.wallet, new MoneroTxQuery().setMinHeight(txs[0].getHeight()).setMaxHeight(txs[txs.length - 1].getHeight())); assert.deepEqual(txs, fetched); // test some filtered by range // TODO: these are separated in Java? @@ -1239,7 +1259,7 @@ class TestMoneroWalletCommon { assert(txs.length > 0, "No transactions; run send to multiple test"); // get and sort block heights in ascending order - let heights = []; + let heights: any = []; for (let tx of txs) { heights.push(tx.getBlock().getHeight()); } @@ -1258,7 +1278,7 @@ class TestMoneroWalletCommon { // assert some transactions filtered let unfilteredCount = txs.length; - txs = await that._getAndTestTxs(that.wallet, {minHeight: minHeight, maxHeight: maxHeight}, true); + txs = await that.getAndTestTxs(that.wallet, {minHeight: minHeight, maxHeight: maxHeight}, true); assert(txs.length < unfilteredCount); for (let tx of txs) { let height = tx.getBlock().getHeight(); @@ -1280,14 +1300,14 @@ class TestMoneroWalletCommon { let paymentIds = randomTxs.map(tx => tx.getPaymentId()); assert(paymentIds.length > 1); for (let paymentId of paymentIds) { - let txs = await that._getAndTestTxs(that.wallet, {paymentId: paymentId}); + let txs = await that.getAndTestTxs(that.wallet, {paymentId: paymentId}); assert(txs.length > 0); assert(txs[0].getPaymentId()); await MoneroUtils.validatePaymentId(txs[0].getPaymentId()); } // get transactions by payment hashes - let txs = await that._getAndTestTxs(that.wallet, {paymentIds: paymentIds}); + let txs = await that.getAndTestTxs(that.wallet, {paymentIds: paymentIds}); for (let tx of txs) { assert(paymentIds.includes(tx.getPaymentId())); } @@ -1367,36 +1387,38 @@ class TestMoneroWalletCommon { // test collection of invalid hashes txs = await that.wallet.getTxs(new MoneroTxQuery().setHashes([txHash, invalidHash, "invalid_hash_2"])); assert.equal(1, txs.length); - for (let tx of txs) await that._testTxWallet(tx); + for (let tx of txs) await that.testTxWallet(tx); }); if (testConfig.testNonRelays) it("Can get transfers in the wallet, accounts, and subaddresses", async function() { // get all transfers - await that._getAndTestTransfers(that.wallet, undefined, true); + await that.getAndTestTransfers(that.wallet, undefined, true); // get transfers by account index let nonDefaultIncoming = false; for (let account of await that.wallet.getAccounts(true)) { - let accountTransfers = await that._getAndTestTransfers(that.wallet, {accountIndex: account.getIndex()}); + let accountTransfers = await that.getAndTestTransfers(that.wallet, {accountIndex: account.getIndex()}); for (let transfer of accountTransfers) assert.equal(transfer.getAccountIndex(), account.getIndex()); // get transfers by subaddress index - let subaddressTransfers = []; + let subaddressTransfers: MoneroTransfer[] = []; for (let subaddress of account.getSubaddresses()) { - let transfers = await that._getAndTestTransfers(that.wallet, {accountIndex: subaddress.getAccountIndex(), subaddressIndex: subaddress.getIndex()}); + let transfers = await that.getAndTestTransfers(that.wallet, {accountIndex: subaddress.getAccountIndex(), subaddressIndex: subaddress.getIndex()}); for (let transfer of transfers) { // test account and subaddress indices assert.equal(transfer.getAccountIndex(), subaddress.getAccountIndex()); - if (transfer.isIncoming()) { - assert.equal(transfer.getSubaddressIndex(), subaddress.getIndex()); - if (transfer.getAccountIndex() !== 0 && transfer.getSubaddressIndex() !== 0) nonDefaultIncoming = true; + if (transfer.getIsIncoming()) { + const inTransfer = transfer as MoneroIncomingTransfer; + assert.equal(inTransfer.getSubaddressIndex(), subaddress.getIndex()); + if (transfer.getAccountIndex() !== 0 && inTransfer.getSubaddressIndex() !== 0) nonDefaultIncoming = true; } else { - assert(transfer.getSubaddressIndices().includes(subaddress.getIndex())); - if (transfer.getAccountIndex() !== 0) { - for (let subaddrIdx of transfer.getSubaddressIndices()) { + const outTransfer = transfer as MoneroOutgoingTransfer; + assert(outTransfer.getSubaddressIndices().includes(subaddress.getIndex())); + if (outTransfer.getAccountIndex() !== 0) { + for (let subaddrIdx of outTransfer.getSubaddressIndices()) { if (subaddrIdx > 0) { nonDefaultIncoming = true; break; @@ -1421,21 +1443,21 @@ class TestMoneroWalletCommon { // collect unique subaddress indices let subaddressIndices = new Set(); for (let transfer of subaddressTransfers) { - if (transfer.isIncoming()) subaddressIndices.add(transfer.getSubaddressIndex()); - else for (let subaddressIdx of transfer.getSubaddressIndices()) subaddressIndices.add(subaddressIdx); + if (transfer.getIsIncoming()) subaddressIndices.add((transfer as MoneroIncomingTransfer).getSubaddressIndex()); + else for (let subaddressIdx of (transfer as MoneroOutgoingTransfer).getSubaddressIndices()) subaddressIndices.add(subaddressIdx); } // get and test transfers by subaddress indices - let transfers = await that._getAndTestTransfers(that.wallet, new MoneroTransferQuery().setAccountIndex(account.getIndex()).setSubaddressIndices(Array.from(subaddressIndices)), undefined, undefined); + let transfers = await that.getAndTestTransfers(that.wallet, new MoneroTransferQuery().setAccountIndex(account.getIndex()).setSubaddressIndices(Array.from(subaddressIndices) as number[])); //if (transfers.length !== subaddressTransfers.length) console.log("WARNING: outgoing transfers always from subaddress 0 (monero-wallet-rpc #5171)"); assert.equal(transfers.length, subaddressTransfers.length); // TODO monero-wallet-rpc: these may not be equal because outgoing transfers are always from subaddress 0 (#5171) and/or incoming transfers from/to same account are occluded (#4500) for (let transfer of transfers) { assert.equal(account.getIndex(), transfer.getAccountIndex()); - if (transfer.isIncoming()) assert(subaddressIndices.has(transfer.getSubaddressIndex())); + if (transfer.getIsIncoming()) assert(subaddressIndices.has((transfer as MoneroIncomingTransfer).getSubaddressIndex())); else { let overlaps = false; for (let subaddressIdx of subaddressIndices) { - for (let outSubaddressIdx of transfer.getSubaddressIndices()) { + for (let outSubaddressIdx of (transfer as MoneroOutgoingTransfer).getSubaddressIndices()) { if (subaddressIdx === outSubaddressIdx) { overlaps = true; break; @@ -1455,48 +1477,48 @@ class TestMoneroWalletCommon { it("Can get transfers with additional configuration", async function() { // get incoming transfers - let transfers = await that._getAndTestTransfers(that.wallet, {isIncoming: true}, true); - for (let transfer of transfers) assert(transfer.isIncoming()); + let transfers = await that.getAndTestTransfers(that.wallet, {isIncoming: true}, true); + for (let transfer of transfers) assert(transfer.getIsIncoming()); // get outgoing transfers - transfers = await that._getAndTestTransfers(that.wallet, {isOutgoing: true}, true); - for (let transfer of transfers) assert(transfer.isOutgoing()); + transfers = await that.getAndTestTransfers(that.wallet, {isIncoming: false}, true); + for (let transfer of transfers) assert(transfer.getIsOutgoing()); // get confirmed transfers to account 0 - transfers = await that._getAndTestTransfers(that.wallet, {accountIndex: 0, txQuery: {isConfirmed: true}}, true); + transfers = await that.getAndTestTransfers(that.wallet, {accountIndex: 0, txQuery: {isConfirmed: true}}, true); for (let transfer of transfers) { assert.equal(transfer.getAccountIndex(), 0); - assert(transfer.getTx().isConfirmed()); + assert(transfer.getTx().getIsConfirmed()); } // get confirmed transfers to [1, 2] - transfers = await that._getAndTestTransfers(that.wallet, {accountIndex: 1, subaddressIndex: 2, txQuery: {isConfirmed: true}}, true); + transfers = await that.getAndTestTransfers(that.wallet, {accountIndex: 1, subaddressIndex: 2, txQuery: {isConfirmed: true}}, true); for (let transfer of transfers) { assert.equal(transfer.getAccountIndex(), 1); - if (transfer.isIncoming()) assert.equal(transfer.getSubaddressIndex(), 2); - else assert(transfer.getSubaddressIndices().includes(2)); - assert(transfer.getTx().isConfirmed()); + if (transfer.getIsIncoming()) assert.equal((transfer as MoneroIncomingTransfer).getSubaddressIndex(), 2); + else assert((transfer as MoneroOutgoingTransfer).getSubaddressIndices().includes(2)); + assert(transfer.getTx().getIsConfirmed()); } // get transfers in the tx pool - transfers = await that._getAndTestTransfers(that.wallet, {txQuery: {inTxPool: true}}); + transfers = await that.getAndTestTransfers(that.wallet, {txQuery: {inTxPool: true}}); for (let transfer of transfers) { - assert.equal(transfer.getTx().inTxPool(), true); + assert.equal(transfer.getTx().getInTxPool(), true); } // get random transactions let txs = await getRandomTransactions(that.wallet, undefined, 3, 5); // get transfers with a tx hash - let txHashes = []; + let txHashes: any = []; for (let tx of txs) { txHashes.push(tx.getHash()); - transfers = await that._getAndTestTransfers(that.wallet, {txQuery: {hash: tx.getHash()}}, true); + transfers = await that.getAndTestTransfers(that.wallet, {txQuery: {hash: tx.getHash()}}, true); for (let transfer of transfers) assert.equal(transfer.getTx().getHash(), tx.getHash()); } // get transfers with tx hashes - transfers = await that._getAndTestTransfers(that.wallet, {txQuery: {hashes: txHashes}}, true); + transfers = await that.getAndTestTransfers(that.wallet, {txQuery: {hashes: txHashes}}, true); for (let transfer of transfers) assert(txHashes.includes(transfer.getTx().getHash())); // TODO: test that transfers with the same tx hash have the same tx reference @@ -1508,20 +1530,20 @@ class TestMoneroWalletCommon { transferQuery.setIsOutgoing(true); transferQuery.setHasDestinations(true); transferQuery.setTxQuery(new MoneroTxQuery().setIsConfirmed(true)); - transfers = await that._getAndTestTransfers(that.wallet, transferQuery); + transfers = await that.getAndTestTransfers(that.wallet, transferQuery); for (let transfer of transfers) { - assert.equal(transfer.isOutgoing(), true); - assert(transfer.getDestinations().length > 0); - assert.equal(transfer.getTx().isConfirmed(), true); + assert.equal(transfer.getIsOutgoing(), true); + assert((transfer as MoneroOutgoingTransfer).getDestinations().length > 0); + assert.equal(transfer.getTx().getIsConfirmed(), true); } // get incoming transfers to account 0 which has outgoing transfers (i.e. originated from the same wallet) transfers = await that.wallet.getTransfers({accountIndex: 1, isIncoming: true, txQuery: {isOutgoing: true}}) assert(transfers.length > 0); for (let transfer of transfers) { - assert(transfer.isIncoming()); + assert(transfer.getIsIncoming()); assert.equal(transfer.getAccountIndex(), 1); - assert(transfer.getTx().isOutgoing()); + assert(transfer.getTx().getIsOutgoing()); assert.equal(transfer.getTx().getOutgoingTransfer(), undefined); } @@ -1559,7 +1581,7 @@ class TestMoneroWalletCommon { try { let transfers = await that.wallet.getTransfers({accountIndex: 0, subaddressIndex: -1}); throw new Error("Should have failed"); - } catch (e) { + } catch (e: any) { assert.notEqual(e.message, "Should have failed"); } }); @@ -1571,7 +1593,7 @@ class TestMoneroWalletCommon { let inTransfers = await that.wallet.getIncomingTransfers(); assert(inTransfers.length > 0); for (let transfer of inTransfers) { - assert(transfer.isIncoming()); + assert(transfer.getIsIncoming()); await testTransfer(transfer, undefined); } @@ -1582,7 +1604,7 @@ class TestMoneroWalletCommon { inTransfers = await that.wallet.getIncomingTransfers({amount: amount, accountIndex: accountIdx, subaddressIndex: subaddressIdx}); assert(inTransfers.length > 0); for (let transfer of inTransfers) { - assert(transfer.isIncoming()); + assert(transfer.getIsIncoming()); assert.equal(transfer.getAmount().toString(), amount.toString()); assert.equal(transfer.getAccountIndex(), accountIdx); assert.equal(transfer.getSubaddressIndex(), subaddressIdx); @@ -1592,7 +1614,7 @@ class TestMoneroWalletCommon { // get incoming transfers with contradictory query try { inTransfers = await that.wallet.getIncomingTransfers(new MoneroTransferQuery().setIsIncoming(false)); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Transfer query contradicts getting incoming transfers"); } @@ -1600,7 +1622,7 @@ class TestMoneroWalletCommon { let outTransfers = await that.wallet.getOutgoingTransfers(); assert(outTransfers.length > 0); for (let transfer of outTransfers) { - assert(transfer.isOutgoing()); + assert(transfer.getIsOutgoing()); await testTransfer(transfer, undefined); } @@ -1608,7 +1630,7 @@ class TestMoneroWalletCommon { outTransfers = await that.wallet.getOutgoingTransfers({accountIndex: accountIdx, subaddressIndex: subaddressIdx}); assert(outTransfers.length > 0); for (let transfer of outTransfers) { - assert(transfer.isOutgoing()); + assert(transfer.getIsOutgoing()); assert.equal(transfer.getAccountIndex(), accountIdx); assert(transfer.getSubaddressIndices().includes(subaddressIdx)); await testTransfer(transfer, undefined); @@ -1617,7 +1639,7 @@ class TestMoneroWalletCommon { // get outgoing transfers with contradictory query try { outTransfers = await that.wallet.getOutgoingTransfers(new MoneroTransferQuery().setIsOutgoing(false)); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Transfer query contradicts getting outgoing transfers"); } }); @@ -1626,7 +1648,7 @@ class TestMoneroWalletCommon { it("Can get outputs in the wallet, accounts, and subaddresses", async function() { // get all outputs - await that._getAndTestOutputs(that.wallet, undefined, true); + await that.getAndTestOutputs(that.wallet, undefined, true); // get outputs for each account let nonDefaultIncoming = false; @@ -1635,16 +1657,16 @@ class TestMoneroWalletCommon { // determine if account is used let isUsed = false; - for (let subaddress of account.getSubaddresses()) if (subaddress.isUsed()) isUsed = true; + for (let subaddress of account.getSubaddresses()) if (subaddress.getIsUsed()) isUsed = true; // get outputs by account index - let accountOutputs = await that._getAndTestOutputs(that.wallet, {accountIndex: account.getIndex()}, isUsed); + let accountOutputs = await that.getAndTestOutputs(that.wallet, {accountIndex: account.getIndex()}, isUsed); for (let output of accountOutputs) assert.equal(output.getAccountIndex(), account.getIndex()); // get outputs by subaddress index - let subaddressOutputs = []; + let subaddressOutputs: any[] = []; for (let subaddress of account.getSubaddresses()) { - let outputs = await that._getAndTestOutputs(that.wallet, {accountIndex: account.getIndex(), subaddressIndex: subaddress.getIndex()}, subaddress.isUsed()); + let outputs = await that.getAndTestOutputs(that.wallet, {accountIndex: account.getIndex(), subaddressIndex: subaddress.getIndex()}, subaddress.getIsUsed()); for (let output of outputs) { assert.equal(output.getAccountIndex(), subaddress.getAccountIndex()); assert.equal(output.getSubaddressIndex(), subaddress.getIndex()); @@ -1656,7 +1678,7 @@ class TestMoneroWalletCommon { // get outputs by subaddress indices let subaddressIndices = Array.from(new Set(subaddressOutputs.map(output => output.getSubaddressIndex()))); - let outputs = await that._getAndTestOutputs(that.wallet, {accountIndex: account.getIndex(), subaddressIndices: subaddressIndices}, isUsed); + let outputs = await that.getAndTestOutputs(that.wallet, {accountIndex: account.getIndex(), subaddressIndices: subaddressIndices}, isUsed); assert.equal(outputs.length, subaddressOutputs.length); for (let output of outputs) { assert.equal(output.getAccountIndex(), account.getIndex()); @@ -1672,32 +1694,32 @@ class TestMoneroWalletCommon { it("Can get outputs with additional configuration", async function() { // get unspent outputs to account 0 - let outputs = await that._getAndTestOutputs(that.wallet, {accountIndex: 0, isSpent: false}); + let outputs = await that.getAndTestOutputs(that.wallet, {accountIndex: 0, isSpent: false}); for (let output of outputs) { assert.equal(output.getAccountIndex(), 0); - assert.equal(output.isSpent(), false); + assert.equal(output.getIsSpent(), false); } // get spent outputs to account 1 - outputs = await that._getAndTestOutputs(that.wallet, {accountIndex: 1, isSpent: true}, true); + outputs = await that.getAndTestOutputs(that.wallet, {accountIndex: 1, isSpent: true}, true); for (let output of outputs) { assert.equal(output.getAccountIndex(), 1); - assert.equal(output.isSpent(), true); + assert.equal(output.getIsSpent(), true); } // get random transactions let txs = await getRandomTransactions(that.wallet, {isConfirmed: true}, 3, 5); // get outputs with a tx hash - let txHashes = []; + let txHashes: any = []; for (let tx of txs) { txHashes.push(tx.getHash()); - outputs = await that._getAndTestOutputs(that.wallet, {txQuery: {hash: tx.getHash()}}, true); + outputs = await that.getAndTestOutputs(that.wallet, {txQuery: {hash: tx.getHash()}}, true); for (let output of outputs) assert.equal(output.getTx().getHash(), tx.getHash()); } // get outputs with tx hashes - outputs = await that._getAndTestOutputs(that.wallet, {txQuery: {hashes: txHashes}}, true); + outputs = await that.getAndTestOutputs(that.wallet, {txQuery: {hashes: txHashes}}, true); for (let output of outputs) assert(txHashes.includes(output.getTx().getHash())); // get confirmed outputs to specific subaddress with pre-built query @@ -1707,12 +1729,12 @@ class TestMoneroWalletCommon { query.setAccountIndex(accountIdx).setSubaddressIndex(subaddressIdx); query.setTxQuery(new MoneroTxQuery().setIsConfirmed(true)); query.setMinAmount(TestUtils.MAX_FEE); - outputs = await that._getAndTestOutputs(that.wallet, query, true); + outputs = await that.getAndTestOutputs(that.wallet, query, true); for (let output of outputs) { assert.equal(output.getAccountIndex(), accountIdx); assert.equal(output.getSubaddressIndex(), subaddressIdx); - assert.equal(output.getTx().isConfirmed(), true); - assert(output.getAmount().compare(TestUtils.MAX_FEE) >= 0); + assert.equal(output.getTx().getIsConfirmed(), true); + assert(output.getAmount() >= TestUtils.MAX_FEE); } // get output by key image @@ -1725,9 +1747,9 @@ class TestMoneroWalletCommon { outputs = await that.wallet.getOutputs({txQuery: {isConfirmed: true, isIncoming: true, isOutgoing: true, includeOutputs: true}}); assert(outputs.length > 0); for (let output of outputs) { - assert(output.getTx().isIncoming()); - assert(output.getTx().isOutgoing()); - assert(output.getTx().isConfirmed()); + assert(output.getTx().getIsIncoming()); + assert(output.getTx().getIsOutgoing()); + assert(output.getTx().getIsConfirmed()); assert(output.getTx().getOutputs().length > 0); assert(GenUtils.arrayContains(output.getTx().getOutputs(), output, true)); } @@ -1783,78 +1805,78 @@ class TestMoneroWalletCommon { let accounts = await that.wallet.getAccounts(true); // includes subaddresses // test wallet balance - TestUtils.testUnsignedBigInteger(walletBalance); - TestUtils.testUnsignedBigInteger(walletUnlockedBalance); - assert(walletBalance.compare(walletUnlockedBalance) >= 0); + TestUtils.testUnsignedBigInt(walletBalance); + TestUtils.testUnsignedBigInt(walletUnlockedBalance); + assert(walletBalance >= walletUnlockedBalance); // test that wallet balance equals sum of account balances - let accountsBalance = new BigInteger(0); - let accountsUnlockedBalance = new BigInteger(0); + let accountsBalance = BigInt(0); + let accountsUnlockedBalance = BigInt(0); for (let account of accounts) { await testAccount(account); // test that account balance equals sum of subaddress balances - accountsBalance = accountsBalance.add(account.getBalance()); - accountsUnlockedBalance = accountsUnlockedBalance.add(account.getUnlockedBalance()); + accountsBalance += account.getBalance(); + accountsUnlockedBalance += account.getUnlockedBalance(); } - assert.equal(walletBalance.compare(accountsBalance), 0); - assert.equal(walletUnlockedBalance.compare(accountsUnlockedBalance), 0); + assert.equal(accountsBalance, walletBalance); + assert.equal(accountsUnlockedBalance, walletUnlockedBalance); // // test that wallet balance equals net of wallet's incoming and outgoing tx amounts // // TODO monero-wallet-rpc: these tests are disabled because incoming transfers are not returned when sent from the same account, so doesn't balance #4500 -// // TODO: test unlocked balance based on txs, requires e.g. tx.isLocked() -// let outgoingSum = new BigInteger(0); -// let incomingSum = new BigInteger(0); +// // TODO: test unlocked balance based on txs, requires e.g. tx.getIsLocked() +// let outgoingSum = BigInt(0); +// let incomingSum = BigInt(0); // for (let tx of txs) { -// if (tx.getOutgoingAmount()) outgoingSum = outgoingSum.add(tx.getOutgoingAmount()); -// if (tx.getIncomingAmount()) incomingSum = incomingSum.add(tx.getIncomingAmount()); +// if (tx.getOutgoingAmount()) outgoingSum = outgoingSum + (tx.getOutgoingAmount()); +// if (tx.getIncomingAmount()) incomingSum = incomingSum + (tx.getIncomingAmount()); // } -// assert.equal(incomingSum.subtract(outgoingSum).toString(), walletBalance.toString()); +// assert.equal(incomingSum - (outgoingSum).toString(), walletBalance.toString()); // // // test that each account's balance equals net of account's incoming and outgoing tx amounts // for (let account of accounts) { // if (account.getIndex() !== 1) continue; // find 1 -// outgoingSum = new BigInteger(0); -// incomingSum = new BigInteger(0); +// outgoingSum = BigInt(0); +// incomingSum = BigInt(0); // let filter = new MoneroTxQuery(); // filter.setAccountIndex(account.getIndex()); // for (let tx of txs.filter(tx => filter.meetsCriteria(tx))) { // normally we'd call wallet.getTxs(filter) but we're using pre-fetched txs // if (tx.getHash() === "8d3919d98dd5a734da8c52eddc558db3fbf059ad55d432f0052ecd59ef122ecb") console.log(tx.toString(0)); // // //console.log((tx.getOutgoingAmount() ? tx.getOutgoingAmount().toString() : "") + ", " + (tx.getIncomingAmount() ? tx.getIncomingAmount().toString() : "")); -// if (tx.getOutgoingAmount()) outgoingSum = outgoingSum.add(tx.getOutgoingAmount()); -// if (tx.getIncomingAmount()) incomingSum = incomingSum.add(tx.getIncomingAmount()); +// if (tx.getOutgoingAmount()) outgoingSum = outgoingSum + (tx.getOutgoingAmount()); +// if (tx.getIncomingAmount()) incomingSum = incomingSum + (tx.getIncomingAmount()); // } -// assert.equal(incomingSum.subtract(outgoingSum).toString(), account.getBalance().toString()); +// assert.equal(incomingSum - (outgoingSum).toString(), account.getBalance().toString()); // } // balance may not equal sum of unspent outputs if unconfirmed txs // TODO monero-wallet-rpc: reason not to return unspent outputs on unconfirmed txs? then this isn't necessary let txs = await that.wallet.getTxs(); let hasUnconfirmedTx = false; - for (let tx of txs) if (tx.inTxPool()) hasUnconfirmedTx = true; + for (let tx of txs) if (tx.getInTxPool()) hasUnconfirmedTx = true; // wallet balance is sum of all unspent outputs - let walletSum = new BigInteger(0); - for (let output of await that.wallet.getOutputs({isSpent: false})) walletSum = walletSum.add(output.getAmount()); - if (walletBalance.compare(walletSum) !== 0) { + let walletSum = BigInt(0); + for (let output of await that.wallet.getOutputs({isSpent: false})) walletSum = walletSum + (output.getAmount()); + if (walletBalance !== walletSum) { // txs may have changed in between calls so retry test - walletSum = new BigInteger(0); - for (let output of await that.wallet.getOutputs({isSpent: false})) walletSum = walletSum.add(output.getAmount()); - if (walletBalance.compare(walletSum) !== 0) assert(hasUnconfirmedTx, "Wallet balance must equal sum of unspent outputs if no unconfirmed txs"); + walletSum = BigInt(0); + for (let output of await that.wallet.getOutputs({isSpent: false})) walletSum = walletSum + (output.getAmount()); + if (walletBalance !== walletSum) assert(hasUnconfirmedTx, "Wallet balance must equal sum of unspent outputs if no unconfirmed txs"); } // account balances are sum of their unspent outputs for (let account of accounts) { - let accountSum = new BigInteger(0); + let accountSum = BigInt(0); let accountOutputs = await that.wallet.getOutputs({accountIndex: account.getIndex(), isSpent: false}); - for (let output of accountOutputs) accountSum = accountSum.add(output.getAmount()); + for (let output of accountOutputs) accountSum = accountSum + (output.getAmount()); if (account.getBalance().toString() !== accountSum.toString()) assert(hasUnconfirmedTx, "Account balance must equal sum of its unspent outputs if no unconfirmed txs"); // subaddress balances are sum of their unspent outputs for (let subaddress of account.getSubaddresses()) { - let subaddressSum = new BigInteger(0); + let subaddressSum = BigInt(0); let subaddressOutputs = await that.wallet.getOutputs({accountIndex: account.getIndex(), subaddressIndex: subaddress.getIndex(), isSpent: false}); - for (let output of subaddressOutputs) subaddressSum = subaddressSum.add(output.getAmount()); + for (let output of subaddressOutputs) subaddressSum = subaddressSum + (output.getAmount()); if (subaddress.getBalance().toString() !== subaddressSum.toString()) assert(hasUnconfirmedTx, "Subaddress balance must equal sum of its unspent outputs if no unconfirmed txs"); } } @@ -1884,8 +1906,8 @@ class TestMoneroWalletCommon { let uuid = GenUtils.getUUID(); let txs = await that.wallet.getTxs(); assert(txs.length >= 3, "Test requires 3 or more wallet transactions; run send tests"); - let txHashes = []; - let txNotes = []; + let txHashes: any = []; + let txNotes: any = []; for (let i = 0; i < txHashes.length; i++) { txHashes.push(txs[i].getHash()); txNotes.push(uuid + i); @@ -1914,7 +1936,7 @@ class TestMoneroWalletCommon { let txs; try { txs = await getRandomTransactions(that.wallet, {isConfirmed: true, isOutgoing: true, transferQuery: {hasDestinations: true}}, 1, MAX_TX_PROOFS); - } catch (e) { + } catch (e: any) { if (e.message.indexOf("found with") >= 0) throw new Error("No txs with outgoing destinations found; run send tests") throw e; } @@ -1927,15 +1949,15 @@ class TestMoneroWalletCommon { assert(tx.getOutgoingTransfer().getDestinations().length > 0); for (let destination of tx.getOutgoingTransfer().getDestinations()) { let check = await that.wallet.checkTxKey(tx.getHash(), key, destination.getAddress()); - if (destination.getAmount().compare(new BigInteger()) > 0) { + if (destination.getAmount() > 0n) { // TODO monero-wallet-rpc: indicates amount received amount is 0 despite transaction with transfer to this address // TODO monero-wallet-rpc: returns 0-4 errors, not consistent -// assert(check.getReceivedAmount().compare(new BigInteger(0)) > 0); - if (check.getReceivedAmount().compare(new BigInteger(0)) === 0) { + //assert(check.getReceivedAmount() > 0n); + if (check.getReceivedAmount() === 0n) { console.log("WARNING: key proof indicates no funds received despite transfer (txid=" + tx.getHash() + ", key=" + key + ", address=" + destination.getAddress() + ", amount=" + destination.getAmount() + ")"); } } - else assert(check.getReceivedAmount().compare(new BigInteger(0)) === 0); + else assert(check.getReceivedAmount() === 0n); testCheckTx(tx, check); } } @@ -1944,8 +1966,8 @@ class TestMoneroWalletCommon { try { await that.wallet.getTxKey("invalid_tx_id"); throw new Error("Should throw exception for invalid key"); - } catch (e) { - that._testInvalidTxHashError(e); + } catch (e: any) { + that.testInvalidTxHashError(e); } // test check with invalid tx hash @@ -1955,24 +1977,24 @@ class TestMoneroWalletCommon { try { await that.wallet.checkTxKey("invalid_tx_id", key, destination.getAddress()); throw new Error("Should have thrown exception"); - } catch (e) { - that._testInvalidTxHashError(e); + } catch (e: any) { + that.testInvalidTxHashError(e); } // test check with invalid key try { await that.wallet.checkTxKey(tx.getHash(), "invalid_tx_key", destination.getAddress()); throw new Error("Should have thrown exception"); - } catch (e) { - that._testInvalidTxKeyError(e); + } catch (e: any) { + that.testInvalidTxKeyError(e); } // test check with invalid address try { await that.wallet.checkTxKey(tx.getHash(), key, "invalid_tx_address"); throw new Error("Should have thrown exception"); - } catch (e) { - that._testInvalidAddressError(e); + } catch (e: any) { + that.testInvalidAddressError(e); } // test check with different address @@ -1988,8 +2010,8 @@ class TestMoneroWalletCommon { } assert(differentAddress, "Could not get a different outgoing address to test; run send tests"); let check = await that.wallet.checkTxKey(tx.getHash(), key, differentAddress); - assert(check.isGood()); - assert(check.getReceivedAmount().compare(new BigInteger(0)) >= 0); + assert(check.getIsGood()); + assert(check.getReceivedAmount() >= 0n); testCheckTx(tx, check); }); @@ -2000,7 +2022,7 @@ class TestMoneroWalletCommon { let txs; try { txs = await getRandomTransactions(that.wallet, {transferQuery: {hasDestinations: true}}, 2, MAX_TX_PROOFS); - } catch (e) { + } catch (e: any) { if (e.message.indexOf("found with") >= 0) throw new Error("No txs with outgoing destinations found; run send tests") throw e; } @@ -2026,46 +2048,46 @@ class TestMoneroWalletCommon { try { await that.wallet.getTxProof("invalid_tx_id", destination.getAddress()); throw new Error("Should throw exception for invalid key"); - } catch (e) { - that._testInvalidTxHashError(e); + } catch (e: any) { + that.testInvalidTxHashError(e); } // test check with invalid tx hash try { await that.wallet.checkTxProof("invalid_tx_id", destination.getAddress(), undefined, signature); throw new Error("Should have thrown exception"); - } catch (e) { - that._testInvalidTxHashError(e); + } catch (e: any) { + that.testInvalidTxHashError(e); } // test check with invalid address try { await that.wallet.checkTxProof(tx.getHash(), "invalid_tx_address", undefined, signature); throw new Error("Should have thrown exception"); - } catch (e) { - that._testInvalidAddressError(e); + } catch (e: any) { + that.testInvalidAddressError(e); } // test check with wrong message signature = await that.wallet.getTxProof(tx.getHash(), destination.getAddress(), "This is the right message"); check = await that.wallet.checkTxProof(tx.getHash(), destination.getAddress(), "This is the wrong message", signature); - assert.equal(check.isGood(), false); + assert.equal(check.getIsGood(), false); testCheckTx(tx, check); // test check with wrong signature let wrongSignature = await that.wallet.getTxProof(txs[1].getHash(), txs[1].getOutgoingTransfer().getDestinations()[0].getAddress(), "This is the right message"); try { check = await that.wallet.checkTxProof(tx.getHash(), destination.getAddress(), "This is the right message", wrongSignature); - assert.equal(check.isGood(), false); - } catch (e) { - that._testInvalidSignatureError(e); + assert.equal(check.getIsGood(), false); + } catch (e: any) { + that.testInvalidSignatureError(e); } // test check with empty signature try { check = await that.wallet.checkTxProof(tx.getHash(), destination.getAddress(), "This is the right message", ""); - assert.equal(check.isGood(), false); - } catch (e) { + assert.equal(check.getIsGood(), false); + } catch (e: any) { assert.equal("Must provide signature to check tx proof", e.message); } }); @@ -2076,7 +2098,7 @@ class TestMoneroWalletCommon { // get random confirmed outgoing txs let txs = await getRandomTransactions(that.wallet, {isIncoming: false, inTxPool: false, isFailed: false}, 2, MAX_TX_PROOFS); for (let tx of txs) { - assert.equal(tx.isConfirmed(), true); + assert.equal(tx.getIsConfirmed(), true); assert.equal(tx.getIncomingTransfers(), undefined); assert(tx.getOutgoingTransfer()); } @@ -2097,16 +2119,16 @@ class TestMoneroWalletCommon { try { await that.wallet.getSpendProof("invalid_tx_id"); throw new Error("Should throw exception for invalid key"); - } catch (e) { - that._testInvalidTxHashError(e); + } catch (e: any) { + that.testInvalidTxHashError(e); } // test check with invalid tx hash try { await that.wallet.checkSpendProof("invalid_tx_id", undefined, signature); throw new Error("Should have thrown exception"); - } catch (e) { - that._testInvalidTxHashError(e); + } catch (e: any) { + that.testInvalidTxHashError(e); } // test check with invalid message @@ -2127,10 +2149,10 @@ class TestMoneroWalletCommon { // check proof of entire wallet let check = await that.wallet.checkReserveProof(await that.wallet.getPrimaryAddress(), "Test message", signature); - assert(check.isGood()); + assert(check.getIsGood()); testCheckReserve(check); let balance = await that.wallet.getBalance(); - if (balance.compare(check.getTotalAmount()) !== 0) { // TODO monero-wallet-rpc: this check fails with unconfirmed txs + if (balance !== check.getTotalAmount()) { // TODO monero-wallet-rpc: this check fails with unconfirmed txs let unconfirmedTxs = await that.wallet.getTxs({inTxPool: true}); assert(unconfirmedTxs.length > 0, "Reserve amount must equal balance unless wallet has unconfirmed txs"); } @@ -2140,29 +2162,29 @@ class TestMoneroWalletCommon { try { await that.wallet.checkReserveProof(differentAddress, "Test message", signature); throw new Error("Should have thrown exception"); - } catch (e) { - that._testNoSubaddressError(e); + } catch (e: any) { + that.testNoSubaddressError(e); } // test subaddress try { await that.wallet.checkReserveProof((await that.wallet.getSubaddress(0, 1)).getAddress(), "Test message", signature); throw new Error("Should have thrown exception"); - } catch (e) { - that._testNoSubaddressError(e); + } catch (e: any) { + that.testNoSubaddressError(e); } // test wrong message check = await that.wallet.checkReserveProof(await that.wallet.getPrimaryAddress(), "Wrong message", signature); - assert.equal(check.isGood(), false); // TODO: specifically test reserve checks, probably separate objects + assert.equal(check.getIsGood(), false); // TODO: specifically test reserve checks, probably separate objects testCheckReserve(check); // test wrong signature try { await that.wallet.checkReserveProof(await that.wallet.getPrimaryAddress(), "Test message", "wrong signature"); throw new Error("Should have thrown exception"); - } catch (e) { - that._testSignatureHeaderCheckError(e); + } catch (e: any) { + that.testSignatureHeaderCheckError(e); } }); @@ -2175,24 +2197,24 @@ class TestMoneroWalletCommon { let accounts = await that.wallet.getAccounts(); let signature; for (let account of accounts) { - if (account.getBalance().compare(new BigInteger(0)) > 0) { - let checkAmount = (await account.getBalance()).divide(new BigInteger(2)); + if (account.getBalance() > 0n) { + let checkAmount = (await account.getBalance()) / (BigInt(2)); signature = await that.wallet.getReserveProofAccount(account.getIndex(), checkAmount, msg); let check = await that.wallet.checkReserveProof(await that.wallet.getPrimaryAddress(), msg, signature); - assert(check.isGood()); + assert(check.getIsGood()); testCheckReserve(check); - assert(check.getTotalAmount().compare(checkAmount) >= 0); + assert (check.getTotalAmount() >= checkAmount); numNonZeroTests++; } else { try { await that.wallet.getReserveProofAccount(account.getIndex(), account.getBalance(), msg); throw new Error("Should have thrown exception"); - } catch (e) { + } catch (e: any) { assert.equal(e.getCode(), -1); try { await that.wallet.getReserveProofAccount(account.getIndex(), TestUtils.MAX_FEE, msg); throw new Error("Should have thrown exception"); - } catch (e2) { + } catch (e2: any) { assert.equal(e2.getCode(), -1); } } @@ -2202,9 +2224,9 @@ class TestMoneroWalletCommon { // test error when not enough balance for requested minimum reserve amount try { - let reserveProof = await that.wallet.getReserveProofAccount(0, accounts[0].getBalance().add(TestUtils.MAX_FEE), "Test message"); + let reserveProof = await that.wallet.getReserveProofAccount(0, accounts[0].getBalance() + (TestUtils.MAX_FEE), "Test message"); throw new Error("should have thrown error"); - } catch (e) { + } catch (e: any) { if (e.message === "should have thrown error") throw new Error("Should have thrown exception but got reserve proof: https://github.com/monero-project/monero/issues/6595"); assert.equal(e.getCode(), -1); } @@ -2214,7 +2236,7 @@ class TestMoneroWalletCommon { try { await that.wallet.checkReserveProof(differentAddress, "Test message", signature); throw new Error("Should have thrown exception"); - } catch (e) { + } catch (e: any) { assert.equal(e.getCode(), -1); } @@ -2222,20 +2244,20 @@ class TestMoneroWalletCommon { try { await that.wallet.checkReserveProof((await that.wallet.getSubaddress(0, 1)).getAddress(), "Test message", signature); throw new Error("Should have thrown exception"); - } catch (e) { + } catch (e: any) { assert.equal(e.getCode(), -1); } // test wrong message let check = await that.wallet.checkReserveProof(await that.wallet.getPrimaryAddress(), "Wrong message", signature); - assert.equal(check.isGood(), false); // TODO: specifically test reserve checks, probably separate objects + assert.equal(check.getIsGood(), false); // TODO: specifically test reserve checks, probably separate objects testCheckReserve(check); // test wrong signature try { await that.wallet.checkReserveProof(await that.wallet.getPrimaryAddress(), "Test message", "wrong signature"); throw new Error("Should have thrown exception"); - } catch (e) { + } catch (e: any) { assert.equal(e.getCode(), -1); } }); @@ -2288,14 +2310,14 @@ class TestMoneroWalletCommon { assert(result.getHeight() > 0); // determine if non-zero spent and unspent amounts are expected - let txs = await that.wallet.getTxs({isConfirmed: true, transferQuery: {isOutgoing: true}}); + let txs = await that.wallet.getTxs({isConfirmed: true, transferQuery: {isIncoming: false}}); let balance = await that.wallet.getBalance(); let hasSpent = txs.length > 0; - let hasUnspent = balance.toJSValue() > 0; + let hasUnspent = balance > 0n; // test amounts - TestUtils.testUnsignedBigInteger(result.getSpentAmount(), hasSpent); - TestUtils.testUnsignedBigInteger(result.getUnspentAmount(), hasUnspent); + TestUtils.testUnsignedBigInt(result.getSpentAmount(), hasSpent); + TestUtils.testUnsignedBigInt(result.getUnspentAmount(), hasUnspent); }); if (testConfig.testNonRelays) @@ -2303,7 +2325,7 @@ class TestMoneroWalletCommon { // message to sign and subaddresses to test let msg = "This is a super important message which needs to be signed and verified."; - let subaddresses = [new MoneroSubaddress(undefined, 0, 0), new MoneroSubaddress(undefined, 0, 1), new MoneroSubaddress(undefined, 1, 0)]; + let subaddresses = [new MoneroSubaddress({accountIndex: 0, index: 0}), new MoneroSubaddress({accountIndex: 0, index: 1}), new MoneroSubaddress({accountIndex: 1, index: 0})]; // test signing message with subaddresses for (let subaddress of subaddresses) { @@ -2311,36 +2333,36 @@ class TestMoneroWalletCommon { // sign and verify message with spend key let signature = await that.wallet.signMessage(msg, MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY, subaddress.getAccountIndex(), subaddress.getIndex()); let result = await that.wallet.verifyMessage(msg, await that.wallet.getAddress(subaddress.getAccountIndex(), subaddress.getIndex()), signature); - assert.deepEqual(result, new MoneroMessageSignatureResult(true, false, MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY, 2)); + assert.deepEqual(result, new MoneroMessageSignatureResult({isGood: true, isOld: false, signatureType: MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY, version: 2})); // verify message with incorrect address result = await that.wallet.verifyMessage(msg, await that.wallet.getAddress(0, 2), signature); - assert.deepEqual(result, new MoneroMessageSignatureResult(false)); + assert.deepEqual(result, new MoneroMessageSignatureResult({isGood: false})); // verify message with external address result = await that.wallet.verifyMessage(msg, await TestUtils.getExternalWalletAddress(), signature); - assert.deepEqual(result, new MoneroMessageSignatureResult(false)); + assert.deepEqual(result, new MoneroMessageSignatureResult({isGood: false})); // verify message with invalid address result = await that.wallet.verifyMessage(msg, "invalid address", signature); - assert.deepEqual(result, new MoneroMessageSignatureResult(false)); + assert.deepEqual(result, new MoneroMessageSignatureResult({isGood: false})); // sign and verify message with view key signature = await that.wallet.signMessage(msg, MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY, subaddress.getAccountIndex(), subaddress.getIndex()); result = await that.wallet.verifyMessage(msg, await that.wallet.getAddress(subaddress.getAccountIndex(), subaddress.getIndex()), signature); - assert.deepEqual(result, new MoneroMessageSignatureResult(true, false, MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY, 2)); - + assert.deepEqual(result, new MoneroMessageSignatureResult({isGood: true, isOld: false, signatureType: MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY, version: 2})); + // verify message with incorrect address result = await that.wallet.verifyMessage(msg, await that.wallet.getAddress(0, 2), signature); - assert.deepEqual(result, new MoneroMessageSignatureResult(false)); + assert.deepEqual(result, new MoneroMessageSignatureResult({isGood: false})); // verify message with external address result = await that.wallet.verifyMessage(msg, await TestUtils.getExternalWalletAddress(), signature); - assert.deepEqual(result, new MoneroMessageSignatureResult(false)); + assert.deepEqual(result, new MoneroMessageSignatureResult({isGood: false})); // verify message with invalid address result = await that.wallet.verifyMessage(msg, "invalid address", signature); - assert.deepEqual(result, new MoneroMessageSignatureResult(false)); + assert.deepEqual(result, new MoneroMessageSignatureResult({isGood: false})); } }); @@ -2355,7 +2377,7 @@ class TestMoneroWalletCommon { // test adding standard addresses const NUM_ENTRIES = 5; let address = (await that.wallet.getSubaddress(0, 0)).getAddress(); - let indices = []; + let indices: any = []; for (let i = 0; i < NUM_ENTRIES; i++) { indices.push(await that.wallet.addAddressBookEntry(address, "hi there!")); } @@ -2456,31 +2478,31 @@ class TestMoneroWalletCommon { it("Can convert between a tx config and payment URI", async function() { // test with address and amount - let config1 = new MoneroTxConfig({address: await that.wallet.getAddress(0, 0), amount: new BigInteger(0)}); + let config1 = new MoneroTxConfig({address: await that.wallet.getAddress(0, 0), amount: BigInt(0)}); let uri = await that.wallet.getPaymentUri(config1); let config2 = await that.wallet.parsePaymentUri(uri); GenUtils.deleteUndefinedKeys(config1); GenUtils.deleteUndefinedKeys(config2); - assert.deepEqual(JSON.parse(JSON.stringify(config2)), JSON.parse(JSON.stringify(config1))); + assert.deepEqual(JSON.parse(JSON.stringify(config2.toJson())), JSON.parse(JSON.stringify(config1.toJson()))); // test with subaddress and all fields config1.getDestinations()[0].setAddress((await that.wallet.getSubaddress(0, 1)).getAddress()); - config1.getDestinations()[0].setAmount(BigInteger.parse("425000000000")); + config1.getDestinations()[0].setAmount(BigInt("425000000000")); config1.setRecipientName("John Doe"); config1.setNote("OMZG XMR FTW"); uri = await that.wallet.getPaymentUri(config1.toJson()); config2 = await that.wallet.parsePaymentUri(uri); GenUtils.deleteUndefinedKeys(config1); GenUtils.deleteUndefinedKeys(config2); - assert.deepEqual(JSON.parse(JSON.stringify(config2)), JSON.parse(JSON.stringify(config1))); + assert.deepEqual(JSON.parse(JSON.stringify(config2.toJson())), JSON.parse(JSON.stringify(config1.toJson()))); // test with undefined address let address = config1.getDestinations()[0].getAddress(); config1.getDestinations()[0].setAddress(undefined); try { await that.wallet.getPaymentUri(config1); - fail("Should have thrown exception with invalid parameters"); - } catch (e) { + throw new Error("Should have thrown exception with invalid parameters"); + } catch (e: any) { assert(e.message.indexOf("Cannot make URI from supplied parameters") >= 0); } config1.getDestinations()[0].setAddress(address); @@ -2489,8 +2511,8 @@ class TestMoneroWalletCommon { config1.setPaymentId("03284e41c342f03603284e41c342f03603284e41c342f03603284e41c342f036"); try { await that.wallet.getPaymentUri(config1); - fail("Should have thrown exception with invalid parameters"); - } catch (e) { + throw new Error("Should have thrown exception with invalid parameters"); + } catch (e: any) { assert(e.message.indexOf("Cannot make URI from supplied parameters") >= 0); } }); @@ -2498,7 +2520,7 @@ class TestMoneroWalletCommon { if (testConfig.testNonRelays) it("Can start and stop mining", async function() { let status = await that.daemon.getMiningStatus(); - if (status.isActive()) await that.wallet.stopMining(); + if (status.getIsActive()) await that.wallet.stopMining(); await that.wallet.startMining(2, false, true); await that.wallet.stopMining(); }); @@ -2521,7 +2543,7 @@ class TestMoneroWalletCommon { try { await that.openWallet(new MoneroWalletConfig().setPath(path).setPassword(TestUtils.WALLET_PASSWORD)); throw new Error("Should have thrown"); - } catch (err) { + } catch (err: any) { assert(err.message === "Failed to open wallet" || err.message === "invalid password"); // TODO: different errors from rpc and wallet2 } @@ -2532,7 +2554,7 @@ class TestMoneroWalletCommon { try { await wallet.changePassword("badpassword", newPassword); throw new Error("Should have thrown"); - } catch (err) { + } catch (err: any) { assert.equal(err.message, "Invalid original password."); } @@ -2620,7 +2642,7 @@ class TestMoneroWalletCommon { let MAX_POLL_TIME = 5000; // maximum time granted for wallet to poll // collect issues as test runs - let issues = []; + let issues: any[] = []; // set sender and receiver let sender = that.wallet; @@ -2632,7 +2654,7 @@ class TestMoneroWalletCommon { // wait for unlocked funds in source account await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(sender); - await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(sender, 0, undefined, TestUtils.MAX_FEE.multiply(new BigInteger("10"))); + await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(sender, 0, undefined, TestUtils.MAX_FEE * (BigInt("10"))); // get balances to compare after sending let senderBalanceBefore = await sender.getBalance(); @@ -2649,14 +2671,14 @@ class TestMoneroWalletCommon { await receiver.addListener(receiverNotificationCollector); // send funds - let ctx = {wallet: sender, isSendResponse: true}; + let ctx: any = {wallet: sender, isSendResponse: true}; let senderTx; let destinationAccounts = sameAccount ? (sweepOutput ? [0] : [0, 1, 2]) : (sweepOutput ? [1] : [1, 2, 3]); - let expectedOutputs = []; + let expectedOutputs: any = []; if (sweepOutput) { ctx.isSweepResponse = true; ctx.isSweepOutputResponse = true; - let outputs = await sender.getOutputs({isSpent: false, accountIndex: 0, minAmount: TestUtils.MAX_FEE.multiply(new BigInteger("5")), txQuery: {isLocked: false}}); + let outputs = await sender.getOutputs({isSpent: false, accountIndex: 0, minAmount: TestUtils.MAX_FEE * (BigInt("5")), txQuery: {isLocked: false}}); if (outputs.length === 0) { issues.push("ERROR: No outputs available to sweep from account 0"); return issues; @@ -2675,29 +2697,29 @@ class TestMoneroWalletCommon { ctx.config = config; } if (createThenRelay) await sender.relayTx(senderTx); - + // start timer to measure end of sync period let startTime = Date.now(); // timestamp in ms // test send tx - await that._testTxWallet(senderTx, ctx); + await that.testTxWallet(senderTx, ctx); // test sender after sending let outputQuery = new MoneroOutputQuery().setTxQuery(new MoneroTxQuery().setHash(senderTx.getHash())); // query for outputs from sender tx if (sameWallet) { if (senderTx.getIncomingAmount() === undefined) issues.push("WARNING: sender tx incoming amount is null when sent to same wallet"); - else if (senderTx.getIncomingAmount().compare(new BigInteger("0")) === 0) issues.push("WARNING: sender tx incoming amount is 0 when sent to same wallet"); - else if (senderTx.getIncomingAmount().compare(senderTx.getOutgoingAmount().subtract(senderTx.getFee())) !== 0) issues.push("WARNING: sender tx incoming amount != outgoing amount - fee when sent to same wallet"); + else if (senderTx.getIncomingAmount() === 0n) issues.push("WARNING: sender tx incoming amount is 0 when sent to same wallet"); + else if (senderTx.getIncomingAmount() !== senderTx.getOutgoingAmount() - (senderTx.getFee())) issues.push("WARNING: sender tx incoming amount != outgoing amount - fee when sent to same wallet"); } else { if (senderTx.getIncomingAmount() !== undefined) issues.push("ERROR: tx incoming amount should be undefined"); // TODO: should be 0? then can remove undefined checks in this method } senderTx = (await sender.getTxs(new MoneroTxQuery().setHash(senderTx.getHash()).setIncludeOutputs(true)))[0]; - if ((await sender.getBalance()).compare(senderBalanceBefore.subtract(senderTx.getFee()).subtract(senderTx.getOutgoingAmount()).add(senderTx.getIncomingAmount() === undefined ? new BigInteger("0") : senderTx.getIncomingAmount())) !== 0) issues.push("ERROR: sender balance after send != balance before - tx fee - outgoing amount + incoming amount (" + toStringBI(await sender.getBalance()) + " != " + toStringBI(senderBalanceBefore) + " - " + toStringBI(senderTx.getFee()) + " - " + toStringBI(senderTx.getOutgoingAmount()) + " + " + toStringBI(senderTx.getIncomingAmount()) + ")"); - if ((await sender.getUnlockedBalance()).compare(senderUnlockedBalanceBefore) >= 0) issues.push("ERROR: sender unlocked balance should have decreased after sending"); + if (await sender.getBalance() !== senderBalanceBefore - (senderTx.getFee()) - (senderTx.getOutgoingAmount()) + (senderTx.getIncomingAmount() === undefined ? BigInt("0") : senderTx.getIncomingAmount())) issues.push("ERROR: sender balance after send != balance before - tx fee - outgoing amount + incoming amount (" + await sender.getBalance() + " != " + senderBalanceBefore + " - " + senderTx.getFee() + " - " + senderTx.getOutgoingAmount() + " + " + senderTx.getIncomingAmount() + ")"); + if (await sender.getUnlockedBalance() >= senderUnlockedBalanceBefore) issues.push("ERROR: sender unlocked balance should have decreased after sending"); if (senderNotificationCollector.getBalanceNotifications().length === 0) issues.push("ERROR: sender did not notify balance change after sending"); else { - if ((await sender.getBalance()).compare(senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1].balance) !== 0) issues.push("ERROR: sender balance != last notified balance after sending (" + toStringBI(await sender.getBalance()) + " != " + toStringBI(senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1][0]) + ")"); - if ((await sender.getUnlockedBalance()).compare(senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1].unlockedBalance) !== 0) issues.push("ERROR: sender unlocked balance != last notified unlocked balance after sending (" + toStringBI(await sender.getUnlockedBalance()) + " != " + toStringBI(senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1][1]) + ")"); + if (await sender.getBalance() !== senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1].balance) issues.push("ERROR: sender balance != last notified balance after sending (" + await sender.getBalance() + " != " + senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1][0]) + ")"; + if (await sender.getUnlockedBalance() !== senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1].unlockedBalance) issues.push("ERROR: sender unlocked balance != last notified unlocked balance after sending (" + await sender.getUnlockedBalance() + " != " + senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1][1]) + ")"; } if (senderNotificationCollector.getOutputsSpent(outputQuery).length === 0) issues.push("ERROR: sender did not announce unconfirmed spent output"); @@ -2705,24 +2727,24 @@ class TestMoneroWalletCommon { await GenUtils.waitFor(TestUtils.SYNC_PERIOD_IN_MS * 2 - (Date.now() - startTime)); startTime = Date.now(); // reset timer let receiverTx = await receiver.getTx(senderTx.getHash()); - if (senderTx.getOutgoingAmount().compare(receiverTx.getIncomingAmount()) !== 0) { - if (sameAccount) issues.push("WARNING: sender tx outgoing amount != receiver tx incoming amount when sent to same account (" + toStringBI(senderTx.getOutgoingAmount()) + " != " + toStringBI(receiverTx.getIncomingAmount()) + ")"); - else issues.push("ERROR: sender tx outgoing amount != receiver tx incoming amount (" + toStringBI(senderTx.getOutgoingAmount()) + " != " + toStringBI(receiverTx.getIncomingAmount()) + ")"); + if (senderTx.getOutgoingAmount() !== receiverTx.getIncomingAmount()) { + if (sameAccount) issues.push("WARNING: sender tx outgoing amount != receiver tx incoming amount when sent to same account (" + senderTx.getOutgoingAmount() + " != " + receiverTx.getIncomingAmount() + ")"); + else issues.push("ERROR: sender tx outgoing amount != receiver tx incoming amount (" + senderTx.getOutgoingAmount() + " != " + receiverTx.getIncomingAmount()) + ")"; } - if ((await receiver.getBalance()).compare(receiverBalanceBefore.add(receiverTx.getIncomingAmount() === undefined ? new BigInteger("0") : receiverTx.getIncomingAmount()).subtract(receiverTx.getOutgoingAmount() === undefined ? new BigInteger("0") : receiverTx.getOutgoingAmount()).subtract(sameWallet ? receiverTx.getFee() : new BigInteger("0"))) !== 0) { - if (sameAccount) issues.push("WARNING: after sending, receiver balance != balance before + incoming amount - outgoing amount - tx fee when sent to same account (" + toStringBI(await receiver.getBalance()) + " != " + toStringBI(receiverBalanceBefore) + " + " + toStringBI(receiverTx.getIncomingAmount()) + " - " + toStringBI(receiverTx.getOutgoingAmount()) + " - " + (sameWallet ? receiverTx.getFee() : new BigInteger("0")).toString() + ")"); - else issues.push("ERROR: after sending, receiver balance != balance before + incoming amount - outgoing amount - tx fee (" + toStringBI(await receiver.getBalance()) + " != " + toStringBI(receiverBalanceBefore) + " + " + toStringBI(receiverTx.getIncomingAmount()) + " - " + toStringBI(receiverTx.getOutgoingAmount()) + " - " + (sameWallet ? receiverTx.getFee() : new BigInteger("0")).toString() + ")"); + if (await receiver.getBalance() !== receiverBalanceBefore + (receiverTx.getIncomingAmount() === undefined ? BigInt("0") : receiverTx.getIncomingAmount()) - (receiverTx.getOutgoingAmount() === undefined ? BigInt("0") : receiverTx.getOutgoingAmount()) - (sameWallet ? receiverTx.getFee() : BigInt("0"))) { + if (sameAccount) issues.push("WARNING: after sending, receiver balance != balance before + incoming amount - outgoing amount - tx fee when sent to same account (" + await receiver.getBalance() + " != " + receiverBalanceBefore + " + " + receiverTx.getIncomingAmount() + " - " + receiverTx.getOutgoingAmount() + " - " + (sameWallet ? receiverTx.getFee() : BigInt("0")).toString() + ")"); + else issues.push("ERROR: after sending, receiver balance != balance before + incoming amount - outgoing amount - tx fee (" + await receiver.getBalance() + " != " + receiverBalanceBefore + " + " + receiverTx.getIncomingAmount() + " - " + receiverTx.getOutgoingAmount() + " - " + (sameWallet ? receiverTx.getFee() : BigInt("0")).toString() + ")"); } - if (!sameWallet && (await receiver.getUnlockedBalance()).compare(receiverUnlockedBalanceBefore) !== 0) issues.push("ERROR: receiver unlocked balance should not have changed after sending"); + if (!sameWallet && await receiver.getUnlockedBalance() !== receiverUnlockedBalanceBefore) issues.push("ERROR: receiver unlocked balance should not have changed after sending"); if (receiverNotificationCollector.getBalanceNotifications().length === 0) issues.push("ERROR: receiver did not notify balance change when funds received"); else { - if ((await receiver.getBalance()).compare(receiverNotificationCollector.getBalanceNotifications()[receiverNotificationCollector.getBalanceNotifications().length - 1].balance) !== 0) issues.push("ERROR: receiver balance != last notified balance after funds received"); - if ((await receiver.getUnlockedBalance()).compare(receiverNotificationCollector.getBalanceNotifications()[receiverNotificationCollector.getBalanceNotifications().length - 1].unlockedBalance) !== 0) issues.push("ERROR: receiver unlocked balance != last notified unlocked balance after funds received"); + if (await receiver.getBalance() !== receiverNotificationCollector.getBalanceNotifications()[receiverNotificationCollector.getBalanceNotifications().length - 1].balance) issues.push("ERROR: receiver balance != last notified balance after funds received"); + if (await receiver.getUnlockedBalance() !== receiverNotificationCollector.getBalanceNotifications()[receiverNotificationCollector.getBalanceNotifications().length - 1].unlockedBalance) issues.push("ERROR: receiver unlocked balance != last notified unlocked balance after funds received"); } if (receiverNotificationCollector.getOutputsReceived(outputQuery).length === 0) issues.push("ERROR: receiver did not announce unconfirmed received output"); else { for (let output of getMissingOutputs(expectedOutputs, receiverNotificationCollector.getOutputsReceived(outputQuery), true)) { - issues.push("ERROR: receiver did not announce received output for amount " + toStringBI(output.getAmount()) + " to subaddress [" + output.getAccountIndex() + ", " + output.getSubaddressIndex() + "]"); + issues.push("ERROR: receiver did not announce received output for amount " + output.getAmount() + " to subaddress [" + output.getAccountIndex() + ", " + output.getSubaddressIndex() + "]"); } } @@ -2730,9 +2752,9 @@ class TestMoneroWalletCommon { await StartMining.startMining(); // loop every sync period until unlock tested - let threads = []; + let threads: any = []; let expectedUnlockTime = lastHeight + unlockDelay; - let confirmHeight = undefined; + let confirmHeight: number | undefined = undefined; while (true) { // test height notifications @@ -2759,13 +2781,13 @@ class TestMoneroWalletCommon { let tx = await receiver.getTx(senderTx.getHash()); // break if tx fails - if (tx.isFailed()) { + if (tx.getIsFailed()) { issues.push("ERROR: tx failed in tx pool"); break; } // test confirm notifications - if (tx.isConfirmed() && confirmHeight === undefined) { + if (tx.getIsConfirmed() && confirmHeight === undefined) { confirmHeight = tx.getHeight(); expectedUnlockTime = Math.max(confirmHeight + NUM_BLOCKS_LOCKED, expectedUnlockTime); // exact unlock time known let threadFn = async function() { @@ -2777,10 +2799,10 @@ class TestMoneroWalletCommon { // if same wallet, net amount spent = tx fee = outputs spent - outputs received if (sameWallet) { - let netAmount = new BigInteger("0"); - for (let outputSpent of senderNotificationCollector.getOutputsSpent(confirmedQuery)) netAmount = netAmount.add(outputSpent.getAmount()); - for (let outputReceived of senderNotificationCollector.getOutputsReceived(confirmedQuery)) netAmount = netAmount.subtract(outputReceived.getAmount()); - if (tx.getFee().compare(netAmount) !== 0) { + let netAmount = BigInt("0"); + for (let outputSpent of senderNotificationCollector.getOutputsSpent(confirmedQuery)) netAmount = netAmount + (outputSpent.getAmount()); + for (let outputReceived of senderNotificationCollector.getOutputsReceived(confirmedQuery)) netAmount = netAmount - (outputReceived.getAmount()); + if (tx.getFee() !== netAmount) { if (sameAccount) issues.push("WARNING: net output amount != tx fee when funds sent to same account: " + netAmount + " vs " + tx.getFee()); else if (sender instanceof MoneroWalletRpc) issues.push("WARNING: net output amount != tx fee when funds sent to same wallet because monero-wallet-rpc does not provide tx inputs: " + netAmount + " vs " + tx.getFee()); // TODO (monero-project): open issue to provide tx inputs else issues.push("ERROR: net output amount must equal tx fee when funds sent to same wallet: " + netAmount + " vs " + tx.getFee()); @@ -2798,16 +2820,16 @@ class TestMoneroWalletCommon { let unlockedQuery = outputQuery.getTxQuery().copy().setIsLocked(false).getOutputQuery(); if (senderNotificationCollector.getOutputsSpent(unlockedQuery).length === 0) issues.push("ERROR: sender did not announce unlocked spent output"); // TODO: test amount? for (let output of getMissingOutputs(expectedOutputs, receiverNotificationCollector.getOutputsReceived(unlockedQuery), true)) issues.push("ERROR: receiver did not announce unlocked received output for amount " + output.getAmount() + " to subaddress [" + output.getAccountIndex() + ", " + output.getSubaddressIndex() + "]"); - if (!sameWallet && (await receiver.getBalance()).compare(await receiver.getUnlockedBalance()) !== 0) issues.push("ERROR: receiver balance != unlocked balance after funds unlocked"); + if (!sameWallet && await receiver.getBalance() !== await receiver.getUnlockedBalance()) issues.push("ERROR: receiver balance != unlocked balance after funds unlocked"); if (senderNotificationCollector.getBalanceNotifications().length === 0) issues.push("ERROR: sender did not announce any balance notifications"); else { - if ((await sender.getBalance()).compare(senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1].balance) !== 0) issues.push("ERROR: sender balance != last notified balance after funds unlocked"); - if ((await sender.getUnlockedBalance()).compare(senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1].unlockedBalance) !== 0) issues.push("ERROR: sender unlocked balance != last notified unlocked balance after funds unlocked"); + if (await sender.getBalance() !== senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1].balance) issues.push("ERROR: sender balance != last notified balance after funds unlocked"); + if (await sender.getUnlockedBalance() !== senderNotificationCollector.getBalanceNotifications()[senderNotificationCollector.getBalanceNotifications().length - 1].unlockedBalance) issues.push("ERROR: sender unlocked balance != last notified unlocked balance after funds unlocked"); } if (receiverNotificationCollector.getBalanceNotifications().length === 0) issues.push("ERROR: receiver did not announce any balance notifications"); else { - if ((await receiver.getBalance()).compare(receiverNotificationCollector.getBalanceNotifications()[receiverNotificationCollector.getBalanceNotifications().length - 1].balance) !== 0) issues.push("ERROR: receiver balance != last notified balance after funds unlocked"); - if ((await receiver.getUnlockedBalance()).compare(receiverNotificationCollector.getBalanceNotifications()[receiverNotificationCollector.getBalanceNotifications().length - 1].unlockedBalance) !== 0) issues.push("ERROR: receiver unlocked balance != last notified unlocked balance after funds unlocked"); + if (await receiver.getBalance() !== receiverNotificationCollector.getBalanceNotifications()[receiverNotificationCollector.getBalanceNotifications().length - 1].balance) issues.push("ERROR: receiver balance != last notified balance after funds unlocked"); + if (await receiver.getUnlockedBalance() !== receiverNotificationCollector.getBalanceNotifications()[receiverNotificationCollector.getBalanceNotifications().length - 1].unlockedBalance) issues.push("ERROR: receiver unlocked balance != last notified unlocked balance after funds unlocked"); } } threads.push(threadFn()); @@ -2829,7 +2851,7 @@ class TestMoneroWalletCommon { for (let output of receiverNotificationCollector.getOutputsReceived(outputQuery)) testNotifiedOutput(output, false, issues); // clean up - if ((await that.daemon.getMiningStatus()).isActive()) await that.daemon.stopMining(); + if ((await that.daemon.getMiningStatus()).getIsActive()) await that.daemon.stopMining(); await sender.removeListener(senderNotificationCollector); senderNotificationCollector.setListening(false); await receiver.removeListener(receiverNotificationCollector); @@ -2838,14 +2860,14 @@ class TestMoneroWalletCommon { return issues; } - function getMissingOutputs(expectedOutputs, actualOutputs, matchSubaddress) { - let missing = []; - let used = []; + function getMissingOutputs(expectedOutputs, actualOutputs, matchSubaddress): any[] { + let missing: any[] = []; + let used: any[] = []; for (let expectedOutput of expectedOutputs) { let found = false; for (let actualOutput of actualOutputs) { if (GenUtils.arrayContains(used, actualOutput, true)) continue; - if (actualOutput.getAmount().compare(expectedOutput.getAmount()) === 0 && (!matchSubaddress || (actualOutput.getAccountIndex() === expectedOutput.getAccountIndex() && actualOutput.getSubaddressIndex() === expectedOutput.getSubaddressIndex()))) { + if (actualOutput.getAmount() === expectedOutput.getAmount() && (!matchSubaddress || (actualOutput.getAccountIndex() === expectedOutput.getAccountIndex() && actualOutput.getSubaddressIndex() === expectedOutput.getSubaddressIndex()))) { used.push(actualOutput); found = true; break; @@ -2874,7 +2896,7 @@ class TestMoneroWalletCommon { else assert(output.getTx().getOutputs().includes(output)); // test output values - TestUtils.testUnsignedBigInteger(output.getAmount()); + TestUtils.testUnsignedBigInt(output.getAmount()); if (output.getAccountIndex() !== undefined) assert(output.getAccountIndex() >= 0); else { if (isTxInput) issues.push("WARNING: notification of " + getOutputState(output) + " spent output missing account index"); // TODO (monero-project): account index not provided when output swept by key image. could retrieve it but slows tx creation significantly @@ -2888,20 +2910,16 @@ class TestMoneroWalletCommon { } function getOutputState(output) { - if (false === output.getTx().isLocked()) return "unlocked"; - if (true === output.getTx().isConfirmed()) return "confirmed"; - if (false === output.getTx().isConfirmed()) return "unconfirmed"; + if (false === output.getTx().getIsLocked()) return "unlocked"; + if (true === output.getTx().getIsConfirmed()) return "confirmed"; + if (false === output.getTx().getIsConfirmed()) return "unconfirmed"; throw new Error("Unknown output state: " + output.toString()); } - function toStringBI(bi) { - return bi ? bi.toString() : bi + ""; - } - it("Can stop listening", async function() { // create offline wallet - let wallet = await that.createWallet(new MoneroWalletConfig().setServerUri(TestUtils.OFFLINE_SERVER_URI)); + let wallet = await that.createWallet(new MoneroWalletConfig().setServer(TestUtils.OFFLINE_SERVER_URI)); // add listener let listener = new WalletNotificationCollector(); @@ -2923,7 +2941,7 @@ class TestMoneroWalletCommon { try { // listen for received outputs - let myListener = new WalletNotificationCollector(receiver); + let myListener = new WalletNotificationCollector(); await receiver.addListener(myListener); // wait for txs to confirm and for sufficient unlocked balance @@ -2934,47 +2952,47 @@ class TestMoneroWalletCommon { let sentTx = await that.wallet.createTx({accountIndex: 0, address: await receiver.getPrimaryAddress(), amount: TestUtils.MAX_FEE, relay: true}); // wait for funds to confirm - try { await StartMining.startMining(); } catch (e) { } - while (!(await that.wallet.getTx(sentTx.getHash())).isConfirmed()) { - if ((await that.wallet.getTx(sentTx.getHash())).isFailed()) throw new Error("Tx failed in mempool: " + sentTx.getHash()); + try { await StartMining.startMining(); } catch (e: any) { } + while (!(await that.wallet.getTx(sentTx.getHash())).getIsConfirmed()) { + if ((await that.wallet.getTx(sentTx.getHash())).getIsFailed()) throw new Error("Tx failed in mempool: " + sentTx.getHash()); await that.daemon.waitForNextBlockHeader(); } // receiver should have notified listeners of received outputs await new Promise(function(resolve) { setTimeout(resolve, 1000); }); // TODO: this lets block slip, okay? assert(myListener.getOutputsReceived().length > 0, "Listener did not receive outputs"); - } catch (e) { + } catch (e: any) { err = e; } // final cleanup await that.closeWallet(receiver); - try { await that.daemon.stopMining(); } catch (e) { } + try { await that.daemon.stopMining(); } catch (e: any) { } if (err) throw err; }); // TODO: test sending to multiple accounts if (testConfig.testRelays && testConfig.testNotifications) it("Can update a locked tx sent from/to the same account as blocks are added to the chain", async function() { - let config = new MoneroTxConfig({accountIndex: 0, address: await that.wallet.getPrimaryAddress(), amount: TestUtils.MAX_FEE, unlockTime: await that.daemon.getHeight() + 3, canSplit: false, relay: true}); + let config = new MoneroTxConfig({accountIndex: 0, address: await that.wallet.getPrimaryAddress(), amount: TestUtils.MAX_FEE, unlockTime: BigInt(await that.daemon.getHeight() + 3), canSplit: false, relay: true}); await testSendAndUpdateTxs(config); }); if (testConfig.testRelays && testConfig.testNotifications && !testConfig.liteMode) it("Can update split locked txs sent from/to the same account as blocks are added to the chain", async function() { - let config = new MoneroTxConfig({accountIndex: 0, address: await that.wallet.getPrimaryAddress(), amount: TestUtils.MAX_FEE, unlockTime: await that.daemon.getHeight() + 3, canSplit: true, relay: true}); + let config = new MoneroTxConfig({accountIndex: 0, address: await that.wallet.getPrimaryAddress(), amount: TestUtils.MAX_FEE, unlockTime: BigInt(await that.daemon.getHeight() + 3), canSplit: true, relay: true}); await testSendAndUpdateTxs(config); }); if (testConfig.testRelays && testConfig.testNotifications && !testConfig.liteMode) it("Can update a locked tx sent from/to different accounts as blocks are added to the chain", async function() { - let config = new MoneroTxConfig({accountIndex: 0, address: (await that.wallet.getSubaddress(1, 0)).getAddress(), amount: TestUtils.MAX_FEE, unlockTime: await that.daemon.getHeight() + 3, canSplit: false, relay: true}); + let config = new MoneroTxConfig({accountIndex: 0, address: (await that.wallet.getSubaddress(1, 0)).getAddress(), amount: TestUtils.MAX_FEE, unlockTime: BigInt(await that.daemon.getHeight() + 3), canSplit: false, relay: true}); await testSendAndUpdateTxs(config); }); if (testConfig.testRelays && testConfig.testNotifications && !testConfig.liteMode) it("Can update locked, split txs sent from/to different accounts as blocks are added to the chain", async function() { - let config = new MoneroTxConfig({accountIndex: 0, address: (await that.wallet.getSubaddress(1, 0)).getAddress(), amount: TestUtils.MAX_FEE, unlockTime: await that.daemon.getHeight() + 3, relay: true}); + let config = new MoneroTxConfig({accountIndex: 0, address: (await that.wallet.getSubaddress(1, 0)).getAddress(), amount: TestUtils.MAX_FEE, unlockTime: BigInt(await that.daemon.getHeight() + 3), relay: true}); await testSendAndUpdateTxs(config); }); @@ -2997,7 +3015,7 @@ class TestMoneroWalletCommon { // wait for txs to confirm and for sufficient unlocked balance await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(that.wallet); assert(!config.getSubaddressIndices()); - await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(that.wallet, config.getAccountIndex(), undefined, TestUtils.MAX_FEE.multiply(BigInteger.parse("2"))); + await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(that.wallet, config.getAccountIndex(), undefined, TestUtils.MAX_FEE * (BigInt("2"))); // this test starts and stops mining, so it's wrapped in order to stop mining if anything fails let err; @@ -3008,9 +3026,9 @@ class TestMoneroWalletCommon { // test sent transactions for (let tx of sentTxs) { - await that._testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true}); - assert.equal(tx.isConfirmed(), false); - assert.equal(tx.inTxPool(), true); + await that.testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true}); + assert.equal(tx.getIsConfirmed(), false); + assert.equal(tx.getInTxPool(), true); } // track resulting outgoing and incoming txs as blocks are added to the chain @@ -3018,7 +3036,7 @@ class TestMoneroWalletCommon { // start mining try { await StartMining.startMining(); } - catch (e) { console.log("WARNING: could not start mining: " + e.message); } // not fatal + catch (e: any) { console.log("WARNING: could not start mining: " + e.message); } // not fatal // loop to update txs through confirmations let numConfirmations = 0; @@ -3035,7 +3053,7 @@ class TestMoneroWalletCommon { // get incoming/outgoing txs with sent hashes let txQuery = new MoneroTxQuery(); txQuery.setHashes(sentTxs.map(sentTx => sentTx.getHash())); // TODO: convenience methods wallet.getTxById(), getTxsById()? - let fetchedTxs = await that._getAndTestTxs(that.wallet, txQuery, true); + let fetchedTxs = await that.getAndTestTxs(that.wallet, txQuery, true); assert(fetchedTxs.length > 0); // test fetched txs @@ -3070,13 +3088,13 @@ class TestMoneroWalletCommon { // update confirmations in order to exit loop numConfirmations = fetchedTxs[0].getNumConfirmations(); } - } catch (e) { + } catch (e: any) { err = e; } // stop mining try { await that.wallet.stopMining(); } - catch (e) { } + catch (e: any) { } // throw error if there was one if (err) throw err; @@ -3111,14 +3129,14 @@ class TestMoneroWalletCommon { } async function testOutInPair(txOut, txIn) { - assert.equal(txIn.isConfirmed(), txOut.isConfirmed()); - assert.equal(txOut.getOutgoingAmount().compare(txIn.getIncomingAmount()), 0); + assert.equal(txIn.getIsConfirmed(), txOut.getIsConfirmed()); + assert.equal(txOut.getOutgoingAmount(), txIn.getIncomingAmount()); } async function testUnlockTx(wallet, tx, config, isSendResponse) { try { - await that._testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: isSendResponse}); - } catch (e) { + await that.testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: isSendResponse}); + } catch (e: any) { console.log(tx.toString()); throw e; } @@ -3133,25 +3151,9 @@ class TestMoneroWalletCommon { try { await that.wallet.createTx({address: "my invalid address", accountIndex: 0, amount: TestUtils.MAX_FEE}); throw new Error("fail"); - } catch (err) { + } catch (err: any) { assert.equal(err.message, "Invalid destination address"); } - - // try sending with invalid amount - try { - await that.wallet.createTx({address: await that.wallet.getPrimaryAddress(), accountIndex: 0, amount: "my invalid amount"}); - throw new Error("fail"); - } catch (err) { - assert.equal(err.message, "Invalid destination amount: my invalid amount"); - } - - // try sending with js number - try { - await that.wallet.createTx({address: await that.wallet.getPrimaryAddress(), accountIndex: 0, amount: 12345}); - throw new Error("fail"); - } catch (err) { - assert.equal(err.message, "Destination amount must be BigInteger or string"); - } }); if (testConfig.testRelays) @@ -3162,7 +3164,7 @@ class TestMoneroWalletCommon { // wait for txs to confirm and for sufficient unlocked balance await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(that.wallet); - let amount = TestUtils.MAX_FEE.multiply(BigInteger.parse("3")); + let amount = TestUtils.MAX_FEE * (BigInt("3")); await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(that.wallet, 0, undefined, amount); // collect sender balances before @@ -3180,10 +3182,10 @@ class TestMoneroWalletCommon { // test balances after let balance2 = await that.wallet.getBalance(); let unlockedBalance2 = await that.wallet.getUnlockedBalance(); - assert(unlockedBalance2.compare(unlockedBalance1) < 0); // unlocked balance should decrease - let expectedBalance = balance1.subtract(tx.getFee()); + assert(unlockedBalance2 < unlockedBalance1); // unlocked balance should decrease + let expectedBalance = balance1 - (tx.getFee()); assert.equal(expectedBalance.toString(), balance2.toString(), "Balance after send was not balance before - net tx amount - fee (5 - 1 != 4 test)"); - } catch (e) { + } catch (e: any) { err = e; } @@ -3200,7 +3202,7 @@ class TestMoneroWalletCommon { // wait for txs to confirm and for sufficient unlocked balance await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(that.wallet); - let amount = TestUtils.MAX_FEE.multiply(BigInteger.parse("3")); + let amount = TestUtils.MAX_FEE * (BigInt("3")); await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(that.wallet, 0, undefined, amount); // create recipient wallet @@ -3221,15 +3223,15 @@ class TestMoneroWalletCommon { // test sender balances after let balance2 = await that.wallet.getBalance(); let unlockedBalance2 = await that.wallet.getUnlockedBalance(); - assert(unlockedBalance2.compare(unlockedBalance1) < 0); // unlocked balance should decrease - let expectedBalance = balance1.subtract(tx.getOutgoingAmount()).subtract(tx.getFee()); + assert(unlockedBalance2 < unlockedBalance1); // unlocked balance should decrease + let expectedBalance = balance1 - (tx.getOutgoingAmount()) - (tx.getFee()); assert.equal(expectedBalance.toString(), balance2.toString(), "Balance after send was not balance before - net tx amount - fee (5 - 1 != 4 test)"); // test recipient balance after await recipient.sync(); assert((await that.wallet.getTxs({isConfirmed: false})).length > 0); assert.equal(amount.toString(), (await recipient.getBalance()).toString()); - } catch (e) { + } catch (e: any) { err = e; } @@ -3248,7 +3250,7 @@ class TestMoneroWalletCommon { await testSendFromMultiple(new MoneroTxConfig().setCanSplit(true)); }); - async function testSendFromMultiple(config) { + async function testSendFromMultiple(config?) { await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(that.wallet); if (!config) config = new MoneroTxConfig(); @@ -3258,14 +3260,14 @@ class TestMoneroWalletCommon { let accounts = await that.wallet.getAccounts(true); assert(accounts.length >= 2, "This test requires at least 2 accounts; run send-to-multiple tests"); let srcAccount; - let unlockedSubaddresses = []; + let unlockedSubaddresses: any[] = []; let hasBalance = false; for (let account of accounts) { unlockedSubaddresses = []; let numSubaddressBalances = 0; for (let subaddress of account.getSubaddresses()) { - if (subaddress.getBalance().compare(TestUtils.MAX_FEE) > 0) numSubaddressBalances++; - if (subaddress.getUnlockedBalance().compare(TestUtils.MAX_FEE) > 0) unlockedSubaddresses.push(subaddress); + if (subaddress.getBalance() > TestUtils.MAX_FEE) numSubaddressBalances++; + if (subaddress.getUnlockedBalance() > TestUtils.MAX_FEE) unlockedSubaddresses.push(subaddress); } if (numSubaddressBalances >= NUM_SUBADDRESSES + 1) hasBalance = true; if (unlockedSubaddresses.length >= NUM_SUBADDRESSES + 1) { @@ -3277,17 +3279,17 @@ class TestMoneroWalletCommon { assert(unlockedSubaddresses.length >= NUM_SUBADDRESSES + 1, "Wallet is waiting on unlocked funds"); // determine the indices of the first two subaddresses with unlocked balances - let fromSubaddressIndices = []; + let fromSubaddressIndices: any[] = []; for (let i = 0; i < NUM_SUBADDRESSES; i++) { fromSubaddressIndices.push(unlockedSubaddresses[i].getIndex()); } // determine the amount to send - let sendAmount = new BigInteger(0); + let sendAmount = BigInt(0); for (let fromSubaddressIdx of fromSubaddressIndices) { - sendAmount = sendAmount.add(srcAccount.getSubaddresses()[fromSubaddressIdx].getUnlockedBalance()); + sendAmount = sendAmount + (srcAccount.getSubaddresses()[fromSubaddressIdx].getUnlockedBalance()); } - sendAmount = sendAmount.divide(new BigInteger(SEND_DIVISOR)) + sendAmount = sendAmount / SEND_DIVISOR; // send from the first subaddresses with unlocked balances let address = await that.wallet.getPrimaryAddress(); @@ -3296,7 +3298,7 @@ class TestMoneroWalletCommon { config.setSubaddressIndices(fromSubaddressIndices); config.setRelay(true); let configCopy = config.copy(); - let txs = []; + let txs: MoneroTxWallet[] = []; if (config.getCanSplit() !== false) { for (let tx of await that.wallet.createTxs(config)) txs.push(tx); } else { @@ -3318,9 +3320,9 @@ class TestMoneroWalletCommon { let subaddressBefore = accounts[i].getSubaddresses()[j]; let subaddressAfter = accountsAfter[i].getSubaddresses()[j]; if (i === srcAccount.getIndex() && fromSubaddressIndices.includes(j)) { - if (subaddressAfter.getUnlockedBalance().compare(subaddressBefore.getUnlockedBalance()) < 0) srcUnlockedBalanceDecreased = true; + if (subaddressAfter.getUnlockedBalance() < subaddressBefore.getUnlockedBalance()) srcUnlockedBalanceDecreased = true; } else { - assert(subaddressAfter.getUnlockedBalance().compare(subaddressBefore.getUnlockedBalance()) === 0, "Subaddress [" + i + "," + j + "] unlocked balance should not have changed"); + assert.equal(subaddressAfter.getUnlockedBalance(), subaddressBefore.getUnlockedBalance(), "Subaddress [" + i + "," + j + "] unlocked balance should not have changed"); } } } @@ -3328,24 +3330,24 @@ class TestMoneroWalletCommon { // test each transaction assert(txs.length > 0); - let outgoingSum = new BigInteger(0); + let outgoingSum = BigInt(0); for (let tx of txs) { - await that._testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true}); - outgoingSum = outgoingSum.add(tx.getOutgoingAmount()); + await that.testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true}); + outgoingSum = outgoingSum + (tx.getOutgoingAmount()); if (tx.getOutgoingTransfer() !== undefined && tx.getOutgoingTransfer().getDestinations()) { - let destinationSum = new BigInteger(0); + let destinationSum = BigInt(0); for (let destination of tx.getOutgoingTransfer().getDestinations()) { await testDestination(destination); assert.equal(destination.getAddress(), address); - destinationSum = destinationSum.add(destination.getAmount()); + destinationSum = destinationSum + (destination.getAmount()); } - assert(tx.getOutgoingAmount().compare(destinationSum) === 0); // assert that transfers sum up to tx amount + assert.equal(tx.getOutgoingAmount(), destinationSum); // assert that transfers sum up to tx amount } } // assert that tx amounts sum up the amount sent within a small margin - if (Math.abs(sendAmount.subtract(outgoingSum).toJSValue()) > SEND_MAX_DIFF) { // send amounts may be slightly different - throw new Error("Tx amounts are too different: " + sendAmount + " - " + outgoingSum + " = " + sendAmount.subtract(outgoingSum)); + if (GenUtils.abs(sendAmount - outgoingSum) > SEND_MAX_DIFF) { // send amounts may be slightly different + throw new Error("Tx amounts are too different: " + sendAmount + " - " + outgoingSum + " = " + (sendAmount - outgoingSum)); } } @@ -3362,7 +3364,7 @@ class TestMoneroWalletCommon { try { await testSendToSingle(new MoneroTxConfig().setCanSplit(false).setPaymentId(paymentId + paymentId + paymentId + paymentId)); // 64 character payment id throw new Error("fail"); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Standalone payment IDs are obsolete. Use subaddresses or integrated addresses instead"); } }); @@ -3388,14 +3390,14 @@ class TestMoneroWalletCommon { // find a non-primary subaddress to send from let sufficientBalance = false; - let fromAccount = undefined; - let fromSubaddress = undefined; + let fromAccount: MoneroAccount | undefined = undefined; + let fromSubaddress: MoneroSubaddress | undefined = undefined; let accounts = await that.wallet.getAccounts(true); for (let account of accounts) { let subaddresses = account.getSubaddresses(); for (let i = 1; i < subaddresses.length; i++) { - if (subaddresses[i].getBalance().compare(TestUtils.MAX_FEE) > 0) sufficientBalance = true; - if (subaddresses[i].getUnlockedBalance().compare(TestUtils.MAX_FEE) > 0) { + if (subaddresses[i].getBalance() > TestUtils.MAX_FEE) sufficientBalance = true; + if (subaddresses[i].getUnlockedBalance() > TestUtils.MAX_FEE) { fromAccount = account; fromSubaddress = subaddresses[i]; break; @@ -3411,15 +3413,15 @@ class TestMoneroWalletCommon { let unlockedBalanceBefore = fromSubaddress.getUnlockedBalance(); // init tx config - let sendAmount = unlockedBalanceBefore.subtract(TestUtils.MAX_FEE).divide(new BigInteger(SEND_DIVISOR)); + let sendAmount = (unlockedBalanceBefore - TestUtils.MAX_FEE) / SEND_DIVISOR; let address = await that.wallet.getPrimaryAddress(); config.setDestinations([new MoneroDestination(address, sendAmount)]); - config.setAccountIndex(fromAccount.getIndex()); + config.setAccountIndex(fromAccount!.getIndex()); config.setSubaddressIndices([fromSubaddress.getIndex()]); let reqCopy = config.copy(); // send to self - let txs = [] + let txs: any[] = [] if (config.getCanSplit() !== false) { for (let tx of await that.wallet.createTxs(config)) txs.push(tx); } else { @@ -3439,7 +3441,7 @@ class TestMoneroWalletCommon { // test transactions for (let tx of txs) { - await that._testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true}); + await that.testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true}); } // txs are not in the pool @@ -3453,7 +3455,7 @@ class TestMoneroWalletCommon { let txHashes; if (config.getCanSplit() !== true) txHashes = [await that.wallet.relayTx(txs[0])]; // test relayTx() with single transaction else { - let txMetadatas = []; + let txMetadatas: any[] = []; for (let tx of txs) txMetadatas.push(tx.getMetadata()); txHashes = await that.wallet.relayTxs(txMetadatas); // test relayTxs() with potentially multiple transactions } @@ -3465,22 +3467,22 @@ class TestMoneroWalletCommon { // test that balance and unlocked balance decreased // TODO: test that other balances did not decrease - let subaddress = await that.wallet.getSubaddress(fromAccount.getIndex(), fromSubaddress.getIndex()); - assert(subaddress.getBalance().compare(balanceBefore) < 0); - assert(subaddress.getUnlockedBalance().compare(unlockedBalanceBefore) < 0); + let subaddress = await that.wallet.getSubaddress(fromAccount!.getIndex(), fromSubaddress.getIndex()); + assert(subaddress.getBalance() < balanceBefore); + assert(subaddress.getUnlockedBalance() < unlockedBalanceBefore); // query locked txs - let lockedTxs = await that._getAndTestTxs(that.wallet, new MoneroTxQuery().setIsLocked(true), undefined, true); - for (let lockedTx of lockedTxs) assert.equal(lockedTx.isLocked(), true); + let lockedTxs = await that.getAndTestTxs(that.wallet, new MoneroTxQuery().setIsLocked(true), true); + for (let lockedTx of lockedTxs) assert.equal(lockedTx.getIsLocked(), true); // test transactions assert(txs.length > 0); for (let tx of txs) { - await that._testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: config.getRelay() === true}); - assert.equal(tx.getOutgoingTransfer().getAccountIndex(), fromAccount.getIndex()); + await that.testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: config.getRelay() === true}); + assert.equal(tx.getOutgoingTransfer().getAccountIndex(), fromAccount!.getIndex()); assert.equal(tx.getOutgoingTransfer().getSubaddressIndices().length, 1); assert.equal(tx.getOutgoingTransfer().getSubaddressIndices()[0], fromSubaddress.getIndex()); - assert(sendAmount.compare(tx.getOutgoingAmount()) === 0); + assert.equal(sendAmount, tx.getOutgoingAmount()); if (config.getPaymentId()) assert.equal(config.getPaymentId(), tx.getPaymentId()); // test outgoing destinations @@ -3489,7 +3491,7 @@ class TestMoneroWalletCommon { for (let destination of tx.getOutgoingTransfer().getDestinations()) { await testDestination(destination); assert.equal(destination.getAddress(), address); - assert(sendAmount.compare(destination.getAmount()) === 0); + assert.equal(sendAmount, destination.getAmount()); } } @@ -3522,7 +3524,7 @@ class TestMoneroWalletCommon { if (testConfig.testRelays) it("Can send dust to multiple addresses in split transactions", async function() { - let dustAmt = (await that.daemon.getFeeEstimate()).getFee().divide(new BigInteger(2)); + let dustAmt = (await that.daemon.getFeeEstimate()).getFee() / BigInt(2); await testSendToMultiple(5, 3, true, dustAmt); }); @@ -3546,21 +3548,21 @@ class TestMoneroWalletCommon { * @param useJsConfig specifies if the api should be invoked with a JS object instead of a MoneroTxConfig * @param subtractFeeFromDestinations specifies to subtract the fee from destination addresses */ - async function testSendToMultiple(numAccounts, numSubaddressesPerAccount, canSplit, sendAmountPerSubaddress, useJsConfig, subtractFeeFromDestinations) { + async function testSendToMultiple(numAccounts, numSubaddressesPerAccount, canSplit, sendAmountPerSubaddress?, useJsConfig?, subtractFeeFromDestinations?) { await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(that.wallet); // compute the minimum account unlocked balance needed in order to fulfill the request let minAccountAmount; let totalSubaddresses = numAccounts * numSubaddressesPerAccount; - if (sendAmountPerSubaddress !== undefined) minAccountAmount = new BigInteger(totalSubaddresses).multiply(sendAmountPerSubaddress.add(TestUtils.MAX_FEE)); // min account amount must cover the total amount being sent plus the tx fee = numAddresses * (amtPerSubaddress + fee) - else minAccountAmount = TestUtils.MAX_FEE.multiply(new BigInteger(totalSubaddresses)).multiply(new BigInteger(SEND_DIVISOR)).add(TestUtils.MAX_FEE); // account balance must be more than fee * numAddresses * divisor + fee so each destination amount is at least a fee's worth (so dust is not sent) + if (sendAmountPerSubaddress !== undefined) minAccountAmount = BigInt(totalSubaddresses) * sendAmountPerSubaddress + TestUtils.MAX_FEE; // min account amount must cover the total amount being sent plus the tx fee = numAddresses * (amtPerSubaddress + fee) + else minAccountAmount = TestUtils.MAX_FEE * BigInt(totalSubaddresses) * SEND_DIVISOR + TestUtils.MAX_FEE; // account balance must be more than fee * numAddresses * divisor + fee so each destination amount is at least a fee's worth (so dust is not sent) // send funds from first account with sufficient unlocked funds let srcAccount; let hasBalance = false; for (let account of await that.wallet.getAccounts()) { - if (account.getBalance().compare(minAccountAmount) > 0) hasBalance = true; - if (account.getUnlockedBalance().compare(minAccountAmount) > 0) { + if (account.getBalance() > minAccountAmount) hasBalance = true; + if (account.getUnlockedBalance() > minAccountAmount) { srcAccount = account; break; } @@ -3573,10 +3575,10 @@ class TestMoneroWalletCommon { // get amount to send total and per subaddress let sendAmount; if (sendAmountPerSubaddress === undefined) { - sendAmount = TestUtils.MAX_FEE.multiply(new BigInteger("5")).multiply(new BigInteger(totalSubaddresses)); - sendAmountPerSubaddress = sendAmount.divide(new BigInteger(totalSubaddresses)); + sendAmount = TestUtils.MAX_FEE * BigInt("5") * BigInt(totalSubaddresses); + sendAmountPerSubaddress = sendAmount / BigInt(totalSubaddresses); } else { - sendAmount = sendAmountPerSubaddress.multiply(new BigInteger(totalSubaddresses)); + sendAmount = sendAmountPerSubaddress * (BigInt(totalSubaddresses)); } // create minimum number of accounts @@ -3586,7 +3588,7 @@ class TestMoneroWalletCommon { } // create minimum number of subaddresses per account and collect destination addresses - let destinationAddresses = []; + let destinationAddresses: any = []; for (let i = 0; i < numAccounts; i++) { let subaddresses = await that.wallet.getSubaddresses(i); for (let j = 0; j < numSubaddressesPerAccount - subaddresses.length; j++) await that.wallet.createSubaddress(i); @@ -3598,12 +3600,12 @@ class TestMoneroWalletCommon { // build tx config using MoneroTxConfig let config = new MoneroTxConfig(); config.setAccountIndex(srcAccount.getIndex()) - config.setSubaddressIndices(undefined); // test assigning undefined + config.setSubaddressIndices([]); // test assigning undefined config.setDestinations([]); config.setRelay(true); config.setCanSplit(canSplit); config.setPriority(MoneroTxPriority.NORMAL); - let subtractFeeFrom = []; + let subtractFeeFrom: number[] = []; for (let i = 0; i < destinationAddresses.length; i++) { config.getDestinations().push(new MoneroDestination(destinationAddresses[i], sendAmountPerSubaddress)); subtractFeeFrom.push(i); @@ -3625,14 +3627,14 @@ class TestMoneroWalletCommon { // send tx(s) with config xor js object let configCopy = config.copy(); - let txs = undefined; + let txs: MoneroTxWallet[] = undefined; try { if (canSplit) { txs = await that.wallet.createTxs(useJsConfig ? jsConfig : config); } else { txs = [await that.wallet.createTx(useJsConfig ? jsConfig : config)]; } - } catch (err) { + } catch (err: any) { // test error applying subtractFromFee with split txs if (subtractFeeFromDestinations && !txs) { @@ -3649,38 +3651,38 @@ class TestMoneroWalletCommon { // test that wallet balance decreased let account = await that.wallet.getAccount(srcAccount.getIndex()); - assert(account.getBalance().compare(balance) < 0); - assert(account.getUnlockedBalance().compare(unlockedBalance) < 0); + assert(account.getBalance() < balance); + assert(account.getUnlockedBalance() < unlockedBalance); // build test context config.setCanSplit(canSplit); - let ctx = {}; + let ctx: any = {}; ctx.wallet = that.wallet; ctx.config = config; ctx.isSendResponse = true; // test each transaction assert(txs.length > 0); - let feeSum = new BigInteger(0); - let outgoingSum = new BigInteger(0); - await that._testTxsWallet(txs, ctx); + let feeSum = BigInt(0); + let outgoingSum = BigInt(0); + await that.testTxsWallet(txs, ctx); for (let tx of txs) { - feeSum = feeSum.add(tx.getFee()); - outgoingSum = outgoingSum.add(tx.getOutgoingAmount()); + feeSum = feeSum + tx.getFee(); + outgoingSum = outgoingSum + tx.getOutgoingAmount(); if (tx.getOutgoingTransfer() !== undefined && tx.getOutgoingTransfer().getDestinations()) { - let destinationSum = new BigInteger(0); + let destinationSum = BigInt(0); for (let destination of tx.getOutgoingTransfer().getDestinations()) { await testDestination(destination); assert(destinationAddresses.includes(destination.getAddress())); - destinationSum = destinationSum.add(destination.getAmount()); + destinationSum = destinationSum + (destination.getAmount()); } - assert(tx.getOutgoingAmount().compare(destinationSum) === 0); // assert that transfers sum up to tx amount + assert.equal(tx.getOutgoingAmount(), destinationSum); // assert that transfers sum up to tx amount } } // assert that outgoing amounts sum up to the amount sent within a small margin - if (Math.abs(sendAmount.subtract(subtractFeeFromDestinations ? feeSum : new BigInteger(0)).subtract(outgoingSum).toJSValue()) > SEND_MAX_DIFF) { // send amounts may be slightly different - throw new Error("Actual send amount is too different from requested send amount: " + sendAmount + " - " + (subtractFeeFromDestinations ? feeSum : new BigInteger(0)) + " - " + outgoingSum + " = " + sendAmount.subtract(subtractFeeFromDestinations ? feeSum : new BigInteger(0)).subtract(outgoingSum)); + if (GenUtils.abs((sendAmount - (subtractFeeFromDestinations ? feeSum : BigInt(0)) - outgoingSum)) > SEND_MAX_DIFF) { // send amounts may be slightly different + throw new Error("Actual send amount is too different from requested send amount: " + sendAmount + " - " + (subtractFeeFromDestinations ? feeSum : BigInt(0)) + " - " + outgoingSum + " = " + sendAmount.subtract(subtractFeeFromDestinations ? feeSum : BigInt(0)).subtract(outgoingSum)); } } @@ -3689,14 +3691,14 @@ class TestMoneroWalletCommon { // create view-only and offline wallets let viewOnlyWallet = await that.createWallet({primaryAddress: await that.wallet.getPrimaryAddress(), privateViewKey: await that.wallet.getPrivateViewKey(), restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT}); - let offlineWallet = await that.createWallet({primaryAddress: await that.wallet.getPrimaryAddress(), privateViewKey: await that.wallet.getPrivateViewKey(), privateSpendKey: await that.wallet.getPrivateSpendKey(), serverUri: TestUtils.OFFLINE_SERVER_URI, restoreHeight: 0}); + let offlineWallet = await that.createWallet({primaryAddress: await that.wallet.getPrimaryAddress(), privateViewKey: await that.wallet.getPrivateViewKey(), privateSpendKey: await that.wallet.getPrivateSpendKey(), server: TestUtils.OFFLINE_SERVER_URI, restoreHeight: 0}); await viewOnlyWallet.sync(); // test tx signing with wallets let err; try { - await that._testViewOnlyAndOfflineWallets(viewOnlyWallet, offlineWallet); - } catch (e) { + await that.testViewOnlyAndOfflineWallets(viewOnlyWallet, offlineWallet); + } catch (e: any) { err = e; } @@ -3715,18 +3717,18 @@ class TestMoneroWalletCommon { // get outputs to sweep (not spent, unlocked, and amount >= fee) let spendableUnlockedOutputs = await that.wallet.getOutputs(new MoneroOutputQuery().setIsSpent(false).setTxQuery(new MoneroTxQuery().setIsLocked(false))); - let outputsToSweep = []; + let outputsToSweep: any[] = []; for (let i = 0; i < spendableUnlockedOutputs.length && outputsToSweep.length < numOutputs; i++) { - if (spendableUnlockedOutputs[i].getAmount().compare(TestUtils.MAX_FEE) > 0) outputsToSweep.push(spendableUnlockedOutputs[i]); // output cannot be swept if amount does not cover fee + if (spendableUnlockedOutputs[i].getAmount() > TestUtils.MAX_FEE) outputsToSweep.push(spendableUnlockedOutputs[i]); // output cannot be swept if amount does not cover fee } assert(outputsToSweep.length >= numOutputs, "Wallet does not have enough sweepable outputs; run send tests"); // sweep each output by key image for (let output of outputsToSweep) { testOutputWallet(output); - assert.equal(output.isSpent(), false); - assert.equal(output.isLocked(), false); - if (output.getAmount().compare(TestUtils.MAX_FEE) <= 0) continue; + assert.equal(output.getIsSpent(), false); + assert.equal(output.getIsLocked(), false); + if (output.getAmount() <= TestUtils.MAX_FEE) continue; // sweep output to address let address = await that.wallet.getAddress(output.getAccountIndex(), output.getSubaddressIndex()); @@ -3735,7 +3737,7 @@ class TestMoneroWalletCommon { // test resulting tx config.setCanSplit(false); - await that._testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true, isSweepResponse: true, isSweepOutputResponse: true}); + await that.testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true, isSweepResponse: true, isSweepOutputResponse: true}); } // get outputs after sweeping @@ -3745,7 +3747,7 @@ class TestMoneroWalletCommon { for (let afterOutput of afterOutputs) { for (let output of outputsToSweep) { if (output.getKeyImage().getHex() === afterOutput.getKeyImage().getHex()) { - assert(afterOutput.isSpent(), "Output should be spent"); + assert(afterOutput.getIsSpent(), "Output should be spent"); } } } @@ -3762,21 +3764,21 @@ class TestMoneroWalletCommon { // test txs let ctx = {config: new MoneroTxConfig(), isSendResponse: true, isSweepResponse: true}; for (let tx of txs) { - await that._testTxWallet(tx, ctx); + await that.testTxWallet(tx, ctx); } // relay txs - let metadatas = []; + let metadatas: any = []; for (let tx of txs) metadatas.push(tx.getMetadata()); let txHashes = await that.wallet.relayTxs(metadatas); assert.equal(txs.length, txHashes.length); for (let txHash of txHashes) assert.equal(txHash.length, 64); // fetch and test txs - txs = wallet.getTxs(new MoneroTxQuery().setHashes(txHashes)); + txs = await that.wallet.getTxs(new MoneroTxQuery().setHashes(txHashes)); ctx.config.setRelay(true); for (let tx of txs) { - await that._testTxWallet(tx, ctx); + await that.testTxWallet(tx, ctx); } }); @@ -3790,14 +3792,14 @@ class TestMoneroWalletCommon { // test any txs let ctx = {wallet: that.wallet, isSendResponse: true, isSweepResponse: true}; for (let tx of txs) { - await that._testTxWallet(tx, ctx); + await that.testTxWallet(tx, ctx); } }); it("Supports multisig wallets", async function() { - await that._testMultisig(2, 2, false); // n/n - await that._testMultisig(2, 3, false); // (n-1)/n - await that._testMultisig(2, 4, testConfig.testRelays && !testConfig.liteMode); // m/n + await that.testMultisig(2, 2, false); // n/n + await that.testMultisig(2, 3, false); // (n-1)/n + await that.testMultisig(2, 4, testConfig.testRelays && !testConfig.liteMode); // m/n }); // ---------------------------- TEST RESETS ----------------------------- @@ -3809,15 +3811,15 @@ class TestMoneroWalletCommon { const NUM_SUBADDRESSES_TO_SWEEP = 2; // collect subaddresses with balance and unlocked balance - let subaddresses = []; - let subaddressesBalance = []; - let subaddressesUnlocked = []; + let subaddresses: any[] = []; + let subaddressesBalance: any[] = []; + let subaddressesUnlocked: any[] = []; for (let account of await that.wallet.getAccounts(true)) { if (account.getIndex() === 0) continue; // skip default account for (let subaddress of account.getSubaddresses()) { subaddresses.push(subaddress); - if (subaddress.getBalance().compare(TestUtils.MAX_FEE) > 0) subaddressesBalance.push(subaddress); - if (subaddress.getUnlockedBalance().compare(TestUtils.MAX_FEE) > 0) subaddressesUnlocked.push(subaddress); + if (subaddress.getBalance() > TestUtils.MAX_FEE) subaddressesBalance.push(subaddress); + if (subaddress.getUnlockedBalance() > TestUtils.MAX_FEE) subaddressesUnlocked.push(subaddress); } } @@ -3842,16 +3844,16 @@ class TestMoneroWalletCommon { assert(txs.length > 0); for (let tx of txs) { assert(GenUtils.arrayContains(tx.getTxSet().getTxs(), tx)); - await that._testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true, isSweepResponse: true}); + await that.testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true, isSweepResponse: true}); } // assert unlocked balance is less than max fee let subaddress = await that.wallet.getSubaddress(unlockedSubaddress.getAccountIndex(), unlockedSubaddress.getIndex()); - assert(subaddress.getUnlockedBalance().compare(TestUtils.MAX_FEE) < 0); + assert(subaddress.getUnlockedBalance() < TestUtils.MAX_FEE); } // test subaddresses after sweeping - let subaddressesAfter = []; + let subaddressesAfter: any[] = []; for (let account of await that.wallet.getAccounts(true)) { if (account.getIndex() === 0) continue; // skip default account for (let subaddress of account.getSubaddresses()) { @@ -3874,9 +3876,9 @@ class TestMoneroWalletCommon { // assert unlocked balance is less than max fee if swept, unchanged otherwise if (swept) { - assert(subaddressAfter.getUnlockedBalance().compare(TestUtils.MAX_FEE) < 0); + assert(subaddressAfter.getUnlockedBalance() < TestUtils.MAX_FEE); } else { - assert(subaddressBefore.getUnlockedBalance().compare(subaddressAfter.getUnlockedBalance()) === 0); + assert.equal(subaddressBefore.getUnlockedBalance(), subaddressAfter.getUnlockedBalance()); } } }); @@ -3887,13 +3889,13 @@ class TestMoneroWalletCommon { const NUM_ACCOUNTS_TO_SWEEP = 1; // collect accounts with sufficient balance and unlocked balance to cover the fee - let accounts = await that.wallet.getAccounts(true); - let accountsBalance = []; - let accountsUnlocked = []; + let accounts: any[] = await that.wallet.getAccounts(true); + let accountsBalance: any[] = []; + let accountsUnlocked: any[] = []; for (let account of accounts) { if (account.getIndex() === 0) continue; // skip default account - if (account.getBalance().compare(TestUtils.MAX_FEE) > 0) accountsBalance.push(account); - if (account.getUnlockedBalance().compare(TestUtils.MAX_FEE) > 0) accountsUnlocked.push(account); + if (account.getBalance() > TestUtils.MAX_FEE) accountsBalance.push(account); + if (account.getUnlockedBalance() > TestUtils.MAX_FEE) accountsUnlocked.push(account); } // test requires at least one more accounts than the number being swept to verify it does not change @@ -3911,12 +3913,12 @@ class TestMoneroWalletCommon { // test transactions assert(txs.length > 0); for (let tx of txs) { - await that._testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true, isSweepResponse: true}); + await that.testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true, isSweepResponse: true}); } // assert unlocked account balance less than max fee let account = await that.wallet.getAccount(unlockedAccount.getIndex()); - assert(account.getUnlockedBalance().compare(TestUtils.MAX_FEE) < 0); + assert(account.getUnlockedBalance() < TestUtils.MAX_FEE); } // test accounts after sweeping @@ -3937,9 +3939,9 @@ class TestMoneroWalletCommon { // assert unlocked balance is less than max fee if swept, unchanged otherwise if (swept) { - assert(accountAfter.getUnlockedBalance().compare(TestUtils.MAX_FEE) < 0); + assert(accountAfter.getUnlockedBalance() < TestUtils.MAX_FEE); } else { - assert.equal(accountBefore.getUnlockedBalance().compare(accountAfter.getUnlockedBalance()), 0); + assert.equal(accountBefore.getUnlockedBalance(), accountAfter.getUnlockedBalance()); } } }); @@ -3947,25 +3949,25 @@ class TestMoneroWalletCommon { if (testConfig.testResets) it("Can sweep the whole wallet by accounts", async function() { assert(false, "Are you sure you want to sweep the whole wallet?"); - await _testSweepWallet(); + await testSweepWallet(); }); if (testConfig.testResets) it("Can sweep the whole wallet by subaddresses", async function() { assert(false, "Are you sure you want to sweep the whole wallet?"); - await _testSweepWallet(true); + await testSweepWallet(true); }); - async function _testSweepWallet(sweepEachSubaddress) { + async function testSweepWallet(sweepEachSubaddress = false) { await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(that.wallet); // verify 2 subaddresses with enough unlocked balance to cover the fee - let subaddressesBalance = []; - let subaddressesUnlocked = []; + let subaddressesBalance: MoneroSubaddress[] = []; + let subaddressesUnlocked: MoneroSubaddress[] = []; for (let account of await that.wallet.getAccounts(true)) { for (let subaddress of account.getSubaddresses()) { - if (subaddress.getBalance().compare(TestUtils.MAX_FEE) > 0) subaddressesBalance.push(subaddress); - if (subaddress.getUnlockedBalance().compare(TestUtils.MAX_FEE) > 0) subaddressesUnlocked.push(subaddress); + if (subaddress.getBalance() > TestUtils.MAX_FEE) subaddressesBalance.push(subaddress); + if (subaddress.getUnlockedBalance() > TestUtils.MAX_FEE) subaddressesUnlocked.push(subaddress); } } assert(subaddressesBalance.length >= 2, "Test requires multiple accounts with a balance greater than the fee; run send to multiple first"); @@ -3991,13 +3993,13 @@ class TestMoneroWalletCommon { sweepEachSubaddress: sweepEachSubaddress, relay: true }); - await that._testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true, isSweepResponse: true}); + await that.testTxWallet(tx, {wallet: that.wallet, config: config, isSendResponse: true, isSweepResponse: true}); } // all unspent, unlocked outputs must be less than fee let spendableOutputs = await that.wallet.getOutputs(new MoneroOutputQuery().setIsSpent(false).setTxQuery(new MoneroTxQuery().setIsLocked(false))); for (let spendableOutput of spendableOutputs) { - assert(spendableOutput.getAmount().compare(TestUtils.MAX_FEE) < 0, "Unspent output should have been swept\n" + spendableOutput.toString()); + assert(spendableOutput.getAmount() < TestUtils.MAX_FEE, "Unspent output should have been swept\n" + spendableOutput.toString()); } // all subaddress unlocked balances must be less than fee @@ -4005,7 +4007,7 @@ class TestMoneroWalletCommon { subaddressesUnlocked = []; for (let account of await that.wallet.getAccounts(true)) { for (let subaddress of account.getSubaddresses()) { - assert(subaddress.getUnlockedBalance().compare(TestUtils.MAX_FEE) < 0, "No subaddress should have more unlocked than the fee"); + assert(subaddress.getUnlockedBalance() < TestUtils.MAX_FEE, "No subaddress should have more unlocked than the fee"); } } } @@ -4013,7 +4015,7 @@ class TestMoneroWalletCommon { it("Can scan transactions by id", async function() { // get a few tx hashes - let txHashes = []; + let txHashes: string[] = []; let txs = await that.wallet.getTxs(); if (txs.length < 3) throw new Error("Not enough txs to scan"); for (let i = 0; i < 3; i++) txHashes.push(txs[i].getHash()); @@ -4043,10 +4045,10 @@ class TestMoneroWalletCommon { // disabled so tests don't delete local cache if (testConfig.testResets) it("Can rescan the blockchain", async function() { - assert(false, "Are you sure you want to discard local wallet data and rescan the blockchain?"); + //assert(false, "Are you sure you want to discard local wallet data and rescan the blockchain?"); await that.wallet.rescanBlockchain(); for (let tx of await that.wallet.getTxs()) { - await that._testTxWallet(tx); + await that.testTxWallet(tx); } }); @@ -4055,12 +4057,12 @@ class TestMoneroWalletCommon { // get an available output let outputs = await that.wallet.getOutputs(new MoneroOutputQuery().setIsSpent(false).setIsFrozen(false).setTxQuery(new MoneroTxQuery().setIsLocked(false))); - for (let output of outputs) assert.equal(false, output.isFrozen()); + for (let output of outputs) assert.equal(false, output.getIsFrozen()); assert(outputs.length > 0); let output = outputs[0]; - assert.equal(false, output.getTx().isLocked()); - assert.equal(false, output.isSpent()); - assert.equal(false, output.isFrozen()); + assert.equal(false, output.getTx().getIsLocked()); + assert.equal(false, output.getIsSpent()); + assert.equal(false, output.getIsFrozen()); assert.equal(false, await that.wallet.isOutputFrozen(output.getKeyImage().getHex())); // freeze output by key image @@ -4073,30 +4075,22 @@ class TestMoneroWalletCommon { outputs = await that.wallet.getOutputs(new MoneroOutputQuery().setKeyImage(new MoneroKeyImage().setHex(output.getKeyImage().getHex())).setIsFrozen(true)); assert.equal(1, outputs.length); let outputFrozen = outputs[0]; - assert.equal(true, outputFrozen.isFrozen()); + assert.equal(true, outputFrozen.getIsFrozen()); assert.equal(output.getKeyImage().getHex(), outputFrozen.getKeyImage().getHex()); // try to sweep frozen output try { await that.wallet.sweepOutput(new MoneroTxConfig().setAddress(await that.wallet.getPrimaryAddress()).setKeyImage(output.getKeyImage().getHex())); throw new Error("Should have thrown error"); - } catch (e) { + } catch (e: any) { assert.equal("No outputs found", e.message); } - // try to freeze undefined key image - try { - await that.wallet.freezeOutput(undefined); - throw new Error("Should have thrown error"); - } catch (e) { - assert.equal("Must specify key image to freeze", e.message); - } - // try to freeze empty key image try { await that.wallet.freezeOutput(""); throw new Error("Should have thrown error"); - } catch (e) { + } catch (e: any) { assert.equal("Must specify key image to freeze", e.message); } @@ -4104,7 +4098,7 @@ class TestMoneroWalletCommon { try { await that.wallet.freezeOutput("123"); throw new Error("Should have thrown error"); - } catch (e) { + } catch (e: any) { //assert.equal("Bad key image", e.message); } @@ -4119,7 +4113,7 @@ class TestMoneroWalletCommon { outputs = await that.wallet.getOutputs(new MoneroOutputQuery().setKeyImage(new MoneroKeyImage().setHex(output.getKeyImage().getHex())).setIsFrozen(false)); assert.equal(1, outputs.length); let outputThawed = outputs[0]; - assert.equal(false, outputThawed.isFrozen()); + assert.equal(false, outputThawed.getIsFrozen()); assert.equal(output.getKeyImage().getHex(), outputThawed.getKeyImage().getHex()); }); @@ -4137,17 +4131,17 @@ class TestMoneroWalletCommon { } // test unrelayed sweep dust - let dustKeyImages = []; + let dustKeyImages: any[] = []; for (let tx of await that.wallet.sweepDust(false)) { testSpendTx(tx); - for (let input of tx.getInputs()) dustKeyImages.add(input.getKeyImage().getHex()); + for (let input of tx.getInputs()) dustKeyImages.push(input.getKeyImage().getHex()); } // get available outputs above min amount let outputs = await that.wallet.getOutputs(new MoneroOutputQuery().setAccountIndex(accountIndex).setSubaddressIndex(subaddressIndex).setIsSpent(false).setIsFrozen(false).setTxQuery(new MoneroTxQuery().setIsLocked(false)).setMinAmount(TestUtils.MAX_FEE)); // filter dust outputs - let dustOutputs = []; + let dustOutputs: any[] = []; for (let output of outputs) { if (dustKeyImages.includes(output.getKeyImage().getHex())) dustOutputs.push(output); } @@ -4168,15 +4162,15 @@ class TestMoneroWalletCommon { assert(sweptKeyImages.size > 0); // max skipped output is less than max fee amount - let maxSkippedOutput = undefined; + let maxSkippedOutput: MoneroOutputWallet | undefined = undefined; for (let output of outputs) { if (!sweptKeyImages.has(output.getKeyImage().getHex())) { - if (maxSkippedOutput === undefined || maxSkippedOutput.getAmount().compare(output.getAmount()) < 0) { + if (maxSkippedOutput === undefined || maxSkippedOutput.getAmount() < output.getAmount()) { maxSkippedOutput = output; } } } - assert(maxSkippedOutput === undefined || maxSkippedOutput.getAmount().compare(TestUtils.MAX_FEE) < 0); + assert(maxSkippedOutput === undefined || maxSkippedOutput.getAmount() < TestUtils.MAX_FEE); }); function testSpendTx(spendTx) { @@ -4194,37 +4188,37 @@ class TestMoneroWalletCommon { let address3 = await that.wallet.getAddress(1, 0); let tx = await that.wallet.createTx(new MoneroTxConfig() .setAccountIndex(0) - .addDestination(address1, TestUtils.MAX_FEE) - .addDestination(address2, TestUtils.MAX_FEE.multiply(new BigInteger("2"))) - .addDestination(address3, TestUtils.MAX_FEE.multiply(new BigInteger("3")))); + .addDestination(address1, TestUtils.MAX_FEE) + .addDestination(address2, TestUtils.MAX_FEE * (BigInt("2"))) + .addDestination(address3, TestUtils.MAX_FEE * (BigInt("3")))); // submit tx to daemon but do not relay let result = await that.daemon.submitTxHex(tx.getFullHex(), true); - assert.equal(result.isGood(), true); + assert.equal(result.getIsGood(), true); // create random wallet to verify transfers let verifyingWallet = await that.createWallet(new MoneroWalletConfig()); // verify transfer 1 let check = await verifyingWallet.checkTxKey(tx.getHash(), tx.getKey(), address1); - assert.equal(check.isGood(), true); - assert.equal(check.inTxPool(), true); + assert.equal(check.getIsGood(), true); + assert.equal(check.getInTxPool(), true); assert.equal(check.getNumConfirmations(), 0); assert.equal(check.getReceivedAmount().toString(), TestUtils.MAX_FEE.toString()); // verify transfer 2 check = await verifyingWallet.checkTxKey(tx.getHash(), tx.getKey(), address2); - assert.equal(check.isGood(), true); - assert.equal(check.inTxPool(), true); + assert.equal(check.getIsGood(), true); + assert.equal(check.getInTxPool(), true); assert.equal(check.getNumConfirmations(), 0); - assert.equal(check.getReceivedAmount().compare(TestUtils.MAX_FEE.multiply(new BigInteger("2"))) >= 0, true); // + change amount + assert(check.getReceivedAmount() >= TestUtils.MAX_FEE * 2n); // + change amount // verify transfer 3 check = await verifyingWallet.checkTxKey(tx.getHash(), tx.getKey(), address3); - assert.equal(check.isGood(), true); - assert.equal(check.inTxPool(), true); + assert.equal(check.getIsGood(), true); + assert.equal(check.getInTxPool(), true); assert.equal(check.getNumConfirmations(), 0); - assert.equal(check.getReceivedAmount().toString(), TestUtils.MAX_FEE.multiply(new BigInteger("3")).toString()); + assert.equal(check.getReceivedAmount().toString(), (TestUtils.MAX_FEE * BigInt("3")).toString()); // cleanup await that.daemon.flushTxPool(tx.getHash()); @@ -4236,26 +4230,26 @@ class TestMoneroWalletCommon { // -------------------------------- PRIVATE --------------------------------- async getSubaddressesWithBalance() { - let subaddresses = []; + let subaddresses: any[] = []; for (let account of await this.wallet.getAccounts(true)) { for (let subaddress of account.getSubaddresses()) { - if (subaddress.getBalance().toJSValue() > 0) subaddresses.push(subaddress); + if (subaddress.getBalance() > 0) subaddresses.push(subaddress); } } return subaddresses; } async getSubaddressesWithUnlockedBalance() { - let subaddresses = []; + let subaddresses: any[] = []; for (let account of await this.wallet.getAccounts(true)) { for (let subaddress of account.getSubaddresses()) { - if (subaddress.getUnlockedBalance().toJSValue() > 0) subaddresses.push(subaddress); + if (subaddress.getUnlockedBalance() > 0n) subaddresses.push(subaddress); } } return subaddresses; } - async _testGetSubaddressAddressOutOfRange() { + protected async testGetSubaddressAddressOutOfRange() { let accounts = await this.wallet.getAccounts(true); let accountIdx = accounts.length - 1; let subaddressIdx = accounts[accountIdx].getSubaddresses().length; @@ -4269,7 +4263,7 @@ class TestMoneroWalletCommon { * * TODO: convert query to query object and ensure each tx passes filter, same with getAndTestTransfer, getAndTestOutputs */ - async _getAndTestTxs(wallet, query, isExpected) { + protected async getAndTestTxs(wallet, query: Partial | undefined, isExpected?): Promise { let copy; if (query !== undefined) { if (query instanceof MoneroTxQuery) copy = query.copy(); @@ -4279,7 +4273,7 @@ class TestMoneroWalletCommon { assert(Array.isArray(txs)); if (isExpected === false) assert.equal(txs.length, 0); if (isExpected === true) assert(txs.length > 0, "Transactions were expected but not found; run send tests?"); - for (let tx of txs) await this._testTxWallet(tx, Object.assign({wallet: wallet}, query)); + for (let tx of txs) await this.testTxWallet(tx, Object.assign({wallet: wallet}, query)); testGetTxsStructure(txs, query); if (query !== undefined) { if (query instanceof MoneroTxQuery) assert.deepEqual(query.toJson(), copy.toJson()); @@ -4291,7 +4285,7 @@ class TestMoneroWalletCommon { /** * Fetches and tests transfers according to the given query. */ - async _getAndTestTransfers(wallet, query, isExpected) { + protected async getAndTestTransfers(wallet: MoneroWallet, query: Partial, isExpected?): Promise { let copy; if (query !== undefined) { if (query instanceof MoneroTransferQuery) copy = query.copy(); @@ -4301,7 +4295,7 @@ class TestMoneroWalletCommon { assert(Array.isArray(transfers)); if (isExpected === false) assert.equal(transfers.length, 0); if (isExpected === true) assert(transfers.length > 0, "Transfers were expected but not found; run send tests?"); - for (let transfer of transfers) await this._testTxWallet(transfer.getTx(), Object.assign({wallet: wallet}, query)); + for (let transfer of transfers) await this.testTxWallet(transfer.getTx(), Object.assign({wallet: wallet}, query)); if (query !== undefined) { if (query instanceof MoneroTransferQuery) assert.deepEqual(query.toJson(), copy.toJson()); else assert.deepEqual(query, copy); @@ -4312,7 +4306,7 @@ class TestMoneroWalletCommon { /** * Fetches and tests outputs according to the given query. */ - async _getAndTestOutputs(wallet, query, isExpected) { + protected async getAndTestOutputs(wallet: MoneroWallet, query: Partial, isExpected?) { let copy; if (query !== undefined) { if (query instanceof MoneroOutputQuery) copy = query.copy(); @@ -4330,11 +4324,11 @@ class TestMoneroWalletCommon { return outputs; } - async _testTxsWallet(txs, ctx) { + protected async testTxsWallet(txs: MoneroTxWallet[], ctx) { // test each transaction assert(txs.length > 0); - for (let tx of txs) await this._testTxWallet(tx, ctx); + for (let tx of txs) await this.testTxWallet(tx, ctx); // test destinations across transactions if (ctx.config && ctx.config.getDestinations()) { @@ -4348,11 +4342,11 @@ class TestMoneroWalletCommon { return; } - let amountDiff = new BigInteger(0); + let amountDiff = BigInt(0); for (let destination of tx.getOutgoingTransfer().getDestinations()) { let ctxDestination = ctx.config.getDestinations()[destinationIdx]; assert.equal(destination.getAddress(), ctxDestination.getAddress()); - if (subtractFeeFromDestinations) amountDiff = amountDiff.add(ctxDestination.getAmount().subtract(destination.getAmount())); + if (subtractFeeFromDestinations) amountDiff = amountDiff + ctxDestination.getAmount() - destination.getAmount(); else assert.equal(destination.getAmount().toString(), ctxDestination.getAmount().toString()); destinationIdx++; } @@ -4373,7 +4367,7 @@ class TestMoneroWalletCommon { * ctx.hasDestinations specifies if the tx has an outgoing transfer with destinations, undefined if doesn't matter * ctx.includeOutputs specifies if outputs were fetched and should therefore be expected with incoming transfers */ - async _testTxWallet(tx, ctx) { + protected async testTxWallet(tx: MoneroTxWallet, ctx?: any) { // validate / sanitize inputs ctx = Object.assign({}, ctx); @@ -4392,16 +4386,16 @@ class TestMoneroWalletCommon { // test common field types assert.equal(typeof tx.getHash(), "string"); - assert.equal(typeof tx.isConfirmed(), "boolean"); - assert.equal(typeof tx.isMinerTx(), "boolean"); - assert.equal(typeof tx.isFailed(), "boolean"); - assert.equal(typeof tx.isRelayed(), "boolean"); - assert.equal(typeof tx.inTxPool(), "boolean"); - assert.equal(typeof tx.isLocked(), "boolean"); - TestUtils.testUnsignedBigInteger(tx.getFee()); + assert.equal(typeof tx.getIsConfirmed(), "boolean"); + assert.equal(typeof tx.getIsMinerTx(), "boolean"); + assert.equal(typeof tx.getIsFailed(), "boolean"); + assert.equal(typeof tx.getIsRelayed(), "boolean"); + assert.equal(typeof tx.getInTxPool(), "boolean"); + assert.equal(typeof tx.getIsLocked(), "boolean"); + TestUtils.testUnsignedBigInt(tx.getFee()); if (tx.getPaymentId()) assert.notEqual(tx.getPaymentId(), MoneroTx.DEFAULT_PAYMENT_ID); // default payment id converted to undefined if (tx.getNote()) assert(tx.getNote().length > 0); // empty notes converted to undefined - assert(tx.getUnlockTime().compare(new BigInteger(0)) >= 0); + assert(tx.getUnlockTime() >= BigInt(0)); assert.equal(tx.getSize(), undefined); // TODO monero-wallet-rpc: add tx_size to get_transfers and get_transfer_by_txid assert.equal(tx.getReceivedTimestamp(), undefined); // TODO monero-wallet-rpc: return received timestamp (asked to file issue if wanted) @@ -4417,16 +4411,16 @@ class TestMoneroWalletCommon { } // test confirmed - if (tx.isConfirmed()) { + if (tx.getIsConfirmed()) { assert(tx.getBlock()); assert(tx.getBlock().getTxs().includes(tx)); assert(tx.getBlock().getHeight() > 0); assert(tx.getBlock().getTimestamp() > 0); - assert.equal(tx.isRelayed(), true); - assert.equal(tx.isFailed(), false); - assert.equal(tx.inTxPool(), false); + assert.equal(tx.getIsRelayed(), true); + assert.equal(tx.getIsFailed(), false); + assert.equal(tx.getInTxPool(), false); assert.equal(tx.getRelay(), true); - assert.equal(tx.isDoubleSpendSeen(), false); + assert.equal(tx.getIsDoubleSpendSeen(), false); assert(tx.getNumConfirmations() > 0); } else { assert.equal(undefined, tx.getBlock()); @@ -4434,12 +4428,12 @@ class TestMoneroWalletCommon { } // test in tx pool - if (tx.inTxPool()) { - assert.equal(tx.isConfirmed(), false); + if (tx.getInTxPool()) { + assert.equal(tx.getIsConfirmed(), false); assert.equal(tx.getRelay(), true); - assert.equal(tx.isRelayed(), true); - assert.equal(tx.isDoubleSpendSeen(), false); // TODO: test double spend attempt - assert.equal(tx.isLocked(), true); + assert.equal(tx.getIsRelayed(), true); + assert.equal(tx.getIsDoubleSpendSeen(), false); // TODO: test double spend attempt + assert.equal(tx.getIsLocked(), true); // these should be initialized unless a response from sending if (!ctx.isSendResponse) { @@ -4450,21 +4444,21 @@ class TestMoneroWalletCommon { } // test miner tx - if (tx.isMinerTx()) { - assert.equal(tx.getFee().compare(new BigInteger(0)), 0); + if (tx.getIsMinerTx()) { + assert.equal(tx.getFee(), 0n); assert(tx.getIncomingTransfers().length > 0); } // test failed // TODO: what else to test associated with failed - if (tx.isFailed()) { + if (tx.getIsFailed()) { assert(tx.getOutgoingTransfer() instanceof MoneroTransfer); //assert(tx.getReceivedTimestamp() > 0); // TODO: re-enable when received timestamp returned in wallet rpc } else { - if (tx.isRelayed()) assert.equal(tx.isDoubleSpendSeen(), false); + if (tx.getIsRelayed()) assert.equal(tx.getIsDoubleSpendSeen(), false); else { - assert.equal(tx.isRelayed(), false); + assert.equal(tx.getIsRelayed(), false); assert.notEqual(tx.getRelay(), true); - assert.equal(tx.isDoubleSpendSeen(), undefined); + assert.equal(tx.getIsDoubleSpendSeen(), undefined); } } assert.equal(tx.getLastFailedHeight(), undefined); @@ -4472,12 +4466,12 @@ class TestMoneroWalletCommon { // received time only for tx pool or failed txs if (tx.getReceivedTimestamp() !== undefined) { - assert(tx.inTxPool() || tx.isFailed()); + assert(tx.getInTxPool() || tx.getIsFailed()); } // test relayed tx - if (tx.isRelayed()) assert.equal(tx.getRelay(), true); - if (tx.getRelay() !== true) assert.equal(tx.isRelayed(), false); + if (tx.getIsRelayed()) assert.equal(tx.getRelay(), true); + if (tx.getRelay() !== true) assert.equal(tx.getIsRelayed(), false); // test outgoing transfer per configuration if (ctx.isOutgoing === false) assert(tx.getOutgoingTransfer() === undefined); @@ -4485,7 +4479,7 @@ class TestMoneroWalletCommon { // test outgoing transfer if (tx.getOutgoingTransfer()) { - assert(tx.isOutgoing()); + assert(tx.getIsOutgoing()); await testTransfer(tx.getOutgoingTransfer(), ctx); if (ctx.isSweepResponse) assert.equal(tx.getOutgoingTransfer().getDestinations().length, 1); @@ -4502,23 +4496,23 @@ class TestMoneroWalletCommon { // test incoming transfers if (tx.getIncomingTransfers()) { - assert(tx.isIncoming()); + assert(tx.getIsIncoming()); assert(tx.getIncomingTransfers().length > 0); - TestUtils.testUnsignedBigInteger(tx.getIncomingAmount()); - assert.equal(tx.isFailed(), false); + TestUtils.testUnsignedBigInt(tx.getIncomingAmount()); + assert.equal(tx.getIsFailed(), false); // test each transfer and collect transfer sum - let transferSum = new BigInteger(0); + let transferSum = BigInt(0); for (let transfer of tx.getIncomingTransfers()) { await testTransfer(transfer, ctx); - transferSum = transferSum.add(transfer.getAmount()); + transferSum += transfer.getAmount(); if (ctx.wallet) assert.equal(transfer.getAddress(), await ctx.wallet.getAddress(transfer.getAccountIndex(), transfer.getSubaddressIndex())); // TODO special case: transfer amount of 0 } // incoming transfers add up to incoming tx amount - assert.equal(transferSum.compare(tx.getIncomingAmount()), 0); + assert.equal(transferSum, tx.getIncomingAmount()) } else { assert(tx.getOutgoingTransfer()); assert.equal(tx.getIncomingAmount(), undefined); @@ -4542,29 +4536,29 @@ class TestMoneroWalletCommon { // test common attributes let config = ctx.config; - assert.equal(tx.isConfirmed(), false); + assert.equal(tx.getIsConfirmed(), false); await testTransfer(tx.getOutgoingTransfer(), ctx); assert.equal(tx.getRingSize(), MoneroUtils.RING_SIZE); - assert.equal(tx.getUnlockTime().toString(), (config.getUnlockTime() ? config.getUnlockTime() : new BigInteger(0)).toString()); + assert.equal(tx.getUnlockTime().toString(), (config.getUnlockTime() ? config.getUnlockTime() : BigInt(0)).toString()); assert.equal(tx.getBlock(), undefined); assert(tx.getKey().length > 0); assert.equal(typeof tx.getFullHex(), "string"); assert(tx.getFullHex().length > 0); assert(tx.getMetadata()); assert.equal(tx.getReceivedTimestamp(), undefined); - assert.equal(tx.isLocked(), true); + assert.equal(tx.getIsLocked(), true); // test locked state - if (new BigInteger(0).compare(tx.getUnlockTime()) === 0) assert.equal(!tx.isLocked(), tx.isConfirmed()); - else assert.equal(tx.isLocked(), true); + if (BigInt(0) === tx.getUnlockTime()) assert.equal(!tx.getIsLocked(), tx.getIsConfirmed()); + else assert.equal(tx.getIsLocked(), true); if (tx.getOutputs() !== undefined) { - for (let output of tx.getOutputs()) { - assert.equal(output.isLocked(), tx.isLocked()); + for (let output of tx.getOutputsWallet()) { + assert.equal(output.getIsLocked(), tx.getIsLocked()); } } // test destinations of sent tx - if (tx.getOutgoingTransfer().getDestinations() == null) { + if (tx.getOutgoingTransfer().getDestinations() === undefined) { assert(config.getCanSplit()); console.warn("Destinations not returned from split transactions"); // TODO: remove this after >18.2.2 when amounts_by_dest_list official } else { @@ -4582,20 +4576,20 @@ class TestMoneroWalletCommon { // test relayed txs if (config.getRelay()) { - assert.equal(tx.inTxPool(), true); + assert.equal(tx.getInTxPool(), true); assert.equal(tx.getRelay(), true); - assert.equal(tx.isRelayed(), true); + assert.equal(tx.getIsRelayed(), true); assert(tx.getLastRelayedTimestamp() > 0); - assert.equal(tx.isDoubleSpendSeen(), false); + assert.equal(tx.getIsDoubleSpendSeen(), false); } // test non-relayed txs else { - assert.equal(tx.inTxPool(), false); + assert.equal(tx.getInTxPool(), false); assert.notEqual(tx.getRelay(), true); - assert.equal(tx.isRelayed(), false); + assert.equal(tx.getIsRelayed(), false); assert.equal(tx.getLastRelayedTimestamp(), undefined); - assert.equal(tx.isDoubleSpendSeen(), undefined); + assert.equal(tx.getIsDoubleSpendSeen(), undefined); } } @@ -4610,16 +4604,16 @@ class TestMoneroWalletCommon { } // test inputs - if (tx.isOutgoing() && ctx.isSendResponse) { + if (tx.getIsOutgoing() && ctx.isSendResponse) { assert(tx.getInputs() !== undefined); assert(tx.getInputs().length > 0); } else { - if (tx.getInputs()) for (let input of tx.getInputs()) testInputWallet(output); + if (tx.getInputs()) for (let input of tx.getInputs()) testInputWallet(input); } // test outputs - if (tx.isIncoming() && ctx.includeOutputs) { - if (tx.isConfirmed()) { + if (tx.getIsIncoming() && ctx.includeOutputs) { + if (tx.getIsConfirmed()) { assert(tx.getOutputs() !== undefined); assert(tx.getOutputs().length > 0); } else { @@ -4630,11 +4624,11 @@ class TestMoneroWalletCommon { if (tx.getOutputs()) for (let output of tx.getOutputs()) testOutputWallet(output); // test deep copy - if (!ctx.isCopy) await this._testTxWalletCopy(tx, ctx); + if (!ctx.isCopy) await this.testTxWalletCopy(tx, ctx); } - // TODO: move below _testTxWalletCopy - async _testTxWalletCopy(tx, ctx) { + // TODO: move below testTxWalletCopy + protected async testTxWalletCopy(tx, ctx) { // copy tx and assert deep equality let copy = tx.copy(); @@ -4645,14 +4639,11 @@ class TestMoneroWalletCommon { if (tx.getOutgoingTransfer()) { assert(tx.getOutgoingTransfer() !== copy.getOutgoingTransfer()); assert(tx.getOutgoingTransfer().getTx() !== copy.getOutgoingTransfer().getTx()); - //assert(tx.getOutgoingTransfer().getAmount() !== copy.getOutgoingTransfer().getAmount()); // TODO: BI 0 === BI 0?, testing this instead: - if (tx.getOutgoingTransfer().getAmount() === copy.getOutgoingTransfer().getAmount()) assert(tx.getOutgoingTransfer().getAmount().compare(new BigInteger(0)) === 0); if (tx.getOutgoingTransfer().getDestinations()) { assert(tx.getOutgoingTransfer().getDestinations() !== copy.getOutgoingTransfer().getDestinations()); for (let i = 0; i < tx.getOutgoingTransfer().getDestinations().length; i++) { assert.deepEqual(copy.getOutgoingTransfer().getDestinations()[i], tx.getOutgoingTransfer().getDestinations()[i]); assert(tx.getOutgoingTransfer().getDestinations()[i] !== copy.getOutgoingTransfer().getDestinations()[i]); - if (tx.getOutgoingTransfer().getDestinations()[i].getAmount() === copy.getOutgoingTransfer().getDestinations()[i].getAmount()) assert(tx.getOutgoingTransfer().getDestinations()[i].getAmount().toJSValue() === 0); } } } @@ -4660,7 +4651,6 @@ class TestMoneroWalletCommon { for (let i = 0; i < tx.getIncomingTransfers().length; i++) { assert.deepEqual(copy.getIncomingTransfers()[i].toJson(), tx.getIncomingTransfers()[i].toJson()); assert(tx.getIncomingTransfers()[i] !== copy.getIncomingTransfers()[i]); - if (tx.getIncomingTransfers()[i].getAmount() == copy.getIncomingTransfers()[i].getAmount()) assert(tx.getIncomingTransfers()[i].getAmount().toJSValue() === 0); } } if (tx.getInputs()) { @@ -4673,7 +4663,6 @@ class TestMoneroWalletCommon { for (let i = 0; i < tx.getOutputs().length; i++) { assert.deepEqual(copy.getOutputs()[i].toJson(), tx.getOutputs()[i].toJson()); assert(tx.getOutputs()[i] !== copy.getOutputs()[i]); - if (tx.getOutputs()[i].getAmount() == copy.getOutputs()[i].getAmount()) assert(tx.getOutputs()[i].getAmount().toJSValue() === 0); } } @@ -4681,24 +4670,24 @@ class TestMoneroWalletCommon { ctx = Object.assign({}, ctx); ctx.isCopy = true; if (tx.getBlock()) copy.setBlock(tx.getBlock().copy().setTxs([copy])); // copy block for testing - await this._testTxWallet(copy, ctx); + await this.testTxWallet(copy, ctx); // test merging with copy let merged = copy.merge(copy.copy()); assert.equal(merged.toString(), tx.toString()); } - async _testMultisig(M, N, testTx) { + protected async testMultisig(M, N, testTx) { // create N participants - let participants = []; + let participants: MoneroWallet[] = []; for (let i = 0; i < N; i++) participants.push(await this.createWallet(new MoneroWalletConfig())); // test multisig let err; try { - await this._testMultisigParticipants(participants, M, N, testTx); - } catch (e) { + await this.testMultisigParticipants(participants, M, N, testTx); + } catch (e: any) { err = e; } @@ -4711,31 +4700,31 @@ class TestMoneroWalletCommon { if (err) throw err; } - async _testMultisigParticipants(participants, M, N, testTx) { - console.log("_testMultisig(" + M + ", " + N + ")"); + protected async testMultisigParticipants(participants, M, N, testTx) { + console.log("testMultisig(" + M + ", " + N + ")"); assert.equal(N, participants.length); // prepare multisig hexes - let preparedMultisigHexes = []; + let preparedMultisigHexes: string[] = []; for (let i = 0; i < N; i++) { let participant = participants[i]; preparedMultisigHexes.push(await participant.prepareMultisig()); } // make wallets multisig - let madeMultisigHexes = []; + let madeMultisigHexes: string[] = []; for (let i = 0; i < participants.length; i++) { let participant = participants[i]; // collect prepared multisig hexes from wallet's peers - let peerMultisigHexes = []; + let peerMultisigHexes: string[] = []; for (let j = 0; j < participants.length; j++) if (j !== i) peerMultisigHexes.push(preparedMultisigHexes[j]); // test bad input try { await participant.makeMultisig(["asd", "dsa"], M, TestUtils.WALLET_PASSWORD); throw new Error("Should have thrown error making wallet multisig with bad input"); - } catch (err) { + } catch (err: any) { if (!(err instanceof MoneroError)) throw err; assert.equal(err.message, "basic_string"); // TODO (monero-project): improve error message https://github.com/monero-project/monero/issues/8493 } @@ -4749,19 +4738,19 @@ class TestMoneroWalletCommon { try { await participants[0].getSeed(); throw new Error("Should have thrown exception getting multisig seed before initialized"); - } catch (err) { + } catch (err: any) { assert.equal("This wallet is multisig, but not yet finalized", err.message); } // exchange keys N - M + 1 times let address = undefined; assert.equal(madeMultisigHexes.length, N); - let prevMultisigHexes = madeMultisigHexes; + let prevMultisigHexes: string[] = madeMultisigHexes; for (let i = 0; i < N - M + 1; i++) { //console.log("Exchanging multisig keys round " + (i + 1) + " / " + (N - M)); // exchange multisig keys with each wallet and collect results - let exchangeMultisigHexes = []; + let exchangeMultisigHexes: string[] = []; for (let j = 0; j < participants.length; j++) { let participant = participants[j]; @@ -4769,13 +4758,13 @@ class TestMoneroWalletCommon { try { await participant.exchangeMultisigKeys([], TestUtils.WALLET_PASSWORD); throw new Error("Should have thrown error exchanging multisig keys with bad input"); - } catch (err) { + } catch (err: any) { if (!(err instanceof MoneroError)) throw err; assert(err.message.length > 0); } // collect the multisig hexes of the wallet's peers from last round - let peerMultisigHexes = []; + let peerMultisigHexes: string[] = []; for (let k = 0; k < participants.length; k++) if (k !== j) peerMultisigHexes.push(prevMultisigHexes[k]); // import the multisig hexes of the wallet's peers @@ -4802,7 +4791,7 @@ class TestMoneroWalletCommon { // validate final multisig let participant = participants[0]; await MoneroUtils.validateAddress(await participant.getPrimaryAddress(), TestUtils.NETWORK_TYPE); - this._testMultisigInfo(await participant.getMultisigInfo(), M, N); + this.testMultisigInfo(await participant.getMultisigInfo(), M, N); let seed = await participant.getSeed(); assert(seed.length > 0); @@ -4811,7 +4800,7 @@ class TestMoneroWalletCommon { participant = await this.createWallet(new MoneroWalletConfig().setSeed(seed).setIsMultisig(true)); await MoneroUtils.validateAddress(await participant.getPrimaryAddress(), TestUtils.NETWORK_TYPE); assert.equal(await participant.getPrimaryAddress(), address); - this._testMultisigInfo(await participant.getMultisigInfo(), M, N); + this.testMultisigInfo(await participant.getMultisigInfo(), M, N); assert.equal(await participant.getSeed(), seed); participants[0] = participant; @@ -4824,18 +4813,18 @@ class TestMoneroWalletCommon { // get destinations to subaddresses within the account of the multisig wallet let numSubaddresses = 3; - let destinations = []; + let destinations: MoneroDestination[] = []; for (let i = 0; i < numSubaddresses; i++) { - destinations.push(new MoneroDestination(await participant.getAddress(accountIdx, i), TestUtils.MAX_FEE.multiply(new BigInteger(2)))); + destinations.push(new MoneroDestination(await participant.getAddress(accountIdx, i), TestUtils.MAX_FEE * BigInt(2))); if (i + 1 < numSubaddresses) participant.createSubaddress(accountIdx); } // wait for txs to confirm and for sufficient unlocked balance await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(this.wallet); - await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(this.wallet, 0, undefined, TestUtils.MAX_FEE.multiply(BigInteger.parse("20"))); + await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(this.wallet, 0, undefined, TestUtils.MAX_FEE * (BigInt("20"))); // send funds from the main test wallet to destinations in the first multisig wallet - assert((await this.wallet.getBalance()).compare(new BigInteger(0)) > 0); + assert(await this.wallet.getBalance() > 0n); console.log("Sending funds from main wallet"); await this.wallet.createTx({accountIndex: 0, destinations: destinations, relay: true}); let returnAddress = await this.wallet.getPrimaryAddress(); // funds will be returned to this address from the multisig wallet @@ -4846,7 +4835,7 @@ class TestMoneroWalletCommon { await StartMining.startMining(); // wait for the multisig wallet's funds to unlock // TODO: replace with MoneroWalletListener.onOutputReceived() which is called when output unlocked - let lastNumConfirmations = undefined; + let lastNumConfirmations: number | undefined = undefined; while (true) { // wait for a moment @@ -4864,10 +4853,10 @@ class TestMoneroWalletCommon { lastNumConfirmations = numConfirmations; // outputs are not spent - for (let output of outputs) assert(output.isSpent() === false); + for (let output of outputs) assert(output.getIsSpent() === false); // break if output is unlocked - if (!outputs[0].isLocked()) break; + if (!outputs[0].getIsLocked()) break; } } @@ -4876,33 +4865,33 @@ class TestMoneroWalletCommon { // multisig wallet should have unlocked balance in subaddresses 0-3 for (let i = 0; i < numSubaddresses; i++) { - assert((await participant.getUnlockedBalance(accountIdx, i)).compare(new BigInteger(0)) > 0); + assert((await participant.getUnlockedBalance(accountIdx, i)) > BigInt(0)); } let outputs = await participant.getOutputs({accountIndex: accountIdx}); assert(outputs.length > 0); if (outputs.length < 3) console.log("WARNING: not one output per subaddress?"); //assert(outputs.length >= 3); // TODO - for (let output of outputs) assert.equal(output.isLocked(), false); + for (let output of outputs) assert.equal(output.getIsLocked(), false); // wallet requires importing multisig to be reliable assert(await participant.isMultisigImportNeeded()); // attempt creating and relaying transaction without synchronizing with participants try { - await participant.createTx({accountIndex: accountIdx, address: returnAddress, amount: TestUtils.MAX_FEE.multiply(new BigInteger(3))}); + await participant.createTx({accountIndex: accountIdx, address: returnAddress, amount: TestUtils.MAX_FEE * BigInt(3)}); throw new Error("Should have failed sending funds without synchronizing with peers"); - } catch (e) { + } catch (e: any) { if (e.message !== "No transaction created") throw new Error(e); } // synchronize the multisig participants since receiving outputs console.log("Synchronizing participants"); - await this._synchronizeMultisigParticipants(participants); + await this.synchronizeMultisigParticipants(participants); // expect error exporting key images try { await participant.exportKeyImages(true); - } catch (e) { + } catch (e: any) { if (e.message.indexOf("key_image generated not matched with cached key image") < 0) throw new Error(e); } @@ -4910,7 +4899,7 @@ class TestMoneroWalletCommon { try { await participant.createTxs({address: returnAddress, amount: TestUtils.MAX_FEE, accountIndex: accountIdx, subaddressIndex: 0, relay: true}); throw new Error("Should have failed"); - } catch (e) { + } catch (e: any) { if (e.message !== "Cannot relay multisig transaction until co-signed") throw new Error(e); } @@ -4943,7 +4932,7 @@ class TestMoneroWalletCommon { // synchronize the multisig participants since spending outputs console.log("Synchronizing participants"); - await this._synchronizeMultisigParticipants(participants); + await this.synchronizeMultisigParticipants(participants); // fetch the wallet's multisig txs let multisigTxs = await participant.getTxs({hashes: txHashes}); @@ -4952,7 +4941,7 @@ class TestMoneroWalletCommon { // sweep an output from subaddress [accountIdx,1] outputs = await participant.getOutputs({accountIndex: accountIdx, subaddressIndex: 1}); assert(outputs.length > 0); - assert(outputs[0].isSpent() === false); + assert(outputs[0].getIsSpent() === false); txSet = (await participant.sweepOutput({address: returnAddress, keyImage: outputs[0].getKeyImage().getHex(), relay: true})).getTxSet(); assert.notEqual(txSet.getMultisigTxHex(), undefined); assert.equal(txSet.getSignedTxHex(), undefined); @@ -4976,7 +4965,7 @@ class TestMoneroWalletCommon { // synchronize the multisig participants since spending outputs console.log("Synchronizing participants"); - await this._synchronizeMultisigParticipants(participants); + await this.synchronizeMultisigParticipants(participants); // fetch the wallet's multisig txs multisigTxs = await participant.getTxs({hashes: txHashes}); @@ -5019,7 +5008,7 @@ class TestMoneroWalletCommon { // synchronize the multisig participants since spending outputs console.log("Synchronizing participants"); - await this._synchronizeMultisigParticipants(participants); + await this.synchronizeMultisigParticipants(participants); // fetch the wallet's multisig txs multisigTxs = await participant.getTxs({hashes: txHashes}); @@ -5027,10 +5016,10 @@ class TestMoneroWalletCommon { } } - async _synchronizeMultisigParticipants(wallets) { + protected async synchronizeMultisigParticipants(wallets) { // collect multisig hex of all participants to synchronize - let multisigHexes = []; + let multisigHexes: string[] = []; for (let wallet of wallets) { await wallet.sync(); multisigHexes.push(await wallet.exportMultisigHex()); @@ -5038,7 +5027,7 @@ class TestMoneroWalletCommon { // import each wallet's peer multisig hex for (let i = 0; i < wallets.length; i++) { - let peerMultisigHexes = []; + let peerMultisigHexes: string[] = []; for (let j = 0; j < wallets.length; j++) if (j !== i) peerMultisigHexes.push(multisigHexes[j]); let wallet = wallets[i]; await wallet.sync(); @@ -5046,18 +5035,18 @@ class TestMoneroWalletCommon { } } - async _testMultisigInfo(info, M, N) { - assert(info.isMultisig()); - assert(info.isReady()); + protected async testMultisigInfo(info: MoneroMultisigInfo, M, N) { + assert(info.getIsMultisig()); + assert(info.getIsReady()); assert.equal(info.getThreshold(), M); assert.equal(info.getNumParticipants(), N); } - async _testViewOnlyAndOfflineWallets(viewOnlyWallet, offlineWallet) { + protected async testViewOnlyAndOfflineWallets(viewOnlyWallet, offlineWallet) { // wait for txs to confirm and for sufficient unlocked balance await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(this.wallet); - await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(this.wallet, 0, undefined, TestUtils.MAX_FEE.multiply(BigInteger.parse("4"))); + await TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(this.wallet, 0, undefined, TestUtils.MAX_FEE * (BigInt("4"))); // test getting txs, transfers, and outputs from view-only wallet assert((await viewOnlyWallet.getTxs()).length, "View-only wallet has no transactions"); @@ -5076,19 +5065,19 @@ class TestMoneroWalletCommon { try { await await viewOnlyWallet.getSeed(); throw new Error(errMsg); - } catch (e) { + } catch (e: any) { if (e.message === errMsg) throw e; } try { await await viewOnlyWallet.getSeedLanguage(); throw new Error(errMsg); - } catch (e) { + } catch (e: any) { if (e.message === errMsg) throw e; } try { await await viewOnlyWallet.getPrivateSpendKey(); throw new Error(errMsg); - } catch (e) { + } catch (e: any) { if (e.message === errMsg) throw e; } assert(await viewOnlyWallet.isConnectedToDaemon(), "Wallet created from keys is not connected to authenticated daemon"); // TODO @@ -5118,7 +5107,7 @@ class TestMoneroWalletCommon { assert.equal((await viewOnlyWallet.getBalance()).toString(), (await this.wallet.getBalance()).toString()); // create unsigned tx using view-only wallet - let unsignedTx = await viewOnlyWallet.createTx({accountIndex: 0, address: primaryAddress, amount: TestUtils.MAX_FEE.multiply(BigInteger.parse("3"))}); + let unsignedTx = await viewOnlyWallet.createTx({accountIndex: 0, address: primaryAddress, amount: TestUtils.MAX_FEE * (BigInt("3"))}); assert.equal(typeof unsignedTx.getTxSet().getUnsignedTxHex(), "string"); assert(unsignedTx.getTxSet().getUnsignedTxHex()); @@ -5139,27 +5128,27 @@ class TestMoneroWalletCommon { } } - _testInvalidAddressError(err) { + protected testInvalidAddressError(err) { assert.equal("Invalid address", err.message); } - _testInvalidTxHashError(err) { + protected testInvalidTxHashError(err) { assert.equal("TX hash has invalid format", err.message); } - _testInvalidTxKeyError(err) { + protected testInvalidTxKeyError(err) { assert.equal("Tx key has invalid format", err.message); } - _testInvalidSignatureError(err) { + protected testInvalidSignatureError(err) { assert.equal("Signature size mismatch with additional tx pubkeys", err.message); } - _testNoSubaddressError(err) { + protected testNoSubaddressError(err) { assert.equal("Address must not be a subaddress", err.message); } - _testSignatureHeaderCheckError(err) { + protected testSignatureHeaderCheckError(err) { assert.equal("Signature header check error", err.message); } } @@ -5172,22 +5161,25 @@ async function testAccount(account) { assert(account); assert(account.getIndex() >= 0); await MoneroUtils.validateAddress(account.getPrimaryAddress(), TestUtils.NETWORK_TYPE); - TestUtils.testUnsignedBigInteger(account.getBalance()); - TestUtils.testUnsignedBigInteger(account.getUnlockedBalance()); + TestUtils.testUnsignedBigInt(account.getBalance()); + TestUtils.testUnsignedBigInt(account.getUnlockedBalance()); + await MoneroUtils.validateAddress(account.getPrimaryAddress(), TestUtils.NETWORK_TYPE); + TestUtils.testUnsignedBigInt(account.getBalance()); + TestUtils.testUnsignedBigInt(account.getUnlockedBalance()); // if given, test subaddresses and that their balances add up to account balances if (account.getSubaddresses()) { - let balance = new BigInteger(0); - let unlockedBalance = new BigInteger(0); + let balance = BigInt(0); + let unlockedBalance = BigInt(0); for (let i = 0; i < account.getSubaddresses().length; i++) { testSubaddress(account.getSubaddresses()[i]); assert.equal(account.getSubaddresses()[i].getAccountIndex(), account.getIndex()); assert.equal(account.getSubaddresses()[i].getIndex(), i); - balance = balance.add(account.getSubaddresses()[i].getBalance()); - unlockedBalance = unlockedBalance.add(account.getSubaddresses()[i].getUnlockedBalance()); + balance = balance + (account.getSubaddresses()[i].getBalance()); + unlockedBalance = unlockedBalance + (account.getSubaddresses()[i].getUnlockedBalance()); } - assert(account.getBalance().compare(balance) === 0, "Subaddress balances " + balance.toString() + " != account " + account.getIndex() + " balance " + account.getBalance().toString()); - assert(account.getUnlockedBalance().compare(unlockedBalance) === 0, "Subaddress unlocked balances " + unlockedBalance.toString() + " != account " + account.getIndex() + " unlocked balance " + account.getUnlockedBalance().toString()); + assert.equal(account.getBalance(), balance, "Subaddress balances " + balance.toString() + " != account " + account.getIndex() + " balance " + account.getBalance().toString()); + assert.equal(account.getUnlockedBalance(), unlockedBalance, "Subaddress unlocked balances " + unlockedBalance.toString() + " != account " + account.getIndex() + " unlocked balance " + account.getUnlockedBalance().toString()); } // tag must be undefined or non-empty @@ -5201,11 +5193,11 @@ function testSubaddress(subaddress) { assert(subaddress.getAddress()); assert(subaddress.getLabel() === undefined || typeof subaddress.getLabel() === "string"); if (typeof subaddress.getLabel() === "string") assert(subaddress.getLabel().length > 0); - TestUtils.testUnsignedBigInteger(subaddress.getBalance()); - TestUtils.testUnsignedBigInteger(subaddress.getUnlockedBalance()); + TestUtils.testUnsignedBigInt(subaddress.getBalance()); + TestUtils.testUnsignedBigInt(subaddress.getUnlockedBalance()); assert(subaddress.getNumUnspentOutputs() >= 0); - assert(typeof subaddress.isUsed() === "boolean"); - if (subaddress.getBalance().compare(new BigInteger(0)) > 0) assert(subaddress.isUsed()); + assert(typeof subaddress.getIsUsed() === "boolean"); + if (subaddress.getBalance() > 0n) assert(subaddress.getIsUsed()); assert(subaddress.getNumBlocksToUnlock() >= 0); } @@ -5226,33 +5218,33 @@ async function getRandomTransactions(wallet, query, minTxs, maxTxs) { else return txs.slice(0, Math.min(maxTxs, txs.length)); } -async function testTransfer(transfer, ctx) { +async function testTransfer(transfer, ctx?) { if (ctx === undefined) ctx = {}; assert(transfer instanceof MoneroTransfer); - TestUtils.testUnsignedBigInteger(transfer.getAmount()); + TestUtils.testUnsignedBigInt(transfer.getAmount()); if (!ctx.isSweepOutputResponse) assert(transfer.getAccountIndex() >= 0); - if (transfer.isIncoming()) testIncomingTransfer(transfer); + if (transfer.getIsIncoming()) testIncomingTransfer(transfer); else await testOutgoingTransfer(transfer, ctx); // transfer and tx reference each other assert(transfer.getTx()); if (transfer !== transfer.getTx().getOutgoingTransfer()) { assert(transfer.getTx().getIncomingTransfers()); - assert(transfer.getTx().getIncomingTransfers().includes(transfer), "Transaction does not reference given transfer"); + assert(transfer.getTx().getIncomingTransfers().includes(transfer as MoneroIncomingTransfer), "Transaction does not reference given transfer"); } } function testIncomingTransfer(transfer) { - assert(transfer.isIncoming()); - assert(!transfer.isOutgoing()); + assert(transfer.getIsIncoming()); + assert(!transfer.getIsOutgoing()); assert(transfer.getAddress()); assert(transfer.getSubaddressIndex() >= 0); assert(transfer.getNumSuggestedConfirmations() > 0); } async function testOutgoingTransfer(transfer, ctx) { - assert(!transfer.isIncoming()); - assert(transfer.isOutgoing()); + assert(!transfer.getIsIncoming()); + assert(transfer.getIsOutgoing()); if (!ctx.isSendResponse) assert(transfer.getSubaddressIndices()); if (transfer.getSubaddressIndices()) { assert(transfer.getSubaddressIndices().length >= 1); @@ -5266,20 +5258,20 @@ async function testOutgoingTransfer(transfer, ctx) { // test destinations sum to outgoing amount if (transfer.getDestinations()) { assert(transfer.getDestinations().length > 0); - let sum = new BigInteger(0); + let sum = BigInt(0); for (let destination of transfer.getDestinations()) { await testDestination(destination); - TestUtils.testUnsignedBigInteger(destination.getAmount(), true); - sum = sum.add(destination.getAmount()); + TestUtils.testUnsignedBigInt(destination.getAmount(), true); + sum += destination.getAmount(); } - if (transfer.getAmount().compare(sum) !== 0) console.log(transfer.getTx().getTxSet() === undefined ? transfer.getTx().toString() : transfer.getTx().getTxSet().toString()); + if (transfer.getAmount() !== sum) console.log(transfer.getTx().getTxSet() === undefined ? transfer.getTx().toString() : transfer.getTx().getTxSet().toString()); assert.equal(sum.toString(), transfer.getAmount().toString()); } } async function testDestination(destination) { await MoneroUtils.validateAddress(destination.getAddress(), TestUtils.NETWORK_TYPE); - TestUtils.testUnsignedBigInteger(destination.getAmount(), true); + TestUtils.testUnsignedBigInt(destination.getAmount(), true); } function testInputWallet(input) { @@ -5296,13 +5288,13 @@ function testOutputWallet(output) { assert(output.getAccountIndex() >= 0); assert(output.getSubaddressIndex() >= 0); assert(output.getIndex() >= 0); - assert.equal(typeof output.isSpent(), "boolean"); - assert.equal(typeof output.isLocked(), "boolean"); - assert.equal(typeof output.isFrozen(), "boolean"); + assert.equal(typeof output.getIsSpent(), "boolean"); + assert.equal(typeof output.getIsLocked(), "boolean"); + assert.equal(typeof output.getIsFrozen(), "boolean"); assert(output.getKeyImage()); assert(output.getKeyImage() instanceof MoneroKeyImage); assert(output.getKeyImage().getHex()); - TestUtils.testUnsignedBigInteger(output.getAmount(), true); + TestUtils.testUnsignedBigInt(output.getAmount(), true); // output has circular reference to its transaction which has some initialized fields let tx = output.getTx(); @@ -5310,10 +5302,10 @@ function testOutputWallet(output) { assert(tx instanceof MoneroTxWallet); assert(tx.getOutputs().includes(output)); assert(tx.getHash()); - assert.equal(typeof tx.isLocked(), "boolean"); - assert.equal(tx.isConfirmed(), true); // TODO monero-wallet-rpc: possible to get unconfirmed outputs? - assert.equal(tx.isRelayed(), true); - assert.equal(tx.isFailed(), false); + assert.equal(typeof tx.getIsLocked(), "boolean"); + assert.equal(tx.getIsConfirmed(), true); // TODO monero-wallet-rpc: possible to get unconfirmed outputs? + assert.equal(tx.getIsRelayed(), true); + assert.equal(tx.getIsFailed(), false); assert(tx.getHeight() > 0); // test copying @@ -5350,28 +5342,28 @@ function testCommonTxSets(txs, hasSigned, hasUnsigned, hasMultisig) { } } -function testCheckTx(tx, check) { - assert.equal(typeof check.isGood(), "boolean"); - if (check.isGood()) { +function testCheckTx(tx, check: MoneroCheckTx) { + assert.equal(typeof check.getIsGood(), "boolean"); + if (check.getIsGood()) { assert(check.getNumConfirmations() >= 0); - assert.equal(typeof check.inTxPool(), "boolean"); - TestUtils.testUnsignedBigInteger(check.getReceivedAmount()); - if (check.inTxPool()) assert.equal(0, check.getNumConfirmations()); + assert.equal(typeof check.getInTxPool(), "boolean"); + TestUtils.testUnsignedBigInt(check.getReceivedAmount()); + if (check.getInTxPool()) assert.equal(0, check.getNumConfirmations()); else assert(check.getNumConfirmations() > 0); // TODO (monero-wall-rpc) this fails (confirmations is 0) for (at least one) transaction that has 1 confirmation on testCheckTxKey() } else { assert.equal(check.getNumConfirmations(), undefined); - assert.equal(check.inTxPool(), undefined); + assert.equal(check.getInTxPool(), undefined); assert.equal(check.getReceivedAmount(), undefined); } } -function testCheckReserve(check) { - assert.equal(typeof check.isGood(), "boolean"); - if (check.isGood()) { - TestUtils.testUnsignedBigInteger(check.getTotalAmount()); - assert(check.getTotalAmount().compare(new BigInteger(0)) >= 0); - TestUtils.testUnsignedBigInteger(check.getUnconfirmedSpentAmount()); - assert(check.getUnconfirmedSpentAmount().compare(new BigInteger(0)) >= 0); +function testCheckReserve(check: MoneroCheckReserve) { + assert.equal(typeof check.getIsGood(), "boolean"); + if (check.getIsGood()) { + TestUtils.testUnsignedBigInt(check.getTotalAmount()); + assert(check.getTotalAmount() >= 0n); + TestUtils.testUnsignedBigInt(check.getUnconfirmedSpentAmount()); + assert(check.getUnconfirmedSpentAmount() >= 0n); } else { assert.equal(check.getTotalAmount(), undefined); assert.equal(check.getUnconfirmedSpentAmount(), undefined); @@ -5389,22 +5381,22 @@ async function testDescribedTxSet(describedTxSet) { assert.equal(describedTxSet.getMultisigTxHex(), undefined); for (let describedTx of describedTxSet.getTxs()) { assert(describedTx.getTxSet() === describedTxSet); - TestUtils.testUnsignedBigInteger(describedTx.getInputSum(), true); - TestUtils.testUnsignedBigInteger(describedTx.getOutputSum(), true); - TestUtils.testUnsignedBigInteger(describedTx.getFee()); - TestUtils.testUnsignedBigInteger(describedTx.getChangeAmount()); - if (describedTx.getChangeAmount().compare(new BigInteger(0)) === 0) assert.equal(describedTx.getChangeAddress(), undefined); + TestUtils.testUnsignedBigInt(describedTx.getInputSum(), true); + TestUtils.testUnsignedBigInt(describedTx.getOutputSum(), true); + TestUtils.testUnsignedBigInt(describedTx.getFee()); + TestUtils.testUnsignedBigInt(describedTx.getChangeAmount()); + if (describedTx.getChangeAmount() === 0n) assert.equal(describedTx.getChangeAddress(), undefined); else await MoneroUtils.validateAddress(describedTx.getChangeAddress(), TestUtils.NETWORK_TYPE); assert(describedTx.getRingSize() > 1); - assert(describedTx.getUnlockTime().compare(new BigInteger(0)) >= 0); + assert (describedTx.getUnlockTime() >= 0n); assert(describedTx.getNumDummyOutputs() >= 0); assert(describedTx.getExtraHex()); assert(describedTx.getPaymentId() === undefined || describedTx.getPaymentId().length > 0); - assert(describedTx.isOutgoing()); + assert(describedTx.getIsOutgoing()); assert.notEqual(describedTx.getOutgoingTransfer(), undefined); assert.notEqual(describedTx.getOutgoingTransfer().getDestinations(), undefined); assert(describedTx.getOutgoingTransfer().getDestinations().length > 0); - assert.equal(describedTx.isIncoming(), undefined); + assert.equal(describedTx.getIsIncoming(), undefined); for (let destination of describedTx.getOutgoingTransfer().getDestinations()) { await testDestination(destination); } @@ -5421,7 +5413,7 @@ async function testAddressBookEntry(entry) { * Tests the integrity of the full structure in the given txs from the block down * to transfers / destinations. */ -function testGetTxsStructure(txs, query = undefined) { +function testGetTxsStructure(txs: MoneroTxWallet[], query?) { // normalize query if (query === undefined) query = new MoneroTxQuery(); @@ -5429,8 +5421,8 @@ function testGetTxsStructure(txs, query = undefined) { // collect unique blocks in order (using set and list instead of TreeSet for direct portability to other languages) let seenBlocks = new Set(); - let blocks = []; - let unconfirmedTxs = []; + let blocks: MoneroBlock[] = []; + let unconfirmedTxs: MoneroTxWallet[] = []; for (let tx of txs) { if (tx.getBlock() === undefined) unconfirmedTxs.push(tx); else { @@ -5452,7 +5444,7 @@ function testGetTxsStructure(txs, query = undefined) { // test that txs and blocks reference each other and blocks are in ascending order unless specific tx hashes requested let index = 0; - let prevBlockHeight = undefined; + let prevBlockHeight: number | undefined = undefined; for (let block of blocks) { if (prevBlockHeight === undefined) prevBlockHeight = block.getHeight(); else if (query.getHashes() === undefined) assert(block.getHeight() > prevBlockHeight, "Blocks are not in order of heights: " + prevBlockHeight + " vs " + block.getHeight()); @@ -5469,8 +5461,8 @@ function testGetTxsStructure(txs, query = undefined) { // test that incoming transfers are in order of ascending accounts and subaddresses for (let tx of txs) { - let prevAccountIdx = undefined; - let prevSubaddressIdx = undefined; + let prevAccountIdx: number | undefined = undefined; + let prevSubaddressIdx: number | undefined = undefined; if (tx.getIncomingTransfers() === undefined) continue; for (let transfer of tx.getIncomingTransfers()) { if (prevAccountIdx === undefined) prevAccountIdx = transfer.getAccountIndex(); @@ -5515,6 +5507,14 @@ function getModes(counts) { */ class ReceivedOutputNotificationTester extends MoneroWalletListener { + txHash: string; + testComplete: boolean; + unlockedSeen: boolean; + lastOnNewBlockHeight: number; + lastOnBalancesChangedBalance: BigInt; + lastOnBalancesChangedUnlockedBalance: BigInt; + lastNotifiedOutput: MoneroOutputWallet; + constructor(txHash) { super(); this.txHash = txHash; @@ -5522,16 +5522,16 @@ class ReceivedOutputNotificationTester extends MoneroWalletListener { this.unlockedSeen = false; } - onNewBlock(height) { + async onNewBlock(height) { this.lastOnNewBlockHeight = height; } - onBalancesChanged(newBalance, newUnlockedBalance) { + async onBalancesChanged(newBalance, newUnlockedBalance) { this.lastOnBalancesChangedBalance = newBalance; this.lastOnBalancesChangedUnlockedBalance = newUnlockedBalance; } - onOutputReceived(output) { + async onOutputReceived(output) { if (output.getTx().getHash() === this.txHash) this.lastNotifiedOutput = output; } } @@ -5540,6 +5540,13 @@ class ReceivedOutputNotificationTester extends MoneroWalletListener { * Wallet listener to collect output notifications. */ class WalletNotificationCollector extends MoneroWalletListener { + + listening: any; + blockNotifications: any; + balanceNotifications: any; + outputsReceived: any; + outputsSpent: any; + lastNotification: any; constructor() { super(); @@ -5550,13 +5557,13 @@ class WalletNotificationCollector extends MoneroWalletListener { this.outputsSpent = []; } - onNewBlock(height) { + async onNewBlock(height) { assert(this.listening); if (this.blockNotifications.length > 0) assert(height === this.blockNotifications[this.blockNotifications.length - 1] + 1); this.blockNotifications.push(height); } - onBalancesChanged(newBalance, newUnlockedBalance) { + async onBalancesChanged(newBalance, newUnlockedBalance) { assert(this.listening); if (this.balanceNotifications.length > 0) { this.lastNotification = this.balanceNotifications[this.balanceNotifications.length - 1]; @@ -5565,12 +5572,12 @@ class WalletNotificationCollector extends MoneroWalletListener { this.balanceNotifications.push({balance: newBalance, unlockedBalance: newUnlockedBalance}); } - onOutputReceived(output) { + async onOutputReceived(output) { assert(this.listening); this.outputsReceived.push(output); } - onOutputSpent(output) { + async onOutputSpent(output) { assert(this.listening); this.outputsSpent.push(output); } @@ -5583,11 +5590,11 @@ class WalletNotificationCollector extends MoneroWalletListener { return this.balanceNotifications; } - getOutputsReceived(query) { + getOutputsReceived(query?) { return Filter.apply(query, this.outputsReceived); } - getOutputsSpent(query) { + getOutputsSpent(query?) { return Filter.apply(query, this.outputsSpent); } @@ -5596,4 +5603,3 @@ class WalletNotificationCollector extends MoneroWalletListener { } } -module.exports = TestMoneroWalletCommon; \ No newline at end of file diff --git a/src/test/TestMoneroWalletFull.js b/src/test/TestMoneroWalletFull.ts similarity index 85% rename from src/test/TestMoneroWalletFull.js rename to src/test/TestMoneroWalletFull.ts index a36bc1895..990cdcd24 100644 --- a/src/test/TestMoneroWalletFull.js +++ b/src/test/TestMoneroWalletFull.ts @@ -1,36 +1,37 @@ -const assert = require("assert"); -const TestUtils = require("./utils/TestUtils"); -const TestMoneroWalletCommon = require("./TestMoneroWalletCommon"); -const StartMining = require("./utils/StartMining"); -const WalletSyncPrinter = require("./utils/WalletSyncPrinter"); -const WalletEqualityUtils = require("./utils/WalletEqualityUtils"); -const monerojs = require("../../index"); -const MoneroWalletListener = monerojs.MoneroWalletListener; -const LibraryUtils = monerojs.LibraryUtils; -const MoneroWalletConfig = monerojs.MoneroWalletConfig; -const GenUtils = monerojs.GenUtils; -const MoneroUtils = monerojs.MoneroUtils; -const BigInteger = monerojs.BigInteger; -const MoneroNetworkType = monerojs.MoneroNetworkType; -const MoneroTxWallet = monerojs.MoneroTxWallet; -const MoneroTxConfig = monerojs.MoneroTxConfig; -const MoneroDestination = monerojs.MoneroDestination; -const MoneroOutputQuery = monerojs.MoneroOutputQuery; -const MoneroOutputWallet = monerojs.MoneroOutputWallet; -const MoneroRpcConnection = monerojs.MoneroRpcConnection; -const MoneroWalletFull = monerojs.MoneroWalletFull; +import assert from "assert"; +import TestUtils from "./utils/TestUtils"; +import TestMoneroWalletCommon from "./TestMoneroWalletCommon"; +import StartMining from "./utils/StartMining"; +import WalletSyncPrinter from "./utils/WalletSyncPrinter"; +import WalletEqualityUtils from "./utils/WalletEqualityUtils"; +import {createWalletFull, + openWalletFull, + LibraryUtils, + MoneroWalletConfig, + GenUtils, + MoneroUtils, + MoneroNetworkType, + MoneroTxWallet, + MoneroOutputQuery, + MoneroOutputWallet, + MoneroRpcConnection, + MoneroWallet, + MoneroWalletFull, + MoneroWalletRpc} from "../../index"; /** * Tests a Monero wallet using WebAssembly to bridge to monero-project's wallet2. */ -class TestMoneroWalletFull extends TestMoneroWalletCommon { +export default class TestMoneroWalletFull extends TestMoneroWalletCommon { + + static FULL_TESTS_RUN: boolean; constructor(testConfig) { super(testConfig); } - async beforeAll(currentTest) { - await super.beforeAll(currentTest); + async beforeAll() { + await super.beforeAll(); } async beforeEach(currentTest) { @@ -51,8 +52,9 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // remove non-whitelisted wallets let whitelist = [TestUtils.WALLET_NAME, "ground_truth", "moved"]; - let items = TestUtils.getDefaultFs().readdirSync(TestUtils.TEST_WALLETS_DIR); + let items = (await TestUtils.getDefaultFs()).readdirSync(TestUtils.TEST_WALLETS_DIR, "buffer"); for (let item of items) { + item = item + ""; // get filename as string let found = false; for (let whitelisted of whitelist) { if (item === whitelisted || item === whitelisted + ".keys" || item === whitelisted + ".address.txt") { @@ -60,7 +62,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { break; } } - if (!found) TestUtils.getDefaultFs().unlinkSync(TestUtils.TEST_WALLETS_DIR + "/" + item); + if (!found) (await TestUtils.getDefaultFs()).unlinkSync(TestUtils.TEST_WALLETS_DIR + "/" + item); } } @@ -72,23 +74,23 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { return await TestUtils.getDaemonRpc(); } - async openWallet(config, startSyncing) { + async openWallet(config: Partial, startSyncing?: any): Promise { // assign defaults config = new MoneroWalletConfig(config); if (config.getPassword() === undefined) config.setPassword(TestUtils.WALLET_PASSWORD); if (config.getNetworkType() === undefined) config.setNetworkType(TestUtils.NETWORK_TYPE); if (config.getProxyToWorker() === undefined) config.setProxyToWorker(TestUtils.PROXY_TO_WORKER); - if (config.getServer() === undefined && config.getServerUri() === undefined) config.setServer(TestUtils.getDaemonRpcConnection()); - if (config.getFs() === undefined) config.setFs(TestUtils.getDefaultFs()); + if (config.getServer() === undefined) config.setServer(TestUtils.getDaemonRpcConnection()); + if (config.getFs() === undefined) config.setFs(await TestUtils.getDefaultFs()); // open wallet - let wallet = await monerojs.openWalletFull(config); + let wallet = await openWalletFull(config); if (startSyncing !== false && await wallet.isConnectedToDaemon()) await wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); return wallet; } - async createWallet(config, startSyncing) { + async createWallet(config?: Partial, startSyncing?): Promise { // assign defaults config = new MoneroWalletConfig(config); @@ -99,20 +101,20 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { if (!config.getRestoreHeight() && !random) config.setRestoreHeight(0); if (!config.getServer() && !config.getConnectionManager()) config.setServer(TestUtils.getDaemonRpcConnection()); if (config.getProxyToWorker() === undefined) config.setProxyToWorker(TestUtils.PROXY_TO_WORKER); - if (config.getFs() === undefined) config.setFs(TestUtils.getDefaultFs()); + if (config.getFs() === undefined) config.setFs(await TestUtils.getDefaultFs()); // create wallet - let wallet = await monerojs.createWalletFull(config); + let wallet = await createWalletFull(config); if (!random) assert.equal(await wallet.getRestoreHeight(), config.getRestoreHeight() === undefined ? 0 : config.getRestoreHeight()); if (startSyncing !== false && await wallet.isConnectedToDaemon()) await wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); return wallet; } - async closeWallet(wallet, save) { + async closeWallet(wallet, save?) { await wallet.close(save); } - async getSeedLanguages() { + async getSeedLanguages(): Promise { return await MoneroWalletFull.getSeedLanguages(); } @@ -130,14 +132,14 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { afterEach(async function() { await that.afterEach(this.currentTest); }); // run tests specific to full wallet - that._testWalletFull(); + that.testWalletFull(); // run common tests that.runCommonTests(); }); } - _testWalletFull() { + protected testWalletFull() { let that = this; let testConfig = this.testConfig; describe("Tests specific to WebAssembly wallet", function() { @@ -148,7 +150,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { //let wallet = await that.createWallet({seed: TestUtils.SEED, restoreHeight: restoreHeight}, false); for (let i = 0; i < 100; i++) { console.log(process.memoryUsage()); - await _testSyncSeed(TestUtils.FIRST_RECEIVE_HEIGHT, undefined, false, true); + await testSyncSeed(TestUtils.FIRST_RECEIVE_HEIGHT, undefined, false, true); } }); @@ -161,7 +163,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { if (testConfig.testNonRelays && !testConfig.liteMode) it("Can open, sync, and close wallets repeatedly", async function() { - let wallets = []; + let wallets: MoneroWalletFull[] = []; for (let i = 0; i < 4; i++) { let wallet = await that.createWallet({seed: TestUtils.SEED, restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT}); await wallet.startSyncing(); @@ -172,7 +174,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { if (testConfig.testNonRelays) it("Can get the daemon's max peer height", async function() { - let height = await that.wallet.getDaemonMaxPeerHeight(); + let height = await (that.wallet as MoneroWalletFull).getDaemonMaxPeerHeight(); assert(height > 0); }); @@ -180,7 +182,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { it("Can create a random full wallet", async function() { // create unconnected random wallet - let wallet = await that.createWallet({networkType: MoneroNetworkType.MAINNET, serverUri: TestUtils.OFFLINE_SERVER_URI}); + let wallet = await that.createWallet({networkType: MoneroNetworkType.MAINNET, server: TestUtils.OFFLINE_SERVER_URI}); await MoneroUtils.validateMnemonic(await wallet.getSeed()); await MoneroUtils.validateAddress(await wallet.getPrimaryAddress(), MoneroNetworkType.MAINNET); assert.equal(await wallet.getNetworkType(), MoneroNetworkType.MAINNET); @@ -194,7 +196,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // cannot get daemon chain height try { await wallet.getDaemonHeight(); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Wallet is not connected to daemon"); } @@ -228,7 +230,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { it("Can create a full wallet from seed", async function() { // create unconnected wallet with mnemonic - let wallet = await that.createWallet({seed: TestUtils.SEED, serverUri: TestUtils.OFFLINE_SERVER_URI}); + let wallet = await that.createWallet({seed: TestUtils.SEED, server: TestUtils.OFFLINE_SERVER_URI}); assert.equal(await wallet.getSeed(), TestUtils.SEED); assert.equal(await wallet.getPrimaryAddress(), TestUtils.ADDRESS); assert.equal(await wallet.getNetworkType(), TestUtils.NETWORK_TYPE); @@ -238,7 +240,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { assert(!(await wallet.isSynced())); assert.equal(await wallet.getHeight(), 1); assert.equal(await wallet.getRestoreHeight(), 0); - try { await wallet.startSyncing(); } catch (e) { assert.equal(e.message, "Wallet is not connected to daemon"); } + try { await wallet.startSyncing(); } catch (e: any) { assert.equal(e.message, "Wallet is not connected to daemon"); } await wallet.close(); // create wallet without restore height @@ -260,7 +262,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // create wallet with seed, no connection, and restore height let restoreHeight = 10000; - wallet = await that.createWallet({seed: TestUtils.SEED, restoreHeight: restoreHeight, serverUri: TestUtils.OFFLINE_SERVER_URI}); + wallet = await that.createWallet({seed: TestUtils.SEED, restoreHeight: restoreHeight, server: TestUtils.OFFLINE_SERVER_URI}); assert.equal(await wallet.getSeed(), TestUtils.SEED); assert.equal(await wallet.getPrimaryAddress(), TestUtils.ADDRESS); assert.equal(await wallet.getNetworkType(), TestUtils.NETWORK_TYPE); @@ -271,7 +273,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { assert.equal(await wallet.getRestoreHeight(), restoreHeight); let path = await wallet.getPath(); await wallet.close(true); - wallet = await that.openWallet({path: path, serverUri: TestUtils.OFFLINE_SERVER_URI}); + wallet = await that.openWallet({path: path, server: TestUtils.OFFLINE_SERVER_URI}); assert(!(await wallet.isConnectedToDaemon())); assert(!(await wallet.isSynced())); assert.equal(await wallet.getHeight(), 1); @@ -282,7 +284,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { wallet = await that.createWallet({seed: TestUtils.SEED, restoreHeight: restoreHeight}, false); assert.equal(await wallet.getSeed(), TestUtils.SEED); assert(await wallet.getPrimaryAddress(), TestUtils.ADDRESS); - assert(await wallet.getNetworkType(), TestUtils.NETWORK_TYPE); + assert.equal(await wallet.getNetworkType(), TestUtils.NETWORK_TYPE); assert(await wallet.getDaemonConnection()); assert(await that.daemon.getRpcConnection() != wallet.getDaemonConnection()); assert.equal((await wallet.getDaemonConnection()).getUri(), (await that.daemon.getRpcConnection()).getUri()); @@ -301,7 +303,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // recreate test wallet from keys let wallet = that.wallet; - let walletKeys = await that.createWallet({serverUri: TestUtils.OFFLINE_SERVER_URI, networkType: await wallet.getNetworkType(), primaryAddress: await wallet.getPrimaryAddress(), privateViewKey: await wallet.getPrivateViewKey(), privateSpendKey: await wallet.getPrivateSpendKey(), restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT}); + let walletKeys = await that.createWallet({server: TestUtils.OFFLINE_SERVER_URI, networkType: await (wallet as MoneroWalletFull).getNetworkType(), primaryAddress: await wallet.getPrimaryAddress(), privateViewKey: await wallet.getPrivateViewKey(), privateSpendKey: await wallet.getPrivateSpendKey(), restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT}); let err; try { assert.equal(await walletKeys.getSeed(), await wallet.getSeed()); @@ -334,7 +336,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { await walletRpc.close(true); // open as full wallet - let walletFull = await monerojs.openWalletFull(new MoneroWalletConfig().setPath(TestUtils.WALLET_RPC_LOCAL_WALLET_DIR + "/" + walletName).setPassword(TestUtils.WALLET_PASSWORD).setNetworkType(TestUtils.NETWORK_TYPE).setServer(TestUtils.DAEMON_RPC_CONFIG)); + let walletFull = await openWalletFull(new MoneroWalletConfig().setPath(TestUtils.WALLET_RPC_LOCAL_WALLET_DIR + "/" + walletName).setPassword(TestUtils.WALLET_PASSWORD).setNetworkType(TestUtils.NETWORK_TYPE).setServer(TestUtils.DAEMON_RPC_CONFIG)); await walletFull.sync(); assert.equal(TestUtils.SEED, await walletFull.getSeed()); assert.equal(TestUtils.ADDRESS, await walletFull.getPrimaryAddress()); @@ -345,15 +347,15 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // create full wallet walletName = GenUtils.getUUID(); let path = TestUtils.WALLET_RPC_LOCAL_WALLET_DIR + "/" + walletName; - walletFull = await monerojs.createWalletFull(new MoneroWalletConfig().setPath(path).setPassword(TestUtils.WALLET_PASSWORD).setNetworkType(TestUtils.NETWORK_TYPE).setSeed(TestUtils.SEED).setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT).setServer(TestUtils.DAEMON_RPC_CONFIG)); + walletFull = await createWalletFull(new MoneroWalletConfig().setPath(path).setPassword(TestUtils.WALLET_PASSWORD).setNetworkType(TestUtils.NETWORK_TYPE).setSeed(TestUtils.SEED).setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT).setServer(TestUtils.DAEMON_RPC_CONFIG)); await walletFull.sync(); balance = await walletFull.getBalance(); outputsHex = await walletFull.exportOutputs(); await walletFull.close(true); // rebuild wallet cache using full wallet - TestUtils.getDefaultFs().unlinkSync(path); - walletFull = await monerojs.openWalletFull(new MoneroWalletConfig().setPath(path).setPassword(TestUtils.WALLET_PASSWORD).setNetworkType(TestUtils.NETWORK_TYPE).setServer(TestUtils.DAEMON_RPC_CONFIG)); + (await TestUtils.getDefaultFs()).unlinkSync(path); + walletFull = await openWalletFull(new MoneroWalletConfig().setPath(path).setPassword(TestUtils.WALLET_PASSWORD).setNetworkType(TestUtils.NETWORK_TYPE).setServer(TestUtils.DAEMON_RPC_CONFIG)); await walletFull.close(true); // open wallet using monero-wallet-rpc @@ -371,15 +373,21 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // create view-only wallet in wallet rpc process let viewOnlyWallet = await TestUtils.startWalletRpcProcess(); - await viewOnlyWallet.createWallet({path: GenUtils.getUUID(), password: TestUtils.WALLET_PASSWORD, primaryAddress: await that.wallet.getPrimaryAddress(), privateViewKey: await that.wallet.getPrivateViewKey(), restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT}); + await viewOnlyWallet.createWallet({ + path: GenUtils.getUUID(), + password: TestUtils.WALLET_PASSWORD, + primaryAddress: await that.wallet.getPrimaryAddress(), + privateViewKey: await that.wallet.getPrivateViewKey(), + restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT + }); await viewOnlyWallet.sync(); // create offline full wallet - let offlineWallet = await that.createWallet({primaryAddress: await that.wallet.getPrimaryAddress(), privateViewKey: await that.wallet.getPrivateViewKey(), privateSpendKey: await that.wallet.getPrivateSpendKey(), serverUri: TestUtils.OFFLINE_SERVER_URI, restoreHeight: 0}); + let offlineWallet = await that.createWallet({primaryAddress: await that.wallet.getPrimaryAddress(), privateViewKey: await that.wallet.getPrivateViewKey(), privateSpendKey: await that.wallet.getPrivateSpendKey(), server: TestUtils.OFFLINE_SERVER_URI, restoreHeight: 0}); // test tx signing with wallets let err; - try { await that._testViewOnlyAndOfflineWallets(viewOnlyWallet, offlineWallet); } + try { await that.testViewOnlyAndOfflineWallets(viewOnlyWallet, offlineWallet); } catch (e) { err = e; } // finally @@ -392,7 +400,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { it("Is compatible with monero-wallet-rpc multisig wallets", async function() { // create participants with monero-wallet-rpc and full wallet - let participants = []; + let participants: MoneroWallet[] = []; participants.push(await (await TestUtils.startWalletRpcProcess()).createWallet(new MoneroWalletConfig().setPath(GenUtils.getUUID()).setPassword(TestUtils.WALLET_PASSWORD))); participants.push(await (await TestUtils.startWalletRpcProcess()).createWallet(new MoneroWalletConfig().setPath(GenUtils.getUUID()).setPassword(TestUtils.WALLET_PASSWORD))); participants.push(await that.createWallet(new MoneroWalletConfig())); @@ -400,7 +408,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // test multisig let err; try { - await that._testMultisigParticipants(participants, 3, 3, true); + await that.testMultisigParticipants(participants, 3, 3, true); } catch (e) { err = e; } @@ -410,7 +418,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { catch (e) { } // save and close participants - if (participants[0] instanceof monerojs.MoneroWalletRpc) await TestUtils.stopWalletRpcProcess(participants[0]); + if (participants[0] instanceof MoneroWalletRpc) await TestUtils.stopWalletRpcProcess(participants[0]); else participants[0].close(true); // multisig tests might restore wallet from seed await TestUtils.stopWalletRpcProcess(participants[1]); await that.closeWallet(participants[2], true); @@ -420,7 +428,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // TODO monero-project: cannot re-sync from lower block height after wallet saved if (testConfig.testNonRelays && !testConfig.liteMode && false) it("Can re-sync an existing wallet from scratch", async function() { - let wallet = await that.openWallet({path: TestUtils.WALLET_FULL_PATH, password: TestUtils.WALLET_PASSWORD, networkType: MoneroNetworkType.TESTNET, serverUri: TestUtils.OFFLINE_SERVER_URI}, true); // wallet must already exist + let wallet = await that.openWallet({path: TestUtils.WALLET_FULL_PATH, password: TestUtils.WALLET_PASSWORD, networkType: MoneroNetworkType.TESTNET, server: TestUtils.OFFLINE_SERVER_URI}, true); // wallet must already exist await wallet.setDaemonConnection(TestUtils.getDaemonRpcConnection()); //long startHeight = TestUtils.TEST_RESTORE_HEIGHT; let startHeight = 0; @@ -479,7 +487,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { assert.equal(await wallet.getHeight(), await that.daemon.getHeight()); // compare wallet to ground truth - await TestMoneroWalletFull._testWalletEqualityOnChain(walletGt, wallet); + await TestMoneroWalletFull.testWalletEqualityOnChain(walletGt, wallet); } catch (e) { err = e; } @@ -490,12 +498,12 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { if (err) throw err; // attempt to sync unconnected wallet - wallet = await that.createWallet({serverUri: TestUtils.OFFLINE_SERVER_URI}); + wallet = await that.createWallet({server: TestUtils.OFFLINE_SERVER_URI}); err = undefined; try { await wallet.sync(); throw new Error("Should have thrown exception"); - } catch (e1) { + } catch (e1: any) { try { assert.equal(e1.message, "Wallet is not connected to daemon"); } catch (e2) { @@ -510,30 +518,30 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { if (false && testConfig.testNonRelays && !testConfig.liteMode) // TODO: re-enable before release it("Can sync a wallet created from seed from the genesis", async function() { - await _testSyncSeed(undefined, undefined, true, false); + await testSyncSeed(undefined, undefined, true, false); }); if (testConfig.testNonRelays) it("Can sync a wallet created from seed from a restore height", async function() { - await _testSyncSeed(undefined, TestUtils.FIRST_RECEIVE_HEIGHT); + await testSyncSeed(undefined, TestUtils.FIRST_RECEIVE_HEIGHT); }); if (testConfig.testNonRelays && !testConfig.liteMode) it("Can sync a wallet created from seed from a start height.", async function() { - await _testSyncSeed(TestUtils.FIRST_RECEIVE_HEIGHT, undefined, false, true); + await testSyncSeed(TestUtils.FIRST_RECEIVE_HEIGHT, undefined, false, true); }); if (testConfig.testNonRelays && !testConfig.liteMode) it("Can sync a wallet created from seed from a start height less than the restore height", async function() { - await _testSyncSeed(TestUtils.FIRST_RECEIVE_HEIGHT, TestUtils.FIRST_RECEIVE_HEIGHT + 3); + await testSyncSeed(TestUtils.FIRST_RECEIVE_HEIGHT, TestUtils.FIRST_RECEIVE_HEIGHT + 3); }); if (testConfig.testNonRelays && !testConfig.liteMode) it("Can sync a wallet created from seed from a start height greater than the restore height", async function() { - await _testSyncSeed(TestUtils.FIRST_RECEIVE_HEIGHT + 3, TestUtils.FIRST_RECEIVE_HEIGHT); + await testSyncSeed(TestUtils.FIRST_RECEIVE_HEIGHT + 3, TestUtils.FIRST_RECEIVE_HEIGHT); }); - async function _testSyncSeed(startHeight, restoreHeight, skipGtComparison, testPostSyncNotifications) { + async function testSyncSeed(startHeight, restoreHeight?, skipGtComparison?, testPostSyncNotifications?) { assert(await that.daemon.isConnected(), "Not connected to daemon"); if (startHeight !== undefined && restoreHeight != undefined) assert(startHeight <= TestUtils.FIRST_RECEIVE_HEIGHT || restoreHeight <= TestUtils.FIRST_RECEIVE_HEIGHT); @@ -547,7 +555,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { let endHeightExpected = await wallet.getDaemonMaxPeerHeight(); // test wallet and close as final step - let walletGt = undefined; + let walletGt: MoneroWallet | undefined = undefined; let err = undefined; // to permit final cleanup like Java's try...catch...finally try { @@ -588,7 +596,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // compare with ground truth if (!skipGtComparison) { walletGt = await TestUtils.createWalletGroundTruth(TestUtils.NETWORK_TYPE, await wallet.getSeed(), startHeight, restoreHeight); - await TestMoneroWalletFull._testWalletEqualityOnChain(walletGt, wallet); + await TestMoneroWalletFull.testWalletEqualityOnChain(walletGt, wallet); } // if testing post-sync notifications, wait for a block to be added to the chain @@ -601,7 +609,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // attempt to start mining to push the network along // TODO: TestUtils.tryStartMining() : reqId, TestUtils.tryStopMining(reqId) let startedMining = false; let miningStatus = await that.daemon.getMiningStatus(); - if (!miningStatus.isActive()) { + if (!miningStatus.getIsActive()) { try { await StartMining.startMining(); startedMining = true; @@ -679,7 +687,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { assert.equal((await walletKeys.getTxs())[0].getHeight(), TestUtils.FIRST_RECEIVE_HEIGHT); // wallet should be fully synced so first tx happens on true restore height // compare with ground truth - await TestMoneroWalletFull._testWalletEqualityOnChain(walletGt, walletKeys); + await TestMoneroWalletFull.testWalletEqualityOnChain(walletGt, walletKeys); } catch (e) { err = e; } @@ -696,14 +704,14 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // test unconnected wallet let err; // used to emulate Java's try...catch...finally - let path = TestMoneroWalletFull._getRandomWalletPath(); - let wallet = await that.createWallet({path: path, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, serverUri: TestUtils.OFFLINE_SERVER_URI}); + let path = TestMoneroWalletFull.getRandomWalletPath(); + let wallet = await that.createWallet({path: path, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, server: TestUtils.OFFLINE_SERVER_URI}); try { assert.notEqual(await wallet.getSeed(), undefined); assert.equal(await wallet.getHeight(), 1); - assert.equal(await wallet.getBalance(), BigInteger.parse("0")); + assert.equal(await wallet.getBalance(), BigInt("0")); await wallet.startSyncing(); - } catch (e1) { // first error is expected + } catch (e1: any) { // first error is expected try { assert.equal(e1.message, "Wallet is not connected to daemon"); } catch (e2) { @@ -716,15 +724,15 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { if (err) throw err; // test connecting wallet - path = TestMoneroWalletFull._getRandomWalletPath(); - wallet = await that.createWallet({path: path, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, serverUri: TestUtils.OFFLINE_SERVER_URI}); + path = TestMoneroWalletFull.getRandomWalletPath(); + wallet = await that.createWallet({path: path, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, server: TestUtils.OFFLINE_SERVER_URI}); try { assert.notEqual(wallet.getSeed(), undefined); assert(!await wallet.isConnectedToDaemon()); await wallet.setDaemonConnection(await that.daemon.getRpcConnection()); assert.equal(await wallet.getHeight(), 1); assert(!await wallet.isSynced()); - assert.equal((await wallet.getBalance()).compare(new BigInteger(0)), 0); + assert.equal(await wallet.getBalance(), 0n); let chainHeight = await wallet.getDaemonHeight(); await wallet.setRestoreHeight(chainHeight - 3); await wallet.startSyncing(); @@ -746,7 +754,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // test that sync starts automatically let restoreHeight = await that.daemon.getHeight() - 100; - path = TestMoneroWalletFull._getRandomWalletPath(); + path = TestMoneroWalletFull.getRandomWalletPath(); wallet = await that.createWallet({path: path, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, seed: TestUtils.SEED, server: await that.daemon.getRpcConnection(), restoreHeight: restoreHeight}, false); try { @@ -754,7 +762,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { assert.equal(await wallet.getHeight(), 1); assert.equal(await wallet.getRestoreHeight(), restoreHeight); assert(!(await wallet.isSynced())); - assert.equal(await wallet.getBalance(), new BigInteger(0)); + assert.equal(await wallet.getBalance(), BigInt(0)); await wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); // pause for sync to start @@ -853,21 +861,21 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { console.log("Creating " + M + "/" + N + " multisig wallet"); // create participating wallets - let wallets = [] + let wallets: MoneroWallet[] = [] for (let i = 0; i < N; i++) { wallets.push(await that.createWallet()); } // prepare and collect multisig hex from each participant - let preparedMultisigHexes = [] + let preparedMultisigHexes: string[] = [] for (let wallet of wallets) preparedMultisigHexes.push(await wallet.prepareMultisig()); // make each wallet multisig and collect results - let madeMultisigHexes = []; + let madeMultisigHexes: string[] = []; for (let i = 0; i < wallets.length; i++) { // collect prepared multisig hexes from wallet's peers - let peerMultisigHexes = []; + let peerMultisigHexes: string[] = []; for (let j = 0; j < wallets.length; j++) if (j !== i) peerMultisigHexes.push(preparedMultisigHexes[j]); // make wallet multisig and collect result hex @@ -880,7 +888,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { for (let i = 0; i < N - M + 1; i++) { // exchange multisig keys among participants and collect results for next round if applicable - let resultMultisigHexes = []; + let resultMultisigHexes: string[] = []; for (let wallet of wallets) { // import the multisig hex of other participants and collect results @@ -895,10 +903,10 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // wallets are now multisig for (let wallet of wallets) { let primaryAddress = await wallet.getAddress(0, 0); - await MoneroUtils.validateAddress(primaryAddress, await wallet.getNetworkType()); + await MoneroUtils.validateAddress(primaryAddress, await (wallet as MoneroWalletFull).getNetworkType()); let info = await wallet.getMultisigInfo(); - assert(info.isMultisig()); - assert(info.isReady()); + assert(info.getIsMultisig()); + assert(info.getIsReady()); assert.equal(info.getThreshold(), M); assert.equal(info.getNumParticipants(), N); await wallet.close(true); @@ -909,27 +917,27 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { it("Can be saved", async function() { // create unique path for new test wallet - let path = TestMoneroWalletFull._getRandomWalletPath(); + let path = TestMoneroWalletFull.getRandomWalletPath(); // wallet does not exist - assert(!(await MoneroWalletFull.walletExists(path, TestUtils.getDefaultFs()))); + assert(!(await MoneroWalletFull.walletExists(path, await TestUtils.getDefaultFs()))); // cannot open non-existent wallet try { - await that.openWallet({path: path, serverUri: ""}); + await that.openWallet({path: path, server: ""}); throw new Error("Cannot open non-existent wallet"); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Wallet does not exist at path: " + path); } // create wallet at the path let restoreHeight = await that.daemon.getHeight() - 200; - let wallet = await that.createWallet({path: path, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, seed: TestUtils.SEED, restoreHeight: restoreHeight, serverUri: TestUtils.OFFLINE_SERVER_URI}); + let wallet = await that.createWallet({path: path, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, seed: TestUtils.SEED, restoreHeight: restoreHeight, server: TestUtils.OFFLINE_SERVER_URI}); // test wallet at newly created state let err; try { - assert(await MoneroWalletFull.walletExists(path, TestUtils.getDefaultFs())); + assert(await MoneroWalletFull.walletExists(path, await TestUtils.getDefaultFs())); assert.equal(await wallet.getSeed(), TestUtils.SEED); assert.equal(await wallet.getNetworkType(), TestUtils.NETWORK_TYPE); assert.deepEqual(await wallet.getDaemonConnection(), new MoneroRpcConnection(TestUtils.OFFLINE_SERVER_URI)); @@ -947,10 +955,10 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { await wallet.close(); // re-open the wallet - wallet = await that.openWallet({path: path, serverUri: TestUtils.OFFLINE_SERVER_URI}); + wallet = await that.openWallet({path: path, server: TestUtils.OFFLINE_SERVER_URI}); // test wallet is at newly created state - assert(await MoneroWalletFull.walletExists(path, TestUtils.getDefaultFs())); + assert(await MoneroWalletFull.walletExists(path, await TestUtils.getDefaultFs())); assert.equal(await wallet.getSeed(), TestUtils.SEED); assert.equal(await wallet.getNetworkType(), TestUtils.NETWORK_TYPE); assert.deepEqual(await wallet.getDaemonConnection(), new MoneroRpcConnection(TestUtils.OFFLINE_SERVER_URI)); @@ -974,7 +982,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { await wallet.close(); // re-open the wallet - wallet = await that.openWallet({path: path, serverUri: TestUtils.OFFLINE_SERVER_URI}); + wallet = await that.openWallet({path: path, server: TestUtils.OFFLINE_SERVER_URI}); // test wallet state is saved assert(!(await wallet.isConnectedToDaemon())); @@ -983,7 +991,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { assert(await wallet.isConnectedToDaemon()); assert.equal(await wallet.getHeight(), prevHeight); assert(await wallet.getRestoreHeight() > 0); - assert(await MoneroWalletFull.walletExists(path, TestUtils.getDefaultFs())); + assert(await MoneroWalletFull.walletExists(path, await TestUtils.getDefaultFs())); assert.equal(await wallet.getSeed(), TestUtils.SEED); assert.equal(await wallet.getNetworkType(), TestUtils.NETWORK_TYPE); assert.equal(await wallet.getSeedLanguage(), "English"); @@ -1006,8 +1014,8 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { try { // create random wallet - wallet = await monerojs.createWalletFull({ - networkType: "mainnet", + wallet = await createWalletFull({ + networkType: MoneroNetworkType.MAINNET, password: "password123" }); @@ -1017,8 +1025,8 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { let cacheData = walletData[1]; // import wallet files - let wallet2 = await monerojs.openWalletFull({ - networkType: "mainnet", + let wallet2 = await openWalletFull({ + networkType: MoneroNetworkType.MAINNET, password: "password123", keysData: keysData, cacheData: cacheData @@ -1026,7 +1034,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // test that wallets are equal assert.equal(await wallet.getSeed(), await wallet2.getSeed()); - await TestMoneroWalletFull._testWalletEqualityOnChain(wallet, wallet2); + await TestMoneroWalletFull.testWalletEqualityOnChain(wallet, wallet2); } catch (e) { err = e; } @@ -1053,9 +1061,9 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // move wallet from memory to disk let path1 = TestUtils.TEST_WALLETS_DIR + "/" + GenUtils.getUUID(); - assert(!MoneroWalletFull.walletExists(path1, TestUtils.getDefaultFs())); + assert(!MoneroWalletFull.walletExists(path1, await TestUtils.getDefaultFs())); await wallet.moveTo(path1); - assert(MoneroWalletFull.walletExists(path1, TestUtils.getDefaultFs())); + assert(MoneroWalletFull.walletExists(path1, await TestUtils.getDefaultFs())); assert.equal(await wallet.getSeed(), mnemonic); assert.equal("myval1", await wallet.getAttribute("mykey")); @@ -1063,7 +1071,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { await wallet.setAttribute("mykey", "myval2"); await wallet.moveTo(path1); await wallet.close(); - assert(MoneroWalletFull.walletExists(path1, TestUtils.getDefaultFs())); + assert(MoneroWalletFull.walletExists(path1, await TestUtils.getDefaultFs())); wallet = await that.openWallet(new MoneroWalletConfig().setPath(path1).setPassword(password2)); assert.equal(await wallet.getSeed(), mnemonic); assert.equal("myval2", await wallet.getAttribute("mykey")); @@ -1072,8 +1080,8 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { let path2 = TestUtils.TEST_WALLETS_DIR + "/moved/" + GenUtils.getUUID(); await wallet.setAttribute("mykey", "myval3"); await wallet.moveTo(path2); - assert(!MoneroWalletFull.walletExists(path1, TestUtils.getDefaultFs())); - assert(MoneroWalletFull.walletExists(path2, TestUtils.getDefaultFs())); + assert(!MoneroWalletFull.walletExists(path1, await TestUtils.getDefaultFs())); + assert(MoneroWalletFull.walletExists(path2, await TestUtils.getDefaultFs())); assert.equal(await wallet.getSeed(), mnemonic); // re-open and test wallet @@ -1089,6 +1097,7 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // final cleanup if (wallet) await wallet.close(); if (err) throw err; + console.log("All done with test"); }); if (testConfig.testNonRelays) @@ -1111,15 +1120,15 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { // attempt to interact with the wallet try { await wallet.getHeight(); } - catch (e) { assert.equal(e.message, "Wallet is closed"); } + catch (e: any) { assert.equal(e.message, "Wallet is closed"); } try { await wallet.getSeed(); } - catch (e) { assert.equal(e.message, "Wallet is closed"); } + catch (e: any) { assert.equal(e.message, "Wallet is closed"); } try { await wallet.sync(); } - catch (e) { assert.equal(e.message, "Wallet is closed"); } + catch (e: any) { assert.equal(e.message, "Wallet is closed"); } try { await wallet.startSyncing(); } - catch (e) { assert.equal(e.message, "Wallet is closed"); } + catch (e: any) { assert.equal(e.message, "Wallet is closed"); } try { await wallet.stopSyncing(); } - catch (e) { assert.equal(e.message, "Wallet is closed"); } + catch (e: any) { assert.equal(e.message, "Wallet is closed"); } // re-open the wallet wallet = await that.openWallet({path: path}); @@ -1169,12 +1178,12 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { //----------------------------- PRIVATE HELPERS ----------------------------- - static _getRandomWalletPath() { + protected static getRandomWalletPath() { return TestUtils.TEST_WALLETS_DIR + "/test_wallet_" + GenUtils.getUUID(); } // possible configuration: on chain xor local wallet data ("strict"), txs ordered same way? TBD - static async _testWalletEqualityOnChain(wallet1, wallet2) { + protected static async testWalletEqualityOnChain(wallet1, wallet2) { await WalletEqualityUtils.testWalletEqualityOnChain(wallet1, wallet2); assert.equal(await wallet2.getNetworkType(), await wallet1.getNetworkType()); assert.equal(await wallet2.getRestoreHeight(), await wallet1.getRestoreHeight()); @@ -1188,6 +1197,14 @@ class TestMoneroWalletFull extends TestMoneroWalletCommon { * Helper class to test progress updates. */ class SyncProgressTester extends WalletSyncPrinter { + + wallet: any; + startHeight: number; + prevEndHeight: number; + prevCompleteHeight: number; + prevHeight: number; + isDone: boolean; + onSyncProgressAfterDone: boolean; constructor(wallet, startHeight, endHeight) { super(); @@ -1199,7 +1216,7 @@ class SyncProgressTester extends WalletSyncPrinter { this.isDone = false; } - onSyncProgress(height, startHeight, endHeight, percentDone, message) { + async onSyncProgress(height, startHeight, endHeight, percentDone, message) { super.onSyncProgress(height, startHeight, endHeight, percentDone, message); // registered wallet listeners will continue to get sync notifications after the wallet's initial sync @@ -1255,16 +1272,25 @@ class SyncProgressTester extends WalletSyncPrinter { * Internal class to test all wallet notifications on sync. */ class WalletSyncTester extends SyncProgressTester { + + incomingTotal: bigint; + outgoingTotal: bigint; + prevBalance: bigint; + prevUnlockedBalance: bigint; + prevOutputReceived: MoneroOutputWallet; + prevOutputSpent: MoneroOutputWallet; + walletTesterPrevHeight: number; + onNewBlockAfterDone: boolean; constructor(wallet, startHeight, endHeight) { super(wallet, startHeight, endHeight); assert(startHeight >= 0); assert(endHeight >= 0); - this.incomingTotal = BigInteger.parse("0"); - this.outgoingTotal = BigInteger.parse("0"); + this.incomingTotal = BigInt("0"); + this.outgoingTotal = BigInt("0"); } - onNewBlock(height) { + async onNewBlock(height) { if (this.isDone) { assert(this.wallet.getListeners().includes(this), "Listener has completed and is not registered so should not be called again"); this.onNewBlockAfterDone = true; @@ -1280,12 +1306,12 @@ class WalletSyncTester extends SyncProgressTester { this.prevUnlockedBalance = newUnlockedBalance; } - onOutputReceived(output) { + async onOutputReceived(output) { assert.notEqual(output, undefined); this.prevOutputReceived = output; // test output - TestUtils.testUnsignedBigInteger(output.getAmount()); + TestUtils.testUnsignedBigInt(output.getAmount()); assert(output.getAccountIndex() >= 0); assert(output.getSubaddressIndex() >= 0); @@ -1304,15 +1330,15 @@ class WalletSyncTester extends SyncProgressTester { assert.equal(output.getTx().getExtra(), undefined); // add incoming amount to running total - if (output.isLocked()) this.incomingTotal = this.incomingTotal.add(output.getAmount()); + if (output.getIsLocked()) this.incomingTotal = this.incomingTotal + (output.getAmount()); } - onOutputSpent(output) { + async onOutputSpent(output) { assert.notEqual(output, undefined); this.prevOutputSpent = output; // test output - TestUtils.testUnsignedBigInteger(output.getAmount()); + TestUtils.testUnsignedBigInt(output.getAmount()); assert(output.getAccountIndex() >= 0); if (output.getSubaddressIndex() !== undefined) assert(output.getSubaddressIndex() >= 0); // TODO (monero-project): can be undefined because inputs not provided so one created from outgoing transfer @@ -1331,7 +1357,7 @@ class WalletSyncTester extends SyncProgressTester { assert.equal(output.getTx().getExtra(), undefined); // add outgoing amount to running total - if (output.isLocked()) this.outgoingTotal = this.outgoingTotal.add(output.getAmount()); + if (output.getIsLocked()) this.outgoingTotal = this.outgoingTotal + (output.getAmount()); } async onDone(chainHeight) { @@ -1339,7 +1365,7 @@ class WalletSyncTester extends SyncProgressTester { assert.notEqual(this.walletTesterPrevHeight, undefined); assert.notEqual(this.prevOutputReceived, undefined); assert.notEqual(this.prevOutputSpent, undefined); - let balance = this.incomingTotal.subtract(this.outgoingTotal); + let balance = this.incomingTotal - (this.outgoingTotal); assert.equal(balance.toString(), (await this.wallet.getBalance()).toString()); assert.equal(this.prevBalance.toString(), (await this.wallet.getBalance()).toString()); assert.equal(this.prevUnlockedBalance.toString(), (await this.wallet.getUnlockedBalance()).toString()); @@ -1350,4 +1376,3 @@ class WalletSyncTester extends SyncProgressTester { } } -module.exports = TestMoneroWalletFull; diff --git a/src/test/TestMoneroWalletKeys.js b/src/test/TestMoneroWalletKeys.ts similarity index 79% rename from src/test/TestMoneroWalletKeys.js rename to src/test/TestMoneroWalletKeys.ts index 3a37744c6..9ae03f401 100644 --- a/src/test/TestMoneroWalletKeys.js +++ b/src/test/TestMoneroWalletKeys.ts @@ -1,23 +1,20 @@ -const assert = require("assert"); -const TestUtils = require("./utils/TestUtils"); -const WalletEqualityUtils = require("./utils/WalletEqualityUtils"); -const TestMoneroWalletCommon = require("./TestMoneroWalletCommon"); -const monerojs = require("../../index"); -const MoneroWalletConfig = monerojs.MoneroWalletConfig; -const GenUtils = monerojs.GenUtils; -const MoneroUtils = monerojs.MoneroUtils; +import assert from "assert"; +import TestUtils from "./utils/TestUtils"; +import WalletEqualityUtils from "./utils/WalletEqualityUtils"; +import TestMoneroWalletCommon from "./TestMoneroWalletCommon"; +import {createWalletKeys, MoneroWalletConfig, MoneroWalletKeys, GenUtils, MoneroUtils} from "../../index"; /** * Tests the implementation of MoneroWallet which only manages keys using WebAssembly. */ -class TestMoneroWalletKeys extends TestMoneroWalletCommon { +export default class TestMoneroWalletKeys extends TestMoneroWalletCommon { constructor(config) { super(config); } - async beforeAll(currentTest) { - await super.beforeAll(currentTest); + async beforeAll() { + await super.beforeAll(); } async beforeEach(currentTest) { @@ -41,7 +38,7 @@ class TestMoneroWalletKeys extends TestMoneroWalletCommon { return await TestUtils.getDaemonRpc(); } - async openWallet(config) { + async openWallet(config?): Promise { throw new Error("TestMoneroWalletKeys.openWallet(config) not applicable, use createWallet()"); } @@ -54,15 +51,15 @@ class TestMoneroWalletKeys extends TestMoneroWalletCommon { if (config.getServer()) throw new Error("Cannot initialize keys wallet with connection"); // create wallet - return await monerojs.createWalletKeys(config); + return await createWalletKeys(config); } async closeWallet(wallet, save) { await wallet.close(save); } - async getSeedLanguages() { - return await monerojs.MoneroWalletKeys.getSeedLanguages(); + async getSeedLanguages(): Promise { + return await MoneroWalletKeys.getSeedLanguages(); } runTests() { @@ -76,7 +73,7 @@ class TestMoneroWalletKeys extends TestMoneroWalletCommon { afterEach(async function() { await that.afterEach(this.currentTest); }); // run tests specific to keys wallet - that._testWalletKeys(); + that.testWalletKeys(); // run common tests that.runCommonTests(); @@ -85,9 +82,9 @@ class TestMoneroWalletKeys extends TestMoneroWalletCommon { // ---------------------------------- PRIVATE ------------------------------- - _testWalletKeys() { + protected testWalletKeys() { let that = this; - let config = this.config; + let config = this.testConfig; let daemon = this.daemon; describe("Tests specific to keys wallet", function() { @@ -106,7 +103,7 @@ class TestMoneroWalletKeys extends TestMoneroWalletCommon { await walletRpc.createWallet({path: GenUtils.getUUID(), password: TestUtils.WALLET_PASSWORD, seed: await walletRpc.getSeed(), restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT, seedOffset: seedOffset}); // create keys-only wallet with offset - let walletKeys = await monerojs.createWalletKeys({ + let walletKeys = await createWalletKeys({ networkType: TestUtils.NETWORK_TYPE, seed: TestUtils.SEED, seedOffset: seedOffset @@ -127,4 +124,3 @@ class TestMoneroWalletKeys extends TestMoneroWalletCommon { } } -module.exports = TestMoneroWalletKeys \ No newline at end of file diff --git a/src/test/TestMoneroWalletRpc.js b/src/test/TestMoneroWalletRpc.ts similarity index 86% rename from src/test/TestMoneroWalletRpc.js rename to src/test/TestMoneroWalletRpc.ts index eeb7537bd..4970bbcfd 100644 --- a/src/test/TestMoneroWalletRpc.js +++ b/src/test/TestMoneroWalletRpc.ts @@ -1,18 +1,18 @@ -const assert = require("assert"); -const TestUtils = require("./utils/TestUtils"); -const TestMoneroWalletCommon = require("./TestMoneroWalletCommon"); -const TestMoneroWalletFull = require("./TestMoneroWalletFull"); -const monerojs = require("../../index"); -const MoneroError = monerojs.MoneroError; -const GenUtils = monerojs.GenUtils; -const MoneroWalletConfig = monerojs.MoneroWalletConfig; -const MoneroUtils = monerojs.MoneroUtils; -const MoneroAccountTag = monerojs.MoneroAccountTag; +import assert from "assert"; +import TestUtils from "./utils/TestUtils"; +import TestMoneroWalletCommon from "./TestMoneroWalletCommon"; +import TestMoneroWalletFull from "./TestMoneroWalletFull"; +import {MoneroError, + GenUtils, + MoneroWalletConfig, + MoneroUtils, + MoneroAccountTag, + MoneroWalletRpc} from "../../index"; /** * Tests the Monero Wallet RPC client and server. */ -class TestMoneroWalletRpc extends TestMoneroWalletCommon { +export default class TestMoneroWalletRpc extends TestMoneroWalletCommon { constructor(testConfig) { super(testConfig); @@ -61,7 +61,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { if (!config.getServer()) config.setServer(await this.daemon.getRpcConnection()); // create client connected to internal monero-wallet-rpc executable - let offline = TestUtils.OFFLINE_SERVER_URI === config.getServerUri(); + let offline = config.getServer() && config.getServer().getUri() === TestUtils.OFFLINE_SERVER_URI; let wallet = await TestUtils.startWalletRpcProcess(offline); // open wallet @@ -76,7 +76,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { } } - async createWallet(config) { + async createWallet(config: Partial) { // assign defaults config = new MoneroWalletConfig(config); @@ -87,7 +87,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { if (!config.getServer() && !config.getConnectionManager()) config.setServer(await this.daemon.getRpcConnection()); // create client connected to internal monero-wallet-rpc executable - let offline = config.getServerUri() === GenUtils.normalizeUri(TestUtils.OFFLINE_SERVER_URI); + let offline = config.getServer() && config.getServer().getUri() === GenUtils.normalizeUri(TestUtils.OFFLINE_SERVER_URI); let wallet = await TestUtils.startWalletRpcProcess(offline); // create wallet @@ -102,13 +102,13 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { } } - async closeWallet(wallet, save) { + async closeWallet(wallet, save?) { await wallet.close(save); await TestUtils.stopWalletRpcProcess(wallet); } - async getSeedLanguages() { - return await this.wallet.getSeedLanguages(); + async getSeedLanguages(): Promise { + return await (this.wallet as MoneroWalletRpc).getSeedLanguages(); } runTests() { @@ -123,7 +123,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { afterEach(async function() { await that.afterEach(this.currentTest); }); // run tests specific to wallet rpc - that._testWalletRpc(testConfig); + that.testWalletRpc(testConfig); // run common tests that.runCommonTests(testConfig); @@ -133,15 +133,15 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { // ---------------------------------- PRIVATE ------------------------------- // rpc-specific tx test - async _testTxWallet(tx, ctx) { + async testTxWallet(tx, ctx) { ctx = Object.assign({}, ctx); // run common tests - await super._testTxWallet(tx, ctx); + await super.testTxWallet(tx, ctx); } // rpc-specific out-of-range subaddress test - async _testGetSubaddressAddressOutOfRange() { + async testGetSubaddressAddressOutOfRange() { let accounts = await this.wallet.getAccounts(true); let accountIdx = accounts.length - 1; let subaddressIdx = accounts[accountIdx].getSubaddresses().length; @@ -149,37 +149,37 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { assert.equal(address, undefined); } - _testInvalidAddressError(err) { - super._testInvalidAddressError(err); + testInvalidAddressError(err) { + super.testInvalidAddressError(err); assert.equal(-2, err.getCode()); } - _testInvalidTxHashError(err) { - super._testInvalidTxHashError(err); + testInvalidTxHashError(err) { + super.testInvalidTxHashError(err); assert.equal(-8, err.getCode()); } - _testInvalidTxKeyError(err) { - super._testInvalidTxKeyError(err); + testInvalidTxKeyError(err) { + super.testInvalidTxKeyError(err); assert.equal(-25, err.getCode()); } - _testInvalidSignatureError(err) { - super._testInvalidSignatureError(err); + testInvalidSignatureError(err) { + super.testInvalidSignatureError(err); assert.equal(-1, err.getCode()); } - _testNoSubaddressError(err) { - super._testNoSubaddressError(err); + testNoSubaddressError(err) { + super.testNoSubaddressError(err); assert.equal(-1, err.getCode()); } - _testSignatureHeaderCheckError(err) { - super._testSignatureHeaderCheckError(err); + testSignatureHeaderCheckError(err) { + super.testSignatureHeaderCheckError(err); assert.equal(-1, err.getCode()); } - _testWalletRpc(testConfig) { + protected testWalletRpc(testConfig) { let that = this; describe("Tests specific to RPC wallet", function() { @@ -209,7 +209,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { // attempt to create wallet which already exists try { await that.createWallet({ path: path, language: "Spanish" }); - } catch (e) { + } catch (e: any) { assert.equal(e.message, "Wallet already exists: " + path); assert.equal(-21, e.getCode()) assert.equal(mnemonic, await wallet.getSeed()); @@ -246,11 +246,11 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { // create names of test wallets let numTestWallets = 3; - let names = []; + let names: string[] = []; for (let i = 0; i < numTestWallets; i++) names.push(GenUtils.getUUID()); // create test wallets - let mnemonics = []; + let mnemonics: string[] = []; for (let name of names) { let wallet = await that.createWallet({ path: name, password: TestUtils.WALLET_PASSWORD }); mnemonics.push(await wallet.getSeed()); @@ -258,7 +258,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { } // open test wallets - let wallets = []; + let wallets: MoneroWalletRpc[] = []; for (let i = 0; i < numTestWallets; i++) { let wallet = await that.openWallet({ path: names[i], password: TestUtils.WALLET_PASSWORD }); assert.equal(await wallet.getSeed(), mnemonics[i]); @@ -268,7 +268,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { // attempt to re-open already opened wallet try { await that.openWallet({ path: names[numTestWallets - 1], password: TestUtils.WALLET_PASSWORD }); - } catch (e) { + } catch (e: any) { assert.equal(e.getCode(), -1); } @@ -276,7 +276,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { try { await that.openWallet({ path: "btc_integrity", password: TestUtils.WALLET_PASSWORD }); throw new Error("Cannot open wallet which is already open"); - } catch (e) { + } catch (e: any) { assert(e instanceof MoneroError); assert.equal(e.getCode(), -1); // -1 indicates wallet does not exist (or is open by another app) } @@ -298,7 +298,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { assert(accounts.length >= 3, "Not enough accounts to test; run create account test"); // tag some of the accounts - let tag = new MoneroAccountTag("my_tag_" + GenUtils.getUUID(), "my tag label", [0, 1]); + let tag = new MoneroAccountTag({tag: "my_tag_" + GenUtils.getUUID(), label: "my tag label", accountIndices: [0, 1]}); await that.wallet.tagAccounts(tag.getTag(), tag.getAccountIndices()); // query accounts by tag @@ -317,7 +317,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { assert(GenUtils.arrayContains(tags, tag)); // re-tag an account - let tag2 = new MoneroAccountTag("my_tag_" + GenUtils.getUUID(), "my tag label 2", [1]); + let tag2 = new MoneroAccountTag({tag: "my_tag_" + GenUtils.getUUID(), label: "my tag label 2", accountIndices: [1]}); await that.wallet.tagAccounts(tag2.getTag(), tag2.getAccountIndices()); let taggedAccounts2 = await that.wallet.getAccounts(undefined, tag2.getTag()) assert.equal(taggedAccounts2.length, 1); @@ -335,23 +335,23 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { assert.equal((await that.wallet.getAccountTags()).length, 0); try { await that.wallet.getAccounts(undefined, tag.getTag()); - fail("Should have thrown exception with unregistered tag"); - } catch (e) { + throw new Error("Should have thrown exception with unregistered tag"); + } catch (e: any) { assert.equal(e.getCode(), -1); } // test that non-existing tag returns no accounts try { await that.wallet.getAccounts(undefined, "non_existing_tag"); - fail("Should have thrown exception with unregistered tag"); - } catch (e) { + throw new Error("Should have thrown exception with unregistered tag"); + } catch (e: any) { assert.equal(e.getCode(), -1); } }); if (testConfig.testNonRelays) it("Can fetch accounts and subaddresses without balance info because this is another RPC call", async function() { - let accounts = await that.wallet.getAccounts(true, undefined, true); + let accounts = await (that.wallet as MoneroWalletRpc).getAccounts(true, undefined, true); assert(accounts.length > 0); for (let account of accounts) { assert(account.getSubaddresses().length > 0); @@ -362,7 +362,7 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { assert(subaddress.getIndex() >= 0); assert(subaddress.getLabel() === undefined || typeof subaddress.getLabel() === "string"); if (typeof subaddress.getLabel() === "string") assert(subaddress.getLabel().length > 0); - assert.equal(typeof subaddress.isUsed(), "boolean"); + assert.equal(typeof subaddress.getIsUsed(), "boolean"); assert.equal(subaddress.getNumUnspentOutputs(), undefined); assert.equal(subaddress.getBalance(), undefined); assert.equal(subaddress.getUnlockedBalance(), undefined); @@ -395,19 +395,19 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { // attempt to interact with the wallet try { await wallet.getHeight(); - } catch (e) { + } catch (e: any) { assert.equal(e.getCode(), -13); assert.equal(e.message, "No wallet file"); } try { await wallet.getSeed(); - } catch (e) { + } catch (e: any) { assert.equal(e.getCode(), -13); assert.equal(e.message, "No wallet file"); } try { await wallet.sync(); - } catch (e) { + } catch (e: any) { assert.equal(e.getCode(), -13); assert.equal(e.message, "No wallet file"); } @@ -423,14 +423,12 @@ class TestMoneroWalletRpc extends TestMoneroWalletCommon { if (false && testConfig.testNonRelays) // disabled so server not actually stopped it("Can stop the RPC server", async function() { - await that.wallet.stop(); + await (that.wallet as MoneroWalletRpc).stop(); }); }); } } -module.exports = TestMoneroWalletRpc; - function testAddressBookEntry(entry) { assert(entry.getIndex() >= 0); assert(entry.getAddress()); diff --git a/src/test/TestSampleCode.js b/src/test/TestSampleCode.ts similarity index 66% rename from src/test/TestSampleCode.js rename to src/test/TestSampleCode.ts index ca6f9f233..d85f72229 100644 --- a/src/test/TestSampleCode.js +++ b/src/test/TestSampleCode.ts @@ -1,106 +1,106 @@ -const assert = require("assert"); -const monerojs = require("../../index"); -const TestUtils = require("./utils/TestUtils"); +import assert from "assert"; +import TestUtils from "./utils/TestUtils.js"; +import * as moneroTs from "../../index"; /** * Test the sample code in README.md. */ -class TestSampleCode { +export default class TestSampleCode { runTests() { describe("Test sample code", function() { - let that = this; - let wallet; + let that = this; // Unnecessary? That is never used in the following code + let wallet: moneroTs.MoneroWalletFull; // initialize wallet before(async function() { try { - + // all wallets need to wait for txs to confirm to reliably sync TestUtils.WALLET_TX_TRACKER.reset(); // create rpc test wallet let walletRpc = await TestUtils.getWalletRpc(); await walletRpc.close(); - // create directory for test wallets if it doesn't exist - let fs = TestUtils.getDefaultFs(); + let fs = await TestUtils.getDefaultFs(); if (!fs.existsSync(TestUtils.TEST_WALLETS_DIR)) { if (!fs.existsSync(process.cwd())) fs.mkdirSync(process.cwd(), { recursive: true }); // create current process directory for relative paths which does not exist in memory fs fs.mkdirSync(TestUtils.TEST_WALLETS_DIR); } - // create full test wallet wallet = await TestUtils.getWalletFull(); + } catch (e) { console.error("Error before tests: "); console.error(e); throw e; } }); - after(async function() { if (wallet) await wallet.close(true); }); it("Sample code demonstration", async function() { - // import library - const monerojs = require("../../index"); // *** CHANGE README TO "monero-javascript" *** - + // import monero-ts (or import as needed) + // import * as moneroTs from "monero-ts"; // *** UNCOMMENT IN README *** + // connect to daemon - let daemon = await monerojs.connectToDaemonRpc("http://localhost:28081"); - let height = await daemon.getHeight(); // 1523651 - let txsInPool = await daemon.getTxPool(); // get transactions in the pool - - // open wallet on monero-wallet-rpc - let walletRpc = await monerojs.connectToWalletRpc("http://localhost:28084", "rpc_user", "abc123"); - await walletRpc.openWallet("test_wallet_1", "supersecretpassword123"); // *** CHANGE README TO "sample_wallet_rpc" *** - let primaryAddress = await walletRpc.getPrimaryAddress(); // 555zgduFhmKd2o8rPUz... - let balance = await walletRpc.getBalance(); // 533648366742 - let txs = await walletRpc.getTxs(); // get transactions containing transfers to/from the wallet - - // create wallet from seed phrase using WebAssembly bindings to monero-project - let walletFull = await monerojs.createWalletFull({ - path: "./test_wallets/" + monerojs.GenUtils.getUUID(), // *** CHANGE README TO "sample_wallet_full" + let daemon = await moneroTs.connectToDaemonRpc("http://localhost:28081"); + let height = await daemon.getHeight(); // 1523651 + let txsInPool = await daemon.getTxPool(); // get transactions in the pool + + // create wallet from mnemonic phrase using WebAssembly bindings to monero-project + let walletFull = await moneroTs.createWalletFull({ + path: "./test_wallets/" + moneroTs.GenUtils.getUUID(), // *** CHANGE README TO "sample_wallet_full" + fs: await TestUtils.getDefaultFs(), // *** REMOVE FROM README SAMPLE *** password: "supersecretpassword123", - networkType: "testnet", - serverUri: "http://localhost:28081", - serverUsername: "superuser", - serverPassword: "abctesting123", - seed: TestUtils.SEED, // *** REPLACE README WITH SEED *** + networkType: moneroTs.MoneroNetworkType.TESTNET, + seed: TestUtils.SEED, // *** REPLACE README WITH SEED *** restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT, // *** REPLACE README WITH FIRST RECEIVE HEIGHT *** - fs: TestUtils.getDefaultFs() + server: { // provide url or MoneroRpcConnection + uri: "http://localhost:28081", + username: "superuser", + password: "abctesting123" + } }); - + // synchronize with progress notifications - await walletFull.sync(new class extends monerojs.MoneroWalletListener { - onSyncProgress(height, startHeight, endHeight, percentDone, message) { + await walletFull.sync(new class extends moneroTs.MoneroWalletListener { + async onSyncProgress(height: number, startHeight: number, endHeight: number, percentDone: number, message: string) { // feed a progress bar? } - }); - + } as moneroTs.MoneroWalletListener); + // synchronize in the background every 5 seconds await walletFull.startSyncing(5000); // receive notifications when funds are received, confirmed, and unlocked let fundsReceived = false; - await walletFull.addListener(new class extends monerojs.MoneroWalletListener { - onOutputReceived(output) { + await walletFull.addListener(new class extends moneroTs.MoneroWalletListener { + async onOutputReceived(output: moneroTs.MoneroOutputWallet) { let amount = output.getAmount(); let txHash = output.getTx().getHash(); - let isConfirmed = output.getTx().isConfirmed(); - let isLocked = output.getTx().isLocked(); + let isConfirmed = output.getTx().getIsConfirmed(); + let isLocked = output.getTx().getIsLocked(); fundsReceived = true; } }); - + + // connect to wallet RPC endpoint and open wallet + let walletRpc = await moneroTs.connectToWalletRpc("http://localhost:28084", "rpc_user", "abc123"); + await walletRpc.openWallet("test_wallet_1", "supersecretpassword123"); // *** CHANGE README TO "sample_wallet_rpc" *** + let primaryAddress = await walletRpc.getPrimaryAddress(); // 555zgduFhmKd2o8rPUz... + let balance = await walletRpc.getBalance(); // 533648366742 + let txs = await walletRpc.getTxs(); // get transactions containing transfers to/from the wallet + // send funds from RPC wallet to WebAssembly wallet await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(walletRpc); // *** REMOVE FROM README SAMPLE *** let createdTx = await walletRpc.createTx({ accountIndex: 0, address: await walletFull.getAddress(1, 0), - amount: "250000000000", // send 0.25 XMR (denominated in atomic units) + amount: BigInt("250000000000"), // send 0.25 XMR (denominated in atomic units) relay: false // create transaction and relay to the network if true }); let fee = createdTx.getFee(); // "Are you sure you want to send... ?" @@ -115,43 +115,37 @@ class TestSampleCode { }); it("Connection manager demonstration", async function() { - - // imports - const monerojs = require("../../index"); // *** CHANGE README TO "monero-javascript" *** - const MoneroRpcConnection = monerojs.MoneroRpcConnection; - const MoneroConnectionManager = monerojs.MoneroConnectionManager; - const MoneroConnectionManagerListener = monerojs.MoneroConnectionManagerListener; + + // import monero-ts (or import types individually) + // import * as moneroTs from "monero-ts"; // *** UNCOMMENT IN README *** // create connection manager - let connectionManager = new MoneroConnectionManager(); + let connectionManager = new moneroTs.MoneroConnectionManager(); // add managed connections with priorities - connectionManager.addConnection(new MoneroRpcConnection("http://localhost:28081").setPriority(1)); // use localhost as first priority - connectionManager.addConnection(new MoneroRpcConnection("http://example.com")); // default priority is prioritized last + await connectionManager.addConnection({uri: "http://localhost:28081", priority: 1}); // use localhost as first priority + await connectionManager.addConnection("http://example.com"); // default priority is prioritized last // set current connection - connectionManager.setConnection(new MoneroRpcConnection("http://foo.bar", "admin", "password")); // connection is added if new + await connectionManager.setConnection({uri: "http://foo.bar", username: "admin", password: "password"}); // connection is added if new - // create wallet with managed connections or set later - let walletFull = await monerojs.createWalletFull({ - path: "./test_wallets/" + monerojs.GenUtils.getUUID(), // *** CHANGE README TO "sample_wallet_full" + // create wallet governed by connection manager + let walletFull = await moneroTs.createWalletFull({ + path: "./test_wallets/" + moneroTs.GenUtils.getUUID(), // *** CHANGE README TO "sample_wallet_full" password: "supersecretpassword123", - networkType: "testnet", + networkType: moneroTs.MoneroNetworkType.TESTNET, connectionManager: connectionManager, seed: TestUtils.SEED, // *** REPLACE IN README *** restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT, // *** REPLACE IN README *** - fs: TestUtils.getDefaultFs() // *** REPLACE IN README *** + fs: await TestUtils.getDefaultFs() // *** REPLACE IN README *** }); // check connection status await connectionManager.checkConnection(); - console.log("Connection manager is connected: " + connectionManager.isConnected()); - console.log("Connection is online: " + connectionManager.getConnection().isOnline()); - console.log("Connection is authenticated: " + connectionManager.getConnection().isAuthenticated()); // receive notifications of any changes to current connection - connectionManager.addListener(new class extends MoneroConnectionManagerListener { - onConnectionChanged(connection) { + connectionManager.addListener(new class extends moneroTs.MoneroConnectionManagerListener { + async onConnectionChanged(connection: moneroTs.MoneroRpcConnection) { console.log("Connection changed to: " + connection); } }); @@ -169,7 +163,7 @@ class TestSampleCode { let connections = connectionManager.getConnections(); // clear connection manager - connectionManager.clear(); + await connectionManager.clear(); }); it("Test developer guide transaction queries", async function() { @@ -183,7 +177,7 @@ class TestSampleCode { isConfirmed: false }); for (let tx of txs) { - assert(!tx.isConfirmed()); + assert(!tx.getIsConfirmed()); } // get transactions since height 582106 with incoming transfers to @@ -197,11 +191,11 @@ class TestSampleCode { } }); for (let tx of txs) { - assert(tx.isConfirmed()); + assert(tx.getIsConfirmed()); assert(tx.getHeight() >= 582106) - let found = false; + let found: boolean = false; for (let transfer of tx.getTransfers()) { - if (transfer.isIncoming() && transfer.getAccountIndex() === 0 && transfer.getSubaddressIndex() === 1) { + if (transfer.getIsIncoming() && transfer.getAccountIndex() === 0 && (transfer as moneroTs.MoneroIncomingTransfer).getSubaddressIndex() === 1) { found = true; break; } @@ -217,11 +211,11 @@ class TestSampleCode { } }); for (let tx of txs) { - assert(!tx.isLocked()); + assert(!tx.getIsLocked()); assert(tx.getOutputs().length > 0); let found = false; - for (let output of tx.getOutputs()) { - if (!output.isSpent()) { + for (let output of tx.getOutputsWallet()) { + if (!output.getIsSpent()) { found = true; break; } @@ -245,9 +239,9 @@ class TestSampleCode { subaddressIndex: 1 }); for (let transfer of transfers) { - assert.equal(transfer.isIncoming(), true); + assert.equal(transfer.getIsIncoming(), true); assert.equal(transfer.getAccountIndex(), 0); - assert.equal(transfer.getSubaddressIndex(), 1); + assert.equal((transfer as moneroTs.MoneroIncomingTransfer).getSubaddressIndex(), 1); } // get transfers in the tx pool @@ -257,12 +251,12 @@ class TestSampleCode { } }); for (let transfer of transfers) { - assert.equal(transfer.getTx().inTxPool(), true); + assert.equal(transfer.getTx().getInTxPool(), true); } // get confirmed outgoing transfers since a block height transfers = await wallet.getTransfers({ - isOutgoing: true, + isIncoming: false, txQuery: { isConfirmed: true, minHeight: TestUtils.FIRST_RECEIVE_HEIGHT // *** REPLACE WITH NUMBER IN .MD FILE *** @@ -270,8 +264,8 @@ class TestSampleCode { }); assert(transfers.length > 0); for (let transfer of transfers) { - assert(transfer.isOutgoing()); - assert(transfer.getTx().isConfirmed()); + assert(transfer.getIsOutgoing()); + assert(transfer.getTx().getIsConfirmed()); assert(transfer.getTx().getHeight() >= TestUtils.FIRST_RECEIVE_HEIGHT); } }); @@ -291,14 +285,14 @@ class TestSampleCode { }); assert(outputs.length > 0); for (let output of outputs) { - assert(!output.isSpent()); - assert(!output.getTx().isLocked()); + assert(!output.getIsSpent()); + assert(!output.getTx().getIsLocked()); } // get outputs by amount let amount = outputs[0].getAmount(); outputs = await wallet.getOutputs({ - amount: amount.toString() // *** REPLACE WITH BigInteger IN .MD FILE *** + amount: amount // *** REPLACE WITH bigint IN .MD FILE *** }); assert(outputs.length > 0); for (let output of outputs) assert.equal(output.getAmount().toString(), amount.toString()); @@ -315,29 +309,33 @@ class TestSampleCode { } // get output by key image - let keyImage = outputs[0].getKeyImage().getHex(); + let keyImage: string = outputs[0].getKeyImage().getHex(); outputs = await wallet.getOutputs({ - keyImage: keyImage + keyImage: { + hex: keyImage + } }); assert.equal(outputs.length, 1); assert.equal(outputs[0].getKeyImage().getHex(), keyImage); }); it("Test developer guide send funds", async function() { - + // create in-memory test wallet with randomly generated seed - let wallet = await monerojs.createWalletFull({ + let wallet = await moneroTs.createWalletFull({ password: "abctesting123", - networkType: "testnet", - serverUri: "http://localhost:28081" + networkType: moneroTs.MoneroNetworkType.TESTNET, + server: "http://localhost:28081", + proxyToWorker: TestUtils.PROXY_TO_WORKER }); - + try { + // create a transaction to send funds to an address, but do not relay let tx = await wallet.createTx({ accountIndex: 0, // source account to send funds from address: "9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75", - amount: "1000000000000" // send 1 XMR (denominated in atomic units) + amount: BigInt("1000000000000") // send 1 XMR (denominated in atomic units) }); // can confirm with the user @@ -345,23 +343,25 @@ class TestSampleCode { // relay the transaction let hash = await wallet.relayTx(tx); - } catch (err) { - assert.equal(err.message, "not enough money"); + } catch (err: any) { + if (err.message !== "not enough money") throw err; } try { + // send funds to a single destination let tx = await wallet.createTx({ accountIndex: 0, // source account to send funds from address: "9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75", - amount: "1000000000000", // send 1 XMR (denominated in atomic units) + amount: BigInt("1000000000000"), // send 1 XMR (denominated in atomic units) relay: true // relay the transaction to the network }); - } catch (err) { - assert.equal(err.message, "not enough money"); + } catch (err: any) { + if (err.message !== "not enough money") throw err; } - + try { + // send funds from a specific subaddress to multiple destinations, // allowing transfers to be split across multiple transactions if needed let txs = await wallet.createTxs({ @@ -369,51 +369,55 @@ class TestSampleCode { subaddressIndex: 1, // source subaddress to send funds from destinations: [{ address: "9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75", - amount: "500000000000", // send 0.5 XMR (denominated in atomic units) + amount: BigInt("500000000000"), // send 0.5 XMR (denominated in atomic units) }, { - address: "9y3bAgpF9iajSsNa7t4FN7Zh73MadCL4oMDTcD8SGzbxBGnkYhGyC67AD4pVkvaYw1XL97uwDYuFGf9hi1KEVgZpQtPWcZm", - amount: "500000000000", // send 0.5 XMR (denominated in atomic units) + address: "9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75", + amount: BigInt("500000000000"), // send 0.5 XMR (denominated in atomic units) }], - priority: monerojs.MoneroTxPriority.IMPORTANT, + priority: moneroTs.MoneroTxPriority.ELEVATED, relay: true // relay the transaction to the network }); - } catch (err) { - assert.equal(err.message, "not enough money"); + } catch (err: any) { + if (err.message !== "not enough money") throw err; } - + try { + // sweep an output let tx = await wallet.sweepOutput({ address: "9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75", keyImage: "b7afd6afbb1615c98b1c0350b81c98a77d6d4fc0ab92020d25fd76aca0914f1e", relay: true }); - } catch (err) { - assert.equal(err.message, "No outputs found"); + } catch (err: any) { + if (err.message !== "No outputs found") throw err; } try { + // sweep all unlocked funds in a wallet let txs = await wallet.sweepUnlocked({ address: "9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75", relay: true }); - } catch (err) { - assert.equal(err.message, "No unlocked balance in the specified account"); + } catch (err: any) { + if (err.message !== "No unlocked balance in the specified account") throw err; } try { + // sweep unlocked funds in an account let txs = await wallet.sweepUnlocked({ accountIndex: 0, address: "9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75", relay: true }); - } catch (err) { - assert.equal(err.message, "No unlocked balance in the specified account"); + } catch (err: any) { + if (err.message !== "No unlocked balance in the specified account") throw err; } try { + // sweep unlocked funds in a subaddress let txs = await wallet.sweepUnlocked({ accountIndex: 0, @@ -421,12 +425,10 @@ class TestSampleCode { address: "9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75", relay: true }); - } catch (err) { - assert.equal(err.message, "No unlocked balance in the specified account"); + } catch (err: any) { + if (err.message !== "No unlocked balance in the specified account") throw err; } }); }); } } - -module.exports = TestSampleCode; \ No newline at end of file diff --git a/src/test/browser/favicon.ico b/src/test/browser/favicon.ico index 8cd55fe49..f5ae04c32 100644 Binary files a/src/test/browser/favicon.ico and b/src/test/browser/favicon.ico differ diff --git a/src/test/browser/tests.html b/src/test/browser/tests.html index 9269de9ab..0ca366500 100644 --- a/src/test/browser/tests.html +++ b/src/test/browser/tests.html @@ -7,6 +7,6 @@
- + - \ No newline at end of file + diff --git a/src/test/browser/tests.js b/src/test/browser/tests.js index d78f38a87..719dab6cd 100644 --- a/src/test/browser/tests.js +++ b/src/test/browser/tests.js @@ -23,4 +23,4 @@ function runTests() { // run tests require("../TestAll"); mocha.run(); -} \ No newline at end of file +} diff --git a/src/test/utils/RunWalletRpcTestServers.js b/src/test/utils/RunWalletRpcTestServers.ts similarity index 81% rename from src/test/utils/RunWalletRpcTestServers.js rename to src/test/utils/RunWalletRpcTestServers.ts index 8687d7af9..0a4ddb5a4 100644 --- a/src/test/utils/RunWalletRpcTestServers.js +++ b/src/test/utils/RunWalletRpcTestServers.ts @@ -1,16 +1,17 @@ -const TestUtils = require("./TestUtils"); +import TestUtils from "./TestUtils"; +import { MoneroWalletRpc } from "../../../index"; /** * Utility class to run monero-wallet-rpc test servers until terminated. */ -class RunWalletRpcTestServers { +export default class RunWalletRpcTestServers { static async run() { // start monero-wallet-rpc servers console.log("Starting monero-wallet-rpc servers..."); let numProcesses = 10; - let processPromises = []; + let processPromises: Promise[] = []; for (let i = 0; i < numProcesses; i++) { processPromises.push(TestUtils.startWalletRpcProcess()); } @@ -29,5 +30,3 @@ class RunWalletRpcTestServers { // run until termination RunWalletRpcTestServers.run(); - -module.exports = RunWalletRpcTestServers; \ No newline at end of file diff --git a/src/test/utils/StartMining.js b/src/test/utils/StartMining.ts similarity index 74% rename from src/test/utils/StartMining.js rename to src/test/utils/StartMining.ts index 77141fffd..1a992c2ca 100644 --- a/src/test/utils/StartMining.js +++ b/src/test/utils/StartMining.ts @@ -1,16 +1,14 @@ -const TestUtils = require("./TestUtils"); +import TestUtils from "./TestUtils"; /** * Utility class to start mining. */ -class StartMining { +export default class StartMining { - static async startMining(numThreads) { + static async startMining(numThreads?: number) { if (!numThreads) numThreads = 1; //TestUtils.getWalletRpc().startMining(numThreads, false, true); let daemon = await TestUtils.getDaemonRpc(); await daemon.startMining("9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75", numThreads, false, false); // random subaddress } } - -module.exports = StartMining; \ No newline at end of file diff --git a/src/test/utils/TestUtils.js b/src/test/utils/TestUtils.ts similarity index 59% rename from src/test/utils/TestUtils.js rename to src/test/utils/TestUtils.ts index d0933f9f2..febdbc75f 100644 --- a/src/test/utils/TestUtils.js +++ b/src/test/utils/TestUtils.ts @@ -1,38 +1,87 @@ -const assert = require("assert"); -const WalletSyncPrinter = require("./WalletSyncPrinter"); -const monerojs = require("../../../index"); -const LibraryUtils = monerojs.LibraryUtils; -const GenUtils = monerojs.GenUtils; -const MoneroRpcError = monerojs.MoneroRpcError; -const MoneroRpcConnection = monerojs.MoneroRpcConnection; -const BigInteger = monerojs.BigInteger; -const MoneroNetworkType = monerojs.MoneroNetworkType; -const MoneroWalletRpc = monerojs.MoneroWalletRpc; +import assert from "assert"; +import WalletSyncPrinter from "./WalletSyncPrinter"; +import WalletTxTracker from "./WalletTxTracker"; +import {LibraryUtils, + GenUtils, + MoneroRpcError, + MoneroRpcConnection, + MoneroNetworkType, + MoneroDaemonRpc, + MoneroWalletRpc, + connectToWalletRpc, + connectToDaemonRpc, + openWalletFull, + createWalletFull, + createWalletKeys, + MoneroWalletFull, + MoneroWalletKeys} from "../../../index"; /** * Collection of test utilities and configurations. - * - * TODO: move hard coded to config */ -class TestUtils { +export default class TestUtils { + + // classes to test + static daemonRpc: MoneroDaemonRpc; + static walletRpc: MoneroWalletRpc; + static walletFull: MoneroWalletFull; + static walletKeys: MoneroWalletKeys; + + // common config + static PROXY_TO_WORKER = true; + static MONERO_BINS_DIR = "/Users/woodser/git/haveno/.localnet"; // directory with monero binaries to test (monerod and monero-wallet-rpc) + static SYNC_PERIOD_IN_MS = 5000; // period between wallet syncs in milliseconds + static OFFLINE_SERVER_URI = "offline_server_uri"; // dummy server uri to remain offline because wallet2 connects to default if not given + static AUTO_CONNECT_TIMEOUT_MS = 2000; + + // wallet config + static NETWORK_TYPE = MoneroNetworkType.TESTNET; + static SEED = "silk mocked cucumber lettuce hope adrenalin aching lush roles fuel revamp baptism wrist long tender teardrop midst pastry pigment equip frying inbound pinched ravine frying"; + static ADDRESS = "A1y9sbVt8nqhZAVm3me1U18rUVXcjeNKuBd1oE2cTs8biA9cozPMeyYLhe77nPv12JA3ejJN3qprmREriit2fi6tJDi99RR"; + static FIRST_RECEIVE_HEIGHT = 171; // NOTE: this value must be the height of the wallet's first tx for tests + static WALLET_NAME = "test_wallet_1"; + static WALLET_PASSWORD = "supersecretpassword123"; + static TEST_WALLETS_DIR = "./test_wallets"; + static WALLET_FULL_PATH = TestUtils.TEST_WALLETS_DIR + "/" + TestUtils.WALLET_NAME; + static WALLET_RPC_PORT_START = 28084; + static WALLET_PORT_OFFSETS = {}; + static WALLET_RPC_LOCAL_PATH = TestUtils.MONERO_BINS_DIR + "/monero-wallet-rpc"; + static WALLET_RPC_LOCAL_WALLET_DIR = TestUtils.MONERO_BINS_DIR; + static WALLET_RPC_ACCESS_CONTROL_ORIGINS = "http://localhost:8080"; // cors access from web browser + static MAX_FEE = BigInt("7500000") * BigInt("10000"); + static WALLET_TX_TRACKER = new WalletTxTracker(); // used to track wallet txs for tests + static WALLET_RPC_CONFIG = { + uri: "localhost:28084", + username: "rpc_user", + password: "abc123", + rejectUnauthorized: true // reject self-signed certificates if true + }; + + // daemon config + static DAEMON_LOCAL_PATH = TestUtils.MONERO_BINS_DIR + "/monerod"; + static DAEMON_RPC_CONFIG = { + uri: "localhost:28081", + username: "", + password: "", + rejectUnauthorized: true // reject self-signed certificates if true + }; /** * Get a default file system. Uses an in-memory file system if running in the browser. * - * @return nodejs-compatible file system + * @return {any} nodejs-compatible file system */ - static getDefaultFs() { - if (!LibraryUtils.FS) LibraryUtils.FS = GenUtils.isBrowser() ? require('memfs') : require('fs'); - return LibraryUtils.FS; + static async getDefaultFs(): Promise { + return GenUtils.isBrowser() ? (await import('memfs')).fs : await import('fs'); } /** * Get a singleton daemon RPC instance shared among tests. * - * @return {MoneroDaemonRpc} a daemon RPC instance + * @return {Promise} a daemon RPC instance */ - static async getDaemonRpc() { - if (TestUtils.daemonRpc === undefined) TestUtils.daemonRpc = await monerojs.connectToDaemonRpc(Object.assign({proxyToWorker: TestUtils.PROXY_TO_WORKER}, TestUtils.DAEMON_RPC_CONFIG)); + static async getDaemonRpc(): Promise { + if (TestUtils.daemonRpc === undefined) TestUtils.daemonRpc = await connectToDaemonRpc(Object.assign({proxyToWorker: TestUtils.PROXY_TO_WORKER}, TestUtils.DAEMON_RPC_CONFIG)); return TestUtils.daemonRpc; } @@ -46,13 +95,13 @@ class TestUtils { /** * Get a singleton instance of a monero-wallet-rpc client. * - * @return {MoneroWalletRpc} a wallet RPC instance + * @return {Promise} a wallet RPC instance */ - static async getWalletRpc() { + static async getWalletRpc(): Promise { if (TestUtils.walletRpc === undefined) { // construct wallet rpc instance with daemon connection - TestUtils.walletRpc = await monerojs.connectToWalletRpc(TestUtils.WALLET_RPC_CONFIG); + TestUtils.walletRpc = await connectToWalletRpc(TestUtils.WALLET_RPC_CONFIG); } // attempt to open test wallet @@ -60,11 +109,14 @@ class TestUtils { await TestUtils.walletRpc.openWallet({path: TestUtils.WALLET_NAME, password: TestUtils.WALLET_PASSWORD}); } catch (e) { if (!(e instanceof MoneroRpcError)) throw e; + + console.log(e); // -1 returned when wallet does not exist or fails to open e.g. it's already open by another application if (e.getCode() === -1) { // create wallet + console.log("Creating wallet!"); await TestUtils.walletRpc.createWallet({path: TestUtils.WALLET_NAME, password: TestUtils.WALLET_PASSWORD, seed: TestUtils.SEED, restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT}); } else { throw e; @@ -87,10 +139,10 @@ class TestUtils { /** * Create a monero-wallet-rpc process bound to the next available port. * - * @param {boolean} offline - wallet is started in offline mode + * @param {boolean} [offline] - wallet is started in offline mode (default false) * @return {Promise} - client connected to an internal monero-wallet-rpc instance */ - static async startWalletRpcProcess(offline) { + static async startWalletRpcProcess(offline = false): Promise { // get next available offset of ports to bind to let portOffset = 1; @@ -101,13 +153,13 @@ class TestUtils { let wallet; if (GenUtils.isBrowser()) { let uri = TestUtils.WALLET_RPC_CONFIG.uri.substring(0, TestUtils.WALLET_RPC_CONFIG.uri.lastIndexOf(":")) + ":" + (TestUtils.WALLET_RPC_PORT_START + portOffset); - wallet = await monerojs.connectToWalletRpc(uri, TestUtils.WALLET_RPC_CONFIG.username, TestUtils.WALLET_RPC_CONFIG.password); + wallet = await connectToWalletRpc(uri, TestUtils.WALLET_RPC_CONFIG.username, TestUtils.WALLET_RPC_CONFIG.password); } else { // create command to start client with internal monero-wallet-rpc process let cmd = [ TestUtils.WALLET_RPC_LOCAL_PATH, - "--" + MoneroNetworkType.toString(TestUtils.NETWORK_TYPE), + "--" + GenUtils.getEnumKeyByValue(MoneroNetworkType, TestUtils.NETWORK_TYPE)!.toLowerCase(), "--rpc-bind-port", "" + (TestUtils.WALLET_RPC_PORT_START + portOffset), "--rpc-login", TestUtils.WALLET_RPC_CONFIG.username + ":" + TestUtils.WALLET_RPC_CONFIG.password, "--wallet-dir", TestUtils.WALLET_RPC_LOCAL_WALLET_DIR, @@ -120,7 +172,7 @@ class TestUtils { // TODO: include zmq params when supported and enabled // create and connect to monero-wallet-rpc process - wallet = await monerojs.connectToWalletRpc(cmd); + wallet = await connectToWalletRpc(cmd); } // register wallet with port offset @@ -160,24 +212,24 @@ class TestUtils { if (!TestUtils.walletFull || await TestUtils.walletFull.isClosed()) { // create wallet from seed phrase if it doesn't exist - let fs = TestUtils.getDefaultFs(); - if (!await monerojs.MoneroWalletFull.walletExists(TestUtils.WALLET_FULL_PATH, fs)) { - + let fs = await TestUtils.getDefaultFs(); + if (!await MoneroWalletFull.walletExists(TestUtils.WALLET_FULL_PATH, fs)) { // create directory for test wallets if it doesn't exist + fs.existsSync(TestUtils.TEST_WALLETS_DIR) || fs.mkdirSync(TestUtils.TEST_WALLETS_DIR); if (!fs.existsSync(TestUtils.TEST_WALLETS_DIR)) { if (!fs.existsSync(process.cwd())) fs.mkdirSync(process.cwd(), { recursive: true }); // create current process directory for relative paths which does not exist in memory fs fs.mkdirSync(TestUtils.TEST_WALLETS_DIR); } // create wallet with connection - TestUtils.walletFull = await monerojs.createWalletFull({path: TestUtils.WALLET_FULL_PATH, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, seed: TestUtils.SEED, server: TestUtils.getDaemonRpcConnection(), restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT, proxyToWorker: TestUtils.PROXY_TO_WORKER, fs: fs}); + TestUtils.walletFull = await createWalletFull({path: TestUtils.WALLET_FULL_PATH, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, seed: TestUtils.SEED, server: TestUtils.getDaemonRpcConnection(), restoreHeight: TestUtils.FIRST_RECEIVE_HEIGHT, proxyToWorker: TestUtils.PROXY_TO_WORKER, fs: fs}); assert.equal(await TestUtils.walletFull.getRestoreHeight(), TestUtils.FIRST_RECEIVE_HEIGHT); assert.deepEqual(await TestUtils.walletFull.getDaemonConnection(), TestUtils.getDaemonRpcConnection()); } // otherwise open existing wallet else { - TestUtils.walletFull = await monerojs.openWalletFull({path: TestUtils.WALLET_FULL_PATH, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, server: TestUtils.getDaemonRpcConnection(), proxyToWorker: TestUtils.PROXY_TO_WORKER, fs: TestUtils.getDefaultFs()}); + TestUtils.walletFull = await openWalletFull({path: TestUtils.WALLET_FULL_PATH, password: TestUtils.WALLET_PASSWORD, networkType: TestUtils.NETWORK_TYPE, server: TestUtils.getDaemonRpcConnection(), proxyToWorker: TestUtils.PROXY_TO_WORKER, fs: fs}); await TestUtils.walletFull.setDaemonConnection(TestUtils.getDaemonRpcConnection()); } } @@ -202,7 +254,7 @@ class TestUtils { if (TestUtils.walletKeys === undefined) { // create wallet from seed - TestUtils.walletKeys = await monerojs.createWalletKeys({networkType: TestUtils.NETWORK_TYPE, seed: TestUtils.SEED}); + TestUtils.walletKeys = await createWalletKeys({networkType: TestUtils.NETWORK_TYPE, seed: TestUtils.SEED}); } return TestUtils.walletKeys; } @@ -219,7 +271,7 @@ class TestUtils { static async createWalletGroundTruth(networkType, seed, startHeight, restoreHeight) { // create directory for test wallets if it doesn't exist - let fs = TestUtils.getDefaultFs(); + let fs = await TestUtils.getDefaultFs(); if (!fs.existsSync(TestUtils.TEST_WALLETS_DIR)) { if (!fs.existsSync(process.cwd())) fs.mkdirSync(process.cwd(), { recursive: true }); // create current process directory for relative paths which does not exist in memory fs fs.mkdirSync(TestUtils.TEST_WALLETS_DIR); @@ -228,7 +280,7 @@ class TestUtils { // create ground truth wallet let daemonConnection = new MoneroRpcConnection(TestUtils.DAEMON_RPC_CONFIG); let path = TestUtils.TEST_WALLETS_DIR + "/gt_wallet_" + new Date().getTime(); - let gtWallet = await monerojs.createWalletFull({ + let gtWallet = await createWalletFull({ path: path, password: TestUtils.WALLET_PASSWORD, networkType: networkType, @@ -238,22 +290,20 @@ class TestUtils { fs: fs }); assert.equal(await gtWallet.getRestoreHeight(), restoreHeight ? restoreHeight : 0); - await gtWallet.sync(startHeight, new WalletSyncPrinter()); + await gtWallet.sync(new WalletSyncPrinter(), startHeight); await gtWallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); return gtWallet; } - static testUnsignedBigInteger(num, nonZero) { - assert(num); - assert(num instanceof BigInteger); - let comparison = num.compare(new BigInteger(0)); - assert(comparison >= 0); - if (nonZero === true) assert(comparison > 0); - if (nonZero === false) assert(comparison === 0); + static testUnsignedBigInt(num, nonZero?) { + assert.equal("bigint", typeof num); + assert(num >= 0n); + if (nonZero === true) assert(num > 0n); + else if (nonZero === false) assert(num === 0n); } static async getExternalWalletAddress() { - let wallet = await monerojs.createWalletKeys({networkType: TestUtils.NETWORK_TYPE}); + let wallet = await createWalletKeys({networkType: TestUtils.NETWORK_TYPE}); return await wallet.getAddress(0, 1); // subaddress } @@ -261,8 +311,8 @@ class TestUtils { try { let copy1 = tx1.copy(); let copy2 = tx2.copy(); - if (copy1.isConfirmed()) copy1.setBlock(tx1.getBlock().copy().setTxs([copy1])); - if (copy2.isConfirmed()) copy2.setBlock(tx2.getBlock().copy().setTxs([copy2])); + if (copy1.getIsConfirmed()) copy1.setBlock(tx1.getBlock().copy().setTxs([copy1])); + if (copy2.getIsConfirmed()) copy2.setBlock(tx2.getBlock().copy().setTxs([copy2])); copy1.merge(copy2); return true; } catch (e) { @@ -270,58 +320,4 @@ class TestUtils { return false; } } -} - -// ---------------------------- STATIC TEST CONFIG ---------------------------- - -// TODO: export these to key/value properties file for tests - -// directory with monero binaries to test (monerod and monero-wallet-rpc) -TestUtils.MONERO_BINS_DIR = "/path/to/bins"; - -// test wallet config -TestUtils.WALLET_NAME = "test_wallet_1"; -TestUtils.WALLET_PASSWORD = "supersecretpassword123"; -TestUtils.TEST_WALLETS_DIR = "./test_wallets"; -TestUtils.WALLET_FULL_PATH = TestUtils.TEST_WALLETS_DIR + "/" + TestUtils.WALLET_NAME; - -TestUtils.MAX_FEE = new BigInteger("7500000").multiply(new BigInteger("10000")); -TestUtils.NETWORK_TYPE = MoneroNetworkType.TESTNET; - -// default keypair to test -TestUtils.SEED = "silk mocked cucumber lettuce hope adrenalin aching lush roles fuel revamp baptism wrist long tender teardrop midst pastry pigment equip frying inbound pinched ravine frying"; -TestUtils.ADDRESS = "A1y9sbVt8nqhZAVm3me1U18rUVXcjeNKuBd1oE2cTs8biA9cozPMeyYLhe77nPv12JA3ejJN3qprmREriit2fi6tJDi99RR"; -TestUtils.FIRST_RECEIVE_HEIGHT = 171; // NOTE: this value must be the height of the wallet's first tx for tests - -// wallet RPC config -TestUtils.WALLET_RPC_CONFIG = { - uri: "localhost:28084", - username: "rpc_user", - password: "abc123", - rejectUnauthorized: true // reject self-signed certificates if true -}; - -// daemon config -TestUtils.DAEMON_LOCAL_PATH = TestUtils.MONERO_BINS_DIR + "/monerod"; -TestUtils.DAEMON_RPC_CONFIG = { - uri: "localhost:28081", - username: undefined, - password: undefined, - rejectUnauthorized: true // reject self-signed certificates if true -}; - -const WalletTxTracker = require("./WalletTxTracker"); -TestUtils.WALLET_TX_TRACKER = new WalletTxTracker(); // used to track wallet txs for tests -TestUtils.PROXY_TO_WORKER = true; -TestUtils.SYNC_PERIOD_IN_MS = 5000; // period between wallet syncs in milliseconds -TestUtils.OFFLINE_SERVER_URI = "offline_server_uri"; // dummy server uri to remain offline because wallet2 connects to default if not given -TestUtils.AUTO_CONNECT_TIMEOUT_MS = 2000; - -// monero-wallet-rpc process management -TestUtils.WALLET_RPC_PORT_START = 28084; -TestUtils.WALLET_PORT_OFFSETS = {}; -TestUtils.WALLET_RPC_LOCAL_PATH = TestUtils.MONERO_BINS_DIR + "/monero-wallet-rpc"; -TestUtils.WALLET_RPC_LOCAL_WALLET_DIR = TestUtils.MONERO_BINS_DIR; -TestUtils.WALLET_RPC_ACCESS_CONTROL_ORIGINS = "http://localhost:8080"; // cors access from web browser - -module.exports = TestUtils; +} \ No newline at end of file diff --git a/src/test/utils/WalletEqualityUtils.js b/src/test/utils/WalletEqualityUtils.ts similarity index 81% rename from src/test/utils/WalletEqualityUtils.js rename to src/test/utils/WalletEqualityUtils.ts index f67a278fe..f65d779d9 100644 --- a/src/test/utils/WalletEqualityUtils.js +++ b/src/test/utils/WalletEqualityUtils.ts @@ -1,18 +1,16 @@ -const assert = require("assert"); -const TestUtils = require("./TestUtils"); -const monerojs = require("../../../index"); -const BigInteger = monerojs.BigInteger; -const GenUtils = monerojs.GenUtils; -const MoneroTxQuery = monerojs.MoneroTxQuery; -const MoneroTransferQuery = monerojs.MoneroTransferQuery; -const MoneroOutputQuery = monerojs.MoneroOutputQuery; -const MoneroOutgoingTransfer = monerojs.MoneroOutgoingTransfer; -const MoneroWalletRpc = monerojs.MoneroWalletRpc; +import assert from "assert"; +import TestUtils from "./TestUtils"; +import {GenUtils, + MoneroTxQuery, + MoneroTransferQuery, + MoneroOutputQuery, + MoneroOutgoingTransfer, + MoneroWalletRpc} from "../../../index"; /** * Utilities to deep compare wallets. */ -class WalletEqualityUtils { +export default class WalletEqualityUtils { /** * Compare the keys of two wallets. @@ -36,7 +34,7 @@ class WalletEqualityUtils { // wait for relayed txs associated with wallets to clear pool assert.equal(await w1.isConnectedToDaemon(), await w2.isConnectedToDaemon()); - if (await w1.isConnectedToDaemon()) await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool(w1, w2); + if (await w1.isConnectedToDaemon()) await TestUtils.WALLET_TX_TRACKER.waitForWalletTxsToClearPool([w1, w2]); // sync the wallets until same height while (await w1.getHeight() !== await w2.getHeight()) { @@ -51,41 +49,41 @@ class WalletEqualityUtils { assert.equal(await w2.getPrivateViewKey(), await w1.getPrivateViewKey()); assert.equal(await w2.getPrivateSpendKey(), await w1.getPrivateSpendKey()); let txQuery = new MoneroTxQuery().setIsConfirmed(true); - await WalletEqualityUtils._testTxWalletsEqualOnChain(await w1.getTxs(txQuery), await w2.getTxs(txQuery)); + await WalletEqualityUtils.testTxWalletsEqualOnChain(await w1.getTxs(txQuery), await w2.getTxs(txQuery)); txQuery.setIncludeOutputs(true); - await WalletEqualityUtils._testTxWalletsEqualOnChain(await w1.getTxs(txQuery), await w2.getTxs(txQuery)); // fetch and compare outputs - await WalletEqualityUtils._testAccountsEqualOnChain(await w1.getAccounts(true), await w2.getAccounts(true)); + await WalletEqualityUtils.testTxWalletsEqualOnChain(await w1.getTxs(txQuery), await w2.getTxs(txQuery)); // fetch and compare outputs + await WalletEqualityUtils.testAccountsEqualOnChain(await w1.getAccounts(true), await w2.getAccounts(true)); assert.equal((await w2.getBalance()).toString(), (await w1.getBalance()).toString()); assert.equal((await w2.getUnlockedBalance()).toString(), (await w1.getUnlockedBalance()).toString()); let transferQuery = new MoneroTransferQuery().setTxQuery(new MoneroTxQuery().setIsConfirmed(true)); - await WalletEqualityUtils._testTransfersEqualOnChain(await w1.getTransfers(transferQuery), await w2.getTransfers(transferQuery)); + await WalletEqualityUtils.testTransfersEqualOnChain(await w1.getTransfers(transferQuery), await w2.getTransfers(transferQuery)); let outputQuery = new MoneroOutputQuery().setTxQuery(new MoneroTxQuery().setIsConfirmed(true)); - await WalletEqualityUtils._testOutputWalletsEqualOnChain(await w1.getOutputs(outputQuery), await w2.getOutputs(outputQuery)); + await WalletEqualityUtils.testOutputWalletsEqualOnChain(await w1.getOutputs(outputQuery), await w2.getOutputs(outputQuery)); } - static async _testAccountsEqualOnChain(accounts1, accounts2) { + protected static async testAccountsEqualOnChain(accounts1, accounts2) { for (let i = 0; i < Math.max(accounts1.length, accounts2.length); i++) { if (i < accounts1.length && i < accounts2.length) { - await WalletEqualityUtils._testAccountEqualOnChain(accounts1[i], accounts2[i]); + await WalletEqualityUtils.testAccountEqualOnChain(accounts1[i], accounts2[i]); } else if (i >= accounts1.length) { for (let j = i; j < accounts2.length; j++) { - assert.equal(accounts2[j].getBalance().toString(), BigInteger.parse("0").toString()); + assert.equal(accounts2[j].getBalance().toString(), BigInt("0").toString()); assert(accounts2[j].getSubaddresses().length >= 1); - for (let subaddress of accounts2[j].getSubaddresses()) assert(!subaddress.isUsed()); + for (let subaddress of accounts2[j].getSubaddresses()) assert(!subaddress.getIsUsed()); } return; } else { for (let j = i; j < accounts1.length; j++) { - assert.equal(accounts1[j].getBalance().toString(), BigInteger.parse("0")); + assert.equal(accounts1[j].getBalance().toString(), BigInt("0")); assert(accounts1[j].getSubaddresses().length >= 1); - for (let subaddress of accounts1[j].getSubaddresses()) assert(!subaddress.isUsed()); + for (let subaddress of accounts1[j].getSubaddresses()) assert(!subaddress.getIsUsed()); } return; } } } - static async _testAccountEqualOnChain(account1, account2) { + protected static async testAccountEqualOnChain(account1, account2) { // nullify off-chain data for comparison let subaddresses1 = account1.getSubaddresses(); @@ -97,23 +95,23 @@ class WalletEqualityUtils { // test account equality assert(GenUtils.equals(account2, account1)); - await WalletEqualityUtils._testSubaddressesEqualOnChain(subaddresses1, subaddresses2); + await WalletEqualityUtils.testSubaddressesEqualOnChainAux(subaddresses1, subaddresses2); } - static async _testSubaddressesEqualOnChain(subaddresses1, subaddresses2) { + protected static async testSubaddressesEqualOnChainAux(subaddresses1, subaddresses2) { for (let i = 0; i < Math.max(subaddresses1.length, subaddresses2.length); i++) { if (i < subaddresses1.length && i < subaddresses2.length) { - await WalletEqualityUtils._testSubaddressesEqualOnChain(subaddresses1[i], subaddresses2[i]); + await WalletEqualityUtils.testSubaddressesEqualOnChainAux(subaddresses1[i], subaddresses2[i]); } else if (i >= subaddresses1.length) { for (let j = i; j < subaddresses2.length; j++) { - assert.equal(BigInteger.parse("0"), subaddresses2[j].getBalance().toString()); - assert(!subaddresses2[j].isUsed()); + assert.equal(BigInt("0"), subaddresses2[j].getBalance().toString()); + assert(!subaddresses2[j].getIsUsed()); } return; } else { for (let j = i; j < subaddresses1.length; j++) { - assert.equal(BigInteger.parse("0"), subaddresses1[i].getBalance()); - assert(!subaddresses1[j].isUsed()); + assert.equal(BigInt("0"), subaddresses1[i].getBalance()); + assert(!subaddresses1[j].getIsUsed()); } return; } @@ -126,10 +124,10 @@ class WalletEqualityUtils { assert(GenUtils.equals(subaddress2, subaddress1)); } - static async _testTxWalletsEqualOnChain(txs1, txs2) { + protected static async testTxWalletsEqualOnChain(txs1, txs2) { // nullify off-chain data for comparison - let allTxs = []; + let allTxs: any = []; for (let tx1 of txs1) allTxs.push(tx1); for (let tx2 of txs2) allTxs.push(tx2); for (let tx of allTxs) { @@ -148,9 +146,9 @@ class WalletEqualityUtils { // transfer cached info if known for comparison if (tx1.getOutgoingTransfer() !== undefined && tx1.getOutgoingTransfer().getDestinations() !== undefined) { - if (tx2.getOutgoingTransfer() === undefined || tx2.getOutgoingTransfer().getDestinations() === undefined) WalletEqualityUtils._transferCachedInfo(tx1, tx2); + if (tx2.getOutgoingTransfer() === undefined || tx2.getOutgoingTransfer().getDestinations() === undefined) WalletEqualityUtils.transferCachedInfo(tx1, tx2); } else if (tx2.getOutgoingTransfer() !== undefined && tx2.getOutgoingTransfer().getDestinations() !== undefined) { - WalletEqualityUtils._transferCachedInfo(tx2, tx1); + WalletEqualityUtils.transferCachedInfo(tx2, tx1); } // test tx equality by merging @@ -171,7 +169,7 @@ class WalletEqualityUtils { } } - static async _transferCachedInfo(src, tgt) { + protected static async transferCachedInfo(src, tgt) { // fill in missing incoming transfers when sending from/to the same account if (src.getIncomingTransfers() !== undefined) { @@ -181,7 +179,7 @@ class WalletEqualityUtils { } } // sort transfers - tgt.getIncomingTransfers().sort(MoneroWalletRpc._compareIncomingTransfers); + tgt.getIncomingTransfers().sort(MoneroWalletRpc.compareIncomingTransfers); } // transfer info to outgoing transfer @@ -195,7 +193,7 @@ class WalletEqualityUtils { if (tgt.getOutgoingTransfer() !== undefined) tgt.setPaymentId(src.getPaymentId()); } - static async _testTransfersEqualOnChain(transfers1, transfers2) { + protected static async testTransfersEqualOnChain(transfers1, transfers2) { assert.equal(transfers2.length, transfers1.length); // test and collect transfers per transaction @@ -254,15 +252,15 @@ class WalletEqualityUtils { let transfer2 = txTransfers2[i]; // normalize outgoing transfers - if (transfer1 instanceof MoneroOutgoingTransfer) { + if (transfer1 instanceof MoneroOutgoingTransfer && transfer2 instanceof MoneroOutgoingTransfer) { let ot1 = transfer1; let ot2 = transfer2; // transfer destination info if known for comparison if (ot1.getDestinations() !== undefined) { - if (ot2.getDestinations() === undefined) await WalletEqualityUtils._transferCachedInfo(ot1.getTx(), ot2.getTx()); + if (ot2.getDestinations() === undefined) await WalletEqualityUtils.transferCachedInfo(ot1.getTx(), ot2.getTx()); } else if (ot2.getDestinations() !== undefined) { - await WalletEqualityUtils._transferCachedInfo(ot2.getTx(), ot1.getTx()); + await WalletEqualityUtils.transferCachedInfo(ot2.getTx(), ot1.getTx()); } // nullify other local wallet data @@ -284,7 +282,7 @@ class WalletEqualityUtils { } } - static async _testOutputWalletsEqualOnChain(outputs1, outputs2) { + protected static async testOutputWalletsEqualOnChain(outputs1, outputs2) { assert.equal(outputs2.length, outputs1.length); // test and collect outputs per transaction @@ -348,4 +346,3 @@ class WalletEqualityUtils { } } -module.exports = WalletEqualityUtils; diff --git a/src/test/utils/WalletSyncPrinter.js b/src/test/utils/WalletSyncPrinter.ts similarity index 58% rename from src/test/utils/WalletSyncPrinter.js rename to src/test/utils/WalletSyncPrinter.ts index 0aa011953..c02b643ca 100644 --- a/src/test/utils/WalletSyncPrinter.js +++ b/src/test/utils/WalletSyncPrinter.ts @@ -1,22 +1,23 @@ -const MoneroWalletListener = require("../../main/js/wallet/model/MoneroWalletListener"); +import { MoneroWalletListener } from "../../../index"; /** * Print sync progress every X blocks. */ -class WalletSyncPrinter extends MoneroWalletListener { +export default class WalletSyncPrinter extends MoneroWalletListener { + + nextIncrement: number; + syncResolution: number; - constructor(syncResolution) { + constructor(syncResolution?: number) { super(); this.nextIncrement = 0; this.syncResolution = syncResolution ? syncResolution : .05; } - onSyncProgress(height, startHeight, endHeight, percentDone, message) { + async onSyncProgress(height, startHeight, endHeight, percentDone, message) { if (percentDone === 1 || percentDone >= this.nextIncrement) { console.log("onSyncProgress(" + height + ", " + startHeight + ", " + endHeight + ", " + percentDone + ", " + message + ")"); this.nextIncrement += this.syncResolution; } } } - -module.exports = WalletSyncPrinter; \ No newline at end of file diff --git a/src/test/utils/WalletTxTracker.js b/src/test/utils/WalletTxTracker.ts similarity index 79% rename from src/test/utils/WalletTxTracker.js rename to src/test/utils/WalletTxTracker.ts index 4741468b0..50aadf255 100644 --- a/src/test/utils/WalletTxTracker.js +++ b/src/test/utils/WalletTxTracker.ts @@ -1,6 +1,7 @@ -const monerojs = require("../../../index"); -const GenUtils = monerojs.GenUtils; -const MoneroUtils = monerojs.MoneroUtils; +import TestUtils from "./TestUtils"; // to avoid circular reference +import StartMining from "./StartMining"; +import {GenUtils, + MoneroUtils} from "../../../index"; /** * Tracks wallets which are in sync with the tx pool and therefore whose txs in the pool @@ -11,7 +12,9 @@ const MoneroUtils = monerojs.MoneroUtils; * * TODO monero-project: sync txs relayed outside wallet so this class is unecessary */ -class WalletTxTracker { +export default class WalletTxTracker { + + clearedWallets: Set; constructor() { this.clearedWallets = new Set(); @@ -40,7 +43,6 @@ class WalletTxTracker { */ async waitForWalletTxsToClearPool(wallets) { wallets = GenUtils.listify(wallets); - // get wallet tx hashes let txHashesWallet = new Set(); for (let wallet of wallets) { @@ -55,15 +57,15 @@ class WalletTxTracker { // loop until all wallet txs clear from pool let isFirst = true; let miningStarted = false; - const TestUtils = require("./TestUtils"); // to avoid circular reference + //import TestUtils from "./TestUtils"; // to avoid circular reference let daemon = await TestUtils.getDaemonRpc(); while (true) { // get hashes of relayed, non-failed txs in the pool let txHashesPool = new Set(); for (let tx of await daemon.getTxPool()) { - if (!tx.isRelayed()) continue; - else if (tx.isFailed()) await daemon.flushTxPool(tx.getHash()); // flush tx if failed + if (!tx.getIsRelayed()) continue; + else if (tx.getIsFailed()) await daemon.flushTxPool(tx.getHash()); // flush tx if failed else txHashesPool.add(tx.getHash()); } @@ -82,9 +84,9 @@ class WalletTxTracker { isFirst = false; console.log("Waiting for wallet txs to clear from the pool in order to fully sync and avoid double spend attempts (known issue)"); let miningStatus = await daemon.getMiningStatus(); - if (!miningStatus.isActive()) { + if (!miningStatus.getIsActive()) { try { - const StartMining = require("./StartMining"); + //import StartMining from "./StartMining"; await StartMining.startMining(); miningStarted = true; } catch (e) { @@ -109,34 +111,34 @@ class WalletTxTracker { } async waitForUnlockedBalance(wallet, accountIndex, subaddressIndex, minAmount) { - if (!minAmount) minAmount = new BigInteger("0"); + if (!minAmount) minAmount = BigInt("0"); // check if wallet has balance - if ((await wallet.getBalance(accountIndex, subaddressIndex)).compare(minAmount) < 0) throw new Error("Wallet does not have enough balance to wait for"); + if (await wallet.getBalance(accountIndex, subaddressIndex) < minAmount) throw new Error("Wallet does not have enough balance to wait for"); // check if wallet has unlocked balance let unlockedBalance = await wallet.getUnlockedBalance(accountIndex, subaddressIndex); - if (unlockedBalance.compare(minAmount) > 0) return unlockedBalance; + if (unlockedBalance > minAmount) return unlockedBalance; // start mining - const TestUtils = require("./TestUtils"); // to avoid circular reference + //import TestUtils from "./TestUtils"; // to avoid circular reference let daemon = await TestUtils.getDaemonRpc(); let miningStarted = false; - if (!(await daemon.getMiningStatus()).isActive()) { + if (!(await daemon.getMiningStatus()).getIsActive()) { try { console.log("Starting mining!"); - const StartMining = require("./StartMining"); // to avoid circular reference + //import StartMining from "./StartMining"; // to avoid circular reference await StartMining.startMining(); miningStarted = true; } catch (err) { console.error("Error starting mining:"); - console.error(e); + console.error(err); } } // wait for unlocked balance // TODO: promote to MoneroWallet interface? console.log("Waiting for unlocked balance"); - while (unlockedBalance.compare(minAmount) < 0) { + while (unlockedBalance < minAmount) { unlockedBalance = await wallet.getUnlockedBalance(accountIndex, subaddressIndex); await new Promise(function(resolve) { setTimeout(resolve, TestUtils.SYNC_PERIOD_IN_MS); }); } @@ -146,5 +148,3 @@ class WalletTxTracker { return unlockedBalance; } } - -module.exports = WalletTxTracker; \ No newline at end of file diff --git a/test.txt b/test.txt new file mode 100644 index 000000000..e69de29bb diff --git a/todo.txt b/todo.txt index 0187a6db5..5faf720c7 100644 --- a/todo.txt +++ b/todo.txt @@ -6,7 +6,7 @@ support strings in queries, etc wallet.waitForTxsToClearPool()? http request timeout run testSendToMultiple() a few times to start wallet or send to random subaddress -monerowalletrpc.js/java can use subaddr_indices_all instead of manually setting all indices (wallet_rpc_server.cpp:1420) +monerowalletrpc.ts/java can use subaddr_indices_all instead of manually setting all indices (wallet_rpc_server.cpp:1420) simplify getting available outputs - alias isLocked? isAvailable? support sign(msg, accountIndex, subaddressIndex) default 0s support create_subaddress count up to 64 at a time @@ -56,7 +56,7 @@ namespace exported types to object instead of this.? "Can update a locked tx sent from/to the same account as blocks are added to the chain" sometimes gets stuck waiting for updates, possibly because tx was rejected with double spend? daemon rpc getTxs() does not return tx blocks overload toJson() model protos and use JSON.stringify() instead of class toJsons() -master sort MoneroWallet.js with top level methods +master sort MoneroWallet.ts with top level methods monero_wallet_full get_keys_file_buffer/get_cache_file_buffer use m_password instead of arg overload create_wallet_* instead of http_client=null? compare sync speeds with and without notifications diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..5e91d40aa --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "declaration": true, + "module": "commonjs", + "target": "es2021", + "allowJs": true, + "outDir": "dist", + "skipLibCheck": true, + "types": [ + "mocha", + "node", + "jquery" + ], + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitAny": false, + "noImplicitThis": false, + "strictNullChecks": false, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["index.ts", "src/**/*"], + "exclude": ["node_modules", "dist/*", "types"] +} \ No newline at end of file diff --git a/webpack.base.js b/webpack.base.js index a26ce8a92..d79fe7fb1 100644 --- a/webpack.base.js +++ b/webpack.base.js @@ -7,6 +7,11 @@ let configBase = { mode: 'production', module: { rules: [ + { + test: /\.ts?$/, + loader: 'ts-loader', + exclude: /node_modules/ + }, { test: /\.js$/, exclude: path.join(__dirname, 'node_modules'), @@ -31,12 +36,15 @@ let configBase = { }), ], resolve: { + extensions: ['.js', '.ts'], alias: { - "fs": "html5-fs" + "fs": "html5-fs", + ['~']: path.resolve(__dirname + '/app') }, - extensions: ['.js', '.jsx', '.css', '.json', 'otf', 'ttf', 'eot', 'svg'], + extensions: ['.js', '.jsx', '.css', '.json', 'otf', 'ttf', 'eot', 'svg', '.ts', '.tsx'], modules: [ - 'node_modules' + 'node_modules', + path.resolve(__dirname + '/src') ], fallback: { // browser polyfills assert: require.resolve('assert'), diff --git a/webpack.tests.js b/webpack.tests.js index 28e164c37..8c97f6f87 100644 --- a/webpack.tests.js +++ b/webpack.tests.js @@ -5,10 +5,10 @@ const configBase = require("./webpack.base.js"); let configBrowserTest = Object.assign({}, configBase, { name: "Browser tests config", - entry: "./src/test/browser/tests.js", + entry: "./dist/src/test/browser/tests.js", output: { path: path.resolve(__dirname, "browser_build"), - filename: "monero-javascript-tests.js" + filename: "monero-ts-tests.js" }, }); diff --git a/webpack.worker.js b/webpack.worker.js index ba76cf6ad..0071cfdd2 100644 --- a/webpack.worker.js +++ b/webpack.worker.js @@ -5,7 +5,7 @@ const configBase = require("./webpack.base.js"); let configMoneroWebWorker = Object.assign({}, configBase, { name: "Monero web worker config", - entry: "./src/main/js/common/MoneroWebWorker.js", + entry: "./src/main/ts/common/MoneroWebWorker.ts", output: { path: path.resolve(__dirname, "dist"), filename: "monero_web_worker.js"