From 91375519efea9c9f5d1be7a88f26b37a843f5c25 Mon Sep 17 00:00:00 2001 From: Anderson Arboleya Date: Fri, 26 Jan 2024 12:06:02 -0300 Subject: [PATCH] rc: salamander (#1495) * chore: updating code owners (#1496) * docs: purge hardcoded snippets on 'using typegen' page (#1403) * chore: remove method that's same as base method (#1445) * chore: implement RC workflow (#1497) * Revert "feat: add `Predicate.getTransferTxId` helper (#1467)" * chore: fix rc release string replace (#1529) * docs: Update some hyperlinks to reference the new documentation hub (#1520) * chore: improve rc release message (#1559) * feat: GraphQL subscriptions (#1374) * chore: pin `graphql-request` to `v5` (#1567) * chore: upgrade `tsx` (#1574) * feat: migrate from Jest to Vitest (#1310) * chore: fix temp test workflow (#1579) * chore: update required node engine in `create-fuels` (#1582) * chore: add node version test matrix (#1575) * chore: fix broken rc message (#1580) * chore: update nodejs to v20 (#1544) * feat: accepting addresses as `string` (#1583) * chore: properly format the PR coverage report comment (#1586) * fix: flaky test (#1590) * docs: update `deposit-and-withdraw` page (#1591) * feat: retry mechanism (#1474) * feat: replaced `semver` dependency with custom implementation (#1594) * feat: replace `elliptic` with `@noble/curves` (#1601) * chore: fix CI failing due to missing tag in test (#1614) * feat: improve ABI Coders `decode` validation (#1426) * fix: do not generate a coverage diff without coverage artifact (#1629) * chore: pinpoint vitest to 1.0.4 (#1637) * chore: remove `ethers` dependency from `utils` (#1640) * fix: `getOperation` for `Transfer Asset` (#1619) * fix: remove external font dependencies (#1642) * fix: generate RC PR comment on `pull_request` event only (#1648) * fix: fix failing `rc` comment (#1657) * chore: add missing test group (#1658) * feat: implement browser compatibility testing (#1630) * chore: fix string replace in `rc` ci (#1659) * chore: adding extra reporters (#1661) * chore: manually trigger `rc` CI (#1660) * feat: use `submitAndAwait` graphql endpoint (#1615) * fix: flaky retry test (#1654) * feat: create a wallet without a provider (#1566) * chore!: Share single chainConfig and review node-related utilities (#1602) * chore: use new temporary coverage artifact (#1676) * fix: internalizing `findBinPath` utility (#1679) --- .changeset/angry-humans-happen.md | 2 + .changeset/big-boxes-tan.md | 2 + .changeset/bright-elephants-shop.md | 2 + .changeset/early-humans-count.md | 2 + .changeset/eight-ladybugs-cheer.md | 2 + .changeset/flat-dodos-crash.md | 2 + .changeset/fresh-terms-yell.md | 10 + .changeset/funny-radios-doubt.md | 2 + .changeset/giant-pumpkins-refuse.md | 5 + .changeset/gorgeous-tips-walk.md | 7 + .changeset/happy-days-repeat.md | 5 + .changeset/honest-schools-complain.md | 2 + .changeset/kind-hotels-divide.md | 5 + .changeset/late-mugs-kick.md | 2 + .changeset/odd-kids-buy.md | 2 + .changeset/olive-clocks-double.md | 2 + .changeset/perfect-kangaroos-speak.md | 2 + .changeset/poor-ravens-enjoy.md | 2 + .changeset/rare-snails-shave.md | 2 + .changeset/real-owls-leave.md | 2 + .changeset/rotten-seas-battle.md | 2 + .changeset/selfish-wombats-dance.md | 2 + .changeset/serious-laws-judge.md | 6 + .changeset/sharp-clocks-camp.md | 2 + .changeset/smart-lemons-end.md | 2 + .changeset/soft-falcons-agree.md | 7 + .changeset/soft-wombats-smell.md | 2 + .changeset/spicy-ducks-invent.md | 2 + .changeset/stale-lizards-shop.md | 2 + .changeset/swift-students-ring.md | 5 + .changeset/tasty-hotels-pay.md | 2 + .changeset/tasty-pets-run.md | 6 + .changeset/tender-onions-reflect.md | 2 + .changeset/tender-tips-whisper.md | 28 + .changeset/thick-windows-tease.md | 2 + .changeset/twenty-clocks-peel.md | 2 + .changeset/warm-poems-swim.md | 6 + .changeset/wet-dots-marry.md | 5 + .changeset/wicked-timers-push.md | 2 + .eslintignore | 4 +- .eslintrc.js | 7 +- .github/CODEOWNERS | 2 +- .github/actions/ci-setup/action.yaml | 2 +- .github/actions/test-setup/action.yaml | 2 +- .github/rc-list.md | 148 + .github/workflows/pr-lint.yaml | 8 + .github/workflows/rc-release.yaml | 69 + .github/workflows/test.yaml | 104 +- .gitignore | 3 + .nvmrc | 2 +- .nycrc | 15 + .prettierignore | 4 +- .vscode/extensions.json | 2 +- .vscode/launch.json | 22 - CONTRIBUTING.md | 11 +- README.md | 24 +- apps/demo-fuels/src/index.test.ts | 4 + apps/demo-fuels/tsdoc.json | 4 + apps/demo-nextjs/public/inter.ttf | Bin 0 -> 310252 bytes apps/demo-nextjs/src/app/layout.tsx | 4 +- apps/demo-typegen/contract/src/main.sw | 4 +- apps/demo-typegen/package.json | 10 +- apps/demo-typegen/predicate/.gitignore | 2 + apps/demo-typegen/predicate/Forc.toml | 7 + .../demo-typegen}/predicate/src/main.sw | 0 apps/demo-typegen/script/.gitignore | 2 + apps/demo-typegen/script/Forc.toml | 7 + apps/demo-typegen/script/src/main.sw | 5 + apps/demo-typegen/src/demo.test.ts | 110 +- apps/demo-typegen/tsdoc.json | 4 + .../guide/contracts/call-parameters.test.ts | 3 + .../calls-with-different-wallets.test.ts | 3 + .../contracts/configurable-constants.test.ts | 3 + .../guide/contracts/contract-balance.test.ts | 3 + .../guide/contracts/cost-estimation.test.ts | 3 + .../contracts/deploying-contracts.test.ts | 3 + .../src/guide/contracts/index.test.ts | 3 + .../contracts/inter-contract-calls.test.ts | 3 + .../src/guide/contracts/logs.test.ts | 3 + .../managing-deployed-contracts.test.ts | 3 + .../src/guide/contracts/multicalls.test.ts | 3 + .../contracts/simulate-transactions.test.ts | 3 + .../contracts/transaction-parameters.test.ts | 3 + .../cookbook/deposit-and-withdraw.test.ts | 64 + .../cookbook/transferring-assets.test.ts | 3 + .../src/guide/predicates/index.test.ts | 3 + .../predicate-with-configurable.test.ts | 3 + ...nd-and-spend-funds-from-predicates.test.ts | 5 +- .../scripts/script-custom-transaction.test.ts | 3 + .../scripts/script-with-configurable.test.ts | 3 + .../testing/tweaking-the-blockchain.test.ts | 3 + .../src/guide/types/address.test.ts | 3 + .../src/guide/types/arrays.test.ts | 3 + .../src/guide/types/asset-id.test.ts | 3 + .../src/guide/types/bech32.test.ts | 3 + .../src/guide/types/bits256.test.ts | 3 + .../src/guide/types/bits512.test.ts | 3 + .../src/guide/types/bytes.test.ts | 3 + .../src/guide/types/bytes32.test.ts | 3 + .../src/guide/types/conversion.test.ts | 3 + .../src/guide/types/enums.test.ts | 3 + .../src/guide/types/evm-address.test.ts | 3 + .../src/guide/types/options.test.ts | 3 + .../src/guide/types/raw-slice.test.ts | 3 + .../src/guide/types/std-string.test.ts | 3 + .../src/guide/types/string.test.ts | 3 + .../src/guide/types/struct.test.ts | 3 + .../src/guide/types/tuples.test.ts | 3 + .../src/guide/types/vector.test.ts | 3 + ...etting-started-with-wallet-manager.test.ts | 3 + ...cking-and-unlocking-wallet-manager.test.ts | 3 + ...ypting-and-decrypting-json-wallets.test.ts | 3 + apps/docs-snippets/src/utils.ts | 6 +- .../test/fixtures/forc-projects/Forc.toml | 1 + .../test/fixtures/forc-projects/index.ts | 1 + .../forc-projects/liquidity-pool/Forc.toml | 1 + .../forc-projects/liquidity-pool/src/main.sw | 51 + apps/docs-snippets/tsdoc.json | 4 + apps/docs/.vitepress/config.ts | 8 +- .../plugins/codeInContextPlugin.test.ts | 3 + apps/docs/src/guide/abi-typegen/index.md | 6 +- .../abi-typegen/using-generated-types.md | 81 +- .../guide/contracts/deploying-contracts.md | 4 +- .../guide/cookbook/deposit-and-withdraw.md | 34 +- apps/docs/src/guide/predicates/index.md | 4 +- .../src/guide/providers/retrying-calls.md | 5 + apps/docs/src/guide/scripts/index.md | 2 +- .../guide/scripts/instantiating-a-script.md | 2 +- apps/docs/src/guide/testing/index.md | 8 +- .../testing/setting-up-a-custom-chain.md | 2 +- apps/docs/src/guide/testing/testing-in-ts.md | 13 + .../src/guide/testing/testing-with-jest.md | 11 - apps/docs/src/guide/types/index.md | 2 +- apps/docs/src/guide/wallets/access.md | 6 + apps/docs/src/index.md | 6 +- internal/check-tests/README.md | 5 + internal/check-tests/package.json | 12 + internal/check-tests/src/all.test.ts | 11 + internal/check-tests/src/browser.test.ts | 10 + internal/check-tests/src/index.ts | 20 + internal/check-tests/src/node.test.ts | 10 + internal/check-tests/tsconfig.json | 7 + internal/check-tests/tsdoc.json | 4 + jest.config.ts | 28 - nodemon.config.json | 4 +- package.json | 38 +- packages/abi-coder/README.md | 2 +- packages/abi-coder/package.json | 2 +- .../abi-coder/src/coders/abstract-coder.ts | 5 - packages/abi-coder/src/coders/array.test.ts | 35 +- packages/abi-coder/src/coders/array.ts | 11 +- packages/abi-coder/src/coders/b256.test.ts | 74 +- packages/abi-coder/src/coders/b256.ts | 25 +- packages/abi-coder/src/coders/b512.test.ts | 50 +- packages/abi-coder/src/coders/b512.ts | 27 +- packages/abi-coder/src/coders/boolean.test.ts | 23 +- packages/abi-coder/src/coders/boolean.ts | 19 +- packages/abi-coder/src/coders/byte.test.ts | 40 + packages/abi-coder/src/coders/byte.ts | 16 +- packages/abi-coder/src/coders/enum.test.ts | 17 +- packages/abi-coder/src/coders/enum.ts | 4 + packages/abi-coder/src/coders/number.test.ts | 33 +- packages/abi-coder/src/coders/number.ts | 17 +- packages/abi-coder/src/coders/option.test.ts | 26 + packages/abi-coder/src/coders/option.ts | 6 + .../abi-coder/src/coders/raw-slice.test.ts | 34 + packages/abi-coder/src/coders/raw-slice.ts | 10 +- .../abi-coder/src/coders/stdString.test.ts | 29 + packages/abi-coder/src/coders/stdString.ts | 14 +- packages/abi-coder/src/coders/string.test.ts | 43 +- packages/abi-coder/src/coders/string.ts | 13 +- packages/abi-coder/src/coders/struct.test.ts | 24 + packages/abi-coder/src/coders/struct.ts | 8 +- packages/abi-coder/src/coders/tuple.test.ts | 24 + packages/abi-coder/src/coders/tuple.ts | 8 +- packages/abi-coder/src/coders/u64.test.ts | 33 +- packages/abi-coder/src/coders/u64.ts | 22 +- packages/abi-coder/src/coders/vec.test.ts | 36 + packages/abi-coder/src/coders/vec.ts | 22 +- packages/abi-coder/src/constants.ts | 1 + packages/abi-coder/src/utilities.test.ts | 4 + packages/abi-coder/test/interface.test.ts | 3 + packages/abi-coder/tsdoc.json | 4 + packages/abi-typegen/README.md | 2 +- packages/abi-typegen/package.json | 2 +- packages/abi-typegen/src/AbiTypeGen.test.ts | 26 +- packages/abi-typegen/src/abi/Abi.test.ts | 9 +- .../src/abi/configurable/Configurable.test.ts | 9 +- .../src/abi/functions/Function.test.ts | 3 + .../src/abi/types/ArrayType.test.ts | 11 +- .../src/abi/types/AssetIdType.test.ts | 3 + .../src/abi/types/B256Type.test.ts | 3 + .../src/abi/types/B512Type.test.ts | 3 + .../src/abi/types/BoolType.test.ts | 3 + .../src/abi/types/BytesType.test.ts | 3 + .../src/abi/types/EnumType.test.ts | 3 + .../src/abi/types/EvmAddressType.test.ts | 5 +- .../src/abi/types/GenericType.test.ts | 3 + .../src/abi/types/OptionType.test.ts | 3 + .../src/abi/types/RawUntypedPtr.test.ts | 3 + .../src/abi/types/RawUntypedSlice.test.ts | 3 + .../src/abi/types/StdStringType.test.ts | 3 + .../src/abi/types/StrSlicesType.test.ts | 3 + .../abi-typegen/src/abi/types/StrType.test.ts | 3 + .../src/abi/types/StructType.test.ts | 5 +- .../src/abi/types/TupleType.test.ts | 5 +- .../abi-typegen/src/abi/types/U16Type.test.ts | 3 + .../abi-typegen/src/abi/types/U32Type.test.ts | 3 + .../abi-typegen/src/abi/types/U64Type.test.ts | 3 + .../abi-typegen/src/abi/types/U8Type.test.ts | 3 + .../src/abi/types/VectorType.test.ts | 3 + packages/abi-typegen/src/cli.test.ts | 19 +- packages/abi-typegen/src/index.test.ts | 3 + packages/abi-typegen/src/runTypegen.test.ts | 66 +- .../src/templates/common/common.test.ts | 3 + .../src/templates/common/index.test.ts | 3 + .../src/templates/contract/bytecode.test.ts | 3 + .../src/templates/contract/dts.test.ts | 3 + .../src/templates/contract/factory.test.ts | 3 + .../src/templates/predicate/factory.test.ts | 3 + .../src/templates/renderHbsTemplate.test.ts | 3 + .../src/templates/script/factory.test.ts | 3 + .../utils/formatConfigurables.test.ts | 5 +- .../src/templates/utils/formatEnums.test.ts | 3 + .../src/templates/utils/formatImports.test.ts | 3 + .../src/templates/utils/formatStructs.test.ts | 3 + .../src/utils/assembleContracts.test.ts | 21 +- .../src/utils/assemblePredicates.test.ts | 28 +- .../src/utils/assembleScripts.test.ts | 28 +- .../src/utils/collectBinFilePaths.test.ts | 13 +- .../collectStorageSlotsFilePaths.test.ts | 7 +- .../src/utils/extractStructName.test.ts | 3 + .../abi-typegen/src/utils/findType.test.ts | 5 +- .../src/utils/makeConfigurable.test.ts | 5 +- .../src/utils/makeFunction.test.ts | 3 + .../abi-typegen/src/utils/makeType.test.ts | 3 + .../src/utils/parseConfigurables.test.ts | 7 +- .../src/utils/parseFunctions.test.ts | 3 + .../src/utils/parseTypeArguments.test.ts | 3 + .../abi-typegen/src/utils/parseTypes.test.ts | 3 + .../src/utils/shouldSkipType.test.ts | 3 + .../src/utils/supportedTypes.test.ts | 3 + .../src/utils/validateBinFile.test.ts | 3 + .../fixtures/templates/contract/bytecode.hbs | 2 +- .../abi-typegen/test/utils/mockVersions.ts | 15 +- packages/abi-typegen/tsdoc.json | 4 + packages/address/README.md | 2 +- packages/address/package.json | 2 +- packages/address/src/address.test.ts | 4 + packages/contract/README.md | 2 +- packages/contract/package.json | 2 +- packages/contract/src/contract-factory.ts | 5 +- packages/create-fuels/package.json | 2 +- packages/create-fuels/test/cli.test.ts | 3 + packages/create-fuels/tsconfig.json | 2 +- packages/create-fuels/tsdoc.json | 4 + packages/crypto/README.md | 2 +- packages/crypto/package.json | 2 +- packages/crypto/src/shared/keccak256.test.ts | 24 +- packages/crypto/src/shared/scrypt.test.ts | 24 +- packages/crypto/test/aes-ctr.test.ts | 12 +- packages/crypto/test/bufferFromString.test.ts | 46 +- .../crypto/test/encryptJsonWalletData.test.ts | 28 +- packages/crypto/test/envs.ts | 7 - packages/crypto/test/stringFromBuffer.test.ts | 39 +- packages/crypto/tsdoc.json | 4 + packages/errors/README.md | 2 +- packages/errors/package.json | 2 +- packages/errors/src/error-codes.ts | 1 + packages/errors/src/fuel-error.test.ts | 4 + packages/errors/src/test-utils.test.ts | 6 +- .../expect-to-throw-fuel-error.test.ts | 4 + .../errors/src/test-utils/safeExec.test.ts | 4 + packages/errors/tsdoc.json | 4 + .../fuel-gauge/src/advanced-logging.test.ts | 3 + packages/fuel-gauge/src/auth-testing.test.ts | 3 + .../fuel-gauge/src/await-execution.test.ts | 97 + packages/fuel-gauge/src/bytes.test.ts | 3 + .../fuel-gauge/src/call-test-contract.test.ts | 3 + .../src/configurable-contract.test.ts | 3 + .../fuel-gauge/src/contract-factory.test.ts | 3 + packages/fuel-gauge/src/contract.test.ts | 13 +- .../fuel-gauge/src/coverage-contract.test.ts | 3 + packages/fuel-gauge/src/doc-examples.test.ts | 113 +- packages/fuel-gauge/src/e2e-script.test.ts | 4 + packages/fuel-gauge/src/edge-cases.test.ts | 3 + packages/fuel-gauge/src/fee.test.ts | 3 + .../src/funding-transaction.test.ts | 9 +- .../src/generic-types-contract.test.ts | 3 + packages/fuel-gauge/src/min-gas.test.ts | 3 + .../src/multi-token-contract.test.ts | 3 + .../fuel-gauge/src/payable-annotation.test.ts | 3 + packages/fuel-gauge/src/policies.test.ts | 3 + .../src/predicate-conditional-inputs.test.ts | 3 + .../src/predicate/predicate-arguments.test.ts | 3 + .../predicate/predicate-configurables.test.ts | 3 + .../predicate/predicate-estimations.test.ts | 3 + .../predicate/predicate-evaluations.test.ts | 3 + .../predicate/predicate-input-data.test.ts | 3 + .../predicate/predicate-invalidations.test.ts | 3 + .../predicate/predicate-with-contract.test.ts | 44 +- .../predicate/predicate-with-script.test.ts | 3 + .../utils/predicate/fundPredicate.ts | 3 +- packages/fuel-gauge/src/raw-slice.test.ts | 3 + packages/fuel-gauge/src/revert-error.test.ts | 3 + .../fuel-gauge/src/script-main-args.test.ts | 3 + .../src/script-with-configurable.test.ts | 3 + .../src/script-with-vectors.test.ts | 3 + packages/fuel-gauge/src/small-bytes.test.ts | 3 + .../fuel-gauge/src/std-lib-string.test.ts | 3 + .../src/storage-test-contract.test.ts | 3 + .../src/token-test-contract.test.ts | 3 + .../src/transaction-response.test.ts | 43 +- .../src/transaction-summary.test.ts | 3 + packages/fuel-gauge/src/vector-types.test.ts | 3 + packages/fuel-gauge/src/vectors.test.ts | 3 + .../test/fixtures/forc-projects/Forc.toml | 1 - .../coverage-contract/src/main.sw | 12 +- packages/fuel-gauge/test/fixtures/index.ts | 1 - packages/fuel-gauge/tsdoc.json | 4 + packages/fuels/.gitignore | 4 +- packages/fuels/package.json | 3 +- packages/fuels/src/bin.ts | 2 +- packages/fuels/src/cli.test.ts | 30 +- packages/fuels/src/cli.ts | 11 +- .../commands/build/buildSwayProgram.test.ts | 82 + .../cli/commands/build/buildSwayProgram.ts | 32 + .../commands/build/buildSwayPrograms.test.ts | 140 +- .../cli/commands/build/buildSwayPrograms.ts | 48 +- .../cli/commands/build/forcHandlers.test.ts | 32 + .../src/cli/commands/build/forcHandlers.ts | 19 + .../fuels/src/cli/commands/build/index.ts | 2 +- .../cli/commands/deploy/createWallet.test.ts | 3 + .../commands/deploy/getDeployConfig.test.ts | 3 + .../commands/dev/autoStartFuelCore.test.ts | 107 + .../src/cli/commands/dev/autoStartFuelCore.ts | 69 + .../cli/commands/dev/defaultChainConfig.ts | 522 -- .../fuels/src/cli/commands/dev/index.test.ts | 124 +- packages/fuels/src/cli/commands/dev/index.ts | 4 +- .../cli/commands/dev/startFuelCore.test.ts | 176 - .../src/cli/commands/dev/startFuelCore.ts | 130 - packages/fuels/src/cli/commands/init/index.ts | 36 +- .../init/shouldUseBuiltinForc.test.ts | 31 +- .../init/shouldUseBuiltinFuelCore.test.ts | 20 +- .../fuels/src/cli/commands/withConfig.test.ts | 23 +- .../fuels/src/cli/config/forcUtils.test.ts | 3 + .../fuels/src/cli/config/loadConfig.test.ts | 128 +- packages/fuels/src/cli/config/loadConfig.ts | 2 +- packages/fuels/src/cli/index.test.ts | 5 +- packages/fuels/src/cli/utils/findBinPath.ts | 16 - packages/fuels/src/cli/utils/logger.test.ts | 7 +- packages/fuels/src/index.test.ts | 3 + packages/fuels/src/run.ts | 6 + packages/fuels/test/features/build.test.ts | 183 +- packages/fuels/test/features/deploy.test.ts | 53 +- packages/fuels/test/features/dev.test.ts | 54 +- packages/fuels/test/features/init.test.ts | 80 +- .../fixtures/{config => }/fuels.config.ts | 6 +- .../fixtures/project/.fuels/chainConfig.json | 515 ++ .../fixtures/{project => workspace}/Forc.toml | 0 .../contracts/bar/Forc.toml | 0 .../contracts/bar/src/main.sw | 0 .../contracts/foo/Forc.toml | 0 .../contracts/foo/src/main.sw | 0 .../predicates/predicate/Forc.toml | 0 .../predicates/predicate/src/main.sw | 5 + .../scripts/script/Forc.toml | 0 .../scripts/script/src/main.sw | 0 .../fuels/test/utils/mockAutoStartFuelCore.ts | 26 + packages/fuels/test/utils/mockLogger.ts | 8 +- .../fuels/test/utils/mockStartFuelCore.ts | 21 - .../fuels/test/utils/resetDiskAndMocks.ts | 18 +- packages/fuels/test/utils/runCommands.ts | 167 +- packages/fuels/tsdoc.json | 4 + packages/hasher/README.md | 2 +- packages/hasher/package.json | 2 +- packages/hasher/src/hasher.test.ts | 12 +- packages/hasher/tsdoc.json | 4 + packages/hdwallet/README.md | 2 +- packages/hdwallet/package.json | 2 +- packages/hdwallet/src/hdwallet.test.ts | 4 + packages/hdwallet/tsdoc.json | 4 + packages/interfaces/README.md | 2 +- packages/interfaces/package.json | 2 +- packages/interfaces/src/index.ts | 4 +- packages/math/README.md | 2 +- packages/math/package.json | 2 +- packages/math/src/bn.test.ts | 4 + packages/math/src/functional.test.ts | 3 + packages/math/tsdoc.json | 4 + packages/merkle/README.md | 2 +- packages/merkle/package.json | 2 +- .../src/binary/binaryMerkleTree.test.ts | 3 + .../src/sparse/sparseMerkleTree.test.ts | 3 + packages/merkle/src/sum/sumMerkleTree.test.ts | 3 + packages/merkle/tsdoc.json | 4 + packages/mnemonic/README.md | 2 +- packages/mnemonic/package.json | 2 +- packages/mnemonic/src/mnemonic.test.ts | 4 + packages/mnemonic/src/utils.test.ts | 4 + packages/mnemonic/tsdoc.json | 4 + packages/predicate/README.md | 2 +- packages/predicate/package.json | 2 +- packages/predicate/src/predicate.ts | 8 +- .../src/utils/getPredicateRoot.test.ts | 3 + .../test/features/predicate-functions.test.ts | 4 + .../features/predicate-transactions.test.ts | 12 +- packages/program/README.md | 2 +- packages/program/package.json | 2 +- packages/program/src/contract.test.ts | 5 +- .../src/functions/base-invocation-scope.ts | 4 +- packages/providers/README.md | 2 +- packages/providers/codegen.json | 2 +- packages/providers/package.json | 10 +- .../providers/src/coin-quantityfy.test.ts | 3 + .../providers/src/fuel-graphql-subscriber.ts | 81 + packages/providers/src/operations.graphql | 59 +- packages/providers/src/provider.ts | 144 +- .../hash-transaction.test.ts | 3 + .../transaction-request.test.ts | 3 + .../transaction-request.ts | 5 +- .../transaction-response.ts | 60 +- .../assemble-transaction-summary.test.ts | 26 + .../calculate-transaction-fee.test.ts | 3 + .../src/transaction-summary/date.test.ts | 15 +- .../src/transaction-summary/input.test.ts | 3 + .../transaction-summary/operations.test.ts | 133 +- .../src/transaction-summary/operations.ts | 100 +- .../src/transaction-summary/output.test.ts | 3 + .../src/transaction-summary/receipt.test.ts | 3 + .../src/transaction-summary/status.test.ts | 3 + .../src/utils/auto-retry-fetch.test.ts | 130 + .../providers/src/utils/auto-retry-fetch.ts | 87 + .../src/utils/block-explorer.test.ts | 3 + packages/providers/src/utils/gas.test.ts | 3 + packages/providers/src/utils/json.test.ts | 3 + .../src/utils/merge-quantities.test.ts | 3 + packages/providers/src/utils/receipts.test.ts | 3 + packages/providers/src/utils/time.test.ts | 3 + .../test/__snapshots__/provider.test.ts.snap | 6 +- .../providers/test/auto-retry-fetch.test.ts | 33 + packages/providers/test/memory-cache.test.ts | 4 + packages/providers/test/provider.test.ts | 228 +- packages/providers/tsconfig.json | 2 +- packages/script/package.json | 2 +- .../script/src/script-invocation-scope.ts | 28 +- packages/script/src/script.test.ts | 3 + packages/signer/README.md | 2 +- packages/signer/package.json | 9 +- packages/signer/src/index.ts | 1 - packages/signer/src/signer.test.ts | 6 +- packages/signer/src/signer.ts | 67 +- packages/signer/tsdoc.json | 4 + packages/testcases/tsdoc.json | 4 + packages/transactions/README.md | 2 +- packages/transactions/package.json | 2 +- .../src/coders/byte-array.test.ts | 4 + .../transactions/src/coders/input.test.ts | 4 + .../transactions/src/coders/output.test.ts | 4 + .../transactions/src/coders/policy.test.ts | 4 + .../transactions/src/coders/receipt.test.ts | 4 + packages/transactions/src/coders/receipt.ts | 6 +- .../src/coders/storage-slot.test.ts | 4 + .../src/coders/transaction.test.ts | 4 + .../src/coders/tx-pointer.test.ts | 4 + .../transactions/src/coders/utxo-id.test.ts | 4 + .../transactions/src/coders/witness.test.ts | 4 + packages/transactions/tsdoc.json | 4 + packages/utils/README.md | 2 +- packages/utils/package.json | 12 +- packages/utils/src/cli-utils.ts | 1 + .../utils/src/cli-utils/findBinPath.test.ts | 57 + packages/utils/src/cli-utils/findBinPath.ts | 17 + packages/utils/src/index.test.ts | 4 + packages/utils/src/index.ts | 1 + packages/utils/src/test-util.test.ts | 3 + .../src/test-utils/expectToBeInRange.test.ts | 4 + .../src/test-utils/getForcProject.test.ts | 64 +- .../utils/src/test-utils/getForcProject.ts | 2 +- packages/utils/src/utils/arrayify.test.ts | 4 + packages/utils/src/utils/arrayify.ts | 2 +- .../utils/src/utils/capitalizeString.test.ts | 4 + packages/utils/src/utils/chainConfig.json | 515 ++ .../utils/src/utils/chunkAndPadBytes.test.ts | 8 +- packages/utils/src/utils/concat.test.ts | 9 +- packages/utils/src/utils/concat.ts | 7 +- .../utils/src/utils/defaultChainConfig.ts | 6 + packages/utils/src/utils/hexlify.test.ts | 4 + packages/utils/src/utils/hexlify.ts | 7 +- .../utils/src/utils/normalizeString.test.ts | 4 + packages/utils/tsdoc.json | 4 + packages/utils/tsup.config.ts | 1 + packages/versions/README.md | 2 +- packages/versions/package.json | 8 +- .../versions/scripts/rewriteVersions.test.ts | 17 +- packages/versions/scripts/rewriteVersions.ts | 4 +- packages/versions/src/cli.test.ts | 30 +- packages/versions/src/index.test.ts | 3 + .../checkFuelCoreVersionCompatibility.test.ts | 9 +- .../lib/checkFuelCoreVersionCompatibility.ts | 17 +- .../src/lib/colorizeUserVersion.test.ts | 3 + .../src/lib/compareSystemVersions.test.ts | 9 +- .../versions/src/lib/compareSystemVersions.ts | 11 +- .../src/lib/getBuiltinVersions.test.ts | 3 + .../src/lib/getSystemVersions.test.ts | 23 +- packages/versions/src/lib/semver.test.ts | 43 + packages/versions/src/lib/semver.ts | 46 + packages/versions/tsdoc.json | 4 + packages/wallet-manager/README.md | 2 +- packages/wallet-manager/package.json | 2 +- .../src/storages/memory-storage.test.ts | 4 + .../src/vaults/mnemonic-vault.test.ts | 8 +- .../src/vaults/mnemonic-vault.ts | 9 +- .../src/vaults/privatekey-vault.test.ts | 4 + .../src/vaults/privatekey-vault.ts | 8 +- .../wallet-manager/src/wallet-manager.test.ts | 11 +- packages/wallet-manager/src/wallet-manager.ts | 15 +- packages/wallet-manager/tsdoc.json | 4 + packages/wallet/README.md | 2 +- packages/wallet/package.json | 4 +- packages/wallet/src/account.test.ts | 116 +- packages/wallet/src/account.ts | 61 +- packages/wallet/src/base-unlocked-wallet.ts | 16 +- packages/wallet/src/configs.test.ts | 7 +- packages/wallet/src/keystore-wallet.test.ts | 9 +- packages/wallet/src/keystore-wallet.ts | 7 +- .../src/test-utils/defaultChainConfig.ts | 515 -- .../wallet/src/test-utils/launchNode.test.ts | 223 +- packages/wallet/src/test-utils/launchNode.ts | 155 +- .../launchNodeAndGetWallets.test.ts | 95 + .../wallet/src/test-utils/seedTestWallet.ts | 4 +- packages/wallet/src/transfer.test.ts | 7 +- packages/wallet/src/types/GenerateOptions.ts | 2 +- packages/wallet/src/utils.test.ts | 27 +- packages/wallet/src/wallet-unlocked.test.ts | 115 +- packages/wallet/src/wallet.test.ts | 178 +- packages/wallet/src/wallet.ts | 21 +- packages/wallet/src/wallets.ts | 28 +- packages/wordlists/README.md | 2 +- packages/wordlists/package.json | 2 +- packages/wordlists/src/wordlists.test.ts | 3 + packages/wordlists/tsdoc.json | 4 + pnpm-lock.yaml | 4403 ++++++++++++----- scripts/run-node.sh | 2 + scripts/{ci-test.sh => tests-ci.sh} | 7 +- scripts/tests-coverage-diff.ts | 27 + scripts/tests-coverage-merge.ts | 36 + scripts/tests-find.sh | 15 + scripts/tests-validate.sh | 12 + tsconfig.base.json | 3 +- vite.base.config.mts | 43 + vite.browser.config.mts | 36 + jest.env.ts => vite.env.ts | 8 +- vite.node.config.mts | 14 + 554 files changed, 10371 insertions(+), 4535 deletions(-) create mode 100644 .changeset/angry-humans-happen.md create mode 100644 .changeset/big-boxes-tan.md create mode 100644 .changeset/bright-elephants-shop.md create mode 100644 .changeset/early-humans-count.md create mode 100644 .changeset/eight-ladybugs-cheer.md create mode 100644 .changeset/flat-dodos-crash.md create mode 100644 .changeset/fresh-terms-yell.md create mode 100644 .changeset/funny-radios-doubt.md create mode 100644 .changeset/giant-pumpkins-refuse.md create mode 100644 .changeset/gorgeous-tips-walk.md create mode 100644 .changeset/happy-days-repeat.md create mode 100644 .changeset/honest-schools-complain.md create mode 100644 .changeset/kind-hotels-divide.md create mode 100644 .changeset/late-mugs-kick.md create mode 100644 .changeset/odd-kids-buy.md create mode 100644 .changeset/olive-clocks-double.md create mode 100644 .changeset/perfect-kangaroos-speak.md create mode 100644 .changeset/poor-ravens-enjoy.md create mode 100644 .changeset/rare-snails-shave.md create mode 100644 .changeset/real-owls-leave.md create mode 100644 .changeset/rotten-seas-battle.md create mode 100644 .changeset/selfish-wombats-dance.md create mode 100644 .changeset/serious-laws-judge.md create mode 100644 .changeset/sharp-clocks-camp.md create mode 100644 .changeset/smart-lemons-end.md create mode 100644 .changeset/soft-falcons-agree.md create mode 100644 .changeset/soft-wombats-smell.md create mode 100644 .changeset/spicy-ducks-invent.md create mode 100644 .changeset/stale-lizards-shop.md create mode 100644 .changeset/swift-students-ring.md create mode 100644 .changeset/tasty-hotels-pay.md create mode 100644 .changeset/tasty-pets-run.md create mode 100644 .changeset/tender-onions-reflect.md create mode 100644 .changeset/tender-tips-whisper.md create mode 100644 .changeset/thick-windows-tease.md create mode 100644 .changeset/twenty-clocks-peel.md create mode 100644 .changeset/warm-poems-swim.md create mode 100644 .changeset/wet-dots-marry.md create mode 100644 .changeset/wicked-timers-push.md create mode 100644 .github/rc-list.md create mode 100644 .github/workflows/rc-release.yaml create mode 100644 .nycrc delete mode 100644 .vscode/launch.json create mode 100644 apps/demo-fuels/tsdoc.json create mode 100644 apps/demo-nextjs/public/inter.ttf create mode 100644 apps/demo-typegen/predicate/.gitignore create mode 100644 apps/demo-typegen/predicate/Forc.toml rename {packages/fuels/test/fixtures/project/predicates => apps/demo-typegen}/predicate/src/main.sw (100%) create mode 100644 apps/demo-typegen/script/.gitignore create mode 100644 apps/demo-typegen/script/Forc.toml create mode 100644 apps/demo-typegen/script/src/main.sw create mode 100644 apps/demo-typegen/tsdoc.json create mode 100644 apps/docs-snippets/src/guide/cookbook/deposit-and-withdraw.test.ts rename {packages/fuel-gauge => apps/docs-snippets}/test/fixtures/forc-projects/liquidity-pool/Forc.toml (86%) create mode 100644 apps/docs-snippets/test/fixtures/forc-projects/liquidity-pool/src/main.sw create mode 100644 apps/docs-snippets/tsdoc.json create mode 100644 apps/docs/src/guide/providers/retrying-calls.md create mode 100644 apps/docs/src/guide/testing/testing-in-ts.md delete mode 100644 apps/docs/src/guide/testing/testing-with-jest.md create mode 100644 internal/check-tests/README.md create mode 100644 internal/check-tests/package.json create mode 100644 internal/check-tests/src/all.test.ts create mode 100644 internal/check-tests/src/browser.test.ts create mode 100644 internal/check-tests/src/index.ts create mode 100644 internal/check-tests/src/node.test.ts create mode 100644 internal/check-tests/tsconfig.json create mode 100644 internal/check-tests/tsdoc.json delete mode 100644 jest.config.ts create mode 100644 packages/abi-coder/tsdoc.json create mode 100644 packages/abi-typegen/tsdoc.json create mode 100644 packages/create-fuels/tsdoc.json delete mode 100644 packages/crypto/test/envs.ts create mode 100644 packages/crypto/tsdoc.json create mode 100644 packages/errors/tsdoc.json create mode 100644 packages/fuel-gauge/src/await-execution.test.ts create mode 100644 packages/fuel-gauge/tsdoc.json create mode 100644 packages/fuels/src/cli/commands/build/buildSwayProgram.test.ts create mode 100644 packages/fuels/src/cli/commands/build/buildSwayProgram.ts create mode 100644 packages/fuels/src/cli/commands/build/forcHandlers.test.ts create mode 100644 packages/fuels/src/cli/commands/build/forcHandlers.ts create mode 100644 packages/fuels/src/cli/commands/dev/autoStartFuelCore.test.ts create mode 100644 packages/fuels/src/cli/commands/dev/autoStartFuelCore.ts delete mode 100644 packages/fuels/src/cli/commands/dev/defaultChainConfig.ts delete mode 100644 packages/fuels/src/cli/commands/dev/startFuelCore.test.ts delete mode 100644 packages/fuels/src/cli/commands/dev/startFuelCore.ts delete mode 100644 packages/fuels/src/cli/utils/findBinPath.ts create mode 100644 packages/fuels/src/run.ts rename packages/fuels/test/fixtures/{config => }/fuels.config.ts (84%) create mode 100644 packages/fuels/test/fixtures/project/.fuels/chainConfig.json rename packages/fuels/test/fixtures/{project => workspace}/Forc.toml (100%) rename packages/fuels/test/fixtures/{project => workspace}/contracts/bar/Forc.toml (100%) rename packages/fuels/test/fixtures/{project => workspace}/contracts/bar/src/main.sw (100%) rename packages/fuels/test/fixtures/{project => workspace}/contracts/foo/Forc.toml (100%) rename packages/fuels/test/fixtures/{project => workspace}/contracts/foo/src/main.sw (100%) rename packages/fuels/test/fixtures/{project => workspace}/predicates/predicate/Forc.toml (100%) create mode 100644 packages/fuels/test/fixtures/workspace/predicates/predicate/src/main.sw rename packages/fuels/test/fixtures/{project => workspace}/scripts/script/Forc.toml (100%) rename packages/fuels/test/fixtures/{project => workspace}/scripts/script/src/main.sw (100%) create mode 100644 packages/fuels/test/utils/mockAutoStartFuelCore.ts delete mode 100644 packages/fuels/test/utils/mockStartFuelCore.ts create mode 100644 packages/fuels/tsdoc.json create mode 100644 packages/hasher/tsdoc.json create mode 100644 packages/hdwallet/tsdoc.json create mode 100644 packages/math/tsdoc.json create mode 100644 packages/merkle/tsdoc.json create mode 100644 packages/mnemonic/tsdoc.json create mode 100644 packages/providers/src/fuel-graphql-subscriber.ts create mode 100644 packages/providers/src/utils/auto-retry-fetch.test.ts create mode 100644 packages/providers/src/utils/auto-retry-fetch.ts create mode 100644 packages/providers/test/auto-retry-fetch.test.ts create mode 100644 packages/signer/tsdoc.json create mode 100644 packages/testcases/tsdoc.json create mode 100644 packages/transactions/tsdoc.json create mode 100644 packages/utils/src/cli-utils.ts create mode 100644 packages/utils/src/cli-utils/findBinPath.test.ts create mode 100644 packages/utils/src/cli-utils/findBinPath.ts create mode 100644 packages/utils/src/utils/chainConfig.json create mode 100644 packages/utils/src/utils/defaultChainConfig.ts create mode 100644 packages/utils/tsdoc.json create mode 100644 packages/versions/src/lib/semver.test.ts create mode 100644 packages/versions/src/lib/semver.ts create mode 100644 packages/versions/tsdoc.json create mode 100644 packages/wallet-manager/tsdoc.json delete mode 100644 packages/wallet/src/test-utils/defaultChainConfig.ts create mode 100644 packages/wallet/src/test-utils/launchNodeAndGetWallets.test.ts create mode 100644 packages/wordlists/tsdoc.json rename scripts/{ci-test.sh => tests-ci.sh} (68%) create mode 100644 scripts/tests-coverage-diff.ts create mode 100644 scripts/tests-coverage-merge.ts create mode 100755 scripts/tests-find.sh create mode 100755 scripts/tests-validate.sh create mode 100644 vite.base.config.mts create mode 100644 vite.browser.config.mts rename jest.env.ts => vite.env.ts (83%) create mode 100644 vite.node.config.mts diff --git a/.changeset/angry-humans-happen.md b/.changeset/angry-humans-happen.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/angry-humans-happen.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/big-boxes-tan.md b/.changeset/big-boxes-tan.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/big-boxes-tan.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/bright-elephants-shop.md b/.changeset/bright-elephants-shop.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/bright-elephants-shop.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/early-humans-count.md b/.changeset/early-humans-count.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/early-humans-count.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/eight-ladybugs-cheer.md b/.changeset/eight-ladybugs-cheer.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/eight-ladybugs-cheer.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/flat-dodos-crash.md b/.changeset/flat-dodos-crash.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/flat-dodos-crash.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/fresh-terms-yell.md b/.changeset/fresh-terms-yell.md new file mode 100644 index 00000000000..d3600a5c0b4 --- /dev/null +++ b/.changeset/fresh-terms-yell.md @@ -0,0 +1,10 @@ +--- +"@fuel-ts/program": minor +"@fuel-ts/providers": minor +"@fuel-ts/wallet": minor +"@fuel-ts/predicate": minor +--- + +- Transaction execution can now be await with the `{awaitExecution: true}` option on `Provider.sendTransaction` +- Added same functionality to accounts (unlocked wallet, predicate) +- `BaseInvocationScope` internally now uses `{awaitExecution: true}` to reduce amount of network calls diff --git a/.changeset/funny-radios-doubt.md b/.changeset/funny-radios-doubt.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/funny-radios-doubt.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/giant-pumpkins-refuse.md b/.changeset/giant-pumpkins-refuse.md new file mode 100644 index 00000000000..7e1b1c12c66 --- /dev/null +++ b/.changeset/giant-pumpkins-refuse.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/abi-coder": minor +--- + +Improve decode validation of ABI Coders diff --git a/.changeset/gorgeous-tips-walk.md b/.changeset/gorgeous-tips-walk.md new file mode 100644 index 00000000000..aaf5d8d2bd8 --- /dev/null +++ b/.changeset/gorgeous-tips-walk.md @@ -0,0 +1,7 @@ +--- +"create-fuels": patch +"fuels": patch +"@fuel-ts/utils": patch +--- + +Fixing and internalizing `findBinPath` utility diff --git a/.changeset/happy-days-repeat.md b/.changeset/happy-days-repeat.md new file mode 100644 index 00000000000..26a20c45403 --- /dev/null +++ b/.changeset/happy-days-repeat.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/wallet": minor +--- + +Made provider argument optional for wallet instantiation diff --git a/.changeset/honest-schools-complain.md b/.changeset/honest-schools-complain.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/honest-schools-complain.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/kind-hotels-divide.md b/.changeset/kind-hotels-divide.md new file mode 100644 index 00000000000..7683a229116 --- /dev/null +++ b/.changeset/kind-hotels-divide.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/transactions": minor +--- + +update deposit and withdraw doc page diff --git a/.changeset/late-mugs-kick.md b/.changeset/late-mugs-kick.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/late-mugs-kick.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/odd-kids-buy.md b/.changeset/odd-kids-buy.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/odd-kids-buy.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/olive-clocks-double.md b/.changeset/olive-clocks-double.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/olive-clocks-double.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/perfect-kangaroos-speak.md b/.changeset/perfect-kangaroos-speak.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/perfect-kangaroos-speak.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/poor-ravens-enjoy.md b/.changeset/poor-ravens-enjoy.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/poor-ravens-enjoy.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/rare-snails-shave.md b/.changeset/rare-snails-shave.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/rare-snails-shave.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/real-owls-leave.md b/.changeset/real-owls-leave.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/real-owls-leave.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/rotten-seas-battle.md b/.changeset/rotten-seas-battle.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/rotten-seas-battle.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/selfish-wombats-dance.md b/.changeset/selfish-wombats-dance.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/selfish-wombats-dance.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/serious-laws-judge.md b/.changeset/serious-laws-judge.md new file mode 100644 index 00000000000..23babd84b11 --- /dev/null +++ b/.changeset/serious-laws-judge.md @@ -0,0 +1,6 @@ +--- +"@fuel-ts/providers": minor +"@fuel-ts/errors": patch +--- + +Implemented GraphQL subscriptions diff --git a/.changeset/sharp-clocks-camp.md b/.changeset/sharp-clocks-camp.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/sharp-clocks-camp.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/smart-lemons-end.md b/.changeset/smart-lemons-end.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/smart-lemons-end.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/soft-falcons-agree.md b/.changeset/soft-falcons-agree.md new file mode 100644 index 00000000000..114d1a3c8c8 --- /dev/null +++ b/.changeset/soft-falcons-agree.md @@ -0,0 +1,7 @@ +--- +"@fuel-ts/providers": minor +"@fuel-ts/wallet": minor +"@fuel-ts/wallet-manager": minor +--- + +accepting string as address instead of only AbstractAddress diff --git a/.changeset/soft-wombats-smell.md b/.changeset/soft-wombats-smell.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/soft-wombats-smell.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/spicy-ducks-invent.md b/.changeset/spicy-ducks-invent.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/spicy-ducks-invent.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/stale-lizards-shop.md b/.changeset/stale-lizards-shop.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/stale-lizards-shop.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/swift-students-ring.md b/.changeset/swift-students-ring.md new file mode 100644 index 00000000000..6ec5502856a --- /dev/null +++ b/.changeset/swift-students-ring.md @@ -0,0 +1,5 @@ +--- +"create-fuels": minor +--- + +Update supported node version in create fuels diff --git a/.changeset/tasty-hotels-pay.md b/.changeset/tasty-hotels-pay.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/tasty-hotels-pay.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/tasty-pets-run.md b/.changeset/tasty-pets-run.md new file mode 100644 index 00000000000..4216a1257be --- /dev/null +++ b/.changeset/tasty-pets-run.md @@ -0,0 +1,6 @@ +--- +"@fuel-ts/interfaces": patch +"@fuel-ts/utils": patch +--- + +Remove ethers dependency from the utils package diff --git a/.changeset/tender-onions-reflect.md b/.changeset/tender-onions-reflect.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/tender-onions-reflect.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/tender-tips-whisper.md b/.changeset/tender-tips-whisper.md new file mode 100644 index 00000000000..d7132a6ef25 --- /dev/null +++ b/.changeset/tender-tips-whisper.md @@ -0,0 +1,28 @@ +--- +"fuels": minor +"@fuel-ts/utils": minor +"@fuel-ts/wallet": minor +--- + +chore!: share single chainconfig and `launchNode` utility throughout the codebase. + +- `startFuelCore` now re-uses `launchNode` instead of having its own node-launching logic +- `@fuel-ts/utils` now exports a `defaultChainConfig` and a `defaultConsensusKey` which is used everywhere in the source code. +- The `chainConfig.json` file inside the `.fuel-core` folder at the root also uses the same chain config. The `run-node` script has been modified to copy over the contents of the chain config file from the utils package. + +# Breaking Changes + +- Multiple fuel-core config-related options have been removed from `LaunchNodeOptions`: + + - `chainConfigPath` + - `consensusKey` + - `useInMemoryDb` + - `poaInstant` + +- The only way to pass in these config values now is through the `args` property, i.e.: + +```ts +const { cleanup, ip, port } = await launchNode({ + args: ["--poa-interval-period", "750ms", "--poa-instant", "false"], +}); +``` diff --git a/.changeset/thick-windows-tease.md b/.changeset/thick-windows-tease.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/thick-windows-tease.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/twenty-clocks-peel.md b/.changeset/twenty-clocks-peel.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/twenty-clocks-peel.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/warm-poems-swim.md b/.changeset/warm-poems-swim.md new file mode 100644 index 00000000000..c4462671a88 --- /dev/null +++ b/.changeset/warm-poems-swim.md @@ -0,0 +1,6 @@ +--- +"@fuel-ts/signer": minor +--- + +- Stopped exporting `getCurve()` / secp256k1 +- Replaced `elliptic` with `@noble/curves` diff --git a/.changeset/wet-dots-marry.md b/.changeset/wet-dots-marry.md new file mode 100644 index 00000000000..60c9eeba545 --- /dev/null +++ b/.changeset/wet-dots-marry.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/providers": minor +--- + +Made getOperations to consider multiple assets transfer for a Transfer Asset operation diff --git a/.changeset/wicked-timers-push.md b/.changeset/wicked-timers-push.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/wicked-timers-push.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.eslintignore b/.eslintignore index 3e521fec0b4..af1f9fa5503 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,7 +4,9 @@ dist/ out/ apps/demo-fuels/src/sway-programs-api -apps/demo-typegen/src/generated-types +apps/demo-typegen/src/contract-types +apps/demo-typegen/src/script-types +apps/demo-typegen/src/predicate-types apps/demo-nextjs apps/demo-react-cra apps/demo-react-vite diff --git a/.eslintrc.js b/.eslintrc.js index 370c57d37cf..6e46310f855 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,6 +21,12 @@ module.exports = { }, }, rules: { + 'no-restricted-syntax': [ + 'off', + { + selector: 'ForOfStatement', + }, + ], '@typescript-eslint/no-non-null-assertion': 1, // Disable error on devDependencies importing since this isn't a TS library 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], @@ -56,7 +62,6 @@ module.exports = { 'warn', { argsIgnorePattern: '^_', - varsIgnorePattern: '^_', }, ], '@typescript-eslint/no-explicit-any': 'error', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1616404c9e7..a37a76de9b9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @digorithm @arboleya @camsjams @Torres-ssf @Dhaiwat10 @danielbate @nedsalk +* @digorithm @arboleya @Torres-ssf @Dhaiwat10 @danielbate @nedsalk diff --git a/.github/actions/ci-setup/action.yaml b/.github/actions/ci-setup/action.yaml index 9343b93b1cc..aff49b09ed1 100644 --- a/.github/actions/ci-setup/action.yaml +++ b/.github/actions/ci-setup/action.yaml @@ -2,7 +2,7 @@ name: "CI Setup" inputs: node-version: description: "Node version" - default: 18.17.1 + default: 20.10.0 pnpm-version: description: "PNPM version" default: 8.9.0 diff --git a/.github/actions/test-setup/action.yaml b/.github/actions/test-setup/action.yaml index 94cadf1e6ab..b6541ef9e54 100644 --- a/.github/actions/test-setup/action.yaml +++ b/.github/actions/test-setup/action.yaml @@ -2,7 +2,7 @@ name: "Test Setup" inputs: node-version: description: "Node version" - default: 18.17.1 + default: 20.10.0 pnpm-version: description: "PNPM version" default: 8.9.0 diff --git a/.github/rc-list.md b/.github/rc-list.md new file mode 100644 index 00000000000..57069e7c6e8 --- /dev/null +++ b/.github/rc-list.md @@ -0,0 +1,148 @@ +- [ ] African Elephant +- [ ] Albatross +- [ ] Alpaca +- [ ] Angelfish +- [ ] Ant +- [ ] Appaloosa +- [ ] Arabian Horse +- [ ] Arctic Fox +- [ ] Asian Elephant +- [ ] Bactrian Camel +- [ ] Bald Eagle +- [ ] Beagle +- [ ] Bee +- [ ] Beluga Whale +- [ ] Black Bear +- [ ] Blue Jay +- [ ] Blue Whale +- [ ] Bobcat +- [ ] Brown Bear +- [ ] Bulldog +- [ ] Butterfly +- [ ] Camel +- [ ] Caracal +- [ ] Cardinal +- [ ] Cheetah +- [ ] Chicken +- [ ] Chimpanzee +- [ ] Clownfish +- [ ] Clydesdale +- [ ] Cockatoo +- [ ] Cockroach +- [ ] Coral +- [ ] Cougar +- [ ] Coyote +- [ ] Crab +- [ ] Cricket +- [ ] Crow +- [ ] Cuttlefish +- [ ] Dachshund +- [ ] Dingo +- [ ] Dolphin +- [ ] Domestic Cat +- [ ] Domestic Dog +- [ ] Dragonfly +- [ ] Dromedary +- [ ] Duck +- [ ] Elephant +- [ ] Falcon +- [ ] Fennec Fox +- [ ] Finch +- [ ] Firefly +- [ ] Flamingo +- [ ] Friesian +- [ ] German Shepherd +- [ ] Giraffe +- [ ] Golden Retriever +- [ ] Goose +- [ ] Gorilla +- [ ] Grasshopper +- [ ] Grevy's Zebra +- [ ] Grizzly Bear +- [ ] Hawk +- [ ] Hermit Crab +- [ ] Hippopotamus +- [ ] Hornet +- [ ] Horse +- [ ] Hummingbird +- [ ] Humpback Whale +- [ ] Husky +- [ ] Hyena +- [ ] Jaguar +- [ ] Jellyfish +- [ ] Kangaroo +- [ ] Katydid +- [ ] Kingfisher +- [ ] Koala +- [ ] Labrador Retriever +- [ ] Ladybug +- [ ] Leopard +- [ ] Lion +- [ ] Llama +- [ ] Lobster +- [ ] Lynx +- [ ] Macaw +- [ ] Magpie +- [ ] Manatee +- [ ] Mantis Shrimp +- [ ] Moth +- [ ] Mountain Lion +- [ ] Mountain Zebra +- [ ] Mustang +- [ ] Narwhal +- [ ] Nautilus +- [ ] Octopus +- [ ] Orca +- [ ] Ostrich +- [ ] Owl +- [ ] Palomino +- [ ] Panda +- [ ] Panther +- [ ] Parrot +- [ ] Peacock +- [ ] Pelican +- [ ] Penguin +- [ ] Pigeon +- [ ] Plains Zebra +- [ ] Polar Bear +- [ ] Pony +- [ ] Poodle +- [ ] Praying Mantis +- [ ] Pufferfish +- [ ] Puffin +- [ ] Quarter Horse +- [ ] Raven +- [ ] Red Panda +- [ ] Rhino +- [ ] Rhinoceros Beetle +- [ ] Robin +- [ ] Rooster +- [ ] Rottweiler +- [x] Salamander +- [ ] Scarab Beetle +- [ ] Sea Anemone +- [ ] Sea Cucumber +- [ ] Sea Lion +- [ ] Sea Urchin +- [ ] Seahorse +- [ ] Seal +- [ ] Serval +- [ ] Shetland Pony +- [ ] Shrimp +- [ ] Snow Leopard +- [ ] Sparrow +- [ ] Sperm Whale +- [ ] Squid +- [ ] Starfish +- [ ] Stork +- [ ] Swan +- [ ] Thoroughbred +- [ ] Tiger +- [ ] Toucan +- [ ] Turkey +- [ ] Vicuña +- [ ] Walrus +- [ ] Wasp +- [ ] Wolf +- [ ] Woodpecker +- [ ] Zebra diff --git a/.github/workflows/pr-lint.yaml b/.github/workflows/pr-lint.yaml index 6978cf6b1f2..de7a77c30da 100644 --- a/.github/workflows/pr-lint.yaml +++ b/.github/workflows/pr-lint.yaml @@ -19,6 +19,14 @@ jobs: - uses: amannn/action-semantic-pull-request@v4 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + chore + docs + ci + rc validate-changeset: name: Validate PR Changeset diff --git a/.github/workflows/rc-release.yaml b/.github/workflows/rc-release.yaml new file mode 100644 index 00000000000..e50d9082af2 --- /dev/null +++ b/.github/workflows/rc-release.yaml @@ -0,0 +1,69 @@ +name: Release to @rc- tag on npm + +on: + pull_request: + branches: + - "master" + +jobs: + release-pr: + # Switch to `true` for release candidates + if: ${{ true && startsWith(github.head_ref, 'rc/') }} + name: "Release RC to npm" + runs-on: ubuntu-latest + permissions: write-all + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.ref }} + + - name: CI Setup + uses: ./.github/actions/ci-setup + + - name: Ensure NPM access + run: npm whoami + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Build + run: pnpm build + + - name: Get RC name + uses: frabert/replace-string-action@v2 + id: rc_name + with: + string: ${{ github.head_ref }} + pattern: "rc/" + replace-with: "rc-" + + - name: Release to @${{ steps.rc_name.outputs.replaced }} tag on npm + id: release + run: | + echo "${{ steps.rc_name.outputs.replaced }}" + pnpm changeset:next + git add .changeset/fuel-labs-ci.md + pnpm changeset version --snapshot ${{ steps.rc_name.outputs.replaced }} + changetsets=$(pnpm changeset publish --tag ${{ steps.rc_name.outputs.replaced }}) + published_version=$(echo "$changetsets" | grep -oP '@\K([0-9]+\.){2}[0-9]+-${{ steps.rc_name.outputs.replaced }}-\d+' | head -1) + echo "published_version=$published_version" >> $GITHUB_OUTPUT + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Add PR comment + uses: mshick/add-pr-comment@v2 + with: + message: | + RC published under the `${{ steps.rc_name.outputs.replaced }}` tag. + + Install it: + ```bash + pnpm add fuels@${{ steps.rc_name.outputs.replaced }} + pnpm add fuels@${{ steps.release.outputs.published_version }} + ``` + Check it out: + - https://www.npmjs.com/package/fuels/v/${{ steps.rc_name.outputs.replaced }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f09ae9d77fc..e2b8da8fd2a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -17,8 +17,19 @@ jobs: doc-folder-path: "apps/docs/src" spellcheck-config-path: "apps/docs/.spellcheck.yml" - test: - runs-on: buildjet-4vcpu-ubuntu-2204 + environments: + runs-on: ubuntu-latest + # name will be node@ or browser + name: ${{ matrix.env.name }}${{ matrix.env.name == 'node' && '@' || ''}}${{ matrix.env.name == 'node' && matrix.env.version || ''}} + strategy: + fail-fast: false + matrix: + env: + [ + { name: node, version: 18.18.2 }, + { name: node, version: 20 }, + { name: browser }, + ] timeout-minutes: 25 steps: - name: Checkout @@ -26,6 +37,8 @@ jobs: - name: CI Setup uses: ./.github/actions/test-setup + with: + node-version: ${{ matrix.env.version || 20 }} - name: Verify package.json integrity run: pnpm lint:package-jsons @@ -40,43 +53,86 @@ jobs: - name: Lint run: pnpm lint + - name: Validate Tests + run: pnpm test:validate + + - name: Run Tests - ${{ matrix.env.name }} + run: pnpm ci:test --${{ matrix.env.name }} + + - name: Upload Coverage - ${{ matrix.env.name }} + uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.env.name }} + path: coverage/environments/${{ matrix.env.name }} + + test: + runs-on: ubuntu-latest + needs: [environments] + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: CI Setup + uses: ./.github/actions/test-setup + + - name: Generate Coverage Directory + run: mkdir -p coverage/environments + + - name: Download Coverage Artifact for Node Tests + uses: actions/download-artifact@v3 + with: + name: node + path: coverage/environments + + - name: Generate Coverage + run: pnpm test:coverage-merge + - name: Find PR Number uses: jwalton/gh-find-current-pr@v1 id: findPr - - name: Run tests and collect coverage - if: ${{ !steps.findPr.outputs.number }} - run: pnpm ci:test -- --json --coverage --testLocationInResults --outputFile=report.master.json - - - name: Upload coverage to Github artifacts + - name: Upload Master Coverage Artifact uses: actions/upload-artifact@v3 if: ${{ !steps.findPr.outputs.number }} with: - name: coverage-reports - path: report.master.json + name: coverage-master + path: coverage/report - - name: Download coverage artifact + - name: Download Master Coverage Artifact uses: dawidd6/action-download-artifact@v2 - if: ${{ steps.findPr.outputs.number }} + # change to pr number check when rc/salamander has been merged + if: false with: workflow: test.yaml branch: master - name: coverage-reports - path: coverage-reports + name: coverage-master + path: coverage-master + + - name: Download Master Coverage Artifact (temporary) + uses: dawidd6/action-download-artifact@v2 + if: ${{ (steps.findPr.outputs.number) }} + with: + workflow: test.yaml + run_id: 7625704138 + name: coverage-master + path: coverage-master + + - name: Generate Coverage Diff + if: ${{ (steps.findPr.outputs.number) }} + run: pnpm test:coverage-diff - - name: Run tests and post reports to PR - uses: ArtiomTr/jest-coverage-report-action@v2 + - name: Report Coverage + uses: thollander/actions-comment-pull-request@v2 if: ${{ steps.findPr.outputs.number }} with: - github-token: ${{ secrets.GITHUB_TOKEN }} - package-manager: pnpm - annotations: failed-tests - test-script: pnpm ci:test - prnumber: ${{ steps.findPr.outputs.number }} - base-coverage-file: coverage-reports/report.master.json - - live-tests: - name: Run Live Tests + filePath: coverage/report/coverage-diff.txt + pr_number: ${{ (steps.findPr.outputs.number) }} + comment_tag: diff + mode: recreate + create_if_not_exists: true + + test-e2e: + name: e2e if: github.head_ref == 'changeset-release/master' runs-on: ubuntu-latest timeout-minutes: 5 diff --git a/.gitignore b/.gitignore index b75e9f4c6ff..0f6c8b2285c 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,6 @@ Forc.lock # dir used by fuel-core for storing db data .fuel-core/db + +# This chainConfig is copied over from @fuel-ts/utils at runtime, no need to track it +.fuel-core/configs/chainConfig.json \ No newline at end of file diff --git a/.nvmrc b/.nvmrc index 4a1f488b6c3..d5a159609d0 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.17.1 +20.10.0 diff --git a/.nycrc b/.nycrc new file mode 100644 index 00000000000..c455077f077 --- /dev/null +++ b/.nycrc @@ -0,0 +1,15 @@ +{ + "extends": "@istanbuljs/nyc-config-typescript", + "reporter": ["json-summary"], + "include": ["packages", "internal", "apps"], + "exclude": [ + "**/node_modules/**", + "**/dist/**", + "**/test/**", + "**/*.test.ts", + "**/*.d.ts", + "packages/fuel-gauge/**", + "apps/demo-*", + "apps/docs" + ] +} diff --git a/.prettierignore b/.prettierignore index 056b5fbe74c..b1453972f95 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,7 +8,9 @@ apps/demo-fuels/src/sway-programs-api apps/demo-nextjs apps/demo-react-cra apps/demo-react-vite -apps/demo-typegen/src/generated-types +apps/demo-typegen/src/contract-types +apps/demo-typegen/src/script-types +apps/demo-typegen/src/predicate-types apps/docs/.vitepress/cache/ packages/fuels/test/fixtures/project diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f9973f00345..05ca3907232 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,6 @@ { "recommendations": [ - "orta.vscode-jest", + "ZixuanChen.vitest-explorer", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint" ] diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 7b10da99fa4..00000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "configurations": [ - { - "type": "node", - "name": "vscode-jest-tests.v2", - "request": "launch", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "disableOptimisticBPs": true, - "program": "${workspaceFolder}/node_modules/jest/bin/jest.js", - "cwd": "${workspaceFolder}", - "args": [ - "--runInBand", - "--watchAll=false", - "--testNamePattern", - "${jest.testNamePattern}", - "--runTestsByPath", - "${jest.testFile}" - ] - } - ] -} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c67d51409e6..acfb6efa036 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,14 +107,17 @@ And then run the tests in another terminal tab: # run all tests pnpm test -# run tests while passing other flags to sub-program -pnpm test -- --coverage --my-other-flag +# watch all tests +pnpm test:watch # run tests for a specific package -pnpm test packages/my-desired-package +pnpm test:filter packages/my-desired-package # run tests for a specific file -pnpm test packages/my-desired-package/src/my.test.ts +pnpm test:filter packages/my-desired-package/src/my.test.ts + +# run tests while passing other flags to sub-program +pnpm test -- --coverage --my-other-flag ``` Or if you want to start a local Fuel-Core node and run all tests serially you can do: diff --git a/README.md b/README.md index c4a61735412..2fd1abbf38a 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,20 @@ [![test](https://github.com/FuelLabs/fuels-ts/actions/workflows/test.yaml/badge.svg)](https://github.com/FuelLabs/fuels-ts/actions/workflows/test.yaml) [![npm](https://img.shields.io/npm/v/fuels)](https://www.npmjs.com/package/fuels) -[![docs](https://img.shields.io/badge/docs-fuels.ts-brightgreen.svg?style=flat)](https://fuellabs.github.io/fuels-ts/) +[![docs](https://img.shields.io/badge/docs-fuels.ts-brightgreen.svg?style=flat)](https://docs.fuel.network/docs/fuels-ts/) [![discord](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) # Resources -The [documentation](https://fuellabs.github.io/fuels-ts) site is your main stop for resources. +The [documentation](https://docs.fuel.network/docs/fuels-ts/) site is your main stop for resources. -- [Quickstart](https://fuellabs.github.io/fuel-docs/master/quickstart/developer-quickstart.html) -- [Documentation](https://fuellabs.github.io/fuels-ts) - - [Wallets](https://fuellabs.github.io/fuels-ts/guide/wallets/) - - [Contracts](https://fuellabs.github.io/fuels-ts/guide/contracts/) - - [Scripts](https://fuellabs.github.io/fuels-ts/guide/scripts/) - - [Predicates](https://fuellabs.github.io/fuels-ts/guide/predicates/) - - [ABI Typegen](https://fuellabs.github.io/fuels-ts/guide/abi-typegen/) +- [Quickstart](https://docs.fuel.network/docs/intro/quickstart-contract/) +- [Documentation](https://docs.fuel.network/docs/fuels-ts/) + - [Wallets](https://docs.fuel.network/docs/fuels-ts/wallets/) + - [Contracts](https://docs.fuel.network/docs/fuels-ts/contracts/) + - [Scripts](https://docs.fuel.network/docs/fuels-ts/scripts/) + - [Predicates](https://docs.fuel.network/docs/fuels-ts/predicates/) + - [ABI Typegen](https://docs.fuel.network/docs/fuels-ts/abi-typegen/) - [Contributing](https://github.com/FuelLabs/fuels-ts/blob/master/CONTRIBUTING.md) - [The Fuel Forum](https://forum.fuel.network/) - [The Fuel Ecosystem](#the-fuel-ecosystem) @@ -51,7 +51,7 @@ console.log(new Wallet("0x0000...0000")); Fuels include some utility commands via built-in CLI tool. -Check the [docs](https://fuellabs.github.io/fuels-ts) for more info. +Check the [docs](https://docs.fuel.network/docs/fuels-ts/) for more info. ```console $ npm add fuels @@ -80,8 +80,8 @@ Commands: Learn more about the Fuel Ecosystem. -- [🌴 Sway](https://fuellabs.github.io/sway/) — The new language, empowering everyone to build reliable and efficient smart contracts -- [🧰 Forc](https://fuellabs.github.io/sway/v0.30.1/forc/index.html) — The Fuel toolbox: _Build, deploy and manage your sway projects_ +- [🌴 Sway](https://docs.fuel.network/docs/sway/) — The new language, empowering everyone to build reliable and efficient smart contracts +- [🧰 Forc](https://docs.fuel.network/docs/forc/) — The Fuel toolbox: _Build, deploy and manage your sway projects_ - [⚙️ Fuel Core](https://github.com/FuelLabs/fuel-core) — The new FuelVM, a blazingly fast blockchain VM - [🔗 Fuel Specs](https://github.com/FuelLabs/fuel-specs) — The Fuel protocol specifications - [💼 Fuels Wallet](https://github.com/FuelLabs/fuels-wallet) — The Official Fuels Wallet diff --git a/apps/demo-fuels/src/index.test.ts b/apps/demo-fuels/src/index.test.ts index bbe32a6c313..cb5d1d61c6c 100644 --- a/apps/demo-fuels/src/index.test.ts +++ b/apps/demo-fuels/src/index.test.ts @@ -14,6 +14,10 @@ import { SampleAbi__factory } from './sway-programs-api'; import bytecode from './sway-programs-api/contracts/SampleAbi.hex'; let gasPrice: BN; + +/** + * @group node + */ describe('ExampleContract', () => { beforeAll(async () => { const provider = await Provider.create(FUEL_NETWORK_URL); diff --git a/apps/demo-fuels/tsdoc.json b/apps/demo-fuels/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/apps/demo-fuels/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/apps/demo-nextjs/public/inter.ttf b/apps/demo-nextjs/public/inter.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5e4851f0ab7e0268da6ce903306e2f871ee19821 GIT binary patch literal 310252 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 z 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%0D1lG~DE2ff7BeoTnFxKM!4172W=IjHdV9zf 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 (https://fuel.network/)", "scripts": { "pretest": "run-s build:forc build:types", - "build:forc": "pnpm fuels-forc build -p contract", - "build:types": "pnpm fuels typegen -i contract/out/debug/demo-contract-abi.json -o src/generated-types" + "build:forc": "run-p forc:*", + "forc:contract": "pnpm fuels-forc build -p contract", + "forc:script": "pnpm fuels-forc build -p script", + "forc:predicate": "pnpm fuels-forc build -p predicate", + "build:types": "run-p types:*", + "types:contract": "pnpm fuels typegen -i contract/out/debug/demo-contract-abi.json -o src/contract-types", + "types:script": "pnpm fuels typegen -i script/out/debug/script-abi.json -o src/script-types --script", + "types:predicate": "pnpm fuels typegen -i predicate/out/debug/predicate-abi.json -o src/predicate-types --predicate" }, "license": "Apache-2.0", "dependencies": { diff --git a/apps/demo-typegen/predicate/.gitignore b/apps/demo-typegen/predicate/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/apps/demo-typegen/predicate/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/apps/demo-typegen/predicate/Forc.toml b/apps/demo-typegen/predicate/Forc.toml new file mode 100644 index 00000000000..b3325bf1458 --- /dev/null +++ b/apps/demo-typegen/predicate/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "predicate" + +[dependencies] diff --git a/packages/fuels/test/fixtures/project/predicates/predicate/src/main.sw b/apps/demo-typegen/predicate/src/main.sw similarity index 100% rename from packages/fuels/test/fixtures/project/predicates/predicate/src/main.sw rename to apps/demo-typegen/predicate/src/main.sw diff --git a/apps/demo-typegen/script/.gitignore b/apps/demo-typegen/script/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/apps/demo-typegen/script/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/apps/demo-typegen/script/Forc.toml b/apps/demo-typegen/script/Forc.toml new file mode 100644 index 00000000000..937fd72852b --- /dev/null +++ b/apps/demo-typegen/script/Forc.toml @@ -0,0 +1,7 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "script" + +[dependencies] diff --git a/apps/demo-typegen/script/src/main.sw b/apps/demo-typegen/script/src/main.sw new file mode 100644 index 00000000000..93cb6bc581d --- /dev/null +++ b/apps/demo-typegen/script/src/main.sw @@ -0,0 +1,5 @@ +script; + +fn main() -> u8 { + 10 +} diff --git a/apps/demo-typegen/src/demo.test.ts b/apps/demo-typegen/src/demo.test.ts index 484f865eefb..74024025643 100644 --- a/apps/demo-typegen/src/demo.test.ts +++ b/apps/demo-typegen/src/demo.test.ts @@ -1,18 +1,49 @@ -// #region Testing-with-jest-ts +// #region Testing-in-ts-ts import { safeExec } from '@fuel-ts/errors/test-utils'; import { generateTestWallet } from '@fuel-ts/wallet/test-utils'; import type { BN } from 'fuels'; -import { ContractFactory, Provider, toHex, BaseAssetId, Wallet, FUEL_NETWORK_URL } from 'fuels'; - -import { DemoContractAbi__factory } from './generated-types'; -import bytecode from './generated-types/DemoContractAbi.hex'; +import { + ContractFactory, + Provider, + toHex, + BaseAssetId, + Wallet, + FUEL_NETWORK_URL, + Address, +} from 'fuels'; + +import storageSlots from '../contract/out/debug/demo-contract-storage_slots.json'; + +import { DemoContractAbi__factory } from './contract-types'; +import bytecode from './contract-types/DemoContractAbi.hex'; +import { PredicateAbi__factory } from './predicate-types'; +import { ScriptAbi__factory } from './script-types'; let gasPrice: BN; + +/** + * @group node + */ describe('ExampleContract', () => { beforeAll(async () => { const provider = await Provider.create(FUEL_NETWORK_URL); ({ minGasPrice: gasPrice } = provider.getGasConfig()); }); + it('with imported storage slots', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + const wallet = await generateTestWallet(provider, [[500_000, BaseAssetId]]); + + // #region typegen-demo-contract-storage-slots + // #context import storageSlots from './contract/out/debug/demo-contract-storage_slots.json'; + + const contract = await DemoContractAbi__factory.deployContract(bytecode, wallet, { + storageSlots, + gasPrice, + }); + // #endregion typegen-demo-contract-storage-slots + + expect(contract.id).toBeTruthy(); + }); it('should return the input', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); const wallet = await generateTestWallet(provider, [[500_000, BaseAssetId]]); @@ -20,6 +51,7 @@ describe('ExampleContract', () => { // Deploy const factory = new ContractFactory(bytecode, DemoContractAbi__factory.abi, wallet); const contract = await factory.deployContract({ gasPrice }); + const contractId = contract.id; // Call const { value } = await contract.functions @@ -31,11 +63,15 @@ describe('ExampleContract', () => { expect(value.toHex()).toEqual(toHex(1337)); // You can also make a call using the factory - const contractInstance = DemoContractAbi__factory.connect(contract.id, wallet); + // #region typegen-demo-contract-factory-connect + // #context import { DemoContractAbi__factory } from './types'; + + const contractInstance = DemoContractAbi__factory.connect(contractId, wallet); const { value: v2 } = await contractInstance.functions .return_input(1337) .txParams({ gasPrice, gasLimit: 10_000 }) .call(); + // #endregion typegen-demo-contract-factory-connect expect(v2.toHex()).toBe(toHex(1337)); }); @@ -43,8 +79,13 @@ describe('ExampleContract', () => { const provider = await Provider.create(FUEL_NETWORK_URL); const wallet = await generateTestWallet(provider, [[500_000, BaseAssetId]]); + // #region typegen-demo-contract-factory-deploy + // #context import { DemoContractAbi__factory } from './types'; + // #context import bytecode from './types/DemoContractAbi.hex'; + // Deploy const contract = await DemoContractAbi__factory.deployContract(bytecode, wallet, { gasPrice }); + // #endregion typegen-demo-contract-factory-deploy // Call const { value } = await contract.functions @@ -56,7 +97,7 @@ describe('ExampleContract', () => { expect(value.toHex()).toEqual(toHex(1337)); }); }); -// #endregion Testing-with-jest-ts +// #endregion Testing-in-ts-ts it('should throw when simulating via contract factory with wallet with no resources', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); @@ -89,3 +130,58 @@ it('should throw when dry running via contract factory with wallet with no resou expect((error).message).toMatch('not enough coins to fit the target'); }); + +test('Example script', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + const wallet = await generateTestWallet(provider, [[500_000, BaseAssetId]]); + + // TODO: investigate why do we need to specify the gasLimit here. If we don't specify it, the call fails saying `FuelError: Gas limit '0' is lower than the required: '19'.` + + // #region typegen-demo-script + // #context import { ScriptAbi__factory } from './types'; + + const script = ScriptAbi__factory.createInstance(wallet); + const { value } = await script.functions + .main() + .txParams({ + gasPrice: provider.getGasConfig().minGasPrice, + gasLimit: 10_000, + }) + .call(); + // #endregion typegen-demo-script + // @ts-expect-error TODO: investitage - typegen is expecting value to be a number but the value being returned is the string '0xa' + expect(value.toNumber()).toBe(10); +}); + +test('Example predicate', async () => { + // #region typegen-demo-predicate + // #context import { PredicateAbi__factory } from './types'; + + // In this exchange, we are first transferring some coins to the predicate + const provider = await Provider.create(FUEL_NETWORK_URL); + const wallet = await generateTestWallet(provider, [[500_000, BaseAssetId]]); + const receiver = Wallet.fromAddress(Address.fromRandom(), provider); + + const predicate = PredicateAbi__factory.createInstance(provider); + + const tx = await wallet.transfer(predicate.address, 100_000, BaseAssetId, { + gasPrice: provider.getGasConfig().minGasPrice, + gasLimit: 50, + }); + await tx.wait(); + + const initialPredicateBalance = await predicate.getBalance(); + + // Then we are transferring some coins from the predicate to a random address (receiver) + const tx2 = await predicate.transfer(receiver.address, 50_000, BaseAssetId, { + gasPrice: provider.getGasConfig().minGasPrice, + gasLimit: 50, + }); + await tx2.wait(); + + expect((await receiver.getBalance()).toNumber()).toEqual(50_000); + expect((await predicate.getBalance()).toNumber()).toBeLessThan( + initialPredicateBalance.toNumber() + ); + // #endregion typegen-demo-predicate +}); diff --git a/apps/demo-typegen/tsdoc.json b/apps/demo-typegen/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/apps/demo-typegen/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/apps/docs-snippets/src/guide/contracts/call-parameters.test.ts b/apps/docs-snippets/src/guide/contracts/call-parameters.test.ts index e7dff259132..deca9db3808 100644 --- a/apps/docs-snippets/src/guide/contracts/call-parameters.test.ts +++ b/apps/docs-snippets/src/guide/contracts/call-parameters.test.ts @@ -4,6 +4,9 @@ import { BN, BaseAssetId } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; let provider: Provider; diff --git a/apps/docs-snippets/src/guide/contracts/calls-with-different-wallets.test.ts b/apps/docs-snippets/src/guide/contracts/calls-with-different-wallets.test.ts index c0f4d3c7ead..232eec8f9c5 100644 --- a/apps/docs-snippets/src/guide/contracts/calls-with-different-wallets.test.ts +++ b/apps/docs-snippets/src/guide/contracts/calls-with-different-wallets.test.ts @@ -4,6 +4,9 @@ import { FUEL_NETWORK_URL, Provider, WalletUnlocked } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let deployedContract: Contract; diff --git a/apps/docs-snippets/src/guide/contracts/configurable-constants.test.ts b/apps/docs-snippets/src/guide/contracts/configurable-constants.test.ts index a9326298b23..25349667a09 100644 --- a/apps/docs-snippets/src/guide/contracts/configurable-constants.test.ts +++ b/apps/docs-snippets/src/guide/contracts/configurable-constants.test.ts @@ -7,6 +7,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { getTestWallet } from '../../utils'; +/** + * @group node + */ describe('configurable-constants', () => { let wallet: WalletUnlocked; diff --git a/apps/docs-snippets/src/guide/contracts/contract-balance.test.ts b/apps/docs-snippets/src/guide/contracts/contract-balance.test.ts index 513b03add9a..cfa83ec3e42 100644 --- a/apps/docs-snippets/src/guide/contracts/contract-balance.test.ts +++ b/apps/docs-snippets/src/guide/contracts/contract-balance.test.ts @@ -4,6 +4,9 @@ import { Wallet, BN, BaseAssetId, Provider, FUEL_NETWORK_URL } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; let provider: Provider; diff --git a/apps/docs-snippets/src/guide/contracts/cost-estimation.test.ts b/apps/docs-snippets/src/guide/contracts/cost-estimation.test.ts index d946b662a1e..7aa93a9de0c 100644 --- a/apps/docs-snippets/src/guide/contracts/cost-estimation.test.ts +++ b/apps/docs-snippets/src/guide/contracts/cost-estimation.test.ts @@ -3,6 +3,9 @@ import { BaseAssetId, type Contract, type Provider } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; let provider: Provider; diff --git a/apps/docs-snippets/src/guide/contracts/deploying-contracts.test.ts b/apps/docs-snippets/src/guide/contracts/deploying-contracts.test.ts index 290a2320206..1bee45e5e21 100644 --- a/apps/docs-snippets/src/guide/contracts/deploying-contracts.test.ts +++ b/apps/docs-snippets/src/guide/contracts/deploying-contracts.test.ts @@ -5,6 +5,9 @@ import { join } from 'path'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { getTestWallet } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let PRIVATE_KEY: string; let projectsPath: string; diff --git a/apps/docs-snippets/src/guide/contracts/index.test.ts b/apps/docs-snippets/src/guide/contracts/index.test.ts index 9f59c61fbd6..b2a627007e3 100644 --- a/apps/docs-snippets/src/guide/contracts/index.test.ts +++ b/apps/docs-snippets/src/guide/contracts/index.test.ts @@ -3,6 +3,9 @@ import type { Contract } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/contracts/inter-contract-calls.test.ts b/apps/docs-snippets/src/guide/contracts/inter-contract-calls.test.ts index 603a71f3a80..63a2c8e4935 100644 --- a/apps/docs-snippets/src/guide/contracts/inter-contract-calls.test.ts +++ b/apps/docs-snippets/src/guide/contracts/inter-contract-calls.test.ts @@ -7,6 +7,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { getTestWallet } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let wallet: WalletUnlocked; let simpleToken: Contract; diff --git a/apps/docs-snippets/src/guide/contracts/logs.test.ts b/apps/docs-snippets/src/guide/contracts/logs.test.ts index 8bc759e3a04..9bdec2f8497 100644 --- a/apps/docs-snippets/src/guide/contracts/logs.test.ts +++ b/apps/docs-snippets/src/guide/contracts/logs.test.ts @@ -4,6 +4,9 @@ import { BN } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; let provider: Provider; diff --git a/apps/docs-snippets/src/guide/contracts/managing-deployed-contracts.test.ts b/apps/docs-snippets/src/guide/contracts/managing-deployed-contracts.test.ts index bb30ec6eff4..10580b42d76 100644 --- a/apps/docs-snippets/src/guide/contracts/managing-deployed-contracts.test.ts +++ b/apps/docs-snippets/src/guide/contracts/managing-deployed-contracts.test.ts @@ -7,6 +7,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { getTestWallet } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; let contractId: AbstractAddress; diff --git a/apps/docs-snippets/src/guide/contracts/multicalls.test.ts b/apps/docs-snippets/src/guide/contracts/multicalls.test.ts index 55686685193..0f99c716766 100644 --- a/apps/docs-snippets/src/guide/contracts/multicalls.test.ts +++ b/apps/docs-snippets/src/guide/contracts/multicalls.test.ts @@ -7,6 +7,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { getTestWallet } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let echoContract: Contract; let counterContract: Contract; diff --git a/apps/docs-snippets/src/guide/contracts/simulate-transactions.test.ts b/apps/docs-snippets/src/guide/contracts/simulate-transactions.test.ts index 496670b215b..847771c3087 100644 --- a/apps/docs-snippets/src/guide/contracts/simulate-transactions.test.ts +++ b/apps/docs-snippets/src/guide/contracts/simulate-transactions.test.ts @@ -8,6 +8,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { it('should successfully simulate contract call with forwarded amount', async () => { const contract = await createAndDeployContractFromProject( diff --git a/apps/docs-snippets/src/guide/contracts/transaction-parameters.test.ts b/apps/docs-snippets/src/guide/contracts/transaction-parameters.test.ts index eb7f369d538..7c5ce65d7ba 100644 --- a/apps/docs-snippets/src/guide/contracts/transaction-parameters.test.ts +++ b/apps/docs-snippets/src/guide/contracts/transaction-parameters.test.ts @@ -4,6 +4,9 @@ import { BN, PolicyType } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; let provider: Provider; diff --git a/apps/docs-snippets/src/guide/cookbook/deposit-and-withdraw.test.ts b/apps/docs-snippets/src/guide/cookbook/deposit-and-withdraw.test.ts new file mode 100644 index 00000000000..dfc1b2c120b --- /dev/null +++ b/apps/docs-snippets/src/guide/cookbook/deposit-and-withdraw.test.ts @@ -0,0 +1,64 @@ +import type { Contract, WalletUnlocked, Provider } from 'fuels'; +import { ContractFactory, BaseAssetId, Wallet, ZeroBytes32, getAssetId } from 'fuels'; + +import { + DocSnippetProjectsEnum, + getDocsSnippetsForcProject, +} from '../../../test/fixtures/forc-projects'; +import { getTestWallet } from '../../utils'; + +/** + * @group node + */ +describe(__filename, () => { + let sender: WalletUnlocked; + let liquidityPoolContract: Contract; + let provider: Provider; + + beforeAll(async () => { + sender = await getTestWallet(); + + const { abiContents, binHexlified } = getDocsSnippetsForcProject( + DocSnippetProjectsEnum.LIQUIDITY_POOL + ); + provider = sender.provider; + const factory = new ContractFactory(binHexlified, abiContents, sender); + const { minGasPrice } = sender.provider.getGasConfig(); + liquidityPoolContract = await factory.deployContract({ gasPrice: minGasPrice }); + }); + + it('deposit and withdraw cookbook guide', async () => { + // #region deposit-and-withdraw-cookbook-2 + const depositAmount = 100_000; + const liquidityOwner = Wallet.generate({ provider }); + + // the subId used to mint the new asset is a zero b256 on the contract + const subId = ZeroBytes32; + const contractId = liquidityPoolContract.id.toB256(); + + const assetId = getAssetId(contractId, subId); + + await liquidityPoolContract.functions + .deposit({ value: liquidityOwner.address.toB256() }) + .callParams({ forward: [depositAmount, BaseAssetId] }) + .txParams({ gasLimit: 1_000, variableOutputs: 1, gasPrice: 1 }) + .call(); + + const liquidityAmount = await liquidityOwner.getBalance(assetId); + + expect(liquidityAmount.toNumber()).toBe(depositAmount * 2); + // #endregion deposit-and-withdraw-cookbook-2 + + // #region deposit-and-withdraw-cookbook-3 + await liquidityPoolContract.functions + .withdraw({ value: liquidityOwner.address.toB256() }) + .callParams({ forward: [depositAmount, BaseAssetId] }) + .txParams({ gasLimit: 1_000, variableOutputs: 1, gasPrice: 1 }) + .call(); + + const baseAssetAfterWithdraw = await liquidityOwner.getBalance(BaseAssetId); + + expect(baseAssetAfterWithdraw.toNumber()).toBe(depositAmount / 2); + // #endregion deposit-and-withdraw-cookbook-3 + }); +}); diff --git a/apps/docs-snippets/src/guide/cookbook/transferring-assets.test.ts b/apps/docs-snippets/src/guide/cookbook/transferring-assets.test.ts index 59f3e2c0429..d1ca605fd9a 100644 --- a/apps/docs-snippets/src/guide/cookbook/transferring-assets.test.ts +++ b/apps/docs-snippets/src/guide/cookbook/transferring-assets.test.ts @@ -7,6 +7,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { getTestWallet } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let sender: WalletUnlocked; let deployedContract: Contract; diff --git a/apps/docs-snippets/src/guide/predicates/index.test.ts b/apps/docs-snippets/src/guide/predicates/index.test.ts index 57ea16c3171..ddeaaff06fc 100644 --- a/apps/docs-snippets/src/guide/predicates/index.test.ts +++ b/apps/docs-snippets/src/guide/predicates/index.test.ts @@ -2,6 +2,9 @@ import { FUEL_NETWORK_URL, Provider, Predicate } from 'fuels'; import { DocSnippetProjectsEnum, getDocsSnippetsForcProject } from '../../../test/fixtures/forc-projects'; +/** + * @group node + */ describe(__filename, () => { const { abiContents: jsonAbi, binHexlified: binary } = getDocsSnippetsForcProject( DocSnippetProjectsEnum.RETURN_TRUE_PREDICATE diff --git a/apps/docs-snippets/src/guide/predicates/predicate-with-configurable.test.ts b/apps/docs-snippets/src/guide/predicates/predicate-with-configurable.test.ts index 8321c6e58fc..4178f0cac61 100644 --- a/apps/docs-snippets/src/guide/predicates/predicate-with-configurable.test.ts +++ b/apps/docs-snippets/src/guide/predicates/predicate-with-configurable.test.ts @@ -6,6 +6,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { getTestWallet } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let wallet: WalletUnlocked; let gasPrice: BN; diff --git a/apps/docs-snippets/src/guide/predicates/send-and-spend-funds-from-predicates.test.ts b/apps/docs-snippets/src/guide/predicates/send-and-spend-funds-from-predicates.test.ts index 6cd38a81e05..857fbd5fd2b 100644 --- a/apps/docs-snippets/src/guide/predicates/send-and-spend-funds-from-predicates.test.ts +++ b/apps/docs-snippets/src/guide/predicates/send-and-spend-funds-from-predicates.test.ts @@ -8,6 +8,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { getTestWallet } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let walletWithFunds: WalletUnlocked; let provider: Provider; @@ -54,7 +57,7 @@ describe(__filename, () => { }); const tx2 = await predicate.transfer( - receiverWallet.address, + receiverWallet.address.toB256(), amountToPredicate - 1000, BaseAssetId, { diff --git a/apps/docs-snippets/src/guide/scripts/script-custom-transaction.test.ts b/apps/docs-snippets/src/guide/scripts/script-custom-transaction.test.ts index ad2c185150b..4149cfe75c0 100644 --- a/apps/docs-snippets/src/guide/scripts/script-custom-transaction.test.ts +++ b/apps/docs-snippets/src/guide/scripts/script-custom-transaction.test.ts @@ -7,6 +7,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { defaultTxParams, getTestWallet } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let wallet: WalletUnlocked; let provider: Provider; diff --git a/apps/docs-snippets/src/guide/scripts/script-with-configurable.test.ts b/apps/docs-snippets/src/guide/scripts/script-with-configurable.test.ts index ef604f4757c..7951238aa5e 100644 --- a/apps/docs-snippets/src/guide/scripts/script-with-configurable.test.ts +++ b/apps/docs-snippets/src/guide/scripts/script-with-configurable.test.ts @@ -7,6 +7,9 @@ import { } from '../../../test/fixtures/forc-projects'; import { getTestWallet } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let wallet: WalletUnlocked; let gasPrice: BN; diff --git a/apps/docs-snippets/src/guide/testing/tweaking-the-blockchain.test.ts b/apps/docs-snippets/src/guide/testing/tweaking-the-blockchain.test.ts index ac2b561074b..0c59f593422 100644 --- a/apps/docs-snippets/src/guide/testing/tweaking-the-blockchain.test.ts +++ b/apps/docs-snippets/src/guide/testing/tweaking-the-blockchain.test.ts @@ -1,5 +1,8 @@ import { FUEL_NETWORK_URL, Provider, fromTai64ToUnix } from 'fuels'; +/** + * @group node + */ test('produceBlocks with custom timestamp docs snippet', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); const latestBlock = await provider.getBlock('latest'); diff --git a/apps/docs-snippets/src/guide/types/address.test.ts b/apps/docs-snippets/src/guide/types/address.test.ts index c0674b945e6..320c9bdff43 100644 --- a/apps/docs-snippets/src/guide/types/address.test.ts +++ b/apps/docs-snippets/src/guide/types/address.test.ts @@ -1,5 +1,8 @@ import { Address, FUEL_NETWORK_URL, Provider, Wallet } from 'fuels'; +/** + * @group node + */ describe(__filename, () => { it('should successfully create new address from bech32 string', () => { // #region address-2 diff --git a/apps/docs-snippets/src/guide/types/arrays.test.ts b/apps/docs-snippets/src/guide/types/arrays.test.ts index ce931fefa6f..cd354c77dce 100644 --- a/apps/docs-snippets/src/guide/types/arrays.test.ts +++ b/apps/docs-snippets/src/guide/types/arrays.test.ts @@ -4,6 +4,9 @@ import { BN } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/types/asset-id.test.ts b/apps/docs-snippets/src/guide/types/asset-id.test.ts index 71143ec6c7e..c02dff29160 100644 --- a/apps/docs-snippets/src/guide/types/asset-id.test.ts +++ b/apps/docs-snippets/src/guide/types/asset-id.test.ts @@ -4,6 +4,9 @@ import { Address } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe('AssetId', () => { let contract: Contract; const Bits256: B256Address = '0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c'; diff --git a/apps/docs-snippets/src/guide/types/bech32.test.ts b/apps/docs-snippets/src/guide/types/bech32.test.ts index cba787e7480..e7ff0cf9f7a 100644 --- a/apps/docs-snippets/src/guide/types/bech32.test.ts +++ b/apps/docs-snippets/src/guide/types/bech32.test.ts @@ -1,5 +1,8 @@ import { Address } from 'fuels'; +/** + * @group node + */ describe(__filename, () => { it('should successfully generate a bech32 address', () => { // #region bech32-2 diff --git a/apps/docs-snippets/src/guide/types/bits256.test.ts b/apps/docs-snippets/src/guide/types/bits256.test.ts index de25250585d..ce250618049 100644 --- a/apps/docs-snippets/src/guide/types/bits256.test.ts +++ b/apps/docs-snippets/src/guide/types/bits256.test.ts @@ -1,5 +1,8 @@ import { Address, arrayify, getRandomB256, hexlify } from 'fuels'; +/** + * @group node + */ describe(__filename, () => { it('should successfully generate and validate bit256 hexed string', () => { // #region bits256-1 diff --git a/apps/docs-snippets/src/guide/types/bits512.test.ts b/apps/docs-snippets/src/guide/types/bits512.test.ts index d067ad4be2a..d177f866def 100644 --- a/apps/docs-snippets/src/guide/types/bits512.test.ts +++ b/apps/docs-snippets/src/guide/types/bits512.test.ts @@ -4,6 +4,9 @@ import { Wallet } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/types/bytes.test.ts b/apps/docs-snippets/src/guide/types/bytes.test.ts index 12f11cc1de7..b3f58668da9 100644 --- a/apps/docs-snippets/src/guide/types/bytes.test.ts +++ b/apps/docs-snippets/src/guide/types/bytes.test.ts @@ -3,6 +3,9 @@ import type { Contract, Bytes } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe('Bytes', () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/types/bytes32.test.ts b/apps/docs-snippets/src/guide/types/bytes32.test.ts index 4adcbc9ee9f..e02398ba93a 100644 --- a/apps/docs-snippets/src/guide/types/bytes32.test.ts +++ b/apps/docs-snippets/src/guide/types/bytes32.test.ts @@ -1,6 +1,9 @@ import type { Bytes } from 'fuels'; import { arrayify, hexlify, randomBytes } from 'fuels'; +/** + * @group node + */ describe(__filename, () => { it('should successfully generate and convert byte32 to hexlified string', () => { // #region bytes32-1 diff --git a/apps/docs-snippets/src/guide/types/conversion.test.ts b/apps/docs-snippets/src/guide/types/conversion.test.ts index 081ae61829f..7db51567ae5 100644 --- a/apps/docs-snippets/src/guide/types/conversion.test.ts +++ b/apps/docs-snippets/src/guide/types/conversion.test.ts @@ -16,6 +16,9 @@ import { getDocsSnippetsForcProject, } from '../../../test/fixtures/forc-projects'; +/** + * @group node + */ describe(__filename, () => { const { abiContents: abi } = getDocsSnippetsForcProject(DocSnippetProjectsEnum.ECHO_VALUES); let provider: Provider; diff --git a/apps/docs-snippets/src/guide/types/enums.test.ts b/apps/docs-snippets/src/guide/types/enums.test.ts index becc21746e2..2e383ab4c4d 100644 --- a/apps/docs-snippets/src/guide/types/enums.test.ts +++ b/apps/docs-snippets/src/guide/types/enums.test.ts @@ -3,6 +3,9 @@ import type { Contract } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/types/evm-address.test.ts b/apps/docs-snippets/src/guide/types/evm-address.test.ts index c425bdbb244..0d617996f92 100644 --- a/apps/docs-snippets/src/guide/types/evm-address.test.ts +++ b/apps/docs-snippets/src/guide/types/evm-address.test.ts @@ -4,6 +4,9 @@ import { Address } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe('EvMAddress', () => { let contract: Contract; const Bits256: B256AddressEvm = diff --git a/apps/docs-snippets/src/guide/types/options.test.ts b/apps/docs-snippets/src/guide/types/options.test.ts index 996c24c137f..683e05d08f2 100644 --- a/apps/docs-snippets/src/guide/types/options.test.ts +++ b/apps/docs-snippets/src/guide/types/options.test.ts @@ -3,6 +3,9 @@ import type { Contract } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/types/raw-slice.test.ts b/apps/docs-snippets/src/guide/types/raw-slice.test.ts index 8c8ce63b1de..04acc258694 100644 --- a/apps/docs-snippets/src/guide/types/raw-slice.test.ts +++ b/apps/docs-snippets/src/guide/types/raw-slice.test.ts @@ -3,6 +3,9 @@ import type { Contract, BN, RawSlice } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe('RawSlice', () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/types/std-string.test.ts b/apps/docs-snippets/src/guide/types/std-string.test.ts index 6132853a2c2..20233482f7b 100644 --- a/apps/docs-snippets/src/guide/types/std-string.test.ts +++ b/apps/docs-snippets/src/guide/types/std-string.test.ts @@ -3,6 +3,9 @@ import type { Contract, StdString } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe('StdString', () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/types/string.test.ts b/apps/docs-snippets/src/guide/types/string.test.ts index eaf9228bc59..7a22511e915 100644 --- a/apps/docs-snippets/src/guide/types/string.test.ts +++ b/apps/docs-snippets/src/guide/types/string.test.ts @@ -3,6 +3,9 @@ import type { BN, Contract } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; let gasPrice: BN; diff --git a/apps/docs-snippets/src/guide/types/struct.test.ts b/apps/docs-snippets/src/guide/types/struct.test.ts index c06024bc223..c4cbd6c94fe 100644 --- a/apps/docs-snippets/src/guide/types/struct.test.ts +++ b/apps/docs-snippets/src/guide/types/struct.test.ts @@ -1,3 +1,6 @@ +/** + * @group node + */ describe(__filename, () => { it('should successfully validate struct representation', () => { // #region struct-2 diff --git a/apps/docs-snippets/src/guide/types/tuples.test.ts b/apps/docs-snippets/src/guide/types/tuples.test.ts index b19fef5e8b7..48094322120 100644 --- a/apps/docs-snippets/src/guide/types/tuples.test.ts +++ b/apps/docs-snippets/src/guide/types/tuples.test.ts @@ -4,6 +4,9 @@ import { BN } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/types/vector.test.ts b/apps/docs-snippets/src/guide/types/vector.test.ts index cee03dc093c..0c550f657e8 100644 --- a/apps/docs-snippets/src/guide/types/vector.test.ts +++ b/apps/docs-snippets/src/guide/types/vector.test.ts @@ -4,6 +4,9 @@ import { BN, getRandomB256 } from 'fuels'; import { DocSnippetProjectsEnum } from '../../../test/fixtures/forc-projects'; import { createAndDeployContractFromProject } from '../../utils'; +/** + * @group node + */ describe(__filename, () => { let contract: Contract; diff --git a/apps/docs-snippets/src/guide/wallet-manager/getting-started-with-wallet-manager.test.ts b/apps/docs-snippets/src/guide/wallet-manager/getting-started-with-wallet-manager.test.ts index 8872219eb79..7383ca54cc1 100644 --- a/apps/docs-snippets/src/guide/wallet-manager/getting-started-with-wallet-manager.test.ts +++ b/apps/docs-snippets/src/guide/wallet-manager/getting-started-with-wallet-manager.test.ts @@ -1,6 +1,9 @@ import { WalletManager } from '@fuel-ts/wallet-manager'; import { FUEL_NETWORK_URL, Provider, Wallet } from 'fuels'; +/** + * @group node + */ describe(__filename, () => { it('instantiates the WalletManager', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); diff --git a/apps/docs-snippets/src/guide/wallet-manager/locking-and-unlocking-wallet-manager.test.ts b/apps/docs-snippets/src/guide/wallet-manager/locking-and-unlocking-wallet-manager.test.ts index 7414ac345b0..3fd7dfa275c 100644 --- a/apps/docs-snippets/src/guide/wallet-manager/locking-and-unlocking-wallet-manager.test.ts +++ b/apps/docs-snippets/src/guide/wallet-manager/locking-and-unlocking-wallet-manager.test.ts @@ -1,6 +1,9 @@ import { WalletManager } from '@fuel-ts/wallet-manager'; import { FUEL_NETWORK_URL, Provider } from 'fuels'; +/** + * @group node + */ describe(__filename, () => { it('should', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); diff --git a/apps/docs-snippets/src/guide/wallets/encrypting-and-decrypting-json-wallets.test.ts b/apps/docs-snippets/src/guide/wallets/encrypting-and-decrypting-json-wallets.test.ts index 2dc6d69840c..3a52746d659 100644 --- a/apps/docs-snippets/src/guide/wallets/encrypting-and-decrypting-json-wallets.test.ts +++ b/apps/docs-snippets/src/guide/wallets/encrypting-and-decrypting-json-wallets.test.ts @@ -1,5 +1,8 @@ import { FUEL_NETWORK_URL, Provider, Wallet } from 'fuels'; +/** + * @group node + */ describe(__filename, () => { it('should successfully encrypt wallet', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); diff --git a/apps/docs-snippets/src/utils.ts b/apps/docs-snippets/src/utils.ts index aa21997bc55..06e69e3bbd7 100644 --- a/apps/docs-snippets/src/utils.ts +++ b/apps/docs-snippets/src/utils.ts @@ -45,11 +45,7 @@ export const getTestWallet = async (seedQuantities?: CoinQuantityLike[]) => { // funding the transaction with the required quantities await genesisWallet.fund(request, requiredQuantities, minFee); - // execute the transaction, transferring resources to the test wallet - const response = await genesisWallet.sendTransaction(request); - - // wait for the transaction to be confirmed - await response.wait(); + await genesisWallet.sendTransaction(request, { awaitExecution: true }); // return the test wallet return testWallet; diff --git a/apps/docs-snippets/test/fixtures/forc-projects/Forc.toml b/apps/docs-snippets/test/fixtures/forc-projects/Forc.toml index ba78a81940a..866df5611da 100644 --- a/apps/docs-snippets/test/fixtures/forc-projects/Forc.toml +++ b/apps/docs-snippets/test/fixtures/forc-projects/Forc.toml @@ -2,6 +2,7 @@ members = [ "counter", "echo-enum", + "liquidity-pool", "log-values", "sum-script", "echo-values", diff --git a/apps/docs-snippets/test/fixtures/forc-projects/index.ts b/apps/docs-snippets/test/fixtures/forc-projects/index.ts index 91b257ed3ea..a09b1bcbfbb 100644 --- a/apps/docs-snippets/test/fixtures/forc-projects/index.ts +++ b/apps/docs-snippets/test/fixtures/forc-projects/index.ts @@ -13,6 +13,7 @@ export enum DocSnippetProjectsEnum { ECHO_U64_ARRAY = 'echo-u64-array', RETURN_CONTEXT = 'return-context', TOKEN_DEPOSITOR = 'token-depositor', + LIQUIDITY_POOL = 'liquidity-pool', SIMPLE_PREDICATE = 'simple-predicate', ECHO_CONFIGURABLES = 'echo-configurables', TRANSFER_TO_ADDRESS = 'transfer-to-address', diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/liquidity-pool/Forc.toml b/apps/docs-snippets/test/fixtures/forc-projects/liquidity-pool/Forc.toml similarity index 86% rename from packages/fuel-gauge/test/fixtures/forc-projects/liquidity-pool/Forc.toml rename to apps/docs-snippets/test/fixtures/forc-projects/liquidity-pool/Forc.toml index fe0f80317d9..6fbb1a576a4 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/liquidity-pool/Forc.toml +++ b/apps/docs-snippets/test/fixtures/forc-projects/liquidity-pool/Forc.toml @@ -1,5 +1,6 @@ [project] authors = ["Fuel Labs "] +entry = "main.sw" license = "Apache-2.0" name = "liquidity-pool" diff --git a/apps/docs-snippets/test/fixtures/forc-projects/liquidity-pool/src/main.sw b/apps/docs-snippets/test/fixtures/forc-projects/liquidity-pool/src/main.sw new file mode 100644 index 00000000000..3f4a16b7614 --- /dev/null +++ b/apps/docs-snippets/test/fixtures/forc-projects/liquidity-pool/src/main.sw @@ -0,0 +1,51 @@ +// #region deposit-and-withdraw-cookbook-1 +contract; + +use std::{ + call_frames::{ + msg_asset_id, + }, + context::msg_amount, + token::{ + mint_to_address, + transfer_to_address, + }, +}; +use std::constants::ZERO_B256; + +abi LiquidityPool { + #[payable] + fn deposit(recipient: Address); + #[payable] + fn withdraw(recipient: Address); +} + +const BASE_TOKEN: AssetId = AssetId::from( + 0x0000000000000000000000000000000000000000000000000000000000000000, +); + +impl LiquidityPool for Contract { + #[payable] + fn deposit(recipient: Address) { + assert(BASE_TOKEN == msg_asset_id()); + assert(0 < msg_amount()); + + // Mint two times the amount. + let amount_to_mint = msg_amount() * 2; + + // Mint some LP token based upon the amount of the base token. + mint_to_address(recipient, ZERO_B256, amount_to_mint); + } + + #[payable] + fn withdraw(recipient: Address) { + assert(0 < msg_amount()); + + // Amount to withdraw. + let amount_to_transfer = msg_amount() / 2; + + // Transfer base token to recipient. + transfer_to_address(recipient, BASE_TOKEN, amount_to_transfer); + } +} +// #endregion deposit-and-withdraw-cookbook-1 diff --git a/apps/docs-snippets/tsdoc.json b/apps/docs-snippets/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/apps/docs-snippets/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/apps/docs/.vitepress/config.ts b/apps/docs/.vitepress/config.ts index 2cb972c4966..cfe47caff1f 100644 --- a/apps/docs/.vitepress/config.ts +++ b/apps/docs/.vitepress/config.ts @@ -240,6 +240,10 @@ export default defineConfig({ text: 'Querying the Chain', link: '/guide/providers/querying-the-chain', }, + { + text: 'Retrying calls', + link: '/guide/providers/retrying-calls', + }, ], }, { @@ -395,8 +399,8 @@ export default defineConfig({ collapsed: true, items: [ { - text: 'Testing with Jest', - link: '/guide/testing/testing-with-jest', + text: 'Testing in TS', + link: '/guide/testing/testing-in-ts', }, { text: 'Setting Up a Custom Chain', diff --git a/apps/docs/.vitepress/plugins/codeInContextPlugin.test.ts b/apps/docs/.vitepress/plugins/codeInContextPlugin.test.ts index b013f21516d..89e7c6b970d 100644 --- a/apps/docs/.vitepress/plugins/codeInContextPlugin.test.ts +++ b/apps/docs/.vitepress/plugins/codeInContextPlugin.test.ts @@ -9,6 +9,9 @@ import { import MarkdownIt from 'markdown-it'; import Token from 'markdown-it/lib/token'; +/** + * @group node + */ describe('codeInContextPlugin', () => { describe('isCodeSnippetToken', () => { it('should returns true for valid code snippet tokens', () => { diff --git a/apps/docs/src/guide/abi-typegen/index.md b/apps/docs/src/guide/abi-typegen/index.md index 96271ec4f52..1f2ec28e053 100644 --- a/apps/docs/src/guide/abi-typegen/index.md +++ b/apps/docs/src/guide/abi-typegen/index.md @@ -2,13 +2,13 @@ import { data } from '../../versions.data' const { forc } = data const abiUrl = ` - https://fuellabs.github.io/sway/v${forc}/book/sway-program-types/smart_contracts.html#the-abi-declaration + https://docs.fuel.network/docs/sway/sway-program-types/smart_contracts/#the-abi-declaration ` const contractsUrl = ` - https://fuellabs.github.io/sway/v${forc}/book/sway-program-types/smart_contracts.html + https://docs.fuel.network/docs/sway/sway-program-types/smart_contracts/ ` const scriptsUrl = ` - https://fuellabs.github.io/sway/v${forc}/book/sway-program-types/scripts.html + https://docs.fuel.network/docs/sway/sway-program-types/scripts/ ` diff --git a/apps/docs/src/guide/abi-typegen/using-generated-types.md b/apps/docs/src/guide/abi-typegen/using-generated-types.md index 27c793061f9..3d995760118 100644 --- a/apps/docs/src/guide/abi-typegen/using-generated-types.md +++ b/apps/docs/src/guide/abi-typegen/using-generated-types.md @@ -10,49 +10,19 @@ pnpm fuels typegen -i ./abis/*-abi.json -o ./types We can use these files like so: - - -```ts -import { Wallet } from "fuels"; -import { MyContract__factory } from "./types"; - -const contractId = "0x..."; -const wallet = Wallet.fromAddress("..."); -const contract = MyContract__factory.connect(contractId, wallet); - -// All contract methods are available under functions with the correct types -const { transactionId, value } = await contract.functions.my_fn(1).call(); - -console.log(transactionId, value); -``` +<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-contract-factory-connect{ts:line-numbers} ## Contract Let's use the Contract class to deploy a contract: -```ts -import { Wallet } from "fuels"; -import { MyContract__factory } from "./types"; -import bytecode from "./types/MyContract.hex.ts"; - -const wallet = Wallet.fromAddress("..."); - -const contract = await MyContract__factory.deployContract(bytecode, wallet); - -console.log(contract.id); -``` +<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-contract-factory-deploy{ts:line-numbers} ### Autoloading of Storage Slots Typegen tries to resolve, auto-load, and embed the [Storage Slots](../contracts//storage-slots.md) for your Contract within the `MyContract__factory` class. Still, you can override it alongside other options from [`DeployContractOptions`](https://github.com/FuelLabs/fuels-ts/blob/a64b67b9fb2d7f764ab9151a21d2266bf2df3643/packages/contract/src/contract-factory.ts#L19-L24), when calling the `deployContract` method: -```ts -import storageSlots from "../contract/out/debug/storage-slots.json"; - -const contract = await MyContract__factory.deployContract(bytecode, wallet, { - storageSlots, -}); -``` +<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-contract-storage-slots{ts:line-numbers} ## Script @@ -64,27 +34,11 @@ pnpm fuels typegen -i ./abis/*-abi.json -o ./types --script We can use these files like so: - - -```ts -import { Wallet } from "fuels"; -import { MyScript__factory } from "./types"; - -const wallet = Wallet.fromAddress("..."); -const script = ScriptAbi__factory.createInstance(wallet); - -const { value, logs } = await script.functions.main(1).call(); - -console.log({ value, logs }); -``` +<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-script{ts:line-numbers} ## Predicate -Consider the following predicate: - -<<< @/../../../packages/fuel-gauge/test/fixtures/forc-projects/predicate-main-args-struct/src/main.sw#Predicate-main-args{ts:line-numbers} - -Now, after generating types via: +After generating types via: ```console pnpm fuels typegen -i ./abis/*-abi.json -o ./types --predicate @@ -92,30 +46,7 @@ pnpm fuels typegen -i ./abis/*-abi.json -o ./types --predicate We can use these files like so: - - -```ts -import { Wallet } from "fuels"; -import { MyPredicate__factory } from "./types"; - -const wallet = Wallet.fromAddress("..."); -const predicate = MyPredicate__factory.createInstance(); - -await predicate - .setData({ - has_account: true, - total_complete: 100, - }) - .transfer(wallet.address, ); - -const walletBalance = await wallet.getBalance(); -const predicateBalance = await predicate.getBalance(); - -console.log({ - walletBalance, - predicateBalance, -}); -``` +<<< @/../../demo-typegen/src/demo.test.ts#typegen-demo-predicate{ts:line-numbers} See also: diff --git a/apps/docs/src/guide/contracts/deploying-contracts.md b/apps/docs/src/guide/contracts/deploying-contracts.md index f9edf0df2a4..5cb2cf34aa9 100644 --- a/apps/docs/src/guide/contracts/deploying-contracts.md +++ b/apps/docs/src/guide/contracts/deploying-contracts.md @@ -1,8 +1,8 @@ # Deploying Contracts diff --git a/apps/docs/src/guide/cookbook/deposit-and-withdraw.md b/apps/docs/src/guide/cookbook/deposit-and-withdraw.md index fa70f045325..0ae4806e3c9 100644 --- a/apps/docs/src/guide/cookbook/deposit-and-withdraw.md +++ b/apps/docs/src/guide/cookbook/deposit-and-withdraw.md @@ -1,37 +1,15 @@ # Deposit And Withdraw -Consider the following contracts: +Consider the following contract: -<<< @/../../../packages/fuel-gauge/test/fixtures/forc-projects/token_contract/src/main.sw#token-contract{rust:line-numbers} +<<< @/../../docs-snippets/test/fixtures/forc-projects/liquidity-pool/src/main.sw#deposit-and-withdraw-cookbook-1{rust:line-numbers} -<<< @/../../../packages/fuel-gauge/test/fixtures/forc-projects/liquidity-pool/src/main.sw#liquidity-pool-contract{rust:line-numbers} +As the name implies, this contract represents a simplified version of a liquidity pool. The `deposit()` method allows you to supply an arbitrary amount of `BASE_TOKEN`. In response, it mints twice the amount of the liquidity asset to the caller's address. Similarly, the `withdraw()` method transfers half the amount of the `BASE_TOKEN` back to the caller's address. -The first contract is a contract that represents a simple token. +Now, let's deposit some tokens into the liquidity pool contract. Since this requires forwarding assets to the contract, we need to pass the appropriate values to `callParams` when creating a contract call. -The second contract, as its name suggests, represents a simplified example of a liquidity pool contract. The method deposit() expects you to supply an arbitrary amount of the `base_token`. As a result, it mints double the amount of the liquidity asset to the calling address. Analogously, if you call `withdraw()` supplying it with the liquidity asset, it will transfer half that amount of the `base_token` back to the calling address except for deducting it from the contract balance instead of minting it. - -The first step towards interacting with any contract in the TypeScript SDK is using the `typegen` CLI utility to generate type-safe bindings for the contract methods: - -```sh -$ npx fuels typegen -i ./contract/out/debug/*-abi.json -o ./contract-types -``` - -Next, let's setup a [`Wallet`](../wallets/index.md) and seed it with some coins. We will need these coins to deploy the contracts and to interact with them. - -<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-wallet-setup{ts:line-numbers} - -Let's now deploy both the contracts and set them up. - -<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-contract-deployments{ts:line-numbers} - -Next, let's mint some tokens and transfer them to our wallet. - -<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-mint-and-transfer{ts:line-numbers} - -Now, let's deposit some tokens into the liquidity pool contract. Since we have to transfer assets to the contract, we create the appropriate [`callParams`](../contracts/call-parameters.md) and chain them to the method call. - -<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-deposit{ts:line-numbers} +<<< @/../../docs-snippets/src/guide/cookbook/deposit-and-withdraw.test.ts#deposit-and-withdraw-cookbook-2{ts:line-numbers} As a final demonstration, let's use all our liquidity asset balance to withdraw from the pool and confirm we retrieved the initial amount. For this, we get our liquidity asset balance and supply it to the `withdraw()` function via `callParams`. -<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#deposit-and-withdraw-cookbook-withdraw{ts:line-numbers} +<<< @/../../docs-snippets/src/guide/cookbook/deposit-and-withdraw.test.ts#deposit-and-withdraw-cookbook-3{ts:line-numbers} diff --git a/apps/docs/src/guide/predicates/index.md b/apps/docs/src/guide/predicates/index.md index 1fede44dfac..50e07ba6632 100644 --- a/apps/docs/src/guide/predicates/index.md +++ b/apps/docs/src/guide/predicates/index.md @@ -1,8 +1,8 @@ # Predicates diff --git a/apps/docs/src/guide/providers/retrying-calls.md b/apps/docs/src/guide/providers/retrying-calls.md new file mode 100644 index 00000000000..e5a3651a260 --- /dev/null +++ b/apps/docs/src/guide/providers/retrying-calls.md @@ -0,0 +1,5 @@ +# Retrying calls + +The default behavior of calls done via the `Provider` towards a fuel node is that they'll fail if the connection breaks. Specifying retry options allows you to customize how many additional attempts you want to make when the connection to the node breaks before ultimately throwing an error. You can also specify the back-off algorithm as well as the base delay that algorithm will use to calculate the wait time for each request. + +<<< @/../../../packages/providers/test/auto-retry-fetch.test.ts#provider-retry-options{ts:line-numbers} diff --git a/apps/docs/src/guide/scripts/index.md b/apps/docs/src/guide/scripts/index.md index 6743b331e5d..bc1262823f7 100644 --- a/apps/docs/src/guide/scripts/index.md +++ b/apps/docs/src/guide/scripts/index.md @@ -2,7 +2,7 @@ import { data } from '../../versions.data' const { forc } = data const url = ` - https://fuellabs.github.io/sway/v${forc}/book/sway-program-types/scripts.html#scripts-and-the-sdks + https://docs.fuel.network/docs/sway/sway-program-types/scripts/#scripts-and-the-sdks ` diff --git a/apps/docs/src/guide/scripts/instantiating-a-script.md b/apps/docs/src/guide/scripts/instantiating-a-script.md index 952762707e6..6f8ddf1c2ca 100644 --- a/apps/docs/src/guide/scripts/instantiating-a-script.md +++ b/apps/docs/src/guide/scripts/instantiating-a-script.md @@ -2,7 +2,7 @@ import { data } from '../../versions.data' const { forc } = data const url = ` - https://fuellabs.github.io/sway/v${forc}/book/introduction/index.html + https://docs.fuel.network/docs/sway/introduction/ ` diff --git a/apps/docs/src/guide/testing/index.md b/apps/docs/src/guide/testing/index.md index d90f2bc74ea..d0e7a69657f 100644 --- a/apps/docs/src/guide/testing/index.md +++ b/apps/docs/src/guide/testing/index.md @@ -1,7 +1,7 @@ # Testing @@ -10,11 +10,11 @@ In order to test your Sway and TS-SDK applications, you can test your code in a 1. Testing with TS-SDK: Compiling you Sway code and connecting to the methods using TS-SDK and JS testing frameworks 2. Using `forc test` see the Sway docs for more info -3. Using [the Rust SDK](https://fuellabs.github.io/fuels-rs/v0.31.1/testing/index.html) +3. Using [the Rust SDK](https://docs.fuel.network/docs/fuels-rs/testing/) ### Testing with TS-SDK -To test your Sway applications using the TS-SDK, you can pick whatever testing library or framework you feel comfortable with. There isn't any specific testing framework needed, it is entirely up to the user. That being said, the TS-SDK uses [Jest](https://jestjs.io/) for its tests. +To test your Sway applications using the TS-SDK, you can pick whatever testing library or framework you feel comfortable with. There isn't any specific testing framework needed, it is entirely up to the user. That being said, the TS-SDK uses [`Vitest`](https://vitest.dev/) for its tests. ### Wallet Test Utilities @@ -40,4 +40,4 @@ stop(); See also: 1. [Setting up test wallets](../wallets/test-wallets.md) -2. [Testing with Jest](./testing-with-jest.md) +2. [Testing in TS](./testing-in-ts.md) diff --git a/apps/docs/src/guide/testing/setting-up-a-custom-chain.md b/apps/docs/src/guide/testing/setting-up-a-custom-chain.md index 825e1dd6933..f965ab99169 100644 --- a/apps/docs/src/guide/testing/setting-up-a-custom-chain.md +++ b/apps/docs/src/guide/testing/setting-up-a-custom-chain.md @@ -4,7 +4,7 @@ The `launchNodeAndGetWallets` method lets you launch a local Fuel node with vari In the code snippet below, we provide a custom chain config file to the `launchNodeAndGetWallets` method. You can use a chain config file to customize things like the chain's consensus parameters or specify some initial states for the chain. Click here to see what a chain config file looks like: [`chainConfig.json`](https://github.com/FuelLabs/fuels-ts/blob/master/.fuel-core/configs/chainConfig.json) -<<< @/../../../packages/wallet/src/test-utils/launchNode.test.ts#launchNode-custom-config{ts:line-numbers} +<<< @/../../../packages/wallet/src/test-utils/launchNodeAndGetWallets.test.ts#launchNode-custom-config{ts:line-numbers} ## Customization options diff --git a/apps/docs/src/guide/testing/testing-in-ts.md b/apps/docs/src/guide/testing/testing-in-ts.md new file mode 100644 index 00000000000..fa8eec5a01f --- /dev/null +++ b/apps/docs/src/guide/testing/testing-in-ts.md @@ -0,0 +1,13 @@ +# Testing in TS + +As noted in [the testing intro](./index.md), you are free to test your Sway and TS-SDK code with any JS framework available. Below we have an example of how to load and test a contract using `Vitest`, but the general principles and steps are the same for any testing harness. + +Here is a simple Sway program that takes an input and then returns it: + +<<< @/../../demo-typegen/contract/src/main.sw#Testing-in-ts-rust{rust:line-numbers} + +Here is JavaScript code testing the above program using a conventional `Vitest` setup: + +<<< @/../../demo-typegen/src/demo.test.ts#Testing-in-ts-ts{ts:line-numbers} + +> **Note:** The TS-SDK has recently migrated to `Vitest` however it follows a very similar API to Jest, and the above example applies to Jest also. \ No newline at end of file diff --git a/apps/docs/src/guide/testing/testing-with-jest.md b/apps/docs/src/guide/testing/testing-with-jest.md deleted file mode 100644 index 9f7b536a2d0..00000000000 --- a/apps/docs/src/guide/testing/testing-with-jest.md +++ /dev/null @@ -1,11 +0,0 @@ -# Testing with Jest - -As noted in [the testing intro](./index.md), you are free to test your Sway and TS-SDK code with any JS framework available. Below we have an example of how to load and test a contract using Jest, but the general principles and steps are the same for any testing harness. - -Here is a simple Sway program that takes an input and then returns it: - -<<< @/../../demo-typegen/contract/src/main.sw#Testing-with-jest-rust{rust:line-numbers} - -Here is JavaScript code testing the above program using a conventional Jest setup: - -<<< @/../../demo-typegen/src/demo.test.ts#Testing-with-jest-ts{ts:line-numbers} diff --git a/apps/docs/src/guide/types/index.md b/apps/docs/src/guide/types/index.md index 5fbe40c3215..4a7180d8d9a 100644 --- a/apps/docs/src/guide/types/index.md +++ b/apps/docs/src/guide/types/index.md @@ -18,6 +18,6 @@ In this section, you will learn about: As you progress through the documentation, you may find it helpful to refer back to the following resources: -- [Sway Documentation](https://fuellabs.github.io/sway/): Explore the Sway documentation homepage for an overview of Sway Types, as well as other sections. +- [Sway Documentation](https://docs.fuel.network/docs/sway/): Explore the Sway documentation homepage for an overview of Sway Types, as well as other sections. - [The Fuel Book](https://fuelbook.fuel.network/master/index.html): A comprehensive guide to the whole Fuel ecosystem. diff --git a/apps/docs/src/guide/wallets/access.md b/apps/docs/src/guide/wallets/access.md index df6facfcbcf..b44aaa24052 100644 --- a/apps/docs/src/guide/wallets/access.md +++ b/apps/docs/src/guide/wallets/access.md @@ -27,6 +27,12 @@ provides greater access via its private key. <<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#wallets{ts:line-numbers} +## Optional Provider + +You can choose not to pass through a provider argument on `Wallet` construction: + +<<< @/../../../packages/fuel-gauge/src/doc-examples.test.ts#wallet-optional-provider{ts:line-numbers} + ## Transitioning States A [`WalletLocked`](../../api/Wallet/WalletLocked.md) instance can be unlocked by providing the private key: diff --git a/apps/docs/src/index.md b/apps/docs/src/index.md index d5d8b08c283..2b1fbced0eb 100644 --- a/apps/docs/src/index.md +++ b/apps/docs/src/index.md @@ -1,7 +1,7 @@ @@ -15,7 +15,7 @@ Please visit the Fuel's [installation guide](https://docs.fuel.network/guides/in ## Developer Quickstart Guide -We recommend starting with the [Developer Quickstart](https://fuellabs.github.io/fuel-docs/master/quickstart/developer-quickstart.html) for a walk through on building your first DApp on Fuel. +We recommend starting with the [Developer Quickstart](https://docs.fuel.network/docs/intro/quickstart-contract/) for a walk through on building your first DApp on Fuel. - [Guide](./guide/) - [Sample Application](https://github.com/FuelLabs/beta2-quickstart) @@ -25,7 +25,7 @@ We recommend starting with the [Developer Quickstart](https://fuellabs.github.io Learn more about the Fuel Ecosystem. -- [🌴 Sway](https://fuellabs.github.io/sway/) the new language. Empowering everyone to build reliable and efficient smart contracts. +- [🌴 Sway](https://docs.fuel.network/docs/sway/) the new language. Empowering everyone to build reliable and efficient smart contracts. - 🧰 Forc the Fuel toolbox. Build, deploy and manage your sway projects. - [⚙️ Fuel Core](https://github.com/FuelLabs/fuel-core) the new FuelVM, a blazingly fast blockchain VM. - [🔗 Fuel Specs](https://github.com/FuelLabs/fuel-specs) the Fuel protocol specifications. diff --git a/internal/check-tests/README.md b/internal/check-tests/README.md new file mode 100644 index 00000000000..8b4c4eedc0e --- /dev/null +++ b/internal/check-tests/README.md @@ -0,0 +1,5 @@ +This package validates our environment testing setup. + +It ensures that environment specific functionalities should be supported by the SDK. + +If one of these tests fails, it may indicate that an environment is configured incorrectly. diff --git a/internal/check-tests/package.json b/internal/check-tests/package.json new file mode 100644 index 00000000000..40bbfeed323 --- /dev/null +++ b/internal/check-tests/package.json @@ -0,0 +1,12 @@ +{ + "private": true, + "name": "@internal/check-tests", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc --noEmit" + }, + "license": "Apache-2.0", + "version": null +} diff --git a/internal/check-tests/src/all.test.ts b/internal/check-tests/src/all.test.ts new file mode 100644 index 00000000000..8e2a0847a9c --- /dev/null +++ b/internal/check-tests/src/all.test.ts @@ -0,0 +1,11 @@ +import { testAll } from './index'; + +/** + * @group node + * @group browser + */ +describe('in:everywhere', () => { + it('should work everywhere', () => { + expect(testAll()).toEqual('thank you'); + }); +}); diff --git a/internal/check-tests/src/browser.test.ts b/internal/check-tests/src/browser.test.ts new file mode 100644 index 00000000000..2cac616e56a --- /dev/null +++ b/internal/check-tests/src/browser.test.ts @@ -0,0 +1,10 @@ +import { testEach } from './index'; + +/** + * @group browser + */ +describe('in:browser', () => { + it('should work on browser', () => { + expect(testEach()).toEqual('browser'); + }); +}); diff --git a/internal/check-tests/src/index.ts b/internal/check-tests/src/index.ts new file mode 100644 index 00000000000..b78b737db7e --- /dev/null +++ b/internal/check-tests/src/index.ts @@ -0,0 +1,20 @@ +const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined'; + +const isNode = + typeof process !== 'undefined' && process.versions != null && process.versions.node != null; + +export const testEach = (): string => { + let env: string; + + if (isBrowser) { + env = 'browser'; + } else if (isNode) { + env = 'node'; + } else { + throw new Error('Oops, no browser/node detected'); + } + + return env; +}; + +export const testAll = (): string => 'thank you'; diff --git a/internal/check-tests/src/node.test.ts b/internal/check-tests/src/node.test.ts new file mode 100644 index 00000000000..e62c3ba7031 --- /dev/null +++ b/internal/check-tests/src/node.test.ts @@ -0,0 +1,10 @@ +import { testEach } from './index'; + +/** + * @group node + */ +describe('in:node', () => { + it('should work on node', () => { + expect(testEach()).toEqual('node'); + }); +}); diff --git a/internal/check-tests/tsconfig.json b/internal/check-tests/tsconfig.json new file mode 100644 index 00000000000..b0fced27d72 --- /dev/null +++ b/internal/check-tests/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist" + }, + "include": ["src"] +} diff --git a/internal/check-tests/tsdoc.json b/internal/check-tests/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/internal/check-tests/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/jest.config.ts b/jest.config.ts deleted file mode 100644 index c202af65e18..00000000000 --- a/jest.config.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { Config } from '@jest/types'; - -const config: Config.InitialOptions = { - preset: 'ts-jest', - testEnvironment: 'node', - setupFiles: ['./jest.env.ts'], - testPathIgnorePatterns: ['/node_modules/', '/dist/'], - modulePathIgnorePatterns: [ - '/dist/', - '/apps/demo-nextjs', - '/apps/demo-react-cra', - '/dist/demo-react-vite', - ], - coveragePathIgnorePatterns: [ - '/dist/', - '/test/', - '.test.ts', - '.d.ts', - '/fuel-gauge/', - '/apps/demo-*', - ], - testTimeout: 15000, - transform: { - '\\.hbs$': 'jest-text-transformer', - }, -}; - -export default config; diff --git a/nodemon.config.json b/nodemon.config.json index 2cf1fb9a45f..b972bfeec77 100644 --- a/nodemon.config.json +++ b/nodemon.config.json @@ -11,6 +11,8 @@ "**/Forc.lock", "**/getBuiltinVersions.ts", "**/out/debug/**", - "apps/demo-typegen/src/generated-types/**" + "apps/demo-typegen/src/contract-types/**", + "apps/demo-typegen/src/predicate-types/**", + "apps/demo-typegen/src/script-types/**" ] } diff --git a/package.json b/package.json index 1b916ce63d6..5d0f840cbf2 100644 --- a/package.json +++ b/package.json @@ -5,20 +5,24 @@ "author": "Fuel Labs (https://fuel.network/)", "private": true, "engines": { - "node": "^18.17.1", + "node": "^18.18.2 || ^20.0.0", "pnpm": "^8.9.0" }, "packageManager": "pnpm@8.9.0", "scripts": { - "dan": "tsx ./scripts/release-unpublish.ts", "dev": "nodemon --config nodemon.config.json -x 'pnpm build:packages'", "build": "turbo run build --cache-dir=.turbo", "build:packages": "turbo run build --filter=!docs", - "ci:test": "./scripts/ci-test.sh", + "ci:test": "./scripts/tests-ci.sh", "pretest": "turbo run pretest", - "test": "jest --no-cache --runInBand --coverage", - "test:e2e": "jest --no-cache --runInBand packages/fuel-gauge/src/e2e-script.test.ts", - "test:watch": "jest --no-cache --watchAll", + "test": "vitest --run --coverage --config vite.node.config.mts $(scripts/tests-find.sh --node)", + "test:filter": "vitest --run --coverage --config vite.node.config.mts", + "test:coverage-merge": "tsx ./scripts/tests-coverage-merge.ts", + "test:coverage-diff": "tsx ./scripts/tests-coverage-diff.ts", + "test:watch": "vitest --watch --config vite.node.config.mts $(scripts/tests-find.sh --node)", + "test:validate": "./scripts/tests-validate.sh", + "test:browser": "vitest --run --coverage --config vite.browser.config.mts $(scripts/tests-find.sh --browser)", + "test:e2e": "vitest --run --config vite.node.config.mts $(scripts/tests-find.sh --e2e)", "lint": "run-s lint:check prettier:check", "lint:check": "eslint . --ext .ts --max-warnings 0", "lint:fix": "pnpm lint:check --fix", @@ -50,17 +54,20 @@ "@changesets/get-github-info": "^0.5.2", "@fuel-ts/forc": "workspace:*", "@fuel-ts/fuel-core": "workspace:*", + "@fuel-ts/utils": "workspace:*", "@fuel-ts/versions": "workspace:^", "@internal/tsup": "workspace:*", - "@jest/types": "^29.5.0", - "@types/jest": "^29.5.0", + "@istanbuljs/nyc-config-typescript": "^1.0.2", "@types/node": "18.15.3", "@types/node-fetch": "^2.6.2", "@types/web": "^0.0.65", "@typescript-eslint/eslint-plugin": "^6.9.0", "@typescript-eslint/parser": "^6.9.0", + "@vitest/browser": "^1.1.3", + "@vitest/coverage-istanbul": "^1.1.3", "compare-versions": "^6.1.0", "conventional-changelog-angular": "^5.0.13", + "coverage-diff": "^3.2.0", "dotenv": "^9.0.2", "eslint": "^8.52.0", "eslint-config-airbnb-base": "^15.0.0", @@ -74,23 +81,26 @@ "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-tsdoc": "^0.2.17", - "ethers": "^6.7.1", "glob": "^10.2.6", - "jest": "^29.7.0", - "jest-text-transformer": "^1.0.4", + "memfs": "^4.6.0", "nodemon": "^2.0.22", "npm-run-all": "^4.1.5", + "nyc": "^15.1.0", "open": "^8.4.0", "prettier": "^3.0.3", "rimraf": "^3.0.2", "textlint": "^13.3.2", "textlint-rule-no-dead-link": "^5.1.2", "ts-generator": "^0.1.1", - "ts-jest": "^29.1.1", "tsup": "^6.7.0", - "tsx": "^3.12.7", + "tsx": "^4.7.0", "turbo": "^1.8.8", - "typescript": "~5.2.2" + "typescript": "~5.2.2", + "vite": "^5.0.11", + "vite-plugin-node-polyfills": "^0.17.0", + "vite-plugin-plain-text": "^1.4.2", + "vitest": "^1.1.3", + "webdriverio": "^8.27.0" }, "pnpm": { "overrides": { diff --git a/packages/abi-coder/README.md b/packages/abi-coder/README.md index 24cd682c3f7..dbd65ed7984 100644 --- a/packages/abi-coder/README.md +++ b/packages/abi-coder/README.md @@ -18,7 +18,7 @@ This module is responsible for encoding and decoding the Application Binary Inte -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/abi-coder/package.json b/packages/abi-coder/package.json index e253d7d062f..c164333c9fa 100644 --- a/packages/abi-coder/package.json +++ b/packages/abi-coder/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/abi-coder/src/coders/abstract-coder.ts b/packages/abi-coder/src/coders/abstract-coder.ts index c14e1e7301b..51ce8c2610c 100644 --- a/packages/abi-coder/src/coders/abstract-coder.ts +++ b/packages/abi-coder/src/coders/abstract-coder.ts @@ -1,4 +1,3 @@ -import { FuelError, type ErrorCode } from '@fuel-ts/errors'; import type { BN } from '@fuel-ts/math'; import type { BytesLike } from 'ethers'; @@ -88,10 +87,6 @@ export abstract class Coder { this.encodedLength = encodedLength; } - throwError(errorCode: ErrorCode, message: string): never { - throw new FuelError(errorCode, message); - } - abstract encode(value: TInput, length?: number): Uint8Array; abstract decode(data: Uint8Array, offset: number, length?: number): [TDecoded, number]; diff --git a/packages/abi-coder/src/coders/array.test.ts b/packages/abi-coder/src/coders/array.test.ts index 64b93ead7b7..82603d7f262 100644 --- a/packages/abi-coder/src/coders/array.test.ts +++ b/packages/abi-coder/src/coders/array.test.ts @@ -1,7 +1,7 @@ import { FuelError, ErrorCode } from '@fuel-ts/errors'; import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; -import { U8_MAX } from '../../test/utils/constants'; +import { U32_MAX, U8_MAX } from '../../test/utils/constants'; import type { SmallBytesOptions } from './abstract-coder'; import { ArrayCoder } from './array'; @@ -9,6 +9,9 @@ import { BooleanCoder } from './boolean'; import { EnumCoder } from './enum'; import { NumberCoder } from './number'; +/** + * @group node + */ describe('ArrayCoder', () => { const sbOptions: SmallBytesOptions = { isSmallBytes: true, @@ -105,4 +108,34 @@ describe('ArrayCoder', () => { new FuelError(ErrorCode.ENCODE_ERROR, 'Types/values length mismatch.') ); }); + + it('throws when decoding empty bytes', async () => { + const coder = new ArrayCoder(new NumberCoder('u8'), 1); + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid array data size.') + ); + }); + + it('throws when decoding invalid bytes (too small)', async () => { + const coder = new ArrayCoder(new NumberCoder('u8'), 8); + const input = new Uint8Array([0]); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid array data size.') + ); + }); + + it('throws when decoding vec larger than max size', async () => { + const coder = new ArrayCoder(new NumberCoder('u8'), 8); + const input = new Uint8Array(U32_MAX + 1); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid array data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/array.ts b/packages/abi-coder/src/coders/array.ts index 64907163473..2fe43d807d2 100644 --- a/packages/abi-coder/src/coders/array.ts +++ b/packages/abi-coder/src/coders/array.ts @@ -1,5 +1,6 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { MAX_BYTES } from '../constants'; import { concatWithDynamicData } from '../utilities'; import type { TypesOfCoder } from './abstract-coder'; @@ -23,17 +24,21 @@ export class ArrayCoder extends Coder< encode(value: InputValueOf): Uint8Array { if (!Array.isArray(value)) { - this.throwError(ErrorCode.ENCODE_ERROR, `Expected array value.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Expected array value.`); } if (this.length !== value.length) { - this.throwError(ErrorCode.ENCODE_ERROR, `Types/values length mismatch.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Types/values length mismatch.`); } return concatWithDynamicData(Array.from(value).map((v) => this.coder.encode(v))); } decode(data: Uint8Array, offset: number): [DecodedValueOf, number] { + if (data.length < this.encodedLength || data.length > MAX_BYTES) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid array data size.`); + } + let newOffset = offset; const decodedValue = Array(this.length) .fill(0) diff --git a/packages/abi-coder/src/coders/b256.test.ts b/packages/abi-coder/src/coders/b256.test.ts index 14942b654e2..e581cd7b1e6 100644 --- a/packages/abi-coder/src/coders/b256.test.ts +++ b/packages/abi-coder/src/coders/b256.test.ts @@ -1,5 +1,12 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + import { B256Coder } from './b256'; +/** + * @group node + * @group browser + */ describe('B256Coder', () => { const B256_DECODED = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'; const B256_ENCODED = new Uint8Array([ @@ -43,52 +50,67 @@ describe('B256Coder', () => { expect(actualLength).toBe(expectedLength); }); - it('should throw an error when encoding a 256 bit hash string that is too short', () => { + it('should throw an error when encoding a 256 bit hash string that is too short', async () => { const invalidInput = B256_DECODED.slice(0, B256_DECODED.length - 1); - expect(() => { - coder.encode(invalidInput); - }).toThrow('Invalid b256'); + await expectToThrowFuelError( + () => coder.encode(invalidInput), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b256.') + ); }); - it('should throw an error when decoding an encoded 256 bit hash string that is too short', () => { + it('should throw an error when decoding an encoded 256 bit hash string that is too short', async () => { const invalidInput = B256_ENCODED.slice(0, B256_ENCODED.length - 1); - expect(() => { - coder.decode(invalidInput, 0); - }).toThrow(); + await expectToThrowFuelError( + () => coder.decode(invalidInput, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b256 data size.') + ); }); - it('should throw an error when encoding a 256 bit hash string that is too long', () => { + it('should throw an error when encoding a 256 bit hash string that is too long', async () => { const invalidInput = `${B256_DECODED}0`; - expect(() => { - coder.encode(invalidInput); - }).toThrow('Invalid b256'); + await expectToThrowFuelError( + () => coder.encode(invalidInput), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b256.') + ); }); - it('should throw an error when encoding a 512 bit hash string', () => { + it('should throw an error when encoding a 512 bit hash string', async () => { const B512 = '0x8e9dda6f7793745ac5aacf9e907cae30b2a01fdf0d23b7750a85c6a44fca0c29f0906f9d1f1e92e6a1fb3c3dcef3cc3b3cdbaae27e47b9d9a4c6a4fce4cf16b2'; - expect(() => { - coder.encode(B512); - }).toThrow('Invalid b256'); + await expectToThrowFuelError( + () => coder.encode(B512), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b256.') + ); }); - it('should throw an error when decoding an encoded 256 bit hash string that is too long', () => { - const invalidInput = new Uint8Array(Array.from(Array(32).keys())); + it('should throw an error when encoding a 256 bit hash string that is not a hex string', async () => { + const invalidInput = 'not a hex string'; - expect(() => { - coder.decode(invalidInput, 1); - }).toThrow('Invalid size for b256'); + await expectToThrowFuelError( + () => coder.encode(invalidInput), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid b256.') + ); }); - it('should throw an error when encoding a 256 bit hash string that is not a hex string', () => { - const invalidInput = 'not a hex string'; + it('throws when decoding empty bytes', async () => { + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b256 data size.') + ); + }); + + it('should throw an error when decoding an encoded b256 bit hash string that is too long', async () => { + const invalidInput = new Uint8Array(Array.from(Array(65).keys())); - expect(() => { - coder.encode(invalidInput); - }).toThrow('Invalid b256'); + await expectToThrowFuelError( + () => coder.decode(invalidInput, 62), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b256 byte data size.') + ); }); }); diff --git a/packages/abi-coder/src/coders/b256.ts b/packages/abi-coder/src/coders/b256.ts index 4b76a1a13b7..d8e28eee0fb 100644 --- a/packages/abi-coder/src/coders/b256.ts +++ b/packages/abi-coder/src/coders/b256.ts @@ -1,12 +1,14 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { bn, toHex } from '@fuel-ts/math'; import { getBytesCopy } from 'ethers'; +import { WORD_SIZE } from '../constants'; + import { Coder } from './abstract-coder'; export class B256Coder extends Coder { constructor() { - super('b256', 'b256', 32); + super('b256', 'b256', WORD_SIZE * 4); } encode(value: string): Uint8Array { @@ -14,23 +16,30 @@ export class B256Coder extends Coder { try { encodedValue = getBytesCopy(value); } catch (error) { - this.throwError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); } - if (encodedValue.length !== 32) { - this.throwError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); + if (encodedValue.length !== this.encodedLength) { + throw new FuelError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); } return encodedValue; } decode(data: Uint8Array, offset: number): [string, number] { - let bytes = data.slice(offset, offset + 32); + if (data.length < this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid b256 data size.`); + } + + let bytes = data.slice(offset, offset + this.encodedLength); + const decoded = bn(bytes); if (decoded.isZero()) { bytes = new Uint8Array(32); } - if (bytes.length !== 32) { - this.throwError(ErrorCode.DECODE_ERROR, `'Invalid size for b256'.`); + + if (bytes.length !== this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid b256 byte data size.`); } + return [toHex(bytes, 32), offset + 32]; } } diff --git a/packages/abi-coder/src/coders/b512.test.ts b/packages/abi-coder/src/coders/b512.test.ts index a83cc3d799b..2a62b738a26 100644 --- a/packages/abi-coder/src/coders/b512.test.ts +++ b/packages/abi-coder/src/coders/b512.test.ts @@ -1,5 +1,12 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + import { B512Coder } from './b512'; +/** + * @group node + * @group browser + */ describe('B512Coder', () => { const B512_DECODED = '0x8e9dda6f7793745ac5aacf9e907cae30b2a01fdf0d23b7750a85c6a44fca0c29f0906f9d1f1e92e6a1fb3c3dcef3cc3b3cdbaae27e47b9d9a4c6a4fce4cf16b2'; @@ -55,14 +62,6 @@ describe('B512Coder', () => { }).toThrow(/Invalid struct B512/); }); - it('should throw an error when decoding an encoded 512 bit hash string that is too short', () => { - const invalidInput = B512_ENCODED.slice(0, B512_ENCODED.length - 1); - - expect(() => { - coder.decode(invalidInput, 0); - }).toThrow('Invalid size for b512'); - }); - it('should throw an error when encoding a 512 bit hash string that is too long', () => { const invalidInput = `${B512_DECODED}0`; @@ -79,14 +78,6 @@ describe('B512Coder', () => { }).toThrow(/Invalid struct B512/); }); - it('should throw an error when decoding an encoded 512 bit hash string that is too long', () => { - const invalidInput = new Uint8Array(Array.from(Array(32).keys())); - - expect(() => { - coder.decode(invalidInput, 1); - }).toThrow('Invalid size for b512'); - }); - it('should throw an error when encoding a 512 bit hash string that is not a hex string', () => { const invalidInput = 'not a hex string'; @@ -94,4 +85,31 @@ describe('B512Coder', () => { coder.encode(invalidInput); }).toThrow(/Invalid struct B512/); }); + + it('throws when decoding empty bytes', async () => { + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b512 data size.') + ); + }); + + it('should throw an error when decoding an encoded 512 bit hash string that is too short', async () => { + const invalidInput = B512_ENCODED.slice(0, B512_ENCODED.length - 1); + + await expectToThrowFuelError( + () => coder.decode(invalidInput, 8), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b512 data size.') + ); + }); + + it('should throw an error when decoding an encoded 512 bit hash string that is too long', async () => { + const invalidInput = new Uint8Array(Array.from(Array(65).keys())); + + await expectToThrowFuelError( + () => coder.decode(invalidInput, 8), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid b512 byte data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/b512.ts b/packages/abi-coder/src/coders/b512.ts index 94059e4ee61..3af4cb74a28 100644 --- a/packages/abi-coder/src/coders/b512.ts +++ b/packages/abi-coder/src/coders/b512.ts @@ -1,12 +1,14 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { bn, toHex } from '@fuel-ts/math'; import { getBytesCopy } from 'ethers'; +import { WORD_SIZE } from '../constants'; + import { Coder } from './abstract-coder'; export class B512Coder extends Coder { constructor() { - super('b512', 'struct B512', 64); + super('b512', 'struct B512', WORD_SIZE * 8); } encode(value: string): Uint8Array { @@ -14,23 +16,30 @@ export class B512Coder extends Coder { try { encodedValue = getBytesCopy(value); } catch (error) { - this.throwError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); } - if (encodedValue.length !== 64) { - this.throwError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); + if (encodedValue.length !== this.encodedLength) { + throw new FuelError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); } return encodedValue; } decode(data: Uint8Array, offset: number): [string, number] { - let bytes = data.slice(offset, offset + 64); + if (data.length < this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid b512 data size.`); + } + + let bytes = data.slice(offset, offset + this.encodedLength); + const decoded = bn(bytes); if (decoded.isZero()) { bytes = new Uint8Array(64); } - if (bytes.length !== 64) { - this.throwError(ErrorCode.DECODE_ERROR, `Invalid size for b512.`); + + if (bytes.length !== this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid b512 byte data size.`); } - return [toHex(bytes, 64), offset + 64]; + + return [toHex(bytes, this.encodedLength), offset + this.encodedLength]; } } diff --git a/packages/abi-coder/src/coders/boolean.test.ts b/packages/abi-coder/src/coders/boolean.test.ts index cbe6234181f..35c39ee2f4d 100644 --- a/packages/abi-coder/src/coders/boolean.test.ts +++ b/packages/abi-coder/src/coders/boolean.test.ts @@ -3,11 +3,17 @@ import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; import { BooleanCoder } from './boolean'; -jest.mock('@fuel-ts/math', () => ({ - __esModule: true, - ...jest.requireActual('@fuel-ts/math'), -})); +vi.mock('@fuel-ts/math', async () => { + const mod = await vi.importActual('@fuel-ts/math'); + return { + __esModule: true, + ...mod, + }; +}); +/** + * @group node + */ describe('BooleanCoder', () => { const TRUE_DECODED = true; const TRUE_ENCODED = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]); @@ -64,4 +70,13 @@ describe('BooleanCoder', () => { new FuelError(ErrorCode.DECODE_ERROR, 'Invalid boolean value.') ); }); + + it('throws when decoding empty bytes', async () => { + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid boolean data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/boolean.ts b/packages/abi-coder/src/coders/boolean.ts index 7f03ea06d73..2784daef6dc 100644 --- a/packages/abi-coder/src/coders/boolean.ts +++ b/packages/abi-coder/src/coders/boolean.ts @@ -1,4 +1,4 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { bn, toBytes } from '@fuel-ts/math'; import type { SmallBytesOptions } from './abstract-coder'; @@ -26,7 +26,7 @@ export class BooleanCoder extends Coder { const isTrueBool = value === true || value === false; if (!isTrueBool) { - this.throwError(ErrorCode.ENCODE_ERROR, `Invalid boolean value.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Invalid boolean value.`); } const output: Uint8Array = toBytes(value ? 1 : 0, this.paddingLength); @@ -39,20 +39,25 @@ export class BooleanCoder extends Coder { } decode(data: Uint8Array, offset: number): [boolean, number] { + if (data.length < this.paddingLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid boolean data size.`); + } + let bytes; if (this.options.isRightPadded) { - bytes = bn(data.slice(offset, offset + 1)); + bytes = data.slice(offset, offset + 1); } else { - bytes = bn(data.slice(offset, offset + this.paddingLength)); + bytes = data.slice(offset, offset + this.paddingLength); } - if (bytes.isZero()) { + const decodedValue = bn(bytes); + if (decodedValue.isZero()) { return [false, offset + this.paddingLength]; } - if (!bytes.eq(bn(1))) { - this.throwError(ErrorCode.DECODE_ERROR, `Invalid boolean value.`); + if (!decodedValue.eq(bn(1))) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid boolean value.`); } return [true, offset + this.paddingLength]; diff --git a/packages/abi-coder/src/coders/byte.test.ts b/packages/abi-coder/src/coders/byte.test.ts index 7891d84792a..d2038acc5f3 100644 --- a/packages/abi-coder/src/coders/byte.test.ts +++ b/packages/abi-coder/src/coders/byte.test.ts @@ -5,6 +5,10 @@ import type { Uint8ArrayWithDynamicData } from '../utilities'; import { ByteCoder } from './byte'; +/** + * @group node + * @group browser + */ describe('ByteCoder', () => { it('should encode a byte', () => { const coder = new ByteCoder(); @@ -56,4 +60,40 @@ describe('ByteCoder', () => { expect(actual).toEqual(expected); expect(newOffset).toEqual(24); }); + + it('should decode a byte [with padding]', () => { + const coder = new ByteCoder(); + const input = new Uint8Array([ + 0, 0, 0, 0, 3, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 11, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, + ]); + const expected = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + const [actual, newOffset] = coder.decode(input, 0); + + expect(actual).toEqual(expected); + expect(newOffset).toEqual(24); + }); + + it('throws when decoding empty bytes', async () => { + const coder = new ByteCoder(); + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid byte data size.') + ); + }); + + it('throws when decoding empty byte data', async () => { + const coder = new ByteCoder(); + const input = new Uint8Array([ + 0, 0, 0, 0, 3, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 255, + ]); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid bytes byte data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/byte.ts b/packages/abi-coder/src/coders/byte.ts index 13e4314186d..64aebaba9eb 100644 --- a/packages/abi-coder/src/coders/byte.ts +++ b/packages/abi-coder/src/coders/byte.ts @@ -1,4 +1,4 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { bn } from '@fuel-ts/math'; import { concat } from '@fuel-ts/utils'; @@ -17,7 +17,7 @@ export class ByteCoder extends Coder { encode(value: number[]): Uint8Array { if (!Array.isArray(value)) { - this.throwError(ErrorCode.ENCODE_ERROR, `Expected array value.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Expected array value.`); } const parts: Uint8Array[] = []; @@ -54,9 +54,17 @@ export class ByteCoder extends Coder { } decode(data: Uint8Array, offset: number): [Uint8Array, number] { + if (data.length < BASE_VECTOR_OFFSET) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid byte data size.`); + } + const len = data.slice(16, 24); - const length = bn(new U64Coder().decode(len, 0)[0]).toNumber(); - const byteData = data.slice(BASE_VECTOR_OFFSET, BASE_VECTOR_OFFSET + length * 8); + const encodedLength = bn(new U64Coder().decode(len, 0)[0]).toNumber(); + const byteData = data.slice(BASE_VECTOR_OFFSET, BASE_VECTOR_OFFSET + encodedLength); + + if (byteData.length !== encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid bytes byte data size.`); + } return [byteData, offset + BASE_VECTOR_OFFSET]; } diff --git a/packages/abi-coder/src/coders/enum.test.ts b/packages/abi-coder/src/coders/enum.test.ts index 476a9257b73..2bf17f9888f 100644 --- a/packages/abi-coder/src/coders/enum.test.ts +++ b/packages/abi-coder/src/coders/enum.test.ts @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; import { bn } from '@fuel-ts/math'; import { U64_MAX } from '../../test/utils/constants'; @@ -7,6 +9,10 @@ import { BooleanCoder } from './boolean'; import { EnumCoder } from './enum'; import { U64Coder } from './u64'; +/** + * @group node + * @group browser + */ describe('EnumCoder', () => { const coder = new EnumCoder('TestEnum', { a: new BooleanCoder(), b: new U64Coder() }); @@ -57,9 +63,9 @@ describe('EnumCoder', () => { }); it('should throw an error when decoded value accesses an invalid index', () => { - const input = new Uint8Array(Array.from(Array(3).keys())); + const input = new Uint8Array(Array.from(Array(8).keys())); expect(() => { - coder.decode(input, 1); + coder.decode(input, 0); }).toThrow('Invalid caseIndex'); }); @@ -94,4 +100,11 @@ describe('EnumCoder', () => { ) ).toThrow(); }); + + it('throws when decoding empty bytes', async () => { + await expectToThrowFuelError( + () => coder.decode(new Uint8Array(), 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid enum data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/enum.ts b/packages/abi-coder/src/coders/enum.ts index 6f4e3ae1b17..c579b9b997a 100644 --- a/packages/abi-coder/src/coders/enum.ts +++ b/packages/abi-coder/src/coders/enum.ts @@ -78,6 +78,10 @@ export class EnumCoder> extends Coder< } decode(data: Uint8Array, offset: number): [DecodedValueOf, number] { + if (data.length < this.#encodedValueSize) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid enum data size.`); + } + let newOffset = offset; let decoded; diff --git a/packages/abi-coder/src/coders/number.test.ts b/packages/abi-coder/src/coders/number.test.ts index 4b1c4cda22e..13f610d5558 100644 --- a/packages/abi-coder/src/coders/number.test.ts +++ b/packages/abi-coder/src/coders/number.test.ts @@ -5,6 +5,10 @@ import { U8_MAX, U16_MAX, U32_MAX } from '../../test/utils/constants'; import { NumberCoder } from './number'; +/** + * @group node + * @group browser + */ describe('NumberCoder', () => { it('should encode min u8 number as a u8 coder', () => { const coder = new NumberCoder('u8'); @@ -120,13 +124,14 @@ describe('NumberCoder', () => { expect(actualLength).toBe(expectedLength); }); - it('should throw if a negative number is encoded', () => { + it('should throw if a negative number is encoded', async () => { const coder = new NumberCoder('u8'); const invalidInput = -1; - expect(() => { - coder.encode(invalidInput); - }).toThrow('Invalid u8'); + await expectToThrowFuelError( + () => coder.encode(invalidInput), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u8.') + ); }); it('should throw if coder is too small for number size', async () => { @@ -138,4 +143,24 @@ describe('NumberCoder', () => { new FuelError(ErrorCode.ENCODE_ERROR, `Invalid u8, too many bytes.`) ); }); + + it('throws when decoding empty bytes', async () => { + const coder = new NumberCoder('u32'); + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid number data size.') + ); + }); + + it('throws when decoding empty byte data', async () => { + const coder = new NumberCoder('u32'); + const input = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); + + await expectToThrowFuelError( + () => coder.decode(input, 8), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid number byte data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/number.ts b/packages/abi-coder/src/coders/number.ts index a8d9ee77106..757f5dfbafe 100644 --- a/packages/abi-coder/src/coders/number.ts +++ b/packages/abi-coder/src/coders/number.ts @@ -1,4 +1,4 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { toNumber, toBytes } from '@fuel-ts/math'; import type { SmallBytesOptions } from './abstract-coder'; @@ -48,11 +48,11 @@ export class NumberCoder extends Coder { try { bytes = toBytes(value); } catch (error) { - this.throwError(ErrorCode.ENCODE_ERROR, `Invalid ${this.baseType}.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Invalid ${this.baseType}.`); } if (bytes.length > this.length) { - this.throwError(ErrorCode.ENCODE_ERROR, `Invalid ${this.baseType}, too many bytes.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Invalid ${this.baseType}, too many bytes.`); } const output = toBytes(bytes, this.paddingLength); @@ -77,11 +77,20 @@ export class NumberCoder extends Coder { } decode(data: Uint8Array, offset: number): [number, number] { + if (data.length < this.paddingLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid number data size.`); + } + if (this.baseType === 'u8') { return this.decodeU8(data, offset); } - const bytes = data.slice(offset, offset + 8).slice(8 - this.length, 8); + let bytes = data.slice(offset, offset + this.paddingLength); + bytes = bytes.slice(8 - this.length, 8); + + if (bytes.length !== this.paddingLength - (this.paddingLength - this.length)) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid number byte data size.`); + } return [toNumber(bytes), offset + 8]; } diff --git a/packages/abi-coder/src/coders/option.test.ts b/packages/abi-coder/src/coders/option.test.ts index 09d11df8657..75c661d63f0 100644 --- a/packages/abi-coder/src/coders/option.test.ts +++ b/packages/abi-coder/src/coders/option.test.ts @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; import { BN } from '@fuel-ts/math'; import { U8_MAX } from '../../test/utils/constants'; @@ -6,6 +8,10 @@ import { U8_MAX } from '../../test/utils/constants'; import { OptionCoder } from './option'; import { U64Coder } from './u64'; +/** + * @group node + * @group browser + */ describe('OptionCoder', () => { it('should encode a some u64 option ', () => { const coder = new OptionCoder('test option', { Some: new U64Coder() }); @@ -51,4 +57,24 @@ describe('OptionCoder', () => { expect(actualValue).toBe(expectedValue); expect(actualLength).toBe(expectedLength); }); + + it('throws when decoding empty bytes', async () => { + const coder = new OptionCoder('test option', { None: new U64Coder() }); + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid option data size.') + ); + }); + + it('should throw when decoding invalid byte data size (too small)', async () => { + const coder = new OptionCoder('test option', { None: new U64Coder() }); + const input = new Uint8Array([0, 0]); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid option data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/option.ts b/packages/abi-coder/src/coders/option.ts index 33b447ec140..16a43f23bfd 100644 --- a/packages/abi-coder/src/coders/option.ts +++ b/packages/abi-coder/src/coders/option.ts @@ -1,3 +1,5 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; + import type { Coder } from './abstract-coder'; import type { InputValueOf, DecodedValueOf } from './enum'; import { EnumCoder } from './enum'; @@ -20,6 +22,10 @@ export class OptionCoder> extends EnumCode } decode(data: Uint8Array, offset: number): [DecodedValueOf, number] { + if (data.length < this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid option data size.`); + } + const [decoded, newOffset] = super.decode(data, offset); return [this.toOption(decoded) as DecodedValueOf, newOffset]; } diff --git a/packages/abi-coder/src/coders/raw-slice.test.ts b/packages/abi-coder/src/coders/raw-slice.test.ts index a30d821fed3..d863cb25954 100644 --- a/packages/abi-coder/src/coders/raw-slice.test.ts +++ b/packages/abi-coder/src/coders/raw-slice.test.ts @@ -6,6 +6,10 @@ import type { Uint8ArrayWithDynamicData } from '../utilities'; import { RawSliceCoder } from './raw-slice'; +/** + * @group node + * @group browser + */ describe('RawSliceCoder', () => { it('should encode a raw-slice', () => { const coder = new RawSliceCoder(); @@ -43,4 +47,34 @@ describe('RawSliceCoder', () => { expect(actual.map((v: BN) => v.toNumber())).toStrictEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); expect(newOffset).toEqual(80); }); + + it('throws when decoding empty bytes', async () => { + const coder = new RawSliceCoder(); + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid raw slice data size.') + ); + }); + + it('should throw when decoding invalid data size (too small)', async () => { + const coder = new RawSliceCoder(); + const input = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid raw slice data size.') + ); + }); + + it('should throw when decoding invalid data size (remainder)', async () => { + const coder = new RawSliceCoder(); + const input = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1]); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid raw slice data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/raw-slice.ts b/packages/abi-coder/src/coders/raw-slice.ts index 843c41ad370..1bad4981dd7 100644 --- a/packages/abi-coder/src/coders/raw-slice.ts +++ b/packages/abi-coder/src/coders/raw-slice.ts @@ -1,4 +1,4 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { BN } from '@fuel-ts/math'; import { WORD_SIZE } from '../constants'; @@ -16,7 +16,7 @@ export class RawSliceCoder extends Coder { encode(value: number[]): Uint8Array { if (!Array.isArray(value)) { - this.throwError(ErrorCode.ENCODE_ERROR, `Expected array value.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Expected array value.`); } const parts: Uint8Array[] = []; @@ -39,7 +39,11 @@ export class RawSliceCoder extends Coder { } decode(data: Uint8Array, offset: number): [BN[], number] { - const internalCoder = new ArrayCoder(new U64Coder(), data.length / 8); + if (data.length < BASE_RAW_SLICE_OFFSET || data.length % WORD_SIZE !== 0) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid raw slice data size.`); + } + + const internalCoder = new ArrayCoder(new U64Coder(), data.length / WORD_SIZE); const decoded = internalCoder.decode(data, offset); return decoded; diff --git a/packages/abi-coder/src/coders/stdString.test.ts b/packages/abi-coder/src/coders/stdString.test.ts index 6c7a039cdfe..28cf0a16f76 100644 --- a/packages/abi-coder/src/coders/stdString.test.ts +++ b/packages/abi-coder/src/coders/stdString.test.ts @@ -1,7 +1,14 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + import type { Uint8ArrayWithDynamicData } from '../utilities'; import { StdStringCoder } from './stdString'; +/** + * @group node + * @group browser + */ describe('StdStringCoder', () => { it('should encode an empty string', () => { const coder = new StdStringCoder(); @@ -89,4 +96,26 @@ describe('StdStringCoder', () => { expect(actual).toEqual(expected); expect(newOffset).toEqual(40); }); + + it('throws when decoding a string with empty bytes', async () => { + const coder = new StdStringCoder(); + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid std string data size.') + ); + }); + + it('throws when decoding a string with empty byte data', async () => { + const coder = new StdStringCoder(); + const input = new Uint8Array([ + 0, 0, 0, 0, 3, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 255, + ]); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid std string byte data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/stdString.ts b/packages/abi-coder/src/coders/stdString.ts index 6eef7cb83b1..8270a0955bd 100644 --- a/packages/abi-coder/src/coders/stdString.ts +++ b/packages/abi-coder/src/coders/stdString.ts @@ -1,3 +1,4 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { bn } from '@fuel-ts/math'; import { concat } from '@fuel-ts/utils'; import { toUtf8Bytes, toUtf8String } from 'ethers'; @@ -50,9 +51,18 @@ export class StdStringCoder extends Coder { } decode(data: Uint8Array, offset: number): [string, number] { + if (data.length < this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid std string data size.`); + } + const len = data.slice(16, 24); - const length = bn(new U64Coder().decode(len, 0)[0]).toNumber(); - const byteData = data.slice(BASE_VECTOR_OFFSET, BASE_VECTOR_OFFSET + length); + const encodedLength = bn(new U64Coder().decode(len, 0)[0]).toNumber(); + const byteData = data.slice(BASE_VECTOR_OFFSET, BASE_VECTOR_OFFSET + encodedLength); + + if (byteData.length !== encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid std string byte data size.`); + } + const value = toUtf8String(byteData); return [value, offset + BASE_VECTOR_OFFSET]; } diff --git a/packages/abi-coder/src/coders/string.test.ts b/packages/abi-coder/src/coders/string.test.ts index c84237946dd..c7056a7debc 100644 --- a/packages/abi-coder/src/coders/string.test.ts +++ b/packages/abi-coder/src/coders/string.test.ts @@ -1,7 +1,14 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + import { U8_MAX } from '../../test/utils/constants'; import { StringCoder } from './string'; +/** + * @group node + * @group browser + */ describe('StringCoder', () => { const STRING_MIN_DECODED = ''; const STRING_MIN_ENCODED = new Uint8Array(); @@ -44,22 +51,28 @@ describe('StringCoder', () => { expect(actualLength).toBe(expectedLength); }); - it('should throw when encoding a string that is too big', () => { + it('should throw when encoding a string that is too big', async () => { const coder = new StringCoder(0); const invalidInput = STRING_MAX_DECODED; - expect(() => { - coder.encode(invalidInput); - }).toThrow(); + await expectToThrowFuelError( + () => coder.encode(invalidInput), + new FuelError(ErrorCode.ENCODE_ERROR, 'Value length mismatch during encode.') + ); }); - it('should throw when encoding a string that is too small', () => { + it('should throw when encoding a string that is too small', async () => { const coder = new StringCoder(1); const invalidInput = STRING_MIN_DECODED; expect(() => { coder.encode(invalidInput); }).toThrow(); + + await expectToThrowFuelError( + () => coder.encode(invalidInput), + new FuelError(ErrorCode.ENCODE_ERROR, 'Value length mismatch during encode.') + ); }); it('should not completely decode a string that is too big for the coder', () => { @@ -70,4 +83,24 @@ describe('StringCoder', () => { expect(actualValue).not.toBe(STRING_MAX_DECODED); expect(actualLength).toBe(8); }); + + it('throws when decoding empty bytes', async () => { + const coder = new StringCoder(1); + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid string data size.') + ); + }); + + it('throws when decoding empty byte data', async () => { + const coder = new StringCoder(1); + const input = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); + + await expectToThrowFuelError( + () => coder.decode(input, 8), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid string byte data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/string.ts b/packages/abi-coder/src/coders/string.ts index d866fa3c393..746c8e8c4c5 100644 --- a/packages/abi-coder/src/coders/string.ts +++ b/packages/abi-coder/src/coders/string.ts @@ -1,4 +1,4 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { concat } from '@fuel-ts/utils'; import { toUtf8Bytes, toUtf8String } from 'ethers'; @@ -18,7 +18,7 @@ export class StringCoder extends Coder extends Coder { const STRUCT_NAME = 'TestStruct'; const coder = new StructCoder(STRUCT_NAME, { a: new BooleanCoder(), b: new U64Coder() }); @@ -128,4 +134,22 @@ describe('StructCoder', () => { ) ).toThrow(`Invalid struct ${STRUCT_NAME}`); }); + + it('throws when decoding empty bytes', async () => { + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid struct data size.') + ); + }); + + it('throws when decoding empty bytes', async () => { + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid struct data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/struct.ts b/packages/abi-coder/src/coders/struct.ts index a351586d1e7..3b8c883be4f 100644 --- a/packages/abi-coder/src/coders/struct.ts +++ b/packages/abi-coder/src/coders/struct.ts @@ -1,4 +1,4 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { concatWithDynamicData, @@ -41,7 +41,7 @@ export class StructCoder> extends Coder< const fieldValue = value[fieldName]; if (!(fieldCoder instanceof OptionCoder) && fieldValue == null) { - this.throwError( + throw new FuelError( ErrorCode.ENCODE_ERROR, `Invalid ${this.type}. Field "${fieldName}" not present.` ); @@ -60,6 +60,10 @@ export class StructCoder> extends Coder< } decode(data: Uint8Array, offset: number): [DecodedValueOf, number] { + if (data.length < this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid struct data size.`); + } + let newOffset = offset; const decodedValue = Object.keys(this.coders).reduce((obj, fieldName) => { const fieldCoder = this.coders[fieldName]; diff --git a/packages/abi-coder/src/coders/tuple.test.ts b/packages/abi-coder/src/coders/tuple.test.ts index d54934c197b..a8d15eeeeff 100644 --- a/packages/abi-coder/src/coders/tuple.test.ts +++ b/packages/abi-coder/src/coders/tuple.test.ts @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; import { bn } from '@fuel-ts/math'; import { U64_MAX } from '../../test/utils/constants'; @@ -7,6 +9,10 @@ import { BooleanCoder } from './boolean'; import { TupleCoder } from './tuple'; import { U64Coder } from './u64'; +/** + * @group node + * @group browser + */ describe('Tuple Coder', () => { const coder = new TupleCoder<[BooleanCoder, U64Coder]>([new BooleanCoder(), new U64Coder()]); @@ -105,4 +111,22 @@ describe('Tuple Coder', () => { ) ).toThrow(); }); + + it('throws when decoding empty bytes', async () => { + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid tuple data size.') + ); + }); + + it('throws when decoding empty bytes', async () => { + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid tuple data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/tuple.ts b/packages/abi-coder/src/coders/tuple.ts index 9a2564607d9..b52b41477bc 100644 --- a/packages/abi-coder/src/coders/tuple.ts +++ b/packages/abi-coder/src/coders/tuple.ts @@ -1,4 +1,4 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { concatWithDynamicData, @@ -31,7 +31,7 @@ export class TupleCoder extends Coder< encode(value: InputValueOf): Uint8Array { if (this.coders.length !== value.length) { - this.throwError(ErrorCode.ENCODE_ERROR, `Types/values length mismatch.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Types/values length mismatch.`); } return concatWithDynamicData( @@ -46,6 +46,10 @@ export class TupleCoder extends Coder< } decode(data: Uint8Array, offset: number): [DecodedValueOf, number] { + if (data.length < this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid tuple data size.`); + } + let newOffset = offset; const decodedValue = this.coders.map((coder) => { let decoded; diff --git a/packages/abi-coder/src/coders/u64.test.ts b/packages/abi-coder/src/coders/u64.test.ts index b4307fe0521..a22e1ff42c0 100644 --- a/packages/abi-coder/src/coders/u64.test.ts +++ b/packages/abi-coder/src/coders/u64.test.ts @@ -1,9 +1,15 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; import { BN, bn } from '@fuel-ts/math'; import { U8_MAX, U16_MAX, U32_MAX, U64_MAX } from '../../test/utils/constants'; import { U64Coder } from './u64'; +/** + * @group node + * @group browser + */ describe('U64Coder', () => { const coder = new U64Coder(); @@ -102,9 +108,28 @@ describe('U64Coder', () => { expect(actualLength).toBe(expectedLength); }); - it('should throw an error when encoding an invalid u64', () => { - expect(() => { - coder.encode(bn(U64_MAX).add(1)); - }).toThrow('Invalid u64'); + it('should throw an error when encoding an invalid u64', async () => { + await expectToThrowFuelError( + () => coder.encode(bn(U64_MAX).add(1)), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid u64.') + ); + }); + + it('throws when decoding empty bytes', async () => { + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid u64 data size.') + ); + }); + + it('throws when decoding empty byte data', async () => { + const input = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]); + + await expectToThrowFuelError( + () => coder.decode(input, 8), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid u64 byte data size.') + ); }); }); diff --git a/packages/abi-coder/src/coders/u64.ts b/packages/abi-coder/src/coders/u64.ts index 3d837aab331..bfa021e70fa 100644 --- a/packages/abi-coder/src/coders/u64.ts +++ b/packages/abi-coder/src/coders/u64.ts @@ -1,30 +1,38 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { BN, BNInput } from '@fuel-ts/math'; import { bn, toBytes } from '@fuel-ts/math'; +import { WORD_SIZE } from '../constants'; + import { Coder } from './abstract-coder'; export class U64Coder extends Coder { constructor() { - super('u64', 'u64', 8); + super('u64', 'u64', WORD_SIZE); } encode(value: BNInput): Uint8Array { let bytes; try { - bytes = toBytes(value, 8); + bytes = toBytes(value, WORD_SIZE); } catch (error) { - this.throwError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Invalid ${this.type}.`); } return bytes; } decode(data: Uint8Array, offset: number): [BN, number] { - let bytes = data.slice(offset, offset + 8); - bytes = bytes.slice(0, 8); + if (data.length < this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid ${this.type} data size.`); + } + const byteData = data.slice(offset, offset + this.encodedLength); + + if (byteData.length !== this.encodedLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid ${this.type} byte data size.`); + } - return [bn(bytes), offset + 8]; + return [bn(byteData), offset + WORD_SIZE]; } } diff --git a/packages/abi-coder/src/coders/vec.test.ts b/packages/abi-coder/src/coders/vec.test.ts index f9e0b73af45..122bc8606b7 100644 --- a/packages/abi-coder/src/coders/vec.test.ts +++ b/packages/abi-coder/src/coders/vec.test.ts @@ -1,6 +1,7 @@ import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; +import { U32_MAX } from '../../test/utils/constants'; import type { Uint8ArrayWithDynamicData } from '../utilities'; import type { SmallBytesOptions } from './abstract-coder'; @@ -8,6 +9,9 @@ import { BooleanCoder } from './boolean'; import { NumberCoder } from './number'; import { VecCoder } from './vec'; +/** + * @group node + */ describe('VecCoder', () => { const sbOptions: SmallBytesOptions = { isSmallBytes: true, @@ -47,4 +51,36 @@ describe('VecCoder', () => { expect(actual).toEqual(expected); expect(newOffset).toEqual(24); }); + + it('throws when decoding empty vec bytes', async () => { + const coder = new VecCoder(new NumberCoder('u8')); + const input = new Uint8Array(0); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.ENCODE_ERROR, 'Invalid vec data size.') + ); + }); + + it('throws when decoding empty vec byte data', async () => { + const coder = new VecCoder(new NumberCoder('u8')); + const input = new Uint8Array([ + 0, 0, 0, 0, 3, 255, 255, 225, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 255, + ]); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid vec byte data size.') + ); + }); + + it('throws when decoding vec larger than max size', async () => { + const coder = new VecCoder(new NumberCoder('u8')); + const input = new Uint8Array(U32_MAX + 1); + + await expectToThrowFuelError( + () => coder.decode(input, 0), + new FuelError(ErrorCode.DECODE_ERROR, 'Invalid vec data size.') + ); + }); }); diff --git a/packages/abi-coder/src/coders/vec.ts b/packages/abi-coder/src/coders/vec.ts index 666b5ea7155..ce00085f7f8 100644 --- a/packages/abi-coder/src/coders/vec.ts +++ b/packages/abi-coder/src/coders/vec.ts @@ -1,6 +1,7 @@ -import { ErrorCode } from '@fuel-ts/errors'; +import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { bn } from '@fuel-ts/math'; +import { MAX_BYTES } from '../constants'; import type { Uint8ArrayWithDynamicData } from '../utilities'; import { concatWithDynamicData, BASE_VECTOR_OFFSET, chunkByLength } from '../utilities'; @@ -24,7 +25,7 @@ export class VecCoder extends Coder< encode(value: InputValueOf): Uint8Array { if (!Array.isArray(value)) { - this.throwError(ErrorCode.ENCODE_ERROR, `Expected array value.`); + throw new FuelError(ErrorCode.ENCODE_ERROR, `Expected array value.`); } const parts: Uint8Array[] = []; @@ -48,12 +49,19 @@ export class VecCoder extends Coder< } decode(data: Uint8Array, offset: number): [DecodedValueOf, number] { + if (data.length < BASE_VECTOR_OFFSET || data.length > MAX_BYTES) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid vec data size.`); + } + const len = data.slice(16, 24); - const length = bn(new U64Coder().decode(len, 0)[0]).toNumber(); - const vectorRawData = data.slice( - BASE_VECTOR_OFFSET, - BASE_VECTOR_OFFSET + length * this.coder.encodedLength - ); + const encodedLength = bn(new U64Coder().decode(len, 0)[0]).toNumber(); + const vectorRawDataLength = encodedLength * this.coder.encodedLength; + const vectorRawData = data.slice(BASE_VECTOR_OFFSET, BASE_VECTOR_OFFSET + vectorRawDataLength); + + if (vectorRawData.length !== vectorRawDataLength) { + throw new FuelError(ErrorCode.DECODE_ERROR, `Invalid vec byte data size.`); + } + return [ chunkByLength(vectorRawData, this.coder.encodedLength).map( (chunk) => this.coder.decode(chunk, 0)[0] diff --git a/packages/abi-coder/src/constants.ts b/packages/abi-coder/src/constants.ts index 453837b966a..5358c00be47 100644 --- a/packages/abi-coder/src/constants.ts +++ b/packages/abi-coder/src/constants.ts @@ -19,6 +19,7 @@ export const ADDRESS_LEN = BYTES_32; export const NONCE_LEN = BYTES_32; export const TX_LEN = WORD_SIZE * 4; export const TX_POINTER_LEN = WORD_SIZE * 2; +export const MAX_BYTES = 2 ** 32 - 1; // Max u32 export const calculateVmTxMemory = ({ maxInputs }: { maxInputs: number }) => BYTES_32 + // Tx ID diff --git a/packages/abi-coder/src/utilities.test.ts b/packages/abi-coder/src/utilities.test.ts index 1eb978f2566..d0e04ab5294 100644 --- a/packages/abi-coder/src/utilities.test.ts +++ b/packages/abi-coder/src/utilities.test.ts @@ -3,6 +3,10 @@ import { concat } from '@fuel-ts/utils'; import type { Uint8ArrayWithDynamicData } from './utilities'; import { unpackDynamicData, concatWithDynamicData } from './utilities'; +/** + * @group node + * @group browser + */ describe('Abi Coder Utilities', () => { it('can concatWithVectorData [no dynamicData, should match original concat]', () => { const data1 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 24]); diff --git a/packages/abi-coder/test/interface.test.ts b/packages/abi-coder/test/interface.test.ts index e8933804c39..207ab3a9144 100644 --- a/packages/abi-coder/test/interface.test.ts +++ b/packages/abi-coder/test/interface.test.ts @@ -49,6 +49,9 @@ function encodeVectorFully( const exhaustiveExamplesInterface = new Interface(exhaustiveExamplesAbi); +/** + * @group node + */ describe('Abi interface', () => { it('can retrieve a function fragment', () => { const fn = exhaustiveExamplesInterface.functions.entry_one; diff --git a/packages/abi-coder/tsdoc.json b/packages/abi-coder/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/abi-coder/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/abi-typegen/README.md b/packages/abi-typegen/README.md index be3540555b1..767cc52c95c 100644 --- a/packages/abi-typegen/README.md +++ b/packages/abi-typegen/README.md @@ -17,7 +17,7 @@ See the full ABI-spec [here](https://github.com/FuelLabs/fuel-specs/blob/master/ ## Documentation -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/guide/abi-typegen/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/abi-typegen/) ## Installation diff --git a/packages/abi-typegen/package.json b/packages/abi-typegen/package.json index 5958ca010dd..2f101172f83 100644 --- a/packages/abi-typegen/package.json +++ b/packages/abi-typegen/package.json @@ -10,7 +10,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/abi-typegen/src/AbiTypeGen.test.ts b/packages/abi-typegen/src/AbiTypeGen.test.ts index 5581d49833a..bf42604796b 100644 --- a/packages/abi-typegen/src/AbiTypeGen.test.ts +++ b/packages/abi-typegen/src/AbiTypeGen.test.ts @@ -7,18 +7,23 @@ import * as assembleContractsMod from './utils/assembleContracts'; import * as assemblePredicatesMod from './utils/assemblePredicates'; import * as assembleScriptsMod from './utils/assembleScripts'; +/** + * @group node + */ describe('AbiTypegen.ts', () => { // Use as e sample of HORRIBLE auto-code-formatting function mockAllDeps() { - const assembleContracts = jest + const assembleContracts = vi .spyOn(assembleContractsMod, 'assembleContracts') - .mockImplementation(); + .mockImplementation(() => []); - const assembleScripts = jest.spyOn(assembleScriptsMod, 'assembleScripts').mockImplementation(); + const assembleScripts = vi + .spyOn(assembleScriptsMod, 'assembleScripts') + .mockImplementation(() => []); - const assemblePredicates = jest + const assemblePredicates = vi .spyOn(assemblePredicatesMod, 'assemblePredicates') - .mockImplementation(); + .mockImplementation(() => []); return { assembleContracts, @@ -27,8 +32,13 @@ describe('AbiTypegen.ts', () => { }; } - beforeEach(jest.resetAllMocks); - afterEach(jest.resetAllMocks); + beforeEach(() => { + vi.resetAllMocks(); + }); + + afterEach(() => { + vi.resetAllMocks(); + }); test('should create multiple ABI instances for: contracts', () => { const { assembleContracts, assembleScripts, assemblePredicates } = mockAllDeps(); @@ -75,7 +85,7 @@ describe('AbiTypegen.ts', () => { test('should throw for unknown programType', async () => { const { assembleContracts, assembleScripts, assemblePredicates } = mockAllDeps(); - const programType = 'nope' as ProgramTypeEnum; // forced cast to cause error + const programType = 'nope' as ProgramTypeEnum; const { error } = await safeExec(() => { getNewAbiTypegen({ programType, includeBinFiles: true }); diff --git a/packages/abi-typegen/src/abi/Abi.test.ts b/packages/abi-typegen/src/abi/Abi.test.ts index 01a50cf80e2..7fc76c5f5e5 100644 --- a/packages/abi-typegen/src/abi/Abi.test.ts +++ b/packages/abi-typegen/src/abi/Abi.test.ts @@ -15,18 +15,21 @@ import { EnumType } from './types/EnumType'; import { OptionType } from './types/OptionType'; import { VectorType } from './types/VectorType'; +/** + * @group node + */ describe('Abi.ts', () => { /* Test helpers */ function mockAllDeps() { - const parseTypes = jest.spyOn(parseTypesMod, 'parseTypes').mockImplementation(() => []); + const parseTypes = vi.spyOn(parseTypesMod, 'parseTypes').mockImplementation(() => []); - const parseFunctions = jest + const parseFunctions = vi .spyOn(parseFunctionsMod, 'parseFunctions') .mockImplementation(() => []); - const parseConfigurables = jest + const parseConfigurables = vi .spyOn(parseConfigurablesMod, 'parseConfigurables') .mockImplementation(() => []); diff --git a/packages/abi-typegen/src/abi/configurable/Configurable.test.ts b/packages/abi-typegen/src/abi/configurable/Configurable.test.ts index 515e7cbe6ca..3122d5263f8 100644 --- a/packages/abi-typegen/src/abi/configurable/Configurable.test.ts +++ b/packages/abi-typegen/src/abi/configurable/Configurable.test.ts @@ -8,6 +8,9 @@ import * as findTypeMod from '../../utils/findType'; import { Configurable } from './Configurable'; +/** + * @group node + */ describe('Configurable.ts', () => { function mockAllDeps() { const rawAbiType: IRawAbiTypeRoot = { @@ -25,10 +28,12 @@ describe('Configurable.ts', () => { }, rawAbiType, requiredFuelsMembersImports: [], - parseComponentsAttributes: jest.fn(), + parseComponentsAttributes: vi.fn(), }; - const findType = jest.spyOn(findTypeMod, 'findType').mockReturnValue(type); + const findType = vi + .spyOn(findTypeMod, 'findType') + .mockImplementation(vi.fn().mockReturnValue(type)); return { type, diff --git a/packages/abi-typegen/src/abi/functions/Function.test.ts b/packages/abi-typegen/src/abi/functions/Function.test.ts index ccb4a45002a..8eaabb796c3 100644 --- a/packages/abi-typegen/src/abi/functions/Function.test.ts +++ b/packages/abi-typegen/src/abi/functions/Function.test.ts @@ -6,6 +6,9 @@ import { parseTypes } from '../../utils/parseTypes'; import { Function } from './Function'; +/** + * @group node + */ describe('Function.ts', () => { /* Method: `getDeclaration` diff --git a/packages/abi-typegen/src/abi/types/ArrayType.test.ts b/packages/abi-typegen/src/abi/types/ArrayType.test.ts index f5b5ddf9742..aac57706da5 100644 --- a/packages/abi-typegen/src/abi/types/ArrayType.test.ts +++ b/packages/abi-typegen/src/abi/types/ArrayType.test.ts @@ -10,8 +10,13 @@ import * as parseTypeArgumentsMod from '../../utils/parseTypeArguments'; import { ArrayType } from './ArrayType'; import { TupleType } from './TupleType'; +/** + * @group node + */ describe('ArrayType.ts', () => { - beforeEach(jest.restoreAllMocks); + beforeEach(() => { + vi.restoreAllMocks(); + }); test('should properly evaluate type suitability', () => { const suitableForTuple = ArrayType.isSuitableFor({ type: TupleType.swayType }); @@ -22,7 +27,7 @@ describe('ArrayType.ts', () => { }); test('should properly parse type attributes: simple', () => { - const parseTypeArguments = jest.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); + const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); const project = getTypegenForcProject(AbiTypegenProjectsEnum.STRUCT_WITH_ARRAY); @@ -40,7 +45,7 @@ describe('ArrayType.ts', () => { }); test('should properly parse type attributes: nested', () => { - const parseTypeArguments = jest.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); + const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); const project = getTypegenForcProject(AbiTypegenProjectsEnum.ARRAY_WITH_GENERICS); diff --git a/packages/abi-typegen/src/abi/types/AssetIdType.test.ts b/packages/abi-typegen/src/abi/types/AssetIdType.test.ts index cbfe43ac48b..e4dcf999dbb 100644 --- a/packages/abi-typegen/src/abi/types/AssetIdType.test.ts +++ b/packages/abi-typegen/src/abi/types/AssetIdType.test.ts @@ -1,6 +1,9 @@ import { AssetIdType } from './AssetIdType'; import { BoolType } from './BoolType'; +/** + * @group node + */ describe('AssetIdType.ts', () => { test('should properly parse type attributes', () => { const b512 = new AssetIdType({ diff --git a/packages/abi-typegen/src/abi/types/B256Type.test.ts b/packages/abi-typegen/src/abi/types/B256Type.test.ts index bcd502bb1e3..904d57ffb16 100644 --- a/packages/abi-typegen/src/abi/types/B256Type.test.ts +++ b/packages/abi-typegen/src/abi/types/B256Type.test.ts @@ -1,6 +1,9 @@ import { B256Type } from './B256Type'; import { BoolType } from './BoolType'; +/** + * @group node + */ describe('B256Type.ts', () => { test('should properly parse type attributes', () => { const b256 = new B256Type({ diff --git a/packages/abi-typegen/src/abi/types/B512Type.test.ts b/packages/abi-typegen/src/abi/types/B512Type.test.ts index fbf2cb8c8bc..65be8cda76f 100644 --- a/packages/abi-typegen/src/abi/types/B512Type.test.ts +++ b/packages/abi-typegen/src/abi/types/B512Type.test.ts @@ -1,6 +1,9 @@ import { B512Type } from './B512Type'; import { BoolType } from './BoolType'; +/** + * @group node + */ describe('B512Type.ts', () => { test('should properly parse type attributes', () => { const b512 = new B512Type({ diff --git a/packages/abi-typegen/src/abi/types/BoolType.test.ts b/packages/abi-typegen/src/abi/types/BoolType.test.ts index 19d13cbba50..203f4ff2031 100644 --- a/packages/abi-typegen/src/abi/types/BoolType.test.ts +++ b/packages/abi-typegen/src/abi/types/BoolType.test.ts @@ -1,6 +1,9 @@ import { BoolType } from './BoolType'; import { U16Type } from './U16Type'; +/** + * @group node + */ describe('BoolType.ts', () => { test('should properly parse type attributes', () => { const bool = new BoolType({ diff --git a/packages/abi-typegen/src/abi/types/BytesType.test.ts b/packages/abi-typegen/src/abi/types/BytesType.test.ts index 7d081332d1a..44ace6cc41a 100644 --- a/packages/abi-typegen/src/abi/types/BytesType.test.ts +++ b/packages/abi-typegen/src/abi/types/BytesType.test.ts @@ -1,6 +1,9 @@ import { BytesType } from './BytesType'; import { StructType } from './StructType'; +/** + * @group node + */ describe('BytesType.ts', () => { test('should properly parse type attributes', () => { const bytes = new BytesType({ diff --git a/packages/abi-typegen/src/abi/types/EnumType.test.ts b/packages/abi-typegen/src/abi/types/EnumType.test.ts index ef143ffb4c4..bbe407502b8 100644 --- a/packages/abi-typegen/src/abi/types/EnumType.test.ts +++ b/packages/abi-typegen/src/abi/types/EnumType.test.ts @@ -11,6 +11,9 @@ import type { EnumType } from './EnumType'; import { StructType } from './StructType'; import { U16Type } from './U16Type'; +/** + * @group node + */ describe('EnumType.ts', () => { /* Test helpers diff --git a/packages/abi-typegen/src/abi/types/EvmAddressType.test.ts b/packages/abi-typegen/src/abi/types/EvmAddressType.test.ts index acba25c5908..a3345fc121c 100644 --- a/packages/abi-typegen/src/abi/types/EvmAddressType.test.ts +++ b/packages/abi-typegen/src/abi/types/EvmAddressType.test.ts @@ -11,9 +11,12 @@ import { EvmAddressType } from './EvmAddressType'; import { StructType } from './StructType'; import { VectorType } from './VectorType'; +/** + * @group node + */ describe('EvmAddressType.ts', () => { test('should properly parse type attributes', () => { - const parseTypeArguments = jest.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); + const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); const project = getTypegenForcProject(AbiTypegenProjectsEnum.EVM_ADDRESS); diff --git a/packages/abi-typegen/src/abi/types/GenericType.test.ts b/packages/abi-typegen/src/abi/types/GenericType.test.ts index 9dadff39f9e..be4d6c1b3bb 100644 --- a/packages/abi-typegen/src/abi/types/GenericType.test.ts +++ b/packages/abi-typegen/src/abi/types/GenericType.test.ts @@ -1,6 +1,9 @@ import { GenericType } from './GenericType'; import { StructType } from './StructType'; +/** + * @group node + */ describe('GenericType.ts', () => { test('should properly parse type attributes', () => { const generic = new GenericType({ diff --git a/packages/abi-typegen/src/abi/types/OptionType.test.ts b/packages/abi-typegen/src/abi/types/OptionType.test.ts index 231e5c20009..2e8d80f7b3c 100644 --- a/packages/abi-typegen/src/abi/types/OptionType.test.ts +++ b/packages/abi-typegen/src/abi/types/OptionType.test.ts @@ -9,6 +9,9 @@ import { makeType } from '../../utils/makeType'; import { EnumType } from './EnumType'; import { OptionType } from './OptionType'; +/** + * @group node + */ describe('OptionType.ts', () => { /* Test helpers diff --git a/packages/abi-typegen/src/abi/types/RawUntypedPtr.test.ts b/packages/abi-typegen/src/abi/types/RawUntypedPtr.test.ts index 544165c3888..21f34af07b2 100644 --- a/packages/abi-typegen/src/abi/types/RawUntypedPtr.test.ts +++ b/packages/abi-typegen/src/abi/types/RawUntypedPtr.test.ts @@ -1,6 +1,9 @@ import { RawUntypedPtr } from './RawUntypedPtr'; import { U8Type } from './U8Type'; +/** + * @group node + */ describe('RawUntypedPtrType.ts', () => { test('should properly parse type attributes', () => { const rawUntypedPtr = new RawUntypedPtr({ diff --git a/packages/abi-typegen/src/abi/types/RawUntypedSlice.test.ts b/packages/abi-typegen/src/abi/types/RawUntypedSlice.test.ts index dff7d7121cf..a300099c7da 100644 --- a/packages/abi-typegen/src/abi/types/RawUntypedSlice.test.ts +++ b/packages/abi-typegen/src/abi/types/RawUntypedSlice.test.ts @@ -1,6 +1,9 @@ import { RawUntypedSlice } from './RawUntypedSlice'; import { StructType } from './StructType'; +/** + * @group node + */ describe('RawUntypedSlice.ts', () => { test('should properly parse type attributes', () => { const rawSlice = new RawUntypedSlice({ diff --git a/packages/abi-typegen/src/abi/types/StdStringType.test.ts b/packages/abi-typegen/src/abi/types/StdStringType.test.ts index 8d87fc6974e..46624dc9d14 100644 --- a/packages/abi-typegen/src/abi/types/StdStringType.test.ts +++ b/packages/abi-typegen/src/abi/types/StdStringType.test.ts @@ -1,6 +1,9 @@ import { StdStringType } from './StdStringType'; import { StructType } from './StructType'; +/** + * @group node + */ describe('StdStringType.ts', () => { test('should properly parse type attributes', () => { const stdString = new StdStringType({ diff --git a/packages/abi-typegen/src/abi/types/StrSlicesType.test.ts b/packages/abi-typegen/src/abi/types/StrSlicesType.test.ts index 285c42afe5c..ac9242642ab 100644 --- a/packages/abi-typegen/src/abi/types/StrSlicesType.test.ts +++ b/packages/abi-typegen/src/abi/types/StrSlicesType.test.ts @@ -3,6 +3,9 @@ import { StdStringType } from './StdStringType'; import { StrSliceType } from './StrSliceType'; import { StrType } from './StrType'; +/** + * @group node + */ describe('StrSlicesType.ts', () => { test('should properly parse type attributes', () => { const strSlices = new StrSliceType({ diff --git a/packages/abi-typegen/src/abi/types/StrType.test.ts b/packages/abi-typegen/src/abi/types/StrType.test.ts index 6da95512f22..ff141931005 100644 --- a/packages/abi-typegen/src/abi/types/StrType.test.ts +++ b/packages/abi-typegen/src/abi/types/StrType.test.ts @@ -1,6 +1,9 @@ import { BoolType } from './BoolType'; import { StrType } from './StrType'; +/** + * @group node + */ describe('StrType.ts', () => { test('should properly parse type attributes', () => { const str = new StrType({ diff --git a/packages/abi-typegen/src/abi/types/StructType.test.ts b/packages/abi-typegen/src/abi/types/StructType.test.ts index 7f275ecacaf..05237215cda 100644 --- a/packages/abi-typegen/src/abi/types/StructType.test.ts +++ b/packages/abi-typegen/src/abi/types/StructType.test.ts @@ -14,9 +14,12 @@ import { StdStringType } from './StdStringType'; import { StructType } from './StructType'; import { U16Type } from './U16Type'; +/** + * @group node + */ describe('StructType.ts', () => { test('should properly parse type attributes', () => { - const parseTypeArguments = jest.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); + const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); const project = getTypegenForcProject(AbiTypegenProjectsEnum.STRUCT_SIMPLE); diff --git a/packages/abi-typegen/src/abi/types/TupleType.test.ts b/packages/abi-typegen/src/abi/types/TupleType.test.ts index ec43dbe5f18..51b8eded04d 100644 --- a/packages/abi-typegen/src/abi/types/TupleType.test.ts +++ b/packages/abi-typegen/src/abi/types/TupleType.test.ts @@ -10,9 +10,12 @@ import * as parseTypeArgumentsMod from '../../utils/parseTypeArguments'; import { ArrayType } from './ArrayType'; import { TupleType } from './TupleType'; +/** + * @group node + */ describe('TupleType.ts', () => { test('should properly parse type attributes', () => { - const parseTypeArguments = jest.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); + const parseTypeArguments = vi.spyOn(parseTypeArgumentsMod, 'parseTypeArguments'); const project = getTypegenForcProject(AbiTypegenProjectsEnum.TUPLE_SIMPLE); const rawTypes = project.abiContents.types; diff --git a/packages/abi-typegen/src/abi/types/U16Type.test.ts b/packages/abi-typegen/src/abi/types/U16Type.test.ts index b9c59d8590e..4ef9f20688d 100644 --- a/packages/abi-typegen/src/abi/types/U16Type.test.ts +++ b/packages/abi-typegen/src/abi/types/U16Type.test.ts @@ -1,6 +1,9 @@ import { U16Type } from './U16Type'; import { U32Type } from './U32Type'; +/** + * @group node + */ describe('U16Type.ts', () => { test('should properly parse type attributes', () => { const u16 = new U16Type({ diff --git a/packages/abi-typegen/src/abi/types/U32Type.test.ts b/packages/abi-typegen/src/abi/types/U32Type.test.ts index 8020f02b2fa..2d442c3ee4d 100644 --- a/packages/abi-typegen/src/abi/types/U32Type.test.ts +++ b/packages/abi-typegen/src/abi/types/U32Type.test.ts @@ -1,6 +1,9 @@ import { U32Type } from './U32Type'; import { U64Type } from './U64Type'; +/** + * @group node + */ describe('U32Type.ts', () => { test('should properly parse type attributes', () => { const u32 = new U32Type({ diff --git a/packages/abi-typegen/src/abi/types/U64Type.test.ts b/packages/abi-typegen/src/abi/types/U64Type.test.ts index 05035480327..97c47d08ae3 100644 --- a/packages/abi-typegen/src/abi/types/U64Type.test.ts +++ b/packages/abi-typegen/src/abi/types/U64Type.test.ts @@ -1,6 +1,9 @@ import { U32Type } from './U32Type'; import { U64Type } from './U64Type'; +/** + * @group node + */ describe('U64Type.ts', () => { test('should properly parse type attributes', () => { const u64 = new U64Type({ diff --git a/packages/abi-typegen/src/abi/types/U8Type.test.ts b/packages/abi-typegen/src/abi/types/U8Type.test.ts index bd31eacbb64..87b9828c714 100644 --- a/packages/abi-typegen/src/abi/types/U8Type.test.ts +++ b/packages/abi-typegen/src/abi/types/U8Type.test.ts @@ -1,6 +1,9 @@ import { U16Type } from './U16Type'; import { U8Type } from './U8Type'; +/** + * @group node + */ describe('U8Type.ts', () => { test('should properly parse type attributes', () => { const u8 = new U8Type({ diff --git a/packages/abi-typegen/src/abi/types/VectorType.test.ts b/packages/abi-typegen/src/abi/types/VectorType.test.ts index fbb97706dce..ca6b22a0edb 100644 --- a/packages/abi-typegen/src/abi/types/VectorType.test.ts +++ b/packages/abi-typegen/src/abi/types/VectorType.test.ts @@ -1,6 +1,9 @@ import { StructType } from './StructType'; import { VectorType } from './VectorType'; +/** + * @group node + */ describe('VectorType.ts', () => { test('should properly parse type attributes', () => { const vector = new VectorType({ diff --git a/packages/abi-typegen/src/cli.test.ts b/packages/abi-typegen/src/cli.test.ts index ef0fe525bb0..683ffafca44 100644 --- a/packages/abi-typegen/src/cli.test.ts +++ b/packages/abi-typegen/src/cli.test.ts @@ -9,21 +9,30 @@ import { run, runCliAction } from './cli'; import * as runTypegenMod from './runTypegen'; import { ProgramTypeEnum } from './types/enums/ProgramTypeEnum'; +/** + * @group node + */ describe('cli.ts', () => { function mockDeps(params?: { runTypegenError: Error }) { - const runTypegen = jest.spyOn(runTypegenMod, 'runTypegen').mockImplementation(() => { + const runTypegen = vi.spyOn(runTypegenMod, 'runTypegen').mockImplementation(() => { if (params?.runTypegenError) { throw params?.runTypegenError; } }); - const exit = jest.spyOn(process, 'exit').mockImplementation(); - const err = jest.spyOn(stderr, 'write').mockImplementation(); + + const exit = vi.spyOn(process, 'exit').mockImplementation(vi.fn()); + const err = vi.spyOn(stderr, 'write').mockResolvedValue(true); return { exit, err, runTypegen }; } - beforeEach(jest.resetAllMocks); - afterEach(jest.restoreAllMocks); + beforeEach(() => { + vi.resetAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); test('should call runTypegen with proper params: for Contracts', async () => { const { runTypegen } = mockDeps(); diff --git a/packages/abi-typegen/src/index.test.ts b/packages/abi-typegen/src/index.test.ts index 62a9b1d7645..2de17d17b44 100644 --- a/packages/abi-typegen/src/index.test.ts +++ b/packages/abi-typegen/src/index.test.ts @@ -1,5 +1,8 @@ import * as indexMod from './index'; +/** + * @group node + */ describe('index.ts', () => { test('should export AbiTypeGen class', () => { expect(indexMod.AbiTypeGen).toBeTruthy(); diff --git a/packages/abi-typegen/src/runTypegen.test.ts b/packages/abi-typegen/src/runTypegen.test.ts index 1ca5777fa0c..dab049f18e7 100644 --- a/packages/abi-typegen/src/runTypegen.test.ts +++ b/packages/abi-typegen/src/runTypegen.test.ts @@ -12,6 +12,9 @@ import { import { runTypegen } from './runTypegen'; import { ProgramTypeEnum } from './types/enums/ProgramTypeEnum'; +/** + * @group node + */ describe('runTypegen.js', () => { test('should run typegen, using: globals', async () => { const project = getTypegenForcProject(AbiTypegenProjectsEnum.FULL); @@ -157,7 +160,7 @@ describe('runTypegen.js', () => { }); test('should log messages to stdout', async () => { - const stdoutWrite = jest.spyOn(process.stdout, 'write').mockImplementation(); + const stdoutWrite = vi.spyOn(process.stdout, 'write').mockResolvedValue(true); // setup temp sway project const project = getTypegenForcProject(AbiTypegenProjectsEnum.SCRIPT); @@ -252,6 +255,67 @@ describe('runTypegen.js', () => { ); }); + test('should write messages to stdout', async () => { + const project = getTypegenForcProject(AbiTypegenProjectsEnum.FULL); + + // compute filepaths + const cwd = process.cwd(); + const inputs = [project.inputGlobal]; + const output = project.tempDir; + const normalizedName = project.normalizedName; + const programType = ProgramTypeEnum.CONTRACT; + const silent = false; + + // duplicates ABI JSON so we can validate if all inputs + // are being collected (and not only the first one) + const from = project.abiPath; + const to = from.replace('-abi.json', '2-abi.json'); + + // also duplicates BIN file + const fromBin = project.binPath; + const toBin = fromBin.replace('.bin', '2.bin'); + + cpSync(from, to); + cpSync(fromBin, toBin); + + // mocking + const write = vi.spyOn(process.stdout, 'write').mockReturnValue(true); + + // executes program + const fn = () => + runTypegen({ + cwd, + inputs, + output, + programType, + silent, + }); + + const { error } = await safeExec(fn); + + // validates execution was ok + expect(error).toBeFalsy(); + + // check if all files were created + const files = [ + join(output, 'index.ts'), + join(output, 'common.d.ts'), + join(output, `${normalizedName}Abi.d.ts`), + join(output, `${normalizedName}2Abi.d.ts`), + join(output, 'factories', `${normalizedName}Abi__factory.ts`), + join(output, `${normalizedName}Abi.hex.ts`), + join(output, `${normalizedName}2Abi.hex.ts`), + ]; + + expect(files.length).toEqual(7); + + files.forEach((f) => { + expect(existsSync(f)).toEqual(true); + }); + + expect(write).toHaveBeenCalled(); + }); + test('should error for no ABI in inputs', async () => { const cwd = process.cwd(); const inputs = ['./*-abis.json']; // abi don't exist diff --git a/packages/abi-typegen/src/templates/common/common.test.ts b/packages/abi-typegen/src/templates/common/common.test.ts index 601981e6a63..6209fb706a4 100644 --- a/packages/abi-typegen/src/templates/common/common.test.ts +++ b/packages/abi-typegen/src/templates/common/common.test.ts @@ -2,6 +2,9 @@ import { mockVersions } from '../../../test/utils/mockVersions'; import { renderCommonTemplate } from './common'; +/** + * @group node + */ describe('templates/common', () => { test('should render common template', () => { // mocking diff --git a/packages/abi-typegen/src/templates/common/index.test.ts b/packages/abi-typegen/src/templates/common/index.test.ts index f49e9aa97f4..bf5355502c8 100644 --- a/packages/abi-typegen/src/templates/common/index.test.ts +++ b/packages/abi-typegen/src/templates/common/index.test.ts @@ -9,6 +9,9 @@ import { ProgramTypeEnum } from '../../types/enums/ProgramTypeEnum'; import { renderIndexTemplate } from './index'; +/** + * @group node + */ describe('templates/index', () => { test('should render index template', () => { // mocking diff --git a/packages/abi-typegen/src/templates/contract/bytecode.test.ts b/packages/abi-typegen/src/templates/contract/bytecode.test.ts index f8af6617893..e110dfb93a7 100644 --- a/packages/abi-typegen/src/templates/contract/bytecode.test.ts +++ b/packages/abi-typegen/src/templates/contract/bytecode.test.ts @@ -7,6 +7,9 @@ import { mockVersions } from '../../../test/utils/mockVersions'; import { renderBytecodeTemplate } from './bytecode'; +/** + * @group node + */ describe('templates/contract/bytecode', () => { test('should render bytecode template', () => { // mocking diff --git a/packages/abi-typegen/src/templates/contract/dts.test.ts b/packages/abi-typegen/src/templates/contract/dts.test.ts index 2e6b59ff9b9..15e6221b6ab 100644 --- a/packages/abi-typegen/src/templates/contract/dts.test.ts +++ b/packages/abi-typegen/src/templates/contract/dts.test.ts @@ -10,6 +10,9 @@ import { ProgramTypeEnum } from '../../types/enums/ProgramTypeEnum'; import { renderDtsTemplate } from './dts'; +/** + * @group node + */ describe('templates/dts', () => { test('should render dts template', () => { // mocking diff --git a/packages/abi-typegen/src/templates/contract/factory.test.ts b/packages/abi-typegen/src/templates/contract/factory.test.ts index 04c3311d2e4..fc7fd116af9 100644 --- a/packages/abi-typegen/src/templates/contract/factory.test.ts +++ b/packages/abi-typegen/src/templates/contract/factory.test.ts @@ -9,6 +9,9 @@ import { ProgramTypeEnum } from '../../types/enums/ProgramTypeEnum'; import { renderFactoryTemplate } from './factory'; +/** + * @group node + */ describe('templates/factory', () => { test('should render factory template', () => { // mocking diff --git a/packages/abi-typegen/src/templates/predicate/factory.test.ts b/packages/abi-typegen/src/templates/predicate/factory.test.ts index bfe6c494093..7d5e0e91d01 100644 --- a/packages/abi-typegen/src/templates/predicate/factory.test.ts +++ b/packages/abi-typegen/src/templates/predicate/factory.test.ts @@ -12,6 +12,9 @@ import { ProgramTypeEnum } from '../../types/enums/ProgramTypeEnum'; import { renderFactoryTemplate } from './factory'; +/** + * @group node + */ describe('factory.ts', () => { test('should render factory template', () => { const { restore } = mockVersions(); diff --git a/packages/abi-typegen/src/templates/renderHbsTemplate.test.ts b/packages/abi-typegen/src/templates/renderHbsTemplate.test.ts index c9e2253eec7..d1e0633ff8f 100644 --- a/packages/abi-typegen/src/templates/renderHbsTemplate.test.ts +++ b/packages/abi-typegen/src/templates/renderHbsTemplate.test.ts @@ -2,6 +2,9 @@ import { mockVersions } from '../../test/utils/mockVersions'; import { renderHbsTemplate } from './renderHbsTemplate'; +/** + * @group node + */ describe('renderHbsTemplate.ts', () => { test('should render given template w/ data while injecting header', () => { // mocking diff --git a/packages/abi-typegen/src/templates/script/factory.test.ts b/packages/abi-typegen/src/templates/script/factory.test.ts index c4618a3b995..0267d01492a 100644 --- a/packages/abi-typegen/src/templates/script/factory.test.ts +++ b/packages/abi-typegen/src/templates/script/factory.test.ts @@ -12,6 +12,9 @@ import { ProgramTypeEnum } from '../../types/enums/ProgramTypeEnum'; import { renderFactoryTemplate } from './factory'; +/** + * @group node + */ describe('factory.ts', () => { test('should render factory template', () => { const { restore } = mockVersions(); diff --git a/packages/abi-typegen/src/templates/utils/formatConfigurables.test.ts b/packages/abi-typegen/src/templates/utils/formatConfigurables.test.ts index c2b5f9f519f..70425c68df1 100644 --- a/packages/abi-typegen/src/templates/utils/formatConfigurables.test.ts +++ b/packages/abi-typegen/src/templates/utils/formatConfigurables.test.ts @@ -1,5 +1,8 @@ import { formatConfigurables } from './formatConfigurables'; +/** + * @group node + */ describe('formatConfigurables.ts', () => { function mockAllDeps() { const rawAbiType = { @@ -17,7 +20,7 @@ describe('formatConfigurables.ts', () => { }, rawAbiType, requiredFuelsMembersImports: [], - parseComponentsAttributes: jest.fn(), + parseComponentsAttributes: vi.fn(), }; const rawAbiConfigurable = { diff --git a/packages/abi-typegen/src/templates/utils/formatEnums.test.ts b/packages/abi-typegen/src/templates/utils/formatEnums.test.ts index 114cf947849..e760962614c 100644 --- a/packages/abi-typegen/src/templates/utils/formatEnums.test.ts +++ b/packages/abi-typegen/src/templates/utils/formatEnums.test.ts @@ -7,6 +7,9 @@ import { ProgramTypeEnum } from '../../types/enums/ProgramTypeEnum'; import { formatEnums } from './formatEnums'; +/** + * @group node + */ describe('formatEnums.ts', () => { test('should format enums just fine', () => { const project = getTypegenForcProject(AbiTypegenProjectsEnum.ENUM_OF_ENUMS); diff --git a/packages/abi-typegen/src/templates/utils/formatImports.test.ts b/packages/abi-typegen/src/templates/utils/formatImports.test.ts index 6fc9a0ca709..ce4f79b2244 100644 --- a/packages/abi-typegen/src/templates/utils/formatImports.test.ts +++ b/packages/abi-typegen/src/templates/utils/formatImports.test.ts @@ -3,6 +3,9 @@ import { U8Type } from '../../abi/types/U8Type'; import { formatImports } from './formatImports'; +/** + * @group node + */ describe('formatImports.ts', () => { const baseMembers = ['Contract']; diff --git a/packages/abi-typegen/src/templates/utils/formatStructs.test.ts b/packages/abi-typegen/src/templates/utils/formatStructs.test.ts index 1a7bb64fea8..f00d5a0f008 100644 --- a/packages/abi-typegen/src/templates/utils/formatStructs.test.ts +++ b/packages/abi-typegen/src/templates/utils/formatStructs.test.ts @@ -7,6 +7,9 @@ import { ProgramTypeEnum } from '../../types/enums/ProgramTypeEnum'; import { formatStructs } from './formatStructs'; +/** + * @group node + */ describe('formatStructs.ts', () => { test('should format structs just fine', () => { const project = getTypegenForcProject(AbiTypegenProjectsEnum.STRUCT_SIMPLE); diff --git a/packages/abi-typegen/src/utils/assembleContracts.test.ts b/packages/abi-typegen/src/utils/assembleContracts.test.ts index bda3ae13dbe..887c6fbecf1 100644 --- a/packages/abi-typegen/src/utils/assembleContracts.test.ts +++ b/packages/abi-typegen/src/utils/assembleContracts.test.ts @@ -7,25 +7,28 @@ import { ProgramTypeEnum } from '../types/enums/ProgramTypeEnum'; import { assembleContracts } from './assembleContracts'; +/** + * @group node + */ describe('assembleContracts.ts', () => { function mockAllDeps() { - jest.resetAllMocks(); + vi.resetAllMocks(); - const renderCommonTemplate = jest + const renderCommonTemplate = vi .spyOn(renderCommonTemplateMod, 'renderCommonTemplate') - .mockImplementation(); + .mockReturnValue(''); - const renderFactoryTemplate = jest + const renderFactoryTemplate = vi .spyOn(renderFactoryTemplateMod, 'renderFactoryTemplate') - .mockImplementation(); + .mockReturnValue(''); - const renderIndexTemplate = jest + const renderIndexTemplate = vi .spyOn(renderIndexTemplateMod, 'renderIndexTemplate') - .mockImplementation(); + .mockReturnValue(''); - const renderBytecodeTemplate = jest + const renderBytecodeTemplate = vi .spyOn(renderBytecodeTemplateMod, 'renderBytecodeTemplate') - .mockImplementation(); + .mockReturnValue(''); return { renderCommonTemplate, diff --git a/packages/abi-typegen/src/utils/assemblePredicates.test.ts b/packages/abi-typegen/src/utils/assemblePredicates.test.ts index 04c7a9d8f27..603d87a9ec1 100644 --- a/packages/abi-typegen/src/utils/assemblePredicates.test.ts +++ b/packages/abi-typegen/src/utils/assemblePredicates.test.ts @@ -6,19 +6,22 @@ import { ProgramTypeEnum } from '../types/enums/ProgramTypeEnum'; import { assemblePredicates } from './assemblePredicates'; +/** + * @group node + */ describe('assemblePredicates.ts', () => { function mockAllDeps() { - const renderCommonTemplate = jest + const renderCommonTemplate = vi .spyOn(renderCommonTemplateMod, 'renderCommonTemplate') - .mockImplementation(); + .mockImplementation(vi.fn().mockResolvedValue('')); - const renderFactoryTemplate = jest + const renderFactoryTemplate = vi .spyOn(renderFactoryTemplateMod, 'renderFactoryTemplate') - .mockImplementation(); + .mockImplementation(vi.fn().mockResolvedValue('')); - const renderIndexTemplate = jest + const renderIndexTemplate = vi .spyOn(renderIndexTemplateMod, 'renderIndexTemplate') - .mockImplementation(); + .mockImplementation(vi.fn().mockResolvedValue('')); return { renderCommonTemplate, @@ -27,8 +30,13 @@ describe('assemblePredicates.ts', () => { }; } - beforeEach(jest.resetAllMocks); - afterEach(jest.restoreAllMocks); + beforeEach(() => { + vi.resetAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); test('should assemble all files from Predicate ABI ', () => { const { renderCommonTemplate, renderFactoryTemplate, renderIndexTemplate } = mockAllDeps(); @@ -42,7 +50,7 @@ describe('assemblePredicates.ts', () => { includeBinFiles: true, }); - jest.resetAllMocks(); + vi.resetAllMocks(); const files = assemblePredicates({ abis, outputDir }); @@ -65,7 +73,7 @@ describe('assemblePredicates.ts', () => { includeBinFiles: true, }); - jest.resetAllMocks(); + vi.resetAllMocks(); const files = assemblePredicates({ abis, outputDir }); diff --git a/packages/abi-typegen/src/utils/assembleScripts.test.ts b/packages/abi-typegen/src/utils/assembleScripts.test.ts index 0be690ba72b..d7cdfbb942c 100644 --- a/packages/abi-typegen/src/utils/assembleScripts.test.ts +++ b/packages/abi-typegen/src/utils/assembleScripts.test.ts @@ -6,19 +6,22 @@ import { ProgramTypeEnum } from '../types/enums/ProgramTypeEnum'; import { assembleScripts } from './assembleScripts'; +/** + * @group node + */ describe('assembleScripts.ts', () => { function mockAllDeps() { - const renderCommonTemplate = jest + const renderCommonTemplate = vi .spyOn(renderCommonTemplateMod, 'renderCommonTemplate') - .mockImplementation(); + .mockResolvedValue(''); - const renderFactoryTemplate = jest + const renderFactoryTemplate = vi .spyOn(renderFactoryTemplateMod, 'renderFactoryTemplate') - .mockImplementation(); + .mockResolvedValue(''); - const renderIndexTemplate = jest + const renderIndexTemplate = vi .spyOn(renderIndexTemplateMod, 'renderIndexTemplate') - .mockImplementation(); + .mockResolvedValue(''); return { renderCommonTemplate, @@ -27,8 +30,13 @@ describe('assembleScripts.ts', () => { }; } - beforeEach(jest.resetAllMocks); - afterEach(jest.restoreAllMocks); + beforeEach(() => { + vi.resetAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); test('should assemble all files from Script ABI ', () => { const { renderCommonTemplate, renderFactoryTemplate, renderIndexTemplate } = mockAllDeps(); @@ -42,7 +50,7 @@ describe('assembleScripts.ts', () => { includeBinFiles: true, }); - jest.resetAllMocks(); + vi.resetAllMocks(); const files = assembleScripts({ abis, outputDir }); @@ -65,7 +73,7 @@ describe('assembleScripts.ts', () => { includeBinFiles: true, }); - jest.resetAllMocks(); + vi.resetAllMocks(); const files = assembleScripts({ abis, outputDir }); diff --git a/packages/abi-typegen/src/utils/collectBinFilePaths.test.ts b/packages/abi-typegen/src/utils/collectBinFilePaths.test.ts index 72d6bb5db80..5c061a1e3b9 100644 --- a/packages/abi-typegen/src/utils/collectBinFilePaths.test.ts +++ b/packages/abi-typegen/src/utils/collectBinFilePaths.test.ts @@ -7,18 +7,27 @@ import { ProgramTypeEnum } from '../types/enums/ProgramTypeEnum'; import { collectBinFilepaths } from './collectBinFilePaths'; import * as validateBinFileMod from './validateBinFile'; +/** + * @group node + */ describe('collectBinFilePaths.ts', () => { const script = getTypegenForcProject(AbiTypegenProjectsEnum.SCRIPT); const predicate = getTypegenForcProject(AbiTypegenProjectsEnum.PREDICATE); const contract = getTypegenForcProject(AbiTypegenProjectsEnum.MINIMAL); function mockDeps() { - const validateBinFile = jest.spyOn(validateBinFileMod, 'validateBinFile').mockImplementation(); + const validateBinFile = vi + .spyOn(validateBinFileMod, 'validateBinFile') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .mockResolvedValue({} as any); + // const validateBinFile = vi.spyOn(validateBinFileMod, 'validateBinFile').getMockImplementation(); return { validateBinFile }; } - afterEach(jest.restoreAllMocks); + afterEach(() => { + vi.restoreAllMocks(); + }); test('should collect bin files for Scripts', () => { const { validateBinFile } = mockDeps(); diff --git a/packages/abi-typegen/src/utils/collectStorageSlotsFilePaths.test.ts b/packages/abi-typegen/src/utils/collectStorageSlotsFilePaths.test.ts index a449db73926..4bbb9ad285c 100644 --- a/packages/abi-typegen/src/utils/collectStorageSlotsFilePaths.test.ts +++ b/packages/abi-typegen/src/utils/collectStorageSlotsFilePaths.test.ts @@ -3,12 +3,17 @@ import { ProgramTypeEnum } from '../types/enums/ProgramTypeEnum'; import { collectStorageSlotsFilepaths } from './collectStorageSlotsFilePaths'; +/** + * @group node + */ describe('collectStorageSlotsFilePaths.ts', () => { const script = getTypegenForcProject(AbiTypegenProjectsEnum.SCRIPT); const predicate = getTypegenForcProject(AbiTypegenProjectsEnum.PREDICATE); const contract = getTypegenForcProject(AbiTypegenProjectsEnum.MINIMAL); - afterEach(jest.restoreAllMocks); + beforeEach(() => { + vi.resetAllMocks(); + }); test('should collect storage slot files', () => { const contractStorageSlots = collectStorageSlotsFilepaths({ diff --git a/packages/abi-typegen/src/utils/extractStructName.test.ts b/packages/abi-typegen/src/utils/extractStructName.test.ts index b5df918687e..0e701354544 100644 --- a/packages/abi-typegen/src/utils/extractStructName.test.ts +++ b/packages/abi-typegen/src/utils/extractStructName.test.ts @@ -5,6 +5,9 @@ import type { IRawAbiTypeRoot } from '../types/interfaces/IRawAbiType'; import { extractStructName } from './extractStructName'; +/** + * @group node + */ describe('extractStructName.ts', () => { /* Test helpers diff --git a/packages/abi-typegen/src/utils/findType.test.ts b/packages/abi-typegen/src/utils/findType.test.ts index 1fba83a0800..2f7fd78a663 100644 --- a/packages/abi-typegen/src/utils/findType.test.ts +++ b/packages/abi-typegen/src/utils/findType.test.ts @@ -6,6 +6,9 @@ import type { IType } from '../types/interfaces/IType'; import { findType } from './findType'; import { makeType } from './makeType'; +/** + * @group node + */ describe('findType.ts', () => { test('should find type', () => { const rawAbiType: IRawAbiTypeRoot = { @@ -17,7 +20,7 @@ describe('findType.ts', () => { const type: IType = makeType({ rawAbiType }); - const parseComponentsAttributesSpy = jest.spyOn(type, 'parseComponentsAttributes'); + const parseComponentsAttributesSpy = vi.spyOn(type, 'parseComponentsAttributes'); const typeId = 1; const types: IType[] = [type]; // array with type to be found diff --git a/packages/abi-typegen/src/utils/makeConfigurable.test.ts b/packages/abi-typegen/src/utils/makeConfigurable.test.ts index 0c46ed4f717..726c713a210 100644 --- a/packages/abi-typegen/src/utils/makeConfigurable.test.ts +++ b/packages/abi-typegen/src/utils/makeConfigurable.test.ts @@ -2,6 +2,9 @@ import { Configurable } from '../abi/configurable/Configurable'; import { makeConfigurable } from './makeConfigurable'; +/** + * @group node + */ describe('makeConfigurable.ts', () => { function mockAllDeps() { const rawAbiType = { @@ -19,7 +22,7 @@ describe('makeConfigurable.ts', () => { }, rawAbiType, requiredFuelsMembersImports: [], - parseComponentsAttributes: jest.fn(), + parseComponentsAttributes: vi.fn(), }; const rawAbiConfigurable = { diff --git a/packages/abi-typegen/src/utils/makeFunction.test.ts b/packages/abi-typegen/src/utils/makeFunction.test.ts index 302c6cc2a25..d1ad5f1b157 100644 --- a/packages/abi-typegen/src/utils/makeFunction.test.ts +++ b/packages/abi-typegen/src/utils/makeFunction.test.ts @@ -5,6 +5,9 @@ import type { IType } from '../types/interfaces/IType'; import { makeFunction } from './makeFunction'; import { makeType } from './makeType'; +/** + * @group node + */ describe('functions.ts', () => { test('should instantiate a new Function instance', () => { const rawU8: IRawAbiTypeRoot = { diff --git a/packages/abi-typegen/src/utils/makeType.test.ts b/packages/abi-typegen/src/utils/makeType.test.ts index 6e1cfbe0e45..29c96c0936b 100644 --- a/packages/abi-typegen/src/utils/makeType.test.ts +++ b/packages/abi-typegen/src/utils/makeType.test.ts @@ -5,6 +5,9 @@ import type { IRawAbiTypeRoot } from '../types/interfaces/IRawAbiType'; import { makeType } from './makeType'; +/** + * @group node + */ describe('makeType.ts', () => { test('should create a new Type instance just fine', () => { const rawAbiType: IRawAbiTypeRoot = { diff --git a/packages/abi-typegen/src/utils/parseConfigurables.test.ts b/packages/abi-typegen/src/utils/parseConfigurables.test.ts index 5517a8b689e..51a07b8dbc3 100644 --- a/packages/abi-typegen/src/utils/parseConfigurables.test.ts +++ b/packages/abi-typegen/src/utils/parseConfigurables.test.ts @@ -1,6 +1,9 @@ import * as makeConfigurableMod from './makeConfigurable'; import { parseConfigurables } from './parseConfigurables'; +/** + * @group node + */ describe('parseConfigurables.ts', () => { function mockAllDeps() { const rawAbiType = { @@ -18,7 +21,7 @@ describe('parseConfigurables.ts', () => { }, rawAbiType, requiredFuelsMembersImports: [], - parseComponentsAttributes: jest.fn(), + parseComponentsAttributes: vi.fn(), }; const rawAbiConfigurable = { @@ -37,7 +40,7 @@ describe('parseConfigurables.ts', () => { rawAbiConfigurable, }; - const makeConfigurable = jest + const makeConfigurable = vi .spyOn(makeConfigurableMod, 'makeConfigurable') .mockReturnValue(configurable); diff --git a/packages/abi-typegen/src/utils/parseFunctions.test.ts b/packages/abi-typegen/src/utils/parseFunctions.test.ts index 75909f2eca0..e3533a5333c 100644 --- a/packages/abi-typegen/src/utils/parseFunctions.test.ts +++ b/packages/abi-typegen/src/utils/parseFunctions.test.ts @@ -5,6 +5,9 @@ import type { IType } from '../types/interfaces/IType'; import { makeType } from './makeType'; import { parseFunctions } from './parseFunctions'; +/** + * @group node + */ describe('functions.ts', () => { test('should parse an array of raw abi functions', () => { const rawU8: IRawAbiTypeRoot = { diff --git a/packages/abi-typegen/src/utils/parseTypeArguments.test.ts b/packages/abi-typegen/src/utils/parseTypeArguments.test.ts index a2479b6b3da..397e624733f 100644 --- a/packages/abi-typegen/src/utils/parseTypeArguments.test.ts +++ b/packages/abi-typegen/src/utils/parseTypeArguments.test.ts @@ -63,6 +63,9 @@ const defautRawTypes: IRawAbiTypeRoot[] = [ }, ]; +/** + * @group node + */ describe('parseTypeArguments.ts', () => { /* Test helpers diff --git a/packages/abi-typegen/src/utils/parseTypes.test.ts b/packages/abi-typegen/src/utils/parseTypes.test.ts index e86359764dc..63213bb2bbc 100644 --- a/packages/abi-typegen/src/utils/parseTypes.test.ts +++ b/packages/abi-typegen/src/utils/parseTypes.test.ts @@ -2,6 +2,9 @@ import type { IRawAbiTypeRoot } from '../types/interfaces/IRawAbiType'; import { parseTypes } from './parseTypes'; +/** + * @group node + */ describe('types.ts', () => { test('should parse an array of raw abi types', () => { const rawU8: IRawAbiTypeRoot = { diff --git a/packages/abi-typegen/src/utils/shouldSkipType.test.ts b/packages/abi-typegen/src/utils/shouldSkipType.test.ts index dc15d1c3487..6faa48fe650 100644 --- a/packages/abi-typegen/src/utils/shouldSkipType.test.ts +++ b/packages/abi-typegen/src/utils/shouldSkipType.test.ts @@ -1,6 +1,9 @@ import { shouldSkipAbiType } from './shouldSkipAbiType'; import { supportedTypes } from './supportedTypes'; +/** + * @group node + */ describe('types.ts', () => { test('should always skip these types', () => { expect(shouldSkipAbiType({ type: '()' })).toEqual(true); diff --git a/packages/abi-typegen/src/utils/supportedTypes.test.ts b/packages/abi-typegen/src/utils/supportedTypes.test.ts index 2a36f578e0e..391d9e6ed46 100644 --- a/packages/abi-typegen/src/utils/supportedTypes.test.ts +++ b/packages/abi-typegen/src/utils/supportedTypes.test.ts @@ -1,5 +1,8 @@ import { supportedTypes } from './supportedTypes'; +/** + * @group node + */ describe('supportedTypes.ts', () => { test('should export all supported types', () => { expect(supportedTypes.length).toEqual(21); diff --git a/packages/abi-typegen/src/utils/validateBinFile.test.ts b/packages/abi-typegen/src/utils/validateBinFile.test.ts index 35bc8639622..4a1f0b9f260 100644 --- a/packages/abi-typegen/src/utils/validateBinFile.test.ts +++ b/packages/abi-typegen/src/utils/validateBinFile.test.ts @@ -2,6 +2,9 @@ import { ProgramTypeEnum } from '../types/enums/ProgramTypeEnum'; import { validateBinFile } from './validateBinFile'; +/** + * @group node + */ describe('validateBinFile.ts', () => { test('should not throw for existent Script BIN file', () => { const params = { diff --git a/packages/abi-typegen/test/fixtures/templates/contract/bytecode.hbs b/packages/abi-typegen/test/fixtures/templates/contract/bytecode.hbs index 35bfe22e30e..0693e86bdf1 100644 --- a/packages/abi-typegen/test/fixtures/templates/contract/bytecode.hbs +++ b/packages/abi-typegen/test/fixtures/templates/contract/bytecode.hbs @@ -9,4 +9,4 @@ Fuel-Core version: 33.33.33 */ -export default '0x740000034700000000000000000000445dfcc00110fff3005d4060495d47f001134904407648000272f0007b36f000001aec5000910000005c43f000244000004700000001000000000000000000000055b7ae10'\ +export default '0x740000034700000000000000000000445dfcc00110fff3005d4060495d47f001134904407648000272f0007b36f000001aec5000910000005c43f000244000004700000001000000000000000000000055b7ae10' \ No newline at end of file diff --git a/packages/abi-typegen/test/utils/mockVersions.ts b/packages/abi-typegen/test/utils/mockVersions.ts index ce7421caeb2..bf0b666a746 100644 --- a/packages/abi-typegen/test/utils/mockVersions.ts +++ b/packages/abi-typegen/test/utils/mockVersions.ts @@ -9,10 +9,13 @@ import * as versionsMod from '@fuel-ts/versions'; * * https://stackoverflow.com/a/72885576 */ -jest.mock('@fuel-ts/versions', () => ({ - __esModule: true, - ...jest.requireActual('@fuel-ts/versions'), -})); +vi.mock('@fuel-ts/versions', async () => { + const mod = await vi.importActual('@fuel-ts/versions'); + return { + __esModule: true, + ...mod, + }; +}); export function mockVersions( values: { @@ -25,12 +28,12 @@ export function mockVersions( FUEL_CORE: '33.33.33', } ) { - const mock = jest.replaceProperty(versionsMod, 'versions', values); + const mock = vi.spyOn(versionsMod, 'versions', 'get').mockReturnValue(values); return { versions: values, restore() { - mock.restore(); + mock.mockRestore(); }, }; } diff --git a/packages/abi-typegen/tsdoc.json b/packages/abi-typegen/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/abi-typegen/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/address/README.md b/packages/address/README.md index d3a9ceb2340..1bc2595404c 100644 --- a/packages/address/README.md +++ b/packages/address/README.md @@ -16,7 +16,7 @@ This module contains the utilities for encoding and decoding address and contrac ## Documentation -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/guide/types/address.html) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/types/address/) ## Usage diff --git a/packages/address/package.json b/packages/address/package.json index 24954d358b1..cb1c373ecef 100644 --- a/packages/address/package.json +++ b/packages/address/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/address/src/address.test.ts b/packages/address/src/address.test.ts index c01cb002b91..964e1e7dc76 100644 --- a/packages/address/src/address.test.ts +++ b/packages/address/src/address.test.ts @@ -25,6 +25,10 @@ const ADDRESS_BYTES = [ const expectedAddress = 'fuel1785jcs4epy625cmjuv9u269rymmwv6s6q2y9jhnw877nj2j08ehqce3rxf'; const expectedB256Address = '0xf1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e'; +/** + * @group node + * @group browser + */ describe('Address utils', () => { test('fromBech32 (bech32 to decoded bech32)', () => { const result = utils.fromBech32(ADDRESS_BECH32); diff --git a/packages/contract/README.md b/packages/contract/README.md index a399d3306f1..a3d0eeb9490 100644 --- a/packages/contract/README.md +++ b/packages/contract/README.md @@ -16,7 +16,7 @@ This module allows for a simple way to serialize calls and transactions to an on ## Documentation -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/guide/contracts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/contracts/) ## Usage diff --git a/packages/contract/package.json b/packages/contract/package.json index 5e1982e7961..8ba72eee8e7 100644 --- a/packages/contract/package.json +++ b/packages/contract/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/contract/src/contract-factory.ts b/packages/contract/src/contract-factory.ts index 430cff694f7..6838e6fbfe9 100644 --- a/packages/contract/src/contract-factory.ts +++ b/packages/contract/src/contract-factory.ts @@ -152,8 +152,9 @@ export default class ContractFactory { transactionRequest.maxFee = this.account.provider.getGasConfig().maxGasPerTx; await this.account.fund(transactionRequest, requiredQuantities, maxFee); - const response = await this.account.sendTransaction(transactionRequest); - await response.wait(); + await this.account.sendTransaction(transactionRequest, { + awaitExecution: true, + }); return new Contract(contractId, this.interface, this.account); } diff --git a/packages/create-fuels/package.json b/packages/create-fuels/package.json index 136e5c98e2e..4bde52b49d6 100644 --- a/packages/create-fuels/package.json +++ b/packages/create-fuels/package.json @@ -11,7 +11,7 @@ "templates" ], "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "license": "Apache-2.0", "scripts": { diff --git a/packages/create-fuels/test/cli.test.ts b/packages/create-fuels/test/cli.test.ts index a172bccfbc3..5c7b36e8251 100644 --- a/packages/create-fuels/test/cli.test.ts +++ b/packages/create-fuels/test/cli.test.ts @@ -3,6 +3,9 @@ import { join } from 'path'; import { runScaffoldCli } from '../src/cli'; +/** + * @group node + */ test('create-fuels extracts the template to the specified directory', async () => { // move the templates folder from the root of the project to the root of the create-fuels package temporarily. // this is needed because of the way the create-fuels package is setup. diff --git a/packages/create-fuels/tsconfig.json b/packages/create-fuels/tsconfig.json index d84d8e05857..3529ad76cd1 100644 --- a/packages/create-fuels/tsconfig.json +++ b/packages/create-fuels/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "outDir": "./dist" }, - "include": ["src", "scripts"], + "include": ["src", "scripts", "test"], "exclude": ["templates"] } diff --git a/packages/create-fuels/tsdoc.json b/packages/create-fuels/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/create-fuels/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/crypto/README.md b/packages/crypto/README.md index 539fe19ccca..65af022d0fe 100644 --- a/packages/crypto/README.md +++ b/packages/crypto/README.md @@ -18,7 +18,7 @@ This module contains the utilities for encrypting and decrypting data objects us -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/crypto/package.json b/packages/crypto/package.json index c19842bf483..9e4bdb3f9ae 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "browser": { "./dist/index.mjs": "./dist/index.browser.mjs" diff --git a/packages/crypto/src/shared/keccak256.test.ts b/packages/crypto/src/shared/keccak256.test.ts index 730e2835ed4..ca130a26218 100644 --- a/packages/crypto/src/shared/keccak256.test.ts +++ b/packages/crypto/src/shared/keccak256.test.ts @@ -1,20 +1,24 @@ -import * as ethereumCryptography from 'ethereum-cryptography/keccak'; - import { bufferFromString } from '..'; -import { keccak256 } from './keccak256'; +import { keccak256 as keccak } from './keccak256'; -describe('keccak256', () => { - afterEach(jest.restoreAllMocks); +const data = bufferFromString('hashedKey'); - it('hashes using keccak256', () => { - const data = bufferFromString('hashedKey'); +vi.mock('ethereum-cryptography/keccak', () => ({ + keccak256: vi.fn(() => data), +})); - const mock = jest.spyOn(ethereumCryptography, 'keccak256').mockImplementationOnce(() => data); +/** + * @group node + */ +describe('keccak256', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); - const hashedKey = keccak256(data); + it('hashes using keccak256', () => { + const hashedKey = keccak(data); - expect(mock).toBeCalledTimes(1); expect(hashedKey).toEqual(data); }); }); diff --git a/packages/crypto/src/shared/scrypt.test.ts b/packages/crypto/src/shared/scrypt.test.ts index 06660b9ab37..571a2371d1e 100644 --- a/packages/crypto/src/shared/scrypt.test.ts +++ b/packages/crypto/src/shared/scrypt.test.ts @@ -1,20 +1,23 @@ -import * as ethereumCryptography from 'ethereum-cryptography/scrypt'; - import { bufferFromString } from '..'; import type { IScryptParams } from '../types'; import { scrypt } from './scrypt'; -describe('scrypt', () => { - afterEach(jest.restoreAllMocks); +const data = bufferFromString('hashedKey'); - it('hashes using scrypt', () => { - const mockedHashedKey = bufferFromString('hashedKey'); +vi.mock('ethereum-cryptography/scrypt', () => ({ + scryptSync: vi.fn(() => data), +})); - const mock = jest - .spyOn(ethereumCryptography, 'scryptSync') - .mockImplementationOnce(() => mockedHashedKey); +/** + * @group node + */ +describe('scrypt', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + it('hashes using scrypt', () => { const password = bufferFromString('password'); const salt = bufferFromString('salt'); @@ -29,7 +32,6 @@ describe('scrypt', () => { const hashedKey = scrypt(params); - expect(mock).toBeCalledTimes(1); - expect(hashedKey).toEqual(mockedHashedKey); + expect(hashedKey).toEqual(data); }); }); diff --git a/packages/crypto/test/aes-ctr.test.ts b/packages/crypto/test/aes-ctr.test.ts index ae14cc0a33d..46b4ce26710 100644 --- a/packages/crypto/test/aes-ctr.test.ts +++ b/packages/crypto/test/aes-ctr.test.ts @@ -1,7 +1,11 @@ -import { envs } from './envs'; +import { encrypt, decrypt } from '..'; +/** + * @group node + * @group browser + */ describe('Keystore', () => { - it.each(envs)('Encrypt and Decrypt', async ({ encrypt, decrypt }) => { + it('Encrypt and Decrypt', async () => { const password = '0b540281-f87b-49ca-be37-2264c7f260f7'; const data = { name: 'test', @@ -17,7 +21,7 @@ describe('Keystore', () => { expect(decryptedResult).toEqual(data); }); - it.each(envs)('Decrypt with wrong password should throw', async ({ encrypt, decrypt }) => { + it('Decrypt with wrong password should throw', async () => { const password = '0b540281-f87b-49ca-be37-2264c7f260f7'; const data = { name: 'test', @@ -27,7 +31,7 @@ describe('Keystore', () => { await expect(decrypt(`${password}123`, encryptedResult)).rejects.toThrow('Invalid credentials'); }); - it.each(envs)('Decrypt Loop', async ({ decrypt }) => { + it('Decrypt Loop', async () => { const INPUTS = [ { data: '07yJczBTonXWyKdJfEcx', diff --git a/packages/crypto/test/bufferFromString.test.ts b/packages/crypto/test/bufferFromString.test.ts index 40a89b714e6..992ba30a122 100644 --- a/packages/crypto/test/bufferFromString.test.ts +++ b/packages/crypto/test/bufferFromString.test.ts @@ -1,31 +1,27 @@ -import { envs } from './envs'; +import { bufferFromString } from '..'; +/** + * @group node + * @group browser + */ describe('bufferFromString', () => { const buffer = new Uint8Array([104, 101, 108, 108, 111]); // ASCII values for "hello" - it.each(envs)( - 'should correctly convert string to Uint8Array with base64 encoding in %s environment', - ({ bufferFromString }) => { - const string = 'aGVsbG8='; // "hello" in Base64 - const result = bufferFromString(string, 'base64'); - expect(result).toStrictEqual(buffer); // ASCII values for "hello" - } - ); + it('should correctly convert string to Uint8Array with base64 encoding in a node environment', () => { + const string = 'aGVsbG8='; // "hello" in Base64 + const result = bufferFromString(string, 'base64'); + expect(result).toStrictEqual(buffer); // ASCII values for "hello" + }); - it.each(envs)( - 'should correctly convert string to Uint8Array with utf-8 encoding in %s environment', - ({ bufferFromString }) => { - const string = 'hello'; - const result = bufferFromString(string, 'utf-8'); - expect(result).toStrictEqual(buffer); // ASCII values for "hello" - } - ); - it.each(envs)( - 'should correctly convert string to Uint8Array with hex encoding in %s environment', - ({ bufferFromString }) => { - const string = '68656c6c6f'; // "hello" in Hex - const result = bufferFromString(string, 'hex'); - expect(result).toStrictEqual(buffer); // ASCII values for "hello" - } - ); + it('should correctly convert string to Uint8Array with utf-8 encoding in a node environment', () => { + const string = 'hello'; + const result = bufferFromString(string, 'utf-8'); + expect(result).toStrictEqual(buffer); // ASCII values for "hello" + }); + + it('should correctly convert string to Uint8Array with hex encoding in a node environment', () => { + const string = '68656c6c6f'; // "hello" in Hex + const result = bufferFromString(string, 'hex'); + expect(result).toStrictEqual(buffer); // ASCII values for "hello" + }); }); diff --git a/packages/crypto/test/encryptJsonWalletData.test.ts b/packages/crypto/test/encryptJsonWalletData.test.ts index 1018ef8901a..56438e86140 100644 --- a/packages/crypto/test/encryptJsonWalletData.test.ts +++ b/packages/crypto/test/encryptJsonWalletData.test.ts @@ -1,18 +1,18 @@ -import { envs } from './envs'; - +import { encryptJsonWalletData, decryptJsonWalletData, randomBytes } from '..'; +/** + * @group node + * @group browser + */ describe('encryptJsonWalletData', () => { - it.each(envs)( - 'should encrypt and decrypt json wallet data correctly in %s environment', - async ({ encryptJsonWalletData, decryptJsonWalletData, randomBytes }) => { - const testData = new Uint8Array([104, 101, 108, 108, 111]); - const testKey = randomBytes(16); - const testIv = randomBytes(16); + it('should encrypt and decrypt json wallet data correctly in a node environment', async () => { + const testData = new Uint8Array([104, 101, 108, 108, 111]); + const testKey = randomBytes(16); + const testIv = randomBytes(16); - const encryptedData = await encryptJsonWalletData(testData, testKey, testIv); - expect(encryptedData).not.toEqual(testData); // ensure data was encrypted + const encryptedData = await encryptJsonWalletData(testData, testKey, testIv); + expect(encryptedData).not.toEqual(testData); // ensure data was encrypted - const decryptedData = await decryptJsonWalletData(encryptedData, testKey, testIv); - expect(decryptedData).toEqual(testData); // ensure data was decrypted correctly - } - ); + const decryptedData = await decryptJsonWalletData(encryptedData, testKey, testIv); + expect(decryptedData).toEqual(testData); // ensure data was decrypted correctly + }); }); diff --git a/packages/crypto/test/envs.ts b/packages/crypto/test/envs.ts deleted file mode 100644 index 007eb74ee88..00000000000 --- a/packages/crypto/test/envs.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as node from '../src/index'; -// import * as browser from '../src/index.browser'; - -export const envs = [ - { toString: () => 'node', ...node }, - // { toString: () => 'browser', ...browser }, TODO: Uncomment when running tests in browser is supported -]; diff --git a/packages/crypto/test/stringFromBuffer.test.ts b/packages/crypto/test/stringFromBuffer.test.ts index e6be8e23951..1647b13ccc6 100644 --- a/packages/crypto/test/stringFromBuffer.test.ts +++ b/packages/crypto/test/stringFromBuffer.test.ts @@ -1,29 +1,24 @@ -import { envs } from './envs'; +import { stringFromBuffer } from '..'; +/** + * @group node + * @group browser + */ describe('stringFromBuffer', () => { const buffer = new Uint8Array([104, 101, 108, 108, 111]); // ASCII values for "hello" - it.each(envs)( - 'should correctly convert Uint8Array to string with base64 encoding in %s environment', - ({ stringFromBuffer }) => { - const result = stringFromBuffer(buffer, 'base64'); - expect(result).toEqual('aGVsbG8='); // "hello" in Base64 - } - ); + it('should correctly convert Uint8Array to string with base64 encoding in a node environment', () => { + const result = stringFromBuffer(buffer, 'base64'); + expect(result).toEqual('aGVsbG8='); // "hello" in Base64 + }); - it.each(envs)( - 'should correctly convert Uint8Array to string with utf-8 encoding in %s environment', - ({ stringFromBuffer }) => { - const result = stringFromBuffer(buffer, 'utf-8'); - expect(result).toEqual('hello'); - } - ); + it('should correctly convert Uint8Array to string with utf-8 encoding in a node environment', () => { + const result = stringFromBuffer(buffer, 'utf-8'); + expect(result).toEqual('hello'); + }); - it.each(envs)( - 'should correctly convert Uint8Array to string with hex encoding in %s environment', - ({ stringFromBuffer }) => { - const result = stringFromBuffer(buffer, 'hex'); - expect(result).toEqual('68656c6c6f'); // "hello" in Hex - } - ); + it('should correctly convert Uint8Array to string with hex encoding in a node environment', () => { + const result = stringFromBuffer(buffer, 'hex'); + expect(result).toEqual('68656c6c6f'); // "hello" in Hex + }); }); diff --git a/packages/crypto/tsdoc.json b/packages/crypto/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/crypto/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/errors/README.md b/packages/errors/README.md index bde1e5f4f52..acb3452beab 100644 --- a/packages/errors/README.md +++ b/packages/errors/README.md @@ -20,7 +20,7 @@ This package contains core utilities regarding throwing errors internally inside -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/errors/package.json b/packages/errors/package.json index 2328c5da983..65e23f389c0 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/errors/src/error-codes.ts b/packages/errors/src/error-codes.ts index 244c1d463f3..e02c67180eb 100644 --- a/packages/errors/src/error-codes.ts +++ b/packages/errors/src/error-codes.ts @@ -49,6 +49,7 @@ export enum ErrorCode { CONVERTING_FAILED = 'converting-error', ELEMENT_NOT_FOUND = 'element-not-found', MISSING_REQUIRED_PARAMETER = 'missing-required-parameter', + INVALID_REQUEST = 'invalid-request', UNEXPECTED_HEX_VALUE = 'unexpected-hex-value', // transaction diff --git a/packages/errors/src/fuel-error.test.ts b/packages/errors/src/fuel-error.test.ts index b77ffc6fb06..33e73fd5e24 100644 --- a/packages/errors/src/fuel-error.test.ts +++ b/packages/errors/src/fuel-error.test.ts @@ -3,6 +3,10 @@ import { versions } from '@fuel-ts/versions'; import { FuelError, ErrorCode } from '.'; import { expectToThrowFuelError } from './test-utils/expect-to-throw-fuel-error'; +/** + * @group node + * @group browser + */ it('has properties set as expected on creation', () => { const message = 'whatever'; const error = new FuelError(FuelError.CODES.PARSE_FAILED, message); diff --git a/packages/errors/src/test-utils.test.ts b/packages/errors/src/test-utils.test.ts index 5514bff4cb0..f1352fcb13a 100644 --- a/packages/errors/src/test-utils.test.ts +++ b/packages/errors/src/test-utils.test.ts @@ -1,6 +1,10 @@ import * as indexMod from './test-utils'; -describe(__filename, () => { +/** + * @group node + * @group browser + */ +describe('test utils', () => { test('should export all test utilities', () => { expect(indexMod.expectToThrowFuelError).toBeTruthy(); expect(indexMod.safeExec).toBeTruthy(); diff --git a/packages/errors/src/test-utils/expect-to-throw-fuel-error.test.ts b/packages/errors/src/test-utils/expect-to-throw-fuel-error.test.ts index 640d9f3818f..bd20f07f351 100644 --- a/packages/errors/src/test-utils/expect-to-throw-fuel-error.test.ts +++ b/packages/errors/src/test-utils/expect-to-throw-fuel-error.test.ts @@ -2,6 +2,10 @@ import { FuelError } from '..'; import { expectToThrowFuelError } from './expect-to-throw-fuel-error'; +/** + * @group node + * @group browser + */ describe('expect-to-throw-fuel-error', () => { const otherError = new Error('Original Error'); const fuelError = new FuelError(FuelError.CODES.PARSE_FAILED, 'FuelError 1'); diff --git a/packages/errors/src/test-utils/safeExec.test.ts b/packages/errors/src/test-utils/safeExec.test.ts index b58dcd7f8c8..09aea1a8b89 100644 --- a/packages/errors/src/test-utils/safeExec.test.ts +++ b/packages/errors/src/test-utils/safeExec.test.ts @@ -1,5 +1,9 @@ import { safeExec } from './safeExec'; +/** + * @group node + * @group browser + */ describe('safeExec.js', () => { it('should catch error', async () => { const ERROR_MSG = 'I am an error.'; diff --git a/packages/errors/tsdoc.json b/packages/errors/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/errors/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/fuel-gauge/src/advanced-logging.test.ts b/packages/fuel-gauge/src/advanced-logging.test.ts index 32f74899b10..eb3110a1206 100644 --- a/packages/fuel-gauge/src/advanced-logging.test.ts +++ b/packages/fuel-gauge/src/advanced-logging.test.ts @@ -18,6 +18,9 @@ beforeAll(async () => { ({ minGasPrice: gasPrice } = contractInstance.provider.getGasConfig()); }); +/** + * @group node + */ describe('Advanced Logging', () => { it('can get log data', async () => { const { value, logs } = await contractInstance.functions diff --git a/packages/fuel-gauge/src/auth-testing.test.ts b/packages/fuel-gauge/src/auth-testing.test.ts index 7e69e2135c0..a5824035ca5 100644 --- a/packages/fuel-gauge/src/auth-testing.test.ts +++ b/packages/fuel-gauge/src/auth-testing.test.ts @@ -15,6 +15,9 @@ let contractInstance: Contract; let wallet: WalletUnlocked; let gasPrice: BN; +/** + * @group node + */ describe('Auth Testing', () => { beforeAll(async () => { const provider = await Provider.create(FUEL_NETWORK_URL); diff --git a/packages/fuel-gauge/src/await-execution.test.ts b/packages/fuel-gauge/src/await-execution.test.ts new file mode 100644 index 00000000000..34010ac7b99 --- /dev/null +++ b/packages/fuel-gauge/src/await-execution.test.ts @@ -0,0 +1,97 @@ +import { launchNode } from '@fuel-ts/wallet/test-utils'; +import { + Provider, + WalletUnlocked, + randomBytes, + Wallet, + BaseAssetId, + FUEL_NETWORK_URL, +} from 'fuels'; + +/** + * @group node + */ +describe('await-execution', () => { + test('awaiting execution of a transaction on the provider works', async () => { + const { cleanup, ip, port } = await launchNode({ + args: ['--poa-instant', 'false', '--poa-interval-period', '400ms'], + }); + const nodeProvider = await Provider.create(`http://${ip}:${port}/graphql`); + + const genesisWallet = new WalletUnlocked( + process.env.GENESIS_SECRET || randomBytes(32), + nodeProvider + ); + + const destination = Wallet.generate({ provider: nodeProvider }); + + const transfer = await genesisWallet.createTransfer(destination.address, 100, BaseAssetId, { + gasPrice: nodeProvider.getGasConfig().minGasPrice, + gasLimit: 10_000, + }); + + transfer.updateWitnessByOwner( + genesisWallet.address, + await genesisWallet.signTransaction(transfer) + ); + + const response = await nodeProvider.sendTransaction(transfer, { awaitExecution: true }); + + expect(response.gqlTransaction?.status?.type).toBe('SuccessStatus'); + + cleanup(); + }); + + test.skip('transferring funds with awaitExecution works', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + const genesisWallet = new WalletUnlocked( + process.env.GENESIS_SECRET || randomBytes(32), + provider + ); + + const sendTransactionSpy = vi.spyOn(provider, 'sendTransaction'); + + const destination = Wallet.generate({ provider }); + + await genesisWallet.transfer( + destination.address, + 100, + BaseAssetId, + { + gasPrice: provider.getGasConfig().minGasPrice, + gasLimit: 10_000, + } + // { awaitExecution: true } + ); + + expect(sendTransactionSpy).toHaveBeenCalledTimes(1); + const awaitExecutionArg = sendTransactionSpy.mock.calls[0][1]; + expect(awaitExecutionArg).toMatchObject({ awaitExecution: true }); + }); + + test.skip('withdrawToBaseLayer works with awaitExecution', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + const genesisWallet = new WalletUnlocked( + process.env.GENESIS_SECRET || randomBytes(32), + provider + ); + + const sendTransactionSpy = vi.spyOn(provider, 'sendTransaction'); + + const destination = Wallet.generate({ provider }); + + await genesisWallet.withdrawToBaseLayer( + destination.address, + 100, + { + gasPrice: provider.getGasConfig().minGasPrice, + gasLimit: 10_000, + } + // { awaitExecution: true } + ); + + expect(sendTransactionSpy).toHaveBeenCalledTimes(1); + const awaitExecutionArg = sendTransactionSpy.mock.calls[0][1]; + expect(awaitExecutionArg).toMatchObject({ awaitExecution: true }); + }); +}); diff --git a/packages/fuel-gauge/src/bytes.test.ts b/packages/fuel-gauge/src/bytes.test.ts index 00328174aa9..c547c927b3a 100644 --- a/packages/fuel-gauge/src/bytes.test.ts +++ b/packages/fuel-gauge/src/bytes.test.ts @@ -30,6 +30,9 @@ const setup = async (balance = 500_000) => { return wallet; }; +/** + * @group node + */ describe('Bytes Tests', () => { let gasPrice: BN; beforeAll(async () => { diff --git a/packages/fuel-gauge/src/call-test-contract.test.ts b/packages/fuel-gauge/src/call-test-contract.test.ts index 9b83a5bd2b3..c8759669636 100644 --- a/packages/fuel-gauge/src/call-test-contract.test.ts +++ b/packages/fuel-gauge/src/call-test-contract.test.ts @@ -16,6 +16,9 @@ const setupContract = createSetupConfig({ const U64_MAX = bn(2).pow(64).sub(1); +/** + * @group node + */ describe('CallTestContract', () => { let gasPrice: BN; beforeAll(async () => { diff --git a/packages/fuel-gauge/src/configurable-contract.test.ts b/packages/fuel-gauge/src/configurable-contract.test.ts index 77485283bc3..5462dfb5c2b 100644 --- a/packages/fuel-gauge/src/configurable-contract.test.ts +++ b/packages/fuel-gauge/src/configurable-contract.test.ts @@ -25,6 +25,9 @@ const defaultValues = { }, }; +/** + * @group node + */ describe('Configurable Contract', () => { let wallet: WalletUnlocked; let factory: ContractFactory; diff --git a/packages/fuel-gauge/src/contract-factory.test.ts b/packages/fuel-gauge/src/contract-factory.test.ts index 9f89a87abb5..84053d24726 100644 --- a/packages/fuel-gauge/src/contract-factory.test.ts +++ b/packages/fuel-gauge/src/contract-factory.test.ts @@ -14,6 +14,9 @@ import { import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../test/fixtures'; +/** + * @group node + */ describe('Contract Factory', () => { let gasPrice: BN; diff --git a/packages/fuel-gauge/src/contract.test.ts b/packages/fuel-gauge/src/contract.test.ts index 4d7803d8b70..a1d7ae26b85 100644 --- a/packages/fuel-gauge/src/contract.test.ts +++ b/packages/fuel-gauge/src/contract.test.ts @@ -162,6 +162,9 @@ const txPointer = '0x00000000000000000000000000000000'; const AltToken = '0x0101010101010101010101010101010101010101010101010101010101010101'; +/** + * @group node + */ describe('Contract', () => { let gasPrice: BN; beforeAll(async () => { @@ -171,11 +174,11 @@ describe('Contract', () => { it('generates function methods on a simple contract', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); - const spy = jest.spyOn(provider, 'sendTransaction'); + const spy = vi.spyOn(provider, 'sendTransaction'); const wallet = await generateTestWallet(provider, [[1_000, BaseAssetId]]); const contract = new Contract(ZeroBytes32, jsonFragment, wallet); const fragment = contract.interface.getFunction('entry_one'); - const interfaceSpy = jest.spyOn(fragment, 'encodeArguments'); + const interfaceSpy = vi.spyOn(fragment, 'encodeArguments'); try { await contract.functions.entry_one(42); @@ -189,11 +192,11 @@ describe('Contract', () => { it('generates function methods on a complex contract', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); - const spy = jest.spyOn(provider, 'sendTransaction'); + const spy = vi.spyOn(provider, 'sendTransaction'); const wallet = await generateTestWallet(provider, [[1_000, BaseAssetId]]); const contract = new Contract(ZeroBytes32, complexFragment, wallet); const fragment = contract.interface.getFunction('tuple_function'); - const interfaceSpy = jest.spyOn(fragment, 'encodeArguments'); + const interfaceSpy = vi.spyOn(fragment, 'encodeArguments'); try { await contract.functions.tuple_function({ @@ -966,7 +969,7 @@ describe('Contract', () => { const amountToContract = 100; - const tx = await wallet.transferToContract(contract.id, amountToContract, asset, { + const tx = await wallet.transferToContract(contract.id.toB256(), amountToContract, asset, { gasPrice, gasLimit: 10_000, }); diff --git a/packages/fuel-gauge/src/coverage-contract.test.ts b/packages/fuel-gauge/src/coverage-contract.test.ts index 86341df09d1..4a8f571b572 100644 --- a/packages/fuel-gauge/src/coverage-contract.test.ts +++ b/packages/fuel-gauge/src/coverage-contract.test.ts @@ -48,6 +48,9 @@ enum ColorEnumOutput { Blue = 'Blue', } +/** + * @group node + */ describe('Coverage Contract', () => { it('can return outputs', async () => { // Call contract methods diff --git a/packages/fuel-gauge/src/doc-examples.test.ts b/packages/fuel-gauge/src/doc-examples.test.ts index 2ef0ec692d2..e69efa90eae 100644 --- a/packages/fuel-gauge/src/doc-examples.test.ts +++ b/packages/fuel-gauge/src/doc-examples.test.ts @@ -23,7 +23,6 @@ import { Wallet, WalletUnlocked, Signer, - ContractFactory, ZeroBytes32, BaseAssetId, FUEL_NETWORK_URL, @@ -35,9 +34,6 @@ const { abiContents: callTestAbi } = getFuelGaugeForcProject( FuelGaugeProjectsEnum.CALL_TEST_CONTRACT ); -const { binHexlified: liquidityPoolContractBytecode, abiContents: liquidityPoolABI } = - getFuelGaugeForcProject(FuelGaugeProjectsEnum.LIQUIDITY_POOL); - const { binHexlified: predicateTriple } = getFuelGaugeForcProject( FuelGaugeProjectsEnum.PREDICATE_TRIPLE_SIG ); @@ -46,9 +42,6 @@ const { binHexlified: testPredicateTrue } = getFuelGaugeForcProject( FuelGaugeProjectsEnum.PREDICATE_TRUE ); -const { binHexlified: tokenContractBytecode, abiContents: tokenContractABI } = - getFuelGaugeForcProject(FuelGaugeProjectsEnum.TOKEN_CONTRACT); - const PUBLIC_KEY = '0x2f34bc0df4db0ec391792cedb05768832b49b1aa3a2dd8c30054d1af00f67d00b74b7acbbf3087c8e0b1a4c343db50aa471d21f278ff5ce09f07795d541fb47e'; @@ -62,6 +55,9 @@ const ADDRESS_BYTES = new Uint8Array([ 89, 94, 110, 63, 189, 57, 42, 79, 62, 110, ]); +/** + * @group node + */ describe('Doc Examples', () => { let gasPrice: BN; @@ -197,6 +193,22 @@ describe('Doc Examples', () => { expect(balances.length).toEqual(0); }); + it('it can be created without a provider', async () => { + // #region wallet-optional-provider + // #context import { Wallet, WalletUnlocked } from 'fuels'; + + // You can create a wallet, without a provider + let unlockedWallet: WalletUnlocked = Wallet.generate(); + unlockedWallet = Wallet.fromPrivateKey(unlockedWallet.privateKey); + + // All non-provider dependent methods are available + unlockedWallet.lock(); + + // All provider dependent methods will throw + await expect(() => unlockedWallet.getCoins()).rejects.toThrow(/Provider not set/); + // #endregion wallet-optional-provider + }); + it('it can work sign messages with wallets', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); // #region wallet-message-signing @@ -468,91 +480,4 @@ describe('Doc Examples', () => { // assert that predicate funds now belong to the receiver expect(bn(receiverBalance).gte(bn(amountToReceiver))).toBeTruthy(); }); - - test.skip('deposit and withdraw cookbook guide', async () => { - // #region deposit-and-withdraw-cookbook-wallet-setup - const provider = await Provider.create(FUEL_NETWORK_URL); - const PRIVATE_KEY = '0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301'; - const wallet = Wallet.fromPrivateKey(PRIVATE_KEY, provider); - await seedTestWallet(wallet, [{ assetId: BaseAssetId, amount: bn(100_000) }]); - // #endregion deposit-and-withdraw-cookbook-wallet-setup - - // #region deposit-and-withdraw-cookbook-contract-deployments - const tokenContractFactory = new ContractFactory( - tokenContractBytecode, - tokenContractABI, - wallet - ); - const tokenContract = await tokenContractFactory.deployContract({ gasPrice }); - const tokenContractID = tokenContract.id; - - const liquidityPoolContractFactory = new ContractFactory( - liquidityPoolContractBytecode, - liquidityPoolABI, - wallet - ); - const liquidityPoolContract = await liquidityPoolContractFactory.deployContract({ gasPrice }); - const liquidityPoolContractID = liquidityPoolContract.id; - await liquidityPoolContract.functions.set_base_token(tokenContractID).call(); - // #endregion deposit-and-withdraw-cookbook-contract-deployments - - // mint some base tokens to the current wallet - // #region deposit-and-withdraw-cookbook-mint-and-transfer - await tokenContract.functions.mint_coins(500, 1).call(); - await tokenContract.functions - .transfer_coins_to_output( - 200, - { - value: tokenContract.id, - }, - { - value: wallet.address.toB256(), - } - ) - .txParams({ - variableOutputs: 1, - gasPrice, - }) - .call(); - // #endregion deposit-and-withdraw-cookbook-mint-and-transfer - - // deposit base tokens into the liquidity pool - // #region deposit-and-withdraw-cookbook-deposit - await liquidityPoolContract.functions - .deposit({ - value: wallet.address.toB256(), - }) - .callParams({ - forward: { - amount: bn(100), - assetId: tokenContractID.toB256(), - }, - }) - .call(); - // #endregion deposit-and-withdraw-cookbook-deposit - - // verify balances - expect(await wallet.getBalance(tokenContractID.toB256())).toEqual(bn(100)); - expect(await wallet.getBalance(liquidityPoolContractID.toB256())).toEqual(bn(200)); - - // withdraw base tokens from the liquidity pool - // #region deposit-and-withdraw-cookbook-withdraw - const lpTokenBalance = await wallet.getBalance(liquidityPoolContractID.toB256()); - await liquidityPoolContract.functions - .withdraw({ - value: wallet.address.toB256(), - }) - .callParams({ - forward: { - amount: lpTokenBalance, - assetId: liquidityPoolContractID.toB256(), - }, - }) - .call(); - // #endregion deposit-and-withdraw-cookbook-withdraw - - // verify balances again - expect(await wallet.getBalance(tokenContractID.toB256())).toEqual(bn(200)); - expect(await wallet.getBalance(liquidityPoolContractID.toB256())).toEqual(bn(0)); - }); }); diff --git a/packages/fuel-gauge/src/e2e-script.test.ts b/packages/fuel-gauge/src/e2e-script.test.ts index 1211610fed9..01eee0d941f 100644 --- a/packages/fuel-gauge/src/e2e-script.test.ts +++ b/packages/fuel-gauge/src/e2e-script.test.ts @@ -73,6 +73,10 @@ type MainArgs = [ VecInAStructInAVec, // VEC_IN_A_VEC_IN_A_STRUCT_IN_A_VEC ]; +/** + * @group node + * @group e2e + */ describe('Live Script Test', () => { it('can use script against live Fuel Node', async () => { if (!process.env.FUEL_NETWORK_GENESIS_KEY) { diff --git a/packages/fuel-gauge/src/edge-cases.test.ts b/packages/fuel-gauge/src/edge-cases.test.ts index 4b4601f9f68..6e904c50832 100644 --- a/packages/fuel-gauge/src/edge-cases.test.ts +++ b/packages/fuel-gauge/src/edge-cases.test.ts @@ -1,5 +1,8 @@ import { getSetupContract } from './utils'; +/** + * @group node + */ describe('Edge Cases', () => { it('can run collision_in_fn_names', async () => { const contract = await getSetupContract('collision_in_fn_names')(); diff --git a/packages/fuel-gauge/src/fee.test.ts b/packages/fuel-gauge/src/fee.test.ts index 0482d6f9cc4..b44e0a4d2f8 100644 --- a/packages/fuel-gauge/src/fee.test.ts +++ b/packages/fuel-gauge/src/fee.test.ts @@ -14,6 +14,9 @@ import { import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../test/fixtures'; +/** + * @group node + */ describe('Fee', () => { const assetA: string = '0x0101010101010101010101010101010101010101010101010101010101010101'; const assetB: string = '0x0202020202020202020202020202020202020202020202020202020202020202'; diff --git a/packages/fuel-gauge/src/funding-transaction.test.ts b/packages/fuel-gauge/src/funding-transaction.test.ts index 344ab933c4e..6bb36b665df 100644 --- a/packages/fuel-gauge/src/funding-transaction.test.ts +++ b/packages/fuel-gauge/src/funding-transaction.test.ts @@ -9,6 +9,9 @@ import { bn, } from 'fuels'; +/** + * @group node + */ describe(__filename, () => { let mainWallet: Account; let provider: Provider; @@ -78,7 +81,7 @@ describe(__filename, () => { expect(bn((request.inputs[0]).amount).toNumber()).toBe(300); expect(maxFee.gt(300)).toBeTruthy(); - const getResourcesToSpendSpy = jest.spyOn(sender, 'getResourcesToSpend'); + const getResourcesToSpendSpy = vi.spyOn(sender, 'getResourcesToSpend'); await sender.fund(request, requiredQuantities, maxFee); @@ -129,7 +132,7 @@ describe(__filename, () => { expect(bn((request.inputs[0]).amount).toNumber()).toBe(1000); expect(maxFee.lt(1000)).toBeTruthy(); - const getResourcesToSpendSpy = jest.spyOn(sender, 'getResourcesToSpend'); + const getResourcesToSpendSpy = vi.spyOn(sender, 'getResourcesToSpend'); await sender.fund(request, requiredQuantities, maxFee); @@ -169,7 +172,7 @@ describe(__filename, () => { // TX request does NOT carry any resources, it needs to be funded expect(request.inputs.length).toBe(0); - const getResourcesToSpendSpy = jest.spyOn(sender, 'getResourcesToSpend'); + const getResourcesToSpendSpy = vi.spyOn(sender, 'getResourcesToSpend'); await sender.fund(request, requiredQuantities, maxFee); diff --git a/packages/fuel-gauge/src/generic-types-contract.test.ts b/packages/fuel-gauge/src/generic-types-contract.test.ts index 3a3c792d18b..116ef79ec30 100644 --- a/packages/fuel-gauge/src/generic-types-contract.test.ts +++ b/packages/fuel-gauge/src/generic-types-contract.test.ts @@ -8,6 +8,9 @@ const { binHexlified: contractBytecode, abiContents: abiJSON } = getFuelGaugeFor FuelGaugeProjectsEnum.GENERIC_TYPES_CONTRACT ); +/** + * @group node + */ describe('GenericTypesContract', () => { it('should call complex contract function with generic type', async () => { const contract = await setup({ diff --git a/packages/fuel-gauge/src/min-gas.test.ts b/packages/fuel-gauge/src/min-gas.test.ts index e82019c7210..d9dfb2958cf 100644 --- a/packages/fuel-gauge/src/min-gas.test.ts +++ b/packages/fuel-gauge/src/min-gas.test.ts @@ -17,6 +17,9 @@ import { import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../test/fixtures'; +/** + * @group node + */ describe(__filename, () => { it('sets gas requirements (contract)', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); diff --git a/packages/fuel-gauge/src/multi-token-contract.test.ts b/packages/fuel-gauge/src/multi-token-contract.test.ts index b5357cacc6c..72dd7bbc1c3 100644 --- a/packages/fuel-gauge/src/multi-token-contract.test.ts +++ b/packages/fuel-gauge/src/multi-token-contract.test.ts @@ -26,6 +26,9 @@ const subIds = [ '0xdf78cb1e1a1b31fff104eb0baf734a4767a1b1373687c29a26bf1a2b22d1a3c5', ]; +/** + * @group node + */ describe('MultiTokenContract', () => { let gasPrice: BN; beforeAll(async () => { diff --git a/packages/fuel-gauge/src/payable-annotation.test.ts b/packages/fuel-gauge/src/payable-annotation.test.ts index 211f3786ecd..da1104405a9 100644 --- a/packages/fuel-gauge/src/payable-annotation.test.ts +++ b/packages/fuel-gauge/src/payable-annotation.test.ts @@ -22,6 +22,9 @@ beforeAll(async () => { ({ minGasPrice: gasPrice } = contract.provider.getGasConfig()); }); +/** + * @group node + */ test('allow sending coins to payable functions', async () => { // This should not fail because the function is payable await expect( diff --git a/packages/fuel-gauge/src/policies.test.ts b/packages/fuel-gauge/src/policies.test.ts index d79583b45dd..43f21e9c923 100644 --- a/packages/fuel-gauge/src/policies.test.ts +++ b/packages/fuel-gauge/src/policies.test.ts @@ -16,6 +16,9 @@ import { getFuelGaugeForcProject, FuelGaugeProjectsEnum } from '../test/fixtures import { createSetupConfig } from './utils'; +/** + * @group node + */ describe('Policies', () => { let provider: Provider; beforeAll(async () => { diff --git a/packages/fuel-gauge/src/predicate-conditional-inputs.test.ts b/packages/fuel-gauge/src/predicate-conditional-inputs.test.ts index b826f79b23d..0d54b84d224 100644 --- a/packages/fuel-gauge/src/predicate-conditional-inputs.test.ts +++ b/packages/fuel-gauge/src/predicate-conditional-inputs.test.ts @@ -12,6 +12,9 @@ import { import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../test/fixtures'; +/** + * @group node + */ describe('PredicateConditionalInputs', () => { const assetIdA = '0x0101010101010101010101010101010101010101010101010101010101010101'; const assetIdB = '0x0202020202020202020202020202020202020202020202020202020202020202'; diff --git a/packages/fuel-gauge/src/predicate/predicate-arguments.test.ts b/packages/fuel-gauge/src/predicate/predicate-arguments.test.ts index 6c6dcd50fb3..6a52eb84c55 100644 --- a/packages/fuel-gauge/src/predicate/predicate-arguments.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-arguments.test.ts @@ -6,6 +6,9 @@ import type { Validation } from '../types/predicate'; import { setupWallets, assertBalances, fundPredicate } from './utils/predicate'; +/** + * @group node + */ describe('Predicate', () => { const { binHexlified: predicateBytesAddress, abiContents: predicateAbiMainArgsAddress } = getFuelGaugeForcProject(FuelGaugeProjectsEnum.PREDICATE_ADDRESS); diff --git a/packages/fuel-gauge/src/predicate/predicate-configurables.test.ts b/packages/fuel-gauge/src/predicate/predicate-configurables.test.ts index 68d834b8fa4..c4aec565fb2 100644 --- a/packages/fuel-gauge/src/predicate/predicate-configurables.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-configurables.test.ts @@ -13,6 +13,9 @@ import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../../test/fixtu import { fundPredicate, assertBalance } from './utils/predicate'; +/** + * @group node + */ describe('Predicate', () => { const { binHexlified: predicateBytesTrue, abiContents: predicateAbiTrue } = getFuelGaugeForcProject(FuelGaugeProjectsEnum.PREDICATE_TRUE); diff --git a/packages/fuel-gauge/src/predicate/predicate-estimations.test.ts b/packages/fuel-gauge/src/predicate/predicate-estimations.test.ts index a2e6381e3df..cb802fe7fbb 100644 --- a/packages/fuel-gauge/src/predicate/predicate-estimations.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-estimations.test.ts @@ -13,6 +13,9 @@ import { import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../../test/fixtures'; import type { Validation } from '../types/predicate'; +/** + * @group node + */ describe('Predicate', () => { const { binHexlified: predicateTrueBytecode } = getFuelGaugeForcProject( FuelGaugeProjectsEnum.PREDICATE_TRUE diff --git a/packages/fuel-gauge/src/predicate/predicate-evaluations.test.ts b/packages/fuel-gauge/src/predicate/predicate-evaluations.test.ts index 55b0561dc26..4740b6fbee4 100644 --- a/packages/fuel-gauge/src/predicate/predicate-evaluations.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-evaluations.test.ts @@ -5,6 +5,9 @@ import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../../test/fixtu import { setupWallets, assertBalances, fundPredicate } from './utils/predicate'; +/** + * @group node + */ describe('Predicate', () => { const { binHexlified: predicateBytesTrue } = getFuelGaugeForcProject( FuelGaugeProjectsEnum.PREDICATE_TRUE diff --git a/packages/fuel-gauge/src/predicate/predicate-input-data.test.ts b/packages/fuel-gauge/src/predicate/predicate-input-data.test.ts index d3cdfc7394e..d8e80930533 100644 --- a/packages/fuel-gauge/src/predicate/predicate-input-data.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-input-data.test.ts @@ -5,6 +5,9 @@ import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../../test/fixtu import { setupWallets, fundPredicate } from './utils/predicate'; +/** + * @group node + */ describe('Predicate', () => { const { binHexlified, abiContents } = getFuelGaugeForcProject( FuelGaugeProjectsEnum.PREDICATE_INPUT_DATA diff --git a/packages/fuel-gauge/src/predicate/predicate-invalidations.test.ts b/packages/fuel-gauge/src/predicate/predicate-invalidations.test.ts index daed4ba398c..81c464af8f5 100644 --- a/packages/fuel-gauge/src/predicate/predicate-invalidations.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-invalidations.test.ts @@ -6,6 +6,9 @@ import type { Validation } from '../types/predicate'; import { fundPredicate, setupWallets } from './utils/predicate'; +/** + * @group node + */ describe('Predicate', () => { const { binHexlified: predicateBytesMainArgsStruct, abiContents: predicateAbiMainArgsStruct } = getFuelGaugeForcProject(FuelGaugeProjectsEnum.PREDICATE_MAIN_ARGS_STRUCT); diff --git a/packages/fuel-gauge/src/predicate/predicate-with-contract.test.ts b/packages/fuel-gauge/src/predicate/predicate-with-contract.test.ts index d780c15b534..ff92f02d3fa 100644 --- a/packages/fuel-gauge/src/predicate/predicate-with-contract.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-with-contract.test.ts @@ -16,12 +16,16 @@ import type { Validation } from '../types/predicate'; import { fundPredicate, setupContractWithConfig } from './utils/predicate'; +/** + * @group node + */ describe('Predicate', () => { const { binHexlified: contractBytes, abiContents: contractAbi } = getFuelGaugeForcProject( FuelGaugeProjectsEnum.CALL_TEST_CONTRACT ); - const { binHexlified: liquidityPoolBytes, abiContents: liquidityPoolAbi } = - getFuelGaugeForcProject(FuelGaugeProjectsEnum.LIQUIDITY_POOL); + const { binHexlified: tokenPoolBytes, abiContents: tokenPoolAbi } = getFuelGaugeForcProject( + FuelGaugeProjectsEnum.TOKEN_CONTRACT + ); const { abiContents: predicateAbiMainArgsStruct } = getFuelGaugeForcProject( FuelGaugeProjectsEnum.PREDICATE_MAIN_ARGS_STRUCT @@ -83,8 +87,8 @@ describe('Predicate', () => { it('calls a predicate and uses proceeds for a contract call', async () => { const contract = await new ContractFactory( - liquidityPoolBytes, - liquidityPoolAbi, + tokenPoolBytes, + tokenPoolAbi, wallet ).deployContract({ gasPrice }); @@ -94,15 +98,10 @@ describe('Predicate', () => { contract.account = receiver; await expect( contract.functions - .deposit({ - value: receiver.address.toB256(), - }) - .callParams({ - forward: [100, BaseAssetId], - }) + .mint_coins(200) .txParams({ gasPrice, - gasLimit: 10_000, + gasLimit: 1_000, }) .call() ).rejects.toThrow(/not enough coins to fit the target/); @@ -136,23 +135,12 @@ describe('Predicate', () => { // calling the contract with the receiver account (with resources) const contractAmount = 10; const { - transactionResult: { fee: receiverTxFee1 }, - } = await contract.functions - .set_base_token(BaseAssetId) - .txParams({ gasPrice, gasLimit: 10_000 }) - .call(); - const { - transactionResult: { fee: receiverTxFee2 }, + transactionResult: { fee: receiverTxFee }, } = await contract.functions - .deposit({ - value: receiver.address.toB256(), - }) - .callParams({ - forward: [contractAmount, BaseAssetId], - }) + .mint_coins(200) .txParams({ gasPrice, - gasLimit: 10_000, + gasLimit: 1_000, }) .call(); @@ -160,11 +148,7 @@ describe('Predicate', () => { const remainingPredicateBalance = toNumber(await predicate.getBalance()); const expectedFinalReceiverBalance = - initialReceiverBalance + - amountToReceiver - - contractAmount - - receiverTxFee1.toNumber() - - receiverTxFee2.toNumber(); + initialReceiverBalance + amountToReceiver - contractAmount - receiverTxFee.toNumber(); expectToBeInRange({ value: finalReceiverBalance, diff --git a/packages/fuel-gauge/src/predicate/predicate-with-script.test.ts b/packages/fuel-gauge/src/predicate/predicate-with-script.test.ts index 6f78d603780..2dcf40d58ba 100644 --- a/packages/fuel-gauge/src/predicate/predicate-with-script.test.ts +++ b/packages/fuel-gauge/src/predicate/predicate-with-script.test.ts @@ -8,6 +8,9 @@ import type { Validation } from '../types/predicate'; import { fundPredicate } from './utils/predicate'; +/** + * @group node + */ describe('Predicate', () => { const { binHexlified: scriptBytes, abiContents: scriptAbi } = getFuelGaugeForcProject( FuelGaugeProjectsEnum.SCRIPT_MAIN_ARGS diff --git a/packages/fuel-gauge/src/predicate/utils/predicate/fundPredicate.ts b/packages/fuel-gauge/src/predicate/utils/predicate/fundPredicate.ts index 427a53b77ff..ec42d86e5ff 100644 --- a/packages/fuel-gauge/src/predicate/utils/predicate/fundPredicate.ts +++ b/packages/fuel-gauge/src/predicate/utils/predicate/fundPredicate.ts @@ -17,8 +17,7 @@ export const fundPredicate = async ( request.gasLimit = gasUsed; await wallet.fund(request, requiredQuantities, minFee); - const tx = await wallet.sendTransaction(request); - await tx.waitForResult(); + await wallet.sendTransaction(request, { awaitExecution: true }); return predicate.getBalance(); }; diff --git a/packages/fuel-gauge/src/raw-slice.test.ts b/packages/fuel-gauge/src/raw-slice.test.ts index 47bed5d2731..aaac3d20018 100644 --- a/packages/fuel-gauge/src/raw-slice.test.ts +++ b/packages/fuel-gauge/src/raw-slice.test.ts @@ -33,6 +33,9 @@ beforeAll(async () => { ({ minGasPrice: gasPrice } = contractInstance.provider.getGasConfig()); }); +/** + * @group node + */ describe('Raw Slice Tests', () => { it('should test raw slice output', async () => { const INPUT = 10; diff --git a/packages/fuel-gauge/src/revert-error.test.ts b/packages/fuel-gauge/src/revert-error.test.ts index 10ff83a422d..7c6e816e435 100644 --- a/packages/fuel-gauge/src/revert-error.test.ts +++ b/packages/fuel-gauge/src/revert-error.test.ts @@ -18,6 +18,9 @@ import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../test/fixtures let contractInstance: Contract; let wallet: WalletUnlocked; +/** + * @group node + */ describe('Revert Error Testing', () => { let gasPrice: BN; diff --git a/packages/fuel-gauge/src/script-main-args.test.ts b/packages/fuel-gauge/src/script-main-args.test.ts index d753d4cae9b..40d10747604 100644 --- a/packages/fuel-gauge/src/script-main-args.test.ts +++ b/packages/fuel-gauge/src/script-main-args.test.ts @@ -19,6 +19,9 @@ type Baz = { x: number; }; +/** + * @group node + */ describe('Script Coverage', () => { let gasPrice: BN; diff --git a/packages/fuel-gauge/src/script-with-configurable.test.ts b/packages/fuel-gauge/src/script-with-configurable.test.ts index a33ae0695ff..702195a142a 100644 --- a/packages/fuel-gauge/src/script-with-configurable.test.ts +++ b/packages/fuel-gauge/src/script-with-configurable.test.ts @@ -10,6 +10,9 @@ const defaultValues = { let wallet: WalletUnlocked; +/** + * @group node + */ describe('Script With Configurable', () => { let gasPrice: BN; diff --git a/packages/fuel-gauge/src/script-with-vectors.test.ts b/packages/fuel-gauge/src/script-with-vectors.test.ts index 6a94c9c2400..a85bda6186e 100644 --- a/packages/fuel-gauge/src/script-with-vectors.test.ts +++ b/packages/fuel-gauge/src/script-with-vectors.test.ts @@ -13,6 +13,9 @@ const setup = async (balance = 500_000) => { return wallet; }; +/** + * @group node + */ describe('Script With Vectors', () => { let gasPrice: BN; beforeAll(async () => { diff --git a/packages/fuel-gauge/src/small-bytes.test.ts b/packages/fuel-gauge/src/small-bytes.test.ts index 14a99bfee8c..fd0ed3883f5 100644 --- a/packages/fuel-gauge/src/small-bytes.test.ts +++ b/packages/fuel-gauge/src/small-bytes.test.ts @@ -5,6 +5,9 @@ import { join } from 'path'; import { getSetupContract, createWallet } from './utils'; +/** + * @group node + */ describe('small-bytes', () => { const smallBytesProjectDir = join(__dirname, '../test/fixtures/forc-projects/small-bytes'); diff --git a/packages/fuel-gauge/src/std-lib-string.test.ts b/packages/fuel-gauge/src/std-lib-string.test.ts index e6e5e34b2eb..424e0daaaa2 100644 --- a/packages/fuel-gauge/src/std-lib-string.test.ts +++ b/packages/fuel-gauge/src/std-lib-string.test.ts @@ -24,6 +24,9 @@ const setup = async (balance = 500_000) => { return wallet; }; +/** + * @group node + */ describe('std-lib-string Tests', () => { const { binHexlified: predicateStdString, abiContents: predicateStdStringAbi } = getFuelGaugeForcProject(FuelGaugeProjectsEnum.PREDICATE_STD_LIB_STRING); diff --git a/packages/fuel-gauge/src/storage-test-contract.test.ts b/packages/fuel-gauge/src/storage-test-contract.test.ts index c296eb55013..fe9b2ada004 100644 --- a/packages/fuel-gauge/src/storage-test-contract.test.ts +++ b/packages/fuel-gauge/src/storage-test-contract.test.ts @@ -29,6 +29,9 @@ const setup = async () => { return contract; }; +/** + * @group node + */ describe('StorageTestContract', () => { let gasPrice: BN; beforeAll(async () => { diff --git a/packages/fuel-gauge/src/token-test-contract.test.ts b/packages/fuel-gauge/src/token-test-contract.test.ts index c0fc2807954..3e2e2895ab9 100644 --- a/packages/fuel-gauge/src/token-test-contract.test.ts +++ b/packages/fuel-gauge/src/token-test-contract.test.ts @@ -31,6 +31,9 @@ beforeAll(async () => { gasPrice = provider.getGasConfig().minGasPrice; }); +/** + * @group node + */ describe('TokenTestContract', () => { it('Can mint and transfer coins', async () => { // New wallet to transfer coins and check balance diff --git a/packages/fuel-gauge/src/transaction-response.test.ts b/packages/fuel-gauge/src/transaction-response.test.ts index e205ba6d1c7..bdd2ec0030f 100644 --- a/packages/fuel-gauge/src/transaction-response.test.ts +++ b/packages/fuel-gauge/src/transaction-response.test.ts @@ -1,7 +1,18 @@ -import { generateTestWallet } from '@fuel-ts/wallet/test-utils'; -import type { BN, WalletUnlocked } from 'fuels'; -import { BaseAssetId, FUEL_NETWORK_URL, Provider, TransactionResponse, Wallet } from 'fuels'; - +import { generateTestWallet, launchNode } from '@fuel-ts/wallet/test-utils'; +import type { BN } from 'fuels'; +import { + BaseAssetId, + FUEL_NETWORK_URL, + Provider, + TransactionResponse, + Wallet, + randomBytes, + WalletUnlocked, +} from 'fuels'; + +/** + * @group node + */ describe('TransactionSummary', () => { let provider: Provider; let adminWallet: WalletUnlocked; @@ -75,25 +86,33 @@ describe('TransactionSummary', () => { }); it('should ensure waitForResult always waits for the transaction to be processed', async () => { - const destination = Wallet.generate({ - provider, + const { cleanup, ip, port } = await launchNode({ + args: ['--poa-interval-period', '750ms', '--poa-instant', 'false'], }); + const nodeProvider = await Provider.create(`http://${ip}:${port}/graphql`); - const { id: transactionId } = await adminWallet.transfer( + const genesisWallet = new WalletUnlocked( + process.env.GENESIS_SECRET || randomBytes(32), + nodeProvider + ); + + const destination = Wallet.generate({ provider: nodeProvider }); + + const { id: transactionId } = await genesisWallet.transfer( destination.address, 100, BaseAssetId, { gasPrice, gasLimit: 10_000 } ); + const response = await TransactionResponse.create(transactionId, nodeProvider); - const response = new TransactionResponse(transactionId, provider); - - expect(response.gqlTransaction).toBeUndefined(); + expect(response.gqlTransaction?.status?.type).toBe('SubmittedStatus'); await response.waitForResult(); - expect(response.gqlTransaction?.status?.type).toBeDefined(); - expect(response.gqlTransaction?.status?.type).not.toEqual('SubmittedStatus'); + expect(response.gqlTransaction?.status?.type).toEqual('SuccessStatus'); expect(response.gqlTransaction?.id).toBe(transactionId); + + cleanup(); }); }); diff --git a/packages/fuel-gauge/src/transaction-summary.test.ts b/packages/fuel-gauge/src/transaction-summary.test.ts index bb5d57ba8e6..dc9810fd70c 100644 --- a/packages/fuel-gauge/src/transaction-summary.test.ts +++ b/packages/fuel-gauge/src/transaction-summary.test.ts @@ -19,6 +19,9 @@ import { Wallet, } from 'fuels'; +/** + * @group node + */ describe('TransactionSummary', () => { let provider: Provider; let wallet: WalletUnlocked; diff --git a/packages/fuel-gauge/src/vector-types.test.ts b/packages/fuel-gauge/src/vector-types.test.ts index 841e2bd587e..05d8b6370d1 100644 --- a/packages/fuel-gauge/src/vector-types.test.ts +++ b/packages/fuel-gauge/src/vector-types.test.ts @@ -84,6 +84,9 @@ const setup = async (balance = 500_000) => { return wallet; }; +/** + * @group node + */ describe('Vector Types Validation', () => { let gasPrice: BN; diff --git a/packages/fuel-gauge/src/vectors.test.ts b/packages/fuel-gauge/src/vectors.test.ts index 7e94057c23c..3f9b49b1b5e 100644 --- a/packages/fuel-gauge/src/vectors.test.ts +++ b/packages/fuel-gauge/src/vectors.test.ts @@ -17,6 +17,9 @@ enum SmallEnum { Empty = 'Empty', } +/** + * @group node + */ describe('Vector Tests', () => { it('should test u8 vector input/output', async () => { const INPUT = [8, 6, 7, 5, 3, 0, 9]; diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml index 6e8bb25e02d..02c4857fb84 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml +++ b/packages/fuel-gauge/test/fixtures/forc-projects/Forc.toml @@ -11,7 +11,6 @@ members = [ "configurable-contract", "coverage-contract", "generic-types-contract", - "liquidity-pool", "multi-token-contract", "payable-annotation", "predicate-address", diff --git a/packages/fuel-gauge/test/fixtures/forc-projects/coverage-contract/src/main.sw b/packages/fuel-gauge/test/fixtures/forc-projects/coverage-contract/src/main.sw index 83fea8c70ba..5115d6fdabf 100644 --- a/packages/fuel-gauge/test/fixtures/forc-projects/coverage-contract/src/main.sw +++ b/packages/fuel-gauge/test/fixtures/forc-projects/coverage-contract/src/main.sw @@ -1,14 +1,14 @@ contract; -use std::*; use core::*; -use std::storage::*; -use std::contract_id::ContractId; -use std::vec::Vec; -use std::option::Option; +use std::*; use std::assert::assert; -use std::logging::log; use std::b512::B512; +use std::contract_id::ContractId; +use std::logging::log; +use std::option::Option; +use std::storage::*; +use std::vec::Vec; pub struct U8Struct { i: u8, diff --git a/packages/fuel-gauge/test/fixtures/index.ts b/packages/fuel-gauge/test/fixtures/index.ts index d52e688247b..c150b70816f 100644 --- a/packages/fuel-gauge/test/fixtures/index.ts +++ b/packages/fuel-gauge/test/fixtures/index.ts @@ -16,7 +16,6 @@ export enum FuelGaugeProjectsEnum { COLLISION_IN_FN_NAMES = 'collision_in_fn_names', COVERAGE_CONTRACT = 'coverage-contract', GENERIC_TYPES_CONTRACT = 'generic-types-contract', - LIQUIDITY_POOL = 'liquidity-pool', MULTI_TOKEN_CONTRACT = 'multi-token-contract', PAYABLE_ANNOTATION = 'payable-annotation', PREDICATE_ADDRESS = 'predicate-address', diff --git a/packages/fuel-gauge/tsdoc.json b/packages/fuel-gauge/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/fuel-gauge/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/fuels/.gitignore b/packages/fuels/.gitignore index 0cf2b811372..289ea019274 100644 --- a/packages/fuels/.gitignore +++ b/packages/fuels/.gitignore @@ -1,4 +1,2 @@ README.md -test/fixtures/project/.fuels -test/fixtures/generated -test/fixtures/fuels.config.ts +test/__temp__* diff --git a/packages/fuels/package.json b/packages/fuels/package.json index fa004e7f1c2..ef401f4e7b9 100644 --- a/packages/fuels/package.json +++ b/packages/fuels/package.json @@ -10,7 +10,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { @@ -76,7 +76,6 @@ "handlebars": "^4.7.7", "joycon": "^3.1.1", "lodash.camelcase": "^4.3.0", - "npm-which": "^3.0.1", "portfinder": "^1.0.32", "rimraf": "^3.0.2", "toml": "^3.0.0", diff --git a/packages/fuels/src/bin.ts b/packages/fuels/src/bin.ts index 016f2671a26..572e5b8df8b 100644 --- a/packages/fuels/src/bin.ts +++ b/packages/fuels/src/bin.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { run } from './cli'; import { error } from './cli/utils/logger'; +import { run } from './run'; try { run(process.argv).catch(process.stderr.write); diff --git a/packages/fuels/src/cli.test.ts b/packages/fuels/src/cli.test.ts index 77694f82466..37bf5bc22dd 100644 --- a/packages/fuels/src/cli.test.ts +++ b/packages/fuels/src/cli.test.ts @@ -3,16 +3,24 @@ import { Command } from 'commander'; import * as cliMod from './cli'; import { Commands } from './cli/types'; import * as loggingMod from './cli/utils/logger'; +import { run } from './run'; +/** + * @group node + */ describe('cli.js', () => { - const { configureCli, run, onPreAction } = cliMod; + beforeEach(() => { + vi.restoreAllMocks(); + }); - beforeEach(() => jest.resetAllMocks()); - afterAll(() => jest.resetAllMocks()); + afterEach(() => { + vi.restoreAllMocks(); + }); it('shoud configure cli', () => { - const program = configureCli(); + const program = cliMod.configureCli(); + expect(program).toBeTruthy(); // top level props and opts expect(program.name()).toEqual('fuels'); expect(program.opts()).toEqual({ @@ -50,14 +58,14 @@ describe('cli.js', () => { }); it('preAction should configure logging', () => { - const spy = jest.spyOn(loggingMod, 'configureLogging'); + const spy = vi.spyOn(loggingMod, 'configureLogging'); const command = new Command(); command.option('-D, --debug', 'Enables verbose logging', false); command.option('-S, --silent', 'Omit output messages', false); command.parse([]); - onPreAction(command); + cliMod.onPreAction(command); expect(spy).toBeCalledWith({ isDebugEnabled: false, isLoggingEnabled: true, @@ -66,12 +74,16 @@ describe('cli.js', () => { it('should run cli program', async () => { const command = new Command(); - const parseAsync = jest.spyOn(command, 'parseAsync'); - const $configureCli = jest.spyOn(cliMod, 'configureCli').mockReturnValue(command); + + const parseAsync = vi + .spyOn(Command.prototype, 'parseAsync') + .mockReturnValue(Promise.resolve(command)); + + const configureCli = vi.spyOn(cliMod, 'configureCli').mockImplementation(() => new Command()); await run([]); - expect($configureCli).toHaveBeenCalledTimes(1); + expect(configureCli).toHaveBeenCalledTimes(1); expect(parseAsync).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/fuels/src/cli.ts b/packages/fuels/src/cli.ts index 5c457f5df9d..427fbac360f 100644 --- a/packages/fuels/src/cli.ts +++ b/packages/fuels/src/cli.ts @@ -1,4 +1,5 @@ import { configureCliOptions as configureTypegenCliOptions } from '@fuel-ts/abi-typegen/cli'; +import { findBinPath } from '@fuel-ts/utils/cli-utils'; import { versions } from '@fuel-ts/versions'; import { runVersions } from '@fuel-ts/versions/cli'; import { Command, Option } from 'commander'; @@ -10,7 +11,6 @@ import { init } from './cli/commands/init'; import { withConfig } from './cli/commands/withConfig'; import { withProgram } from './cli/commands/withProgram'; import { Commands } from './cli/types'; -import { findBinPath } from './cli/utils/findBinPath'; import { configureLogging } from './cli/utils/logger'; export const onPreAction = (command: Command) => { @@ -100,17 +100,12 @@ export const configureCli = () => { */ program.command('core', 'Wrapper around Fuel Core binary', { - executableFile: findBinPath('fuels-core'), + executableFile: findBinPath('fuels-core', __dirname), }); program.command('forc', 'Wrapper around Forc binary', { - executableFile: findBinPath('fuels-forc'), + executableFile: findBinPath('fuels-forc', __dirname), }); return program; }; - -export const run = async (argv: string[]) => { - const program = configureCli(); - return program.parseAsync(argv); -}; diff --git a/packages/fuels/src/cli/commands/build/buildSwayProgram.test.ts b/packages/fuels/src/cli/commands/build/buildSwayProgram.test.ts new file mode 100644 index 00000000000..24cf1be223b --- /dev/null +++ b/packages/fuels/src/cli/commands/build/buildSwayProgram.test.ts @@ -0,0 +1,82 @@ +import * as findBinPathMod from '@fuel-ts/utils/cli-utils'; +import * as childProcessMod from 'child_process'; + +import { fuelsConfig } from '../../../../test/fixtures/fuels.config'; +import { mockLogger } from '../../../../test/utils/mockLogger'; +import { configureLogging } from '../../utils/logger'; + +import { buildSwayProgram } from './buildSwayProgram'; + +vi.mock('child_process', async () => { + const mod = await vi.importActual('child_process'); + return { + __esModule: true, + ...mod, + }; +}); + +/** + * @group node + */ +describe('buildSwayPrograms', () => { + beforeEach(() => { + mockLogger(); + }); + + function mockAll(params: { shouldError: boolean } = { shouldError: false }) { + const findBinPath = vi.spyOn(findBinPathMod, 'findBinPath').mockReturnValue(''); + + const spawnMocks = { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + on: vi.fn((eventName: string, fn: (..._: any) => void) => { + const shouldExit = eventName === 'exit' && !params.shouldError; + const shouldError = eventName === 'error' && params.shouldError; + if (shouldExit || shouldError) { + setTimeout(fn, 10); + } + }), + stderr: { + pipe: vi.fn(), + }, + stdout: { + pipe: vi.fn(), + }, + } as unknown as childProcessMod.ChildProcessWithoutNullStreams; + + const spawn = vi.spyOn(childProcessMod, 'spawn').mockReturnValue(spawnMocks); + + return { + spawn, + spawnMocks, + findBinPath, + }; + } + + test('should pipe stdout', async () => { + const { spawn, spawnMocks } = mockAll(); + + vi.spyOn(process.stdout, 'write').mockReturnValue(true); + configureLogging({ isLoggingEnabled: true, isDebugEnabled: false }); + + await buildSwayProgram(fuelsConfig, '/any/workspace/path'); + + expect(spawn).toHaveBeenCalledTimes(1); + expect(spawnMocks.stderr.pipe).toHaveBeenCalledTimes(1); + expect(spawnMocks.stdout.pipe).toHaveBeenCalledTimes(0); + }); + + test('should pipe stdout and stderr', async () => { + const { spawn, spawnMocks } = mockAll(); + + vi.spyOn(process.stderr, 'write').mockReturnValue(true); + vi.spyOn(process.stdout, 'write').mockReturnValue(true); + configureLogging({ isLoggingEnabled: true, isDebugEnabled: true }); + + await buildSwayProgram(fuelsConfig, '/any/workspace/path'); + + expect(spawn).toHaveBeenCalledTimes(1); + expect(spawnMocks.stderr.pipe).toHaveBeenCalledTimes(1); + expect(spawnMocks.stdout.pipe).toHaveBeenCalledTimes(1); + expect(spawnMocks.on).toHaveBeenCalledTimes(2); + }); +}); diff --git a/packages/fuels/src/cli/commands/build/buildSwayProgram.ts b/packages/fuels/src/cli/commands/build/buildSwayProgram.ts new file mode 100644 index 00000000000..80780a6984b --- /dev/null +++ b/packages/fuels/src/cli/commands/build/buildSwayProgram.ts @@ -0,0 +1,32 @@ +import { findBinPath } from '@fuel-ts/utils/cli-utils'; +import { spawn } from 'child_process'; + +import type { FuelsConfig } from '../../types'; +import { debug, loggingConfig } from '../../utils/logger'; + +import { onForcExit, onForcError } from './forcHandlers'; + +export const buildSwayProgram = async (config: FuelsConfig, path: string) => { + debug('Building Sway program', path); + + return new Promise((resolve, reject) => { + const builtInForcPath = findBinPath('fuels-forc', __dirname); + + const command = config.useBuiltinForc ? builtInForcPath : 'forc'; + const forc = spawn(command, ['build', '-p', path], { stdio: 'pipe' }); + + if (loggingConfig.isLoggingEnabled) { + forc.stderr?.pipe(process.stderr); + } + + if (loggingConfig.isDebugEnabled) { + forc.stdout?.pipe(process.stdout); + } + + const onExit = onForcExit(resolve, reject); + const onError = onForcError(reject); + + forc.on('exit', onExit); + forc.on('error', onError); + }); +}; diff --git a/packages/fuels/src/cli/commands/build/buildSwayPrograms.test.ts b/packages/fuels/src/cli/commands/build/buildSwayPrograms.test.ts index 61dd5f78ab9..9b1ca757485 100644 --- a/packages/fuels/src/cli/commands/build/buildSwayPrograms.test.ts +++ b/packages/fuels/src/cli/commands/build/buildSwayPrograms.test.ts @@ -1,57 +1,21 @@ -import * as childProcessMod from 'child_process'; - -import { fuelsConfig } from '../../../../test/fixtures/config/fuels.config'; +import { fuelsConfig } from '../../../../test/fixtures/fuels.config'; import { mockLogger } from '../../../../test/utils/mockLogger'; -import { resetDiskAndMocks } from '../../../../test/utils/resetDiskAndMocks'; -import { workspaceDir } from '../../../../test/utils/runCommands'; -import { configureLogging } from '../../utils/logger'; - -import * as buildSwayProgramsMod from './buildSwayPrograms'; -jest.mock('child_process', () => ({ - __esModule: true, - ...jest.requireActual('child_process'), -})); +import * as buildSwayProgramMod from './buildSwayProgram'; +import { buildSwayPrograms } from './buildSwayPrograms'; +/** + * @group node + */ describe('buildSwayPrograms', () => { - const { onForcExit, onForcError } = buildSwayProgramsMod; - - beforeEach(mockLogger); - afterEach(resetDiskAndMocks); - - function mockSpawn(params: { shouldError: boolean } = { shouldError: false }) { - const spawnMocks = { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - on: jest.fn((eventName: string, fn: (..._: any) => void) => { - const shouldExit = eventName === 'exit' && !params.shouldError; - const shouldError = eventName === 'error' && params.shouldError; - if (shouldExit || shouldError) { - setTimeout(fn, 10); - } - }), - stderr: { - pipe: jest.fn(), - }, - stdout: { - pipe: jest.fn(), - }, - }; - - const spawn = jest - .spyOn(childProcessMod, 'spawn') - .mockImplementation( - (..._) => spawnMocks as unknown as childProcessMod.ChildProcessWithoutNullStreams - ); - - return { - spawn, - spawnMocks, - }; - } + beforeEach(() => { + vi.resetAllMocks(); + mockLogger(); + }); function mockBuildSwayProgram() { - const buildSwayProgram = jest - .spyOn(buildSwayProgramsMod, 'buildSwayProgram') + const buildSwayProgram = vi + .spyOn(buildSwayProgramMod, 'buildSwayProgram') .mockReturnValue(Promise.resolve()); return { @@ -59,18 +23,15 @@ describe('buildSwayPrograms', () => { }; } - function customConfig(workspace: undefined | string) { - return { - ...structuredClone(fuelsConfig), - workspace, - }; - } - test('building Sway programs using workspace', async () => { - const config = customConfig(workspaceDir); const { buildSwayProgram } = mockBuildSwayProgram(); - await buildSwayProgramsMod.buildSwayPrograms(config); + const config = { + ...structuredClone(fuelsConfig), + workspace: '/any/workspace/path', + }; + + await buildSwayPrograms(config); expect(buildSwayProgram).toHaveBeenCalledTimes(1); expect(buildSwayProgram).toHaveBeenCalledWith(config, config.workspace); @@ -78,68 +39,13 @@ describe('buildSwayPrograms', () => { test('building Sway programs using individual configs', async () => { const { buildSwayProgram } = mockBuildSwayProgram(); - const config = customConfig(undefined); - await buildSwayProgramsMod.buildSwayPrograms(config); + await buildSwayPrograms(fuelsConfig); expect(buildSwayProgram).toHaveBeenCalledTimes(4); - expect(buildSwayProgram).toHaveBeenCalledWith(config, config.contracts[0]); - expect(buildSwayProgram).toHaveBeenCalledWith(config, config.contracts[1]); - expect(buildSwayProgram).toHaveBeenCalledWith(config, config.scripts[0]); - expect(buildSwayProgram).toHaveBeenCalledWith(config, config.predicates[0]); - }); - - test('should pipe stdout', async () => { - const config = customConfig(workspaceDir); - const { spawn, spawnMocks } = mockSpawn(); - - jest.spyOn(process.stdout, 'write').mockImplementation(); - configureLogging({ isLoggingEnabled: true, isDebugEnabled: false }); - - await buildSwayProgramsMod.buildSwayProgram(config, config.workspace); - - expect(spawn).toHaveBeenCalledTimes(1); - expect(spawnMocks.stderr.pipe).toHaveBeenCalledTimes(1); - expect(spawnMocks.stdout.pipe).toHaveBeenCalledTimes(0); - }); - - test('should pipe stdout and stderr', async () => { - const config = customConfig(workspaceDir); - const { spawn, spawnMocks } = mockSpawn(); - - jest.spyOn(process.stderr, 'write').mockImplementation(); - jest.spyOn(process.stdout, 'write').mockImplementation(); - configureLogging({ isLoggingEnabled: true, isDebugEnabled: true }); - - await buildSwayProgramsMod.buildSwayProgram(config, config.workspace); - - expect(spawn).toHaveBeenCalledTimes(1); - expect(spawnMocks.stderr.pipe).toHaveBeenCalledTimes(1); - expect(spawnMocks.stdout.pipe).toHaveBeenCalledTimes(1); - expect(spawnMocks.on).toHaveBeenCalledTimes(2); - }); - - test('should resolve on successful exit', () => { - const resolve = jest.fn(); - const reject = jest.fn((_reason?: number | Error) => {}); - onForcExit(resolve, reject)(null); - expect(reject).toHaveBeenCalledTimes(0); - expect(resolve).toHaveBeenCalledTimes(1); - }); - - test('should reject on failed exit', () => { - const resolve = jest.fn(); - const reject = jest.fn((_reason?: number | Error) => {}); - onForcExit(resolve, reject)(1); - expect(reject).toHaveBeenCalledTimes(1); - expect(resolve).toHaveBeenCalledTimes(0); - }); - - test('should reject on error', () => { - const reject = jest.fn(); - const { error } = mockLogger(); - onForcError(reject)(new Error()); - expect(reject).toHaveBeenCalledTimes(1); - expect(error).toHaveBeenCalledTimes(1); + expect(buildSwayProgram).toHaveBeenCalledWith(fuelsConfig, fuelsConfig.contracts[0]); + expect(buildSwayProgram).toHaveBeenCalledWith(fuelsConfig, fuelsConfig.contracts[1]); + expect(buildSwayProgram).toHaveBeenCalledWith(fuelsConfig, fuelsConfig.scripts[0]); + expect(buildSwayProgram).toHaveBeenCalledWith(fuelsConfig, fuelsConfig.predicates[0]); }); }); diff --git a/packages/fuels/src/cli/commands/build/buildSwayPrograms.ts b/packages/fuels/src/cli/commands/build/buildSwayPrograms.ts index 71270893cfd..57cf9957dd5 100644 --- a/packages/fuels/src/cli/commands/build/buildSwayPrograms.ts +++ b/packages/fuels/src/cli/commands/build/buildSwayPrograms.ts @@ -1,52 +1,8 @@ -import { spawn } from 'child_process'; - import type { FuelsConfig } from '../../types'; -import { findBinPath } from '../../utils/findBinPath'; import { getBinarySource } from '../../utils/getBinarySource'; -import { debug, error, log, loggingConfig } from '../../utils/logger'; - -type OnResultFn = () => void; -type OnErrorFn = (reason?: number | Error) => void; - -export const onForcExit = - (onResultFn: OnResultFn, onErrorFn: OnErrorFn) => (code: number | null) => { - if (code) { - onErrorFn(code); - // process.exit()? - } else { - onResultFn(); - } - }; - -export const onForcError = (onError: OnErrorFn) => (err: Error) => { - error(err); - onError(err); -}; - -export const buildSwayProgram = async (config: FuelsConfig, path: string) => { - debug('Building Sway program', path); - - return new Promise((resolve, reject) => { - const builtInForcPath = findBinPath('fuels-forc'); - - const command = config.useBuiltinForc ? builtInForcPath : 'forc'; - const forc = spawn(command, ['build', '-p', path], { stdio: 'pipe' }); - - if (loggingConfig.isLoggingEnabled) { - forc.stderr?.pipe(process.stderr); - } - - if (loggingConfig.isDebugEnabled) { - forc.stdout?.pipe(process.stdout); - } - - const onExit = onForcExit(resolve, reject); - const onError = onForcError(reject); +import { log } from '../../utils/logger'; - forc.on('exit', onExit); - forc.on('error', onError); - }); -}; +import { buildSwayProgram } from './buildSwayProgram'; export async function buildSwayPrograms(config: FuelsConfig) { log(`Building Sway programs using ${getBinarySource(config.useBuiltinFuelCore)} 'forc' binary`); diff --git a/packages/fuels/src/cli/commands/build/forcHandlers.test.ts b/packages/fuels/src/cli/commands/build/forcHandlers.test.ts new file mode 100644 index 00000000000..e104cdb26a6 --- /dev/null +++ b/packages/fuels/src/cli/commands/build/forcHandlers.test.ts @@ -0,0 +1,32 @@ +import { mockLogger } from '../../../../test/utils/mockLogger'; + +import { onForcExit, onForcError } from './forcHandlers'; + +/** + * @group node + */ +describe('buildSwayPrograms', () => { + test('should resolve on successful exit', () => { + const resolve = vi.fn(); + const reject = vi.fn((_reason?: number | Error) => {}); + onForcExit(resolve, reject)(null); + expect(reject).toHaveBeenCalledTimes(0); + expect(resolve).toHaveBeenCalledTimes(1); + }); + + test('should reject on failed exit', () => { + const resolve = vi.fn(); + const reject = vi.fn((_reason?: number | Error) => {}); + onForcExit(resolve, reject)(1); + expect(reject).toHaveBeenCalledTimes(1); + expect(resolve).toHaveBeenCalledTimes(0); + }); + + test('should reject on error', () => { + const reject = vi.fn(); + const { error } = mockLogger(); + onForcError(reject)(new Error()); + expect(reject).toHaveBeenCalledTimes(1); + expect(error).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/fuels/src/cli/commands/build/forcHandlers.ts b/packages/fuels/src/cli/commands/build/forcHandlers.ts new file mode 100644 index 00000000000..295005077a1 --- /dev/null +++ b/packages/fuels/src/cli/commands/build/forcHandlers.ts @@ -0,0 +1,19 @@ +import { error } from '../../utils/logger'; + +type OnResultFn = () => void; +type OnErrorFn = (reason?: number | Error) => void; + +export const onForcExit = + (onResultFn: OnResultFn, onErrorFn: OnErrorFn) => (code: number | null) => { + if (code) { + onErrorFn(code); + // process.exit()? + } else { + onResultFn(); + } + }; + +export const onForcError = (onError: OnErrorFn) => (err: Error) => { + error(err); + onError(err); +}; diff --git a/packages/fuels/src/cli/commands/build/index.ts b/packages/fuels/src/cli/commands/build/index.ts index 0a35fe65afb..48f1738d67b 100644 --- a/packages/fuels/src/cli/commands/build/index.ts +++ b/packages/fuels/src/cli/commands/build/index.ts @@ -3,7 +3,7 @@ import { type Command } from 'commander'; import type { FuelsConfig } from '../../types'; import { log } from '../../utils/logger'; import { deploy } from '../deploy'; -import { autoStartFuelCore } from '../dev/startFuelCore'; +import { autoStartFuelCore } from '../dev/autoStartFuelCore'; import { buildSwayPrograms } from './buildSwayPrograms'; import { generateTypes } from './generateTypes'; diff --git a/packages/fuels/src/cli/commands/deploy/createWallet.test.ts b/packages/fuels/src/cli/commands/deploy/createWallet.test.ts index c7650bb83bd..4722fa53f97 100644 --- a/packages/fuels/src/cli/commands/deploy/createWallet.test.ts +++ b/packages/fuels/src/cli/commands/deploy/createWallet.test.ts @@ -3,6 +3,9 @@ import { FUEL_NETWORK_URL } from '@fuel-ts/wallet/configs'; import { createWallet } from './createWallet'; +/** + * @group node + */ describe('createWallet', () => { const privateKey = '0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298'; diff --git a/packages/fuels/src/cli/commands/deploy/getDeployConfig.test.ts b/packages/fuels/src/cli/commands/deploy/getDeployConfig.test.ts index c2e674b095a..35d6407d789 100644 --- a/packages/fuels/src/cli/commands/deploy/getDeployConfig.test.ts +++ b/packages/fuels/src/cli/commands/deploy/getDeployConfig.test.ts @@ -1,5 +1,8 @@ import { getDeployConfig } from './getDeployConfig'; +/** + * @group node + */ describe('getDeployConfig', () => { test('deploy options as object', async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/fuels/src/cli/commands/dev/autoStartFuelCore.test.ts b/packages/fuels/src/cli/commands/dev/autoStartFuelCore.test.ts new file mode 100644 index 00000000000..35cbbf508dc --- /dev/null +++ b/packages/fuels/src/cli/commands/dev/autoStartFuelCore.test.ts @@ -0,0 +1,107 @@ +import * as testUtilsMod from '@fuel-ts/wallet/test-utils'; +import { existsSync, rmSync } from 'fs'; +import { join } from 'path'; + +import { fuelsConfig } from '../../../../test/fixtures/fuels.config'; +import type { FuelsConfig } from '../../types'; + +import type { FuelCoreNode } from './autoStartFuelCore'; +import { autoStartFuelCore } from './autoStartFuelCore'; + +vi.mock('@fuel-ts/wallet/test-utils', async () => { + const mod = await vi.importActual('@fuel-ts/wallet/test-utils'); + return { + __esModule: true, + ...mod, + }; +}); + +/** + * @group node + */ +describe('autoStartFuelCore', () => { + afterEach(() => { + vi.restoreAllMocks(); + + const chainConfig = join(fuelsConfig.basePath, '.fuels', 'chainConfig.json'); + if (existsSync(chainConfig)) { + rmSync(chainConfig); + } + }); + + function mockLaunchNode() { + const launchNode = vi.spyOn(testUtilsMod, 'launchNode').mockReturnValue( + Promise.resolve({ + cleanup: () => {}, + ip: '0.0.0.0', + port: '4000', + chainConfigPath: '/some/path/chainConfig.json', + }) + ); + return { launchNode }; + } + + test('should auto start `fuel-core`', async () => { + const { launchNode } = mockLaunchNode(); + + const config = structuredClone(fuelsConfig); + config.autoStartFuelCore = true; + + await autoStartFuelCore(config); + + expect(launchNode).toHaveBeenCalledTimes(1); + }); + + test('should not start `fuel-core`', async () => { + const { launchNode } = mockLaunchNode(); + + const config = structuredClone(fuelsConfig); + config.autoStartFuelCore = false; + + await autoStartFuelCore(config); + + expect(launchNode).toHaveBeenCalledTimes(0); + }); + + test('should start `fuel-core` node using built-in binary', async () => { + const { launchNode } = mockLaunchNode(); + + const copyConfig: FuelsConfig = structuredClone(fuelsConfig); + copyConfig.useBuiltinFuelCore = true; + + // this will cause it to autofind a free port + copyConfig.fuelCorePort = undefined; + delete copyConfig.fuelCorePort; + + const core = (await autoStartFuelCore(copyConfig)) as FuelCoreNode; + + expect(launchNode).toHaveBeenCalledTimes(1); + + expect(core.bindIp).toEqual('0.0.0.0'); + expect(core.accessIp).toEqual('127.0.0.1'); + expect(core.port).toBeGreaterThanOrEqual(4000); + expect(core.providerUrl).toMatch(/http:\/\/127\.0\.0\.1:([0-9]+)\/graphql/); + expect(core.killChildProcess).toBeTruthy(); + + core.killChildProcess(); + }); + + test('should start `fuel-core` node using system binary', async () => { + const { launchNode } = mockLaunchNode(); + + const core = (await autoStartFuelCore({ + ...structuredClone(fuelsConfig), + useBuiltinFuelCore: false, + })) as FuelCoreNode; + + expect(launchNode).toHaveBeenCalledTimes(1); + + expect(core.bindIp).toEqual('0.0.0.0'); + expect(core.accessIp).toEqual('127.0.0.1'); + expect(core.port).toBeGreaterThanOrEqual(4000); + expect(core.providerUrl).toMatch(/http:\/\/127\.0\.0\.1:([0-9]+)\/graphql/); + expect(core.killChildProcess).toBeTruthy(); + + core.killChildProcess(); + }); +}); diff --git a/packages/fuels/src/cli/commands/dev/autoStartFuelCore.ts b/packages/fuels/src/cli/commands/dev/autoStartFuelCore.ts new file mode 100644 index 00000000000..9958ca5660b --- /dev/null +++ b/packages/fuels/src/cli/commands/dev/autoStartFuelCore.ts @@ -0,0 +1,69 @@ +import { defaultConsensusKey } from '@fuel-ts/utils'; +import { launchNode } from '@fuel-ts/wallet/test-utils'; +import type { ChildProcessWithoutNullStreams } from 'child_process'; +import { getPortPromise } from 'portfinder'; + +import type { FuelsConfig } from '../../types'; +import { getBinarySource } from '../../utils/getBinarySource'; +import { log, loggingConfig } from '../../utils/logger'; + +export type FuelCoreNode = { + bindIp: string; + accessIp: string; + port: number; + providerUrl: string; + chainConfigPath: string; + killChildProcess: () => void; +}; + +export type KillNodeParams = { + core: ChildProcessWithoutNullStreams; + killFn: (pid: number) => void; + state: { + isDead: boolean; + }; +}; + +export const autoStartFuelCore = async (config: FuelsConfig) => { + let fuelCore: FuelCoreNode | undefined; + + if (config.autoStartFuelCore) { + log(`Starting ${getBinarySource(config.useBuiltinFuelCore)} 'fuel-core' node..`); + + const bindIp = '0.0.0.0'; + const accessIp = '127.0.0.1'; + + const port = config.fuelCorePort ?? (await getPortPromise({ port: 4000 })); + + const providerUrl = `http://${accessIp}:${port}/graphql`; + + const { cleanup, chainConfigPath } = await launchNode({ + args: [ + ['--chain', config.chainConfig], + ['--db-type', 'rocks-db'], + ].flat() as string[], + ip: bindIp, + port: port.toString(), + loggingEnabled: loggingConfig.isLoggingEnabled, + debugEnabled: loggingConfig.isDebugEnabled, + basePath: config.basePath, + useSystemFuelCore: !config.useBuiltinFuelCore, + }); + + fuelCore = { + bindIp, + accessIp, + port, + providerUrl, + chainConfigPath, + killChildProcess: cleanup, + }; + + // eslint-disable-next-line no-param-reassign + config.providerUrl = fuelCore.providerUrl; + // eslint-disable-next-line no-param-reassign + config.privateKey = defaultConsensusKey; + } + + return fuelCore; +}; diff --git a/packages/fuels/src/cli/commands/dev/defaultChainConfig.ts b/packages/fuels/src/cli/commands/dev/defaultChainConfig.ts deleted file mode 100644 index 80f4eb9de6c..00000000000 --- a/packages/fuels/src/cli/commands/dev/defaultChainConfig.ts +++ /dev/null @@ -1,522 +0,0 @@ -/** - * This is the `privateKey` of the `consensus.PoA.signing_key` below. - * This key is responsible for validating the transactions. - */ -export const defaultConsensusKey = - '0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298'; - -export const defaultChainConfig = { - chain_name: 'local_testnet', - block_gas_limit: 5000000000, - initial_state: { - coins: [ - { - owner: '0x94ffcc53b892684acefaebc8a3d4a595e528a8cf664eeb3ef36f1020b0809d0d', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - ], - messages: [ - { - sender: '0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f', - recipient: '0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba', - nonce: '0101010101010101010101010101010101010101010101010101010101010101', - amount: '0x000000000000FFFF', - data: '', - da_height: '0x00', - }, - { - sender: '0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba', - recipient: '0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f', - nonce: '0e1ef2963832068b0e1ef2963832068b0e1ef2963832068b0e1ef2963832068b', - amount: '0xb04f3c08f59b309e', - data: '', - da_height: '0x00', - }, - ], - }, - consensus_parameters: { - tx_params: { - max_inputs: 255, - max_outputs: 255, - max_witnesses: 255, - max_gas_per_tx: 10000000, - max_size: 17825792, - }, - predicate_params: { - max_predicate_length: 1048576, - max_predicate_data_length: 1048576, - max_gas_per_predicate: 10000000, - max_message_data_length: 1048576, - }, - script_params: { - max_script_length: 1048576, - max_script_data_length: 1048576, - }, - contract_params: { - contract_max_size: 16777216, - max_storage_slots: 255, - }, - fee_params: { - gas_price_factor: 92, - gas_per_byte: 4, - }, - }, - gas_costs: { - add: 1, - addi: 1, - aloc: 1, - and: 1, - andi: 1, - bal: 13, - bhei: 1, - bhsh: 1, - burn: 132, - cb: 1, - cfei: 1, - cfsi: 1, - croo: 16, - div: 1, - divi: 1, - ecr1: 3000, - eck1: 951, - ed19: 3000, - eq: 1, - exp: 1, - expi: 1, - flag: 1, - gm: 1, - gt: 1, - gtf: 1, - ji: 1, - jmp: 1, - jne: 1, - jnei: 1, - jnzi: 1, - jmpf: 1, - jmpb: 1, - jnzf: 1, - jnzb: 1, - jnef: 1, - jneb: 1, - lb: 1, - log: 9, - lt: 1, - lw: 1, - mint: 135, - mlog: 1, - modOp: 1, - modi: 1, - moveOp: 1, - movi: 1, - mroo: 2, - mul: 1, - muli: 1, - mldv: 1, - noop: 1, - not: 1, - or: 1, - ori: 1, - poph: 2, - popl: 2, - pshh: 2, - pshl: 2, - ret: 13, - rvrt: 13, - sb: 1, - sll: 1, - slli: 1, - srl: 1, - srli: 1, - srw: 12, - sub: 1, - subi: 1, - sw: 1, - sww: 67, - time: 1, - tr: 105, - tro: 60, - wdcm: 1, - wqcm: 1, - wdop: 1, - wqop: 1, - wdml: 1, - wqml: 1, - wddv: 1, - wqdv: 2, - wdmd: 3, - wqmd: 4, - wdam: 2, - wqam: 3, - wdmm: 3, - wqmm: 3, - xor: 1, - xori: 1, - call: { - LightOperation: { - base: 144, - units_per_gas: 214, - }, - }, - ccp: { - LightOperation: { - base: 15, - units_per_gas: 103, - }, - }, - csiz: { - LightOperation: { - base: 17, - units_per_gas: 790, - }, - }, - k256: { - LightOperation: { - base: 11, - units_per_gas: 214, - }, - }, - ldc: { - LightOperation: { - base: 15, - units_per_gas: 272, - }, - }, - logd: { - LightOperation: { - base: 26, - units_per_gas: 64, - }, - }, - mcl: { - LightOperation: { - base: 1, - units_per_gas: 3333, - }, - }, - mcli: { - LightOperation: { - base: 1, - units_per_gas: 3333, - }, - }, - mcp: { - LightOperation: { - base: 1, - units_per_gas: 2000, - }, - }, - mcpi: { - LightOperation: { - base: 3, - units_per_gas: 2000, - }, - }, - meq: { - LightOperation: { - base: 1, - units_per_gas: 2500, - }, - }, - retd: { - LightOperation: { - base: 29, - units_per_gas: 62, - }, - }, - s256: { - LightOperation: { - base: 2, - units_per_gas: 214, - }, - }, - scwq: { - LightOperation: { - base: 13, - units_per_gas: 5, - }, - }, - smo: { - LightOperation: { - base: 209, - units_per_gas: 55, - }, - }, - srwq: { - LightOperation: { - base: 47, - units_per_gas: 5, - }, - }, - swwq: { - LightOperation: { - base: 44, - units_per_gas: 5, - }, - }, - contract_root: { - LightOperation: { - base: 75, - units_per_gas: 1, - }, - }, - state_root: { - LightOperation: { - base: 412, - units_per_gas: 1, - }, - }, - vm_initialization: { - HeavyOperation: { - base: 2000, - gas_per_unit: 0, - }, - }, - new_storage_per_byte: 1, - }, - consensus: { - PoA: { - signing_key: '0x94ffcc53b892684acefaebc8a3d4a595e528a8cf664eeb3ef36f1020b0809d0d', - }, - }, -}; diff --git a/packages/fuels/src/cli/commands/dev/index.test.ts b/packages/fuels/src/cli/commands/dev/index.test.ts index 3c70994e844..48e18a0ae02 100644 --- a/packages/fuels/src/cli/commands/dev/index.test.ts +++ b/packages/fuels/src/cli/commands/dev/index.test.ts @@ -1,39 +1,52 @@ import { safeExec } from '@fuel-ts/errors/test-utils'; +import type { FSWatcher } from 'chokidar'; -import { fuelsConfig } from '../../../../test/fixtures/config/fuels.config'; +import { fuelsConfig } from '../../../../test/fixtures/fuels.config'; +import { mockStartFuelCore } from '../../../../test/utils/mockAutoStartFuelCore'; import { mockLogger } from '../../../../test/utils/mockLogger'; import * as loadConfigMod from '../../config/loadConfig'; import type { FuelsConfig } from '../../types'; +import * as buildMod from '../build'; +import * as deployMod from '../deploy'; import * as withConfigMod from '../withConfig'; -import * as indexMod from '.'; -import type { FuelCoreNode } from './startFuelCore'; - +// import * as indexMod from './index'; +import { + closeAllFileHandlers, + configFileChanged, + dev, + getConfigFilepathsToWatch, + workspaceFileChanged, +} from '.'; + +/** + * @group node + */ describe('dev', () => { - beforeEach(jest.restoreAllMocks); + beforeEach(() => { + vi.restoreAllMocks(); + }); function mockAll() { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const killChildProcess: any = jest.fn(); - const closeAllFileHandlers = jest.spyOn(indexMod, 'closeAllFileHandlers').mockImplementation(); - const fuelCore = { killChildProcess } as FuelCoreNode; - const onFailure = jest.fn(); + const { autoStartFuelCore, fuelCore, killChildProcess } = mockStartFuelCore(); + + const onFailure = vi.fn(); - const withConfigErrorHandler = jest + const withConfigErrorHandler = vi .spyOn(withConfigMod, 'withConfigErrorHandler') - .mockImplementation(); + .mockReturnValue(Promise.resolve()); - const loadConfig = jest + const loadConfig = vi .spyOn(loadConfigMod, 'loadConfig') .mockReturnValue(Promise.resolve(fuelsConfig)); - const dev = jest.spyOn(indexMod, 'dev').mockImplementation(() => { - throw new Error('The sky became purple'); - }); + const build = vi.spyOn(buildMod, 'build').mockResolvedValue(); + const deploy = vi.spyOn(deployMod, 'deploy').mockResolvedValue([]); return { - closeAllFileHandlers, - dev, + autoStartFuelCore, + build, + deploy, fuelCore, killChildProcess, loadConfig, @@ -42,31 +55,28 @@ describe('dev', () => { }; } - const { workspaceFileChanged } = indexMod; - test('workspaceFileChanged should log change and call `buildAndDeploy`', async () => { const { log } = mockLogger(); - - const buildAndDeploy = jest.spyOn(indexMod, 'buildAndDeploy').mockImplementation(); + const { build, deploy } = mockAll(); await workspaceFileChanged({ config: fuelsConfig, watchHandlers: [] })('event', 'some/path'); expect(log).toHaveBeenCalledTimes(1); - expect(buildAndDeploy).toHaveBeenCalledTimes(1); + expect(build).toHaveBeenCalledTimes(1); + expect(deploy).toHaveBeenCalledTimes(1); }); - test('dev should handle and log error from `buildAndDeploy`', async () => { - const err = new Error('something happened'); - + it('dev should handle and log error from `buildAndDeploy`', async () => { const { error } = mockLogger(); - jest.spyOn(indexMod, 'buildAndDeploy').mockImplementation(() => { + const err = new Error('something happened'); + vi.spyOn(buildMod, 'build').mockImplementation(() => { throw err; }); const configCopy: FuelsConfig = { ...fuelsConfig, autoStartFuelCore: false }; - const { result, error: safeError } = await safeExec(() => indexMod.dev(configCopy)); + const { result, error: safeError } = await safeExec(() => dev(configCopy)); expect(result).not.toBeTruthy(); expect(safeError).toEqual(err); @@ -76,35 +86,52 @@ describe('dev', () => { }); test('should call `close` on all file handlers', () => { - const close = jest.fn(); + const close = vi.fn(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const handlers: any = [{ close }, { close }, { close }]; - indexMod.closeAllFileHandlers(handlers); + closeAllFileHandlers(handlers); expect(close).toHaveBeenCalledTimes(3); }); test('should restart everything when config file changes', async () => { - const { closeAllFileHandlers, dev, fuelCore, killChildProcess, loadConfig } = mockAll(); const { log } = mockLogger(); + const { + autoStartFuelCore, + build, + deploy, + fuelCore, + killChildProcess, + loadConfig, + withConfigErrorHandler, + } = mockAll(); const config = structuredClone(fuelsConfig); + const close = vi.fn(); + const watchHandlers = [{ close }, { close }] as unknown as FSWatcher[]; - await indexMod.configFileChanged({ config, fuelCore, watchHandlers: [] })('event', 'some/path'); + await configFileChanged({ config, fuelCore, watchHandlers })('event', 'some/path'); - expect(closeAllFileHandlers).toHaveBeenCalledTimes(1); - expect(killChildProcess).toHaveBeenCalledTimes(1); - expect(dev).toHaveBeenCalledTimes(1); + // configFileChanged() internals expect(log).toHaveBeenCalledTimes(1); + expect(close).toHaveBeenCalledTimes(2); + expect(killChildProcess).toHaveBeenCalledTimes(1); expect(loadConfig).toHaveBeenCalledTimes(1); + + // dev() internals + expect(autoStartFuelCore).toHaveBeenCalledTimes(1); + expect(build).toHaveBeenCalledTimes(1); + expect(deploy).toHaveBeenCalledTimes(1); + expect(withConfigErrorHandler).toHaveBeenCalledTimes(0); // never error }); test('should restart everything and handle errors', async () => { + const { log } = mockLogger(); const { - closeAllFileHandlers, - dev, + autoStartFuelCore, + deploy, fuelCore, killChildProcess, loadConfig, @@ -112,20 +139,27 @@ describe('dev', () => { withConfigErrorHandler, } = mockAll(); - const { log } = mockLogger(); + const err = new Error('something happened'); + const build = vi.spyOn(buildMod, 'build').mockImplementation(() => { + throw err; + }); const config = { onFailure, ...structuredClone(fuelsConfig) }; + const close = vi.fn(); + const watchHandlers = [{ close }, { close }] as unknown as FSWatcher[]; - await indexMod.configFileChanged({ config, fuelCore, watchHandlers: [] })('event', 'some/path'); + await configFileChanged({ config, fuelCore, watchHandlers })('event', 'some/path'); + // configFileChanged() internals expect(log).toHaveBeenCalledTimes(1); - - expect(closeAllFileHandlers).toHaveBeenCalledTimes(1); + expect(close).toHaveBeenCalledTimes(2); expect(killChildProcess).toHaveBeenCalledTimes(1); - expect(loadConfig).toHaveBeenCalledTimes(1); - expect(dev).toHaveBeenCalledTimes(1); + // dev() internals + expect(autoStartFuelCore).toHaveBeenCalledTimes(1); + expect(build).toHaveBeenCalledTimes(1); + expect(deploy).toHaveBeenCalledTimes(0); // never deploy expect(withConfigErrorHandler).toHaveBeenCalledTimes(1); }); @@ -133,9 +167,9 @@ describe('dev', () => { const config = structuredClone(fuelsConfig); config.chainConfig = undefined; - expect(indexMod.getConfigFilepathsToWatch(config)).toHaveLength(1); + expect(getConfigFilepathsToWatch(config)).toHaveLength(1); config.chainConfig = '/some/path/to/chainConfig.json'; - expect(indexMod.getConfigFilepathsToWatch(config)).toHaveLength(2); + expect(getConfigFilepathsToWatch(config)).toHaveLength(2); }); }); diff --git a/packages/fuels/src/cli/commands/dev/index.ts b/packages/fuels/src/cli/commands/dev/index.ts index b062b93d064..5481dfa04e5 100644 --- a/packages/fuels/src/cli/commands/dev/index.ts +++ b/packages/fuels/src/cli/commands/dev/index.ts @@ -9,8 +9,8 @@ import { build } from '../build'; import { deploy } from '../deploy'; import { withConfigErrorHandler } from '../withConfig'; -import type { FuelCoreNode } from './startFuelCore'; -import { autoStartFuelCore } from './startFuelCore'; +import type { FuelCoreNode } from './autoStartFuelCore'; +import { autoStartFuelCore } from './autoStartFuelCore'; export const closeAllFileHandlers = (handlers: FSWatcher[]) => { handlers.forEach((h) => h.close()); diff --git a/packages/fuels/src/cli/commands/dev/startFuelCore.test.ts b/packages/fuels/src/cli/commands/dev/startFuelCore.test.ts deleted file mode 100644 index c1188213cda..00000000000 --- a/packages/fuels/src/cli/commands/dev/startFuelCore.test.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { safeExec } from '@fuel-ts/errors/test-utils'; -import * as childProcessMod from 'child_process'; - -import { fuelsConfig } from '../../../../test/fixtures/config/fuels.config'; -import { mockLogger } from '../../../../test/utils/mockLogger'; -import { resetDiskAndMocks } from '../../../../test/utils/resetDiskAndMocks'; -import type { FuelsConfig } from '../../types'; -import { configureLogging, loggingConfig } from '../../utils/logger'; - -import { killNode, startFuelCore } from './startFuelCore'; - -type ChildProcessWithoutNullStreams = childProcessMod.ChildProcessWithoutNullStreams; - -jest.mock('child_process', () => ({ - __esModule: true, - ...jest.requireActual('child_process'), -})); - -describe('startFuelCore', () => { - const loggingConfigBkp = loggingConfig; - - afterEach(() => { - configureLogging(loggingConfigBkp); - resetDiskAndMocks(); - }); - - /** - * This should mimic the stderr.on('data') event, returning both - * success and error messages, as strings. These messages are like - * the ones from `fuel-core` startup log messages. We filter them - * to know fuel-core state. - */ - function mockSpawn(params: { shouldError: boolean } = { shouldError: false }) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const stderrOn = (eventName: string, fn: (data: any) => void) => { - if (eventName === 'data') { - if (params.shouldError) { - // The `IO error` message simulates a possible fuel-core error log message - fn('IO error'); - } else { - // The `Binding GraphQL provider to` message simulates a fuel-core - // successful startup log message, usually meaning that the node - // is up and waiting for connections - fn('Binding GraphQL provider to'); - } - } - }; - - const innerMocks = { - on: jest.fn(), - stderr: { - pipe: jest.fn(), - on: jest.fn(stderrOn), - }, - stdout: { - pipe: jest.fn(), - }, - }; - - const spawn = jest - .spyOn(childProcessMod, 'spawn') - .mockImplementation((..._) => innerMocks as unknown as ChildProcessWithoutNullStreams); - - return { spawn, innerMocks }; - } - - test('should start `fuel-core` node using built-in binary', async () => { - mockSpawn(); - mockLogger(); - - const copyConfig: FuelsConfig = structuredClone(fuelsConfig); - copyConfig.useBuiltinFuelCore = true; - - // this will cause it to autofind a free port - copyConfig.fuelCorePort = undefined; - delete copyConfig.fuelCorePort; - - const core = await startFuelCore(copyConfig); - - expect(core.bindIp).toEqual('0.0.0.0'); - expect(core.accessIp).toEqual('127.0.0.1'); - expect(core.port).toBeGreaterThanOrEqual(4000); - expect(core.providerUrl).toMatch(/http:\/\/127\.0\.0\.1:([0-9]+)\/graphql/); - expect(core.killChildProcess).toBeTruthy(); - - core.killChildProcess(); - }); - - test('should start `fuel-core` node using system binary', async () => { - mockLogger(); - - const { spawn, innerMocks } = mockSpawn(); - - await startFuelCore({ - ...structuredClone(fuelsConfig), - useBuiltinFuelCore: false, - }); - - expect(spawn).toHaveBeenCalledTimes(1); - expect(spawn.mock.calls[0][0]).toMatch(/^fuel-core$/m); - - expect(innerMocks.on).toHaveBeenCalledTimes(1); - expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1); - expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(0); - }); - - test('should throw on error', async () => { - const { innerMocks } = mockSpawn({ shouldError: true }); - const { error } = mockLogger(); - - const { error: safeError, result } = await safeExec(async () => startFuelCore(fuelsConfig)); - - expect(safeError).toBeTruthy(); - expect(result).not.toBeTruthy(); - - expect(error).toHaveBeenCalledTimes(1); - - expect(innerMocks.on).toHaveBeenCalledTimes(1); - expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1); - expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(0); - }); - - test('should pipe stdout', async () => { - mockLogger(); - - jest.spyOn(process.stdout, 'write').mockImplementation(); - - configureLogging({ isDebugEnabled: false, isLoggingEnabled: true }); - - const { innerMocks } = mockSpawn(); - - await startFuelCore(fuelsConfig); - - expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1); - expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(0); - }); - - test('should pipe stdout and stderr', async () => { - mockLogger(); - - jest.spyOn(process.stderr, 'write').mockImplementation(); - jest.spyOn(process.stdout, 'write').mockImplementation(); - - configureLogging({ isDebugEnabled: true, isLoggingEnabled: true }); - - const { innerMocks } = mockSpawn(); - - await startFuelCore(fuelsConfig); - - expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1); - expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(1); - }); - - test('should kill process only if PID exists and node is alive', () => { - const killFn = jest.fn(); - const state = { isDead: true }; - - // should not kill - let core = { pid: undefined } as ChildProcessWithoutNullStreams; - killNode({ core, killFn, state })(); - expect(killFn).toHaveBeenCalledTimes(0); - expect(state.isDead).toEqual(true); - - // should not kill - core = { pid: 1 } as ChildProcessWithoutNullStreams; - killNode({ core, killFn, state })(); - expect(killFn).toHaveBeenCalledTimes(0); - expect(state.isDead).toEqual(true); - - // should kill - state.isDead = false; - killNode({ core, killFn, state })(); - expect(killFn).toHaveBeenCalledTimes(1); - expect(state.isDead).toEqual(true); - }); -}); diff --git a/packages/fuels/src/cli/commands/dev/startFuelCore.ts b/packages/fuels/src/cli/commands/dev/startFuelCore.ts deleted file mode 100644 index ac12446876a..00000000000 --- a/packages/fuels/src/cli/commands/dev/startFuelCore.ts +++ /dev/null @@ -1,130 +0,0 @@ -import type { ChildProcessWithoutNullStreams } from 'child_process'; -import { spawn } from 'child_process'; -import { mkdirSync, writeFileSync } from 'fs'; -import { dirname, join } from 'path'; -import { getPortPromise } from 'portfinder'; -import treeKill from 'tree-kill'; - -import type { FuelsConfig } from '../../types'; -import { findBinPath } from '../../utils/findBinPath'; -import { getBinarySource } from '../../utils/getBinarySource'; -import { error, log, loggingConfig } from '../../utils/logger'; - -import { defaultChainConfig, defaultConsensusKey } from './defaultChainConfig'; - -export type FuelCoreNode = { - bindIp: string; - accessIp: string; - port: number; - providerUrl: string; - chainConfig: string; - killChildProcess: () => void; -}; - -export type KillNodeParams = { - core: ChildProcessWithoutNullStreams; - killFn: (pid: number) => void; - state: { - isDead: boolean; - }; -}; - -export const killNode = (params: KillNodeParams) => () => { - const { core, state, killFn } = params; - if (core.pid && !state.isDead) { - state.isDead = true; - killFn(Number(core.pid)); - } -}; - -export const createTempChainConfig = (coreDir: string) => { - const chainConfigPath = join(coreDir, 'chainConfig.json'); - const chainConfigJson = JSON.stringify(defaultChainConfig, null, 2); - mkdirSync(dirname(chainConfigPath), { recursive: true }); - writeFileSync(chainConfigPath, chainConfigJson); - return chainConfigPath; -}; - -export const startFuelCore = async (config: FuelsConfig): Promise => { - log(`Starting ${getBinarySource(config.useBuiltinFuelCore)} 'fuel-core' node..`); - - const coreDir = join(config.basePath, '.fuels'); - - const bindIp = '0.0.0.0'; - const accessIp = '127.0.0.1'; - - const chainConfig = config.chainConfig ?? createTempChainConfig(coreDir); - const port = config.fuelCorePort ?? (await getPortPromise({ port: 4000 })); - - const providerUrl = `http://${accessIp}:${port}/graphql`; - - const flags = [ - 'run', - ['--ip', bindIp], - ['--port', port.toString()], - ['--db-path', coreDir], - ['--min-gas-price', '0'], - ['--poa-instant', 'true'], - ['--consensus-key', defaultConsensusKey], - ['--chain', chainConfig], - '--vm-backtrace', - '--utxo-validation', - '--debug', - ].flat(); - - return new Promise((resolve, reject) => { - const builtInFuelsCorePath = findBinPath('fuels-core'); - - const command = config.useBuiltinFuelCore ? builtInFuelsCorePath : 'fuel-core'; - const core = spawn(command, flags, { stdio: 'pipe' }); - - if (loggingConfig.isLoggingEnabled) { - core.stderr.pipe(process.stderr); - } - - if (loggingConfig.isDebugEnabled) { - core.stdout.pipe(process.stdout); - } - - const state = { isDead: false }; - const killChildProcess = killNode({ core, state, killFn: treeKill }); - - process.on('beforeExit', killChildProcess); - process.on('uncaughtException', killChildProcess); - - core.stderr?.on('data', (data) => { - if (/Binding GraphQL provider to/.test(data)) { - resolve({ - bindIp, - accessIp, - port, - providerUrl, - killChildProcess, - chainConfig, - }); - } - if (/error/i.test(data)) { - error( - `Some error occurred. Please, check to see if you have another instance running locally.` - ); - reject(data.toString()); - } - }); - - core.on('error', reject); - }); -}; - -export const autoStartFuelCore = async (config: FuelsConfig) => { - let fuelCore: FuelCoreNode | undefined; - - if (config.autoStartFuelCore) { - fuelCore = await startFuelCore(config); - // eslint-disable-next-line no-param-reassign - config.providerUrl = fuelCore.providerUrl; - // eslint-disable-next-line no-param-reassign - config.privateKey = defaultConsensusKey; - } - - return fuelCore; -}; diff --git a/packages/fuels/src/cli/commands/init/index.ts b/packages/fuels/src/cli/commands/init/index.ts index 33fbb6a502e..934f4c1391a 100644 --- a/packages/fuels/src/cli/commands/init/index.ts +++ b/packages/fuels/src/cli/commands/init/index.ts @@ -40,26 +40,26 @@ export function init(program: Command) { // mimicking commander property validation process.stdout.write(`error: required option '-w, --workspace ' not specified\r`); process.exit(1); - } - - const fuelsConfigPath = join(path, 'fuels.config.ts'); + } else { + const fuelsConfigPath = join(path, 'fuels.config.ts'); - if (existsSync(fuelsConfigPath)) { - throw new Error(`Config file exists, aborting.\n ${fuelsConfigPath}`); - } + if (existsSync(fuelsConfigPath)) { + throw new Error(`Config file exists, aborting.\n ${fuelsConfigPath}`); + } - const renderedConfig = renderFuelsConfigTemplate({ - workspace, - contracts, - scripts, - predicates, - output, - useBuiltinForc, - useBuiltinFuelCore, - autoStartFuelCore, - }); + const renderedConfig = renderFuelsConfigTemplate({ + workspace, + contracts, + scripts, + predicates, + output, + useBuiltinForc, + useBuiltinFuelCore, + autoStartFuelCore, + }); - writeFileSync(fuelsConfigPath, renderedConfig); + writeFileSync(fuelsConfigPath, renderedConfig); - log(`Config file created at:\n\n ${fuelsConfigPath}\n`); + log(`Config file created at:\n\n ${fuelsConfigPath}\n`); + } } diff --git a/packages/fuels/src/cli/commands/init/shouldUseBuiltinForc.test.ts b/packages/fuels/src/cli/commands/init/shouldUseBuiltinForc.test.ts index 9efc9c8f179..560c5597616 100644 --- a/packages/fuels/src/cli/commands/init/shouldUseBuiltinForc.test.ts +++ b/packages/fuels/src/cli/commands/init/shouldUseBuiltinForc.test.ts @@ -4,21 +4,32 @@ import { mockLogger } from '../../../../test/utils/mockLogger'; import { shouldUseBuiltinFuelCore } from './shouldUseBuiltinFuelCore'; -jest.mock('@fuel-ts/versions/cli', () => ({ - __esModule: true, - ...jest.requireActual('@fuel-ts/versions/cli'), -})); +vi.mock('@fuel-ts/versions/cli', async () => { + const mod = await vi.importActual('@fuel-ts/versions/cli'); + return { + __esModule: true, + ...mod, + }; +}); -jest.mock('prompts', () => ({ - __esModule: true, - ...jest.requireActual('prompts'), -})); +vi.mock('prompts', async () => { + const mod = await vi.importActual('prompts'); + return { + __esModule: true, + ...mod, + }; +}); +/** + * @group node + */ describe('shouldUseBuiltinFuelCore', () => { - beforeEach(jest.restoreAllMocks); + afterEach(() => { + vi.restoreAllMocks(); + }); function mockAll(returns: { getSystemFuelCore: string | null }) { - const getSystemFuelCore = jest + const getSystemFuelCore = vi .spyOn(getSystemFuelCoreMod, 'getSystemFuelCore') .mockReturnValue({ error: null, systemFuelCoreVersion: returns.getSystemFuelCore }); diff --git a/packages/fuels/src/cli/commands/init/shouldUseBuiltinFuelCore.test.ts b/packages/fuels/src/cli/commands/init/shouldUseBuiltinFuelCore.test.ts index 7449a4087af..04d44fe6629 100644 --- a/packages/fuels/src/cli/commands/init/shouldUseBuiltinFuelCore.test.ts +++ b/packages/fuels/src/cli/commands/init/shouldUseBuiltinFuelCore.test.ts @@ -4,16 +4,24 @@ import { mockLogger } from '../../../../test/utils/mockLogger'; import { shouldUseBuiltinForc } from './shouldUseBuiltinForc'; -jest.mock('@fuel-ts/versions/cli', () => ({ - __esModule: true, - ...jest.requireActual('@fuel-ts/versions/cli'), -})); +vi.mock('@fuel-ts/versions/cli', async () => { + const mod = await vi.importActual('@fuel-ts/versions/cli'); + return { + __esModule: true, + ...mod, + }; +}); +/** + * @group node + */ describe('shouldUseBuiltinForc', () => { - beforeEach(jest.restoreAllMocks); + beforeEach(() => { + vi.restoreAllMocks(); + }); function mockAll(returns: { getSystemForc: string | null }) { - const getSystemForc = jest + const getSystemForc = vi .spyOn(getSystemForcMod, 'getSystemForc') .mockReturnValue({ error: null, systemForcVersion: returns.getSystemForc }); diff --git a/packages/fuels/src/cli/commands/withConfig.test.ts b/packages/fuels/src/cli/commands/withConfig.test.ts index 87e63a5de0e..27f47028ac2 100644 --- a/packages/fuels/src/cli/commands/withConfig.test.ts +++ b/packages/fuels/src/cli/commands/withConfig.test.ts @@ -1,21 +1,28 @@ import { program } from 'commander'; -import { fuelsConfig } from '../../../test/fixtures/config/fuels.config'; +import { fuelsConfig } from '../../../test/fixtures/fuels.config'; import { mockLogger } from '../../../test/utils/mockLogger'; -import { resetDiskAndMocks } from '../../../test/utils/resetDiskAndMocks'; import * as loadConfigMod from '../config/loadConfig'; import type { FuelsConfig } from '../types'; import { Commands } from '../types'; import { withConfig } from './withConfig'; +/** + * @group node + */ describe('withConfig', () => { - beforeEach(mockLogger); - afterEach(resetDiskAndMocks); + beforeEach(() => { + mockLogger(); + }); + + beforeEach(() => { + vi.restoreAllMocks(); + }); function mockAll(params?: { shouldErrorOnDeploy?: boolean; shouldErrorOnLoadConfig?: boolean }) { - const onSuccess = jest.fn(); - const onFailure = jest.fn(); + const onSuccess = vi.fn(); + const onFailure = vi.fn(); const copyConfig: FuelsConfig = { ...structuredClone(fuelsConfig), @@ -29,14 +36,14 @@ describe('withConfig', () => { .command(Commands.deploy) .option('-p, --path ', 'Path to project root', configPath); - const loadConfig = jest.spyOn(loadConfigMod, 'loadConfig').mockImplementation((..._) => { + const loadConfig = vi.spyOn(loadConfigMod, 'loadConfig').mockImplementation(() => { if (params?.shouldErrorOnLoadConfig) { throw new Error('Something happened'); } return Promise.resolve(copyConfig); }); - const deploy = jest.fn(() => { + const deploy = vi.fn(() => { if (params?.shouldErrorOnDeploy) { throw new Error('Something happened'); } diff --git a/packages/fuels/src/cli/config/forcUtils.test.ts b/packages/fuels/src/cli/config/forcUtils.test.ts index a35b8d0c5f0..1cbc972d949 100644 --- a/packages/fuels/src/cli/config/forcUtils.test.ts +++ b/packages/fuels/src/cli/config/forcUtils.test.ts @@ -2,6 +2,9 @@ import { safeExec } from '@fuel-ts/errors/test-utils'; import { readForcToml } from './forcUtils'; +/** + * @group node + */ describe('forcUtils', () => { test('should throw if Toml file is not found', async () => { const tomlPath = '/non/existent/path'; diff --git a/packages/fuels/src/cli/config/loadConfig.test.ts b/packages/fuels/src/cli/config/loadConfig.test.ts index e7c358b25b9..6fbc5dc3e7b 100644 --- a/packages/fuels/src/cli/config/loadConfig.test.ts +++ b/packages/fuels/src/cli/config/loadConfig.test.ts @@ -1,14 +1,11 @@ import { safeExec } from '@fuel-ts/errors/test-utils'; import { readFileSync } from 'fs'; -import { resetDiskAndMocks } from '../../../test/utils/resetDiskAndMocks'; import { runInit, - fixturesDir, - fuelsConfigPath, - initFlagsUseBuiltinBinaries, - generatedDir, - initFlagsWorkspace, + bootstrapProject, + resetConfigAndMocks, + resetDiskAndMocks, } from '../../../test/utils/runCommands'; import * as shouldUseBuiltinForcMod from '../commands/init/shouldUseBuiltinForc'; import * as shouldUseBuiltinFuelCoreMod from '../commands/init/shouldUseBuiltinFuelCore'; @@ -16,8 +13,19 @@ import type { FuelsConfig } from '../types'; import { loadConfig } from './loadConfig'; +/** + * @group node + */ describe('loadConfig', () => { - beforeEach(resetDiskAndMocks); + const paths = bootstrapProject(__filename); + + afterEach(() => { + resetConfigAndMocks(paths.fuelsConfigPath); + }); + + afterAll(() => { + resetDiskAndMocks(paths.root); + }); test('should throw if config path is not found', async () => { const cwd = '/non/existent/path'; @@ -28,29 +36,31 @@ describe('loadConfig', () => { }); test(`should auto start fuel core explicitly`, async () => { - await runInit( - [initFlagsWorkspace, initFlagsUseBuiltinBinaries, '--auto-start-fuel-core'].flat() - ); - - const config = await loadConfig(fixturesDir); - const fuelsContents = readFileSync(fuelsConfigPath, 'utf-8'); - - expect(fuelsContents).toMatch(` autoStartFuelCore: true,`); // not comment + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + useBuiltinBinaries: true, + autoStartFuelCore: true, + }); + + const fuelsContents = readFileSync(paths.fuelsConfigPath, 'utf-8'); + const config = await loadConfig(paths.root); + + expect(fuelsContents).toMatch(` autoStartFuelCore: true,`); // not a comment expect(config.autoStartFuelCore).toEqual(true); }); test(`should resolve individual paths when not using workspaces`, async () => { - await runInit( - [ - initFlagsUseBuiltinBinaries, - ['--contracts', 'project/contracts/*'], - ['--scripts', 'project/scripts/*'], - ['--predicates', 'project/predicates/*'], - ['-o', generatedDir], - ].flat() - ); - - const config = await loadConfig(fixturesDir); + await runInit({ + root: paths.root, + output: paths.outputDir, + contracts: 'workspace/contracts/*', + scripts: 'workspace/scripts/*', + predicates: 'workspace/predicates/*', + }); + + const config = await loadConfig(paths.root); expect(config.contracts.length).toEqual(2); expect(config.scripts.length).toEqual(1); @@ -58,15 +68,13 @@ describe('loadConfig', () => { }); test(`should resolve only contracts`, async () => { - await runInit( - [ - initFlagsUseBuiltinBinaries, - ['--contracts', 'project/contracts/*'], - ['-o', generatedDir], - ].flat() - ); + await runInit({ + root: paths.root, + output: paths.outputDir, + contracts: 'workspace/contracts/*', + }); - const config = await loadConfig(fixturesDir); + const config = await loadConfig(paths.root); expect(config.contracts.length).toEqual(2); expect(config.scripts.length).toEqual(0); @@ -74,11 +82,13 @@ describe('loadConfig', () => { }); test(`should resolve only scripts`, async () => { - await runInit( - [initFlagsUseBuiltinBinaries, ['--scripts', 'project/scripts/*'], ['-o', generatedDir]].flat() - ); + await runInit({ + root: paths.root, + output: paths.outputDir, + scripts: 'workspace/scripts/*', + }); - const config = await loadConfig(fixturesDir); + const config = await loadConfig(paths.root); expect(config.contracts.length).toEqual(0); expect(config.scripts.length).toEqual(1); @@ -86,15 +96,13 @@ describe('loadConfig', () => { }); test(`should resolve only predicates`, async () => { - await runInit( - [ - initFlagsUseBuiltinBinaries, - ['--predicates', 'project/predicates/*'], - ['-o', generatedDir], - ].flat() - ); + await runInit({ + root: paths.root, + output: paths.outputDir, + predicates: 'workspace/predicates/*', + }); - const config = await loadConfig(fixturesDir); + const config = await loadConfig(paths.root); expect(config.contracts.length).toEqual(0); expect(config.scripts.length).toEqual(0); @@ -102,16 +110,14 @@ describe('loadConfig', () => { }); test(`should warn about misconfigured workspace`, async () => { - await runInit( - [ - initFlagsUseBuiltinBinaries, - // passing contract path in workspace config option - ['--workspace', 'project/contracts/bar'], - ['--output', generatedDir], - ].flat() - ); + await runInit({ + root: paths.root, + output: paths.outputDir, + // passing contract path in workspace config option + workspace: 'workspace/contracts/bar', + }); - const { error, result } = await safeExec>(() => loadConfig(fixturesDir)); + const { error, result } = await safeExec>(() => loadConfig(paths.root)); expect(result).not.toBeTruthy(); expect(error?.message).toMatch(/forc workspace not detected/i); @@ -119,17 +125,21 @@ describe('loadConfig', () => { }); test(`should smart-set built-in flags`, async () => { - await runInit(initFlagsWorkspace); + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + }); - const shouldUseBuiltinForc = jest + const shouldUseBuiltinForc = vi .spyOn(shouldUseBuiltinForcMod, 'shouldUseBuiltinForc') .mockReturnValue(false); - const shouldUseBuiltinFuelCore = jest + const shouldUseBuiltinFuelCore = vi .spyOn(shouldUseBuiltinFuelCoreMod, 'shouldUseBuiltinFuelCore') .mockReturnValue(true); - const config = await loadConfig(fixturesDir); + const config = await loadConfig(paths.root); expect(config.useBuiltinForc).toEqual(false); expect(config.useBuiltinFuelCore).toEqual(true); diff --git a/packages/fuels/src/cli/config/loadConfig.ts b/packages/fuels/src/cli/config/loadConfig.ts index d70aa1345b0..87153925049 100644 --- a/packages/fuels/src/cli/config/loadConfig.ts +++ b/packages/fuels/src/cli/config/loadConfig.ts @@ -1,10 +1,10 @@ +import { defaultConsensusKey } from '@fuel-ts/utils'; import { FUEL_NETWORK_URL } from '@fuel-ts/wallet/configs'; import { bundleRequire } from 'bundle-require'; import type { BuildOptions } from 'esbuild'; import JoyCon from 'joycon'; import { resolve, parse } from 'path'; -import { defaultConsensusKey } from '../commands/dev/defaultChainConfig'; import { shouldUseBuiltinForc } from '../commands/init/shouldUseBuiltinForc'; import { shouldUseBuiltinFuelCore } from '../commands/init/shouldUseBuiltinFuelCore'; import type { FuelsConfig, UserFuelsConfig } from '../types'; diff --git a/packages/fuels/src/cli/index.test.ts b/packages/fuels/src/cli/index.test.ts index dc6385cca43..ccf317bc557 100644 --- a/packages/fuels/src/cli/index.test.ts +++ b/packages/fuels/src/cli/index.test.ts @@ -1,4 +1,4 @@ -import { fuelsConfig } from '../../test/fixtures/config/fuels.config'; +import { fuelsConfig } from '../../test/fixtures/fuels.config'; import type { CommandEvent, @@ -11,6 +11,9 @@ import type { } from './index'; import { createConfig } from './index'; +/** + * @group node + */ describe('cli/index.ts', () => { test('should create config via cli index', () => { expect(createConfig(fuelsConfig)).toEqual(fuelsConfig); diff --git a/packages/fuels/src/cli/utils/findBinPath.ts b/packages/fuels/src/cli/utils/findBinPath.ts deleted file mode 100644 index e950e34e5fd..00000000000 --- a/packages/fuels/src/cli/utils/findBinPath.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { existsSync } from 'fs'; -import { join } from 'path'; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const npmWhich = require('npm-which')(__dirname); - -export function findBinPath(binCommandName: string) { - let binPath = npmWhich.sync(binCommandName); - - if (!existsSync(binPath)) { - // The user might be using bun, which has a different structure for binaries inside node_modules - binPath = join('node_modules', '.bin', binCommandName); - } - - return binPath; -} diff --git a/packages/fuels/src/cli/utils/logger.test.ts b/packages/fuels/src/cli/utils/logger.test.ts index 35553d587b4..e38ae184f92 100644 --- a/packages/fuels/src/cli/utils/logger.test.ts +++ b/packages/fuels/src/cli/utils/logger.test.ts @@ -2,6 +2,9 @@ import { resetDiskAndMocks } from '../../../test/utils/resetDiskAndMocks'; import * as loggerMod from './logger'; +/** + * @group node + */ describe('logger', () => { const { configureLogging, loggingConfig } = loggerMod; @@ -16,8 +19,8 @@ describe('logger', () => { afterAll(reset); function mockStdIO() { - const err = jest.spyOn(process.stderr, 'write').mockImplementation(); - const out = jest.spyOn(process.stdout, 'write').mockImplementation(); + const err = vi.spyOn(process.stderr, 'write').mockReturnValue(true); + const out = vi.spyOn(process.stdout, 'write').mockReturnValue(true); return { err, out }; } diff --git a/packages/fuels/src/index.test.ts b/packages/fuels/src/index.test.ts index 3847f6ca3de..ecde17b9279 100644 --- a/packages/fuels/src/index.test.ts +++ b/packages/fuels/src/index.test.ts @@ -1,5 +1,8 @@ import * as fuels from './index'; +/** + * @group node + */ describe('index.js', () => { test('should export everything', () => { expect(fuels.hexlify).toBeTruthy(); diff --git a/packages/fuels/src/run.ts b/packages/fuels/src/run.ts new file mode 100644 index 00000000000..e6bf972037d --- /dev/null +++ b/packages/fuels/src/run.ts @@ -0,0 +1,6 @@ +import { configureCli } from './cli'; + +export const run = async (argv: string[]) => { + const program = configureCli(); + return program.parseAsync(argv); +}; diff --git a/packages/fuels/test/features/build.test.ts b/packages/fuels/test/features/build.test.ts index 5ba49d4ed24..1d45516959d 100644 --- a/packages/fuels/test/features/build.test.ts +++ b/packages/fuels/test/features/build.test.ts @@ -2,85 +2,114 @@ import { existsSync } from 'fs'; import { join } from 'path'; import * as deployMod from '../../src/cli/commands/deploy/index'; -import { mockStartFuelCore } from '../utils/mockStartFuelCore'; -import { resetDiskAndMocks } from '../utils/resetDiskAndMocks'; +import { mockStartFuelCore } from '../utils/mockAutoStartFuelCore'; import { - buildFlagsDeploy, - contractsFooDir, - generatedDir, - initFlagsUseBuiltinBinaries, + bootstrapProject, + resetConfigAndMocks, + resetDiskAndMocks, runBuild, runInit, } from '../utils/runCommands'; -describe('build', () => { - beforeEach(resetDiskAndMocks); - afterEach(resetDiskAndMocks); - - function mockAll() { - const { startFuelCore, killChildProcess } = mockStartFuelCore(); - const deploy = jest.spyOn(deployMod, 'deploy').mockImplementation(); - - return { startFuelCore, killChildProcess, deploy }; - } - - it('should run `build` command', async () => { - const { startFuelCore, killChildProcess, deploy } = mockAll(); - - await runInit(); - await runBuild(); - - const files = [ - 'predicates/factories/PredicateTrueAbi__factory.ts', - 'predicates/index.ts', - 'contracts/BarFooAbi.d.ts', - 'contracts/BarFooAbi.hex.ts', - 'contracts/FooBarAbi.hex.ts', - 'contracts/FooBarAbi.d.ts', - 'contracts/factories/FooBarAbi__factory.ts', - 'contracts/factories/BarFooAbi__factory.ts', - 'contracts/index.ts', - 'scripts/factories/ScriptTrueAbi__factory.ts', - 'scripts/index.ts', - 'index.ts', - ].map((f) => join(__dirname, '..', 'fixtures', 'generated', f)); - - files.forEach((file) => expect(existsSync(file)).toBeTruthy()); - - expect(startFuelCore).toHaveBeenCalledTimes(0); - expect(deploy).toHaveBeenCalledTimes(0); - expect(killChildProcess).toHaveBeenCalledTimes(0); - }); - - it('should run `build` command with contracts-only', async () => { - const { startFuelCore, killChildProcess, deploy } = mockAll(); - - await runInit([initFlagsUseBuiltinBinaries, '-c', contractsFooDir, '-o', generatedDir].flat()); - await runBuild(); - - const files = [ - 'contracts/FooBarAbi.hex.ts', - 'contracts/FooBarAbi.d.ts', - 'contracts/factories/FooBarAbi__factory.ts', - 'contracts/index.ts', - 'index.ts', - ].map((f) => join(__dirname, '..', 'fixtures', 'generated', f)); - - files.forEach((file) => expect(existsSync(file)).toBeTruthy()); - - expect(startFuelCore).toHaveBeenCalledTimes(0); - expect(deploy).toHaveBeenCalledTimes(0); - expect(killChildProcess).toHaveBeenCalledTimes(0); - }); - - it('should run `build` command with `--deploy` flag', async () => { - const { startFuelCore, killChildProcess, deploy } = mockAll(); - - await runInit(); - await runBuild([buildFlagsDeploy]); - - expect(startFuelCore).toHaveBeenCalledTimes(1); - expect(deploy).toHaveBeenCalledTimes(1); - expect(killChildProcess).toHaveBeenCalledTimes(1); - }); -}); +/** + * @group node + */ +describe( + 'build', + () => { + const paths = bootstrapProject(__filename); + + afterEach(() => { + resetConfigAndMocks(paths.fuelsConfigPath); + }); + + afterAll(() => { + resetDiskAndMocks(paths.root); + }); + + function mockAll() { + const { autoStartFuelCore, killChildProcess } = mockStartFuelCore(); + const deploy = vi.spyOn(deployMod, 'deploy').mockResolvedValue([]); + + return { autoStartFuelCore, killChildProcess, deploy }; + } + + it('should run `build` command', async () => { + const { autoStartFuelCore, killChildProcess, deploy } = mockAll(); + + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + }); + + await runBuild({ root: paths.root }); + + const files = [ + 'predicates/factories/PredicateTrueAbi__factory.ts', + 'predicates/index.ts', + 'contracts/BarFooAbi.d.ts', + 'contracts/BarFooAbi.hex.ts', + 'contracts/FooBarAbi.hex.ts', + 'contracts/FooBarAbi.d.ts', + 'contracts/factories/FooBarAbi__factory.ts', + 'contracts/factories/BarFooAbi__factory.ts', + 'contracts/index.ts', + 'scripts/factories/ScriptTrueAbi__factory.ts', + 'scripts/index.ts', + 'index.ts', + ].map((f) => join(paths.outputDir, f)); + + files.forEach((file) => { + expect(existsSync(file)).toBeTruthy(); + }); + + expect(autoStartFuelCore).toHaveBeenCalledTimes(0); + expect(deploy).toHaveBeenCalledTimes(0); + expect(killChildProcess).toHaveBeenCalledTimes(0); + }); + + it('should run `build` command with contracts-only', async () => { + const { autoStartFuelCore, killChildProcess, deploy } = mockAll(); + + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + }); + + await runBuild({ root: paths.root }); + + const files = [ + 'contracts/FooBarAbi.hex.ts', + 'contracts/FooBarAbi.d.ts', + 'contracts/factories/FooBarAbi__factory.ts', + 'contracts/index.ts', + 'index.ts', + ].map((f) => join(paths.outputDir, f)); + + files.forEach((file) => expect(existsSync(file)).toBeTruthy()); + + expect(autoStartFuelCore).toHaveBeenCalledTimes(0); + expect(deploy).toHaveBeenCalledTimes(0); + expect(killChildProcess).toHaveBeenCalledTimes(0); + }); + + it('should run `build` command with `--deploy` flag', async () => { + const { autoStartFuelCore, killChildProcess, deploy } = mockAll(); + + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + }); + + await runBuild({ root: paths.root, deploy: true }); + + expect(autoStartFuelCore).toHaveBeenCalledTimes(1); + expect(deploy).toHaveBeenCalledTimes(1); + expect(killChildProcess).toHaveBeenCalledTimes(1); + }); + }, + { timeout: 10000 } +); diff --git a/packages/fuels/test/features/deploy.test.ts b/packages/fuels/test/features/deploy.test.ts index aa739e99f34..4d4bc74af8d 100644 --- a/packages/fuels/test/features/deploy.test.ts +++ b/packages/fuels/test/features/deploy.test.ts @@ -1,21 +1,46 @@ import { existsSync, readFileSync } from 'fs'; import { resetDiskAndMocks } from '../utils/resetDiskAndMocks'; -import { contractsJsonPath, runBuild, runDeploy, runInit } from '../utils/runCommands'; +import { + bootstrapProject, + resetConfigAndMocks, + runBuild, + runDeploy, + runInit, +} from '../utils/runCommands'; -describe('deploy', () => { - beforeEach(resetDiskAndMocks); - afterEach(resetDiskAndMocks); +/** + * @group node + */ +describe( + 'deploy', + () => { + const paths = bootstrapProject(__filename); - it('should run `deploy` command', async () => { - await runInit(); - await runBuild(); - await runDeploy(); + afterEach(() => { + resetConfigAndMocks(paths.fuelsConfigPath); + }); - expect(existsSync(contractsJsonPath)).toBeTruthy(); + afterAll(() => { + resetDiskAndMocks(paths.root); + }); - const fuelsContents = JSON.parse(readFileSync(contractsJsonPath, 'utf-8')); - expect(fuelsContents.barFoo).toMatch(/0x/); - expect(fuelsContents.fooBar).toMatch(/0x/); - }); -}); + it('should run `deploy` command', async () => { + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + }); + + await runBuild({ root: paths.root }); + await runDeploy({ root: paths.root }); + + expect(existsSync(paths.contractsJsonPath)).toBeTruthy(); + + const fuelsContents = JSON.parse(readFileSync(paths.contractsJsonPath, 'utf-8')); + expect(fuelsContents.barFoo).toMatch(/0x/); + expect(fuelsContents.fooBar).toMatch(/0x/); + }); + }, + { timeout: 15000 } +); diff --git a/packages/fuels/test/features/dev.test.ts b/packages/fuels/test/features/dev.test.ts index f2a14007fd3..bb4daaa47a9 100644 --- a/packages/fuels/test/features/dev.test.ts +++ b/packages/fuels/test/features/dev.test.ts @@ -2,43 +2,61 @@ import * as chokidar from 'chokidar'; import * as buildMod from '../../src/cli/commands/build/index'; import * as deployMod from '../../src/cli/commands/deploy/index'; +import { mockStartFuelCore } from '../utils/mockAutoStartFuelCore'; import { mockLogger } from '../utils/mockLogger'; -import { mockStartFuelCore } from '../utils/mockStartFuelCore'; import { resetDiskAndMocks } from '../utils/resetDiskAndMocks'; -import { runInit, runDev } from '../utils/runCommands'; +import { runInit, runDev, bootstrapProject, resetConfigAndMocks } from '../utils/runCommands'; -jest.mock('chokidar', () => ({ - __esModule: true, - ...jest.requireActual('chokidar'), -})); +vi.mock('chokidar', async () => { + const mod = await vi.importActual('chokidar'); + return { + __esModule: true, + ...mod, + }; +}); +/** + * @group node + */ describe('dev', () => { - beforeEach(mockLogger); - afterEach(resetDiskAndMocks); + const paths = bootstrapProject(__filename); + + afterEach(() => { + resetConfigAndMocks(paths.fuelsConfigPath); + }); + + afterAll(() => { + resetDiskAndMocks(paths.root); + }); function mockAll() { mockLogger(); - const { startFuelCore, killChildProcess } = mockStartFuelCore(); + const { autoStartFuelCore, killChildProcess } = mockStartFuelCore(); - const build = jest.spyOn(buildMod, 'build').mockImplementation(); - const deploy = jest.spyOn(deployMod, 'deploy').mockImplementation(); + const build = vi.spyOn(buildMod, 'build').mockReturnValue(Promise.resolve()); + const deploy = vi.spyOn(deployMod, 'deploy').mockReturnValue(Promise.resolve([])); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const on: any = jest.fn(() => ({ on })); + const on: any = vi.fn(() => ({ on })); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const watch = jest.spyOn(chokidar, 'watch').mockReturnValue({ on } as any); + const watch = vi.spyOn(chokidar, 'watch').mockReturnValue({ on } as any); - return { startFuelCore, killChildProcess, build, deploy, on, watch }; + return { autoStartFuelCore, killChildProcess, build, deploy, on, watch }; } it('should run `dev` command', async () => { - const { startFuelCore, killChildProcess, build, deploy, on, watch } = mockAll(); + const { autoStartFuelCore, killChildProcess, build, deploy, on, watch } = mockAll(); + + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + }); - await runInit(); - await runDev(); + await runDev({ root: paths.root }); - expect(startFuelCore).toHaveBeenCalledTimes(1); + expect(autoStartFuelCore).toHaveBeenCalledTimes(1); expect(killChildProcess).toHaveBeenCalledTimes(0); expect(build).toHaveBeenCalledTimes(1); expect(deploy).toHaveBeenCalledTimes(1); diff --git a/packages/fuels/test/features/init.test.ts b/packages/fuels/test/features/init.test.ts index 1db94d6d7f8..3f111cd75d5 100644 --- a/packages/fuels/test/features/init.test.ts +++ b/packages/fuels/test/features/init.test.ts @@ -1,36 +1,61 @@ import chalk from 'chalk'; import { existsSync, readFileSync } from 'fs'; +import { Commands } from '../../src'; import { mockLogger } from '../utils/mockLogger'; -import { resetDiskAndMocks } from '../utils/resetDiskAndMocks'; import { - fuelsConfigPath, - generatedDir, - initFlagsAutoStartFuelCore, - initFlagsWorkspace, + bootstrapProject, + runCommand, runInit, + resetDiskAndMocks, + resetConfigAndMocks, } from '../utils/runCommands'; +/** + * @group node + */ describe('init', () => { - beforeEach(mockLogger); - afterEach(resetDiskAndMocks); + const paths = bootstrapProject(__filename); + + beforeEach(() => { + mockLogger(); + }); + + afterEach(() => { + resetConfigAndMocks(paths.fuelsConfigPath); + }); + + afterAll(() => { + resetDiskAndMocks(paths.root); + }); it('should run `init` command', async () => { - await runInit([initFlagsWorkspace, initFlagsAutoStartFuelCore].flat()); - expect(existsSync(fuelsConfigPath)).toBeTruthy(); - const fuelsContents = readFileSync(fuelsConfigPath, 'utf-8'); - expect(fuelsContents).toMatch(`workspace: './project',`); - expect(fuelsContents).toMatch(`output: './generated',`); + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + }); + + expect(existsSync(paths.fuelsConfigPath)).toBeTruthy(); + const fuelsContents = readFileSync(paths.fuelsConfigPath, 'utf-8'); + expect(fuelsContents).toMatch(`workspace: './workspace',`); + expect(fuelsContents).toMatch(`output: './output',`); expect(fuelsContents).not.toMatch(`useBuiltinForc: true,`); expect(fuelsContents).not.toMatch(`useBuiltinFuelCore: true,`); }); it('should run `init` command using built-in flags', async () => { - await runInit(); - expect(existsSync(fuelsConfigPath)).toBeTruthy(); - const fuelsContents = readFileSync(fuelsConfigPath, 'utf-8'); - expect(fuelsContents).toMatch(`workspace: './project',`); - expect(fuelsContents).toMatch(`output: './generated',`); + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + useBuiltinBinaries: true, + }); + + expect(existsSync(paths.fuelsConfigPath)).toBeTruthy(); + const fuelsContents = readFileSync(paths.fuelsConfigPath, 'utf-8'); + expect(fuelsContents).toMatch(`workspace: './workspace',`); + expect(fuelsContents).toMatch(`output: './output',`); expect(fuelsContents).toMatch(`useBuiltinForc: true,`); expect(fuelsContents).toMatch(`useBuiltinFuelCore: true,`); }); @@ -38,20 +63,31 @@ describe('init', () => { it('should run `init` command and throw for existent config file', async () => { const { error } = mockLogger(); - await runInit(); + // first time, all good + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + }); + expect(error).toHaveBeenCalledTimes(0); // second time will trigger error - await runInit(); + await runInit({ + root: paths.root, + workspace: paths.workspaceDir, + output: paths.outputDir, + }); + expect(error).toHaveBeenCalledTimes(1); expect(chalk.reset(error.mock.calls[0][0])).toMatch(/Config file exists, aborting/); }); it('should error if no inputs/workspace is supplied', async () => { - const write = jest.spyOn(process.stdout, 'write').mockImplementation(); - const exit = jest.spyOn(process, 'exit').mockImplementation(); + const write = vi.spyOn(process.stdout, 'write').mockReturnValue(true); + const exit = vi.spyOn(process, 'exit').mockResolvedValue({} as never); - await runInit(['-o', generatedDir].flat()); + await runCommand(Commands.init, ['-p', paths.root, '-o', paths.outputDir]); expect(exit).toHaveBeenCalledTimes(1); expect(exit).toHaveBeenCalledWith(1); diff --git a/packages/fuels/test/fixtures/config/fuels.config.ts b/packages/fuels/test/fixtures/fuels.config.ts similarity index 84% rename from packages/fuels/test/fixtures/config/fuels.config.ts rename to packages/fuels/test/fixtures/fuels.config.ts index b974f2b400f..edc85516c41 100644 --- a/packages/fuels/test/fixtures/config/fuels.config.ts +++ b/packages/fuels/test/fixtures/fuels.config.ts @@ -1,9 +1,9 @@ import { FUEL_NETWORK_URL } from '@fuel-ts/wallet/configs'; import { join } from 'path'; -import type { FuelsConfig } from '../../../src'; +import type { FuelsConfig } from '../../src'; -const projectPath = join(__dirname, '..', 'project'); +const projectPath = join(__dirname, 'workspace'); const contractsDir = join(projectPath, 'contracts'); const scriptsDir = join(projectPath, 'scripts'); @@ -15,7 +15,7 @@ export const fuelsConfig: FuelsConfig = { contracts: [join(contractsDir, 'foo'), join(contractsDir, 'bar')], scripts: [join(scriptsDir, 'script')], predicates: [join(predicatesDir, 'predicate')], - output: '/generated-types', + output: '/output', deployConfig: { gasPrice: 5, }, diff --git a/packages/fuels/test/fixtures/project/.fuels/chainConfig.json b/packages/fuels/test/fixtures/project/.fuels/chainConfig.json new file mode 100644 index 00000000000..82345ea1630 --- /dev/null +++ b/packages/fuels/test/fixtures/project/.fuels/chainConfig.json @@ -0,0 +1,515 @@ +{ + "chain_name": "local_testnet", + "block_gas_limit": 5000000000, + "initial_state": { + "coins": [ + { + "owner": "0x94ffcc53b892684acefaebc8a3d4a595e528a8cf664eeb3ef36f1020b0809d0d", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + } + ], + "messages": [ + { + "sender": "0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f", + "recipient": "0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba", + "nonce": "0101010101010101010101010101010101010101010101010101010101010101", + "amount": "0x000000000000FFFF", + "data": "", + "da_height": "0x00" + }, + { + "sender": "0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba", + "recipient": "0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f", + "nonce": "0e1ef2963832068b0e1ef2963832068b0e1ef2963832068b0e1ef2963832068b", + "amount": "0xb04f3c08f59b309e", + "data": "", + "da_height": "0x00" + } + ] + }, + "consensus_parameters": { + "tx_params": { + "max_inputs": 255, + "max_outputs": 255, + "max_witnesses": 255, + "max_gas_per_tx": 10000000, + "max_size": 17825792 + }, + "predicate_params": { + "max_predicate_length": 1048576, + "max_predicate_data_length": 1048576, + "max_gas_per_predicate": 10000000, + "max_message_data_length": 1048576 + }, + "script_params": { + "max_script_length": 1048576, + "max_script_data_length": 1048576 + }, + "contract_params": { + "contract_max_size": 16777216, + "max_storage_slots": 255 + }, + "fee_params": { + "gas_price_factor": 92, + "gas_per_byte": 4 + } + }, + "gas_costs": { + "add": 1, + "addi": 1, + "aloc": 1, + "and": 1, + "andi": 1, + "bal": 13, + "bhei": 1, + "bhsh": 1, + "burn": 132, + "cb": 1, + "cfei": 1, + "cfsi": 1, + "croo": 16, + "div": 1, + "divi": 1, + "ecr1": 3000, + "eck1": 951, + "ed19": 3000, + "eq": 1, + "exp": 1, + "expi": 1, + "flag": 1, + "gm": 1, + "gt": 1, + "gtf": 1, + "ji": 1, + "jmp": 1, + "jne": 1, + "jnei": 1, + "jnzi": 1, + "jmpf": 1, + "jmpb": 1, + "jnzf": 1, + "jnzb": 1, + "jnef": 1, + "jneb": 1, + "lb": 1, + "log": 9, + "lt": 1, + "lw": 1, + "mint": 135, + "mlog": 1, + "modOp": 1, + "modi": 1, + "moveOp": 1, + "movi": 1, + "mroo": 2, + "mul": 1, + "muli": 1, + "mldv": 1, + "noop": 1, + "not": 1, + "or": 1, + "ori": 1, + "poph": 2, + "popl": 2, + "pshh": 2, + "pshl": 2, + "ret": 13, + "rvrt": 13, + "sb": 1, + "sll": 1, + "slli": 1, + "srl": 1, + "srli": 1, + "srw": 12, + "sub": 1, + "subi": 1, + "sw": 1, + "sww": 67, + "time": 1, + "tr": 105, + "tro": 60, + "wdcm": 1, + "wqcm": 1, + "wdop": 1, + "wqop": 1, + "wdml": 1, + "wqml": 1, + "wddv": 1, + "wqdv": 2, + "wdmd": 3, + "wqmd": 4, + "wdam": 2, + "wqam": 3, + "wdmm": 3, + "wqmm": 3, + "xor": 1, + "xori": 1, + "call": { + "LightOperation": { + "base": 144, + "units_per_gas": 214 + } + }, + "ccp": { + "LightOperation": { + "base": 15, + "units_per_gas": 103 + } + }, + "csiz": { + "LightOperation": { + "base": 17, + "units_per_gas": 790 + } + }, + "k256": { + "LightOperation": { + "base": 11, + "units_per_gas": 214 + } + }, + "ldc": { + "LightOperation": { + "base": 15, + "units_per_gas": 272 + } + }, + "logd": { + "LightOperation": { + "base": 26, + "units_per_gas": 64 + } + }, + "mcl": { + "LightOperation": { + "base": 1, + "units_per_gas": 3333 + } + }, + "mcli": { + "LightOperation": { + "base": 1, + "units_per_gas": 3333 + } + }, + "mcp": { + "LightOperation": { + "base": 1, + "units_per_gas": 2000 + } + }, + "mcpi": { + "LightOperation": { + "base": 3, + "units_per_gas": 2000 + } + }, + "meq": { + "LightOperation": { + "base": 1, + "units_per_gas": 2500 + } + }, + "retd": { + "LightOperation": { + "base": 29, + "units_per_gas": 62 + } + }, + "s256": { + "LightOperation": { + "base": 2, + "units_per_gas": 214 + } + }, + "scwq": { + "LightOperation": { + "base": 13, + "units_per_gas": 5 + } + }, + "smo": { + "LightOperation": { + "base": 209, + "units_per_gas": 55 + } + }, + "srwq": { + "LightOperation": { + "base": 47, + "units_per_gas": 5 + } + }, + "swwq": { + "LightOperation": { + "base": 44, + "units_per_gas": 5 + } + }, + "contract_root": { + "LightOperation": { + "base": 75, + "units_per_gas": 1 + } + }, + "state_root": { + "LightOperation": { + "base": 412, + "units_per_gas": 1 + } + }, + "vm_initialization": { + "HeavyOperation": { + "base": 2000, + "gas_per_unit": 0 + } + }, + "new_storage_per_byte": 1 + }, + "consensus": { + "PoA": { + "signing_key": "0x94ffcc53b892684acefaebc8a3d4a595e528a8cf664eeb3ef36f1020b0809d0d" + } + } +} \ No newline at end of file diff --git a/packages/fuels/test/fixtures/project/Forc.toml b/packages/fuels/test/fixtures/workspace/Forc.toml similarity index 100% rename from packages/fuels/test/fixtures/project/Forc.toml rename to packages/fuels/test/fixtures/workspace/Forc.toml diff --git a/packages/fuels/test/fixtures/project/contracts/bar/Forc.toml b/packages/fuels/test/fixtures/workspace/contracts/bar/Forc.toml similarity index 100% rename from packages/fuels/test/fixtures/project/contracts/bar/Forc.toml rename to packages/fuels/test/fixtures/workspace/contracts/bar/Forc.toml diff --git a/packages/fuels/test/fixtures/project/contracts/bar/src/main.sw b/packages/fuels/test/fixtures/workspace/contracts/bar/src/main.sw similarity index 100% rename from packages/fuels/test/fixtures/project/contracts/bar/src/main.sw rename to packages/fuels/test/fixtures/workspace/contracts/bar/src/main.sw diff --git a/packages/fuels/test/fixtures/project/contracts/foo/Forc.toml b/packages/fuels/test/fixtures/workspace/contracts/foo/Forc.toml similarity index 100% rename from packages/fuels/test/fixtures/project/contracts/foo/Forc.toml rename to packages/fuels/test/fixtures/workspace/contracts/foo/Forc.toml diff --git a/packages/fuels/test/fixtures/project/contracts/foo/src/main.sw b/packages/fuels/test/fixtures/workspace/contracts/foo/src/main.sw similarity index 100% rename from packages/fuels/test/fixtures/project/contracts/foo/src/main.sw rename to packages/fuels/test/fixtures/workspace/contracts/foo/src/main.sw diff --git a/packages/fuels/test/fixtures/project/predicates/predicate/Forc.toml b/packages/fuels/test/fixtures/workspace/predicates/predicate/Forc.toml similarity index 100% rename from packages/fuels/test/fixtures/project/predicates/predicate/Forc.toml rename to packages/fuels/test/fixtures/workspace/predicates/predicate/Forc.toml diff --git a/packages/fuels/test/fixtures/workspace/predicates/predicate/src/main.sw b/packages/fuels/test/fixtures/workspace/predicates/predicate/src/main.sw new file mode 100644 index 00000000000..084dacab2c6 --- /dev/null +++ b/packages/fuels/test/fixtures/workspace/predicates/predicate/src/main.sw @@ -0,0 +1,5 @@ +predicate; + +fn main() -> bool { + true +} diff --git a/packages/fuels/test/fixtures/project/scripts/script/Forc.toml b/packages/fuels/test/fixtures/workspace/scripts/script/Forc.toml similarity index 100% rename from packages/fuels/test/fixtures/project/scripts/script/Forc.toml rename to packages/fuels/test/fixtures/workspace/scripts/script/Forc.toml diff --git a/packages/fuels/test/fixtures/project/scripts/script/src/main.sw b/packages/fuels/test/fixtures/workspace/scripts/script/src/main.sw similarity index 100% rename from packages/fuels/test/fixtures/project/scripts/script/src/main.sw rename to packages/fuels/test/fixtures/workspace/scripts/script/src/main.sw diff --git a/packages/fuels/test/utils/mockAutoStartFuelCore.ts b/packages/fuels/test/utils/mockAutoStartFuelCore.ts new file mode 100644 index 00000000000..bbca5f69da9 --- /dev/null +++ b/packages/fuels/test/utils/mockAutoStartFuelCore.ts @@ -0,0 +1,26 @@ +import type { SpyInstance } from 'vitest'; + +import * as autoStartFuelCoreMod from '../../src/cli/commands/dev/autoStartFuelCore'; + +export const mockStartFuelCore = (): { + killChildProcess: SpyInstance; + autoStartFuelCore: SpyInstance; + fuelCore: autoStartFuelCoreMod.FuelCoreNode; +} => { + const killChildProcess = vi.fn(); + + const fuelCore: autoStartFuelCoreMod.FuelCoreNode = { + bindIp: '0.0.0.0', + accessIp: '127.0.0.1', + port: 4000, + providerUrl: `http://127.0.0.1:4000/graphql`, + killChildProcess, + chainConfigPath: '/some/path/chainConfig.json', + }; + + const autoStartFuelCore = vi + .spyOn(autoStartFuelCoreMod, 'autoStartFuelCore') + .mockResolvedValue(fuelCore); + + return { autoStartFuelCore, killChildProcess, fuelCore }; +}; diff --git a/packages/fuels/test/utils/mockLogger.ts b/packages/fuels/test/utils/mockLogger.ts index 7a995d0ab78..0ff436d0b36 100644 --- a/packages/fuels/test/utils/mockLogger.ts +++ b/packages/fuels/test/utils/mockLogger.ts @@ -1,10 +1,10 @@ import * as logger from '../../src/cli/utils/logger'; export function mockLogger() { - const error = jest.spyOn(logger, 'error').mockImplementation(); - const warn = jest.spyOn(logger, 'warn').mockImplementation(); - const log = jest.spyOn(logger, 'log').mockImplementation(); - const debug = jest.spyOn(logger, 'debug').mockImplementation(); + const error = vi.spyOn(logger, 'error').mockReturnValue(); + const warn = vi.spyOn(logger, 'warn').mockReturnValue(); + const log = vi.spyOn(logger, 'log').mockReturnValue(); + const debug = vi.spyOn(logger, 'debug').mockReturnValue(); return { error, warn, diff --git a/packages/fuels/test/utils/mockStartFuelCore.ts b/packages/fuels/test/utils/mockStartFuelCore.ts deleted file mode 100644 index b713d6c0187..00000000000 --- a/packages/fuels/test/utils/mockStartFuelCore.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { FuelsConfig } from '../../src'; -import * as startFuelCoreMod from '../../src/cli/commands/dev/startFuelCore'; - -export const mockStartFuelCore = () => { - const killChildProcess = jest.fn(); - - const startFuelCore = jest - .spyOn(startFuelCoreMod, 'startFuelCore') - .mockImplementation((_config: FuelsConfig) => - Promise.resolve({ - bindIp: '0.0.0.0', - accessIp: '127.0.0.1', - port: 4000, - providerUrl: `http://127.0.0.1:4000/graphql`, - killChildProcess, - chainConfig: '/some/path/chainConfig.json', - }) - ); - - return { startFuelCore, killChildProcess }; -}; diff --git a/packages/fuels/test/utils/resetDiskAndMocks.ts b/packages/fuels/test/utils/resetDiskAndMocks.ts index d46f6d943ac..fddd7eabab2 100644 --- a/packages/fuels/test/utils/resetDiskAndMocks.ts +++ b/packages/fuels/test/utils/resetDiskAndMocks.ts @@ -1,20 +1,12 @@ import { existsSync, rmSync } from 'fs'; -import { fuelsConfigPath, generatedDir } from './runCommands'; - /** * Cleanup routine */ -export function deleteGeneratedFiles() { - if (existsSync(fuelsConfigPath)) { - rmSync(fuelsConfigPath); - } - if (existsSync(generatedDir)) { - rmSync(generatedDir, { recursive: true }); - } -} -export function resetDiskAndMocks() { - deleteGeneratedFiles(); - jest.restoreAllMocks(); +export function resetDiskAndMocks(dirPath: string) { + if (existsSync(dirPath)) { + rmSync(dirPath, { recursive: true }); + } + vi.restoreAllMocks(); } diff --git a/packages/fuels/test/utils/runCommands.ts b/packages/fuels/test/utils/runCommands.ts index cddc499981c..37b3337bd56 100644 --- a/packages/fuels/test/utils/runCommands.ts +++ b/packages/fuels/test/utils/runCommands.ts @@ -1,69 +1,152 @@ -import { join } from 'path'; +import { cpSync, existsSync, mkdirSync, rmSync } from 'fs'; +import { join, basename } from 'path'; import { Commands } from '../../src'; -import { run } from '../../src/cli'; +import { run } from '../../src/run'; /** - * Paths + * Path and Directory utils */ -export const fixturesDir = join(__dirname, '..', 'fixtures'); +export const testDir = join(__dirname, '..'); +export const fixturesDir = join(testDir, 'fixtures'); +export const sampleWorkspaceDir = join(fixturesDir, 'workspace'); +export const sampleConfigPath = join(fixturesDir, 'fuels.config.ts'); -export const workspaceDir = join(fixturesDir, 'project'); +export type Paths = { + root: string; + workspaceDir: string; + contractsDir: string; + contractsFooDir: string; + scriptsDir: string; + predicateDir: string; + fooContractMainPath: string; + fuelsConfigPath: string; + outputDir: string; + contractsJsonPath: string; + fooContractFactoryPath: string; +}; -export const contractsDir = join(workspaceDir, 'contracts'); -export const contractsFooDir = join(contractsDir, 'foo'); -export const scriptsDir = join(workspaceDir, 'scripts'); -export const predicateDir = join(workspaceDir, 'predicate'); +export function bootstrapProject(testFilepath: string) { + const testFilename = basename(testFilepath.replace(/\./g, '-')); + const uniqueName = `__temp__${testFilename}_${new Date().getTime()}`; -export const fooContractMainPath = join(contractsDir, 'foo', 'src', 'main.sw'); + const root = join(testDir, uniqueName); + const workspaceDir = join(root, 'workspace'); + const fuelsConfigPath = join(root, 'fuels.config.ts'); -export const fuelsConfigPath = join(fixturesDir, 'fuels.config.ts'); -export const generatedDir = join(fixturesDir, 'generated'); -export const contractsJsonPath = join(generatedDir, 'contract-ids.json'); -export const fooContractFactoryPath = join( - generatedDir, - 'contracts', - 'factories', - 'FooBarAbi__factory.ts' -); + mkdirSync(workspaceDir, { recursive: true }); + + cpSync(sampleWorkspaceDir, workspaceDir, { recursive: true }); + + const contractsDir = join(workspaceDir, 'contracts'); + const contractsFooDir = join(contractsDir, 'foo'); + const scriptsDir = join(workspaceDir, 'scripts'); + const predicateDir = join(workspaceDir, 'predicate'); + const fooContractMainPath = join(contractsDir, 'foo', 'src', 'main.sw'); + + const outputDir = join(root, 'output'); + const contractsJsonPath = join(outputDir, 'contract-ids.json'); + const fooContractFactoryPath = join(outputDir, 'contracts', 'factories', 'FooBarAbi__factory.ts'); + + return { + root, + workspaceDir, + contractsDir, + contractsFooDir, + scriptsDir, + predicateDir, + fooContractMainPath, + fuelsConfigPath, + outputDir, + contractsJsonPath, + fooContractFactoryPath, + }; +} /** - * Helper + * Command callers */ export async function runCommand(commandName: string, params: string[] = []) { // always `--silent` to avoid polluting tests output - const argv = ['node', 'fuels', '--silent', commandName, '-p', fixturesDir].concat(params); + const argv = ['node', 'fuels', '--silent', commandName].concat(params); return { argv, command: await run(argv) }; } -/** - * Bundled flag combos - */ -export const initFlagsWorkspace = ['-w', workspaceDir, '-o', generatedDir]; -export const initFlagsUseBuiltinBinaries = ['--use-builtin-forc', '--use-builtin-fuel-core']; -export const initFlagsAutoStartFuelCore = '--auto-start-fuel-core'; -export const initFlagsDefault = [ - initFlagsWorkspace, - initFlagsUseBuiltinBinaries, - initFlagsAutoStartFuelCore, -]; -export const buildFlagsDeploy = '--deploy'; +export type BaseParams = { + root: string; +}; + +export type InitParams = BaseParams & { + workspace?: string; + contracts?: string; + scripts?: string; + predicates?: string; + output: string; + useBuiltinBinaries?: boolean; + autoStartFuelCore?: boolean; + build?: boolean; +}; + +export type BuildParams = BaseParams & { + deploy?: boolean; +}; + +export async function runInit(params: InitParams) { + const { + autoStartFuelCore, + contracts, + output, + predicates, + root, + scripts, + useBuiltinBinaries, + workspace, + } = params; + + const flag = (flags: (string | undefined)[], value?: string | boolean): string[] => + value ? (flags as string[]) : []; + + const flags = [ + flag(['-p', root], root), + flag(['-o', output], output), + flag(['-w', workspace], workspace), + flag(['--contracts', contracts], contracts), + flag(['--scripts', scripts], scripts), + flag(['--predicates', predicates], predicates), + flag(['--use-builtin-forc', '--use-builtin-fuel-core'], useBuiltinBinaries), + flag(['--auto-start-fuel-core'], autoStartFuelCore), + ].flat(); -/** - * Command callers - */ -export async function runInit(flags: string[] = initFlagsDefault.flat()) { return runCommand(Commands.init, flags); } -export async function runBuild(flags: string[] = []) { +export async function runBuild(params: BuildParams) { + const { root, deploy } = params; + const flags = [['-p', root], deploy ? ['--deploy'] : []].flat(); return runCommand(Commands.build, flags); } -export async function runDeploy() { - return runCommand(Commands.deploy); +export async function runDeploy(params: BaseParams) { + return runCommand(Commands.deploy, ['-p', params.root]); +} + +export async function runDev(params: BaseParams) { + return runCommand(Commands.dev, ['-p', params.root]); +} + +/** + * Cleanup + */ +export function resetConfigAndMocks(configPath: string) { + if (existsSync(configPath)) { + rmSync(configPath); + } + vi.restoreAllMocks(); } -export async function runDev() { - return runCommand(Commands.dev); +export function resetDiskAndMocks(dirPath: string) { + if (existsSync(dirPath)) { + rmSync(dirPath, { recursive: true }); + } + vi.restoreAllMocks(); } diff --git a/packages/fuels/tsdoc.json b/packages/fuels/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/fuels/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/hasher/README.md b/packages/hasher/README.md index 2a2292a8524..a6c920bbde6 100644 --- a/packages/hasher/README.md +++ b/packages/hasher/README.md @@ -18,7 +18,7 @@ This module contains several common hashing utilities. -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/hasher/package.json b/packages/hasher/package.json index 7d0d6638c9e..52abc4e962f 100644 --- a/packages/hasher/package.json +++ b/packages/hasher/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/hasher/src/hasher.test.ts b/packages/hasher/src/hasher.test.ts index 724400a043c..44e21432204 100644 --- a/packages/hasher/src/hasher.test.ts +++ b/packages/hasher/src/hasher.test.ts @@ -1,5 +1,9 @@ -import { hashMessage, hash } from './hasher'; +import { hashMessage, hash, uint64ToBytesBE } from './hasher'; +/** + * @group node + * @group browser + */ describe('Hasher', () => { it('Hash message', () => { const message = 'my message'; @@ -12,4 +16,10 @@ describe('Hasher', () => { '0xf5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b' ); }); + + it('Convert uint64 to bytes in big-endian order', () => { + const value = 1234567890; + const expectedBytes = new Uint8Array([0, 0, 0, 0, 73, 150, 2, 210]); + expect(uint64ToBytesBE(value)).toEqual(expectedBytes); + }); }); diff --git a/packages/hasher/tsdoc.json b/packages/hasher/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/hasher/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/hdwallet/README.md b/packages/hdwallet/README.md index 31804b1b8f4..0069b8004fe 100644 --- a/packages/hdwallet/README.md +++ b/packages/hdwallet/README.md @@ -18,7 +18,7 @@ This module is an implementation of the BIP-0044 and BIP-0032, Multi-Account Hie -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/hdwallet/package.json b/packages/hdwallet/package.json index b6141785c29..bc42a7bd690 100644 --- a/packages/hdwallet/package.json +++ b/packages/hdwallet/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/hdwallet/src/hdwallet.test.ts b/packages/hdwallet/src/hdwallet.test.ts index 7f280c19512..51894aeb57f 100644 --- a/packages/hdwallet/src/hdwallet.test.ts +++ b/packages/hdwallet/src/hdwallet.test.ts @@ -1,6 +1,10 @@ import HDWallet from './hdwallet'; import HDWalletSpec from './hdwallet-spec'; +/** + * @group node + * @group browser + */ describe('HDWallet', () => { test("Should throw error on invalid extended key's", () => { expect(() => diff --git a/packages/hdwallet/tsdoc.json b/packages/hdwallet/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/hdwallet/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/interfaces/README.md b/packages/interfaces/README.md index 49dd79edce1..03d8e855915 100644 --- a/packages/interfaces/README.md +++ b/packages/interfaces/README.md @@ -18,7 +18,7 @@ This module contains utilities to manipulate binary data and can be used to help -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/interfaces/package.json b/packages/interfaces/package.json index 458a7fc9aef..45cd56c2df7 100644 --- a/packages/interfaces/package.json +++ b/packages/interfaces/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/interfaces/src/index.ts b/packages/interfaces/src/index.ts index 5aed5cd5eaa..90765b34423 100644 --- a/packages/interfaces/src/index.ts +++ b/packages/interfaces/src/index.ts @@ -60,7 +60,7 @@ export abstract class AbstractAccount { abstract address: AbstractAddress; abstract provider: unknown; abstract getResourcesToSpend(quantities: any[], options?: any): any; - abstract sendTransaction(transactionRequest: any): any; + abstract sendTransaction(transactionRequest: any, options?: any): any; abstract simulateTransaction(transactionRequest: any): any; abstract fund(transactionRequest: any, quantities: any, fee: any): Promise; } @@ -74,7 +74,7 @@ export abstract class AbstractProgram { }; abstract provider: { - sendTransaction(transactionRequest: any): any; + sendTransaction(transactionRequest: any, options?: any): any; } | null; } diff --git a/packages/math/README.md b/packages/math/README.md index db5d27e4bf2..fd2d333c3c1 100644 --- a/packages/math/README.md +++ b/packages/math/README.md @@ -20,7 +20,7 @@ Math is based in [bn.js](https://www.npmjs.com/package/bn.js) library, which hel -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/math/package.json b/packages/math/package.json index e196a78d59c..7fcfd789aa0 100644 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/math/src/bn.test.ts b/packages/math/src/bn.test.ts index b92b442e83f..47103bdd3fa 100644 --- a/packages/math/src/bn.test.ts +++ b/packages/math/src/bn.test.ts @@ -2,6 +2,10 @@ import type { BN } from './bn'; import { bn } from './bn'; import type { BigNumberish } from './types'; +/** + * @group node + * @group browser + */ describe('Math - BN', () => { it('can execute operations without losing our BN reference', () => { let test: BN; diff --git a/packages/math/src/functional.test.ts b/packages/math/src/functional.test.ts index 086ebb84770..99acae319dc 100644 --- a/packages/math/src/functional.test.ts +++ b/packages/math/src/functional.test.ts @@ -6,6 +6,9 @@ import { format, formatUnits, toBytes, toHex, toNumber } from './functional'; +/** + * @group node + */ describe('Math - Functional shortcuts', () => { it('should toNumber return a number', () => { expect(toNumber('50000')).toEqual(50000); diff --git a/packages/math/tsdoc.json b/packages/math/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/math/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/merkle/README.md b/packages/merkle/README.md index 480010e4a97..2d1975deb6b 100644 --- a/packages/merkle/README.md +++ b/packages/merkle/README.md @@ -23,7 +23,7 @@ This module contains: -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/merkle/package.json b/packages/merkle/package.json index 18e583d6231..8a3fcb6b2b8 100644 --- a/packages/merkle/package.json +++ b/packages/merkle/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/merkle/src/binary/binaryMerkleTree.test.ts b/packages/merkle/src/binary/binaryMerkleTree.test.ts index afefdf2e4f9..574af5e14ae 100644 --- a/packages/merkle/src/binary/binaryMerkleTree.test.ts +++ b/packages/merkle/src/binary/binaryMerkleTree.test.ts @@ -3,6 +3,9 @@ import { toHex } from '@fuel-ts/math'; import { calcRoot, constructTree, getProof } from './binaryMerkleTree'; import type Node from './types/node'; +/** + * @group node + */ describe('Binary Merkle Tree', () => { const rootAfterLeaves = '0x9e59abcd7c89011ba919f9141624acb32b4cc31c24e76c6d4f64b25093ef366c'; const size = 100; diff --git a/packages/merkle/src/sparse/sparseMerkleTree.test.ts b/packages/merkle/src/sparse/sparseMerkleTree.test.ts index f41f9e0ca92..89f745e40e6 100644 --- a/packages/merkle/src/sparse/sparseMerkleTree.test.ts +++ b/packages/merkle/src/sparse/sparseMerkleTree.test.ts @@ -5,6 +5,9 @@ import { hash } from '../common'; import { DeepSparseMerkleSubTree } from './deepSparseMerkleSubTree'; import { SparseMerkleTree } from './sparseMerkleTree'; +/** + * @group node + */ describe('Sparse Merkle Tree', () => { it('Update and delete', () => { const smt = new SparseMerkleTree(); diff --git a/packages/merkle/src/sum/sumMerkleTree.test.ts b/packages/merkle/src/sum/sumMerkleTree.test.ts index 910d37864e5..d1d5e062df2 100644 --- a/packages/merkle/src/sum/sumMerkleTree.test.ts +++ b/packages/merkle/src/sum/sumMerkleTree.test.ts @@ -3,6 +3,9 @@ import { toHex } from '@fuel-ts/math'; import { calcRoot, constructTree, getProof } from './sumMerkleTree'; import Proof from './types/proof'; +/** + * @group node + */ describe('Sum Merkle Tree', () => { const size = 100; const sumAfterLeaves = toHex(((size - 1) * size) / 2); diff --git a/packages/merkle/tsdoc.json b/packages/merkle/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/merkle/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/mnemonic/README.md b/packages/mnemonic/README.md index 51ccc70777c..27338167f3e 100644 --- a/packages/mnemonic/README.md +++ b/packages/mnemonic/README.md @@ -18,7 +18,7 @@ This module contains utilities to import and export BIP 39 mnemonic phrases in c -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/mnemonic/package.json b/packages/mnemonic/package.json index 8ad4dd8926d..7ee666f6f35 100644 --- a/packages/mnemonic/package.json +++ b/packages/mnemonic/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/mnemonic/src/mnemonic.test.ts b/packages/mnemonic/src/mnemonic.test.ts index ca3af21dafa..f07d1c4ccd0 100644 --- a/packages/mnemonic/src/mnemonic.test.ts +++ b/packages/mnemonic/src/mnemonic.test.ts @@ -3,6 +3,10 @@ import { randomBytes } from '@fuel-ts/crypto'; import Mnemonic from './mnemonic'; import MnemonicSpec from './mnemonic-specs'; +/** + * @group node + * @group browser + */ describe('Mnemonic', () => { const mnemonic = new Mnemonic(); diff --git a/packages/mnemonic/src/utils.test.ts b/packages/mnemonic/src/utils.test.ts index 9e20212f061..5bcc30bd3fc 100644 --- a/packages/mnemonic/src/utils.test.ts +++ b/packages/mnemonic/src/utils.test.ts @@ -1,5 +1,9 @@ import { getWords } from './utils'; +/** + * @group node + * @group browser + */ describe('mnemonic utils', () => { const words = ['a', 'b', 'c']; diff --git a/packages/mnemonic/tsdoc.json b/packages/mnemonic/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/mnemonic/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/predicate/README.md b/packages/predicate/README.md index 4811e836b9b..d966e70e061 100644 --- a/packages/predicate/README.md +++ b/packages/predicate/README.md @@ -16,7 +16,7 @@ This module allows for a simple way to serialize calls to an on-chain predicate ## Documentation -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/guide/predicates/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/predicates/) ## Usage diff --git a/packages/predicate/package.json b/packages/predicate/package.json index 173c5a1ffa2..64a71156469 100644 --- a/packages/predicate/package.json +++ b/packages/predicate/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/predicate/src/predicate.ts b/packages/predicate/src/predicate.ts index f425290b620..d6fd4c49f3c 100644 --- a/packages/predicate/src/predicate.ts +++ b/packages/predicate/src/predicate.ts @@ -14,6 +14,7 @@ import type { BigNumberish } from '@fuel-ts/math'; import type { CallResult, Provider, + ProviderSendTxParams, TransactionRequest, TransactionRequestLike, TransactionResponse, @@ -113,9 +114,12 @@ export class Predicate extends Account implements Abs * @param transactionRequestLike - The transaction request-like object. * @returns A promise that resolves to the transaction response. */ - sendTransaction(transactionRequestLike: TransactionRequestLike): Promise { + sendTransaction( + transactionRequestLike: TransactionRequestLike, + options?: Pick + ): Promise { const transactionRequest = this.populateTransactionPredicateData(transactionRequestLike); - return super.sendTransaction(transactionRequest); + return super.sendTransaction(transactionRequest, options); } /** diff --git a/packages/predicate/src/utils/getPredicateRoot.test.ts b/packages/predicate/src/utils/getPredicateRoot.test.ts index e448adec374..28b2ed93883 100644 --- a/packages/predicate/src/utils/getPredicateRoot.test.ts +++ b/packages/predicate/src/utils/getPredicateRoot.test.ts @@ -2,6 +2,9 @@ import { getBytesCopy } from 'ethers'; import { getPredicateRoot } from './getPredicateRoot'; +/** + * @group node + */ describe('getPredicateRoot', () => { it('should return the correct predicate root', () => { const predicateBytes = getBytesCopy( diff --git a/packages/predicate/test/features/predicate-functions.test.ts b/packages/predicate/test/features/predicate-functions.test.ts index e2c0bf4b0d4..ecdba186b48 100644 --- a/packages/predicate/test/features/predicate-functions.test.ts +++ b/packages/predicate/test/features/predicate-functions.test.ts @@ -5,6 +5,10 @@ import { Predicate } from '../../src/predicate'; import { defaultPredicateAbi } from '../fixtures/abi/default'; import { defaultPredicateBytecode } from '../fixtures/bytecode/default'; +/** + * @group node + * @group browser + */ describe('Predicate', () => { describe('Functions', () => { const predicateAddress = '0x6b6ef590390f0a7de75f8275ab5d7877c17236caba2514039c6565ec15f79111'; diff --git a/packages/predicate/test/features/predicate-transactions.test.ts b/packages/predicate/test/features/predicate-transactions.test.ts index 7fcf95d3f19..3be2fa8d3e9 100644 --- a/packages/predicate/test/features/predicate-transactions.test.ts +++ b/packages/predicate/test/features/predicate-transactions.test.ts @@ -10,6 +10,10 @@ import { Predicate } from '../../src/predicate'; import { defaultPredicateAbi } from '../fixtures/abi/default'; import { defaultPredicateBytecode } from '../fixtures/bytecode/default'; +/** + * @group node + * @group browser + */ describe('Predicate', () => { describe('Transactions', () => { let predicate: Predicate<[string]>; @@ -36,9 +40,9 @@ describe('Predicate', () => { }); it('includes predicate as input when sending a transaction', async () => { - const sendTransactionMock = jest + const sendTransactionMock = vi .spyOn(Account.prototype, 'sendTransaction') - .mockImplementation(); + .mockImplementation(() => []); await predicate.sendTransaction(request); @@ -49,9 +53,9 @@ describe('Predicate', () => { }); it('includes predicate as input when simulating a transaction', async () => { - const sendTransactionMock = jest + const sendTransactionMock = vi .spyOn(Account.prototype, 'simulateTransaction') - .mockImplementation(); + .mockImplementation(() => []); await predicate.simulateTransaction(request); diff --git a/packages/program/README.md b/packages/program/README.md index b8c76974413..f72b241b48b 100644 --- a/packages/program/README.md +++ b/packages/program/README.md @@ -18,7 +18,7 @@ This module allows for a simple way to serialize calls and transactions to Sway -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/program/package.json b/packages/program/package.json index be20a77f9ad..c6c67fdb80b 100644 --- a/packages/program/package.json +++ b/packages/program/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/program/src/contract.test.ts b/packages/program/src/contract.test.ts index 0a09d291e57..6dac19ba45d 100644 --- a/packages/program/src/contract.test.ts +++ b/packages/program/src/contract.test.ts @@ -37,6 +37,9 @@ const ABI: JsonAbi = { configurables: [], }; +/** + * @group node + */ describe('Contract', () => { test('Create contract instance with provider', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); @@ -64,7 +67,7 @@ describe('Contract', () => { // but without reference to the BaseWalletLocked class const BaseWalletLockedCustom = Object.assign(Account); expect(BaseWalletLockedCustom).not.toBeInstanceOf(Account); - const wallet = new BaseWalletLockedCustom(generatedWallet.address); + const wallet = new BaseWalletLockedCustom(generatedWallet.address, generatedWallet.provider); const contract = new Contract(CONTRACT_ID, ABI, wallet); expect(contract.provider).toBe(wallet.provider); expect(contract.account).toBe(wallet); diff --git a/packages/program/src/functions/base-invocation-scope.ts b/packages/program/src/functions/base-invocation-scope.ts index b9857c7b78e..f0e1a777bf6 100644 --- a/packages/program/src/functions/base-invocation-scope.ts +++ b/packages/program/src/functions/base-invocation-scope.ts @@ -297,7 +297,9 @@ export class BaseInvocationScope { await this.fundWithRequiredCoins(maxFee); - const response = await this.program.account.sendTransaction(transactionRequest); + const response = await this.program.account.sendTransaction(transactionRequest, { + awaitExecution: true, + }); return FunctionInvocationResult.build( this.functionInvocationScopes, diff --git a/packages/providers/README.md b/packages/providers/README.md index 7c4fed3c0ca..8e1f0aba614 100644 --- a/packages/providers/README.md +++ b/packages/providers/README.md @@ -16,7 +16,7 @@ This module contains common Provider classes and utility functions for connectin ## Documentation -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/guide/providers/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/providers/) ## Usage diff --git a/packages/providers/codegen.json b/packages/providers/codegen.json index 0f48ea80586..d21c27157c3 100644 --- a/packages/providers/codegen.json +++ b/packages/providers/codegen.json @@ -7,7 +7,7 @@ "plugins": [ { "typescript": {} }, { "typescript-operations": {} }, - { "typescript-graphql-request": {} } + { "typescript-generic-sdk": {} } ], "config": { "scalars": { diff --git a/packages/providers/package.json b/packages/providers/package.json index 5805692f7bf..86f7432ab68 100644 --- a/packages/providers/package.json +++ b/packages/providers/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { @@ -39,7 +39,7 @@ "ethers": "^6.7.1", "@fuel-ts/versions": "workspace:*", "graphql": "^16.6.0", - "graphql-request": "^5.0.0", + "graphql-request": "5.0.0", "graphql-tag": "^2.12.6", "ramda": "^0.29.0", "tai64": "^1.0.0" @@ -48,9 +48,9 @@ "@fuel-ts/utils": "workspace:*", "@graphql-codegen/cli": "^2.13.7", "@graphql-codegen/typescript": "^2.8.0", - "@graphql-codegen/typescript-graphql-request": "^4.5.7", "@graphql-codegen/typescript-operations": "^2.5.5", - "@types/ramda": "^0.29.3", - "get-graphql-schema": "^2.1.2" + "@graphql-codegen/typescript-generic-sdk": "^3.1.0", + "get-graphql-schema": "^2.1.2", + "@types/ramda": "^0.29.3" } } diff --git a/packages/providers/src/coin-quantityfy.test.ts b/packages/providers/src/coin-quantityfy.test.ts index 48c866e95e9..90ec540c3ca 100644 --- a/packages/providers/src/coin-quantityfy.test.ts +++ b/packages/providers/src/coin-quantityfy.test.ts @@ -1,5 +1,8 @@ import { coinQuantityfy } from './coin-quantity'; +/** + * @group node + */ describe('coinQuantityfy', () => { it('should returns 1 when input is < 1', () => { expect(coinQuantityfy([0]).amount.toNumber()).toEqual(1); diff --git a/packages/providers/src/fuel-graphql-subscriber.ts b/packages/providers/src/fuel-graphql-subscriber.ts new file mode 100644 index 00000000000..fb1f80f9a18 --- /dev/null +++ b/packages/providers/src/fuel-graphql-subscriber.ts @@ -0,0 +1,81 @@ +import { FuelError } from '@fuel-ts/errors'; +import type { DocumentNode } from 'graphql'; +import { print } from 'graphql'; + +type FuelGraphQLSubscriberOptions = { + url: string; + query: DocumentNode; + variables?: Record; + fetchFn: typeof fetch; + abortController?: AbortController; +}; + +class FuelSubscriptionStream implements TransformStream { + readable: ReadableStream>; + writable: WritableStream; + private readableStreamController!: ReadableStreamController>; + private static textDecoder = new TextDecoder(); + + constructor() { + this.readable = new ReadableStream({ + start: (controller) => { + this.readableStreamController = controller; + }, + }); + + this.writable = new WritableStream({ + write: (bytes) => { + const text = FuelSubscriptionStream.textDecoder.decode(bytes); + // the fuel node sends keep-alive messages that should be ignored + if (text.startsWith('data:')) { + const { data, errors } = JSON.parse(text.split('data:')[1]); + if (Array.isArray(errors)) { + this.readableStreamController.enqueue( + new FuelError( + FuelError.CODES.INVALID_REQUEST, + errors.map((err) => err.message).join('\n\n') + ) + ); + } else { + this.readableStreamController.enqueue(data); + } + } + }, + }); + } +} + +export async function* fuelGraphQLSubscriber({ + url, + variables, + query, + fetchFn, +}: FuelGraphQLSubscriberOptions) { + const response = await fetchFn(`${url}-sub`, { + method: 'POST', + body: JSON.stringify({ + query: print(query), + variables, + }), + headers: { + 'Content-Type': 'application/json', + Accept: 'text/event-stream', + }, + }); + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const subscriptionStreamReader = response + .body!.pipeThrough(new FuelSubscriptionStream()) + .getReader(); + + while (true) { + const { value, done } = await subscriptionStreamReader.read(); + if (value instanceof FuelError) { + throw value; + } + yield value; + if (done) { + break; + } + } +} diff --git a/packages/providers/src/operations.graphql b/packages/providers/src/operations.graphql index f154d7fb844..a17615a0684 100644 --- a/packages/providers/src/operations.graphql +++ b/packages/providers/src/operations.graphql @@ -4,6 +4,31 @@ # generate `operations.ts` from this file. # Fragments + +fragment transactionStatusFragment on TransactionStatus { + type: __typename + ... on SubmittedStatus { + time + } + ... on SuccessStatus { + block { + id + } + time + programState { + returnType + data + } + } + ... on FailureStatus { + block { + id + } + time + reason + } +} + fragment transactionFragment on Transaction { id rawPayload @@ -12,27 +37,7 @@ fragment transactionFragment on Transaction { ...receiptFragment } status { - type: __typename - ... on SubmittedStatus { - time - } - ... on SuccessStatus { - block { - id - } - time - programState { - returnType - data - } - } - ... on FailureStatus { - block { - id - } - time - reason - } + ...transactionStatusFragment } } @@ -675,3 +680,15 @@ mutation produceBlocks( startTimestamp: $startTimestamp ) } + +subscription submitAndAwait($encodedTransaction: HexString!) { + submitAndAwait(tx: $encodedTransaction) { + ...transactionStatusFragment + } +} + +subscription statusChange($transactionId: TransactionId!) { + statusChange(id: $transactionId) { + ...transactionStatusFragment + } +} diff --git a/packages/providers/src/provider.ts b/packages/providers/src/provider.ts index f85cfe3756a..91fef5afcac 100644 --- a/packages/providers/src/provider.ts +++ b/packages/providers/src/provider.ts @@ -13,6 +13,7 @@ import { import { checkFuelCoreVersionCompatibility } from '@fuel-ts/versions'; import type { BytesLike } from 'ethers'; import { getBytesCopy, hexlify, Network } from 'ethers'; +import type { DocumentNode } from 'graphql'; import { GraphQLClient } from 'graphql-request'; import { clone } from 'ramda'; @@ -26,6 +27,7 @@ import type { import type { Coin } from './coin'; import type { CoinQuantity, CoinQuantityLike } from './coin-quantity'; import { coinQuantityfy } from './coin-quantity'; +import { fuelGraphQLSubscriber } from './fuel-graphql-subscriber'; import { MemoryCache } from './memory-cache'; import type { Message, MessageCoin, MessageProof, MessageStatus } from './message'; import type { ExcludeResourcesOption, Resource } from './resource'; @@ -45,6 +47,8 @@ import { getGasUsedFromReceipts, getReceiptsWithMissingData, } from './utils'; +import type { RetryOptions } from './utils/auto-retry-fetch'; +import { autoRetryFetch } from './utils/auto-retry-fetch'; import { mergeQuantities } from './utils/merge-quantities'; const MAX_RETRIES = 10; @@ -209,8 +213,14 @@ export type FetchRequestOptions = { * Provider initialization options */ export type ProviderOptions = { - fetch?: (url: string, options: FetchRequestOptions) => Promise; + fetch?: ( + url: string, + options: FetchRequestOptions, + providerOptions: Omit + ) => Promise; + timeout?: number; cacheUtxo?: number; + retryOptions?: RetryOptions; }; /** @@ -241,7 +251,16 @@ export type ProviderCallParams = UTXOValidationParams & EstimateTransactionParam /** * Provider Send transaction params */ -export type ProviderSendTxParams = EstimateTransactionParams; +export type ProviderSendTxParams = EstimateTransactionParams & { + /** + * By default, the promise will resolve immediately after the transaction is submitted. + * + * If set to true, the promise will resolve only when the transaction changes status + * from `SubmittedStatus` to one of `SuccessStatus`, `FailureStatus` or `SqueezedOutStatus`. + * + */ + awaitExecution?: boolean; +}; /** * URL - Consensus Params mapping. @@ -268,6 +287,29 @@ export default class Provider { private static chainInfoCache: ChainInfoCache = {}; private static nodeInfoCache: NodeInfoCache = {}; + options: ProviderOptions = { + timeout: undefined, + cacheUtxo: undefined, + fetch: undefined, + retryOptions: undefined, + }; + + private static getFetchFn(options: ProviderOptions): NonNullable { + const { retryOptions, timeout } = options; + + return autoRetryFetch((...args) => { + if (options.fetch) { + return options.fetch(...args); + } + + const url = args[0]; + const request = args[1]; + const signal = timeout ? AbortSignal.timeout(timeout) : undefined; + + return fetch(url, { ...request, signal }); + }, retryOptions); + } + /** * Constructor to initialize a Provider. * @@ -279,9 +321,12 @@ export default class Provider { protected constructor( /** GraphQL endpoint of the Fuel node */ public url: string, - public options: ProviderOptions = {} + options: ProviderOptions = {} ) { - this.operations = this.createOperations(url, options); + this.options = { ...this.options, ...options }; + this.url = url; + + this.operations = this.createOperations(); this.cache = options.cacheUtxo ? new MemoryCache(options.cacheUtxo) : undefined; } @@ -346,7 +391,8 @@ export default class Provider { */ async connect(url: string, options?: ProviderOptions) { this.url = url; - this.operations = this.createOperations(url, options ?? this.options); + this.options = options ?? this.options; + this.operations = this.createOperations(); await this.fetchChainAndNodeInfo(); } @@ -382,14 +428,35 @@ export default class Provider { /** * Create GraphQL client and set operations. * - * @param url - The URL of the Fuel node - * @param options - Additional options for the provider * @returns The operation SDK object */ - private createOperations(url: string, options: ProviderOptions = {}) { - this.url = url; - const gqlClient = new GraphQLClient(url, options.fetch ? { fetch: options.fetch } : undefined); - return getOperationsSdk(gqlClient); + private createOperations() { + const fetchFn = Provider.getFetchFn(this.options); + const gqlClient = new GraphQLClient(this.url, { + fetch: (url: string, requestInit: FetchRequestOptions) => + fetchFn(url, requestInit, this.options), + }); + + const executeQuery = (query: DocumentNode, vars: Record) => { + const opDefinition = query.definitions.find((x) => x.kind === 'OperationDefinition') as { + operation: string; + }; + const isSubscription = opDefinition?.operation === 'subscription'; + + if (isSubscription) { + return fuelGraphQLSubscriber({ + url: this.url, + query, + fetchFn: (url, requestInit) => + fetchFn(url as string, requestInit as FetchRequestOptions, this.options), + variables: vars, + }); + } + return gqlClient.request(query, vars); + }; + + // @ts-expect-error This is due to this function being generic. Its type is specified when calling a specific operation via provider.operations.xyz. + return getOperationsSdk(executeQuery); } /** @@ -506,7 +573,7 @@ export default class Provider { // #region Provider-sendTransaction async sendTransaction( transactionRequestLike: TransactionRequestLike, - { estimateTxDependencies = true }: ProviderSendTxParams = {} + { estimateTxDependencies = true, awaitExecution = false }: ProviderSendTxParams = {} ): Promise { const transactionRequest = transactionRequestify(transactionRequestLike); this.#cacheInputs(transactionRequest.inputs); @@ -515,7 +582,6 @@ export default class Provider { } // #endregion Provider-sendTransaction - const encodedTransaction = hexlify(transactionRequest.toTransactionBytes()); const { gasUsed, minGasPrice } = await this.getTransactionCost(transactionRequest, [], { estimateTxDependencies: false, estimatePredicates: false, @@ -537,12 +603,27 @@ export default class Provider { ); } + const encodedTransaction = hexlify(transactionRequest.toTransactionBytes()); + + if (awaitExecution) { + const subscription = this.operations.submitAndAwait({ encodedTransaction }); + for await (const { submitAndAwait } of subscription) { + if (submitAndAwait.type !== 'SubmittedStatus') { + break; + } + } + + const transactionId = transactionRequest.getTransactionId(this.getChainId()); + const response = new TransactionResponse(transactionId, this); + await response.fetch(); + return response; + } + const { submit: { id: transactionId }, } = await this.operations.submit({ encodedTransaction }); - const response = new TransactionResponse(transactionId, this); - return response; + return new TransactionResponse(transactionId, this); } /** @@ -794,16 +875,17 @@ export default class Provider { } async getResourcesForTransaction( - owner: AbstractAddress, + owner: string | AbstractAddress, transactionRequestLike: TransactionRequestLike, forwardingQuantities: CoinQuantity[] = [] ) { + const ownerAddress = Address.fromAddressOrString(owner); const transactionRequest = transactionRequestify(clone(transactionRequestLike)); const transactionCost = await this.getTransactionCost(transactionRequest, forwardingQuantities); // Add the required resources to the transaction from the owner transactionRequest.addResources( - await this.getResourcesToSpend(owner, transactionCost.requiredQuantities) + await this.getResourcesToSpend(ownerAddress, transactionCost.requiredQuantities) ); // Refetch transaction costs with the new resources // TODO: we could find a way to avoid fetch estimatePredicates again, by returning the transaction or @@ -815,7 +897,7 @@ export default class Provider { transactionRequest, forwardingQuantities ); - const resources = await this.getResourcesToSpend(owner, requiredQuantities); + const resources = await this.getResourcesToSpend(ownerAddress, requiredQuantities); return { resources, @@ -829,16 +911,17 @@ export default class Provider { */ async getCoins( /** The address to get coins for */ - owner: AbstractAddress, + owner: string | AbstractAddress, /** The asset ID of coins to get */ assetId?: BytesLike, /** Pagination arguments */ paginationArgs?: CursorPaginationArgs ): Promise { + const ownerAddress = Address.fromAddressOrString(owner); const result = await this.operations.getCoins({ first: 10, ...paginationArgs, - filter: { owner: owner.toB256(), assetId: assetId && hexlify(assetId) }, + filter: { owner: ownerAddress.toB256(), assetId: assetId && hexlify(assetId) }, }); const coins = result.coins.edges.map((edge) => edge.node); @@ -864,12 +947,13 @@ export default class Provider { */ async getResourcesToSpend( /** The address to get coins for */ - owner: AbstractAddress, + owner: string | AbstractAddress, /** The quantities to get */ quantities: CoinQuantityLike[], /** IDs of excluded resources from the selection. */ excludedIds?: ExcludeResourcesOption ): Promise { + const ownerAddress = Address.fromAddressOrString(owner); const excludeInput = { messages: excludedIds?.messages?.map((nonce) => hexlify(nonce)) || [], utxos: excludedIds?.utxos?.map((id) => hexlify(id)) || [], @@ -882,7 +966,7 @@ export default class Provider { excludeInput.utxos = Array.from(uniqueUtxos); } const coinsQuery = { - owner: owner.toB256(), + owner: ownerAddress.toB256(), queryPerAsset: quantities .map(coinQuantityfy) .map(({ assetId, amount, max: maxPerAsset }) => ({ @@ -1059,12 +1143,12 @@ export default class Provider { */ async getContractBalance( /** The contract ID to get the balance for */ - contractId: AbstractAddress, + contractId: string | AbstractAddress, /** The asset ID of coins to get */ assetId: BytesLike ): Promise { const { contractBalance } = await this.operations.getContractBalance({ - contract: contractId.toB256(), + contract: Address.fromAddressOrString(contractId).toB256(), asset: hexlify(assetId), }); return bn(contractBalance.amount, 10); @@ -1079,12 +1163,12 @@ export default class Provider { */ async getBalance( /** The address to get coins for */ - owner: AbstractAddress, + owner: string | AbstractAddress, /** The asset ID of coins to get */ assetId: BytesLike ): Promise { const { balance } = await this.operations.getBalance({ - owner: owner.toB256(), + owner: Address.fromAddressOrString(owner).toB256(), assetId: hexlify(assetId), }); return bn(balance.amount, 10); @@ -1099,14 +1183,14 @@ export default class Provider { */ async getBalances( /** The address to get coins for */ - owner: AbstractAddress, + owner: string | AbstractAddress, /** Pagination arguments */ paginationArgs?: CursorPaginationArgs ): Promise { const result = await this.operations.getBalances({ first: 10, ...paginationArgs, - filter: { owner: owner.toB256() }, + filter: { owner: Address.fromAddressOrString(owner).toB256() }, }); const balances = result.balances.edges.map((edge) => edge.node); @@ -1126,14 +1210,14 @@ export default class Provider { */ async getMessages( /** The address to get message from */ - address: AbstractAddress, + address: string | AbstractAddress, /** Pagination arguments */ paginationArgs?: CursorPaginationArgs ): Promise { const result = await this.operations.getMessages({ first: 10, ...paginationArgs, - owner: address.toB256(), + owner: Address.fromAddressOrString(address).toB256(), }); const messages = result.messages.edges.map((edge) => edge.node); diff --git a/packages/providers/src/transaction-request/hash-transaction.test.ts b/packages/providers/src/transaction-request/hash-transaction.test.ts index 25364dbcebe..264abf23190 100644 --- a/packages/providers/src/transaction-request/hash-transaction.test.ts +++ b/packages/providers/src/transaction-request/hash-transaction.test.ts @@ -5,6 +5,9 @@ import { SCRIPT_TX_REQUEST } from '../../test/fixtures/transaction-request'; import { hashTransaction } from './hash-transaction'; +/** + * @group node + */ describe('hashTransaction', () => { it('Hash script transaction request', () => { expect(hashTransaction(SCRIPT_TX_REQUEST, 0)).toEqual( diff --git a/packages/providers/src/transaction-request/transaction-request.test.ts b/packages/providers/src/transaction-request/transaction-request.test.ts index 31e3404cf40..422bf88c107 100644 --- a/packages/providers/src/transaction-request/transaction-request.test.ts +++ b/packages/providers/src/transaction-request/transaction-request.test.ts @@ -10,6 +10,9 @@ import { ScriptTransactionRequest } from './script-transaction-request'; import type { TransactionRequestLike } from './types'; import { transactionRequestify } from './utils'; +/** + * @group node + */ describe('TransactionRequest', () => { const assetIdA = '0x0101010101010101010101010101010101010101010101010101010101010101'; const assetIdB = '0x0202020202020202020202020202020202020202020202020202020202020202'; diff --git a/packages/providers/src/transaction-request/transaction-request.ts b/packages/providers/src/transaction-request/transaction-request.ts index 411814b434c..3cc5e083d58 100644 --- a/packages/providers/src/transaction-request/transaction-request.ts +++ b/packages/providers/src/transaction-request/transaction-request.ts @@ -226,8 +226,9 @@ export abstract class BaseTransactionRequest implements BaseTransactionRequestLi * @param address - The address to get the coin input witness index for. * @param signature - The signature to update the witness with. */ - updateWitnessByOwner(address: AbstractAddress, signature: BytesLike) { - const witnessIndex = this.getCoinInputWitnessIndexByOwner(address); + updateWitnessByOwner(address: string | AbstractAddress, signature: BytesLike) { + const ownerAddress = Address.fromAddressOrString(address); + const witnessIndex = this.getCoinInputWitnessIndexByOwner(ownerAddress); if (typeof witnessIndex === 'number') { this.updateWitness(witnessIndex, signature); } diff --git a/packages/providers/src/transaction-response/transaction-response.ts b/packages/providers/src/transaction-response/transaction-response.ts index d356b3e88f7..a0731887028 100644 --- a/packages/providers/src/transaction-response/transaction-response.ts +++ b/packages/providers/src/transaction-response/transaction-response.ts @@ -29,7 +29,6 @@ import type { GqlTransaction, AbiMap, } from '../transaction-summary/types'; -import { sleep } from '../utils'; /** @hidden */ export type TransactionResultCallReceipt = ReceiptCall; @@ -72,9 +71,6 @@ export type TransactionResultReceipt = | TransactionResultMintReceipt | TransactionResultBurnReceipt; -const STATUS_POLLING_INTERVAL_MAX_MS = 5000; -const STATUS_POLLING_INTERVAL_MIN_MS = 1000; - /** @hidden */ export type TransactionResult = TransactionSummary & { gqlTransaction: GqlTransaction; @@ -90,10 +86,6 @@ export class TransactionResponse { provider: Provider; /** Gas used on the transaction */ gasUsed: BN = bn(0); - /** Number of attempts made to fetch the transaction */ - fetchAttempts: number = 0; - /** Number of attempts made to retrieve a processed transaction. */ - resultAttempts: number = 0; /** The graphql Transaction with receipts object. */ gqlTransaction?: GqlTransaction; @@ -133,7 +125,16 @@ export class TransactionResponse { }); if (!response.transaction) { - await this.sleepBasedOnAttempts(++this.fetchAttempts); + const subscription = this.provider.operations.statusChange({ + transactionId: this.id, + }); + + for await (const { statusChange } of subscription) { + if (statusChange) { + break; + } + } + return this.fetch(); } @@ -196,6 +197,25 @@ export class TransactionResponse { return transactionSummary; } + private async waitForStatusChange() { + const status = this.gqlTransaction?.status?.type; + if (status && status !== 'SubmittedStatus') { + return; + } + + const subscription = this.provider.operations.statusChange({ + transactionId: this.id, + }); + + for await (const { statusChange } of subscription) { + if (statusChange.type !== 'SubmittedStatus') { + break; + } + } + + await this.fetch(); + } + /** * Waits for transaction to complete and returns the result. * @@ -204,13 +224,7 @@ export class TransactionResponse { async waitForResult( contractsAbiMap?: AbiMap ): Promise> { - await this.fetch(); - - if (this.gqlTransaction?.status?.type === 'SubmittedStatus') { - await this.sleepBasedOnAttempts(++this.resultAttempts); - - return this.waitForResult(contractsAbiMap); - } + await this.waitForStatusChange(); const transactionSummary = await this.getTransactionSummary(contractsAbiMap); @@ -241,18 +255,4 @@ export class TransactionResponse { return result; } - - /** - * Introduces a delay based on the number of previous attempts made. - * - * @param attempts - The number of attempts. - */ - private async sleepBasedOnAttempts(attempts: number): Promise { - // TODO: Consider adding `maxTimeout` or `maxAttempts` parameter. - // The aim is to avoid perpetual execution; when the limit - // is reached, we can throw accordingly. - await sleep( - Math.min(STATUS_POLLING_INTERVAL_MIN_MS * attempts, STATUS_POLLING_INTERVAL_MAX_MS) - ); - } } diff --git a/packages/providers/src/transaction-summary/assemble-transaction-summary.test.ts b/packages/providers/src/transaction-summary/assemble-transaction-summary.test.ts index 49a06b5dd71..ca4ea114932 100644 --- a/packages/providers/src/transaction-summary/assemble-transaction-summary.test.ts +++ b/packages/providers/src/transaction-summary/assemble-transaction-summary.test.ts @@ -19,8 +19,12 @@ import Provider from '../provider'; import type { TransactionResultReceipt } from '../transaction-response'; import { assembleTransactionSummary } from './assemble-transaction-summary'; +import * as calculateTransactionFeeMod from './calculate-transaction-fee'; import type { GraphqlTransactionStatus, Operation } from './types'; +/** + * @group node + */ describe('TransactionSummary', () => { let provider: Provider; let gasCosts: GqlGasCosts; @@ -44,7 +48,28 @@ describe('TransactionSummary', () => { gasCosts = provider.getChain().gasCosts; }); + beforeEach(() => { + vi.resetAllMocks(); + }); + + const mockCalculateTransactionFee = () => { + const calculateTransactionFee = vi + .spyOn(calculateTransactionFeeMod, 'calculateTransactionFee') + .mockReturnValue({ + fee: bn(0), + minFee: bn(0), + maxFee: bn(0), + feeFromGasUsed: bn(0), + }); + + return { + calculateTransactionFee, + }; + }; + const runTest = (status: GraphqlTransactionStatus, expected: Record) => { + const { calculateTransactionFee } = mockCalculateTransactionFee(); + const transactionSummary = assembleTransactionSummary({ id, gasPerByte, @@ -59,6 +84,7 @@ describe('TransactionSummary', () => { }); expect(transactionSummary).toMatchObject(expected); + expect(calculateTransactionFee).toHaveBeenCalledTimes(1); }; it('should assemble transaction summary just fine (SUCCESS)', () => { diff --git a/packages/providers/src/transaction-summary/calculate-transaction-fee.test.ts b/packages/providers/src/transaction-summary/calculate-transaction-fee.test.ts index fd3007b8385..096ed6b2ea0 100644 --- a/packages/providers/src/transaction-summary/calculate-transaction-fee.test.ts +++ b/packages/providers/src/transaction-summary/calculate-transaction-fee.test.ts @@ -9,6 +9,9 @@ import { import { calculateTransactionFee } from './calculate-transaction-fee'; +/** + * @group node + */ describe('calculateTransactionFee', () => { it('should properly calculate the transaction fee (SCRIPT TX)', () => { const transactionRawPayload = MOCK_TX_SCRIPT_RAW_PAYLOAD; diff --git a/packages/providers/src/transaction-summary/date.test.ts b/packages/providers/src/transaction-summary/date.test.ts index dcd29bbb890..549f6809cb6 100644 --- a/packages/providers/src/transaction-summary/date.test.ts +++ b/packages/providers/src/transaction-summary/date.test.ts @@ -4,12 +4,17 @@ import * as dateMod from './date'; const { fromTai64ToDate: tai64toDate, fromDateToTai64: dateToTai64 } = dateMod; +/** + * @group node + */ describe('transaction-summary/date', () => { - afterEach(jest.restoreAllMocks); + afterEach(() => { + vi.restoreAllMocks(); + }); it('should convert TAI64 to Date correctly', () => { - const fromStringSpy = jest.spyOn(tai64Mod.TAI64, 'fromString'); - const toUnixSpy = jest.spyOn(tai64Mod.TAI64.prototype, 'toUnix'); + const fromStringSpy = vi.spyOn(tai64Mod.TAI64, 'fromString'); + const toUnixSpy = vi.spyOn(tai64Mod.TAI64.prototype, 'toUnix'); const tai64Timestamp = '4611686020121838636'; @@ -22,8 +27,8 @@ describe('transaction-summary/date', () => { }); it('should convert Date to TAI64 correctly', () => { - const fromUnixSpy = jest.spyOn(tai64Mod.TAI64, 'fromUnix'); - const toStringSpy = jest.spyOn(tai64Mod.TAI64.prototype, 'toString'); + const fromUnixSpy = vi.spyOn(tai64Mod.TAI64, 'fromUnix'); + const toStringSpy = vi.spyOn(tai64Mod.TAI64.prototype, 'toString'); const date = new Date(); diff --git a/packages/providers/src/transaction-summary/input.test.ts b/packages/providers/src/transaction-summary/input.test.ts index 892c361792b..773847c29aa 100644 --- a/packages/providers/src/transaction-summary/input.test.ts +++ b/packages/providers/src/transaction-summary/input.test.ts @@ -17,6 +17,9 @@ import { getInputsMessage, } from './input'; +/** + * @group node + */ describe('transaction-summary/input', () => { it('should ensure getInputsCoin return correct inputs', () => { const coinInputs = getInputsCoin([MOCK_INPUT_COIN, MOCK_INPUT_CONTRACT, MOCK_INPUT_MESSAGE]); diff --git a/packages/providers/src/transaction-summary/operations.test.ts b/packages/providers/src/transaction-summary/operations.test.ts index 2b30c568401..c6a31678398 100644 --- a/packages/providers/src/transaction-summary/operations.test.ts +++ b/packages/providers/src/transaction-summary/operations.test.ts @@ -1,3 +1,4 @@ +import { getRandomB256 } from '@fuel-ts/address'; import { bn } from '@fuel-ts/math'; import { ReceiptType, TransactionType } from '@fuel-ts/transactions'; @@ -49,6 +50,9 @@ import { import type { Operation } from './types'; import { AddressType, OperationName, TransactionTypeName, ChainName } from './types'; +/** + * @group node + */ describe('operations', () => { describe('getContractCallOperations', () => { it('should ensure getContractCallOperations return contract call operations', () => { @@ -719,6 +723,9 @@ describe('operations', () => { const fromAddress = getInputAccountAddress(coinInput[0]); + const assetA = '0x0101010101010101010101010101010101010101010101010101010101010101'; + const assetB = '0x0202020202020202020202020202020202020202020202020202020202020202'; + const OPERATION_CONTRACT_CALL = { name: OperationName.contractCall, from: { @@ -746,6 +753,24 @@ describe('operations', () => { ], }; + const OPERATION_TRANSFER = { + name: OperationName.transfer, + from: { + type: 1, + address: getRandomB256(), + }, + to: { + type: 1, + address: getRandomB256(), + }, + assetsSent: [ + { + assetId: '0x0101010101010101010101010101010101010101010101010101010101010101', + amount: bn(100), + }, + ], + }; + it('should just add operation when its the first one', () => { const operations = addOperation([], OPERATION_CONTRACT_CALL); expect(operations.length).toEqual(1); @@ -805,6 +830,7 @@ describe('operations', () => { JSON.parse(JSON.stringify(baseOperations[0].assetsSent)) ); }); + it('should stack when same asset is added', () => { const baseOperations = addOperation([], OPERATION_CONTRACT_CALL); const operationsAddedSameAsset = addOperation(baseOperations, OPERATION_CONTRACT_CALL); @@ -816,7 +842,8 @@ describe('operations', () => { OPERATION_CONTRACT_CALL.assetsSent[0].assetId ); }); - it('should stack when same asset is added together with a different asset', () => { + + it('should stack when same asset is added together with a different asset [CONTRACT-CALL]', () => { const DIF_ASSET_ID = '0x0012300000000000000000000000000000000001'; const operationTwoAssets: Operation = { ...OPERATION_CONTRACT_CALL, @@ -845,6 +872,108 @@ describe('operations', () => { ); expect(operationsAddedSameAsset[0].assetsSent?.[1]?.assetId).toEqual(DIF_ASSET_ID); }); + + it('ensure operation asset transfer stacks multiple assetSents between same addresses', () => { + const operationOne: Operation = { + ...OPERATION_TRANSFER, + assetsSent: [ + { + assetId: assetA, + amount: bn(100), + }, + ], + }; + + const operationTwo: Operation = { + ...OPERATION_TRANSFER, + assetsSent: [ + { + assetId: assetB, + amount: bn(200), + }, + ], + }; + + const baseOperations = addOperation([], operationOne); + const stackedOperation = addOperation(baseOperations, operationTwo); + + expect(stackedOperation.length).toEqual(1); + expect(stackedOperation[0].assetsSent?.length).toEqual(2); + expect(stackedOperation[0].assetsSent?.[0]?.amount.valueOf()).toEqual( + operationOne.assetsSent?.[0]?.amount.valueOf() + ); + expect(stackedOperation[0].assetsSent?.[0]?.assetId).toEqual( + operationOne.assetsSent?.[0].assetId + ); + expect(stackedOperation[0].assetsSent?.[1]?.amount.valueOf()).toEqual( + operationTwo.assetsSent?.[0]?.amount.valueOf() + ); + expect(stackedOperation[0].assetsSent?.[1]?.assetId).toEqual( + operationTwo.assetsSent?.[0].assetId + ); + }); + + it('ensure operation asset transfer does not stack multiple assetSents between different addresses', () => { + const fromOne = getRandomB256(); + const fromTwo = getRandomB256(); + const toAddress2 = getRandomB256(); + + const operationOne: Operation = { + ...OPERATION_TRANSFER, + from: { + address: fromOne, + type: 1, + }, + to: { + address: toAddress2, + type: 1, + }, + assetsSent: [ + { + assetId: assetA, + amount: bn(100), + }, + ], + }; + + const operationTwo: Operation = { + ...OPERATION_TRANSFER, + from: { + address: fromTwo, + type: 1, + }, + to: { + address: toAddress2, + type: 1, + }, + assetsSent: [ + { + assetId: assetB, + amount: bn(200), + }, + ], + }; + + const baseOperation = addOperation([], operationOne); + const multipleOperations = addOperation(baseOperation, operationTwo); + + expect(multipleOperations.length).toEqual(2); + expect(multipleOperations[0].assetsSent?.length).toEqual(1); + expect(multipleOperations[0].assetsSent?.[0]?.amount.valueOf()).toEqual( + operationOne.assetsSent?.[0]?.amount.valueOf() + ); + expect(multipleOperations[0].assetsSent?.[0]?.assetId).toEqual( + operationOne.assetsSent?.[0].assetId + ); + expect(multipleOperations[1].assetsSent?.length).toEqual(1); + expect(multipleOperations[1].assetsSent?.[0]?.amount.valueOf()).toEqual( + operationTwo.assetsSent?.[0]?.amount.valueOf() + ); + expect(multipleOperations[1].assetsSent?.[0]?.assetId).toEqual( + operationTwo.assetsSent?.[0].assetId + ); + }); + it('should always not stack for contract calls', () => { const baseOperations = addOperation([], OPERATION_CONTRACT_CALL); const operationsAddedSameContractCall = addOperation(baseOperations, OPERATION_CONTRACT_CALL); @@ -963,7 +1092,7 @@ describe('operations', () => { }); }); - describe('should ensure getTransactionTypeName works as expected', () => { + it('should ensure getTransactionTypeName works as expected', () => { expect(getTransactionTypeName(TransactionType.Create)).toBe(TransactionTypeName.Create); expect(getTransactionTypeName(TransactionType.Mint)).toBe(TransactionTypeName.Mint); expect(getTransactionTypeName(TransactionType.Script)).toBe(TransactionTypeName.Script); diff --git a/packages/providers/src/transaction-summary/operations.ts b/packages/providers/src/transaction-summary/operations.ts index 23a78efcf0f..f921322d75a 100644 --- a/packages/providers/src/transaction-summary/operations.ts +++ b/packages/providers/src/transaction-summary/operations.ts @@ -103,16 +103,27 @@ export function getReceiptsMessageOut(receipts: TransactionResultReceipt[]) { const mergeAssets = (op1: Operation, op2: Operation) => { const assets1 = op1.assetsSent || []; const assets2 = op2.assetsSent || []; - const filtered = assets2.filter((c) => !assets1.some(hasSameAssetId(c))); - return assets1 - .map((coin) => { - const asset = assets2.find(hasSameAssetId(coin)); - if (!asset) { - return coin; - } - return { ...coin, amount: bn(coin.amount).add(asset.amount) }; - }) - .concat(filtered); + + // Getting assets from op2 that don't exist in op1 + const filteredAssets = assets2.filter( + (asset2) => !assets1.some((asset1) => asset1.assetId === asset2.assetId) + ); + + // Merge assets that already exist in op1 + const mergedAssets = assets1.map((asset1) => { + // Find matching asset in op2 + const matchingAsset = assets2.find((asset2) => asset2.assetId === asset1.assetId); + if (!matchingAsset) { + // No matching asset found, return asset1 + return asset1; + } + // Matching asset found, merge amounts + const mergedAmount = bn(asset1.amount).add(matchingAsset.amount); + return { ...asset1, amount: mergedAmount }; + }); + + // Return merged assets from op1 with filtered assets from op2 + return mergedAssets.concat(filteredAssets); }; /** @hidden */ @@ -128,43 +139,37 @@ function isSameOperation(a: Operation, b: Operation) { /** @hidden */ export function addOperation(operations: Operation[], toAdd: Operation) { - const ops = operations - .map((op) => { - // if it's not same operation, don't change. we just wanna stack the same operation - if (!isSameOperation(op, toAdd)) { - return null; - } - - let newOp = { ...op }; - - // if it's adding new assets - if (toAdd.assetsSent?.length) { - // if prev op had assets, merge them. Otherwise just add the new assets - newOp = { - ...newOp, - assetsSent: op.assetsSent?.length ? mergeAssets(op, toAdd) : toAdd.assetsSent, - }; - } + const allOperations = [...operations]; + + // Verifying if the operation to add already exists. + const index = allOperations.findIndex((op) => isSameOperation(op, toAdd)); + + if (allOperations[index]) { + // Existent operation, we want to edit it. + const existentOperation = { ...allOperations[index] }; + + if (toAdd.assetsSent?.length) { + /** + * If the assetSent already exists, we call 'mergeAssets' to merge possible + * entries of the same 'assetId', otherwise we just add the new 'assetSent'. + */ + existentOperation.assetsSent = existentOperation.assetsSent?.length + ? mergeAssets(existentOperation, toAdd) + : toAdd.assetsSent; + } - // if it's adding new calls, - if (toAdd.calls?.length) { - /* -[] for calls we don't stack as grouping is not desired. - we wanna show all calls in the same operation - with each respective assets, amounts, functions, arguments. - */ - newOp = { - ...newOp, - calls: [...(op.calls || []), ...(toAdd.calls || [])], - }; - } + if (toAdd.calls?.length) { + // We need to stack the new call(s) with the possible existent ones. + existentOperation.calls = [...(existentOperation.calls || []), ...toAdd.calls]; + } - return newOp; - }) - .filter(Boolean) as Operation[]; + allOperations[index] = existentOperation; + } else { + // New operation, we can simply add it. + allOperations.push(toAdd); + } - // if this operation didn't exist before just add it to the end - return ops.length ? ops : [...operations, toAdd]; + return allOperations; } /** @hidden */ @@ -372,7 +377,8 @@ export function getTransferOperations({ const input = getInputFromAssetId(inputs, output.assetId); if (input) { const inputAddress = getInputAccountAddress(input); - operations = addOperation(operations, { + + const operationToAdd: Operation = { name: OperationName.transfer, from: { type: AddressType.account, @@ -388,7 +394,9 @@ export function getTransferOperations({ amount: output.amount, }, ], - }); + }; + + operations = addOperation(operations, operationToAdd); } }); } diff --git a/packages/providers/src/transaction-summary/output.test.ts b/packages/providers/src/transaction-summary/output.test.ts index f3afd64b33c..f4aceb3cfca 100644 --- a/packages/providers/src/transaction-summary/output.test.ts +++ b/packages/providers/src/transaction-summary/output.test.ts @@ -14,6 +14,9 @@ import { getOutputsVariable, } from './output'; +/** + * @group node + */ describe('transaction-summary/output', () => { it('should ensure getOutputsCoin return correct outputs', () => { const coinsOutputs = getOutputsCoin([ diff --git a/packages/providers/src/transaction-summary/receipt.test.ts b/packages/providers/src/transaction-summary/receipt.test.ts index 3f9f532a159..3f71a8f316f 100644 --- a/packages/providers/src/transaction-summary/receipt.test.ts +++ b/packages/providers/src/transaction-summary/receipt.test.ts @@ -10,6 +10,9 @@ import type { import { extractBurnedAssetsFromReceipts, extractMintedAssetsFromReceipts } from './receipt'; import type { MintedAsset, BurnedAsset } from './types'; +/** + * @group node + */ describe('extractMintedAssetsFromReceipts and extractBurnedAssetsFromReceipts', () => { it('should extracts minted and burned assets just fine', () => { // Sample input diff --git a/packages/providers/src/transaction-summary/status.test.ts b/packages/providers/src/transaction-summary/status.test.ts index f5194645e21..b3050375c03 100644 --- a/packages/providers/src/transaction-summary/status.test.ts +++ b/packages/providers/src/transaction-summary/status.test.ts @@ -9,6 +9,9 @@ import { getTransactionStatusName, processGraphqlStatus } from './status'; import type { GqlTransactionStatusesNames, GraphqlTransactionStatus } from './types'; import { TransactionStatus } from './types'; +/** + * @group node + */ describe('status', () => { it('should ensure getTransactionStatusName return status name just fine', () => { let status = getTransactionStatusName('FailureStatus'); diff --git a/packages/providers/src/utils/auto-retry-fetch.test.ts b/packages/providers/src/utils/auto-retry-fetch.test.ts new file mode 100644 index 00000000000..db3526fac50 --- /dev/null +++ b/packages/providers/src/utils/auto-retry-fetch.test.ts @@ -0,0 +1,130 @@ +import { safeExec } from '@fuel-ts/errors/test-utils'; + +import type { FetchRequestOptions } from '../provider'; + +import type { RetryOptions } from './auto-retry-fetch'; +import { autoRetryFetch, getWaitDelay } from './auto-retry-fetch'; + +/** + * @group node + * @group browser + */ +describe('getWaitDelay', () => { + const maxRetries = 10; + const baseDelay = 10; + + it('should get wait delay for linear backoff strategy', () => { + const options: RetryOptions = { maxRetries, baseDelay, backoff: 'linear' }; + for (let attempt = 1; attempt <= maxRetries; attempt++) { + const delay = getWaitDelay(options, attempt); + expect(delay).toEqual(baseDelay * attempt); // linear + } + }); + + it('should get wait delay for fixed backoff strategy', () => { + const options: RetryOptions = { maxRetries, baseDelay, backoff: 'fixed' }; + for (let attempt = 1; attempt <= maxRetries; attempt++) { + const delay = getWaitDelay(options, attempt); + expect(delay).toEqual(baseDelay); // fixed + } + }); + + it('should get wait delay for exponential backoff strategy', () => { + const options: RetryOptions = { maxRetries, baseDelay, backoff: 'exponential' }; + for (let attempt = 1; attempt <= maxRetries; attempt++) { + const delay = getWaitDelay(options, attempt); + expect(delay).toEqual(2 ** (attempt - 1) * baseDelay); // exponential + } + }); + + it('default strategy should be exponential', () => { + const options: RetryOptions = { maxRetries, baseDelay }; // omitting `backoff` + for (let attempt = 1; attempt <= maxRetries; attempt++) { + const delay = getWaitDelay(options, attempt); + expect(delay).toEqual(2 ** (attempt - 1) * baseDelay); // exponential + } + }); + + it('default baseDelay should be 150', () => { + const defaultDuration = 150; + const options: RetryOptions = { maxRetries, backoff: 'fixed' }; // omitting `baseDelay` + for (let attempt = 1; attempt <= maxRetries; attempt++) { + const delay = getWaitDelay(options, attempt); + expect(delay).toEqual(defaultDuration); + } + }); +}); + +describe('autoRetryFetch', () => { + const url = 'http://anythibng.com'; + const fetchOptions: FetchRequestOptions = { method: 'POST', headers: {}, body: '' }; + + const maxRetries = 5; + const baseDelay = 1; + const retryOptions: RetryOptions = { maxRetries, baseDelay, backoff: 'fixed' }; + + const econnRefusedError = new Error(); + econnRefusedError.cause = { code: 'ECONNREFUSED' }; + + it('should not wrap function by default', async () => { + const fn = vi.fn(() => { + throw new Error('anything'); + }); + + const autoRetry = autoRetryFetch(fn); + + const { error, result } = await safeExec(async () => autoRetry(url, fetchOptions, {})); + + expect(fn).toHaveBeenCalledTimes(1); + expect(result).toBeFalsy(); + expect(error).toMatch(/anything/); + }); + + it('should retry until maxRetries and fail', async () => { + const fn = vi.fn(() => { + throw econnRefusedError; + }); + + const autoRetry = autoRetryFetch(fn, retryOptions); + + const { error, result } = await safeExec(async () => autoRetry(url, fetchOptions, {})); + + expect(fn).toHaveBeenCalledTimes(1 + maxRetries); // 1st call is not a retry + expect(result).toBeFalsy(); + expect(error).toBe(econnRefusedError); + }); + + it('should retry until maxRetries and succeed', async () => { + let retries = 0; + + const fn = vi.fn(() => { + if (retries < maxRetries) { + retries += 1; + throw econnRefusedError; + } + return Promise.resolve(new Response()); + }); + + const autoRetry = autoRetryFetch(fn, retryOptions); + + const { error, result } = await safeExec(async () => autoRetry(url, fetchOptions, {})); + + expect(fn).toHaveBeenCalledTimes(1 + maxRetries); // 1st call is not a retry + expect(result).toBeInstanceOf(Response); + expect(error).toBeFalsy(); + }); + + it('throws if error is not ECONNREFUSED and it does not retry', async () => { + const fn = vi.fn(() => { + throw new Error('anything'); + }); + + const autoRetry = autoRetryFetch(fn, retryOptions); + + const { error, result } = await safeExec(async () => autoRetry(url, fetchOptions, {})); + + expect(fn).toHaveBeenCalledTimes(1); + expect(result).toBeFalsy(); + expect(error?.message).toMatch(/anything/); + }); +}); diff --git a/packages/providers/src/utils/auto-retry-fetch.ts b/packages/providers/src/utils/auto-retry-fetch.ts new file mode 100644 index 00000000000..a3a7a9ef981 --- /dev/null +++ b/packages/providers/src/utils/auto-retry-fetch.ts @@ -0,0 +1,87 @@ +import type { ProviderOptions } from '../provider'; + +import { sleep } from './sleep'; + +type Backoff = 'linear' | 'exponential' | 'fixed'; + +/** + * Retry options scheme + */ +export type RetryOptions = { + /** + * Amount of attempts to retry before failing the call. + */ + maxRetries: number; + /** + * Backoff strategy to use when retrying. Default is exponential. + */ + backoff?: Backoff; + /** + * Starting delay for backoff strategy. Default is 150ms. + */ + baseDelay?: number; +}; + +/** + * Calculate the delay for the next retry attempt + * @param options - Retry options configuration + * @param retryAttemptNum - 1-based, tells which retry attempt is this + * @returns Next wait delay + */ +export function getWaitDelay(options: RetryOptions, retryAttemptNum: number) { + const duration = options.baseDelay ?? 150; + + switch (options.backoff) { + case 'linear': + return duration * retryAttemptNum; + case 'fixed': + return duration; + case 'exponential': + default: + return 2 ** (retryAttemptNum - 1) * duration; + } +} + +/** + * Returns a wrapped fetch function that will auto-execute itself in case of errors, until it succeeds + * @param fetchFn - Function to be auto-retried + * @param options - Retry options configuration + * @param retryAttemptNum - ZERO=first call, ONE=first retry, TWO=second retry, etc. + * @returns Whatever is the output of the `fetchFn` function + */ +export function autoRetryFetch( + fetchFn: NonNullable, + options?: RetryOptions, + retryAttemptNum: number = 0 +): NonNullable { + if (options === undefined) { + return fetchFn; + } + + return async (...args) => { + try { + return await fetchFn(...args); + } catch (_error: unknown) { + const error = _error as Error & { cause?: { code: string } }; + + /** + * So far, we are auto-retrying only for `ECONNREFUSED` error. + * TODO: Investigate if we should consider more errors. + */ + if (error.cause?.code !== 'ECONNREFUSED') { + throw error; + } + const retryNum = retryAttemptNum + 1; + + if (retryNum > options.maxRetries) { + throw error; + } + + const delay = getWaitDelay(options, retryNum); + + await sleep(delay); + + return autoRetryFetch(fetchFn, options, retryNum)(...args); + } + }; +} diff --git a/packages/providers/src/utils/block-explorer.test.ts b/packages/providers/src/utils/block-explorer.test.ts index 4409ea4ba99..3ce7c990b7c 100644 --- a/packages/providers/src/utils/block-explorer.test.ts +++ b/packages/providers/src/utils/block-explorer.test.ts @@ -28,6 +28,9 @@ const testBlockExplorerUrlWithInputs = ({ expect(url).toEqual(expectedUrl); }; +/** + * @group node + */ describe('BlockExplorer Utils', () => { test('buildBlockExplorerUrl - empty/undefined inputs', () => { expect(buildBlockExplorerUrl()).toEqual(`${DEFAULT_BLOCK_EXPLORER_URL}/`); diff --git a/packages/providers/src/utils/gas.test.ts b/packages/providers/src/utils/gas.test.ts index edca491e36e..9857395941b 100644 --- a/packages/providers/src/utils/gas.test.ts +++ b/packages/providers/src/utils/gas.test.ts @@ -29,6 +29,9 @@ import { resolveGasDependentCosts, } from './gas'; +/** + * @group node + */ describe('gas', () => { describe('resolveGasDependentCosts', () => { it('calculates cost correctly for LightOperation', () => { diff --git a/packages/providers/src/utils/json.test.ts b/packages/providers/src/utils/json.test.ts index 89664630cb9..931d1aec051 100644 --- a/packages/providers/src/utils/json.test.ts +++ b/packages/providers/src/utils/json.test.ts @@ -3,6 +3,9 @@ import { BN, bn } from '@fuel-ts/math'; import { normalizeJSON } from './json'; +/** + * @group node + */ describe('JSON parser', () => { test('normalizeJSON object', () => { const bytesValue = Uint8Array.from([1, 2, 3, 4]); diff --git a/packages/providers/src/utils/merge-quantities.test.ts b/packages/providers/src/utils/merge-quantities.test.ts index 6c818d5a31d..493658129e4 100644 --- a/packages/providers/src/utils/merge-quantities.test.ts +++ b/packages/providers/src/utils/merge-quantities.test.ts @@ -4,6 +4,9 @@ import type { CoinQuantity } from '../coin-quantity'; import { mergeQuantities } from './merge-quantities'; +/** + * @group node + */ describe('mergeQuantities', () => { const assetIdA = '0x0101010101010101010101010101010101010101010101010101010101010101'; const assetIdB = '0x0202020202020202020202020202020202020202020202020202020202020202'; diff --git a/packages/providers/src/utils/receipts.test.ts b/packages/providers/src/utils/receipts.test.ts index 3a8bebc372c..1d3bc76b0e6 100644 --- a/packages/providers/src/utils/receipts.test.ts +++ b/packages/providers/src/utils/receipts.test.ts @@ -25,6 +25,9 @@ import { GqlReceiptType } from '../__generated__/operations'; import { assembleReceiptByType } from './receipts'; +/** + * @group node + */ describe('assembleReceiptByType', () => { it('should return a ReceiptCall receipt when GqlReceiptType.Call is provided', () => { const receipt = assembleReceiptByType({ diff --git a/packages/providers/src/utils/time.test.ts b/packages/providers/src/utils/time.test.ts index 8f4f3ec84f4..182b4c044cb 100644 --- a/packages/providers/src/utils/time.test.ts +++ b/packages/providers/src/utils/time.test.ts @@ -1,5 +1,8 @@ import { fromTai64ToUnix, fromUnixToTai64 } from './time'; +/** + * @group node + */ test('fromTai64ToUnix', () => { expect(fromTai64ToUnix('4611686020108779312')).toEqual(1681391398); }); diff --git a/packages/providers/test/__snapshots__/provider.test.ts.snap b/packages/providers/test/__snapshots__/provider.test.ts.snap index e9106af6f47..30aaf5b14ec 100644 --- a/packages/providers/test/__snapshots__/provider.test.ts.snap +++ b/packages/providers/test/__snapshots__/provider.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Provider can getMessageProof with all data 1`] = ` +exports[`Provider > can getMessageProof with all data 1`] = ` { "amount": "0xa", "blockProof": { @@ -50,7 +50,7 @@ exports[`Provider can getMessageProof with all data 1`] = ` } `; -exports[`Provider can getMessageStatus 1`] = ` +exports[`Provider > can getMessageStatus 1`] = ` { "state": "SPENT", } diff --git a/packages/providers/test/auto-retry-fetch.test.ts b/packages/providers/test/auto-retry-fetch.test.ts new file mode 100644 index 00000000000..cb0d6b77178 --- /dev/null +++ b/packages/providers/test/auto-retry-fetch.test.ts @@ -0,0 +1,33 @@ +import Provider from '../src/provider'; +import type { RetryOptions } from '../src/utils/auto-retry-fetch'; +import * as autoRetryFetchMod from '../src/utils/auto-retry-fetch'; + +// TODO: Figure out a way to import this constant from `@fuel-ts/wallet/configs` +const FUEL_NETWORK_URL = 'http://127.0.0.1:4000/graphql'; + +/** + * @group node + * TODO: add browser group as well (https://github.com/FuelLabs/fuels-ts/pull/1654#discussion_r1456501593) + */ +describe('Provider correctly', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + test('Provider should always wrap `fetchFn`', async () => { + const autoRetryFetchFn = vi.spyOn(autoRetryFetchMod, 'autoRetryFetch'); + + // #region provider-retry-options + const retryOptions: RetryOptions = { + maxRetries: 5, + baseDelay: 100, + backoff: 'exponential', + }; + + const provider = await Provider.create(FUEL_NETWORK_URL, { retryOptions }); + // #endregion provider-retry-options + + expect(provider).toBeTruthy(); + expect(autoRetryFetchFn).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/providers/test/memory-cache.test.ts b/packages/providers/test/memory-cache.test.ts index 0c4c31f22f3..d4060528a34 100644 --- a/packages/providers/test/memory-cache.test.ts +++ b/packages/providers/test/memory-cache.test.ts @@ -6,6 +6,10 @@ import { MemoryCache } from '../src/memory-cache'; const CACHE_ITEMS = [hexlify(randomBytes(8)), randomBytes(8), randomBytes(8)]; +/** + * @group node + * @group browser + */ describe('Memory Cache', () => { it('can construct [valid numerical ttl]', () => { const memCache = new MemoryCache(1000); diff --git a/packages/providers/test/provider.test.ts b/packages/providers/test/provider.test.ts index 24e3ac66c04..95cc5339398 100644 --- a/packages/providers/test/provider.test.ts +++ b/packages/providers/test/provider.test.ts @@ -10,30 +10,39 @@ import { versions } from '@fuel-ts/versions'; import * as fuelTsVersionsMod from '@fuel-ts/versions'; import { getBytesCopy, hexlify } from 'ethers'; import type { BytesLike } from 'ethers'; -import * as GraphQL from 'graphql-request'; -import type { TransactionCost } from '../src/provider'; +import { fromTai64ToDate } from '../src'; +import type { ChainInfo, NodeInfo, TransactionCost, FetchRequestOptions } from '../src/provider'; import Provider from '../src/provider'; import type { CoinTransactionRequestInput, MessageTransactionRequestInput, } from '../src/transaction-request'; -import { CreateTransactionRequest, ScriptTransactionRequest } from '../src/transaction-request'; -import { fromTai64ToUnix, fromUnixToTai64 } from '../src/utils'; +import { ScriptTransactionRequest, CreateTransactionRequest } from '../src/transaction-request'; +import { TransactionResponse } from '../src/transaction-response'; +import { fromTai64ToUnix, fromUnixToTai64, sleep } from '../src/utils'; import * as gasMod from '../src/utils/gas'; import { messageProofResponse, messageStatusResponse } from './fixtures'; -import { MOCK_CHAIN } from './fixtures/chain'; -import { MOCK_NODE_INFO } from './fixtures/nodeInfo'; -// https://stackoverflow.com/a/72885576 -jest.mock('@fuel-ts/versions', () => ({ - __esModule: true, - ...jest.requireActual('@fuel-ts/versions'), -})); +vi.mock('@fuel-ts/versions', async () => { + const mod = await vi.importActual('@fuel-ts/versions'); + return { + __esModule: true, + ...mod, + }; +}); + +vi.mock('@fuel-ts/math', async () => { + const mod = await vi.importActual('@fuel-ts/math'); + return { + __esModule: true, + ...mod, + }; +}); afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); const getCustomFetch = @@ -63,6 +72,9 @@ const getCustomFetch = // TODO: Figure out a way to import this constant from `@fuel-ts/wallet/configs` const FUEL_NETWORK_URL = 'http://127.0.0.1:4000/graphql'; +/** + * @group node + */ describe('Provider', () => { it('can getVersion()', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); @@ -209,32 +221,27 @@ describe('Provider', () => { const providerUrl1 = FUEL_NETWORK_URL; const providerUrl2 = 'http://127.0.0.1:8080/graphql'; - const provider = await Provider.create(providerUrl1); + const provider = await Provider.create(providerUrl1, { + fetch: (url: string, options: FetchRequestOptions) => + getCustomFetch('getVersion', { nodeInfo: { nodeVersion: url } })(url, options), + }); expect(provider.url).toBe(providerUrl1); + expect(await provider.getVersion()).toEqual(providerUrl1); - const spyGraphQLClient = jest.spyOn(GraphQL, 'GraphQLClient').mockImplementation( - () => - ({ - request: () => - Promise.resolve({ - chain: MOCK_CHAIN, - nodeInfo: MOCK_NODE_INFO, - }), - }) as unknown as GraphQL.GraphQLClient - ); - - const spyFetchChainAndNodeInfo = jest.spyOn(Provider.prototype, 'fetchChainAndNodeInfo'); - const spyFetchChain = jest.spyOn(Provider.prototype, 'fetchChain'); - const spyFetchNode = jest.spyOn(Provider.prototype, 'fetchNode'); + const spyFetchChainAndNodeInfo = vi + .spyOn(Provider.prototype, 'fetchChainAndNodeInfo') + .mockResolvedValue({ + chain: {} as ChainInfo, + nodeInfo: {} as NodeInfo, + }); await provider.connect(providerUrl2); expect(provider.url).toBe(providerUrl2); - expect(spyGraphQLClient).toBeCalledWith(providerUrl2, undefined); + + expect(await provider.getVersion()).toEqual(providerUrl2); expect(spyFetchChainAndNodeInfo).toHaveBeenCalledTimes(1); - expect(spyFetchChain).toHaveBeenCalledTimes(1); - expect(spyFetchNode).toHaveBeenCalledTimes(1); }); it('can accept a custom fetch function', async () => { @@ -254,9 +261,12 @@ describe('Provider', () => { * Mocking and initializing Provider with an invalid fetcher just * to ensure it'll be properly overriden in `connect` method below */ - const fetchChainAndNodeInfo = jest + const fetchChainAndNodeInfo = vi .spyOn(Provider.prototype, 'fetchChainAndNodeInfo') - .mockImplementation(); + .mockResolvedValue({ + chain: {} as ChainInfo, + nodeInfo: {} as NodeInfo, + }); const provider = await Provider.create(providerUrl, { fetch: () => { @@ -286,14 +296,19 @@ describe('Provider', () => { if (!block) { throw new Error('No latest block'); } - const { height: latestBlockNumberBeforeProduce } = block; + const { time: timeLastBlockProduced } = block; const amountOfBlocksToProduce = 3; - const latestBlockNumber = await provider.produceBlocks(amountOfBlocksToProduce); + const producedBlockHeigh = await provider.produceBlocks(amountOfBlocksToProduce); - expect(latestBlockNumber.toHex()).toEqual( - latestBlockNumberBeforeProduce.add(amountOfBlocksToProduce).toHex() - ); + const producedBlock = await provider.getBlock(producedBlockHeigh.toNumber()); + + expect(producedBlock).toBeDefined(); + + const oldest = new Date(fromTai64ToDate(timeLastBlockProduced || '')); + const newest = new Date(fromTai64ToDate(producedBlock?.time || '')); + + expect(newest >= oldest).toBeTruthy(); // #endregion Provider-produce-blocks }); @@ -510,8 +525,9 @@ describe('Provider', () => { expect(EXCLUDED.map((value) => hexlify(value))).toStrictEqual(EXPECTED); const owner = Address.fromRandom(); - const resourcesToSpendMock = jest.fn(() => Promise.resolve({ coinsToSpend: [] })); - // @ts-expect-error mock + const resourcesToSpendMock = vi.fn(() => + Promise.resolve({ coinsToSpend: [] }) + ) as unknown as typeof provider.operations.getCoinsToSpend; provider.operations.getCoinsToSpend = resourcesToSpendMock; await provider.getResourcesToSpend(owner, []); @@ -643,8 +659,9 @@ describe('Provider', () => { expect(EXCLUDED.map((value) => hexlify(value))).toStrictEqual(EXPECTED); const owner = Address.fromRandom(); - const resourcesToSpendMock = jest.fn(() => Promise.resolve({ coinsToSpend: [] })); - // @ts-expect-error mock + const resourcesToSpendMock = vi.fn(() => + Promise.resolve({ coinsToSpend: [] }) + ) as unknown as typeof provider.operations.getCoinsToSpend; provider.operations.getCoinsToSpend = resourcesToSpendMock; await provider.getResourcesToSpend(owner, [], { utxos: [ @@ -747,9 +764,9 @@ describe('Provider', () => { it('should ensure getChain and getNode uses the cache and does not fetch new data', async () => { Provider.clearChainAndNodeCaches(); - const spyFetchChainAndNodeInfo = jest.spyOn(Provider.prototype, 'fetchChainAndNodeInfo'); - const spyFetchChain = jest.spyOn(Provider.prototype, 'fetchChain'); - const spyFetchNode = jest.spyOn(Provider.prototype, 'fetchNode'); + const spyFetchChainAndNodeInfo = vi.spyOn(Provider.prototype, 'fetchChainAndNodeInfo'); + const spyFetchChain = vi.spyOn(Provider.prototype, 'fetchChain'); + const spyFetchNode = vi.spyOn(Provider.prototype, 'fetchNode'); const provider = await Provider.create(FUEL_NETWORK_URL); @@ -768,9 +785,9 @@ describe('Provider', () => { it('should ensure fetchChainAndNodeInfo always fetch new data', async () => { Provider.clearChainAndNodeCaches(); - const spyFetchChainAndNodeInfo = jest.spyOn(Provider.prototype, 'fetchChainAndNodeInfo'); - const spyFetchChain = jest.spyOn(Provider.prototype, 'fetchChain'); - const spyFetchNode = jest.spyOn(Provider.prototype, 'fetchNode'); + const spyFetchChainAndNodeInfo = vi.spyOn(Provider.prototype, 'fetchChainAndNodeInfo'); + const spyFetchChain = vi.spyOn(Provider.prototype, 'fetchChain'); + const spyFetchNode = vi.spyOn(Provider.prototype, 'fetchNode'); const provider = await Provider.create(FUEL_NETWORK_URL); @@ -835,7 +852,7 @@ describe('Provider', () => { throw new Error(); } - const spy = jest.spyOn(fuelTsVersionsMod, 'checkFuelCoreVersionCompatibility'); + const spy = vi.spyOn(fuelTsVersionsMod, 'checkFuelCoreVersionCompatibility'); spy.mockImplementationOnce(() => mock); await expectToThrowFuelError(() => Provider.create(FUEL_NETWORK_URL), { @@ -860,7 +877,7 @@ describe('Provider', () => { throw new Error(); } - const spy = jest.spyOn(fuelTsVersionsMod, 'checkFuelCoreVersionCompatibility'); + const spy = vi.spyOn(fuelTsVersionsMod, 'checkFuelCoreVersionCompatibility'); spy.mockImplementationOnce(() => mock); await expectToThrowFuelError(() => Provider.create(FUEL_NETWORK_URL), { @@ -886,9 +903,9 @@ describe('Provider', () => { usedFee: bn(1), }; - const estimateTxSpy = jest.spyOn(provider, 'estimateTxDependencies').mockImplementation(); + const estimateTxSpy = vi.spyOn(provider, 'estimateTxDependencies').mockResolvedValueOnce(); - const txCostSpy = jest + const txCostSpy = vi .spyOn(provider, 'getTransactionCost') .mockReturnValue(Promise.resolve(transactionCost)); @@ -921,9 +938,9 @@ describe('Provider', () => { usedFee: bn(1), }; - const estimateTxSpy = jest.spyOn(provider, 'estimateTxDependencies').mockImplementation(); + const estimateTxSpy = vi.spyOn(provider, 'estimateTxDependencies').mockResolvedValueOnce(); - const txCostSpy = jest + const txCostSpy = vi .spyOn(provider, 'getTransactionCost') .mockReturnValue(Promise.resolve(transactionCost)); @@ -939,6 +956,78 @@ describe('Provider', () => { expect(estimateTxSpy).toHaveBeenCalled(); }); + it('An invalid subscription request throws a FuelError and does not hold the test runner (closes all handles)', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + + await expectToThrowFuelError( + async () => { + for await (const value of provider.operations.statusChange({ + transactionId: 'invalid transaction id', + })) { + // shouldn't be reached and should fail if reached + expect(value).toBeFalsy(); + } + }, + + { code: FuelError.CODES.INVALID_REQUEST } + ); + + const response = new TransactionResponse('invalid transaction id', provider); + + await expectToThrowFuelError(() => response.waitForResult(), { + code: FuelError.CODES.INVALID_REQUEST, + }); + }); + + it('default timeout is undefined', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + expect(provider.options.timeout).toBeUndefined(); + }); + + it('throws TimeoutError on timeout when calling an operation', async () => { + const timeout = 500; + const provider = await Provider.create(FUEL_NETWORK_URL, { timeout }); + vi.spyOn(global, 'fetch').mockImplementationOnce((...args: unknown[]) => + sleep(timeout).then(() => + fetch(args[0] as RequestInfo | URL, args[1] as RequestInit | undefined) + ) + ); + + const { error } = await safeExec(async () => { + await provider.getBlocks({}); + }); + + expect(error).toMatchObject({ + code: 23, + name: 'TimeoutError', + message: 'The operation was aborted due to timeout', + }); + }); + + it('throws TimeoutError on timeout when calling a subscription', async () => { + const timeout = 500; + const provider = await Provider.create(FUEL_NETWORK_URL, { timeout }); + + vi.spyOn(global, 'fetch').mockImplementationOnce((...args: unknown[]) => + sleep(timeout).then(() => + fetch(args[0] as RequestInfo | URL, args[1] as RequestInit | undefined) + ) + ); + + const { error } = await safeExec(async () => { + for await (const iterator of provider.operations.statusChange({ + transactionId: 'doesnt matter, will be aborted', + })) { + // shouldn't be reached and should fail if reached + expect(iterator).toBeFalsy(); + } + }); + expect(error).toMatchObject({ + code: 23, + name: 'TimeoutError', + message: 'The operation was aborted due to timeout', + }); + }); it('should ensure calculateMaxgas considers gasLimit for ScriptTransactionRequest', async () => { const provider = await Provider.create(FUEL_NETWORK_URL); const { gasPerByte } = provider.getGasConfig(); @@ -948,7 +1037,7 @@ describe('Provider', () => { gasLimit, }); - const maxGasSpy = jest.spyOn(gasMod, 'getMaxGas'); + const maxGasSpy = vi.spyOn(gasMod, 'getMaxGas'); const chainInfo = provider.getChain(); const minGas = bn(200); @@ -977,7 +1066,7 @@ describe('Provider', () => { transactionRequest.addContractCreatedOutput(ZeroBytes32, ZeroBytes32); - const maxGasSpy = jest.spyOn(gasMod, 'getMaxGas'); + const maxGasSpy = vi.spyOn(gasMod, 'getMaxGas'); const chainInfo = provider.getChain(); const minGas = bn(700); @@ -1001,21 +1090,38 @@ describe('Provider', () => { const request = new ScriptTransactionRequest(); // forcing calculatePriceWithFactor to return 0 - const calculatePriceWithFactorMock = jest + const calculatePriceWithFactorMock = vi .spyOn(gasMod, 'calculatePriceWithFactor') .mockReturnValue(bn(0)); - const normalizeZeroToOneSpy = jest.spyOn(BN.prototype, 'normalizeZeroToOne'); - const { minFee, maxFee, usedFee } = await provider.getTransactionCost(request); expect(calculatePriceWithFactorMock).toHaveBeenCalledTimes(3); - expect(normalizeZeroToOneSpy).toHaveBeenCalledTimes(3); - expect(normalizeZeroToOneSpy).toHaveReturnedWith(bn(1)); - expect(maxFee.eq(0)).not.toBeTruthy(); expect(usedFee.eq(0)).not.toBeTruthy(); expect(minFee.eq(0)).not.toBeTruthy(); }); + + it('should accept string addresses in methods that require an address', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + + const b256Str = Address.fromRandom().toB256(); + + const methodCalls = [ + () => provider.getBalance(b256Str, BaseAssetId), + () => provider.getCoins(b256Str), + () => provider.getResourcesForTransaction(b256Str, new ScriptTransactionRequest()), + () => provider.getResourcesToSpend(b256Str, []), + () => provider.getContractBalance(b256Str, BaseAssetId), + () => provider.getBalances(b256Str), + () => provider.getMessages(b256Str), + ]; + + const promises = methodCalls.map(async (call) => { + await expect(call()).resolves.toBeTruthy(); + }); + + await Promise.all(promises); + }); }); diff --git a/packages/providers/tsconfig.json b/packages/providers/tsconfig.json index c723e93fb3c..b22c89a4b35 100644 --- a/packages/providers/tsconfig.json +++ b/packages/providers/tsconfig.json @@ -3,5 +3,5 @@ "compilerOptions": { "outDir": "./dist" }, - "include": ["src/**/*.ts", "src/**/*.json"] + "include": ["src", "test"] } diff --git a/packages/script/package.json b/packages/script/package.json index d19b9d63898..0b781c019af 100644 --- a/packages/script/package.json +++ b/packages/script/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/script/src/script-invocation-scope.ts b/packages/script/src/script-invocation-scope.ts index e4b4ff409a4..5caeaa7b7f6 100644 --- a/packages/script/src/script-invocation-scope.ts +++ b/packages/script/src/script-invocation-scope.ts @@ -1,13 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { FuelError } from '@fuel-ts/errors'; import type { AbstractScript } from '@fuel-ts/interfaces'; -import { - ScriptRequest, - assert, - FunctionInvocationScope, - FunctionInvocationResult, -} from '@fuel-ts/program'; -import type { InvocationScopeLike } from '@fuel-ts/program'; +import { ScriptRequest, FunctionInvocationScope } from '@fuel-ts/program'; import type { Provider } from '@fuel-ts/providers'; import { ByteArrayCoder } from '@fuel-ts/transactions'; @@ -49,24 +43,4 @@ export class ScriptInvocationScope< () => [] as unknown as TReturn ); } - - /** - * Submits a script transaction to the blockchain. - */ - async call(): Promise> { - assert(this.program.account, 'Provider is required!'); - - const transactionRequest = await this.getTransactionRequest(); - const { maxFee } = await this.getTransactionCost(); - await this.fundWithRequiredCoins(maxFee); - - const response = await this.program.account.sendTransaction(transactionRequest); - - return FunctionInvocationResult.build( - this as unknown as InvocationScopeLike, - response, - false, - this.program - ); - } } diff --git a/packages/script/src/script.test.ts b/packages/script/src/script.test.ts index cc83ec59a36..a11b0c8eebd 100644 --- a/packages/script/src/script.test.ts +++ b/packages/script/src/script.test.ts @@ -76,6 +76,9 @@ type MyStruct = { arg_two: BigNumberish; }; +/** + * @group node + */ describe('Script', () => { let scriptRequest: ScriptRequest; beforeAll(() => { diff --git a/packages/signer/README.md b/packages/signer/README.md index a59eb7d6545..e588110d115 100644 --- a/packages/signer/README.md +++ b/packages/signer/README.md @@ -18,7 +18,7 @@ This module contains utilities for secp256-k1 signing, verifying and recovery op -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/signer/package.json b/packages/signer/package.json index b6e678c63c9..06f90e09b61 100644 --- a/packages/signer/package.json +++ b/packages/signer/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { @@ -29,10 +29,7 @@ "@fuel-ts/crypto": "workspace:*", "@fuel-ts/hasher": "workspace:*", "@fuel-ts/math": "workspace:*", - "elliptic": "^6.5.4", - "ethers": "^6.7.1" - }, - "devDependencies": { - "@types/elliptic": "^6.4.14" + "ethers": "^6.7.1", + "@noble/curves": "^1.3.0" } } diff --git a/packages/signer/src/index.ts b/packages/signer/src/index.ts index 3eface77c7f..af5d8964d66 100644 --- a/packages/signer/src/index.ts +++ b/packages/signer/src/index.ts @@ -1,2 +1 @@ -export { default as Signer } from './signer'; export * from './signer'; diff --git a/packages/signer/src/signer.test.ts b/packages/signer/src/signer.test.ts index 954aa3be7ab..2d0635135c7 100644 --- a/packages/signer/src/signer.test.ts +++ b/packages/signer/src/signer.test.ts @@ -1,7 +1,11 @@ import { getBytesCopy, sha256 } from 'ethers'; -import Signer from './signer'; +import { Signer } from './signer'; +/** + * @group node + * @group browser + */ describe('Signer', () => { const expectedPrivateKey = '0x5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1'; const expectedPublicKey = diff --git a/packages/signer/src/signer.ts b/packages/signer/src/signer.ts index 364cd10397a..0444a4c1ece 100644 --- a/packages/signer/src/signer.ts +++ b/packages/signer/src/signer.ts @@ -2,24 +2,11 @@ import { Address } from '@fuel-ts/address'; import { randomBytes } from '@fuel-ts/crypto'; import { hash } from '@fuel-ts/hasher'; import { toBytes } from '@fuel-ts/math'; -import * as elliptic from 'elliptic'; -import { hexlify, concat, getBytesCopy } from 'ethers'; +import { secp256k1 } from '@noble/curves/secp256k1'; import type { BytesLike } from 'ethers'; +import { hexlify, concat, getBytesCopy } from 'ethers'; -/* Importing `ec` like this to avoid the 'Requested module is a CommonJS module, - * which may not support all module.exports as named exports' error - * @see https://github.com/FuelLabs/fuels-ts/issues/841 - */ -const { ec: EC } = elliptic; - -/** - * Return elliptic instance with curve secp256k1 - */ -export function getCurve() { - return new EC('secp256k1'); -} - -class Signer { +export class Signer { readonly address: Address; readonly publicKey: string; @@ -42,16 +29,15 @@ class Signer { privateKey = `0x${privateKey}`; } } - // Convert to byte array, normalize private key input allowing it to be BytesLike // like remove 0x prefix and accept array of bytes - const privateKeyBytes = getBytesCopy(privateKey); - const keyPair = getCurve().keyFromPrivate(privateKeyBytes, 'hex'); + const privateKeyBytes = toBytes(privateKey, 32); - // Slice(1) removes the encoding scheme from the public key - this.compressedPublicKey = hexlify(Uint8Array.from(keyPair.getPublic(true, 'array'))); - this.publicKey = hexlify(Uint8Array.from(keyPair.getPublic(false, 'array').slice(1))); this.privateKey = hexlify(privateKeyBytes); + + // Slice(1) removes the encoding scheme from the public key + this.publicKey = hexlify(secp256k1.getPublicKey(privateKeyBytes, false).slice(1)); + this.compressedPublicKey = hexlify(secp256k1.getPublicKey(privateKeyBytes, true)); this.address = Address.fromPublicKey(this.publicKey); } @@ -64,15 +50,13 @@ class Signer { * @returns hashed signature */ sign(data: BytesLike) { - const keyPair = getCurve().keyFromPrivate(getBytesCopy(this.privateKey), 'hex'); - const signature = keyPair.sign(getBytesCopy(data), { - canonical: true, - }); - const r = toBytes(signature.r, 32); - const s = toBytes(signature.s, 32); + const signature = secp256k1.sign(getBytesCopy(data), getBytesCopy(this.privateKey)); + + const r = toBytes(`0x${signature.r.toString(16)}`, 32); + const s = toBytes(`0x${signature.s.toString(16)}`, 32); // add recoveryParam to first s byte - s[0] |= (signature.recoveryParam || 0) << 7; + s[0] |= (signature.recovery || 0) << 7; return concat([r, s]); } @@ -84,11 +68,10 @@ class Signer { * @returns compressed point on the curve */ addPoint(point: BytesLike) { - const p0 = getCurve().keyFromPublic(getBytesCopy(this.compressedPublicKey)); - const p1 = getCurve().keyFromPublic(getBytesCopy(point)); - const result = p0.getPublic().add(p1.getPublic()); - - return hexlify(Uint8Array.from(result.encode('array', true))); + const p0 = secp256k1.ProjectivePoint.fromHex(getBytesCopy(this.compressedPublicKey)); + const p1 = secp256k1.ProjectivePoint.fromHex(getBytesCopy(point)); + const result = p0.add(p1); + return `0x${result.toHex(true)}`; } /** @@ -107,12 +90,12 @@ class Signer { // remove recoveryParam from s first byte s[0] &= 0x7f; - const publicKey = getCurve() - .recoverPubKey(getBytesCopy(data), { r, s }, recoveryParam) - .encode('array', false) - .slice(1); + const sig = new secp256k1.Signature(BigInt(hexlify(r)), BigInt(hexlify(s))).addRecoveryBit( + recoveryParam + ); - return hexlify(Uint8Array.from(publicKey)); + const publicKey = sig.recoverPublicKey(getBytesCopy(data)).toRawBytes(false).slice(1); + return hexlify(publicKey); } /** @@ -143,9 +126,7 @@ class Signer { * @returns extended publicKey */ static extendPublicKey(publicKey: BytesLike) { - const keyPair = getCurve().keyFromPublic(getBytesCopy(publicKey)); - return hexlify(Uint8Array.from(keyPair.getPublic(false, 'array').slice(1))); + const point = secp256k1.ProjectivePoint.fromHex(getBytesCopy(publicKey)); + return hexlify(point.toRawBytes(false).slice(1)); } } - -export default Signer; diff --git a/packages/signer/tsdoc.json b/packages/signer/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/signer/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/testcases/tsdoc.json b/packages/testcases/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/testcases/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/transactions/README.md b/packages/transactions/README.md index dea49a86ef9..045b842f12c 100644 --- a/packages/transactions/README.md +++ b/packages/transactions/README.md @@ -18,7 +18,7 @@ This module contains various constants and functions for encoding and decoding s -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/transactions/package.json b/packages/transactions/package.json index c14eeed601b..6c10a97659c 100644 --- a/packages/transactions/package.json +++ b/packages/transactions/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/transactions/src/coders/byte-array.test.ts b/packages/transactions/src/coders/byte-array.test.ts index 9bb45ae3063..9d141aabb8e 100644 --- a/packages/transactions/src/coders/byte-array.test.ts +++ b/packages/transactions/src/coders/byte-array.test.ts @@ -2,6 +2,10 @@ import { getBytesCopy, hexlify } from 'ethers'; import { ByteArrayCoder } from './byte-array'; +/** + * @group node + * @group browser + */ describe('ByteArrayCoder', () => { it('Can encode empty byte[]', () => { const bytes = getBytesCopy('0x'); diff --git a/packages/transactions/src/coders/input.test.ts b/packages/transactions/src/coders/input.test.ts index 54302e2699c..b7701fc3909 100644 --- a/packages/transactions/src/coders/input.test.ts +++ b/packages/transactions/src/coders/input.test.ts @@ -9,6 +9,10 @@ import { InputMessageCoder, InputCoder, InputType } from './input'; const B256 = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'; const MAX_U32 = 2 ** 32 - 1; +/** + * @group node + * @group browser + */ describe('InputCoder', () => { it('Can encode Coin', () => { const input: Input = { diff --git a/packages/transactions/src/coders/output.test.ts b/packages/transactions/src/coders/output.test.ts index 3d33bc461a3..d9e17237b30 100644 --- a/packages/transactions/src/coders/output.test.ts +++ b/packages/transactions/src/coders/output.test.ts @@ -6,6 +6,10 @@ import { OutputCoder, OutputType } from './output'; const B256 = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'; +/** + * @group node + * @group browser + */ describe('OutputCoder', () => { it('Can encode Coin', () => { const output: Output = { diff --git a/packages/transactions/src/coders/policy.test.ts b/packages/transactions/src/coders/policy.test.ts index ad20b402647..b799745a912 100644 --- a/packages/transactions/src/coders/policy.test.ts +++ b/packages/transactions/src/coders/policy.test.ts @@ -5,6 +5,10 @@ import { bn } from '@fuel-ts/math'; import type { Policy } from './policy'; import { PoliciesCoder, PolicyType } from './policy'; +/** + * @group node + * @group browser + */ describe('PoliciesCoder', () => { describe('encode', () => { it('should encode policy correctly (GasPrice)', () => { diff --git a/packages/transactions/src/coders/receipt.test.ts b/packages/transactions/src/coders/receipt.test.ts index cddcca27750..3b4a711d0b0 100644 --- a/packages/transactions/src/coders/receipt.test.ts +++ b/packages/transactions/src/coders/receipt.test.ts @@ -10,6 +10,10 @@ const B256_ALT2 = '0x68b401b682ba0c9018150cca596358a6b98576337ea10b9cfb0d02441b3 const B256_ALT3 = '0xeb03488970d05ea240c788a0ea2e07176cc5317b7c7c89f26ac5282bbcd445bd'; const B256_ALT4 = '0x2f6d40e3ac1a172fb9445f9843440a0fc383bea238a7a35a77a3c73d36902992'; +/** + * @group node + * @group browser + */ describe('ReceiptCoder', () => { it('Can encode Call', () => { const receipt: Receipt = { diff --git a/packages/transactions/src/coders/receipt.ts b/packages/transactions/src/coders/receipt.ts index ba80cc8925b..a00a38637eb 100644 --- a/packages/transactions/src/coders/receipt.ts +++ b/packages/transactions/src/coders/receipt.ts @@ -767,7 +767,7 @@ export type ReceiptMint = { is: BN; }; -const getAssetIdForMintAndBurnReceipts = (contractId: string, subId: string): string => { +export const getAssetId = (contractId: string, subId: string): string => { const contractIdBytes = getBytesCopy(contractId); const subIdBytes = getBytesCopy(subId); @@ -780,7 +780,7 @@ export class ReceiptMintCoder extends Coder { } static getAssetId(contractId: string, subId: string): string { - return getAssetIdForMintAndBurnReceipts(contractId, subId); + return getAssetId(contractId, subId); } encode(value: ReceiptMint): Uint8Array { @@ -848,7 +848,7 @@ export class ReceiptBurnCoder extends Coder { } static getAssetId(contractId: string, subId: string): string { - return getAssetIdForMintAndBurnReceipts(contractId, subId); + return getAssetId(contractId, subId); } encode(value: ReceiptBurn): Uint8Array { diff --git a/packages/transactions/src/coders/storage-slot.test.ts b/packages/transactions/src/coders/storage-slot.test.ts index 758da11e06d..72d0842ac1d 100644 --- a/packages/transactions/src/coders/storage-slot.test.ts +++ b/packages/transactions/src/coders/storage-slot.test.ts @@ -4,6 +4,10 @@ import { getBytesCopy, hexlify } from 'ethers'; import type { StorageSlot } from './storage-slot'; import { StorageSlotCoder } from './storage-slot'; +/** + * @group node + * @group browser + */ describe('StorageSlotCoder', () => { it('Can encode and decode', () => { const storageSlot: StorageSlot = { diff --git a/packages/transactions/src/coders/transaction.test.ts b/packages/transactions/src/coders/transaction.test.ts index d9349f94771..3c313736bec 100644 --- a/packages/transactions/src/coders/transaction.test.ts +++ b/packages/transactions/src/coders/transaction.test.ts @@ -12,6 +12,10 @@ const U32 = 1000; const U16 = 32; const U8 = 1; +/** + * @group node + * @group browser + */ describe('TransactionCoder', () => { it('Can encode/decode TransactionScript without inputs, outputs and witnesses', () => { const transaction: Transaction = { diff --git a/packages/transactions/src/coders/tx-pointer.test.ts b/packages/transactions/src/coders/tx-pointer.test.ts index afd3237a982..a7d962be817 100644 --- a/packages/transactions/src/coders/tx-pointer.test.ts +++ b/packages/transactions/src/coders/tx-pointer.test.ts @@ -5,6 +5,10 @@ import { TxPointerCoder } from './tx-pointer'; const B256 = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'; +/** + * @group node + * @group browser + */ describe('TxPointerCoder', () => { it('can encode TxPointer', () => { const txPointer: TxPointer = { diff --git a/packages/transactions/src/coders/utxo-id.test.ts b/packages/transactions/src/coders/utxo-id.test.ts index 947bc1124f7..0e4f9366f92 100644 --- a/packages/transactions/src/coders/utxo-id.test.ts +++ b/packages/transactions/src/coders/utxo-id.test.ts @@ -5,6 +5,10 @@ import { UtxoIdCoder } from './utxo-id'; const B256 = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'; +/** + * @group node + * @group browser + */ describe('UtxoIdCoder', () => { it('can encode UtxoId', () => { const utxoId: UtxoId = { diff --git a/packages/transactions/src/coders/witness.test.ts b/packages/transactions/src/coders/witness.test.ts index 167f6bf01d3..206049651e4 100644 --- a/packages/transactions/src/coders/witness.test.ts +++ b/packages/transactions/src/coders/witness.test.ts @@ -3,6 +3,10 @@ import { getBytesCopy, hexlify } from 'ethers'; import type { Witness } from './witness'; import { WitnessCoder } from './witness'; +/** + * @group node + * @group browser + */ describe('WitnessCoder', () => { it('Can encode empty Witness', () => { const witness: Witness = { diff --git a/packages/transactions/tsdoc.json b/packages/transactions/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/transactions/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/utils/README.md b/packages/utils/README.md index fb0e694228c..8f8a3e96202 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -18,7 +18,7 @@ It's a collection of utilities and test utilities that may be useful in other pl -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Installation diff --git a/packages/utils/package.json b/packages/utils/package.json index 94525cde1ff..482fc781e1f 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { @@ -19,12 +19,20 @@ "require": "./dist/test-utils.js", "import": "./dist/test-utils.mjs", "types": "./dist/test-utils.d.ts" + }, + "./cli-utils": { + "require": "./dist/cli-utils.js", + "import": "./dist/cli-utils.mjs", + "types": "./dist/cli-utils.d.ts" } }, "typesVersions": { "*": { "test-utils": [ "./dist/test-utils.d.ts" + ], + "cli-utils": [ + "./dist/cli-utils.d.ts" ] } }, @@ -39,7 +47,7 @@ }, "license": "Apache-2.0", "dependencies": { - "ethers": "^6.7.1", + "@fuel-ts/interfaces": "workspace:*", "ramda": "^0.29.0", "rimraf": "^3.0.2" }, diff --git a/packages/utils/src/cli-utils.ts b/packages/utils/src/cli-utils.ts new file mode 100644 index 00000000000..eaac016729a --- /dev/null +++ b/packages/utils/src/cli-utils.ts @@ -0,0 +1 @@ +export * from './cli-utils/findBinPath'; diff --git a/packages/utils/src/cli-utils/findBinPath.test.ts b/packages/utils/src/cli-utils/findBinPath.test.ts new file mode 100644 index 00000000000..cb1ddde4c9c --- /dev/null +++ b/packages/utils/src/cli-utils/findBinPath.test.ts @@ -0,0 +1,57 @@ +import { safeExec } from '@fuel-ts/errors/test-utils'; +import { mkdirSync, rmSync, writeFileSync } from 'fs'; +import { join } from 'path'; + +import { findBinPath } from './findBinPath'; + +/** + * @group node + */ +describe('findBinPath', () => { + const bootstrap = (dir: string) => { + const cmdName = 'my-cmd'; + const mods = join(dir, 'node_modules'); + const bin = join(mods, '.bin'); + const cmdPath = join(bin, cmdName); + + const resetDisk = () => rmSync(mods, { recursive: true }); + + mkdirSync(bin, { recursive: true }); + writeFileSync(cmdPath, ''); + + return { resetDisk, mods, cmdName, cmdPath }; + }; + + it('should find bin path in current dir', () => { + const base = __dirname; // current dir + const { cmdName, cmdPath, resetDisk } = bootstrap(base); + const binPath = findBinPath(cmdName, base); + + resetDisk(); + expect(binPath).toEqual(cmdPath); + }); + + it('should find bin path one dir up', () => { + const base = join(__dirname, '..'); // one dir up + const { cmdName, cmdPath, resetDisk } = bootstrap(base); + const binPath = findBinPath(cmdName, base); + + resetDisk(); + expect(binPath).toEqual(cmdPath); + }); + + it('should find bin path two dir up', () => { + const base = join(__dirname, '..', '..'); // two dirs up + const { cmdName, cmdPath, resetDisk } = bootstrap(base); + const binPath = findBinPath(cmdName, base); + + resetDisk(); + expect(binPath).toEqual(cmdPath); + }); + + it('should throw for bin path not found', async () => { + const cmdName = 'non-existent'; + const { error } = await safeExec(() => findBinPath(cmdName, __dirname)); + expect(error?.message).toEqual(`Command not found: ${cmdName}`); + }); +}); diff --git a/packages/utils/src/cli-utils/findBinPath.ts b/packages/utils/src/cli-utils/findBinPath.ts new file mode 100644 index 00000000000..7f255e01bd1 --- /dev/null +++ b/packages/utils/src/cli-utils/findBinPath.ts @@ -0,0 +1,17 @@ +import { existsSync } from 'fs'; +import { join } from 'path'; + +export const findBinPath = (binCommandName: string, startingDir: string): string => { + const cmdPath = join(startingDir, 'node_modules', '.bin', binCommandName); + const parentDir = join(startingDir, '..'); + + if (existsSync(cmdPath)) { + return cmdPath; + } + + if (parentDir === startingDir) { + throw new Error(`Command not found: ${binCommandName}`); + } + + return findBinPath(binCommandName, parentDir); +}; diff --git a/packages/utils/src/index.test.ts b/packages/utils/src/index.test.ts index ecc66d9618d..4d28f6ff581 100644 --- a/packages/utils/src/index.test.ts +++ b/packages/utils/src/index.test.ts @@ -1,5 +1,9 @@ import * as indexMod from '.'; +/** + * @group node + * @group browser + */ describe('index.js', () => { test('should export all utilities', () => { expect(indexMod.normalizeString).toBeTruthy(); diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 5278d7f3b09..7004b0e889f 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -4,3 +4,4 @@ export * from './utils/concat'; export * from './utils/arrayify'; export * from './utils/hexlify'; export * from './utils/normalizeString'; +export * from './utils/defaultChainConfig'; diff --git a/packages/utils/src/test-util.test.ts b/packages/utils/src/test-util.test.ts index e0cecd3dfe6..f2a9a9f00fe 100644 --- a/packages/utils/src/test-util.test.ts +++ b/packages/utils/src/test-util.test.ts @@ -1,5 +1,8 @@ import * as indexMod from './test-utils'; +/** + * @group node + */ describe('index.js', () => { test('should export all test utilities', () => { expect(indexMod.getForcProject).toBeTruthy(); diff --git a/packages/utils/src/test-utils/expectToBeInRange.test.ts b/packages/utils/src/test-utils/expectToBeInRange.test.ts index 95404f65907..6ebe61e1e7a 100644 --- a/packages/utils/src/test-utils/expectToBeInRange.test.ts +++ b/packages/utils/src/test-utils/expectToBeInRange.test.ts @@ -1,5 +1,9 @@ import { expectToBeInRange } from './expectToBeInRange'; +/** + * @group node + * @group browser + */ describe('expectValueToBeInRange', () => { it('should throw an error when value is less than the minimum', () => { expect(() => diff --git a/packages/utils/src/test-utils/getForcProject.test.ts b/packages/utils/src/test-utils/getForcProject.test.ts index 6af5a15d655..a41e47a5f2d 100644 --- a/packages/utils/src/test-utils/getForcProject.test.ts +++ b/packages/utils/src/test-utils/getForcProject.test.ts @@ -1,7 +1,8 @@ -import * as ethers from 'ethers'; import * as fs from 'fs'; import * as path from 'path'; +import * as utilsMod from '../index'; + import { getForcProject, getProjectAbiPath, @@ -13,24 +14,35 @@ import { getProjectTempDir, } from './getForcProject'; -jest.mock('path', () => ({ - __esModule: true, - ...jest.requireActual('path'), -})); +vi.mock('path', async () => { + const mod = await vi.importActual('path'); + return { + __esModule: true, + ...mod, + }; +}); -jest.mock('fs', () => ({ - __esModule: true, - ...jest.requireActual('fs'), -})); +vi.mock('fs', async () => { + const mod = await vi.importActual('fs'); + return { + __esModule: true, + ...mod, + }; +}); +/** + * @group node + */ describe('getForcProject', () => { - afterEach(jest.restoreAllMocks); + afterEach(() => { + vi.restoreAllMocks(); + }); it('should return the correct temporary directory path on getProjectTempDir', () => { const params = { projectDir: '/path/to/project', projectName: 'myProject' }; const expectedPath = '/path/to/project/out/debug/__temp__'; - jest.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); + vi.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); const tempDir = getProjectTempDir(params); @@ -41,7 +53,7 @@ describe('getForcProject', () => { const params = { projectDir: '/path/to/project', projectName: 'myProject' }; const expectedPath = '/path/to/project/out/debug/myProject-abi.json'; - jest.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); + vi.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); const abiPath = getProjectAbiPath(params); @@ -52,7 +64,7 @@ describe('getForcProject', () => { const params = { projectDir: '/path/to/project', projectName: 'myProject' }; const expectedPath = '/path/to/project/out/debug/myProject.bin'; - jest.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); + vi.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); const binPath = getProjectBinPath(params); @@ -63,7 +75,7 @@ describe('getForcProject', () => { const params = { projectDir: '/path/to/project', projectName: 'myProject' }; const expectedPath = '/path/to/project/out/debug/myProject-storage_slots.json'; - jest.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); + vi.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); const storageSlotsPath = getProjectStorageSlotsPath(params); @@ -84,9 +96,9 @@ describe('getForcProject', () => { const expectedPath = '/path/to/project/out/debug/myProject-storage_slots.json'; const fakeStorageSlots = [{ key: 'key1', value: 'value1' }]; - jest.spyOn(fs, 'existsSync').mockReturnValue(true); + vi.spyOn(fs, 'existsSync').mockReturnValue(true); - const readFileSyncSpy = jest + const readFileSyncSpy = vi .spyOn(fs, 'readFileSync') .mockReturnValue(JSON.stringify(fakeStorageSlots)); @@ -97,9 +109,7 @@ describe('getForcProject', () => { }); it('should return the correct debug directory path', () => { - const joinSpy = jest - .spyOn(path, 'join') - .mockImplementation((...segments) => segments.join('/')); + const joinSpy = vi.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); const params = { projectDir: '/path/to/project', projectName: 'myProject' }; const expectedPath = '/path/to/project/out/debug'; @@ -113,8 +123,8 @@ describe('getForcProject', () => { it('should return an empty array if the storage slots file does not exist', () => { const params = { projectDir: '/path/to/project', projectName: 'myProject' }; - jest.spyOn(fs, 'existsSync').mockReturnValue(false); - const readFileSyncSpy = jest.spyOn(fs, 'readFileSync'); + vi.spyOn(fs, 'existsSync').mockReturnValue(false); + const readFileSyncSpy = vi.spyOn(fs, 'readFileSync'); const storageSlots = getProjectStorageSlots(params); @@ -127,9 +137,9 @@ describe('getForcProject', () => { const fakeAbiContent = { contracts: {} }; const fakeStorageSlots = [{ key: 'key1', value: 'value1' }]; - jest.spyOn(ethers, 'hexlify').mockImplementation((param) => param as string); - jest.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); - jest.spyOn(fs, 'readFileSync').mockImplementation((pathParam) => { + vi.spyOn(utilsMod, 'hexlify').mockImplementation((param) => param as string); + vi.spyOn(path, 'join').mockImplementation((...segments) => segments.join('/')); + vi.spyOn(fs, 'readFileSync').mockImplementation((pathParam) => { if ((pathParam).endsWith('.bin')) { return fakeBinContent; } @@ -142,9 +152,9 @@ describe('getForcProject', () => { throw new Error('File not found'); }); - jest - .spyOn(fs, 'existsSync') - .mockImplementation((pathParam) => (pathParam).endsWith('-storage_slots.json')); + vi.spyOn(fs, 'existsSync').mockImplementation((pathParam) => + (pathParam).endsWith('-storage_slots.json') + ); const forcProject = { projectDir: 'fuel_gauge', diff --git a/packages/utils/src/test-utils/getForcProject.ts b/packages/utils/src/test-utils/getForcProject.ts index 877cf068dd3..b68fdbe1572 100644 --- a/packages/utils/src/test-utils/getForcProject.ts +++ b/packages/utils/src/test-utils/getForcProject.ts @@ -1,7 +1,7 @@ -import { hexlify } from 'ethers'; import { existsSync, readFileSync } from 'fs'; import { join } from 'path'; +import { hexlify } from '../index'; import { normalizeString } from '../utils/normalizeString'; interface IGetForcProjectParams { diff --git a/packages/utils/src/utils/arrayify.test.ts b/packages/utils/src/utils/arrayify.test.ts index 1c5fa8d1c16..970d5682612 100644 --- a/packages/utils/src/utils/arrayify.test.ts +++ b/packages/utils/src/utils/arrayify.test.ts @@ -1,5 +1,9 @@ import { arrayify } from './arrayify'; +/** + * @group node + * @group browser + */ describe('arrayify', () => { it('returns Uint8Array from Uint8Array', () => { expect(arrayify(new Uint8Array([0, 1, 2, 3]))).toEqual(new Uint8Array([0, 1, 2, 3])); diff --git a/packages/utils/src/utils/arrayify.ts b/packages/utils/src/utils/arrayify.ts index 4251ed8071d..24eeb00c13e 100644 --- a/packages/utils/src/utils/arrayify.ts +++ b/packages/utils/src/utils/arrayify.ts @@ -1,5 +1,5 @@ import { FuelError, ErrorCode } from '@fuel-ts/errors'; -import type { BytesLike } from 'ethers'; +import type { BytesLike } from '@fuel-ts/interfaces'; /** * Converts a bytes-like value to a `Uint8Array`. diff --git a/packages/utils/src/utils/capitalizeString.test.ts b/packages/utils/src/utils/capitalizeString.test.ts index 94bcc630a47..9ae643912be 100644 --- a/packages/utils/src/utils/capitalizeString.test.ts +++ b/packages/utils/src/utils/capitalizeString.test.ts @@ -1,5 +1,9 @@ import { capitalizeString } from './capitalizeString'; +/** + * @group node + * @group browser + */ describe('capitalizeString', () => { test('should capitalize string', () => { expect(capitalizeString('test')).toEqual('Test'); diff --git a/packages/utils/src/utils/chainConfig.json b/packages/utils/src/utils/chainConfig.json new file mode 100644 index 00000000000..a89ba095811 --- /dev/null +++ b/packages/utils/src/utils/chainConfig.json @@ -0,0 +1,515 @@ +{ + "chain_name": "local_testnet", + "block_gas_limit": 5000000000, + "initial_state": { + "coins": [ + { + "owner": "0x94ffcc53b892684acefaebc8a3d4a595e528a8cf664eeb3ef36f1020b0809d0d", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "owner": "0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "owner": "0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "owner": "0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82", + "amount": "0xFFFFFFFFFFFFFFFF", + "asset_id": "0x0202020202020202020202020202020202020202020202020202020202020202" + } + ], + "messages": [ + { + "sender": "0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f", + "recipient": "0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba", + "nonce": "0101010101010101010101010101010101010101010101010101010101010101", + "amount": "0x000000000000FFFF", + "data": "", + "da_height": "0x00" + }, + { + "sender": "0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba", + "recipient": "0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f", + "nonce": "0e1ef2963832068b0e1ef2963832068b0e1ef2963832068b0e1ef2963832068b", + "amount": "0xb04f3c08f59b309e", + "data": "", + "da_height": "0x00" + } + ] + }, + "consensus_parameters": { + "tx_params": { + "max_inputs": 255, + "max_outputs": 255, + "max_witnesses": 255, + "max_gas_per_tx": 10000000, + "max_size": 17825792 + }, + "predicate_params": { + "max_predicate_length": 1048576, + "max_predicate_data_length": 1048576, + "max_gas_per_predicate": 10000000, + "max_message_data_length": 1048576 + }, + "script_params": { + "max_script_length": 1048576, + "max_script_data_length": 1048576 + }, + "contract_params": { + "contract_max_size": 16777216, + "max_storage_slots": 255 + }, + "fee_params": { + "gas_price_factor": 92, + "gas_per_byte": 4 + } + }, + "gas_costs": { + "add": 1, + "addi": 1, + "aloc": 1, + "and": 1, + "andi": 1, + "bal": 13, + "bhei": 1, + "bhsh": 1, + "burn": 132, + "cb": 1, + "cfei": 1, + "cfsi": 1, + "croo": 16, + "div": 1, + "divi": 1, + "ecr1": 3000, + "eck1": 951, + "ed19": 3000, + "eq": 1, + "exp": 1, + "expi": 1, + "flag": 1, + "gm": 1, + "gt": 1, + "gtf": 1, + "ji": 1, + "jmp": 1, + "jne": 1, + "jnei": 1, + "jnzi": 1, + "jmpf": 1, + "jmpb": 1, + "jnzf": 1, + "jnzb": 1, + "jnef": 1, + "jneb": 1, + "lb": 1, + "log": 9, + "lt": 1, + "lw": 1, + "mint": 135, + "mlog": 1, + "modOp": 1, + "modi": 1, + "moveOp": 1, + "movi": 1, + "mroo": 2, + "mul": 1, + "muli": 1, + "mldv": 1, + "noop": 1, + "not": 1, + "or": 1, + "ori": 1, + "poph": 2, + "popl": 2, + "pshh": 2, + "pshl": 2, + "ret": 13, + "rvrt": 13, + "sb": 1, + "sll": 1, + "slli": 1, + "srl": 1, + "srli": 1, + "srw": 12, + "sub": 1, + "subi": 1, + "sw": 1, + "sww": 67, + "time": 1, + "tr": 105, + "tro": 60, + "wdcm": 1, + "wqcm": 1, + "wdop": 1, + "wqop": 1, + "wdml": 1, + "wqml": 1, + "wddv": 1, + "wqdv": 2, + "wdmd": 3, + "wqmd": 4, + "wdam": 2, + "wqam": 3, + "wdmm": 3, + "wqmm": 3, + "xor": 1, + "xori": 1, + "call": { + "LightOperation": { + "base": 144, + "units_per_gas": 214 + } + }, + "ccp": { + "LightOperation": { + "base": 15, + "units_per_gas": 103 + } + }, + "csiz": { + "LightOperation": { + "base": 17, + "units_per_gas": 790 + } + }, + "k256": { + "LightOperation": { + "base": 11, + "units_per_gas": 214 + } + }, + "ldc": { + "LightOperation": { + "base": 15, + "units_per_gas": 272 + } + }, + "logd": { + "LightOperation": { + "base": 26, + "units_per_gas": 64 + } + }, + "mcl": { + "LightOperation": { + "base": 1, + "units_per_gas": 3333 + } + }, + "mcli": { + "LightOperation": { + "base": 1, + "units_per_gas": 3333 + } + }, + "mcp": { + "LightOperation": { + "base": 1, + "units_per_gas": 2000 + } + }, + "mcpi": { + "LightOperation": { + "base": 3, + "units_per_gas": 2000 + } + }, + "meq": { + "LightOperation": { + "base": 1, + "units_per_gas": 2500 + } + }, + "retd": { + "LightOperation": { + "base": 29, + "units_per_gas": 62 + } + }, + "s256": { + "LightOperation": { + "base": 2, + "units_per_gas": 214 + } + }, + "scwq": { + "LightOperation": { + "base": 13, + "units_per_gas": 5 + } + }, + "smo": { + "LightOperation": { + "base": 209, + "units_per_gas": 55 + } + }, + "srwq": { + "LightOperation": { + "base": 47, + "units_per_gas": 5 + } + }, + "swwq": { + "LightOperation": { + "base": 44, + "units_per_gas": 5 + } + }, + "contract_root": { + "LightOperation": { + "base": 75, + "units_per_gas": 1 + } + }, + "state_root": { + "LightOperation": { + "base": 412, + "units_per_gas": 1 + } + }, + "vm_initialization": { + "HeavyOperation": { + "base": 2000, + "gas_per_unit": 0 + } + }, + "new_storage_per_byte": 1 + }, + "consensus": { + "PoA": { + "signing_key": "0x94ffcc53b892684acefaebc8a3d4a595e528a8cf664eeb3ef36f1020b0809d0d" + } + } +} diff --git a/packages/utils/src/utils/chunkAndPadBytes.test.ts b/packages/utils/src/utils/chunkAndPadBytes.test.ts index 36ff1aee968..a4587965d65 100644 --- a/packages/utils/src/utils/chunkAndPadBytes.test.ts +++ b/packages/utils/src/utils/chunkAndPadBytes.test.ts @@ -1,10 +1,14 @@ -import { getBytesCopy } from 'ethers'; +import { arrayify } from '../index'; import { chunkAndPadBytes } from './chunkAndPadBytes'; +/** + * @group node + * @group browser + */ describe('chunkAndPadBytes', () => { it('can chunk and pad bytes to 16 KiB', () => { - const bytes = getBytesCopy( + const bytes = arrayify( '0x900000044700000000000000000000345dfcc00110fff3005d4060495d47f000134904407348000c72f0007b36f0000024040000000000002151bd4b' ); const chunkSize = 16 * 1024; diff --git a/packages/utils/src/utils/concat.test.ts b/packages/utils/src/utils/concat.test.ts index d384d918d4c..763b4cbf611 100644 --- a/packages/utils/src/utils/concat.test.ts +++ b/packages/utils/src/utils/concat.test.ts @@ -1,7 +1,10 @@ -import { getBytesCopy } from 'ethers'; - +import { arrayify } from './arrayify'; import { concat, concatBytes } from './concat'; +/** + * @group node + * @group browser + */ describe('concat', () => { it('should concatenate multiple BytesLike into a single Uint8Array', () => { const byteslike1 = '0xff61ba809b36351b'; @@ -14,7 +17,7 @@ describe('concat', () => { expect(output).toBeInstanceOf(Uint8Array); expect(output).toStrictEqual( - new Uint8Array([...getBytesCopy(byteslike1), ...byteslike2, ...byteslike3]) + new Uint8Array([...arrayify(byteslike1), ...byteslike2, ...byteslike3]) ); }); }); diff --git a/packages/utils/src/utils/concat.ts b/packages/utils/src/utils/concat.ts index 0ff1421993d..699e6452787 100644 --- a/packages/utils/src/utils/concat.ts +++ b/packages/utils/src/utils/concat.ts @@ -1,5 +1,6 @@ -import type { BytesLike } from 'ethers'; -import { getBytesCopy } from 'ethers'; +import type { BytesLike } from '@fuel-ts/interfaces'; + +import { arrayify } from './arrayify'; /** * Concatenates multiple Uint8Arrays into a single Uint8Array. @@ -35,7 +36,7 @@ export const concatBytes = ( * @returns - The concatenated array. */ export const concat = (arrays: ReadonlyArray): Uint8Array => { - const bytes = arrays.map((v) => getBytesCopy(v)); + const bytes = arrays.map((v) => arrayify(v)); return concatBytes(bytes); }; diff --git a/packages/utils/src/utils/defaultChainConfig.ts b/packages/utils/src/utils/defaultChainConfig.ts new file mode 100644 index 00000000000..fb4b3da4717 --- /dev/null +++ b/packages/utils/src/utils/defaultChainConfig.ts @@ -0,0 +1,6 @@ +import chainConfigJson from './chainConfig.json'; + +export const defaultChainConfig = chainConfigJson; + +export const defaultConsensusKey = + '0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298'; diff --git a/packages/utils/src/utils/hexlify.test.ts b/packages/utils/src/utils/hexlify.test.ts index f54ebf1f009..e90d7bcd87d 100644 --- a/packages/utils/src/utils/hexlify.test.ts +++ b/packages/utils/src/utils/hexlify.test.ts @@ -1,5 +1,9 @@ import { hexlify } from './hexlify'; +/** + * @group node + * @group browser + */ describe('hexlify', () => { it('returns hex from bytes', () => { expect(hexlify(new Uint8Array([0, 1, 2, 3]))).toEqual('0x00010203'); diff --git a/packages/utils/src/utils/hexlify.ts b/packages/utils/src/utils/hexlify.ts index 35014274081..aca2d197683 100644 --- a/packages/utils/src/utils/hexlify.ts +++ b/packages/utils/src/utils/hexlify.ts @@ -1,5 +1,6 @@ -import type { BytesLike } from 'ethers'; -import { getBytes } from 'ethers'; +import type { BytesLike } from '@fuel-ts/interfaces'; + +import { arrayify } from './arrayify'; const HexCharacters: string = '0123456789abcdef'; @@ -7,7 +8,7 @@ const HexCharacters: string = '0123456789abcdef'; * Returns a hex representation of the inputted bytes. */ export function hexlify(data: BytesLike): string { - const bytes = getBytes(data); + const bytes = arrayify(data); let result = '0x'; for (let i = 0; i < bytes.length; i++) { diff --git a/packages/utils/src/utils/normalizeString.test.ts b/packages/utils/src/utils/normalizeString.test.ts index 3f5ab62deff..c8f00c79620 100644 --- a/packages/utils/src/utils/normalizeString.test.ts +++ b/packages/utils/src/utils/normalizeString.test.ts @@ -2,6 +2,10 @@ import { safeExec } from '@fuel-ts/errors/test-utils'; import { normalizeString } from './normalizeString'; +/** + * @group node + * @group browser + */ describe('normalize.ts', () => { test('should normalize strings', () => { expect(normalizeString('DsToken')).toEqual('DsToken'); diff --git a/packages/utils/tsdoc.json b/packages/utils/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/utils/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/utils/tsup.config.ts b/packages/utils/tsup.config.ts index 4972257d818..ce3ba9f9aeb 100644 --- a/packages/utils/tsup.config.ts +++ b/packages/utils/tsup.config.ts @@ -6,6 +6,7 @@ const configs: Options = { entry: { index: 'src/index.ts', 'test-utils': 'src/test-utils.ts', + 'cli-utils': 'src/cli-utils.ts', }, }; diff --git a/packages/versions/README.md b/packages/versions/README.md index 3b38dbad3ad..5797b70f7ff 100644 --- a/packages/versions/README.md +++ b/packages/versions/README.md @@ -26,7 +26,7 @@ Aditionally, the library can be used as a CLI tool to help checking/validating u -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Installation diff --git a/packages/versions/package.json b/packages/versions/package.json index fe3b8f5ae21..c9a8e6303fb 100644 --- a/packages/versions/package.json +++ b/packages/versions/package.json @@ -10,7 +10,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { @@ -42,11 +42,9 @@ "license": "Apache-2.0", "dependencies": { "chalk": "4", - "cli-table": "^0.3.11", - "semver": "^7.3.8" + "cli-table": "^0.3.11" }, "devDependencies": { - "@types/cli-table": "^0.3.1", - "@types/semver": "^7.3.13" + "@types/cli-table": "^0.3.1" } } diff --git a/packages/versions/scripts/rewriteVersions.test.ts b/packages/versions/scripts/rewriteVersions.test.ts index 0c8937a0c46..dd90dee9433 100644 --- a/packages/versions/scripts/rewriteVersions.test.ts +++ b/packages/versions/scripts/rewriteVersions.test.ts @@ -7,12 +7,17 @@ import { rewriteVersions, } from './rewriteVersions'; -// https://stackoverflow.com/a/72885576 -jest.mock('fs', () => ({ - __esModule: true, - ...jest.requireActual('fs'), -})); +vi.mock('fs', async () => { + const mod = await vi.importActual('fs'); + return { + __esModule: true, + ...mod, + }; +}); +/** + * @group node + */ describe('rewriteVersions.js', () => { function modifyEnv() { const envBackup = { ...process.env }; @@ -88,7 +93,7 @@ describe('rewriteVersions.js', () => { test('should rewrite files', () => { // mocking - const writeFileSync = jest.spyOn(fsMod, 'writeFileSync').mockImplementation(); + const writeFileSync = vi.spyOn(fsMod, 'writeFileSync').mockImplementation(() => []); // executing rewriteVersions(); diff --git a/packages/versions/scripts/rewriteVersions.ts b/packages/versions/scripts/rewriteVersions.ts index fd0f4c52751..5f1734bf51d 100644 --- a/packages/versions/scripts/rewriteVersions.ts +++ b/packages/versions/scripts/rewriteVersions.ts @@ -63,7 +63,7 @@ export const rewriteVersions = () => { }; /* istanbul ignore next */ -// Do not auto-run script when inside jest runner -if (!/jest\.js$/m.test(process.argv[1])) { +// Do not auto-run script when inside vitest +if (!process.env.VITEST) { rewriteVersions(); } diff --git a/packages/versions/src/cli.test.ts b/packages/versions/src/cli.test.ts index 507479c5652..2f972a0a7c2 100644 --- a/packages/versions/src/cli.test.ts +++ b/packages/versions/src/cli.test.ts @@ -4,10 +4,18 @@ import * as compareSystemVersionsMod from './lib/compareSystemVersions'; import * as getBuiltinVersionsMod from './lib/getBuiltinVersions'; import * as getSystemVersionsMod from './lib/getSystemVersions'; +/** + * @group node + */ describe('cli.js', () => { // hooks - beforeEach(jest.clearAllMocks); - afterEach(jest.restoreAllMocks); + beforeEach(() => { + vi.resetAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); /* Test (mocking) utility @@ -29,27 +37,27 @@ describe('cli.js', () => { systemForcIsEq, } = params; - const error = jest.spyOn(console, 'error').mockImplementation(); - const info = jest.spyOn(console, 'info').mockImplementation(); - const exit = jest.spyOn(process, 'exit').mockImplementation(); + const error = vi.spyOn(console, 'error').mockImplementation(() => []); + const info = vi.spyOn(console, 'info').mockImplementation(() => []); + const exit = vi.spyOn(process, 'exit').mockImplementation(vi.fn()); - jest - .spyOn(colorizeUserVersionMod, 'colorizeUserVersion') - .mockImplementation(({ version }) => version); + vi.spyOn(colorizeUserVersionMod, 'colorizeUserVersion').mockImplementation( + ({ version }) => version + ); - jest.spyOn(compareSystemVersionsMod, 'compareSystemVersions').mockImplementation(() => ({ + vi.spyOn(compareSystemVersionsMod, 'compareSystemVersions').mockImplementation(() => ({ systemForcIsGt, systemFuelCoreIsGt, systemForcIsEq, systemFuelCoreIsEq, })); - jest.spyOn(getSystemVersionsMod, 'getSystemVersions').mockImplementation(() => ({ + vi.spyOn(getSystemVersionsMod, 'getSystemVersions').mockImplementation(() => ({ systemForcVersion, systemFuelCoreVersion, })); - jest.spyOn(getBuiltinVersionsMod, 'getBuiltinVersions').mockImplementation(() => ({ + vi.spyOn(getBuiltinVersionsMod, 'getBuiltinVersions').mockImplementation(() => ({ FORC: '1.0.0', FUEL_CORE: '1.0.0', FUELS: '1.0.0', diff --git a/packages/versions/src/index.test.ts b/packages/versions/src/index.test.ts index 9dea598ecbd..cedcafd7ff1 100644 --- a/packages/versions/src/index.test.ts +++ b/packages/versions/src/index.test.ts @@ -1,5 +1,8 @@ import * as indexMod from './index'; +/** + * @group node + */ describe('index.js', () => { test('should export versions constant', () => { expect(indexMod.versions).toBeTruthy(); diff --git a/packages/versions/src/lib/checkFuelCoreVersionCompatibility.test.ts b/packages/versions/src/lib/checkFuelCoreVersionCompatibility.test.ts index 262a3ff0671..876534d4e4d 100644 --- a/packages/versions/src/lib/checkFuelCoreVersionCompatibility.test.ts +++ b/packages/versions/src/lib/checkFuelCoreVersionCompatibility.test.ts @@ -1,13 +1,18 @@ import { checkFuelCoreVersionCompatibility } from './checkFuelCoreVersionCompatibility'; import * as getBuiltinVersionsMod from './getBuiltinVersions'; +/** + * @group node + */ describe('getDifferenceToUserFuelCoreVersion', () => { - afterAll(() => jest.restoreAllMocks()); + afterEach(() => { + vi.restoreAllMocks(); + }); it('should validate all possible version mismatches', () => { const supportedVersion = '0.1.2'; - jest.spyOn(getBuiltinVersionsMod, 'getBuiltinVersions').mockImplementation(() => ({ + vi.spyOn(getBuiltinVersionsMod, 'getBuiltinVersions').mockImplementation(() => ({ FUELS: '1', // not under test FORC: '1', // not under test FUEL_CORE: supportedVersion, diff --git a/packages/versions/src/lib/checkFuelCoreVersionCompatibility.ts b/packages/versions/src/lib/checkFuelCoreVersionCompatibility.ts index 169644436c0..b9fa9c4d0c2 100644 --- a/packages/versions/src/lib/checkFuelCoreVersionCompatibility.ts +++ b/packages/versions/src/lib/checkFuelCoreVersionCompatibility.ts @@ -1,22 +1,13 @@ -import semver from 'semver'; - import { getBuiltinVersions } from './getBuiltinVersions'; +import { majorEq, minorEq, patchEq } from './semver'; export function checkFuelCoreVersionCompatibility(networkVersion: string) { const { FUEL_CORE: supportedVersion } = getBuiltinVersions(); - const networkMajor = semver.major(networkVersion); - const networkMinor = semver.minor(networkVersion); - const networkPatch = semver.patch(networkVersion); - - const supportedMajor = semver.major(supportedVersion); - const supportedMinor = semver.minor(supportedVersion); - const supportedPatch = semver.patch(supportedVersion); - return { supportedVersion, - isMajorSupported: networkMajor === supportedMajor, - isMinorSupported: networkMinor === supportedMinor, - isPatchSupported: networkPatch === supportedPatch, + isMajorSupported: majorEq(networkVersion, supportedVersion), + isMinorSupported: minorEq(networkVersion, supportedVersion), + isPatchSupported: patchEq(networkVersion, supportedVersion), }; } diff --git a/packages/versions/src/lib/colorizeUserVersion.test.ts b/packages/versions/src/lib/colorizeUserVersion.test.ts index d7ea91b1bf7..ba9479353ff 100644 --- a/packages/versions/src/lib/colorizeUserVersion.test.ts +++ b/packages/versions/src/lib/colorizeUserVersion.test.ts @@ -2,6 +2,9 @@ import { cyan, red, green } from 'chalk'; import { colorizeUserVersion } from './colorizeUserVersion'; +/** + * @group node + */ describe('colorizeUserVersion.js', () => { test('should colorize user versions just fine', () => { const version = '1.0.0'; diff --git a/packages/versions/src/lib/compareSystemVersions.test.ts b/packages/versions/src/lib/compareSystemVersions.test.ts index 6e634882a1e..e4003a7b5d2 100644 --- a/packages/versions/src/lib/compareSystemVersions.test.ts +++ b/packages/versions/src/lib/compareSystemVersions.test.ts @@ -1,17 +1,22 @@ import { compareSystemVersions } from './compareSystemVersions'; import * as getBuiltinVersionsMod from './getBuiltinVersions'; +/** + * @group node + */ describe('compareSystemVersions.js', () => { /* Hooks */ beforeEach(() => { const v = '1.0.0'; - const spy = jest.spyOn(getBuiltinVersionsMod, 'getBuiltinVersions'); + const spy = vi.spyOn(getBuiltinVersionsMod, 'getBuiltinVersions'); spy.mockImplementation(() => ({ FUELS: v, FORC: v, FUEL_CORE: v })); }); - afterEach(jest.restoreAllMocks); + afterEach(() => { + vi.restoreAllMocks(); + }); /* Tests diff --git a/packages/versions/src/lib/compareSystemVersions.ts b/packages/versions/src/lib/compareSystemVersions.ts index 2cc7dd566cb..12f47794091 100644 --- a/packages/versions/src/lib/compareSystemVersions.ts +++ b/packages/versions/src/lib/compareSystemVersions.ts @@ -1,6 +1,5 @@ -import semver from 'semver'; - import { getBuiltinVersions } from './getBuiltinVersions'; +import { gt, eq } from './semver'; export interface ICompareVersionsParams { systemForcVersion: string; @@ -13,12 +12,12 @@ export function compareSystemVersions(params: ICompareVersionsParams) { const versions = getBuiltinVersions(); // are user's versions GREATER than the ones supported by the SDK? - const systemForcIsGt = semver.gt(systemForcVersion, versions.FORC); - const systemFuelCoreIsGt = semver.gt(systemFuelCoreVersion, versions.FUEL_CORE); + const systemForcIsGt = gt(systemForcVersion, versions.FORC); + const systemFuelCoreIsGt = gt(systemFuelCoreVersion, versions.FUEL_CORE); // are user's versions EXACTLY the ones supported by the SDK? - const systemForcIsEq = semver.eq(systemForcVersion, versions.FORC); - const systemFuelCoreIsEq = semver.eq(systemFuelCoreVersion, versions.FUEL_CORE); + const systemForcIsEq = eq(systemForcVersion, versions.FORC); + const systemFuelCoreIsEq = eq(systemFuelCoreVersion, versions.FUEL_CORE); return { systemForcIsGt, diff --git a/packages/versions/src/lib/getBuiltinVersions.test.ts b/packages/versions/src/lib/getBuiltinVersions.test.ts index b090636ce14..fc7c09e9fd2 100644 --- a/packages/versions/src/lib/getBuiltinVersions.test.ts +++ b/packages/versions/src/lib/getBuiltinVersions.test.ts @@ -2,6 +2,9 @@ import { readVersionsFromFiles } from '../../scripts/rewriteVersions'; import { getBuiltinVersions } from './getBuiltinVersions'; +/** + * @group node + */ describe('getBuiltinVersions.js', () => { test('should return received version of default', () => { const versions = getBuiltinVersions(); diff --git a/packages/versions/src/lib/getSystemVersions.test.ts b/packages/versions/src/lib/getSystemVersions.test.ts index f09f445bdc5..e5f87477461 100644 --- a/packages/versions/src/lib/getSystemVersions.test.ts +++ b/packages/versions/src/lib/getSystemVersions.test.ts @@ -2,12 +2,17 @@ import * as childProcessMod from 'child_process'; import { getSystemVersions } from './getSystemVersions'; -// https://stackoverflow.com/a/72885576 -jest.mock('child_process', () => ({ - __esModule: true, - ...jest.requireActual('child_process'), -})); +vi.mock('child_process', async () => { + const mod = await vi.importActual('child_process'); + return { + __esModule: true, + ...mod, + }; +}); +/** + * @group node + */ describe('getSystemVersions.js', () => { /* Test (mocking) utility @@ -19,17 +24,17 @@ describe('getSystemVersions.js', () => { }) { const { systemForcVersion, systemFuelCoreVersion, shouldThrow } = params; - const error = jest.spyOn(console, 'error').mockImplementation(); + const error = vi.spyOn(console, 'error').mockImplementation(() => []); - const mockedExecOk = jest.fn(); + const mockedExecOk = vi.fn(); mockedExecOk.mockReturnValueOnce(systemForcVersion); // first call (forc) mockedExecOk.mockReturnValueOnce(systemFuelCoreVersion); // second call (fuel-core) - const execSyncThrow = jest.fn(() => { + const execSyncThrow = vi.fn(() => { throw new Error(); }); - const execSync = jest + const execSync = vi .spyOn(childProcessMod, 'execSync') .mockImplementation(shouldThrow ? execSyncThrow : mockedExecOk); diff --git a/packages/versions/src/lib/semver.test.ts b/packages/versions/src/lib/semver.test.ts new file mode 100644 index 00000000000..e1ea836b54f --- /dev/null +++ b/packages/versions/src/lib/semver.test.ts @@ -0,0 +1,43 @@ +import { eq, gt, majorEq, minorEq, patchEq } from './semver'; + +/** + * @group node + */ +describe('semver', () => { + test('majorEq', () => { + expect(majorEq('1.2.3', '1.2.3')).toBe(true); + expect(majorEq('1.2.3', '1.2.4')).toBe(true); + expect(majorEq('1.2.3', '1.3.3')).toBe(true); + expect(majorEq('1.2.3', '2.2.3')).toBe(false); + }); + test('minorEq', () => { + expect(minorEq('1.2.3', '1.2.3')).toBe(true); + expect(minorEq('1.2.3', '1.2.4')).toBe(true); + expect(minorEq('1.2.3', '2.2.3')).toBe(true); + expect(minorEq('1.2.3', '1.3.3')).toBe(false); + }); + test('patchEq', () => { + expect(patchEq('1.2.3', '1.2.3')).toBe(true); + expect(patchEq('1.2.3', '1.3.3')).toBe(true); + expect(patchEq('1.2.3', '2.2.3')).toBe(true); + expect(patchEq('1.2.3', '1.2.4')).toBe(false); + }); + test('gt', () => { + expect(gt('1.2.3', '0.2.3')).toBe(true); + expect(gt('1.2.3', '1.1.3')).toBe(true); + expect(gt('1.2.3', '1.2.2')).toBe(true); + expect(gt('1.2.3', '1.2.3')).toBe(false); + expect(gt('1.2.3', '2.2.3')).toBe(false); + expect(gt('1.2.3', '1.3.3')).toBe(false); + expect(gt('1.2.3', '1.2.4')).toBe(false); + }); + test('eq', () => { + expect(eq('1.2.3', '1.2.3')).toBe(true); + expect(eq('1.2.3', '0.2.3')).toBe(false); + expect(eq('1.2.3', '2.2.3')).toBe(false); + expect(eq('1.2.3', '1.1.3')).toBe(false); + expect(eq('1.2.3', '1.3.3')).toBe(false); + expect(eq('1.2.3', '1.2.2')).toBe(false); + expect(eq('1.2.3', '1.2.4')).toBe(false); + }); +}); diff --git a/packages/versions/src/lib/semver.ts b/packages/versions/src/lib/semver.ts new file mode 100644 index 00000000000..a479e2670e4 --- /dev/null +++ b/packages/versions/src/lib/semver.ts @@ -0,0 +1,46 @@ +function parseVersion(version: string): { major: number; minor: number; patch: number } { + const [major, minor, patch] = version.split('.').map((v) => parseInt(v, 10)); + return { major, minor, patch }; +} + +function versionDiffs( + version1: string, + version2: string +): { major: number; minor: number; patch: number; fullVersionDiff: number } { + const semver1 = parseVersion(version1); + const semver2 = parseVersion(version2); + const major = semver1.major - semver2.major; + const minor = semver1.minor - semver2.minor; + const patch = semver1.patch - semver2.patch; + return { + major, + minor, + patch, + fullVersionDiff: major || minor || patch, + }; +} + +export function gt(version1: string, version2: string): boolean { + const { fullVersionDiff } = versionDiffs(version1, version2); + return fullVersionDiff > 0; +} + +export function eq(version1: string, version2: string): boolean { + const { fullVersionDiff } = versionDiffs(version1, version2); + return fullVersionDiff === 0; +} + +export function majorEq(version1: string, version2: string): boolean { + const { major } = versionDiffs(version1, version2); + return major === 0; +} + +export function minorEq(version1: string, version2: string): boolean { + const { minor } = versionDiffs(version1, version2); + return minor === 0; +} + +export function patchEq(version1: string, version2: string): boolean { + const { patch } = versionDiffs(version1, version2); + return patch === 0; +} diff --git a/packages/versions/tsdoc.json b/packages/versions/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/versions/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/wallet-manager/README.md b/packages/wallet-manager/README.md index d7bd4fa85f0..9b443fd0ef1 100644 --- a/packages/wallet-manager/README.md +++ b/packages/wallet-manager/README.md @@ -18,7 +18,7 @@ This module is a coordination wrapper to manage multiple wallet `Vaults` and sto -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/wallet-manager/package.json b/packages/wallet-manager/package.json index f5a5c4f487c..260016b5974 100644 --- a/packages/wallet-manager/package.json +++ b/packages/wallet-manager/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/wallet-manager/src/storages/memory-storage.test.ts b/packages/wallet-manager/src/storages/memory-storage.test.ts index 77e64a640bb..bc3a0ca6c8a 100644 --- a/packages/wallet-manager/src/storages/memory-storage.test.ts +++ b/packages/wallet-manager/src/storages/memory-storage.test.ts @@ -1,5 +1,9 @@ import MemoryStorage from './memory-storage'; +/** + * @group node + * @group browser + */ describe('MemoryStorage', () => { it('Test storage operations', async () => { const storage = new MemoryStorage(); diff --git a/packages/wallet-manager/src/vaults/mnemonic-vault.test.ts b/packages/wallet-manager/src/vaults/mnemonic-vault.test.ts index 68cbf785c70..0705ae4275f 100644 --- a/packages/wallet-manager/src/vaults/mnemonic-vault.test.ts +++ b/packages/wallet-manager/src/vaults/mnemonic-vault.test.ts @@ -6,6 +6,10 @@ import walletManagerSpec from '../wallet-manager-spec'; import { MnemonicVault } from './mnemonic-vault'; +/** + * @group node + * @group browser + */ describe('MnemonicVault', () => { let provider: Provider; @@ -22,7 +26,9 @@ describe('MnemonicVault', () => { vault.addAccount(); - expect(vault.getWallet(wallet.address).publicKey).toBe(walletManagerSpec.account_0.publicKey); + expect(vault.getWallet(wallet.address.toString()).publicKey).toBe( + walletManagerSpec.account_0.publicKey + ); }); it('Check if accounts are been added correctly', async () => { diff --git a/packages/wallet-manager/src/vaults/mnemonic-vault.ts b/packages/wallet-manager/src/vaults/mnemonic-vault.ts index 0047ca54315..2d8bfc8219b 100644 --- a/packages/wallet-manager/src/vaults/mnemonic-vault.ts +++ b/packages/wallet-manager/src/vaults/mnemonic-vault.ts @@ -1,3 +1,4 @@ +import { Address } from '@fuel-ts/address'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { AbstractAddress } from '@fuel-ts/interfaces'; import { Mnemonic } from '@fuel-ts/mnemonic'; @@ -82,9 +83,9 @@ export class MnemonicVault implements Vault { }; } - exportAccount(address: AbstractAddress): string { + exportAccount(address: string | AbstractAddress): string { let numberOfAccounts = 0; - + const ownerAddress = Address.fromAddressOrString(address); // Look for the account that has the same address do { const wallet = Wallet.fromMnemonic( @@ -92,7 +93,7 @@ export class MnemonicVault implements Vault { this.provider, this.getDerivePath(numberOfAccounts) ); - if (wallet.address.equals(address)) { + if (wallet.address.equals(ownerAddress)) { return wallet.privateKey; } numberOfAccounts += 1; @@ -104,7 +105,7 @@ export class MnemonicVault implements Vault { ); } - getWallet(address: AbstractAddress): WalletUnlocked { + getWallet(address: string | AbstractAddress): WalletUnlocked { const privateKey = this.exportAccount(address); return Wallet.fromPrivateKey(privateKey, this.provider); } diff --git a/packages/wallet-manager/src/vaults/privatekey-vault.test.ts b/packages/wallet-manager/src/vaults/privatekey-vault.test.ts index db1bc98f6fb..b6954c4c0c9 100644 --- a/packages/wallet-manager/src/vaults/privatekey-vault.test.ts +++ b/packages/wallet-manager/src/vaults/privatekey-vault.test.ts @@ -6,6 +6,10 @@ import { FUEL_NETWORK_URL } from '@fuel-ts/wallet/configs'; import { PrivateKeyVault } from './privatekey-vault'; +/** + * @group node + * @group browser + */ describe('PrivateKeyVault', () => { let provider: Provider; let walletSpec: WalletUnlocked; diff --git a/packages/wallet-manager/src/vaults/privatekey-vault.ts b/packages/wallet-manager/src/vaults/privatekey-vault.ts index 75698e080e8..bf2322c09c9 100644 --- a/packages/wallet-manager/src/vaults/privatekey-vault.ts +++ b/packages/wallet-manager/src/vaults/privatekey-vault.ts @@ -1,3 +1,4 @@ +import { Address } from '@fuel-ts/address'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { AbstractAddress } from '@fuel-ts/interfaces'; import type { Provider } from '@fuel-ts/providers'; @@ -64,9 +65,10 @@ export class PrivateKeyVault implements Vault { return this.getPublicAccount(wallet.privateKey); } - exportAccount(address: AbstractAddress): string { + exportAccount(address: string | AbstractAddress): string { + const ownerAddress = Address.fromAddressOrString(address); const privateKey = this.#privateKeys.find((pk) => - Wallet.fromPrivateKey(pk, this.provider).address.equals(address) + Wallet.fromPrivateKey(pk, this.provider).address.equals(ownerAddress) ); if (!privateKey) { @@ -79,7 +81,7 @@ export class PrivateKeyVault implements Vault { return privateKey; } - getWallet(address: AbstractAddress): WalletUnlocked { + getWallet(address: string | AbstractAddress): WalletUnlocked { const privateKey = this.exportAccount(address); return Wallet.fromPrivateKey(privateKey, this.provider); } diff --git a/packages/wallet-manager/src/wallet-manager.test.ts b/packages/wallet-manager/src/wallet-manager.test.ts index 8e64a997198..207a85307bf 100644 --- a/packages/wallet-manager/src/wallet-manager.test.ts +++ b/packages/wallet-manager/src/wallet-manager.test.ts @@ -10,6 +10,9 @@ import type { VaultConfig } from './types'; import { WalletManager } from './wallet-manager'; import WalletManagerSpec from './wallet-manager-spec'; +/** + * @group node + */ describe('Wallet Manager', () => { let provider: Provider; @@ -325,16 +328,16 @@ describe('Wallet Manager', () => { provider, }); // Create object with methods to be able to - // use Jest.spyOn + // use vi.spyOn const listeners = { onLock: () => {}, onUnlock: () => {}, onUpdate: () => {}, }; - const apyLock = jest.spyOn(listeners, 'onLock'); - const spyUnlock = jest.spyOn(listeners, 'onUnlock'); - const spyUpdate = jest.spyOn(listeners, 'onUpdate'); + const apyLock = vi.spyOn(listeners, 'onLock'); + const spyUnlock = vi.spyOn(listeners, 'onUnlock'); + const spyUpdate = vi.spyOn(listeners, 'onUpdate'); walletManager.on('update', listeners.onUpdate); walletManager.on('lock', listeners.onLock); diff --git a/packages/wallet-manager/src/wallet-manager.ts b/packages/wallet-manager/src/wallet-manager.ts index 1259d1810a6..fd4f710f158 100644 --- a/packages/wallet-manager/src/wallet-manager.ts +++ b/packages/wallet-manager/src/wallet-manager.ts @@ -1,3 +1,4 @@ +import { Address } from '@fuel-ts/address'; import type { Keystore } from '@fuel-ts/crypto'; import { encrypt, decrypt } from '@fuel-ts/crypto'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; @@ -110,26 +111,28 @@ export class WalletManager extends EventEmitter { /** * Create a Wallet instance for the specific account */ - getWallet(address: AbstractAddress): WalletUnlocked { + getWallet(address: string | AbstractAddress): WalletUnlocked { + const ownerAddress = Address.fromAddressOrString(address); const vaultState = this.#vaults.find((vs) => - vs.vault.getAccounts().find((a) => a.address.equals(address)) + vs.vault.getAccounts().find((a) => a.address.equals(ownerAddress)) ); assert(vaultState, ERROR_MESSAGES.address_not_found); - return vaultState.vault.getWallet(address); + return vaultState.vault.getWallet(ownerAddress); } /** * Export specific account privateKey */ - exportPrivateKey(address: AbstractAddress) { + exportPrivateKey(address: string | AbstractAddress) { + const ownerAddress = Address.fromAddressOrString(address); assert(!this.#isLocked, ERROR_MESSAGES.wallet_not_unlocked); const vaultState = this.#vaults.find((vs) => - vs.vault.getAccounts().find((a) => a.address.equals(address)) + vs.vault.getAccounts().find((a) => a.address.equals(ownerAddress)) ); assert(vaultState, ERROR_MESSAGES.address_not_found); - return vaultState.vault.exportAccount(address); + return vaultState.vault.exportAccount(ownerAddress); } /** diff --git a/packages/wallet-manager/tsdoc.json b/packages/wallet-manager/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/wallet-manager/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/packages/wallet/README.md b/packages/wallet/README.md index 4ff975221bb..5a8d7f97962 100644 --- a/packages/wallet/README.md +++ b/packages/wallet/README.md @@ -16,7 +16,7 @@ This module contains the class to manage a private key and signing for a standar ## Documentation -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/guide/wallets/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/wallets/) ## Usage diff --git a/packages/wallet/package.json b/packages/wallet/package.json index 4d05feaf57c..70153c6cc1b 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -7,7 +7,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { @@ -48,6 +48,7 @@ "@fuel-ts/abi-coder": "workspace:*", "@fuel-ts/address": "workspace:*", "@fuel-ts/crypto": "workspace:*", + "@fuel-ts/errors": "workspace:*", "@fuel-ts/fuel-core": "workspace:*", "@fuel-ts/hasher": "workspace:*", "@fuel-ts/hdwallet": "workspace:*", @@ -59,7 +60,6 @@ "@fuel-ts/transactions": "workspace:*", "@fuel-ts/utils": "workspace:*", "@fuels/vm-asm": "0.42.1", - "@fuel-ts/errors": "workspace:*", "ethers": "^6.7.1", "portfinder": "^1.0.32", "tree-kill": "^1.2.2", diff --git a/packages/wallet/src/account.test.ts b/packages/wallet/src/account.test.ts index 95207fb8853..e0259eabe54 100644 --- a/packages/wallet/src/account.test.ts +++ b/packages/wallet/src/account.test.ts @@ -16,19 +16,27 @@ import type { TxParamsType } from './account'; import { Account } from './account'; import { FUEL_NETWORK_URL } from './configs'; -jest.mock('@fuel-ts/providers', () => ({ - __esModule: true, - ...jest.requireActual('@fuel-ts/providers'), -})); +vi.mock('@fuel-ts/providers', async () => { + const mod = await vi.importActual('@fuel-ts/providers'); + return { + __esModule: true, + ...mod, + }; +}); let provider: Provider; -afterEach(jest.restoreAllMocks); +afterEach(() => { + vi.restoreAllMocks(); +}); beforeAll(async () => { provider = await Provider.create(FUEL_NETWORK_URL); }); +/** + * @group node + */ describe('Account', () => { const assets = [ '0x0101010101010101010101010101010101010101010101010101010101010101', @@ -36,7 +44,7 @@ describe('Account', () => { '0x0000000000000000000000000000000000000000000000000000000000000000', ]; - it('Create wallet using a address', () => { + it('should create account using an address, with a provider', () => { const account = new Account( '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', provider @@ -46,6 +54,22 @@ describe('Account', () => { ); }); + it('should create account using an address, without a provider', () => { + const account = new Account( + '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db' + ); + expect(account.address.toB256()).toEqual( + '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db' + ); + }); + + it('should throw an error when using a provider dependent method, without a provider', async () => { + const account = new Account( + '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db' + ); + await expect(() => account.getBalance()).rejects.toThrow(/Provider not set/); + }); + it('should get coins just fine', async () => { const account = new Account( '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', @@ -63,9 +87,9 @@ describe('Account', () => { it('should throw if coins length is higher than 9999', async () => { const dummyCoins: Coin[] = new Array(10000); - const getCoins = async () => Promise.resolve(dummyCoins); - - jest.spyOn(providersMod.Provider.prototype, 'getCoins').mockImplementation(getCoins); + vi.spyOn(Provider.prototype, 'getCoins').mockImplementation(async () => + Promise.resolve(dummyCoins) + ); const account = new Account( '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', @@ -127,12 +151,11 @@ describe('Account', () => { }); it('should throw if messages length is higher than 9999', async () => { - // mocking - const messages: Message[] = new Array(10000); - const mockedGetMessages = async () => Promise.resolve(messages); - jest - .spyOn(providersMod.Provider.prototype, 'getMessages') - .mockImplementationOnce(mockedGetMessages); + const dummyMessages: Message[] = new Array(10000); + + vi.spyOn(Provider.prototype, 'getMessages').mockImplementation(async () => + Promise.resolve(dummyMessages) + ); const account = new Account( '0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba', @@ -175,13 +198,11 @@ describe('Account', () => { }); it('should throw if balances length is higher than 9999', async () => { - const dummyBalace: CoinQuantity[] = new Array(10000); - - const mockedGetBalances = async () => Promise.resolve(dummyBalace); + const dummyBalances: CoinQuantity[] = new Array(10000); - jest - .spyOn(providersMod.Provider.prototype, 'getBalances') - .mockImplementation(mockedGetBalances); + vi.spyOn(Provider.prototype, 'getBalances').mockImplementation(async () => + Promise.resolve(dummyBalances) + ); const account = new Account( '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', @@ -218,6 +239,21 @@ describe('Account', () => { expect(account.provider).not.toBe(provider); }); + it('should be able to set a provider', async () => { + const account = new Account( + '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', + provider + ); + const newProviderInstance = await Provider.create(FUEL_NETWORK_URL); + + expect(account.provider).not.toBe(newProviderInstance); + + account.provider = newProviderInstance; + + expect(account.provider).toBe(newProviderInstance); + expect(account.provider).not.toBe(provider); + }); + it('should execute fund just as fine', async () => { const quantities: CoinQuantity[] = [ { @@ -230,13 +266,13 @@ describe('Account', () => { const request = new ScriptTransactionRequest(); const resourcesToSpend: Resource[] = []; - const getResourcesToSpendSpy = jest + const getResourcesToSpendSpy = vi .spyOn(Account.prototype, 'getResourcesToSpend') .mockImplementationOnce(() => Promise.resolve(resourcesToSpend)); - const addResourcesSpy = jest.spyOn(request, 'addResources'); + const addResourcesSpy = vi.spyOn(request, 'addResources'); - const addAmountToAssetSpy = jest.spyOn(providersMod, 'addAmountToAsset'); + const addAmountToAssetSpy = vi.spyOn(providersMod, 'addAmountToAsset'); const account = new Account( '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', @@ -292,21 +328,19 @@ describe('Account', () => { }; const request = new ScriptTransactionRequest(); - jest.spyOn(providersMod, 'ScriptTransactionRequest').mockImplementation(() => request); + vi.spyOn(providersMod, 'ScriptTransactionRequest').mockImplementation(() => request); const transactionResponse = new TransactionResponse('transactionId', provider); - const addCoinOutputSpy = jest.spyOn(request, 'addCoinOutput'); + const addCoinOutputSpy = vi.spyOn(request, 'addCoinOutput'); - const fundSpy = jest - .spyOn(Account.prototype, 'fund') - .mockImplementation(() => Promise.resolve()); + const fundSpy = vi.spyOn(Account.prototype, 'fund').mockImplementation(() => Promise.resolve()); - const sendTransactionSpy = jest + const sendTransactionSpy = vi .spyOn(Account.prototype, 'sendTransaction') .mockImplementation(() => Promise.resolve(transactionResponse)); - const getTransactionCost = jest + const getTransactionCost = vi .spyOn(Provider.prototype, 'getTransactionCost') .mockImplementation(() => Promise.resolve(transactionCost)); @@ -363,17 +397,17 @@ describe('Account', () => { const transactionResponse = {} as unknown as TransactionResponse; - const scriptTransactionRequest = jest + const scriptTransactionRequest = vi .spyOn(providersMod, 'ScriptTransactionRequest') .mockImplementation(() => request); - const getTransactionCost = jest + const getTransactionCost = vi .spyOn(providersMod.Provider.prototype, 'getTransactionCost') .mockImplementation(() => Promise.resolve(cost)); - const fund = jest.spyOn(Account.prototype, 'fund').mockImplementation(() => Promise.resolve()); + const fund = vi.spyOn(Account.prototype, 'fund').mockImplementation(() => Promise.resolve()); - const sendTransaction = jest + const sendTransaction = vi .spyOn(Account.prototype, 'sendTransaction') .mockImplementation(() => Promise.resolve(transactionResponse)); @@ -412,13 +446,13 @@ describe('Account', () => { const transactionRequest = new ScriptTransactionRequest(); const transactionResponse = 'transactionResponse' as unknown as TransactionResponse; - const transactionRequestify = jest.spyOn(providersMod, 'transactionRequestify'); + const transactionRequestify = vi.spyOn(providersMod, 'transactionRequestify'); - const estimateTxDependencies = jest + const estimateTxDependencies = vi .spyOn(providersMod.Provider.prototype, 'estimateTxDependencies') .mockImplementation(() => Promise.resolve()); - const sendTransaction = jest + const sendTransaction = vi .spyOn(providersMod.Provider.prototype, 'sendTransaction') .mockImplementation(() => Promise.resolve(transactionResponse)); @@ -448,15 +482,15 @@ describe('Account', () => { const transactionRequest = new ScriptTransactionRequest(); const callResult = 'callResult' as unknown as CallResult; - const transactionRequestify = jest + const transactionRequestify = vi .spyOn(providersMod, 'transactionRequestify') .mockImplementation(() => transactionRequest); - const estimateTxDependencies = jest + const estimateTxDependencies = vi .spyOn(providersMod.Provider.prototype, 'estimateTxDependencies') .mockImplementation(() => Promise.resolve()); - const simulate = jest + const simulate = vi .spyOn(providersMod.Provider.prototype, 'simulate') .mockImplementation(() => Promise.resolve(callResult)); diff --git a/packages/wallet/src/account.ts b/packages/wallet/src/account.ts index 5a5f0eebd30..9692c7d1dbe 100644 --- a/packages/wallet/src/account.ts +++ b/packages/wallet/src/account.ts @@ -18,6 +18,7 @@ import type { TransactionResponse, Provider, ScriptTransactionRequestLike, + ProviderSendTxParams, } from '@fuel-ts/providers'; import { withdrawScript, @@ -50,20 +51,44 @@ export class Account extends AbstractAccount { /** * The provider used to interact with the network. */ - provider: Provider; + protected _provider?: Provider; /** * Creates a new Account instance. * * @param address - The address of the account. - * @param provider - A Provider instance. + * @param provider - A Provider instance (optional). */ - constructor(address: string | AbstractAddress, provider: Provider) { + constructor(address: string | AbstractAddress, provider?: Provider) { super(); - this.provider = provider; + this._provider = provider; this.address = Address.fromDynamicInput(address); } + /** + * The provider used to interact with the network. + * + * @returns A Provider instance. + * + * @throws `FuelError` if the provider is not set. + */ + get provider(): Provider { + if (!this._provider) { + throw new FuelError(ErrorCode.MISSING_PROVIDER, 'Provider not set'); + } + + return this._provider; + } + + /** + * Sets the provider for the account. + * + * @param provider - A Provider instance. + */ + set provider(provider: Provider) { + this._provider = provider; + } + /** * Changes the provider connection for the account. * @@ -71,7 +96,7 @@ export class Account extends AbstractAccount { * @returns The updated Provider instance. */ connect(provider: Provider): Provider { - this.provider = provider; + this._provider = provider; return this.provider; } @@ -292,7 +317,7 @@ export class Account extends AbstractAccount { */ async createTransfer( /** Address of the destination */ - destination: AbstractAddress, + destination: string | AbstractAddress, /** Amount of coins */ amount: BigNumberish, /** Asset ID of coins */ @@ -303,7 +328,7 @@ export class Account extends AbstractAccount { const { minGasPrice } = this.provider.getGasConfig(); const params = { gasPrice: minGasPrice, ...txParams }; const request = new ScriptTransactionRequest(params); - request.addCoinOutput(destination, amount, assetId); + request.addCoinOutput(Address.fromAddressOrString(destination), amount, assetId); const { maxFee, requiredQuantities } = await this.provider.getTransactionCost(request); await this.fund(request, requiredQuantities, maxFee); return request; @@ -320,7 +345,7 @@ export class Account extends AbstractAccount { */ async transfer( /** Address of the destination */ - destination: AbstractAddress, + destination: string | AbstractAddress, /** Amount of coins */ amount: BigNumberish, /** Asset ID of coins */ @@ -343,7 +368,7 @@ export class Account extends AbstractAccount { */ async transferToContract( /** Contract address */ - contractId: AbstractAddress, + contractId: string | AbstractAddress, /** Amount of coins */ amount: BigNumberish, /** Asset ID of coins */ @@ -351,13 +376,14 @@ export class Account extends AbstractAccount { /** Tx Params */ txParams: TxParamsType = {} ): Promise { + const contractAddress = Address.fromAddressOrString(contractId); const { minGasPrice } = this.provider.getGasConfig(); const params = { gasPrice: minGasPrice, ...txParams }; const script = await composeScriptForTransferringToContract(); const scriptData = formatScriptDataForTransferringToContract( - contractId.toB256(), + contractAddress.toB256(), amount, assetId ); @@ -368,7 +394,7 @@ export class Account extends AbstractAccount { scriptData, }); - request.addContractInputAndOutput(contractId); + request.addContractInputAndOutput(contractAddress); const { maxFee, requiredQuantities } = await this.provider.getTransactionCost(request, [ { amount: bn(amount), assetId: String(assetId) }, @@ -389,15 +415,16 @@ export class Account extends AbstractAccount { */ async withdrawToBaseLayer( /** Address of the recipient on the base chain */ - recipient: AbstractAddress, + recipient: string | AbstractAddress, /** Amount of base asset */ amount: BigNumberish, /** Tx Params */ txParams: TxParamsType = {} ): Promise { + const recipientAddress = Address.fromAddressOrString(recipient); // add recipient and amount to the transaction script code const recipientDataArray = getBytesCopy( - '0x'.concat(recipient.toHexString().substring(2).padStart(64, '0')) + '0x'.concat(recipientAddress.toHexString().substring(2).padStart(64, '0')) ); const amountDataArray = getBytesCopy( '0x'.concat(bn(amount).toHex().substring(2).padStart(16, '0')) @@ -429,11 +456,15 @@ export class Account extends AbstractAccount { * @returns A promise that resolves to the transaction response. */ async sendTransaction( - transactionRequestLike: TransactionRequestLike + transactionRequestLike: TransactionRequestLike, + options?: Pick ): Promise { const transactionRequest = transactionRequestify(transactionRequestLike); await this.provider.estimateTxDependencies(transactionRequest); - return this.provider.sendTransaction(transactionRequest, { estimateTxDependencies: false }); + return this.provider.sendTransaction(transactionRequest, { + ...options, + estimateTxDependencies: false, + }); } /** diff --git a/packages/wallet/src/base-unlocked-wallet.ts b/packages/wallet/src/base-unlocked-wallet.ts index d26627465de..69a1912a257 100644 --- a/packages/wallet/src/base-unlocked-wallet.ts +++ b/packages/wallet/src/base-unlocked-wallet.ts @@ -4,6 +4,7 @@ import type { TransactionRequestLike, CallResult, Provider, + ProviderSendTxParams, } from '@fuel-ts/providers'; import { transactionRequestify } from '@fuel-ts/providers'; import { Signer } from '@fuel-ts/signer'; @@ -21,11 +22,6 @@ export class BaseWalletUnlocked extends Account { */ static defaultPath = "m/44'/1179993420'/0'/0/0"; - /** - * The provider used to interact with the Fuel network. - */ - provider: Provider; - /** * A function that returns the wallet's signer. */ @@ -35,13 +31,12 @@ export class BaseWalletUnlocked extends Account { * Creates a new BaseWalletUnlocked instance. * * @param privateKey - The private key of the wallet. - * @param provider - A Provider instance. + * @param provider - A Provider instance (optional). */ - constructor(privateKey: BytesLike, provider: Provider) { + constructor(privateKey: BytesLike, provider?: Provider) { const signer = new Signer(privateKey); super(signer.address, provider); this.signer = () => signer; - this.provider = provider; } /** @@ -110,13 +105,14 @@ export class BaseWalletUnlocked extends Account { * @returns A promise that resolves to the TransactionResponse object. */ async sendTransaction( - transactionRequestLike: TransactionRequestLike + transactionRequestLike: TransactionRequestLike, + options?: Pick ): Promise { const transactionRequest = transactionRequestify(transactionRequestLike); await this.provider.estimateTxDependencies(transactionRequest); return this.provider.sendTransaction( await this.populateTransactionWitnessesSignature(transactionRequest), - { estimateTxDependencies: false } + { ...options, estimateTxDependencies: false } ); } diff --git a/packages/wallet/src/configs.test.ts b/packages/wallet/src/configs.test.ts index 1e71827fa93..dd336caf8a3 100644 --- a/packages/wallet/src/configs.test.ts +++ b/packages/wallet/src/configs.test.ts @@ -1,3 +1,6 @@ +/** + * @group node + */ describe('Configs', () => { it('exports FUEL_NETWORK_URL', async () => { const configs = await import('./configs'); @@ -9,7 +12,7 @@ describe('Configs - undefined process', () => { const originalProcess = process; beforeEach(() => { - jest.resetModules(); + vi.resetModules(); // @ts-expect-error - test to assert undefined process // eslint-disable-next-line no-global-assign @@ -35,7 +38,7 @@ describe('Configs - overridden env', () => { const originalEnv = process.env; beforeEach(() => { - jest.resetModules(); + vi.resetModules(); process.env = { ...originalEnv, FUEL_NETWORK_URL: 'some-other-network-url' }; }); diff --git a/packages/wallet/src/keystore-wallet.test.ts b/packages/wallet/src/keystore-wallet.test.ts index 5569282f16e..c68d6331ac8 100644 --- a/packages/wallet/src/keystore-wallet.test.ts +++ b/packages/wallet/src/keystore-wallet.test.ts @@ -3,8 +3,13 @@ import { safeExec } from '@fuel-ts/errors/test-utils'; import { decryptKeystoreWallet, encryptKeystoreWallet, removeHexPrefix } from './keystore-wallet'; +/** + * @group node + */ describe('Keystore Wallet', () => { - afterEach(jest.restoreAllMocks); + afterEach(() => { + vi.restoreAllMocks(); + }); const privateKey = '0xeac85e732b683119e62fb52ce3b04c0d2f60539cd55af34c731fcdcf802e5ef4'; @@ -15,7 +20,7 @@ describe('Keystore Wallet', () => { it('should return a valid keystore when given correct parameters', async () => { // Act - const keystore = await encryptKeystoreWallet(privateKey, address, password); + const keystore = await encryptKeystoreWallet(privateKey, address.toB256(), password); // Assert expect(keystore).toBeTruthy(); diff --git a/packages/wallet/src/keystore-wallet.ts b/packages/wallet/src/keystore-wallet.ts index 43f90fc2034..145c43c9de1 100644 --- a/packages/wallet/src/keystore-wallet.ts +++ b/packages/wallet/src/keystore-wallet.ts @@ -1,3 +1,4 @@ +import { Address } from '@fuel-ts/address'; import { bufferFromString, keccak256, @@ -57,12 +58,12 @@ export const removeHexPrefix = (hexString: string) => { export async function encryptKeystoreWallet( privateKey: string, - address: AbstractAddress, + address: string | AbstractAddress, password: string ): Promise { // Convert the hexlified private key string to a Buffer. const privateKeyBuffer = bufferFromString(removeHexPrefix(privateKey), 'hex'); - + const ownerAddress = Address.fromAddressOrString(address); // Generate a random salt. const salt = randomBytes(DEFAULT_KEY_SIZE); @@ -91,7 +92,7 @@ export async function encryptKeystoreWallet( const keystore: KeystoreWallet = { id: uuidv4(), version: 3, - address: removeHexPrefix(address.toHexString()), + address: removeHexPrefix(ownerAddress.toHexString()), crypto: { cipher: 'aes-128-ctr', mac, diff --git a/packages/wallet/src/test-utils/defaultChainConfig.ts b/packages/wallet/src/test-utils/defaultChainConfig.ts deleted file mode 100644 index d4358fca08d..00000000000 --- a/packages/wallet/src/test-utils/defaultChainConfig.ts +++ /dev/null @@ -1,515 +0,0 @@ -export const defaultChainConfig = { - chain_name: 'local_testnet', - block_gas_limit: 5000000000, - initial_state: { - coins: [ - { - owner: '0x94ffcc53b892684acefaebc8a3d4a595e528a8cf664eeb3ef36f1020b0809d0d', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x09c0b2d1a486c439a87bcba6b46a7a1a23f3897cc83a94521a96da5c23bc58db', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x5d99ee966b42cd8fc7bdd1364b389153a9e78b42b7d4a691470674e817888d4e', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xbdaad6a89e073e177895b3e5a9ccd15806749eda134a6438dae32fc5b6601f3f', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x95a7aa6cc32743f8706c40ef49a7423b47da763bb4bbc055b1f07254dc729036', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xcee104acd38b940c8f1c62c6d7ea00a0ad2241d6dee0509a4bf27297508870d3', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x7e3626e306588eba79cafab73f0709e55ab8f4bdfe8c8b75034a430fc56ece89', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x1c31df52b6df56407dd95f83082e8beb9cfc9532ac111d5bd8491651d95ba775', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x09dd7a49174d6fcc9f4c6f7942c18060a935ddd03ee69b594189b8c3581276ea', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x86604282dc604481b809845be49667607c470644f6822fc01eb0d22f167e08cf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xbca334a06d19db5041c78fe2f465b07be5bec828f38b7796b2877e7d1542c950', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xbd9a1dc8d3ec3521c43f6c2c01611b4d0204c7610204ff0178488c8738a30bd2', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0xb32197cf75efe05bf453c26178139f09b391582065549c1422bc92555ecffb64', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x3b24509ed4ab3c7959f5c9391c1445c59290cdb5f13d6f780922f376b7029f30', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x77c6f40b7da70d885f68efaad7c661327482a63ea10dcb4271de819438254ae1', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x6a2c4691c547c43924650dbd30620b184b5fe3fb6dbe5c4446110b08f6f405bf', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - { - owner: '0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0000000000000000000000000000000000000000000000000000000000000000', - }, - { - owner: '0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0101010101010101010101010101010101010101010101010101010101010101', - }, - { - owner: '0x49075a7538e2c88ebe1926ce4d898198a2a4e790d14512943a9864bc536b3c82', - amount: '0xFFFFFFFFFFFFFFFF', - asset_id: '0x0202020202020202020202020202020202020202020202020202020202020202', - }, - ], - messages: [ - { - sender: '0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f', - recipient: '0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba', - nonce: '0101010101010101010101010101010101010101010101010101010101010101', - amount: '0x000000000000FFFF', - data: '', - da_height: '0x00', - }, - { - sender: '0x69a2b736b60159b43bb8a4f98c0589f6da5fa3a3d101e8e269c499eb942753ba', - recipient: '0xc43454aa38dd91f88109a4b7aef5efb96ce34e3f24992fe0f81d233ca686f80f', - nonce: '0e1ef2963832068b0e1ef2963832068b0e1ef2963832068b0e1ef2963832068b', - amount: '0xb04f3c08f59b309e', - data: '', - da_height: '0x00', - }, - ], - }, - consensus_parameters: { - tx_params: { - max_inputs: 255, - max_outputs: 255, - max_witnesses: 255, - max_gas_per_tx: 10000000, - max_size: 17825792, - }, - predicate_params: { - max_predicate_length: 1048576, - max_predicate_data_length: 1048576, - max_gas_per_predicate: 10000000, - max_message_data_length: 1048576, - }, - script_params: { - max_script_length: 1048576, - max_script_data_length: 1048576, - }, - contract_params: { - contract_max_size: 16777216, - max_storage_slots: 255, - }, - fee_params: { - gas_price_factor: 92, - gas_per_byte: 4, - }, - }, - gas_costs: { - add: 1, - addi: 1, - aloc: 1, - and: 1, - andi: 1, - bal: 13, - bhei: 1, - bhsh: 1, - burn: 132, - cb: 1, - cfei: 1, - cfsi: 1, - croo: 16, - div: 1, - divi: 1, - ecr1: 3000, - eck1: 951, - ed19: 3000, - eq: 1, - exp: 1, - expi: 1, - flag: 1, - gm: 1, - gt: 1, - gtf: 1, - ji: 1, - jmp: 1, - jne: 1, - jnei: 1, - jnzi: 1, - jmpf: 1, - jmpb: 1, - jnzf: 1, - jnzb: 1, - jnef: 1, - jneb: 1, - lb: 1, - log: 9, - lt: 1, - lw: 1, - mint: 135, - mlog: 1, - modOp: 1, - modi: 1, - moveOp: 1, - movi: 1, - mroo: 2, - mul: 1, - muli: 1, - mldv: 1, - noop: 1, - not: 1, - or: 1, - ori: 1, - poph: 2, - popl: 2, - pshh: 2, - pshl: 2, - ret: 13, - rvrt: 13, - sb: 1, - sll: 1, - slli: 1, - srl: 1, - srli: 1, - srw: 12, - sub: 1, - subi: 1, - sw: 1, - sww: 67, - time: 1, - tr: 105, - tro: 60, - wdcm: 1, - wqcm: 1, - wdop: 1, - wqop: 1, - wdml: 1, - wqml: 1, - wddv: 1, - wqdv: 2, - wdmd: 3, - wqmd: 4, - wdam: 2, - wqam: 3, - wdmm: 3, - wqmm: 3, - xor: 1, - xori: 1, - call: { - LightOperation: { - base: 144, - units_per_gas: 214, - }, - }, - ccp: { - LightOperation: { - base: 15, - units_per_gas: 103, - }, - }, - csiz: { - LightOperation: { - base: 17, - units_per_gas: 790, - }, - }, - k256: { - LightOperation: { - base: 11, - units_per_gas: 214, - }, - }, - ldc: { - LightOperation: { - base: 15, - units_per_gas: 272, - }, - }, - logd: { - LightOperation: { - base: 26, - units_per_gas: 64, - }, - }, - mcl: { - LightOperation: { - base: 1, - units_per_gas: 3333, - }, - }, - mcli: { - LightOperation: { - base: 1, - units_per_gas: 3333, - }, - }, - mcp: { - LightOperation: { - base: 1, - units_per_gas: 2000, - }, - }, - mcpi: { - LightOperation: { - base: 3, - units_per_gas: 2000, - }, - }, - meq: { - LightOperation: { - base: 1, - units_per_gas: 2500, - }, - }, - retd: { - LightOperation: { - base: 29, - units_per_gas: 62, - }, - }, - s256: { - LightOperation: { - base: 2, - units_per_gas: 214, - }, - }, - scwq: { - LightOperation: { - base: 13, - units_per_gas: 5, - }, - }, - smo: { - LightOperation: { - base: 209, - units_per_gas: 55, - }, - }, - srwq: { - LightOperation: { - base: 47, - units_per_gas: 5, - }, - }, - swwq: { - LightOperation: { - base: 44, - units_per_gas: 5, - }, - }, - contract_root: { - LightOperation: { - base: 75, - units_per_gas: 1, - }, - }, - state_root: { - LightOperation: { - base: 412, - units_per_gas: 1, - }, - }, - vm_initialization: { - HeavyOperation: { - base: 2000, - gas_per_unit: 0, - }, - }, - new_storage_per_byte: 1, - }, - consensus: { - PoA: { - signing_key: '0x94ffcc53b892684acefaebc8a3d4a595e528a8cf664eeb3ef36f1020b0809d0d', - }, - }, -}; diff --git a/packages/wallet/src/test-utils/launchNode.test.ts b/packages/wallet/src/test-utils/launchNode.test.ts index eceebb70b63..f49d0913499 100644 --- a/packages/wallet/src/test-utils/launchNode.test.ts +++ b/packages/wallet/src/test-utils/launchNode.test.ts @@ -1,78 +1,195 @@ -import { Provider } from '@fuel-ts/providers'; -import path from 'path'; -import { cwd } from 'process'; +import { safeExec } from '@fuel-ts/errors/test-utils'; +import * as childProcessMod from 'child_process'; -import { WalletUnlocked } from '../wallets'; +import type { LaunchNodeOptions } from './launchNode'; +import { killNode, launchNode } from './launchNode'; -import { launchNodeAndGetWallets } from './launchNode'; +type ChildProcessWithoutNullStreams = childProcessMod.ChildProcessWithoutNullStreams; +vi.mock('child_process', async () => { + const mod = await vi.importActual('child_process'); + return { + __esModule: true, + ...mod, + }; +}); + +/** + * This should mimic the stderr.on('data') event, returning both + * success and error messages, as strings. These messages are like + * the ones from `fuel-core` startup log messages. We filter them + * to know fuel-core state. + */ +function mockSpawn(params: { shouldError: boolean } = { shouldError: false }) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const stderrOn = (eventName: string, fn: (data: any) => void) => { + if (eventName === 'data') { + if (params.shouldError) { + // The `IO error` message simulates a possible fuel-core error log message + fn('IO error'); + } else { + // The `Binding GraphQL provider to` message simulates a fuel-core + // successful startup log message, usually meaning that the node + // is up and waiting for connections + fn('Binding GraphQL provider to'); + } + } + }; + + const innerMocks = { + on: vi.fn(), + stderr: { + pipe: vi.fn(), + on: vi.fn(stderrOn), + removeAllListeners: vi.fn(), + }, + stdout: { + pipe: vi.fn(), + removeAllListeners: vi.fn(), + }, + } as unknown as ChildProcessWithoutNullStreams; + + const spawn = vi.spyOn(childProcessMod, 'spawn').mockReturnValue(innerMocks); + + return { spawn, innerMocks }; +} + +const defaultLaunchNodeConfig: Partial = { + ip: '0.0.0.0', + port: '4000', +}; + +/** + * @group node + */ describe('launchNode', () => { - test('launchNodeAndGetWallets - empty config', async () => { - const { stop, provider, wallets } = await launchNodeAndGetWallets(); - expect(provider).toBeInstanceOf(Provider); - expect(wallets.length).toBe(10); - wallets.forEach((wallet) => { - expect(wallet).toBeInstanceOf(WalletUnlocked); + test('should start `fuel-core` node using built-in binary', async () => { + mockSpawn(); + + const { cleanup, ip, port } = await launchNode({ + ...defaultLaunchNodeConfig, + useSystemFuelCore: false, }); - stop(); + + expect(ip).toBe('0.0.0.0'); + expect(port).toBe('4000'); + cleanup(); }); - test('launchNodeAndGetWallets - custom config', async () => { - // #region launchNode-custom-config - const chainConfigPath = path.join(cwd(), '.fuel-core/configs/chainConfig.json'); + test('should start `fuel-core` node using system binary', async () => { + mockSpawn(); - const { stop, provider } = await launchNodeAndGetWallets({ - launchNodeOptions: { - chainConfigPath, - args: ['--poa-instant', 'true'], - }, + const { cleanup, ip, port } = await launchNode({ + ...defaultLaunchNodeConfig, + useSystemFuelCore: true, }); - const { - consensusParameters: { gasPerByte }, - } = provider.getChain(); + expect(ip).toBe('0.0.0.0'); + expect(port).toBe('4000'); + + cleanup(); + }); + + test('should throw on error', async () => { + const { innerMocks } = mockSpawn({ shouldError: true }); - expect(gasPerByte.toNumber()).toEqual(4); + const { error: safeError, result } = await safeExec(async () => + launchNode(defaultLaunchNodeConfig) + ); - stop(); - // #endregion launchNode-custom-config + expect(safeError).toBeTruthy(); + expect(result).not.toBeTruthy(); + + expect(innerMocks.on).toHaveBeenCalledTimes(1); + expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1); + expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(0); }); - test('launchNodeAndGetWallets - custom walletCount', async () => { - const { stop, wallets } = await launchNodeAndGetWallets({ - walletCount: 5, - }); - expect(wallets.length).toBe(5); - wallets.forEach((wallet) => { - expect(wallet).toBeInstanceOf(WalletUnlocked); + test('should pipe stdout', async () => { + vi.spyOn(process.stdout, 'write'); + + const { innerMocks } = mockSpawn(); + + const { cleanup } = await launchNode(defaultLaunchNodeConfig); + + expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1); + expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(0); + + cleanup(); + }); + + test('should pipe stdout and stderr', async () => { + vi.spyOn(process.stderr, 'write'); + vi.spyOn(process.stdout, 'write'); + + const { innerMocks } = mockSpawn(); + + await launchNode({ + ...defaultLaunchNodeConfig, + debugEnabled: true, }); - stop(); + + expect(innerMocks.stderr.pipe).toHaveBeenCalledTimes(1); + expect(innerMocks.stdout.pipe).toHaveBeenCalledTimes(1); }); - describe('without a GENESIS_SECRET', () => { - let GENESIS_SECRET: string | undefined; + test('should kill process only if PID exists and node is alive', () => { + const killFn = vi.fn(); + const state = { isDead: true }; - beforeAll(() => { - GENESIS_SECRET = process.env.GENESIS_SECRET; - delete process.env.GENESIS_SECRET; + // should not kill + let child = { + pid: undefined, + stdout: { + removeAllListeners: () => {}, + }, + stderr: { + removeAllListeners: () => {}, + }, + } as ChildProcessWithoutNullStreams; + + killNode({ + child, + configPath: '', + killFn, + state, }); - afterAll(() => { - process.env.GENESIS_SECRET = GENESIS_SECRET; + expect(killFn).toHaveBeenCalledTimes(0); + expect(state.isDead).toEqual(true); + + // should not kill + child = { + pid: 1, + stdout: { + removeAllListeners: () => {}, + }, + stderr: { + removeAllListeners: () => {}, + }, + } as ChildProcessWithoutNullStreams; + + killNode({ + child, + configPath: '', + killFn, + state, }); - test('launchNodeAndGetWallets - empty config', async () => { - const { stop, provider, wallets } = await launchNodeAndGetWallets(); - expect(provider).toBeInstanceOf(Provider); - expect(wallets.length).toBe(10); - wallets.forEach((wallet) => { - expect(wallet).toBeInstanceOf(WalletUnlocked); - }); - - expect(process.env.GENESIS_SECRET).toBeDefined(); - expect(process.env.GENESIS_SECRET).not.toEqual(GENESIS_SECRET); - expect(process.env.GENESIS_SECRET).toHaveLength(66); - stop(); + expect(killFn).toHaveBeenCalledTimes(0); + expect(state.isDead).toEqual(true); + + // should kill + state.isDead = false; + + killNode({ + child, + configPath: '', + killFn, + state, }); + + expect(killFn).toHaveBeenCalledTimes(1); + expect(state.isDead).toEqual(true); }); }); diff --git a/packages/wallet/src/test-utils/launchNode.ts b/packages/wallet/src/test-utils/launchNode.ts index c0284ab08f8..cfbd1f4684d 100644 --- a/packages/wallet/src/test-utils/launchNode.ts +++ b/packages/wallet/src/test-utils/launchNode.ts @@ -2,12 +2,13 @@ import { BaseAssetId } from '@fuel-ts/address/configs'; import { toHex } from '@fuel-ts/math'; import { Provider } from '@fuel-ts/providers'; import { Signer } from '@fuel-ts/signer'; +import { defaultChainConfig, defaultConsensusKey } from '@fuel-ts/utils'; +import { findBinPath } from '@fuel-ts/utils/cli-utils'; import type { ChildProcessWithoutNullStreams } from 'child_process'; import { spawn } from 'child_process'; import { randomUUID } from 'crypto'; import { hexlify } from 'ethers'; -import fsSync from 'fs'; -import fs from 'fs/promises'; +import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs'; import os from 'os'; import path from 'path'; import { getPortPromise } from 'portfinder'; @@ -15,24 +16,42 @@ import treeKill from 'tree-kill'; import type { WalletUnlocked } from '../wallets'; -import { defaultChainConfig } from './defaultChainConfig'; import { generateTestWallet } from './generateTestWallet'; -const defaultFuelCoreArgs = ['--vm-backtrace', '--utxo-validation']; +const getFlagValueFromArgs = (args: string[], flag: string) => { + const flagIndex = args.indexOf(flag); + if (flagIndex === -1) { + return undefined; + } + return args[flagIndex + 1]; +}; -type LaunchNodeOptions = { - chainConfigPath?: string; - consensusKey?: string; +const extractRemainingArgs = (args: string[], flagsToRemove: string[]) => { + const newArgs = [...args]; + flagsToRemove.forEach((flag) => { + const flagIndex = newArgs.indexOf(flag); + if (flagIndex !== -1) { + newArgs.splice(flagIndex, 2); + } + }); + return newArgs; +}; + +export type LaunchNodeOptions = { ip?: string; port?: string; args?: string[]; useSystemFuelCore?: boolean; + loggingEnabled?: boolean; + debugEnabled?: boolean; + basePath?: string; }; export type LaunchNodeResult = Promise<{ cleanup: () => void; ip: string; port: string; + chainConfigPath: string; }>; export type KillNodeParams = { @@ -57,8 +76,8 @@ export const killNode = (params: KillNodeParams) => { child.stderr.removeAllListeners(); // Remove the temporary folder and all its contents. - if (fsSync.existsSync(configPath)) { - fsSync.rmSync(configPath, { recursive: true }); + if (existsSync(configPath)) { + rmSync(configPath, { recursive: true }); } } }; @@ -66,28 +85,49 @@ export const killNode = (params: KillNodeParams) => { // #region launchNode-launchNodeOptions /** * Launches a fuel-core node. - * @param chainConfigPath - path to the chain configuration file. - * @param consensusKey - the consensus key to use. * @param ip - the ip to bind to. (optional, defaults to 0.0.0.0) * @param port - the port to bind to. (optional, defaults to 4000 or the next available port) * @param args - additional arguments to pass to fuel-core. * @param useSystemFuelCore - whether to use the system fuel-core binary or the one provided by the \@fuel-ts/fuel-core package. + * @param loggingEnabled - whether the node should output logs. (optional, defaults to true) + * @param debugEnabled - whether the node should log debug messages. (optional, defaults to false) + * @param basePath - the base path to use for the temporary folder. (optional, defaults to os.tmpdir()) * */ // #endregion launchNode-launchNodeOptions export const launchNode = async ({ - chainConfigPath, - consensusKey = '0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298', ip, port, - args = defaultFuelCoreArgs, + args = [], useSystemFuelCore = false, + loggingEnabled = true, + debugEnabled = false, + basePath, }: LaunchNodeOptions): LaunchNodeResult => // eslint-disable-next-line no-async-promise-executor new Promise(async (resolve, reject) => { + // filter out the flags chain, consensus-key, db-type, and poa-instant. we don't want to pass them twice to fuel-core. see line 214. + const remainingArgs = extractRemainingArgs(args, [ + '--chain', + '--consensus-key', + '--db-type', + '--poa-instant', + ]); + + const chainConfigPath = getFlagValueFromArgs(args, '--chain'); + const consensusKey = getFlagValueFromArgs(args, '--consensus-key') || defaultConsensusKey; + + const dbTypeFlagValue = getFlagValueFromArgs(args, '--db-type'); + const useInMemoryDb = dbTypeFlagValue === 'in-memory' || dbTypeFlagValue === undefined; + + const poaInstantFlagValue = getFlagValueFromArgs(args, '--poa-instant'); + const poaInstant = poaInstantFlagValue === 'true' || poaInstantFlagValue === undefined; + // This string is logged by the client when the node has successfully started. We use it to know when to resolve. const graphQLStartSubstring = 'Binding GraphQL provider to'; - const command = useSystemFuelCore ? 'fuel-core' : './node_modules/.bin/fuels-core'; + const binPath = findBinPath('fuels-core', __dirname); + + const command = useSystemFuelCore ? 'fuel-core' : binPath; const ipToUse = ip || '0.0.0.0'; @@ -100,15 +140,19 @@ export const launchNode = async ({ }) ).toString(); - let chainConfigPathToUse = chainConfigPath; + let chainConfigPathToUse: string; - const tempDirPath = path.join(os.tmpdir(), '.fuels-ts', randomUUID()); + const prefix = basePath || os.tmpdir(); + const suffix = basePath ? '' : randomUUID(); + const tempDirPath = path.join(prefix, '.fuels', suffix); - if (!chainConfigPath) { - if (!fsSync.existsSync(tempDirPath)) { - fsSync.mkdirSync(tempDirPath, { recursive: true }); + if (chainConfigPath) { + chainConfigPathToUse = chainConfigPath; + } else { + if (!existsSync(tempDirPath)) { + mkdirSync(tempDirPath, { recursive: true }); } - const tempChainConfigFilePath = path.join(tempDirPath, '.chainConfig.json'); + const tempChainConfigFilePath = path.join(tempDirPath, 'chainConfig.json'); let chainConfig = defaultChainConfig; @@ -135,25 +179,39 @@ export const launchNode = async ({ } // Write a temporary chain configuration file. - await fs.writeFile(tempChainConfigFilePath, JSON.stringify(chainConfig), 'utf8'); + writeFileSync(tempChainConfigFilePath, JSON.stringify(chainConfig), 'utf8'); chainConfigPathToUse = tempChainConfigFilePath; } - const child = spawn(command, [ - 'run', - '--ip', - ipToUse, - '--port', - portToUse, - '--db-type', - 'in-memory', - '--consensus-key', - consensusKey, - '--chain', - chainConfigPathToUse as string, - ...args, - ]); + const child = spawn( + command, + [ + 'run', + ['--ip', ipToUse], + ['--port', portToUse], + useInMemoryDb ? ['--db-type', 'in-memory'] : ['--db-path', tempDirPath], + ['--min-gas-price', '0'], + poaInstant ? ['--poa-instant', 'true'] : [], + ['--consensus-key', consensusKey], + ['--chain', chainConfigPathToUse as string], + '--vm-backtrace', + '--utxo-validation', + '--debug', + ...remainingArgs, + ].flat(), + { + stdio: 'pipe', + } + ); + + if (loggingEnabled) { + child.stderr.pipe(process.stderr); + } + + if (debugEnabled) { + child.stdout.pipe(process.stdout); + } const cleanupConfig: KillNodeParams = { child, @@ -164,8 +222,6 @@ export const launchNode = async ({ }, }; - child.stderr.setEncoding('utf8'); - // Look for a specific graphql start point in the output. child.stderr.on('data', (chunk: string) => { // Look for the graphql service start. @@ -175,6 +231,7 @@ export const launchNode = async ({ cleanup: () => killNode(cleanupConfig), ip: ipToUse, port: portToUse, + chainConfigPath: chainConfigPathToUse as string, }); } if (/error/i.test(chunk)) { @@ -183,17 +240,18 @@ export const launchNode = async ({ }); // Process exit. - process.on('exit', killNode); + process.on('exit', () => killNode(cleanupConfig)); // Catches ctrl+c event. - process.on('SIGINT', killNode); + process.on('SIGINT', () => killNode(cleanupConfig)); // Catches "kill pid" (for example: nodemon restart). - process.on('SIGUSR1', killNode); - process.on('SIGUSR2', killNode); + process.on('SIGUSR1', () => killNode(cleanupConfig)); + process.on('SIGUSR2', () => killNode(cleanupConfig)); // Catches uncaught exceptions. - process.on('uncaughtException', killNode); + process.on('beforeExit', () => killNode(cleanupConfig)); + process.on('uncaughtException', () => killNode(cleanupConfig)); child.on('error', reject); }); @@ -225,16 +283,7 @@ export const launchNodeAndGetWallets = async ({ launchNodeOptions?: Partial; walletCount?: number; } = {}): LaunchNodeAndGetWalletsResult => { - const defaultNodeOptions: LaunchNodeOptions = { - chainConfigPath: launchNodeOptions?.chainConfigPath, - consensusKey: launchNodeOptions?.consensusKey, - }; - - const { - cleanup: closeNode, - ip, - port, - } = await launchNode({ ...defaultNodeOptions, ...launchNodeOptions }); + const { cleanup: closeNode, ip, port } = await launchNode(launchNodeOptions || {}); const provider = await Provider.create(`http://${ip}:${port}/graphql`); const wallets = await generateWallets(walletCount, provider); diff --git a/packages/wallet/src/test-utils/launchNodeAndGetWallets.test.ts b/packages/wallet/src/test-utils/launchNodeAndGetWallets.test.ts new file mode 100644 index 00000000000..d90d0275ba4 --- /dev/null +++ b/packages/wallet/src/test-utils/launchNodeAndGetWallets.test.ts @@ -0,0 +1,95 @@ +import { Provider } from '@fuel-ts/providers'; +import path from 'path'; +import { cwd } from 'process'; + +import { WalletUnlocked } from '../wallets'; + +import { launchNodeAndGetWallets } from './launchNode'; + +/** + * @group node + */ +describe('launchNode', () => { + test('launchNodeAndGetWallets - empty config', async () => { + const { stop, provider, wallets } = await launchNodeAndGetWallets({ + launchNodeOptions: { + loggingEnabled: false, + }, + }); + expect(provider).toBeInstanceOf(Provider); + expect(wallets.length).toBe(10); + wallets.forEach((wallet) => { + expect(wallet).toBeInstanceOf(WalletUnlocked); + }); + stop(); + }); + + test('launchNodeAndGetWallets - custom config', async () => { + // #region launchNode-custom-config + const chainConfigPath = path.join(cwd(), '.fuel-core/configs/chainConfig.json'); + + const { stop, provider } = await launchNodeAndGetWallets({ + launchNodeOptions: { + args: ['--chain', chainConfigPath], + loggingEnabled: false, + }, + }); + + const { + consensusParameters: { gasPerByte }, + } = provider.getChain(); + + expect(gasPerByte.toNumber()).toEqual(4); + + stop(); + // #endregion launchNode-custom-config + }); + + test('launchNodeAndGetWallets - custom walletCount', async () => { + const { stop, wallets } = await launchNodeAndGetWallets({ + walletCount: 5, + launchNodeOptions: { + loggingEnabled: false, + }, + }); + expect(wallets.length).toBe(5); + wallets.forEach((wallet) => { + expect(wallet).toBeInstanceOf(WalletUnlocked); + }); + stop(); + }); + + describe('without a GENESIS_SECRET', () => { + let GENESIS_SECRET: string | undefined; + + beforeAll(() => { + GENESIS_SECRET = process.env.GENESIS_SECRET; + delete process.env.GENESIS_SECRET; + }); + + afterAll(() => { + process.env.GENESIS_SECRET = GENESIS_SECRET; + }); + + test('launchNodeAndGetWallets - empty config', async () => { + const { stop, provider, wallets } = await launchNodeAndGetWallets({ + launchNodeOptions: { + loggingEnabled: false, + }, + }); + + expect(provider).toBeInstanceOf(Provider); + expect(wallets.length).toBe(10); + + wallets.forEach((wallet) => { + expect(wallet).toBeInstanceOf(WalletUnlocked); + }); + + expect(process.env.GENESIS_SECRET).toBeDefined(); + expect(process.env.GENESIS_SECRET).not.toEqual(GENESIS_SECRET); + expect(process.env.GENESIS_SECRET).toHaveLength(66); + + stop(); + }); + }); +}); diff --git a/packages/wallet/src/test-utils/seedTestWallet.ts b/packages/wallet/src/test-utils/seedTestWallet.ts index e2cb1d9bdd3..4ea14beb95f 100644 --- a/packages/wallet/src/test-utils/seedTestWallet.ts +++ b/packages/wallet/src/test-utils/seedTestWallet.ts @@ -27,7 +27,5 @@ export const seedTestWallet = async (wallet: Account, quantities: CoinQuantityLi quantities .map(coinQuantityfy) .forEach(({ amount, assetId }) => request.addCoinOutput(wallet.address, amount, assetId)); - const response = await genesisWallet.sendTransaction(request); - - await response.wait(); + await genesisWallet.sendTransaction(request, { awaitExecution: true }); }; diff --git a/packages/wallet/src/transfer.test.ts b/packages/wallet/src/transfer.test.ts index 9c62e9d0fef..27b652910ae 100644 --- a/packages/wallet/src/transfer.test.ts +++ b/packages/wallet/src/transfer.test.ts @@ -9,6 +9,9 @@ import { Wallet } from '.'; import { FUEL_NETWORK_URL } from './configs'; import { generateTestWallet, seedTestWallet } from './test-utils'; +/** + * @group node + */ describe('Wallet', () => { let provider: Provider; let gasPrice: BN; @@ -39,7 +42,7 @@ describe('Wallet', () => { const sender = await generateTestWallet(provider, [[500_000, BaseAssetId]]); const receiver = await generateTestWallet(provider); - const request = await sender.createTransfer(receiver.address, 1, BaseAssetId, { + const request = await sender.createTransfer(receiver.address.toB256(), 1, BaseAssetId, { gasPrice, gasLimit: 10_000, }); @@ -175,7 +178,7 @@ describe('Wallet', () => { const AMOUNT = 10; const recipient = Address.fromB256(RECIPIENT_ID); - const tx = await sender.withdrawToBaseLayer(recipient, AMOUNT, { + const tx = await sender.withdrawToBaseLayer(recipient.toB256(), AMOUNT, { gasPrice, gasLimit: 10_000, }); diff --git a/packages/wallet/src/types/GenerateOptions.ts b/packages/wallet/src/types/GenerateOptions.ts index 8a6ac35583f..ef23a3dc507 100644 --- a/packages/wallet/src/types/GenerateOptions.ts +++ b/packages/wallet/src/types/GenerateOptions.ts @@ -4,5 +4,5 @@ import type { BytesLike } from 'ethers'; export interface GenerateOptions { /** Additional entropy for the random bytes */ entropy?: BytesLike; - provider: Provider; + provider?: Provider; } diff --git a/packages/wallet/src/utils.test.ts b/packages/wallet/src/utils.test.ts index 1e93a43bc28..6dc90931c7e 100644 --- a/packages/wallet/src/utils.test.ts +++ b/packages/wallet/src/utils.test.ts @@ -9,8 +9,29 @@ import { formatScriptDataForTransferringToContract, } from './utils'; +vi.mock('@fuels/vm-asm', async () => { + const mod = await vi.importActual('@fuels/vm-asm'); + return { + __esModule: true, + ...mod, + }; +}); + +vi.mock('ethers', async () => { + const mod = await vi.importActual('ethers'); + return { + __esModule: true, + ...mod, + }; +}); + +/** + * @group node + */ describe('util', () => { - afterEach(jest.restoreAllMocks); + afterEach(() => { + vi.restoreAllMocks(); + }); it('should ensure "composeScriptForTransferringToContract" returns script just fine', async () => { const script = await composeScriptForTransferringToContract(); @@ -24,9 +45,9 @@ describe('util', () => { it('should ensure "formatScriptDataForTransferringToContract" returns script data just fine', () => { const byte: number[] = [0, 0, 0, 0, 0, 0, 0, 1]; - const encode = jest.spyOn(U64Coder.prototype, 'encode').mockReturnValue(Uint8Array.from(byte)); + const encode = vi.spyOn(U64Coder.prototype, 'encode').mockReturnValue(Uint8Array.from(byte)); - const arrayify = jest + const arrayify = vi .spyOn(getBytesCopyMod, 'getBytesCopy') .mockReturnValue(Uint8Array.from(byte)); diff --git a/packages/wallet/src/wallet-unlocked.test.ts b/packages/wallet/src/wallet-unlocked.test.ts index aaccc847537..e02f3f0a006 100644 --- a/packages/wallet/src/wallet-unlocked.test.ts +++ b/packages/wallet/src/wallet-unlocked.test.ts @@ -14,13 +14,19 @@ import * as keystoreWMod from './keystore-wallet'; import walletSpec from './wallet-spec'; import { WalletLocked, WalletUnlocked } from './wallets'; -jest.mock('@fuel-ts/providers', () => ({ - __esModule: true, - ...jest.requireActual('@fuel-ts/providers'), -})); +vi.mock('@fuel-ts/providers', async () => { + const mod = await vi.importActual('@fuel-ts/providers'); + return { + __esModule: true, + ...mod, + }; +}); const { ScriptTransactionRequest } = providersMod; +/** + * @group node + */ describe('WalletUnlocked', () => { const expectedPrivateKey = '0x5f70feeff1f229e4a95e1056e8b4d80d0b24b565674860cc213bdb07127ce1b1'; const expectedPublicKey = @@ -97,7 +103,7 @@ describe('WalletUnlocked', () => { const wallet = new WalletUnlocked(PRIVATE_KEY, provider); let signature: BytesLike | undefined; // Intercept Provider.sendTransaction to collect signature - const spy = jest + const spy = vi .spyOn(wallet.provider, 'sendTransaction') .mockImplementation(async (transaction: TransactionRequestLike) => { signature = transaction.witnesses?.[0]; @@ -144,48 +150,75 @@ describe('WalletUnlocked', () => { expect(wallet.address).toEqual(recoveredAddress); }); - it('Create wallet from seed', async () => { - const provider = await Provider.create(FUEL_NETWORK_URL); - const wallet = WalletUnlocked.fromSeed(walletSpec.seed, provider, walletSpec.account_1.path); + describe('WalletUnlocked.fromSeed', () => { + it('Create wallet from seed', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + const wallet = WalletUnlocked.fromSeed(walletSpec.seed, provider, walletSpec.account_1.path); - expect(wallet.publicKey).toBe(walletSpec.account_1.publicKey); - expect(wallet.provider.url).toBe(walletSpec.providerUrl); - }); + expect(wallet.publicKey).toBe(walletSpec.account_1.publicKey); + expect(wallet.provider.url).toBe(walletSpec.providerUrl); + }); - it('Create wallet from mnemonic', async () => { - const provider = await Provider.create(FUEL_NETWORK_URL); - const wallet = WalletUnlocked.fromMnemonic( - walletSpec.mnemonic, - provider, - walletSpec.account_1.path, - undefined - ); + it('Create wallet from seed with default path', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + const wallet = WalletUnlocked.fromSeed(walletSpec.seed, provider); - expect(wallet.publicKey).toBe(walletSpec.account_1.publicKey); - expect(wallet.provider.url).toBe(walletSpec.providerUrl); - }); + expect(wallet.publicKey).toBe(walletSpec.account_0.publicKey); + expect(wallet.provider.url).toBe(walletSpec.providerUrl); + }); - it('Create wallet from mnemonic with default path', async () => { - const provider = await Provider.create(FUEL_NETWORK_URL); - const wallet = WalletUnlocked.fromMnemonic(walletSpec.mnemonic, provider); + it('Create wallet from seed with default path, without a provider', () => { + const wallet = WalletUnlocked.fromSeed(walletSpec.seed); - expect(wallet.publicKey).toBe(walletSpec.account_0.publicKey); + expect(wallet.publicKey).toBe(walletSpec.account_0.publicKey); + expect(() => wallet.provider).toThrowError('Provider not set'); + }); }); - it('Create wallet from extendedKey', async () => { - const provider = await Provider.create(FUEL_NETWORK_URL); - const wallet = WalletUnlocked.fromExtendedKey(walletSpec.account_0.xprv, provider); + describe('WalletUnlocked.fromMnemonic', () => { + it('Create wallet from mnemonic', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + const wallet = WalletUnlocked.fromMnemonic( + walletSpec.mnemonic, + provider, + walletSpec.account_1.path, + undefined + ); + + expect(wallet.publicKey).toBe(walletSpec.account_1.publicKey); + expect(wallet.provider.url).toBe(walletSpec.providerUrl); + }); + + it('Create wallet from mnemonic with default path', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + const wallet = WalletUnlocked.fromMnemonic(walletSpec.mnemonic, provider); + + expect(wallet.publicKey).toBe(walletSpec.account_0.publicKey); + }); + + it('Create wallet from mnemonic with default path, without a provider', () => { + const wallet = WalletUnlocked.fromMnemonic(walletSpec.mnemonic); - expect(wallet.publicKey).toBe(walletSpec.account_0.publicKey); - expect(wallet.provider.url).toBe(walletSpec.providerUrl); + expect(wallet.publicKey).toBe(walletSpec.account_0.publicKey); + expect(() => wallet.provider).toThrowError('Provider not set'); + }); }); - it('Create wallet from seed with default path', async () => { - const provider = await Provider.create(FUEL_NETWORK_URL); - const wallet = WalletUnlocked.fromSeed(walletSpec.seed, provider); + describe('WalletUnlocked.extendedKey', () => { + it('Create wallet from extendedKey', async () => { + const provider = await Provider.create(FUEL_NETWORK_URL); + const wallet = WalletUnlocked.fromExtendedKey(walletSpec.account_0.xprv, provider); + + expect(wallet.publicKey).toBe(walletSpec.account_0.publicKey); + expect(wallet.provider.url).toBe(walletSpec.providerUrl); + }); - expect(wallet.publicKey).toBe(walletSpec.account_0.publicKey); - expect(wallet.provider.url).toBe(walletSpec.providerUrl); + it('Create wallet from extendedKey, without provider', () => { + const wallet = WalletUnlocked.fromExtendedKey(walletSpec.account_0.xprv); + + expect(wallet.publicKey).toBe(walletSpec.account_0.publicKey); + expect(() => wallet.provider).toThrowError('Provider not set'); + }); }); it('Create wallet and lock it', async () => { @@ -205,19 +238,19 @@ describe('WalletUnlocked', () => { const transactionReq = new ScriptTransactionRequest(); const callResult = 'callResult' as unknown as CallResult; - const transactionRequestify = jest + const transactionRequestify = vi .spyOn(providersMod, 'transactionRequestify') .mockImplementation(() => transactionReq); - const estimateTxDependencies = jest + const estimateTxDependencies = vi .spyOn(providersMod.Provider.prototype, 'estimateTxDependencies') .mockImplementation(() => Promise.resolve()); - const call = jest + const call = vi .spyOn(providersMod.Provider.prototype, 'call') .mockImplementation(() => Promise.resolve(callResult)); - const populateTransactionWitnessesSignatureSpy = jest + const populateTransactionWitnessesSignatureSpy = vi .spyOn(BaseWalletUnlocked.prototype, 'populateTransactionWitnessesSignature') .mockImplementationOnce(() => Promise.resolve(transactionReq)); @@ -250,7 +283,7 @@ describe('WalletUnlocked', () => { }); const password = 'password'; - const encryptKeystoreWalletSpy = jest.spyOn(keystoreWMod, 'encryptKeystoreWallet'); + const encryptKeystoreWalletSpy = vi.spyOn(keystoreWMod, 'encryptKeystoreWallet'); const keystore = wallet.encrypt(password); diff --git a/packages/wallet/src/wallet.test.ts b/packages/wallet/src/wallet.test.ts index 324b0e15aa5..af9617c272c 100644 --- a/packages/wallet/src/wallet.test.ts +++ b/packages/wallet/src/wallet.test.ts @@ -8,72 +8,168 @@ import { transactionRequestify, Provider } from '@fuel-ts/providers'; import { FUEL_NETWORK_URL } from './configs'; import { generateTestWallet } from './test-utils/generateTestWallet'; import { Wallet } from './wallet'; -import { WalletUnlocked } from './wallets'; +import { WalletLocked, WalletUnlocked } from './wallets'; +/** + * @group node + */ describe('Wallet', () => { let wallet: WalletUnlocked; let provider: Provider; let gasPrice: BN; beforeAll(async () => { provider = await Provider.create(FUEL_NETWORK_URL); - wallet = Wallet.generate({ - provider, - }); + wallet = Wallet.generate({ provider }); gasPrice = provider.getGasConfig().minGasPrice; }); - it('Instantiate a new wallet', () => { - const lockedWallet = Wallet.fromAddress(wallet.address, provider); - expect(lockedWallet.address).toEqual(wallet.address); + describe('WalletLocked.constructor', () => { + it('should instatiate from a constructor', () => { + const lockedWallet = new WalletLocked(wallet.address, provider); + + expect(lockedWallet.address).toEqual(wallet.address); + }); + + it('should instatiate from a constructor, without a provider', () => { + const lockedWallet = new WalletLocked(wallet.address); + + expect(lockedWallet.address).toStrictEqual(wallet.address); + expect(lockedWallet).toBeInstanceOf(WalletLocked); + }); + }); + + describe('WalletLocked.fromAddress', () => { + it('should instantiate from an address', () => { + const lockedWallet = Wallet.fromAddress(wallet.address, provider); + + expect(lockedWallet.address).toStrictEqual(wallet.address); + expect(lockedWallet).toBeInstanceOf(WalletLocked); + }); + + it('should instantiate from an address, without a provider', () => { + const lockedWallet = Wallet.fromAddress(wallet.address); + + expect(lockedWallet.address).toStrictEqual(wallet.address); + expect(lockedWallet).toBeInstanceOf(WalletLocked); + }); }); - it('Create a locked wallet', () => { - const lockedWallet = Wallet.fromAddress(wallet.address, provider); - expect(lockedWallet.address).toEqual(wallet.address); + describe('WalletLocked.unlock', () => { + it('should be able to unlock a locked wallet', () => { + const lockedWallet = Wallet.fromAddress(wallet.address, provider); + expect(lockedWallet).toBeInstanceOf(WalletLocked); + + const unlockedWallet = lockedWallet.unlock(wallet.privateKey); + + expect(unlockedWallet).toBeInstanceOf(WalletUnlocked); + expect(unlockedWallet.address).toStrictEqual(lockedWallet.address); + expect(unlockedWallet.privateKey).toEqual(wallet.privateKey); + }); }); - it('Unlock a locked wallet', () => { - const lockedWallet = Wallet.fromAddress(wallet.address, provider); - const unlockedWallet = lockedWallet.unlock(wallet.privateKey); - expect(unlockedWallet.address).toEqual(lockedWallet.address); - expect(unlockedWallet.privateKey).toEqual(wallet.privateKey); + describe('WalletUnlocked.constructor', () => { + it('Should instatiate from a constructor', () => { + const unlockedWallet = new WalletUnlocked(wallet.privateKey, provider); + + expect(unlockedWallet.address).toStrictEqual(wallet.address); + }); + + it('should instatiate from a constructor, without a provider', () => { + const unlockedWallet = new WalletUnlocked(wallet.privateKey); + + expect(unlockedWallet.address).toStrictEqual(wallet.address); + expect(unlockedWallet.privateKey).toEqual(wallet.privateKey); + expect(() => unlockedWallet.provider).toThrowError('Provider not set'); + expect(unlockedWallet).toBeInstanceOf(WalletUnlocked); + }); }); - it('Create from privateKey', () => { - const unlockedWallet = Wallet.fromPrivateKey(wallet.privateKey, provider); - expect(unlockedWallet.address).toStrictEqual(wallet.address); - expect(unlockedWallet.privateKey).toEqual(wallet.privateKey); + /** + * @see {@link WalletUnlocked.fromPrivateKey} + */ + describe('WalletUnlocked.fromPrivateKey', () => { + it('Should instantiate fromPrivateKey', () => { + const unlockedWallet = Wallet.fromPrivateKey(wallet.privateKey, provider); + + expect(unlockedWallet.address).toStrictEqual(wallet.address); + expect(unlockedWallet.privateKey).toEqual(wallet.privateKey); + }); + + it('Should instantiate fromPrivateKey, without a provider', () => { + const unlockedWallet = Wallet.fromPrivateKey(wallet.privateKey); + + expect(unlockedWallet.address).toStrictEqual(wallet.address); + expect(unlockedWallet.privateKey).toEqual(wallet.privateKey); + expect(() => unlockedWallet.provider).toThrowError('Provider not set'); + }); }); - it('encrypts and decrypts a JSON wallet', async () => { - wallet = WalletUnlocked.generate({ - provider, + /** + * @see {@link WalletUnlocked.generate} + */ + describe('WalletUnlocked.generate', () => { + it('Should instantiate from generate', () => { + const unlockedWallet = WalletUnlocked.generate({ provider }); + + expect(unlockedWallet.address).toBeDefined(); + expect(unlockedWallet.privateKey).toBeDefined(); }); - const password = 'password'; - const jsonWallet = await wallet.encrypt(password); - const decryptedWallet = await Wallet.fromEncryptedJson(jsonWallet, password, provider); + it('Should instantiate from generate, without a provider', () => { + const unlockedWallet = WalletUnlocked.generate(); - expect(decryptedWallet.address).toStrictEqual(wallet.address); - expect(decryptedWallet.privateKey).toEqual(wallet.privateKey); - expect(decryptedWallet.address.toB256()).toEqual(wallet.address.toB256()); + expect(unlockedWallet.address).toBeDefined(); + expect(unlockedWallet.privateKey).toBeDefined(); + expect(() => unlockedWallet.provider).toThrowError('Provider not set'); + }); }); - it('Should fail to decrypt JSON wallet for a given wrong password', async () => { - wallet = WalletUnlocked.generate({ - provider, + /** + * @see {@link WalletUnlocked.fromEncryptedJson} + */ + describe('WalletUnlocked.fromEncryptedJson', () => { + it('should encrypt and decrypt a JSON wallet', async () => { + wallet = WalletUnlocked.generate({ + provider, + }); + const password = 'password'; + const jsonWallet = await wallet.encrypt(password); + + const decryptedWallet = await Wallet.fromEncryptedJson(jsonWallet, password, provider); + + expect(decryptedWallet.address).toStrictEqual(wallet.address); + expect(decryptedWallet.privateKey).toEqual(wallet.privateKey); + expect(decryptedWallet.address.toB256()).toEqual(wallet.address.toB256()); }); - const password = 'password'; - const jsonWallet = await wallet.encrypt(password); - const { error, result } = await safeExec(() => - Wallet.fromEncryptedJson(jsonWallet, 'wrong-password', provider) - ); + it('should encrypt and decrypt a JSON wallet, without a provider', async () => { + wallet = WalletUnlocked.generate(); + const password = 'password'; + const jsonWallet = await wallet.encrypt(password); - expect(result).toBeUndefined(); - expect(error?.message).toBe( - 'Failed to decrypt the keystore wallet, the provided password is incorrect.' - ); + const decryptedWallet = await Wallet.fromEncryptedJson(jsonWallet, password); + + expect(decryptedWallet.address).toStrictEqual(wallet.address); + expect(decryptedWallet.privateKey).toEqual(wallet.privateKey); + expect(() => decryptedWallet.provider).toThrowError('Provider not set'); + }); + + it('Should fail to decrypt JSON wallet for a given wrong password', async () => { + wallet = WalletUnlocked.generate({ + provider, + }); + const password = 'password'; + const jsonWallet = await wallet.encrypt(password); + + const { error, result } = await safeExec(() => + Wallet.fromEncryptedJson(jsonWallet, 'wrong-password', provider) + ); + + expect(result).toBeUndefined(); + expect(error?.message).toBe( + 'Failed to decrypt the keystore wallet, the provided password is incorrect.' + ); + }); }); it('Provide a custom provider on a public wallet to the contract instance', async () => { @@ -102,7 +198,7 @@ describe('Wallet', () => { const transactionRequest = transactionRequestify(transactionRequestLike); // Simulate a external request of signature const signedTransaction = await externalWallet.signTransaction(transactionRequest); - transactionRequest.updateWitnessByOwner(externalWallet.address, signedTransaction); + transactionRequest.updateWitnessByOwner(externalWallet.address.toB256(), signedTransaction); return super.sendTransaction(transactionRequestLike); } } diff --git a/packages/wallet/src/wallet.ts b/packages/wallet/src/wallet.ts index 5d4df4422cb..13cba2b6ed4 100644 --- a/packages/wallet/src/wallet.ts +++ b/packages/wallet/src/wallet.ts @@ -12,10 +12,10 @@ export class Wallet { * Creates a locked wallet instance from an address and a provider. * * @param address - The address of the wallet. - * @param provider - A Provider instance. + * @param provider - A Provider instance (optional). * @returns A locked wallet instance. */ - static fromAddress(address: string | AbstractAddress, provider: Provider): WalletLocked { + static fromAddress(address: string | AbstractAddress, provider?: Provider): WalletLocked { return new WalletLocked(address, provider); } @@ -23,10 +23,10 @@ export class Wallet { * Creates an unlocked wallet instance from a private key and a provider. * * @param privateKey - The private key of the wallet. - * @param provider - A Provider instance. + * @param provider - A Provider instance (optional). * @returns An unlocked wallet instance. */ - static fromPrivateKey(privateKey: BytesLike, provider: Provider) { + static fromPrivateKey(privateKey: BytesLike, provider?: Provider) { return new WalletUnlocked(privateKey, provider); } @@ -42,8 +42,8 @@ export class Wallet { * Create a Wallet Unlocked from a seed. * * @param seed - The seed phrase. - * @param path - The derivation path (optional). * @param provider - A Provider instance (optional). + * @param path - The derivation path (optional). * @returns An unlocked wallet instance. */ static fromSeed = WalletUnlocked.fromSeed; @@ -52,9 +52,9 @@ export class Wallet { * Create a Wallet Unlocked from a mnemonic phrase. * * @param mnemonic - The mnemonic phrase. + * @param provider - A Provider instance (optional). * @param path - The derivation path (optional). * @param passphrase - The passphrase for the mnemonic (optional). - * @param provider - A Provider instance (optional). * @returns An unlocked wallet instance. */ static fromMnemonic = WalletUnlocked.fromMnemonic; @@ -67,5 +67,14 @@ export class Wallet { * @returns An unlocked wallet instance. */ static fromExtendedKey = WalletUnlocked.fromExtendedKey; + + /** + * Create a Wallet Unlocked from an encrypted JSON. + * + * @param jsonWallet - The encrypted JSON keystore. + * @param password - The password to decrypt the JSON. + * @param provider - A Provider instance (optional). + * @returns An unlocked wallet instance. + */ static fromEncryptedJson = WalletUnlocked.fromEncryptedJson; } diff --git a/packages/wallet/src/wallets.ts b/packages/wallet/src/wallets.ts index c3969a07991..6d2e6b68e7f 100644 --- a/packages/wallet/src/wallets.ts +++ b/packages/wallet/src/wallets.ts @@ -26,7 +26,7 @@ export class WalletLocked extends Account { */ unlock(privateKey: BytesLike): WalletUnlocked { // eslint-disable-next-line @typescript-eslint/no-use-before-define - return new WalletUnlocked(privateKey, this.provider); + return new WalletUnlocked(privateKey, this._provider); } } @@ -41,7 +41,7 @@ export class WalletUnlocked extends BaseWalletUnlocked { */ lock(): WalletLocked { this.signer = () => new Signer('0x00'); - return new WalletLocked(this.address, this.provider); + return new WalletLocked(this.address, this._provider); } /** @@ -50,7 +50,7 @@ export class WalletUnlocked extends BaseWalletUnlocked { * @param generateOptions - Options to customize the generation process (optional). * @returns An instance of WalletUnlocked. */ - static generate(generateOptions: GenerateOptions): WalletUnlocked { + static generate(generateOptions?: GenerateOptions): WalletUnlocked { const privateKey = Signer.generatePrivateKey(generateOptions?.entropy); return new WalletUnlocked(privateKey, generateOptions?.provider); @@ -60,11 +60,11 @@ export class WalletUnlocked extends BaseWalletUnlocked { * Create a Wallet Unlocked from a seed. * * @param seed - The seed phrase. - * @param provider - A Provider instance. + * @param provider - A Provider instance (optional). * @param path - The derivation path (optional). * @returns An instance of WalletUnlocked. */ - static fromSeed(seed: string, provider: Provider, path?: string): WalletUnlocked { + static fromSeed(seed: string, provider?: Provider, path?: string): WalletUnlocked { const hdWallet = HDWallet.fromSeed(seed); const childWallet = hdWallet.derivePath(path || WalletUnlocked.defaultPath); @@ -75,14 +75,14 @@ export class WalletUnlocked extends BaseWalletUnlocked { * Create a Wallet Unlocked from a mnemonic phrase. * * @param mnemonic - The mnemonic phrase. - * @param provider - A Provider instance. + * @param provider - A Provider instance (optional). * @param path - The derivation path (optional). * @param passphrase - The passphrase for the mnemonic (optional). * @returns An instance of WalletUnlocked. */ static fromMnemonic( mnemonic: string, - provider: Provider, + provider?: Provider, path?: string, passphrase?: BytesLike ): WalletUnlocked { @@ -97,19 +97,27 @@ export class WalletUnlocked extends BaseWalletUnlocked { * Create a Wallet Unlocked from an extended key. * * @param extendedKey - The extended key. - * @param provider - A Provider instance. + * @param provider - A Provider instance (optional). * @returns An instance of WalletUnlocked. */ - static fromExtendedKey(extendedKey: string, provider: Provider): WalletUnlocked { + static fromExtendedKey(extendedKey: string, provider?: Provider): WalletUnlocked { const hdWallet = HDWallet.fromExtendedKey(extendedKey); return new WalletUnlocked(hdWallet.privateKey, provider); } + /** + * Create a Wallet Unlocked from an encrypted JSON. + * + * @param jsonWallet - The encrypted JSON keystore. + * @param password - The password to decrypt the JSON. + * @param provider - A Provider instance (optional). + * @returns An unlocked wallet instance. + */ static async fromEncryptedJson( jsonWallet: string, password: string, - provider: Provider + provider?: Provider ): Promise { const privateKey = await decryptKeystoreWallet(jsonWallet, password); diff --git a/packages/wordlists/README.md b/packages/wordlists/README.md index 34761cb2996..3ffb7b78adf 100644 --- a/packages/wordlists/README.md +++ b/packages/wordlists/README.md @@ -18,7 +18,7 @@ This module contains a wordlist for BIP 39 mnemonic phrases, currently limited t -See [Fuels-ts Documentation](https://fuellabs.github.io/fuels-ts/) +See [Fuels-ts Documentation](https://docs.fuel.network/docs/fuels-ts/) ## Usage diff --git a/packages/wordlists/package.json b/packages/wordlists/package.json index ea579771849..05ee69d0706 100644 --- a/packages/wordlists/package.json +++ b/packages/wordlists/package.json @@ -8,7 +8,7 @@ "module": "dist/index.mjs", "types": "dist/index.d.ts", "engines": { - "node": "^18.17.1" + "node": "^18.18.2 || ^20.0.0" }, "exports": { ".": { diff --git a/packages/wordlists/src/wordlists.test.ts b/packages/wordlists/src/wordlists.test.ts index ca98da15298..3d72a3e917e 100644 --- a/packages/wordlists/src/wordlists.test.ts +++ b/packages/wordlists/src/wordlists.test.ts @@ -7,6 +7,9 @@ const checksum = (wordlists: string[]) => .update(Buffer.from(`${wordlists.join('\n')}\n`)) .digest('hex'); +/** + * @group node + */ describe('Checksum word lists', () => { test('Checksum english list', () => { expect(checksum(english)).toBe( diff --git a/packages/wordlists/tsdoc.json b/packages/wordlists/tsdoc.json new file mode 100644 index 00000000000..4514b072727 --- /dev/null +++ b/packages/wordlists/tsdoc.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["../../tsdoc.base.json"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33d19326297..ccaf958ca6d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,18 +23,18 @@ importers: '@fuel-ts/fuel-core': specifier: workspace:* version: link:packages/fuel-core + '@fuel-ts/utils': + specifier: workspace:* + version: link:packages/utils '@fuel-ts/versions': specifier: workspace:^ version: link:packages/versions '@internal/tsup': specifier: workspace:* version: link:internal/tsup - '@jest/types': - specifier: ^29.5.0 - version: 29.5.0 - '@types/jest': - specifier: ^29.5.0 - version: 29.5.0 + '@istanbuljs/nyc-config-typescript': + specifier: ^1.0.2 + version: 1.0.2(nyc@15.1.0) '@types/node': specifier: 18.15.3 version: 18.15.3 @@ -50,12 +50,21 @@ importers: '@typescript-eslint/parser': specifier: ^6.9.0 version: 6.9.1(eslint@8.52.0)(typescript@5.2.2) + '@vitest/browser': + specifier: ^1.1.3 + version: 1.1.3(vitest@1.1.3)(webdriverio@8.27.2) + '@vitest/coverage-istanbul': + specifier: ^1.1.3 + version: 1.1.3(vitest@1.1.3) compare-versions: specifier: ^6.1.0 version: 6.1.0 conventional-changelog-angular: specifier: ^5.0.13 version: 5.0.13 + coverage-diff: + specifier: ^3.2.0 + version: 3.2.0 dotenv: specifier: ^9.0.2 version: 9.0.2 @@ -95,24 +104,21 @@ importers: eslint-plugin-tsdoc: specifier: ^0.2.17 version: 0.2.17 - ethers: - specifier: ^6.7.1 - version: 6.7.1 glob: specifier: ^10.2.6 version: 10.2.6 - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@18.15.3) - jest-text-transformer: - specifier: ^1.0.4 - version: 1.0.4 + memfs: + specifier: ^4.6.0 + version: 4.6.0(quill-delta@5.1.0)(rxjs@7.8.1)(tslib@2.6.0) nodemon: specifier: ^2.0.22 version: 2.0.22 npm-run-all: specifier: ^4.1.5 version: 4.1.5 + nyc: + specifier: ^15.1.0 + version: 15.1.0 open: specifier: ^8.4.0 version: 8.4.0 @@ -131,21 +137,33 @@ importers: ts-generator: specifier: ^0.1.1 version: 0.1.1 - ts-jest: - specifier: ^29.1.1 - version: 29.1.1(@babel/core@7.23.3)(@jest/types@29.5.0)(esbuild@0.17.19)(jest@29.7.0)(typescript@5.2.2) tsup: specifier: ^6.7.0 version: 6.7.0(typescript@5.2.2) tsx: - specifier: ^3.12.7 - version: 3.12.7 + specifier: ^4.7.0 + version: 4.7.0 turbo: specifier: ^1.8.8 version: 1.8.8 typescript: specifier: ~5.2.2 version: 5.2.2 + vite: + specifier: ^5.0.11 + version: 5.0.11(@types/node@18.15.3) + vite-plugin-node-polyfills: + specifier: ^0.17.0 + version: 0.17.0(vite@5.0.11) + vite-plugin-plain-text: + specifier: ^1.4.2 + version: 1.4.2 + vitest: + specifier: ^1.1.3 + version: 1.1.3(@types/node@18.15.3)(@vitest/browser@1.1.3) + webdriverio: + specifier: ^8.27.0 + version: 8.27.2(typescript@5.2.2) apps/demo-fuels: dependencies: @@ -450,6 +468,8 @@ importers: specifier: workspace:* version: link:../../packages/fuels + internal/check-tests: {} + internal/tsup: {} packages/abi-coder: @@ -764,9 +784,6 @@ importers: lodash.camelcase: specifier: ^4.3.0 version: 4.3.0 - npm-which: - specifier: ^3.0.1 - version: 3.0.1 portfinder: specifier: ^1.0.32 version: 1.0.32 @@ -791,7 +808,7 @@ importers: version: 3.0.2 vite: specifier: ^4.3.9 - version: 4.3.9(@types/node@18.15.3) + version: 4.3.9 packages/hasher: dependencies: @@ -999,7 +1016,7 @@ importers: specifier: ^16.6.0 version: 16.6.0 graphql-request: - specifier: ^5.0.0 + specifier: 5.0.0 version: 5.0.0(graphql@16.6.0) graphql-tag: specifier: ^2.12.6 @@ -1020,9 +1037,9 @@ importers: '@graphql-codegen/typescript': specifier: ^2.8.0 version: 2.8.0(graphql@16.6.0) - '@graphql-codegen/typescript-graphql-request': - specifier: ^4.5.7 - version: 4.5.7(graphql-request@5.0.0)(graphql-tag@2.12.6)(graphql@16.6.0) + '@graphql-codegen/typescript-generic-sdk': + specifier: ^3.1.0 + version: 3.1.0(graphql-tag@2.12.6)(graphql@16.6.0) '@graphql-codegen/typescript-operations': specifier: ^2.5.5 version: 2.5.5(graphql@16.6.0) @@ -1090,16 +1107,12 @@ importers: '@fuel-ts/math': specifier: workspace:* version: link:../math - elliptic: - specifier: ^6.5.4 - version: 6.5.4 + '@noble/curves': + specifier: ^1.3.0 + version: 1.3.0 ethers: specifier: ^6.7.1 version: 6.7.1 - devDependencies: - '@types/elliptic': - specifier: ^6.4.14 - version: 6.4.14 packages/transactions: dependencies: @@ -1124,9 +1137,9 @@ importers: packages/utils: dependencies: - ethers: - specifier: ^6.7.1 - version: 6.7.1 + '@fuel-ts/interfaces': + specifier: workspace:* + version: link:../interfaces ramda: specifier: ^0.29.0 version: 0.29.0 @@ -1146,16 +1159,10 @@ importers: cli-table: specifier: ^0.3.11 version: 0.3.11 - semver: - specifier: ^7.3.8 - version: 7.3.8 devDependencies: '@types/cli-table': specifier: ^0.3.1 version: 0.3.1 - '@types/semver': - specifier: ^7.3.13 - version: 7.3.13 packages/wallet: dependencies: @@ -1318,6 +1325,7 @@ packages: /@adraffy/ens-normalize@1.9.2: resolution: {integrity: sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==} + dev: false /@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.20.0)(algoliasearch@4.18.0)(search-insights@2.11.0): resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==} @@ -1906,7 +1914,7 @@ packages: '@babel/helper-function-name': 7.22.5 '@babel/template': 7.22.5 '@babel/traverse': 7.22.5 - '@babel/types': 7.22.5 + '@babel/types': 7.23.4 transitivePeerDependencies: - supports-color dev: false @@ -2114,6 +2122,16 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.3): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} @@ -2122,6 +2140,16 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.22.5): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} @@ -2131,6 +2159,15 @@ packages: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.3): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.22.5): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} @@ -2225,6 +2262,16 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.3): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} @@ -2233,6 +2280,16 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.5): resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} @@ -2260,6 +2317,16 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.3): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} @@ -2268,6 +2335,16 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.22.5): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} @@ -2276,6 +2353,16 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.3): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} @@ -2285,6 +2372,15 @@ packages: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: @@ -2292,6 +2388,16 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.22.5): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} @@ -2300,6 +2406,16 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.3): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.22.5): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} @@ -2319,6 +2435,17 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.3): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.22.5): resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} @@ -2328,6 +2455,17 @@ packages: dependencies: '@babel/core': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 + dev: false + + /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.3): + resolution: {integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.23.3 + '@babel/helper-plugin-utils': 7.22.5 + dev: false /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.22.5): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} @@ -3236,6 +3374,7 @@ packages: /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: false /@changesets/apply-release-plan@6.1.4: resolution: {integrity: sha512-FMpKF1fRlJyCZVYHr3CbinpZZ+6MwvOtWUuO8uo+svcATEoc1zRDcj23pAurJ2TZ/uVz1wFHH6K3NlACy0PLew==} @@ -3668,26 +3807,14 @@ packages: jsdoc-type-pratt-parser: 4.0.0 dev: true - /@esbuild-kit/cjs-loader@2.4.2: - resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==} - dependencies: - '@esbuild-kit/core-utils': 3.1.0 - get-tsconfig: 4.6.2 - dev: true - - /@esbuild-kit/core-utils@3.1.0: - resolution: {integrity: sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==} - dependencies: - esbuild: 0.17.19 - source-map-support: 0.5.21 - dev: true - - /@esbuild-kit/esm-loader@2.5.5: - resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==} - dependencies: - '@esbuild-kit/core-utils': 3.1.0 - get-tsconfig: 4.6.2 + /@esbuild/aix-ppc64@0.19.11: + resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true dev: true + optional: true /@esbuild/android-arm64@0.17.19: resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} @@ -3697,6 +3824,15 @@ packages: requiresBuild: true optional: true + /@esbuild/android-arm64@0.19.11: + resolution: {integrity: sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm64@0.19.3: resolution: {integrity: sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw==} engines: {node: '>=12'} @@ -3714,6 +3850,15 @@ packages: requiresBuild: true optional: true + /@esbuild/android-arm@0.19.11: + resolution: {integrity: sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm@0.19.3: resolution: {integrity: sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA==} engines: {node: '>=12'} @@ -3731,6 +3876,15 @@ packages: requiresBuild: true optional: true + /@esbuild/android-x64@0.19.11: + resolution: {integrity: sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64@0.19.3: resolution: {integrity: sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ==} engines: {node: '>=12'} @@ -3748,6 +3902,15 @@ packages: requiresBuild: true optional: true + /@esbuild/darwin-arm64@0.19.11: + resolution: {integrity: sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64@0.19.3: resolution: {integrity: sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw==} engines: {node: '>=12'} @@ -3765,6 +3928,15 @@ packages: requiresBuild: true optional: true + /@esbuild/darwin-x64@0.19.11: + resolution: {integrity: sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64@0.19.3: resolution: {integrity: sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw==} engines: {node: '>=12'} @@ -3782,6 +3954,15 @@ packages: requiresBuild: true optional: true + /@esbuild/freebsd-arm64@0.19.11: + resolution: {integrity: sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64@0.19.3: resolution: {integrity: sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg==} engines: {node: '>=12'} @@ -3799,6 +3980,15 @@ packages: requiresBuild: true optional: true + /@esbuild/freebsd-x64@0.19.11: + resolution: {integrity: sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64@0.19.3: resolution: {integrity: sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA==} engines: {node: '>=12'} @@ -3816,6 +4006,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-arm64@0.19.11: + resolution: {integrity: sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64@0.19.3: resolution: {integrity: sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ==} engines: {node: '>=12'} @@ -3833,6 +4032,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-arm@0.19.11: + resolution: {integrity: sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm@0.19.3: resolution: {integrity: sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ==} engines: {node: '>=12'} @@ -3850,6 +4058,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-ia32@0.19.11: + resolution: {integrity: sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32@0.19.3: resolution: {integrity: sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA==} engines: {node: '>=12'} @@ -3867,6 +4084,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-loong64@0.19.11: + resolution: {integrity: sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64@0.19.3: resolution: {integrity: sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A==} engines: {node: '>=12'} @@ -3884,6 +4110,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-mips64el@0.19.11: + resolution: {integrity: sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el@0.19.3: resolution: {integrity: sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A==} engines: {node: '>=12'} @@ -3901,6 +4136,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-ppc64@0.19.11: + resolution: {integrity: sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64@0.19.3: resolution: {integrity: sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g==} engines: {node: '>=12'} @@ -3918,21 +4162,39 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-riscv64@0.19.3: - resolution: {integrity: sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==} + /@esbuild/linux-riscv64@0.19.11: + resolution: {integrity: sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] requiresBuild: true - dev: false + dev: true optional: true - /@esbuild/linux-s390x@0.17.19: - resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + /@esbuild/linux-riscv64@0.19.3: + resolution: {integrity: sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-s390x@0.17.19: + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + optional: true + + /@esbuild/linux-s390x@0.19.11: + resolution: {integrity: sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==} engines: {node: '>=12'} cpu: [s390x] os: [linux] requiresBuild: true + dev: true optional: true /@esbuild/linux-s390x@0.19.3: @@ -3952,6 +4214,15 @@ packages: requiresBuild: true optional: true + /@esbuild/linux-x64@0.19.11: + resolution: {integrity: sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64@0.19.3: resolution: {integrity: sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ==} engines: {node: '>=12'} @@ -3969,6 +4240,15 @@ packages: requiresBuild: true optional: true + /@esbuild/netbsd-x64@0.19.11: + resolution: {integrity: sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64@0.19.3: resolution: {integrity: sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw==} engines: {node: '>=12'} @@ -3986,6 +4266,15 @@ packages: requiresBuild: true optional: true + /@esbuild/openbsd-x64@0.19.11: + resolution: {integrity: sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64@0.19.3: resolution: {integrity: sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q==} engines: {node: '>=12'} @@ -4003,6 +4292,15 @@ packages: requiresBuild: true optional: true + /@esbuild/sunos-x64@0.19.11: + resolution: {integrity: sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64@0.19.3: resolution: {integrity: sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw==} engines: {node: '>=12'} @@ -4020,6 +4318,15 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-arm64@0.19.11: + resolution: {integrity: sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64@0.19.3: resolution: {integrity: sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A==} engines: {node: '>=12'} @@ -4037,6 +4344,15 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-ia32@0.19.11: + resolution: {integrity: sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32@0.19.3: resolution: {integrity: sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q==} engines: {node: '>=12'} @@ -4054,6 +4370,15 @@ packages: requiresBuild: true optional: true + /@esbuild/win32-x64@0.19.11: + resolution: {integrity: sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64@0.19.3: resolution: {integrity: sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw==} engines: {node: '>=12'} @@ -4415,18 +4740,16 @@ packages: tslib: 2.4.1 dev: true - /@graphql-codegen/typescript-graphql-request@4.5.7(graphql-request@5.0.0)(graphql-tag@2.12.6)(graphql@16.6.0): - resolution: {integrity: sha512-1YPaCO+0q5z0Um6Om+5LMWdB8+WQxda8eXRXwy0dqSGRy9X5HTZz/pxqaTgy76yMtPBxq1UNa7lruBTzszHhJg==} + /@graphql-codegen/typescript-generic-sdk@3.1.0(graphql-tag@2.12.6)(graphql@16.6.0): + resolution: {integrity: sha512-nQZi/YGRI1+qCZZsh0V5nz6+hCHSN4OU9tKyOTDsEPyDFnGEukDuRdCH2IZasGn22a3Iu5TUDkgp5w9wEQwGmg==} peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - graphql-request: ^3.4.0 || ^4.0.0 || ^5.0.0 graphql-tag: ^2.0.0 dependencies: - '@graphql-codegen/plugin-helpers': 2.7.2(graphql@16.6.0) - '@graphql-codegen/visitor-plugin-common': 2.13.0(graphql@16.6.0) + '@graphql-codegen/plugin-helpers': 3.1.2(graphql@16.6.0) + '@graphql-codegen/visitor-plugin-common': 2.13.1(graphql@16.6.0) auto-bind: 4.0.0 graphql: 16.6.0 - graphql-request: 5.0.0(graphql@16.6.0) graphql-tag: 2.12.6(graphql@16.6.0) tslib: 2.4.1 transitivePeerDependencies: @@ -4487,6 +4810,27 @@ packages: - supports-color dev: true + /@graphql-codegen/visitor-plugin-common@2.13.1(graphql@16.6.0): + resolution: {integrity: sha512-mD9ufZhDGhyrSaWQGrU1Q1c5f01TeWtSWy/cDwXYjJcHIj1Y/DG2x0tOflEfCvh5WcnmHNIw4lzDsg1W7iFJEg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 2.7.2(graphql@16.6.0) + '@graphql-tools/optimize': 1.4.0(graphql@16.6.0) + '@graphql-tools/relay-operation-optimizer': 6.5.18(graphql@16.6.0) + '@graphql-tools/utils': 8.13.1(graphql@16.6.0) + auto-bind: 4.0.0 + change-case-all: 1.0.14 + dependency-graph: 0.11.0 + graphql: 16.6.0 + graphql-tag: 2.12.6(graphql@16.6.0) + parse-filepath: 1.0.2 + tslib: 2.4.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@graphql-tools/apollo-engine-loader@7.3.26(graphql@16.6.0): resolution: {integrity: sha512-h1vfhdJFjnCYn9b5EY1Z91JTF0KB3hHVJNQIsiUV2mpQXZdeOXQoaWeYEKaiI5R6kwBw5PP9B0fv3jfUIG8LyQ==} peerDependencies: @@ -4935,6 +5279,16 @@ packages: js-yaml: 3.14.1 resolve-from: 5.0.0 + /@istanbuljs/nyc-config-typescript@1.0.2(nyc@15.1.0): + resolution: {integrity: sha512-iKGIyMoyJuFnJRSVTZ78POIRvNnwZaWIf8vG4ZS3rQq58MMDrqEX2nnzx0R28V2X8JvmKYiqY9FP2hlJsm8A0w==} + engines: {node: '>=8'} + peerDependencies: + nyc: '>=15' + dependencies: + '@istanbuljs/schema': 0.1.3 + nyc: 15.1.0 + dev: true + /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} @@ -4944,7 +5298,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 chalk: 4.1.2 jest-message-util: 27.5.1 jest-util: 27.5.1 @@ -4956,25 +5310,13 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 - '@types/node': 16.18.34 + '@types/node': 20.10.5 chalk: 4.1.2 jest-message-util: 28.1.3 jest-util: 28.1.3 slash: 3.0.0 dev: false - /@jest/console@29.7.0: - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - dev: true - /@jest/core@27.5.1: resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -4989,7 +5331,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.8.1 @@ -5020,91 +5362,22 @@ packages: - utf-8-validate dev: false - /@jest/core@29.7.0: - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.8.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@16.18.34) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /@jest/environment@27.5.1: resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 jest-mock: 27.5.1 dev: false - /@jest/environment@29.7.0: - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - jest-mock: 29.7.0 - dev: true - /@jest/expect-utils@29.5.0: resolution: {integrity: sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: jest-get-type: 29.4.3 - - /@jest/expect-utils@29.7.0: - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.6.3 - dev: true - - /@jest/expect@29.7.0: - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true + dev: false /@jest/fake-timers@27.5.1: resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==} @@ -5112,24 +5385,12 @@ packages: dependencies: '@jest/types': 27.5.1 '@sinonjs/fake-timers': 8.1.0 - '@types/node': 16.18.34 + '@types/node': 20.10.5 jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 dev: false - /@jest/fake-timers@29.7.0: - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 16.18.34 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - dev: true - /@jest/globals@27.5.1: resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -5139,18 +5400,6 @@ packages: expect: 27.5.1 dev: false - /@jest/globals@29.7.0: - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/reporters@27.5.1: resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -5165,17 +5414,17 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 glob: 7.2.3 graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 istanbul-lib-instrument: 5.2.1 istanbul-lib-report: 3.0.0 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 + istanbul-reports: 3.1.6 jest-haste-map: 27.5.1 jest-resolve: 27.5.1 jest-util: 27.5.1 @@ -5189,43 +5438,6 @@ packages: - supports-color dev: false - /@jest/reporters@29.7.0: - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 16.18.34 - chalk: 4.1.2 - collect-v8-coverage: 1.0.1 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 6.0.1 - istanbul-lib-report: 3.0.0 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.1.0 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/schemas@28.1.3: resolution: {integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -5238,6 +5450,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@sinclair/typebox': 0.25.24 + dev: false /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} @@ -5255,15 +5468,6 @@ packages: source-map: 0.6.1 dev: false - /@jest/source-map@29.6.3: - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.18 - callsites: 3.1.0 - graceful-fs: 4.2.11 - dev: true - /@jest/test-result@27.5.1: resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -5284,16 +5488,6 @@ packages: collect-v8-coverage: 1.0.1 dev: false - /@jest/test-result@29.7.0: - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.1 - dev: true - /@jest/test-sequencer@27.5.1: resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -5306,16 +5500,6 @@ packages: - supports-color dev: false - /@jest/test-sequencer@29.7.0: - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - dev: true - /@jest/transform@27.5.1: resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -5339,36 +5523,13 @@ packages: - supports-color dev: false - /@jest/transform@29.7.0: - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.22.5 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.18 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.5 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/types@27.5.1: resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 '@types/yargs': 16.0.5 chalk: 4.1.2 dev: false @@ -5380,7 +5541,7 @@ packages: '@jest/schemas': 28.1.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 '@types/yargs': 17.0.24 chalk: 4.1.2 dev: false @@ -5395,18 +5556,7 @@ packages: '@types/node': 16.18.34 '@types/yargs': 17.0.24 chalk: 4.1.2 - - /@jest/types@29.6.3: - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 16.18.34 - '@types/yargs': 17.0.24 - chalk: 4.1.2 - dev: true + dev: false /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} @@ -5497,13 +5647,149 @@ packages: resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} dev: true - /@next/env@13.4.4: - resolution: {integrity: sha512-q/y7VZj/9YpgzDe64Zi6rY1xPizx80JjlU2BTevlajtaE3w1LqweH1gGgxou2N7hdFosXHjGrI4OUvtFXXhGLg==} - dev: false + /@napi-rs/magic-string-android-arm-eabi@0.3.4: + resolution: {integrity: sha512-sszAYxqtzzJ4FDerDNHcqL9NhqPhj8W4DNiOanXYy50mA5oojlRtaAFPiB5ZMrWDBM32v5Q30LrmxQ4eTtu2Dg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true - /@next/env@14.0.1: - resolution: {integrity: sha512-Ms8ZswqY65/YfcjrlcIwMPD7Rg/dVjdLapMcSHG26W6O67EJDF435ShW4H4LXi1xKO1oRc97tLXUpx8jpLe86A==} - dev: false + /@napi-rs/magic-string-android-arm64@0.3.4: + resolution: {integrity: sha512-jdQ6HuO0X5rkX4MauTcWR4HWdgjakTOmmzqXg8L26+jOHVVG1LZE+Su5qvV4bP8vMb2h+vPE+JsnwqSmWymu3Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-darwin-arm64@0.3.4: + resolution: {integrity: sha512-6NmMtvURce9/oq09XBZmuIeI6lPLGtEJ2ZPO/QzL3nLZa6wygiCnO/sFACKYNg5/73ET5HMMTeuogE1JI+r2Lw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-darwin-x64@0.3.4: + resolution: {integrity: sha512-f9LmfMiUAKDOtl0meOuLYeVb6OERrgGzrTg1Tn3R3fTAShM2kxRbfAuPE9ljuXxIFzOv/uqRNLSl/LqCJwpREA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-freebsd-x64@0.3.4: + resolution: {integrity: sha512-rqduQ4odiDK4QdM45xHWRTU4wtFIfpp8g8QGpz+3qqg7ivldDqbbNOrBaf6Oeu77uuEvWggnkyuChotfKgJdJQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-linux-arm-gnueabihf@0.3.4: + resolution: {integrity: sha512-pVaJEdEpiPqIfq3M4+yMAATS7Z9muDcWYn8H7GFH1ygh8GwgLgKfy/n/lG2M6zp18Mwd0x7E2E/qg9GgCyUzoQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-linux-arm64-gnu@0.3.4: + resolution: {integrity: sha512-9FwoAih/0tzEZx0BjYYIxWkSRMjonIn91RFM3q3MBs/evmThXUYXUqLNa1PPIkK1JoksswtDi48qWWLt8nGflQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-linux-arm64-musl@0.3.4: + resolution: {integrity: sha512-wCR7R+WPOcAKmVQc1s6h6HwfwW1vL9pM8BjUY9Ljkdb8wt1LmZEmV2Sgfc1SfbRQzbyl+pKeufP6adRRQVzYDA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-linux-x64-gnu@0.3.4: + resolution: {integrity: sha512-sbxFDpYnt5WFbxQ1xozwOvh5A7IftqSI0WnE9O7KsQIOi0ej2dvFbfOW4tmFkvH/YP8KJELo5AhP2+kEq1DpYA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-linux-x64-musl@0.3.4: + resolution: {integrity: sha512-jN4h/7e2Ul8v3UK5IZu38NXLMdzVWhY4uEDlnwuUAhwRh26wBQ1/pLD97Uy/Z3dFNBQPcsv60XS9fOM1YDNT6w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-win32-arm64-msvc@0.3.4: + resolution: {integrity: sha512-gMUyTRHLWpzX2ntJFCbW2Gnla9Y/WUmbkZuW5SBAo/Jo8QojHn76Y4PNgnoXdzcsV9b/45RBxurYKAfFg9WTyg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-win32-ia32-msvc@0.3.4: + resolution: {integrity: sha512-QIMauMOvEHgL00K9np/c9CT/CRtLOz3mRTQqcZ9XGzSoAMrpxH71KSpDJrKl7h7Ro6TZ+hJ0C3T+JVuTCZNv4A==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string-win32-x64-msvc@0.3.4: + resolution: {integrity: sha512-V8FMSf828MzOI3P6/765MR7zHU6CUZqiyPhmAnwYoKFNxfv7oCviN/G6NcENeCdcYOvNgh5fYzaNLB96ndId5A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@napi-rs/magic-string@0.3.4: + resolution: {integrity: sha512-DEWl/B99RQsyMT3F9bvrXuhL01/eIQp/dtNSE3G1jQ4mTGRcP4iHWxoPZ577WrbjUinrNgvRA5+08g8fkPgimQ==} + engines: {node: '>= 10'} + optionalDependencies: + '@napi-rs/magic-string-android-arm-eabi': 0.3.4 + '@napi-rs/magic-string-android-arm64': 0.3.4 + '@napi-rs/magic-string-darwin-arm64': 0.3.4 + '@napi-rs/magic-string-darwin-x64': 0.3.4 + '@napi-rs/magic-string-freebsd-x64': 0.3.4 + '@napi-rs/magic-string-linux-arm-gnueabihf': 0.3.4 + '@napi-rs/magic-string-linux-arm64-gnu': 0.3.4 + '@napi-rs/magic-string-linux-arm64-musl': 0.3.4 + '@napi-rs/magic-string-linux-x64-gnu': 0.3.4 + '@napi-rs/magic-string-linux-x64-musl': 0.3.4 + '@napi-rs/magic-string-win32-arm64-msvc': 0.3.4 + '@napi-rs/magic-string-win32-ia32-msvc': 0.3.4 + '@napi-rs/magic-string-win32-x64-msvc': 0.3.4 + dev: true + + /@next/env@13.4.4: + resolution: {integrity: sha512-q/y7VZj/9YpgzDe64Zi6rY1xPizx80JjlU2BTevlajtaE3w1LqweH1gGgxou2N7hdFosXHjGrI4OUvtFXXhGLg==} + dev: false + + /@next/env@14.0.1: + resolution: {integrity: sha512-Ms8ZswqY65/YfcjrlcIwMPD7Rg/dVjdLapMcSHG26W6O67EJDF435ShW4H4LXi1xKO1oRc97tLXUpx8jpLe86A==} + dev: false /@next/eslint-plugin-next@14.0.1: resolution: {integrity: sha512-bLjJMwXdzvhnQOnxvHoTTUh/+PYk6FF/DCgHi4BXwXCINer+o1ZYfL9aVeezj/oI7wqGJOqwGIXrlBvPbAId3w==} @@ -5685,16 +5971,29 @@ packages: '@noble/hashes': 1.3.1 dev: false + /@noble/curves@1.3.0: + resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==} + dependencies: + '@noble/hashes': 1.3.3 + dev: false + /@noble/hashes@1.1.2: resolution: {integrity: sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==} + dev: false /@noble/hashes@1.3.1: resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} engines: {node: '>= 16'} dev: false + /@noble/hashes@1.3.3: + resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} + engines: {node: '>= 16'} + dev: false + /@noble/secp256k1@1.7.1: resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} + dev: false /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -5798,6 +6097,48 @@ packages: webpack-dev-server: 4.15.1(webpack@5.88.0) dev: false + /@polka/url@1.0.0-next.24: + resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} + dev: true + + /@puppeteer/browsers@1.4.6(typescript@5.2.2): + resolution: {integrity: sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==} + engines: {node: '>=16.3.0'} + hasBin: true + peerDependencies: + typescript: '>= 4.7.4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + debug: 4.3.4 + extract-zip: 2.0.1 + progress: 2.0.3 + proxy-agent: 6.3.0 + tar-fs: 3.0.4 + typescript: 5.2.2 + unbzip2-stream: 1.4.3 + yargs: 17.7.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@puppeteer/browsers@1.9.1: + resolution: {integrity: sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==} + engines: {node: '>=16.3.0'} + hasBin: true + dependencies: + debug: 4.3.4 + extract-zip: 2.0.1 + progress: 2.0.3 + proxy-agent: 6.3.1 + tar-fs: 3.0.4 + unbzip2-stream: 1.4.3 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + dev: true + /@radix-ui/colors@2.1.0: resolution: {integrity: sha512-gcBnxjS2u2c6thQz/9K1+Pt2ZYcm5WKU4SLi0emYkRmYbVUw+37rlc5wgLtYOsSsRP9nxVtbJJYj6WVO7UUmZg==} dev: false @@ -7841,6 +8182,20 @@ packages: rollup: 2.79.1 dev: false + /@rollup/plugin-inject@5.0.5: + resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0 + estree-walker: 2.0.2 + magic-string: 0.30.5 + dev: true + /@rollup/plugin-node-resolve@11.2.1(rollup@2.79.1): resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} engines: {node: '>= 10.0.0'} @@ -7878,6 +8233,124 @@ packages: rollup: 2.79.1 dev: false + /@rollup/pluginutils@5.1.0: + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.1 + estree-walker: 2.0.2 + picomatch: 2.3.1 + dev: true + + /@rollup/rollup-android-arm-eabi@4.9.4: + resolution: {integrity: sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.9.4: + resolution: {integrity: sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.9.4: + resolution: {integrity: sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.9.4: + resolution: {integrity: sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.9.4: + resolution: {integrity: sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.9.4: + resolution: {integrity: sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.9.4: + resolution: {integrity: sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.9.4: + resolution: {integrity: sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.9.4: + resolution: {integrity: sha512-dIYgo+j1+yfy81i0YVU5KnQrIJZE8ERomx17ReU4GREjGtDW4X+nvkBak2xAUpyqLs4eleDSj3RrV72fQos7zw==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.9.4: + resolution: {integrity: sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.9.4: + resolution: {integrity: sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.9.4: + resolution: {integrity: sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.9.4: + resolution: {integrity: sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@rushstack/eslint-patch@1.3.2: resolution: {integrity: sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==} dev: false @@ -7911,29 +8384,23 @@ packages: /@sinclair/typebox@0.25.24: resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} + dev: false /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true + /@sindresorhus/is@5.6.0: + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + dev: true + /@sinonjs/commons@1.8.6: resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} dependencies: type-detect: 4.0.8 dev: false - /@sinonjs/commons@3.0.0: - resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} - dependencies: - type-detect: 4.0.8 - dev: true - - /@sinonjs/fake-timers@10.3.0: - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - dependencies: - '@sinonjs/commons': 3.0.0 - dev: true - /@sinonjs/fake-timers@8.1.0: resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==} dependencies: @@ -8083,6 +8550,13 @@ packages: tslib: 2.6.0 dev: false + /@szmarczak/http-timer@5.0.1: + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + dependencies: + defer-to-connect: 2.0.1 + dev: true + /@tabler/icons@2.44.0: resolution: {integrity: sha512-WPPtihDcAwEm1QZM9MXQw6+r/R2/qx7KMU1eegsi9DsqBLAb0W2kbt6e/syvd6j9c+6XNpRVBW1ziGqSWQAWOg==} dev: false @@ -8326,6 +8800,10 @@ packages: engines: {node: '>= 6'} dev: false + /@tootallnate/quickjs-emscripten@0.23.0: + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + dev: true + /@trysound/sax@0.2.0: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -8363,39 +8841,44 @@ packages: '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.20.1 + dev: false /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: '@babel/types': 7.22.5 + dev: false /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: '@babel/parser': 7.22.5 '@babel/types': 7.22.5 + dev: false /@types/babel__traverse@7.20.1: resolution: {integrity: sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==} dependencies: '@babel/types': 7.22.5 + dev: false /@types/bn.js@5.1.1: resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==} dependencies: '@types/node': 16.18.34 + dev: false /@types/body-parser@1.19.2: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false /@types/bonjour@3.5.10: resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false /@types/cli-table@0.3.1: @@ -8406,21 +8889,15 @@ packages: resolution: {integrity: sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==} dependencies: '@types/express-serve-static-core': 4.17.35 - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false /@types/connect@3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false - /@types/elliptic@6.4.14: - resolution: {integrity: sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ==} - dependencies: - '@types/bn.js': 5.1.1 - dev: true - /@types/eslint-scope@3.7.4: resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} dependencies: @@ -8441,12 +8918,15 @@ packages: /@types/estree@1.0.1: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} - dev: false + + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true /@types/express-serve-static-core@4.17.35: resolution: {integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 '@types/send': 0.17.1 @@ -8475,12 +8955,17 @@ packages: /@types/graceful-fs@4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 + dev: false /@types/html-minifier-terser@6.1.0: resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} dev: false + /@types/http-cache-semantics@4.0.4: + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + dev: true + /@types/http-errors@2.0.1: resolution: {integrity: sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==} dev: false @@ -8488,7 +8973,7 @@ packages: /@types/http-proxy@1.17.11: resolution: {integrity: sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false /@types/is-ci@3.0.0: @@ -8499,16 +8984,19 @@ packages: /@types/istanbul-lib-coverage@2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + dev: false /@types/istanbul-lib-report@3.0.0: resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} dependencies: '@types/istanbul-lib-coverage': 2.0.4 + dev: false /@types/istanbul-reports@3.0.1: resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} dependencies: '@types/istanbul-lib-report': 3.0.0 + dev: false /@types/jest@27.5.2: resolution: {integrity: sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==} @@ -8522,6 +9010,7 @@ packages: dependencies: expect: 29.5.0 pretty-format: 29.5.0 + dev: false /@types/js-yaml@4.0.5: resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==} @@ -8619,6 +9108,7 @@ packages: /@types/node@18.15.13: resolution: {integrity: sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==} + dev: false /@types/node@18.15.3: resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==} @@ -8627,7 +9117,6 @@ packages: resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==} dependencies: undici-types: 5.26.5 - dev: true /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -8688,7 +9177,7 @@ packages: /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false /@types/retry@0.12.0: @@ -8720,7 +9209,7 @@ packages: resolution: {integrity: sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==} dependencies: '@types/mime': 1.3.2 - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false /@types/serve-index@1.9.1: @@ -8734,17 +9223,18 @@ packages: dependencies: '@types/http-errors': 2.0.1 '@types/mime': 3.0.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false /@types/sockjs@0.3.33: resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false /@types/stack-utils@2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + dev: false /@types/testing-library__jest-dom@5.14.6: resolution: {integrity: sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA==} @@ -8772,6 +9262,10 @@ packages: resolution: {integrity: sha512-dl7VRSYBhxhOVxHA/ec8e7EzGXSQJrrGvRgLQWxusaJk1oT4I9nLpWiqsxekYI2eAEuet05toEm+rByL0G8x2Q==} dev: true + /@types/which@2.0.2: + resolution: {integrity: sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==} + dev: true + /@types/ws@8.5.5: resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} dependencies: @@ -8779,6 +9273,7 @@ packages: /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + dev: false /@types/yargs@16.0.5: resolution: {integrity: sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==} @@ -8790,6 +9285,15 @@ packages: resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} dependencies: '@types/yargs-parser': 21.0.0 + dev: false + + /@types/yauzl@2.10.3: + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + requiresBuild: true + dependencies: + '@types/node': 20.10.5 + dev: true + optional: true /@typescript-eslint/eslint-plugin@5.59.0(@typescript-eslint/parser@5.59.0)(eslint@8.38.0)(typescript@5.2.2): resolution: {integrity: sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw==} @@ -9261,6 +9765,86 @@ packages: vue: 3.2.47 dev: true + /@vitest/browser@1.1.3(vitest@1.1.3)(webdriverio@8.27.2): + resolution: {integrity: sha512-ksI0V8YqonFYfjVYMPTvDR84i7ix7QPL2Sc8G2mHirVGAf4jJS3uC/CsifRLe5/E2r8QUhHbAdZQpvMCqBJV5w==} + peerDependencies: + playwright: '*' + safaridriver: '*' + vitest: ^1.0.0 + webdriverio: '*' + peerDependenciesMeta: + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + dependencies: + '@vitest/utils': 1.1.3 + magic-string: 0.30.5 + sirv: 2.0.4 + vitest: 1.1.3(@types/node@18.15.3)(@vitest/browser@1.1.3) + webdriverio: 8.27.2(typescript@5.2.2) + dev: true + + /@vitest/coverage-istanbul@1.1.3(vitest@1.1.3): + resolution: {integrity: sha512-pqx/RaLjJ7oxsbi0Vc/CjyXBXd86yQ0lKq1PPnk9BMNLqMTEWwfcTelcNrl41yK+IVRhHlFtwcjDva3VslbMMQ==} + peerDependencies: + vitest: ^1.0.0 + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.6 + magicast: 0.3.2 + picocolors: 1.0.0 + test-exclude: 6.0.0 + vitest: 1.1.3(@types/node@18.15.3)(@vitest/browser@1.1.3) + transitivePeerDependencies: + - supports-color + dev: true + + /@vitest/expect@1.1.3: + resolution: {integrity: sha512-MnJqsKc1Ko04lksF9XoRJza0bGGwTtqfbyrsYv5on4rcEkdo+QgUdITenBQBUltKzdxW7K3rWh+nXRULwsdaVg==} + dependencies: + '@vitest/spy': 1.1.3 + '@vitest/utils': 1.1.3 + chai: 4.4.0 + dev: true + + /@vitest/runner@1.1.3: + resolution: {integrity: sha512-Va2XbWMnhSdDEh/OFxyUltgQuuDRxnarK1hW5QNN4URpQrqq6jtt8cfww/pQQ4i0LjoYxh/3bYWvDFlR9tU73g==} + dependencies: + '@vitest/utils': 1.1.3 + p-limit: 5.0.0 + pathe: 1.1.2 + dev: true + + /@vitest/snapshot@1.1.3: + resolution: {integrity: sha512-U0r8pRXsLAdxSVAyGNcqOU2H3Z4Y2dAAGGelL50O0QRMdi1WWeYHdrH/QWpN1e8juWfVKsb8B+pyJwTC+4Gy9w==} + dependencies: + magic-string: 0.30.5 + pathe: 1.1.2 + pretty-format: 29.7.0 + dev: true + + /@vitest/spy@1.1.3: + resolution: {integrity: sha512-Ec0qWyGS5LhATFQtldvChPTAHv08yHIOZfiNcjwRQbFPHpkih0md9KAbs7TfeIfL7OFKoe7B/6ukBTqByubXkQ==} + dependencies: + tinyspy: 2.2.0 + dev: true + + /@vitest/utils@1.1.3: + resolution: {integrity: sha512-Dyt3UMcdElTll2H75vhxfpZu03uFpXRCHxWnzcrFjZxT1kTbq8ALUYIeBgGolo1gldVdI0YSlQRacsqxTwNqwg==} + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + /@vue/compiler-core@3.2.47: resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} dependencies: @@ -9373,6 +9957,70 @@ packages: - vue dev: true + /@wdio/config@8.27.2: + resolution: {integrity: sha512-qR1r7K7/jsQhi9g5NiW40lgbvbzCcwwk8nz07hzTj6m8fQ8TXkQPob2fnrlDaNrXjzbZC4od0uv0a5fimK9YOQ==} + engines: {node: ^16.13 || >=18} + dependencies: + '@wdio/logger': 8.24.12 + '@wdio/types': 8.27.2 + '@wdio/utils': 8.27.2 + decamelize: 6.0.0 + deepmerge-ts: 5.1.0 + glob: 10.2.6 + import-meta-resolve: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@wdio/logger@8.24.12: + resolution: {integrity: sha512-QisOiVIWKTUCf1H7S+DOtC+gruhlpimQrUXfWMTeeh672PvAJYnTpOJDWA+BtXfsikkUYFAzAaq8SeMJk8rqKg==} + engines: {node: ^16.13 || >=18} + dependencies: + chalk: 5.3.0 + loglevel: 1.8.1 + loglevel-plugin-prefix: 0.8.4 + strip-ansi: 7.1.0 + dev: true + + /@wdio/protocols@8.24.12: + resolution: {integrity: sha512-QnVj3FkapmVD3h2zoZk+ZQ8gevSj9D9MiIQIy8eOnY4FAneYZ9R9GvoW+mgNcCZO8S8++S/jZHetR8n+8Q808g==} + dev: true + + /@wdio/repl@8.24.12: + resolution: {integrity: sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==} + engines: {node: ^16.13 || >=18} + dependencies: + '@types/node': 20.10.5 + dev: true + + /@wdio/types@8.27.2: + resolution: {integrity: sha512-z/TtSQysEtAUNh+DooOs22G7xotTsJC2RcIZKaVtHY4Gl6lF+tn8kLRXD79jem2ta1byB1TpW62K366k1vzcLw==} + engines: {node: ^16.13 || >=18} + dependencies: + '@types/node': 20.10.5 + dev: true + + /@wdio/utils@8.27.2: + resolution: {integrity: sha512-jWxUhGjlZ4L3uOsP96oLKWjkITpoH/KPTtKzU7xdoVGhd1LXK4d/Fr8cTFTNkDBXM7yuM7C+EMmQ8HJHR55KTA==} + engines: {node: ^16.13 || >=18} + dependencies: + '@puppeteer/browsers': 1.9.1 + '@wdio/logger': 8.24.12 + '@wdio/types': 8.27.2 + decamelize: 6.0.0 + deepmerge-ts: 5.1.0 + edgedriver: 5.3.9 + geckodriver: 4.3.0 + get-port: 7.0.0 + import-meta-resolve: 4.0.0 + locate-app: 2.2.7 + safaridriver: 0.1.2 + split2: 4.2.0 + wait-port: 1.1.0 + transitivePeerDependencies: + - supports-color + dev: true + /@webassemblyjs/ast@1.11.6: resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} dependencies: @@ -9602,12 +10250,22 @@ packages: engines: {node: '>=0.4.0'} dev: true + /acorn-walk@8.3.2: + resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + engines: {node: '>=0.4.0'} + dev: true + /acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} hasBin: true dev: false + /acorn@8.11.3: + resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + engines: {node: '>=0.4.0'} + hasBin: true + /acorn@8.9.0: resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==} engines: {node: '>=0.4.0'} @@ -9628,6 +10286,7 @@ packages: /aes-js@4.0.0-beta.5: resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + dev: false /agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} @@ -9776,6 +10435,42 @@ packages: normalize-path: 3.0.0 picomatch: 2.3.1 + /append-transform@2.0.0: + resolution: {integrity: sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==} + engines: {node: '>=8'} + dependencies: + default-require-extensions: 3.0.1 + dev: true + + /archiver-utils@4.0.1: + resolution: {integrity: sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==} + engines: {node: '>= 12.0.0'} + dependencies: + glob: 8.1.0 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash: 4.17.21 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + dev: true + + /archiver@6.0.1: + resolution: {integrity: sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==} + engines: {node: '>= 12.0.0'} + dependencies: + archiver-utils: 4.0.1 + async: 3.2.4 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 3.1.6 + zip-stream: 5.0.1 + dev: true + + /archy@1.0.0: + resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + dev: true + /are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} @@ -9956,6 +10651,15 @@ packages: /asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + /asn1.js@5.4.1: + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + dependencies: + bn.js: 4.12.0 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + safer-buffer: 2.1.2 + dev: true + /asn1js@3.0.5: resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} engines: {node: '>=12.0.0'} @@ -9965,9 +10669,30 @@ packages: tslib: 2.6.0 dev: true + /assert@2.1.0: + resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + dependencies: + call-bind: 1.0.5 + is-nan: 1.3.2 + object-is: 1.1.5 + object.assign: 4.1.4 + util: 0.12.5 + dev: true + + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /ast-types-flow@0.0.7: resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} + /ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + dependencies: + tslib: 2.6.0 + dev: true + /astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -9981,7 +10706,6 @@ packages: /async@3.2.4: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} - dev: false /asynciterator.prototype@1.0.0: resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} @@ -10035,6 +10759,10 @@ packages: dequal: 2.0.3 dev: true + /b4a@1.6.4: + resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + dev: true + /babel-jest@27.5.1(@babel/core@7.22.5): resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -10054,23 +10782,24 @@ packages: - supports-color dev: false - /babel-jest@29.7.0(@babel/core@7.22.5): - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /babel-jest@27.5.1(@babel/core@7.23.3): + resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: - '@babel/core': 7.22.5 - '@jest/transform': 29.7.0 + '@babel/core': 7.23.3 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 '@types/babel__core': 7.20.1 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.22.5) + babel-preset-jest: 27.5.1(@babel/core@7.23.3) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color - dev: true + dev: false /babel-loader@8.3.0(@babel/core@7.22.5)(webpack@5.88.0): resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} @@ -10098,6 +10827,7 @@ packages: test-exclude: 6.0.0 transitivePeerDependencies: - supports-color + dev: false /babel-plugin-jest-hoist@27.5.1: resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==} @@ -10109,16 +10839,6 @@ packages: '@types/babel__traverse': 7.20.1 dev: false - /babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.22.5 - '@types/babel__core': 7.20.1 - '@types/babel__traverse': 7.20.1 - dev: true - /babel-plugin-macros@3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} @@ -10198,6 +10918,27 @@ packages: '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.5) '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.5) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.5) + dev: false + + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.3): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.23.3 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.3) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.3) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.3) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.3) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.3) + dev: false /babel-preset-fbjs@3.4.0(@babel/core@7.22.5): resolution: {integrity: sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==} @@ -10247,16 +10988,16 @@ packages: babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5) dev: false - /babel-preset-jest@29.6.3(@babel/core@7.22.5): - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /babel-preset-jest@27.5.1(@babel/core@7.23.3): + resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.22.5 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5) - dev: true + '@babel/core': 7.23.3 + babel-plugin-jest-hoist: 27.5.1 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.3) + dev: false /babel-preset-react-app@10.0.1: resolution: {integrity: sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==} @@ -10293,6 +11034,11 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true + /basic-ftp@5.0.4: + resolution: {integrity: sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==} + engines: {node: '>=10.0.0'} + dev: true + /batch@0.6.1: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} dev: false @@ -10331,6 +11077,13 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} + /binary@0.3.0: + resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + dependencies: + buffers: 0.1.1 + chainsaw: 0.1.0 + dev: true + /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -10339,17 +11092,20 @@ packages: readable-stream: 3.6.2 dev: true + /bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + dev: true + /bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} dev: false /bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} - dev: false + dev: true /bn.js@5.2.1: resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - dev: false /body-parser@1.20.1: resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} @@ -10424,12 +11180,74 @@ packages: /brorand@1.1.0: resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - dev: false + dev: true /browser-process-hrtime@1.0.0: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} dev: false + /browser-resolve@2.0.0: + resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + dependencies: + resolve: 1.22.8 + dev: true + + /browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + dependencies: + buffer-xor: 1.0.3 + cipher-base: 1.0.4 + create-hash: 1.2.0 + evp_bytestokey: 1.0.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + dependencies: + browserify-aes: 1.2.0 + browserify-des: 1.0.2 + evp_bytestokey: 1.0.3 + dev: true + + /browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + dependencies: + cipher-base: 1.0.4 + des.js: 1.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + + /browserify-rsa@4.1.0: + resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} + dependencies: + bn.js: 5.2.1 + randombytes: 2.1.0 + dev: true + + /browserify-sign@4.2.2: + resolution: {integrity: sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==} + engines: {node: '>= 4'} + dependencies: + bn.js: 5.2.1 + browserify-rsa: 4.1.0 + create-hash: 1.2.0 + create-hmac: 1.1.7 + elliptic: 6.5.4 + inherits: 2.0.4 + parse-asn1: 5.1.6 + readable-stream: 3.6.2 + safe-buffer: 5.2.1 + dev: true + + /browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + dependencies: + pako: 1.0.11 + dev: true + /browserslist@4.21.9: resolution: {integrity: sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -10450,20 +11268,27 @@ packages: node-releases: 2.0.13 update-browserslist-db: 1.0.13(browserslist@4.22.1) - /bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - dependencies: - fast-json-stable-stringify: 2.1.0 - dev: true - /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: node-int64: 0.4.0 + /buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: false + + /buffer-indexof-polyfill@1.0.2: + resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} + engines: {node: '>=0.10'} + dev: true + + /buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + dev: true /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -10472,10 +11297,26 @@ packages: ieee754: 1.2.1 dev: true + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + dev: true + /builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} + /builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + dev: true + /bundle-name@3.0.0: resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} engines: {node: '>=12'} @@ -10524,11 +11365,39 @@ packages: engines: {node: '>=8'} dev: true - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.2.1 + /cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + dev: true + + /cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + dependencies: + '@types/http-cache-semantics': 4.0.4 + get-stream: 6.0.1 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + mimic-response: 4.0.0 + normalize-url: 8.0.0 + responselike: 3.0.0 + dev: true + + /caching-transform@4.0.0: + resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==} + engines: {node: '>=8'} + dependencies: + hasha: 5.2.2 + make-dir: 3.1.0 + package-hash: 4.0.0 + write-file-atomic: 3.0.3 + dev: true + + /call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.1 /call-bind@1.0.5: resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} @@ -10568,6 +11437,7 @@ packages: /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + dev: false /caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} @@ -10602,6 +11472,25 @@ packages: resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} dev: true + /chai@4.4.0: + resolution: {integrity: sha512-x9cHNq1uvkCdU+5xTkNh5WtgD4e4yDFCsp9jVc7N7qVeKeftv3gO/ZrviX5d+3ZfxdYnZXZYujjRInu1RogU6A==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /chainsaw@0.1.0: + resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} + dependencies: + traverse: 0.3.9 + dev: true + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -10633,6 +11522,11 @@ packages: ansi-styles: 4.3.0 supports-color: 7.2.0 + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + /change-case-all@1.0.14: resolution: {integrity: sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA==} dependencies: @@ -10683,6 +11577,7 @@ packages: /char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + dev: false /char-regex@2.0.1: resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==} @@ -10709,6 +11604,12 @@ packages: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} dev: true + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + /check-types@11.2.2: resolution: {integrity: sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==} dev: false @@ -10736,12 +11637,29 @@ packages: engines: {node: '>=6.0'} dev: false + /chromium-bidi@0.4.16(devtools-protocol@0.0.1147663): + resolution: {integrity: sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==} + peerDependencies: + devtools-protocol: '*' + dependencies: + devtools-protocol: 0.0.1147663 + mitt: 3.0.0 + dev: true + /ci-info@3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} + /cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /cjs-module-lexer@1.2.3: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + dev: false /classnames@2.3.2: resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} @@ -10833,6 +11751,7 @@ packages: /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: false /coa@2.0.2: resolution: {integrity: sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==} @@ -10845,6 +11764,7 @@ packages: /collect-v8-coverage@1.0.1: resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} + dev: false /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -10902,7 +11822,6 @@ packages: /commander@9.4.1: resolution: {integrity: sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==} engines: {node: ^12.20.0 || >=14} - dev: false /comment-parser@1.4.0: resolution: {integrity: sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==} @@ -10919,7 +11838,6 @@ packages: /commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - dev: false /compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} @@ -10932,6 +11850,16 @@ packages: resolution: {integrity: sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==} dev: true + /compress-commons@5.0.1: + resolution: {integrity: sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==} + engines: {node: '>= 12.0.0'} + dependencies: + crc-32: 1.2.2 + crc32-stream: 5.0.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + dev: true + /compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -10965,6 +11893,10 @@ packages: engines: {node: '>=0.8'} dev: false + /console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + dev: true + /constant-case@3.0.4: resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} dependencies: @@ -10973,6 +11905,10 @@ packages: upper-case: 2.0.2 dev: true + /constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + dev: true + /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -11032,7 +11968,6 @@ packages: /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: false /cosmiconfig-toml-loader@1.0.0: resolution: {integrity: sha512-H/2gurFWVi7xXvCyvsWRLCMekl4tITJcX0QEsDMpzxtuxDyM59xLatYNg4s/k9AA/HdtCYfj2su8mgA0GSDLDA==} @@ -11102,23 +12037,52 @@ packages: path-type: 4.0.0 yaml: 1.10.2 - /create-jest@29.7.0(@types/node@18.15.3): - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /coverage-diff@3.2.0: + resolution: {integrity: sha512-ZapsZXZZTQAEfWlUoObrpkvRnMYHbi99T96XRJlrbcZkIKpDvSmVxfJ0dPwjI3eTbl122hVMTmEwielS7qvTfg==} + dependencies: + markdown-table: 2.0.0 + dev: true + + /crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} hasBin: true + dev: true + + /crc32-stream@5.0.0: + resolution: {integrity: sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==} + engines: {node: '>= 12.0.0'} dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.15.3) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node + crc-32: 1.2.2 + readable-stream: 3.6.2 + dev: true + + /create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + dependencies: + bn.js: 4.12.0 + elliptic: 6.5.4 + dev: true + + /create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + dependencies: + cipher-base: 1.0.4 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + dev: true + + /create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + dependencies: + cipher-base: 1.0.4 + create-hash: 1.2.0 + inherits: 2.0.4 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 dev: true /create-require@1.1.1: @@ -11163,6 +12127,22 @@ packages: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} dev: true + /crypto-browserify@3.12.0: + resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + dependencies: + browserify-cipher: 1.0.1 + browserify-sign: 4.2.2 + create-ecdh: 4.0.4 + create-hash: 1.2.0 + create-hmac: 1.1.7 + diffie-hellman: 5.0.3 + inherits: 2.0.4 + pbkdf2: 3.1.2 + public-encrypt: 4.0.3 + randombytes: 2.1.0 + randomfill: 1.0.4 + dev: true + /crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} @@ -11179,13 +12159,13 @@ packages: postcss-selector-parser: 6.0.13 dev: false - /css-declaration-sorter@6.4.0(postcss@8.4.24): + /css-declaration-sorter@6.4.0(postcss@8.4.31): resolution: {integrity: sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==} engines: {node: ^10 || ^12 || >=14} peerDependencies: postcss: ^8.0.9 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: false /css-has-pseudo@3.0.4(postcss@8.4.24): @@ -11205,12 +12185,12 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.24) - postcss: 8.4.24 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.24) - postcss-modules-local-by-default: 4.0.3(postcss@8.4.24) - postcss-modules-scope: 3.0.0(postcss@8.4.24) - postcss-modules-values: 4.0.0(postcss@8.4.24) + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 + postcss-modules-extract-imports: 3.0.0(postcss@8.4.31) + postcss-modules-local-by-default: 4.0.3(postcss@8.4.31) + postcss-modules-scope: 3.0.0(postcss@8.4.31) + postcss-modules-values: 4.0.0(postcss@8.4.31) postcss-value-parser: 4.2.0 semver: 7.5.4 webpack: 5.88.0(esbuild@0.17.19) @@ -11235,10 +12215,10 @@ packages: esbuild: optional: true dependencies: - cssnano: 5.1.15(postcss@8.4.24) + cssnano: 5.1.15(postcss@8.4.31) esbuild: 0.17.19 jest-worker: 27.5.1 - postcss: 8.4.24 + postcss: 8.4.31 schema-utils: 4.2.0 serialize-javascript: 6.0.1 source-map: 0.6.1 @@ -11288,6 +12268,10 @@ packages: nth-check: 2.1.1 dev: false + /css-shorthand-properties@1.1.1: + resolution: {integrity: sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==} + dev: true + /css-tree@1.0.0-alpha.37: resolution: {integrity: sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==} engines: {node: '>=8.0.0'} @@ -11320,6 +12304,10 @@ packages: source-map-js: 1.0.2 dev: false + /css-value@0.0.1: + resolution: {integrity: sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==} + dev: true + /css-what@3.4.2: resolution: {integrity: sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==} engines: {node: '>= 6'} @@ -11343,62 +12331,62 @@ packages: engines: {node: '>=4'} hasBin: true - /cssnano-preset-default@5.2.14(postcss@8.4.24): + /cssnano-preset-default@5.2.14(postcss@8.4.31): resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - css-declaration-sorter: 6.4.0(postcss@8.4.24) - cssnano-utils: 3.1.0(postcss@8.4.24) - postcss: 8.4.24 - postcss-calc: 8.2.4(postcss@8.4.24) - postcss-colormin: 5.3.1(postcss@8.4.24) - postcss-convert-values: 5.1.3(postcss@8.4.24) - postcss-discard-comments: 5.1.2(postcss@8.4.24) - postcss-discard-duplicates: 5.1.0(postcss@8.4.24) - postcss-discard-empty: 5.1.1(postcss@8.4.24) - postcss-discard-overridden: 5.1.0(postcss@8.4.24) - postcss-merge-longhand: 5.1.7(postcss@8.4.24) - postcss-merge-rules: 5.1.4(postcss@8.4.24) - postcss-minify-font-values: 5.1.0(postcss@8.4.24) - postcss-minify-gradients: 5.1.1(postcss@8.4.24) - postcss-minify-params: 5.1.4(postcss@8.4.24) - postcss-minify-selectors: 5.2.1(postcss@8.4.24) - postcss-normalize-charset: 5.1.0(postcss@8.4.24) - postcss-normalize-display-values: 5.1.0(postcss@8.4.24) - postcss-normalize-positions: 5.1.1(postcss@8.4.24) - postcss-normalize-repeat-style: 5.1.1(postcss@8.4.24) - postcss-normalize-string: 5.1.0(postcss@8.4.24) - postcss-normalize-timing-functions: 5.1.0(postcss@8.4.24) - postcss-normalize-unicode: 5.1.1(postcss@8.4.24) - postcss-normalize-url: 5.1.0(postcss@8.4.24) - postcss-normalize-whitespace: 5.1.1(postcss@8.4.24) - postcss-ordered-values: 5.1.3(postcss@8.4.24) - postcss-reduce-initial: 5.1.2(postcss@8.4.24) - postcss-reduce-transforms: 5.1.0(postcss@8.4.24) - postcss-svgo: 5.1.0(postcss@8.4.24) - postcss-unique-selectors: 5.1.1(postcss@8.4.24) - dev: false - - /cssnano-utils@3.1.0(postcss@8.4.24): + css-declaration-sorter: 6.4.0(postcss@8.4.31) + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 + postcss-calc: 8.2.4(postcss@8.4.31) + postcss-colormin: 5.3.1(postcss@8.4.31) + postcss-convert-values: 5.1.3(postcss@8.4.31) + postcss-discard-comments: 5.1.2(postcss@8.4.31) + postcss-discard-duplicates: 5.1.0(postcss@8.4.31) + postcss-discard-empty: 5.1.1(postcss@8.4.31) + postcss-discard-overridden: 5.1.0(postcss@8.4.31) + postcss-merge-longhand: 5.1.7(postcss@8.4.31) + postcss-merge-rules: 5.1.4(postcss@8.4.31) + postcss-minify-font-values: 5.1.0(postcss@8.4.31) + postcss-minify-gradients: 5.1.1(postcss@8.4.31) + postcss-minify-params: 5.1.4(postcss@8.4.31) + postcss-minify-selectors: 5.2.1(postcss@8.4.31) + postcss-normalize-charset: 5.1.0(postcss@8.4.31) + postcss-normalize-display-values: 5.1.0(postcss@8.4.31) + postcss-normalize-positions: 5.1.1(postcss@8.4.31) + postcss-normalize-repeat-style: 5.1.1(postcss@8.4.31) + postcss-normalize-string: 5.1.0(postcss@8.4.31) + postcss-normalize-timing-functions: 5.1.0(postcss@8.4.31) + postcss-normalize-unicode: 5.1.1(postcss@8.4.31) + postcss-normalize-url: 5.1.0(postcss@8.4.31) + postcss-normalize-whitespace: 5.1.1(postcss@8.4.31) + postcss-ordered-values: 5.1.3(postcss@8.4.31) + postcss-reduce-initial: 5.1.2(postcss@8.4.31) + postcss-reduce-transforms: 5.1.0(postcss@8.4.31) + postcss-svgo: 5.1.0(postcss@8.4.31) + postcss-unique-selectors: 5.1.1(postcss@8.4.31) + dev: false + + /cssnano-utils@3.1.0(postcss@8.4.31): resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: false - /cssnano@5.1.15(postcss@8.4.24): + /cssnano@5.1.15(postcss@8.4.31): resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - cssnano-preset-default: 5.2.14(postcss@8.4.24) + cssnano-preset-default: 5.2.14(postcss@8.4.31) lilconfig: 2.1.0 - postcss: 8.4.24 + postcss: 8.4.31 yaml: 1.10.2 dev: false @@ -11463,6 +12451,16 @@ packages: /damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + /data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + dev: true + + /data-uri-to-buffer@6.0.1: + resolution: {integrity: sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==} + engines: {node: '>= 14'} + dev: true + /data-urls@2.0.0: resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==} engines: {node: '>=10'} @@ -11495,6 +12493,17 @@ packages: ms: 2.0.0 dev: false + /debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: false + /debug@3.2.7(supports-color@5.5.0): resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -11530,21 +12539,31 @@ packages: engines: {node: '>=0.10.0'} dev: true + /decamelize@6.0.0: + resolution: {integrity: sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} dev: false + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dependencies: + mimic-response: 3.1.0 + dev: true + /dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: false - /dedent@1.5.1: - resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 dev: true /deep-equal@2.2.1: @@ -11578,9 +12597,15 @@ packages: engines: {node: '>=4.0.0'} dev: false + /deepmerge-ts@5.1.0: + resolution: {integrity: sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==} + engines: {node: '>=16.0.0'} + dev: true + /deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + dev: false /default-browser-id@3.0.0: resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} @@ -11607,12 +12632,24 @@ packages: execa: 5.1.1 dev: false + /default-require-extensions@3.0.1: + resolution: {integrity: sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==} + engines: {node: '>=8'} + dependencies: + strip-bom: 4.0.0 + dev: true + /defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 dev: true + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: true + /define-data-property@1.1.1: resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} engines: {node: '>= 0.4'} @@ -11647,6 +12684,15 @@ packages: object-keys: 1.1.1 dev: true + /degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + dev: true + /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -11670,6 +12716,13 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + /des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + dev: true + /destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -11683,6 +12736,7 @@ packages: /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} + dev: false /detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} @@ -11703,6 +12757,14 @@ packages: - supports-color dev: false + /devtools-protocol@0.0.1147663: + resolution: {integrity: sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==} + dev: true + + /devtools-protocol@0.0.1239539: + resolution: {integrity: sha512-uS7hZVqZxGyZwR8lX/8wWyNLGEYs1wWWxN7qeRC+wBZ4VM5JXYwCJg8hofEna5yX0W2cavpjHOE4ukHXLHlEaA==} + dev: true + /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -11714,6 +12776,7 @@ packages: /diff-sequences@29.4.3: resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: false /diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} @@ -11725,6 +12788,14 @@ packages: engines: {node: '>=0.3.1'} dev: true + /diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + dependencies: + bn.js: 4.12.0 + miller-rabin: 4.0.1 + randombytes: 2.1.0 + dev: true + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -11790,6 +12861,11 @@ packages: entities: 4.5.0 dev: false + /domain-browser@4.23.0: + resolution: {integrity: sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==} + engines: {node: '>=10'} + dev: true + /domelementtype@1.3.1: resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} dev: false @@ -11879,6 +12955,12 @@ packages: engines: {node: '>=4'} dev: true + /duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + dependencies: + readable-stream: 2.3.8 + dev: true + /duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} dev: false @@ -11886,6 +12968,27 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + /edge-paths@3.0.5: + resolution: {integrity: sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==} + engines: {node: '>=14.0.0'} + dependencies: + '@types/which': 2.0.2 + which: 2.0.2 + dev: true + + /edgedriver@5.3.9: + resolution: {integrity: sha512-G0wNgFMFRDnFfKaXG2R6HiyVHqhKwdQ3EgoxW3wPlns2wKqem7F+HgkWBcevN7Vz0nN4AXtskID7/6jsYDXcKw==} + hasBin: true + requiresBuild: true + dependencies: + '@wdio/logger': 8.24.12 + decamelize: 6.0.0 + edge-paths: 3.0.5 + node-fetch: 3.3.2 + unzipper: 0.10.14 + which: 4.0.0 + dev: true + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: false @@ -11914,18 +13017,13 @@ packages: inherits: 2.0.4 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: false + dev: true /emittery@0.10.2: resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} engines: {node: '>=12'} dev: false - /emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - dev: true - /emittery@0.8.1: resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==} engines: {node: '>=10'} @@ -11947,6 +13045,12 @@ packages: engines: {node: '>= 0.8'} dev: false + /end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: true + /enhanced-resolve@5.15.0: resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} engines: {node: '>=10.13.0'} @@ -12132,6 +13236,10 @@ packages: is-date-object: 1.0.5 is-symbol: 1.0.4 + /es6-error@4.1.1: + resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + dev: true + /esbuild@0.17.19: resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} engines: {node: '>=12'} @@ -12161,6 +13269,37 @@ packages: '@esbuild/win32-ia32': 0.17.19 '@esbuild/win32-x64': 0.17.19 + /esbuild@0.19.11: + resolution: {integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.11 + '@esbuild/android-arm': 0.19.11 + '@esbuild/android-arm64': 0.19.11 + '@esbuild/android-x64': 0.19.11 + '@esbuild/darwin-arm64': 0.19.11 + '@esbuild/darwin-x64': 0.19.11 + '@esbuild/freebsd-arm64': 0.19.11 + '@esbuild/freebsd-x64': 0.19.11 + '@esbuild/linux-arm': 0.19.11 + '@esbuild/linux-arm64': 0.19.11 + '@esbuild/linux-ia32': 0.19.11 + '@esbuild/linux-loong64': 0.19.11 + '@esbuild/linux-mips64el': 0.19.11 + '@esbuild/linux-ppc64': 0.19.11 + '@esbuild/linux-riscv64': 0.19.11 + '@esbuild/linux-s390x': 0.19.11 + '@esbuild/linux-x64': 0.19.11 + '@esbuild/netbsd-x64': 0.19.11 + '@esbuild/openbsd-x64': 0.19.11 + '@esbuild/sunos-x64': 0.19.11 + '@esbuild/win32-arm64': 0.19.11 + '@esbuild/win32-ia32': 0.19.11 + '@esbuild/win32-x64': 0.19.11 + dev: true + /esbuild@0.19.3: resolution: {integrity: sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw==} engines: {node: '>=12'} @@ -12206,6 +13345,7 @@ packages: /escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} + dev: false /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -12224,6 +13364,18 @@ packages: source-map: 0.6.1 dev: false + /escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + dev: true + /eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.29.0)(eslint@8.52.0): resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} engines: {node: ^10.12.0 || >=12.0.0} @@ -13115,6 +14267,12 @@ packages: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} dev: true + /estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + dependencies: + '@types/estree': 1.0.5 + dev: true + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -13147,6 +14305,7 @@ packages: transitivePeerDependencies: - bufferutil - utf-8-validate + dev: false /event-target-polyfill@0.0.3: resolution: {integrity: sha512-ZMc6UuvmbinrCk4RzGyVmRyIsAyxMRlp4CqSrcQRO8Dy0A9ldbiRy5kdtBj4OtP7EClGdqGfIqo9JmOClMsGLQ==} @@ -13163,7 +14322,13 @@ packages: /events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - dev: false + + /evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + dependencies: + md5.js: 1.3.5 + safe-buffer: 5.2.1 + dev: true /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} @@ -13194,9 +14359,25 @@ packages: strip-final-newline: 3.0.0 dev: true + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + /exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} + dev: false /expect@27.5.1: resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==} @@ -13217,17 +14398,7 @@ packages: jest-matcher-utils: 29.5.0 jest-message-util: 29.5.0 jest-util: 29.5.0 - - /expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - dev: true + dev: false /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} @@ -13293,11 +14464,30 @@ packages: /extract-files@9.0.0: resolution: {integrity: sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==} engines: {node: ^10.17.0 || ^12.0.0 || >= 13.7.0} + dev: false + + /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 + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + dev: true /fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} dev: true + /fast-deep-equal@2.0.1: + resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} + dev: true + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -13309,6 +14499,10 @@ packages: resolution: {integrity: sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==} dev: true + /fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + dev: true + /fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} @@ -13388,7 +14582,21 @@ packages: - encoding dev: true - /figures@3.2.0: + /fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + dependencies: + pend: 1.2.0 + dev: true + + /fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.2.1 + dev: true + + /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} dependencies: @@ -13458,7 +14666,6 @@ packages: commondir: 1.0.1 make-dir: 3.1.0 pkg-dir: 4.2.0 - dev: false /find-up@2.1.0: resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} @@ -13537,6 +14744,14 @@ packages: dependencies: is-callable: 1.2.7 + /foreground-child@2.0.0: + resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} + engines: {node: '>=8.0.0'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 3.0.7 + dev: true + /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -13580,6 +14795,11 @@ packages: resolution: {integrity: sha512-rahaRMkN8P8d/tgK/BLPX+WBVM27NbvdXBxqQujBtkDAIFspaRqN7Od7lfdGQA6KAD+f82fYCLBq1ipvcu8qLw==} dev: true + /form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + dev: true + /form-data@3.0.1: resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} engines: {node: '>= 6'} @@ -13601,6 +14821,13 @@ packages: web-streams-polyfill: 4.0.0-beta.3 dev: true + /formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + dependencies: + fetch-blob: 3.2.0 + dev: true + /forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -13632,6 +14859,10 @@ packages: engines: {node: '>= 0.6'} dev: false + /fromentries@1.3.2: + resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==} + dev: true + /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -13683,6 +14914,16 @@ packages: requiresBuild: true optional: true + /fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.6.3 + dev: true + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} @@ -13711,6 +14952,24 @@ packages: /functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + /geckodriver@4.3.0: + resolution: {integrity: sha512-QfpvxFsMORwKpvnLslkHCr3NTCczHAvkte6+pQGsiUZXKBe6mO4TTb727b+9KMVSK6XZqhR6ZwImKdP+F5vS6A==} + engines: {node: ^16.13 || >=18 || >=20} + hasBin: true + requiresBuild: true + dependencies: + '@wdio/logger': 8.24.12 + decamelize: 6.0.0 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + node-fetch: 3.3.2 + tar-fs: 3.0.4 + unzipper: 0.10.14 + which: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -13719,6 +14978,10 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true + /get-graphql-schema@2.1.2: resolution: {integrity: sha512-1z5Hw91VrE3GrpCZE6lE8Dy+jz4kXWesLS7rCSjwOxf5BOcIedAZeTUJRIeIzmmR+PA9CKOkPTYFRJbdgUtrxA==} hasBin: true @@ -13761,15 +15024,32 @@ packages: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} + /get-port@7.0.0: + resolution: {integrity: sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==} + engines: {node: '>=16'} + dev: true + /get-stdin@5.0.1: resolution: {integrity: sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==} engines: {node: '>=0.12.0'} dev: true + /get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + /get-symbol-description@1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} @@ -13783,6 +15063,24 @@ packages: resolve-pkg-maps: 1.0.0 dev: true + /get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /get-uri@6.0.2: + resolution: {integrity: sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==} + engines: {node: '>= 14'} + dependencies: + basic-ftp: 5.0.4 + data-uri-to-buffer: 6.0.1 + debug: 4.3.4 + fs-extra: 8.1.0 + transitivePeerDependencies: + - supports-color + dev: true + /get-url-origin@1.0.1: resolution: {integrity: sha512-MMSKo16gB2+6CjWy55jNdIAqUEaKgw3LzZCb8wVVtFrhoQ78EXyuYXxDdn3COI3A4Xr4ZfM3fZa9RTjO6DOTxw==} dev: true @@ -13845,6 +15143,17 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: true + /global-modules@2.0.0: resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} engines: {node: '>=6'} @@ -13908,6 +15217,23 @@ packages: dependencies: get-intrinsic: 1.2.1 + /got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 + dev: true + /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -13959,6 +15285,7 @@ packages: graphql: 16.6.0 transitivePeerDependencies: - encoding + dev: false /graphql-request@6.1.0(graphql@16.6.0): resolution: {integrity: sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==} @@ -14070,12 +15397,29 @@ packages: dependencies: function-bind: 1.1.1 + /hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + safe-buffer: 5.2.1 + dev: true + /hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: false + dev: true + + /hasha@5.2.2: + resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} + engines: {node: '>=8'} + dependencies: + is-stream: 2.0.1 + type-fest: 0.8.1 + dev: true /hasown@2.0.0: resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} @@ -14101,7 +15445,7 @@ packages: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: false + dev: true /hoopy@0.1.4: resolution: {integrity: sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==} @@ -14172,6 +15516,10 @@ packages: entities: 2.2.0 dev: false + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: true + /http-deceiver@1.2.7: resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} dev: false @@ -14222,6 +15570,16 @@ packages: - supports-color dev: true + /http-proxy-agent@7.0.0: + resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /http-proxy-middleware@2.0.6(@types/express@4.17.17): resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} engines: {node: '>=12.0.0'} @@ -14252,6 +15610,18 @@ packages: - debug dev: false + /http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + dev: true + + /https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + dev: true + /https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} @@ -14272,6 +15642,16 @@ packages: - supports-color dev: true + /https-proxy-agent@7.0.2: + resolution: {integrity: sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} dev: true @@ -14285,6 +15665,16 @@ packages: engines: {node: '>=14.18.0'} dev: true + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + + /hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + dev: true + /iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -14298,13 +15688,13 @@ packages: safer-buffer: 2.1.2 dev: false - /icss-utils@5.1.0(postcss@8.4.24): + /icss-utils@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: false /idb@7.1.1: @@ -14362,6 +15752,11 @@ packages: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 + dev: false + + /import-meta-resolve@4.0.0: + resolution: {integrity: sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==} + dev: true /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -14431,6 +15826,14 @@ packages: dependencies: loose-envify: 1.4.0 + /ip@1.1.8: + resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} + dev: true + + /ip@2.0.0: + resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + dev: true + /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -14466,7 +15869,6 @@ packages: dependencies: call-bind: 1.0.2 has-tostringtag: 1.0.0 - dev: false /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} @@ -14582,6 +15984,7 @@ packages: /is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} + dev: false /is-generator-function@1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} @@ -14626,6 +16029,14 @@ packages: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} dev: false + /is-nan@1.3.2: + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + dev: true + /is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -14669,6 +16080,11 @@ packages: engines: {node: '>=10'} dev: false + /is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + dev: true + /is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: false @@ -14752,7 +16168,6 @@ packages: /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: false /is-unc-path@1.0.0: resolution: {integrity: sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==} @@ -14803,7 +16218,6 @@ packages: /isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: false /isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -14811,6 +16225,16 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + /isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + dev: true + + /isomorphic-timers-promises@1.0.1: + resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} + engines: {node: '>=10'} + dev: true + /isomorphic-ws@5.0.0(ws@8.13.0): resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} peerDependencies: @@ -14823,31 +16247,67 @@ packages: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} + /istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + /istanbul-lib-hook@3.0.0: + resolution: {integrity: sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==} + engines: {node: '>=8'} + dependencies: + append-transform: 2.0.0 + dev: true + + /istanbul-lib-instrument@4.0.3: + resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.23.3 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + /istanbul-lib-instrument@5.2.1: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.22.5 - '@babel/parser': 7.22.5 + '@babel/core': 7.23.3 + '@babel/parser': 7.23.4 '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.0 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 transitivePeerDependencies: - supports-color + dev: false /istanbul-lib-instrument@6.0.1: resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} engines: {node: '>=10'} dependencies: - '@babel/core': 7.22.5 - '@babel/parser': 7.22.5 + '@babel/core': 7.23.3 + '@babel/parser': 7.23.4 '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 semver: 7.5.4 transitivePeerDependencies: - supports-color dev: true + /istanbul-lib-processinfo@2.0.3: + resolution: {integrity: sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==} + engines: {node: '>=8'} + dependencies: + archy: 1.0.0 + cross-spawn: 7.0.3 + istanbul-lib-coverage: 3.2.0 + p-map: 3.0.0 + rimraf: 3.0.2 + uuid: 8.3.2 + dev: true + /istanbul-lib-report@3.0.0: resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} engines: {node: '>=8'} @@ -14856,12 +16316,20 @@ packages: make-dir: 3.1.0 supports-color: 7.2.0 + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + /istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: - supports-color @@ -14872,6 +16340,14 @@ packages: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.0 + dev: true + + /istanbul-reports@3.1.6: + resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 /iterall@1.3.0: resolution: {integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==} @@ -14915,15 +16391,6 @@ packages: throat: 6.0.2 dev: false - /jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 - dev: true - /jest-circus@27.5.1: resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -14931,7 +16398,7 @@ packages: '@jest/environment': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -14951,35 +16418,6 @@ packages: - supports-color dev: false - /jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.1 - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.0.2 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - /jest-cli@27.5.1: resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15010,34 +16448,6 @@ packages: - utf-8-validate dev: false - /jest-cli@29.7.0(@types/node@18.15.3): - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.15.3) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.15.3) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /jest-config@27.5.1: resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15047,10 +16457,10 @@ packages: ts-node: optional: true dependencies: - '@babel/core': 7.22.5 + '@babel/core': 7.23.3 '@jest/test-sequencer': 27.5.1 '@jest/types': 27.5.1 - babel-jest: 27.5.1(@babel/core@7.22.5) + babel-jest: 27.5.1(@babel/core@7.23.3) chalk: 4.1.2 ci-info: 3.8.0 deepmerge: 4.3.1 @@ -15078,89 +16488,9 @@ packages: - utf-8-validate dev: false - /jest-config@29.7.0(@types/node@16.18.34): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.22.5 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - babel-jest: 29.7.0(@babel/core@7.22.5) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-config@29.7.0(@types/node@18.15.3): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.22.5 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.15.3 - babel-jest: 29.7.0(@babel/core@7.22.5) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-diff@27.5.1: - resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-diff@27.5.1: + resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: chalk: 4.1.2 diff-sequences: 27.5.1 @@ -15176,16 +16506,7 @@ packages: diff-sequences: 29.4.3 jest-get-type: 29.4.3 pretty-format: 29.5.0 - - /jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true + dev: false /jest-docblock@27.5.1: resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==} @@ -15194,13 +16515,6 @@ packages: detect-newline: 3.1.0 dev: false - /jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 - dev: true - /jest-each@27.5.1: resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15212,17 +16526,6 @@ packages: pretty-format: 27.5.1 dev: false - /jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - dev: true - /jest-environment-jsdom@27.5.1: resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15230,7 +16533,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 jest-mock: 27.5.1 jest-util: 27.5.1 jsdom: 16.7.0 @@ -15248,23 +16551,11 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 jest-mock: 27.5.1 jest-util: 27.5.1 dev: false - /jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - jest-mock: 29.7.0 - jest-util: 29.7.0 - dev: true - /jest-get-type@27.5.1: resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15273,11 +16564,7 @@ packages: /jest-get-type@29.4.3: resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true + dev: false /jest-haste-map@27.5.1: resolution: {integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==} @@ -15285,7 +16572,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@types/graceful-fs': 4.1.6 - '@types/node': 16.18.34 + '@types/node': 20.10.5 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -15299,25 +16586,6 @@ packages: fsevents: 2.3.3 dev: false - /jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - 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': 16.18.34 - 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.7.0 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - dev: true - /jest-jasmine2@27.5.1: resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15326,7 +16594,7 @@ packages: '@jest/source-map': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 chalk: 4.1.2 co: 4.6.0 expect: 27.5.1 @@ -15351,14 +16619,6 @@ packages: pretty-format: 27.5.1 dev: false - /jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true - /jest-matcher-utils@27.5.1: resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15377,16 +16637,7 @@ packages: jest-diff: 29.5.0 jest-get-type: 29.4.3 pretty-format: 29.5.0 - - /jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - dev: true + dev: false /jest-message-util@27.5.1: resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} @@ -15431,39 +16682,16 @@ packages: pretty-format: 29.5.0 slash: 3.0.0 stack-utils: 2.0.6 - - /jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.22.5 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 - dev: true + dev: false /jest-mock@27.5.1: resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 dev: false - /jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - jest-util: 29.7.0 - dev: true - /jest-pnp-resolver@1.2.3(jest-resolve@27.5.1): resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} @@ -15476,18 +16704,6 @@ packages: jest-resolve: 27.5.1 dev: false - /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 29.7.0 - dev: true - /jest-regex-util@27.5.1: resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15498,11 +16714,6 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dev: false - /jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - /jest-resolve-dependencies@27.5.1: resolution: {integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15514,16 +16725,6 @@ packages: - supports-color dev: false - /jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - dev: true - /jest-resolve@27.5.1: resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15540,21 +16741,6 @@ packages: slash: 3.0.0 dev: false - /jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.8 - resolve.exports: 2.0.2 - slash: 3.0.0 - dev: true - /jest-runner@27.5.1: resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15564,7 +16750,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 chalk: 4.1.2 emittery: 0.8.1 graceful-fs: 4.2.11 @@ -15587,35 +16773,6 @@ packages: - utf-8-validate dev: false - /jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - dev: true - /jest-runtime@27.5.1: resolution: {integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15646,41 +16803,11 @@ packages: - supports-color dev: false - /jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - chalk: 4.1.2 - cjs-module-lexer: 1.2.3 - collect-v8-coverage: 1.0.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /jest-serializer@27.5.1: resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 graceful-fs: 4.2.11 dev: false @@ -15688,16 +16815,16 @@ packages: resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@babel/core': 7.22.5 + '@babel/core': 7.23.3 '@babel/generator': 7.22.5 - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.5) + '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.3) '@babel/traverse': 7.22.5 - '@babel/types': 7.22.5 + '@babel/types': 7.23.4 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 '@types/babel__traverse': 7.20.1 '@types/prettier': 2.7.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.3) chalk: 4.1.2 expect: 27.5.1 graceful-fs: 4.2.11 @@ -15714,47 +16841,12 @@ packages: - supports-color dev: false - /jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.22.5 - '@babel/generator': 7.22.5 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.5) - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.5) - '@babel/types': 7.22.5 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.5) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-text-transformer@1.0.4: - resolution: {integrity: sha512-Qi3FpWP6EFxZimSD05Zlmd/WER8l/3agVG7e5voHgdnM2vTs45sxS/i8qMWMO/dBkoxajndrGXfJTvhEENnjqw==} - engines: {node: '>=9.5.0', npm: '>=5.8.0'} - dependencies: - uuid: 3.4.0 - dev: true - /jest-util@27.5.1: resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -15766,7 +16858,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@jest/types': 28.1.3 - '@types/node': 16.18.34 + '@types/node': 20.10.5 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -15783,18 +16875,7 @@ packages: ci-info: 3.8.0 graceful-fs: 4.2.11 picomatch: 2.3.1 - - /jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - chalk: 4.1.2 - ci-info: 3.8.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - dev: true + dev: false /jest-validate@27.5.1: resolution: {integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==} @@ -15808,18 +16889,6 @@ packages: pretty-format: 27.5.1 dev: false - /jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 - dev: true - /jest-watch-typeahead@1.1.0(jest@27.5.1): resolution: {integrity: sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -15842,7 +16911,7 @@ packages: dependencies: '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.34 + '@types/node': 20.10.5 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 27.5.1 @@ -15855,7 +16924,7 @@ packages: dependencies: '@jest/test-result': 28.1.3 '@jest/types': 28.1.3 - '@types/node': 16.18.34 + '@types/node': 20.10.5 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.10.2 @@ -15863,25 +16932,11 @@ packages: string-length: 4.0.2 dev: false - /jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.34 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 - dev: true - /jest-worker@26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 merge-stream: 2.0.0 supports-color: 7.2.0 dev: false @@ -15890,7 +16945,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 merge-stream: 2.0.0 supports-color: 8.1.1 dev: false @@ -15899,21 +16954,11 @@ packages: resolution: {integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@types/node': 16.18.34 + '@types/node': 20.10.5 merge-stream: 2.0.0 supports-color: 8.1.1 dev: false - /jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 16.18.34 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - dev: true - /jest@27.5.1: resolution: {integrity: sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -15935,27 +16980,6 @@ packages: - utf-8-validate dev: false - /jest@29.7.0(@types/node@18.15.3): - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0 - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.15.3) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /jiti@1.18.2: resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==} hasBin: true @@ -16007,7 +17031,7 @@ packages: optional: true dependencies: abab: 2.0.6 - acorn: 8.9.0 + acorn: 8.11.3 acorn-globals: 6.0.0 cssom: 0.4.4 cssstyle: 2.3.0 @@ -16049,6 +17073,26 @@ packages: engines: {node: '>=4'} hasBin: true + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-joy@9.9.1(quill-delta@5.1.0)(rxjs@7.8.1)(tslib@2.6.0): + resolution: {integrity: sha512-/d7th2nbQRBQ/nqTkBe6KjjvDciSwn9UICmndwk3Ed/Bk9AqkTRm4PnLVfXG4DKbT0rEY0nKnwE7NqZlqKE6kg==} + engines: {node: '>=10.0'} + hasBin: true + peerDependencies: + quill-delta: ^5 + rxjs: '7' + tslib: '2' + dependencies: + arg: 5.0.2 + hyperdyperid: 1.2.0 + quill-delta: 5.1.0 + rxjs: 7.8.1 + tslib: 2.6.0 + dev: true + /json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} dev: true @@ -16127,6 +17171,12 @@ packages: array-includes: 3.1.6 object.assign: 4.1.4 + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -16145,6 +17195,11 @@ packages: engines: {node: '>= 8'} dev: false + /ky@0.33.3: + resolution: {integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==} + engines: {node: '>=14.16'} + dev: true + /language-subtag-registry@0.3.22: resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} @@ -16167,9 +17222,17 @@ packages: shell-quote: 1.8.1 dev: false + /lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + dependencies: + readable-stream: 2.3.8 + dev: true + /leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} + dev: false /levn@0.3.0: resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} @@ -16199,6 +17262,10 @@ packages: uc.micro: 1.0.6 dev: true + /listenercount@1.0.1: + resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} + dev: true + /listr2@4.0.5: resolution: {integrity: sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==} engines: {node: '>=12'} @@ -16272,6 +17339,22 @@ packages: engines: {node: '>= 12.13.0'} dev: false + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.4.2 + pkg-types: 1.0.3 + dev: true + + /locate-app@2.2.7: + resolution: {integrity: sha512-4NR8WidaCRCozDZ0BW0U5wL91EPuuIshFun2//4Kpca4DIi5XPQHAUEbj+MQt7NihZTYs+HKfOuaoqurZ58bUg==} + dependencies: + n12: 1.8.10 + type-fest: 2.13.0 + userhome: 1.0.0 + dev: true + /locate-path@2.0.0: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} engines: {node: '>=4'} @@ -16308,12 +17391,25 @@ packages: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} dev: false + /lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + dev: true + /lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: false + /lodash.flattendeep@4.4.0: + resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} + dev: true + + /lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + dev: true + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: false /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -16333,6 +17429,10 @@ packages: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} dev: false + /lodash.zip@4.2.0: + resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} + dev: true + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -16354,6 +17454,15 @@ packages: wrap-ansi: 6.2.0 dev: true + /loglevel-plugin-prefix@0.8.4: + resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} + dev: true + + /loglevel@1.8.1: + resolution: {integrity: sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==} + engines: {node: '>= 0.6.0'} + dev: true + /long@4.0.0: resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} dev: false @@ -16368,6 +17477,12 @@ packages: dependencies: js-tokens: 4.0.0 + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + /lower-case-first@2.0.2: resolution: {integrity: sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==} dependencies: @@ -16379,6 +17494,11 @@ packages: dependencies: tslib: 2.6.0 + /lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /lru-cache@10.0.0: resolution: {integrity: sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==} engines: {node: 14 || >=16.14} @@ -16401,6 +17521,11 @@ packages: dependencies: yallist: 4.0.0 + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: true + /lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -16414,11 +17539,32 @@ packages: dependencies: sourcemap-codec: 1.4.8 + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /magicast@0.3.2: + resolution: {integrity: sha512-Fjwkl6a0syt9TFN0JSYpOybxiMCkYNEeOTnOTNRbjphirLakznZXAqrXgj/7GG3D1dvETONNwrBfinvAbpunDg==} + dependencies: + '@babel/parser': 7.23.4 + '@babel/types': 7.23.4 + source-map-js: 1.0.2 + dev: true + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} dependencies: - semver: 6.3.0 + semver: 6.3.1 + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -16428,6 +17574,7 @@ packages: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: tmpl: 1.0.5 + dev: false /map-age-cleaner@0.1.3: resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} @@ -16473,6 +17620,14 @@ packages: engines: {node: '>= 12'} hasBin: true + /md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /md5@2.3.0: resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} dependencies: @@ -16613,6 +17768,20 @@ packages: fs-monkey: 1.0.4 dev: false + /memfs@4.6.0(quill-delta@5.1.0)(rxjs@7.8.1)(tslib@2.6.0): + resolution: {integrity: sha512-I6mhA1//KEZfKRQT9LujyW6lRbX7RkC24xKododIDO3AGShcaFAMKElv1yFGWX8fD4UaSiwasr3NeQ5TdtHY1A==} + engines: {node: '>= 4.0.0'} + peerDependencies: + tslib: '2' + dependencies: + json-joy: 9.9.1(quill-delta@5.1.0)(rxjs@7.8.1)(tslib@2.6.0) + thingies: 1.15.0(tslib@2.6.0) + tslib: 2.6.0 + transitivePeerDependencies: + - quill-delta + - rxjs + dev: true + /memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} @@ -16742,6 +17911,14 @@ packages: braces: 3.0.2 picomatch: 2.3.1 + /miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + dev: true + /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -16767,6 +17944,16 @@ packages: engines: {node: '>=12'} dev: true + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: true + + /mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -16783,11 +17970,10 @@ packages: /minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - dev: false /minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - dev: false + dev: true /minimatch@3.0.5: resolution: {integrity: sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==} @@ -16812,7 +17998,13 @@ packages: engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 - dev: false + + /minimatch@6.2.0: + resolution: {integrity: sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true /minimatch@9.0.2: resolution: {integrity: sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==} @@ -16842,11 +18034,19 @@ packages: resolution: {integrity: sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==} engines: {node: '>=16 || 14 >=14.17'} + /mitt@3.0.0: + resolution: {integrity: sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==} + dev: true + /mixme@0.5.9: resolution: {integrity: sha512-VC5fg6ySUscaWUpI4gxCBTQMH2RdUpNrk+MsbpCYtIvf9SBJdiUey4qE7BXviJsJR4nDQxCZ+3yaYNW3guz/Pw==} engines: {node: '>= 8.0.0'} dev: true + /mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + dev: true + /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -16858,6 +18058,20 @@ packages: engines: {node: '>=10'} hasBin: true + /mlly@1.4.2: + resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} + dependencies: + acorn: 8.11.3 + pathe: 1.1.2 + pkg-types: 1.0.3 + ufo: 1.3.2 + dev: true + + /mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} + dev: true + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: false @@ -16887,6 +18101,10 @@ packages: object-assign: 4.1.1 thenify-all: 1.6.0 + /n12@1.8.10: + resolution: {integrity: sha512-/iREutgBDWCLwSqVOTKyAXRfToeY8Y9PmFPk3egwWVf6UYUyL9UXIaVnEkW4mx+g3dBGBywfvWilfKFEkiGK+A==} + dev: true + /nanoclone@0.2.1: resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==} dev: false @@ -16896,6 +18114,12 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} @@ -16911,6 +18135,11 @@ packages: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: false + /netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + dev: true + /next@13.4.4(@babel/core@7.23.3)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-C5S0ysM0Ily9McL4Jb48nOQHT1BukOWI59uC3X/xCMlYIh9rJZCv7nzG92J6e1cOBqQbKovlpgvHWFmz4eKKEA==} engines: {node: '>=16.8.0'} @@ -17030,6 +18259,15 @@ packages: dependencies: whatwg-url: 5.0.0 + /node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + dev: true + /node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} @@ -17038,12 +18276,52 @@ packages: /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + /node-preload@0.2.1: + resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} + engines: {node: '>=8'} + dependencies: + process-on-spawn: 1.0.0 + dev: true + /node-releases@2.0.12: resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==} /node-releases@2.0.13: resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + /node-stdlib-browser@1.2.0: + resolution: {integrity: sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg==} + engines: {node: '>=10'} + dependencies: + assert: 2.1.0 + browser-resolve: 2.0.0 + browserify-zlib: 0.2.0 + buffer: 5.7.1 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + create-require: 1.1.1 + crypto-browserify: 3.12.0 + domain-browser: 4.23.0 + events: 3.3.0 + https-browserify: 1.0.0 + isomorphic-timers-promises: 1.0.1 + os-browserify: 0.3.0 + path-browserify: 1.0.1 + pkg-dir: 5.0.0 + process: 0.11.10 + punycode: 1.4.1 + querystring-es3: 0.2.1 + readable-stream: 3.6.2 + stream-browserify: 3.0.0 + stream-http: 3.2.0 + string_decoder: 1.3.0 + timers-browserify: 2.0.12 + tty-browserify: 0.0.1 + url: 0.11.3 + util: 0.12.5 + vm-browserify: 1.1.2 + dev: true + /nodemon@2.0.22: resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} engines: {node: '>=8.10.0'} @@ -17097,13 +18375,10 @@ packages: engines: {node: '>=10'} dev: false - /npm-path@2.0.4: - resolution: {integrity: sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw==} - engines: {node: '>=0.8'} - hasBin: true - dependencies: - which: 1.3.1 - dev: false + /normalize-url@8.0.0: + resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==} + engines: {node: '>=14.16'} + dev: true /npm-run-all@4.1.5: resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} @@ -17134,16 +18409,6 @@ packages: path-key: 4.0.0 dev: true - /npm-which@3.0.1: - resolution: {integrity: sha512-CM8vMpeFQ7MAPin0U3wzDhSGV0hMHNwHU0wjo402IVizPDrs45jSfSuoC+wThevY88LQti8VvaAnqYAeVy3I1A==} - engines: {node: '>=4.2.0'} - hasBin: true - dependencies: - commander: 2.20.3 - npm-path: 2.0.4 - which: 1.3.1 - dev: false - /nth-check@1.0.2: resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} dependencies: @@ -17164,6 +18429,42 @@ packages: resolution: {integrity: sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==} dev: false + /nyc@15.1.0: + resolution: {integrity: sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==} + engines: {node: '>=8.9'} + hasBin: true + dependencies: + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + caching-transform: 4.0.0 + convert-source-map: 1.9.0 + decamelize: 1.2.0 + find-cache-dir: 3.3.2 + find-up: 4.1.0 + foreground-child: 2.0.0 + get-package-type: 0.1.0 + glob: 7.2.3 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-hook: 3.0.0 + istanbul-lib-instrument: 4.0.3 + istanbul-lib-processinfo: 2.0.3 + istanbul-lib-report: 3.0.0 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.5 + make-dir: 3.1.0 + node-preload: 0.2.1 + p-map: 3.0.0 + process-on-spawn: 1.0.0 + resolve-from: 5.0.0 + rimraf: 3.0.2 + signal-exit: 3.0.7 + spawn-wrap: 2.0.0 + test-exclude: 6.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - supports-color + dev: true + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -17185,7 +18486,6 @@ packages: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 - dev: false /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} @@ -17358,6 +18658,10 @@ packages: wcwidth: 1.0.1 dev: true + /os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + dev: true + /os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -17367,6 +18671,11 @@ packages: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} dev: true + /p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + dev: true + /p-defer@1.0.0: resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==} engines: {node: '>=4'} @@ -17408,6 +18717,13 @@ packages: dependencies: yocto-queue: 0.1.0 + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.0.0 + dev: true + /p-locate@2.0.0: resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} engines: {node: '>=4'} @@ -17439,6 +18755,13 @@ packages: engines: {node: '>=6'} dev: true + /p-map@3.0.0: + resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} + engines: {node: '>=8'} + dependencies: + aggregate-error: 3.1.0 + dev: true + /p-map@4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} @@ -17486,6 +18809,45 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + /pac-proxy-agent@7.0.1: + resolution: {integrity: sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==} + engines: {node: '>= 14'} + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.0 + debug: 4.3.4 + get-uri: 6.0.2 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + pac-resolver: 7.0.0 + socks-proxy-agent: 8.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /pac-resolver@7.0.0: + resolution: {integrity: sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==} + engines: {node: '>= 14'} + dependencies: + degenerator: 5.0.1 + ip: 1.1.8 + netmask: 2.0.2 + dev: true + + /package-hash@4.0.0: + resolution: {integrity: sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==} + engines: {node: '>=8'} + dependencies: + graceful-fs: 4.2.11 + hasha: 5.2.2 + lodash.flattendeep: 4.4.0 + release-zalgo: 1.0.0 + dev: true + + /pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + dev: true + /param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: @@ -17498,6 +18860,16 @@ packages: dependencies: callsites: 3.1.0 + /parse-asn1@5.1.6: + resolution: {integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==} + dependencies: + asn1.js: 5.4.1 + browserify-aes: 1.2.0 + evp_bytestokey: 1.0.3 + pbkdf2: 3.1.2 + safe-buffer: 5.2.1 + dev: true + /parse-entities@2.0.0: resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} dependencies: @@ -17557,6 +18929,10 @@ packages: no-case: 3.0.4 tslib: 2.6.0 + /path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + dev: true + /path-case@3.0.4: resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} dependencies: @@ -17640,6 +19016,29 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + dependencies: + create-hash: 1.2.0 + create-hmac: 1.1.7 + ripemd160: 2.0.2 + safe-buffer: 5.2.1 + sha.js: 2.4.11 + dev: true + + /pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + dev: true + /performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} dev: false @@ -17697,6 +19096,21 @@ packages: dependencies: find-up: 4.1.0 + /pkg-dir@5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + dependencies: + find-up: 5.0.0 + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.2 + pathe: 1.1.2 + dev: true + /pkg-up@3.1.0: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} @@ -17713,7 +19127,7 @@ packages: engines: {node: '>= 0.12.0'} dependencies: async: 2.6.4 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7 mkdirp: 0.5.6 transitivePeerDependencies: - supports-color @@ -17740,12 +19154,12 @@ packages: postcss: 8.4.24 dev: false - /postcss-calc@8.2.4(postcss@8.4.24): + /postcss-calc@8.2.4(postcss@8.4.31): resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} peerDependencies: postcss: ^8.2.2 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 dev: false @@ -17790,7 +19204,7 @@ packages: postcss-value-parser: 4.2.0 dev: false - /postcss-colormin@5.3.1(postcss@8.4.24): + /postcss-colormin@5.3.1(postcss@8.4.31): resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: @@ -17799,18 +19213,18 @@ packages: browserslist: 4.22.1 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-convert-values@5.1.3(postcss@8.4.24): + /postcss-convert-values@5.1.3(postcss@8.4.31): resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: browserslist: 4.22.1 - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false @@ -17854,40 +19268,40 @@ packages: postcss-selector-parser: 6.0.13 dev: false - /postcss-discard-comments@5.1.2(postcss@8.4.24): + /postcss-discard-comments@5.1.2(postcss@8.4.31): resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: false - /postcss-discard-duplicates@5.1.0(postcss@8.4.24): + /postcss-discard-duplicates@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: false - /postcss-discard-empty@5.1.1(postcss@8.4.24): + /postcss-discard-empty@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: false - /postcss-discard-overridden@5.1.0(postcss@8.4.24): + /postcss-discard-overridden@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: false /postcss-double-position-gradients@3.1.2(postcss@8.4.24): @@ -18069,18 +19483,18 @@ packages: postcss: 8.4.24 dev: false - /postcss-merge-longhand@5.1.7(postcss@8.4.24): + /postcss-merge-longhand@5.1.7(postcss@8.4.31): resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 - stylehacks: 5.1.1(postcss@8.4.24) + stylehacks: 5.1.1(postcss@8.4.31) dev: false - /postcss-merge-rules@5.1.4(postcss@8.4.24): + /postcss-merge-rules@5.1.4(postcss@8.4.31): resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: @@ -18088,94 +19502,94 @@ packages: dependencies: browserslist: 4.22.1 caniuse-api: 3.0.0 - cssnano-utils: 3.1.0(postcss@8.4.24) - postcss: 8.4.24 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: false - /postcss-minify-font-values@5.1.0(postcss@8.4.24): + /postcss-minify-font-values@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-gradients@5.1.1(postcss@8.4.24): + /postcss-minify-gradients@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: colord: 2.9.3 - cssnano-utils: 3.1.0(postcss@8.4.24) - postcss: 8.4.24 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-params@5.1.4(postcss@8.4.24): + /postcss-minify-params@5.1.4(postcss@8.4.31): resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: browserslist: 4.22.1 - cssnano-utils: 3.1.0(postcss@8.4.24) - postcss: 8.4.24 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-minify-selectors@5.2.1(postcss@8.4.24): + /postcss-minify-selectors@5.2.1(postcss@8.4.31): resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: false - /postcss-modules-extract-imports@3.0.0(postcss@8.4.24): + /postcss-modules-extract-imports@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: false - /postcss-modules-local-by-default@4.0.3(postcss@8.4.24): + /postcss-modules-local-by-default@4.0.3(postcss@8.4.31): resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.24) - postcss: 8.4.24 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-selector-parser: 6.0.13 postcss-value-parser: 4.2.0 dev: false - /postcss-modules-scope@3.0.0(postcss@8.4.24): + /postcss-modules-scope@3.0.0(postcss@8.4.31): resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: false - /postcss-modules-values@4.0.0(postcss@8.4.24): + /postcss-modules-values@4.0.0(postcss@8.4.31): resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: postcss: ^8.1.0 dependencies: - icss-utils: 5.1.0(postcss@8.4.24) - postcss: 8.4.24 + icss-utils: 5.1.0(postcss@8.4.31) + postcss: 8.4.31 dev: false /postcss-nested@6.0.1(postcss@8.4.24): @@ -18198,94 +19612,94 @@ packages: postcss-selector-parser: 6.0.13 dev: false - /postcss-normalize-charset@5.1.0(postcss@8.4.24): + /postcss-normalize-charset@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 dev: false - /postcss-normalize-display-values@5.1.0(postcss@8.4.24): + /postcss-normalize-display-values@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-positions@5.1.1(postcss@8.4.24): + /postcss-normalize-positions@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-repeat-style@5.1.1(postcss@8.4.24): + /postcss-normalize-repeat-style@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-string@5.1.0(postcss@8.4.24): + /postcss-normalize-string@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-timing-functions@5.1.0(postcss@8.4.24): + /postcss-normalize-timing-functions@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-unicode@5.1.1(postcss@8.4.24): + /postcss-normalize-unicode@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: browserslist: 4.22.1 - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-url@5.1.0(postcss@8.4.24): + /postcss-normalize-url@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: normalize-url: 6.1.0 - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false - /postcss-normalize-whitespace@5.1.1(postcss@8.4.24): + /postcss-normalize-whitespace@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false @@ -18312,14 +19726,14 @@ packages: postcss: 8.4.24 dev: false - /postcss-ordered-values@5.1.3(postcss@8.4.24): + /postcss-ordered-values@5.1.3(postcss@8.4.31): resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - cssnano-utils: 3.1.0(postcss@8.4.24) - postcss: 8.4.24 + cssnano-utils: 3.1.0(postcss@8.4.31) + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false @@ -18419,7 +19833,7 @@ packages: postcss-selector-parser: 6.0.13 dev: false - /postcss-reduce-initial@5.1.2(postcss@8.4.24): + /postcss-reduce-initial@5.1.2(postcss@8.4.31): resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: @@ -18427,16 +19841,16 @@ packages: dependencies: browserslist: 4.22.1 caniuse-api: 3.0.0 - postcss: 8.4.24 + postcss: 8.4.31 dev: false - /postcss-reduce-transforms@5.1.0(postcss@8.4.24): + /postcss-reduce-transforms@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 dev: false @@ -18465,24 +19879,24 @@ packages: cssesc: 3.0.0 util-deprecate: 1.0.2 - /postcss-svgo@5.1.0(postcss@8.4.24): + /postcss-svgo@5.1.0(postcss@8.4.31): resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-value-parser: 4.2.0 svgo: 2.8.0 dev: false - /postcss-unique-selectors@5.1.1(postcss@8.4.24): + /postcss-unique-selectors@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: - postcss: 8.4.24 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: false @@ -18523,6 +19937,15 @@ packages: source-map-js: 1.0.2 dev: false + /postcss@8.4.33: + resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /preact@10.15.1: resolution: {integrity: sha512-qs2ansoQEwzNiV5eAcRT1p1EC/dmEzaATVDJNiB3g2sRDWdA7b7MurXdJjB2+/WQktGWZwxvDrnuRFbWuIr64g==} dev: true @@ -18602,6 +20025,7 @@ packages: '@jest/schemas': 29.4.3 ansi-styles: 5.2.0 react-is: 18.2.0 + dev: false /pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} @@ -18614,7 +20038,23 @@ packages: /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: false + + /process-on-spawn@1.0.0: + resolution: {integrity: sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==} + engines: {node: '>=8'} + dependencies: + fromentries: 1.3.2 + dev: true + + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: true + + /progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: true /promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} @@ -18634,6 +20074,7 @@ packages: dependencies: kleur: 3.0.3 sisteransi: 1.0.5 + dev: false /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -18654,6 +20095,42 @@ packages: ipaddr.js: 1.9.1 dev: false + /proxy-agent@6.3.0: + resolution: {integrity: sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + lru-cache: 7.18.3 + pac-proxy-agent: 7.0.1 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /proxy-agent@6.3.1: + resolution: {integrity: sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + http-proxy-agent: 7.0.0 + https-proxy-agent: 7.0.2 + lru-cache: 7.18.3 + pac-proxy-agent: 7.0.1 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: true + /pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true @@ -18666,6 +20143,24 @@ packages: resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} dev: true + /public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + dependencies: + bn.js: 4.12.0 + browserify-rsa: 4.1.0 + create-hash: 1.2.0 + parse-asn1: 5.1.6 + randombytes: 2.1.0 + safe-buffer: 5.2.1 + dev: true + + /pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: true + /punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} dev: true @@ -18674,8 +20169,27 @@ packages: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} - /pure-rand@6.0.2: - resolution: {integrity: sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==} + /puppeteer-core@20.9.0(typescript@5.2.2): + resolution: {integrity: sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==} + engines: {node: '>=16.3.0'} + peerDependencies: + typescript: '>= 4.7.4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@puppeteer/browsers': 1.4.6(typescript@5.2.2) + chromium-bidi: 0.4.16(devtools-protocol@0.0.1147663) + cross-fetch: 4.0.0 + debug: 4.3.4 + devtools-protocol: 0.0.1147663 + typescript: 5.2.2 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate dev: true /pvtsutils@1.3.2: @@ -18700,6 +20214,22 @@ packages: side-channel: 1.0.4 dev: false + /qs@6.11.2: + resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: true + + /query-selector-shadow-dom@1.0.1: + resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + dev: true + + /querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + dev: true + /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} dev: false @@ -18707,11 +20237,29 @@ packages: /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + /queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + dev: true + /quick-lru@4.0.1: resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} engines: {node: '>=8'} dev: true + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true + + /quill-delta@5.1.0: + resolution: {integrity: sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==} + engines: {node: '>= 12.0.0'} + dependencies: + fast-diff: 1.3.0 + lodash.clonedeep: 4.5.0 + lodash.isequal: 4.5.0 + dev: true + /raf@3.4.1: resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} dependencies: @@ -18726,7 +20274,13 @@ packages: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: safe-buffer: 5.2.1 - dev: false + + /randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + dev: true /range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} @@ -19179,7 +20733,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: false /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} @@ -19189,6 +20742,12 @@ packages: string_decoder: 1.3.0 util-deprecate: 1.0.2 + /readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + dependencies: + minimatch: 5.1.6 + dev: true + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -19300,6 +20859,13 @@ packages: - encoding dev: true + /release-zalgo@1.0.0: + resolution: {integrity: sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==} + engines: {node: '>=4'} + dependencies: + es6-error: 4.1.1 + dev: true + /remark-footnotes@3.0.0: resolution: {integrity: sha512-ZssAvH9FjGYlJ/PBVKdSmfyPc3Cz4rTWgZLI4iE/SX8Nt5l3o3oEjv3wwG5VD7xOjktzdwp5coac+kJV9l4jgg==} dependencies: @@ -19386,11 +20952,16 @@ packages: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} dev: false + /resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + dev: true + /resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} dependencies: resolve-from: 5.0.0 + dev: false /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -19428,11 +20999,6 @@ packages: engines: {node: '>=10'} dev: false - /resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - dev: true - /resolve@1.19.0: resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==} dependencies: @@ -19464,6 +21030,19 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + /responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + dependencies: + lowercase-keys: 3.0.0 + dev: true + + /resq@1.11.0: + resolution: {integrity: sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==} + dependencies: + fast-deep-equal: 2.0.1 + dev: true + /restore-cursor@3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} @@ -19485,6 +21064,10 @@ packages: resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} dev: true + /rgb2hex@0.2.5: + resolution: {integrity: sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==} + dev: true + /rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} hasBin: true @@ -19498,6 +21081,13 @@ packages: dependencies: glob: 7.2.3 + /ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + dev: true + /rollup-plugin-terser@7.0.2(rollup@2.79.1): resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser @@ -19527,6 +21117,29 @@ packages: fsevents: 2.3.3 dev: true + /rollup@4.9.4: + resolution: {integrity: sha512-2ztU7pY/lrQyXSCnnoU4ICjT/tCG9cdH3/G25ERqE3Lst6vl2BCM5hL2Nw+sslAvAf+ccKsAq1SkKQALyqhR7g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.5 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.9.4 + '@rollup/rollup-android-arm64': 4.9.4 + '@rollup/rollup-darwin-arm64': 4.9.4 + '@rollup/rollup-darwin-x64': 4.9.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.9.4 + '@rollup/rollup-linux-arm64-gnu': 4.9.4 + '@rollup/rollup-linux-arm64-musl': 4.9.4 + '@rollup/rollup-linux-riscv64-gnu': 4.9.4 + '@rollup/rollup-linux-x64-gnu': 4.9.4 + '@rollup/rollup-linux-x64-musl': 4.9.4 + '@rollup/rollup-win32-arm64-msvc': 4.9.4 + '@rollup/rollup-win32-ia32-msvc': 4.9.4 + '@rollup/rollup-win32-x64-msvc': 4.9.4 + fsevents: 2.3.3 + dev: true + /run-applescript@5.0.0: resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} engines: {node: '>=12'} @@ -19550,6 +21163,10 @@ packages: tslib: 2.6.0 dev: true + /safaridriver@0.1.2: + resolution: {integrity: sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==} + dev: true + /safe-array-concat@1.0.0: resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} engines: {node: '>=0.4'} @@ -19572,7 +21189,6 @@ packages: /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: false /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -19757,6 +21373,13 @@ packages: upper-case-first: 2.0.2 dev: true + /serialize-error@11.0.3: + resolution: {integrity: sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==} + engines: {node: '>=14.16'} + dependencies: + type-fest: 2.19.0 + dev: true + /serialize-javascript@4.0.0: resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} dependencies: @@ -19831,6 +21454,14 @@ packages: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} dev: false + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -19871,6 +21502,10 @@ packages: get-intrinsic: 1.2.1 object-inspect: 1.12.3 + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -19878,6 +21513,11 @@ packages: resolution: {integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==} engines: {node: '>=14'} + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + /signedsource@1.0.0: resolution: {integrity: sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==} dev: true @@ -19889,8 +21529,18 @@ packages: semver: 7.0.0 dev: true + /sirv@2.0.4: + resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.24 + mrmime: 2.0.0 + totalist: 3.0.1 + dev: true + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: false /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} @@ -19919,6 +21569,11 @@ packages: is-fullwidth-code-point: 3.0.0 dev: true + /smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + dev: true + /smartwrap@2.0.2: resolution: {integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA==} engines: {node: '>=6'} @@ -19947,6 +21602,25 @@ packages: websocket-driver: 0.7.4 dev: false + /socks-proxy-agent@8.0.2: + resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.0 + debug: 4.3.4 + socks: 2.7.1 + transitivePeerDependencies: + - supports-color + dev: true + + /socks@2.7.1: + resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} + engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + dependencies: + ip: 2.0.0 + smart-buffer: 4.2.0 + dev: true + /source-list-map@2.0.1: resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} dev: false @@ -19967,18 +21641,12 @@ packages: webpack: 5.88.0(esbuild@0.17.19) dev: false - /source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - dev: true - /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + dev: false /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} @@ -19999,6 +21667,18 @@ packages: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} deprecated: Please use @jridgewell/sourcemap-codec instead + /spawn-wrap@2.0.0: + resolution: {integrity: sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==} + engines: {node: '>=8'} + dependencies: + foreground-child: 2.0.0 + is-windows: 1.0.2 + make-dir: 3.1.0 + rimraf: 3.0.2 + signal-exit: 3.0.7 + which: 2.0.2 + dev: true + /spawndamnit@2.0.0: resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} dependencies: @@ -20054,6 +21734,11 @@ packages: - supports-color dev: false + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + dev: true + /sponge-case@1.0.1: resolution: {integrity: sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==} dependencies: @@ -20073,6 +21758,11 @@ packages: engines: {node: '>=10'} dependencies: escape-string-regexp: 2.0.0 + dev: false + + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true /stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} @@ -20088,6 +21778,10 @@ packages: engines: {node: '>= 0.8'} dev: false + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + dev: true + /stop-iteration-iterator@1.0.0: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} @@ -20095,6 +21789,22 @@ packages: internal-slot: 1.0.5 dev: false + /stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: true + + /stream-http@3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + xtend: 4.0.2 + dev: true + /stream-transform@2.1.3: resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} dependencies: @@ -20105,6 +21815,13 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} + /streamx@2.15.6: + resolution: {integrity: sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==} + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + dev: true + /string-env-interpolation@1.0.1: resolution: {integrity: sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==} dev: true @@ -20115,6 +21832,7 @@ packages: dependencies: char-regex: 1.0.2 strip-ansi: 6.0.1 + dev: false /string-length@5.0.1: resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} @@ -20216,7 +21934,6 @@ packages: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: false /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -20283,6 +22000,12 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + dependencies: + acorn: 8.11.3 + dev: true + /structured-source@4.0.0: resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==} dependencies: @@ -20316,14 +22039,14 @@ packages: react: 18.2.0 dev: false - /stylehacks@5.1.1(postcss@8.4.24): + /stylehacks@5.1.1(postcss@8.4.31): resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: postcss: ^8.2.15 dependencies: browserslist: 4.22.1 - postcss: 8.4.24 + postcss: 8.4.31 postcss-selector-parser: 6.0.13 dev: false @@ -20357,6 +22080,7 @@ packages: engines: {node: '>=10'} dependencies: has-flag: 4.0.0 + dev: false /supports-hyperlinks@2.3.0: resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} @@ -20500,6 +22224,22 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + /tar-fs@3.0.4: + resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} + dependencies: + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 3.1.6 + dev: true + + /tar-stream@3.1.6: + resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} + dependencies: + b4a: 1.6.4 + fast-fifo: 1.3.2 + streamx: 2.15.6 + dev: true + /temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -20651,6 +22391,15 @@ packages: dependencies: any-promise: 1.3.0 + /thingies@1.15.0(tslib@2.6.0): + resolution: {integrity: sha512-ZSJlvEpD8QllYim0VSGlbAoob/iPrTWNlV/m8ltizMvMmzzU2gVJvHfH9ijLstyciWF70ZiQXqz+BCXWJq+ZQw==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + dependencies: + tslib: 2.6.0 + dev: true + /throat@6.0.2: resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==} dev: false @@ -20663,6 +22412,27 @@ packages: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} dev: false + /timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + dependencies: + setimmediate: 1.0.5 + dev: true + + /tinybench@2.5.1: + resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} + dev: true + + /tinypool@0.8.1: + resolution: {integrity: sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.0: + resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} + engines: {node: '>=14.0.0'} + dev: true + /title-case@3.0.3: resolution: {integrity: sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==} dependencies: @@ -20683,6 +22453,7 @@ packages: /tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: false /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} @@ -20711,6 +22482,11 @@ packages: resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} dev: false + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + dev: true + /touch@3.1.0: resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} hasBin: true @@ -20743,6 +22519,10 @@ packages: punycode: 2.3.0 dev: false + /traverse@0.3.9: + resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} + dev: true + /traverse@0.6.7: resolution: {integrity: sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==} dev: true @@ -20778,63 +22558,27 @@ packages: dev: true /ts-essentials@1.0.4: - resolution: {integrity: sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ==} - dev: true - - /ts-generator@0.1.1: - resolution: {integrity: sha512-N+ahhZxTLYu1HNTQetwWcx3so8hcYbkKBHTr4b4/YgObFTIKkOSSsaa+nal12w8mfrJAyzJfETXawbNjSfP2gQ==} - hasBin: true - dependencies: - '@types/mkdirp': 0.5.2 - '@types/prettier': 2.7.3 - '@types/resolve': 0.0.8 - chalk: 2.4.2 - glob: 7.2.3 - mkdirp: 0.5.6 - prettier: 2.8.8 - resolve: 1.22.2 - ts-essentials: 1.0.4 - dev: true - - /ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - - /ts-jest@29.1.1(@babel/core@7.23.3)(@jest/types@29.5.0)(esbuild@0.17.19)(jest@29.7.0)(typescript@5.2.2): - resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 - esbuild: '*' - jest: ^29.0.0 - typescript: '>=4.3 <6' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - dependencies: - '@babel/core': 7.23.3 - '@jest/types': 29.5.0 - bs-logger: 0.2.6 - esbuild: 0.17.19 - fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@18.15.3) - jest-util: 29.5.0 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.5.4 - typescript: 5.2.2 - yargs-parser: 21.1.1 + resolution: {integrity: sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ==} + dev: true + + /ts-generator@0.1.1: + resolution: {integrity: sha512-N+ahhZxTLYu1HNTQetwWcx3so8hcYbkKBHTr4b4/YgObFTIKkOSSsaa+nal12w8mfrJAyzJfETXawbNjSfP2gQ==} + hasBin: true + dependencies: + '@types/mkdirp': 0.5.2 + '@types/prettier': 2.7.3 + '@types/resolve': 0.0.8 + chalk: 2.4.2 + glob: 7.2.3 + mkdirp: 0.5.6 + prettier: 2.8.8 + resolve: 1.22.2 + ts-essentials: 1.0.4 dev: true + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + /ts-log@2.2.5: resolution: {integrity: sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==} dev: true @@ -20887,6 +22631,7 @@ packages: /tslib@2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + dev: false /tslib@2.4.1: resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} @@ -20940,17 +22685,21 @@ packages: tslib: 1.14.1 typescript: 5.2.2 - /tsx@3.12.7: - resolution: {integrity: sha512-C2Ip+jPmqKd1GWVQDvz/Eyc6QJbGfE7NrR3fx5BpEHMZsEHoIxHL1j+lKdGobr8ovEyqeNkPLSKp6SCSOt7gmw==} + /tsx@4.7.0: + resolution: {integrity: sha512-I+t79RYPlEYlHn9a+KzwrvEwhJg35h/1zHsLC2JXvhC2mdynMv6Zxzvhv5EMV6VF5qJlLlkSnMVvdZV3PSIGcg==} + engines: {node: '>=18.0.0'} hasBin: true dependencies: - '@esbuild-kit/cjs-loader': 2.4.2 - '@esbuild-kit/core-utils': 3.1.0 - '@esbuild-kit/esm-loader': 2.5.5 + esbuild: 0.19.11 + get-tsconfig: 4.7.2 optionalDependencies: fsevents: 2.3.3 dev: true + /tty-browserify@0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + dev: true + /tty-table@4.2.1: resolution: {integrity: sha512-xz0uKo+KakCQ+Dxj1D/tKn2FSyreSYWzdkL/BYhgN6oMW808g8QRMuh1atAV9fjTPbWBjfbkKQpI/5rEcnAc7g==} engines: {node: '>=8.0.0'} @@ -21071,6 +22820,16 @@ packages: engines: {node: '>=8'} dev: true + /type-fest@2.13.0: + resolution: {integrity: sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw==} + engines: {node: '>=12.20'} + dev: true + + /type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + dev: true + /type-fest@3.1.0: resolution: {integrity: sha512-StmrZmK3eD9mDF9Vt7UhqthrDSk66O9iYl5t5a0TSoVkHjl0XZx/xuc/BRz4urAXXGHOY5OLsE0RdJFIApSFmw==} engines: {node: '>=14.16'} @@ -21125,7 +22884,6 @@ packages: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: is-typedarray: 1.0.0 - dev: false /typedoc-plugin-markdown@3.15.3(typedoc@0.25.3): resolution: {integrity: sha512-idntFYu3vfaY3eaD+w9DeRd0PmNGqGuNLKihPU9poxFGnATJYGn9dPtEhn2QrTdishFMg7jPXAhos+2T6YCWRQ==} @@ -21176,6 +22934,10 @@ packages: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} dev: true + /ufo@1.3.2: + resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} + dev: true + /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} @@ -21192,6 +22954,13 @@ packages: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + /unbzip2-stream@1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + dependencies: + buffer: 5.7.1 + through: 2.3.8 + dev: true + /unc-path-regex@0.1.2: resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} engines: {node: '>=0.10.0'} @@ -21203,7 +22972,6 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true /undici@5.22.1: resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==} @@ -21319,6 +23087,21 @@ packages: engines: {node: '>=8'} dev: true + /unzipper@0.10.14: + resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} + dependencies: + big-integer: 1.6.51 + binary: 0.3.0 + bluebird: 3.4.7 + buffer-indexof-polyfill: 1.0.2 + duplexer2: 0.1.4 + fstream: 1.0.12 + graceful-fs: 4.2.11 + listenercount: 1.0.1 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + dev: true + /upath@1.2.0: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} engines: {node: '>=4'} @@ -21368,6 +23151,13 @@ packages: requires-port: 1.0.0 dev: false + /url@0.11.3: + resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==} + dependencies: + punycode: 1.4.1 + qs: 6.11.2 + dev: true + /urlpattern-polyfill@8.0.2: resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} dev: true @@ -21424,6 +23214,11 @@ packages: react: 18.2.0 dev: false + /userhome@1.0.0: + resolution: {integrity: sha512-ayFKY3H+Pwfy4W98yPdtH1VqH4psDeyW8lYYFzfecR9d6hqLpqhecktvYR3SEEXt7vG0S1JEpciI3g94pMErig==} + engines: {node: '>= 0.8.0'} + dev: true + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -21436,6 +23231,16 @@ packages: object.getownpropertydescriptors: 2.1.6 dev: false + /util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.12 + which-typed-array: 1.1.13 + dev: true + /utila@0.4.0: resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} dev: false @@ -21445,16 +23250,9 @@ packages: engines: {node: '>= 0.4.0'} dev: false - /uuid@3.4.0: - resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. - hasBin: true - dev: true - /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - dev: false /uuid@9.0.0: resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} @@ -21474,15 +23272,6 @@ packages: source-map: 0.7.4 dev: false - /v8-to-istanbul@9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} - engines: {node: '>=10.12.0'} - dependencies: - '@jridgewell/trace-mapping': 0.3.18 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 - dev: true - /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -21516,6 +23305,81 @@ packages: vfile-message: 2.0.4 dev: true + /vite-node@1.1.3(@types/node@18.15.3): + resolution: {integrity: sha512-BLSO72YAkIUuNrOx+8uznYICJfTEbvBAmWClY3hpath5+h1mbPS5OMn42lrTxXuyCazVyZoDkSRnju78GiVCqA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.0.11(@types/node@18.15.3) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite-plugin-node-polyfills@0.17.0(vite@5.0.11): + resolution: {integrity: sha512-iPmPn7376e5u6QvoTSJa16hf5Q0DFwHFXJk2uYpsNlmI3JdPms7hWyh55o+OysJ5jo9J5XPhLC9sMOYifwFd1w==} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + dependencies: + '@rollup/plugin-inject': 5.0.5 + buffer-polyfill: /buffer@6.0.3 + node-stdlib-browser: 1.2.0 + process: 0.11.10 + vite: 5.0.11(@types/node@18.15.3) + transitivePeerDependencies: + - rollup + dev: true + + /vite-plugin-plain-text@1.4.2: + resolution: {integrity: sha512-nkCWW16lkTidaGZ9kItwMZ5OEkUeXMrY4Okc9IQXrN/p6SAuDYmEiGqMRKl1rnhm6CR1h98uJtn+ODkv0cL7DA==} + dependencies: + '@napi-rs/magic-string': 0.3.4 + fast-glob: 3.3.1 + minimatch: 6.2.0 + dev: true + + /vite@4.3.9: + resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.17.19 + postcss: 8.4.24 + rollup: 3.25.3 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /vite@4.3.9(@types/node@18.15.3): resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -21549,6 +23413,42 @@ packages: fsevents: 2.3.3 dev: true + /vite@5.0.11(@types/node@18.15.3): + resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.15.3 + esbuild: 0.19.11 + postcss: 8.4.33 + rollup: 4.9.4 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /vitepress-plugin-search@1.0.4-alpha.19(flexsearch@0.7.31)(vitepress@1.0.0-alpha.51)(vue@3.2.47): resolution: {integrity: sha512-WFOPn5dStyMINd+rVjNxbEmGa7U+qGHLxLnda56EG+ATil1i0yOauGhJEh5LPMvuCUVIA9tInJnFXklOBb39dA==} engines: {node: ^14.13.1 || ^16.7.0 || >=18} @@ -21593,6 +23493,68 @@ packages: - terser dev: true + /vitest@1.1.3(@types/node@18.15.3)(@vitest/browser@1.1.3): + resolution: {integrity: sha512-2l8om1NOkiA90/Y207PsEvJLYygddsOyr81wLQ20Ra8IlLKbyQncWsGZjnbkyG2KwwuTXLQjEPOJuxGMG8qJBQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': ^1.0.0 + '@vitest/ui': ^1.0.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/node': 18.15.3 + '@vitest/browser': 1.1.3(vitest@1.1.3)(webdriverio@8.27.2) + '@vitest/expect': 1.1.3 + '@vitest/runner': 1.1.3 + '@vitest/snapshot': 1.1.3 + '@vitest/spy': 1.1.3 + '@vitest/utils': 1.1.3 + acorn-walk: 8.3.2 + cac: 6.7.14 + chai: 4.4.0 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.5 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 1.3.0 + tinybench: 2.5.1 + tinypool: 0.8.1 + vite: 5.0.11(@types/node@18.15.3) + vite-node: 1.1.3(@types/node@18.15.3) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + dev: true + /vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} @@ -21638,10 +23600,23 @@ packages: xml-name-validator: 3.0.0 dev: false + /wait-port@1.1.0: + resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} + engines: {node: '>=10'} + hasBin: true + dependencies: + chalk: 4.1.2 + commander: 9.4.1 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: makeerror: 1.0.12 + dev: false /watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} @@ -21687,6 +23662,68 @@ packages: tslib: 2.6.0 dev: true + /webdriver@8.27.2: + resolution: {integrity: sha512-vY2Lr0ZNr83n0v8PjLCXtJwR9E7QGycJVS+ev2G72gI54/rFwLv58HMSbJNn8CtE27VkhtewMUPlDpSkj5wGPQ==} + engines: {node: ^16.13 || >=18} + dependencies: + '@types/node': 20.10.5 + '@types/ws': 8.5.5 + '@wdio/config': 8.27.2 + '@wdio/logger': 8.24.12 + '@wdio/protocols': 8.24.12 + '@wdio/types': 8.27.2 + '@wdio/utils': 8.27.2 + deepmerge-ts: 5.1.0 + got: 12.6.1 + ky: 0.33.3 + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + + /webdriverio@8.27.2(typescript@5.2.2): + resolution: {integrity: sha512-X6PhKE8e8XsB33Q/KSS1zYKP2Rqkq2Nef0YKOhQO+5OTlTkeqMCjnEtyRcfmdtfAwT0DEFqMnGnUKEbTajFC4Q==} + engines: {node: ^16.13 || >=18} + peerDependencies: + devtools: ^8.14.0 + peerDependenciesMeta: + devtools: + optional: true + dependencies: + '@types/node': 20.10.5 + '@wdio/config': 8.27.2 + '@wdio/logger': 8.24.12 + '@wdio/protocols': 8.24.12 + '@wdio/repl': 8.24.12 + '@wdio/types': 8.27.2 + '@wdio/utils': 8.27.2 + archiver: 6.0.1 + aria-query: 5.3.0 + css-shorthand-properties: 1.1.1 + css-value: 0.0.1 + devtools-protocol: 0.0.1239539 + grapheme-splitter: 1.0.4 + import-meta-resolve: 4.0.0 + is-plain-obj: 4.1.0 + lodash.clonedeep: 4.5.0 + lodash.zip: 4.2.0 + minimatch: 9.0.3 + puppeteer-core: 20.9.0(typescript@5.2.2) + query-selector-shadow-dom: 1.0.1 + resq: 1.11.0 + rgb2hex: 0.2.5 + serialize-error: 11.0.3 + webdriver: 8.27.2 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - typescript + - utf-8-validate + dev: true + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -21971,6 +24008,23 @@ packages: dependencies: isexe: 2.0.0 + /which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + dependencies: + isexe: 3.1.1 + dev: true + + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + /word-wrap@1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} @@ -22179,15 +24233,6 @@ packages: is-typedarray: 1.0.0 signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 - dev: false - - /write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - dev: true /write@1.0.3: resolution: {integrity: sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==} @@ -22232,6 +24277,7 @@ packages: optional: true utf-8-validate: optional: true + dev: false /xml-name-validator@3.0.0: resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} @@ -22245,6 +24291,11 @@ packages: resolution: {integrity: sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw==} dev: false + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + /y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} dev: true @@ -22323,6 +24374,19 @@ packages: yargs-parser: 20.2.9 dev: false + /yargs@17.7.1: + resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + /yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -22336,6 +24400,13 @@ packages: yargs-parser: 21.1.1 dev: true + /yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + dev: true + /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -22345,6 +24416,11 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + /yup@0.32.11: resolution: {integrity: sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==} engines: {node: '>=10'} @@ -22358,6 +24434,15 @@ packages: toposort: 2.0.2 dev: false + /zip-stream@5.0.1: + resolution: {integrity: sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==} + engines: {node: '>= 12.0.0'} + dependencies: + archiver-utils: 4.0.1 + compress-commons: 5.0.1 + readable-stream: 3.6.2 + dev: true + /zod@3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} dev: false diff --git a/scripts/run-node.sh b/scripts/run-node.sh index 51257639cc1..28749fd65d7 100755 --- a/scripts/run-node.sh +++ b/scripts/run-node.sh @@ -5,6 +5,8 @@ mkdir -p .fuel-core/db +cp ./packages/utils/src/utils/chainConfig.json .fuel-core/configs/chainConfig.json + pnpm fuels-core run \ --db-path .fuel-core/db \ --consensus-key 0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298 \ diff --git a/scripts/ci-test.sh b/scripts/tests-ci.sh similarity index 68% rename from scripts/ci-test.sh rename to scripts/tests-ci.sh index d5a42af2d78..1ecfac1a748 100755 --- a/scripts/ci-test.sh +++ b/scripts/tests-ci.sh @@ -8,8 +8,11 @@ pnpm node:run > /dev/null 2>&1 & echo "Started Fuel-Core node in background." -if [[ "$*" == *"--coverage"* ]]; then - pnpm test $@ +if [[ "$*" == *"--browser"* ]]; then + pnpm test:browser + TEST_RESULT=$? +elif [[ "$*" == *"--node"* ]]; then + pnpm test TEST_RESULT=$? else pnpm test diff --git a/scripts/tests-coverage-diff.ts b/scripts/tests-coverage-diff.ts new file mode 100644 index 00000000000..68b0450d532 --- /dev/null +++ b/scripts/tests-coverage-diff.ts @@ -0,0 +1,27 @@ +import { diff as coverageDiff } from 'coverage-diff'; +import { readFileSync, writeFileSync } from 'fs'; + +const getDiff = () => { + const base = JSON.parse(readFileSync('coverage-master/coverage-summary.json').toString()); + const head = JSON.parse(readFileSync('coverage/report/coverage-summary.json').toString()); + return coverageDiff(base, head); +}; + +(() => { + const { results } = getDiff(); + const formattedResults = results.split(`${process.cwd()}/`).join(''); + const resultsSections = formattedResults.split('Total:'); + const report = ` + ***Coverage Report:*** + ${resultsSections[1]} + +

+ + Changed Files: + + ${resultsSections[0]} +
+ `; + + writeFileSync('coverage/report/coverage-diff.txt', report, { flag: 'w' }); +})(); diff --git a/scripts/tests-coverage-merge.ts b/scripts/tests-coverage-merge.ts new file mode 100644 index 00000000000..ce2a74aae8b --- /dev/null +++ b/scripts/tests-coverage-merge.ts @@ -0,0 +1,36 @@ +import { execSync } from 'child_process'; +import { readdirSync, renameSync, rmSync } from 'fs'; +import { join } from 'path'; + +const restructureCoverageDirectory = () => { + const coverageDir = join(__dirname, '../coverage/'); + const environmentsDir = join(coverageDir, '/environments/'); + const validEnvironments = ['node', 'browser']; + + const environments = readdirSync(environmentsDir); + environments.forEach((environment) => { + if (validEnvironments.includes(environment)) { + // Move environment coverage directories to a single file + renameSync( + join(environmentsDir, `${environment}/coverage-final.json`), + join(environmentsDir, `${environment}.json`) + ); + // Remove environment coverage directory + rmSync(join(environmentsDir, environment), { recursive: true, force: true }); + } + }); +}; + +(() => { + // Structure all coverage environment dirs into a single dir + restructureCoverageDirectory(); + + // Merge all coverage files + execSync('nyc merge coverage/environments coverage/merged/coverage.json', { stdio: 'inherit' }); + + // Generate coverage report + execSync( + 'nyc report --temp-dir=coverage/merged --report-dir=coverage/report --exclude-after-remap=false', + { stdio: 'inherit' } + ); +})(); diff --git a/scripts/tests-find.sh b/scripts/tests-find.sh new file mode 100755 index 00000000000..264d459b3cf --- /dev/null +++ b/scripts/tests-find.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +ROOT=$(cd "$(dirname "$0")/.."; pwd) + +FILES=$(find $ROOT/{apps,packages,internal} -name '*.test.ts') + +if [[ $* == *--all* ]]; then + grep -lE "@group\s+(node|browser|e2e)" $FILES +elif [[ $* == *--node* ]]; then + grep -lE "@group\s+node" $FILES +elif [[ $* == *--browser* ]]; then + grep -lE "@group\s+browser" $FILES +elif [[ $* == *--e2e* ]]; then + grep -lE "@group\s+e2e" $FILES +fi \ No newline at end of file diff --git a/scripts/tests-validate.sh b/scripts/tests-validate.sh new file mode 100755 index 00000000000..74917d2fb82 --- /dev/null +++ b/scripts/tests-validate.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +ROOT=$(cd "$(dirname "$0")/.."; pwd) + +FILES=$(find $ROOT/{apps,packages,internal} -name '*.test.ts') +INVALID_FILES=$(grep -LE "@group\s+(node|browser|e2e)" $FILES) + +if [ ! -z "$INVALID_FILES" ]; then + echo -e "Test files don't contain a test environment configuration:" + echo -e $INVALID_FILES + exit 1 +fi \ No newline at end of file diff --git a/tsconfig.base.json b/tsconfig.base.json index 5c5915c7030..09debdaee95 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -11,7 +11,8 @@ "skipLibCheck": true, "strict": true, "target": "ES2022", - "sourceMap": true + "sourceMap": true, + "types": ["vitest/globals", "node", "node-fetch", "web"] }, "exclude": ["**/dist", "**/node_modules"] } diff --git a/vite.base.config.mts b/vite.base.config.mts new file mode 100644 index 00000000000..fd72f14ee81 --- /dev/null +++ b/vite.base.config.mts @@ -0,0 +1,43 @@ +import plainText from "vite-plugin-plain-text"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [ + plainText("**/*.hbs", { + namedExport: false, + }), + ], + test: { + exclude: [ + "**/node_modules/**", + "**/dist/**", + "/apps/demo-nextjs", + "/apps/demo-react-cra", + "/apps/demo-react-vite", + ], + globals: true, + setupFiles: ["./vite.env.ts"], + coverage: { + enabled: true, + provider: "istanbul", + reporter: ["json", "text", "html"], + include: ["packages", "internal", "apps"], + exclude: [ + "**/node_modules/**", + "**/dist/**", + "**/test/**", + "**/*.test.ts", + "**/*.d.ts", + "packages/fuel-gauge/**", + "apps/demo-*", + "apps/docs", + ], + }, + poolOptions: { + threads: { + minThreads: 1, + maxThreads: 16, + }, + } + }, +}); diff --git a/vite.browser.config.mts b/vite.browser.config.mts new file mode 100644 index 00000000000..51b1dbc3bc1 --- /dev/null +++ b/vite.browser.config.mts @@ -0,0 +1,36 @@ +import { nodePolyfills } from "vite-plugin-node-polyfills"; +import { mergeConfig } from "vitest/config"; +import type { UserConfig } from "vitest/config"; + +import baseConfig from "./vite.base.config.mts"; + +const config: UserConfig = { + plugins: [ + nodePolyfills({ + globals: { + process: true, + Buffer: true, + global: true, + }, + include: ["fs", "crypto", "buffer", "fs"], + overrides: { + fs: "memfs", + }, + }), + ], + optimizeDeps: { + exclude: ["fsevents"], + }, + test: { + coverage: { + reportsDirectory: "coverage/environments/browser", + }, + browser: { + headless: true, + enabled: true, + name: "chrome", + }, + }, +}; + +export default mergeConfig(baseConfig, config); diff --git a/jest.env.ts b/vite.env.ts similarity index 83% rename from jest.env.ts rename to vite.env.ts index e550cae7793..689c8aa5304 100644 --- a/jest.env.ts +++ b/vite.env.ts @@ -1,9 +1,9 @@ -import { hexlify } from 'ethers'; +import { hexlify } from '@fuel-ts/utils'; import faucets from './.fuel-core/configs/faucets.json'; /** - * Gets the private key for the current Jest worker. + * Gets the private key for the current Vitest worker. * * The PK is basically the current worker's ID. This allows us to * have a unique faucet wallet for each file being tested and prevents @@ -18,11 +18,11 @@ import faucets from './.fuel-core/configs/faucets.json'; * - 0x0101010101010101010101010101010101010101010101010101010101010101 * - 0x0202020202020202020202020202020202020202020202020202020202020202 * - * See: https://jestjs.io/docs/environment-variables#jest_worker_id + * See: https://vitest.dev/config/#setupfiles */ const getPrivateKeyForCurrentWorker = () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const workerId = Number.parseInt(process.env.JEST_WORKER_ID!, 10); + const workerId = Number.parseInt(process.env.VITEST_POOL_ID!, 10) || 1; if (workerId > faucets.length) { throw new Error(`At most ${faucets.length} workers are supported.`); diff --git a/vite.node.config.mts b/vite.node.config.mts new file mode 100644 index 00000000000..57e002b56df --- /dev/null +++ b/vite.node.config.mts @@ -0,0 +1,14 @@ +import type { UserConfig } from "vitest/config"; +import { mergeConfig } from "vitest/config"; + +import baseConfig from "./vite.base.config.mts"; + +const config: UserConfig = { + test: { + coverage: { + reportsDirectory: "coverage/environments/node", + }, + }, +}; + +export default mergeConfig(baseConfig, config);