diff --git a/.changeset/two-dragons-pretend.md b/.changeset/two-dragons-pretend.md new file mode 100644 index 0000000000..971a6aa2f7 --- /dev/null +++ b/.changeset/two-dragons-pretend.md @@ -0,0 +1,8 @@ +--- +'@kadena/client': patch +--- + +Expose two new functions: +- `getHostUrl` to use with `@kadena/client-utils` package +- `submitOne` to make piping easier. As the piped arguments can be ambiguous + (array or single transaction) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index a24c9eba10..969801cd3b 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -37,7 +37,10 @@ jobs: run: pnpm install - name: Build client and dependencies - run: pnpm turbo build --filter @kadena/client + run: pnpm turbo build --filter @kadena/client-utils - - name: run integration tests + - name: run client integration tests run: pnpm run test:integration --filter @kadena/client + + - name: run client-utils integration tests + run: pnpm run test:integration --filter @kadena/client-utils diff --git a/packages/libs/client-examples/.eslintrc.js b/packages/libs/client-examples/.eslintrc.js index b2c1cd5129..b16df78311 100644 --- a/packages/libs/client-examples/.eslintrc.js +++ b/packages/libs/client-examples/.eslintrc.js @@ -3,5 +3,9 @@ require('@rushstack/eslint-config/patch/modern-module-resolution'); module.exports = { extends: ['@kadena-dev/eslint-config/profile/lib'], + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + '@rushstack/typedef-var': 'off', + }, parserOptions: { tsconfigRootDir: __dirname }, }; diff --git a/packages/libs/client-examples/package.json b/packages/libs/client-examples/package.json index 2b38c4e3be..90ab19934d 100644 --- a/packages/libs/client-examples/package.json +++ b/packages/libs/client-examples/package.json @@ -30,6 +30,7 @@ "dependencies": { "@kadena/chainweb-node-client": "workspace:*", "@kadena/client": "workspace:*", + "@kadena/client-utils": "workspace:*", "@kadena/pactjs": "workspace:*" }, "devDependencies": { diff --git a/packages/libs/client-examples/src/example-contract/functional/crosschain-transfer-fp.ts b/packages/libs/client-examples/src/example-contract/functional/crosschain-transfer-fp.ts index 11d97608ea..1b3dd68c13 100644 --- a/packages/libs/client-examples/src/example-contract/functional/crosschain-transfer-fp.ts +++ b/packages/libs/client-examples/src/example-contract/functional/crosschain-transfer-fp.ts @@ -5,6 +5,7 @@ import { readKeyset, signWithChainweaver, } from '@kadena/client'; +import { asyncPipe } from '@kadena/client-utils/core'; import { addKeyset, addSigner, @@ -16,8 +17,9 @@ import { } from '@kadena/client/fp'; import { isSignedCommand } from '@kadena/pactjs'; import type { ChainId } from '@kadena/types'; -import { listen, pollCreateSpv, submit } from '../util/client'; -import { asyncPipe, inspect } from '../util/fp-helpers'; + +import { listen, pollCreateSpv, submitOne } from '../util/client'; +import { inspect } from '../util/fp-helpers'; import { keyFromAccount } from '../util/keyFromAccount'; interface IAccount { @@ -38,7 +40,6 @@ const receiverAccount: string = const NETWORK_ID: string = 'testnet04'; -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type function startInTheFirstChain(from: IAccount, to: IAccount, amount: string) { return composePactCommand( execution( @@ -71,7 +72,6 @@ function startInTheFirstChain(from: IAccount, to: IAccount, amount: string) { ); } -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type const finishInTheTargetChain = ( targetChainId: ChainId, gasPayer: string = 'kadena-xchain-gas', @@ -99,19 +99,17 @@ const finishInTheTargetChain = ( ); }; -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type const rejectIfFailed = (message: string) => (response: ICommandResult) => response.result.status === 'failure' ? Promise.reject(new Error(message)) : response; -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type async function doCrossChainTransfer( from: IAccount, to: IAccount, amount: string, ) { - return asyncPipe( + const debit = asyncPipe( startInTheFirstChain(from, to, amount), createTransaction, inspect('TX_CREATED'), @@ -119,14 +117,17 @@ async function doCrossChainTransfer( inspect('TX_SIGNED'), (command) => isSignedCommand(command) ? command : Promise.reject('CMD_NOT_SIGNED'), - submit, + submitOne, inspect('TX_SUBMITTED'), listen, inspect('TX_RESULT'), rejectIfFailed('DEBIT_REJECTED'), + ); + + const credit = asyncPipe( (status: ICommandResult) => Promise.all([ - status.continuation?.pactId, + status.continuation!.pactId, pollCreateSpv( { requestKey: status.reqKey, @@ -135,17 +136,19 @@ async function doCrossChainTransfer( }, to.chainId, ), - ]), + ] as const), inspect('SPV_CREATED'), finishInTheTargetChain(to.chainId), createTransaction, inspect('CONT_CREATED'), - submit, + submitOne, inspect('CONT_SUBMITTED'), listen, inspect('CONT_RESULT'), rejectIfFailed('CREDIT REJECTED'), - )({}); + ); + + return asyncPipe(debit, credit)({}); } const from: IAccount = { diff --git a/packages/libs/client-examples/src/example-contract/functional/transfer-fp.ts b/packages/libs/client-examples/src/example-contract/functional/transfer-fp.ts index ec3ebcf572..1dd0040e48 100644 --- a/packages/libs/client-examples/src/example-contract/functional/transfer-fp.ts +++ b/packages/libs/client-examples/src/example-contract/functional/transfer-fp.ts @@ -1,10 +1,6 @@ import type { ChainId } from '@kadena/client'; -import { - createTransaction, - isSignedTransaction, - Pact, - signWithChainweaver, -} from '@kadena/client'; +import { createTransaction, Pact, signWithChainweaver } from '@kadena/client'; +import { asyncPipe } from '@kadena/client-utils/core'; import { addSigner, composePactCommand, @@ -12,10 +8,10 @@ import { setMeta, setNetworkId, } from '@kadena/client/fp'; -import { pollStatus, preflight, submit } from '../util/client'; -import { asyncPipe, inspect } from '../util/fp-helpers'; -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +import { pollStatus, submitOne } from '../util/client'; +import { inspect, safeSign } from '../util/fp-helpers'; + const getTransferCommand = ({ sender, receiver, @@ -45,7 +41,6 @@ const getTransferCommand = ({ setNetworkId(networkId), ); -// eslint-disable-next-line @rushstack/typedef-var const doTransfer = asyncPipe( // you can edit the command form the input or complete it if it needs more information // for example hear we add gasLimit and gasPrice @@ -57,13 +52,8 @@ const doTransfer = asyncPipe( ), inspect('command'), createTransaction, - signWithChainweaver, - (tr) => (isSignedTransaction(tr) ? tr : Promise.reject('TR_NOT_SIGNED')), - // do preflight first to check if everything is ok without paying gas - (tr) => preflight(tr).then((res) => [tr, res]), - ([tr, res]) => (res.result.status === 'success' ? tr : Promise.reject(res)), - // submit the tr if the preflight is ok - submit, + safeSign(signWithChainweaver), + submitOne, pollStatus, ); diff --git a/packages/libs/client-examples/src/example-contract/util/client.ts b/packages/libs/client-examples/src/example-contract/util/client.ts index 4da0839b35..d312851da4 100644 --- a/packages/libs/client-examples/src/example-contract/util/client.ts +++ b/packages/libs/client-examples/src/example-contract/util/client.ts @@ -1,5 +1,6 @@ +import type { ITransactionDescriptor } from '@kadena/client'; import { createClient } from '@kadena/client'; -import type { ChainId } from '@kadena/types'; +import type { ChainId, ICommand, IUnsignedCommand } from '@kadena/types'; // you can edit this function if you want to use different network like dev-net or a private net export const apiHostGenerator = ({ @@ -37,3 +38,7 @@ export const { getStatus, createSpv, } = createClient(apiHostGenerator); + +export const submitOne = async ( + transaction: ICommand | IUnsignedCommand, +): Promise => submit(transaction as ICommand); diff --git a/packages/libs/client-examples/src/example-contract/util/fp-helpers.ts b/packages/libs/client-examples/src/example-contract/util/fp-helpers.ts index 1d3042995f..0401047831 100644 --- a/packages/libs/client-examples/src/example-contract/util/fp-helpers.ts +++ b/packages/libs/client-examples/src/example-contract/util/fp-helpers.ts @@ -1,17 +1,36 @@ -// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any -type all = any; - -// pipe async functions -export const asyncPipe = - (...args: Array<(arg: all) => all>): ((init: all) => Promise) => - (init: all): Promise => - args.reduce((chain, fn) => chain.then(fn), Promise.resolve(init)); - -export const head = (args: all[]): any => args[0]; +import { isSignedTransaction } from '@kadena/client'; +import type { ICommand, IUnsignedCommand } from '@kadena/types'; export const inspect = - (tag: string): ((data: T) => T) => - (data: T): T => { + (tag: string) => + (data: T): T => { console.log(tag, data); return data; }; + +export const validateSign = ( + tx: IUnsignedCommand, + signedTx: ICommand | IUnsignedCommand, +): ICommand => { + const { sigs, hash } = signedTx; + const txWithSigs = { ...tx, sigs }; + if (txWithSigs.hash !== hash) { + throw new Error('Hash mismatch'); + } + if (!isSignedTransaction(txWithSigs)) { + throw new Error('Signing failed'); + } + return txWithSigs; +}; + +export const safeSign = + ( + sign: ( + transaction: IUnsignedCommand, + ) => Promise, + ) => + async (tx: IUnsignedCommand) => { + if (tx.sigs.length === 0) return tx as ICommand; + const signedTx = await sign(tx); + return validateSign(tx, signedTx); + }; diff --git a/packages/libs/client-utils/.eslintrc.js b/packages/libs/client-utils/.eslintrc.js new file mode 100644 index 0000000000..10967f20b9 --- /dev/null +++ b/packages/libs/client-utils/.eslintrc.js @@ -0,0 +1,11 @@ +// This is a workaround for https://github.com/eslint/eslint/issues/3458 +require('@rushstack/eslint-config/patch/modern-module-resolution'); + +module.exports = { + extends: ['@kadena-dev/eslint-config/profile/lib'], + parserOptions: { tsconfigRootDir: __dirname }, + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + '@rushstack/typedef-var': 'off', + }, +}; diff --git a/packages/libs/client-utils/.gitignore b/packages/libs/client-utils/.gitignore new file mode 100644 index 0000000000..1b00d21fdf --- /dev/null +++ b/packages/libs/client-utils/.gitignore @@ -0,0 +1 @@ +contracts/* diff --git a/packages/libs/client-utils/.prettierignore b/packages/libs/client-utils/.prettierignore new file mode 100644 index 0000000000..c009d92878 --- /dev/null +++ b/packages/libs/client-utils/.prettierignore @@ -0,0 +1,6 @@ +dist +etc +lib +temp +**/*.md +tsdoc-metadata.json diff --git a/packages/libs/client-utils/README.md b/packages/libs/client-utils/README.md new file mode 100644 index 0000000000..e28151fc7b --- /dev/null +++ b/packages/libs/client-utils/README.md @@ -0,0 +1,82 @@ + + +# @kadena/client-utils + +Utility functions build as a wrapper around @kadena/client + + + + kadena.js logo + + + + +## Kadena client utils + +Introducing `@kadena/client-utils`, a library that aims to provide a +higher-level API for interacting with smart contracts. This PR includes helpers +for the `coin` module, which can be imported using `@kadena/client-utils/coin`. +The library also exports utilities under `/core` for smart contract developers +to develop APIs, including some functions that can be used for any kind of smart +contracts. + +- asyncPipe +- submitClient +- preflightClient +- dirtyReadClient +- crossChainClient + +examples + +```TS +import { getBalance, transferCrossChain } from "@kadena/client-utils/coin" +import { signWithChainweaver } from "@kadena/client" + +const balance = await getBalance( + accountOne.account, + 'fast-development', + '0', + 'http://localhost:8080', + ); + + const result = await createAccount( + { + account: 'javad', + keyset: { + pred: 'keys-all', + keys: ['key-a', 'key-b'], + }, + gasPayer: { account: 'gasPayer', publicKeys: [''] }, + chainId: '0', + }, + { + host: 'https://api.testnet.chainweb.com', + defaults: { + networkId: 'testnet04', + }, + sign: signWithChainweaver, + }, +) + // signed Tx + .on('sign', (data) => console.log(data)) + // preflight result + .on('preflight', (data) => console.log(data)) + // submit result + .on('submit', (data) => console.log(data)) + // listen result + .on('listen', (data) => console.log(data)) + .execute(); +``` + +### Future work + +- `npx create @kadena/client-utils` + + - to allow community members to create their own interfaces for their + smart-contracts + +- @kadena/client-utils/ + - faucet + - marmalade + - principles + - namespace diff --git a/packages/libs/client-utils/config/api-extractor-coin.json b/packages/libs/client-utils/config/api-extractor-coin.json new file mode 100644 index 0000000000..f681a5a8d2 --- /dev/null +++ b/packages/libs/client-utils/config/api-extractor-coin.json @@ -0,0 +1,376 @@ +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + // "projectFolder": "..", + + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "/lib/coin/index.d.ts", + + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we can specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + */ + "bundledPackages": [], + + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + // "tsconfigFilePath": "/tsconfig.json", + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + // "overrideTsconfig": { + // . . . + // } + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true, + }, + + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": true, + + /** + * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce + * a full file path. + * + * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: ".api.md" + */ + "reportFileName": "client-utils-coin.api.md" + + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportFolder": "/temp/", + + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportTempFolder": "/temp/" + }, + + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": true, + + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + "apiJsonFilePath": "/temp/client-utils-coin.api.json" + }, + + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": true, + + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + "untrimmedFilePath": "/temp/client-utils-coin.d.ts" + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. + * This file will include only declarations that are marked as "@public", "@beta", or "@alpha". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "alphaTrimmedFilePath": "/dist/-alpha.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "publicTrimmedFilePath": "/dist/-public.d.ts", + + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + // "enabled": true, + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + /** + * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files + * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. + * To use the OS's default newline kind, specify "os". + * + * DEFAULT VALUE: "crlf" + */ + // "newlineKind": "crlf", + + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // "addToApiReportFile": false + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "ae-extra-release-tag": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/packages/libs/client-utils/config/api-extractor-core.json b/packages/libs/client-utils/config/api-extractor-core.json new file mode 100644 index 0000000000..a7544d188a --- /dev/null +++ b/packages/libs/client-utils/config/api-extractor-core.json @@ -0,0 +1,376 @@ +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + // "projectFolder": "..", + + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "/lib/core/index.d.ts", + + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we can specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + */ + "bundledPackages": [], + + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + // "tsconfigFilePath": "/tsconfig.json", + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + // "overrideTsconfig": { + // . . . + // } + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true, + }, + + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": true, + + /** + * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce + * a full file path. + * + * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: ".api.md" + */ + "reportFileName": "client-utils-core.api.md" + + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportFolder": "/temp/", + + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportTempFolder": "/temp/" + }, + + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": true, + + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + "apiJsonFilePath": "/temp/client-utils-core.api.json" + }, + + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": true, + + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + "untrimmedFilePath": "/temp/client-utils-core.d.ts" + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. + * This file will include only declarations that are marked as "@public", "@beta", or "@alpha". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "alphaTrimmedFilePath": "/dist/-alpha.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "publicTrimmedFilePath": "/dist/-public.d.ts", + + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + // "enabled": true, + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + /** + * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files + * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. + * To use the OS's default newline kind, specify "os". + * + * DEFAULT VALUE: "crlf" + */ + // "newlineKind": "crlf", + + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // "addToApiReportFile": false + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "ae-extra-release-tag": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/packages/libs/client-utils/config/rig.json b/packages/libs/client-utils/config/rig.json new file mode 100644 index 0000000000..8993b4048c --- /dev/null +++ b/packages/libs/client-utils/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "@kadena-dev/heft-rig" +} diff --git a/packages/libs/client-utils/etc/client-utils-coin.api.md b/packages/libs/client-utils/etc/client-utils-coin.api.md new file mode 100644 index 0000000000..e7d0985b5d --- /dev/null +++ b/packages/libs/client-utils/etc/client-utils-coin.api.md @@ -0,0 +1,135 @@ +## API Report File for "@kadena/client-utils" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { ChainId } from '@kadena/client'; +import { ICommand } from '@kadena/types'; +import { ICommandResult } from '@kadena/chainweb-node-client'; +import { ILocalCommandResult } from '@kadena/chainweb-node-client'; +import type { INetworkOptions } from '@kadena/client'; +import type { IPactCommand } from '@kadena/client'; +import { IPactDecimal } from '@kadena/types'; +import { IPactInt } from '@kadena/types'; +import type { ISignFunction } from '@kadena/client'; +import { ITransactionDescriptor } from '@kadena/client'; +import { PactValue } from '@kadena/types'; + +// Warning: (ae-forgotten-export) The symbol "ICreateAccountCommandInput" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "IClientConfig" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "IEmitterWrapper" needs to be exported by the entry point index.d.ts +// +// @alpha (undocumented) +export const createAccount: (inputs: ICreateAccountCommandInput, config: IClientConfig) => IEmitterWrapper<[{ +event: "sign"; +data: ICommand; +}, { +event: "preflight"; +data: ILocalCommandResult; +}, { +event: "submit"; +data: ITransactionDescriptor; +}, { +event: "listen"; +data: ICommandResult; +}], [], Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise>; + +// @alpha (undocumented) +export const details: (account: string, networkId: string, chainId: ChainId, host?: IClientConfig['host']) => Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise; + +// @alpha (undocumented) +export const getBalance: (account: string, networkId: string, chainId: ChainId, host?: IClientConfig['host']) => Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise; + +// Warning: (ae-forgotten-export) The symbol "IRotateCommandInput" needs to be exported by the entry point index.d.ts +// +// @alpha (undocumented) +export const rotate: (inputs: IRotateCommandInput, config: IClientConfig) => IEmitterWrapper<[{ +event: "sign"; +data: ICommand; +}, { +event: "preflight"; +data: ILocalCommandResult; +}, { +event: "submit"; +data: ITransactionDescriptor; +}, { +event: "listen"; +data: ICommandResult; +}], [], Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise>; + +// Warning: (ae-forgotten-export) The symbol "ITransferInput" needs to be exported by the entry point index.d.ts +// +// @alpha (undocumented) +export const transfer: (inputs: ITransferInput, config: IClientConfig) => IEmitterWrapper<[{ +event: "sign"; +data: ICommand; +}, { +event: "preflight"; +data: ILocalCommandResult; +}, { +event: "submit"; +data: ITransactionDescriptor; +}, { +event: "listen"; +data: ICommandResult; +}], [], Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise>; + +// Warning: (ae-forgotten-export) The symbol "ICreateTransferInput" needs to be exported by the entry point index.d.ts +// +// @alpha (undocumented) +export const transferCreate: (inputs: ICreateTransferInput, config: IClientConfig) => IEmitterWrapper<[{ +event: "sign"; +data: ICommand; +}, { +event: "preflight"; +data: ILocalCommandResult; +}, { +event: "submit"; +data: ITransactionDescriptor; +}, { +event: "listen"; +data: ICommandResult; +}], [], Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise>; + +// Warning: (ae-forgotten-export) The symbol "ICrossChainInput" needs to be exported by the entry point index.d.ts +// +// @alpha (undocumented) +export const transferCrossChain: (inputs: ICrossChainInput, config: IClientConfig) => IEmitterWrapper<[{ +event: "sign"; +data: ICommand; +}, { +event: "preflight"; +data: ILocalCommandResult; +}, { +event: "submit"; +data: ITransactionDescriptor; +}, { +event: "listen"; +data: ICommandResult; +}, { +event: "spv-proof"; +data: { +pactId?: string | undefined; +step?: number | undefined; +rollback?: boolean | undefined; +data?: Record | undefined; +proof?: string | undefined; +}; +}, { +event: "sign" | "gas-station"; +data: ICommand; +}, { +event: "submit-continuation"; +data: ITransactionDescriptor; +}, { +event: "listen-continuation"; +data: ICommandResult; +}], [{ +event: "poll-spv"; +data: string; +}], Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise>; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/libs/client-utils/etc/client-utils-core.api.md b/packages/libs/client-utils/etc/client-utils-core.api.md new file mode 100644 index 0000000000..835818cc7f --- /dev/null +++ b/packages/libs/client-utils/etc/client-utils-core.api.md @@ -0,0 +1,98 @@ +## API Report File for "@kadena/client-utils" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { ChainId } from '@kadena/client'; +import { IClient } from '@kadena/client'; +import { ICommand } from '@kadena/types'; +import { ICommandResult } from '@kadena/chainweb-node-client'; +import { ILocalCommandResult } from '@kadena/chainweb-node-client'; +import type { INetworkOptions } from '@kadena/client'; +import type { IPactCommand } from '@kadena/client'; +import { IPactCommand as IPactCommand_2 } from '@kadena/client/lib/interfaces/IPactCommand'; +import { IPactDecimal } from '@kadena/types'; +import { IPactInt } from '@kadena/types'; +import type { ISignFunction } from '@kadena/client'; +import { ITransactionDescriptor } from '@kadena/client'; +import { PactValue } from '@kadena/types'; + +// Warning: (ae-forgotten-export) The symbol "IAsyncPipe" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export const asyncPipe: IAsyncPipe; + +// Warning: (ae-forgotten-export) The symbol "IClientConfig" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "IAccount" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "IEmitterWrapper" needs to be exported by the entry point index.d.ts +// +// @alpha (undocumented) +export const crossChainClient: (args_0: IClientConfig, args_1?: IClient | undefined) => (targetChainId: ChainId, targetChainGasPayer: IAccount) => (cmd?: (Partial | (() => Partial)) | undefined) => IEmitterWrapper<[{ +event: "sign"; +data: ICommand; +}, { +event: "preflight"; +data: ILocalCommandResult; +}, { +event: "submit"; +data: ITransactionDescriptor; +}, { +event: "listen"; +data: ICommandResult; +}, { +event: "spv-proof"; +data: { +pactId?: string | undefined; +step?: number | undefined; +rollback?: boolean | undefined; +data?: Record | undefined; +proof?: string | undefined; +}; +}, { +event: "sign" | "gas-station"; +data: ICommand; +}, { +event: "submit-continuation"; +data: ITransactionDescriptor; +}, { +event: "listen-continuation"; +data: ICommandResult; +}], [{ +event: 'poll-spv'; +data: string; +}], Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise>; + +// @alpha (undocumented) +export const dirtyReadClient: (args_0: Omit, args_1?: IClient | undefined) => (cmd?: (Partial | (() => Partial)) | undefined) => IEmitterWrapper<[{ +event: "dirtyRead"; +data: ICommandResult; +}], [], Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise>; + +// @alpha (undocumented) +export const preflightClient: (args_0: IClientConfig, args_1?: IClient | undefined) => (cmd?: (Partial | (() => Partial)) | undefined) => IEmitterWrapper<[{ +event: "sign"; +data: ICommand; +}, { +event: "preflight"; +data: ILocalCommandResult; +}], [], Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise>; + +// @alpha (undocumented) +export const submitClient: (args_0: IClientConfig, args_1?: IClient | undefined) => (cmd?: (Partial | (() => Partial)) | undefined) => IEmitterWrapper<[{ +event: "sign"; +data: ICommand; +}, { +event: "preflight"; +data: ILocalCommandResult; +}, { +event: "submit"; +data: ITransactionDescriptor; +}, { +event: "listen"; +data: ICommandResult; +}], [], Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise | Promise>; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/libs/client-utils/package.json b/packages/libs/client-utils/package.json new file mode 100644 index 0000000000..9b6a099ff9 --- /dev/null +++ b/packages/libs/client-utils/package.json @@ -0,0 +1,80 @@ +{ + "name": "@kadena/client-utils", + "version": "0.0.1", + "description": "Utility functions build as a wrapper around @kadena/client", + "repository": { + "type": "git", + "url": "https://github.com/kadena-community/kadena.js.git", + "directory": "packages/libs/client-utils" + }, + "license": "MIT", + "exports": { + "./coin": "./lib/coin/index.js", + "./core": "./lib/core/index.js" + }, + "main": "./lib/core/index.js", + "typesVersions": { + "*": { + "core": [ + "./lib/core/index.d.ts" + ], + "coin": [ + "./lib/coin/index.d.ts" + ] + } + }, + "files": [ + "dist", + "lib", + "coin", + "core", + "src" + ], + "scripts": { + "ae": "pnpm run ae:core && pnpm run ae:coin", + "ae:coin": "api-extractor run --verbose -c ./config/api-extractor-coin.json", + "ae:core": "api-extractor run --verbose -c ./config/api-extractor-core.json", + "build": "pnpm run pactjs:generate:contract && pnpm run generate-pipe-type && tsc && pnpm run ae", + "dev:ae:coin": "api-extractor run --local --verbose -c ./config/api-extractor-coin.json", + "dev:ae:core": "api-extractor run --local --verbose -c ./config/api-extractor-core.json", + "dev:postinstall": "pnpm run pactjs:generate:contract", + "format": "pnpm run /^format:.*/", + "format:lint": "pnpm run lint:src --fix", + "format:md": "remark README.md -o --use @kadena-dev/markdown", + "format:src": "prettier . --cache --write", + "generate-pipe-type": "ts-node src/scripts/create-async-pipe-type.ts 30", + "lint": "pnpm run /^lint:.*/", + "lint:fmt": "prettier . --cache --check", + "lint:pkg": "lint-package", + "lint:src": "eslint src --ext .js,.ts", + "pactjs:generate:contract": "pactjs contract-generate --contract coin --api https://api.chainweb.com/chainweb/0.0/mainnet01/chain/1/pact", + "test": "vitest", + "test:integration": "vitest run -c ./vitest.integration.config.ts" + }, + "dependencies": { + "@kadena/chainweb-node-client": "workspace:*", + "@kadena/client": "workspace:*", + "@kadena/cryptography-utils": "workspace:*", + "@kadena/types": "workspace:*", + "debug": "~4.3.4", + "ramda": "^0.29.0" + }, + "devDependencies": { + "@kadena-dev/eslint-config": "workspace:*", + "@kadena-dev/heft-rig": "workspace:*", + "@kadena-dev/lint-package": "workspace:*", + "@kadena-dev/markdown": "workspace:*", + "@kadena/pactjs-cli": "workspace:^", + "@microsoft/api-extractor": "^7.38.0", + "@rushstack/eslint-config": "~3.3.0", + "@rushstack/heft": "~0.50.6", + "@types/debug": "~4.1.7", + "@types/node": "^18.17.14", + "@types/ramda": "^0.29.5", + "eslint": "^8.45.0", + "prettier": "~3.0.3", + "prettier-plugin-packagejson": "^2.4.6", + "ts-node": "~10.8.2", + "vitest": "^0.34.6" + } +} diff --git a/packages/libs/client-utils/src/coin/create-account.ts b/packages/libs/client-utils/src/coin/create-account.ts new file mode 100644 index 0000000000..1248b54440 --- /dev/null +++ b/packages/libs/client-utils/src/coin/create-account.ts @@ -0,0 +1,44 @@ +import type { ChainId } from '@kadena/client'; +import { Pact, readKeyset } from '@kadena/client'; +import { + addKeyset, + addSigner, + composePactCommand, + execution, + setMeta, +} from '@kadena/client/fp'; + +import { submitClient } from '../core/client-helpers'; +import type { IClientConfig } from '../core/utils/helpers'; + +interface ICreateAccountCommandInput { + account: string; + keyset: { + keys: string[]; + pred: 'keys-all' | 'keys-two' | 'keys-one'; + }; + gasPayer: { account: string; publicKeys: string[] }; + chainId: ChainId; +} + +const createAccountCommand = ({ + account, + keyset, + gasPayer, + chainId, +}: ICreateAccountCommandInput) => + composePactCommand( + execution( + Pact.modules.coin['create-account'](account, readKeyset('account-guard')), + ), + addKeyset('account-guard', keyset.pred, ...keyset.keys), + addSigner(gasPayer.publicKeys, (signFor) => [signFor('coin.GAS')]), + setMeta({ senderAccount: gasPayer.account, chainId }), + ); +/** + * @alpha + */ +export const createAccount = ( + inputs: ICreateAccountCommandInput, + config: IClientConfig, +) => submitClient(config)(createAccountCommand(inputs)); diff --git a/packages/libs/client-utils/src/coin/details.ts b/packages/libs/client-utils/src/coin/details.ts new file mode 100644 index 0000000000..124fb953fa --- /dev/null +++ b/packages/libs/client-utils/src/coin/details.ts @@ -0,0 +1,31 @@ +import type { ChainId } from '@kadena/client'; +import { Pact } from '@kadena/client'; +import { execution } from '@kadena/client/fp'; + +import { dirtyReadClient } from '../core/client-helpers'; +import type { IClientConfig } from '../core/utils/helpers'; + +import pipe from 'ramda/es/pipe'; + +/** + * @alpha + */ +export const details = ( + account: string, + networkId: string, + chainId: ChainId, + host?: IClientConfig['host'], +) => { + const getDetails = pipe( + (name) => Pact.modules.coin.details(name), + execution, + dirtyReadClient({ + host, + defaults: { + networkId, + meta: { chainId }, + }, + }), + ); + return getDetails(account).execute(); +}; diff --git a/packages/libs/client-utils/src/coin/get-balance.ts b/packages/libs/client-utils/src/coin/get-balance.ts new file mode 100644 index 0000000000..20a939bc85 --- /dev/null +++ b/packages/libs/client-utils/src/coin/get-balance.ts @@ -0,0 +1,31 @@ +import type { ChainId } from '@kadena/client'; +import { Pact } from '@kadena/client'; +import { execution } from '@kadena/client/fp'; + +import { dirtyReadClient } from '../core/client-helpers'; +import type { IClientConfig } from '../core/utils/helpers'; + +import pipe from 'ramda/es/pipe'; + +/** + * @alpha + */ +export const getBalance = ( + account: string, + networkId: string, + chainId: ChainId, + host?: IClientConfig['host'], +) => { + const balance = pipe( + (name) => Pact.modules.coin['get-balance'](name), + execution, + dirtyReadClient({ + host, + defaults: { + networkId, + meta: { chainId }, + }, + }), + ); + return balance(account).execute(); +}; diff --git a/packages/libs/client-utils/src/coin/index.ts b/packages/libs/client-utils/src/coin/index.ts new file mode 100644 index 0000000000..51bfef15d4 --- /dev/null +++ b/packages/libs/client-utils/src/coin/index.ts @@ -0,0 +1,7 @@ +export * from './create-account'; +export * from './details'; +export * from './get-balance'; +export * from './rotate'; +export * from './transfer'; +export * from './transfer-create'; +export * from './transfer-crosschain'; diff --git a/packages/libs/client-utils/src/coin/rotate.ts b/packages/libs/client-utils/src/coin/rotate.ts new file mode 100644 index 0000000000..6eed44ae3f --- /dev/null +++ b/packages/libs/client-utils/src/coin/rotate.ts @@ -0,0 +1,46 @@ +import type { ChainId } from '@kadena/client'; +import { Pact, readKeyset } from '@kadena/client'; +import { + addKeyset, + addSigner, + composePactCommand, + execution, + setMeta, +} from '@kadena/client/fp'; + +import { submitClient } from '../core/client-helpers'; +import type { IClientConfig } from '../core/utils/helpers'; + +interface IRotateCommandInput { + account: { account: string; publicKeys: string[] }; + newguard: { + keys: string[]; + pred: 'keys-all' | 'keys-two' | 'keys-one'; + }; + gasPayer: { account: string; publicKeys: string[] }; + chainId: ChainId; +} + +const rotateCommand = ({ + account, + newguard, + gasPayer = account, + chainId, +}: IRotateCommandInput) => + composePactCommand( + execution( + Pact.modules.coin.rotate(account.account, readKeyset('new-guard')), + ), + addKeyset('new-guard', newguard.pred, ...newguard.keys), + addSigner(account.publicKeys, (signFor) => [ + signFor('coin.ROTATE', account.account), + ]), + addSigner(gasPayer.publicKeys, (signFor) => [signFor('coin.GAS')]), + setMeta({ senderAccount: gasPayer.account, chainId }), + ); + +/** + * @alpha + */ +export const rotate = (inputs: IRotateCommandInput, config: IClientConfig) => + submitClient(config)(rotateCommand(inputs)); diff --git a/packages/libs/client-utils/src/coin/transfer-create.ts b/packages/libs/client-utils/src/coin/transfer-create.ts new file mode 100644 index 0000000000..047da1b355 --- /dev/null +++ b/packages/libs/client-utils/src/coin/transfer-create.ts @@ -0,0 +1,62 @@ +import type { ChainId } from '@kadena/client'; +import { Pact, readKeyset } from '@kadena/client'; +import { + addKeyset, + addSigner, + composePactCommand, + execution, + setMeta, +} from '@kadena/client/fp'; + +import { submitClient } from '../core/client-helpers'; +import type { IClientConfig } from '../core/utils/helpers'; + +interface ICreateTransferInput { + sender: { account: string; publicKeys: string[] }; + receiver: { + account: string; + keyset: { + keys: string[]; + pred: 'keys-all' | 'keys-two' | 'keys-one'; + }; + }; + amount: string; + gasPayer?: { account: string; publicKeys: string[] }; + chainId: ChainId; +} + +const transferCreateCommand = ({ + sender, + receiver, + amount, + gasPayer = sender, + chainId, +}: ICreateTransferInput) => + composePactCommand( + execution( + Pact.modules.coin['transfer-create']( + sender.account, + receiver.account, + readKeyset('account-guard'), + { + decimal: amount, + }, + ), + ), + addKeyset('account-guard', receiver.keyset.pred, ...receiver.keyset.keys), + addSigner(sender.publicKeys, (signFor) => [ + signFor('coin.TRANSFER', sender.account, receiver.account, { + decimal: amount, + }), + ]), + addSigner(gasPayer.publicKeys, (signFor) => [signFor('coin.GAS')]), + setMeta({ senderAccount: gasPayer.account, chainId }), + ); + +/** + * @alpha + */ +export const transferCreate = ( + inputs: ICreateTransferInput, + config: IClientConfig, +) => submitClient(config)(transferCreateCommand(inputs)); diff --git a/packages/libs/client-utils/src/coin/transfer-crosschain.ts b/packages/libs/client-utils/src/coin/transfer-crosschain.ts new file mode 100644 index 0000000000..38d926d0a9 --- /dev/null +++ b/packages/libs/client-utils/src/coin/transfer-crosschain.ts @@ -0,0 +1,74 @@ +import type { ChainId } from '@kadena/client'; +import { Pact, readKeyset } from '@kadena/client'; +import { + addKeyset, + addSigner, + composePactCommand, + execution, + setMeta, +} from '@kadena/client/fp'; + +import { crossChainClient } from '../core/client-helpers'; +import type { IClientConfig } from '../core/utils/helpers'; + +interface ICrossChainInput { + sender: { account: string; publicKeys: string[] }; + receiver: { + account: string; + keyset: { + keys: string[]; + pred: 'keys-all' | 'keys-two' | 'keys-one'; + }; + }; + amount: string; + targetChainId: ChainId; + targetChainGasPayer?: { account: string; publicKeys: string[] }; + gasPayer?: { account: string; publicKeys: string[] }; + chainId: ChainId; +} + +const createCrossChainCommand = ({ + sender, + receiver, + amount, + targetChainId, + gasPayer = sender, + chainId, +}: Omit) => + composePactCommand( + execution( + Pact.modules.coin.defpact['transfer-crosschain']( + sender.account, + receiver.account, + readKeyset('account-guard'), + targetChainId, + { + decimal: amount, + }, + ), + ), + addKeyset('account-guard', receiver.keyset.pred, ...receiver.keyset.keys), + addSigner(sender.publicKeys, (signFor) => [ + signFor( + 'coin.TRANSFER_XCHAIN', + sender.account, + receiver.account, + { decimal: amount }, + targetChainId, + ), + ]), + addSigner(gasPayer.publicKeys, (signFor) => [signFor('coin.GAS')]), + setMeta({ senderAccount: gasPayer.account, chainId }), + ); + +/** + * @alpha + */ +export const transferCrossChain = ( + inputs: ICrossChainInput, + config: IClientConfig, +) => + crossChainClient(config)( + inputs.targetChainId, + inputs.targetChainGasPayer ?? inputs.sender, + )(createCrossChainCommand(inputs)); diff --git a/packages/libs/client-utils/src/coin/transfer.ts b/packages/libs/client-utils/src/coin/transfer.ts new file mode 100644 index 0000000000..3e628e4f17 --- /dev/null +++ b/packages/libs/client-utils/src/coin/transfer.ts @@ -0,0 +1,47 @@ +import type { ChainId } from '@kadena/client'; +import { Pact } from '@kadena/client'; +import { + addSigner, + composePactCommand, + execution, + setMeta, +} from '@kadena/client/fp'; + +import { submitClient } from '../core/client-helpers'; +import type { IClientConfig } from '../core/utils/helpers'; + +interface ITransferInput { + sender: { account: string; publicKeys: string[] }; + receiver: string; + amount: string; + gasPayer?: { account: string; publicKeys: string[] }; + chainId: ChainId; +} + +const transferCommand = ({ + sender, + receiver, + amount, + gasPayer = sender, + chainId, +}: ITransferInput) => + composePactCommand( + execution( + Pact.modules.coin.transfer(sender.account, receiver, { + decimal: amount, + }), + ), + addSigner(sender.publicKeys, (signFor) => [ + signFor('coin.TRANSFER', sender.account, receiver, { + decimal: amount, + }), + ]), + addSigner(gasPayer.publicKeys, (signFor) => [signFor('coin.GAS')]), + setMeta({ senderAccount: gasPayer.account, chainId }), + ); + +/** + * @alpha + */ +export const transfer = (inputs: ITransferInput, config: IClientConfig) => + submitClient(config)(transferCommand(inputs)); diff --git a/packages/libs/client-utils/src/core/client-helpers.ts b/packages/libs/client-utils/src/core/client-helpers.ts new file mode 100644 index 0000000000..ff4584eaa5 --- /dev/null +++ b/packages/libs/client-utils/src/core/client-helpers.ts @@ -0,0 +1,39 @@ +import type { ChainId } from '@kadena/client'; + +import { crossChain } from './cross-chain'; +import { preflight } from './preflight'; +import { dirtyRead } from './read-dirty'; +import { submitAndListen } from './submit-and-listen'; +import type { IAccount } from './utils/helpers'; +import type { WithEmitter } from './utils/with-emitter'; +import { withEmitter } from './utils/with-emitter'; + +import pipe from 'ramda/es/pipe'; + +/** + * @alpha + */ +export const submitClient = pipe(submitAndListen, withEmitter); + +/** + * @alpha + */ +export const preflightClient = pipe(preflight, withEmitter); + +/** + * @alpha + */ +export const dirtyReadClient = pipe(dirtyRead, withEmitter); + +/** + * @alpha + */ +export const crossChainClient = pipe( + crossChain, + (cb) => (targetChainId: ChainId, targetChainGasPayer: IAccount) => + ( + withEmitter as unknown as WithEmitter< + [{ event: 'poll-spv'; data: string }] + > + )((emit) => cb({ emit, targetChainGasPayer, targetChainId })), +); diff --git a/packages/libs/client-utils/src/core/cross-chain.ts b/packages/libs/client-utils/src/core/cross-chain.ts new file mode 100644 index 0000000000..7516c0ecab --- /dev/null +++ b/packages/libs/client-utils/src/core/cross-chain.ts @@ -0,0 +1,108 @@ +import type { + ChainId, + IClient, + ICommandResult, + IContinuationPayloadObject, + IPactCommand, + ITransactionDescriptor, +} from '@kadena/client'; +import { createTransaction } from '@kadena/client'; +import { + addSigner, + composePactCommand, + continuation, + setMeta, +} from '@kadena/client/fp'; + +import { asyncPipe } from './utils/asyncPipe'; +import type { IAccount, IClientConfig, IEmit } from './utils/helpers'; +import { + extractResult, + getClient, + pickFirst, + safeSign, + throwIfFails, + withInput, +} from './utils/helpers'; + +const requestSpvProof = + (targetChainId: ChainId, client: IClient, onPoll: (id: string) => void) => + ([txDesc, txResult]: [ITransactionDescriptor, ICommandResult]) => { + return client + .pollCreateSpv(txDesc, targetChainId, { + onPoll, + }) + .then( + (proof) => + ({ + pactId: txResult.continuation!.pactId, + step: txResult.continuation!.step + 1, + proof, + rollback: false, + data: {}, + }) as IContinuationPayloadObject['cont'], + ); + }; + +const useGasStation = (targetChainGasPayer: IAccount) => + targetChainGasPayer.publicKeys === undefined || + targetChainGasPayer.publicKeys.length === 0; + +const signers = ( + publicKeys?: string[], +): ((cmd: Partial) => Partial) => + Array.isArray(publicKeys) && publicKeys.length + ? addSigner(publicKeys!, (signFor) => [signFor('coin.GAS')]) + : (cmd) => cmd; + +const createPactCommand = ( + targetChainId: ChainId, + targetChainGasPayer: IAccount, +) => + composePactCommand( + setMeta({ + chainId: targetChainId, + senderAccount: targetChainGasPayer.account, + }), + signers(targetChainGasPayer.publicKeys), + ); + +export const crossChain = ( + { host, defaults, sign }: IClientConfig, + client = getClient(host), +) => { + return ({ + emit, + targetChainGasPayer, + targetChainId, + }: { + emit: IEmit; + targetChainId: ChainId; + targetChainGasPayer: { account: string; publicKeys?: string[] }; + }) => + asyncPipe( + composePactCommand(defaults ?? {}), + createTransaction, + safeSign(sign), + emit('sign'), + withInput(asyncPipe(client.preflight, emit('preflight'), throwIfFails)), + pickFirst, + client.submitOne, + emit('submit'), + withInput(asyncPipe(client.listen, emit('listen'), throwIfFails)), + requestSpvProof(targetChainId, client, emit('poll-spv')), + emit('spv-proof'), + continuation, + createPactCommand(targetChainId, targetChainGasPayer), + composePactCommand(defaults ?? {}), + createTransaction, + safeSign(sign), + emit(useGasStation(targetChainGasPayer) ? 'gas-station' : 'sign'), + client.submitOne, + emit('submit-continuation'), + client.listen, + emit('listen-continuation'), + throwIfFails, + extractResult, + ); +}; diff --git a/packages/libs/client-utils/src/core/index.ts b/packages/libs/client-utils/src/core/index.ts new file mode 100644 index 0000000000..1a84a2ee5d --- /dev/null +++ b/packages/libs/client-utils/src/core/index.ts @@ -0,0 +1,2 @@ +export * from './client-helpers'; +export * from './utils/asyncPipe'; diff --git a/packages/libs/client-utils/src/core/preflight.ts b/packages/libs/client-utils/src/core/preflight.ts new file mode 100644 index 0000000000..5142332040 --- /dev/null +++ b/packages/libs/client-utils/src/core/preflight.ts @@ -0,0 +1,25 @@ +import { createTransaction } from '@kadena/client'; +import { composePactCommand } from '@kadena/client/fp'; + +import { asyncPipe } from './utils/asyncPipe'; +import type { IClientConfig, IEmit } from './utils/helpers'; +import { + extractResult, + getClient, + safeSign, + throwIfFails, +} from './utils/helpers'; + +export const preflight = + ({ host, defaults, sign }: IClientConfig, client = getClient(host)) => + (emit: IEmit) => + asyncPipe( + composePactCommand(defaults ?? {}), + createTransaction, + safeSign(sign), + emit('sign'), + client.preflight, + emit('preflight'), + throwIfFails, + extractResult, + ); diff --git a/packages/libs/client-utils/src/core/read-dirty.ts b/packages/libs/client-utils/src/core/read-dirty.ts new file mode 100644 index 0000000000..41529f949e --- /dev/null +++ b/packages/libs/client-utils/src/core/read-dirty.ts @@ -0,0 +1,18 @@ +import { createTransaction } from '@kadena/client'; +import { composePactCommand } from '@kadena/client/fp'; + +import { asyncPipe } from './utils/asyncPipe'; +import type { IClientConfig, IEmit } from './utils/helpers'; +import { extractResult, getClient, throwIfFails } from './utils/helpers'; + +export const dirtyRead = + ({ host, defaults }: Omit, client = getClient(host)) => + (emit: IEmit) => + asyncPipe( + composePactCommand(defaults ?? {}), + createTransaction, + client.dirtyRead, + emit('dirtyRead'), + throwIfFails, + extractResult, + ); diff --git a/packages/libs/client-utils/src/core/submit-and-listen.ts b/packages/libs/client-utils/src/core/submit-and-listen.ts new file mode 100644 index 0000000000..7ce9333cbe --- /dev/null +++ b/packages/libs/client-utils/src/core/submit-and-listen.ts @@ -0,0 +1,31 @@ +import { createTransaction } from '@kadena/client'; +import { composePactCommand } from '@kadena/client/fp'; + +import { asyncPipe } from './utils/asyncPipe'; +import type { IClientConfig, IEmit } from './utils/helpers'; +import { + checkSuccess, + extractResult, + getClient, + safeSign, + throwIfFails, +} from './utils/helpers'; + +export const submitAndListen = + ({ host, defaults, sign }: IClientConfig, client = getClient(host)) => + (emit: IEmit) => + asyncPipe( + composePactCommand(defaults ?? {}), + createTransaction, + safeSign(sign), + emit('sign'), + checkSuccess( + asyncPipe(client.preflight, emit('preflight'), throwIfFails), + ), + client.submitOne, + emit('submit'), + client.listen, + emit('listen'), + throwIfFails, + extractResult, + ); diff --git a/packages/libs/client-utils/src/core/utils/asyncPipe.ts b/packages/libs/client-utils/src/core/utils/asyncPipe.ts new file mode 100644 index 0000000000..5f9837fd97 --- /dev/null +++ b/packages/libs/client-utils/src/core/utils/asyncPipe.ts @@ -0,0 +1,18 @@ +import type { IAsyncPipe } from '../../interfaces/async-pipe-type'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Any = any; + +/** + * @public + */ +export const asyncPipe: IAsyncPipe = ( + first: (...i: Any[]) => Any, + ...fns: ((i: Any, input: Any) => Any)[] +) => + ((...value: Any[]) => { + return fns.reduce( + (acc, fn) => acc.then((data) => fn(data, value)), + Promise.resolve(first(...value)), + ); + }) as Any; diff --git a/packages/libs/client-utils/src/core/utils/helpers.ts b/packages/libs/client-utils/src/core/utils/helpers.ts new file mode 100644 index 0000000000..22cdce2a03 --- /dev/null +++ b/packages/libs/client-utils/src/core/utils/helpers.ts @@ -0,0 +1,113 @@ +import type { + ICommandResult, + INetworkOptions, + IPactCommand, + ISignFunction, +} from '@kadena/client'; +import { createClient, getHostUrl, isSignedTransaction } from '@kadena/client'; +import type { ICommand, IUnsignedCommand } from '@kadena/types'; + +import type { Any } from './types'; + +export const inspect = + (tag: string) => + (data: T): T => { + console.log(tag, data); + return data; + }; + +// TODO: check if its possible to check the correctness of the signature as well +export const validateSign = ( + tx: IUnsignedCommand, + signedTx: ICommand | IUnsignedCommand, +): ICommand => { + const { sigs, hash } = signedTx; + const txWidthSigs = { ...tx, sigs }; + if (txWidthSigs.hash !== hash) { + throw new Error('Hash mismatch'); + } + if (!isSignedTransaction(txWidthSigs)) { + throw new Error('Signing failed'); + } + return txWidthSigs; +}; + +export const safeSign = + ( + sign: ( + transaction: IUnsignedCommand, + ) => Promise, + ) => + async (tx: IUnsignedCommand) => { + if (tx.sigs.length === 0) return tx as ICommand; + const signedTx = await sign(tx); + return validateSign(tx, signedTx); + }; + +export interface IEmit { + ( + tag: Tag, + ): { + (data: T): T; + _event_type: [{ event: Tag; data: T }]; + }; +} + +export interface IClientConfig { + host?: string | ((options: INetworkOptions) => string); + defaults?: Partial; + sign: ISignFunction; +} + +export interface IAccount { + account: string; + publicKeys?: string[]; +} + +export const withInput = < + I extends Any, + T extends { (arg: I): Any; _event_type?: Any }, +>( + fn: T, +): { + (input: I): Promise<[input: I, output: Awaited>]>; + _event_type: T['_event_type']; +} => + ((input: I): Promise<[input: I, output: Awaited>]> => + Promise.resolve(fn(input)).then((output) => [input, output])) as Any; + +export const checkSuccess = < + I extends Any, + T extends { (arg: I): Any; _event_type?: Any }, +>( + fn: T, +): { + (input: I): Promise; + _event_type: T['_event_type']; +} => + ((input: I): Promise => + Promise.resolve(fn(input)).then((output) => input)) as Any; + +// throw if the result is failed ; we might introduce another api for error handling +export const throwIfFails = (response: ICommandResult): ICommandResult => { + if (response.result.status === 'success') { + return response; + } + throw response.result.error; +}; + +export const pickFirst = ([tx]: T) => tx; + +export const extractResult = (response: ICommandResult) => { + if (response.result.status === 'success') { + return response.result.data; + } + return undefined; +}; + +export const getClient = ( + host?: string | ((arg: INetworkOptions) => string), +) => + typeof host === 'string' + ? createClient(getHostUrl(host)) + : createClient(host); diff --git a/packages/libs/client-utils/src/core/utils/test/asyncPipe.test.ts b/packages/libs/client-utils/src/core/utils/test/asyncPipe.test.ts new file mode 100644 index 0000000000..c3097b30dc --- /dev/null +++ b/packages/libs/client-utils/src/core/utils/test/asyncPipe.test.ts @@ -0,0 +1,37 @@ +import { asyncPipe } from '../asyncPipe'; + +describe('asyncPipe', () => { + it('pipes functions together and returns a single function', async () => { + const asyncAlways = (input: string): Promise => + Promise.resolve(input); + + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + const asyncAppend = (append: string) => (input: string) => + Promise.resolve(`${input} ${append}`); + + const appendAsync = asyncPipe( + asyncAlways, + asyncAppend('one'), + asyncAppend('two'), + ); + const result = await appendAsync('hello'); + expect(result).toEqual('hello one two'); + }); + + it('The function takes the same input arguments as the first function and returns the same type as the last one.', async () => { + const asyncTest = (one: string, two: string): Promise => + Promise.resolve(`"${one}-${two}"`); + + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + const asyncAppend = (append: string) => (input: string) => + Promise.resolve(`${input} ${append}`); + + const appendAsync = asyncPipe( + asyncTest, + asyncAppend('one'), + asyncAppend('two'), + ); + const result = await appendAsync('hello', 'world'); + expect(result).toEqual('"hello-world" one two'); + }); +}); diff --git a/packages/libs/client-utils/src/core/utils/test/helpers.test.ts b/packages/libs/client-utils/src/core/utils/test/helpers.test.ts new file mode 100644 index 0000000000..c69d0c22fd --- /dev/null +++ b/packages/libs/client-utils/src/core/utils/test/helpers.test.ts @@ -0,0 +1,43 @@ +import { inspect, safeSign, validateSign } from '../helpers'; + +describe('inspect', () => { + it('returns the value passed in', () => { + expect(inspect('tag')(1)).toBe(1); + }); +}); + +describe('validateSign', () => { + it('check if hash of the tx is not changed ', () => { + const unsignedTx = { + cmd: 'cmd', + hash: 'hash', + sigs: [undefined], + }; + const signedTx = { + cmd: 'cmd', + hash: 'hash', + sigs: [{ sig: 'sig' }], + }; + expect(validateSign(unsignedTx, signedTx)).toStrictEqual(signedTx); + }); +}); + +describe('safeSign', () => { + it('add signature to the tx by using sign function', async () => { + const signedTx = { + cmd: 'cmd', + hash: 'hash', + sigs: [{ sig: 'sig' }], + }; + const sign = vitest.fn().mockResolvedValue(signedTx); + const signFn = safeSign(sign); + + const result = await signFn({ + cmd: 'cmd', + hash: 'hash', + sigs: [undefined], + }); + + expect(result).toStrictEqual(signedTx); + }); +}); diff --git a/packages/libs/client-utils/src/core/utils/types.ts b/packages/libs/client-utils/src/core/utils/types.ts new file mode 100644 index 0000000000..65df2e9a79 --- /dev/null +++ b/packages/libs/client-utils/src/core/utils/types.ts @@ -0,0 +1,24 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type Any = any; + +export type AnyFunc = (...arg: Any[]) => Any; + +export type IfAny = 0 extends 1 & T ? Y : N; + +export type First = T extends [infer One] + ? One + : T extends [infer HD, ...Any[]] + ? HD + : never; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export type Tail = T extends [infer _] + ? [] + : // eslint-disable-next-line @typescript-eslint/no-unused-vars + T extends [infer _, ...infer TL] + ? TL + : never; + +export type Prettify = { + [K in keyof T]: T[K]; +} & {}; diff --git a/packages/libs/client-utils/src/core/utils/with-emitter.ts b/packages/libs/client-utils/src/core/utils/with-emitter.ts new file mode 100644 index 0000000000..7746cf7aaa --- /dev/null +++ b/packages/libs/client-utils/src/core/utils/with-emitter.ts @@ -0,0 +1,85 @@ +import type { IEmit } from './helpers'; +import type { Any, AnyFunc, First, IfAny, Tail } from './types'; + +// the default EventTarget does not throw errors when dispatching events +class MyEventTarget { + private _listeners: Record void)[]> = {}; + + public addEventListener(event: string, cb: (event: Any) => void) { + if (this._listeners[event] === undefined) { + this._listeners[event] = []; + } + this._listeners[event].push(cb); + } + + public dispatchEvent(event: string, data: Any) { + if (this._listeners[event] === undefined) { + return; + } + this._listeners[event].forEach((cb) => cb(data)); + } +} + +type GeneralEvent = (event: string, cb: (data: unknown) => Any) => T; + +type EventListenerType< + Events extends Array<{ event: string; data: Any }>, + WrapperType = Any, + Acc extends AnyFunc = Any, +> = Events extends [] + ? Acc & GeneralEvent + : First extends { event: infer Tag; data: infer R } + ? EventListenerType< + Tail extends Any[] ? Tail : [], + WrapperType, + IfAny< + Acc, + (event: Tag, cb: (data: R) => Any) => WrapperType, + Acc & ((event: Tag, cb: (data: R) => Any) => WrapperType) + > + > + : EventListenerType< + Tail extends Any[] ? Tail : [], + WrapperType, + Acc + >; + +export interface IEmitterWrapper< + T extends Array<{ event: string; data: Any }>, + Extra extends Array<{ event: string; data: Any }>, + ExecReturnType, +> { + on: EventListenerType<[...Extra, ...T], this>; + execute: () => ExecReturnType; +} + +export type WithEmitter< + Extra extends [...Array<{ event: string; data: Any }>] = [], +> = Any>( + fn: T, +) => ( + ...args: Parameters> +) => IEmitterWrapper< + ReturnType['_event_type'], + Extra, + ReturnType> +>; + +export const withEmitter: WithEmitter = + (fn) => + (...args: Any[]): Any => { + const emitter = new MyEventTarget(); + const execute = fn(((event: string) => (data: Any) => { + emitter.dispatchEvent(event, data); + return data; + }) as Any); + const wrapper = { + on: (event: string, cb: (data: Any) => Any) => { + // CustomEvent is not typed correctly + emitter.addEventListener(event, cb); + return wrapper; + }, + execute: () => execute(...args), + }; + return wrapper; + }; diff --git a/packages/libs/client-utils/src/example-contract/use-coin.ts b/packages/libs/client-utils/src/example-contract/use-coin.ts new file mode 100644 index 0000000000..20e950717c --- /dev/null +++ b/packages/libs/client-utils/src/example-contract/use-coin.ts @@ -0,0 +1,31 @@ +import { signWithChainweaver } from '@kadena/client'; + +import { createAccount } from '../coin/create-account'; + +export async function consumer() { + const result = await createAccount( + { + account: 'javad', + keyset: { + pred: 'keys-all', + keys: ['key-a', 'key-b'], + }, + gasPayer: { account: 'gasPayer', publicKeys: [''] }, + chainId: '0', + }, + { + host: 'https://api.testnet.chainweb.com', + defaults: { + networkId: 'testnet04', + }, + sign: signWithChainweaver, + }, + ) + .on('sign', (data) => console.log(data)) + .on('preflight', (data) => console.log(data)) + .on('submit', (data) => console.log(data)) + .on('listen', (data) => console.log(data)) + .execute(); + + console.log(result); +} diff --git a/packages/libs/client-utils/src/integration-tests/coin.int.test.ts b/packages/libs/client-utils/src/integration-tests/coin.int.test.ts new file mode 100644 index 0000000000..7089ea54b0 --- /dev/null +++ b/packages/libs/client-utils/src/integration-tests/coin.int.test.ts @@ -0,0 +1,233 @@ +import { createSignWithKeypair } from '@kadena/client'; + +import { + createAccount, + details, + getBalance, + transfer, + transferCreate, + transferCrossChain, +} from '../coin'; + +import { NetworkIds } from './support/NetworkIds'; +import { withStepFactory } from './support/helpers'; +import { + sender00Account, + sourceAccount, + targetAccount, +} from './test-data/accounts'; + +const accountOne = { + account: `receiver${Date.now()}`, + publicKey: sourceAccount.publicKey, +}; +const accountTwo = { + account: `new${Date.now()}`, + publicKey: targetAccount.publicKey, +}; + +describe('transferCreate', () => { + it('should transfer kda from sender00 account to receiverAccount and create the account if its not exist', async () => { + const withStep = withStepFactory(); + const result = await transferCreate( + { + sender: { + account: sender00Account.account, + publicKeys: [sender00Account.publicKey], + }, + receiver: { + account: accountOne.account, + keyset: { + keys: [accountOne.publicKey], + pred: 'keys-all', + }, + }, + amount: '100', + chainId: '0', + }, + { + host: 'http://localhost:8080', + defaults: { + networkId: 'fast-development', + }, + sign: createSignWithKeypair([sender00Account]), + }, + ) + .on( + 'sign', + withStep((step, tx) => { + expect(step).toBe(1); + expect(tx.sigs).toHaveLength(1); + expect(tx.sigs[0].sig).toBeTruthy(); + }), + ) + .on( + 'preflight', + withStep((step, prResult) => { + expect(step).toBe(2); + if (prResult.result.status === 'failure') { + expect(prResult.result.status).toBe('success'); + } else { + expect(prResult.result.data).toBe('Write succeeded'); + } + }), + ) + .on( + 'submit', + withStep((step, trDesc) => { + expect(step).toBe(3); + expect(trDesc.networkId).toBe(NetworkIds.fast_development); + expect(trDesc.chainId).toBe('0'); + expect(trDesc.requestKey).toBeTruthy(); + }), + ) + .on( + 'listen', + withStep((step, sbResult) => { + expect(step).toBe(4); + if (sbResult.result.status === 'failure') { + expect(sbResult.result.status).toBe('success'); + } else { + expect(sbResult.result.data).toBe('Write succeeded'); + } + }), + ) + .execute(); + + expect(result).toBe('Write succeeded'); + }); +}); + +describe('getBalance', () => { + it("should return the account's balance", async () => { + const balance = await getBalance( + accountOne.account, + 'fast-development', + '0', + 'http://localhost:8080', + ); + expect(balance).toBe(100); + }); +}); + +describe('getDetails', () => { + it("should return the account's details", async () => { + const data = await details( + accountOne.account, + 'fast-development', + '0', + 'http://localhost:8080', + ); + expect(data).toEqual({ + account: accountOne.account, + balance: 100, + guard: { + keys: [accountOne.publicKey], + pred: 'keys-all', + }, + }); + }); +}); + +describe('createAccount', () => { + it("should create the account with 0 balance if it's not exist", async () => { + const result = await createAccount( + { + account: accountTwo.account, + keyset: { + keys: [accountTwo.publicKey], + pred: 'keys-all', + }, + gasPayer: { + account: sourceAccount.account, + publicKeys: [sourceAccount.publicKey], + }, + chainId: '0', + }, + { + host: 'http://localhost:8080', + defaults: { + networkId: 'fast-development', + }, + sign: createSignWithKeypair([sourceAccount]), + }, + ).execute(); + + expect(result).toBe('Write succeeded'); + }); +}); + +describe('transfer', () => { + it('should transfer amount to an existed account', async () => { + const result = await transfer( + { + sender: { + account: sourceAccount.account, + publicKeys: [sourceAccount.publicKey], + }, + receiver: accountTwo.account, + amount: '10', + chainId: '0', + }, + { + host: 'http://localhost:8080', + defaults: { + networkId: 'fast-development', + }, + sign: createSignWithKeypair([sourceAccount]), + }, + ).execute(); + + expect(result).toBe('Write succeeded'); + + const balance = await getBalance( + accountTwo.account, + 'fast-development', + '0', + 'http://localhost:8080', + ); + + expect(balance).toBe(10); + }); +}); + +describe('cross chain transfer', () => { + it('should transfer amount to another chain', async () => { + const result = await transferCrossChain( + { + sender: { + account: sender00Account.account, + publicKeys: [sender00Account.publicKey], + }, + receiver: { + account: accountOne.account, + keyset: { + keys: [accountOne.publicKey], + pred: 'keys-all', + }, + }, + amount: '10', + chainId: '0', + targetChainId: '1', + }, + { + host: 'http://localhost:8080', + defaults: { + networkId: 'fast-development', + }, + sign: createSignWithKeypair([sender00Account]), + }, + ).execute(); + + expect(result).toBe('Write succeeded'); + + const balance = await getBalance( + accountOne.account, + 'fast-development', + '1', + 'http://localhost:8080', + ); + + expect(balance).toBe(10); + }); +}); diff --git a/packages/libs/client-utils/src/integration-tests/support/NetworkIds.ts b/packages/libs/client-utils/src/integration-tests/support/NetworkIds.ts new file mode 100644 index 0000000000..6a64da767f --- /dev/null +++ b/packages/libs/client-utils/src/integration-tests/support/NetworkIds.ts @@ -0,0 +1,3 @@ +export const NetworkIds = { + fast_development: 'fast-development', +}; diff --git a/packages/libs/client-utils/src/integration-tests/support/helpers.ts b/packages/libs/client-utils/src/integration-tests/support/helpers.ts new file mode 100644 index 0000000000..5cc4556e4f --- /dev/null +++ b/packages/libs/client-utils/src/integration-tests/support/helpers.ts @@ -0,0 +1,16 @@ +import type { Any } from '../../core/utils/types'; + +export const withStepFactory = () => { + let step = 0; + return < + Args extends Any[], + Rt extends Any, + T extends (step: number, ...args: Args) => Rt, + >( + cb: T, + ) => + (...args: Args): Rt => { + step += 1; + return cb(step, ...args); + }; +}; diff --git a/packages/libs/client-utils/src/integration-tests/support/interfaces.ts b/packages/libs/client-utils/src/integration-tests/support/interfaces.ts new file mode 100644 index 0000000000..39665a1706 --- /dev/null +++ b/packages/libs/client-utils/src/integration-tests/support/interfaces.ts @@ -0,0 +1,13 @@ +import type { ChainId } from '@kadena/types'; + +export interface IAccount { + account: string; + publicKey: string; + chainId: ChainId; + guard: string; +} + +// this is here only for testing purposes. in a real world scenario, the secret key should never be exposed +export interface IAccountWithSecretKey extends IAccount { + secretKey: string; +} diff --git a/packages/libs/client-utils/src/integration-tests/test-data/accounts.ts b/packages/libs/client-utils/src/integration-tests/test-data/accounts.ts new file mode 100644 index 0000000000..cf23a0cf9d --- /dev/null +++ b/packages/libs/client-utils/src/integration-tests/test-data/accounts.ts @@ -0,0 +1,27 @@ +import type { IAccount, IAccountWithSecretKey } from '../support/interfaces'; + +// the pre-funded account that will be used to fund other accounts +export const sender00Account: IAccountWithSecretKey = { + account: 'sender00', + publicKey: '368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca', + chainId: '0', + guard: '368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca', + // this is here only for testing purposes. in a real world scenario, the secret key should never be exposed + secretKey: '251a920c403ae8c8f65f59142316af3c82b631fba46ddea92ee8c95035bd2898', +}; + +export const sourceAccount: IAccountWithSecretKey = { + account: 'k:5a2afbc4564b76b2c27ce5a644cab643c43663835ea0be22433b209d3351f937', + publicKey: '5a2afbc4564b76b2c27ce5a644cab643c43663835ea0be22433b209d3351f937', + chainId: '0', + guard: '5a2afbc4564b76b2c27ce5a644cab643c43663835ea0be22433b209d3351f937', + // this is here only for testing purposes. in a real world scenario, the secret key should never be exposed + secretKey: 'e97b30547784bf05eb71a765b1d45127ed89d9b3c0cf21b71a107efb170eed33', +}; + +export const targetAccount: IAccount = { + account: 'k:5a2afbc4564b76b2c27ce5a644cab643c43663835ea0be22433b209d3351f937', + publicKey: '5a2afbc4564b76b2c27ce5a644cab643c43663835ea0be22433b209d3351f937', + chainId: '1', + guard: '5a2afbc4564b76b2c27ce5a644cab643c43663835ea0be22433b209d3351f937', +}; diff --git a/packages/libs/client-utils/src/interfaces/.gitignore b/packages/libs/client-utils/src/interfaces/.gitignore new file mode 100644 index 0000000000..564cccfe02 --- /dev/null +++ b/packages/libs/client-utils/src/interfaces/.gitignore @@ -0,0 +1 @@ +async-pipe-type.ts diff --git a/packages/libs/client-utils/src/scripts/create-async-pipe-type.ts b/packages/libs/client-utils/src/scripts/create-async-pipe-type.ts new file mode 100644 index 0000000000..0bd1a0c2f1 --- /dev/null +++ b/packages/libs/client-utils/src/scripts/create-async-pipe-type.ts @@ -0,0 +1,71 @@ +import { writeFile } from 'fs/promises'; +import path from 'path'; +import * as prettier from 'prettier'; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +function createAsyncPipeOverload(length: number) { + const genericTypes: string[] = []; + const functions: string[] = []; + const Events: string[] = []; + for (let i = 1; i <= length; i++) { + const ext = i === 1 ? ' extends Any[]' : ''; + genericTypes.push(`T${i}${ext}`); + Events.push(`EVENTS${i}`); + const outputType = i === length ? 'TOut' : `T${i + 1}`; + if (i === 1) { + functions.push(`Func`); + } else { + functions.push(`PipeFunction`); + } + } + + return `<${genericTypes.join(', ')}, TOut, ${Events.map( + (name) => `${name}`, + ).join(', ')}>( + ${functions.map((arg, i) => `op${i + 1}: ${arg}`).join(',\n')}\n + ): { + _event_type: [${Events.map((name) => `...AsArray<${name}>`).join(',')}]; + (...args: T1): Output; + }; + `; +} + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +function createAsyncPipeType(maxLength: number) { + const overloads = Array.from({ length: maxLength }, (arg, i) => + createAsyncPipeOverload(i + 1), + ); + + return ` +/** + * THIS FILE IS GENERATED. DO NOT EDIT. + * check ../scripts/create-async-pipe-type.ts + * */ +// eslint-disable-next-line @kadena-dev/no-eslint-disable +/* eslint-disable max-lines */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Any = any; +type Func = ((...args: I) => O) & {_event_type?: EVENTS}; +type PipeFunction = ((arg: Awaited)=> O) & {_event_type?: EVENTS}; +type Output = T extends Promise ? T : Promise; +type AsArray = T extends Any[] ? T : []; +export interface IAsyncPipe { + ${overloads.join('\n')} + }`; +} + +const type = createAsyncPipeType( + parseInt(process.argv[process.argv.length - 1]) || 10, +); + +async function run() { + return writeFile( + path.join(__dirname, '..', 'interfaces', 'async-pipe-type.ts'), + await prettier.format(type, { parser: 'typescript' }), + 'utf-8', + ); +} + +run().catch((err) => { + console.error(err); +}); diff --git a/packages/libs/client-utils/tsconfig.json b/packages/libs/client-utils/tsconfig.json new file mode 100644 index 0000000000..993685d6ad --- /dev/null +++ b/packages/libs/client-utils/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./node_modules/@kadena-dev/heft-rig/tsconfig-base.json", + "compilerOptions": { + "types": [".kadena/pactjs-generated", "vitest/globals", "node"], + "lib": ["es2019"] + } +} diff --git a/packages/libs/client-utils/turbo.json b/packages/libs/client-utils/turbo.json new file mode 100644 index 0000000000..0d133573fb --- /dev/null +++ b/packages/libs/client-utils/turbo.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "pipeline": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", "lib/**", "core/**", "coin/**"], + "outputMode": "new-only" + } + } +} diff --git a/packages/libs/client-utils/vitest.config.ts b/packages/libs/client-utils/vitest.config.ts new file mode 100644 index 0000000000..fe36811f7f --- /dev/null +++ b/packages/libs/client-utils/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['src/**/*.test.ts'], + exclude: ['src/**/*.int.test.ts'], + globals: true, + threads: false, // To prevent error in tests using jsdom environment: Module did not self-register: canvas.node + }, +}); diff --git a/packages/libs/client-utils/vitest.integration.config.ts b/packages/libs/client-utils/vitest.integration.config.ts new file mode 100644 index 0000000000..1c63468263 --- /dev/null +++ b/packages/libs/client-utils/vitest.integration.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['src/**/*.int.test.ts'], + globals: true, + testTimeout: 60000, + }, +}); diff --git a/packages/libs/client/.eslintrc.js b/packages/libs/client/.eslintrc.js index b2c1cd5129..10967f20b9 100644 --- a/packages/libs/client/.eslintrc.js +++ b/packages/libs/client/.eslintrc.js @@ -4,4 +4,8 @@ require('@rushstack/eslint-config/patch/modern-module-resolution'); module.exports = { extends: ['@kadena-dev/eslint-config/profile/lib'], parserOptions: { tsconfigRootDir: __dirname }, + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + '@rushstack/typedef-var': 'off', + }, }; diff --git a/packages/libs/client/etc/client.api.md b/packages/libs/client/etc/client.api.md index b827bf898c..b7dc2cb0fd 100644 --- a/packages/libs/client/etc/client.api.md +++ b/packages/libs/client/etc/client.api.md @@ -50,6 +50,9 @@ export function createWalletConnectQuicksign(client: Client, session: SessionTyp // @public export function createWalletConnectSign(client: Client, session: SessionTypes.Struct, walletConnectChainId: TWalletConnectChainId): ISingleSignFunction; +// @public +export const getHostUrl: (hostBaseUrl: string) => ({ networkId, chainId }: INetworkOptions) => string; + // @public (undocumented) export interface IBaseClient { createSpv: (transactionDescriptor: ITransactionDescriptor, targetChainId: ChainId) => Promise; @@ -94,6 +97,7 @@ export interface IClient extends IBaseClient { // @deprecated send: ISubmit; signatureVerification: (transaction: ICommand) => Promise; + submitOne: (transaction: ICommand) => Promise; } export { ICommand } diff --git a/packages/libs/client/src/client/client.ts b/packages/libs/client/src/client/client.ts index 1fae903db8..c7cabb02b2 100644 --- a/packages/libs/client/src/client/client.ts +++ b/packages/libs/client/src/client/client.ts @@ -212,6 +212,12 @@ export interface IClient extends IBaseClient { */ send: ISubmit; + /** + * Alias for `submit` that accepts only one transaction. useful when you want more precise type checking. + * {@link IBaseClient.submit | submit() function} + */ + submitOne: (transaction: ICommand) => Promise; + /** * Use {@link IBaseClient.getStatus | getStatus() function} * Alias for `getStatus`. @@ -351,6 +357,7 @@ export const createClient: ICreateClient = ( return { ...client, + submitOne: client.submit, preflight(body) { return client.local(body, { preflight: true, diff --git a/packages/libs/client/src/client/index.ts b/packages/libs/client/src/client/index.ts index 5f351fddf7..80a763e606 100644 --- a/packages/libs/client/src/client/index.ts +++ b/packages/libs/client/src/client/index.ts @@ -1,2 +1,3 @@ export * from './client'; export type * from './interfaces/interfaces'; +export { getHostUrl } from './utils/utils'; diff --git a/packages/libs/client/src/client/utils/utils.ts b/packages/libs/client/src/client/utils/utils.ts index 8cef95aaf6..1450a50ed6 100644 --- a/packages/libs/client/src/client/utils/utils.ts +++ b/packages/libs/client/src/client/utils/utils.ts @@ -1,5 +1,7 @@ -import type { ChainId } from '@kadena/types'; -import type { IPollRequestPromise } from '../interfaces/interfaces'; +import type { + INetworkOptions, + IPollRequestPromise, +} from '../interfaces/interfaces'; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export const jsonRequest = (body: object) => ({ @@ -32,18 +34,36 @@ export function getUrl( return url.toString(); } +/** + * + * @public + * Creates endpoint url based on the baseUrl, networkId and chainId + * + * @example + * const getLocalHostUrl = getHostUrl('http://localhost:8080') + * const client = createClient(getLocalHostUrl) + */ + +export const getHostUrl = (hostBaseUrl: string) => { + const base = hostBaseUrl.endsWith('/') + ? hostBaseUrl.slice(0, hostBaseUrl.length - 1) + : hostBaseUrl; + return ({ networkId, chainId }: INetworkOptions) => + `${base}/chainweb/0.0/${networkId}/chain/${chainId}/pact`; +}; + export const kadenaHostGenerator = ({ networkId, chainId, -}: { - networkId: string; - chainId: ChainId; -}): string => { +}: INetworkOptions): string => { switch (networkId) { case 'mainnet01': - return `https://api.chainweb.com/chainweb/0.0/${networkId}/chain/${chainId}/pact`; + return getHostUrl('https://api.chainweb.com')({ networkId, chainId }); case 'testnet04': - return `https://api.testnet.chainweb.com/chainweb/0.0/${networkId}/chain/${chainId}/pact`; + return getHostUrl('https://api.testnet.chainweb.com')({ + networkId, + chainId, + }); default: throw new Error(`UNKNOWN_NETWORK_ID: ${networkId}`); } diff --git a/packages/libs/client/src/composePactCommand/utils/addSigner.ts b/packages/libs/client/src/composePactCommand/utils/addSigner.ts index c1447c979f..8516435767 100644 --- a/packages/libs/client/src/composePactCommand/utils/addSigner.ts +++ b/packages/libs/client/src/composePactCommand/utils/addSigner.ts @@ -6,17 +6,15 @@ import type { } from '../../interfaces/type-utilities'; import { patchCommand } from './patchCommand'; +export type ISigner = + | string + | { pubKey: string; scheme?: 'ED25519' | 'ETH'; address?: string }; + interface IAddSigner { - ( - first: - | string - | { pubKey: string; scheme?: 'ED25519' | 'ETH'; address?: string }, - ): () => Partial; + (first: ISigner | ISigner[]): () => Partial; // eslint-disable-next-line @typescript-eslint/no-explicit-any ( - first: - | string - | { pubKey: string; scheme?: 'ED25519' | 'ETH'; address?: string }, + first: ISigner | ISigner[], capability: (withCapability: ExtractType) => ICap[], ): TCommand; } @@ -27,9 +25,7 @@ interface IAddSigner { * @public */ export const addSigner: IAddSigner = (( - first: - | string - | { pubKey: string; scheme?: 'ED25519' | 'ETH'; address?: string }, + signer: ISigner | ISigner[], capability: ( withCapability: ( name: string, @@ -37,11 +33,7 @@ export const addSigner: IAddSigner = (( ) => { name: string; args: unknown[] }, ) => ICap[], ): unknown => { - const { - pubKey, - scheme = 'ED25519', - address = undefined, - } = typeof first === 'object' ? first : { pubKey: first }; + const signers = Array.isArray(signer) ? signer : [signer]; let clist: undefined | Array<{ name: string; args: unknown[] }>; if (typeof capability === 'function') { clist = capability((name: string, ...args: unknown[]) => ({ @@ -52,15 +44,20 @@ export const addSigner: IAddSigner = (( return (cmd: Partial) => patchCommand(cmd, { - signers: [ - { + signers: signers.map((item) => { + const { + pubKey, + scheme = 'ED25519', + address = undefined, + } = typeof item === 'object' ? item : { pubKey: item }; + return { pubKey, scheme, ...(address !== undefined ? { address } : {}), ...(clist !== undefined ? { clist } : {}), // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any, - ], + } as any; + }), }); // eslint-disable-next-line @typescript-eslint/no-explicit-any }) as any; diff --git a/packages/libs/client/src/createTransactionBuilder/createTransactionBuilder.ts b/packages/libs/client/src/createTransactionBuilder/createTransactionBuilder.ts index 0656e6bace..d66cee4db7 100644 --- a/packages/libs/client/src/createTransactionBuilder/createTransactionBuilder.ts +++ b/packages/libs/client/src/createTransactionBuilder/createTransactionBuilder.ts @@ -11,6 +11,7 @@ import { setNonce, } from '../composePactCommand'; import type { ValidDataTypes } from '../composePactCommand/utils/addData'; +import type { ISigner } from '../composePactCommand/utils/addSigner'; import { patchCommand } from '../composePactCommand/utils/patchCommand'; import type { IContinuationPayloadObject, @@ -26,11 +27,7 @@ interface IAddSigner { /** * Add signer without capability */ - ( - first: - | string - | { pubKey: string; scheme?: 'ED25519' | 'ETH'; address?: string }, - ): IBuilder; + (first: ISigner | ISigner[]): IBuilder; /** * Add a signer including capabilities. The withCapability function is obtained from * the function you call in the execution part. @@ -43,9 +40,7 @@ interface IAddSigner { * ]) */ ( - first: - | string - | { pubKey: string; scheme?: 'ED25519' | 'ETH'; address?: string }, + first: ISigner | ISigner[], capability: (withCapability: ExtractCapabilityType) => ICap[], ): IBuilder; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c0733c000..e402306ef5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - importers: .: @@ -34,7 +30,7 @@ importers: version: 11.2.1 tsx: specifier: ^3.12.10 - version: 3.13.0 + version: 3.12.10 turbo: specifier: ^1.10.15 version: 1.10.16 @@ -366,7 +362,7 @@ importers: version: 8.10.2 '@types/seedrandom': specifier: ~3.0.5 - version: 3.0.6 + version: 3.0.5 prettier: specifier: ~3.0.3 version: 3.0.3 @@ -1038,6 +1034,9 @@ importers: '@kadena/client': specifier: workspace:* version: link:../client + '@kadena/client-utils': + specifier: workspace:* + version: link:../client-utils '@kadena/pactjs': specifier: workspace:* version: link:../pactjs @@ -1079,6 +1078,76 @@ importers: specifier: 5.2.2 version: 5.2.2 + packages/libs/client-utils: + dependencies: + '@kadena/chainweb-node-client': + specifier: workspace:* + version: link:../chainweb-node-client + '@kadena/client': + specifier: workspace:* + version: link:../client + '@kadena/cryptography-utils': + specifier: workspace:* + version: link:../cryptography-utils + '@kadena/types': + specifier: workspace:* + version: link:../types + debug: + specifier: ~4.3.4 + version: 4.3.4(supports-color@5.5.0) + ramda: + specifier: ^0.29.0 + version: 0.29.0 + devDependencies: + '@kadena-dev/eslint-config': + specifier: workspace:* + version: link:../../tools/eslint-config + '@kadena-dev/heft-rig': + specifier: workspace:* + version: link:../../tools/heft-rig + '@kadena-dev/lint-package': + specifier: workspace:* + version: link:../../tools/lint-package + '@kadena-dev/markdown': + specifier: workspace:* + version: link:../../tools/markdown + '@kadena/pactjs-cli': + specifier: workspace:^ + version: link:../../tools/pactjs-cli + '@microsoft/api-extractor': + specifier: ^7.38.0 + version: 7.38.0(@types/node@18.17.14) + '@rushstack/eslint-config': + specifier: ~3.3.0 + version: 3.3.0(eslint@8.48.0)(typescript@5.2.2) + '@rushstack/heft': + specifier: ~0.50.6 + version: 0.50.6(@types/node@18.17.14) + '@types/debug': + specifier: ~4.1.7 + version: 4.1.8 + '@types/node': + specifier: ^18.17.14 + version: 18.17.14 + '@types/ramda': + specifier: ^0.29.5 + version: 0.29.5 + eslint: + specifier: ^8.45.0 + version: 8.48.0 + prettier: + specifier: ~3.0.3 + version: 3.0.3 + prettier-plugin-packagejson: + specifier: ^2.4.6 + version: 2.4.6(prettier@3.0.3) + ts-node: + specifier: ~10.8.2 + version: 10.8.2(@types/node@18.17.14)(typescript@5.2.2) + vitest: + specifier: ^0.34.6 + version: 0.34.6(happy-dom@12.9.1) + packages/libs/cryptography-utils: dependencies: blakejs: @@ -1527,7 +1596,7 @@ importers: version: 8.45.0 eslint-import-resolver-typescript: specifier: 3.5.5 - version: 3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.45.0) + version: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0) eslint-plugin-import: specifier: ~2.27.5 version: 2.27.5(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) @@ -1708,7 +1777,7 @@ importers: version: 9.0.0(eslint@8.45.0) eslint-import-resolver-typescript: specifier: 3.5.5 - version: 3.5.5(@typescript-eslint/parser@5.23.0)(eslint-plugin-import@2.27.5)(eslint@8.45.0) + version: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0) eslint-plugin-import: specifier: ~2.27.5 version: 2.27.5(@typescript-eslint/parser@5.23.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) @@ -2223,7 +2292,7 @@ packages: '@babel/core': 7.22.15 '@babel/generator': 7.22.15 '@babel/parser': 7.22.15 - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 '@babel/traverse': 7.22.15 '@babel/types': 7.22.15 babel-preset-fbjs: 3.4.0(@babel/core@7.22.15) @@ -2370,20 +2439,6 @@ packages: lru-cache: 5.1.1 semver: 6.3.1 - /@babel/helper-compilation-targets@7.22.9(@babel/core@7.22.15): - resolution: {integrity: sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.22.9 - '@babel/core': 7.22.15 - '@babel/helper-validator-option': 7.22.15 - browserslist: 4.21.10 - lru-cache: 5.1.1 - semver: 6.3.1 - dev: true - /@babel/helper-compilation-targets@7.22.9(@babel/core@7.22.9): resolution: {integrity: sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==} engines: {node: '>=6.9.0'} @@ -2652,11 +2707,6 @@ packages: resolution: {integrity: sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.22.5: - resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-validator-option@7.22.15: resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} engines: {node: '>=6.9.0'} @@ -4495,9 +4545,9 @@ packages: dependencies: '@babel/compat-data': 7.22.9 '@babel/core': 7.22.15 - '@babel/helper-compilation-targets': 7.22.9(@babel/core@7.22.15) + '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.22.5 + '@babel/helper-validator-option': 7.22.15 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.22.5(@babel/core@7.22.15) '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.22.5(@babel/core@7.22.15) '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.22.15) @@ -4568,7 +4618,7 @@ packages: '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.22.15) '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.22.15) '@babel/preset-modules': 0.1.6(@babel/core@7.22.15) - '@babel/types': 7.22.5 + '@babel/types': 7.22.15 babel-plugin-polyfill-corejs2: 0.4.5(@babel/core@7.22.15) babel-plugin-polyfill-corejs3: 0.8.3(@babel/core@7.22.15) babel-plugin-polyfill-regenerator: 0.5.2(@babel/core@7.22.15) @@ -4818,12 +4868,6 @@ packages: dependencies: regenerator-runtime: 0.13.11 - /@babel/runtime@7.23.1: - resolution: {integrity: sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.0 - /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} @@ -4888,7 +4932,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.15 to-fast-properties: 2.0.0 dev: true @@ -5236,6 +5280,27 @@ packages: unescape-js: 1.1.4 dev: true + /@esbuild-kit/cjs-loader@2.4.2: + resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==} + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.7.0 + dev: true + + /@esbuild-kit/core-utils@3.3.2: + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + dev: true + + /@esbuild-kit/esm-loader@2.6.5: + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.7.0 + dev: true + /@esbuild/android-arm64@0.17.6: resolution: {integrity: sha512-YnYSCceN/dUzUr5kdtUzB+wZprCafuD89Hs0Aqv9QSdwhYQybhXTaSTcrl6X/aWThn1a/j0eEpUBGOE7269REg==} engines: {node: '>=12'} @@ -5617,12 +5682,27 @@ packages: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: eslint: 8.45.0 - eslint-visitor-keys: 3.4.2 + eslint-visitor-keys: 3.4.3 + + /@eslint-community/eslint-utils@4.4.0(eslint@8.48.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.48.0 + eslint-visitor-keys: 3.4.3 + dev: true /@eslint-community/regexpp@4.6.2: resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + /@eslint-community/regexpp@4.8.0: + resolution: {integrity: sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + /@eslint/eslintrc@0.4.3: resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -5647,7 +5727,7 @@ packages: ajv: 6.12.6 debug: 4.3.4(supports-color@5.5.0) espree: 9.6.1 - globals: 13.20.0 + globals: 13.21.0 ignore: 5.2.4 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -5656,10 +5736,32 @@ packages: transitivePeerDependencies: - supports-color + /@eslint/eslintrc@2.1.2: + resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4(supports-color@5.5.0) + espree: 9.6.1 + globals: 13.21.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + /@eslint/js@8.44.0: resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /@eslint/js@8.48.0: + resolution: {integrity: sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /@exodus/schemasafe@1.1.1: resolution: {integrity: sha512-Pd7+aGvWIaTDL5ecV4ZBEtBrjXnk8/ly5xyHbikxVhgcq7qhihzHWHbcYmFupQBT2A5ggNZGvT7Bpj0M6AKHjA==} dev: false @@ -6501,6 +6603,17 @@ packages: transitivePeerDependencies: - supports-color + /@humanwhocodes/config-array@0.11.11: + resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4(supports-color@5.5.0) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /@humanwhocodes/config-array@0.5.0: resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} engines: {node: '>=10.10.0'} @@ -6750,7 +6863,7 @@ packages: convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 - jest-haste-map: 29.6.4 + jest-haste-map: 29.7.0 jest-regex-util: 29.6.3 jest-util: 29.7.0 micromatch: 4.0.5 @@ -7546,7 +7659,7 @@ packages: '@percy/logger': 1.24.0 content-disposition: 0.5.4 cross-spawn: 7.0.3 - extract-zip: 2.0.1 + extract-zip: 2.0.1(supports-color@8.1.1) fast-glob: 3.3.1 micromatch: 4.0.5 mime-types: 2.1.35 @@ -7938,7 +8051,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0) '@types/react': 18.2.15 '@types/react-dom': 18.2.7 @@ -8315,7 +8428,7 @@ packages: '@types/react-dom': optional: true dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.15)(react@18.2.0) @@ -8390,7 +8503,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.15)(react@18.2.0) '@types/react': 18.2.15 react: 18.2.0 @@ -8433,7 +8546,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 '@radix-ui/rect': 1.0.1 '@types/react': 18.2.15 react: 18.2.0 @@ -8448,7 +8561,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.15)(react@18.2.0) '@types/react': 18.2.15 react: 18.2.0 @@ -8478,7 +8591,7 @@ packages: /@radix-ui/rect@1.0.1: resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 dev: true /@redocly/ajv@8.11.0: @@ -8533,6 +8646,29 @@ packages: transitivePeerDependencies: - supports-color + /@rushstack/eslint-config@3.3.0(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-7eqoCDc52QK07yTDD7txyv1/5kt5jPfo4NnLgqX3NlMNBCMWSuWTJyCPXLZiwVLWAOjcPSxN8/10WuEIQkGMiw==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '>=4.7.0' + dependencies: + '@rushstack/eslint-patch': 1.3.0 + '@rushstack/eslint-plugin': 0.12.0(eslint@8.48.0)(typescript@5.2.2) + '@rushstack/eslint-plugin-packlets': 0.7.0(eslint@8.48.0)(typescript@5.2.2) + '@rushstack/eslint-plugin-security': 0.6.0(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/eslint-plugin': 5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/experimental-utils': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/parser': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.2.2) + eslint: 8.48.0 + eslint-plugin-promise: 6.0.1(eslint@8.48.0) + eslint-plugin-react: 7.27.1(eslint@8.48.0) + eslint-plugin-tsdoc: 0.2.17 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + /@rushstack/eslint-patch@1.3.0: resolution: {integrity: sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w==} @@ -8551,6 +8687,19 @@ packages: - supports-color - typescript + /@rushstack/eslint-plugin-packlets@0.7.0(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-ftvrRvN7a5dfpDidDtrqJHH25JvL4huqk3a0S4zv5Rlh1kz6sfPvaKosDQowzEHBIWLvAtTN+P8ygWoyL0/XYw==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@rushstack/tree-pattern': 0.2.4 + '@typescript-eslint/experimental-utils': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + eslint: 8.48.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@rushstack/eslint-plugin-security@0.6.0(eslint@8.45.0)(typescript@5.2.2): resolution: {integrity: sha512-gJFBGoCCofU34GGFtR3zEjymEsRr2wDLu2u13mHVcDzXyZ3EDlt6ImnJtmn8VRDLGjJ7QFPOiYMSZQaArxWmGg==} peerDependencies: @@ -8563,6 +8712,19 @@ packages: - supports-color - typescript + /@rushstack/eslint-plugin-security@0.6.0(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-gJFBGoCCofU34GGFtR3zEjymEsRr2wDLu2u13mHVcDzXyZ3EDlt6ImnJtmn8VRDLGjJ7QFPOiYMSZQaArxWmGg==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@rushstack/tree-pattern': 0.2.4 + '@typescript-eslint/experimental-utils': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + eslint: 8.48.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@rushstack/eslint-plugin@0.11.0(eslint@8.45.0)(typescript@5.2.2): resolution: {integrity: sha512-e8eVBOgb/xkpkgFmPP+oifrqCLh8I5BFI/emB/nf5+WnuS4hsTHkgprCEiJuvkhJRypsWrbchkIda9/1YFadxg==} peerDependencies: @@ -8588,6 +8750,19 @@ packages: - supports-color - typescript + /@rushstack/eslint-plugin@0.12.0(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-kDB35khQeoDjabzHkHDs/NgvNNZzogkoU/UfrXnNSJJlcCxOxmhyscUQn5OptbixiiYCOFZh9TN9v2yGBZ3vJQ==} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@rushstack/tree-pattern': 0.2.4 + '@typescript-eslint/experimental-utils': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + eslint: 8.48.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@rushstack/heft-config-file@0.12.2(@types/node@18.17.14): resolution: {integrity: sha512-luAU7LLtW50A3wv3U5YaFCpu0zn9TrAt6rcN28B3eD1sgCFbYH+VCmrPYzJEbzWXJhl1WpGbvObR6avVh5cc8w==} engines: {node: '>=10.13.0'} @@ -10957,8 +11132,8 @@ packages: /@types/jest@29.5.3: resolution: {integrity: sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==} dependencies: - expect: 29.6.2 - pretty-format: 29.6.2 + expect: 29.7.0 + pretty-format: 29.7.0 dev: true /@types/js-cookie@2.2.7: @@ -11150,6 +11325,12 @@ packages: resolution: {integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==} dev: true + /@types/ramda@0.29.5: + resolution: {integrity: sha512-oBBdRfoZoCl/aBIpBbct/uUHAbJ5i7vSOHK83SvH2Qr9ermYITRNKnEYgGJlnkagUY2cu8L2//Jq7o1355Go5A==} + dependencies: + types-ramda: 0.29.4 + dev: true + /@types/range-parser@1.2.4: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} dev: true @@ -11188,8 +11369,8 @@ packages: /@types/scheduler@0.16.3: resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} - /@types/seedrandom@3.0.6: - resolution: {integrity: sha512-3+JLzzsdbTrEDXSKaudKajj3w8x4ZWDk7CunwHnSWxtfMoetnKtIbFXMru7RXw6Hq1dU0td+soPX8vVjdgCPBA==} + /@types/seedrandom@3.0.5: + resolution: {integrity: sha512-kopEpYpFQvQdYsZkZVwht/0THHmTFFYXDaqV/lM45eweJ8kcGVDgZHs0RVTolSq55UPZNmjhKc9r7UvLu/mQQg==} dev: true /@types/semver@7.5.0: @@ -11352,6 +11533,34 @@ packages: transitivePeerDependencies: - supports-color + /@typescript-eslint/eslint-plugin@5.59.11(@typescript-eslint/parser@5.59.11)(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.6.2 + '@typescript-eslint/parser': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/scope-manager': 5.59.11 + '@typescript-eslint/type-utils': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + '@typescript-eslint/utils': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.48.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/experimental-utils@3.10.1(eslint@7.32.0)(typescript@3.9.10): resolution: {integrity: sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -11407,6 +11616,19 @@ packages: - supports-color - typescript + /@typescript-eslint/experimental-utils@5.59.11(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-GkQGV0UF/V5Ra7gZMBmiD1WrYUFOJNvCZs+XQnUyJoxmqfWMXVNyB2NVCPRKefoQcpvTv9UpJyfCvsJFs8NzzQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@typescript-eslint/utils': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + eslint: 8.48.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/parser@3.10.1(eslint@7.32.0)(typescript@3.9.10): resolution: {integrity: sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -11466,6 +11688,26 @@ packages: transitivePeerDependencies: - supports-color + /@typescript-eslint/parser@5.59.11(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.59.11 + '@typescript-eslint/types': 5.59.11 + '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.2.2) + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.48.0 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/parser@5.62.0(eslint@8.45.0)(typescript@5.2.2): resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -11559,6 +11801,26 @@ packages: transitivePeerDependencies: - supports-color + /@typescript-eslint/type-utils@5.59.11(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.2.2) + '@typescript-eslint/utils': 5.59.11(eslint@8.48.0)(typescript@5.2.2) + debug: 4.3.4(supports-color@5.5.0) + eslint: 8.48.0 + tsutils: 3.21.0(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/types@3.10.1: resolution: {integrity: sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==} engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} @@ -11784,6 +12046,26 @@ packages: - supports-color - typescript + /@typescript-eslint/utils@5.59.11(eslint@8.48.0)(typescript@5.2.2): + resolution: {integrity: sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@types/json-schema': 7.0.12 + '@types/semver': 7.5.1 + '@typescript-eslint/scope-manager': 5.59.11 + '@typescript-eslint/types': 5.59.11 + '@typescript-eslint/typescript-estree': 5.59.11(typescript@5.2.2) + eslint: 8.48.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/utils@5.62.0(eslint@8.45.0)(typescript@5.2.2): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -13421,7 +13703,7 @@ packages: styled-components: '>= 2' dependencies: '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-module-imports': 7.22.5 + '@babel/helper-module-imports': 7.22.15 '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.15) lodash: 4.17.21 picomatch: 2.3.1 @@ -14426,7 +14708,7 @@ packages: dev: true /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} /concat-stream@1.6.2: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} @@ -15812,56 +16094,9 @@ packages: dependencies: debug: 3.2.7(supports-color@8.1.1) is-core-module: 2.12.1 - resolve: 1.22.2 - transitivePeerDependencies: - - supports-color - - /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.23.0)(eslint-plugin-import@2.27.5)(eslint@8.45.0): - resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - dependencies: - debug: 4.3.4(supports-color@5.5.0) - enhanced-resolve: 5.15.0 - eslint: 8.45.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.23.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.23.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) - get-tsconfig: 4.6.2 - globby: 13.2.2 - is-core-module: 2.12.1 - is-glob: 4.0.3 - synckit: 0.8.5 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - - /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.45.0): - resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - dependencies: - debug: 4.3.4(supports-color@5.5.0) - enhanced-resolve: 5.15.0 - eslint: 8.45.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) - get-tsconfig: 4.6.2 - globby: 13.2.2 - is-core-module: 2.12.1 - is-glob: 4.0.3 - synckit: 0.8.5 + resolve: 1.22.4 transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - supports-color - dev: true /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0): resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} @@ -15911,7 +16146,7 @@ packages: debug: 3.2.7(supports-color@8.1.1) eslint: 8.45.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.23.0)(eslint-plugin-import@2.27.5)(eslint@8.45.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0) transitivePeerDependencies: - supports-color @@ -15940,7 +16175,7 @@ packages: debug: 3.2.7(supports-color@8.1.1) eslint: 8.45.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.45.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0) transitivePeerDependencies: - supports-color dev: true @@ -15994,11 +16229,11 @@ packages: eslint-import-resolver-node: 0.3.7 eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.23.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) has: 1.0.3 - is-core-module: 2.12.1 + is-core-module: 2.13.0 is-glob: 4.0.3 minimatch: 3.1.2 object.values: 1.1.6 - resolve: 1.22.2 + resolve: 1.22.4 semver: 6.3.1 tsconfig-paths: 3.14.2 transitivePeerDependencies: @@ -16083,6 +16318,15 @@ packages: dependencies: eslint: 8.45.0 + /eslint-plugin-promise@6.0.1(eslint@8.48.0): + resolution: {integrity: sha512-uM4Tgo5u3UWQiroOyDEsYcVMOo7re3zmno0IZmB5auxoaQNIceAbXEkSt8RNrKtaYehARHG06pYK6K1JhtP0Zw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.48.0 + dev: true + /eslint-plugin-react-hooks@4.6.0(eslint@8.45.0): resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} @@ -16113,6 +16357,29 @@ packages: semver: 6.3.1 string.prototype.matchall: 4.0.8 + /eslint-plugin-react@7.27.1(eslint@8.48.0): + resolution: {integrity: sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.6 + array.prototype.flatmap: 1.3.1 + doctrine: 2.1.0 + eslint: 8.48.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.6 + object.fromentries: 2.0.6 + object.hasown: 1.1.2 + object.values: 1.1.6 + prop-types: 15.8.1 + resolve: 2.0.0-next.4 + semver: 6.3.1 + string.prototype.matchall: 4.0.8 + dev: true + /eslint-plugin-react@7.31.11(eslint@8.45.0): resolution: {integrity: sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==} engines: {node: '>=4'} @@ -16201,6 +16468,10 @@ packages: resolution: {integrity: sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + /eslint@7.32.0: resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==} engines: {node: ^10.12.0 || >=12.0.0} @@ -16295,6 +16566,52 @@ packages: transitivePeerDependencies: - supports-color + /eslint@8.48.0: + resolution: {integrity: sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0) + '@eslint-community/regexpp': 4.8.0 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.48.0 + '@humanwhocodes/config-array': 0.11.11 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4(supports-color@5.5.0) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.21.0 + graphemer: 1.4.0 + ignore: 5.2.4 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /espree@6.2.1: resolution: {integrity: sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==} engines: {node: '>=6.0.0'} @@ -16523,18 +16840,6 @@ packages: engines: {node: '>= 0.8.0'} dev: false - /expect@29.6.2: - resolution: {integrity: sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.7.0 - '@types/node': 18.17.14 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - dev: true - /expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -16544,7 +16849,6 @@ packages: jest-matcher-utils: 29.7.0 jest-message-util: 29.7.0 jest-util: 29.7.0 - dev: false /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} @@ -16623,20 +16927,6 @@ packages: - supports-color dev: true - /extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true - dependencies: - debug: 4.3.4(supports-color@5.5.0) - get-stream: 5.2.0 - yauzl: 2.10.0 - optionalDependencies: - '@types/yauzl': 2.10.0 - transitivePeerDependencies: - - supports-color - dev: true - /extract-zip@2.0.1(supports-color@8.1.1): resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -16825,7 +17115,7 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: - flat-cache: 3.0.4 + flat-cache: 3.1.0 /file-system-cache@2.3.0: resolution: {integrity: sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ==} @@ -16938,13 +17228,6 @@ packages: pkg-dir: 4.2.0 dev: true - /flat-cache@3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flatted: 3.2.7 - rimraf: 3.0.2 - /flat-cache@3.1.0: resolution: {integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==} engines: {node: '>=12.0.0'} @@ -16952,7 +17235,6 @@ packages: flatted: 3.2.7 keyv: 4.5.3 rimraf: 3.0.2 - dev: true /flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} @@ -17340,8 +17622,8 @@ packages: dependencies: resolve-pkg-maps: 1.0.0 - /get-tsconfig@4.7.2: - resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + /get-tsconfig@4.7.0: + resolution: {integrity: sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw==} dependencies: resolve-pkg-maps: 1.0.0 dev: true @@ -17493,7 +17775,6 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.20.2 - dev: false /globalthis@1.0.3: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} @@ -18999,25 +19280,6 @@ packages: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - /jest-haste-map@29.6.4: - resolution: {integrity: sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.6 - '@types/node': 18.17.14 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.6.4 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - dev: true - /jest-haste-map@29.7.0: resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -19035,7 +19297,6 @@ packages: walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 - dev: false /jest-leak-detector@29.7.0: resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} @@ -19270,16 +19531,6 @@ packages: merge-stream: 2.0.0 supports-color: 8.1.1 - /jest-worker@29.6.4: - resolution: {integrity: sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 18.17.14 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - dev: true - /jest-worker@29.7.0: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -19288,7 +19539,6 @@ packages: jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - dev: false /jest@29.7.0: resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} @@ -19456,7 +19706,6 @@ packages: /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -19582,7 +19831,6 @@ packages: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} dependencies: json-buffer: 3.0.1 - dev: true /keyvaluestorage-interface@1.0.0: resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} @@ -20557,7 +20805,7 @@ packages: /media-query-parser@2.0.2: resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 /media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} @@ -21826,7 +22074,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: hosted-git-info: 5.2.1 - is-core-module: 2.12.1 + is-core-module: 2.13.0 semver: 7.5.4 validate-npm-package-license: 3.0.4 dev: false @@ -21836,7 +22084,7 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: hosted-git-info: 6.1.1 - is-core-module: 2.12.1 + is-core-module: 2.13.0 semver: 7.5.4 validate-npm-package-license: 3.0.4 dev: true @@ -22967,15 +23215,6 @@ packages: react-is: 17.0.2 dev: true - /pretty-format@29.6.2: - resolution: {integrity: sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.2.0 - dev: true - /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -23293,7 +23532,6 @@ packages: /ramda@0.29.0: resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==} - dev: true /randexp@0.4.6: resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} @@ -23378,7 +23616,7 @@ packages: dependencies: '@babel/core': 7.22.15 '@babel/generator': 7.22.15 - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 ast-types: 0.14.2 commander: 2.20.3 doctrine: 3.0.0 @@ -23930,7 +24168,7 @@ packages: /relay-runtime@12.0.0: resolution: {integrity: sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 fbjs: 3.0.5 invariant: 2.2.4 transitivePeerDependencies: @@ -24217,9 +24455,10 @@ packages: resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} hasBin: true dependencies: - is-core-module: 2.12.1 + is-core-module: 2.13.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + dev: true /resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} @@ -24233,7 +24472,7 @@ packages: resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} hasBin: true dependencies: - is-core-module: 2.12.1 + is-core-module: 2.13.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -24325,7 +24564,7 @@ packages: /rtl-css-js@1.16.1: resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} dependencies: - '@babel/runtime': 7.23.1 + '@babel/runtime': 7.22.15 dev: false /run-applescript@5.0.0: @@ -25661,29 +25900,6 @@ packages: terser: 5.19.4 webpack: 5.88.2(@swc/core@1.3.80)(esbuild@0.18.20) - /terser-webpack-plugin@5.3.9(webpack@5.88.2): - resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true - dependencies: - '@jridgewell/trace-mapping': 0.3.19 - jest-worker: 27.5.1 - schema-utils: 3.3.0 - serialize-javascript: 6.0.1 - terser: 5.19.4 - webpack: 5.88.2(webpack-cli@4.9.2) - /terser@5.19.2: resolution: {integrity: sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==} engines: {node: '>=10'} @@ -25701,7 +25917,7 @@ packages: hasBin: true dependencies: '@jridgewell/source-map': 0.3.5 - acorn: 8.10.0 + acorn: 8.8.2 commander: 2.20.3 source-map-support: 0.5.21 @@ -26053,13 +26269,13 @@ packages: tslib: 1.14.1 typescript: 5.2.2 - /tsx@3.13.0: - resolution: {integrity: sha512-rjmRpTu3as/5fjNq/kOkOtihgLxuIz6pbKdj9xwP4J5jOLkBxw/rjN5ANw+KyrrOXV5uB7HC8+SrrSJxT65y+A==} + /tsx@3.12.10: + resolution: {integrity: sha512-2+46h4xvUt1aLDNvk5YBT8Uzw+b7BolGbn7iSMucYqCXZiDc+1IMghLVdw8kKjING32JFOeO+Am9posvjkeclA==} hasBin: true dependencies: - esbuild: 0.18.20 - get-tsconfig: 4.7.2 - source-map-support: 0.5.21 + '@esbuild-kit/cjs-loader': 2.4.2 + '@esbuild-kit/core-utils': 3.3.2 + '@esbuild-kit/esm-loader': 2.6.5 optionalDependencies: fsevents: 2.3.3 dev: true @@ -26273,6 +26489,12 @@ packages: /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + /types-ramda@0.29.4: + resolution: {integrity: sha512-XO/820iRsCDwqLjE8XE+b57cVGPyk1h+U9lBGpDWvbEky+NQChvHVwaKM05WnW1c5z3EVQh8NhXFmh2E/1YazQ==} + dependencies: + ts-toolbelt: 9.6.0 + dev: true + /typescript@3.9.10: resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} engines: {node: '>=4.2.0'} @@ -27353,7 +27575,7 @@ packages: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.9(webpack@5.88.2) + terser-webpack-plugin: 5.3.9(@swc/core@1.3.80)(esbuild@0.18.20)(webpack@5.88.2) watchpack: 2.4.0 webpack-cli: 4.9.2(webpack@5.88.2) webpack-sources: 3.2.3 @@ -27768,3 +27990,7 @@ packages: /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false