diff --git a/.gitignore b/.gitignore index 6fe20f0..b548e42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ node_modules -.env -package-lock.json \ No newline at end of file +priv diff --git a/API.md b/API.md deleted file mode 100644 index cde8695..0000000 --- a/API.md +++ /dev/null @@ -1,583 +0,0 @@ -## Classes - -
-
CardanocliJs
-
-
- -## Typedefs - -
-
lovelace
-
-
path
-
-
paymentAddr
-
-
stakeAddr
-
-
TxIn : Object
-
-
TxOut : Object
-
-
MintAction : Object
-
-
Mint : Object
-
-
Certificate : Object
-
-
Withdrawal : Object
-
-
- - - -## CardanocliJs -**Kind**: global class - -* [CardanocliJs](#CardanocliJs) - * [new CardanocliJs(options)](#new_CardanocliJs_new) - * [.queryProtocolParameters()](#CardanocliJs+queryProtocolParameters) ⇒ object - * [.queryTip()](#CardanocliJs+queryTip) ⇒ object - * [.queryStakeAddressInfo(address)](#CardanocliJs+queryStakeAddressInfo) ⇒ object - * [.queryUtxo(address)](#CardanocliJs+queryUtxo) ⇒ object - * [.addressKeyGen(account)](#CardanocliJs+addressKeyGen) - * [.stakeAddressKeyGen(account)](#CardanocliJs+stakeAddressKeyGen) - * [.stakeAddressBuild(account)](#CardanocliJs+stakeAddressBuild) ⇒ [path](#path) - * [.addressBuild(account, options)](#CardanocliJs+addressBuild) - * [.addressKeyHash(account)](#CardanocliJs+addressKeyHash) - * [.addressInfo(address)](#CardanocliJs+addressInfo) ⇒ object - * [.addressBuildScript(script)](#CardanocliJs+addressBuildScript) ⇒ [paymentAddr](#paymentAddr) - * [.wallet(account)](#CardanocliJs+wallet) - * [.pool(poolName)](#CardanocliJs+pool) - * [.stakeAddressRegistrationCertificate(account)](#CardanocliJs+stakeAddressRegistrationCertificate) ⇒ [path](#path) - * [.stakeAddressDeregistrationCertificate(account)](#CardanocliJs+stakeAddressDeregistrationCertificate) ⇒ [path](#path) - * [.stakeAddressDelegationCertificate(account, poolId)](#CardanocliJs+stakeAddressDelegationCertificate) ⇒ [path](#path) - * [.stakeAddressKeyHash(account)](#CardanocliJs+stakeAddressKeyHash) ⇒ string - * [.nodeKeyGenKES(poolName)](#CardanocliJs+nodeKeyGenKES) - * [.nodeKeyGen(poolName)](#CardanocliJs+nodeKeyGen) - * [.nodeIssueOpCert(poolName, [kesPeriod])](#CardanocliJs+nodeIssueOpCert) ⇒ [path](#path) - * [.nodeKeyGenVRF(poolName)](#CardanocliJs+nodeKeyGenVRF) - * [.stakePoolId(poolName)](#CardanocliJs+stakePoolId) ⇒ string - * [.stakePoolMetadataHash(metadata)](#CardanocliJs+stakePoolMetadataHash) ⇒ string - * [.stakePoolRegistrationCertificate(poolName, options)](#CardanocliJs+stakePoolRegistrationCertificate) ⇒ [path](#path) - * [.stakePoolDeregistrationCertificate(poolName, epoch)](#CardanocliJs+stakePoolDeregistrationCertificate) ⇒ [path](#path) - * [.transactionBuildRaw(options)](#CardanocliJs+transactionBuildRaw) ⇒ [path](#path) - * [.transactionCalculateMinFee(options)](#CardanocliJs+transactionCalculateMinFee) ⇒ [lovelace](#lovelace) - * [.transactionPolicyid(script)](#CardanocliJs+transactionPolicyid) ⇒ string - * [.transactionSign(options)](#CardanocliJs+transactionSign) ⇒ [path](#path) - * [.transactionWitness(options)](#CardanocliJs+transactionWitness) ⇒ [path](#path) - * [.transactionAssemble(options)](#CardanocliJs+transactionAssemble) ⇒ [path](#path) - * [.transactionCalculateMinValue(value)](#CardanocliJs+transactionCalculateMinValue) ⇒ [lovelace](#lovelace) - * [.transactionSubmit(tx)](#CardanocliJs+transactionSubmit) ⇒ string - * [.transactionTxid(options)](#CardanocliJs+transactionTxid) ⇒ [path](#path) - * [.transactionView(options)](#CardanocliJs+transactionView) ⇒ [path](#path) - * [.KESPeriod()](#CardanocliJs+KESPeriod) ⇒ number - * [.getDownloadUrl(filePath)](#CardanocliJs+getDownloadUrl) ⇒ [path](#path) - * [.toLovelace(ada)](#CardanocliJs+toLovelace) ⇒ [lovelace](#lovelace) - * [.toAda(lovelace)](#CardanocliJs+toAda) ⇒ number - - - -### new CardanocliJs(options) - -| Param | Type | Description | -| --- | --- | --- | -| options | Object | | -| [options.shelleyGenesisPath] | [path](#path) | | -| [options.socketPath] | [path](#path) | Default: Env Variable | -| [options.cliPath] | [path](#path) | Default: Env Variable | -| [options.dir] | [path](#path) | Default: Working Dir | -| [options.era] | string | | -| [options.network] | string | Default: mainnet | -| [options.httpProvider] | string | Optional - Useful when using cli at different location than node or in browser | - - - -### cardanocliJs.queryProtocolParameters() ⇒ object -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - - -### cardanocliJs.queryTip() ⇒ object -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - - -### cardanocliJs.queryStakeAddressInfo(address) ⇒ object -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| address | [stakeAddr](#stakeAddr) | - - - -### cardanocliJs.queryUtxo(address) ⇒ object -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| address | [paymentAddr](#paymentAddr) | - - - -### cardanocliJs.addressKeyGen(account) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of account | - - - -### cardanocliJs.stakeAddressKeyGen(account) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of account | - - - -### cardanocliJs.stakeAddressBuild(account) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of account | - - - -### cardanocliJs.addressBuild(account, options) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of account | -| options | Object | | -| [options.paymentVkey] | [path](#path) | | -| [options.stakeVkey] | [path](#path) | | -| [options.paymentScript] | object | | -| [options.stakeScript] | object | | - - - -### cardanocliJs.addressKeyHash(account) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of account | - - - -### cardanocliJs.addressInfo(address) ⇒ object -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| address | [paymentAddr](#paymentAddr) | - - - -### cardanocliJs.addressBuildScript(script) ⇒ [paymentAddr](#paymentAddr) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| script | object | - - - -### cardanocliJs.wallet(account) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of the account | - - - -### cardanocliJs.pool(poolName) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| poolName | string | Name of the pool | - - - -### cardanocliJs.stakeAddressRegistrationCertificate(account) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of the account | - - - -### cardanocliJs.stakeAddressDeregistrationCertificate(account) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of the account | - - - -### cardanocliJs.stakeAddressDelegationCertificate(account, poolId) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of the account | -| poolId | string | Stake pool verification key (Bech32 or hex-encoded) | - - - -### cardanocliJs.stakeAddressKeyHash(account) ⇒ string -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| account | string | Name of the account | - - - -### cardanocliJs.nodeKeyGenKES(poolName) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| poolName | string | Name of the pool | - - - -### cardanocliJs.nodeKeyGen(poolName) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| poolName | string | Name of the pool | - - - -### cardanocliJs.nodeIssueOpCert(poolName, [kesPeriod]) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| poolName | string | Name of the pool | -| [kesPeriod] | number | Optional (Offline mode) | - - - -### cardanocliJs.nodeKeyGenVRF(poolName) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| poolName | string | Name of the pool | - - - -### cardanocliJs.stakePoolId(poolName) ⇒ string -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| poolName | string | Name of the pool | - - - -### cardanocliJs.stakePoolMetadataHash(metadata) ⇒ string -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| metadata | string | Raw File | - - - -### cardanocliJs.stakePoolRegistrationCertificate(poolName, options) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| poolName | string | Name of the pool | -| options | Object | | -| options.pledge | [lovelace](#lovelace) | | -| options.margin | number | | -| options.cost | [lovelace](#lovelace) | | -| options.url | string | | -| options.metaHash | string | | -| options.rewardAccount | [path](#path) | | -| options.owners | [Array.<path>](#path) | | -| options.relays | Array.<Object> | | - - - -### cardanocliJs.stakePoolDeregistrationCertificate(poolName, epoch) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| poolName | string | Name of the pool | -| epoch | number | Retirement Epoch | - - - -### cardanocliJs.transactionBuildRaw(options) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| options | Object | | -| options.txIn | [Array.<TxIn>](#TxIn) | | -| options.txOut | [Array.<TxOut>](#TxOut) | | -| [options.withdrawals] | [Array.<Withdrawal>](#Withdrawal) | | -| [options.certs] | [Array.<Certificate>](#Certificate) | | -| [options.fee] | [lovelace](#lovelace) | | -| [options.mint] | [Mint](#Mint) | | -| [options.auxScript] | Array.<object> | | -| [options.metadata] | object | | -| [options.invalidBefore] | number | Default: 0 | -| [options.invalidAfter] | number | Default: current+10000 | - - - -### cardanocliJs.transactionCalculateMinFee(options) ⇒ [lovelace](#lovelace) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| options | Object | -| options.txBody | [path](#path) | -| options.txIn | Array.<object> | -| options.txOut | Array.<object> | -| options.witnessCount | number | - - - -### cardanocliJs.transactionPolicyid(script) ⇒ string -**Kind**: instance method of [CardanocliJs](#CardanocliJs) -**Returns**: string - - Policy Id - -| Param | Type | -| --- | --- | -| script | object | - - - -### cardanocliJs.transactionSign(options) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | Description | -| --- | --- | --- | -| options | Object | | -| options.signingKeys | [Array.<path>](#path) | One ore more signing keys | -| options.txBody | [path](#path) | | - - - -### cardanocliJs.transactionWitness(options) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| options | Object | -| options.txBody | [path](#path) | -| options.signingKey | [path](#path) | - - - -### cardanocliJs.transactionAssemble(options) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| options | Object | -| options.txBody | [path](#path) | -| options.witnessFiles | [Array.<path>](#path) | - - - -### cardanocliJs.transactionCalculateMinValue(value) ⇒ [lovelace](#lovelace) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| value | object | - - - -### cardanocliJs.transactionSubmit(tx) ⇒ string -**Kind**: instance method of [CardanocliJs](#CardanocliJs) -**Returns**: string - - Transaction Hash - -| Param | Type | Description | -| --- | --- | --- | -| tx | [path](#path) \| string | Path or Signed Tx File | - - - -### cardanocliJs.transactionTxid(options) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| options | Object | -| [options.txBody] | [path](#path) | -| [options.txFile] | [path](#path) | - - - -### cardanocliJs.transactionView(options) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| options | Object | -| [options.txBody] | [path](#path) | -| [options.txFile] | [path](#path) | - - - -### cardanocliJs.KESPeriod() ⇒ number -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - - -### cardanocliJs.getDownloadUrl(filePath) ⇒ [path](#path) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) -**Returns**: [path](#path) - - Download link for the file - -| Param | Type | -| --- | --- | -| filePath | [path](#path) | - - - -### cardanocliJs.toLovelace(ada) ⇒ [lovelace](#lovelace) -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| ada | number | - - - -### cardanocliJs.toAda(lovelace) ⇒ number -**Kind**: instance method of [CardanocliJs](#CardanocliJs) - -| Param | Type | -| --- | --- | -| lovelace | [lovelace](#lovelace) | - - - -## lovelace -**Kind**: global typedef -**Properties** - -| Type | -| --- | -| number | - - - -## path -**Kind**: global typedef -**Properties** - -| Type | -| --- | -| string | - - - -## paymentAddr -**Kind**: global typedef -**Properties** - -| Type | -| --- | -| string | - - - -## stakeAddr -**Kind**: global typedef -**Properties** - -| Type | -| --- | -| string | - - - -## TxIn : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| txHash | string | -| txId | string | -| [script] | object | - - - -## TxOut : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| address | string | -| value | object | - - - -## MintAction : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| action | string | -| quantity | number | -| asset | string | - - - -## Mint : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| mintAction | [Array.<MintAction>](#MintAction) | -| script | Array.<object> | - - - -## Certificate : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| cert | [path](#path) | -| [script] | object | - - - -## Withdrawal : Object -**Kind**: global typedef -**Properties** - -| Name | Type | -| --- | --- | -| stakingAddress | string | -| reward | [lovelace](#lovelace) | -| [script] | object | - diff --git a/README.md b/README.md index 3d3e1c0..74b3414 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,16 @@ ## Overview -This is a library, which wraps the cardano-cli with JavaScript and makes it possible to interact with the cli-commands much faster and more efficient. +This is a library wrapping the cardano-cli with TypeScript (can also be used in Javascript projects) which makes it possible to interact with the cardano CLI much faster and more efficient. -#### This library was initially brought by [BerryPool](http://pipool.online/) and currently maintained by [Shareslake](https://www.shareslake.com). You can support the work by delegating to the Berry pool. +## Prerequisites -#### Donations (ADA): +You need access to a Cardano Node socket. If you have a remote node you can create a ssh tunnel with the socket file as follows: -- Shareslake: -``` -addr1q9rsrh7kjhct7llm88dug6l5mh047gq6yq3wt3gjfd6uk3ldch4wp7w7v3ac4wp6q33gz2kemwn8ap6zch0u3za6pypshaa6ry -``` -- Berry: ``` -addr1q97x8rfnkw4pmdgnwjzavl8jvg77tuy6wn3wm90x9emwgj8nhh356yzp7k3qwmhe4fk0g5u6kx5ka4rz5qcq4j7mvh2sg67tj5 +ssh -nNT -L /tmp/forwarded.socket:/path/to/remote/node.socket remote-machine-user@remote-machine-ip ``` - -## Prerequisites - -- `cardano-node >= 1.29.0` -- `node.js >= 12.19.0` - ## Install #### NPM @@ -34,7 +23,7 @@ npm install cardanocli-js #### From source ```bash -git clone https://github.com/shareslake/cardanocli-js.git +git clone https://github.com/miguelaeh/cardanocli-js.git cd cardanocli-js npm install ``` @@ -48,75 +37,41 @@ const shelleyGenesisPath = "/home/ada/mainnet-shelley-genesis.json"; const cardanocliJs = new CardanocliJs({ shelleyGenesisPath }); const createWallet = (account) => { - const payment = cardanocliJs.addressKeyGen(account); - const stake = cardanocliJs.stakeAddressKeyGen(account); - cardanocliJs.stakeAddressBuild(account); - cardanocliJs.addressBuild(account, { + const payment = cardanocliJs.address.keyGen(account); + const stake = cardanocliJs.stake_address.keyGen(account); + cardanocliJs.stake_address.build(account); + const addr = cardanocliJs.address.build(account, { paymentVkey: payment.vkey, stakeVkey: stake.vkey, }); - return cardanocliJs.wallet(account); + return addr; }; -const createPool = (name) => { - cardanocliJs.nodeKeyGenKES(name); - cardanocliJs.nodeKeyGen(name); - cardanocliJs.nodeIssueOpCert(name); - cardanocliJs.nodeKeyGenVRF(name); - return cardanocliJs.pool(name); -}; +const walletAddr = createWallet("my-wallet-name"); -const wallet = createWallet("Ada"); -const pool = createPool("Berry"); - -console.log(wallet.paymentAddr); -console.log(pool.vrf.vkey); +console.log("My wallet address:", walletAddr); ``` -Check /examples for more use cases. - -## API +Check `/examples` for more use cases. If you have doubts on how to use a specific command you can also check the `tests` folder, where all the commands are tested. -- API Documentation - -## Structure - -All files will be stored and used in the directory you choose when instantiating CardanocliJs (`dir`). -The directory is split in two subfolders `tmp` and `priv`. -In the `tmp` folder are stored protocol paramters, raw transactions, signed transactions and witnesses with unique identifiers. -The `priv` folder is again divided into two subolders holding on one site the pools `pool` and on the other side the wallets `wallet` (like [CNTools](https://cardano-community.github.io/guild-operators/#/) structure). +## Tests -Example structure: +Install npm dev dependencies using `npm install --also=dev`. -``` -dir - tmp - - ... - priv - pool - Berry - - - - ... - wallet - Lovelace - - - ... -``` +Tests are using Jest framework and can be run by using `npm run-script test` command. -## Tests +Tests are configured to run with the Sancho network. You may need to update your `cardano-cli` binary to the sancho one in order to run the tests. -Install npm dev dependencies using `npm install --also=dev`. +## Major changes -Tests are using Jest framework and can be run by using `npm -s run test` command. +### 5.0.0 -To configure the test suite, make a copy of `.env.dist` and rename it `.env`. Then change all parameters values to fit your environment. +Starting on version `4.0.0` the HTTP provider has been removed. There are better options for that as of today. For example, the blockfrost API. +This means than to use this library you need a fully synced node. And it is, as its name states, a wrapper over the CLI to make your life easier when creating scripts. +If you need to connect to your own remote cardano nodes you can forward the socket via an SSH tunnel: -**Caution**: The `TEST_WORKSPACE_DIR` will be deleted at the end of the test suite. **NEVER USE AN EXISTING DIRECTORY !!!** You may disable this behavior by commenting the cleanup function in `test/index.test.js`: +``` +ssh -nNT -L /tmp/forwarded.socket:/path/to/remote/node.socket remote-machine-user@remote-machine-ip +``` - afterAll(() => { - // cleanUpTestDirectory(); - }); +For better maintencane, the library has been moved to TypesScript and its internal structure changed. diff --git a/examples/deregisterWallet.js b/examples/deregisterWallet.js deleted file mode 100644 index c77b7bf..0000000 --- a/examples/deregisterWallet.js +++ /dev/null @@ -1,58 +0,0 @@ -const CardanocliJs = require("../index.js"); -const os = require("os"); -const path = require("path"); - -const dir = path.join(os.homedir(), "testnet"); -const shelleyPath = path.join( - os.homedir(), - "testnet", - "testnet-shelley-genesis.json" -); - -const cardanocliJs = new CardanocliJs({ - network: "testnet-magic 1097911063", - dir: dir, - shelleyGenesisPath: shelleyPath, -}); - -const deregisterWallet = (wallet) => { - let account = wallet.name; - let stakeAddressDeposit = cardanocliJs.queryProtocolParameters().stakeAddressDeposit; - let stakeCert = cardanocliJs.stakeAddressDeregistrationCertificate(account); - let paymentAddress = cardanocliJs.wallet(account).paymentAddr; - let balance = cardanocliJs.wallet(account).balance().value.lovelace; - let tx = { - txIn: cardanocliJs.queryUtxo(paymentAddress), - txOut: [ - { address: paymentAddress, value: { lovelace: balance + stakeAddressDeposit } }, - ], - certs: [{ cert: stakeCert }], - witnessCount: 2, - }; - let txBodyRaw = cardanocliJs.transactionBuildRaw(tx); - let fee = cardanocliJs.transactionCalculateMinFee({ - ...tx, - txBody: txBodyRaw, - }); - tx.txOut[0].value.lovelace -= fee; - let txBody = cardanocliJs.transactionBuildRaw({ ...tx, fee }); - let txSigned = cardanocliJs.transactionSign({ - txBody, - signingKeys: [ - cardanocliJs.wallet(account).payment.skey, - cardanocliJs.wallet(account).stake.skey, - ], - }); - - return txSigned; -}; - -let wallet = cardanocliJs.wallet("Test"); - -console.log(wallet); - -let tx = deregisterWallet(wallet); - -let txHash = cardanocliJs.transactionSubmit(tx); - -console.log("TxHash: " + txHash); diff --git a/examples/mintMA.js b/examples/mintMA.js index 21cd4cd..a5c94fb 100644 --- a/examples/mintMA.js +++ b/examples/mintMA.js @@ -1,63 +1,71 @@ -const CardanocliJs = require("../index.js"); -const os = require("os"); -const path = require("path"); - -const dir = path.join(os.homedir(), "testnet"); -const shelleyPath = path.join( - os.homedir(), - "testnet", - "testnet-shelley-genesis.json" -); - -const cardanocliJs = new CardanocliJs({ - network: "testnet-magic 1097911063", - dir: dir, - shelleyGenesisPath: shelleyPath, -}); +import CardanoCliJs from "../index"; +import { CardanoCliJsOptions } from "../lib/cardanoclijs"; +import fs from 'fs'; -const createTransaction = (tx) => { - let raw = cardanocliJs.transactionBuildRaw(tx); - let fee = cardanocliJs.transactionCalculateMinFee({ - ...tx, - txBody: raw, - }); - tx.txOut[0].value.lovelace -= fee; - return cardanocliJs.transactionBuildRaw({ ...tx, fee }); -}; +const options = new CardanoCliJsOptions({ shelleyGenesisPath: `${__dirname}/../tests/assets/shelley-genesis.json` }); +const cli = new CardanoCliJs(options); -const signTransaction = (wallet, tx, script) => { - return cardanocliJs.transactionSign({ - signingKeys: [wallet.payment.skey, wallet.payment.skey], - txBody: tx, +const createWallet = (account) => { + const payment = cardanocliJs.address.keyGen(account); + const stake = cardanocliJs.stake_address.keyGen(account); + cardanocliJs.stake_address.build(account); + const addr = cardanocliJs.address.build(account, { + paymentVkey: payment.vkey, + stakeVkey: stake.vkey, }); + return addr; }; -const wallet = cardanocliJs.wallet("Berry"); +const walletName = "test-wallet"; +const wallet = createWallet(walletName); const mintScript = { - keyHash: cardanocliJs.addressKeyHash(wallet.name), + keyHash: cli.address.keyHash(walletName), type: "sig", }; -const policy = cardanocliJs.transactionPolicyid(mintScript); +const scriptFile = fs.writeFileSync("/tmp/script.json", JSON.stringify(mintScript)); +const policyId = cli.transaction.transactionPolicyId(scriptFile); const realAssetName = "Berrycoin" const assetName = Buffer.from(realAssetName).toString('hex') -const BERRYCOIN = policy + "." + assetName; - -const tx = { - txIn: wallet.balance().utxo, - txOut: [ - { - address: wallet.paymentAddr, - value: { ...wallet.balance().value, [BERRYCOIN]: 100 }, - }, - ], - mint: [ - { action: "mint", quantity: 100, asset: BERRYCOIN, script: mintScript }, - ], - witnessCount: 2, -}; +const BERRYCOIN = policyId + "." + assetName; + +const addr = fs.readFileSync(`${cli.options.dir}/priv/wallet/${walletName}/${walletName}.payment.addr`, 'utf8'); +const utxos = cli.query.utxo(addr); +const balance = utxos.forEach((utxo) => { + Object.keys(utxo.value).forEach((asset) => { + if (!value[asset]) value[asset] = 0; + value[asset] += utxo.value[asset]; + }); +}); + +// Create raw tx +const raw_opts = []; +for (let utxo of Object.keys(utxos)) { + raw_opts.push({ name: 'tx-in', value: utxo}); +} +raw_opts.push({ name: 'tx-out', value: `${addr} ${balance}`}); +// Mint 100 tokens +raw_opts.push({ name: 'mint', value: `100 ${BERRYCOIN}`}); +raw_opts.push({ name: 'minting-script-file', value: scriptFile }); +const draftTxFile = cli.transaction.buildRaw(raw_opts); +const fee = cli.transaction.calculateMinFee(draftTxFile, utxos.length, 1, 1); + +// Create the options again with the correct amount (removing the fee) +const opts = []; +for (let utxo of Object.keys(utxos)) { + opts.push({ name: 'tx-in', value: utxo}); +} +opts.push({ name: 'tx-out', value: `${addr} ${balance - fee}`}); +// Mint 100 tokens +opts.push({ name: 'mint', value: `100 ${BERRYCOIN}`}); +opts.push({ name: 'minting-script-file', value: scriptFile }); +opts.push({ name: 'fee', value: fee }); +const rawTxFile = cli.transaction.buildRaw(opts); + +const signedTxFile = cli.transaction.sign(rawTxFile, [ + `${cli.options.dir}/priv/wallet/${walletName}/${walletName}.payment.skey` +]); + +console.log(cli.transaction.view({ txFile: signedTxFile })); -const raw = createTransaction(tx); -const signed = signTransaction(wallet, raw); -console.log(cardanocliJs.transactionView({ txFile: signed })); -const txHash = cardanocliJs.transactionSubmit(signed); -console.log(txHash); +// Submit the transaction to actually mint the asset +cli.transaction.submit(signedTxFile); diff --git a/examples/queryTip.js b/examples/queryTip.js index 3e81435..e528055 100644 --- a/examples/queryTip.js +++ b/examples/queryTip.js @@ -1,20 +1,7 @@ -const CardanocliJs = require("../index.js"); -const os = require("os"); -const path = require("path"); +import CardanoCliJs from "../index"; +import { CardanoCliJsOptions } from "../lib/cardanoclijs"; -const dir = path.join(os.homedir(), "testnet"); -const shelleyPath = path.join( - os.homedir(), - "testnet", - "testnet-shelley-genesis.json" -); +const options = new CardanoCliJsOptions({ shelleyGenesisPath: `${__dirname}/../tests/assets/shelley-genesis.json` }); +const cli = new CardanoCliJs(options); -const cardanocliJs = new CardanocliJs({ - network: "testnet-magic 1097911063", - era: "alonzo", - dir: dir, - shelleyGenesisPath: shelleyPath, - socketPath: path.join(os.homedir(), "testnet", "db", "socket"), -}); - -console.log(cardanocliJs.queryTip()); +console.log(cli.query.tip()); diff --git a/examples/registerPool.js b/examples/registerPool.js index cdc050c..085074c 100644 --- a/examples/registerPool.js +++ b/examples/registerPool.js @@ -1,86 +1,53 @@ -const CardanocliJs = require("../index.js"); -const os = require("os"); -const path = require("path"); -const fetch = require("sync-fetch"); - -const dir = path.join(os.homedir(), "testnet"); -const shelleyPath = path.join( - os.homedir(), - "testnet", - "testnet-shelley-genesis.json" -); - -const cardanocliJs = new CardanocliJs({ - network: "testnet-magic 1097911063", - dir: dir, - shelleyGenesisPath: shelleyPath, -}); - -const createPool = (name) => { - cardanocliJs.nodeKeyGenKES(name); - cardanocliJs.nodeKeyGen(name); - cardanocliJs.nodeIssueOpCert(name); - cardanocliJs.nodeKeyGenVRF(name); - return cardanocliJs.pool(name); -}; - -const registerPool = (pool, wallet, data) => { - let name = pool.name; - let poolId = cardanocliJs.stakePoolId(name); - let poolCert = cardanocliJs.stakePoolRegistrationCertificate(name, data); - let delegCert = cardanocliJs.stakeAddressDelegationCertificate( - wallet.name, - poolId - ); - let poolDeposit = cardanocliJs.queryProtocolParameters().poolDeposit; - let tx = { - txIn: wallet.balance().utxo, - txOut: [ - { - address: wallet.paymentAddr, - value: { lovelace: wallet.balance().value.lovelace - poolDeposit }, - }, - ], - witnessCount: 3, - certs: [{ cert: poolCert }, { cert: delegCert }], - }; - let txBodyRaw = cardanocliJs.transactionBuildRaw(tx); - let fee = cardanocliJs.transactionCalculateMinFee({ - ...tx, - txBody: txBodyRaw, - }); - tx.txOut[0].value.lovelace -= fee; - let txBody = cardanocliJs.transactionBuildRaw({ ...tx, fee }); - let txSigned = cardanocliJs.transactionSign({ - txBody, - signingKeys: [wallet.payment.skey, wallet.stake.skey, pool.node.skey], +import CardanoCliJs from "../index"; +import { CardanoCliJsOptions } from "../lib/cardanoclijs"; + +const options = new CardanoCliJsOptions({ shelleyGenesisPath: `${__dirname}/../tests/assets/shelley-genesis.json` }); +const cli = new CardanoCliJs(options); + +const createWallet = (account) => { + const payment = cardanocliJs.address.keyGen(account); + const stake = cardanocliJs.stake_address.keyGen(account); + cardanocliJs.stake_address.build(account); + const addr = cardanocliJs.address.build(account, { + paymentVkey: payment.vkey, + stakeVkey: stake.vkey, }); - return txSigned; + return addr; }; -let pool = createPool("BerryJs"); - -const wallet = cardanocliJs.wallet("Ada"); -console.log(wallet); - -const poolData = { - pledge: cardanocliJs.toLovelace(100), - margin: 0.015, - cost: cardanocliJs.toLovelace(340), - owners: [wallet.stake.vkey], - rewardAccount: wallet.stake.vkey, +const walletName = "test-wallet"; +const wallet = createWallet(walletName); + +const poolName = "test-pool"; +const poolKesKeys = cli.node.keyGenKES(poolName); +const poolKeys = cli.node.keyGen(poolName); +const poolOpCert = cli.node.issueOpCert(poolName, {}); +const poolVrfKeys = cli.node.keyGenVRF(poolName); + +const poolId = cli.stake_pool.id(poolName); +const poolCert = cli.stake_pool.registrationCertificate(poolName, { + pledge: 100000000, + margin: 0.0015, + cost: 340000000, + url: "https://test-url.com", + metaHash: cli.stake_pool.metadataHash({ + name: "YourPoolName", + description: "Your pool description", + ticker: "TEST", + homepage: "https://yourpoollink.com" + }), + rewardAccountFile: `${cli.options.dir}/priv/wallet/${walletName}/${walletName}.stake.vkey`, + ownersStakeVKeyFiles: [ `${cli.options.dir}/priv/wallet/${walletName}/${walletName}.stake.vkey` ], relays: [ { host: "relay.one.io", port: 3001 }, { host: "relay.two.io", port: 3001 }, ], - url: "", - metaHash: cardanocliJs.stakePoolMetadataHash(fetch("").text()), -}; - -console.log(poolData); - -let tx = registerPool(pool, wallet, poolData); +}); -let txHash = cardanocliJs.transactionSubmit(tx); +const stakeAddrDelegCert = cli.stake_address.delegationCertificate(walletName, { stakePoolId: poolId }); +const poolDeposit = cli.query.protocolParameters().poolDeposit; -console.log("TxHash: " + txHash); +// Just create the transaction as always and sign with 3 witness count: +// `${cli.options.dir}/priv/wallet/${walletName}/${walletName}.payment.skey` +// `${cli.options.dir}/priv/wallet/${walletName}/${walletName}.stake.vkey` +// `${cli.options.dir}/priv/wallet/${poolName}/${poolName}.node.skey` diff --git a/examples/registerWallet.js b/examples/registerWallet.js deleted file mode 100644 index 6b651e6..0000000 --- a/examples/registerWallet.js +++ /dev/null @@ -1,72 +0,0 @@ -const CardanocliJs = require("../index.js"); -const os = require("os"); -const path = require("path"); - -const dir = path.join(os.homedir(), "testnet"); -const shelleyPath = path.join( - os.homedir(), - "testnet", - "testnet-shelley-genesis.json" -); - -const cardanocliJs = new CardanocliJs({ - network: "testnet-magic 1097911063", - dir: dir, - shelleyGenesisPath: shelleyPath, -}); - -const createWallet = (account) => { - const payment = cardanocliJs.addressKeyGen(account); - const stake = cardanocliJs.stakeAddressKeyGen(account); - cardanocliJs.stakeAddressBuild(account); - cardanocliJs.addressBuild(account, { - paymentVkey: payment.vkey, - stakeVkey: stake.vkey, - }); - return cardanocliJs.wallet(account); -}; - -const registerWallet = (wallet) => { - let account = wallet.name; - let stakeAddressDeposit = cardanocliJs.queryProtocolParameters().stakeAddressDeposit; - let stakeCert = cardanocliJs.stakeAddressRegistrationCertificate(account); - let paymentAddress = cardanocliJs.wallet(account).paymentAddr; - let balance = cardanocliJs.wallet(account).balance().value.lovelace; - if (balance < stakeAddressDeposit) { - throw new Error("The wallet balance needs to be higher than stakeAddressDeposit"); - } - let tx = { - txIn: cardanocliJs.queryUtxo(paymentAddress), - txOut: [ - { address: paymentAddress, value: { lovelace: balance - stakeAddressDeposit } }, - ], - certs: [{ cert: stakeCert }], - witnessCount: 2, - }; - let txBodyRaw = cardanocliJs.transactionBuildRaw(tx); - let fee = cardanocliJs.transactionCalculateMinFee({ - ...tx, - txBody: txBodyRaw, - }); - tx.txOut[0].value.lovelace -= fee; - let txBody = cardanocliJs.transactionBuildRaw({ ...tx, fee }); - let txSigned = cardanocliJs.transactionSign({ - txBody, - signingKeys: [ - cardanocliJs.wallet(account).payment.skey, - cardanocliJs.wallet(account).stake.skey, - ], - }); - - return txSigned; -}; - -let wallet = createWallet("Test"); - -console.log(wallet); - -let tx = registerWallet(wallet); - -let txHash = cardanocliJs.transactionSubmit(tx); - -console.log("TxHash: " + txHash); diff --git a/examples/simpleTransaction.js b/examples/simpleTransaction.js index 6704c71..7329c8b 100644 --- a/examples/simpleTransaction.js +++ b/examples/simpleTransaction.js @@ -1,68 +1,33 @@ -const CardanocliJs = require("../index.js"); -const os = require("os"); -const path = require("path"); +import CardanoCliJs from "../index"; +import { CardanoCliJsOptions } from "../lib/cardanoclijs"; -const dir = path.join(os.homedir(), "testnet"); -const shelleyPath = path.join( - os.homedir(), - "testnet", - "testnet-shelley-genesis.json" -); - -const cardanocliJs = new CardanocliJs({ - network: "testnet-magic 1097911063", - dir: dir, - shelleyGenesisPath: shelleyPath, - socketPath: path.join(os.homedir(), "testnet", "db", "socket"), -}); +const options = new CardanoCliJsOptions({ shelleyGenesisPath: `${__dirname}/../tests/assets/shelley-genesis.json` }); +const cli = new CardanoCliJs(options); //funded wallet -const sender = cardanocliJs.wallet("Ales"); -console.log( - "Balance of Sender wallet: " + - cardanocliJs.toAda(sender.balance().value.lovelace) + - " ADA" -); +const sender = "addr_test1qzjlc05tyyw264wy7m4u7np5yqdwglks0xhu6765cl4qex9r9kvav4hmznru9px9n7cpa2hmmv4593eegve3t834xppqwdsllm3km"; +const utxos = cli.query.utxo(addr); +const utxoIds = Object.keys(utxos); +const balance = utxos.forEach((utxo) => { + Object.keys(utxo.value).forEach((asset) => { + if (!value[asset]) value[asset] = 0; + value[asset] += utxo.value[asset]; + }); +}); //receiver address const receiver = "addr_test1qzjlc05tyyw264wy7m4u7np5yqdwglks0xhu6765cl4qex9r9kvav4hmznru9px9n7cpa2hmmv4593eegve3t834xppqwskp4t"; -// create raw transaction -let txInfo = { - txIn: cardanocliJs.queryUtxo(sender.paymentAddr), - txOut: [ - { - address: sender.paymentAddr, - value: { - lovelace: sender.balance().value.lovelace - cardanocliJs.toLovelace(5), - }, - }, //value going back to sender - { address: receiver, value: { lovelace: cardanocliJs.toLovelace(5) } }, //value going to receiver - ], - metadata: { 1: { cardanocliJs: "First Metadata from cardanocli-js" } }, -}; -let raw = cardanocliJs.transactionBuildRaw(txInfo); - -//calculate fee -let fee = cardanocliJs.transactionCalculateMinFee({ - ...txInfo, - txBody: raw, - witnessCount: 1, -}); - -//pay the fee by subtracting it from the sender utxo -txInfo.txOut[0].value.lovelace -= fee; +const amountToSend = 5000000; // 5 ADA +const rawTxFile = cli.transaction.build([ + ...utxoIds.map((u) => ({ name: "tx-in", value: u })), + { name: 'tx-out', value: `${receiver} ${amountToSend}`}, + { name: "change-address", value: sender }, +]); -//create final transaction -let tx = cardanocliJs.transactionBuildRaw({ ...txInfo, fee }); - -//sign the transaction -let txSigned = cardanocliJs.transactionSign({ - txBody: tx, - signingKeys: [sender.payment.skey], -}); +const signedTxFile = cli.transaction.sign(rawTxFile, [ + `path/to/payment.skey` +]); -//broadcast transaction -let txHash = cardanocliJs.transactionSubmit(txSigned); -console.log("TxHash: " + txHash); +cli.transaction.submit(signedTxFile); diff --git a/helper.js b/helper.js deleted file mode 100644 index bcdb2de..0000000 --- a/helper.js +++ /dev/null @@ -1,254 +0,0 @@ -const fs = require("fs"); - -exports.ownerToString = (ownerList) => { - let result = ""; - ownerList.forEach( - (owner) => (result += `--pool-owner-stake-verification-key-file ${owner} `) - ); - return result; -}; - -exports.relayToString = (relayList) => { - let result = ""; - relayList.forEach((relay) => { - if (!((relay.host || relay.ip) && relay.port) && !relay.multiHost) - throw new Error("Relay is missing arguments"); - if (relay.host) { - result += `--single-host-pool-relay ${relay.host} --pool-relay-port ${relay.port} `; - } else if (relay.ip) { - result += `--pool-relay-ipv4 ${relay.ip} --pool-relay-port ${relay.port} `; - } else if (relay.multiHost) { - result += `--multi-host-pool-relay ${relay.multiHost} `; - } - }); - return result; -}; - -exports.certToString = (dir, certList) => { - let result = ""; - certList.forEach( - (cert) => - (result += `--certificate ${cert.cert} ${ - cert.script - ? `--certificate-script-file ${this.jsonToPath(dir, cert.script)} ` - : "" - } ${ - cert.datum - ? `--certificate-script-datum-value '${JSON.stringify(cert.datum)}' ` - : "" - } ${ - cert.redeemer - ? `--certificate-script-redeemer-value '${JSON.stringify( - cert.redeemer - )}' ` - : "" - } ${ - cert.executionUnits - ? `--certificate-execution-units "(${ - cert.executionUnits[0] + "," + cert.executionUnits[1] - })" ` - : "" - }`) - ); - return result; -}; - -exports.withdrawalToString = (dir, withdrawalList) => { - let result = ""; - withdrawalList.forEach( - (withdrawal) => - (result += `--withdrawal ${withdrawal.stakingAddress}+${ - withdrawal.reward - } ${ - withdrawal.script - ? `--withdrawal-script-file ${this.jsonToPath( - dir, - withdrawal.script - )} ` - : "" - } ${ - withdrawal.datum - ? `--withdrawal-script-datum-value '${JSON.stringify( - withdrawal.datum - )}' ` - : "" - } ${ - withdrawal.redeemer - ? `--withdrawal-script-redeemer-value '${JSON.stringify( - withdrawal.redeemer - )}' ` - : "" - } ${ - withdrawal.executionUnits - ? `--withdrawal-execution-units "(${ - withdrawal.executionUnits[0] + "," + withdrawal.executionUnits[1] - })" ` - : "" - }`) - ); - return result; -}; - -exports.auxScriptToString = (dir, scriptList) => { - return scriptList - .map((script) => `--auxiliary-script-file ${this.jsonToPath(dir, script)}`) - .join(" "); -}; - -exports.jsonToPath = (dir, json, type = "script") => { - let scriptUID = Math.random().toString(36).substr(2, 9); - fs.writeFileSync( - `${dir}/tmp/${type}_${scriptUID}.json`, - JSON.stringify(json) - ); - return `${dir}/tmp/${type}_${scriptUID}.json`; -}; - -exports.txInToString = (dir, txInList, isCollateral) => { - let result = ""; - txInList.forEach( - (txIn) => - (result += `--tx-in${isCollateral ? "-collateral" : ""} ${txIn.txHash}#${ - txIn.txId - } ${ - txIn.script - ? `--tx-in-script-file ${this.jsonToPath(dir, txIn.script)} ` - : "" - } ${ - txIn.datum - ? `--tx-in-datum-value '${JSON.stringify(txIn.datum)}' ` - : "" - } ${ - txIn.redeemer - ? `--tx-in-redeemer-value '${JSON.stringify(txIn.redeemer)}' ` - : "" - } ${ - txIn.executionUnits - ? `--tx-in-execution-units "(${ - txIn.executionUnits[0] + "," + txIn.executionUnits[1] - })" ` - : "" - }`) - ); - return result; -}; - -exports.txOutToString = (txOutList) => { - let result = ""; - let assetOutStr; - txOutList.forEach((txOut) => { - assetOutStr = ""; - result += `--tx-out ${txOut.address}+${txOut.value.lovelace}`; - Object.keys(txOut.value).forEach((asset) => { - if (asset == "lovelace") return; - assetOutStr += `+${txOut.value[asset]} ${asset}`; - }); - if (assetOutStr) - result += `+"${assetOutStr.slice(1)}"`; - txOut.datumHash && (result += ` --tx-out-datum-hash ${txOut.datumHash}`); - result += " "; - }); - return result; -}; - -exports.signingKeysToString = (signingKeys) => { - let result = ""; - signingKeys.forEach( - (signingKey) => (result += `--signing-key-file ${signingKey} `) - ); - return result; -}; - -exports.witnessFilesToString = (witnessFiles) => { - let result = ""; - witnessFiles.forEach( - (witnessFile) => (result += `--witness-file ${witnessFile} `) - ); - return result; -}; - -exports.fileException = (callback) => { - try { - callback(); - } catch {} -}; - -exports.setKeys = (obj, path, value) => { - var pList = path.split("."); - var len = pList.length; - for (var i = 0; i < len - 1; i++) { - var elem = pList[i]; - if (!obj[elem]) obj[elem] = {}; - obj = obj[elem]; - } - - obj[pList[len - 1]] = value; -}; - -exports.fileExists = (files) => { - for (file of files) { - let exists; - this.fileException(() => { - exists = fs.readFileSync(file); - }); - if (exists) - throw new Error( - `File ${file} already exists. Remove it manually if you want to create a new file.` - ); - } -}; - -exports.mintToString = (dir, minting) => { - let result = `--mint="`; - minting.forEach((mint, index, arr) => { - if ( - !( - (mint.quantity || mint.asset) && - (mint.action == "mint" || mint.action == "burn") - ) - ) - throw new Error("action, asset and quantity property required"); - if (Object.is(arr.length - 1, index)) { - result += `${mint.action == "mint" ? "" : "-"}${mint.quantity} ${ - mint.asset - }`; - } else { - result += `${mint.action == "mint" ? "" : "-"}${mint.quantity} ${ - mint.asset - }+`; - } - }); - result = result.trim(); - result += `" `; - const usedScripts = []; - result += minting - .map((mint) => { - const script = this.jsonToPath(dir, mint.script); - if (usedScripts.includes(script)) return ""; - usedScripts.push(script); - return `--mint-script-file ${script} ${ - mint.redeemer - ? `--mint-redeemer-value '${JSON.stringify(mint.redeemer)}' ` - : "" - } ${ - mint.executionUnits - ? `--mint-execution-units "(${ - mint.executionUnits[0] + "," + mint.executionUnits[1] - })" ` - : "" - }`; - }) - .join(" "); - return result; -}; - -exports.multiAssetToString = (options) => { - let result = `"${options.address} + `; - result += `${options.value.lovelace}`; - Object.keys(options.value).forEach((asset) => { - if (asset == "lovelace") return; - result += `+${options.value[asset]} ${asset}`; - }); - result += `"`; - return result; -}; diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 42fe762..0000000 --- a/index.d.ts +++ /dev/null @@ -1,231 +0,0 @@ -// Typescript type definitions for cardanocli-js - -export class CardanocliJs { - constructor(options: CardanocliJs.ConstructorOptions); - - queryProtocolParameters(): any; - queryTip(): CardanocliJs.QueryTip; - queryStakeAddressInfo(address: string): CardanocliJs.StakeAddressInfo[]; - queryUtxo(address: string): CardanocliJs.Utxo[]; - - addressKeyGen(account: string): CardanocliJs.Account; - - stakeAddressKeyGen(account: string): CardanocliJs.Account; - stakeAddressBuild(account: string): string; - - addressBuild(account: string, options: CardanocliJs.AddressBuildOptions): string; - addressKeyHash(account: string): string; - addressInfo(address: string): CardanocliJs.AddressInfo; - addressBuildScript(script: any): string; - - wallet(account: string): CardanocliJs.Wallet; - - pool(poolName: string): CardanocliJs.Pool; - - stakeAddressRegistrationCertificate(account: string): string; - stakeAddressDeregistrationCertificate(accout: string): string; - stakeAddressDelegationCertificate(account: string, poolId: string): string; - stakeAddressKeyHash(account: string): string; - - nodeKeyGenKES(poolName: string): CardanocliJs.Account; - nodeKeyGen(poolName: string): CardanocliJs.Account; - nodeIssueOpCert(poolName: string, kesPeriod: number): string; - nodeKeyGenVRF(poolName: string): CardanocliJs.Account; - nodeNewCounter(poolName: string, counter: string): string; - - stakePoolId(poolName: string): string; - stakePoolMetadataHash(metadata: string): string; - stakePoolRegistrationCertificate(poolName: string, options: CardanocliJs.StakePoolRegistrationOptions): string; - stakePoolDeregistrationCertificate(poolName: string, epoch: number): string; - - transactionBuildRaw(options: CardanocliJs.Transaction): string; - transactionBuild(options: CardanocliJs.Transaction): string; - transactionCalculateMinFee(options: CardanocliJs.CalculateMinFeeOptions): number; - transactionPolicyid(script: any): string; - transactionHashScriptData(script: any): string; - transactionSign(options: CardanocliJs.TransationSignOptions): string; - transactionWitness(options:CardanocliJs.TransactionWitnessOptions): string; - transactionAssemble(options: CardanocliJs.TransactionAssembleOptions): string; - transactionCalculateMinValue(value: number): number; - transactionCalculateMinRequiredUtxo(address: string, value: any): number; - transactionSubmit(tx: string): string; - transactionTxid(options: CardanocliJs.TransactionViewOptions): string; - transactionView(options: CardanocliJs.TransactionViewOptions): string; - - KESPeriod(): number; - getDownloadUrl(filePath: string): string; - - toLovelace(ada: number): number; - toAda(lovelace: number): number; -} - -declare namespace CardanocliJs { - - export interface ConstructorOptions { - shelleyGenesisPath: string, - socketPath?: string, - cliPath?: string, - dir?: string, - era?: string, - network?: string, - httpProvider?: string, - } - - export interface AddressBuildOptions { - paymentVkey?: string, - stakeVkey?: string, - paymentScript?: string, - stakeScript?: string, - } - - export interface StakePoolRegistrationOptions { - pledge: number, - margin: number, - cost: number, - url: string, - metaHash: string, - rewardAccount: string, - owners: string[], - relays: any[] - } - - export interface CalculateMinFeeOptions { - txBody: string, - txIn: TxIn[], - txOut: TxOut[], - witnessCount: number, - } - - export interface TransationSignOptions { - signingKeys: string[], - txBody: string, - } - - export interface TransactionWitnessOptions { - txBody: string, - signingKey: string, - } - - export interface TransactionAssembleOptions { - txBody: string, - witnessFiles: string[], - } - - export interface TransactionViewOptions { - txBody?: string, - txFile?: string, - } - - export interface Transaction { - txIn: TxIn[], - txOut: TxOut[], - txInCollateral?: TxInCollateral[], - withdrawals?: Withdrawal[], - certs?: Certificate[], - fee?: number, - mint?: Mint[], - auxScript?: any, - metadata?: any, - invalidBefore?: number, - invalidAfter?: number, - scriptInvalid?: boolean, - } - - export interface QueryTip { - epoch: number, - hash: string, - slot: number, - block: number, - era: string, - syncProgress: string, - } - - export interface Wallet { - name: string, - paymentAddr: string, - stakingAddr: string, - balance: () => { - utxo: Utxo[], - value: any - }, - reward: () => StakeAddressInfo | string, - } - - export interface Pool { - name: string, - id: string, - } - - export interface StakeAddressInfo { - address: string, - rewardAccountBalance: number, - delegation: string, - } - - export interface AddressInfo { - address: string, - era: string, - encoding: string, - type: string, - base16: string, - } - - export interface TxIn { - txHash: string, - txId: string, - script?: any, - datum?: any, - redeemer?: any, - executionUnits: any, - } - - export interface TxOut { - address: string, - value: any, - datumHash: string, - } - - export interface TxInCollateral { - txHash: string, - txId: string, - } - - export interface Withdrawal { - stakingAddress: string, - reward: number, - script: any, - datum: any, - redeemer: any, - executionUnits: any, - } - - export interface Certificate { - cert: string, - script: any, - datum: any, - redeemer: any, - executionUnits: any, - } - - export interface Mint { - action: string, - quantity: string, - asset: string, - script: any, - datum: any, - redeemer: any, - executionUnits: any, - } - - export interface Utxo { - txHash: string, - txId: number, - value: any - } - - export interface Account { - vkey: string, - skey: string, - counter?: string, - } -} diff --git a/index.js b/index.js deleted file mode 100644 index e2f0968..0000000 --- a/index.js +++ /dev/null @@ -1,1369 +0,0 @@ -const execSync = - typeof window !== "undefined" || require("child_process").execSync; -const fs = typeof window !== "undefined" || require("fs"); -const { - ownerToString, - relayToString, - certToString, - txInToString, - txOutToString, - signingKeysToString, - witnessFilesToString, - fileException, - setKeys, - fileExists, - mintToString, - jsonToPath, - auxScriptToString, - withdrawalToString, - multiAssetToString, -} = require("./helper"); -const fetch = - typeof window !== "undefined" ? window.fetch : require("sync-fetch"); - -/** - * @typedef lovelace - * @property {number} - */ - -/** - * @typedef asset - * @property {string} - */ - -/** - * @typedef quantity - * @property {string} - */ - -/** - * @typedef path - * @property {string} - */ - -/** - * @typedef paymentAddr - * @property {string} - */ - -/** - * @typedef stakeAddr - * @property {string} - */ - -/** - * @typedef {Object} TxIn - * @property {string} txHash - * @property {string} txId - * @property {object=} script - * @property {object=} datum - * @property {object=} redeemer - * @property {[number, number]} executionUnits - */ -/** - * @typedef {Object} TxOut - * @property {string} address - * @property {object} value - * @property {string} datumHash - */ -/** - * @typedef {Object} TxInCollateral - * @property {string} txHash - * @property {string} txId - */ -/** - * @typedef {Object} Mint - * @property {string} action - * @property {string} quantity - * @property {string} asset - * @property {object} script - * @property {object=} datum - * @property {object=} redeemer - * @property {[number, number]} executionUnits - */ -/** - * @typedef {Object} Certificate - * @property {path} cert - * @property {object=} script - * @property {object=} datum - * @property {object=} redeemer - * @property {[number, number]} executionUnits - */ -/** - * @typedef {Object} Withdrawal - * @property {string} stakingAddress - * @property {lovelace} reward - * @property {object=} script - * @property {object=} datum - * @property {object=} redeemer - * @property {[number, number]} executionUnits - */ -/** - * @typedef {Object.} Value - */ - -class CardanocliJs { - /** - * - * @param {Object} options - * @param {path=} options.shelleyGenesisPath - * @param {path=} options.socketPath - Default: Env Variable - * @param {path=} options.cliPath - Default: Env Variable - * @param {path=} options.dir - Default: Working Dir - * @param {string=} options.era - * @param {string=} options.network - Default: mainnet - * @param {string=} options.httpProvider - Optional - Useful when using cli at different location than node or in browser - */ - - constructor(options) { - this.network = "mainnet"; - this.era = ""; - this.dir = "."; - this.cliPath = "cardano-cli"; - - if (options) { - options.shelleyGenesisPath && - (this.shelleyGenesis = JSON.parse( - execSync(`cat ${options.shelleyGenesisPath}`).toString() - )); - - options.socketPath && - (process.env["CARDANO_NODE_SOCKET_PATH"] = options.socketPath); - options.era && (this.era = "--" + options.era + "-era"); - options.network && (this.network = options.network); - options.dir && (this.dir = options.dir); - options.cliPath && (this.cliPath = options.cliPath); - options.httpProvider && (this.httpProvider = options.httpProvider); - if (!this.httpProvider && typeof window !== "undefined") - throw new Error("httpProvider required"); - if (this.httpProvider) { - if (typeof window === "undefined") { - this.shelleyGenesis = fetch( - `${this.httpProvider}/shelleyGenesis` - ).json(); - } else { - (() => async () => { - this.shelleyGenesis = await fetch( - `${this.httpProvider}/shelleyGenesis` - ).then((res) => res.json()); - })(); - } - } - } - - typeof window !== "undefined" || execSync(`mkdir -p ${this.dir}/tmp`); - } - - /** - * @returns {object} - */ - queryProtocolParameters() { - if (this.httpProvider) { - let response = fetch(`${this.httpProvider}/queryProtocolParameters`); - if (typeof window === "undefined") { - response = response.json(); - fs.writeFileSync( - `${this.dir}/tmp/protocolParams.json`, - JSON.stringify(response) - ); - this.protocolParametersPath = `${this.dir}/tmp/protocolParams.json`; - return response; - } - return response.then((res) => res.json()); - } - execSync(`${this.cliPath} query protocol-parameters \ - --${this.network} \ - --cardano-mode \ - --out-file ${this.dir}/tmp/protocolParams.json - `); - this.protocolParametersPath = `${this.dir}/tmp/protocolParams.json`; - return JSON.parse(execSync(`cat ${this.dir}/tmp/protocolParams.json`)); - } - - /** - * @returns {object} - */ - queryTip() { - if (this.httpProvider) { - let response = fetch(`${this.httpProvider}/queryTip`); - return typeof window !== "undefined" - ? response.then((res) => res.json()) - : response.json(); - } - return JSON.parse( - execSync(`${this.cliPath} query tip \ - --${this.network} \ - --cardano-mode - `).toString() - ); - } - - /** - * @param {stakeAddr} address - * @returns {object} - */ - queryStakeAddressInfo(address) { - if (this.httpProvider) { - let response = fetch( - `${this.httpProvider}/queryStakeAddressInfo/${address}` - ); - return typeof window !== "undefined" - ? response.then((res) => res.json()) - : response.json(); - } - return JSON.parse( - execSync(`${this.cliPath} query stake-address-info \ - --${this.network} \ - --address ${address} - `).toString() - ); - } - - /** - * @param {paymentAddr} address - * @returns {object} - */ - queryUtxo(address) { - if (this.httpProvider) { - let response = fetch(`${this.httpProvider}/queryUtxo/${address}`); - return typeof window !== "undefined" - ? response.then((res) => res.json()) - : response.json(); - } - const utxosRaw = execSync(`${this.cliPath} query utxo \ - --${this.network} \ - --address ${address} \ - --cardano-mode - `).toString(); - - const utxos = utxosRaw.split("\n"); - utxos.splice(0, 1); - utxos.splice(0, 1); - utxos.splice(utxos.length - 1, 1); - const result = utxos.map((raw, index) => { - const utxo = raw.replace(/\s+/g, " ").split(" "); - const txHash = utxo[0]; - const txId = parseInt(utxo[1]); - const valueList = utxo.slice(2, utxo.length).join(" ").split("+"); - const value = {}; - let datumHash; - valueList.forEach((v) => { - if (v.includes("TxOutDatumHash") || v.includes("TxOutDatumNone") ) { - if (!v.includes("None")) - datumHash = JSON.parse(v.trim().split(" ")[2]); - return; - } - let [quantity, asset] = v.trim().split(" "); - quantity = parseInt(quantity); - value[asset] = quantity; - }); - const result = { txHash, txId, value }; - if (datumHash) result.datumHash = datumHash; - - return result; - }); - - return result; - } - - /** - * - * @param {string} account - Name of account - */ - addressKeyGen(account) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/${account}/addressKeyGen`); - return response.then((res) => res.json()); - } - let vkey = `${this.dir}/priv/wallet/${account}/${account}.payment.vkey`; - let skey = `${this.dir}/priv/wallet/${account}/${account}.payment.skey`; - fileExists([vkey, skey]); - execSync(`mkdir -p ${this.dir}/priv/wallet/${account}`); - execSync(`${this.cliPath} address key-gen \ - --verification-key-file ${vkey} \ - --signing-key-file ${skey} - `); - return { - vkey, - skey, - }; - } - - /** - * - * @param {string} account - Name of account - */ - stakeAddressKeyGen(account) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch( - `${this.httpProvider}/${account}/stakeAddressKeyGen` - ); - return response.then((res) => res.json()); - } - let vkey = `${this.dir}/priv/wallet/${account}/${account}.stake.vkey`; - let skey = `${this.dir}/priv/wallet/${account}/${account}.stake.skey`; - fileExists([vkey, skey]); - execSync(`mkdir -p ${this.dir}/priv/wallet/${account}`); - execSync(`${this.cliPath} stake-address key-gen \ - --verification-key-file ${vkey} \ - --signing-key-file ${skey} - `); - return { - vkey, - skey, - }; - } - - /** - * - * @param {string} account - Name of account - * @returns {path} - */ - stakeAddressBuild(account) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/${account}/stakeAddressBuild`); - return response.then((res) => res.text()); - } - execSync(`${this.cliPath} stake-address build \ - --staking-verification-key-file ${this.dir}/priv/wallet/${account}/${account}.stake.vkey \ - --out-file ${this.dir}/priv/wallet/${account}/${account}.stake.addr \ - --${this.network} - `); - return `${this.dir}/priv/wallet/${account}/${account}.stake.addr`; - } - - /** - * - * @param {string} account - Name of account - * @param {Object} options - * @param {path} [options.paymentVkey] - * @param {path} [options.stakeVkey] - * @param {object} [options.paymentScript] - * @param {object} [options.stakeScript] - */ - addressBuild(account, options) { - const paymentVkey = options.paymentVkey - ? `--payment-verification-key-file ${options.paymentVkey}` - : ""; - const stakeVkey = options.stakeVkey - ? `--staking-verification-key-file ${options.stakeVkey}` - : ""; - const paymentScript = options.paymentScript - ? `--payment-script-file ${jsonToPath(this.dir, options.paymentScript)}` - : ""; - const stakeScript = options.stakeScript - ? `--stake-script-file ${jsonToPath(this.dir, options.stakeScript)}` - : ""; - - execSync(`${this.cliPath} address build \ - ${paymentVkey} \ - ${stakeVkey} \ - ${paymentScript} \ - ${stakeScript} \ - --out-file ${this.dir}/priv/wallet/${account}/${account}.payment.addr \ - --${this.network} - `); - return `${this.dir}/priv/wallet/${account}/${account}.payment.addr`; - } - - /** - * - * @param {string} account - Name of account - */ - addressKeyHash(account) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/${account}/addressKeyHash`); - return response.then((res) => res.text()); - } - return execSync(`${this.cliPath} address key-hash \ - --payment-verification-key-file ${this.dir}/priv/wallet/${account}/${account}.payment.vkey \ - `) - .toString() - .trim(); - } - - /** - * - * @param {paymentAddr} address - * @returns {object} - */ - addressInfo(address) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/addressInfo/${address}`); - return response.then((res) => res.json()); - } - return JSON.parse( - execSync(`${this.cliPath} address info \ - --address ${address} \ - `) - .toString() - .replace(/\s+/g, " ") - ); - } - - /** - * - * @param {object} script - * @returns {paymentAddr} - */ - addressBuildScript(script) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/addressBuildScript`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(script), - }); - return response.then((res) => res.text()); - } - let UID = Math.random().toString(36).substr(2, 9); - fs.writeFileSync( - `${this.dir}/tmp/script_${UID}.json`, - JSON.stringify(script) - ); - let scriptAddr = execSync( - `${this.cliPath} address build-script --script-file ${this.dir}/tmp/script_${UID}.json --${this.network}` - ) - .toString() - .replace(/\s+/g, " "); - return scriptAddr; - } - - /** - * - * @param {string} account - Name of the account - */ - wallet(account) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}${account}/wallet`); - return response.then((res) => res.json()); - } - let paymentAddr = "No payment keys generated"; - let stakingAddr = "No staking keys generated"; - - fileException(() => { - paymentAddr = fs - .readFileSync( - `${this.dir}/priv/wallet/${account}/${account}.payment.addr` - ) - .toString(); - }); - fileException(() => { - stakingAddr = fs - .readFileSync( - `${this.dir}/priv/wallet/${account}/${account}.stake.addr` - ) - .toString(); - }); - - let files = fs.readdirSync(`${this.dir}/priv/wallet/${account}`); - let keysPath = {}; - files.forEach((file) => { - let name = file.split(".")[1] + "." + file.split(".")[2]; - setKeys(keysPath, name, `${this.dir}/priv/wallet/${account}/${file}`); - }); - - const balance = () => { - const utxos = this.queryUtxo(paymentAddr); - const value = {}; - utxos.forEach((utxo) => { - Object.keys(utxo.value).forEach((asset) => { - if (!value[asset]) value[asset] = 0; - value[asset] += utxo.value[asset]; - }); - }); - - return { utxo: utxos, value }; - }; - let reward = () => { - let r; - try { - r = this.queryStakeAddressInfo(stakingAddr); - r = r.find( - (delegation) => delegation.address == stakingAddr - ).rewardAccountBalance; - } catch { - r = "Staking key is not registered"; - } - return r; - }; - - return { - name: account, - paymentAddr, - stakingAddr, - balance, - reward, - ...keysPath, - }; - } - - /** - * - * @param {string} poolName - Name of the pool - */ - pool(poolName) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}${poolName}/pool`); - return response.then((res) => res.json()); - } - let id; - fileException(() => { - fs.readFileSync( - `${this.dir}/priv/pool/${poolName}/${poolName}.node.vkey` - ); - id = this.stakePoolId(poolName); - }); - let files = fs.readdirSync(`${this.dir}/priv/pool/${poolName}`); - let keysPath = {}; - files.forEach((file) => { - let name = file.split(".")[1] + "." + file.split(".")[2]; - setKeys(keysPath, name, `${this.dir}/priv/pool/${poolName}/${file}`); - }); - return { - name: poolName, - id, - ...keysPath, - }; - } - - /** - * - * @param {string} account - Name of the account - * @returns {path} - */ - stakeAddressRegistrationCertificate(account) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch( - `${this.httpProvider}${account}/stakeAddressRegistrationCertificate` - ); - return response.then((res) => res.text()); - } - execSync(`${this.cliPath} stake-address registration-certificate \ - --staking-verification-key-file ${this.dir}/priv/wallet/${account}/${account}.stake.vkey \ - --out-file ${this.dir}/priv/wallet/${account}/${account}.stake.cert - `); - return `${this.dir}/priv/wallet/${account}/${account}.stake.cert`; - } - - /** - * - * @param {string} account - Name of the account - * @returns {path} - */ - stakeAddressDeregistrationCertificate(account) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch( - `${this.httpProvider}${account}/stakeAddressDeregistrationCertificate` - ); - return response.then((res) => res.text()); - } - execSync(`${this.cliPath} stake-address deregistration-certificate \ - --staking-verification-key-file ${this.dir}/priv/wallet/${account}/${account}.stake.vkey \ - --out-file ${this.dir}/priv/wallet/${account}/${account}.stake.cert - `); - return `${this.dir}/priv/wallet/${account}/${account}.stake.cert`; - } - - /** - * - * @param {string} account - Name of the account - * @param {string} poolId - Stake pool verification key (Bech32 or hex-encoded) - * @returns {path} - */ - stakeAddressDelegationCertificate(account, poolId) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch( - `${this.httpProvider}${account}/stakeAddressDelegationCertificate?poolId=${poolId}` - ); - return response.then((res) => res.text()); - } - execSync(`${this.cliPath} stake-address delegation-certificate \ - --staking-verification-key-file ${this.dir}/priv/wallet/${account}/${account}.stake.vkey \ - --stake-pool-id ${poolId} \ - --out-file ${this.dir}/priv/wallet/${account}/${account}.deleg.cert - `); - return `${this.dir}/priv/wallet/${account}/${account}.deleg.cert`; - } - - /** - * - * @param {string} account - Name of the account - * @returns {string} - */ - stakeAddressKeyHash(account) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch( - `${this.httpProvider}/${account}/stakeAddressKeyHash` - ); - return response.then((res) => res.text()); - } - return execSync(`${this.cliPath} stake-address key-hash \ - --staking-verification-key-file ${this.dir}/priv/wallet/${account}/${account}.stake.vkey \ - `) - .toString() - .trim(); - } - - /** - * - * @param {string} poolName - Name of the pool - */ - nodeKeyGenKES(poolName) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/${poolName}/nodeKeyGenKES`); - return response.then((res) => res.json()); - } - let vkey = `${this.dir}/priv/pool/${poolName}/${poolName}.kes.vkey`; - let skey = `${this.dir}/priv/pool/${poolName}/${poolName}.kes.skey`; - fileExists([vkey, skey]); - execSync(`mkdir -p ${this.dir}/priv/pool/${poolName}`); - execSync(`${this.cliPath} node key-gen-KES \ - --verification-key-file ${vkey} \ - --signing-key-file ${skey} - `); - return { - vkey, - skey, - }; - } - - /** - * - * @param {string} poolName - Name of the pool - */ - nodeKeyGen(poolName) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/${poolName}/nodeKeyGen`); - return response.then((res) => res.json()); - } - let vkey = `${this.dir}/priv/pool/${poolName}/${poolName}.node.vkey`; - let skey = `${this.dir}/priv/pool/${poolName}/${poolName}.node.skey`; - let counter = `${this.dir}/priv/pool/${poolName}/${poolName}.node.counter`; - fileExists([vkey, skey, counter]); - execSync(`mkdir -p ${this.dir}/priv/pool/${poolName}`); - execSync(`${this.cliPath} node key-gen \ - --cold-verification-key-file ${vkey} \ - --cold-signing-key-file ${skey} \ - --operational-certificate-issue-counter ${counter} - `); - return { - vkey, - skey, - counter, - }; - } - - /** - * - * @param {string} poolName - Name of the pool - * @param {number=} kesPeriod - Optional (Offline mode) - * @returns {path} - */ - nodeIssueOpCert(poolName, kesPeriod) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/${poolName}/nodeIssueOpCert`); - return response.then((res) => res.text()); - } - execSync(`${this.cliPath} node issue-op-cert \ - --kes-verification-key-file ${ - this.dir - }/priv/pool/${poolName}/${poolName}.kes.vkey \ - --cold-signing-key-file ${ - this.dir - }/priv/pool/${poolName}/${poolName}.node.skey \ - --operational-certificate-issue-counter ${ - this.dir - }/priv/pool/${poolName}/${poolName}.node.counter \ - --kes-period ${ - kesPeriod ? kesPeriod : this.KESPeriod() - } \ - --out-file ${ - this.dir - }/priv/pool/${poolName}/${poolName}.node.cert - `); - return `${this.dir}/priv/pool/${poolName}/${poolName}.node.cert`; - } - - /** - * - * @param {string} poolName - Name of the pool - */ - nodeKeyGenVRF(poolName) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/${poolName}/nodeKeyGenVRF`); - return response.then((res) => res.json()); - } - let vkey = `${this.dir}/priv/pool/${poolName}/${poolName}.vrf.vkey`; - let skey = `${this.dir}/priv/pool/${poolName}/${poolName}.vrf.skey`; - fileExists([vkey, skey]); - execSync(`mkdir -p ${this.dir}/priv/pool/${poolName}`); - execSync(`${this.cliPath} node key-gen-VRF \ - --verification-key-file ${vkey} \ - --signing-key-file ${skey} - `); - return { - vkey, - skey, - }; - } - - nodeNewCounter(poolName, counter) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch( - `${this.httpProvider}/${poolName}/nodeNewCounter?counter=${counter}` - ); - return response.then((res) => res.text()); - } - - execSync(`mkdir -p ${this.dir}/priv/pool/${poolName}`); - execSync(`${this.cliPath} node new-counter \ - --cold-verification-key-file ${this.dir}/priv/pool/${poolName}/${poolName}.node.vkey \ - --counter-value ${counter} \ - --operational-certificate-issue-counter-file ${this.dir}/priv/pool/${poolName}/${poolName}.node.counter - `); - return `${this.dir}/priv/pool/${poolName}/${poolName}.node.counter`; - } - - /** - * - * @param {string} poolName - Name of the pool - * @returns {string} - */ - stakePoolId(poolName) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/${poolName}/stakePoolId`); - return response.then((res) => res.text()); - } - return execSync( - `${this.cliPath} stake-pool id --cold-verification-key-file ${this.dir}/priv/pool/${poolName}/${poolName}.node.vkey` - ) - .toString() - .replace(/\s+/g, " "); - } - - /** - * - * @param {string} metadata - Raw File - * @returns {string} - */ - stakePoolMetadataHash(metadata) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/stakePoolMetadataHash`, { - method: "POST", - body: metadata, - }); - return response.then((res) => res.text()); - } - fs.writeFileSync(`${this.dir}/tmp/poolmeta.json`, metadata); - let metaHash = execSync( - `${this.cliPath} stake-pool metadata-hash --pool-metadata-file ${this.dir}/tmp/poolmeta.json` - ) - .toString() - .replace(/\s+/g, " "); - execSync(`rm ${this.dir}/tmp/poolmeta.json`); - return metaHash; - } - - /** - * @param {string} poolName - Name of the pool - * @param {Object} options - * @param {lovelace} options.pledge - * @param {number} options.margin - * @param {lovelace} options.cost - * @param {string} options.url - * @param {string} options.metaHash - * @param {path} options.rewardAccount - * @param {Array} options.owners - * @param {Array} options.relays - * @returns {path} - */ - stakePoolRegistrationCertificate(poolName, options) { - if ( - !( - options && - options.pledge && - options.margin && - options.cost && - options.url && - options.metaHash && - options.rewardAccount && - options.owners && - options.relays - ) - ) - throw new Error("All options are required"); - - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch( - `${this.httpProvider}/${poolName}/stakePoolRegistrationCertificate`, - { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(options), - } - ); - return response.then((res) => res.text()); - } - - let owners = ownerToString(options.owners); - let relays = relayToString(options.relays); - execSync(`${this.cliPath} stake-pool registration-certificate \ - --cold-verification-key-file ${this.dir}/priv/pool/${poolName}/${poolName}.node.vkey \ - --vrf-verification-key-file ${this.dir}/priv/pool/${poolName}/${poolName}.vrf.vkey \ - --pool-pledge ${options.pledge} \ - --pool-cost ${options.cost} \ - --pool-margin ${options.margin} \ - --pool-reward-account-verification-key-file ${options.rewardAccount} \ - ${owners} \ - ${relays} \ - --${this.network} \ - --metadata-url ${options.url} \ - --metadata-hash ${options.metaHash} \ - --out-file ${this.dir}/priv/pool/${poolName}/${poolName}.pool.cert - `); - return `${this.dir}/priv/pool/${poolName}/${poolName}.pool.cert`; - } - - /** - * - * @param {string} poolName - Name of the pool - * @param {number} epoch - Retirement Epoch - * @returns {path} - */ - stakePoolDeregistrationCertificate(poolName, epoch) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch( - `${this.httpProvider}/${poolName}/stakePoolDeregistrationCertificate` - ); - return response.then((res) => res.text()); - } - execSync(`${this.cliPath} stake-pool deregistration-certificate \ - --cold-verification-key-file ${this.dir}/priv/pool/${poolName}/${poolName}.node.vkey \ - --epoch ${epoch} \ - --out-file ${this.dir}/priv/pool/${poolName}/${poolName}.pool.cert - `); - return `${this.dir}/priv/pool/${poolName}/${poolName}.pool.cert`; - } - - /** - * - * @param {Object} options - * @param {Array} options.txIn - * @param {Array} options.txOut - * @param {Array=} options.txInCollateral - * @param {Array=} options.withdrawals - * @param {Array=} options.certs - * @param {lovelace=} options.fee - * @param {Array=} options.mint - * @param {Array=} options.auxScript - * @param {object=} options.metadata - * @param {number=} options.invalidBefore - Default: 0 - * @param {number=} options.invalidAfter - Default: current+10000 - * @param {boolean} options.scriptInvalid - Default: false - * @returns {path} - */ - transactionBuildRaw(options) { - if (!(options && options.txIn && options.txOut)) - throw new Error("TxIn and TxOut required"); - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/transactionBuildRaw`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(options), - }); - return response.then((res) => res.text()); - } - let UID = Math.random().toString(36).substr(2, 9); - const txInString = txInToString(this.dir, options.txIn); - const txOutString = txOutToString(options.txOut); - const txInCollateralString = options.txInCollateral - ? txInToString(this.dir, options.txInCollateral, true) - : ""; - const mintString = options.mint ? mintToString(this.dir, options.mint) : ""; - const withdrawals = options.withdrawals - ? withdrawalToString(this.dir, options.withdrawals) - : ""; - const certs = options.certs ? certToString(this.dir, options.certs) : ""; - const metadata = options.metadata - ? "--metadata-json-file " + - jsonToPath(this.dir, options.metadata, "metadata") - : ""; - const auxScript = options.auxScript - ? auxScriptToString(this.dir, options.auxScript) - : ""; - - if (!this.protocolParametersPath) this.queryProtocolParameters(); - - const scriptInvalid = options.scriptInvalid ? "--script-invalid" : ""; - execSync(`${this.cliPath} transaction build-raw \ - ${this.era ? this.era : "--alonzo-era"} \ - ${txInString} \ - ${txOutString} \ - ${txInCollateralString} \ - ${certs} \ - ${withdrawals} \ - ${mintString} \ - ${auxScript} \ - ${metadata} \ - ${scriptInvalid} \ - --invalid-hereafter ${ - options.invalidAfter - ? options.invalidAfter - : this.queryTip().slot + 10000 - } \ - --invalid-before ${ - options.invalidBefore ? options.invalidBefore : 0 - } \ - --fee ${options.fee ? options.fee : 0} \ - --out-file ${this.dir}/tmp/tx_${UID}.raw \ - --protocol-params-file ${this.protocolParametersPath}`); - - return `${this.dir}/tmp/tx_${UID}.raw`; - } - - /** - * - * @param {Object} options - * @param {Array} options.txIn - * @param {Array} options.txOut - * @param {Array=} options.txInCollateral - * @param {string=} options.changeAddress - * @param {Array=} options.withdrawals - * @param {Array=} options.certs - * @param {lovelace=} options.fee - * @param {Array=} options.mint - * @param {Array=} options.auxScript - * @param {object=} options.metadata - * @param {number=} options.invalidBefore - Default: 0 - * @param {number=} options.invalidAfter - Default: current+10000 - * @param {boolean} options.scriptInvalid - Default: false - * @param {number=} options.witnessOverride - * @returns {path} - */ - transactionBuild(options) { - if (!(options && options.txIn && options.txOut)) - throw new Error("TxIn and TxOut required"); - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/transactionBuildRaw`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(options), - }); - return response.then((res) => res.text()); - } - let UID = Math.random().toString(36).substr(2, 9); - const txInString = txInToString(this.dir, options.txIn); - const txOutString = txOutToString(options.txOut); - const txInCollateralString = options.txInCollateral - ? txInToString(this.dir, options.txInCollateral, true) - : ""; - const changeAddressString = options.changeAddress - ? `--change-address ${options.changeAddress}` - : ""; - const mintString = options.mint ? mintToString(this.dir, options.mint) : ""; - const withdrawals = options.withdrawals - ? withdrawalToString(this.dir, options.withdrawals) - : ""; - const certs = options.certs ? certToString(this.dir, options.certs) : ""; - const metadata = options.metadata - ? "--metadata-json-file " + - jsonToPath(this.dir, options.metadata, "metadata") - : ""; - const auxScript = options.auxScript - ? auxScriptToString(this.dir, options.auxScript) - : ""; - const scriptInvalid = options.scriptInvalid ? "--script-invalid" : ""; - const witnessOverride = options.witnessOverride - ? `--witness-override ${options.witnessOverride}` - : ""; - if (!this.protocolParametersPath) this.queryProtocolParameters(); - execSync(`${this.cliPath} transaction build \ - ${txInString} \ - ${txOutString} \ - ${txInCollateralString} \ - ${certs} \ - ${withdrawals} \ - ${mintString} \ - ${auxScript} \ - ${metadata} \ - ${scriptInvalid} \ - ${witnessOverride} \ - --invalid-hereafter ${ - options.invalidAfter - ? options.invalidAfter - : this.queryTip().slot + 10000 - } \ - --invalid-before ${ - options.invalidBefore ? options.invalidBefore : 0 - } \ - --out-file ${this.dir}/tmp/tx_${UID}.raw \ - ${changeAddressString} \ - --${this.network} \ - --protocol-params-file ${this.protocolParametersPath} \ - ${this.era}`); - - return `${this.dir}/tmp/tx_${UID}.raw`; - } - - /** - * - * @param {Object} options - * @param {path} options.txBody - * @param {Array} options.txIn - * @param {Array} options.txOut - * @param {number} options.witnessCount - * @returns {lovelace} - */ - transactionCalculateMinFee(options) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/transactionCalculateMinFee`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(options), - }); - return response.then((res) => res.text()); - } - this.queryProtocolParameters(); - return parseInt( - execSync(`${this.cliPath} transaction calculate-min-fee \ - --tx-body-file ${options.txBody} \ - --tx-in-count ${options.txIn.length} \ - --tx-out-count ${options.txOut.length} \ - --${this.network} \ - --witness-count ${options.witnessCount} \ - --protocol-params-file ${this.protocolParametersPath}`) - .toString() - .replace(/\s+/g, " ") - .split(" ")[0] - ); - } - - /** - * - * @param {object} script - * @returns {string} Policy Id - */ - transactionPolicyid(script) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/transactionPolicyid`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(script), - }); - return response.then((res) => res.text()); - } - let UID = Math.random().toString(36).substr(2, 9); - fs.writeFileSync( - `${this.dir}/tmp/script_${UID}.json`, - JSON.stringify(script) - ); - return execSync( - `${this.cliPath} transaction policyid --script-file ${this.dir}/tmp/script_${UID}.json` - ) - .toString() - .trim(); - } - - /** - * - * @param {object} script - * @returns {string} Datum hash - */ - transactionHashScriptData(script) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/transactionHashScriptData`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(script), - }); - return response.then((res) => res.text()); - } - return execSync( - `${ - this.cliPath - } transaction hash-script-data --script-data-value '${JSON.stringify( - script - )}'` - ) - .toString() - .trim(); - } - - /** - * - * @param {Object} options - * @param {Array} options.signingKeys - One ore more signing keys - * @param {path} options.txBody - * @returns {path} - */ - transactionSign(options) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/transactionSign`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(options), - }); - return response.then((res) => res.text()); - } - const UID = Math.random().toString(36).substr(2, 9); - const signingKeys = signingKeysToString(options.signingKeys); - execSync(`${this.cliPath} transaction sign \ - --tx-body-file ${options.txBody} \ - --${this.network} \ - ${signingKeys} \ - --out-file ${this.dir}/tmp/tx_${UID}.signed`); - return `${this.dir}/tmp/tx_${UID}.signed`; - } - - /** - * - * @param {Object} options - * @param {path} options.txBody - * @param {path} options.scriptFile - * @param {path} options.signingKey - * @returns {path} - */ - transactionWitness(options) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/transactionWitness`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(options), - }); - return response.then((res) => res.text()); - } - const UID = Math.random().toString(36).substr(2, 9); - if (!options.signingKey && !options.scriptFile) { - throw new Error("script-file or signing-key required for transaction witness command"); - } - let signingParams = ""; - if (options.scriptFile) { - signingParams += `--script-file ${options.scriptFile} `; - } - if (options.signingKey) { - signingParams += `--signing-key-file ${options.signingKey}`; - } - execSync(`${this.cliPath} transaction witness \ - --tx-body-file ${options.txBody} \ - --${this.network} \ - --out-file ${this.dir}/tmp/tx_${UID}.witness \ - ${signingParams}`); - return `${this.dir}/tmp/tx_${UID}.witness`; - } - - /** - * - * @param {Object} options - * @param {path} options.txBody - * @param {Array} options.witnessFiles - * @returns {path} - */ - transactionAssemble(options) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/transactionAssemble`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(options), - }); - return response.then((res) => res.text()); - } - let UID = Math.random().toString(36).substr(2, 9); - let witnessFiles = witnessFilesToString(options.witnessFiles); - execSync(`${this.cliPath} transaction assemble \ - --tx-body-file ${options.txBody} \ - ${witnessFiles} \ - --out-file ${this.dir}/tmp/tx_${UID}.signed`); - return `${this.dir}/tmp/tx_${UID}.signed`; - } - - /** - * - * @param {options} options - * @returns {lovelace} - */ - transactionCalculateMinValue(options) { - this.queryProtocolParameters(); - const multiAsset = multiAssetToString(options); - return parseInt( - execSync(`${this.cliPath} transaction calculate-min-required-utxo \ - --tx-out ${multiAsset} \ - --protocol-params-file ${this.protocolParametersPath} \ - --babbage-era`) - .toString() - .replace(/\s+/g, " ") - .split(" ")[1] - ); - } - - /** - * - * @param {paymentAddr} address - * @param {Value} value - * @returns {lovelace} - */ - transactionCalculateMinRequiredUtxo(address, value) { - this.queryProtocolParameters(); - const multiAsset = multiAssetToString(value); - return parseInt( - execSync(`${this.cliPath} transaction calculate-min-required-utxo \ - --alonzo-era \ - --tx-out ${address}+${multiAsset} \ - --protocol-params-file ${this.protocolParametersPath}`) - .toString() - .replace(/\s+/g, " ") - .split(" ")[1] - ); - } - - /** - * - * @param {path | string} tx - Path or Signed Tx File - * @returns {string} - Transaction Hash - */ - transactionSubmit(tx) { - if (this.httpProvider) { - if (typeof window === "undefined") { - let body = fs.readFileSync(tx).toString(); - return fetch(`${this.httpProvider}/transactionSubmit`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: body, - }).text(); - } else { - return fetch( - `${this.httpProvider}/transactionSubmit?filePath=${tx}` - ).then((res) => res.text()); - } - } - let UID = Math.random().toString(36).substr(2, 9); - let parsedTx; - - if (typeof tx == "object") { - fs.writeFileSync(`${this.dir}/tmp/tx_${UID}.signed`, JSON.stringify(tx)); - parsedTx = `${this.dir}/tmp/tx_${UID}.signed`; - } else { - parsedTx = tx; - } - execSync( - `${this.cliPath} transaction submit --${this.network} --tx-file ${parsedTx}` - ); - return this.transactionTxid({ txFile: parsedTx }); - } - - /** - * - * @param {Object} options - * @param {path=} options.txBody - * @param {path=} options.txFile - * @returns {path} - */ - transactionTxid(options) { - if (this.httpProvider && typeof window !== "undefined") { - let response = fetch(`${this.httpProvider}/transactionTxid`, { - headers: { - "Content-Type": "application/json", - }, - method: "POST", - body: JSON.stringify(options), - }); - return typeof window !== "undefined" - ? response.then((res) => res.text()) - : response.text(); - } - let txArg = options.txBody - ? `--tx-body-file ${options.txBody}` - : `--tx-file ${options.txFile}`; - return execSync(`${this.cliPath} transaction txid ${txArg}`) - .toString() - .trim(); - } - - /** - * - * @param {Object} options - * @param {path=} options.txBody - * @param {path=} options.txFile - * @returns {path} - */ - transactionView(options) { - const txArg = options.txBody - ? `--tx-body-file ${options.txBody}` - : `--tx-file ${options.txFile}`; - return execSync(`${this.cliPath} transaction view ${txArg}`) - .toString() - .trim(); - } - - /** - * @returns {number} - */ - KESPeriod() { - if (this.httpProvider) { - let response = fetch(`${this.httpProvider}/KESPeriod`); - return typeof window !== "undefined" - ? response.then((res) => parseInt(res.text())) - : parseInt(response.text()); - } - if (!this.shelleyGenesis) throw new Error("shelleyGenesisPath required"); - return parseInt( - this.queryTip().slot / this.shelleyGenesis.slotsPerKESPeriod - ); - } - - /** - * - * @param {path} filePath - * @returns {path} - Download link for the file - */ - getDownloadUrl(filePath) { - if (!this.httpProvider) throw new Error("No httpProvider"); - let response = fetch( - `${this.httpProvider}/getDownloadUrl?filePath=${filePath}` - ); - return typeof window !== "undefined" - ? response.then((res) => res.text()) - : response.text(); - } - - /** - * - * @param {number} ada - * @returns {lovelace} - */ - toLovelace(ada) { - return ada * 1000000; - } - - /** - * - * @param {lovelace} lovelace - * @returns {number} - */ - toAda(lovelace) { - return lovelace / 1000000; - } -} - -module.exports = CardanocliJs; diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..8439903 --- /dev/null +++ b/index.ts @@ -0,0 +1,3 @@ +import { CardanoCliJs } from "./lib/cardanoclijs"; + +export default CardanoCliJs; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..3745fc2 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/lib/address.ts b/lib/address.ts new file mode 100644 index 0000000..82fdc5e --- /dev/null +++ b/lib/address.ts @@ -0,0 +1,90 @@ +import { CliCommand } from "./command"; +import { filesShouldExist, filesShouldNotExist, mkdirp, objectToFile } from "./helpers"; + +export class AddressCommand extends CliCommand { + command = "address"; + + /** + * + * @param account Name of the account + * @returns Object with the path to the verification key file and the signgin key file + */ + keyGen(account: string): {vkey: string, skey: string } { + const privAccountDir = `${this.cli.options.dir}/priv/wallet/${account}`; + const vkey = `${privAccountDir}/${account}.payment.vkey`; + const skey = `${privAccountDir}/${account}.payment.skey`; + filesShouldNotExist([vkey, skey]); + mkdirp(privAccountDir); + + this.run("key-gen", [ + { name: "verification-key-file", value: vkey }, + { name: "signing-key-file", value: skey }, + ]); + + return { + vkey, + skey, + }; + } + + /** + * + * @param account Name of the account + * @returns Path to the payment address file + */ + build( + account: string, + options: { paymentScriptFile?: string, stakeScriptFile?: string } + ) { + const privAccountDir = `${this.cli.options.dir}/priv/wallet/${account}`; + const paymentVkeyFile = `${privAccountDir}/${account}.payment.vkey`; + const stakeVKeyFile = `${privAccountDir}/${account}.stake.vkey`; + const outPaymentAddrFile = `${privAccountDir}/${account}.payment.addr`; + + filesShouldExist([ + stakeVKeyFile, + paymentVkeyFile, + ]); + if (options.paymentScriptFile) filesShouldExist([options.paymentScriptFile]); + if (options.stakeScriptFile) filesShouldExist([options.stakeScriptFile]); + filesShouldNotExist([outPaymentAddrFile]); + + const params = [ + { name: "payment-verification-key-file", value: paymentVkeyFile }, + { name: "stake-verification-key-file", value: stakeVKeyFile }, + { name: "out-file", value: outPaymentAddrFile }, + ]; + if (options.paymentScriptFile) params.push({ name: "payment-script-file", value: options.paymentScriptFile }); + if (options.stakeScriptFile) params.push({ name: "stake-script-file", value: options.stakeScriptFile }); + + this.run("build", params, true); + + return outPaymentAddrFile; + } + + /** + * + * @param account + * @returns the payment verificaiton key hash string + */ + keyHash(account: string): string { + const privAccountDir = `${this.cli.options.dir}/priv/wallet/${account}`; + const paymentVkeyFile = `${privAccountDir}/${account}.payment.vkey`; + + filesShouldExist([paymentVkeyFile]); + + return this.run("key-hash", [ + { name: "payment-verification-key-file", value: paymentVkeyFile } + ]); + } + + /** + * + * @param address + * @returns + */ + info(address: string): string { + const res = this.run("info", [{ name: "address", value: address }]); + return JSON.parse(res); + } +} diff --git a/lib/cardanoclijs.ts b/lib/cardanoclijs.ts new file mode 100644 index 0000000..38e9063 --- /dev/null +++ b/lib/cardanoclijs.ts @@ -0,0 +1,106 @@ +import { AddressCommand } from "./address"; +import { fileToObject } from "./helpers"; +import { NodeCommand } from "./node"; +import { QueryCommand } from "./query"; +import { StakeAddressCommand } from "./stake_address"; +import { StakePoolCommand } from "./stake_pool"; +import { TransactionCommand } from "./transaction"; +import { CommandParameter } from "./types"; + +const execSync = require("child_process").execSync; + +type CommandDef = { + command: string, + subcommand?: string, + parameters?: CommandParameter[], +}; + +/* + * Options that the user passes to the constructor + */ +export type UserOptions = { + network?: string, + era?: string, + dir?: string, + cliPath?: string, + socketPath?: string, + shelleyGenesisPath: string, +}; + +class Command { + command: CommandDef; + constructor(cmd: CommandDef) { + this.command = cmd; + } + + run(options: CardanoCliJsOptions) { + const command = [ + options.cliPath, + options.era, + this.command.command, + this.command.subcommand || "", + this.command.parameters?.map(p => `--${p.name} ${p.value}`).join(" ") || "", + ]; + + return execSync(command.join(" ")); + } +} + +export class CardanoCliJsOptions { + network: string; + era: string; + dir: string; + cliPath: string; + shelleyGenesis: any; + socketPath: string; + + constructor(opts: UserOptions) { + this.network = opts.network || "mainnet"; + this.era = opts.era || "conway"; + this.dir = opts.dir || "."; + this.cliPath = opts.cliPath || "cardano-cli"; + + this.socketPath = process.env["CARDANO_NODE_SOCKET_PATH"] || ""; + if (opts.socketPath) this.socketPath = opts.socketPath; + if (!this.socketPath) { + throw new Error("Please provide socketPath in the options or set the CARDANO_NODE_SOCKET_PATH env var"); + } + + if (opts.shelleyGenesisPath) { + this.shelleyGenesis = fileToObject(opts.shelleyGenesisPath); + } else { + throw new Error("Missing shelleyGenesisPath options"); + } + } +} + +export class CardanoCliJs { + options: CardanoCliJsOptions; + public query: QueryCommand; + public address: AddressCommand; + public stake_address: StakeAddressCommand; + public node: NodeCommand; + public stake_pool: StakePoolCommand; + public transaction: TransactionCommand; + + constructor(opts: CardanoCliJsOptions) { + this.options = opts; + this.query = new QueryCommand(this); + this.address = new AddressCommand(this); + this.stake_address = new StakeAddressCommand(this); + this.node = new NodeCommand(this); + this.stake_pool = new StakePoolCommand(this); + this.transaction = new TransactionCommand(this); + } + + runCommand(cmd: CommandDef) { + const command = new Command(cmd); + return command.run(this.options); + } + + getKesPeriod() : number { + const slotsPerKESPeriod = this.options.shelleyGenesis.slotsPerKESPeriod; + const tip = this.query.tip(); + return Math.ceil(Number(tip.slot) / Number(slotsPerKESPeriod)); + } +} diff --git a/lib/command.ts b/lib/command.ts new file mode 100644 index 0000000..2cca215 --- /dev/null +++ b/lib/command.ts @@ -0,0 +1,31 @@ +import { CardanoCliJs } from "./cardanoclijs"; +import { CommandParameter } from "./types"; + +/** + * Super class that all CLI commands extend. = + * Makes it easy to run subcommands + */ +export abstract class CliCommand { + cli: CardanoCliJs; + abstract command: string; // To be set on each subclass + + constructor(cli: CardanoCliJs) { + this.cli = cli; + } + + run(subcommand: string, parameters: CommandParameter[], needsNetwork: boolean = false): string { + if (needsNetwork) { + if (this.cli.options.network == "mainnet") { + parameters.push({ name: "mainnet", value: "" }) + } else { + parameters.push({ name: "testnet-magic", value: this.cli.options.network }) + } + } + + return this.cli.runCommand({ + command: this.command, + subcommand, + parameters, + }).toString().trim(); + } +} diff --git a/lib/helpers.ts b/lib/helpers.ts new file mode 100644 index 0000000..9ce56b2 --- /dev/null +++ b/lib/helpers.ts @@ -0,0 +1,34 @@ +import * as fs from "fs"; + +export function filesShouldNotExist(files: string[]) { + for (let file of files) { + if (fs.existsSync(file)) throw new Error( + `File ${file} already exists. Remove it manually if you want to create a new file.` + ); + } +} + +export function filesShouldExist(files: string[]) { + for (let file of files) { + if (!fs.existsSync(file)) throw new Error( + `File ${file} does not exist. Please ensure you call the command in charge of generating it before this one.` + ); + } +} + +export function mkdirp(dir: string) { + fs.mkdirSync(dir, { recursive: true }); +} + +export function objectToFile(obj: any, file: string) { + fs.writeFileSync(file, JSON.stringify(obj)); +} + +export function fileToObject(file: string) : any { + const jsonData = fs.readFileSync(file, 'utf8'); + return JSON.parse(jsonData); +} + +export function removeFile(path: string) { + fs.unlinkSync(path); +} diff --git a/lib/node.ts b/lib/node.ts new file mode 100644 index 0000000..fa330ff --- /dev/null +++ b/lib/node.ts @@ -0,0 +1,140 @@ +import { CliCommand } from "./command"; +import { filesShouldExist, filesShouldNotExist, mkdirp } from "./helpers"; + +export class NodeCommand extends CliCommand { + command = "node"; + + /** + * Generate a new node keys + * @param poolName + * @returns vkey, skey and counter with the file paths to the key files + */ + keyGen(poolName: string): { vkey: string, skey: string, counter: string } { + const nodeAccountDir = `${this.cli.options.dir}/priv/pool/${poolName}`; + const vkey = `${nodeAccountDir}/${poolName}.node.vkey`; + const skey = `${nodeAccountDir}/${poolName}.node.skey`; + const counter = `${nodeAccountDir}/${poolName}.node.counter`; + + filesShouldNotExist([vkey, skey, counter]); + mkdirp(nodeAccountDir); + + this.run("key-gen", [ + { name: "cold-verification-key-file", value: vkey }, + { name: "cold-signing-key-file", value: skey }, + { name: "operational-certificate-issue-counter", value: counter }, + ]); + + return { vkey, skey, counter }; + } + + /** + * Generate node KES keys + * @param poolName + * @returns vkey and skey with the paths to the generated key files + */ + keyGenKES(poolName: string): { vkey: string, skey: string } { + const nodeAccountDir = `${this.cli.options.dir}/priv/pool/${poolName}`; + const vkey = `${nodeAccountDir}/${poolName}.kes.vkey`; + const skey = `${nodeAccountDir}/${poolName}.kes.skey`; + + filesShouldNotExist([vkey, skey]); + mkdirp(nodeAccountDir); + + this.run("key-gen-KES", [ + { name: "verification-key-file", value: vkey }, + { name: "signing-key-file", value: skey }, + ]); + + return { vkey, skey }; + } + + /** + * Generate node VRF keys + * @param poolName + * @returns vkey and skey with the paths to the generated key files + */ + keyGenVRF(poolName: string) { + const nodeAccountDir = `${this.cli.options.dir}/priv/pool/${poolName}`; + const vkey = `${nodeAccountDir}/${poolName}.vrf.vkey`; + const skey = `${nodeAccountDir}/${poolName}.vrf.skey`; + + filesShouldNotExist([vkey, skey]); + mkdirp(nodeAccountDir); + + this.run("key-gen-VRF", [ + { name: "verification-key-file", value: vkey }, + { name: "signing-key-file", value: skey }, + ]); + + return { vkey, skey }; + } + + /** + * Get the VRF key hash + * @param poolName + * @returns the hash of the VRF key + */ + keyHashVRF(poolName: string) { + const nodeAccountDir = `${this.cli.options.dir}/priv/pool/${poolName}`; + const vkey = `${nodeAccountDir}/${poolName}.vrf.vkey`; + + filesShouldExist([vkey]); + + const hash = this.run("key-hash-VRF", [ + { name: "verification-key-file", value: vkey }, + ]); + + return hash; + } + + /** + * Generate a new node counter certificate + * @param poolName + * @param counterValue + * @returns path to the issue counter file + */ + newCounter(poolName: string, counterValue: number): string { + const nodeAccountDir = `${this.cli.options.dir}/priv/pool/${poolName}`; + const nodeCounter = `${nodeAccountDir}/${poolName}.node.counter`; + const nodeColdVkey = `${nodeAccountDir}/${poolName}.node.vkey`; + + filesShouldExist([nodeCounter, nodeColdVkey]); + mkdirp(nodeAccountDir); + + this.run("new-counter", [ + { name: "cold-verification-key-file", value: nodeColdVkey }, + { name: "counter-value", value: counterValue }, + { name: "operational-certificate-issue-counter-file", value: nodeCounter }, + ]); + + return nodeCounter; + } + + /** + * Generate a new operational certificate for the node + * @param poolName + * @param options + * @returns string - Path to the node operational certificate + */ + issueOpCert(poolName: string, options: { kesPeriod?: number}) : string { + const nodeAccountDir = `${this.cli.options.dir}/priv/pool/${poolName}`; + const kesVKey = `${nodeAccountDir}/${poolName}.kes.vkey`; + const nodeCounter = `${nodeAccountDir}/${poolName}.node.counter`; + const nodeColdSkey = `${nodeAccountDir}/${poolName}.node.skey`; + const nodeCert = `${nodeAccountDir}/${poolName}.node.cert`; + + filesShouldExist([kesVKey, nodeCounter, nodeColdSkey]); + mkdirp(nodeAccountDir); + + this.run("issue-op-cert", [ + { name: "kes-verification-key-file", value: kesVKey }, + { name: "cold-signing-key-file", value: nodeColdSkey }, + { name: "operational-certificate-issue-counter-file", value: nodeCounter }, + { name: "kes-period", value: options.kesPeriod ? options.kesPeriod : this.cli.getKesPeriod()}, + { name: "out-file", value: nodeCert }, + ] + ); + + return nodeCert; + } +} diff --git a/lib/query.ts b/lib/query.ts new file mode 100644 index 0000000..305bfc4 --- /dev/null +++ b/lib/query.ts @@ -0,0 +1,127 @@ +import { CliCommand } from "./command"; + +const fs = require("fs"); + +export class QueryCommand extends CliCommand { + command = "query"; + + /** + * Fetch the protocol parameters into a file + * @returns string - Path to file containing protocol parameters + */ + protocolParameters() : any { + const file = `/tmp/protocolParams.json`; + + const opts = [{ name: "out-file", value: file }]; + this.run("protocol-parameters", opts, true); + return JSON.parse(fs.readFileSync(file)); + } + + /** + * Fetch the protocol parameters into a file + * @returns string - Path to file containing protocol parameters + */ + protocolParametersFile() : any { + const file = `/tmp/protocolParams.json`; + + const opts = [{ name: "out-file", value: file }]; + this.run("protocol-parameters", opts, true); + return file; + } + + /** + * + * @returns The tip as a number + */ + tip(): any { + return JSON.parse(this.run("tip", [], true)); + } + + /** + * + * @param address Address string + * @returns the Stake address information + */ + stakeAddressInfo(address: string) : any { + return this.run("stake-address-info", [{ + name: "address", + value: address, + }], true); + } + + /** + * + * @param address Address string + * @returns Array of UTxO objects + */ + utxo(address: string): any[] { + const rawResult = this.run("utxo", [ + { + name: "address", + value: address, + }, + { + name: "output-json", + value: "", + } + ], true); + return JSON.parse(rawResult); + } + + /** + * Query the governance state + * @returns JSON object with the governance state + */ + govState() { + const file = `/tmp/govState.json`; + const opts = [{ name: "out-file", value: file }]; + this.run("gov-state", opts, true); + return JSON.parse(fs.readFileSync(file)); + } + + /** + * Query DRep state + * @param opts + */ + drepState(opts: { drepVKeyFile?: string, drepKeyHash?: string }) { + const params = []; + if (!opts.drepVKeyFile && !opts.drepKeyHash) { + params.push({ name: "all-dreps", value: ""}) + } + if (opts.drepVKeyFile && opts.drepKeyHash) { + throw new Error("'drepVKeyFile and drepKeyHash cannot be set at the same time"); + } else if (opts.drepKeyHash) { + params.push({ name: "--drep-key-hash", value: opts.drepKeyHash }); + } else if (opts.drepVKeyFile) { + params.push({ name: "drep-verification-key-file", value: opts.drepVKeyFile }); + } + + const file = `/tmp/drepState.json`; + params.push({ name: "out-file", value: file }); + this.run("drep-state", params, true); + return JSON.parse(fs.readFileSync(file)); + } + + /** + * Query DRep stake distribution + * @param opts + */ + drepStakeDistribution(opts: { drepVKeyFile?: string, drepKeyHash?: string }) { + const params = []; + if (!opts.drepVKeyFile && !opts.drepKeyHash) { + params.push({ name: "all-dreps", value: ""}) + } + if (opts.drepVKeyFile && opts.drepKeyHash) { + throw new Error("'drepVKeyFile and drepKeyHash cannot be set at the same time"); + } else if (opts.drepKeyHash) { + params.push({ name: "--drep-key-hash", value: opts.drepKeyHash }); + } else if (opts.drepVKeyFile) { + params.push({ name: "drep-verification-key-file", value: opts.drepVKeyFile }); + } + + const file = `/tmp/drepState.json`; + params.push({ name: "out-file", value: file }); + this.run("drep-stake-distribution", params, true); + return JSON.parse(fs.readFileSync(file)); + } +} diff --git a/lib/stake_address.ts b/lib/stake_address.ts new file mode 100644 index 0000000..293b2f5 --- /dev/null +++ b/lib/stake_address.ts @@ -0,0 +1,148 @@ +import { CliCommand } from "./command"; +import { filesShouldExist, filesShouldNotExist, mkdirp } from "./helpers"; + +export class StakeAddressCommand extends CliCommand { + command = "stake-address"; + + /** + * + * @param account Name of the account + * @returns Object with the path to the verification key file and the signgin key file + */ + keyGen(account: string): {vkey: string, skey: string } { + const privAccountDir = `${this.cli.options.dir}/priv/wallet/${account}`; + const vkey = `${privAccountDir}/${account}.stake.vkey`; + const skey = `${privAccountDir}/${account}.stake.skey`; + filesShouldNotExist([vkey, skey]); + mkdirp(privAccountDir); + + this.run("key-gen", [ + { name: "verification-key-file", value: vkey }, + { name: "signing-key-file", value: skey }, + ]); + + return { + vkey, + skey, + }; + } + + /** + * Get a stake address key hash + * @param account + * @returns hash of the key as string + */ + keyHash(account: string): string { + const privAccountDir = `${this.cli.options.dir}/priv/wallet/${account}`; + const stakeVKeyFile = `${privAccountDir}/${account}.stake.vkey`; + + return this.run("key-hash", [ + { name: "stake-verification-key-file", value: stakeVKeyFile }, + ]); + } + + /** + * + * @param account Name of the account + * @returns Path to the stake address file + */ + build(account: string) { + const privAccountDir = `${this.cli.options.dir}/priv/wallet/${account}`; + const stakeVKeyFile = `${privAccountDir}/${account}.stake.vkey`; + const addrFilePath = `${privAccountDir}/${account}.stake.addr`; + filesShouldExist([stakeVKeyFile]); + filesShouldNotExist([addrFilePath]); + mkdirp(privAccountDir); + + this.run("build", [ + { name: "stake-verification-key-file", value: stakeVKeyFile }, + { name: "out-file", value: addrFilePath }, + ], true); + + return addrFilePath; + } + + /** + * + * @param account Account name + * @return file path of the generated certificate + */ + registrationCertificate(account: string): string { + const privAccountDir = `${this.cli.options.dir}/priv/wallet/${account}`; + const stakeVKeyFile = `${privAccountDir}/${account}.stake.vkey`; + const stakeRegCertFile = `${privAccountDir}/${account}-reg.stake.cert`; + + filesShouldExist([stakeVKeyFile]); + filesShouldNotExist([stakeRegCertFile]); + mkdirp(privAccountDir); + + this.run("registration-certificate", [ + { name: "stake-verification-key-file", value: stakeVKeyFile }, + { name: "key-reg-deposit-amt", value: this.cli.query.govState().currentPParams.stakeAddressDeposit }, + { name: "out-file", value: stakeRegCertFile }, + ]); + + return stakeRegCertFile; + } + + /** + * + * @param account Account name + * @return file path of the generated certificate + */ + deRegistrationCertificate(account: string, options: { stakeScriptFile?: string, amount?: string }): string { + const privAccountDir = `${this.cli.options.dir}/priv/wallet/${account}`; + const stakeVKeyFile = `${privAccountDir}/${account}.stake.vkey`; + const stakeDeregCertFile = `${privAccountDir}/${account}-dereg.stake.cert`; + + filesShouldExist([stakeVKeyFile]); + if (options.stakeScriptFile) filesShouldExist([options.stakeScriptFile]); + filesShouldNotExist([stakeDeregCertFile]); + mkdirp(privAccountDir); + + let params = [ + { name: "stake-verification-key-file", value: stakeVKeyFile }, + { name: "key-reg-deposit-amt", value: this.cli.query.govState().currentPParams.stakeAddressDeposit }, + { name: "out-file", value: stakeDeregCertFile }, + ]; + if (options.amount) params.push({ name: "key-reg-deposit-amt", value: options.amount }); + if (options.stakeScriptFile) params.push( { name: "stake-script-file", value: options.stakeScriptFile }); + + this.run("deregistration-certificate", params); + + return stakeDeregCertFile; + } + + /** + * Create stake delegation certificate. If no stake script file is provided it uses the account stake verification key. + * @param account Account name + * @return file path of the generated certificate + */ + delegationCertificate(account: string, options: { stakePoolId: string, stakeScriptFile?: string }): string { + const privAccountDir = `${this.cli.options.dir}/priv/wallet/${account}`; + const stakeVKeyFile = `${privAccountDir}/${account}.stake.vkey`; + const stakeDelegCertFile = `${privAccountDir}/${account}.deleg.cert`; + + if (!options.stakePoolId) throw "Missing options.stakePoolId in call to delegationCertificate"; + + filesShouldExist([stakeVKeyFile]); + if (options.stakeScriptFile) filesShouldExist([options.stakeScriptFile]); + filesShouldNotExist([stakeDelegCertFile]); + mkdirp(privAccountDir); + + let params = [ + { name: "stake-pool-id", value: options.stakePoolId }, + { name: "out-file", value: stakeDelegCertFile }, + ]; + // One of the following is required + if (options.stakeScriptFile) { + params.push({ name: "stake-script-file", value: options.stakeScriptFile }); + } else { + params.push({ name: "stake-verification-key-file", value: stakeVKeyFile }); + } + + this.run("stake-delegation-certificate", params); + + return stakeDelegCertFile; + } +} diff --git a/lib/stake_pool.ts b/lib/stake_pool.ts new file mode 100644 index 0000000..e4b76d8 --- /dev/null +++ b/lib/stake_pool.ts @@ -0,0 +1,123 @@ +import { CliCommand } from "./command"; +import { filesShouldExist, objectToFile, removeFile, mkdirp, filesShouldNotExist } from "./helpers"; + +export class StakePoolCommand extends CliCommand { + command = "stake-pool"; + + /** + * Get the stake pool Id + * @param poolName + * @returns string - Pool Id + */ + id(poolName: string): string { + const nodeAccountDir = `${this.cli.options.dir}/priv/pool/${poolName}`; + const vkey = `${nodeAccountDir}/${poolName}.node.vkey`; + + return this.run("id", [ + { name: "cold-verification-key-file", value: vkey }, + ]); + } + + /** + * Get the stake pool metadata hash + * @param metadata Metadata object + * @returns string- Metadata hash + */ + metadataHash(metadata: any): string { + const metadataFile = "/tmp/pool-metadata.json"; + objectToFile(metadata, metadataFile); + const hash = this.run("metadata-hash", [ + { name: "pool-metadata-file", value: metadataFile }, + ]); + removeFile(metadataFile); + return hash; + } + + /** + * Create pool registration certificate + * @param poolName + * @param options + * @returns string - path to the registration certificate + */ + registrationCertificate( + poolName: string, + options: { + pledge: number, + margin: number, + cost: number, + url: string, + metaHash: string, + rewardAccountFile: string, + ownersStakeVKeyFiles: string[], + relays: any[], + } + ) : string { + if (!options || !options.pledge || !options.margin || !options.cost + || !options.url || !options.metaHash || !options.rewardAccountFile + || !options.ownersStakeVKeyFiles || !options.relays + ) throw new Error("All options are required"); + + const nodeAccountDir = `${this.cli.options.dir}/priv/pool/${poolName}`; + const coldVkey = `${nodeAccountDir}/${poolName}.node.vkey`; + const vrfVKey = `${nodeAccountDir}/${poolName}.vrf.vkey`; + const poolRegCertFile = `${nodeAccountDir}/${poolName}-reg.pool.cert`; + + mkdirp(nodeAccountDir); + filesShouldExist([ nodeAccountDir, coldVkey, vrfVKey ]); + filesShouldNotExist([poolRegCertFile]); + + let params = [ + { name: "cold-verification-key-file", value: coldVkey }, + { name: "vrf-verification-key-file", value: vrfVKey }, + { name: "pool-pledge", value: options.pledge }, + { name: "pool-cost", value: options.cost }, + { name: "pool-margin", value: options.margin }, + { name: "pool-reward-account-verification-key-file", value: options.rewardAccountFile }, + { name: "metadata-url", value: options.url }, + { name: "metadata-hash", value: options.metaHash }, + { name: "out-file", value: poolRegCertFile }, + ]; + const owners = options.ownersStakeVKeyFiles.map((o: string) => ({ name: "pool-owner-stake-verification-key-file", value: o })); + params = params.concat(owners); + + const relays = options.relays.map((r: any) => { + if (!((r.host || r.ip) && r.port) && !r.multiHost) + throw new Error("Relay is missing arguments"); + if (r.host) { + return [{ name: "single-host-pool-relay", value: r.host }, { name: "pool-relay-port", value: r.port }]; + } else if (r.ip) { + return [{ name: "pool-relay-ipv4", value: r.ip }, { name: "pool-relay-port", value: r.port }]; + } else if (r.multiHost) { + return [{ name: "multi-host-pool-relay", value: r.multiHost }]; + } + return { name: "", value: "" }; // HACK: make compiler happy + }).flat(); + params.concat(relays); + + this.run("registration-certificate", params, true); + + return poolRegCertFile; + } + + /** + * Create de-registration certificate for the pool + * @param poolName + * @param epoch + * @returns string - File path of the deregistration certificate + */ + deRegistrationCertificate(poolName: string, epoch: number): string { + const nodeAccountDir = `${this.cli.options.dir}/priv/pool/${poolName}`; + const coldVkey = `${nodeAccountDir}/${poolName}.node.vkey`; + const poolDeRegCertFile = `${nodeAccountDir}/${poolName}-dereg.pool.cert`; + + mkdirp(nodeAccountDir); + + this.run("deregistration-certificate", [ + { name: "cold-verification-key-file", value: coldVkey }, + { name: "epoch", value: epoch }, + { name: "out-file", value: poolDeRegCertFile }, + ]); + + return poolDeRegCertFile; + } +} diff --git a/lib/transaction.ts b/lib/transaction.ts new file mode 100644 index 0000000..1f42a71 --- /dev/null +++ b/lib/transaction.ts @@ -0,0 +1,229 @@ +import { CliCommand } from "./command"; +import { CommandParameter } from "./types"; + +export class TransactionCommand extends CliCommand { + command = "transaction"; + + /** + * Build a raw transaction + * @param options - Array of parameters. Usually tx-in and tx-out in the form [{ name: "tx-in", value: "hash#outId" }, { name: "tx-out", value: "addr amount" }]. You can also add other parameters + */ + buildRaw(options: CommandParameter[]) { + const tx_in = options.find((o) => o.name === "tx-in"); + if (!tx_in) throw new Error("At least one tx-in must be provided"); + const tx_out = options.find((o) => o.name === "tx-out"); + if (!tx_out) throw new Error("At least one tx-out must be provided"); + + const invalid_hereafter = options.find((o) => o.name === "invalid-hereafter"); + if (!invalid_hereafter) options.push({ name: "invalid-hereafter", value: Number(this.cli.query.tip().slot) + 10000 }); + const invalid_before = options.find((o) => o.name === "invalid-before"); + if (!invalid_before) options.push({ name: "invalid-before", value: 0 }); + const fee = options.find((o) => o.name === "fee"); + if (!fee) options.push({ name: "fee", value: 0 }); + + let uid = Math.random().toString(36).substring(2, 9); + const out_file = `/tmp/transaction-${uid}.raw`; + options.push({ name: "out-file", value: out_file }); + + this.run("build-raw", options); + + return out_file; + } + + /** + * Build a transaction + * @param options - Array of parameters. Usually tx-in and tx-out in the form [{ name: "tx-in", value: "hash#outId" }, { name: "tx-out", value: "addr#amount" }]. You can also add other parameters + */ + build(options: CommandParameter[]) { + const tx_in = options.find((o) => o.name === "tx-in"); + if (!tx_in) throw new Error("At least one tx-in must be provided"); + const tx_out = options.find((o) => o.name === "tx-out"); + if (!tx_out) throw new Error("At least one tx-out must be provided"); + + const invalid_hereafter = options.find((o) => o.name === "invalid-hereafter"); + if (!invalid_hereafter) options.push({ name: "invalid-hereafter", value: Number(this.cli.query.tip().slot) + 10000 }); + const invalid_before = options.find((o) => o.name === "invalid-before"); + if (!invalid_before) options.push({ name: "invalid-before", value: 0 }); + //const protocol_params = options.find((o) => o.name === "protocol-params-file"); + //if (!protocol_params) options.push({ name: "protocol-params-file", value: this.cli.query.protocolParametersFile() }); + + let uid = Math.random().toString(36).substring(2, 9); + const out_file = `/tmp/transaction-${uid}.raw`; + options.push({ name: "out-file", value: out_file }); + + this.run("build", options, true); + + return out_file; + } + + /** + * Calcuate the min fee of a transaction + * @param txFilePath + * @param txInCount + * @param txOutCount + * @param witnessCount + * @returns number - the min fee in lovelace + */ + calculateMinFee( + txFilePath: string, + txInCount: number, + txOutCount: number, + witnessCount: number + ): number { + const res = this.run("calculate-min-fee", [ + { name: "tx-body-file", value: txFilePath }, + { name: "tx-in-count", value: txInCount }, + { name: "tx-out-count", value: txOutCount }, + { name: "witness-count", value: witnessCount }, + { name: "protocol-params-file", value: this.cli.query.protocolParametersFile() }, + ]); + const minFee = res.split(" ")[0]; + return parseInt(minFee); + } + + /** + * Get the transaction policy id from the script file + * @param scriptFile + * @returns string - the policy id + */ + transactionPolicyId(scriptFile: string): string { + return this.run("policyid", [ + { name: "script-file", value: scriptFile } + ]); + } + + /** + * Get the script data hash (datum hash) + * @param scriptData Object containing the script data + * @returns string - Datum hash + */ + hashScriptData(scriptData: any) : string { + return this.run("hash-script-data", [ + { name: "script-data-value", value: JSON.stringify(scriptData) } + ]); + } + + /** + * Sign a transaction and returns the signed transaction file path + * @param txBodyFile string - file path for the transaction body file + * @param signingKeyFiles string - an array of file paths to the signing keys + * @returns string - Signed transaction file path + */ + sign(txBodyFile: string, signingKeyFiles: string[]) : string { + let uid = Math.random().toString(36).substring(2, 9); + const outFile = `/tmp/transaction-${uid}.signed`; + const params = [ + { name: "tx-body-file", value: txBodyFile }, + { name: "out-file", value: outFile }, + ]; + const signings = signingKeyFiles.map((s) => ({ name: "signing-key-file", value: s })); + params.concat(signings); + + this.run("sign", params); + + return outFile; + } + + /** + * Create witness file for a transaction + * @param txBodyFile string - File path of the transcation body + * @param signingKeyFiles string[] - List of signing key file paths + * @returns string - file path for the generated witness file + */ + witness(txBodyFile: string, signingKeyFiles: string[]) { + let uid = Math.random().toString(36).substring(2, 9); + const outFile = `/tmp/transaction-${uid}.witness`; + const params = [ + { name: "tx-body-file", value: txBodyFile }, + { name: "out-file", value: outFile }, + ]; + const signings = signingKeyFiles.map((s) => ({ name: "signing-key-file", value: s })); + params.concat(signings); + + this.run("witness", params); + + return outFile; + } + + /** + * Assemble a transaction from a transaction body and some witnesses + * @param txBodyFile string - path to the tx body + * @param witnessFiles string[] - list of witness file paths + * @returns string - file path to the assembled transaction + */ + assemble(txBodyFile: string, witnessFiles: string[]) : string { + let uid = Math.random().toString(36).substring(2, 9); + const outFile = `/tmp/transaction-${uid}.signed`; + const params = [ + { name: "tx-body-file", value: txBodyFile }, + { name: "out-file", value: outFile }, + ]; + const witnesses = witnessFiles.map((s) => ({ name: "witness-file", value: s })); + params.concat(witnesses); + + this.run("assemble", params); + + return outFile; + } + + /** + * Calucate the min required UTxo of a transaction. + * @param options List of command parameters in the form [{name: "param-name", value: "someValue" | number }]. "protocol-params-file" is automatically added + * @returns the min requierd utxo for a transaction + */ + calculateMinRequiredUtxo(options: CommandParameter[]) : string { + const protocol_params = options.find((o) => o.name === "protocol-params-file"); + if (!protocol_params) options.push({ name: "protocol-params-file", value: this.cli.query.protocolParametersFile() }); + + const res = this.run("calculate-min-required-utxo", options); + const minRequiredUtxo = res.split(" ")[1]; + return minRequiredUtxo; + } + + submit(signedTxFile: string) { + this.run("submit", [ + { name: "tx-file", value: signedTxFile }, + { name: "socket-path", value: this.cli.options.socketPath } + ]); + + return this.txId({ txFile: signedTxFile }); + } + + /** + * Return the transaction Id + * @param options + * @returns string - transaction ID + */ + txId(options: { txBodyFile?: string, txFile?: string }) { + if (!options.txBodyFile && !options.txFile) { + throw new Error("Either txBodyFile or txFile must be provided to the txId function"); + } + if (options.txBodyFile && options.txFile) { + throw new Error("Only one of txBodyFile or txFile must be provided to the txId function"); + } + const params = []; + if (options.txBodyFile) params.push({name: "tx-body-file", value: options.txBodyFile }); + if (options.txFile) params.push({name: "tx-body-file", value: options.txFile }); + return this.run("txid", params); + } + + /** + * Return tx view information + * @param options + * @returns + */ + view(options: { txBodyFile?: string, txFile?: string }): any { + if (!options.txBodyFile && !options.txFile) { + throw new Error("Either txBodyFile or txFile must be provided to the view function"); + } + if (options.txBodyFile && options.txFile) { + throw new Error("Only one of txBodyFile or txFile must be provided to the view function"); + } + const params = [ + { name: "output-json", value: "" } + ]; + if (options.txBodyFile) params.push({name: "tx-body-file", value: options.txBodyFile }); + if (options.txFile) params.push({name: "tx-body-file", value: options.txFile }); + return this.run("view", params); + } +} diff --git a/lib/types.ts b/lib/types.ts new file mode 100644 index 0000000..c067b84 --- /dev/null +++ b/lib/types.ts @@ -0,0 +1,4 @@ +export type CommandParameter = { + name: string, + value: string | number, +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c641739 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3593 @@ +{ + "name": "cardanocli-js", + "version": "5.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cardanocli-js", + "version": "5.0.0", + "license": "ISC", + "devDependencies": { + "@types/jest": "^29.5.12", + "@types/node": "^20.12.11", + "jest": "^29.7.0", + "ts-jest": "^29.1.2", + "typescript": "^5.4.5" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "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.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.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "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.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", + "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/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": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/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": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/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": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/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/@babel/highlight/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@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.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "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/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.12.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", + "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": ">=8" + } + }, + "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/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": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.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/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": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "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": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.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/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": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001620", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", + "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", + "dev": true, + "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/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" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "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/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/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/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.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==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.774", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz", + "integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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 + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "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": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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": ">=6.9.0" + } + }, + "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": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": { + "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": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "engines": { + "node": ">=4" + } + }, + "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/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/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "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": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "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/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/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" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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": { + "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 + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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": ">=4" + } + }, + "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/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "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": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "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, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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 + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "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/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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/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/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/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" + } + }, + "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" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "engines": { + "node": ">=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==", + "dev": true, + "engines": { + "node": ">=0.10.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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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" + } + }, + "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, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "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" + } + }, + "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, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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, + "bin": { + "semver": "bin/semver.js" + } + }, + "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, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "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.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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" + }, + "engines": { + "node": ">=8" + } + }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "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, + "engines": { + "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==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.1.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", + "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "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": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "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, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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, + "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/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 + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.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/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index 70260b7..750834e 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,26 @@ { "name": "cardanocli-js", - "version": "4.1.6", - "description": "A library binding the cardano-cli to JavaScript", - "main": "index.js", - "types": "./index.d.ts", + "version": "5.0.0", + "description": "A library wrapping the Cardano CLI for Javascript and Typescript", + "main": "index.ts", "scripts": { - "test": "jest" + "test": "jest -i" }, "repository": { "type": "git", - "url": "https://github.com/shareslake/cardanocli-js.git" + "url": "git+https://github.com/miguelaeh/cardanocli-js.git" }, "author": "", "license": "ISC", - "dependencies": { - "sync-fetch": "^0.3.0" + "bugs": { + "url": "https://github.com/miguelaeh/cardanocli-js/issues" }, + "homepage": "https://github.com/miguelaeh/cardanocli-js#readme", "devDependencies": { - "dotenv": "^8.2.0", - "jest": "^26.6.3" + "@types/jest": "^29.5.12", + "@types/node": "^20.12.11", + "jest": "^29.7.0", + "ts-jest": "^29.1.2", + "typescript": "^5.4.5" } } diff --git a/test/index.test.js b/test/index.test.js deleted file mode 100644 index 8437454..0000000 --- a/test/index.test.js +++ /dev/null @@ -1,511 +0,0 @@ -require('dotenv').config(); -const CardanoJs = require("../index.js"); -const path = require("path"); -const fs = require('fs'); -const rimraf = require("rimraf"); - -const options = { - shelleyGenesisPath: process.env.TEST_GENESIS_PATH, - dir: process.env.TEST_WORKSPACE_DIR, - era: process.env.TEST_ERA, - network: process.env.TEST_NETWORK -} - -const cardanoJs = new CardanoJs(options); - -let tmpPath = "", - tmpSignedPath = "", - tmpWitnessPath = ""; - -afterAll(() => { - cleanUpTestDirectory(); -}); - -describe('Basics & Utilities', () => { - - test('queryProtocolParameters()', () => { - let obj = cardanoJs.queryProtocolParameters(); - - expect(Object.keys(obj).length).toEqual(17); - - expect(obj.hasOwnProperty('poolDeposit')).toBeTruthy(); - expect(obj.hasOwnProperty('protocolVersion')).toBeTruthy(); - expect(obj.hasOwnProperty('minUTxOValue')).toBeTruthy(); - expect(obj.hasOwnProperty('decentralisationParam')).toBeTruthy(); - expect(obj.hasOwnProperty('maxTxSize')).toBeTruthy(); - expect(obj.hasOwnProperty('minPoolCost')).toBeTruthy(); - expect(obj.hasOwnProperty('minFeeA')).toBeTruthy(); - expect(obj.hasOwnProperty('maxBlockBodySize')).toBeTruthy(); - expect(obj.hasOwnProperty('minFeeB')).toBeTruthy(); - expect(obj.hasOwnProperty('eMax')).toBeTruthy(); - expect(obj.hasOwnProperty('extraEntropy')).toBeTruthy(); - expect(obj.hasOwnProperty('maxBlockHeaderSize')).toBeTruthy(); - expect(obj.hasOwnProperty('stakeAddressDeposit')).toBeTruthy(); - expect(obj.hasOwnProperty('nOpt')).toBeTruthy(); - expect(obj.hasOwnProperty('rho')).toBeTruthy(); - expect(obj.hasOwnProperty('a0')).toBeTruthy(); - expect(obj.hasOwnProperty('rho')).toBeTruthy(); - }); - - test('queryTip()', () => { - let obj = cardanoJs.queryTip(); - - expect(Object.keys(obj).length).toEqual(3); - - expect(obj.hasOwnProperty('blockNo')).toBeTruthy(); - expect(obj.hasOwnProperty('headerHash')).toBeTruthy(); - expect(obj.hasOwnProperty('slotNo')).toBeTruthy(); - }); - - test('KESPeriod()', () => { - let obj = cardanoJs.KESPeriod(); - expect(typeof obj).toEqual('number'); - }); - - test('toLovelace(ada)', () => { - let obj = cardanoJs.toLovelace(100); - - expect(obj).toEqual(100000000); - }); - - test('toAda(lovelace)', () => { - let obj = cardanoJs.toAda(100000000); - - expect(obj).toEqual(100); - }); - -}); - -describe('Wallet Operations', () => { - - test('addressKeyGen(account)', () => { - cardanoJs.addressKeyGen(process.env.TEST_ACCOUNT_NAME); - - let keysPath = path.join(cardanoJs.dir, 'priv', 'wallet', process.env.TEST_ACCOUNT_NAME), - vkey = path.join(keysPath, `${process.env.TEST_ACCOUNT_NAME}.payment.vkey`), - skey = path.join(keysPath, `${process.env.TEST_ACCOUNT_NAME}.payment.skey`); - - expect(fs.existsSync(vkey)).toBeTruthy(); - expect(fs.existsSync(skey)).toBeTruthy(); - }); - - test('stakeAddressKeyGen(account)', () => { - cardanoJs.stakeAddressKeyGen(process.env.TEST_ACCOUNT_NAME); - - let keysPath = path.join(cardanoJs.dir, 'priv', 'wallet', process.env.TEST_ACCOUNT_NAME), - vkey = path.join(keysPath, `${process.env.TEST_ACCOUNT_NAME}.stake.vkey`), - skey = path.join(keysPath, `${process.env.TEST_ACCOUNT_NAME}.stake.skey`); - - expect(fs.existsSync(vkey)).toBeTruthy(); - expect(fs.existsSync(skey)).toBeTruthy(); - }); - - test('stakeAddressBuild(account)', () => { - cardanoJs.stakeAddressBuild(process.env.TEST_ACCOUNT_NAME); - - let keysPath = path.join(cardanoJs.dir, 'priv', 'wallet', process.env.TEST_ACCOUNT_NAME), - addr = path.join(keysPath, `${process.env.TEST_ACCOUNT_NAME}.stake.addr`); - - expect(fs.existsSync(addr)).toBeTruthy(); - }); - - test('addressBuild(account)', () => { - cardanoJs.addressBuild(process.env.TEST_ACCOUNT_NAME); - - let keysPath = path.join(cardanoJs.dir, 'priv', 'wallet', process.env.TEST_ACCOUNT_NAME), - addr = path.join(keysPath, `${process.env.TEST_ACCOUNT_NAME}.payment.addr`); - - expect(fs.existsSync(addr)).toBeTruthy(); - }); - - test('addressBuildScript(script)', () => { - - let script = { - "scripts": [ - { - "keyHash": "e09d36c79dec9bd1b3d9e152247701cd0bb860b5ebfd1de8abb6735a", - "type": "sig" - }, - { - "keyHash": "a687dcc24e00dd3caafbeb5e68f97ca8ef269cb6fe971345eb951756", - "type": "sig" - } - ], - "type": "all" - }; - - let obj = cardanoJs.addressBuildScript(script); - - expect(typeof obj).toEqual('string'); - }); - - test('queryStakeAddressInfo(address)', () => { - let obj = cardanoJs.queryStakeAddressInfo(process.env.TEST_STAKE_ADDRESS); - - expect(Array.isArray(obj)).toBeTruthy(); - - obj = obj[0]; - - expect(Object.keys(obj).length).toEqual(3); - - expect(obj.hasOwnProperty('address')).toBeTruthy(); - expect(obj.hasOwnProperty('delegation')).toBeTruthy(); - expect(obj.hasOwnProperty('rewardAccountBalance')).toBeTruthy(); - }); - - test('queryUtxo(address)', () => { - let obj = cardanoJs.queryUtxo(process.env.TEST_PAYMENT_ADDRESS); - - expect(Array.isArray(obj)).toBeTruthy(); - - obj = obj[0]; - - expect(Object.keys(obj).length).toEqual(3); - - expect(obj.hasOwnProperty('txHash')).toBeTruthy(); - expect(obj.hasOwnProperty('txId')).toBeTruthy(); - expect(obj.hasOwnProperty('amount')).toBeTruthy(); - }); - - test('addressInfo(address)', () => { - let obj = cardanoJs.addressInfo(process.env.TEST_PAYMENT_ADDRESS); - - expect(Object.keys(obj).length).toEqual(5); - - expect(obj.hasOwnProperty('address')).toBeTruthy(); - expect(obj.hasOwnProperty('base16')).toBeTruthy(); - expect(obj.hasOwnProperty('type')).toBeTruthy(); - expect(obj.hasOwnProperty('encoding')).toBeTruthy(); - expect(obj.hasOwnProperty('era')).toBeTruthy(); - }); - - test('stakeAddressRegistrationCertificate(account)', () => { - let obj = cardanoJs.stakeAddressRegistrationCertificate(process.env.TEST_ACCOUNT_NAME); - - expect(typeof obj).toEqual('string'); - expect(fs.existsSync(obj)).toBeTruthy(); - }); - - test('stakeAddressDeregistrationCertificate(account)', () => { - let obj = cardanoJs.stakeAddressDeregistrationCertificate(process.env.TEST_ACCOUNT_NAME); - - expect(typeof obj).toEqual('string'); - expect(fs.existsSync(obj)).toBeTruthy(); - }); - - test('stakeAddressDelegationCertificate(account, poolId)', () => { - let obj = cardanoJs.stakeAddressDelegationCertificate( - process.env.TEST_ACCOUNT_NAME, process.env.TEST_POOL_ID); - - expect(typeof obj).toEqual('string'); - expect(fs.existsSync(obj)).toBeTruthy(); - }); - - test('stakeAddressKeyHash(account)', () => { - let obj = cardanoJs.stakeAddressKeyHash(process.env.TEST_ACCOUNT_NAME); - - expect(typeof obj).toEqual('string'); - }); - - test('addressKeyHash(account)', () => { - let obj = cardanoJs.addressKeyHash(process.env.TEST_ACCOUNT_NAME); - - expect(typeof obj).toEqual('string'); - }); - - test('wallet(account)', () => { - let obj = cardanoJs.wallet(process.env.TEST_ACCOUNT_NAME); - - expect(Object.keys(obj).length).toEqual(8); - - expect(obj.hasOwnProperty('name')).toBeTruthy(); - expect(obj.hasOwnProperty('paymentAddr')).toBeTruthy(); - expect(obj.hasOwnProperty('stakingAddr')).toBeTruthy(); - expect(obj.hasOwnProperty('balance')).toBeTruthy(); - expect(obj.hasOwnProperty('reward')).toBeTruthy(); - - expect(obj.hasOwnProperty('deleg')).toBeTruthy(); - expect(Object.keys(obj.deleg).length).toEqual(1); - expect(obj.deleg.hasOwnProperty('cert')).toBeTruthy(); - - expect(obj.hasOwnProperty('payment')).toBeTruthy(); - expect(Object.keys(obj.payment).length).toEqual(3); - expect(obj.payment.hasOwnProperty('addr')).toBeTruthy(); - expect(obj.payment.hasOwnProperty('skey')).toBeTruthy(); - expect(obj.payment.hasOwnProperty('vkey')).toBeTruthy(); - - expect(obj.hasOwnProperty('stake')).toBeTruthy(); - expect(Object.keys(obj.stake).length).toEqual(4); - expect(obj.stake.hasOwnProperty('addr')).toBeTruthy(); - expect(obj.stake.hasOwnProperty('skey')).toBeTruthy(); - expect(obj.stake.hasOwnProperty('vkey')).toBeTruthy(); - expect(obj.stake.hasOwnProperty('cert')).toBeTruthy(); - }); - - test('wallet(account).balance()', () => { - let obj = cardanoJs.wallet(process.env.TEST_ACCOUNT_NAME).balance(); - - expect(Object.keys(obj).length).toEqual(2); - expect(obj.hasOwnProperty('utxo')).toBeTruthy(); - expect(obj.hasOwnProperty('amount')).toBeTruthy(); - - }); - - test('wallet(account).reward()', () => { - let obj = cardanoJs.wallet(process.env.TEST_ACCOUNT_NAME).reward(); - - if ('string' === typeof obj) { - expect(obj).toEqual('Staking key is not registered'); - } else { - expect(typeof obj).toEqual('number'); - } - }); -}); - -describe('Pool Operations', () => { - - test('nodeKeyGenKES(poolName)', () => { - cardanoJs.nodeKeyGenKES(process.env.TEST_POOL_NAME); - - let keysPath = path.join(cardanoJs.dir, 'priv', 'pool', process.env.TEST_POOL_NAME), - vkey = path.join(keysPath, `${process.env.TEST_POOL_NAME}.kes.vkey`), - skey = path.join(keysPath, `${process.env.TEST_POOL_NAME}.kes.skey`); - - expect(fs.existsSync(vkey)).toBeTruthy(); - expect(fs.existsSync(skey)).toBeTruthy(); - }); - - test('nodeKeyGen(poolName)', () => { - cardanoJs.nodeKeyGen(process.env.TEST_POOL_NAME); - - let keysPath = path.join(cardanoJs.dir, 'priv', 'pool', process.env.TEST_POOL_NAME), - vkey = path.join(keysPath, `${process.env.TEST_POOL_NAME}.node.vkey`), - skey = path.join(keysPath, `${process.env.TEST_POOL_NAME}.node.skey`), - counter = path.join(keysPath, `${process.env.TEST_POOL_NAME}.node.counter`); - - expect(fs.existsSync(vkey)).toBeTruthy(); - expect(fs.existsSync(skey)).toBeTruthy(); - expect(fs.existsSync(counter)).toBeTruthy(); - }); - - test('nodeIssueOpCert(poolName)', () => { - cardanoJs.nodeIssueOpCert(process.env.TEST_POOL_NAME); - - let keysPath = path.join(cardanoJs.dir, 'priv', 'pool', process.env.TEST_POOL_NAME), - cert = path.join(keysPath, `${process.env.TEST_POOL_NAME}.node.cert`); - - expect(fs.existsSync(cert)).toBeTruthy(); - }); - - test('nodeKeyGenVRF(poolName)', () => { - cardanoJs.nodeKeyGenVRF(process.env.TEST_POOL_NAME); - - let keysPath = path.join(cardanoJs.dir, 'priv', 'pool', process.env.TEST_POOL_NAME), - vkey = path.join(keysPath, `${process.env.TEST_POOL_NAME}.vrf.vkey`), - skey = path.join(keysPath, `${process.env.TEST_POOL_NAME}.vrf.skey`); - - expect(fs.existsSync(vkey)).toBeTruthy(); - expect(fs.existsSync(skey)).toBeTruthy(); - }); - - test('stakePoolId(poolName)', () => { - let obj = cardanoJs.stakePoolId(process.env.TEST_POOL_NAME); - - expect(typeof obj).toEqual('string'); - }); - - test('stakePoolMetadataHash(metadata)', () => { - let obj = cardanoJs.stakePoolMetadataHash( - `{ - "name": "Hello World", - "description": "Hello World is the best pool ever!", - "ticker": "HELLO", - "homepage": "https://pool.com/", - "extended": "https://pool.com/ext.json" - }` - ); - - expect(typeof obj).toEqual('string'); - }); - - test('stakePoolRegistrationCertificate(poolName, options)', () => { - let obj = cardanoJs.stakePoolRegistrationCertificate( - process.env.TEST_POOL_NAME, - { - pledge: cardanoJs.toLovelace(100000), - margin: 0.05, - cost: cardanoJs.toLovelace(340), - owners: [cardanoJs.wallet(process.env.TEST_ACCOUNT_NAME).stake.vkey], - rewardAccount: cardanoJs.wallet(process.env.TEST_ACCOUNT_NAME).stake.vkey, - relays: [ - { host: "relay.one.io", port: 3001 }, - { host: "relay.two.io", port: 3001 }, - ], - url: "my-metadata-hash.com", - metaHash: "364261d6b5d896492f1d741303a7568ef0eddade304a5a67d0bf8700a644c6be", - } - ); - - expect(typeof obj).toEqual('string'); - expect(fs.existsSync(obj)).toBeTruthy(); - }); - - test('stakePoolDeregistrationCertificate(poolName, epoch)', () => { - let obj = cardanoJs.stakePoolDeregistrationCertificate(process.env.TEST_POOL_NAME, 0); - - expect(typeof obj).toEqual('string'); - expect(fs.existsSync(obj)).toBeTruthy(); - }); - - test('pool(poolName)', () => { - let obj = cardanoJs.pool(process.env.TEST_POOL_NAME); - - expect(Object.keys(obj).length).toEqual(6); - - expect(obj.hasOwnProperty('name')).toBeTruthy(); - expect(obj.hasOwnProperty('id')).toBeTruthy(); - - expect(obj.hasOwnProperty('kes')).toBeTruthy(); - expect(Object.keys(obj.kes).length).toEqual(2); - expect(obj.kes.hasOwnProperty('skey')).toBeTruthy(); - expect(obj.kes.hasOwnProperty('vkey')).toBeTruthy(); - - expect(obj.hasOwnProperty('node')).toBeTruthy(); - expect(Object.keys(obj.node).length).toEqual(4); - expect(obj.node.hasOwnProperty('cert')).toBeTruthy(); - expect(obj.node.hasOwnProperty('counter')).toBeTruthy(); - expect(obj.node.hasOwnProperty('skey')).toBeTruthy(); - expect(obj.node.hasOwnProperty('vkey')).toBeTruthy(); - - expect(obj.hasOwnProperty('pool')).toBeTruthy(); - expect(Object.keys(obj.pool).length).toEqual(1); - expect(obj.pool.hasOwnProperty('cert')).toBeTruthy(); - - expect(obj.hasOwnProperty('vrf')).toBeTruthy(); - expect(Object.keys(obj.vrf).length).toEqual(2); - expect(obj.vrf.hasOwnProperty('skey')).toBeTruthy(); - expect(obj.vrf.hasOwnProperty('vkey')).toBeTruthy(); - }); -}); - -describe('Transactions Building', () => { - - test('transactionBuildRaw(options)', () => { - let obj = cardanoJs.transactionBuildRaw({ - txIn: [ - { - txHash: '14800565f4adf3cb49ce7a61b26f966f742b8235ce9b6f926bc96773d361bf17', - txId: 0 - }, - { - txHash: '6535fb0b73645d79f05dce69fb148a6f0ce14fea8875bfa44d0f42d493da6951', - txId: 1 - } - ], - txOut: [ - { - address: process.env.TEST_PAYMENT_ADDRESS, - amount: cardanoJs.toLovelace(1000) - }, - { - address: process.env.TEST_PAYMENT_ADDRESS, - amount: cardanoJs.toLovelace(500) - } - ], - certs: [ - cardanoJs.wallet(process.env.TEST_ACCOUNT_NAME).stake.cert, - cardanoJs.pool(process.env.TEST_POOL_NAME).pool.cert - ] - }); - - expect(typeof obj).toEqual('string'); - expect(fs.existsSync(obj)).toBeTruthy(); - - tmpPath = obj; - }); - - test('transactionCalculateMinFee(options)', () => { - let obj = cardanoJs.transactionCalculateMinFee({ - txBody: tmpPath, - txIn: [ - { - txHash: '14800565f4adf3cb49ce7a61b26f966f742b8235ce9b6f926bc96773d361bf17', - txId: 0 - }, - { - txHash: '6535fb0b73645d79f05dce69fb148a6f0ce14fea8875bfa44d0f42d493da6951', - txId: 1 - } - ], - txOut: [ - { - address: process.env.TEST_PAYMENT_ADDRESS, - amount: cardanoJs.toLovelace(1000) - }, - { - address: process.env.TEST_PAYMENT_ADDRESS, - amount: cardanoJs.toLovelace(500) - } - ], - witnessCount: 2 - }); - - expect(typeof obj).toEqual('number'); - }); - - test('transactionSign(options)', () => { - let obj = cardanoJs.transactionSign({ - txBody: tmpPath, - signingKeys: [ - cardanoJs.wallet(process.env.TEST_ACCOUNT_NAME).payment.skey, - cardanoJs.wallet(process.env.TEST_ACCOUNT_NAME).stake.skey - ] - }); - - expect(typeof obj).toEqual('string'); - expect(fs.existsSync(obj)).toBeTruthy(); - - tmpSignedPath = obj; - }); - - test('transactionWitness(options)', () => { - let obj = cardanoJs.transactionWitness({ - txBody: tmpPath, - signingKey: cardanoJs.wallet(process.env.TEST_ACCOUNT_NAME).payment.skey - }); - - expect(typeof obj).toEqual('string'); - expect(fs.existsSync(obj)).toBeTruthy(); - - tmpWitnessPath = obj; - }); - - test('transactionAssemble(options)', () => { - let obj = cardanoJs.transactionAssemble({ - txBody: tmpPath, - witnessFiles: [tmpWitnessPath, tmpWitnessPath] - }); - - expect(typeof obj).toEqual('string'); - expect(fs.existsSync(obj)).toBeTruthy(); - }); - - test('transactionTxid(options)', () => { - let obj1 = cardanoJs.transactionTxid({ - txBody: tmpPath - }); - expect(typeof obj1).toEqual('string'); - - let obj2 = cardanoJs.transactionTxid({ - txFile: tmpSignedPath - }); - expect(typeof obj2).toEqual('string'); - - expect(obj1).toEqual(obj2); - }); - -}); - -function cleanUpTestDirectory() { - rimraf.sync(process.env.TEST_WORKSPACE_DIR); -} diff --git a/tests/assets/addr-script-file.json b/tests/assets/addr-script-file.json new file mode 100644 index 0000000..6bd7d7b --- /dev/null +++ b/tests/assets/addr-script-file.json @@ -0,0 +1,13 @@ +{ + "scripts": [ + { + "keyHash": "e09d36c79dec9bd1b3d9e152247701cd0bb860b5ebfd1de8abb6735a", + "type": "sig" + }, + { + "keyHash": "a687dcc24e00dd3caafbeb5e68f97ca8ef269cb6fe971345eb951756", + "type": "sig" + } + ], + "type": "all" +} diff --git a/tests/assets/shelley-genesis.json b/tests/assets/shelley-genesis.json new file mode 100644 index 0000000..0ff4e17 --- /dev/null +++ b/tests/assets/shelley-genesis.json @@ -0,0 +1,56 @@ +{ + "activeSlotsCoeff": 5.0e-2, + "epochLength": 86400, + "genDelegs": { + "c1ad22cabb342cbb83ce3859708232f4945ccb669e9b5f932cffc0ed": { + "delegate": "405357b552c397e81f73dcb5a0da0828fe29610bd25197d86130df34", + "vrf": "458215df6c07abc66e80082caa7a189dc2f4995ad4b4b5f09481a55d8d0692d2" + }, + "c264bca994a3a5deee5a1d9b92a3d7e9d6cbdb81f2f6989bb7f7b437": { + "delegate": "d9d9d0f0e1f25c4af4d80cb2d62878b611d8b3a8e1ef548d01f246d7", + "vrf": "624f1bf3b2f978e0c95644f26228b307d7acca7fc7eb3d88fb6f107e0aa1198c" + }, + "d4bf7eb45b72dffa5ac33d5c902fe409e4e611f2e9a52fb0d09784c3": { + "delegate": "806eb0c17d9b0fe6d99acbabe7be76ef72bf9de96c5b58435e50837f", + "vrf": "57e52289207a7128c29e0b7e96a02c731a961a5944329b363bed751ad8f377ee" + } + }, + "initialFunds": {}, + "maxKESEvolutions": 62, + "maxLovelaceSupply": 45000000000000000, + "networkId": "Testnet", + "networkMagic": 4, + "protocolParams": { + "a0": 0.3, + "decentralisationParam": 1.0, + "eMax": 18, + "extraEntropy": { + "tag": "NeutralNonce" + }, + "keyDeposit": 2000000, + "maxBlockBodySize": 65536, + "maxBlockHeaderSize": 1100, + "maxTxSize": 16384, + "minFeeA": 44, + "minFeeB": 155381, + "minPoolCost": 340000000, + "minUTxOValue": 1000000, + "nOpt": 150, + "poolDeposit": 500000000, + "protocolVersion": { + "major": 6, + "minor": 0 + }, + "rho": 3.0e-3, + "tau": 0.2 + }, + "securityParam": 432, + "slotLength": 1, + "slotsPerKESPeriod": 129600, + "staking": { + "pools": {}, + "stake": {} + }, + "systemStart": "2023-06-15T00:30:00Z", + "updateQuorum": 3 +} diff --git a/tests/in-order.test.ts b/tests/in-order.test.ts new file mode 100644 index 0000000..35fc112 --- /dev/null +++ b/tests/in-order.test.ts @@ -0,0 +1,258 @@ +import CardanoCliJs from "../index"; +import { CardanoCliJsOptions } from "../lib/cardanoclijs"; +import fs from 'fs'; + +const accountName = "test-account"; + +const options = new CardanoCliJsOptions({ shelleyGenesisPath: `${__dirname}/assets/shelley-genesis.json`, network: "4" }); +const cli = new CardanoCliJs(options); + +let stakeAddrFile = ""; + +describe('Stake Address Commands', () => { + test('stake address key-gen', () => { + const { vkey, skey } = cli.stake_address.keyGen(accountName); + + expect(fs.existsSync(vkey)).toBe(true); + expect(fs.existsSync(skey)).toBe(true); + + // Check if files are not empty + expect(fs.statSync(vkey).size).toBeGreaterThan(0); + expect(fs.statSync(skey).size).toBeGreaterThan(0); + }); + + test('stake address build', () => { + stakeAddrFile = cli.stake_address.build(accountName); + expect(fs.existsSync(stakeAddrFile)).toBe(true); + // Check if files are not empty + expect(fs.statSync(stakeAddrFile).size).toBeGreaterThan(0); + }); + + test('stake address key-hash', () => { + const hash = cli.stake_address.keyHash(accountName); + expect(hash).toBeTruthy(); + }); + + test('stake address registration-certificate', () => { + const certFile = cli.stake_address.registrationCertificate(accountName); + expect(fs.existsSync(certFile)).toBe(true); + // check it is non empty + expect(fs.statSync(certFile).size).toBeGreaterThan(0); + }); + test('stake address deregistration-certificate', () => { + const certFile = cli.stake_address.deRegistrationCertificate(accountName, {}); + expect(fs.existsSync(certFile)).toBe(true); + // check it is non empty + expect(fs.statSync(certFile).size).toBeGreaterThan(0); + }); + test('stake address delegation-certificate', () => { + const certFile = cli.stake_address.delegationCertificate(accountName, { stakePoolId: "pool16agnvfan65ypnswgg6rml52lqtcqe5guxltexkn82sqgj2crqtx" }); + expect(fs.existsSync(certFile)).toBe(true); + // check it is non empty + expect(fs.statSync(certFile).size).toBeGreaterThan(0); + }); +}); + +const poolName = "test-pool"; +let metadataHash = ""; + +describe('Node Commands', () => { + test('node key-gen', () => { + const { vkey, skey, counter } = cli.node.keyGen(poolName); + + expect(fs.existsSync(vkey)).toBe(true); + expect(fs.existsSync(skey)).toBe(true); + expect(fs.existsSync(counter)).toBe(true); + + // Check if files are not empty + expect(fs.statSync(vkey).size).toBeGreaterThan(0); + expect(fs.statSync(skey).size).toBeGreaterThan(0); + expect(fs.statSync(counter).size).toBeGreaterThan(0); + }); + + test('node key-gen-kes', () => { + const { vkey, skey } = cli.node.keyGenKES(poolName); + + expect(fs.existsSync(vkey)).toBe(true); + expect(fs.existsSync(skey)).toBe(true); + + // Check if files are not empty + expect(fs.statSync(vkey).size).toBeGreaterThan(0); + expect(fs.statSync(skey).size).toBeGreaterThan(0); + }); + + test('node key-gen-vrf', () => { + const { vkey, skey } = cli.node.keyGenVRF(poolName); + + expect(fs.existsSync(vkey)).toBe(true); + expect(fs.existsSync(skey)).toBe(true); + + // Check if files are not empty + expect(fs.statSync(vkey).size).toBeGreaterThan(0); + expect(fs.statSync(skey).size).toBeGreaterThan(0); + }); + + test('node key-hash-vrf', () => { + const hash = cli.node.keyHashVRF(poolName); + expect(hash).toBeTruthy(); + }); + + test('node new-counter', () => { + const counterFile = cli.node.newCounter(poolName, 2); + expect(fs.existsSync(counterFile)).toBe(true); + // Check if files are not empty + expect(fs.statSync(counterFile).size).toBeGreaterThan(0); + }); + + test('node issue-op-cert', () => { + const certFile = cli.node.issueOpCert(poolName, {}); + expect(fs.existsSync(certFile)).toBe(true); + // Check if files are not empty + expect(fs.statSync(certFile).size).toBeGreaterThan(0); + }); +}); + +let paymentAddrFile = ""; + +describe('Address Commands', () => { + test('address key-gen', () => { + const { vkey, skey } = cli.address.keyGen(accountName); + + expect(fs.existsSync(vkey)).toBe(true); + expect(fs.existsSync(skey)).toBe(true); + + // Check if files are not empty + expect(fs.statSync(vkey).size).toBeGreaterThan(0); + expect(fs.statSync(skey).size).toBeGreaterThan(0); + }); + + test('address build', () => { + paymentAddrFile = cli.address.build(accountName, {}); + expect(fs.existsSync(paymentAddrFile)).toBe(true); + // Check if files are not empty + expect(fs.statSync(paymentAddrFile).size).toBeGreaterThan(0); + }); + +// test('address build with scripts', () => { +// const scriptFile = `${__dirname}/assets/address-script-file.json`; +// paymentAddrFile = cli.address.build(accountName, { +// paymentScriptFile: scriptFile, +// }); +// expect(fs.existsSync(paymentAddrFile)).toBe(true); +// // Check if files are not empty +// expect(fs.statSync(paymentAddrFile).size).toBeGreaterThan(0); +// }); + + test('address key-hash', () => { + const hash = cli.address.keyHash(accountName); + expect(hash).toBeTruthy(); + }); + + test('address info', () => { + const address = fs.readFileSync(paymentAddrFile, "utf-8"); + const info = cli.address.info(address); + expect(info).toBeTruthy(); + }); +}); + +describe('Stake Pool Commands', () => { + test('stake-pool id', () => { + const id = cli.stake_pool.id(poolName); + expect(id).toBeTruthy(); + }); + + test('stake-pool metadata-hash', () => { + const metadata = { + name: "YourPoolName", + description: "Your pool description", + ticker: "TEST", + homepage: "https://yourpoollink.com" + }; + metadataHash = cli.stake_pool.metadataHash(metadata); + expect(metadataHash).toBeTruthy(); + }); + + test('stake-pool registration-certificate', () => { + const certFile = cli.stake_pool.registrationCertificate( + poolName, + { + pledge: 3, + margin: 0.001, + cost: 5, + url: "https://test.example.com/pool", + metaHash: metadataHash, + rewardAccountFile: `priv/wallet/${accountName}/${accountName}.stake.vkey`, + ownersStakeVKeyFiles: [`priv/wallet/${accountName}/${accountName}.stake.vkey`], + relays: [{ host: "some-node.com", port: 1111 }], + }, + ); + + expect(fs.existsSync(certFile)).toBe(true); + // Check if files are not empty + expect(fs.statSync(certFile).size).toBeGreaterThan(0); + }); + + test('stake-pool deregistration-certificate', () => { + const certFile = cli.stake_pool.deRegistrationCertificate(poolName, 30); + expect(fs.existsSync(certFile)).toBe(true); + // Check if files are not empty + expect(fs.statSync(certFile).size).toBeGreaterThan(0); + }); +}); + +let rawTxFilePath = ""; +let txFilePath = ""; +let minFee = 0; + +describe('Transaction Commands', () => { + test('transcation build-raw', () => { + // NOTE: We use a mock transaction for the tests, we won't submit it + rawTxFilePath = cli.transaction.buildRaw([ + { name: "tx-in", value: "4e3a6e7fdcb0d0efa17bf79c13aed2b4cb9baf37fb1aa2e39553d5bd720c5c99#4" }, + { name: "tx-out", value: "addr_test1wr64gtafm8rpkndue4ck2nx95u4flhwf643l2qmg9emjajg2ww0nj+0" }, + { name: "tx-out", value: "addr_test1wr64gtafm8rpkndue4ck2nx95u4flhwf643l2qmg9emjajg2ww0nj+0" }, + ]); + + expect(fs.existsSync(rawTxFilePath)).toBe(true); + // Check if files are not empty + expect(fs.statSync(rawTxFilePath).size).toBeGreaterThan(0); + }); + + test('transaction calculate-min-fee', () => { + // NOTE: We use the txInCount, txOutCount and witnessCount based on the TX hardcoded above + minFee = cli.transaction.calculateMinFee(rawTxFilePath, 1, 2, 1); + expect(minFee).toBeTruthy(); + }); + + test('transaction build', () => { + // NOTE: We use a mock transaction for the tests, we won't submit it + // TODO: this is an unstable test as it depends on the address below and that the UTxOs do not contain assets in the future + const utxos = cli.query.utxo("addr_test1qz63slx7l0zmf8wuz76z8sre7pchwgdq8zp28vn9h4xp9cyq7vn27vqzw0ge6kj5r4zm4fpwhszzvkqkwddsy66lxjjqxnc9zk"); + const utxoIds = Object.keys(utxos); + txFilePath = cli.transaction.build([ + { name: "tx-in", value: utxoIds[0] }, + { name: "tx-out", value: "addr_test1qz63slx7l0zmf8wuz76z8sre7pchwgdq8zp28vn9h4xp9cyq7vn27vqzw0ge6kj5r4zm4fpwhszzvkqkwddsy66lxjjqxnc9zk+1000000" }, + { name: "change-address", value: "addr_test1qz63slx7l0zmf8wuz76z8sre7pchwgdq8zp28vn9h4xp9cyq7vn27vqzw0ge6kj5r4zm4fpwhszzvkqkwddsy66lxjjqxnc9zk" }, + ]); + + expect(fs.existsSync(txFilePath)).toBe(true); + // Check if files are not empty + expect(fs.statSync(txFilePath).size).toBeGreaterThan(0); + }); + + test('transaction sign', () => { + // NOTE: the sKey does not really belong to the hash hardcoded above, just used to test the sign command + const minFee = cli.transaction.sign(txFilePath, ['priv/wallet/test-account/test-account.payment.skey']); + expect(minFee).toBeTruthy(); + }); + + test('transaction tx-id', () => { + const id = cli.transaction.txId({ txFile: txFilePath }); + expect(id).toBeTruthy(); + }); + + test('transaction view', () => { + const view = cli.transaction.txId({ txFile: txFilePath }); + expect(view).toBeTruthy(); + }); +}); diff --git a/tests/query.test.ts b/tests/query.test.ts new file mode 100644 index 0000000..987898c --- /dev/null +++ b/tests/query.test.ts @@ -0,0 +1,47 @@ +import CardanoCliJs from "../index"; +import { CardanoCliJsOptions } from "../lib/cardanoclijs"; + +const accountName = "test-account"; + +const options = new CardanoCliJsOptions({ shelleyGenesisPath: `${__dirname}/assets/shelley-genesis.json`, network: "4" }); +const cli = new CardanoCliJs(options); + +describe('Query Commands', () => { + + test('query protocol-parameters', () => { + let obj = cli.query.protocolParameters(); + + // Just check one field, no need to check all of them + expect(obj.hasOwnProperty('stakePoolDeposit')).toBeTruthy(); + }); + + test('query tip', () => { + const tip = cli.query.tip(); + expect(tip).toBeTruthy(); + }); + + test('query stake-address-info', () => { + const tip = cli.query.stakeAddressInfo("stake1u94zcxqmhq2vq7nlekdq2rgklmzqkm6g886f0sfjuc0htvcsw9z7c"); + expect(tip).toBeTruthy(); + }); + + test('query utxo', () => { + const utxos = cli.query.utxo("addr_test1qz63slx7l0zmf8wuz76z8sre7pchwgdq8zp28vn9h4xp9cyq7vn27vqzw0ge6kj5r4zm4fpwhszzvkqkwddsy66lxjjqxnc9zk"); + expect(utxos).toBeTruthy(); + }); + + test('query gov-state', () => { + const govState = cli.query.govState(); + expect(govState).toBeTruthy(); + }); + + test('query drep-state', () => { + const state = cli.query.drepState({}); + expect(state).toBeTruthy(); + }); + + test('query drep-stake-distribution', () => { + const stake = cli.query.drepStakeDistribution({}); + expect(stake).toBeTruthy(); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e075f97 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}