From 1166c0c1c845dc6a4c6672f7b8a93c0a1195cd9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 00:00:35 +0200 Subject: [PATCH 01/19] feat: init KYC controller and add logic from mobile repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- README.md | 7 + packages/kyc-controller/CHANGELOG.md | 18 + packages/kyc-controller/LICENSE | 6 + packages/kyc-controller/LICENSE.APACHE2 | 201 ++ packages/kyc-controller/LICENSE.MIT | 21 + packages/kyc-controller/README.md | 23 + packages/kyc-controller/jest.config.js | 26 + packages/kyc-controller/package.json | 89 + .../src/KycController-method-action-types.ts | 160 + .../kyc-controller/src/KycController.test.ts | 814 +++++ packages/kyc-controller/src/KycController.ts | 855 +++++ .../src/KycService-method-action-types.ts | 87 + .../kyc-controller/src/KycService.test.ts | 352 ++ packages/kyc-controller/src/KycService.ts | 418 +++ .../kyc-controller/src/countryCodes.test.ts | 19 + packages/kyc-controller/src/countryCodes.ts | 270 ++ packages/kyc-controller/src/crypto.test.ts | 204 ++ packages/kyc-controller/src/crypto.ts | 252 ++ packages/kyc-controller/src/index.test.ts | 19 + packages/kyc-controller/src/index.ts | 76 + packages/kyc-controller/src/selectors.test.ts | 33 + packages/kyc-controller/src/selectors.ts | 42 + packages/kyc-controller/src/types.ts | 119 + packages/kyc-controller/tsconfig.build.json | 16 + packages/kyc-controller/tsconfig.json | 14 + packages/kyc-controller/typedoc.json | 7 + tsconfig.build.json | 3 + tsconfig.json | 3 + yarn.lock | 3003 +++++++++-------- 29 files changed, 5695 insertions(+), 1462 deletions(-) create mode 100644 packages/kyc-controller/CHANGELOG.md create mode 100644 packages/kyc-controller/LICENSE create mode 100644 packages/kyc-controller/LICENSE.APACHE2 create mode 100644 packages/kyc-controller/LICENSE.MIT create mode 100644 packages/kyc-controller/README.md create mode 100644 packages/kyc-controller/jest.config.js create mode 100644 packages/kyc-controller/package.json create mode 100644 packages/kyc-controller/src/KycController-method-action-types.ts create mode 100644 packages/kyc-controller/src/KycController.test.ts create mode 100644 packages/kyc-controller/src/KycController.ts create mode 100644 packages/kyc-controller/src/KycService-method-action-types.ts create mode 100644 packages/kyc-controller/src/KycService.test.ts create mode 100644 packages/kyc-controller/src/KycService.ts create mode 100644 packages/kyc-controller/src/countryCodes.test.ts create mode 100644 packages/kyc-controller/src/countryCodes.ts create mode 100644 packages/kyc-controller/src/crypto.test.ts create mode 100644 packages/kyc-controller/src/crypto.ts create mode 100644 packages/kyc-controller/src/index.test.ts create mode 100644 packages/kyc-controller/src/index.ts create mode 100644 packages/kyc-controller/src/selectors.test.ts create mode 100644 packages/kyc-controller/src/selectors.ts create mode 100644 packages/kyc-controller/src/types.ts create mode 100644 packages/kyc-controller/tsconfig.build.json create mode 100644 packages/kyc-controller/tsconfig.json create mode 100644 packages/kyc-controller/typedoc.json diff --git a/README.md b/README.md index 24c9ede1fed..ea6739a6593 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ yarn skills --reset # clear saved local selection - [`@metamask/json-rpc-engine`](packages/json-rpc-engine) - [`@metamask/json-rpc-middleware-stream`](packages/json-rpc-middleware-stream) - [`@metamask/keyring-controller`](packages/keyring-controller) +- [`@metamask/kyc-controller`](packages/kyc-controller) - [`@metamask/local-node-utils`](packages/local-node-utils) - [`@metamask/logging-controller`](packages/logging-controller) - [`@metamask/message-manager`](packages/message-manager) @@ -191,6 +192,7 @@ linkStyle default opacity:0.5 json_rpc_engine(["@metamask/json-rpc-engine"]); json_rpc_middleware_stream(["@metamask/json-rpc-middleware-stream"]); keyring_controller(["@metamask/keyring-controller"]); + kyc_controller(["@metamask/kyc-controller"]); local_node_utils(["@metamask/local-node-utils"]); logging_controller(["@metamask/logging-controller"]); message_manager(["@metamask/message-manager"]); @@ -418,6 +420,11 @@ linkStyle default opacity:0.5 keyring_controller --> base_controller; keyring_controller --> controller_utils; keyring_controller --> messenger; + kyc_controller --> base_controller; + kyc_controller --> controller_utils; + kyc_controller --> geolocation_controller; + kyc_controller --> messenger; + kyc_controller --> profile_sync_controller; logging_controller --> base_controller; logging_controller --> controller_utils; logging_controller --> messenger; diff --git a/packages/kyc-controller/CHANGELOG.md b/packages/kyc-controller/CHANGELOG.md new file mode 100644 index 00000000000..90c4dac4c9c --- /dev/null +++ b/packages/kyc-controller/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Add optional `baseUrl` option to `KycService` constructor that overrides the base URL derived from `env`, enabling clients to target a custom (e.g. local or staging) KYC API ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Initial release of `KycController` and `KycService`, a shared, platform-agnostic KYC / identity-verification controller used across MetaMask clients ([#0000](https://github.com/MetaMask/core/pull/0000)) + - `KycController` (`BaseController`) owns the flow state machine, the Check/Auth frame message protocol, X25519 credential decryption, and SumSub orchestration via an injected `KycSumSubLauncher` adapter. + - `KycService` performs the Universal KYC (UKYC) HTTP calls via an injected `fetch`, sourcing the auth bearer token and geolocation through the messenger. + - Exposes a vendor-neutral, per-product surface (`ramps`, `card`) plus reselect selectors. + +[Unreleased]: https://github.com/MetaMask/core/ diff --git a/packages/kyc-controller/LICENSE b/packages/kyc-controller/LICENSE new file mode 100644 index 00000000000..9ec4f4514ea --- /dev/null +++ b/packages/kyc-controller/LICENSE @@ -0,0 +1,6 @@ +This project is licensed under either of + + * MIT license ([LICENSE.MIT](LICENSE.MIT)) + * Apache License, Version 2.0 ([LICENSE.APACHE2](LICENSE.APACHE2)) + +at your option. diff --git a/packages/kyc-controller/LICENSE.APACHE2 b/packages/kyc-controller/LICENSE.APACHE2 new file mode 100644 index 00000000000..e6e77b08909 --- /dev/null +++ b/packages/kyc-controller/LICENSE.APACHE2 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/kyc-controller/LICENSE.MIT b/packages/kyc-controller/LICENSE.MIT new file mode 100644 index 00000000000..fe29e78e0fe --- /dev/null +++ b/packages/kyc-controller/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 MetaMask + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/kyc-controller/README.md b/packages/kyc-controller/README.md new file mode 100644 index 00000000000..5cc5ccfcb25 --- /dev/null +++ b/packages/kyc-controller/README.md @@ -0,0 +1,23 @@ +# `@metamask/kyc-controller` + +Shared KYC / identity verification controller used across MetaMask clients + +## Installation + +`yarn add @metamask/kyc-controller` + +or + +`npm install @metamask/kyc-controller` + +## Development + +To rebuild the package automatically whenever you change a source file, run the `build:watch` script: + +`yarn workspace @metamask/kyc-controller run build:watch` + +This watches `src/**/*.ts` and re-runs the build on each change (it also performs an initial build on start), which is useful when developing against a client that consumes this package locally. + +## Contributing + +This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme). diff --git a/packages/kyc-controller/jest.config.js b/packages/kyc-controller/jest.config.js new file mode 100644 index 00000000000..ca084133399 --- /dev/null +++ b/packages/kyc-controller/jest.config.js @@ -0,0 +1,26 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +const merge = require('deepmerge'); +const path = require('path'); + +const baseConfig = require('../../jest.config.packages'); + +const displayName = path.basename(__dirname); + +module.exports = merge(baseConfig, { + // The display name when running multiple projects + displayName, + + // An object that configures minimum threshold enforcement for coverage results + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); diff --git a/packages/kyc-controller/package.json b/packages/kyc-controller/package.json new file mode 100644 index 00000000000..2c5b3b1d55d --- /dev/null +++ b/packages/kyc-controller/package.json @@ -0,0 +1,89 @@ +{ + "name": "@metamask/kyc-controller", + "version": "0.0.0", + "description": "Shared KYC / identity verification controller used across MetaMask clients", + "keywords": [ + "Ethereum", + "MetaMask" + ], + "homepage": "https://github.com/MetaMask/core/tree/main/packages/kyc-controller#readme", + "bugs": { + "url": "https://github.com/MetaMask/core/issues" + }, + "license": "(MIT OR Apache-2.0)", + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/core.git" + }, + "files": [ + "dist/" + ], + "sideEffects": false, + "main": "./dist/index.cjs", + "types": "./dist/index.d.cts", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "./package.json": "./package.json" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "scripts": { + "build": "ts-bridge --project tsconfig.build.json --verbose --clean --no-references", + "build:all": "ts-bridge --project tsconfig.build.json --verbose --clean", + "build:docs": "typedoc", + "build:watch": "chokidar 'src/**/*.ts' -c 'ts-bridge --project tsconfig.build.json --verbose --no-references' --initial", + "changelog:update": "../../scripts/update-changelog.sh @metamask/kyc-controller", + "changelog:validate": "../../scripts/validate-changelog.sh @metamask/kyc-controller", + "lint:tsconfigs": "tsx ../../scripts/lint-tsconfigs/lint-tsconfigs.mts", + "lint:tsconfigs:fix": "tsx ../../scripts/lint-tsconfigs/lint-tsconfigs.mts --fix", + "messenger-action-types:check": "tsx ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --check", + "messenger-action-types:generate": "tsx ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --generate", + "since-latest-release": "../../scripts/since-latest-release.sh", + "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter", + "test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache", + "test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose", + "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" + }, + "dependencies": { + "@metamask/base-controller": "^9.1.0", + "@metamask/controller-utils": "^12.3.0", + "@metamask/geolocation-controller": "^0.1.3", + "@metamask/messenger": "^2.0.0", + "@metamask/profile-sync-controller": "^28.3.0", + "@metamask/superstruct": "^3.1.0", + "@metamask/utils": "^11.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.9.2", + "@noble/hashes": "^1.8.0", + "@scure/base": "^1.2.6", + "reselect": "^5.1.1" + }, + "devDependencies": { + "@metamask/auto-changelog": "^6.1.0", + "@ts-bridge/cli": "^0.6.4", + "@types/jest": "^29.5.14", + "chokidar-cli": "^3.0.0", + "deepmerge": "^4.2.2", + "jest": "^29.7.0", + "nock": "^13.3.1", + "ts-jest": "^29.2.5", + "tsx": "^4.20.5", + "typedoc": "^0.25.13", + "typedoc-plugin-missing-exports": "^2.0.0", + "typescript": "~5.3.3" + }, + "engines": { + "node": "^18.18 || >=20" + } +} diff --git a/packages/kyc-controller/src/KycController-method-action-types.ts b/packages/kyc-controller/src/KycController-method-action-types.ts new file mode 100644 index 00000000000..a8610c695ad --- /dev/null +++ b/packages/kyc-controller/src/KycController-method-action-types.ts @@ -0,0 +1,160 @@ +/** + * This file is auto generated. + * Do not edit manually. + */ + +import type { KycController } from './KycController'; + +/** + * Resolves persisted terms + geolocation, and auto-creates a session when + * terms are already accepted and an email is available. + * + * @param params - Optional parameters. + * @param params.email - The account email to associate with the session. + */ +export type KycControllerInitializeAction = { + type: `KycController:initialize`; + handler: KycController['initialize']; +}; + +/** + * Loads the disclaimers for the resolved (or provided) country. + * + * @param params - Optional parameters. + * @param params.country - ISO 3166-1 alpha-3 country code override. + */ +export type KycControllerLoadDisclaimersAction = { + type: `KycController:loadDisclaimers`; + handler: KycController['loadDisclaimers']; +}; + +/** + * Captures terms acceptance for the currently loaded disclaimers and creates + * a session. + * + * @param params - Optional parameters. + * @param params.email - The account email to associate with the session. + */ +export type KycControllerAcceptTermsAndStartSessionAction = { + type: `KycController:acceptTermsAndStartSession`; + handler: KycController['acceptTermsAndStartSession']; +}; + +/** + * Clears the persisted terms acceptance. + */ +export type KycControllerClearSavedTermsAction = { + type: `KycController:clearSavedTerms`; + handler: KycController['clearSavedTerms']; +}; + +/** + * Handles a message posted by a Check/Auth frame and advances the flow. + * + * The transport-agnostic caller (WebView on mobile, iframe on web) forwards + * the raw message and injects the returned `reply` back into the frame. + * + * @param params - The parameters. + * @param params.message - The raw message posted by the frame. + * @returns An object whose optional `reply` should be posted back. + */ +export type KycControllerHandleFrameMessageAction = { + type: `KycController:handleFrameMessage`; + handler: KycController['handleFrameMessage']; +}; + +/** + * Builds the Check-frame URL, or `null` when no session exists yet. + * + * @returns The Check-frame URL or `null`. + */ +export type KycControllerBuildCheckFrameUrlAction = { + type: `KycController:buildCheckFrameUrl`; + handler: KycController['buildCheckFrameUrl']; +}; + +/** + * Builds the Auth-frame URL, or `null` when no client token is available. + * + * @returns The Auth-frame URL or `null`. + */ +export type KycControllerBuildAuthFrameUrlAction = { + type: `KycController:buildAuthFrameUrl`; + handler: KycController['buildAuthFrameUrl']; +}; + +/** + * Builds the Reset-frame URL. + * + * @returns The Reset-frame URL. + */ +export type KycControllerBuildResetFrameUrlAction = { + type: `KycController:buildResetFrameUrl`; + handler: KycController['buildResetFrameUrl']; +}; + +/** + * Checks whether KYC is required for a product and caches the result. + * + * @param params - The parameters. + * @param params.product - The consuming feature. + * @param params.country - Optional alpha-3 country override. + * @returns Whether KYC is required. + */ +export type KycControllerCheckKycRequiredAction = { + type: `KycController:checkKycRequired`; + handler: KycController['checkKycRequired']; +}; + +/** + * Reads the cached "is KYC required" result for a product. + * + * @param params - The parameters. + * @param params.product - The consuming feature. + * @returns The cached value, or `undefined` if not yet checked. + */ +export type KycControllerGetKycStatusAction = { + type: `KycController:getKycStatus`; + handler: KycController['getKycStatus']; +}; + +/** + * Runs the SumSub document-verification sub-flow: creates a UKYC session, + * exchanges the wrapped key for an applicant access token, and presents the + * SDK via the injected launcher. + * + * @param params - Optional parameters. + * @param params.locale - BCP-47 locale for the SDK UI. + * @param params.debug - Enables SDK debug logging. + * @returns The SDK result. + */ +export type KycControllerStartSumSubAction = { + type: `KycController:startSumSub`; + handler: KycController['startSumSub']; +}; + +/** + * Resets the flow to idle, clearing session tokens and sub-flow state while + * preserving persisted terms acceptance and the per-product cache. + */ +export type KycControllerResetAction = { + type: `KycController:reset`; + handler: KycController['reset']; +}; + +/** + * Union of all KycController action types. + */ +export type KycControllerMethodActions = + | KycControllerInitializeAction + | KycControllerLoadDisclaimersAction + | KycControllerAcceptTermsAndStartSessionAction + | KycControllerClearSavedTermsAction + | KycControllerHandleFrameMessageAction + | KycControllerBuildCheckFrameUrlAction + | KycControllerBuildAuthFrameUrlAction + | KycControllerBuildResetFrameUrlAction + | KycControllerCheckKycRequiredAction + | KycControllerGetKycStatusAction + | KycControllerStartSumSubAction + | KycControllerResetAction; diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts new file mode 100644 index 00000000000..5ab54f75aed --- /dev/null +++ b/packages/kyc-controller/src/KycController.test.ts @@ -0,0 +1,814 @@ +import { Messenger, MOCK_ANY_NAMESPACE } from '@metamask/messenger'; +import type { + MockAnyNamespace, + MessengerActions, + MessengerEvents, +} from '@metamask/messenger'; +import { gcm } from '@noble/ciphers/aes'; +import { x25519 } from '@noble/curves/ed25519'; +import { hkdf } from '@noble/hashes/hkdf'; +import { sha256 } from '@noble/hashes/sha2'; +import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils'; + +import { KycController } from './KycController'; +import type { KycControllerMessenger } from './KycController'; +import type { KycSumSubLauncher } from './types'; + +/** + * Builds an encrypted envelope for a recipient's X25519 public key. + * + * @param publicKey - The recipient's public key bytes. + * @param credentials - The plaintext credentials to encrypt. + * @returns The encrypted envelope. + */ +function makeEnvelope( + publicKey: Uint8Array, + credentials: Record, +): { ephemeralPublicKey: string; iv: string; ciphertext: string } { + const ephemeralPrivate = x25519.utils.randomSecretKey(); + const ephemeralPublic = x25519.getPublicKey(ephemeralPrivate); + const shared = x25519.getSharedSecret(ephemeralPrivate, publicKey); + const key = hkdf(sha256, shared, undefined, undefined, 32); + const iv = new Uint8Array(12).fill(7); + const ciphertext = gcm(key, iv).encrypt( + utf8ToBytes(JSON.stringify(credentials)), + ); + return { + ephemeralPublicKey: bytesToHex(ephemeralPublic), + iv: bytesToHex(iv), + ciphertext: bytesToHex(ciphertext), + }; +} + +/** + * Extracts the controller's ephemeral public key from the Check-frame URL and + * builds a decryptable credentials envelope for it. + * + * @param controller - The controller under test (must have a session token). + * @param credentials - The plaintext credentials to encrypt. + * @returns The encrypted envelope. + */ +function envelopeFor( + controller: KycController, + credentials: Record, +): { ephemeralPublicKey: string; iv: string; ciphertext: string } { + const url = controller.buildCheckFrameUrl(); + const publicKeyHex = new URL(url as string).searchParams.get( + 'publicKey', + ) as string; + return makeEnvelope(hexToBytes(publicKeyHex), credentials); +} + +describe('KycController', () => { + describe('constructor', () => { + it('accepts initial state merged over defaults', async () => { + await withController( + { options: { state: { phase: 'form' } } }, + ({ controller }) => { + expect(controller.state.phase).toBe('form'); + expect(controller.state.sumsub.status).toBe('idle'); + }, + ); + }); + }); + + describe('initialize', () => { + it('auto-creates a session when terms and email are present', async () => { + await withController( + { + options: { + state: { termsAcceptedAt: 't', acceptedDisclaimerIds: ['1'] }, + }, + }, + async ({ controller, handlers }) => { + handlers.getGeoCountry.mockResolvedValue('USA'); + handlers.createSession.mockResolvedValue({ sessionToken: 'sess' }); + + await controller.initialize({ email: 'a@b.co' }); + + expect(controller.state.geoCountry).toBe('USA'); + expect(controller.state.sessionToken).toBe('sess'); + expect(controller.state.phase).toBe('check'); + }, + ); + }); + + it('falls back to the terms phase and loads disclaimers when geo fails and no terms exist', async () => { + await withController(async ({ controller, handlers }) => { + handlers.getGeoCountry.mockRejectedValue(new Error('geo down')); + + await controller.initialize(); + + expect(controller.state.phase).toBe('terms'); + expect(controller.state.disclaimersError).toMatch(/Failed to load/u); + }); + }); + + it('stays on terms when terms exist but no email is available', async () => { + await withController( + { + options: { + state: { termsAcceptedAt: 't', acceptedDisclaimerIds: ['1'] }, + }, + }, + async ({ controller, handlers }) => { + handlers.getGeoCountry.mockResolvedValue('USA'); + handlers.fetchDisclaimers.mockResolvedValue([]); + + await controller.initialize(); + + expect(controller.state.phase).toBe('terms'); + }, + ); + }); + }); + + describe('loadDisclaimers', () => { + it('loads disclaimers for a provided country', async () => { + await withController(async ({ controller, handlers }) => { + const disclaimers = [{ id: '1', display_name: 'T', url: 'u' }]; + handlers.fetchDisclaimers.mockResolvedValue(disclaimers); + + await controller.loadDisclaimers({ country: 'USA' }); + + expect(controller.state.disclaimers).toStrictEqual(disclaimers); + expect(handlers.getGeoCountry).not.toHaveBeenCalled(); + }); + }); + + it('uses the cached geoCountry when no country is provided', async () => { + await withController( + { options: { state: { geoCountry: 'USA' } } }, + async ({ controller, handlers }) => { + handlers.fetchDisclaimers.mockResolvedValue([]); + + await controller.loadDisclaimers(); + + expect(handlers.getGeoCountry).not.toHaveBeenCalled(); + expect(handlers.fetchDisclaimers).toHaveBeenCalledWith({ + country: 'USA', + }); + }, + ); + }); + + it('resolves the country when neither param nor cache is available', async () => { + await withController(async ({ controller, handlers }) => { + handlers.getGeoCountry.mockResolvedValue('FRA'); + handlers.fetchDisclaimers.mockResolvedValue([]); + + await controller.loadDisclaimers(); + + expect(controller.state.geoCountry).toBe('FRA'); + expect(handlers.fetchDisclaimers).toHaveBeenCalledWith({ + country: 'FRA', + }); + }); + }); + + it('records an error when loading fails', async () => { + await withController(async ({ controller, handlers }) => { + handlers.fetchDisclaimers.mockRejectedValue(new Error('boom')); + + await controller.loadDisclaimers({ country: 'USA' }); + + expect(controller.state.disclaimersError).toMatch(/boom/u); + }); + }); + }); + + describe('acceptTermsAndStartSession', () => { + it('captures terms and creates a session', async () => { + await withController( + { + options: { + state: { disclaimers: [{ id: '1', display_name: 'T', url: 'u' }] }, + }, + }, + async ({ controller, handlers }) => { + handlers.createSession.mockResolvedValue({ sessionToken: 'sess' }); + + await controller.acceptTermsAndStartSession({ email: 'a@b.co' }); + + expect(controller.state.acceptedDisclaimerIds).toStrictEqual(['1']); + expect(controller.state.termsAcceptedAt).not.toBeNull(); + expect(controller.state.phase).toBe('check'); + }, + ); + }); + + it('reverts to terms when session creation fails', async () => { + await withController( + { + options: { + state: { + email: 'a@b.co', + disclaimers: [{ id: '1', display_name: 'T', url: 'u' }], + }, + }, + }, + async ({ controller, handlers }) => { + handlers.createSession.mockRejectedValue(new Error('nope')); + handlers.fetchDisclaimers.mockResolvedValue([]); + + await controller.acceptTermsAndStartSession(); + + expect(controller.state.phase).toBe('terms'); + expect(controller.state.termsAcceptedAt).toBeNull(); + expect(controller.state.error).toMatch(/Session creation failed/u); + }, + ); + }); + + it('fails when no email is available', async () => { + await withController( + { + options: { + state: { disclaimers: [{ id: '1', display_name: 'T', url: 'u' }] }, + }, + }, + async ({ controller }) => { + await controller.acceptTermsAndStartSession(); + + expect(controller.state.phase).toBe('error'); + expect(controller.state.error).toMatch(/Missing email/u); + }, + ); + }); + + it('fails when no disclaimers were accepted', async () => { + await withController(async ({ controller }) => { + await controller.acceptTermsAndStartSession({ email: 'a@b.co' }); + + expect(controller.state.phase).toBe('error'); + expect(controller.state.error).toMatch(/Missing terms acceptance/u); + }); + }); + }); + + describe('clearSavedTerms', () => { + it('clears persisted terms', async () => { + await withController( + { + options: { + state: { termsAcceptedAt: 't', acceptedDisclaimerIds: ['1'] }, + }, + }, + ({ controller }) => { + controller.clearSavedTerms(); + expect(controller.state.termsAcceptedAt).toBeNull(); + expect(controller.state.acceptedDisclaimerIds).toStrictEqual([]); + }, + ); + }); + }); + + describe('handleFrameMessage', () => { + it('acks a handshake', async () => { + await withController(async ({ controller }) => { + const result = await controller.handleFrameMessage({ + message: { kind: 'handshake', meta: { channelId: 'ch_1' } }, + }); + expect(result).toStrictEqual({ + reply: { version: 2, meta: { channelId: 'ch_1' }, kind: 'ack' }, + }); + }); + }); + + it('ignores undefined and non-complete messages', async () => { + await withController(async ({ controller }) => { + expect( + await controller.handleFrameMessage({ message: undefined }), + ).toStrictEqual({}); + expect( + await controller.handleFrameMessage({ message: { kind: 'other' } }), + ).toStrictEqual({}); + }); + }); + + it('captures the customer id and ignores a status-less complete message', async () => { + await withController(async ({ controller }) => { + const result = await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { customer: { id: 'cust-1' } }, + }, + }); + expect(result).toStrictEqual({}); + expect(controller.state.moonpayCustomerId).toBe('cust-1'); + }); + }); + + it('ignores messages on an unknown channel', async () => { + await withController(async ({ controller }) => { + const result = await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_unknown' }, + payload: { status: 'active' }, + }, + }); + expect(result).toStrictEqual({}); + }); + }); + + it('fails when credential decryption throws', async () => { + await withController( + { options: { state: { sessionToken: 'tok' } } }, + async ({ controller }) => { + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'active', credentials: 'not-decryptable' }, + }, + }); + expect(controller.state.phase).toBe('error'); + expect(controller.state.error).toMatch(/Failed to decrypt/u); + }, + ); + }); + + describe('check frame', () => { + it('moves to form on an active status with an access token', async () => { + await withController( + { options: { state: { sessionToken: 'tok' } } }, + async ({ controller }) => { + const envelope = envelopeFor(controller, { + accessToken: 'access-1', + }); + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'active', credentials: envelope }, + }, + }); + expect(controller.state.phase).toBe('form'); + expect(controller.state.accessToken).toBe('access-1'); + }, + ); + }); + + it('moves to auth on connectionRequired and enables the auth frame URL', async () => { + await withController( + { options: { state: { sessionToken: 'tok' } } }, + async ({ controller }) => { + const envelope = envelopeFor(controller, { + clientToken: 'client-1', + }); + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { + status: 'connectionRequired', + credentials: envelope, + }, + }, + }); + expect(controller.state.phase).toBe('auth'); + expect(controller.buildAuthFrameUrl()).toContain( + 'clientToken=client-1', + ); + }, + ); + }); + + it('requires re-acceptance on termsAcceptanceRequired', async () => { + await withController( + { + options: { + state: { + sessionToken: 'tok', + termsAcceptedAt: 't', + acceptedDisclaimerIds: ['1'], + }, + }, + }, + async ({ controller }) => { + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'termsAcceptanceRequired' }, + }, + }); + expect(controller.state.phase).toBe('terms'); + expect(controller.state.termsAcceptedAt).toBeNull(); + }, + ); + }); + + it('fails on an unexpected status', async () => { + await withController( + { options: { state: { sessionToken: 'tok' } } }, + async ({ controller }) => { + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'failed' }, + }, + }); + expect(controller.state.phase).toBe('error'); + }, + ); + }); + }); + + describe('auth frame', () => { + it('moves to form on an active status with an access token', async () => { + await withController( + { options: { state: { sessionToken: 'tok' } } }, + async ({ controller }) => { + const envelope = envelopeFor(controller, { + accessToken: 'access-2', + }); + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_2' }, + payload: { status: 'active', credentials: envelope }, + }, + }); + expect(controller.state.phase).toBe('form'); + expect(controller.state.accessToken).toBe('access-2'); + }, + ); + }); + + it('requires re-acceptance on termsAcceptanceRequired', async () => { + await withController(async ({ controller }) => { + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_2' }, + payload: { status: 'termsAcceptanceRequired' }, + }, + }); + expect(controller.state.phase).toBe('terms'); + }); + }); + + it('fails on an unexpected status', async () => { + await withController(async ({ controller }) => { + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_2' }, + payload: { status: 'unavailable' }, + }, + }); + expect(controller.state.phase).toBe('error'); + }); + }); + }); + }); + + describe('frame URL builders', () => { + it('returns null for the check frame without a session', async () => { + await withController(({ controller }) => { + expect(controller.buildCheckFrameUrl()).toBeNull(); + }); + }); + + it('builds the check frame URL with a session', async () => { + await withController( + { options: { state: { sessionToken: 'tok' } } }, + ({ controller }) => { + const url = controller.buildCheckFrameUrl() as string; + expect(url).toContain('sessionToken=tok'); + expect(url).toContain('channelId=ch_1'); + expect(url).toContain('skipKyc=true'); + }, + ); + }); + + it('returns null for the auth frame without a client token', async () => { + await withController(({ controller }) => { + expect(controller.buildAuthFrameUrl()).toBeNull(); + }); + }); + + it('builds the reset frame URL', async () => { + await withController(({ controller }) => { + expect(controller.buildResetFrameUrl()).toContain('channelId=ch_reset'); + }); + }); + }); + + describe('checkKycRequired', () => { + it('fails without an access token', async () => { + await withController(async ({ controller }) => { + expect(await controller.checkKycRequired({ product: 'ramps' })).toBe( + false, + ); + expect(controller.state.error).toMatch(/Missing accessToken/u); + }); + }); + + it('fails without a country', async () => { + await withController( + { options: { state: { accessToken: 'a' } } }, + async ({ controller }) => { + expect(await controller.checkKycRequired({ product: 'ramps' })).toBe( + false, + ); + expect(controller.state.error).toMatch(/Missing country/u); + }, + ); + }); + + it('caches the result on success (cached country)', async () => { + await withController( + { options: { state: { accessToken: 'a', geoCountry: 'USA' } } }, + async ({ controller, handlers }) => { + handlers.checkKycRequired.mockResolvedValue({ kycRequired: true }); + + expect(await controller.checkKycRequired({ product: 'ramps' })).toBe( + true, + ); + expect(controller.state.kycRequiredByProduct.ramps).toBe(true); + expect(controller.state.phase).toBe('done'); + }, + ); + }); + + it('accepts a country override', async () => { + await withController( + { options: { state: { accessToken: 'a' } } }, + async ({ controller, handlers }) => { + handlers.checkKycRequired.mockResolvedValue({ kycRequired: false }); + + await controller.checkKycRequired({ + product: 'card', + country: 'FRA', + }); + + expect(handlers.checkKycRequired).toHaveBeenCalledWith({ + accessToken: 'a', + country: 'FRA', + capabilities: [{ product: 'card' }], + }); + }, + ); + }); + + it('fails when the service throws', async () => { + await withController( + { options: { state: { accessToken: 'a', geoCountry: 'USA' } } }, + async ({ controller, handlers }) => { + handlers.checkKycRequired.mockRejectedValue(new Error('down')); + + expect(await controller.checkKycRequired({ product: 'ramps' })).toBe( + false, + ); + expect(controller.state.error).toMatch(/KYC check failed/u); + }, + ); + }); + }); + + describe('getKycStatus', () => { + it('returns the cached value or undefined', async () => { + await withController( + { options: { state: { kycRequiredByProduct: { ramps: true } } } }, + ({ controller }) => { + expect(controller.getKycStatus({ product: 'ramps' })).toBe(true); + expect(controller.getKycStatus({ product: 'card' })).toBeUndefined(); + }, + ); + }); + }); + + describe('startSumSub', () => { + it('throws and marks failed when the SDK is unavailable', async () => { + await withController(async ({ controller, launcher }) => { + launcher.isAvailable.mockReturnValue(false); + + await expect(controller.startSumSub()).rejects.toThrow( + /not available/u, + ); + expect(controller.state.sumsub.status).toBe('failed'); + }); + }); + + it('runs the full sub-flow and completes', async () => { + await withController(async ({ controller, handlers, launcher }) => { + handlers.createUkycSession.mockResolvedValue({ + sessionId: 'sid', + wrappingPublicKey: 'wpk', + idosSessionId: 'idos', + }); + handlers.submitWrappedKey.mockResolvedValue({ + status: 'ok', + applicantAccessToken: 'aat', + }); + launcher.launch.mockImplementation( + async ({ onStatusChange, onTokenExpiration }) => { + onStatusChange?.('idle', 'InProgress'); + onStatusChange?.('InProgress', 'Completed'); + await onTokenExpiration(); + return { ok: true }; + }, + ); + + const result = await controller.startSumSub({ + locale: 'fr', + debug: true, + }); + + expect(result).toStrictEqual({ ok: true }); + expect(controller.state.sumsub.status).toBe('complete'); + expect(controller.state.sumsub.applicantAccessToken).toBe('aat'); + // onTokenExpiration re-invokes the exchange. + expect(handlers.submitWrappedKey).toHaveBeenCalledTimes(2); + }); + }); + + it('defaults locale and debug when no params are given', async () => { + await withController(async ({ controller, launcher }) => { + await controller.startSumSub(); + + expect(launcher.launch).toHaveBeenCalledWith( + expect.objectContaining({ locale: 'en', debug: false }), + ); + expect(controller.state.sumsub.status).toBe('complete'); + }); + }); + + it('marks failed and returns the error when a step throws', async () => { + await withController(async ({ controller, handlers }) => { + handlers.createUkycSession.mockRejectedValue(new Error('ukyc down')); + + const result = await controller.startSumSub(); + + expect(result).toMatchObject({ + error: expect.stringContaining('ukyc down'), + }); + expect(controller.state.sumsub.status).toBe('failed'); + }); + }); + }); + + describe('reset', () => { + it('clears session state but preserves persisted terms', async () => { + await withController( + { + options: { + state: { + phase: 'form', + sessionToken: 'tok', + accessToken: 'a', + termsAcceptedAt: 't', + acceptedDisclaimerIds: ['1'], + kycRequiredByProduct: { ramps: true }, + }, + }, + }, + ({ controller }) => { + controller.reset(); + expect(controller.state.phase).toBe('idle'); + expect(controller.state.sessionToken).toBeNull(); + expect(controller.state.accessToken).toBeNull(); + expect(controller.state.termsAcceptedAt).toBe('t'); + expect(controller.state.kycRequiredByProduct.ramps).toBe(true); + }, + ); + }); + }); + + describe('messenger actions', () => { + it('exposes methods as messenger actions', async () => { + await withController(({ rootMessenger }) => { + expect( + rootMessenger.call('KycController:buildResetFrameUrl'), + ).toContain('ch_reset'); + }); + }); + }); +}); + +type RootMessenger = Messenger< + MockAnyNamespace, + MessengerActions, + MessengerEvents +>; + +type ServiceHandlers = { + getGeoCountry: jest.Mock; + fetchDisclaimers: jest.Mock; + createSession: jest.Mock; + checkKycRequired: jest.Mock; + createUkycSession: jest.Mock; + submitWrappedKey: jest.Mock; +}; + +type Launcher = { + isAvailable: jest.Mock; + launch: jest.Mock; +}; + +type WithControllerCallback = (payload: { + controller: KycController; + rootMessenger: RootMessenger; + handlers: ServiceHandlers; + launcher: Launcher; +}) => Promise | ReturnValue; + +type WithControllerOptions = { + options: Partial[0]>; +}; + +const SERVICE_ACTIONS = [ + 'KycService:getGeoCountry', + 'KycService:fetchDisclaimers', + 'KycService:createSession', + 'KycService:checkKycRequired', + 'KycService:createUkycSession', + 'KycService:submitWrappedKey', +] as const; + +/** + * Wraps a test with a fully-wired controller, mocked service handlers, and a + * mocked SumSub launcher. + * + * @param args - Either a callback, or an options bag and a callback. + * @returns The callback's return value. + */ +function withController( + ...args: + | [WithControllerCallback] + | [WithControllerOptions, WithControllerCallback] +): ReturnValue | Promise { + const [{ options = {} }, testFunction] = + args.length === 2 ? args : [{}, args[0]]; + + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + captureException: jest.fn(), + }); + const messenger: KycControllerMessenger = new Messenger({ + namespace: 'KycController', + parent: rootMessenger, + }); + rootMessenger.delegate({ + actions: SERVICE_ACTIONS, + events: [], + messenger, + }); + + const handlers: ServiceHandlers = { + getGeoCountry: jest.fn().mockResolvedValue('USA'), + fetchDisclaimers: jest.fn().mockResolvedValue([]), + createSession: jest.fn().mockResolvedValue({ sessionToken: 'sess' }), + checkKycRequired: jest.fn().mockResolvedValue({ kycRequired: false }), + createUkycSession: jest.fn().mockResolvedValue({ + sessionId: 'sid', + wrappingPublicKey: 'wpk', + idosSessionId: 'idos', + }), + submitWrappedKey: jest + .fn() + .mockResolvedValue({ status: 'ok', applicantAccessToken: 'aat' }), + }; + rootMessenger.registerActionHandler( + 'KycService:getGeoCountry', + handlers.getGeoCountry, + ); + rootMessenger.registerActionHandler( + 'KycService:fetchDisclaimers', + handlers.fetchDisclaimers, + ); + rootMessenger.registerActionHandler( + 'KycService:createSession', + handlers.createSession, + ); + rootMessenger.registerActionHandler( + 'KycService:checkKycRequired', + handlers.checkKycRequired, + ); + rootMessenger.registerActionHandler( + 'KycService:createUkycSession', + handlers.createUkycSession, + ); + rootMessenger.registerActionHandler( + 'KycService:submitWrappedKey', + handlers.submitWrappedKey, + ); + + const launcher: Launcher = { + isAvailable: jest.fn().mockReturnValue(true), + launch: jest.fn().mockResolvedValue({ ok: true }), + }; + + const controller = new KycController({ + messenger, + sumsubLauncher: launcher as unknown as KycSumSubLauncher, + ...options, + }); + + return testFunction({ controller, rootMessenger, handlers, launcher }); +} diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts new file mode 100644 index 00000000000..55b59d6a2ac --- /dev/null +++ b/packages/kyc-controller/src/KycController.ts @@ -0,0 +1,855 @@ +import type { + ControllerGetStateAction, + ControllerStateChangeEvent, + StateMetadata, +} from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller'; +import type { Messenger } from '@metamask/messenger'; +import type { Json } from '@metamask/utils'; + +import { decryptCredentials, generateKeyPair } from './crypto'; +import type { EncryptedCredentialsEnvelope, X25519KeyPair } from './crypto'; +import type { KycControllerMethodActions } from './KycController-method-action-types'; +import type { KycServiceMethodActions } from './KycService-method-action-types'; +import type { + KycDisclaimer, + KycPhase, + KycProduct, + KycSumSubLauncher, + KycSumSubStatus, +} from './types'; + +// === GENERAL === + +export const controllerName = 'KycController'; + +const FRAMES_BASE_URL = 'https://blocks.moonpay.com/platform/v1'; +const CHANNEL_CHECK = 'ch_1'; +const CHANNEL_AUTH = 'ch_2'; +const CHANNEL_RESET = 'ch_reset'; + +// Placeholder credentials for the SumSub sub-flow. These are demo values that +// must be replaced with real UKYC-issued material before production use. +const MOCK_JWT_TOKEN = 'mock-jwt-token'; + +// === STATE === + +/** + * Describes the shape of the state object for {@link KycController}. + */ +export type KycControllerState = { + /** Current phase of the identity flow. */ + phase: KycPhase; + /** Human-readable status message for the current phase. */ + statusMessage: string; + /** The current error message, or `null`. */ + error: string | null; + + /** Email associated with the session (sourced from the account). */ + email: string | null; + + /** ISO-8601 timestamp of the customer's terms acceptance (persisted). */ + termsAcceptedAt: string | null; + /** IDs of the disclaimers the customer accepted (persisted). */ + acceptedDisclaimerIds: string[]; + + /** Disclaimers fetched for the current country. */ + disclaimers: KycDisclaimer[]; + /** Error encountered while loading disclaimers, or `null`. */ + disclaimersError: string | null; + + /** Resolved ISO 3166-1 alpha-3 country code. */ + geoCountry: string | null; + + /** Vendor session token (not persisted, not logged). */ + sessionToken: string | null; + /** Vendor access token (not persisted, not logged). */ + accessToken: string | null; + /** Vendor customer id, used for the SumSub hand-off. */ + moonpayCustomerId: string | null; + + /** Cached "is KYC required" result per product (persisted). */ + kycRequiredByProduct: Partial>; + /** ISO-8601 timestamp of the last KYC-required check (persisted). */ + lastCheckedAt: string | null; + + /** SumSub document-verification sub-flow state. */ + sumsub: { + status: KycSumSubStatus; + result: Json | null; + sessionId: string | null; + applicantAccessToken: string | null; + }; +}; + +const kycControllerMetadata = { + phase: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: false, + usedInUi: true, + }, + statusMessage: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: false, + usedInUi: true, + }, + error: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: false, + usedInUi: true, + }, + email: { + includeInDebugSnapshot: false, + includeInStateLogs: false, + persist: false, + usedInUi: false, + }, + termsAcceptedAt: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: true, + usedInUi: false, + }, + acceptedDisclaimerIds: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: true, + usedInUi: false, + }, + disclaimers: { + includeInDebugSnapshot: false, + includeInStateLogs: false, + persist: false, + usedInUi: true, + }, + disclaimersError: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: false, + usedInUi: true, + }, + geoCountry: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: false, + usedInUi: true, + }, + sessionToken: { + includeInDebugSnapshot: false, + includeInStateLogs: false, + persist: false, + usedInUi: false, + }, + accessToken: { + includeInDebugSnapshot: false, + includeInStateLogs: false, + persist: false, + usedInUi: false, + }, + moonpayCustomerId: { + includeInDebugSnapshot: false, + includeInStateLogs: false, + persist: false, + usedInUi: false, + }, + kycRequiredByProduct: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: true, + usedInUi: true, + }, + lastCheckedAt: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: true, + usedInUi: false, + }, + sumsub: { + includeInDebugSnapshot: false, + includeInStateLogs: false, + persist: false, + usedInUi: true, + }, +} satisfies StateMetadata; + +/** + * Constructs the default {@link KycController} state. + * + * @returns The default state. + */ +export function getDefaultKycControllerState(): KycControllerState { + return { + phase: 'idle', + statusMessage: '', + error: null, + email: null, + termsAcceptedAt: null, + acceptedDisclaimerIds: [], + disclaimers: [], + disclaimersError: null, + geoCountry: null, + sessionToken: null, + accessToken: null, + moonpayCustomerId: null, + kycRequiredByProduct: {}, + lastCheckedAt: null, + sumsub: { + status: 'idle', + result: null, + sessionId: null, + applicantAccessToken: null, + }, + }; +} + +// === MESSENGER === + +const MESSENGER_EXPOSED_METHODS = [ + 'initialize', + 'loadDisclaimers', + 'acceptTermsAndStartSession', + 'clearSavedTerms', + 'handleFrameMessage', + 'buildCheckFrameUrl', + 'buildAuthFrameUrl', + 'buildResetFrameUrl', + 'checkKycRequired', + 'getKycStatus', + 'startSumSub', + 'reset', +] as const; + +export type KycControllerGetStateAction = ControllerGetStateAction< + typeof controllerName, + KycControllerState +>; + +export type KycControllerActions = + | KycControllerGetStateAction + | KycControllerMethodActions; + +type AllowedActions = KycServiceMethodActions; + +export type KycControllerStateChangeEvent = ControllerStateChangeEvent< + typeof controllerName, + KycControllerState +>; + +export type KycControllerEvents = KycControllerStateChangeEvent; + +type AllowedEvents = never; + +export type KycControllerMessenger = Messenger< + typeof controllerName, + KycControllerActions | AllowedActions, + KycControllerEvents | AllowedEvents +>; + +/** + * Options for constructing a {@link KycController}. + */ +export type KycControllerOptions = { + messenger: KycControllerMessenger; + state?: Partial; + /** + * Platform adapter that presents the SumSub SDK. Injected by each client so + * the controller stays platform-agnostic. + */ + sumsubLauncher: KycSumSubLauncher; +}; + +/** + * The shape of a message posted by a Check/Auth frame. + */ +type FrameMessage = { + meta?: { channelId?: string }; + kind?: string; + payload?: { + status?: + | 'active' + | 'connectionRequired' + | 'termsAcceptanceRequired' + | 'pending' + | 'unavailable' + | 'failed'; + credentials?: EncryptedCredentialsEnvelope | string; + customer?: { id?: string }; + }; +}; + +// === CONTROLLER DEFINITION === + +/** + * `KycController` orchestrates the vendor-backed KYC / identity-verification + * flow (MoonPay identity + SumSub documents) behind a vendor-neutral, per + * product surface used by ramps and card. It owns all state, HTTP + * orchestration (via `KycService`), crypto, and the frame message protocol; + * platform-specific presentation (WebView/iframe, SumSub SDK) is injected. + */ +export class KycController extends BaseController< + typeof controllerName, + KycControllerState, + KycControllerMessenger +> { + readonly #sumsubLauncher: KycSumSubLauncher; + + /** Ephemeral X25519 keypair for the frame key exchange (never persisted). */ + readonly #keypair: X25519KeyPair; + + /** Auth-frame client token, kept out of state. */ + #authClientToken: string | null = null; + + /** + * Constructs a new {@link KycController}. + * + * @param options - The constructor options. + * @param options.messenger - The messenger suited for this controller. + * @param options.state - Partial initial state; merged over defaults. + * @param options.sumsubLauncher - The platform SumSub launcher adapter. + */ + constructor({ messenger, state, sumsubLauncher }: KycControllerOptions) { + super({ + messenger, + metadata: kycControllerMetadata, + name: controllerName, + state: { ...getDefaultKycControllerState(), ...state }, + }); + + this.#sumsubLauncher = sumsubLauncher; + this.#keypair = generateKeyPair(); + + this.messenger.registerMethodActionHandlers( + this, + MESSENGER_EXPOSED_METHODS, + ); + } + + /** + * Resolves persisted terms + geolocation, and auto-creates a session when + * terms are already accepted and an email is available. + * + * @param params - Optional parameters. + * @param params.email - The account email to associate with the session. + */ + async initialize(params?: { email?: string }): Promise { + if (params?.email) { + this.update((state) => { + state.email = params.email as string; + }); + } + + // Resolve country for display; non-blocking. + try { + const country = await this.messenger.call('KycService:getGeoCountry'); + this.update((state) => { + state.geoCountry = country; + }); + } catch { + // Ignore; disclaimers loading will surface a country error if needed. + } + + const hasTerms = + Boolean(this.state.termsAcceptedAt) && + this.state.acceptedDisclaimerIds.length > 0; + + if (hasTerms && this.state.email) { + await this.#createSession(); + return; + } + + this.update((state) => { + state.phase = 'terms'; + }); + await this.loadDisclaimers(); + } + + /** + * Loads the disclaimers for the resolved (or provided) country. + * + * @param params - Optional parameters. + * @param params.country - ISO 3166-1 alpha-3 country code override. + */ + async loadDisclaimers(params?: { country?: string }): Promise { + try { + const cachedCountry = params?.country ?? this.state.geoCountry; + const country = + cachedCountry ?? + (await this.messenger.call('KycService:getGeoCountry')); + if (!cachedCountry) { + this.update((state) => { + state.geoCountry = country; + }); + } + const disclaimers = await this.messenger.call( + 'KycService:fetchDisclaimers', + { country }, + ); + this.update((state) => { + state.disclaimers = disclaimers; + state.disclaimersError = null; + }); + } catch (error) { + this.update((state) => { + state.disclaimersError = `Failed to load disclaimers: ${String(error)}`; + }); + } + } + + /** + * Captures terms acceptance for the currently loaded disclaimers and creates + * a session. + * + * @param params - Optional parameters. + * @param params.email - The account email to associate with the session. + */ + async acceptTermsAndStartSession(params?: { email?: string }): Promise { + const termsAcceptedAt = new Date().toISOString(); + const disclaimerIds = this.state.disclaimers.map( + (disclaimer) => disclaimer.id, + ); + this.update((state) => { + if (params?.email) { + state.email = params.email; + } + state.termsAcceptedAt = termsAcceptedAt; + state.acceptedDisclaimerIds = disclaimerIds; + }); + await this.#createSession(); + } + + /** + * Creates a vendor session from the currently stored terms + email. + */ + async #createSession(): Promise { + const { email, termsAcceptedAt, acceptedDisclaimerIds } = this.state; + if (!email) { + this.#fail('Missing email for session creation.'); + return; + } + if (!termsAcceptedAt || acceptedDisclaimerIds.length === 0) { + this.#fail('Missing terms acceptance for session creation.'); + return; + } + + this.update((state) => { + state.error = null; + state.phase = 'session'; + state.statusMessage = 'Creating session...'; + }); + + try { + const { sessionToken } = await this.messenger.call( + 'KycService:createSession', + { email, termsAcceptedAt, disclaimerIds: acceptedDisclaimerIds }, + ); + this.update((state) => { + state.sessionToken = sessionToken; + state.phase = 'check'; + state.statusMessage = 'Authenticating via Check frame...'; + }); + } catch (error) { + // Invalidate the stored acceptance so the customer can retry. + this.update((state) => { + state.termsAcceptedAt = null; + state.acceptedDisclaimerIds = []; + state.error = `Session creation failed: ${String(error)}`; + state.statusMessage = + 'Session creation failed — accept the terms to try again.'; + state.phase = 'terms'; + }); + await this.loadDisclaimers(); + } + } + + /** + * Clears the persisted terms acceptance. + */ + clearSavedTerms(): void { + this.update((state) => { + state.termsAcceptedAt = null; + state.acceptedDisclaimerIds = []; + }); + } + + /** + * Handles a message posted by a Check/Auth frame and advances the flow. + * + * The transport-agnostic caller (WebView on mobile, iframe on web) forwards + * the raw message and injects the returned `reply` back into the frame. + * + * @param params - The parameters. + * @param params.message - The raw message posted by the frame. + * @returns An object whose optional `reply` should be posted back. + */ + async handleFrameMessage(params: { + message: unknown; + }): Promise<{ reply?: unknown }> { + const payload = params.message as FrameMessage | undefined; + + if (!payload) { + return {}; + } + + if (payload.kind === 'handshake') { + const channelId = payload.meta?.channelId; + return { reply: { version: 2, meta: { channelId }, kind: 'ack' } }; + } + + if (payload.kind !== 'complete') { + return {}; + } + + const channelId = payload.meta?.channelId; + const status = payload.payload?.status; + const credsEnvelope = payload.payload?.credentials; + + const customerId = payload.payload?.customer?.id ?? null; + if (customerId) { + this.update((state) => { + state.moonpayCustomerId = customerId; + }); + } + + if (!status) { + return {}; + } + + let accessToken: string | undefined; + let clientToken: string | undefined; + if (credsEnvelope) { + try { + const { credentials } = decryptCredentials( + credsEnvelope, + this.#keypair.privateKey, + ); + accessToken = credentials.accessToken; + clientToken = credentials.clientToken; + } catch (error) { + this.#fail(`Failed to decrypt frame credentials: ${String(error)}`); + return {}; + } + } + + if (channelId === CHANNEL_CHECK) { + this.#handleCheckOutcome(status, accessToken, clientToken); + return {}; + } + + if (channelId === CHANNEL_AUTH) { + this.#handleAuthOutcome(status, accessToken); + return {}; + } + + return {}; + } + + /** + * Applies a Check-frame outcome. + * + * @param status - The frame status. + * @param accessToken - The decrypted access token, if any. + * @param clientToken - The decrypted client token, if any. + */ + #handleCheckOutcome( + status: NonNullable['status'], + accessToken?: string, + clientToken?: string, + ): void { + if (status === 'active' && accessToken) { + this.update((state) => { + state.accessToken = accessToken; + state.phase = 'form'; + state.statusMessage = 'Already authenticated. Review to submit.'; + }); + return; + } + if (status === 'connectionRequired' && clientToken) { + this.#authClientToken = clientToken; + this.update((state) => { + state.phase = 'auth'; + state.statusMessage = 'Verify your email via OTP in the Auth frame.'; + }); + return; + } + if (status === 'termsAcceptanceRequired') { + this.#requireTermsReacceptance(); + return; + } + this.#fail(`Check frame returned status: ${status}`); + } + + /** + * Applies an Auth-frame outcome. + * + * @param status - The frame status. + * @param accessToken - The decrypted access token, if any. + */ + #handleAuthOutcome( + status: NonNullable['status'], + accessToken?: string, + ): void { + if (status === 'active' && accessToken) { + this.update((state) => { + state.accessToken = accessToken; + state.phase = 'form'; + state.statusMessage = 'Authenticated. Review to submit.'; + }); + return; + } + if (status === 'termsAcceptanceRequired') { + this.#requireTermsReacceptance(); + return; + } + this.#fail(`Auth frame returned status: ${status}`); + } + + /** + * Invalidates stored terms and returns to the terms phase. + */ + #requireTermsReacceptance(): void { + this.update((state) => { + state.termsAcceptedAt = null; + state.acceptedDisclaimerIds = []; + state.phase = 'terms'; + state.statusMessage = + 'The vendor updated its Terms of Use — please re-accept.'; + }); + } + + /** + * Builds the Check-frame URL, or `null` when no session exists yet. + * + * @returns The Check-frame URL or `null`. + */ + buildCheckFrameUrl(): string | null { + if (!this.state.sessionToken) { + return null; + } + const url = new URL(`${FRAMES_BASE_URL}/check-connection`); + url.searchParams.set('sessionToken', this.state.sessionToken); + url.searchParams.set('publicKey', this.#keypair.publicKeyHex); + url.searchParams.set('channelId', CHANNEL_CHECK); + url.searchParams.set('skipKyc', 'true'); + return url.toString(); + } + + /** + * Builds the Auth-frame URL, or `null` when no client token is available. + * + * @returns The Auth-frame URL or `null`. + */ + buildAuthFrameUrl(): string | null { + if (!this.#authClientToken) { + return null; + } + const url = new URL(`${FRAMES_BASE_URL}/auth`); + url.searchParams.set('clientToken', this.#authClientToken); + url.searchParams.set('publicKey', this.#keypair.publicKeyHex); + url.searchParams.set('channelId', CHANNEL_AUTH); + return url.toString(); + } + + /** + * Builds the Reset-frame URL. + * + * @returns The Reset-frame URL. + */ + buildResetFrameUrl(): string { + const url = new URL(`${FRAMES_BASE_URL}/reset`); + url.searchParams.set('channelId', CHANNEL_RESET); + return url.toString(); + } + + /** + * Checks whether KYC is required for a product and caches the result. + * + * @param params - The parameters. + * @param params.product - The consuming feature. + * @param params.country - Optional alpha-3 country override. + * @returns Whether KYC is required. + */ + async checkKycRequired(params: { + product: KycProduct; + country?: string; + }): Promise { + const { accessToken } = this.state; + if (!accessToken) { + this.#fail('Missing accessToken — repeat the authentication step.'); + return false; + } + const country = params.country ?? this.state.geoCountry; + if (!country) { + this.#fail('Missing country for KYC-required check.'); + return false; + } + + this.update((state) => { + state.phase = 'submit'; + state.statusMessage = 'Checking KYC status...'; + }); + + try { + const { kycRequired } = await this.messenger.call( + 'KycService:checkKycRequired', + { accessToken, country, capabilities: [{ product: params.product }] }, + ); + this.update((state) => { + state.kycRequiredByProduct[params.product] = kycRequired; + state.lastCheckedAt = new Date().toISOString(); + state.phase = 'done'; + state.statusMessage = 'KYC check complete.'; + }); + return kycRequired; + } catch (error) { + this.#fail(`KYC check failed: ${String(error)}`); + return false; + } + } + + /** + * Reads the cached "is KYC required" result for a product. + * + * @param params - The parameters. + * @param params.product - The consuming feature. + * @returns The cached value, or `undefined` if not yet checked. + */ + getKycStatus(params: { product: KycProduct }): boolean | undefined { + return this.state.kycRequiredByProduct[params.product]; + } + + /** + * Runs the SumSub document-verification sub-flow: creates a UKYC session, + * exchanges the wrapped key for an applicant access token, and presents the + * SDK via the injected launcher. + * + * @param params - Optional parameters. + * @param params.locale - BCP-47 locale for the SDK UI. + * @param params.debug - Enables SDK debug logging. + * @returns The SDK result. + */ + async startSumSub(params?: { + locale?: string; + debug?: boolean; + }): Promise> { + if (!this.#sumsubLauncher.isAvailable()) { + const error = 'SumSub SDK is not available in this runtime.'; + this.update((state) => { + state.sumsub.status = 'failed'; + state.sumsub.result = { error }; + }); + throw new Error(error); + } + + try { + this.update((state) => { + state.sumsub.status = 'creatingSession'; + state.sumsub.result = null; + }); + + const jwtToken = MOCK_JWT_TOKEN; + const { sessionId, wrappingPublicKey, idosSessionId } = + await this.messenger.call('KycService:createUkycSession', { + jwtToken, + vendorMetadata: { + moonPayAccessToken: this.state.accessToken, + moonPayUserId: this.state.moonpayCustomerId, + }, + }); + // Retain the exchange material so the SDK can refresh its token. The + // session's `wrappingPublicKey` is forwarded opaquely as the request's + // `wrappedUserKey` field (the /wrapped-key endpoint's body is unchanged). + const exchange = { + sessionId, + wrappedUserKey: wrappingPublicKey, + idosSessionId, + jwtToken, + }; + + this.update((state) => { + state.sumsub.status = 'fetchingToken'; + state.sumsub.sessionId = sessionId; + }); + + const { applicantAccessToken } = await this.messenger.call( + 'KycService:submitWrappedKey', + exchange, + ); + + this.update((state) => { + state.sumsub.status = 'launching'; + state.sumsub.applicantAccessToken = applicantAccessToken; + }); + + const result = await this.#sumsubLauncher.launch({ + applicantAccessToken, + onTokenExpiration: async () => { + const refreshed = await this.messenger.call( + 'KycService:submitWrappedKey', + exchange, + ); + return refreshed.applicantAccessToken; + }, + onStatusChange: (_prev, next) => { + this.update((state) => { + state.sumsub.status = + next === 'Completed' ? 'complete' : 'inProgress'; + }); + }, + locale: params?.locale ?? 'en', + debug: params?.debug ?? false, + }); + + this.update((state) => { + state.sumsub.status = 'complete'; + state.sumsub.result = result as Json; + }); + return result; + } catch (error) { + const result = { error: String(error) }; + this.update((state) => { + state.sumsub.status = 'failed'; + state.sumsub.result = result; + }); + return result; + } + } + + /** + * Resets the flow to idle, clearing session tokens and sub-flow state while + * preserving persisted terms acceptance and the per-product cache. + */ + reset(): void { + this.#authClientToken = null; + this.update((state) => { + state.phase = 'idle'; + state.statusMessage = ''; + state.error = null; + state.disclaimers = []; + state.disclaimersError = null; + state.sessionToken = null; + state.accessToken = null; + state.moonpayCustomerId = null; + state.sumsub = { + status: 'idle', + result: null, + sessionId: null, + applicantAccessToken: null, + }; + }); + } + + /** + * Transitions to the error phase with a message. + * + * @param message - The error message. + */ + #fail(message: string): void { + this.update((state) => { + state.error = message; + state.phase = 'error'; + }); + } +} diff --git a/packages/kyc-controller/src/KycService-method-action-types.ts b/packages/kyc-controller/src/KycService-method-action-types.ts new file mode 100644 index 00000000000..844e36faf4c --- /dev/null +++ b/packages/kyc-controller/src/KycService-method-action-types.ts @@ -0,0 +1,87 @@ +/** + * This file is auto generated. + * Do not edit manually. + */ + +import type { KycService } from './KycService'; + +/** + * Resolves the customer's country from the geolocation source and converts it + * to an ISO 3166-1 alpha-3 code. + * + * @returns The alpha-3 country code. + * @throws If the country cannot be determined or mapped. + */ +export type KycServiceGetGeoCountryAction = { + type: `KycService:getGeoCountry`; + handler: KycService['getGeoCountry']; +}; + +/** + * Fetches the disclaimers the customer must accept before a session is + * created. + * + * @param params - The parameters. + * @param params.country - ISO 3166-1 alpha-3 country code. + * @returns The disclaimers. + */ +export type KycServiceFetchDisclaimersAction = { + type: `KycService:fetchDisclaimers`; + handler: KycService['fetchDisclaimers']; +}; + +/** + * Creates a vendor session via the UKYC backend. + * + * @param params - The session parameters. + * @returns The created session token. + */ +export type KycServiceCreateSessionAction = { + type: `KycService:createSession`; + handler: KycService['createSession']; +}; + +/** + * Checks whether KYC is required for the given access token, country, and + * capabilities. + * + * @param params - The check parameters. + * @returns Whether KYC is required. + */ +export type KycServiceCheckKycRequiredAction = { + type: `KycService:checkKycRequired`; + handler: KycService['checkKycRequired']; +}; + +/** + * Creates a UKYC session for the SumSub document-verification sub-flow. + * + * @param params - The session parameters. + * @returns The UKYC session identifiers and wrapped key. + */ +export type KycServiceCreateUkycSessionAction = { + type: `KycService:createUkycSession`; + handler: KycService['createUkycSession']; +}; + +/** + * Exchanges the wrapped user key for a SumSub applicant access token. + * + * @param params - The exchange parameters. + * @returns The applicant access token and status. + */ +export type KycServiceSubmitWrappedKeyAction = { + type: `KycService:submitWrappedKey`; + handler: KycService['submitWrappedKey']; +}; + +/** + * Union of all KycService action types. + */ +export type KycServiceMethodActions = + | KycServiceGetGeoCountryAction + | KycServiceFetchDisclaimersAction + | KycServiceCreateSessionAction + | KycServiceCheckKycRequiredAction + | KycServiceCreateUkycSessionAction + | KycServiceSubmitWrappedKeyAction; diff --git a/packages/kyc-controller/src/KycService.test.ts b/packages/kyc-controller/src/KycService.test.ts new file mode 100644 index 00000000000..c359add4e76 --- /dev/null +++ b/packages/kyc-controller/src/KycService.test.ts @@ -0,0 +1,352 @@ +import { Messenger, MOCK_ANY_NAMESPACE } from '@metamask/messenger'; +import type { + MockAnyNamespace, + MessengerActions, + MessengerEvents, +} from '@metamask/messenger'; +import nock, { cleanAll } from 'nock'; + +import type { KycServiceMessenger } from './KycService'; +import { KycService } from './KycService'; + +const MOCK_API_URL = 'https://kyc-api.dev-api.cx.metamask.io'; + +describe('KycService', () => { + afterEach(() => { + cleanAll(); + }); + + describe('getGeoCountry', () => { + it('maps the geolocation to an ISO alpha-3 country code', async () => { + const { service } = getService({ geolocation: 'US-NY' }); + expect(await service.getGeoCountry()).toBe('USA'); + }); + + it('throws when the location is unknown', async () => { + const { service } = getService({ geolocation: 'UNKNOWN' }); + await expect(service.getGeoCountry()).rejects.toThrow( + /Unable to determine country/u, + ); + }); + + it('throws when the country cannot be mapped to alpha-3', async () => { + const { service } = getService({ geolocation: 'ZZ' }); + await expect(service.getGeoCountry()).rejects.toThrow( + /Unable to map country code "ZZ"/u, + ); + }); + + it('throws when the location resolves to a nullish value', async () => { + const { service } = getService({ geolocation: null }); + await expect(service.getGeoCountry()).rejects.toThrow( + /Unable to determine country/u, + ); + }); + + it('constructs with the default service policy options', async () => { + const { service } = getService({ + defaultPolicy: true, + geolocation: 'US', + }); + expect(await service.getGeoCountry()).toBe('USA'); + }); + }); + + describe('fetchDisclaimers', () => { + it('returns the disclaimers for a country', async () => { + const disclaimers = [ + { id: '1', display_name: 'Terms', url: 'https://t' }, + ]; + nock(MOCK_API_URL) + .get('/vendors/moonpay/disclaimers') + .query({ country: 'USA' }) + .reply(200, disclaimers); + const { service } = getService(); + + expect(await service.fetchDisclaimers({ country: 'USA' })).toStrictEqual( + disclaimers, + ); + }); + + it('throws on a malformed response', async () => { + nock(MOCK_API_URL) + .get('/vendors/moonpay/disclaimers') + .query({ country: 'USA' }) + .reply(200, [{ id: 1 }]); + const { service } = getService(); + + await expect( + service.fetchDisclaimers({ country: 'USA' }), + ).rejects.toThrow(/Malformed response received from disclaimers API/u); + }); + + it('throws when no bearer token is available', async () => { + const { service } = getService({ bearerToken: '' }); + await expect( + service.fetchDisclaimers({ country: 'USA' }), + ).rejects.toThrow(/Unable to obtain an authentication bearer token/u); + }); + + it('throws an HttpError on a non-ok response', async () => { + nock(MOCK_API_URL) + .get('/vendors/moonpay/disclaimers') + .query({ country: 'USA' }) + .reply(500); + const { service } = getService(); + + await expect( + service.fetchDisclaimers({ country: 'USA' }), + ).rejects.toThrow(/failed with status '500'/u); + }); + }); + + describe('createSession', () => { + it('creates a session and returns the token', async () => { + nock(MOCK_API_URL) + .post('/vendors/moonpay/sessions') + .reply(200, { sessionToken: 'session-1' }); + const { service } = getService(); + + expect( + await service.createSession({ + email: 'a@b.co', + termsAcceptedAt: '2026-01-01T00:00:00.000Z', + disclaimerIds: ['1'], + }), + ).toStrictEqual({ sessionToken: 'session-1' }); + }); + + it('throws on a malformed response', async () => { + nock(MOCK_API_URL).post('/vendors/moonpay/sessions').reply(200, {}); + const { service } = getService(); + + await expect( + service.createSession({ + email: 'a@b.co', + termsAcceptedAt: '2026-01-01T00:00:00.000Z', + disclaimerIds: ['1'], + }), + ).rejects.toThrow(/Malformed response received from sessions API/u); + }); + }); + + describe('checkKycRequired', () => { + it('returns whether KYC is required (default capabilities)', async () => { + nock(MOCK_API_URL) + .post('/vendors/moonpay/kyc-required', { + accessToken: 'access-1', + country: 'USA', + capabilities: [{ product: 'ramps' }], + }) + .reply(200, { required: true }); + const { service } = getService(); + + expect( + await service.checkKycRequired({ + accessToken: 'access-1', + country: 'USA', + }), + ).toStrictEqual({ kycRequired: true }); + }); + + it('passes provided capabilities', async () => { + nock(MOCK_API_URL) + .post('/vendors/moonpay/kyc-required', { + accessToken: 'access-1', + country: 'USA', + capabilities: [{ product: 'card' }], + }) + .reply(200, { required: false }); + const { service } = getService(); + + expect( + await service.checkKycRequired({ + accessToken: 'access-1', + country: 'USA', + capabilities: [{ product: 'card' }], + }), + ).toStrictEqual({ kycRequired: false }); + }); + + it('throws on a malformed response', async () => { + nock(MOCK_API_URL).post('/vendors/moonpay/kyc-required').reply(200, {}); + const { service } = getService(); + + await expect( + service.checkKycRequired({ accessToken: 'access-1', country: 'USA' }), + ).rejects.toThrow(/Malformed response received from kyc-required API/u); + }); + + it('surfaces the specific field mismatch and payload in the error', async () => { + nock(MOCK_API_URL) + .post('/vendors/moonpay/kyc-required') + .reply(200, { required: 'yes' }); + const { service } = getService(); + + await expect( + service.checkKycRequired({ accessToken: 'access-1', country: 'USA' }), + ).rejects.toThrow( + /Malformed response received from kyc-required API:.*required.*received: \{"required":"yes"\}/su, + ); + }); + }); + + describe('createUkycSession', () => { + it('creates a UKYC session', async () => { + const response = { + sessionId: 'sid', + wrappingPublicKey: 'wpk', + idosSessionId: 'idos', + }; + nock(MOCK_API_URL).post('/sessions').reply(200, response); + const { service } = getService(); + + expect( + await service.createUkycSession({ + jwtToken: 'jwt', + vendorMetadata: { foo: 'bar' }, + }), + ).toStrictEqual(response); + }); + + it('throws on a malformed response', async () => { + nock(MOCK_API_URL).post('/sessions').reply(200, { sessionId: 'sid' }); + const { service } = getService(); + + await expect( + service.createUkycSession({ jwtToken: 'jwt', vendorMetadata: {} }), + ).rejects.toThrow(/Malformed response received from UKYC sessions API/u); + }); + }); + + describe('submitWrappedKey', () => { + it('exchanges the wrapped key for an applicant access token', async () => { + const response = { status: 'ok', applicantAccessToken: 'aat' }; + nock(MOCK_API_URL).post('/sessions/sid/wrapped-key').reply(200, response); + const { service } = getService(); + + expect( + await service.submitWrappedKey({ + sessionId: 'sid', + wrappedUserKey: 'wuk', + idosSessionId: 'idos', + jwtToken: 'jwt', + }), + ).toStrictEqual(response); + }); + + it('throws on a malformed response', async () => { + nock(MOCK_API_URL) + .post('/sessions/sid/wrapped-key') + .reply(200, { status: 'ok' }); + const { service } = getService(); + + await expect( + service.submitWrappedKey({ + sessionId: 'sid', + wrappedUserKey: 'wuk', + idosSessionId: 'idos', + jwtToken: 'jwt', + }), + ).rejects.toThrow(/Malformed response received from wrapped-key API/u); + }); + }); + + describe('baseUrl override', () => { + it('uses the provided baseUrl instead of the env-derived URL', async () => { + const customUrl = 'https://kyc-api.local.test'; + const disclaimers = [{ id: '1', display_name: 'Terms', url: 'https://t' }]; + nock(customUrl) + .get('/vendors/moonpay/disclaimers') + .query({ country: 'USA' }) + .reply(200, disclaimers); + const { service } = getService({ baseUrl: customUrl }); + + expect(await service.fetchDisclaimers({ country: 'USA' })).toStrictEqual( + disclaimers, + ); + }); + }); + + describe('messenger actions', () => { + it('exposes methods as messenger actions', async () => { + nock(MOCK_API_URL) + .get('/vendors/moonpay/disclaimers') + .query({ country: 'USA' }) + .reply(200, []); + const { rootMessenger } = getService(); + + expect( + await rootMessenger.call('KycService:fetchDisclaimers', { + country: 'USA', + }), + ).toStrictEqual([]); + }); + }); +}); + +type RootMessenger = Messenger< + MockAnyNamespace, + MessengerActions, + MessengerEvents +>; + +/** + * Constructs the service under test with mocked auth + geo handlers. + * + * @param args - Options. + * @param args.bearerToken - The bearer token the auth handler returns. + * @param args.geolocation - The location the geolocation handler returns. + * @param args.defaultPolicy - When true, omit `policyOptions` to use defaults. + * @param args.baseUrl - When provided, overrides the env-derived base URL. + * @returns The service, root messenger, and service messenger. + */ +function getService({ + bearerToken = 'test-bearer', + geolocation = 'US-NY', + defaultPolicy = false, + baseUrl, +}: { + bearerToken?: string; + geolocation?: string | null; + defaultPolicy?: boolean; + baseUrl?: string; +} = {}): { + service: KycService; + rootMessenger: RootMessenger; + messenger: KycServiceMessenger; +} { + const rootMessenger: RootMessenger = new Messenger({ + namespace: MOCK_ANY_NAMESPACE, + }); + const messenger: KycServiceMessenger = new Messenger({ + namespace: 'KycService', + parent: rootMessenger, + }); + rootMessenger.delegate({ + actions: [ + 'AuthenticationController:getBearerToken', + 'GeolocationController:getGeolocation', + ], + events: [], + messenger, + }); + rootMessenger.registerActionHandler( + 'AuthenticationController:getBearerToken', + async () => bearerToken, + ); + rootMessenger.registerActionHandler( + 'GeolocationController:getGeolocation', + async () => geolocation as string, + ); + + const service = new KycService({ + fetch, + messenger, + env: 'development', + ...(baseUrl ? { baseUrl } : {}), + ...(defaultPolicy ? {} : { policyOptions: { maxRetries: 0 } }), + }); + + return { service, rootMessenger, messenger }; +} diff --git a/packages/kyc-controller/src/KycService.ts b/packages/kyc-controller/src/KycService.ts new file mode 100644 index 00000000000..48117c65407 --- /dev/null +++ b/packages/kyc-controller/src/KycService.ts @@ -0,0 +1,418 @@ +import type { + CreateServicePolicyOptions, + ServicePolicy, +} from '@metamask/controller-utils'; +import { createServicePolicy, HttpError } from '@metamask/controller-utils'; +import type { GeolocationControllerGetGeolocationAction } from '@metamask/geolocation-controller'; +import type { Messenger } from '@metamask/messenger'; +import type { AuthenticationControllerGetBearerTokenAction } from '@metamask/profile-sync-controller/auth'; +import type { Infer, Struct } from '@metamask/superstruct'; +import { + array, + assert, + boolean, + string, + StructError, + type, +} from '@metamask/superstruct'; + +import { alpha2ToAlpha3 } from './countryCodes'; +import type { KycServiceMethodActions } from './KycService-method-action-types'; +import type { KycDisclaimer } from './types'; + +// === GENERAL === + +/** + * The name of the {@link KycService}, used to namespace the service's actions. + */ +export const serviceName = 'KycService'; + +/** + * The supported environments for the Universal KYC API. + */ +export type KycServiceEnvironment = 'production' | 'development'; + +const KYC_API_URLS: Record = { + production: 'https://kyc-api.cx.metamask.io', + development: 'https://kyc-api.dev-api.cx.metamask.io', +}; + +// === MESSENGER === + +const MESSENGER_EXPOSED_METHODS = [ + 'getGeoCountry', + 'fetchDisclaimers', + 'createSession', + 'checkKycRequired', + 'createUkycSession', + 'submitWrappedKey', +] as const; + +/** + * Actions that {@link KycService} exposes to other consumers. + */ +export type KycServiceActions = KycServiceMethodActions; + +/** + * Actions from other messengers that {@link KycService} calls. + */ +type AllowedActions = + | AuthenticationControllerGetBearerTokenAction + | GeolocationControllerGetGeolocationAction; + +/** + * Events that {@link KycService} exposes to other consumers. + */ +export type KycServiceEvents = never; + +/** + * Events from other messengers that {@link KycService} subscribes to. + */ +type AllowedEvents = never; + +/** + * The messenger restricted to actions and events accessed by + * {@link KycService}. + */ +export type KycServiceMessenger = Messenger< + typeof serviceName, + KycServiceActions | AllowedActions, + KycServiceEvents | AllowedEvents +>; + +/** + * Options for constructing a {@link KycService}. + */ +export type KycServiceOptions = { + messenger: KycServiceMessenger; + fetch: typeof fetch; + env: KycServiceEnvironment; + /** + * Overrides the base URL derived from `env`. When provided, this value is + * used verbatim as the base URL for all requests, which is useful for + * targeting a local or staging KYC API. + */ + baseUrl?: string; + policyOptions?: CreateServicePolicyOptions; +}; + +// === API RESPONSE SCHEMAS === + +const DisclaimerStruct = type({ + id: string(), + display_name: string(), + url: string(), +}); +const DisclaimersResponseStruct = array(DisclaimerStruct); + +const CreateSessionResponseStruct = type({ sessionToken: string() }); + +// The live KYC API returns the flag under `required`; the service normalizes +// this to `kycRequired` for consumers (see `checkKycRequired`). +const KycRequiredResponseStruct = type({ required: boolean() }); + +const UkycSessionResponseStruct = type({ + sessionId: string(), + wrappingPublicKey: string(), + idosSessionId: string(), +}); +export type UkycSessionResponse = Infer; + +const WrappedKeyResponseStruct = type({ + status: string(), + applicantAccessToken: string(), +}); +export type WrappedKeyResponse = Infer; + +// === PARAM TYPES === + +export type CreateSessionParams = { + email: string; + termsAcceptedAt: string; + disclaimerIds: string[]; +}; + +export type CheckKycRequiredParams = { + accessToken: string; + country: string; + capabilities?: { product: string }[]; +}; + +export type CreateUkycSessionParams = { + jwtToken: string; + vendorMetadata: Record; +}; + +export type SubmitWrappedKeyParams = { + sessionId: string; + wrappedUserKey: string; + idosSessionId: string; + jwtToken: string; +}; + +// === SERVICE DEFINITION === + +/** + * `KycService` communicates with the Universal KYC (UKYC) backend to drive the + * identity + document-verification flow. It is stateless and platform-agnostic: + * HTTP is performed through an injected `fetch`, and the auth bearer token and + * geolocation come from other controllers via the messenger. + */ +export class KycService { + readonly name: typeof serviceName; + + readonly #messenger: KycServiceMessenger; + + readonly #fetch: typeof fetch; + + readonly #baseUrl: string; + + readonly #policy: ServicePolicy; + + /** + * Constructs a new KycService. + * + * @param options - The constructor options. + * @param options.messenger - The messenger suited for this service. + * @param options.fetch - A function used to make HTTP requests. + * @param options.env - The environment; determines the base URL. + * @param options.baseUrl - Overrides the base URL derived from `env`. + * @param options.policyOptions - Options for the request service policy. + */ + constructor({ + messenger, + fetch: fetchFunction, + env, + baseUrl, + policyOptions, + }: KycServiceOptions) { + this.name = serviceName; + this.#messenger = messenger; + this.#fetch = fetchFunction; + this.#baseUrl = baseUrl ?? KYC_API_URLS[env]; + this.#policy = createServicePolicy(policyOptions ?? {}); + this.#messenger.registerMethodActionHandlers( + this, + MESSENGER_EXPOSED_METHODS, + ); + } + + /** + * Resolves the customer's country from the geolocation source and converts it + * to an ISO 3166-1 alpha-3 code. + * + * @returns The alpha-3 country code. + * @throws If the country cannot be determined or mapped. + */ + async getGeoCountry(): Promise { + const location = await this.#messenger.call( + 'GeolocationController:getGeolocation', + ); + const alpha2 = String(location ?? '') + .split('-')[0] + .toUpperCase(); + if (!alpha2 || alpha2 === 'UNKNOWN') { + throw new Error( + `Unable to determine country from geolocation (got "${location}").`, + ); + } + const alpha3 = alpha2ToAlpha3(alpha2); + if (!alpha3) { + throw new Error( + `Unable to map country code "${alpha2}" to an ISO 3166-1 alpha-3 code.`, + ); + } + return alpha3; + } + + /** + * Fetches the disclaimers the customer must accept before a session is + * created. + * + * @param params - The parameters. + * @param params.country - ISO 3166-1 alpha-3 country code. + * @returns The disclaimers. + */ + async fetchDisclaimers({ + country, + }: { + country: string; + }): Promise { + const url = new URL('/vendors/moonpay/disclaimers', this.#baseUrl); + url.searchParams.set('country', country); + const data = await this.#request(url, { method: 'GET' }); + return this.#validateResponse( + data, + DisclaimersResponseStruct, + 'disclaimers', + ) as KycDisclaimer[]; + } + + /** + * Creates a vendor session via the UKYC backend. + * + * @param params - The session parameters. + * @returns The created session token. + */ + async createSession( + params: CreateSessionParams, + ): Promise> { + const url = new URL('/vendors/moonpay/sessions', this.#baseUrl); + const data = await this.#request(url, { + method: 'POST', + body: JSON.stringify(params), + }); + return this.#validateResponse(data, CreateSessionResponseStruct, 'sessions'); + } + + /** + * Checks whether KYC is required for the given access token, country, and + * capabilities. + * + * @param params - The check parameters. + * @returns Whether KYC is required. + */ + async checkKycRequired( + params: CheckKycRequiredParams, + ): Promise<{ kycRequired: boolean }> { + const url = new URL('/vendors/moonpay/kyc-required', this.#baseUrl); + const data = await this.#request(url, { + method: 'POST', + body: JSON.stringify({ + accessToken: params.accessToken, + country: params.country, + capabilities: params.capabilities ?? [{ product: 'ramps' }], + }), + }); + const { required } = this.#validateResponse( + data, + KycRequiredResponseStruct, + 'kyc-required', + ); + return { kycRequired: required }; + } + + /** + * Creates a UKYC session for the SumSub document-verification sub-flow. + * + * @param params - The session parameters. + * @returns The UKYC session identifiers and wrapped key. + */ + async createUkycSession( + params: CreateUkycSessionParams, + ): Promise { + const url = new URL('/sessions', this.#baseUrl); + const data = await this.#request(url, { + method: 'POST', + body: JSON.stringify({ + vendorId: 'moonpay', + vendorUserId: 'mockedId', + jwtToken: params.jwtToken, + vendorMetadata: params.vendorMetadata, + }), + }); + return this.#validateResponse( + data, + UkycSessionResponseStruct, + 'UKYC sessions', + ); + } + + /** + * Exchanges the wrapped user key for a SumSub applicant access token. + * + * @param params - The exchange parameters. + * @returns The applicant access token and status. + */ + async submitWrappedKey( + params: SubmitWrappedKeyParams, + ): Promise { + const url = new URL( + `/sessions/${encodeURIComponent(params.sessionId)}/wrapped-key`, + this.#baseUrl, + ); + const data = await this.#request(url, { + method: 'POST', + body: JSON.stringify({ + wrappedUserKey: params.wrappedUserKey, + jwtToken: params.jwtToken, + idosSessionId: params.idosSessionId, + }), + }); + return this.#validateResponse( + data, + WrappedKeyResponseStruct, + 'wrapped-key', + ); + } + + /** + * Validates a parsed API response against a superstruct schema, throwing a + * descriptive error when the response does not match. + * + * Unlike a bare `Struct.is` check, this surfaces exactly which field was + * missing or had the wrong type, which is essential for diagnosing shape + * mismatches between the client and the live API. + * + * @param data - The parsed response body. + * @param struct - The superstruct schema the body is expected to satisfy. + * @param apiName - A human-readable name of the API, used in the error message. + * @returns The validated, typed response. + * @throws If `data` does not match `struct`. + */ + #validateResponse( + data: unknown, + struct: Struct, + apiName: string, + ): Type { + try { + assert(data, struct); + return data; + } catch (error) { + const detail = + error instanceof StructError + ? `${error.message} (received: ${JSON.stringify(data)})` + : String(error); + throw new Error( + `Malformed response received from ${apiName} API: ${detail}`, + ); + } + } + + /** + * Performs an authenticated JSON request wrapped in the service policy. + * + * @param url - The request URL. + * @param init - The request init (method, body). + * @returns The parsed JSON response. + */ + async #request(url: URL, init: RequestInit): Promise { + const bearerToken = await this.#messenger.call( + 'AuthenticationController:getBearerToken', + ); + if (!bearerToken) { + throw new Error( + 'Unable to obtain an authentication bearer token — is the wallet signed in?', + ); + } + + const response = await this.#policy.execute(async () => { + const localResponse = await this.#fetch(url.toString(), { + ...init, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${bearerToken}`, + }, + }); + if (!localResponse.ok) { + throw new HttpError( + localResponse.status, + `Fetching '${url.toString()}' failed with status '${localResponse.status}'`, + ); + } + return localResponse; + }); + + return response.json(); + } +} diff --git a/packages/kyc-controller/src/countryCodes.test.ts b/packages/kyc-controller/src/countryCodes.test.ts new file mode 100644 index 00000000000..55446663199 --- /dev/null +++ b/packages/kyc-controller/src/countryCodes.test.ts @@ -0,0 +1,19 @@ +import { ALPHA2_TO_ALPHA3, alpha2ToAlpha3 } from './countryCodes'; + +describe('countryCodes', () => { + it('exposes the alpha-2 to alpha-3 map', () => { + expect(ALPHA2_TO_ALPHA3.US).toBe('USA'); + }); + + it('maps a known uppercase alpha-2 code', () => { + expect(alpha2ToAlpha3('GB')).toBe('GBR'); + }); + + it('is case-insensitive', () => { + expect(alpha2ToAlpha3('fr')).toBe('FRA'); + }); + + it('returns undefined for an unknown code', () => { + expect(alpha2ToAlpha3('ZZ')).toBeUndefined(); + }); +}); diff --git a/packages/kyc-controller/src/countryCodes.ts b/packages/kyc-controller/src/countryCodes.ts new file mode 100644 index 00000000000..a5712d24109 --- /dev/null +++ b/packages/kyc-controller/src/countryCodes.ts @@ -0,0 +1,270 @@ +/** + * ISO 3166-1 alpha-2 to alpha-3 country code mapping. + * + * The geolocation source returns ISO 3166-2 codes whose leading segment is an + * alpha-2 country code (e.g. "US", "US-NY"). The identity vendor APIs + * (disclaimers, kyc-required) expect alpha-3 codes (e.g. "USA"). This map + * bridges the two. + */ +export const ALPHA2_TO_ALPHA3: Record = { + AD: 'AND', + AE: 'ARE', + AF: 'AFG', + AG: 'ATG', + AI: 'AIA', + AL: 'ALB', + AM: 'ARM', + AO: 'AGO', + AQ: 'ATA', + AR: 'ARG', + AS: 'ASM', + AT: 'AUT', + AU: 'AUS', + AW: 'ABW', + AX: 'ALA', + AZ: 'AZE', + BA: 'BIH', + BB: 'BRB', + BD: 'BGD', + BE: 'BEL', + BF: 'BFA', + BG: 'BGR', + BH: 'BHR', + BI: 'BDI', + BJ: 'BEN', + BL: 'BLM', + BM: 'BMU', + BN: 'BRN', + BO: 'BOL', + BQ: 'BES', + BR: 'BRA', + BS: 'BHS', + BT: 'BTN', + BV: 'BVT', + BW: 'BWA', + BY: 'BLR', + BZ: 'BLZ', + CA: 'CAN', + CC: 'CCK', + CD: 'COD', + CF: 'CAF', + CG: 'COG', + CH: 'CHE', + CI: 'CIV', + CK: 'COK', + CL: 'CHL', + CM: 'CMR', + CN: 'CHN', + CO: 'COL', + CR: 'CRI', + CU: 'CUB', + CV: 'CPV', + CW: 'CUW', + CX: 'CXR', + CY: 'CYP', + CZ: 'CZE', + DE: 'DEU', + DJ: 'DJI', + DK: 'DNK', + DM: 'DMA', + DO: 'DOM', + DZ: 'DZA', + EC: 'ECU', + EE: 'EST', + EG: 'EGY', + EH: 'ESH', + ER: 'ERI', + ES: 'ESP', + ET: 'ETH', + FI: 'FIN', + FJ: 'FJI', + FK: 'FLK', + FM: 'FSM', + FO: 'FRO', + FR: 'FRA', + GA: 'GAB', + GB: 'GBR', + GD: 'GRD', + GE: 'GEO', + GF: 'GUF', + GG: 'GGY', + GH: 'GHA', + GI: 'GIB', + GL: 'GRL', + GM: 'GMB', + GN: 'GIN', + GP: 'GLP', + GQ: 'GNQ', + GR: 'GRC', + GS: 'SGS', + GT: 'GTM', + GU: 'GUM', + GW: 'GNB', + GY: 'GUY', + HK: 'HKG', + HM: 'HMD', + HN: 'HND', + HR: 'HRV', + HT: 'HTI', + HU: 'HUN', + ID: 'IDN', + IE: 'IRL', + IL: 'ISR', + IM: 'IMN', + IN: 'IND', + IO: 'IOT', + IQ: 'IRQ', + IR: 'IRN', + IS: 'ISL', + IT: 'ITA', + JE: 'JEY', + JM: 'JAM', + JO: 'JOR', + JP: 'JPN', + KE: 'KEN', + KG: 'KGZ', + KH: 'KHM', + KI: 'KIR', + KM: 'COM', + KN: 'KNA', + KP: 'PRK', + KR: 'KOR', + KW: 'KWT', + KY: 'CYM', + KZ: 'KAZ', + LA: 'LAO', + LB: 'LBN', + LC: 'LCA', + LI: 'LIE', + LK: 'LKA', + LR: 'LBR', + LS: 'LSO', + LT: 'LTU', + LU: 'LUX', + LV: 'LVA', + LY: 'LBY', + MA: 'MAR', + MC: 'MCO', + MD: 'MDA', + ME: 'MNE', + MF: 'MAF', + MG: 'MDG', + MH: 'MHL', + MK: 'MKD', + ML: 'MLI', + MM: 'MMR', + MN: 'MNG', + MO: 'MAC', + MP: 'MNP', + MQ: 'MTQ', + MR: 'MRT', + MS: 'MSR', + MT: 'MLT', + MU: 'MUS', + MV: 'MDV', + MW: 'MWI', + MX: 'MEX', + MY: 'MYS', + MZ: 'MOZ', + NA: 'NAM', + NC: 'NCL', + NE: 'NER', + NF: 'NFK', + NG: 'NGA', + NI: 'NIC', + NL: 'NLD', + NO: 'NOR', + NP: 'NPL', + NR: 'NRU', + NU: 'NIU', + NZ: 'NZL', + OM: 'OMN', + PA: 'PAN', + PE: 'PER', + PF: 'PYF', + PG: 'PNG', + PH: 'PHL', + PK: 'PAK', + PL: 'POL', + PM: 'SPM', + PN: 'PCN', + PR: 'PRI', + PS: 'PSE', + PT: 'PRT', + PW: 'PLW', + PY: 'PRY', + QA: 'QAT', + RE: 'REU', + RO: 'ROU', + RS: 'SRB', + RU: 'RUS', + RW: 'RWA', + SA: 'SAU', + SB: 'SLB', + SC: 'SYC', + SD: 'SDN', + SE: 'SWE', + SG: 'SGP', + SH: 'SHN', + SI: 'SVN', + SJ: 'SJM', + SK: 'SVK', + SL: 'SLE', + SM: 'SMR', + SN: 'SEN', + SO: 'SOM', + SR: 'SUR', + SS: 'SSD', + ST: 'STP', + SV: 'SLV', + SX: 'SXM', + SY: 'SYR', + SZ: 'SWZ', + TC: 'TCA', + TD: 'TCD', + TF: 'ATF', + TG: 'TGO', + TH: 'THA', + TJ: 'TJK', + TK: 'TKL', + TL: 'TLS', + TM: 'TKM', + TN: 'TUN', + TO: 'TON', + TR: 'TUR', + TT: 'TTO', + TV: 'TUV', + TW: 'TWN', + TZ: 'TZA', + UA: 'UKR', + UG: 'UGA', + UM: 'UMI', + US: 'USA', + UY: 'URY', + UZ: 'UZB', + VA: 'VAT', + VC: 'VCT', + VE: 'VEN', + VG: 'VGB', + VI: 'VIR', + VN: 'VNM', + VU: 'VUT', + WF: 'WLF', + WS: 'WSM', + YE: 'YEM', + YT: 'MYT', + ZA: 'ZAF', + ZM: 'ZMB', + ZW: 'ZWE', +}; + +/** + * Converts an ISO 3166-1 alpha-2 country code (e.g. "US") to its alpha-3 + * equivalent (e.g. "USA"). Returns `undefined` for unknown codes. + * + * @param alpha2 - The ISO 3166-1 alpha-2 country code. + * @returns The alpha-3 code, or `undefined` if the input is not recognized. + */ +export function alpha2ToAlpha3(alpha2: string): string | undefined { + return ALPHA2_TO_ALPHA3[alpha2.toUpperCase()]; +} diff --git a/packages/kyc-controller/src/crypto.test.ts b/packages/kyc-controller/src/crypto.test.ts new file mode 100644 index 00000000000..b26d985c9ac --- /dev/null +++ b/packages/kyc-controller/src/crypto.test.ts @@ -0,0 +1,204 @@ +import { gcm } from '@noble/ciphers/aes'; +import { x25519 } from '@noble/curves/ed25519'; +import { hkdf } from '@noble/hashes/hkdf'; +import { sha256 } from '@noble/hashes/sha2'; +import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils'; +import { base64 } from '@scure/base'; + +import type { EncryptedCredentialsEnvelope } from './crypto'; +import { decryptCredentials, generateKeyPair } from './crypto'; + +/** + * Builds an encrypted-credentials envelope that `decryptCredentials` can + * reverse with `ourPublicKey`'s matching private key. + * + * @param ourPublicKey - The recipient's X25519 public key. + * @param credentials - The plaintext credentials to encrypt. + * @param options - Encoding options. + * @param options.encoding - `'hex'` (default) or `'base64'`. + * @param options.ivLength - IV length in bytes (default 12). + * @param options.useNonceField - Emit `nonce` instead of `iv`. + * @returns The encrypted envelope. + */ +function makeEnvelope( + ourPublicKey: Uint8Array, + credentials: Record, + { + encoding = 'hex' as 'hex' | 'base64', + ivLength = 12, + useNonceField = false, + } = {}, +): EncryptedCredentialsEnvelope { + const ephemeralPrivate = x25519.utils.randomSecretKey(); + const ephemeralPublic = x25519.getPublicKey(ephemeralPrivate); + const shared = x25519.getSharedSecret(ephemeralPrivate, ourPublicKey); + const key = hkdf(sha256, shared, undefined, undefined, 32); + const iv = new Uint8Array(ivLength).fill(7); + const ciphertext = gcm(key, iv).encrypt( + utf8ToBytes(JSON.stringify(credentials)), + ); + const encode = (bytes: Uint8Array): string => + encoding === 'hex' ? bytesToHex(bytes) : base64.encode(bytes); + const envelope: EncryptedCredentialsEnvelope = { + ephemeralPublicKey: encode(ephemeralPublic), + ciphertext: encode(ciphertext), + }; + if (useNonceField) { + envelope.nonce = encode(iv); + } else { + envelope.iv = encode(iv); + } + return envelope; +} + +describe('crypto', () => { + describe('generateKeyPair', () => { + it('produces a 32-byte keypair with a hex public key', () => { + const keypair = generateKeyPair(); + expect(keypair.privateKey).toHaveLength(32); + expect(keypair.publicKey).toHaveLength(32); + expect(keypair.publicKeyHex).toMatch(/^[0-9a-f]{64}$/u); + }); + }); + + describe('decryptCredentials', () => { + it('decrypts a hex-encoded envelope object', () => { + const keypair = generateKeyPair(); + const envelope = makeEnvelope(keypair.publicKey, { + accessToken: 'access-1', + }); + + const { credentials, method } = decryptCredentials( + envelope, + keypair.privateKey, + ); + + expect(credentials.accessToken).toBe('access-1'); + expect(method).toBe('aes-256-gcm/hkdf-sha256'); + }); + + it('decrypts a base64-encoded envelope', () => { + const keypair = generateKeyPair(); + const envelope = makeEnvelope( + keypair.publicKey, + { clientToken: 'client-1' }, + { encoding: 'base64' }, + ); + + const { credentials } = decryptCredentials(envelope, keypair.privateKey); + + expect(credentials.clientToken).toBe('client-1'); + }); + + it('honors an explicit base64 encoding hint', () => { + const keypair = generateKeyPair(); + const envelope = makeEnvelope( + keypair.publicKey, + { accessToken: 'access-2' }, + { encoding: 'base64' }, + ); + envelope.encoding = 'base64'; + + const { credentials } = decryptCredentials(envelope, keypair.privateKey); + + expect(credentials.accessToken).toBe('access-2'); + }); + + it('accepts a `nonce` field as an alias for `iv`', () => { + const keypair = generateKeyPair(); + const envelope = makeEnvelope( + keypair.publicKey, + { accessToken: 'access-3' }, + { useNonceField: true }, + ); + + const { credentials } = decryptCredentials(envelope, keypair.privateKey); + + expect(credentials.accessToken).toBe('access-3'); + }); + + it('decrypts an envelope delivered as a JSON string', () => { + const keypair = generateKeyPair(); + const envelope = makeEnvelope(keypair.publicKey, { + accessToken: 'access-4', + }); + + const { credentials } = decryptCredentials( + JSON.stringify(envelope), + keypair.privateKey, + ); + + expect(credentials.accessToken).toBe('access-4'); + }); + + it('decrypts an envelope delivered as base64(JSON)', () => { + const keypair = generateKeyPair(); + const envelope = makeEnvelope(keypair.publicKey, { + accessToken: 'access-5', + }); + const base64Json = base64.encode(utf8ToBytes(JSON.stringify(envelope))); + + const { credentials } = decryptCredentials( + base64Json, + keypair.privateKey, + ); + + expect(credentials.accessToken).toBe('access-5'); + }); + + it('throws for a JSON string that fails to parse', () => { + const keypair = generateKeyPair(); + expect(() => + decryptCredentials('{ not valid json', keypair.privateKey), + ).toThrow(/looked like JSON but failed to parse/u); + }); + + it('throws for base64 that decodes to non-JSON starting with a brace', () => { + const keypair = generateKeyPair(); + const bad = base64.encode(utf8ToBytes('{ still not json')); + expect(() => decryptCredentials(bad, keypair.privateKey)).toThrow( + /base64-decoded to non-JSON/u, + ); + }); + + it('throws for an opaque string that is neither JSON nor base64(JSON)', () => { + const keypair = generateKeyPair(); + const bad = base64.encode(utf8ToBytes('hello world')); + expect(() => decryptCredentials(bad, keypair.privateKey)).toThrow( + /opaque string/u, + ); + }); + + it('throws for an object missing required fields', () => { + const keypair = generateKeyPair(); + expect(() => + decryptCredentials( + { ephemeralPublicKey: 'aa' } as EncryptedCredentialsEnvelope, + keypair.privateKey, + ), + ).toThrow(/missing required fields/u); + }); + + it('reports the value type for a non-object input', () => { + const keypair = generateKeyPair(); + expect(() => + decryptCredentials( + 123 as unknown as EncryptedCredentialsEnvelope, + keypair.privateKey, + ), + ).toThrow(/Got: number/u); + }); + + it('throws when the IV length is not 12 bytes', () => { + const keypair = generateKeyPair(); + const envelope = makeEnvelope( + keypair.publicKey, + { accessToken: 'x' }, + { ivLength: 16 }, + ); + expect(() => decryptCredentials(envelope, keypair.privateKey)).toThrow( + /Unexpected IV length 16/u, + ); + }); + }); +}); diff --git a/packages/kyc-controller/src/crypto.ts b/packages/kyc-controller/src/crypto.ts new file mode 100644 index 00000000000..e54ea730915 --- /dev/null +++ b/packages/kyc-controller/src/crypto.ts @@ -0,0 +1,252 @@ +/** + * Check / Auth frame key exchange and credential decryption. + * + * The identity vendor's Check and Auth frames return encrypted credentials. + * The confirmed protocol is X25519 ECDH + AES-256-GCM (an "ECDH-ES" pattern + * signalled by a 12-byte IV): + * + * 1. Client generates an X25519 keypair, sends `publicKey` (hex) into the + * frame as a URL param. + * 2. Frame generates its own ephemeral X25519 keypair and encrypts the + * credentials, returning `{ ephemeralPublicKey, iv, ciphertext }`. + * 3. Client reverses: + * shared = X25519(ourPrivate, theirEphemeralPublic) + * key = HKDF-SHA256(shared, salt=none, info=none, 32 bytes) + * plain = AES-256-GCM.decrypt(key, iv, ciphertext || 16-byte tag) + * + * This module is platform-agnostic: it uses `@noble/*` + `@scure/base` and + * avoids `Buffer` / `atob` so it runs unchanged on mobile, extension, and web. + */ + +import { gcm } from '@noble/ciphers/aes'; +import { x25519 } from '@noble/curves/ed25519'; +import { hkdf } from '@noble/hashes/hkdf'; +import { sha256 } from '@noble/hashes/sha2'; +import { bytesToHex, hexToBytes } from '@noble/hashes/utils'; +import { base64 } from '@scure/base'; + +/** + * An X25519 keypair used for the Check/Auth frame key exchange. + */ +export type X25519KeyPair = { + /** Raw 32-byte X25519 private (scalar) key. Never leaves the device. */ + privateKey: Uint8Array; + /** Raw 32-byte X25519 public key. */ + publicKey: Uint8Array; + /** Hex-encoded public key, ready to drop into a Check/Auth frame URL. */ + publicKeyHex: string; +}; + +/** + * The encrypted-credentials envelope returned by the Check/Auth frames. Binary + * fields may be hex or base64; the IV field may be named `iv` or `nonce`. + */ +export type EncryptedCredentialsEnvelope = { + /** Ephemeral public key produced by the frame for this exchange (32 bytes). */ + ephemeralPublicKey: string; + /** Per-message IV. May be provided as `iv` or `nonce`. */ + iv?: string; + nonce?: string; + /** Ciphertext (plaintext + 16-byte GCM auth tag). */ + ciphertext: string; + /** Optional explicit encoding hint. Defaults to auto-detect. */ + encoding?: 'hex' | 'base64'; +}; + +/** + * Decrypted Check/Auth frame credentials. + * + * - `accessToken` is the Bearer token for the identity API. + * - `clientToken` is the short-lived token consumed by the Auth frame when the + * Check frame returns `connectionRequired`. + */ +export type DecryptedCredentials = { + accessToken?: string; + clientToken?: string; + [key: string]: unknown; +}; + +/** + * Result of a successful decryption — the credentials plus the `method` that + * authenticated. + */ +export type DecryptResult = { + credentials: DecryptedCredentials; + method: string; +}; + +/** + * Generate a fresh X25519 keypair. The private key never leaves the device; + * only `publicKeyHex` is sent to the vendor via the frame URL. + * + * @returns The generated keypair. + */ +export function generateKeyPair(): X25519KeyPair { + const privateKey = x25519.utils.randomSecretKey(); + const publicKey = x25519.getPublicKey(privateKey); + return { + privateKey, + publicKey, + publicKeyHex: bytesToHex(publicKey), + }; +} + +/** + * Decode a base64 / base64url string to bytes without relying on `atob` or + * `Buffer`. + * + * @param value - The (possibly url-safe, possibly unpadded) base64 string. + * @returns The decoded bytes. + */ +function base64ToBytes(value: string): Uint8Array { + const normalized = value.replace(/-/gu, '+').replace(/_/gu, '/'); + const padded = normalized.padEnd( + normalized.length + ((4 - (normalized.length % 4)) % 4), + '=', + ); + return base64.decode(padded); +} + +/** + * Decode a binary envelope field that may be hex or base64. + * + * @param value - The encoded field. + * @param encoding - Optional explicit encoding; auto-detected when omitted. + * @returns The decoded bytes. + */ +function decodeBinary(value: string, encoding?: 'hex' | 'base64'): Uint8Array { + const isHex = + encoding === 'hex' || + (encoding === undefined && /^[0-9a-fA-F]+$/u.test(value)); + if (isHex) { + return hexToBytes(value); + } + return base64ToBytes(value); +} + +/** + * Coerce the `credentials` field into a structured envelope. The frame may + * deliver it as an object, a JSON string, or base64(JSON). + * + * @param input - The raw credentials value. + * @returns The normalized envelope. + * @throws If the value is not a structured or base64(JSON) envelope, or is + * missing required fields. + */ +function normalizeEnvelope( + input: EncryptedCredentialsEnvelope | string, +): EncryptedCredentialsEnvelope { + let value: unknown = input; + + if (typeof value === 'string') { + const trimmed = value.trim(); + if (trimmed.startsWith('{')) { + try { + value = JSON.parse(trimmed); + } catch { + throw new Error( + `credentials looked like JSON but failed to parse (preview: "${trimmed.slice( + 0, + 64, + )}").`, + ); + } + } else { + let decodedText: string | null = null; + try { + decodedText = new TextDecoder().decode(base64ToBytes(trimmed)); + } catch { + decodedText = null; + } + const decodedTrimmed = decodedText?.trim(); + if (decodedTrimmed?.startsWith('{')) { + try { + value = JSON.parse(decodedTrimmed); + } catch { + throw new Error( + `credentials base64-decoded to non-JSON (preview: "${decodedTrimmed.slice( + 0, + 64, + )}").`, + ); + } + } else { + throw new Error( + `credentials is an opaque string, not a structured or base64(JSON) envelope (preview: "${trimmed.slice( + 0, + 64, + )}").`, + ); + } + } + } + + const env = value as Partial; + if (!env.ephemeralPublicKey || !(env.iv ?? env.nonce) || !env.ciphertext) { + const keys = + value && typeof value === 'object' + ? Object.keys(value).join(', ') + : typeof value; + throw new Error( + `credentials envelope missing required fields (ephemeralPublicKey/iv/ciphertext). Got: ${keys}`, + ); + } + return env as EncryptedCredentialsEnvelope; +} + +/** + * X25519 ECDH to AES-256-GCM decryption. + * + * @param theirPublicKey - The frame's ephemeral public key. + * @param iv - The 12-byte GCM IV. + * @param ciphertext - The ciphertext including the 16-byte auth tag. + * @param ourPrivateKey - Our X25519 private key. + * @returns The decrypted credentials and method. + */ +function aesGcmDecrypt( + theirPublicKey: Uint8Array, + iv: Uint8Array, + ciphertext: Uint8Array, + ourPrivateKey: Uint8Array, +): DecryptResult { + const shared = x25519.getSharedSecret(ourPrivateKey, theirPublicKey); + const key = hkdf(sha256, shared, undefined, undefined, 32); + const plaintext = gcm(key, iv).decrypt(ciphertext); + const text = new TextDecoder().decode(plaintext); + return { + credentials: JSON.parse(text) as DecryptedCredentials, + method: 'aes-256-gcm/hkdf-sha256', + }; +} + +/** + * Decrypt a Check/Auth frame credentials envelope using our X25519 private + * key. + * + * @param rawEnvelope - The raw envelope (object, JSON string, or base64(JSON)). + * @param ourPrivateKey - Our X25519 private key. + * @returns The parsed credentials and the method that authenticated. + * @throws If the envelope is malformed or the IV length is not 12 bytes. + */ +export function decryptCredentials( + rawEnvelope: EncryptedCredentialsEnvelope | string, + ourPrivateKey: Uint8Array, +): DecryptResult { + const envelope = normalizeEnvelope(rawEnvelope); + const theirPublicKey = decodeBinary( + envelope.ephemeralPublicKey, + envelope.encoding, + ); + // `normalizeEnvelope` guarantees one of `iv` / `nonce` is present. + const ivField = (envelope.iv ?? envelope.nonce) as string; + const iv = decodeBinary(ivField, envelope.encoding); + const ciphertext = decodeBinary(envelope.ciphertext, envelope.encoding); + + if (iv.length !== 12) { + throw new Error( + `Unexpected IV length ${iv.length} (expected 12 for AES-256-GCM).`, + ); + } + + return aesGcmDecrypt(theirPublicKey, iv, ciphertext, ourPrivateKey); +} diff --git a/packages/kyc-controller/src/index.test.ts b/packages/kyc-controller/src/index.test.ts new file mode 100644 index 00000000000..b95071359e1 --- /dev/null +++ b/packages/kyc-controller/src/index.test.ts @@ -0,0 +1,19 @@ +import * as packageExports from '.'; + +describe('@metamask/kyc-controller', () => { + it('exports the controller, service, selectors, and helpers', () => { + expect(packageExports).toMatchObject({ + KycController: expect.any(Function), + KycService: expect.any(Function), + getDefaultKycControllerState: expect.any(Function), + selectKycPhase: expect.any(Function), + selectKycSumSub: expect.any(Function), + selectIsKycRequiredForProduct: expect.any(Function), + alpha2ToAlpha3: expect.any(Function), + generateKeyPair: expect.any(Function), + decryptCredentials: expect.any(Function), + controllerName: 'KycController', + serviceName: 'KycService', + }); + }); +}); diff --git a/packages/kyc-controller/src/index.ts b/packages/kyc-controller/src/index.ts new file mode 100644 index 00000000000..816afb882b8 --- /dev/null +++ b/packages/kyc-controller/src/index.ts @@ -0,0 +1,76 @@ +export { + KycController, + getDefaultKycControllerState, + controllerName, +} from './KycController'; +export type { + KycControllerActions, + KycControllerEvents, + KycControllerGetStateAction, + KycControllerMessenger, + KycControllerOptions, + KycControllerState, + KycControllerStateChangeEvent, +} from './KycController'; +export type { + KycControllerAcceptTermsAndStartSessionAction, + KycControllerBuildAuthFrameUrlAction, + KycControllerBuildCheckFrameUrlAction, + KycControllerBuildResetFrameUrlAction, + KycControllerCheckKycRequiredAction, + KycControllerClearSavedTermsAction, + KycControllerGetKycStatusAction, + KycControllerHandleFrameMessageAction, + KycControllerInitializeAction, + KycControllerLoadDisclaimersAction, + KycControllerResetAction, + KycControllerStartSumSubAction, +} from './KycController-method-action-types'; + +export { KycService, serviceName } from './KycService'; +export type { + CheckKycRequiredParams, + CreateSessionParams, + CreateUkycSessionParams, + KycServiceActions, + KycServiceEnvironment, + KycServiceEvents, + KycServiceMessenger, + KycServiceOptions, + SubmitWrappedKeyParams, + UkycSessionResponse, + WrappedKeyResponse, +} from './KycService'; +export type { + KycServiceCheckKycRequiredAction, + KycServiceCreateSessionAction, + KycServiceCreateUkycSessionAction, + KycServiceFetchDisclaimersAction, + KycServiceGetGeoCountryAction, + KycServiceSubmitWrappedKeyAction, +} from './KycService-method-action-types'; + +export { + selectIsKycRequiredForProduct, + selectKycPhase, + selectKycSumSub, +} from './selectors'; + +export { alpha2ToAlpha3, ALPHA2_TO_ALPHA3 } from './countryCodes'; +export { decryptCredentials, generateKeyPair } from './crypto'; +export type { + DecryptedCredentials, + DecryptResult, + EncryptedCredentialsEnvelope, + X25519KeyPair, +} from './crypto'; + +export type { + KycDisclaimer, + KycPhase, + KycProduct, + KycSumSubLaunchParams, + KycSumSubLauncher, + KycSumSubStatus, + KycVendor, +} from './types'; diff --git a/packages/kyc-controller/src/selectors.test.ts b/packages/kyc-controller/src/selectors.test.ts new file mode 100644 index 00000000000..725ec0e592d --- /dev/null +++ b/packages/kyc-controller/src/selectors.test.ts @@ -0,0 +1,33 @@ +import { getDefaultKycControllerState } from './KycController'; +import { + selectIsKycRequiredForProduct, + selectKycPhase, + selectKycSumSub, +} from './selectors'; + +describe('selectors', () => { + it('selectKycPhase returns the current phase', () => { + const state = { ...getDefaultKycControllerState(), phase: 'form' as const }; + expect(selectKycPhase(state)).toBe('form'); + }); + + it('selectKycSumSub returns the sub-flow state', () => { + const state = getDefaultKycControllerState(); + expect(selectKycSumSub(state)).toStrictEqual(state.sumsub); + }); + + describe('selectIsKycRequiredForProduct', () => { + it('returns the cached requirement for a product', () => { + const state = { + ...getDefaultKycControllerState(), + kycRequiredByProduct: { ramps: true }, + }; + expect(selectIsKycRequiredForProduct('ramps')(state)).toBe(true); + }); + + it('returns undefined when the product has not been checked', () => { + const state = getDefaultKycControllerState(); + expect(selectIsKycRequiredForProduct('card')(state)).toBeUndefined(); + }); + }); +}); diff --git a/packages/kyc-controller/src/selectors.ts b/packages/kyc-controller/src/selectors.ts new file mode 100644 index 00000000000..658169a15fc --- /dev/null +++ b/packages/kyc-controller/src/selectors.ts @@ -0,0 +1,42 @@ +import { createSelector } from 'reselect'; + +import type { KycControllerState } from './KycController'; +import type { KycProduct } from './types'; + +const selectKycRequiredByProduct = ( + state: KycControllerState, +): KycControllerState['kycRequiredByProduct'] => state.kycRequiredByProduct; + +/** + * Selects the current flow phase. + * + * @param state - The KycController state. + * @returns The current phase. + */ +export const selectKycPhase = ( + state: KycControllerState, +): KycControllerState['phase'] => state.phase; + +/** + * Selects the SumSub sub-flow state. + * + * @param state - The KycController state. + * @returns The SumSub state. + */ +export const selectKycSumSub = ( + state: KycControllerState, +): KycControllerState['sumsub'] => state.sumsub; + +/** + * Creates a selector that returns whether KYC is required for a product. + * + * @param product - The consuming feature. + * @returns A selector returning the cached requirement, or `undefined`. + */ +export const selectIsKycRequiredForProduct = ( + product: KycProduct, +): ((state: KycControllerState) => boolean | undefined) => + createSelector( + [selectKycRequiredByProduct], + (map): boolean | undefined => map[product], + ); diff --git a/packages/kyc-controller/src/types.ts b/packages/kyc-controller/src/types.ts new file mode 100644 index 00000000000..4d41052f8fa --- /dev/null +++ b/packages/kyc-controller/src/types.ts @@ -0,0 +1,119 @@ +/** + * Shared types for the KYC controller and service. + * + * The KYC flow is vendor-backed (currently MoonPay for identity + SumSub for + * document verification) but the surface exposed to consumers (ramps, card) is + * intentionally vendor-neutral so a future vendor swap does not ripple out. + */ + +/** + * A MetaMask feature that consumes KYC. Used to key the per-product + * "is KYC required" cache so ramps and card can share one controller. + */ +export type KycProduct = 'ramps' | 'card'; + +/** + * Identity vendors supported behind the KYC surface. + */ +export type KycVendor = 'moonpay'; + +/** + * Phases of the end-to-end identity flow. + * + * - `idle` — nothing started. + * - `terms` — waiting for the customer to accept the vendor terms. + * - `session` — creating the vendor session. + * - `check` — running the invisible connection-check frame. + * - `auth` — running the visible authentication (OTP) frame. + * - `form` — authenticated; ready to submit the identity check. + * - `submit` — submitting the KYC-required check. + * - `done` — flow complete; see `kycRequiredByProduct` / `sumsub`. + * - `error` — flow halted; see `error`. + */ +export type KycPhase = + | 'idle' + | 'terms' + | 'session' + | 'check' + | 'auth' + | 'form' + | 'submit' + | 'done' + | 'error'; + +/** + * Progress of the SumSub document-verification sub-flow. + */ +export type KycSumSubStatus = + | 'idle' + | 'creatingSession' + | 'fetchingToken' + | 'launching' + | 'inProgress' + | 'complete' + | 'failed'; + +/** + * A single disclaimer/term the customer must accept before a session is + * created. + */ +export type KycDisclaimer = { + id: string; + // Mirrors the vendor API response field, which is snake_case. + // eslint-disable-next-line @typescript-eslint/naming-convention + display_name: string; + url: string; +}; + +/** + * Parameters passed to a platform SumSub launcher. + */ +export type KycSumSubLaunchParams = { + /** + * The applicant access token used to initialize the SumSub SDK. + */ + applicantAccessToken: string; + + /** + * Called by the SDK when the access token expires; must resolve with a fresh + * applicant access token. + */ + onTokenExpiration: () => Promise; + + /** + * Called when the SDK reports a status transition. + */ + onStatusChange?: (prevStatus: string, newStatus: string) => void; + + /** + * BCP-47 locale for the SDK UI. + */ + locale?: string; + + /** + * Enables SDK debug logging. + */ + debug?: boolean; +}; + +/** + * Platform adapter that launches the native/web SumSub SDK. + * + * The KYC controller is platform-agnostic and does not import any SDK; each + * client (mobile / extension / web) injects an implementation of this + * interface. The controller owns all orchestration (session creation, token + * exchange, token refresh, state) and only delegates the actual SDK + * presentation to `launch`. + */ +export type KycSumSubLauncher = { + /** + * Whether the underlying SDK is available in the current runtime (e.g. the + * native module is linked). When `false`, `startSumSub` fails fast. + */ + isAvailable(): boolean; + + /** + * Presents the SumSub verification flow and resolves with the SDK result. + */ + launch(params: KycSumSubLaunchParams): Promise>; +}; diff --git a/packages/kyc-controller/tsconfig.build.json b/packages/kyc-controller/tsconfig.build.json new file mode 100644 index 00000000000..b36e81be15e --- /dev/null +++ b/packages/kyc-controller/tsconfig.build.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.packages.build.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist", + "rootDir": "./src" + }, + "references": [ + { "path": "../base-controller/tsconfig.build.json" }, + { "path": "../controller-utils/tsconfig.build.json" }, + { "path": "../geolocation-controller/tsconfig.build.json" }, + { "path": "../messenger/tsconfig.build.json" }, + { "path": "../profile-sync-controller/tsconfig.build.json" } + ], + "include": ["../../types", "./src"] +} diff --git a/packages/kyc-controller/tsconfig.json b/packages/kyc-controller/tsconfig.json new file mode 100644 index 00000000000..be54252e245 --- /dev/null +++ b/packages/kyc-controller/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "baseUrl": "./" + }, + "references": [ + { "path": "../base-controller" }, + { "path": "../controller-utils" }, + { "path": "../geolocation-controller" }, + { "path": "../messenger" }, + { "path": "../profile-sync-controller" } + ], + "include": ["../../types", "./src"] +} diff --git a/packages/kyc-controller/typedoc.json b/packages/kyc-controller/typedoc.json new file mode 100644 index 00000000000..c9da015dbf8 --- /dev/null +++ b/packages/kyc-controller/typedoc.json @@ -0,0 +1,7 @@ +{ + "entryPoints": ["./src/index.ts"], + "excludePrivate": true, + "hideGenerator": true, + "out": "docs", + "tsconfig": "./tsconfig.build.json" +} diff --git a/tsconfig.build.json b/tsconfig.build.json index 5e805550062..05d63286108 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -145,6 +145,9 @@ { "path": "./packages/keyring-controller/tsconfig.build.json" }, + { + "path": "./packages/kyc-controller/tsconfig.build.json" + }, { "path": "./packages/local-node-utils/tsconfig.build.json" }, diff --git a/tsconfig.json b/tsconfig.json index 83755477993..52cdfd86d76 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -140,6 +140,9 @@ { "path": "./packages/keyring-controller" }, + { + "path": "./packages/kyc-controller" + }, { "path": "./packages/local-node-utils" }, diff --git a/yarn.lock b/yarn.lock index 8b0f0e77cc5..2a784809575 100644 --- a/yarn.lock +++ b/yarn.lock @@ -242,20 +242,7 @@ __metadata: languageName: node linkType: hard -"@asamuzakjp/css-color@npm:^3.2.0": - version: 3.2.0 - resolution: "@asamuzakjp/css-color@npm:3.2.0" - dependencies: - "@csstools/css-calc": "npm:^2.1.3" - "@csstools/css-color-parser": "npm:^3.0.9" - "@csstools/css-parser-algorithms": "npm:^3.0.4" - "@csstools/css-tokenizer": "npm:^3.0.3" - lru-cache: "npm:^10.4.3" - checksum: 10/870f661460173174fef8bfebea0799ba26566f3aa7b307e5adabb7aae84fed2da68e40080104ed0c83b43c5be632ee409e65396af13bfe948a3ef4c2c729ecd9 - languageName: node - linkType: hard - -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.29.7": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.29.7": version: 7.29.7 resolution: "@babel/code-frame@npm:7.29.7" dependencies: @@ -273,7 +260,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.21.3, @babel/core@npm:^7.23.2, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.9, @babel/core@npm:^7.27.4": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.21.3, @babel/core@npm:^7.23.2, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.9": version: 7.29.7 resolution: "@babel/core@npm:7.29.7" dependencies: @@ -296,7 +283,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.25.9, @babel/generator@npm:^7.27.5, @babel/generator@npm:^7.29.7": +"@babel/generator@npm:^7.25.9, @babel/generator@npm:^7.29.7, @babel/generator@npm:^7.7.2": version: 7.29.7 resolution: "@babel/generator@npm:7.29.7" dependencies: @@ -510,7 +497,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.29.7": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.29.7": version: 7.29.7 resolution: "@babel/parser@npm:7.29.7" dependencies: @@ -700,7 +687,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.27.1, @babel/plugin-syntax-jsx@npm:^7.29.7": +"@babel/plugin-syntax-jsx@npm:^7.29.7, @babel/plugin-syntax-jsx@npm:^7.7.2": version: 7.29.7 resolution: "@babel/plugin-syntax-jsx@npm:7.29.7" dependencies: @@ -799,7 +786,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.27.1, @babel/plugin-syntax-typescript@npm:^7.29.7": +"@babel/plugin-syntax-typescript@npm:^7.29.7, @babel/plugin-syntax-typescript@npm:^7.7.2": version: 7.29.7 resolution: "@babel/plugin-syntax-typescript@npm:7.29.7" dependencies: @@ -1646,7 +1633,7 @@ __metadata: languageName: node linkType: hard -"@babel/template@npm:^7.29.7": +"@babel/template@npm:^7.29.7, @babel/template@npm:^7.3.3": version: 7.29.7 resolution: "@babel/template@npm:7.29.7" dependencies: @@ -1672,7 +1659,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.23.0, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.7, @babel/types@npm:^7.4.4": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.23.0, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.7, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.29.7 resolution: "@babel/types@npm:7.29.7" dependencies: @@ -1740,7 +1727,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-calc@npm:^2.1.3, @csstools/css-calc@npm:^2.1.4": +"@csstools/css-calc@npm:^2.1.4": version: 2.1.4 resolution: "@csstools/css-calc@npm:2.1.4" peerDependencies: @@ -1750,7 +1737,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-color-parser@npm:^3.0.9, @csstools/css-color-parser@npm:^3.1.0": +"@csstools/css-color-parser@npm:^3.1.0": version: 3.1.0 resolution: "@csstools/css-color-parser@npm:3.1.0" dependencies: @@ -1763,7 +1750,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-parser-algorithms@npm:^3.0.4, @csstools/css-parser-algorithms@npm:^3.0.5": +"@csstools/css-parser-algorithms@npm:^3.0.5": version: 3.0.5 resolution: "@csstools/css-parser-algorithms@npm:3.0.5" peerDependencies: @@ -1772,7 +1759,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-tokenizer@npm:^3.0.3, @csstools/css-tokenizer@npm:^3.0.4": +"@csstools/css-tokenizer@npm:^3.0.4": version: 3.0.4 resolution: "@csstools/css-tokenizer@npm:3.0.4" checksum: 10/eb6c84c086312f6bb8758dfe2c85addd7475b0927333c5e39a4d59fb210b9810f8c346972046f95e60a721329cffe98895abe451e51de753ad1ca7a8c24ec65f @@ -4714,205 +4701,152 @@ __metadata: languageName: node linkType: hard -"@jest/console@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/console@npm:30.4.1" +"@jest/console@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/console@npm:29.7.0" dependencies: - "@jest/types": "npm:30.4.1" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - chalk: "npm:^4.1.2" - jest-message-util: "npm:30.4.1" - jest-util: "npm:30.4.1" + chalk: "npm:^4.0.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" slash: "npm:^3.0.0" - checksum: 10/4eb463d29654c20716f5f9cde43e5d958cb3b9234477df57da5b3814c3f1a4a0ab611a8eaf4b5abc146190a012584d7025f445f3560ed62acd843fc95c0a0e65 + checksum: 10/4a80c750e8a31f344233cb9951dee9b77bf6b89377cb131f8b3cde07ff218f504370133a5963f6a786af4d2ce7f85642db206ff7a15f99fe58df4c38ac04899e languageName: node linkType: hard -"@jest/core@npm:30.4.2": - version: 30.4.2 - resolution: "@jest/core@npm:30.4.2" +"@jest/core@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/core@npm:29.7.0" dependencies: - "@jest/console": "npm:30.4.1" - "@jest/pattern": "npm:30.4.0" - "@jest/reporters": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" + "@jest/console": "npm:^29.7.0" + "@jest/reporters": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - ansi-escapes: "npm:^4.3.2" - chalk: "npm:^4.1.2" - ci-info: "npm:^4.2.0" - exit-x: "npm:^0.2.2" - fast-json-stable-stringify: "npm:^2.1.0" - graceful-fs: "npm:^4.2.11" - jest-changed-files: "npm:30.4.1" - jest-config: "npm:30.4.2" - jest-haste-map: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-resolve: "npm:30.4.1" - jest-resolve-dependencies: "npm:30.4.2" - jest-runner: "npm:30.4.2" - jest-runtime: "npm:30.4.2" - jest-snapshot: "npm:30.4.1" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - jest-watcher: "npm:30.4.1" - pretty-format: "npm:30.4.1" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + exit: "npm:^0.1.2" + graceful-fs: "npm:^4.2.9" + jest-changed-files: "npm:^29.7.0" + jest-config: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-resolve-dependencies: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + pretty-format: "npm:^29.7.0" slash: "npm:^3.0.0" + strip-ansi: "npm:^6.0.0" peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: node-notifier: optional: true - checksum: 10/ecc695392685ab56c6df5d29d6f7927141071d8f3f75e5f7c7664f0faded1307caf5daf051074252d5ddb9546bf2bfe3b0c63ca81fe6238dc34e1bb5f8a7a261 - languageName: node - linkType: hard - -"@jest/diff-sequences@npm:30.4.0": - version: 30.4.0 - resolution: "@jest/diff-sequences@npm:30.4.0" - checksum: 10/65c27937c10a7157899dad5d176806104286f9d55464f318955a0cee98db8aed6b8f70ad4aee7133468087146422cdd391d49b1e101ec543db3283ee4eb59c06 - languageName: node - linkType: hard - -"@jest/environment-jsdom-abstract@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/environment-jsdom-abstract@npm:30.4.1" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/fake-timers": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/jsdom": "npm:^21.1.7" - "@types/node": "npm:*" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - peerDependencies: - canvas: ^3.0.0 - jsdom: "*" - peerDependenciesMeta: - canvas: - optional: true - checksum: 10/e51537587162d0972df0abd6f0ce0b2cf5245029035e6b61b62da075547a37cd174254c315b456447a6419b22938623036b355c063498b7876f7ea0352404fa6 + checksum: 10/ab6ac2e562d083faac7d8152ec1cc4eccc80f62e9579b69ed40aedf7211a6b2d57024a6cd53c4e35fd051c39a236e86257d1d99ebdb122291969a0a04563b51e languageName: node linkType: hard -"@jest/environment@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/environment@npm:30.4.1" +"@jest/environment@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/environment@npm:29.7.0" dependencies: - "@jest/fake-timers": "npm:30.4.1" - "@jest/types": "npm:30.4.1" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - jest-mock: "npm:30.4.1" - checksum: 10/c25946fee29604f5aa24ea059bc3cc7bc4c8cdaf26db1ed6ffa4f28e37f5193cc4e868650c807d89caff4123e44d07b58200d4cb5960ebdb7d66531509d76359 + jest-mock: "npm:^29.7.0" + checksum: 10/90b5844a9a9d8097f2cf107b1b5e57007c552f64315da8c1f51217eeb0a9664889d3f145cdf8acf23a84f4d8309a6675e27d5b059659a004db0ea9546d1c81a8 languageName: node linkType: hard -"@jest/expect-utils@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/expect-utils@npm:30.4.1" +"@jest/expect-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect-utils@npm:29.7.0" dependencies: - "@jest/get-type": "npm:30.1.0" - checksum: 10/3f0337ec791d669cacd07594521f2da71b956712dfd0c0007253dd5e886ef640df510af1357878a80ac56f09d3db9fd68e3db66959f0fdb3add5f551dd7e0f35 + jest-get-type: "npm:^29.6.3" + checksum: 10/ef8d379778ef574a17bde2801a6f4469f8022a46a5f9e385191dc73bb1fc318996beaed4513fbd7055c2847227a1bed2469977821866534593a6e52a281499ee languageName: node linkType: hard -"@jest/expect@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/expect@npm:30.4.1" +"@jest/expect@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect@npm:29.7.0" dependencies: - expect: "npm:30.4.1" - jest-snapshot: "npm:30.4.1" - checksum: 10/40ae0317a3590ced7a7fd21c49e6b1af6b122e6a83822e643af83f02034dfed6485248cae08d6bcf9380039ba3824ac56db18478712c64ddf5f709ee23cf30cd + expect: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + checksum: 10/fea6c3317a8da5c840429d90bfe49d928e89c9e89fceee2149b93a11b7e9c73d2f6e4d7cdf647163da938fc4e2169e4490be6bae64952902bc7a701033fd4880 languageName: node linkType: hard -"@jest/fake-timers@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/fake-timers@npm:30.4.1" +"@jest/fake-timers@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/fake-timers@npm:29.7.0" dependencies: - "@jest/types": "npm:30.4.1" - "@sinonjs/fake-timers": "npm:^15.4.0" + "@jest/types": "npm:^29.6.3" + "@sinonjs/fake-timers": "npm:^10.0.2" "@types/node": "npm:*" - jest-message-util: "npm:30.4.1" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - checksum: 10/bc7aff23548395d6e7957bc24f699f921a9616f2357ab49616b0468c7b5e94e6ac4cbdd45d306f1a5d7f72e2a055294f52be3666e4c1da7c137874c5b226e1c6 - languageName: node - linkType: hard - -"@jest/get-type@npm:30.1.0": - version: 30.1.0 - resolution: "@jest/get-type@npm:30.1.0" - checksum: 10/e2a95fbb49ce2d15547db8af5602626caf9b05f62a5e583b4a2de9bd93a2bfe7175f9bbb2b8a5c3909ce261d467b6991d7265bb1d547cb60e7e97f571f361a70 - languageName: node - linkType: hard - -"@jest/globals@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/globals@npm:30.4.1" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/expect": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - jest-mock: "npm:30.4.1" - checksum: 10/5fe04b9c3b97f0061e4464201ee0dd674dd958843eb80542791d1c576c51f12aaa3f3b369e136d5fd3f8c716f9c9bbfbb76491a3cbc3c4efb3cc71063f909132 + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 10/9b394e04ffc46f91725ecfdff34c4e043eb7a16e1d78964094c9db3fde0b1c8803e45943a980e8c740d0a3d45661906de1416ca5891a538b0660481a3a828c27 languageName: node linkType: hard -"@jest/pattern@npm:30.4.0": - version: 30.4.0 - resolution: "@jest/pattern@npm:30.4.0" +"@jest/globals@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/globals@npm:29.7.0" dependencies: - "@types/node": "npm:*" - jest-regex-util: "npm:30.4.0" - checksum: 10/4fb1db0e586713708d2fcd79059315600978608483ef2d80e04a0a59b20b0d8de0d3f47cad950ff90bfb9ea3cb788709ee3d1eb225734e4dbf1c4b743c93d204 + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + jest-mock: "npm:^29.7.0" + checksum: 10/97dbb9459135693ad3a422e65ca1c250f03d82b2a77f6207e7fa0edd2c9d2015fbe4346f3dc9ebff1678b9d8da74754d4d440b7837497f8927059c0642a22123 languageName: node linkType: hard -"@jest/reporters@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/reporters@npm:30.4.1" +"@jest/reporters@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/reporters@npm:29.7.0" dependencies: "@bcoe/v8-coverage": "npm:^0.2.3" - "@jest/console": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@jridgewell/trace-mapping": "npm:^0.3.25" + "@jest/console": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@jridgewell/trace-mapping": "npm:^0.3.18" "@types/node": "npm:*" - chalk: "npm:^4.1.2" - collect-v8-coverage: "npm:^1.0.2" - exit-x: "npm:^0.2.2" - glob: "npm:^10.5.0" - graceful-fs: "npm:^4.2.11" + chalk: "npm:^4.0.0" + collect-v8-coverage: "npm:^1.0.0" + exit: "npm:^0.1.2" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" istanbul-lib-coverage: "npm:^3.0.0" istanbul-lib-instrument: "npm:^6.0.0" istanbul-lib-report: "npm:^3.0.0" - istanbul-lib-source-maps: "npm:^5.0.0" + istanbul-lib-source-maps: "npm:^4.0.0" istanbul-reports: "npm:^3.1.3" - jest-message-util: "npm:30.4.1" - jest-util: "npm:30.4.1" - jest-worker: "npm:30.4.1" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" slash: "npm:^3.0.0" - string-length: "npm:^4.0.2" + string-length: "npm:^4.0.1" + strip-ansi: "npm:^6.0.0" v8-to-istanbul: "npm:^9.0.1" peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: node-notifier: optional: true - checksum: 10/e14e3717c9fe49004b6406cc53554f90954345917bb5f077d23e3a2cecde26d90493e1522d95f63b6c14ef06fb63d45b695ac6400d63b96fecc7a80d1ef83f4d - languageName: node - linkType: hard - -"@jest/schemas@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/schemas@npm:30.4.1" - dependencies: - "@sinclair/typebox": "npm:^0.34.0" - checksum: 10/86e62c8fd8fc77535085f1ede3a416430a3740f78b8f88ec7d0ee4516b22daf3326ffc1ade9d5f7839bbde923aaf1b5ac430a42ed4bb1a38edc3de5005a58f51 + checksum: 10/a17d1644b26dea14445cedd45567f4ba7834f980be2ef74447204e14238f121b50d8b858fde648083d2cd8f305f81ba434ba49e37a5f4237a6f2a61180cc73dc languageName: node linkType: hard @@ -4925,87 +4859,61 @@ __metadata: languageName: node linkType: hard -"@jest/snapshot-utils@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/snapshot-utils@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - natural-compare: "npm:^1.4.0" - checksum: 10/8f17768702153267388b3043f358027385e591ac4668699bfce3547cb8e08ac146a074913bcddf68c0a4f7155e24a6d582d27f4592f5c3bd5f9fbc3f9182ef78 - languageName: node - linkType: hard - -"@jest/source-map@npm:30.0.1": - version: 30.0.1 - resolution: "@jest/source-map@npm:30.0.1" +"@jest/source-map@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/source-map@npm:29.6.3" dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.25" - callsites: "npm:^3.1.0" - graceful-fs: "npm:^4.2.11" - checksum: 10/161b27cdf8d9d80fd99374d55222b90478864c6990514be6ebee72b7184a034224c9aceed12c476f3a48d48601bf8ed2e0c047a5a81bd907dc192ebe71365ed4 + "@jridgewell/trace-mapping": "npm:^0.3.18" + callsites: "npm:^3.0.0" + graceful-fs: "npm:^4.2.9" + checksum: 10/bcc5a8697d471396c0003b0bfa09722c3cd879ad697eb9c431e6164e2ea7008238a01a07193dfe3cbb48b1d258eb7251f6efcea36f64e1ebc464ea3c03ae2deb languageName: node linkType: hard -"@jest/test-result@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/test-result@npm:30.4.1" +"@jest/test-result@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-result@npm:29.7.0" dependencies: - "@jest/console": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/istanbul-lib-coverage": "npm:^2.0.6" - collect-v8-coverage: "npm:^1.0.2" - checksum: 10/c420182d72cef64827981230b4c84b2de3f4312067e7baf1e3e13c501dc57f73faa09fed1a5ed1a6e96bc29f6c67ac2c14de5f973945f14853010729678cb44a + "@jest/console": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/istanbul-lib-coverage": "npm:^2.0.0" + collect-v8-coverage: "npm:^1.0.0" + checksum: 10/c073ab7dfe3c562bff2b8fee6cc724ccc20aa96bcd8ab48ccb2aa309b4c0c1923a9e703cea386bd6ae9b71133e92810475bb9c7c22328fc63f797ad3324ed189 languageName: node linkType: hard -"@jest/test-sequencer@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/test-sequencer@npm:30.4.1" +"@jest/test-sequencer@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-sequencer@npm:29.7.0" dependencies: - "@jest/test-result": "npm:30.4.1" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" + "@jest/test-result": "npm:^29.7.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" slash: "npm:^3.0.0" - checksum: 10/d911ef0c527c402d41537aa5f9754725a58732c1c6616401454633fd45729da0b2f01b4c50322b1b789a9f2d4edf3a24aecb0b2e4ca4d873c4335894b63bc5b0 + checksum: 10/4420c26a0baa7035c5419b0892ff8ffe9a41b1583ec54a10db3037cd46a7e29dd3d7202f8aa9d376e9e53be5f8b1bc0d16e1de6880a6d319b033b01dc4c8f639 languageName: node linkType: hard -"@jest/transform@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/transform@npm:30.4.1" +"@jest/transform@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/transform@npm:29.7.0" dependencies: - "@babel/core": "npm:^7.27.4" - "@jest/types": "npm:30.4.1" - "@jridgewell/trace-mapping": "npm:^0.3.25" - babel-plugin-istanbul: "npm:^7.0.1" - chalk: "npm:^4.1.2" + "@babel/core": "npm:^7.11.6" + "@jest/types": "npm:^29.6.3" + "@jridgewell/trace-mapping": "npm:^0.3.18" + babel-plugin-istanbul: "npm:^6.1.1" + chalk: "npm:^4.0.0" convert-source-map: "npm:^2.0.0" fast-json-stable-stringify: "npm:^2.1.0" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-util: "npm:30.4.1" - pirates: "npm:^4.0.7" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + pirates: "npm:^4.0.4" slash: "npm:^3.0.0" - write-file-atomic: "npm:^5.0.1" - checksum: 10/7b570451f6c26360f1b852c2281dcc4e36fe685dbc159cf5eabf83d49d6aae4569f444d38f3afb5b3b6e0b809eb41b65f3145c0cac5fee3eec9c9b178fb1f0ea - languageName: node - linkType: hard - -"@jest/types@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/types@npm:30.4.1" - dependencies: - "@jest/pattern": "npm:30.4.0" - "@jest/schemas": "npm:30.4.1" - "@types/istanbul-lib-coverage": "npm:^2.0.6" - "@types/istanbul-reports": "npm:^3.0.4" - "@types/node": "npm:*" - "@types/yargs": "npm:^17.0.33" - chalk: "npm:^4.1.2" - checksum: 10/cc0999508613487c6d0f55661cd342ebe7cfe579fa9917534b94310204358f03f94524f70f00b4fe3c6dd2ccd0fd44657615a1b9f420ab310d68b43964bff87c + write-file-atomic: "npm:^4.0.2" + checksum: 10/30f42293545ab037d5799c81d3e12515790bb58513d37f788ce32d53326d0d72ebf5b40f989e6896739aa50a5f77be44686e510966370d58511d5ad2637c68c1 languageName: node linkType: hard @@ -5080,7 +4988,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": version: 0.3.31 resolution: "@jridgewell/trace-mapping@npm:0.3.31" dependencies: @@ -5531,12 +5439,12 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" fast-deep-equal: "npm:^3.1.3" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5568,14 +5476,14 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/readable-stream": "npm:^2.3.0" deepmerge: "npm:^4.2.2" ethereum-cryptography: "npm:^2.1.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5609,10 +5517,10 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5630,10 +5538,10 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5650,12 +5558,12 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5674,11 +5582,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5694,10 +5602,10 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5720,10 +5628,10 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5741,11 +5649,11 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nanoid: "npm:^3.3.8" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5753,7 +5661,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/assets-controller@npm:^11.2.0, @metamask/assets-controller@workspace:packages/assets-controller": +"@metamask/assets-controller@npm:^11.1.0, @metamask/assets-controller@workspace:packages/assets-controller": version: 0.0.0-use.local resolution: "@metamask/assets-controller@workspace:packages/assets-controller" dependencies: @@ -5762,12 +5670,12 @@ __metadata: "@ethersproject/providers": "npm:^5.7.0" "@metamask/account-tree-controller": "npm:^7.5.5" "@metamask/accounts-controller": "npm:^39.0.5" - "@metamask/assets-controllers": "npm:^110.0.0" + "@metamask/assets-controllers": "npm:^109.4.1" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/client-controller": "npm:^1.0.1" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/core-backend": "npm:^7.0.0" + "@metamask/core-backend": "npm:^6.5.0" "@metamask/keyring-api": "npm:^23.5.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-internal-api": "npm:^11.0.1" @@ -5782,18 +5690,18 @@ __metadata: "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/snaps-controllers": "npm:^19.0.0" "@metamask/snaps-utils": "npm:^12.1.2" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/lodash": "npm:^4.14.191" async-mutex: "npm:^0.5.0" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" p-limit: "npm:^3.1.0" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5801,7 +5709,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/assets-controllers@npm:^110.0.0, @metamask/assets-controllers@workspace:packages/assets-controllers": +"@metamask/assets-controllers@npm:^109.4.1, @metamask/assets-controllers@workspace:packages/assets-controllers": version: 0.0.0-use.local resolution: "@metamask/assets-controllers@workspace:packages/assets-controllers" dependencies: @@ -5821,7 +5729,7 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/contract-metadata": "npm:^2.4.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/core-backend": "npm:^7.0.0" + "@metamask/core-backend": "npm:^6.5.0" "@metamask/eth-query": "npm:^4.0.0" "@metamask/ethjs-provider-http": "npm:^0.3.0" "@metamask/keyring-api": "npm:^23.5.0" @@ -5844,12 +5752,12 @@ __metadata: "@metamask/snaps-sdk": "npm:^11.0.0" "@metamask/snaps-utils": "npm:^12.1.2" "@metamask/storage-service": "npm:^1.0.2" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/bn.js": "npm:^5.1.5" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/lodash": "npm:^4.14.191" "@types/node": "npm:^16.18.54" "@types/uuid": "npm:^8.3.0" @@ -5858,14 +5766,14 @@ __metadata: bn.js: "npm:^5.2.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" lodash: "npm:^4.17.21" multiformats: "npm:^9.9.0" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" single-call-balance-checker-abi: "npm:^1.0.0" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5923,11 +5831,11 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5966,11 +5874,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5988,15 +5896,15 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/lodash": "npm:^4.14.191" cockatiel: "npm:^3.1.2" deepmerge: "npm:^4.2.2" fast-deep-equal: "npm:^3.1.3" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6011,10 +5919,10 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/local-node-utils": "npm:^1.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6024,7 +5932,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/bridge-controller@npm:^78.0.0, @metamask/bridge-controller@workspace:packages/bridge-controller": +"@metamask/bridge-controller@npm:^77.6.0, @metamask/bridge-controller@workspace:packages/bridge-controller": version: 0.0.0-use.local resolution: "@metamask/bridge-controller@workspace:packages/bridge-controller" dependencies: @@ -6034,13 +5942,13 @@ __metadata: "@ethersproject/contracts": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.0" "@metamask/accounts-controller": "npm:^39.0.5" - "@metamask/assets-controller": "npm:^11.2.0" - "@metamask/assets-controllers": "npm:^110.0.0" + "@metamask/assets-controller": "npm:^11.1.0" + "@metamask/assets-controllers": "npm:^109.4.1" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" "@metamask/eth-json-rpc-provider": "npm:^6.0.1" - "@metamask/gas-fee-controller": "npm:^26.3.0" + "@metamask/gas-fee-controller": "npm:^26.2.4" "@metamask/keyring-api": "npm:^23.5.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" @@ -6051,18 +5959,18 @@ __metadata: "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/snaps-controllers": "npm:^19.0.0" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6078,9 +5986,9 @@ __metadata: "@metamask/accounts-controller": "npm:^39.0.5" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/bridge-controller": "npm:^78.0.0" + "@metamask/bridge-controller": "npm:^77.6.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/gas-fee-controller": "npm:^26.3.0" + "@metamask/gas-fee-controller": "npm:^26.2.4" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" @@ -6088,17 +5996,17 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/snaps-controllers": "npm:^19.0.0" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6124,10 +6032,10 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/eslint": "npm:^8.44.7" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6147,11 +6055,11 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6159,7 +6067,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/chomp-api-service@npm:^4.0.0, @metamask/chomp-api-service@workspace:packages/chomp-api-service": +"@metamask/chomp-api-service@npm:^3.1.0, @metamask/chomp-api-service@workspace:packages/chomp-api-service": version: 0.0.0-use.local resolution: "@metamask/chomp-api-service@workspace:packages/chomp-api-service" dependencies: @@ -6171,11 +6079,11 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6195,10 +6103,10 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6214,11 +6122,11 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6234,15 +6142,15 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/contract-metadata": "npm:^2.4.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/core-backend": "npm:^7.0.0" + "@metamask/core-backend": "npm:^6.5.0" "@metamask/keyring-api": "npm:^23.5.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6261,12 +6169,12 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6283,11 +6191,11 @@ __metadata: "@metamask/json-rpc-engine": "npm:^10.5.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6295,7 +6203,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/config-registry-controller@npm:^0.4.1, @metamask/config-registry-controller@workspace:packages/config-registry-controller": +"@metamask/config-registry-controller@workspace:packages/config-registry-controller": version: 0.0.0-use.local resolution: "@metamask/config-registry-controller@workspace:packages/config-registry-controller" dependencies: @@ -6311,12 +6219,12 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6332,11 +6240,11 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6385,7 +6293,7 @@ __metadata: "@spruceid/siwe-parser": "npm:2.1.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/bn.js": "npm:^5.1.5" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/lodash": "npm:^4.14.191" bignumber.js: "npm:^9.1.2" bn.js: "npm:^5.2.1" @@ -6393,11 +6301,11 @@ __metadata: deepmerge: "npm:^4.2.2" eth-ens-namehash: "npm:^2.0.8" fast-deep-equal: "npm:^3.1.3" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6407,7 +6315,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/core-backend@npm:^7.0.0, @metamask/core-backend@workspace:packages/core-backend": +"@metamask/core-backend@npm:^6.5.0, @metamask/core-backend@workspace:packages/core-backend": version: 0.0.0-use.local resolution: "@metamask/core-backend@workspace:packages/core-backend" dependencies: @@ -6421,12 +6329,12 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6453,14 +6361,13 @@ __metadata: "@metamask/skills": "npm:^0.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/lodash": "npm:^4.14.191" "@types/node": "npm:^16.18.54" "@types/semver": "npm:^7" "@typescript-eslint/eslint-plugin": "npm:^8.48.0" "@typescript-eslint/parser": "npm:^8.48.0" "@yarnpkg/types": "npm:^4.0.0" - bats: "npm:^1.13.0" comment-json: "npm:^4.5.1" eslint: "npm:^9.39.1" eslint-config-prettier: "npm:^9.1.0" @@ -6473,13 +6380,14 @@ __metadata: eslint-plugin-promise: "npm:^7.1.0" execa: "npm:^5.0.0" isomorphic-fetch: "npm:^3.0.0" - jest: "npm:^30.4.2" - jest-silent-reporter: "npm:^0.6.0" + jest: "npm:^29.7.0" + jest-silent-reporter: "npm:^0.5.0" knip: "npm:^6.23.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" oxfmt: "npm:^0.44.0" prettier: "npm:^3.3.3" + prettier-2: "npm:prettier@^2.8.8" rimraf: "npm:^5.0.5" semver: "npm:^7.6.3" simple-git-hooks: "npm:^2.8.0" @@ -6532,10 +6440,10 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6575,13 +6483,13 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/stake-sdk": "npm:^3.2.1" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6599,15 +6507,15 @@ __metadata: "@metamask/preferences-controller": "npm:^23.1.0" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" klona: "npm:^2.0.6" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6626,10 +6534,10 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6649,11 +6557,11 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6673,11 +6581,11 @@ __metadata: "@metamask/network-controller": "npm:^34.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" punycode: "npm:^2.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6758,12 +6666,12 @@ __metadata: "@metamask/safe-event-emitter": "npm:^3.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/json-rpc-random-id": "npm:^1.0.1" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" json-rpc-random-id: "npm:^1.0.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6830,10 +6738,10 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deep-freeze-strict: "npm:^1.1.1" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" klona: "npm:^2.0.6" safe-stable-stringify: "npm:^2.4.3" tsx: "npm:^4.20.5" @@ -6867,12 +6775,12 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" ethers: "npm:^6.12.0" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nanoid: "npm:^3.3.8" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typescript: "npm:~5.3.3" @@ -7108,16 +7016,16 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^6.1.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/unzipper": "npm:^0.10.10" "@types/yargs": "npm:^17.0.32" "@types/yargs-parser": "npm:^21.0.3" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" minipass: "npm:^7.1.2" nock: "npm:^13.3.1" tar: "npm:^7.4.3" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7131,7 +7039,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/gas-fee-controller@npm:^26.3.0, @metamask/gas-fee-controller@workspace:packages/gas-fee-controller": +"@metamask/gas-fee-controller@npm:^26.2.4, @metamask/gas-fee-controller@workspace:packages/gas-fee-controller": version: 0.0.0-use.local resolution: "@metamask/gas-fee-controller@workspace:packages/gas-fee-controller" dependencies: @@ -7147,15 +7055,15 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/bn.js": "npm:^5.1.5" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/jest-when": "npm:^2.7.3" "@types/uuid": "npm:^8.3.0" bn.js: "npm:^5.2.1" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - jest-when: "npm:^3.7.0" + jest: "npm:^29.7.0" + jest-when: "npm:^3.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7183,13 +7091,13 @@ __metadata: "@metamask/snaps-controllers": "npm:^19.0.0" "@metamask/snaps-sdk": "npm:^11.0.0" "@metamask/snaps-utils": "npm:^12.1.2" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7206,10 +7114,10 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7224,10 +7132,10 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/local-node-utils": "npm:^1.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7250,12 +7158,12 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deep-freeze-strict: "npm:^1.1.1" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" klona: "npm:^2.0.6" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typescript: "npm:~5.3.3" @@ -7271,13 +7179,13 @@ __metadata: "@metamask/safe-event-emitter": "npm:^3.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/readable-stream": "npm:^2.3.0" deepmerge: "npm:^4.2.2" extension-port-stream: "npm:^3.0.0" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" readable-stream: "npm:^3.6.2" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7334,15 +7242,15 @@ __metadata: "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" ethereumjs-wallet: "npm:^1.0.1" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - jest-environment-node: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7438,17 +7346,48 @@ __metadata: languageName: node linkType: hard +"@metamask/kyc-controller@workspace:packages/kyc-controller": + version: 0.0.0-use.local + resolution: "@metamask/kyc-controller@workspace:packages/kyc-controller" + dependencies: + "@metamask/auto-changelog": "npm:^6.1.0" + "@metamask/base-controller": "npm:^9.1.0" + "@metamask/controller-utils": "npm:^12.3.0" + "@metamask/geolocation-controller": "npm:^0.1.3" + "@metamask/messenger": "npm:^2.0.0" + "@metamask/profile-sync-controller": "npm:^28.3.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.11.0" + "@noble/ciphers": "npm:^1.3.0" + "@noble/curves": "npm:^1.9.2" + "@noble/hashes": "npm:^1.8.0" + "@scure/base": "npm:^1.2.6" + "@ts-bridge/cli": "npm:^0.6.4" + "@types/jest": "npm:^29.5.14" + chokidar-cli: "npm:^3.0.0" + deepmerge: "npm:^4.2.2" + jest: "npm:^29.7.0" + nock: "npm:^13.3.1" + reselect: "npm:^5.1.1" + ts-jest: "npm:^29.2.5" + tsx: "npm:^4.20.5" + typedoc: "npm:^0.25.13" + typedoc-plugin-missing-exports: "npm:^2.0.0" + typescript: "npm:~5.3.3" + languageName: unknown + linkType: soft + "@metamask/local-node-utils@npm:^1.0.0, @metamask/local-node-utils@workspace:packages/local-node-utils": version: 0.0.0-use.local resolution: "@metamask/local-node-utils@workspace:packages/local-node-utils" dependencies: "@metamask/auto-changelog": "npm:^6.1.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7466,10 +7405,10 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7489,12 +7428,12 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" jsonschema: "npm:^1.4.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7510,14 +7449,14 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/yargs": "npm:^17.0.32" deepmerge: "npm:^4.2.2" execa: "npm:^5.0.0" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" oxfmt: "npm:^0.44.0" prettier: "npm:^3.3.3" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typescript: "npm:~5.3.3" yargs: "npm:^17.7.2" @@ -7556,11 +7495,11 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7577,7 +7516,7 @@ __metadata: languageName: node linkType: hard -"@metamask/money-account-api-data-service@npm:^0.3.0, @metamask/money-account-api-data-service@workspace:packages/money-account-api-data-service": +"@metamask/money-account-api-data-service@npm:^0.2.0, @metamask/money-account-api-data-service@workspace:packages/money-account-api-data-service": version: 0.0.0-use.local resolution: "@metamask/money-account-api-data-service@workspace:packages/money-account-api-data-service" dependencies: @@ -7589,11 +7528,11 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7612,17 +7551,17 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/money-account-api-data-service": "npm:^0.3.0" + "@metamask/money-account-api-data-service": "npm:^0.2.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7644,11 +7583,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7663,7 +7602,7 @@ __metadata: "@metamask/authenticated-user-storage": "npm:^3.0.1" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/chomp-api-service": "npm:^4.0.0" + "@metamask/chomp-api-service": "npm:^3.1.0" "@metamask/delegation-controller": "npm:^3.0.2" "@metamask/delegation-core": "npm:^2.2.1" "@metamask/delegation-deployments": "npm:^1.4.0" @@ -7672,11 +7611,11 @@ __metadata: "@metamask/network-controller": "npm:^34.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - jest-environment-node: "npm:^30.4.1" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7711,13 +7650,13 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/uuid": "npm:^8.3.0" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7752,11 +7691,11 @@ __metadata: "@open-rpc/meta-schema": "npm:^1.14.6" "@open-rpc/schema-utils-js": "npm:^2.0.5" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" jsonschema: "npm:^1.4.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7781,15 +7720,15 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@solana/addresses": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/lodash": "npm:^4.14.191" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7815,12 +7754,12 @@ __metadata: "@metamask/snaps-utils": "npm:^12.1.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7839,11 +7778,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7865,11 +7804,11 @@ __metadata: "@metamask/network-enablement-controller": "npm:^5.6.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7899,7 +7838,7 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/jest-when": "npm:^2.7.3" "@types/lodash": "npm:^4.14.191" "@types/node-fetch": "npm:^2.6.12" @@ -7908,15 +7847,15 @@ __metadata: deepmerge: "npm:^4.2.2" fast-deep-equal: "npm:^3.1.3" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" - jest-when: "npm:^3.7.0" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" + jest-when: "npm:^3.4.2" lodash: "npm:^4.17.21" loglevel: "npm:^1.8.1" nock: "npm:^13.3.1" node-fetch: "npm:^2.7.0" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7932,21 +7871,20 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/config-registry-controller": "npm:^0.4.1" "@metamask/controller-utils": "npm:^12.3.0" "@metamask/keyring-api": "npm:^23.5.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/multichain-network-controller": "npm:^3.2.1" "@metamask/network-controller": "npm:^34.0.0" "@metamask/slip44": "npm:^4.3.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7982,7 +7920,7 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/lodash": "npm:^4.14.191" "@types/readable-stream": "npm:^2.3.0" "@types/semver": "npm:^7" @@ -7990,13 +7928,13 @@ __metadata: contentful: "npm:^10.15.0" deepmerge: "npm:^4.2.2" firebase: "npm:^11.2.0" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" lodash: "npm:^4.17.21" loglevel: "npm:^1.8.1" nock: "npm:^13.3.1" semver: "npm:^7.6.3" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8039,12 +7977,12 @@ __metadata: "@noble/curves": "npm:^1.9.2" "@noble/hashes": "npm:^1.8.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - jest-environment-node: "npm:^30.4.1" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8085,13 +8023,13 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deep-freeze-strict: "npm:^1.1.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nanoid: "npm:^3.3.8" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8110,12 +8048,12 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deep-freeze-strict: "npm:^1.1.1" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nanoid: "npm:^3.3.8" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8141,18 +8079,18 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@myx-trade/sdk": "npm:^0.1.265" "@nktkas/hyperliquid": "npm:^0.33.1" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/uuid": "npm:^8.3.0" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8173,18 +8111,18 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@noble/hashes": "npm:^1.8.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/punycode": "npm:^2.1.0" deepmerge: "npm:^4.2.2" ethereum-cryptography: "npm:^2.1.2" fastest-levenshtein: "npm:^1.0.16" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" punycode: "npm:^2.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8206,7 +8144,7 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/node": "npm:^16.18.54" "@types/npm-which": "npm:^3" "@types/react": "npm:^19.0.0" @@ -8214,12 +8152,12 @@ __metadata: deepmerge: "npm:^4.2.2" execa: "npm:^5.0.0" glob: "npm:^13.0.6" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" npm-which: "npm:^3.0.1" prism-react-renderer: "npm:^2.4.1" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" ts-morph: "npm:^28.0.0" tsx: "npm:^4.20.5" typescript: "npm:~5.3.3" @@ -8239,12 +8177,12 @@ __metadata: "@metamask/network-controller": "npm:^34.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" fast-json-stable-stringify: "npm:^2.1.0" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8272,10 +8210,10 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8300,15 +8238,15 @@ __metadata: "@metamask/snaps-sdk": "npm:^11.0.0" "@metamask/snaps-utils": "npm:^12.1.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8338,16 +8276,16 @@ __metadata: "@noble/ciphers": "npm:^1.3.0" "@noble/hashes": "npm:^1.8.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" ethers: "npm:^6.12.0" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" loglevel: "npm:^1.8.1" nock: "npm:^13.3.1" siwe: "npm:^2.3.2" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8380,7 +8318,7 @@ __metadata: languageName: node linkType: hard -"@metamask/ramps-controller@npm:^17.1.0, @metamask/ramps-controller@workspace:packages/ramps-controller": +"@metamask/ramps-controller@npm:^17.0.0, @metamask/ramps-controller@workspace:packages/ramps-controller": version: 0.0.0-use.local resolution: "@metamask/ramps-controller@workspace:packages/ramps-controller" dependencies: @@ -8391,11 +8329,11 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8413,10 +8351,10 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8432,13 +8370,13 @@ __metadata: "@metamask/base-data-service": "npm:^0.1.3" "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^5.62.16" - "@tanstack/react-query": "npm:^5.62.16" + "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/react-query": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8466,11 +8404,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8510,11 +8448,11 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8532,7 +8470,7 @@ __metadata: languageName: node linkType: hard -"@metamask/seedless-onboarding-controller@npm:^10.1.0, @metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller": +"@metamask/seedless-onboarding-controller@npm:^10.0.3, @metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller": version: 0.0.0-use.local resolution: "@metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller" dependencies: @@ -8551,14 +8489,14 @@ __metadata: "@noble/hashes": "npm:^1.8.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/elliptic": "npm:^6" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/json-stable-stringify-without-jsonify": "npm:^1.0.2" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - jest-environment-node: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8579,14 +8517,14 @@ __metadata: "@metamask/swappable-obj-proxy": "npm:^2.3.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8594,7 +8532,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/sentinel-api-service@npm:^1.0.0, @metamask/sentinel-api-service@workspace:packages/sentinel-api-service": +"@metamask/sentinel-api-service@workspace:packages/sentinel-api-service": version: 0.0.0-use.local resolution: "@metamask/sentinel-api-service@workspace:packages/sentinel-api-service" dependencies: @@ -8606,11 +8544,11 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8630,15 +8568,15 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/signature-controller": "npm:^39.2.7" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" cockatiel: "npm:^3.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8664,12 +8602,12 @@ __metadata: "@metamask/network-controller": "npm:^34.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" jsonschema: "npm:^1.4.1" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8714,16 +8652,16 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8748,11 +8686,11 @@ __metadata: "@metamask/snaps-sdk": "npm:^11.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8889,10 +8827,10 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/superstruct": "npm:^3.1.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8907,10 +8845,10 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/local-node-utils": "npm:^1.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8933,10 +8871,10 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^6.1.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8952,10 +8890,10 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8973,14 +8911,14 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/polling-controller": "npm:^16.0.8" "@metamask/profile-sync-controller": "npm:^28.3.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -9020,7 +8958,7 @@ __metadata: languageName: node linkType: hard -"@metamask/transaction-controller@npm:^69.2.1, @metamask/transaction-controller@workspace:packages/transaction-controller": +"@metamask/transaction-controller@npm:^69.2.0, @metamask/transaction-controller@workspace:packages/transaction-controller": version: 0.0.0-use.local resolution: "@metamask/transaction-controller@workspace:packages/transaction-controller" dependencies: @@ -9037,11 +8975,11 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/connectivity-controller": "npm:^0.3.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/core-backend": "npm:^7.0.0" + "@metamask/core-backend": "npm:^6.5.0" "@metamask/eth-block-tracker": "npm:^15.0.0" "@metamask/eth-json-rpc-provider": "npm:^6.0.1" "@metamask/ethjs-provider-http": "npm:^0.3.0" - "@metamask/gas-fee-controller": "npm:^26.3.0" + "@metamask/gas-fee-controller": "npm:^26.2.4" "@metamask/messenger": "npm:^2.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/network-controller": "npm:^34.0.0" @@ -9051,7 +8989,7 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/bn.js": "npm:^5.1.5" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/node": "npm:^16.18.54" async-mutex: "npm:^0.5.0" bignumber.js: "npm:^9.1.2" @@ -9060,11 +8998,11 @@ __metadata: eth-method-registry: "npm:^4.0.0" fast-json-patch: "npm:^3.1.1" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - jest-environment-jsdom: "npm:^30.4.1" + jest: "npm:^29.7.0" + jest-environment-jsdom: "npm:^29.7.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -9083,30 +9021,29 @@ __metadata: "@ethersproject/abi": "npm:^5.7.0" "@ethersproject/contracts": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.0" - "@metamask/assets-controller": "npm:^11.2.0" - "@metamask/assets-controllers": "npm:^110.0.0" + "@metamask/assets-controller": "npm:^11.1.0" + "@metamask/assets-controllers": "npm:^109.4.1" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/gas-fee-controller": "npm:^26.3.0" + "@metamask/gas-fee-controller": "npm:^26.2.4" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/network-controller": "npm:^34.0.0" - "@metamask/ramps-controller": "npm:^17.1.0" + "@metamask/ramps-controller": "npm:^17.0.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" - "@metamask/sentinel-api-service": "npm:^1.0.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" bignumber.js: "npm:^9.1.2" bn.js: "npm:^5.2.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -9124,23 +9061,23 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/eth-block-tracker": "npm:^15.0.0" "@metamask/eth-query": "npm:^4.0.0" - "@metamask/gas-fee-controller": "npm:^26.3.0" + "@metamask/gas-fee-controller": "npm:^26.2.4" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/polling-controller": "npm:^16.0.8" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" bn.js: "npm:^5.2.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -9201,17 +9138,17 @@ __metadata: "@metamask/storage-service": "npm:^1.0.2" "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" - "@metamask/wallet": "npm:^8.1.0" + "@metamask/wallet": "npm:^7.0.1" "@oclif/core": "npm:^4.10.5" "@ts-bridge/cli": "npm:^0.6.4" "@types/better-sqlite3": "npm:^7.6.13" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" better-sqlite3: "npm:^12.9.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^30.4.2" - jest-environment-node: "npm:^30.4.1" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typescript: "npm:~5.3.3" bin: @@ -9237,22 +9174,22 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" "@types/react": "npm:^19.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" + jest: "npm:^29.7.0" nock: "npm:^13.3.1" prism-react-renderer: "npm:^2.4.1" raw-loader: "npm:^4.0.2" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" - ts-jest: "npm:^29.4.11" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typescript: "npm:~5.3.3" languageName: unknown linkType: soft -"@metamask/wallet@npm:^8.1.0, @metamask/wallet@workspace:packages/wallet": +"@metamask/wallet@npm:^7.0.1, @metamask/wallet@workspace:packages/wallet": version: 0.0.0-use.local resolution: "@metamask/wallet@workspace:packages/wallet" dependencies: @@ -9264,22 +9201,21 @@ __metadata: "@metamask/browser-passworder": "npm:^6.0.0" "@metamask/connectivity-controller": "npm:^0.3.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/gas-fee-controller": "npm:^26.3.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/passkey-controller": "npm:^3.0.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/scure-bip39": "npm:^2.1.1" - "@metamask/seedless-onboarding-controller": "npm:^10.1.0" + "@metamask/seedless-onboarding-controller": "npm:^10.0.3" "@metamask/storage-service": "npm:^1.0.2" - "@metamask/transaction-controller": "npm:^69.2.1" + "@metamask/transaction-controller": "npm:^69.2.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^30.0.0" + "@types/jest": "npm:^29.5.14" deepmerge: "npm:^4.2.2" - jest: "npm:^30.4.2" - ts-jest: "npm:^29.4.11" + jest: "npm:^29.7.0" + ts-jest: "npm:^29.2.5" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -9309,7 +9245,7 @@ __metadata: languageName: node linkType: hard -"@napi-rs/wasm-runtime@npm:^0.2.11, @napi-rs/wasm-runtime@npm:^0.2.3": +"@napi-rs/wasm-runtime@npm:^0.2.3": version: 0.2.12 resolution: "@napi-rs/wasm-runtime@npm:0.2.12" dependencies: @@ -10475,13 +10411,6 @@ __metadata: languageName: node linkType: hard -"@pkgr/core@npm:^0.2.9": - version: 0.2.9 - resolution: "@pkgr/core@npm:0.2.9" - checksum: 10/bb2fb86977d63f836f8f5b09015d74e6af6488f7a411dcd2bfdca79d76b5a681a9112f41c45bdf88a9069f049718efc6f3900d7f1de66a2ec966068308ae517f - languageName: node - linkType: hard - "@pnpm/config.env-replace@npm:^1.1.0": version: 1.1.0 resolution: "@pnpm/config.env-replace@npm:1.1.0" @@ -10589,7 +10518,7 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:^1.0.0, @scure/base@npm:^1.1.1, @scure/base@npm:^1.1.3, @scure/base@npm:~1.2.5": +"@scure/base@npm:^1.0.0, @scure/base@npm:^1.1.1, @scure/base@npm:^1.1.3, @scure/base@npm:^1.2.6, @scure/base@npm:~1.2.5": version: 1.2.6 resolution: "@scure/base@npm:1.2.6" checksum: 10/c1a7bd5e0b0c8f94c36fbc220f4a67cc832b00e2d2065c7d8a404ed81ab1c94c5443def6d361a70fc382db3496e9487fb9941728f0584782b274c18a4bed4187 @@ -10682,13 +10611,6 @@ __metadata: languageName: node linkType: hard -"@sinclair/typebox@npm:^0.34.0": - version: 0.34.48 - resolution: "@sinclair/typebox@npm:0.34.48" - checksum: 10/186eebb338255db7cfd77c2f94be0ad91816c7b5ee994c3adb95e0474ae98b769574c2b6b1f26a81613d7148ed20b11e02528f4263d8d95e3ca8dcf8faaf5306 - languageName: node - linkType: hard - "@sindresorhus/is@npm:^4.6.0": version: 4.6.0 resolution: "@sindresorhus/is@npm:4.6.0" @@ -10703,7 +10625,7 @@ __metadata: languageName: node linkType: hard -"@sinonjs/commons@npm:^3.0.1": +"@sinonjs/commons@npm:^3.0.0": version: 3.0.1 resolution: "@sinonjs/commons@npm:3.0.1" dependencies: @@ -10712,12 +10634,12 @@ __metadata: languageName: node linkType: hard -"@sinonjs/fake-timers@npm:^15.4.0": - version: 15.4.0 - resolution: "@sinonjs/fake-timers@npm:15.4.0" +"@sinonjs/fake-timers@npm:^10.0.2": + version: 10.3.0 + resolution: "@sinonjs/fake-timers@npm:10.3.0" dependencies: - "@sinonjs/commons": "npm:^3.0.1" - checksum: 10/3960a9fe065f38a4228c66d184eeb101e8a6af9cbfc8454dd5d45ac397201da72134048d4e808a25993494885b172dd6deecdad9949bbf4c1d3a220ef561f6cc + "@sinonjs/commons": "npm:^3.0.0" + checksum: 10/78155c7bd866a85df85e22028e046b8d46cf3e840f72260954f5e3ed5bd97d66c595524305a6841ffb3f681a08f6e5cef572a2cce5442a8a232dc29fb409b83e languageName: node linkType: hard @@ -11030,28 +10952,43 @@ __metadata: languageName: node linkType: hard -"@tanstack/query-core@npm:5.101.2, @tanstack/query-core@npm:^5.62.16": - version: 5.101.2 - resolution: "@tanstack/query-core@npm:5.101.2" - checksum: 10/567af5e3c21628745a08c1a5054d838597bc620dcbbeabe20cdd6ccffdcca85a8d38128f6e80829b6e04d10f79ca793a7dd8fddc8a3d6bef10c2d580ae214c7d - languageName: node - linkType: hard - -"@tanstack/query-core@npm:^4.43.0": +"@tanstack/query-core@npm:4.43.0, @tanstack/query-core@npm:^4.43.0": version: 4.43.0 resolution: "@tanstack/query-core@npm:4.43.0" checksum: 10/c2a5a151c7adaea8311e01a643255f31946ae3164a71567ba80048242821ae14043f13f5516b695baebe5ea7e4b2cf717fd60908a929d18a5c5125fee925ff67 languageName: node linkType: hard -"@tanstack/react-query@npm:^5.62.16": - version: 5.101.2 - resolution: "@tanstack/react-query@npm:5.101.2" +"@tanstack/query-core@npm:^5.62.16": + version: 5.90.20 + resolution: "@tanstack/query-core@npm:5.90.20" + checksum: 10/25e38f4382442bc15e0f6cce8d787e9df8d8822c61d3f3e9427e89e01b1e2506f848292e086dae29aeb55f8ce71b097c34221f3c5eda37fb4a688b5ceca5d1b3 + languageName: node + linkType: hard + +"@tanstack/react-query@npm:^4.43.0": + version: 4.43.0 + resolution: "@tanstack/react-query@npm:4.43.0" dependencies: - "@tanstack/query-core": "npm:5.101.2" + "@tanstack/query-core": "npm:4.43.0" + use-sync-external-store: "npm:^1.6.0" peerDependencies: - react: ^18 || ^19 - checksum: 10/0837c176b6afb01e3632010bbd8bdfeee7f46c4a25a50ed2ba9ddddce11c34431161d80dd8e53a2f55190c2313bbfb22a84a77308e6ccf29877b4818de68c4b3 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-native: "*" + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + checksum: 10/23f9d18d130fa2a1238d8fba8bc914c67e33753b7fc3a3c7856354a9873c4cbc5d18ce24dbf6364ecf86b8ea787575e1e60998ea75baa2b9e9647ad4b9127e10 + languageName: node + linkType: hard + +"@tootallnate/once@npm:2": + version: 2.0.0 + resolution: "@tootallnate/once@npm:2.0.0" + checksum: 10/ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 languageName: node linkType: hard @@ -11167,7 +11104,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.20.5": +"@types/babel__core@npm:^7.1.14": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" dependencies: @@ -11199,7 +11136,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__traverse@npm:*": +"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": version: 7.28.0 resolution: "@types/babel__traverse@npm:7.28.0" dependencies: @@ -11369,6 +11306,15 @@ __metadata: languageName: node linkType: hard +"@types/graceful-fs@npm:^4.1.3": + version: 4.1.9 + resolution: "@types/graceful-fs@npm:4.1.9" + dependencies: + "@types/node": "npm:*" + checksum: 10/79d746a8f053954bba36bd3d94a90c78de995d126289d656fb3271dd9f1229d33f678da04d10bce6be440494a5a73438e2e363e92802d16b8315b051036c5256 + languageName: node + linkType: hard + "@types/gtag.js@npm:^0.0.20": version: 0.0.20 resolution: "@types/gtag.js@npm:0.0.20" @@ -11422,7 +11368,7 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1, @types/istanbul-lib-coverage@npm:^2.0.6": +"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.6 resolution: "@types/istanbul-lib-coverage@npm:2.0.6" checksum: 10/3feac423fd3e5449485afac999dcfcb3d44a37c830af898b689fadc65d26526460bedb889db278e0d4d815a670331796494d073a10ee6e3a6526301fe7415778 @@ -11438,7 +11384,7 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-reports@npm:^3.0.0, @types/istanbul-reports@npm:^3.0.4": +"@types/istanbul-reports@npm:^3.0.0": version: 3.0.4 resolution: "@types/istanbul-reports@npm:3.0.4" dependencies: @@ -11456,24 +11402,24 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*, @types/jest@npm:^30.0.0": - version: 30.0.0 - resolution: "@types/jest@npm:30.0.0" +"@types/jest@npm:*, @types/jest@npm:^29.5.14": + version: 29.5.14 + resolution: "@types/jest@npm:29.5.14" dependencies: - expect: "npm:^30.0.0" - pretty-format: "npm:^30.0.0" - checksum: 10/cdeaa924c68b5233d9ff92861a89e7042df2b0f197633729bcf3a31e65bd4e9426e751c5665b5ac2de0b222b33f100a5502da22aefce3d2c62931c715e88f209 + expect: "npm:^29.0.0" + pretty-format: "npm:^29.0.0" + checksum: 10/59ec7a9c4688aae8ee529316c43853468b6034f453d08a2e1064b281af9c81234cec986be796288f1bbb29efe943bc950e70c8fa8faae1e460d50e3cf9760f9b languageName: node linkType: hard -"@types/jsdom@npm:^21.1.7": - version: 21.1.7 - resolution: "@types/jsdom@npm:21.1.7" +"@types/jsdom@npm:^20.0.0": + version: 20.0.1 + resolution: "@types/jsdom@npm:20.0.1" dependencies: "@types/node": "npm:*" "@types/tough-cookie": "npm:*" parse5: "npm:^7.0.0" - checksum: 10/a5ee54aec813ac928ef783f69828213af4d81325f584e1fe7573a9ae139924c40768d1d5249237e62d51b9a34ed06bde059c86c6b0248d627457ec5e5d532dfa + checksum: 10/15fbb9a0bfb4a5845cf6e795f2fd12400aacfca53b8c7e5bca4a3e5e8fa8629f676327964d64258aefb127d2d8a2be86dad46359efbfca0e8c9c2b790e7f8a88 languageName: node linkType: hard @@ -11755,7 +11701,7 @@ __metadata: languageName: node linkType: hard -"@types/stack-utils@npm:^2.0.3": +"@types/stack-utils@npm:^2.0.0": version: 2.0.3 resolution: "@types/stack-utils@npm:2.0.3" checksum: 10/72576cc1522090fe497337c2b99d9838e320659ac57fa5560fcbdcbafcf5d0216c6b3a0a8a4ee4fdb3b1f5e3420aa4f6223ab57b82fef3578bec3206425c6cf5 @@ -11831,12 +11777,12 @@ __metadata: languageName: node linkType: hard -"@types/yargs@npm:^17.0.32, @types/yargs@npm:^17.0.33, @types/yargs@npm:^17.0.8": - version: 17.0.35 - resolution: "@types/yargs@npm:17.0.35" +"@types/yargs@npm:^17.0.32, @types/yargs@npm:^17.0.8": + version: 17.0.33 + resolution: "@types/yargs@npm:17.0.33" dependencies: "@types/yargs-parser": "npm:*" - checksum: 10/47bcd4476a4194ea11617ea71cba8a1eddf5505fc39c44336c1a08d452a0de4486aedbc13f47a017c8efbcb5a8aa358d976880663732ebcbc6dbcbbecadb0581 + checksum: 10/16f6681bf4d99fb671bf56029141ed01db2862e3db9df7fc92d8bea494359ac96a1b4b1c35a836d1e95e665fb18ad753ab2015fc0db663454e8fd4e5d5e2ef91 languageName: node linkType: hard @@ -11975,148 +11921,13 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.3.0": +"@ungap/structured-clone@npm:^1.0.0": version: 1.3.1 resolution: "@ungap/structured-clone@npm:1.3.1" checksum: 10/64df206f50aef71c176f9059c1b29e1694821419c6728c446ecf39c80a811eeef156668bf51421b676494a12fd0129ccf09a44f0c641f13c27f50d5f0db6de4e languageName: node linkType: hard -"@unrs/resolver-binding-android-arm-eabi@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.11.1" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@unrs/resolver-binding-android-arm64@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-android-arm64@npm:1.11.1" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-darwin-arm64@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.11.1" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-darwin-x64@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-darwin-x64@npm:1.11.1" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-freebsd-x64@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-freebsd-x64@npm:1.11.1" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.11.1" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm-musleabihf@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-arm-musleabihf@npm:1.11.1" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm64-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-arm64-gnu@npm:1.11.1" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm64-musl@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-arm64-musl@npm:1.11.1" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-ppc64-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-ppc64-gnu@npm:1.11.1" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-riscv64-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-riscv64-gnu@npm:1.11.1" - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-riscv64-musl@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-riscv64-musl@npm:1.11.1" - conditions: os=linux & cpu=riscv64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-s390x-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-s390x-gnu@npm:1.11.1" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-x64-gnu@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-x64-gnu@npm:1.11.1" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-x64-musl@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-linux-x64-musl@npm:1.11.1" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-wasm32-wasi@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.11.1" - dependencies: - "@napi-rs/wasm-runtime": "npm:^0.2.11" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1": - version: 1.11.1 - resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@vercel/stega@npm:^0.1.2": version: 0.1.2 resolution: "@vercel/stega@npm:0.1.2" @@ -12305,6 +12116,13 @@ __metadata: languageName: node linkType: hard +"abab@npm:^2.0.6": + version: 2.0.6 + resolution: "abab@npm:2.0.6" + checksum: 10/ebe95d7278999e605823fc515a3b05d689bc72e7f825536e73c95ebf621636874c6de1b749b3c4bf866b96ccd4b3a2802efa313d0e45ad51a413c8c73247db20 + languageName: node + linkType: hard + "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -12361,6 +12179,16 @@ __metadata: languageName: node linkType: hard +"acorn-globals@npm:^7.0.0": + version: 7.0.1 + resolution: "acorn-globals@npm:7.0.1" + dependencies: + acorn: "npm:^8.1.0" + acorn-walk: "npm:^8.0.2" + checksum: 10/2a2998a547af6d0db5f0cdb90acaa7c3cbca6709010e02121fb8b8617c0fbd8bab0b869579903fde358ac78454356a14fadcc1a672ecb97b04b1c2ccba955ce8 + languageName: node + linkType: hard + "acorn-import-phases@npm:^1.0.3": version: 1.0.4 resolution: "acorn-import-phases@npm:1.0.4" @@ -12379,7 +12207,7 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.0.0": +"acorn-walk@npm:^8.0.0, acorn-walk@npm:^8.0.2": version: 8.3.5 resolution: "acorn-walk@npm:8.3.5" dependencies: @@ -12388,7 +12216,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.0.0, acorn@npm:^8.0.4, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.16.0": +"acorn@npm:^8.0.0, acorn@npm:^8.0.4, acorn@npm:^8.1.0, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.16.0, acorn@npm:^8.8.1": version: 8.16.0 resolution: "acorn@npm:8.16.0" bin: @@ -12425,10 +12253,21 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:^7.1.0, agent-base@npm:^7.1.1, agent-base@npm:^7.1.2": - version: 7.1.4 - resolution: "agent-base@npm:7.1.4" - checksum: 10/79bef167247789f955aaba113bae74bf64aa1e1acca4b1d6bb444bdf91d82c3e07e9451ef6a6e2e35e8f71a6f97ce33e3d855a5328eb9fad1bc3cc4cfd031ed8 +"agent-base@npm:6": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: "npm:4" + checksum: 10/21fb903e0917e5cb16591b4d0ef6a028a54b83ac30cd1fca58dece3d4e0990512a8723f9f83130d88a41e2af8b1f7be1386fda3ea2d181bb1a62155e75e95e23 + languageName: node + linkType: hard + +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": + version: 7.1.1 + resolution: "agent-base@npm:7.1.1" + dependencies: + debug: "npm:^4.3.4" + checksum: 10/c478fec8f79953f118704d007a38f2a185458853f5c45579b9669372bd0e12602e88dc2ad0233077831504f7cd6fcc8251c383375bba5eaaf563b102938bda26 languageName: node linkType: hard @@ -12542,7 +12381,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.3.2": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.2": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -12560,6 +12399,13 @@ __metadata: languageName: node linkType: hard +"ansi-regex@npm:^4.1.0": + version: 4.1.1 + resolution: "ansi-regex@npm:4.1.1" + checksum: 10/b1a6ee44cb6ecdabaa770b2ed500542714d4395d71c7e5c25baa631f680fb2ad322eb9ba697548d498a6fd366949fc8b5bfcf48d49a32803611f648005b01888 + languageName: node + linkType: hard + "ansi-regex@npm:^5.0.1": version: 5.0.1 resolution: "ansi-regex@npm:5.0.1" @@ -12581,6 +12427,15 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^3.2.0": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: "npm:^1.9.0" + checksum: 10/d85ade01c10e5dd77b6c89f34ed7531da5830d2cb5882c645f330079975b716438cd7ebb81d0d6e6b4f9c577f19ae41ab55f07f19786b02f9dfd9e0377395665 + languageName: node + linkType: hard + "ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": version: 4.3.0 resolution: "ansi-styles@npm:4.3.0" @@ -12590,7 +12445,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^5.2.0": +"ansi-styles@npm:^5.0.0": version: 5.2.0 resolution: "ansi-styles@npm:5.2.0" checksum: 10/d7f4e97ce0623aea6bc0d90dcd28881ee04cba06c570b97fd3391bd7a268eedfd9d5e2dd4fdcbdd82b8105df5faf6f24aaedc08eaf3da898e702db5948f63469 @@ -12611,7 +12466,7 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": +"anymatch@npm:^3.0.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" dependencies: @@ -12780,20 +12635,20 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:30.4.1": - version: 30.4.1 - resolution: "babel-jest@npm:30.4.1" +"babel-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "babel-jest@npm:29.7.0" dependencies: - "@jest/transform": "npm:30.4.1" - "@types/babel__core": "npm:^7.20.5" - babel-plugin-istanbul: "npm:^7.0.1" - babel-preset-jest: "npm:30.4.0" - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" + "@jest/transform": "npm:^29.7.0" + "@types/babel__core": "npm:^7.1.14" + babel-plugin-istanbul: "npm:^6.1.1" + babel-preset-jest: "npm:^29.6.3" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" slash: "npm:^3.0.0" peerDependencies: - "@babel/core": ^7.11.0 || ^8.0.0-0 - checksum: 10/f739152bee60b368b27676441c54e235b49bca10329bb6395b54cca5982ced1f7a5a2c504e406e1082aac3dc68ab518771c9de62cf66ffe00993ad071a58fd1b + "@babel/core": ^7.8.0 + checksum: 10/8a0953bd813b3a8926008f7351611055548869e9a53dd36d6e7e96679001f71e65fd7dbfe253265c3ba6a4e630dc7c845cf3e78b17d758ef1880313ce8fba258 languageName: node linkType: hard @@ -12819,25 +12674,28 @@ __metadata: languageName: node linkType: hard -"babel-plugin-istanbul@npm:^7.0.1": - version: 7.0.1 - resolution: "babel-plugin-istanbul@npm:7.0.1" +"babel-plugin-istanbul@npm:^6.1.1": + version: 6.1.1 + resolution: "babel-plugin-istanbul@npm:6.1.1" dependencies: "@babel/helper-plugin-utils": "npm:^7.0.0" "@istanbuljs/load-nyc-config": "npm:^1.0.0" - "@istanbuljs/schema": "npm:^0.1.3" - istanbul-lib-instrument: "npm:^6.0.2" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-instrument: "npm:^5.0.4" test-exclude: "npm:^6.0.0" - checksum: 10/fe9f865f975aaa7a033de9ccb2b63fdcca7817266c5e98d3e02ac7ffd774c695093d215302796cb3770a71ef4574e7a9b298504c3c0c104cf4b48c8eda67b2a6 + checksum: 10/ffd436bb2a77bbe1942a33245d770506ab2262d9c1b3c1f1da7f0592f78ee7445a95bc2efafe619dd9c1b6ee52c10033d6c7d29ddefe6f5383568e60f31dfe8d languageName: node linkType: hard -"babel-plugin-jest-hoist@npm:30.4.0": - version: 30.4.0 - resolution: "babel-plugin-jest-hoist@npm:30.4.0" +"babel-plugin-jest-hoist@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-plugin-jest-hoist@npm:29.6.3" dependencies: - "@types/babel__core": "npm:^7.20.5" - checksum: 10/112f984b3b4315f7ff15d5d17df7f5aa4b500e562c67c2eafcd9974af4369c17d50feed2f9c95cbcec32faba8ccb04f8b62828aca41a47d5fdedc532b49fbf19 + "@babel/template": "npm:^7.3.3" + "@babel/types": "npm:^7.3.3" + "@types/babel__core": "npm:^7.1.14" + "@types/babel__traverse": "npm:^7.0.6" + checksum: 10/9bfa86ec4170bd805ab8ca5001ae50d8afcb30554d236ba4a7ffc156c1a92452e220e4acbd98daefc12bf0216fccd092d0a2efed49e7e384ec59e0597a926d65 languageName: node linkType: hard @@ -12889,9 +12747,9 @@ __metadata: languageName: node linkType: hard -"babel-preset-current-node-syntax@npm:^1.2.0": - version: 1.2.0 - resolution: "babel-preset-current-node-syntax@npm:1.2.0" +"babel-preset-current-node-syntax@npm:^1.0.0": + version: 1.1.0 + resolution: "babel-preset-current-node-syntax@npm:1.1.0" dependencies: "@babel/plugin-syntax-async-generators": "npm:^7.8.4" "@babel/plugin-syntax-bigint": "npm:^7.8.3" @@ -12909,20 +12767,20 @@ __metadata: "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" peerDependencies: - "@babel/core": ^7.0.0 || ^8.0.0-0 - checksum: 10/3608fa671cfa46364ea6ec704b8fcdd7514b7b70e6ec09b1199e13ae73ed346c51d5ce2cb6d4d5b295f6a3f2cad1fdeec2308aa9e037002dd7c929194cc838ea + "@babel/core": ^7.0.0 + checksum: 10/46331111ae72b7121172fd9e6a4a7830f651ad44bf26dbbf77b3c8a60a18009411a3eacb5e72274004290c110371230272109957d5224d155436b4794ead2f1b languageName: node linkType: hard -"babel-preset-jest@npm:30.4.0": - version: 30.4.0 - resolution: "babel-preset-jest@npm:30.4.0" +"babel-preset-jest@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-preset-jest@npm:29.6.3" dependencies: - babel-plugin-jest-hoist: "npm:30.4.0" - babel-preset-current-node-syntax: "npm:^1.2.0" + babel-plugin-jest-hoist: "npm:^29.6.3" + babel-preset-current-node-syntax: "npm:^1.0.0" peerDependencies: - "@babel/core": ^7.11.0 || ^8.0.0-beta.1 - checksum: 10/7fbdcaa1f24b2efbc1b658220df849a375858bd5e208cefcf53b116bca972b28565a0715521cc20bec41adbd20ff73b9dbbdea3634bd71f50062f0ce694a7159 + "@babel/core": ^7.0.0 + checksum: 10/aa4ff2a8a728d9d698ed521e3461a109a1e66202b13d3494e41eea30729a5e7cc03b3a2d56c594423a135429c37bf63a9fa8b0b9ce275298be3095a88c69f6fb languageName: node linkType: hard @@ -13003,15 +12861,6 @@ __metadata: languageName: node linkType: hard -"bats@npm:^1.13.0": - version: 1.13.0 - resolution: "bats@npm:1.13.0" - bin: - bats: bin/bats - checksum: 10/cc120033073f5ed798accfc27d2f45863204d03673d985fdb5d850246df6adf8007be4678511ec206e5a47b46f15801fa612ca7d8e16ccf6afa7a009286c1627 - languageName: node - linkType: hard - "bech32@npm:1.1.4": version: 1.1.4 resolution: "bech32@npm:1.1.4" @@ -13230,11 +13079,11 @@ __metadata: linkType: hard "brace-expansion@npm:^2.0.1, brace-expansion@npm:^2.0.2": - version: 2.1.2 - resolution: "brace-expansion@npm:2.1.2" + version: 2.1.1 + resolution: "brace-expansion@npm:2.1.1" dependencies: balanced-match: "npm:^1.0.0" - checksum: 10/0e0f9b0df1f0809e9c326470e022eeb24334ddb54c43b1ac39f1d38f3cbaeb940794de1db826f5491843ac00d7932c43f10350a65ccd51fe7d05da627152f204 + checksum: 10/4681c533dc4e6c77b3ad795b38683d297fd03c739a17bfb2a338529fa7dcf4540683a79dcd662905f4c5b0db7cfda18daafcd18dd1bbf7c3b076fe0c9c3487eb languageName: node linkType: hard @@ -13468,7 +13317,7 @@ __metadata: languageName: node linkType: hard -"callsites@npm:^3.0.0, callsites@npm:^3.1.0": +"callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" checksum: 10/072d17b6abb459c2ba96598918b55868af677154bec7e73d222ef95a8fdb9bbf7dae96a8421085cdad8cd190d86653b5b6dc55a4484f2e5b2e27d5e0c3fc15b3 @@ -13485,14 +13334,14 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^5.3.1": +"camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": version: 5.3.1 resolution: "camelcase@npm:5.3.1" checksum: 10/e6effce26b9404e3c0f301498184f243811c30dfe6d0b9051863bd8e4034d09c8c2923794f280d6827e5aa055f6c434115ff97864a16a963366fb35fd673024b languageName: node linkType: hard -"camelcase@npm:^6.2.0, camelcase@npm:^6.3.0": +"camelcase@npm:^6.2.0": version: 6.3.0 resolution: "camelcase@npm:6.3.0" checksum: 10/8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d @@ -13639,7 +13488,21 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": +"chokidar-cli@npm:^3.0.0": + version: 3.0.0 + resolution: "chokidar-cli@npm:3.0.0" + dependencies: + chokidar: "npm:^3.5.2" + lodash.debounce: "npm:^4.0.8" + lodash.throttle: "npm:^4.1.1" + yargs: "npm:^13.3.0" + bin: + chokidar: index.js + checksum: 10/b486205063d3b2cb2edb2dc05d2c21ad6beac4085ca3cf2d66a83af3c1dbaa4570f6101be733d7b03c6d68c64a39262c856688d7eddba758e063cbd2466e4ae9 + languageName: node + linkType: hard + +"chokidar@npm:^3.5.2, chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -13700,13 +13563,6 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^4.2.0": - version: 4.4.0 - resolution: "ci-info@npm:4.4.0" - checksum: 10/dfded0c630267d89660c8abb988ac8395a382bdfefedcc03e3e2858523312c5207db777c239c34774e3fcff11f015477c19d2ac8a58ea58aa476614a2e64f434 - languageName: node - linkType: hard - "cipher-base@npm:^1.0.0, cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3": version: 1.0.4 resolution: "cipher-base@npm:1.0.4" @@ -13717,20 +13573,13 @@ __metadata: languageName: node linkType: hard -"cjs-module-lexer@npm:^1.3.1": +"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.3.1": version: 1.4.0 resolution: "cjs-module-lexer@npm:1.4.0" checksum: 10/b041096749792526120d8b8756929f8ef5dd4596502a0e1013f857e3027acd6091915fea77037921d70ee1a99988a100d994d3d3c2e323b04dd4c5ffd516cf13 languageName: node linkType: hard -"cjs-module-lexer@npm:^2.1.0": - version: 2.2.0 - resolution: "cjs-module-lexer@npm:2.2.0" - checksum: 10/fc8eb5c1919504366d8260a150d93c4e857740e770467dc59ca0cc34de4b66c93075559a5af65618f359187866b1be40e036f4e1a1bab2f1e06001c216415f74 - languageName: node - linkType: hard - "clean-css@npm:^5.2.2, clean-css@npm:^5.3.3, clean-css@npm:~5.3.2": version: 5.3.3 resolution: "clean-css@npm:5.3.3" @@ -13790,6 +13639,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^5.0.0": + version: 5.0.0 + resolution: "cliui@npm:5.0.0" + dependencies: + string-width: "npm:^3.1.0" + strip-ansi: "npm:^5.2.0" + wrap-ansi: "npm:^5.1.0" + checksum: 10/381264fcc3c8316b77b378ce5471ff9a1974d1f6217e0be8f4f09788482b3e6f7c0894eb21e0a86eab4ce0c68426653a407226dd51997306cb87f734776f5fdc + languageName: node + linkType: hard + "cliui@npm:^8.0.1": version: 8.0.1 resolution: "cliui@npm:8.0.1" @@ -13854,10 +13714,19 @@ __metadata: languageName: node linkType: hard -"collect-v8-coverage@npm:^1.0.2": - version: 1.0.3 - resolution: "collect-v8-coverage@npm:1.0.3" - checksum: 10/656443261fb7b79cf79e89cba4b55622b07c1d4976c630829d7c5c585c73cda1c2ff101f316bfb19bb9e2c58d724c7db1f70a21e213dcd14099227c5e6019860 +"collect-v8-coverage@npm:^1.0.0": + version: 1.0.2 + resolution: "collect-v8-coverage@npm:1.0.2" + checksum: 10/30ea7d5c9ee51f2fdba4901d4186c5b7114a088ef98fd53eda3979da77eed96758a2cae81cc6d97e239aaea6065868cf908b24980663f7b7e96aa291b3e12fa4 + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0": + version: 1.9.3 + resolution: "color-convert@npm:1.9.3" + dependencies: + color-name: "npm:1.1.3" + checksum: 10/ffa319025045f2973919d155f25e7c00d08836b6b33ea2d205418c59bd63a665d713c52d9737a9e0fe467fb194b40fbef1d849bae80d674568ee220a31ef3d10 languageName: node linkType: hard @@ -13870,6 +13739,13 @@ __metadata: languageName: node linkType: hard +"color-name@npm:1.1.3": + version: 1.1.3 + resolution: "color-name@npm:1.1.3" + checksum: 10/09c5d3e33d2105850153b14466501f2bfb30324a2f76568a408763a3b7433b0e50e5b4ab1947868e65cb101bb7cb75029553f2c333b6d4b8138a73fcc133d69d + languageName: node + linkType: hard + "color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" @@ -14255,6 +14131,23 @@ __metadata: languageName: node linkType: hard +"create-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "create-jest@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + exit: "npm:^0.1.2" + graceful-fs: "npm:^4.2.9" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + prompts: "npm:^2.0.1" + bin: + create-jest: bin/create-jest.js + checksum: 10/847b4764451672b4174be4d5c6d7d63442ec3aa5f3de52af924e4d996d87d7801c18e125504f25232fc75840f6625b3ac85860fac6ce799b5efae7bdcaf4a2b7 + languageName: node + linkType: hard + "cron-parser@npm:^4.5.0": version: 4.9.0 resolution: "cron-parser@npm:4.9.0" @@ -14542,13 +14435,26 @@ __metadata: languageName: node linkType: hard -"cssstyle@npm:^4.2.1": - version: 4.6.0 - resolution: "cssstyle@npm:4.6.0" +"cssom@npm:^0.5.0": + version: 0.5.0 + resolution: "cssom@npm:0.5.0" + checksum: 10/b502a315b1ce020a692036cc38cb36afa44157219b80deadfa040ab800aa9321fcfbecf02fd2e6ec87db169715e27978b4ab3701f916461e9cf7808899f23b54 + languageName: node + linkType: hard + +"cssom@npm:~0.3.6": + version: 0.3.8 + resolution: "cssom@npm:0.3.8" + checksum: 10/49eacc88077555e419646c0ea84ddc73c97e3a346ad7cb95e22f9413a9722d8964b91d781ce21d378bd5ae058af9a745402383fa4e35e9cdfd19654b63f892a9 + languageName: node + linkType: hard + +"cssstyle@npm:^2.3.0": + version: 2.3.0 + resolution: "cssstyle@npm:2.3.0" dependencies: - "@asamuzakjp/css-color": "npm:^3.2.0" - rrweb-cssom: "npm:^0.8.0" - checksum: 10/1cb25c9d66b87adb165f978b75cdeb6f225d7e31ba30a8934666046a0be037e4e7200d359bfa79d4f1a4aef1083ea09633b81bcdb36a2f2ac888e8c73ea3a289 + cssom: "npm:~0.3.6" + checksum: 10/46f7f05a153446c4018b0454ee1464b50f606cb1803c90d203524834b7438eb52f3b173ba0891c618f380ced34ee12020675dc0052a7f1be755fe4ebc27ee977 languageName: node linkType: hard @@ -14559,13 +14465,14 @@ __metadata: languageName: node linkType: hard -"data-urls@npm:^5.0.0": - version: 5.0.0 - resolution: "data-urls@npm:5.0.0" +"data-urls@npm:^3.0.2": + version: 3.0.2 + resolution: "data-urls@npm:3.0.2" dependencies: - whatwg-mimetype: "npm:^4.0.0" - whatwg-url: "npm:^14.0.0" - checksum: 10/5c40568c31b02641a70204ff233bc4e42d33717485d074244a98661e5f2a1e80e38fe05a5755dfaf2ee549f2ab509d6a3af2a85f4b2ad2c984e5d176695eaf46 + abab: "npm:^2.0.6" + whatwg-mimetype: "npm:^3.0.0" + whatwg-url: "npm:^11.0.0" + checksum: 10/033fc3dd0fba6d24bc9a024ddcf9923691dd24f90a3d26f6545d6a2f71ec6956f93462f2cdf2183cc46f10dc01ed3bcb36731a8208456eb1a08147e571fe2a76 languageName: node linkType: hard @@ -14613,7 +14520,14 @@ __metadata: languageName: node linkType: hard -"decimal.js@npm:^10.5.0, decimal.js@npm:^10.6.0": +"decamelize@npm:^1.2.0": + version: 1.2.0 + resolution: "decamelize@npm:1.2.0" + checksum: 10/ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa + languageName: node + linkType: hard + +"decimal.js@npm:^10.4.2, decimal.js@npm:^10.6.0": version: 10.6.0 resolution: "decimal.js@npm:10.6.0" checksum: 10/c0d45842d47c311d11b38ce7ccc911121953d4df3ebb1465d92b31970eb4f6738a065426a06094af59bee4b0d64e42e7c8984abd57b6767c64ea90cf90bb4a69 @@ -14638,7 +14552,7 @@ __metadata: languageName: node linkType: hard -"dedent@npm:^1.6.0": +"dedent@npm:^1.0.0": version: 1.7.1 resolution: "dedent@npm:1.7.1" peerDependencies: @@ -14794,7 +14708,7 @@ __metadata: languageName: node linkType: hard -"detect-newline@npm:^3.1.0": +"detect-newline@npm:^3.0.0": version: 3.1.0 resolution: "detect-newline@npm:3.1.0" checksum: 10/ae6cd429c41ad01b164c59ea36f264a2c479598e61cba7c99da24175a7ab80ddf066420f2bec9a1c57a6bead411b4655ff15ad7d281c000a89791f48cbe939e7 @@ -14830,6 +14744,13 @@ __metadata: languageName: node linkType: hard +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: 10/179daf9d2f9af5c57ad66d97cb902a538bcf8ed64963fa7aa0c329b3de3665ce2eb6ffdc2f69f29d445fa4af2517e5e55e5b6e00c00a9ae4f43645f97f7078cb + languageName: node + linkType: hard + "diff@npm:^5.0.0": version: 5.2.0 resolution: "diff@npm:5.2.0" @@ -14902,6 +14823,15 @@ __metadata: languageName: node linkType: hard +"domexception@npm:^4.0.0": + version: 4.0.0 + resolution: "domexception@npm:4.0.0" + dependencies: + webidl-conversions: "npm:^7.0.0" + checksum: 10/4ed443227d2871d76c58d852b2e93c68e0443815b2741348f20881bedee8c1ad4f9bfc5d30c7dec433cd026b57da63407c010260b1682fef4c8847e7181ea43f + languageName: node + linkType: hard + "domhandler@npm:^4.0.0, domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": version: 4.3.1 resolution: "domhandler@npm:4.3.1" @@ -15042,6 +14972,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^7.0.1": + version: 7.0.3 + resolution: "emoji-regex@npm:7.0.3" + checksum: 10/9159b2228b1511f2870ac5920f394c7e041715429a68459ebe531601555f11ea782a8e1718f969df2711d38c66268174407cbca57ce36485544f695c2dfdc96e + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -15365,6 +15302,24 @@ __metadata: languageName: node linkType: hard +"escodegen@npm:^2.0.0": + version: 2.1.0 + resolution: "escodegen@npm:2.1.0" + dependencies: + esprima: "npm:^4.0.1" + estraverse: "npm:^5.2.0" + esutils: "npm:^2.0.2" + source-map: "npm:~0.6.1" + dependenciesMeta: + source-map: + optional: true + bin: + escodegen: bin/escodegen.js + esgenerate: bin/esgenerate.js + checksum: 10/47719a65b2888b4586e3fa93769068b275961c13089e90d5d01a96a6e8e95871b1c3893576814c8fbf08a4a31a496f37e7b2c937cf231270f4d81de012832c7c + languageName: node + linkType: hard + "eslint-compat-utils@npm:^0.5.1": version: 0.5.1 resolution: "eslint-compat-utils@npm:0.5.1" @@ -15992,10 +15947,10 @@ __metadata: languageName: node linkType: hard -"exit-x@npm:^0.2.2": - version: 0.2.2 - resolution: "exit-x@npm:0.2.2" - checksum: 10/ee043053e6c1e237adf5ad9c4faf9f085b606f64a4ff859e2b138fab63fe642711d00c9af452a9134c4c92c55f752e818bfabab78c24d345022db163f3137027 +"exit@npm:^0.1.2": + version: 0.1.2 + resolution: "exit@npm:0.1.2" + checksum: 10/387555050c5b3c10e7a9e8df5f43194e95d7737c74532c409910e585d5554eaff34960c166643f5e23d042196529daad059c292dcf1fb61b8ca878d3677f4b87 languageName: node linkType: hard @@ -16006,17 +15961,16 @@ __metadata: languageName: node linkType: hard -"expect@npm:30.4.1, expect@npm:^30.0.0": - version: 30.4.1 - resolution: "expect@npm:30.4.1" +"expect@npm:^29.0.0, expect@npm:^29.7.0": + version: 29.7.0 + resolution: "expect@npm:29.7.0" dependencies: - "@jest/expect-utils": "npm:30.4.1" - "@jest/get-type": "npm:30.1.0" - jest-matcher-utils: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - checksum: 10/f25051e5073c55369199ec3108ac01c60074bd09dad0b5a6c9fe40596438051cb52607e0e97505126422535b8d0dacab13aa29bb14e9fac71630bc710201a3f1 + "@jest/expect-utils": "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 10/63f97bc51f56a491950fb525f9ad94f1916e8a014947f8d8445d3847a665b5471b768522d659f5e865db20b6c2033d2ac10f35fcbd881a4d26407a4f6f18451a languageName: node linkType: hard @@ -16260,7 +16214,7 @@ __metadata: languageName: node linkType: hard -"fb-watchman@npm:^2.0.2": +"fb-watchman@npm:^2.0.0": version: 2.0.2 resolution: "fb-watchman@npm:2.0.2" dependencies: @@ -16370,6 +16324,15 @@ __metadata: languageName: node linkType: hard +"find-up@npm:^3.0.0": + version: 3.0.0 + resolution: "find-up@npm:3.0.0" + dependencies: + locate-path: "npm:^3.0.0" + checksum: 10/38eba3fe7a66e4bc7f0f5a1366dc25508b7cfc349f852640e3678d26ad9a6d7e2c43eff0a472287de4a9753ef58f066a0ea892a256fa3636ad51b3fe1e17fae9 + languageName: node + linkType: hard + "find-up@npm:^4.0.0, find-up@npm:^4.1.0": version: 4.1.0 resolution: "find-up@npm:4.1.0" @@ -16600,7 +16563,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.3.3, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": +"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -16610,7 +16573,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.3#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": +"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -16640,7 +16603,7 @@ __metadata: languageName: node linkType: hard -"get-caller-file@npm:^2.0.5": +"get-caller-file@npm:^2.0.1, get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" checksum: 10/b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 @@ -16770,9 +16733,9 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7, glob@npm:^10.5.0": - version: 10.5.0 - resolution: "glob@npm:10.5.0" +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7": + version: 10.4.5 + resolution: "glob@npm:10.4.5" dependencies: foreground-child: "npm:^3.1.0" jackspeak: "npm:^3.1.2" @@ -16782,7 +16745,7 @@ __metadata: path-scurry: "npm:^1.11.1" bin: glob: dist/esm/bin.mjs - checksum: 10/ab3bccfefcc0afaedbd1f480cd0c4a2c0e322eb3f0aa7ceaa31b3f00b825069f17cf0f1fc8b6f256795074b903f37c0ade37ddda6a176aa57f1c2bbfe7240653 + checksum: 10/698dfe11828b7efd0514cd11e573eaed26b2dff611f0400907281ce3eab0c1e56143ef9b35adc7c77ecc71fba74717b510c7c223d34ca8a98ec81777b293d4ac languageName: node linkType: hard @@ -16797,7 +16760,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.1.4, glob@npm:^7.1.7": +"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.7": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -16929,9 +16892,9 @@ __metadata: languageName: node linkType: hard -"handlebars@npm:^4.7.9": - version: 4.7.9 - resolution: "handlebars@npm:4.7.9" +"handlebars@npm:^4.7.8": + version: 4.7.8 + resolution: "handlebars@npm:4.7.8" dependencies: minimist: "npm:^1.2.5" neo-async: "npm:^2.6.2" @@ -16943,7 +16906,7 @@ __metadata: optional: true bin: handlebars: bin/handlebars - checksum: 10/e755433d652e8a15fc02f83d7478e652359e7a4d354c4328818853ed4f8a39d4a09e1d22dad3c7213c5240864a65b3c840970b8b181745575dd957dd258f2b8d + checksum: 10/bd528f4dd150adf67f3f857118ef0fa43ff79a153b1d943fa0a770f2599e38b25a7a0dbac1a3611a4ec86970fd2325a81310fb788b5c892308c9f8743bd02e11 languageName: node linkType: hard @@ -17201,12 +17164,12 @@ __metadata: languageName: node linkType: hard -"html-encoding-sniffer@npm:^4.0.0": - version: 4.0.0 - resolution: "html-encoding-sniffer@npm:4.0.0" +"html-encoding-sniffer@npm:^3.0.0": + version: 3.0.0 + resolution: "html-encoding-sniffer@npm:3.0.0" dependencies: - whatwg-encoding: "npm:^3.1.1" - checksum: 10/e86efd493293a5671b8239bd099d42128433bb3c7b0fdc7819282ef8e118a21f5dead0ad6f358e024a4e5c84f17ebb7a9b36075220fac0a6222b207248bede6f + whatwg-encoding: "npm:^2.0.0" + checksum: 10/707a812ec2acaf8bb5614c8618dc81e2fb6b4399d03e95ff18b65679989a072f4e919b9bef472039301a1bbfba64063ba4c79ea6e851c653ac9db80dbefe8fe5 languageName: node linkType: hard @@ -17369,7 +17332,18 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.2": +"http-proxy-agent@npm:^5.0.0": + version: 5.0.0 + resolution: "http-proxy-agent@npm:5.0.0" + dependencies: + "@tootallnate/once": "npm:2" + agent-base: "npm:6" + debug: "npm:4" + checksum: 10/5ee19423bc3e0fd5f23ce991b0755699ad2a46a440ce9cec99e8126bb98448ad3479d2c0ea54be5519db5b19a4ffaa69616bac01540db18506dd4dac3dc418f0 + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" dependencies: @@ -17418,13 +17392,23 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.6": - version: 7.0.6 - resolution: "https-proxy-agent@npm:7.0.6" +"https-proxy-agent@npm:^5.0.1": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: "npm:6" + debug: "npm:4" + checksum: 10/f0dce7bdcac5e8eaa0be3c7368bb8836ed010fb5b6349ffb412b172a203efe8f807d9a6681319105ea1b6901e1972c7b5ea899672a7b9aad58309f766dcbe0df + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1": + version: 7.0.5 + resolution: "https-proxy-agent@npm:7.0.5" dependencies: - agent-base: "npm:^7.1.2" + agent-base: "npm:^7.0.2" debug: "npm:4" - checksum: 10/784b628cbd55b25542a9d85033bdfd03d4eda630fb8b3c9477959367f3be95dc476ed2ecbb9836c359c7c698027fc7b45723a302324433590f45d6c1706e8c13 + checksum: 10/6679d46159ab3f9a5509ee80c3a3fc83fba3a920a5e18d32176c3327852c3c00ad640c0c4210a8fd70ea3c4a6d3a1b375bf01942516e7df80e2646bdc77658ab languageName: node linkType: hard @@ -17562,7 +17546,7 @@ __metadata: languageName: node linkType: hard -"import-local@npm:^3.2.0": +"import-local@npm:^3.0.2": version: 3.2.0 resolution: "import-local@npm:3.2.0" dependencies: @@ -17812,6 +17796,13 @@ __metadata: languageName: node linkType: hard +"is-fullwidth-code-point@npm:^2.0.0": + version: 2.0.0 + resolution: "is-fullwidth-code-point@npm:2.0.0" + checksum: 10/eef9c6e15f68085fec19ff6a978a6f1b8f48018fd1265035552078ee945573594933b09bbd6f562553e2a241561439f1ef5339276eba68d272001343084cfab8 + languageName: node + linkType: hard + "is-fullwidth-code-point@npm:^3.0.0": version: 3.0.0 resolution: "is-fullwidth-code-point@npm:3.0.0" @@ -17819,7 +17810,7 @@ __metadata: languageName: node linkType: hard -"is-generator-fn@npm:^2.1.0": +"is-generator-fn@npm:^2.0.0": version: 2.1.0 resolution: "is-generator-fn@npm:2.1.0" checksum: 10/a6ad5492cf9d1746f73b6744e0c43c0020510b59d56ddcb78a91cbc173f09b5e6beff53d75c9c5a29feb618bfef2bf458e025ecf3a57ad2268e2fb2569f56215 @@ -18070,7 +18061,20 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-instrument@npm:^6.0.0, istanbul-lib-instrument@npm:^6.0.2": +"istanbul-lib-instrument@npm:^5.0.4": + version: 5.2.1 + resolution: "istanbul-lib-instrument@npm:5.2.1" + dependencies: + "@babel/core": "npm:^7.12.3" + "@babel/parser": "npm:^7.14.7" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-coverage: "npm:^3.2.0" + semver: "npm:^6.3.0" + checksum: 10/bbc4496c2f304d799f8ec22202ab38c010ac265c441947f075c0f7d46bd440b45c00e46017cf9053453d42182d768b1d6ed0e70a142c95ab00df9843aa5ab80e + languageName: node + linkType: hard + +"istanbul-lib-instrument@npm:^6.0.0": version: 6.0.3 resolution: "istanbul-lib-instrument@npm:6.0.3" dependencies: @@ -18094,14 +18098,14 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-source-maps@npm:^5.0.0": - version: 5.0.6 - resolution: "istanbul-lib-source-maps@npm:5.0.6" +"istanbul-lib-source-maps@npm:^4.0.0": + version: 4.0.1 + resolution: "istanbul-lib-source-maps@npm:4.0.1" dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.23" debug: "npm:^4.1.1" istanbul-lib-coverage: "npm:^3.0.0" - checksum: 10/569dd0a392ee3464b1fe1accbaef5cc26de3479eacb5b91d8c67ebb7b425d39fd02247d85649c3a0e9c29b600809fa60b5af5a281a75a89c01f385b1e24823a2 + source-map: "npm:^0.6.1" + checksum: 10/5526983462799aced011d776af166e350191b816821ea7bcf71cab3e5272657b062c47dc30697a22a43656e3ced78893a42de677f9ccf276a28c913190953b82 languageName: node linkType: hard @@ -18141,251 +18145,259 @@ __metadata: languageName: node linkType: hard -"jest-changed-files@npm:30.4.1": - version: 30.4.1 - resolution: "jest-changed-files@npm:30.4.1" +"jest-changed-files@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-changed-files@npm:29.7.0" dependencies: - execa: "npm:^5.1.1" - jest-util: "npm:30.4.1" + execa: "npm:^5.0.0" + jest-util: "npm:^29.7.0" p-limit: "npm:^3.1.0" - checksum: 10/e566af0d6c53115edf34fd1d9bda3b857fcc6626bd032516a480b60ec208d515558eda1e693e76ef992633d71828a4c38a3e91a236142459855483cbd03ff4c4 + checksum: 10/3d93742e56b1a73a145d55b66e96711fbf87ef89b96c2fab7cfdfba8ec06612591a982111ca2b712bb853dbc16831ec8b43585a2a96b83862d6767de59cbf83d languageName: node linkType: hard -"jest-circus@npm:30.4.2": - version: 30.4.2 - resolution: "jest-circus@npm:30.4.2" +"jest-circus@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-circus@npm:29.7.0" dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/expect": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/types": "npm:30.4.1" + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - chalk: "npm:^4.1.2" + chalk: "npm:^4.0.0" co: "npm:^4.6.0" - dedent: "npm:^1.6.0" - is-generator-fn: "npm:^2.1.0" - jest-each: "npm:30.4.1" - jest-matcher-utils: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-runtime: "npm:30.4.2" - jest-snapshot: "npm:30.4.1" - jest-util: "npm:30.4.1" + dedent: "npm:^1.0.0" + is-generator-fn: "npm:^2.0.0" + jest-each: "npm:^29.7.0" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" p-limit: "npm:^3.1.0" - pretty-format: "npm:30.4.1" - pure-rand: "npm:^7.0.0" + pretty-format: "npm:^29.7.0" + pure-rand: "npm:^6.0.0" slash: "npm:^3.0.0" - stack-utils: "npm:^2.0.6" - checksum: 10/b210db2cd3ab595c6053deee4c11e7eb5ea293b9af16fc87021288125dd42e8d13fdc77be21eca71f1636c6fe104edc26e32d6e707168763e97b0d1718c11203 + stack-utils: "npm:^2.0.3" + checksum: 10/716a8e3f40572fd0213bcfc1da90274bf30d856e5133af58089a6ce45089b63f4d679bd44e6be9d320e8390483ebc3ae9921981993986d21639d9019b523123d languageName: node linkType: hard -"jest-cli@npm:30.4.2": - version: 30.4.2 - resolution: "jest-cli@npm:30.4.2" +"jest-cli@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-cli@npm:29.7.0" dependencies: - "@jest/core": "npm:30.4.2" - "@jest/test-result": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - chalk: "npm:^4.1.2" - exit-x: "npm:^0.2.2" - import-local: "npm:^3.2.0" - jest-config: "npm:30.4.2" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - yargs: "npm:^17.7.2" + "@jest/core": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + create-jest: "npm:^29.7.0" + exit: "npm:^0.1.2" + import-local: "npm:^3.0.2" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + yargs: "npm:^17.3.1" peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: node-notifier: optional: true bin: - jest: ./bin/jest.js - checksum: 10/dd33f8ee6500298639d5ec4d5f641306d9876fa182042ee40e9c63c59bdf9a82dc46da5bf38fae0783f6ac874df7f7f099006b263022954cf494f8468dfe583c + jest: bin/jest.js + checksum: 10/6cc62b34d002c034203065a31e5e9a19e7c76d9e8ef447a6f70f759c0714cb212c6245f75e270ba458620f9c7b26063cd8cf6cd1f7e3afd659a7cc08add17307 languageName: node linkType: hard -"jest-config@npm:30.4.2": - version: 30.4.2 - resolution: "jest-config@npm:30.4.2" +"jest-config@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-config@npm:29.7.0" dependencies: - "@babel/core": "npm:^7.27.4" - "@jest/get-type": "npm:30.1.0" - "@jest/pattern": "npm:30.4.0" - "@jest/test-sequencer": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - babel-jest: "npm:30.4.1" - chalk: "npm:^4.1.2" - ci-info: "npm:^4.2.0" - deepmerge: "npm:^4.3.1" - glob: "npm:^10.5.0" - graceful-fs: "npm:^4.2.11" - jest-circus: "npm:30.4.2" - jest-docblock: "npm:30.4.0" - jest-environment-node: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-resolve: "npm:30.4.1" - jest-runner: "npm:30.4.2" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" + "@babel/core": "npm:^7.11.6" + "@jest/test-sequencer": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + babel-jest: "npm:^29.7.0" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + deepmerge: "npm:^4.2.2" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + jest-circus: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + micromatch: "npm:^4.0.4" parse-json: "npm:^5.2.0" - pretty-format: "npm:30.4.1" + pretty-format: "npm:^29.7.0" slash: "npm:^3.0.0" strip-json-comments: "npm:^3.1.1" peerDependencies: "@types/node": "*" - esbuild-register: ">=3.4.0" ts-node: ">=9.0.0" peerDependenciesMeta: "@types/node": optional: true - esbuild-register: - optional: true ts-node: optional: true - checksum: 10/d37d923a5b3e815b73bbefe09c2f7fb092d6933f5f4ec0555fd9cacf97c58e8adb73fc4780a99e6a49e798444b1bb0097c9d5232feb1b8ffa31af589f9c02d9a + checksum: 10/6bdf570e9592e7d7dd5124fc0e21f5fe92bd15033513632431b211797e3ab57eaa312f83cc6481b3094b72324e369e876f163579d60016677c117ec4853cf02b languageName: node linkType: hard -"jest-diff@npm:30.4.1": - version: 30.4.1 - resolution: "jest-diff@npm:30.4.1" +"jest-diff@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-diff@npm:29.7.0" dependencies: - "@jest/diff-sequences": "npm:30.4.0" - "@jest/get-type": "npm:30.1.0" - chalk: "npm:^4.1.2" - pretty-format: "npm:30.4.1" - checksum: 10/594212df96bf101170afdb7eebd188d6d7d27241cbdd18b61d95f1142a3c94ae3b270377d15e719fb3c5efe4458d32acba8ad13dd6230dd7d6917a9eebb32625 + chalk: "npm:^4.0.0" + diff-sequences: "npm:^29.6.3" + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 10/6f3a7eb9cd9de5ea9e5aa94aed535631fa6f80221832952839b3cb59dd419b91c20b73887deb0b62230d06d02d6b6cf34ebb810b88d904bb4fe1e2e4f0905c98 languageName: node linkType: hard -"jest-docblock@npm:30.4.0": - version: 30.4.0 - resolution: "jest-docblock@npm:30.4.0" +"jest-docblock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-docblock@npm:29.7.0" dependencies: - detect-newline: "npm:^3.1.0" - checksum: 10/0ee25351ef941e832e53d10e74f34b38941b8f5fe750584661f6c5a115771818b081b8e39830c49d152fd361af81c7800c2d3a3c3a0e2dc742f7bfdbb6b1baaa + detect-newline: "npm:^3.0.0" + checksum: 10/8d48818055bc96c9e4ec2e217a5a375623c0d0bfae8d22c26e011074940c202aa2534a3362294c81d981046885c05d304376afba9f2874143025981148f3e96d languageName: node linkType: hard -"jest-each@npm:30.4.1": - version: 30.4.1 - resolution: "jest-each@npm:30.4.1" +"jest-each@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-each@npm:29.7.0" dependencies: - "@jest/get-type": "npm:30.1.0" - "@jest/types": "npm:30.4.1" - chalk: "npm:^4.1.2" - jest-util: "npm:30.4.1" - pretty-format: "npm:30.4.1" - checksum: 10/077365c3fd0dc0d74aaa180fe4e3728533685413db58e7a30c8502d19a60a0b08259825d8aa36c569c8175a9d0cf901dd69c0a4eb63567c22c07bd0b566ddf89 + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + jest-get-type: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + pretty-format: "npm:^29.7.0" + checksum: 10/bd1a077654bdaa013b590deb5f7e7ade68f2e3289180a8c8f53bc8a49f3b40740c0ec2d3a3c1aee906f682775be2bebbac37491d80b634d15276b0aa0f2e3fda languageName: node linkType: hard -"jest-environment-jsdom@npm:^30.4.1": - version: 30.4.1 - resolution: "jest-environment-jsdom@npm:30.4.1" +"jest-environment-jsdom@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-jsdom@npm:29.7.0" dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/environment-jsdom-abstract": "npm:30.4.1" - jsdom: "npm:^26.1.0" + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/jsdom": "npm:^20.0.0" + "@types/node": "npm:*" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jsdom: "npm:^20.0.0" peerDependencies: - canvas: ^3.0.0 + canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 10/2ed0860e257d404622306823f18c15f0fe881fc1d16e1245434b54eb690fa0784cf54cd7eef74b2e9373e1699b1726a0fb69d60ae388cc78eb36968fe3fd0bf6 + checksum: 10/23bbfc9bca914baef4b654f7983175a4d49b0f515a5094ebcb8f819f28ec186f53c0ba06af1855eac04bab1457f4ea79dae05f70052cf899863e8096daa6e0f5 languageName: node linkType: hard -"jest-environment-node@npm:30.4.1, jest-environment-node@npm:^30.4.1": - version: 30.4.1 - resolution: "jest-environment-node@npm:30.4.1" +"jest-environment-node@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-node@npm:29.7.0" dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/fake-timers": "npm:30.4.1" - "@jest/types": "npm:30.4.1" + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - checksum: 10/b93157061c6fa4b62808741bdda5fbc0372a9d2a3db844f0ffb819ed104b3b5c6ff73375d9f57c0d8485a0ad6aed07d4cfbb98aca985c38e1a29f64096b940bc + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 10/9cf7045adf2307cc93aed2f8488942e39388bff47ec1df149a997c6f714bfc66b2056768973770d3f8b1bf47396c19aa564877eb10ec978b952c6018ed1bd637 + languageName: node + linkType: hard + +"jest-get-type@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-get-type@npm:29.6.3" + checksum: 10/88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 languageName: node linkType: hard -"jest-haste-map@npm:30.4.1": - version: 30.4.1 - resolution: "jest-haste-map@npm:30.4.1" +"jest-haste-map@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-haste-map@npm:29.7.0" dependencies: - "@jest/types": "npm:30.4.1" + "@jest/types": "npm:^29.6.3" + "@types/graceful-fs": "npm:^4.1.3" "@types/node": "npm:*" - anymatch: "npm:^3.1.3" - fb-watchman: "npm:^2.0.2" - fsevents: "npm:^2.3.3" - graceful-fs: "npm:^4.2.11" - jest-regex-util: "npm:30.4.0" - jest-util: "npm:30.4.1" - jest-worker: "npm:30.4.1" - picomatch: "npm:^4.0.3" + anymatch: "npm:^3.0.3" + fb-watchman: "npm:^2.0.0" + fsevents: "npm:^2.3.2" + graceful-fs: "npm:^4.2.9" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + micromatch: "npm:^4.0.4" walker: "npm:^1.0.8" dependenciesMeta: fsevents: optional: true - checksum: 10/d26404c7258d03fa423604191bca39707438ca1e62a9a471c92fcd468fa386cbdce2c50b3834fb830b25836e3eee34e3070d22b016b42f0ab626c157f5726eeb + checksum: 10/8531b42003581cb18a69a2774e68c456fb5a5c3280b1b9b77475af9e346b6a457250f9d756bfeeae2fe6cbc9ef28434c205edab9390ee970a919baddfa08bb85 languageName: node linkType: hard -"jest-leak-detector@npm:30.4.1": - version: 30.4.1 - resolution: "jest-leak-detector@npm:30.4.1" +"jest-leak-detector@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-leak-detector@npm:29.7.0" dependencies: - "@jest/get-type": "npm:30.1.0" - pretty-format: "npm:30.4.1" - checksum: 10/8c0945d1c73f6a2abde8660f7fc5693b344cd1f5fd66153c0c2d13007f8429486eb99ecf15ac68d3d4410534fd0fad1ed430c32a641cdac66e021dbd33204c9a + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 10/e3950e3ddd71e1d0c22924c51a300a1c2db6cf69ec1e51f95ccf424bcc070f78664813bef7aed4b16b96dfbdeea53fe358f8aeaaea84346ae15c3735758f1605 languageName: node linkType: hard -"jest-matcher-utils@npm:30.4.1": - version: 30.4.1 - resolution: "jest-matcher-utils@npm:30.4.1" +"jest-matcher-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-matcher-utils@npm:29.7.0" dependencies: - "@jest/get-type": "npm:30.1.0" - chalk: "npm:^4.1.2" - jest-diff: "npm:30.4.1" - pretty-format: "npm:30.4.1" - checksum: 10/4da6e5c7fe5903fae7394233ea4b892567fb027065670c03096d01be0b389f858055c5ade20d59e82fedec6f3287e6f1720de526cd9a9ad3495432320adb9194 + chalk: "npm:^4.0.0" + jest-diff: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 10/981904a494299cf1e3baed352f8a3bd8b50a8c13a662c509b6a53c31461f94ea3bfeffa9d5efcfeb248e384e318c87de7e3baa6af0f79674e987482aa189af40 languageName: node linkType: hard -"jest-message-util@npm:30.4.1": - version: 30.4.1 - resolution: "jest-message-util@npm:30.4.1" +"jest-message-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-message-util@npm:29.7.0" dependencies: - "@babel/code-frame": "npm:^7.27.1" - "@jest/types": "npm:30.4.1" - "@types/stack-utils": "npm:^2.0.3" - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - jest-util: "npm:30.4.1" - picomatch: "npm:^4.0.3" - pretty-format: "npm:30.4.1" + "@babel/code-frame": "npm:^7.12.13" + "@jest/types": "npm:^29.6.3" + "@types/stack-utils": "npm:^2.0.0" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + micromatch: "npm:^4.0.4" + pretty-format: "npm:^29.7.0" slash: "npm:^3.0.0" - stack-utils: "npm:^2.0.6" - checksum: 10/f83894efa37aa9c61c0a559b1027ecdb0d0cd8afd3e8ea74e797c707d58daea814e72f04b6db0bb6a148c12ae203e9c6e6c5544832ca5fae286c4f80c18ddc3f + stack-utils: "npm:^2.0.3" + checksum: 10/31d53c6ed22095d86bab9d14c0fa70c4a92c749ea6ceece82cf30c22c9c0e26407acdfbdb0231435dc85a98d6d65ca0d9cbcd25cd1abb377fe945e843fb770b9 languageName: node linkType: hard -"jest-mock@npm:30.4.1": - version: 30.4.1 - resolution: "jest-mock@npm:30.4.1" +"jest-mock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-mock@npm:29.7.0" dependencies: - "@jest/types": "npm:30.4.1" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - jest-util: "npm:30.4.1" - checksum: 10/8d0c2794130217b9030b888ce380fe57d82388eec19351bd666440ba46f1e24a7e2bdf42cbe9bcfda2b881d4c0ea09db3c80131b9ab788fb5224af2a1339b422 + jest-util: "npm:^29.7.0" + checksum: 10/ae51d1b4f898724be5e0e52b2268a68fcd876d9b20633c864a6dd6b1994cbc48d62402b0f40f3a1b669b30ebd648821f086c26c08ffde192ced951ff4670d51c languageName: node linkType: hard -"jest-pnp-resolver@npm:^1.2.3": +"jest-pnp-resolver@npm:^1.2.2": version: 1.2.3 resolution: "jest-pnp-resolver@npm:1.2.3" peerDependencies: @@ -18397,149 +18409,134 @@ __metadata: languageName: node linkType: hard -"jest-regex-util@npm:30.4.0": - version: 30.4.0 - resolution: "jest-regex-util@npm:30.4.0" - checksum: 10/8664fcc1d07c8236a3bd012c0f06ae9d14d96e758b32ee340a3a7c4c326d0b5052d8c4ae4f4c4184f08bf78723d905352f22923647df9658ace3604f03bf074f +"jest-regex-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-regex-util@npm:29.6.3" + checksum: 10/0518beeb9bf1228261695e54f0feaad3606df26a19764bc19541e0fc6e2a3737191904607fb72f3f2ce85d9c16b28df79b7b1ec9443aa08c3ef0e9efda6f8f2a languageName: node linkType: hard -"jest-resolve-dependencies@npm:30.4.2": - version: 30.4.2 - resolution: "jest-resolve-dependencies@npm:30.4.2" +"jest-resolve-dependencies@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve-dependencies@npm:29.7.0" dependencies: - jest-regex-util: "npm:30.4.0" - jest-snapshot: "npm:30.4.1" - checksum: 10/ec7e27d1abf67dfe9ec1a2887b5fa883e1e6ca38eb45506a82f93e29d8df62c18cb40ec07af43fe0fa65d568051a878b6649745f6c032d8962a8dad6323763ad + jest-regex-util: "npm:^29.6.3" + jest-snapshot: "npm:^29.7.0" + checksum: 10/1e206f94a660d81e977bcfb1baae6450cb4a81c92e06fad376cc5ea16b8e8c6ea78c383f39e95591a9eb7f925b6a1021086c38941aa7c1b8a6a813c2f6e93675 languageName: node linkType: hard -"jest-resolve@npm:30.4.1": - version: 30.4.1 - resolution: "jest-resolve@npm:30.4.1" +"jest-resolve@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve@npm:29.7.0" dependencies: - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - jest-pnp-resolver: "npm:^1.2.3" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-pnp-resolver: "npm:^1.2.2" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + resolve: "npm:^1.20.0" + resolve.exports: "npm:^2.0.0" slash: "npm:^3.0.0" - unrs-resolver: "npm:^1.7.11" - checksum: 10/197ca741df92c1006c2367142b5e44827d995f062e94a923f574e87ce04f966634851bb31f54ea377ca163a8362613947cd2311abf8a5712fe879b1ac15f662f + checksum: 10/faa466fd9bc69ea6c37a545a7c6e808e073c66f46ab7d3d8a6ef084f8708f201b85d5fe1799789578b8b47fa1de47b9ee47b414d1863bc117a49e032ba77b7c7 languageName: node linkType: hard -"jest-runner@npm:30.4.2": - version: 30.4.2 - resolution: "jest-runner@npm:30.4.2" +"jest-runner@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runner@npm:29.7.0" dependencies: - "@jest/console": "npm:30.4.1" - "@jest/environment": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" + "@jest/console": "npm:^29.7.0" + "@jest/environment": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - chalk: "npm:^4.1.2" + chalk: "npm:^4.0.0" emittery: "npm:^0.13.1" - exit-x: "npm:^0.2.2" - graceful-fs: "npm:^4.2.11" - jest-docblock: "npm:30.4.0" - jest-environment-node: "npm:30.4.1" - jest-haste-map: "npm:30.4.1" - jest-leak-detector: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-resolve: "npm:30.4.1" - jest-runtime: "npm:30.4.2" - jest-util: "npm:30.4.1" - jest-watcher: "npm:30.4.1" - jest-worker: "npm:30.4.1" + graceful-fs: "npm:^4.2.9" + jest-docblock: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-leak-detector: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-resolve: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" p-limit: "npm:^3.1.0" source-map-support: "npm:0.5.13" - checksum: 10/3ed8ee70019f1f5a63faaf07a15e522ec878601dc6eccbde7eb4d0529e4d1a7314e7041160075458257e3103f41d6e9bbb2df8698806805ae2768a7e90228103 + checksum: 10/9d8748a494bd90f5c82acea99be9e99f21358263ce6feae44d3f1b0cd90991b5df5d18d607e73c07be95861ee86d1cbab2a3fc6ca4b21805f07ac29d47c1da1e languageName: node linkType: hard -"jest-runtime@npm:30.4.2": - version: 30.4.2 - resolution: "jest-runtime@npm:30.4.2" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/fake-timers": "npm:30.4.1" - "@jest/globals": "npm:30.4.1" - "@jest/source-map": "npm:30.0.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" +"jest-runtime@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runtime@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/globals": "npm:^29.7.0" + "@jest/source-map": "npm:^29.6.3" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - chalk: "npm:^4.1.2" - cjs-module-lexer: "npm:^2.1.0" - collect-v8-coverage: "npm:^1.0.2" - glob: "npm:^10.5.0" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-mock: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-resolve: "npm:30.4.1" - jest-snapshot: "npm:30.4.1" - jest-util: "npm:30.4.1" + chalk: "npm:^4.0.0" + cjs-module-lexer: "npm:^1.0.0" + collect-v8-coverage: "npm:^1.0.0" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" slash: "npm:^3.0.0" strip-bom: "npm:^4.0.0" - checksum: 10/bc2612a17650e7187d2c778aa66fc07926f828eeb3a445e47ffa757f65a4fb29628a43c6e792196b0a3a06d099b0405b6654a2c314fc5092013690d01483fd75 + checksum: 10/59eb58eb7e150e0834a2d0c0d94f2a0b963ae7182cfa6c63f2b49b9c6ef794e5193ef1634e01db41420c36a94cefc512cdd67a055cd3e6fa2f41eaf0f82f5a20 languageName: node linkType: hard -"jest-silent-reporter@npm:^0.6.0": - version: 0.6.0 - resolution: "jest-silent-reporter@npm:0.6.0" +"jest-silent-reporter@npm:^0.5.0": + version: 0.5.0 + resolution: "jest-silent-reporter@npm:0.5.0" dependencies: chalk: "npm:^4.0.0" jest-util: "npm:^26.0.0" - checksum: 10/443e0abaf5a6dc8c17da1e8495b7a55f813224adc39b6d1954bf49ff7fe70533b1020571453180dbb8388ace87f8e1dfc79610a4554bb93334f6c4154231c292 + checksum: 10/54853eb1fd342eb074ff97bbd2f177c3425e635deb008ce1a5feede398930b6472549797342e3ea536f8e2e538d8718d6f281f3d44dec2ae4d32f61da0eb547e languageName: node linkType: hard -"jest-snapshot@npm:30.4.1": - version: 30.4.1 - resolution: "jest-snapshot@npm:30.4.1" - dependencies: - "@babel/core": "npm:^7.27.4" - "@babel/generator": "npm:^7.27.5" - "@babel/plugin-syntax-jsx": "npm:^7.27.1" - "@babel/plugin-syntax-typescript": "npm:^7.27.1" - "@babel/types": "npm:^7.27.3" - "@jest/expect-utils": "npm:30.4.1" - "@jest/get-type": "npm:30.1.0" - "@jest/snapshot-utils": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - babel-preset-current-node-syntax: "npm:^1.2.0" - chalk: "npm:^4.1.2" - expect: "npm:30.4.1" - graceful-fs: "npm:^4.2.11" - jest-diff: "npm:30.4.1" - jest-matcher-utils: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-util: "npm:30.4.1" - pretty-format: "npm:30.4.1" - semver: "npm:^7.7.2" - synckit: "npm:^0.11.8" - checksum: 10/6135108d3e0e9fb93ed10fd9ad91d8dbe56f90a9ea84c32a0b551518f8c71f363299dcc301717f3ed82cfe2a276d7993d2b3ccfabea3e8020d49ae8b0f9b6cd8 - languageName: node - linkType: hard - -"jest-util@npm:30.4.1": - version: 30.4.1 - resolution: "jest-util@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - ci-info: "npm:^4.2.0" - graceful-fs: "npm:^4.2.11" - picomatch: "npm:^4.0.3" - checksum: 10/603093e12076906afcf28be514d5b7ac4e3c0e26997b0047614cf2a308b65d773137304a1fb011d747517e881aeed067f6606b9937f5b838d67f6e5734b49ebe +"jest-snapshot@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-snapshot@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@babel/generator": "npm:^7.7.2" + "@babel/plugin-syntax-jsx": "npm:^7.7.2" + "@babel/plugin-syntax-typescript": "npm:^7.7.2" + "@babel/types": "npm:^7.3.3" + "@jest/expect-utils": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + babel-preset-current-node-syntax: "npm:^1.0.0" + chalk: "npm:^4.0.0" + expect: "npm:^29.7.0" + graceful-fs: "npm:^4.2.9" + jest-diff: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + natural-compare: "npm:^1.4.0" + pretty-format: "npm:^29.7.0" + semver: "npm:^7.5.3" + checksum: 10/cb19a3948256de5f922d52f251821f99657339969bf86843bd26cf3332eae94883e8260e3d2fba46129a27c3971c1aa522490e460e16c7fad516e82d10bbf9f8 languageName: node linkType: hard @@ -18571,55 +18568,42 @@ __metadata: languageName: node linkType: hard -"jest-validate@npm:30.4.1": - version: 30.4.1 - resolution: "jest-validate@npm:30.4.1" +"jest-validate@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" dependencies: - "@jest/get-type": "npm:30.1.0" - "@jest/types": "npm:30.4.1" - camelcase: "npm:^6.3.0" - chalk: "npm:^4.1.2" + "@jest/types": "npm:^29.6.3" + camelcase: "npm:^6.2.0" + chalk: "npm:^4.0.0" + jest-get-type: "npm:^29.6.3" leven: "npm:^3.1.0" - pretty-format: "npm:30.4.1" - checksum: 10/527fe8ad02df9a4f7f467ecc4e3ac2a37a27b7b30345e7bc3cb9c2ead33fbc8ed1290c0827baa06471281012c38abb96cb268af274a0a2350548e50db20a434f + pretty-format: "npm:^29.7.0" + checksum: 10/8ee1163666d8eaa16d90a989edba2b4a3c8ab0ffaa95ad91b08ca42b015bfb70e164b247a5b17f9de32d096987cada63ed8491ab82761bfb9a28bc34b27ae161 languageName: node linkType: hard -"jest-watcher@npm:30.4.1": - version: 30.4.1 - resolution: "jest-watcher@npm:30.4.1" +"jest-watcher@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-watcher@npm:29.7.0" dependencies: - "@jest/test-result": "npm:30.4.1" - "@jest/types": "npm:30.4.1" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" "@types/node": "npm:*" - ansi-escapes: "npm:^4.3.2" - chalk: "npm:^4.1.2" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.0.0" emittery: "npm:^0.13.1" - jest-util: "npm:30.4.1" - string-length: "npm:^4.0.2" - checksum: 10/05350ed3d5643e87e22cc5faee14f912dfc06ba63d56944006d9837f2070ed509a1d124c7e7be3e3a9a6a382bd31d491146da6fda4483acd4b8292091888e9bd + jest-util: "npm:^29.7.0" + string-length: "npm:^4.0.1" + checksum: 10/4f616e0345676631a7034b1d94971aaa719f0cd4a6041be2aa299be437ea047afd4fe05c48873b7963f5687a2f6c7cbf51244be8b14e313b97bfe32b1e127e55 languageName: node linkType: hard -"jest-when@npm:^3.7.0": - version: 3.7.0 - resolution: "jest-when@npm:3.7.0" +"jest-when@npm:^3.4.2": + version: 3.6.0 + resolution: "jest-when@npm:3.6.0" peerDependencies: jest: ">= 25" - checksum: 10/b5b88d077ed467aab220c71c885dbc5f448604f06e68f761ce9f479c99bb74e0dbf553d1cc980751d88401b034a578e1c44eec5e4095743b7586f02bb7c6313d - languageName: node - linkType: hard - -"jest-worker@npm:30.4.1": - version: 30.4.1 - resolution: "jest-worker@npm:30.4.1" - dependencies: - "@types/node": "npm:*" - "@ungap/structured-clone": "npm:^1.3.0" - jest-util: "npm:30.4.1" - merge-stream: "npm:^2.0.0" - supports-color: "npm:^8.1.1" - checksum: 10/ff6af73c9097fc07e90490d3e1e354c702390ef66f7f40054a15dd6d56809a25634179969ff80bde782a6c645f49fa48bf3aacfe7d05af7315c48020f9b2b1cd + checksum: 10/0cb92738ccfa5711a685107f4437f18aefbe3cda120c912a9d49b612eeef03a910481ab40fe753fd42c4e617ffbb3d84c6bd66a76d963dac7f1ad9e9e5059359 languageName: node linkType: hard @@ -18634,7 +18618,7 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.4.3": +"jest-worker@npm:^29.4.3, jest-worker@npm:^29.7.0": version: 29.7.0 resolution: "jest-worker@npm:29.7.0" dependencies: @@ -18646,22 +18630,22 @@ __metadata: languageName: node linkType: hard -"jest@npm:^30.4.2": - version: 30.4.2 - resolution: "jest@npm:30.4.2" +"jest@npm:^29.7.0": + version: 29.7.0 + resolution: "jest@npm:29.7.0" dependencies: - "@jest/core": "npm:30.4.2" - "@jest/types": "npm:30.4.1" - import-local: "npm:^3.2.0" - jest-cli: "npm:30.4.2" + "@jest/core": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + import-local: "npm:^3.0.2" + jest-cli: "npm:^29.7.0" peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: node-notifier: optional: true bin: - jest: ./bin/jest.js - checksum: 10/3690f4009a46781b480fbd4c8c60dd910a3cf8af76626f29971003c28eb17dfa322e86f3203e3da168edcc9f7ba973f3b1fbf15ca12393cc5f86bc61f3289fef + jest: bin/jest.js + checksum: 10/97023d78446098c586faaa467fbf2c6b07ff06e2c85a19e3926adb5b0effe9ac60c4913ae03e2719f9c01ae8ffd8d92f6b262cedb9555ceeb5d19263d8c6362a languageName: node linkType: hard @@ -18768,36 +18752,42 @@ __metadata: languageName: node linkType: hard -"jsdom@npm:^26.1.0": - version: 26.1.0 - resolution: "jsdom@npm:26.1.0" +"jsdom@npm:^20.0.0": + version: 20.0.3 + resolution: "jsdom@npm:20.0.3" dependencies: - cssstyle: "npm:^4.2.1" - data-urls: "npm:^5.0.0" - decimal.js: "npm:^10.5.0" - html-encoding-sniffer: "npm:^4.0.0" - http-proxy-agent: "npm:^7.0.2" - https-proxy-agent: "npm:^7.0.6" + abab: "npm:^2.0.6" + acorn: "npm:^8.8.1" + acorn-globals: "npm:^7.0.0" + cssom: "npm:^0.5.0" + cssstyle: "npm:^2.3.0" + data-urls: "npm:^3.0.2" + decimal.js: "npm:^10.4.2" + domexception: "npm:^4.0.0" + escodegen: "npm:^2.0.0" + form-data: "npm:^4.0.0" + html-encoding-sniffer: "npm:^3.0.0" + http-proxy-agent: "npm:^5.0.0" + https-proxy-agent: "npm:^5.0.1" is-potential-custom-element-name: "npm:^1.0.1" - nwsapi: "npm:^2.2.16" - parse5: "npm:^7.2.1" - rrweb-cssom: "npm:^0.8.0" + nwsapi: "npm:^2.2.2" + parse5: "npm:^7.1.1" saxes: "npm:^6.0.0" symbol-tree: "npm:^3.2.4" - tough-cookie: "npm:^5.1.1" - w3c-xmlserializer: "npm:^5.0.0" + tough-cookie: "npm:^4.1.2" + w3c-xmlserializer: "npm:^4.0.0" webidl-conversions: "npm:^7.0.0" - whatwg-encoding: "npm:^3.1.1" - whatwg-mimetype: "npm:^4.0.0" - whatwg-url: "npm:^14.1.1" - ws: "npm:^8.18.0" - xml-name-validator: "npm:^5.0.0" + whatwg-encoding: "npm:^2.0.0" + whatwg-mimetype: "npm:^3.0.0" + whatwg-url: "npm:^11.0.0" + ws: "npm:^8.11.0" + xml-name-validator: "npm:^4.0.0" peerDependencies: - canvas: ^3.0.0 + canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 10/39d78c4889cac20826393400dce1faed1666e9244fe0c8342a8f08c315375878e6be7fcfe339a33d6ff1a083bfe9e71b16d56ecf4d9a87db2da8c795925ea8c1 + checksum: 10/a4cdcff5b07eed87da90b146b82936321533b5efe8124492acf7160ebd5b9cf2b3c2435683592bf1cffb479615245756efb6c173effc1906f845a86ed22af985 languageName: node linkType: hard @@ -19054,6 +19044,16 @@ __metadata: languageName: node linkType: hard +"locate-path@npm:^3.0.0": + version: 3.0.0 + resolution: "locate-path@npm:3.0.0" + dependencies: + p-locate: "npm:^3.0.0" + path-exists: "npm:^3.0.0" + checksum: 10/53db3996672f21f8b0bf2a2c645ae2c13ffdae1eeecfcd399a583bce8516c0b88dcb4222ca6efbbbeb6949df7e46860895be2c02e8d3219abd373ace3bfb4e11 + languageName: node + linkType: hard + "locate-path@npm:^5.0.0": version: 5.0.0 resolution: "locate-path@npm:5.0.0" @@ -19130,6 +19130,13 @@ __metadata: languageName: node linkType: hard +"lodash.throttle@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.throttle@npm:4.1.1" + checksum: 10/9be9fb2ffd686c20543167883305542f4564062a5f712a40e8c6f2f0d9fd8254a6e9d801c2470b1b24e0cdf2ae83c1277b55aa0fb4799a2db6daf545f53820e1 + languageName: node + linkType: hard + "lodash.uniq@npm:^4.5.0": version: 4.5.0 resolution: "lodash.uniq@npm:4.5.0" @@ -19192,7 +19199,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0, lru-cache@npm:^10.4.3": +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": version: 10.4.3 resolution: "lru-cache@npm:10.4.3" checksum: 10/e6e90267360476720fa8e83cc168aa2bf0311f3f2eea20a6ba78b90a885ae72071d9db132f40fda4129c803e7dcec3a6b6a6fbb44ca90b081630b810b5d6a41a @@ -20170,7 +20177,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.5, micromatch@npm:^4.0.8": +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -20522,15 +20529,6 @@ __metadata: languageName: node linkType: hard -"napi-postinstall@npm:^0.3.0": - version: 0.3.4 - resolution: "napi-postinstall@npm:0.3.4" - bin: - napi-postinstall: lib/cli.js - checksum: 10/5541381508f9e1051ff3518701c7130ebac779abb3a1ffe9391fcc3cab4cc0569b0ba0952357db3f6b12909c3bb508359a7a60261ffd795feebbdab967175832 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -20834,7 +20832,7 @@ __metadata: languageName: node linkType: hard -"nwsapi@npm:^2.2.16": +"nwsapi@npm:^2.2.2": version: 2.2.23 resolution: "nwsapi@npm:2.2.23" checksum: 10/aa4a570039c33d70b51436d1bb533f3e2c33c488ccbe9b09285c46a6cee5ef266fd60103461085c6954ba52460786a8138f042958328c7c1b4763898eb3dadfa @@ -21212,7 +21210,7 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^2.2.0": +"p-limit@npm:^2.0.0, p-limit@npm:^2.2.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" dependencies: @@ -21239,6 +21237,15 @@ __metadata: languageName: node linkType: hard +"p-locate@npm:^3.0.0": + version: 3.0.0 + resolution: "p-locate@npm:3.0.0" + dependencies: + p-limit: "npm:^2.0.0" + checksum: 10/83991734a9854a05fe9dbb29f707ea8a0599391f52daac32b86f08e21415e857ffa60f0e120bfe7ce0cc4faf9274a50239c7895fc0d0579d08411e513b83a4ae + languageName: node + linkType: hard + "p-locate@npm:^4.1.0": version: 4.1.0 resolution: "p-locate@npm:4.1.0" @@ -21420,7 +21427,7 @@ __metadata: languageName: node linkType: hard -"parse5@npm:^7.0.0, parse5@npm:^7.2.1, parse5@npm:^7.3.0": +"parse5@npm:^7.0.0, parse5@npm:^7.1.1, parse5@npm:^7.3.0": version: 7.3.0 resolution: "parse5@npm:7.3.0" dependencies: @@ -21453,6 +21460,13 @@ __metadata: languageName: node linkType: hard +"path-exists@npm:^3.0.0": + version: 3.0.0 + resolution: "path-exists@npm:3.0.0" + checksum: 10/96e92643aa34b4b28d0de1cd2eba52a1c5313a90c6542d03f62750d82480e20bfa62bc865d5cfc6165f5fcd5aeb0851043c40a39be5989646f223300021bae0a + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -21586,10 +21600,10 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": - version: 4.0.5 - resolution: "picomatch@npm:4.0.5" - checksum: 10/8bad770af9dcdb7f94ad1a893adcbe08a97d75a18872f8fed37161d3124217471c402b3929f051532f795f4ff2e53dcebb1644df4c1a5f3a9c476fef3b9f8c51 +"picomatch@npm:^4.0.4": + version: 4.0.4 + resolution: "picomatch@npm:4.0.4" + checksum: 10/f6ef80a3590827ce20378ae110ac78209cc4f74d39236370f1780f957b7ee41c12acde0e4651b90f39983506fd2f5e449994716f516db2e9752924aff8de93ce languageName: node linkType: hard @@ -21600,10 +21614,10 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.7": - version: 4.0.7 - resolution: "pirates@npm:4.0.7" - checksum: 10/2427f371366081ae42feb58214f04805d6b41d6b84d74480ebcc9e0ddbd7105a139f7c653daeaf83ad8a1a77214cf07f64178e76de048128fec501eab3305a96 +"pirates@npm:^4.0.4": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: 10/d02dda76f4fec1cbdf395c36c11cf26f76a644f9f9a1bfa84d3167d0d3154d5289aacc72677aa20d599bb4a6937a471de1b65c995e2aea2d8687cbcd7e43ea5f languageName: node linkType: hard @@ -22500,6 +22514,15 @@ __metadata: languageName: node linkType: hard +"prettier-2@npm:prettier@^2.8.8": + version: 2.8.8 + resolution: "prettier@npm:2.8.8" + bin: + prettier: bin-prettier.js + checksum: 10/00cdb6ab0281f98306cd1847425c24cbaaa48a5ff03633945ab4c701901b8e96ad558eb0777364ffc312f437af9b5a07d0f45346266e8245beaf6247b9c62b24 + languageName: node + linkType: hard + "prettier-linter-helpers@npm:^1.0.0": version: 1.0.0 resolution: "prettier-linter-helpers@npm:1.0.0" @@ -22528,15 +22551,14 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:30.4.1, pretty-format@npm:^30.0.0": - version: 30.4.1 - resolution: "pretty-format@npm:30.4.1" +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" dependencies: - "@jest/schemas": "npm:30.4.1" - ansi-styles: "npm:^5.2.0" - react-is-18: "npm:react-is@^18.3.1" - react-is-19: "npm:react-is@^19.2.5" - checksum: 10/60311ef47a646eeaec0432efe66290cb6f0d2eccb123a28ad4ab6d7e53087bc62db91cfd54c3cc00c89d6875aefb2bf6264381b6c9411ce6bff3d6aa8280abad + "@jest/schemas": "npm:^29.6.3" + ansi-styles: "npm:^5.0.0" + react-is: "npm:^18.0.0" + checksum: 10/dea96bc83c83cd91b2bfc55757b6b2747edcaac45b568e46de29deee80742f17bc76fe8898135a70d904f4928eafd8bb693cd1da4896e8bdd3c5e82cadf1d2bb languageName: node linkType: hard @@ -22614,7 +22636,7 @@ __metadata: languageName: node linkType: hard -"prompts@npm:^2.4.2": +"prompts@npm:^2.0.1, prompts@npm:^2.4.2": version: 2.4.2 resolution: "prompts@npm:2.4.2" dependencies: @@ -22693,6 +22715,15 @@ __metadata: languageName: node linkType: hard +"psl@npm:^1.1.33": + version: 1.15.0 + resolution: "psl@npm:1.15.0" + dependencies: + punycode: "npm:^2.3.1" + checksum: 10/5e7467eb5196eb7900d156783d12907d445c0122f76c73203ce96b148a6ccf8c5450cc805887ffada38ff92d634afcf33720c24053cb01d5b6598d1c913c5caf + languageName: node + linkType: hard + "pump@npm:^3.0.0": version: 3.0.4 resolution: "pump@npm:3.0.4" @@ -22726,10 +22757,10 @@ __metadata: languageName: node linkType: hard -"pure-rand@npm:^7.0.0": - version: 7.0.1 - resolution: "pure-rand@npm:7.0.1" - checksum: 10/c61a576fda5032ec9763ecb000da4a8f19263b9e2f9ae9aa2759c8fbd9dc6b192b2ce78391ebd41abb394a5fedb7bcc4b03c9e6141ac8ab20882dd5717698b80 +"pure-rand@npm:^6.0.0": + version: 6.1.0 + resolution: "pure-rand@npm:6.1.0" + checksum: 10/256aa4bcaf9297256f552914e03cbdb0039c8fe1db11fa1e6d3f80790e16e563eb0a859a1e61082a95e224fc0c608661839439f8ecc6a3db4e48d46d99216ee4 languageName: node linkType: hard @@ -22758,6 +22789,13 @@ __metadata: languageName: node linkType: hard +"querystringify@npm:^2.1.1": + version: 2.2.0 + resolution: "querystringify@npm:2.2.0" + checksum: 10/46ab16f252fd892fc29d6af60966d338cdfeea68a231e9457631ffd22d67cec1e00141e0a5236a2eb16c0d7d74175d9ec1d6f963660c6f2b1c2fc85b194c5680 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -22874,20 +22912,6 @@ __metadata: languageName: node linkType: hard -"react-is-18@npm:react-is@^18.3.1": - version: 18.3.1 - resolution: "react-is@npm:18.3.1" - checksum: 10/d5f60c87d285af24b1e1e7eaeb123ec256c3c8bdea7061ab3932e3e14685708221bf234ec50b21e10dd07f008f1b966a2730a0ce4ff67905b3872ff2042aec22 - languageName: node - linkType: hard - -"react-is-19@npm:react-is@^19.2.5": - version: 19.2.7 - resolution: "react-is@npm:19.2.7" - checksum: 10/ae0d3ae7638aa2fa2a82a78a817daf2806e57fa0aef357d07e1e8d1e9a301902e5967168e9334b5816db0df8fa980a725cd57569ba5a9e6e76a71e117b18be04 - languageName: node - linkType: hard - "react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -22895,6 +22919,13 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^18.0.0": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: 10/d5f60c87d285af24b1e1e7eaeb123ec256c3c8bdea7061ab3932e3e14685708221bf234ec50b21e10dd07f008f1b966a2730a0ce4ff67905b3872ff2042aec22 + languageName: node + linkType: hard + "react-json-view-lite@npm:^2.3.0": version: 2.5.0 resolution: "react-json-view-lite@npm:2.5.0" @@ -23343,6 +23374,13 @@ __metadata: languageName: node linkType: hard +"require-main-filename@npm:^2.0.0": + version: 2.0.0 + resolution: "require-main-filename@npm:2.0.0" + checksum: 10/8604a570c06a69c9d939275becc33a65676529e1c3e5a9f42d58471674df79357872b96d70bb93a0380a62d60dc9031c98b1a9dad98c946ffdd61b7ac0c8cedd + languageName: node + linkType: hard + "requires-port@npm:^1.0.0": version: 1.0.0 resolution: "requires-port@npm:1.0.0" @@ -23401,6 +23439,13 @@ __metadata: languageName: node linkType: hard +"resolve.exports@npm:^2.0.0": + version: 2.0.3 + resolution: "resolve.exports@npm:2.0.3" + checksum: 10/536efee0f30a10fac8604e6cdc7844dbc3f4313568d09f06db4f7ed8a5b8aeb8585966fe975083d1f2dfbc87cf5f8bc7ab65a5c23385c14acbb535ca79f8398a + languageName: node + linkType: hard + "resolve@npm:1.22.8": version: 1.22.8 resolution: "resolve@npm:1.22.8" @@ -23414,7 +23459,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.22.11, resolve@npm:^1.22.4": +"resolve@npm:^1.20.0, resolve@npm:^1.22.11, resolve@npm:^1.22.4": version: 1.22.12 resolution: "resolve@npm:1.22.12" dependencies: @@ -23441,7 +23486,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.22.11#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": +"resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.11#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": version: 1.22.12 resolution: "resolve@patch:resolve@npm%3A1.22.12#optional!builtin::version=1.22.12&hash=c3c19d" dependencies: @@ -23524,13 +23569,6 @@ __metadata: languageName: node linkType: hard -"rrweb-cssom@npm:^0.8.0": - version: 0.8.0 - resolution: "rrweb-cssom@npm:0.8.0" - checksum: 10/07521ee36fb6569c17906afad1ac7ff8f099d49ade9249e190693ac36cdf27f88d9acf0cc66978935d5d0a23fca105643d7e9125b9a9d91ed9db9e02d31d7d80 - languageName: node - linkType: hard - "rtlcss@npm:^4.1.0": version: 4.3.0 resolution: "rtlcss@npm:4.3.0" @@ -23713,7 +23751,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.3.1": +"semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" bin: @@ -23722,12 +23760,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.1, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.3, semver@npm:^7.7.2, semver@npm:^7.7.3, semver@npm:^7.8.0, semver@npm:^7.8.1": - version: 7.8.5 - resolution: "semver@npm:7.8.5" +"semver@npm:^7.1.1, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.3, semver@npm:^7.7.3, semver@npm:^7.8.1": + version: 7.8.2 + resolution: "semver@npm:7.8.2" bin: semver: bin/semver.js - checksum: 10/9b01d2ff11e6e4a4539b7ca3c5f280c8704cb397a28504469f2ed4f00ad2194748d756647362a9712fff30984d15772ab7f083108c2fb508e2096ae9e708f22c + checksum: 10/52221d8f1cadacda3cc3f0a2e7f7146e0442c7f4219acb25970bed055f5d0a6afbba5f22e293b078c2e93fca3dce0a08b088485e8b75d32a165f16c3627091c8 languageName: node linkType: hard @@ -23814,6 +23852,13 @@ __metadata: languageName: node linkType: hard +"set-blocking@npm:^2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 10/8980ebf7ae9eb945bb036b6e283c547ee783a1ad557a82babf758a065e2fb6ea337fd82cac30dd565c1e606e423f30024a19fff7afbf4977d784720c4026a8ef + languageName: node + linkType: hard + "set-function-length@npm:^1.2.2": version: 1.2.2 resolution: "set-function-length@npm:1.2.2" @@ -23967,7 +24012,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3": +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: 10/a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -24181,7 +24226,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0": +"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": version: 0.6.1 resolution: "source-map@npm:0.6.1" checksum: 10/59ef7462f1c29d502b3057e822cdbdae0b0e565302c4dd1a95e11e793d8d9d62006cdc10e0fd99163ca33ff2071360cf50ee13f90440806e7ed57d81cba2f7ff @@ -24310,7 +24355,7 @@ __metadata: languageName: node linkType: hard -"stack-utils@npm:^2.0.6": +"stack-utils@npm:^2.0.3": version: 2.0.6 resolution: "stack-utils@npm:2.0.6" dependencies: @@ -24355,7 +24400,7 @@ __metadata: languageName: node linkType: hard -"string-length@npm:^4.0.2": +"string-length@npm:^4.0.1": version: 4.0.2 resolution: "string-length@npm:4.0.2" dependencies: @@ -24376,6 +24421,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^3.0.0, string-width@npm:^3.1.0": + version: 3.1.0 + resolution: "string-width@npm:3.1.0" + dependencies: + emoji-regex: "npm:^7.0.1" + is-fullwidth-code-point: "npm:^2.0.0" + strip-ansi: "npm:^5.1.0" + checksum: 10/57f7ca73d201682816d573dc68bd4bb8e1dff8dc9fcf10470fdfc3474135c97175fec12ea6a159e67339b41e86963112355b64529489af6e7e70f94a7caf08b2 + languageName: node + linkType: hard + "string-width@npm:^5.0.1, string-width@npm:^5.1.2": version: 5.1.2 resolution: "string-width@npm:5.1.2" @@ -24435,6 +24491,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi@npm:^5.0.0, strip-ansi@npm:^5.1.0, strip-ansi@npm:^5.2.0": + version: 5.2.0 + resolution: "strip-ansi@npm:5.2.0" + dependencies: + ansi-regex: "npm:^4.1.0" + checksum: 10/bdb5f76ade97062bd88e7723aa019adbfacdcba42223b19ccb528ffb9fb0b89a5be442c663c4a3fb25268eaa3f6ea19c7c3fbae830bd1562d55adccae1fcec46 + languageName: node + linkType: hard + "strip-ansi@npm:^7.0.1": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" @@ -24548,7 +24613,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8, supports-color@npm:^8.0.0, supports-color@npm:^8.1.1": +"supports-color@npm:^8, supports-color@npm:^8.0.0": version: 8.1.1 resolution: "supports-color@npm:8.1.1" dependencies: @@ -24595,15 +24660,6 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.11.8": - version: 0.11.12 - resolution: "synckit@npm:0.11.12" - dependencies: - "@pkgr/core": "npm:^0.2.9" - checksum: 10/2f51978bfed81aaf0b093f596709a72c49b17909020f42b43c5549f9c0fe18b1fe29f82e41ef771172d729b32e9ce82900a85d2b87fa14d59f886d4df8d7a329 - languageName: node - linkType: hard - "synckit@npm:^0.9.1": version: 0.9.2 resolution: "synckit@npm:0.9.2" @@ -24812,24 +24868,6 @@ __metadata: languageName: node linkType: hard -"tldts-core@npm:^6.1.86": - version: 6.1.86 - resolution: "tldts-core@npm:6.1.86" - checksum: 10/cb5dff9cc15661ac773a2099e98c99a5cb3cebc35909c23cc4261ff7992032c7501995ae995de3574dbbf3431e59c47496534d52f5e96abcb231f0e72144c020 - languageName: node - linkType: hard - -"tldts@npm:^6.1.32": - version: 6.1.86 - resolution: "tldts@npm:6.1.86" - dependencies: - tldts-core: "npm:^6.1.86" - bin: - tldts: bin/cli.js - checksum: 10/f7e66824e44479ccdda55ea556af14ce61c4d27708be403e3f90631defde49f82a580e1ca07187cc7e3b349e257a30c2808a22903f3a0548e136ebb609ccc109 - languageName: node - linkType: hard - "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -24860,21 +24898,24 @@ __metadata: languageName: node linkType: hard -"tough-cookie@npm:^5.1.1": - version: 5.1.2 - resolution: "tough-cookie@npm:5.1.2" +"tough-cookie@npm:^4.1.2": + version: 4.1.4 + resolution: "tough-cookie@npm:4.1.4" dependencies: - tldts: "npm:^6.1.32" - checksum: 10/de430e6e6d34b794137e05b8ac2aa6b74ebbe6cdceb4126f168cf1e76101162a4b2e0e7587c3b70e728bd8654fc39958b2035be7619ee6f08e7257610ba4cd04 + psl: "npm:^1.1.33" + punycode: "npm:^2.1.1" + universalify: "npm:^0.2.0" + url-parse: "npm:^1.5.3" + checksum: 10/75663f4e2cd085f16af0b217e4218772adf0617fb3227171102618a54ce0187a164e505d61f773ed7d65988f8ff8a8f935d381f87da981752c1171b076b4afac languageName: node linkType: hard -"tr46@npm:^5.1.0": - version: 5.1.1 - resolution: "tr46@npm:5.1.1" +"tr46@npm:^3.0.0": + version: 3.0.0 + resolution: "tr46@npm:3.0.0" dependencies: - punycode: "npm:^2.3.1" - checksum: 10/833a0e1044574da5790148fd17866d4ddaea89e022de50279967bcd6b28b4ce0d30d59eb3acf9702b60918975b3bad481400337e3a2e6326cffa5c77b874753d + punycode: "npm:^2.1.1" + checksum: 10/b09a15886cbfaee419a3469081223489051ce9dca3374dd9500d2378adedbee84a3c73f83bfdd6bb13d53657753fc0d4e20a46bfcd3f1b9057ef528426ad7ce4 languageName: node linkType: hard @@ -24917,17 +24958,17 @@ __metadata: languageName: node linkType: hard -"ts-jest@npm:^29.4.11": - version: 29.4.11 - resolution: "ts-jest@npm:29.4.11" +"ts-jest@npm:^29.2.5": + version: 29.4.6 + resolution: "ts-jest@npm:29.4.6" dependencies: bs-logger: "npm:^0.2.6" fast-json-stable-stringify: "npm:^2.1.0" - handlebars: "npm:^4.7.9" + handlebars: "npm:^4.7.8" json5: "npm:^2.2.3" lodash.memoize: "npm:^4.1.2" make-error: "npm:^1.3.6" - semver: "npm:^7.8.0" + semver: "npm:^7.7.3" type-fest: "npm:^4.41.0" yargs-parser: "npm:^21.1.1" peerDependencies: @@ -24937,7 +24978,7 @@ __metadata: babel-jest: ^29.0.0 || ^30.0.0 jest: ^29.0.0 || ^30.0.0 jest-util: ^29.0.0 || ^30.0.0 - typescript: ">=4.3 <7" + typescript: ">=4.3 <6" peerDependenciesMeta: "@babel/core": optional: true @@ -24953,7 +24994,7 @@ __metadata: optional: true bin: ts-jest: cli.js - checksum: 10/8a76800cac1c102c789429a82655adde1d540d7ff025fc888006f4f458afe07ca4d3f73622a10a604edea06aa12c298605cd1a208a6380c0e3d591a3daed8af6 + checksum: 10/e0ff9e13f684166d5331808b288043b8054f49a1c2970480a92ba3caec8d0ff20edd092f2a4e7a3ad8fcb9ba4d674bee10ec7ee75046d8066bbe43a7d16cf72e languageName: node linkType: hard @@ -25342,6 +25383,13 @@ __metadata: languageName: node linkType: hard +"universalify@npm:^0.2.0": + version: 0.2.0 + resolution: "universalify@npm:0.2.0" + checksum: 10/e86134cb12919d177c2353196a4cc09981524ee87abf621f7bc8d249dbbbebaec5e7d1314b96061497981350df786e4c5128dbf442eba104d6e765bc260678b5 + languageName: node + linkType: hard + "universalify@npm:^2.0.0": version: 2.0.1 resolution: "universalify@npm:2.0.1" @@ -25356,73 +25404,6 @@ __metadata: languageName: node linkType: hard -"unrs-resolver@npm:^1.7.11": - version: 1.11.1 - resolution: "unrs-resolver@npm:1.11.1" - dependencies: - "@unrs/resolver-binding-android-arm-eabi": "npm:1.11.1" - "@unrs/resolver-binding-android-arm64": "npm:1.11.1" - "@unrs/resolver-binding-darwin-arm64": "npm:1.11.1" - "@unrs/resolver-binding-darwin-x64": "npm:1.11.1" - "@unrs/resolver-binding-freebsd-x64": "npm:1.11.1" - "@unrs/resolver-binding-linux-arm-gnueabihf": "npm:1.11.1" - "@unrs/resolver-binding-linux-arm-musleabihf": "npm:1.11.1" - "@unrs/resolver-binding-linux-arm64-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-arm64-musl": "npm:1.11.1" - "@unrs/resolver-binding-linux-ppc64-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-riscv64-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-riscv64-musl": "npm:1.11.1" - "@unrs/resolver-binding-linux-s390x-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-x64-gnu": "npm:1.11.1" - "@unrs/resolver-binding-linux-x64-musl": "npm:1.11.1" - "@unrs/resolver-binding-wasm32-wasi": "npm:1.11.1" - "@unrs/resolver-binding-win32-arm64-msvc": "npm:1.11.1" - "@unrs/resolver-binding-win32-ia32-msvc": "npm:1.11.1" - "@unrs/resolver-binding-win32-x64-msvc": "npm:1.11.1" - napi-postinstall: "npm:^0.3.0" - dependenciesMeta: - "@unrs/resolver-binding-android-arm-eabi": - optional: true - "@unrs/resolver-binding-android-arm64": - optional: true - "@unrs/resolver-binding-darwin-arm64": - optional: true - "@unrs/resolver-binding-darwin-x64": - optional: true - "@unrs/resolver-binding-freebsd-x64": - optional: true - "@unrs/resolver-binding-linux-arm-gnueabihf": - optional: true - "@unrs/resolver-binding-linux-arm-musleabihf": - optional: true - "@unrs/resolver-binding-linux-arm64-gnu": - optional: true - "@unrs/resolver-binding-linux-arm64-musl": - optional: true - "@unrs/resolver-binding-linux-ppc64-gnu": - optional: true - "@unrs/resolver-binding-linux-riscv64-gnu": - optional: true - "@unrs/resolver-binding-linux-riscv64-musl": - optional: true - "@unrs/resolver-binding-linux-s390x-gnu": - optional: true - "@unrs/resolver-binding-linux-x64-gnu": - optional: true - "@unrs/resolver-binding-linux-x64-musl": - optional: true - "@unrs/resolver-binding-wasm32-wasi": - optional: true - "@unrs/resolver-binding-win32-arm64-msvc": - optional: true - "@unrs/resolver-binding-win32-ia32-msvc": - optional: true - "@unrs/resolver-binding-win32-x64-msvc": - optional: true - checksum: 10/4de653508cbaae47883a896bd5cdfef0e5e87b428d62620d16fd35cd534beaebf08ebf0cf2f8b4922aa947b2fe745180facf6cc3f39ba364f7ce0f974cb06a70 - languageName: node - linkType: hard - "unzipper@npm:^0.12.3": version: 0.12.3 resolution: "unzipper@npm:0.12.3" @@ -25498,6 +25479,25 @@ __metadata: languageName: node linkType: hard +"url-parse@npm:^1.5.3": + version: 1.5.10 + resolution: "url-parse@npm:1.5.10" + dependencies: + querystringify: "npm:^2.1.1" + requires-port: "npm:^1.0.0" + checksum: 10/c9e96bc8c5b34e9f05ddfeffc12f6aadecbb0d971b3cc26015b58d5b44676a99f50d5aeb1e5c9e61fa4d49961ae3ab1ae997369ed44da51b2f5ac010d188e6ad + languageName: node + linkType: hard + +"use-sync-external-store@npm:^1.6.0": + version: 1.6.0 + resolution: "use-sync-external-store@npm:1.6.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10/b40ad2847ba220695bff2d4ba4f4d60391c0fb4fb012faa7a4c18eb38b69181936f5edc55a522c4d20a788d1a879b73c3810952c9d0fd128d01cb3f22042c09e + languageName: node + linkType: hard + "utf8@npm:^3.0.0": version: 3.0.0 resolution: "utf8@npm:3.0.0" @@ -25677,12 +25677,12 @@ __metadata: languageName: node linkType: hard -"w3c-xmlserializer@npm:^5.0.0": - version: 5.0.0 - resolution: "w3c-xmlserializer@npm:5.0.0" +"w3c-xmlserializer@npm:^4.0.0": + version: 4.0.0 + resolution: "w3c-xmlserializer@npm:4.0.0" dependencies: - xml-name-validator: "npm:^5.0.0" - checksum: 10/d78f59e6b4f924aa53b6dfc56949959229cae7fe05ea9374eb38d11edcec01398b7f5d7a12576bd5acc57ff446abb5c9115cd83b9d882555015437cf858d42f0 + xml-name-validator: "npm:^4.0.0" + checksum: 10/9a00c412b5496f4f040842c9520bc0aaec6e0c015d06412a91a723cd7d84ea605ab903965f546b4ecdb3eae267f5145ba08565222b1d6cb443ee488cda9a0aee languageName: node linkType: hard @@ -25954,6 +25954,15 @@ __metadata: languageName: node linkType: hard +"whatwg-encoding@npm:^2.0.0": + version: 2.0.0 + resolution: "whatwg-encoding@npm:2.0.0" + dependencies: + iconv-lite: "npm:0.6.3" + checksum: 10/162d712d88fd134a4fe587e53302da812eb4215a1baa4c394dfd86eff31d0a079ff932c05233857997de07481093358d6e7587997358f49b8a580a777be22089 + languageName: node + linkType: hard + "whatwg-encoding@npm:^3.1.1": version: 3.1.1 resolution: "whatwg-encoding@npm:3.1.1" @@ -25970,6 +25979,13 @@ __metadata: languageName: node linkType: hard +"whatwg-mimetype@npm:^3.0.0": + version: 3.0.0 + resolution: "whatwg-mimetype@npm:3.0.0" + checksum: 10/96f9f628c663c2ae05412c185ca81b3df54bcb921ab52fe9ebc0081c1720f25d770665401eb2338ab7f48c71568133845638e18a81ed52ab5d4dcef7d22b40ef + languageName: node + linkType: hard + "whatwg-mimetype@npm:^4.0.0": version: 4.0.0 resolution: "whatwg-mimetype@npm:4.0.0" @@ -25977,13 +25993,13 @@ __metadata: languageName: node linkType: hard -"whatwg-url@npm:^14.0.0, whatwg-url@npm:^14.1.1": - version: 14.2.0 - resolution: "whatwg-url@npm:14.2.0" +"whatwg-url@npm:^11.0.0": + version: 11.0.0 + resolution: "whatwg-url@npm:11.0.0" dependencies: - tr46: "npm:^5.1.0" + tr46: "npm:^3.0.0" webidl-conversions: "npm:^7.0.0" - checksum: 10/f0a95b0601c64f417c471536a2d828b4c16fe37c13662483a32f02f183ed0f441616609b0663fb791e524e8cd56d9a86dd7366b1fc5356048ccb09b576495e7c + checksum: 10/dfcd51c6f4bfb54685528fb10927f3fd3d7c809b5671beef4a8cdd7b1408a7abf3343a35bc71dab83a1424f1c1e92cc2700d7930d95d231df0fac361de0c7648 languageName: node linkType: hard @@ -25997,6 +26013,13 @@ __metadata: languageName: node linkType: hard +"which-module@npm:^2.0.0": + version: 2.0.1 + resolution: "which-module@npm:2.0.1" + checksum: 10/1967b7ce17a2485544a4fdd9063599f0f773959cca24176dbe8f405e55472d748b7c549cd7920ff6abb8f1ab7db0b0f1b36de1a21c57a8ff741f4f1e792c52be + languageName: node + linkType: hard + "which@npm:^1.2.10": version: 1.3.1 resolution: "which@npm:1.3.1" @@ -26091,6 +26114,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^5.1.0": + version: 5.1.0 + resolution: "wrap-ansi@npm:5.1.0" + dependencies: + ansi-styles: "npm:^3.2.0" + string-width: "npm:^3.0.0" + strip-ansi: "npm:^5.0.0" + checksum: 10/f02bbbd13f40169f3d69b8c95126c1d2a340e6f149d04125527c3d501d74a304a434f4329a83bfdc3b9fdb82403e9ae0cdd7b83a99f0da0d5a7e544f6b709914 + languageName: node + linkType: hard + "wrap-ansi@npm:^8.0.1, wrap-ansi@npm:^8.1.0": version: 8.1.0 resolution: "wrap-ansi@npm:8.1.0" @@ -26128,7 +26162,17 @@ __metadata: languageName: node linkType: hard -"write-file-atomic@npm:^5.0.0, write-file-atomic@npm:^5.0.1": +"write-file-atomic@npm:^4.0.2": + version: 4.0.2 + resolution: "write-file-atomic@npm:4.0.2" + dependencies: + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^3.0.7" + checksum: 10/3be1f5508a46c190619d5386b1ac8f3af3dbe951ed0f7b0b4a0961eed6fc626bd84b50cf4be768dabc0a05b672f5d0c5ee7f42daa557b14415d18c3a13c7d246 + languageName: node + linkType: hard + +"write-file-atomic@npm:^5.0.0": version: 5.0.1 resolution: "write-file-atomic@npm:5.0.1" dependencies: @@ -26198,7 +26242,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.18.0, ws@npm:^8.18.3": +"ws@npm:^8.11.0, ws@npm:^8.18.0, ws@npm:^8.18.3": version: 8.21.0 resolution: "ws@npm:8.21.0" peerDependencies: @@ -26247,10 +26291,10 @@ __metadata: languageName: node linkType: hard -"xml-name-validator@npm:^5.0.0": - version: 5.0.0 - resolution: "xml-name-validator@npm:5.0.0" - checksum: 10/43f30f3f6786e406dd665acf08cd742d5f8a46486bd72517edb04b27d1bcd1599664c2a4a99fc3f1e56a3194bff588b12f178b7972bc45c8047bdc4c3ac8d4a1 +"xml-name-validator@npm:^4.0.0": + version: 4.0.0 + resolution: "xml-name-validator@npm:4.0.0" + checksum: 10/f9582a3f281f790344a471c207516e29e293c6041b2c20d84dd6e58832cd7c19796c47e108fd4fd4b164a5e72ad94f2268f8ace8231cde4a2c6428d6aa220f92 languageName: node linkType: hard @@ -26268,6 +26312,13 @@ __metadata: languageName: node linkType: hard +"y18n@npm:^4.0.0": + version: 4.0.3 + resolution: "y18n@npm:4.0.3" + checksum: 10/392870b2a100bbc643bc035fe3a89cef5591b719c7bdc8721bcdb3d27ab39fa4870acdca67b0ee096e146d769f311d68eda6b8195a6d970f227795061923013f + languageName: node + linkType: hard + "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8" @@ -26305,6 +26356,16 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^13.1.2": + version: 13.1.2 + resolution: "yargs-parser@npm:13.1.2" + dependencies: + camelcase: "npm:^5.0.0" + decamelize: "npm:^1.2.0" + checksum: 10/89a84fbb32827832a1d34f596f5efe98027c398af731728304a920c2f9ba03071c694418723df16882ebb646ddb72a8fb1c9567552afcbc2f268e86c4faea5a8 + languageName: node + linkType: hard + "yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" @@ -26312,7 +26373,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.0.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": +"yargs@npm:17.7.2, yargs@npm:^17.0.1, yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: @@ -26327,6 +26388,24 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^13.3.0": + version: 13.3.2 + resolution: "yargs@npm:13.3.2" + dependencies: + cliui: "npm:^5.0.0" + find-up: "npm:^3.0.0" + get-caller-file: "npm:^2.0.1" + require-directory: "npm:^2.1.1" + require-main-filename: "npm:^2.0.0" + set-blocking: "npm:^2.0.0" + string-width: "npm:^3.0.0" + which-module: "npm:^2.0.0" + y18n: "npm:^4.0.0" + yargs-parser: "npm:^13.1.2" + checksum: 10/608ba2e62ac2c7c4572b9c6f7a2d3ef76e2deaad8c8082788ed29ae3ef33e9f68e087f07eb804ed5641de2bc4eab977405d3833b1d11ae8dbbaf5847584d96be + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" From 18c67ef7a322c1c97774f0dc12ffa7c79d93e422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 16:25:08 +0200 Subject: [PATCH 02/19] feat: update readme with architecture doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- packages/kyc-controller/README.md | 627 +++++++++++++++++++++++++++++- 1 file changed, 626 insertions(+), 1 deletion(-) diff --git a/packages/kyc-controller/README.md b/packages/kyc-controller/README.md index 5cc5ccfcb25..5c9d3a83308 100644 --- a/packages/kyc-controller/README.md +++ b/packages/kyc-controller/README.md @@ -1,4 +1,4 @@ -# `@metamask/kyc-controller` +# KYC Controller `@metamask/kyc-controller` Shared KYC / identity verification controller used across MetaMask clients @@ -21,3 +21,628 @@ This watches `src/**/*.ts` and re-runs the build on each change (it also perform ## Contributing This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme). + + +## Architecture + +`@metamask/kyc-controller` is a shared, **platform-agnostic** package that owns +the end-to-end KYC / identity-verification flow used across MetaMask clients +(mobile, extension, web). It hides the vendor implementation (currently +**MoonPay** for identity + **SumSub** for document verification) behind a +vendor-neutral, per-product surface consumed by features such as **ramps** and +**card**. + +This document explains: + +- The package's internal building blocks and responsibilities. +- How the pieces communicate (messenger actions, injected adapters). +- The identity flow as a state machine and an end-to-end sequence. +- The encrypted frame message protocol and crypto. +- How the **metamask-mobile** client wires everything together on the client + side. + +--- + +### 1. Design principles + +The package is built around a few deliberate constraints: + +| Principle | How it shows up in the code | +| --- | --- | +| **Vendor-neutral surface** | Consumers deal with `KycProduct` (`'ramps' \| 'card'`) and a phase machine, never with MoonPay/SumSub specifics. `KycVendor` is internal. | +| **Platform-agnostic core** | No React, no `Buffer`/`atob`, no native SDK imports. Crypto uses `@noble/*` + `@scure/base`. WebView/iframe presentation and the SumSub SDK are **injected** by each client. | +| **Controller owns orchestration; clients own presentation** | `KycController` owns all state, HTTP orchestration, crypto and the frame protocol. Clients only render frames, forward raw messages, and present the SumSub SDK. | +| **Stateless service** | `KycService` performs HTTP only; it holds no state and derives auth/geolocation from other controllers via the messenger. | +| **Everything through the messenger** | Both classes register their public methods as messenger actions, and reach external capabilities (auth token, geolocation) via delegated actions. | + +--- + +### 2. Component overview + +The package splits cleanly into a **stateful orchestrator** (`KycController`), a +**stateless HTTP client** (`KycService`), and supporting modules (crypto, +selectors, types). + +```mermaid +graph TB + subgraph pkg["@metamask/kyc-controller"] + direction TB + Controller["KycController
(BaseController)
state + orchestration + frame protocol"] + Service["KycService
(stateless)
HTTP + response validation"] + Crypto["crypto.ts
X25519 ECDH + AES-256-GCM"] + Selectors["selectors.ts
memoized reselect selectors"] + Types["types.ts
KycPhase, KycProduct,
KycSumSubLauncher, ..."] + Country["countryCodes.ts
alpha-2 → alpha-3"] + end + + subgraph deps["External MetaMask dependencies"] + Base["@metamask/base-controller"] + Msgr["@metamask/messenger"] + CU["@metamask/controller-utils
createServicePolicy, HttpError"] + Geo["GeolocationController"] + Auth["AuthenticationController
(profile-sync)"] + end + + subgraph vendor["Vendor backends (HTTP / frames)"] + UKYC["Universal KYC API
kyc-api.cx.metamask.io"] + Frames["MoonPay frames
blocks.moonpay.com"] + SumSubSDK["SumSub SDK
(native / web)"] + end + + Controller -->|"decryptCredentials()"| Crypto + Controller -->|"messenger.call(KycService:*)"| Service + Controller -.->|"injected launcher"| SumSubSDK + Controller -->|"builds frame URLs
handles frame messages"| Frames + + Service -->|"createServicePolicy / HttpError"| CU + Service -->|"messenger.call(GeolocationController:getGeolocation)"| Geo + Service -->|"messenger.call(AuthenticationController:getBearerToken)"| Auth + Service -->|"fetch()"| UKYC + + Controller --- Base + Controller --- Msgr + Service --- Msgr + Selectors -.->|"read"| Controller +``` + +#### 2.1 `KycController` + +- Extends `BaseController<'KycController', KycControllerState, KycControllerMessenger>`. +- Holds **all flow state** (see [§3](#3-state-shape)). +- Owns an ephemeral **X25519 keypair** (`#keypair`) generated at construction — + never persisted, used only for the frame key exchange. +- Registers its public methods as messenger actions via + `registerMethodActionHandlers`. +- Calls `KycService` exclusively **through the messenger** (`KycService:*` + actions), never a direct reference. +- Delegates SumSub SDK presentation to an injected `sumsubLauncher` + (`KycSumSubLauncher`). +- When the flow is scoped to a product (passed to `initialize` / + `acceptTermsAndStartSession` and stored as `activeProduct`), automatically + runs the KYC-required check once authenticated and chains into document + verification when KYC is required — no extra consumer calls needed. + +Exposed messenger actions (`MESSENGER_EXPOSED_METHODS`): + +`initialize`, `loadDisclaimers`, `acceptTermsAndStartSession`, +`clearSavedTerms`, `handleFrameMessage`, `buildCheckFrameUrl`, +`buildAuthFrameUrl`, `buildResetFrameUrl`, `checkKycRequired`, `getKycStatus`, +`startSumSub`, `reset`. + +#### 2.2 `KycService` + +- **Stateless**, platform-agnostic HTTP client for the Universal KYC (UKYC) + backend. +- Base URL derived from `env` (`production` / `development`) or an explicit + `baseUrl` override. +- Every request is wrapped in a **service policy** (`createServicePolicy`) for + retries/circuit-breaking, and carries a **bearer token** obtained from + `AuthenticationController:getBearerToken`. +- Every response is validated with **superstruct** before being returned; + malformed responses throw a descriptive error. +- Resolves the customer's country from `GeolocationController:getGeolocation` + and maps alpha-2 → alpha-3. + +Exposed messenger actions (`MESSENGER_EXPOSED_METHODS`): + +`getGeoCountry`, `fetchDisclaimers`, `createSession`, `checkKycRequired`, +`createUkycSession`, `submitWrappedKey`. + +Endpoints: + +| Method | HTTP | Endpoint | Purpose | +| --- | --- | --- | --- | +| `getGeoCountry` | — | (geolocation action) | Resolve alpha-3 country | +| `fetchDisclaimers` | `GET` | `/vendors/moonpay/disclaimers?country=` | Terms to accept | +| `createSession` | `POST` | `/vendors/moonpay/sessions` | Create vendor session | +| `checkKycRequired` | `POST` | `/vendors/moonpay/kyc-required` | Is KYC required? (normalizes `required` → `kycRequired`) | +| `createUkycSession` | `POST` | `/sessions` | Start SumSub sub-flow | +| `submitWrappedKey` | `POST` | `/sessions/{id}/wrapped-key` | Exchange wrapped key → applicant token | + +### 2.3 `crypto.ts` + +Implements the Check/Auth frame credential decryption: + +1. Client generates an X25519 keypair; the public key (hex) is added to the + frame URL. +2. The frame returns `{ ephemeralPublicKey, iv|nonce, ciphertext }`. +3. Client derives `shared = X25519(ourPriv, theirEphemeralPub)`, then + `key = HKDF-SHA256(shared, 32 bytes)`, then AES-256-GCM decrypts the + ciphertext (which includes the 16-byte tag). IV must be 12 bytes. + +It tolerates envelopes delivered as an object, a JSON string, or base64(JSON), +and hex-or-base64 binary fields. + +#### 2.4 `selectors.ts` + +Memoized `reselect` selectors over `KycControllerState`: +`selectKycPhase`, `selectKycSumSub`, and the parametric +`selectIsKycRequiredForProduct(product)`. + +--- + +### 3. State shape + +```mermaid +classDiagram + class KycControllerState { + +KycPhase phase + +string statusMessage + +string error + +string email + +string termsAcceptedAt [persisted] + +string[] acceptedDisclaimerIds [persisted] + +KycDisclaimer[] disclaimers + +string disclaimersError + +string geoCountry + +string sessionToken [secret] + +string accessToken [secret] + +string moonpayCustomerId + +KycProduct activeProduct + +Record kycRequiredByProduct [persisted] + +string lastCheckedAt [persisted] + +SumSubState sumsub + } + class SumSubState { + +KycSumSubStatus status + +Json result + +string sessionId + +string applicantAccessToken + } + KycControllerState --> SumSubState : sumsub +``` + +> Note: nullable fields (`error`, `email`, `sessionToken`, …) are typed as +> `T | null` in the source; `Record` is `Partial>`. +> Types are simplified above for diagram readability. + +State metadata highlights (`kycControllerMetadata`): + +- **Persisted** (`persist: true`): `termsAcceptedAt`, `acceptedDisclaimerIds`, + `kycRequiredByProduct`, `lastCheckedAt`. These survive restarts so the flow + can skip already-accepted terms and reuse cached results. +- **Secrets, never persisted / never logged**: `sessionToken`, `accessToken`, + `moonpayCustomerId`, `email`, `disclaimers`, and the whole `sumsub` sub-tree. +- Additional non-state secrets kept **off** the state object entirely: the + X25519 private key (`#keypair`) and the Auth-frame client token + (`#authClientToken`). + +--- + +### 4. The identity flow (phase state machine) + +`KycPhase` models the linear identity flow. Each transition is driven by a +controller method or an incoming frame message. + +```mermaid +stateDiagram-v2 + [*] --> idle + idle --> terms : initialize() (no saved terms) + idle --> session : initialize() (saved terms + email) + + terms --> session : acceptTermsAndStartSession() + session --> check : createSession() ok + session --> terms : createSession() fails (clears saved terms) + + check --> form : Check frame → active (already authenticated) + check --> auth : Check frame → connectionRequired (needs OTP) + check --> terms : Check frame → termsAcceptanceRequired + + auth --> form : Auth frame → active (OTP verified) + auth --> terms : Auth frame → termsAcceptanceRequired + + form --> submit : checkKycRequired()
(auto when a product is set) + submit --> done : kyc-required response ok + submit --> error : request failed + + check --> error : unexpected status / decrypt failure + auth --> error : unexpected status + done --> [*] + error --> idle : reset() + done --> idle : reset() +``` + +> When the flow is scoped to a product (a `product` is passed to `initialize` +> or `acceptTermsAndStartSession`), reaching `form` **automatically** runs the +> KYC-required check (`form → submit → done`) with no user interaction, and — if +> KYC is required — automatically launches the SumSub document-verification +> sub-flow (see [§7](#7-sumsub-sub-flow)). When no product is set the flow stops +> at `form` and the consumer drives `checkKycRequired` / `startSumSub` manually. + +Phase meanings (from `types.ts`): + +| Phase | Meaning | +| --- | --- | +| `idle` | Nothing started. | +| `terms` | Waiting for the customer to accept vendor terms. | +| `session` | Creating the vendor session. | +| `check` | Running the **invisible** connection-check frame. | +| `auth` | Running the **visible** authentication (email OTP) frame. | +| `form` | Authenticated. Auto-runs the KYC-required check when a product is set; otherwise waits for the consumer. | +| `submit` | Submitting the KYC-required check. | +| `done` | Complete — see `kycRequiredByProduct` / `sumsub`. Document verification auto-launches when KYC is required. | +| `error` | Halted — see `error`. | + +--- + +### 5. End-to-end sequence + +This sequence shows the full happy path including the two frames and the SumSub +hand-off. The **client transport** (WebView on mobile, iframe on web) is +generic — it only forwards raw frame messages to `handleFrameMessage` and posts +back any returned `reply`. + +```mermaid +sequenceDiagram + autonumber + actor User + participant UI as Client UI + transport
(WebView/iframe) + participant Ctrl as KycController + participant Svc as KycService + participant Geo as GeolocationController + participant API as UKYC API + participant Frame as MoonPay Check/Auth frame + participant Launcher as SumSub launcher (injected) + + User->>Ctrl: initialize({ email, product }) + Ctrl->>Svc: getGeoCountry() + Svc->>Geo: getGeolocation() + Note over Svc: map alpha-2 → alpha-3 locally + Ctrl->>Svc: fetchDisclaimers({ country }) + Svc->>API: GET /disclaimers + Ctrl-->>UI: phase = terms (+ disclaimers) + + User->>Ctrl: acceptTermsAndStartSession({ email }) + Ctrl->>Svc: createSession({ email, termsAcceptedAt, disclaimerIds }) + Svc->>API: POST /sessions + Ctrl-->>UI: phase = check (+ sessionToken) + + UI->>Ctrl: buildCheckFrameUrl() + Ctrl-->>UI: URL (sessionToken + publicKey) + UI->>Frame: load Check frame (invisible) + Frame-->>UI: handshake + UI->>Ctrl: handleFrameMessage(handshake) + Ctrl-->>UI: reply = ack + UI->>Frame: post ack + Frame-->>UI: complete (status + encrypted credentials) + UI->>Ctrl: handleFrameMessage(complete) + Note over Ctrl: decryptCredentials() → accessToken / clientToken + + alt Check → connectionRequired + Ctrl-->>UI: phase = auth + UI->>Frame: load Auth frame (visible, OTP) + Frame-->>UI: complete (active + credentials) + UI->>Ctrl: handleFrameMessage(complete) + end + + Ctrl-->>UI: phase = form (accessToken set) + + Note over Ctrl: activeProduct set at initialize →
continue automatically (no user action) + Ctrl->>Svc: checkKycRequired({ accessToken, country, capabilities }) + Svc->>API: POST /kyc-required + Ctrl-->>UI: phase = done (kycRequiredByProduct[product]) + + opt kycRequired === true → auto-launch document verification + Ctrl->>Svc: createUkycSession({ jwtToken, vendorMetadata }) + Svc->>API: POST /sessions + Ctrl->>Svc: submitWrappedKey({ sessionId, wrappedUserKey, ... }) + Svc->>API: POST /sessions/{id}/wrapped-key + Ctrl->>Launcher: launch({ applicantAccessToken, onTokenExpiration, onStatusChange }) + Launcher-->>Ctrl: SDK result + Ctrl-->>UI: sumsub.status = complete (+ result) + end +``` + +> The KYC-required check and the document-verification launch after `form` are +> driven by the controller itself, not the user — the flow captures the +> `product` at `initialize` and continues automatically. If `initialize` is +> called without a `product`, the flow stops at `form` and the consumer triggers +> `checkKycRequired` (and later `startSumSub`) explicitly. + +--- + +### 6. Frame message protocol & crypto + +The Check, Auth and Reset frames all speak a small `postMessage` protocol. +`KycController.handleFrameMessage` implements the identity portion; the client +transport is responsible only for delivering messages and injecting replies. + +```mermaid +sequenceDiagram + autonumber + participant Frame as MoonPay frame + participant UI as Client transport + participant Ctrl as KycController + + Frame->>UI: { kind: "handshake", meta:{channelId} } + UI->>Ctrl: handleFrameMessage({ message }) + Ctrl-->>UI: { reply: { version:2, meta:{channelId}, kind:"ack" } } + UI->>Frame: postMessage(ack) + + Frame->>UI: { kind:"complete", meta:{channelId},
payload:{ status, credentials, customer } } + UI->>Ctrl: handleFrameMessage({ message }) + Note over Ctrl: 1. store customer.id (moonpayCustomerId)
2. decryptCredentials(envelope, privKey)
3. route by channelId (ch_1 Check / ch_2 Auth) + Ctrl->>Ctrl: apply outcome → next phase +``` + +Channels: `ch_1` = Check, `ch_2` = Auth, `ch_reset` = Reset. + +Credential decryption (`crypto.ts`): + +```mermaid +graph LR + A["envelope
{ ephemeralPublicKey, iv|nonce, ciphertext }"] --> B["X25519 ECDH
shared = f(ourPriv, theirPub)"] + B --> C["HKDF-SHA256
key (32 bytes)"] + C --> D["AES-256-GCM decrypt
(iv = 12 bytes)"] + D --> E["JSON credentials
{ accessToken?, clientToken? }"] +``` + +Check-frame outcomes (`#handleCheckOutcome`): + +- `active` + `accessToken` → phase `form` (already authenticated). +- `connectionRequired` + `clientToken` → store `#authClientToken`, phase `auth`. +- `termsAcceptanceRequired` → clear saved terms, phase `terms`. +- anything else → `error`. + +Auth-frame outcomes (`#handleAuthOutcome`): + +- `active` + `accessToken` → phase `form`. +- `termsAcceptanceRequired` → clear saved terms, phase `terms`. +- anything else → `error`. + +--- + +### 7. SumSub sub-flow + +The document-verification sub-flow tracks its own status independently of the +identity `phase`, and delegates the actual SDK presentation to the injected +launcher. + +```mermaid +stateDiagram-v2 + [*] --> idle + idle --> creatingSession : startSumSub() + creatingSession --> fetchingToken : createUkycSession() ok + fetchingToken --> launching : submitWrappedKey() ok + launching --> inProgress : launcher.onStatusChange + inProgress --> complete : status = Completed + launching --> complete : SDK resolves + creatingSession --> failed : error + fetchingToken --> failed : error + launching --> failed : launcher unavailable / error +``` + +The `KycSumSubLauncher` interface (injected per client): + +```ts +type KycSumSubLauncher = { + isAvailable(): boolean; + launch(params: KycSumSubLaunchParams): Promise>; +}; +``` + +`launch` receives `applicantAccessToken`, an `onTokenExpiration` callback (the +controller re-runs `submitWrappedKey` to refresh), and an `onStatusChange` +callback that the controller maps into `sumsub.status`. + +--- + +### 8. Messenger wiring + +Both classes are messenger-driven. The controller depends on the service's +actions; the service depends on auth + geolocation actions from other +controllers. + +```mermaid +graph LR + subgraph CtrlMsgr["KycControllerMessenger"] + C_own["Own actions:
KycController:getState + 12 methods"] + C_ext["Allowed (delegated):
KycService:*"] + end + subgraph SvcMsgr["KycServiceMessenger"] + S_own["Own actions:
KycService: 6 methods"] + S_ext["Allowed (delegated):
AuthenticationController:getBearerToken
GeolocationController:getGeolocation"] + end + + C_ext -.delegates.-> S_own + S_ext -.delegates.-> Auth["AuthenticationController"] + S_ext -.delegates.-> Geo["GeolocationController"] +``` + +- `KycController` emits `KycController:stateChange` and exposes + `KycController:getState` plus its method actions. +- `KycController`'s `AllowedActions` = `KycServiceMethodActions` — it can call + the service. +- `KycService`'s `AllowedActions` = the auth bearer-token and geolocation + actions. + +--- + +### 9. Client-side usage (metamask-mobile) + +The mobile app is a reference consumer. It wires the controller/service into the +Engine, injects a React Native SumSub launcher, bridges WebView frame messages, +and reads state through Redux selectors. The **package stays free of any of +this** — all React/native/WebView code lives in the app. + +```mermaid +graph TB + subgraph app["metamask-mobile"] + direction TB + subgraph engine["Engine wiring"] + CInit["kyc-controller-init.ts
new KycController({ messenger, state, sumsubLauncher })"] + SInit["kyc-service-init.ts
new KycService({ fetch, env, messenger, baseUrl })"] + CMsgr["kyc-controller-messenger.ts
delegates KycService:*"] + SMsgr["kyc-service-messenger.ts
delegates Auth + Geolocation"] + Launcher["reactNativeSumSubLauncher.ts
lazy-loads @sumsub/react-native-mobilesdk-module"] + end + subgraph ui["UI layer"] + Hook["useKycFlow.ts
binds controller ↔ React"] + Frame["MoonpayFrame + useMoonpayFrame
WebView postMessage bridge"] + Reset["useMoonpayReset.ts
Reset frame"] + Demo["MoonpayDemo / SumSubDemo / KYCDemo
screens"] + end + subgraph redux["Redux"] + Sel["selectors/kycController.ts
wraps core selectors"] + end + end + + subgraph core["@metamask/kyc-controller"] + KC["KycController"] + KS["KycService"] + end + + CInit --> KC + SInit --> KS + CInit --> Launcher + Launcher -. injected .-> KC + CMsgr --> KC + SMsgr --> KS + + Hook -->|"Engine.context.KycController.*"| KC + Hook -->|"useSelector"| Sel + Sel -->|"state.engine.backgroundState.KycController"| KC + Frame -->|"raw frame message"| Hook + Hook -->|"handleFrameMessage()"| KC + Demo --> Hook + Demo --> Frame + Demo --> Reset +``` + +#### 9.1 Engine wiring + +- **`kyc-controller-init.ts`** constructs `KycController` with the persisted + state slice and injects `reactNativeSumSubLauncher`. +- **`kyc-service-init.ts`** constructs `KycService` with the global `fetch`, an + `env` derived from `isProduction()`, and (currently) a dev `baseUrl` override. +- **`kyc-controller-messenger.ts`** delegates the six `KycService:*` actions to + the controller's messenger. +- **`kyc-service-messenger.ts`** delegates + `AuthenticationController:getBearerToken` and + `GeolocationController:getGeolocation` to the service's messenger. + +#### 9.2 SumSub launcher adapter + +`reactNativeSumSubLauncher` implements `KycSumSubLauncher`: + +- `isAvailable()` checks for the native module (`NativeModules.SNSMobileSDKModule`). +- `launch()` **lazily imports** `@sumsub/react-native-mobilesdk-module` (so + merely wiring the controller never loads the native module — important for + Jest / Expo Go), initializes the SDK with the applicant token, and forwards + `onStatusChanged` / token-expiration callbacks back to the controller. + +#### 9.3 React binding — `useKycFlow` + +A thin hook that: + +- Reads controller state from Redux via the `selectors/kycController.ts` + selectors. +- Forwards user intents to controller actions through + `Engine.context.KycController.*` (`initialize`, `acceptTermsAndStartSession`, + `checkKycRequired`, `startSumSub`, `clearSavedTerms`, `reset`). +- Builds frame URLs on demand (`buildCheckFrameUrl` / `buildAuthFrameUrl`) as + the phase changes. +- Bridges WebView frame messages into `handleFrameMessage` and posts back the + returned `reply`. +- Keeps view-only concerns (email input, debug log, frame visibility) in local + React state. + +#### 9.4 WebView transport — `useMoonpayFrame` / `MoonpayFrame` + +- Injects a `postMessage` bridge into the frame that forwards the frame's + outbound messages to React Native via `window.ReactNativeWebView.postMessage`. +- **Validates the origin** (`https://blocks.moonpay.com`) before handing a + message to the controller. +- Implements `reply()` by dispatching a `MessageEvent` back into the WebView on + both `document` and `window` (platform quirk between iOS WKWebView and Android + System WebView). +- The Check frame is rendered **invisible** (1×1, opacity 0) unless the user + toggles it in the debug panel; the Auth frame is rendered visibly for OTP. + +#### 9.5 Redux selectors + +`selectors/kycController.ts` wraps the package's core selectors and reads the +slice at `state.engine.backgroundState.KycController`, exposing app-friendly +selectors (`selectKycPhase`, `selectKycSumSub`, +`selectIsKycRequiredForProduct(product)`, plus per-field selectors). + +--- + +### 10. Boundaries & responsibilities summary + +```mermaid +graph LR + subgraph shared["Shared package (platform-agnostic)"] + A1["Flow orchestration + state"] + A2["HTTP + response validation"] + A3["Crypto (X25519 / AES-GCM)"] + A4["Frame message protocol"] + A5["Selectors + vendor-neutral types"] + end + subgraph client["Client (per platform)"] + B1["Engine/DI wiring"] + B2["WebView / iframe transport"] + B3["SumSub SDK launcher"] + B4["Auth token + geolocation providers"] + B5["UI + Redux binding"] + end + shared -. injected adapters .- client +``` + +| Concern | Owner | +| --- | --- | +| Flow phase machine & state | `KycController` (shared) | +| UKYC HTTP + validation + retries | `KycService` (shared) | +| Credential decryption / key exchange | `crypto.ts` (shared) | +| Frame message semantics | `KycController.handleFrameMessage` (shared) | +| Frame **transport** (WebView/iframe) | Client | +| SumSub SDK presentation | Client (via `KycSumSubLauncher`) | +| Auth bearer token / geolocation | Other controllers (via messenger) | +| Persistence of state | Client (base-controller persistence) | + +--- + +### Appendix — key source files + +| File | Responsibility | +| --- | --- | +| `src/KycController.ts` | Stateful orchestrator, phase machine, frame protocol. | +| `src/KycService.ts` | Stateless UKYC HTTP client + superstruct validation. | +| `src/crypto.ts` | X25519 ECDH + AES-256-GCM credential decryption. | +| `src/selectors.ts` | Memoized selectors over controller state. | +| `src/types.ts` | `KycPhase`, `KycProduct`, `KycSumSubLauncher`, etc. | +| `src/countryCodes.ts` | ISO alpha-2 → alpha-3 mapping. | +| `src/index.ts` | Public exports (no barrel wildcards). | + +Reference client (metamask-mobile): + +| File | Responsibility | +| --- | --- | +| `app/core/Engine/controllers/kyc/kyc-controller-init.ts` | Construct controller + inject launcher. | +| `app/core/Engine/controllers/kyc/kyc-service-init.ts` | Construct service. | +| `app/core/Engine/controllers/kyc/reactNativeSumSubLauncher.ts` | Native SumSub adapter. | +| `app/core/Engine/messengers/kyc/*.ts` | Messenger delegation. | +| `app/components/Views/MoonpayDemo/useKycFlow.ts` | React ↔ controller binding. | +| `app/components/Views/MoonpayDemo/useMoonpayFrame.ts` | WebView postMessage bridge. | +| `app/selectors/kycController.ts` | Redux selectors. | + From 400b874c21bb849c3bd3662d716bfe624a965ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 16:25:59 +0200 Subject: [PATCH 03/19] feat: add product support + automatic calls to kyc required and sumsub flow opening MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- packages/kyc-controller/CHANGELOG.md | 7 +- .../src/KycController-method-action-types.ts | 8 + .../kyc-controller/src/KycController.test.ts | 165 +++++++++++++++++- packages/kyc-controller/src/KycController.ts | 93 ++++++++-- packages/kyc-controller/src/types.ts | 7 +- 5 files changed, 265 insertions(+), 15 deletions(-) diff --git a/packages/kyc-controller/CHANGELOG.md b/packages/kyc-controller/CHANGELOG.md index 90c4dac4c9c..22e08cf4f2a 100644 --- a/packages/kyc-controller/CHANGELOG.md +++ b/packages/kyc-controller/CHANGELOG.md @@ -9,10 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add optional `baseUrl` option to `KycService` constructor that overrides the base URL derived from `env`, enabling clients to target a custom (e.g. local or staging) KYC API ([#0000](https://github.com/MetaMask/core/pull/0000)) -- Initial release of `KycController` and `KycService`, a shared, platform-agnostic KYC / identity-verification controller used across MetaMask clients ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Initial release of `KycController` and `KycService`, a shared, platform-agnostic KYC / identity-verification controller used across MetaMask clients - `KycController` (`BaseController`) owns the flow state machine, the Check/Auth frame message protocol, X25519 credential decryption, and SumSub orchestration via an injected `KycSumSubLauncher` adapter. - `KycService` performs the Universal KYC (UKYC) HTTP calls via an injected `fetch`, sourcing the auth bearer token and geolocation through the messenger. - Exposes a vendor-neutral, per-product surface (`ramps`, `card`) plus reselect selectors. +- Add automatic post-authentication continuation to `KycController` + - `initialize` and `acceptTermsAndStartSession` now accept an optional `product` (`ramps` | `card`), tracked in new `activeProduct` state. + - When a `product` is set, reaching the `form` phase automatically runs the KYC-required check and, when KYC is required, launches the SumSub document-verification sub-flow — no extra `checkKycRequired` / `startSumSub` calls needed. When no `product` is set, the flow stops at `form` for the consumer to drive manually (unchanged behavior). +- Add optional `baseUrl` option to `KycService` constructor that overrides the base URL derived from `env`, enabling clients to target a custom (e.g. local or staging) KYC API [Unreleased]: https://github.com/MetaMask/core/ diff --git a/packages/kyc-controller/src/KycController-method-action-types.ts b/packages/kyc-controller/src/KycController-method-action-types.ts index a8610c695ad..92e34d45eb9 100644 --- a/packages/kyc-controller/src/KycController-method-action-types.ts +++ b/packages/kyc-controller/src/KycController-method-action-types.ts @@ -11,6 +11,11 @@ import type { KycController } from './KycController'; * * @param params - Optional parameters. * @param params.email - The account email to associate with the session. + * @param params.product - The consuming feature the flow runs for. When + * provided, the controller automatically runs the KYC-required check once + * authentication completes (and chains into document verification when KYC + * is required). When omitted, the flow stops at `form` and the consumer must + * call `checkKycRequired` manually. */ export type KycControllerInitializeAction = { type: `KycController:initialize`; @@ -34,6 +39,9 @@ export type KycControllerLoadDisclaimersAction = { * * @param params - Optional parameters. * @param params.email - The account email to associate with the session. + * @param params.product - The consuming feature the flow runs for. See + * {@link initialize} for how the product drives the automatic post + * authentication continuation. */ export type KycControllerAcceptTermsAndStartSessionAction = { type: `KycController:acceptTermsAndStartSession`; diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index 5ab54f75aed..d3f970b1287 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -104,6 +104,17 @@ describe('KycController', () => { }); }); + it('captures the active product for the automatic post-auth continuation', async () => { + await withController(async ({ controller, handlers }) => { + handlers.getGeoCountry.mockResolvedValue('USA'); + handlers.fetchDisclaimers.mockResolvedValue([]); + + await controller.initialize({ product: 'card' }); + + expect(controller.state.activeProduct).toBe('card'); + }); + }); + it('stays on terms when terms exist but no email is available', async () => { await withController( { @@ -188,10 +199,14 @@ describe('KycController', () => { async ({ controller, handlers }) => { handlers.createSession.mockResolvedValue({ sessionToken: 'sess' }); - await controller.acceptTermsAndStartSession({ email: 'a@b.co' }); + await controller.acceptTermsAndStartSession({ + email: 'a@b.co', + product: 'ramps', + }); expect(controller.state.acceptedDisclaimerIds).toStrictEqual(['1']); expect(controller.state.termsAcceptedAt).not.toBeNull(); + expect(controller.state.activeProduct).toBe('ramps'); expect(controller.state.phase).toBe('check'); }, ); @@ -467,6 +482,152 @@ describe('KycController', () => { }); }); + describe('automatic post-authentication continuation', () => { + it('stays at form and does not run the check when no product is set', async () => { + await withController( + { options: { state: { sessionToken: 'tok', geoCountry: 'USA' } } }, + async ({ controller, handlers }) => { + const envelope = envelopeFor(controller, { accessToken: 'access-1' }); + + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'active', credentials: envelope }, + }, + }); + + expect(controller.state.phase).toBe('form'); + expect(handlers.checkKycRequired).not.toHaveBeenCalled(); + }, + ); + }); + + it('auto-runs the KYC check on reaching form and stops at done when KYC is not required', async () => { + await withController( + { + options: { + state: { + sessionToken: 'tok', + activeProduct: 'ramps', + geoCountry: 'USA', + }, + }, + }, + async ({ controller, handlers, launcher }) => { + handlers.checkKycRequired.mockResolvedValue({ kycRequired: false }); + const envelope = envelopeFor(controller, { accessToken: 'access-1' }); + + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'active', credentials: envelope }, + }, + }); + + expect(handlers.checkKycRequired).toHaveBeenCalledWith({ + accessToken: 'access-1', + country: 'USA', + capabilities: [{ product: 'ramps' }], + }); + expect(controller.state.kycRequiredByProduct.ramps).toBe(false); + expect(controller.state.phase).toBe('done'); + expect(launcher.launch).not.toHaveBeenCalled(); + }, + ); + }); + + it('auto-chains into document verification when KYC is required (via the auth frame)', async () => { + await withController( + { + options: { + state: { + sessionToken: 'tok', + activeProduct: 'card', + geoCountry: 'FRA', + }, + }, + }, + async ({ controller, handlers, launcher }) => { + handlers.checkKycRequired.mockResolvedValue({ kycRequired: true }); + const envelope = envelopeFor(controller, { accessToken: 'access-2' }); + + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_2' }, + payload: { status: 'active', credentials: envelope }, + }, + }); + + expect(controller.state.kycRequiredByProduct.card).toBe(true); + expect(launcher.launch).toHaveBeenCalledTimes(1); + expect(controller.state.sumsub.status).toBe('complete'); + }, + ); + }); + + it('records a failed sub-flow without throwing when verification is required but the SDK is unavailable', async () => { + await withController( + { + options: { + state: { + sessionToken: 'tok', + activeProduct: 'ramps', + geoCountry: 'USA', + }, + }, + }, + async ({ controller, handlers, launcher }) => { + handlers.checkKycRequired.mockResolvedValue({ kycRequired: true }); + launcher.isAvailable.mockReturnValue(false); + const envelope = envelopeFor(controller, { accessToken: 'access-1' }); + + const result = await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'active', credentials: envelope }, + }, + }); + + expect(result).toStrictEqual({}); + expect(controller.state.sumsub.status).toBe('failed'); + }, + ); + }); + + it('does not launch verification when the auto-run check fails', async () => { + await withController( + { + options: { + state: { + sessionToken: 'tok', + activeProduct: 'ramps', + geoCountry: 'USA', + }, + }, + }, + async ({ controller, handlers, launcher }) => { + handlers.checkKycRequired.mockRejectedValue(new Error('down')); + const envelope = envelopeFor(controller, { accessToken: 'access-1' }); + + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'active', credentials: envelope }, + }, + }); + + expect(controller.state.phase).toBe('error'); + expect(launcher.launch).not.toHaveBeenCalled(); + }, + ); + }); + }); + describe('frame URL builders', () => { it('returns null for the check frame without a session', async () => { await withController(({ controller }) => { @@ -662,6 +823,7 @@ describe('KycController', () => { phase: 'form', sessionToken: 'tok', accessToken: 'a', + activeProduct: 'ramps', termsAcceptedAt: 't', acceptedDisclaimerIds: ['1'], kycRequiredByProduct: { ramps: true }, @@ -673,6 +835,7 @@ describe('KycController', () => { expect(controller.state.phase).toBe('idle'); expect(controller.state.sessionToken).toBeNull(); expect(controller.state.accessToken).toBeNull(); + expect(controller.state.activeProduct).toBeNull(); expect(controller.state.termsAcceptedAt).toBe('t'); expect(controller.state.kycRequiredByProduct.ramps).toBe(true); }, diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index 55b59d6a2ac..36a641d4623 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -68,6 +68,15 @@ export type KycControllerState = { /** Vendor customer id, used for the SumSub hand-off. */ moonpayCustomerId: string | null; + /** + * The product the current flow is running for. Captured at `initialize` + * (or `acceptTermsAndStartSession`) and used to automatically run the + * KYC-required check once authentication completes. `null` outside a + * product-scoped flow (in which case the flow stops at `form` and the + * consumer drives the check manually). + */ + activeProduct: KycProduct | null; + /** Cached "is KYC required" result per product (persisted). */ kycRequiredByProduct: Partial>; /** ISO-8601 timestamp of the last KYC-required check (persisted). */ @@ -155,6 +164,12 @@ const kycControllerMetadata = { persist: false, usedInUi: false, }, + activeProduct: { + includeInDebugSnapshot: true, + includeInStateLogs: true, + persist: false, + usedInUi: true, + }, kycRequiredByProduct: { includeInDebugSnapshot: true, includeInStateLogs: true, @@ -194,6 +209,7 @@ export function getDefaultKycControllerState(): KycControllerState { sessionToken: null, accessToken: null, moonpayCustomerId: null, + activeProduct: null, kycRequiredByProduct: {}, lastCheckedAt: null, sumsub: { @@ -333,11 +349,24 @@ export class KycController extends BaseController< * * @param params - Optional parameters. * @param params.email - The account email to associate with the session. + * @param params.product - The consuming feature the flow runs for. When + * provided, the controller automatically runs the KYC-required check once + * authentication completes (and chains into document verification when KYC + * is required). When omitted, the flow stops at `form` and the consumer must + * call `checkKycRequired` manually. */ - async initialize(params?: { email?: string }): Promise { - if (params?.email) { + async initialize(params?: { + email?: string; + product?: KycProduct; + }): Promise { + if (params?.email || params?.product) { this.update((state) => { - state.email = params.email as string; + if (params.email) { + state.email = params.email; + } + if (params.product) { + state.activeProduct = params.product; + } }); } @@ -404,8 +433,14 @@ export class KycController extends BaseController< * * @param params - Optional parameters. * @param params.email - The account email to associate with the session. + * @param params.product - The consuming feature the flow runs for. See + * {@link initialize} for how the product drives the automatic post + * authentication continuation. */ - async acceptTermsAndStartSession(params?: { email?: string }): Promise { + async acceptTermsAndStartSession(params?: { + email?: string; + product?: KycProduct; + }): Promise { const termsAcceptedAt = new Date().toISOString(); const disclaimerIds = this.state.disclaimers.map( (disclaimer) => disclaimer.id, @@ -414,6 +449,9 @@ export class KycController extends BaseController< if (params?.email) { state.email = params.email; } + if (params?.product) { + state.activeProduct = params.product; + } state.termsAcceptedAt = termsAcceptedAt; state.acceptedDisclaimerIds = disclaimerIds; }); @@ -534,12 +572,12 @@ export class KycController extends BaseController< } if (channelId === CHANNEL_CHECK) { - this.#handleCheckOutcome(status, accessToken, clientToken); + await this.#handleCheckOutcome(status, accessToken, clientToken); return {}; } if (channelId === CHANNEL_AUTH) { - this.#handleAuthOutcome(status, accessToken); + await this.#handleAuthOutcome(status, accessToken); return {}; } @@ -553,17 +591,18 @@ export class KycController extends BaseController< * @param accessToken - The decrypted access token, if any. * @param clientToken - The decrypted client token, if any. */ - #handleCheckOutcome( + async #handleCheckOutcome( status: NonNullable['status'], accessToken?: string, clientToken?: string, - ): void { + ): Promise { if (status === 'active' && accessToken) { this.update((state) => { state.accessToken = accessToken; state.phase = 'form'; state.statusMessage = 'Already authenticated. Review to submit.'; }); + await this.#continueAfterAuthentication(); return; } if (status === 'connectionRequired' && clientToken) { @@ -587,16 +626,17 @@ export class KycController extends BaseController< * @param status - The frame status. * @param accessToken - The decrypted access token, if any. */ - #handleAuthOutcome( + async #handleAuthOutcome( status: NonNullable['status'], accessToken?: string, - ): void { + ): Promise { if (status === 'active' && accessToken) { this.update((state) => { state.accessToken = accessToken; state.phase = 'form'; state.statusMessage = 'Authenticated. Review to submit.'; }); + await this.#continueAfterAuthentication(); return; } if (status === 'termsAcceptanceRequired') { @@ -606,6 +646,38 @@ export class KycController extends BaseController< this.#fail(`Auth frame returned status: ${status}`); } + /** + * Continues the flow once authentication has completed (phase `form`). + * + * When the flow is scoped to a product (see {@link initialize}), the + * KYC-required check runs automatically, and — when KYC is required — the + * document-verification sub-flow is launched. When no product is set, this is + * a no-op and the flow stays at `form` for the consumer to drive manually. + * + * Errors are already recorded on state by `checkKycRequired` (`error` + * phase) and `startSumSub` (`sumsub.status = 'failed'`); this method swallows + * them so it can be awaited safely from the frame-message handler. + */ + async #continueAfterAuthentication(): Promise { + const product = this.state.activeProduct; + if (!product) { + return; + } + + const kycRequired = await this.checkKycRequired({ product }); + if (!kycRequired) { + return; + } + + try { + await this.startSumSub(); + } catch { + // `startSumSub` already records `sumsub.status = 'failed'`; swallow the + // rethrown error (e.g. SDK unavailable) so the awaited continuation + // resolves cleanly rather than surfacing as an unhandled rejection. + } + } + /** * Invalidates stored terms and returns to the terms phase. */ @@ -832,6 +904,7 @@ export class KycController extends BaseController< state.sessionToken = null; state.accessToken = null; state.moonpayCustomerId = null; + state.activeProduct = null; state.sumsub = { status: 'idle', result: null, diff --git a/packages/kyc-controller/src/types.ts b/packages/kyc-controller/src/types.ts index 4d41052f8fa..7861a293921 100644 --- a/packages/kyc-controller/src/types.ts +++ b/packages/kyc-controller/src/types.ts @@ -25,9 +25,12 @@ export type KycVendor = 'moonpay'; * - `session` — creating the vendor session. * - `check` — running the invisible connection-check frame. * - `auth` — running the visible authentication (OTP) frame. - * - `form` — authenticated; ready to submit the identity check. + * - `form` — authenticated. When the flow is scoped to a product, the + * KYC-required check runs automatically from here; otherwise the consumer + * drives it manually via `checkKycRequired`. * - `submit` — submitting the KYC-required check. - * - `done` — flow complete; see `kycRequiredByProduct` / `sumsub`. + * - `done` — flow complete; see `kycRequiredByProduct` / `sumsub`. When KYC is + * required, the document-verification sub-flow is launched automatically. * - `error` — flow halted; see `error`. */ export type KycPhase = From 9b3de4ca24ada1ea4c5a20b85c69f90b5dab56db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 17:05:55 +0200 Subject: [PATCH 04/19] fix: geoCountry not updated issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- .../kyc-controller/src/KycController.test.ts | 31 +++++++++++++++++++ packages/kyc-controller/src/KycController.ts | 6 ++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index d3f970b1287..00661198987 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -147,6 +147,37 @@ describe('KycController', () => { }); }); + it('caches the provided country override in geoCountry', async () => { + await withController(async ({ controller, handlers }) => { + handlers.fetchDisclaimers.mockResolvedValue([]); + + await controller.loadDisclaimers({ country: 'USA' }); + + expect(controller.state.geoCountry).toBe('USA'); + }); + }); + + it('lets a later checkKycRequired reuse the overridden country without an override', async () => { + await withController( + { options: { state: { accessToken: 'a' } } }, + async ({ controller, handlers }) => { + handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.checkKycRequired.mockResolvedValue({ kycRequired: true }); + + await controller.loadDisclaimers({ country: 'USA' }); + await controller.checkKycRequired({ product: 'ramps' }); + + expect(handlers.getGeoCountry).not.toHaveBeenCalled(); + expect(handlers.checkKycRequired).toHaveBeenCalledWith({ + accessToken: 'a', + country: 'USA', + capabilities: [{ product: 'ramps' }], + }); + expect(controller.state.error).toBeNull(); + }, + ); + }); + it('uses the cached geoCountry when no country is provided', async () => { await withController( { options: { state: { geoCountry: 'USA' } } }, diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index 36a641d4623..4410f2734ea 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -403,11 +403,11 @@ export class KycController extends BaseController< */ async loadDisclaimers(params?: { country?: string }): Promise { try { - const cachedCountry = params?.country ?? this.state.geoCountry; const country = - cachedCountry ?? + params?.country ?? + this.state.geoCountry ?? (await this.messenger.call('KycService:getGeoCountry')); - if (!cachedCountry) { + if (country !== this.state.geoCountry) { this.update((state) => { state.geoCountry = country; }); From 133fa9b6bc63d9d30a6c951dffac3455583add1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 17:09:23 +0200 Subject: [PATCH 05/19] fix: previously selected product is not reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- .../kyc-controller/src/KycController.test.ts | 14 +++++++++++++ packages/kyc-controller/src/KycController.ts | 20 +++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index 00661198987..0622d121e52 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -115,6 +115,20 @@ describe('KycController', () => { }); }); + it('clears a stale active product when re-initialized without one', async () => { + await withController( + { options: { state: { activeProduct: 'card' } } }, + async ({ controller, handlers }) => { + handlers.getGeoCountry.mockResolvedValue('USA'); + handlers.fetchDisclaimers.mockResolvedValue([]); + + await controller.initialize({ email: 'a@b.co' }); + + expect(controller.state.activeProduct).toBeNull(); + }, + ); + }); + it('stays on terms when terms exist but no email is available', async () => { await withController( { diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index 4410f2734ea..f1234254960 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -359,16 +359,16 @@ export class KycController extends BaseController< email?: string; product?: KycProduct; }): Promise { - if (params?.email || params?.product) { - this.update((state) => { - if (params.email) { - state.email = params.email; - } - if (params.product) { - state.activeProduct = params.product; - } - }); - } + // `initialize` starts a fresh flow, so `activeProduct` is always reset to + // this call's product (or `null`). Otherwise a prior run's product could + // linger and cause `#continueAfterAuthentication` to auto-run the check / + // sub-flow when the caller intended the manual (product-less) flow. + this.update((state) => { + if (params?.email) { + state.email = params.email; + } + state.activeProduct = params?.product ?? null; + }); // Resolve country for display; non-blocking. try { From 89ac0b42891e727c58eaa0b53d1dc1622e73bc89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 18:33:51 +0200 Subject: [PATCH 06/19] fix: issue with sumsub complete status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- .../kyc-controller/src/KycController.test.ts | 26 +++++++++++++++++++ packages/kyc-controller/src/KycController.ts | 20 ++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index 0622d121e52..e62d5a3dbe0 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -596,6 +596,10 @@ describe('KycController', () => { }, async ({ controller, handlers, launcher }) => { handlers.checkKycRequired.mockResolvedValue({ kycRequired: true }); + launcher.launch.mockImplementation(async ({ onStatusChange }) => { + onStatusChange?.('InProgress', 'Completed'); + return { ok: true }; + }); const envelope = envelopeFor(controller, { accessToken: 'access-2' }); await controller.handleFrameMessage({ @@ -836,6 +840,11 @@ describe('KycController', () => { it('defaults locale and debug when no params are given', async () => { await withController(async ({ controller, launcher }) => { + launcher.launch.mockImplementation(async ({ onStatusChange }) => { + onStatusChange?.('InProgress', 'Completed'); + return { ok: true }; + }); + await controller.startSumSub(); expect(launcher.launch).toHaveBeenCalledWith( @@ -845,6 +854,23 @@ describe('KycController', () => { }); }); + it('marks failed when launch resolves without a Completed status', async () => { + await withController(async ({ controller, launcher }) => { + launcher.launch.mockImplementation(async ({ onStatusChange }) => { + // The applicant abandons the flow: the SDK reports progress but never + // a Completed status, yet `launch` still resolves. + onStatusChange?.('idle', 'InProgress'); + return { ok: false }; + }); + + const result = await controller.startSumSub(); + + expect(result).toStrictEqual({ ok: false }); + expect(controller.state.sumsub.status).toBe('failed'); + expect(controller.state.sumsub.result).toStrictEqual({ ok: false }); + }); + }); + it('marks failed and returns the error when a step throws', async () => { await withController(async ({ controller, handlers }) => { handlers.createUkycSession.mockRejectedValue(new Error('ukyc down')); diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index f1234254960..08696b5a089 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -32,6 +32,11 @@ const CHANNEL_RESET = 'ch_reset'; // must be replaced with real UKYC-issued material before production use. const MOCK_JWT_TOKEN = 'mock-jwt-token'; +// The SumSub SDK status that signals the applicant finished the flow +// successfully. Any other resolution (abandonment, failure, or a non-success +// outcome) must not be recorded as `complete`. +const SUMSUB_COMPLETED_STATUS = 'Completed'; + // === STATE === /** @@ -855,6 +860,11 @@ export class KycController extends BaseController< state.sumsub.applicantAccessToken = applicantAccessToken; }); + // Track whether the SDK ever reported a successful completion. A resolved + // `launch` alone does not imply success — the applicant may have + // abandoned the flow or the SDK may have reported a non-success outcome. + let reachedCompletion = false; + const result = await this.#sumsubLauncher.launch({ applicantAccessToken, onTokenExpiration: async () => { @@ -865,17 +875,23 @@ export class KycController extends BaseController< return refreshed.applicantAccessToken; }, onStatusChange: (_prev, next) => { + if (next === SUMSUB_COMPLETED_STATUS) { + reachedCompletion = true; + } this.update((state) => { state.sumsub.status = - next === 'Completed' ? 'complete' : 'inProgress'; + next === SUMSUB_COMPLETED_STATUS ? 'complete' : 'inProgress'; }); }, locale: params?.locale ?? 'en', debug: params?.debug ?? false, }); + // Only record `complete` when the SDK actually reported completion; + // otherwise treat the resolved-but-unfinished flow as `failed` so + // consumers and UI do not mistake it for a finished verification. this.update((state) => { - state.sumsub.status = 'complete'; + state.sumsub.status = reachedCompletion ? 'complete' : 'failed'; state.sumsub.result = result as Json; }); return result; From 3392d001eff6943e7733897c34b3de3836789157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 18:36:43 +0200 Subject: [PATCH 07/19] feat: update lock file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- yarn.lock | 2488 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 1737 insertions(+), 751 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2a784809575..a78434321db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -242,7 +242,20 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.29.7": +"@asamuzakjp/css-color@npm:^3.2.0": + version: 3.2.0 + resolution: "@asamuzakjp/css-color@npm:3.2.0" + dependencies: + "@csstools/css-calc": "npm:^2.1.3" + "@csstools/css-color-parser": "npm:^3.0.9" + "@csstools/css-parser-algorithms": "npm:^3.0.4" + "@csstools/css-tokenizer": "npm:^3.0.3" + lru-cache: "npm:^10.4.3" + checksum: 10/870f661460173174fef8bfebea0799ba26566f3aa7b307e5adabb7aae84fed2da68e40080104ed0c83b43c5be632ee409e65396af13bfe948a3ef4c2c729ecd9 + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.29.7": version: 7.29.7 resolution: "@babel/code-frame@npm:7.29.7" dependencies: @@ -260,7 +273,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.21.3, @babel/core@npm:^7.23.2, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.9": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.21.3, @babel/core@npm:^7.23.2, @babel/core@npm:^7.23.9, @babel/core@npm:^7.25.9, @babel/core@npm:^7.27.4": version: 7.29.7 resolution: "@babel/core@npm:7.29.7" dependencies: @@ -283,7 +296,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.25.9, @babel/generator@npm:^7.29.7, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:^7.25.9, @babel/generator@npm:^7.27.5, @babel/generator@npm:^7.29.7, @babel/generator@npm:^7.7.2": version: 7.29.7 resolution: "@babel/generator@npm:7.29.7" dependencies: @@ -687,7 +700,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-jsx@npm:^7.29.7, @babel/plugin-syntax-jsx@npm:^7.7.2": +"@babel/plugin-syntax-jsx@npm:^7.27.1, @babel/plugin-syntax-jsx@npm:^7.29.7, @babel/plugin-syntax-jsx@npm:^7.7.2": version: 7.29.7 resolution: "@babel/plugin-syntax-jsx@npm:7.29.7" dependencies: @@ -786,7 +799,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-typescript@npm:^7.29.7, @babel/plugin-syntax-typescript@npm:^7.7.2": +"@babel/plugin-syntax-typescript@npm:^7.27.1, @babel/plugin-syntax-typescript@npm:^7.29.7, @babel/plugin-syntax-typescript@npm:^7.7.2": version: 7.29.7 resolution: "@babel/plugin-syntax-typescript@npm:7.29.7" dependencies: @@ -1659,7 +1672,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.23.0, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.7, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.23.0, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.7, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.29.7 resolution: "@babel/types@npm:7.29.7" dependencies: @@ -1727,7 +1740,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-calc@npm:^2.1.4": +"@csstools/css-calc@npm:^2.1.3, @csstools/css-calc@npm:^2.1.4": version: 2.1.4 resolution: "@csstools/css-calc@npm:2.1.4" peerDependencies: @@ -1737,7 +1750,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-color-parser@npm:^3.1.0": +"@csstools/css-color-parser@npm:^3.0.9, @csstools/css-color-parser@npm:^3.1.0": version: 3.1.0 resolution: "@csstools/css-color-parser@npm:3.1.0" dependencies: @@ -1750,7 +1763,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-parser-algorithms@npm:^3.0.5": +"@csstools/css-parser-algorithms@npm:^3.0.4, @csstools/css-parser-algorithms@npm:^3.0.5": version: 3.0.5 resolution: "@csstools/css-parser-algorithms@npm:3.0.5" peerDependencies: @@ -1759,7 +1772,7 @@ __metadata: languageName: node linkType: hard -"@csstools/css-tokenizer@npm:^3.0.4": +"@csstools/css-tokenizer@npm:^3.0.3, @csstools/css-tokenizer@npm:^3.0.4": version: 3.0.4 resolution: "@csstools/css-tokenizer@npm:3.0.4" checksum: 10/eb6c84c086312f6bb8758dfe2c85addd7475b0927333c5e39a4d59fb210b9810f8c346972046f95e60a721329cffe98895abe451e51de753ad1ca7a8c24ec65f @@ -4701,6 +4714,20 @@ __metadata: languageName: node linkType: hard +"@jest/console@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/console@npm:30.4.1" + dependencies: + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + chalk: "npm:^4.1.2" + jest-message-util: "npm:30.4.1" + jest-util: "npm:30.4.1" + slash: "npm:^3.0.0" + checksum: 10/4eb463d29654c20716f5f9cde43e5d958cb3b9234477df57da5b3814c3f1a4a0ab611a8eaf4b5abc146190a012584d7025f445f3560ed62acd843fc95c0a0e65 + languageName: node + linkType: hard + "@jest/console@npm:^29.7.0": version: 29.7.0 resolution: "@jest/console@npm:29.7.0" @@ -4715,6 +4742,47 @@ __metadata: languageName: node linkType: hard +"@jest/core@npm:30.4.2": + version: 30.4.2 + resolution: "@jest/core@npm:30.4.2" + dependencies: + "@jest/console": "npm:30.4.1" + "@jest/pattern": "npm:30.4.0" + "@jest/reporters": "npm:30.4.1" + "@jest/test-result": "npm:30.4.1" + "@jest/transform": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + ansi-escapes: "npm:^4.3.2" + chalk: "npm:^4.1.2" + ci-info: "npm:^4.2.0" + exit-x: "npm:^0.2.2" + fast-json-stable-stringify: "npm:^2.1.0" + graceful-fs: "npm:^4.2.11" + jest-changed-files: "npm:30.4.1" + jest-config: "npm:30.4.2" + jest-haste-map: "npm:30.4.1" + jest-message-util: "npm:30.4.1" + jest-regex-util: "npm:30.4.0" + jest-resolve: "npm:30.4.1" + jest-resolve-dependencies: "npm:30.4.2" + jest-runner: "npm:30.4.2" + jest-runtime: "npm:30.4.2" + jest-snapshot: "npm:30.4.1" + jest-util: "npm:30.4.1" + jest-validate: "npm:30.4.1" + jest-watcher: "npm:30.4.1" + pretty-format: "npm:30.4.1" + slash: "npm:^3.0.0" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: 10/ecc695392685ab56c6df5d29d6f7927141071d8f3f75e5f7c7664f0faded1307caf5daf051074252d5ddb9546bf2bfe3b0c63ca81fe6238dc34e1bb5f8a7a261 + languageName: node + linkType: hard + "@jest/core@npm:^29.7.0": version: 29.7.0 resolution: "@jest/core@npm:29.7.0" @@ -4756,6 +4824,46 @@ __metadata: languageName: node linkType: hard +"@jest/diff-sequences@npm:30.4.0": + version: 30.4.0 + resolution: "@jest/diff-sequences@npm:30.4.0" + checksum: 10/65c27937c10a7157899dad5d176806104286f9d55464f318955a0cee98db8aed6b8f70ad4aee7133468087146422cdd391d49b1e101ec543db3283ee4eb59c06 + languageName: node + linkType: hard + +"@jest/environment-jsdom-abstract@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/environment-jsdom-abstract@npm:30.4.1" + dependencies: + "@jest/environment": "npm:30.4.1" + "@jest/fake-timers": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@types/jsdom": "npm:^21.1.7" + "@types/node": "npm:*" + jest-mock: "npm:30.4.1" + jest-util: "npm:30.4.1" + peerDependencies: + canvas: ^3.0.0 + jsdom: "*" + peerDependenciesMeta: + canvas: + optional: true + checksum: 10/e51537587162d0972df0abd6f0ce0b2cf5245029035e6b61b62da075547a37cd174254c315b456447a6419b22938623036b355c063498b7876f7ea0352404fa6 + languageName: node + linkType: hard + +"@jest/environment@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/environment@npm:30.4.1" + dependencies: + "@jest/fake-timers": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + jest-mock: "npm:30.4.1" + checksum: 10/c25946fee29604f5aa24ea059bc3cc7bc4c8cdaf26db1ed6ffa4f28e37f5193cc4e868650c807d89caff4123e44d07b58200d4cb5960ebdb7d66531509d76359 + languageName: node + linkType: hard + "@jest/environment@npm:^29.7.0": version: 29.7.0 resolution: "@jest/environment@npm:29.7.0" @@ -4768,6 +4876,15 @@ __metadata: languageName: node linkType: hard +"@jest/expect-utils@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/expect-utils@npm:30.4.1" + dependencies: + "@jest/get-type": "npm:30.1.0" + checksum: 10/3f0337ec791d669cacd07594521f2da71b956712dfd0c0007253dd5e886ef640df510af1357878a80ac56f09d3db9fd68e3db66959f0fdb3add5f551dd7e0f35 + languageName: node + linkType: hard + "@jest/expect-utils@npm:^29.7.0": version: 29.7.0 resolution: "@jest/expect-utils@npm:29.7.0" @@ -4777,6 +4894,16 @@ __metadata: languageName: node linkType: hard +"@jest/expect@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/expect@npm:30.4.1" + dependencies: + expect: "npm:30.4.1" + jest-snapshot: "npm:30.4.1" + checksum: 10/40ae0317a3590ced7a7fd21c49e6b1af6b122e6a83822e643af83f02034dfed6485248cae08d6bcf9380039ba3824ac56db18478712c64ddf5f709ee23cf30cd + languageName: node + linkType: hard + "@jest/expect@npm:^29.7.0": version: 29.7.0 resolution: "@jest/expect@npm:29.7.0" @@ -4787,6 +4914,20 @@ __metadata: languageName: node linkType: hard +"@jest/fake-timers@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/fake-timers@npm:30.4.1" + dependencies: + "@jest/types": "npm:30.4.1" + "@sinonjs/fake-timers": "npm:^15.4.0" + "@types/node": "npm:*" + jest-message-util: "npm:30.4.1" + jest-mock: "npm:30.4.1" + jest-util: "npm:30.4.1" + checksum: 10/bc7aff23548395d6e7957bc24f699f921a9616f2357ab49616b0468c7b5e94e6ac4cbdd45d306f1a5d7f72e2a055294f52be3666e4c1da7c137874c5b226e1c6 + languageName: node + linkType: hard + "@jest/fake-timers@npm:^29.7.0": version: 29.7.0 resolution: "@jest/fake-timers@npm:29.7.0" @@ -4801,6 +4942,25 @@ __metadata: languageName: node linkType: hard +"@jest/get-type@npm:30.1.0": + version: 30.1.0 + resolution: "@jest/get-type@npm:30.1.0" + checksum: 10/e2a95fbb49ce2d15547db8af5602626caf9b05f62a5e583b4a2de9bd93a2bfe7175f9bbb2b8a5c3909ce261d467b6991d7265bb1d547cb60e7e97f571f361a70 + languageName: node + linkType: hard + +"@jest/globals@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/globals@npm:30.4.1" + dependencies: + "@jest/environment": "npm:30.4.1" + "@jest/expect": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + jest-mock: "npm:30.4.1" + checksum: 10/5fe04b9c3b97f0061e4464201ee0dd674dd958843eb80542791d1c576c51f12aaa3f3b369e136d5fd3f8c716f9c9bbfbb76491a3cbc3c4efb3cc71063f909132 + languageName: node + linkType: hard + "@jest/globals@npm:^29.7.0": version: 29.7.0 resolution: "@jest/globals@npm:29.7.0" @@ -4813,6 +4973,52 @@ __metadata: languageName: node linkType: hard +"@jest/pattern@npm:30.4.0": + version: 30.4.0 + resolution: "@jest/pattern@npm:30.4.0" + dependencies: + "@types/node": "npm:*" + jest-regex-util: "npm:30.4.0" + checksum: 10/4fb1db0e586713708d2fcd79059315600978608483ef2d80e04a0a59b20b0d8de0d3f47cad950ff90bfb9ea3cb788709ee3d1eb225734e4dbf1c4b743c93d204 + languageName: node + linkType: hard + +"@jest/reporters@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/reporters@npm:30.4.1" + dependencies: + "@bcoe/v8-coverage": "npm:^0.2.3" + "@jest/console": "npm:30.4.1" + "@jest/test-result": "npm:30.4.1" + "@jest/transform": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@jridgewell/trace-mapping": "npm:^0.3.25" + "@types/node": "npm:*" + chalk: "npm:^4.1.2" + collect-v8-coverage: "npm:^1.0.2" + exit-x: "npm:^0.2.2" + glob: "npm:^10.5.0" + graceful-fs: "npm:^4.2.11" + istanbul-lib-coverage: "npm:^3.0.0" + istanbul-lib-instrument: "npm:^6.0.0" + istanbul-lib-report: "npm:^3.0.0" + istanbul-lib-source-maps: "npm:^5.0.0" + istanbul-reports: "npm:^3.1.3" + jest-message-util: "npm:30.4.1" + jest-util: "npm:30.4.1" + jest-worker: "npm:30.4.1" + slash: "npm:^3.0.0" + string-length: "npm:^4.0.2" + v8-to-istanbul: "npm:^9.0.1" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: 10/e14e3717c9fe49004b6406cc53554f90954345917bb5f077d23e3a2cecde26d90493e1522d95f63b6c14ef06fb63d45b695ac6400d63b96fecc7a80d1ef83f4d + languageName: node + linkType: hard + "@jest/reporters@npm:^29.7.0": version: 29.7.0 resolution: "@jest/reporters@npm:29.7.0" @@ -4850,6 +5056,15 @@ __metadata: languageName: node linkType: hard +"@jest/schemas@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/schemas@npm:30.4.1" + dependencies: + "@sinclair/typebox": "npm:^0.34.0" + checksum: 10/86e62c8fd8fc77535085f1ede3a416430a3740f78b8f88ec7d0ee4516b22daf3326ffc1ade9d5f7839bbde923aaf1b5ac430a42ed4bb1a38edc3de5005a58f51 + languageName: node + linkType: hard + "@jest/schemas@npm:^29.6.3": version: 29.6.3 resolution: "@jest/schemas@npm:29.6.3" @@ -4859,6 +5074,29 @@ __metadata: languageName: node linkType: hard +"@jest/snapshot-utils@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/snapshot-utils@npm:30.4.1" + dependencies: + "@jest/types": "npm:30.4.1" + chalk: "npm:^4.1.2" + graceful-fs: "npm:^4.2.11" + natural-compare: "npm:^1.4.0" + checksum: 10/8f17768702153267388b3043f358027385e591ac4668699bfce3547cb8e08ac146a074913bcddf68c0a4f7155e24a6d582d27f4592f5c3bd5f9fbc3f9182ef78 + languageName: node + linkType: hard + +"@jest/source-map@npm:30.0.1": + version: 30.0.1 + resolution: "@jest/source-map@npm:30.0.1" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.25" + callsites: "npm:^3.1.0" + graceful-fs: "npm:^4.2.11" + checksum: 10/161b27cdf8d9d80fd99374d55222b90478864c6990514be6ebee72b7184a034224c9aceed12c476f3a48d48601bf8ed2e0c047a5a81bd907dc192ebe71365ed4 + languageName: node + linkType: hard + "@jest/source-map@npm:^29.6.3": version: 29.6.3 resolution: "@jest/source-map@npm:29.6.3" @@ -4870,6 +5108,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-result@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/test-result@npm:30.4.1" + dependencies: + "@jest/console": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@types/istanbul-lib-coverage": "npm:^2.0.6" + collect-v8-coverage: "npm:^1.0.2" + checksum: 10/c420182d72cef64827981230b4c84b2de3f4312067e7baf1e3e13c501dc57f73faa09fed1a5ed1a6e96bc29f6c67ac2c14de5f973945f14853010729678cb44a + languageName: node + linkType: hard + "@jest/test-result@npm:^29.7.0": version: 29.7.0 resolution: "@jest/test-result@npm:29.7.0" @@ -4882,6 +5132,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-sequencer@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/test-sequencer@npm:30.4.1" + dependencies: + "@jest/test-result": "npm:30.4.1" + graceful-fs: "npm:^4.2.11" + jest-haste-map: "npm:30.4.1" + slash: "npm:^3.0.0" + checksum: 10/d911ef0c527c402d41537aa5f9754725a58732c1c6616401454633fd45729da0b2f01b4c50322b1b789a9f2d4edf3a24aecb0b2e4ca4d873c4335894b63bc5b0 + languageName: node + linkType: hard + "@jest/test-sequencer@npm:^29.7.0": version: 29.7.0 resolution: "@jest/test-sequencer@npm:29.7.0" @@ -4894,6 +5156,28 @@ __metadata: languageName: node linkType: hard +"@jest/transform@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/transform@npm:30.4.1" + dependencies: + "@babel/core": "npm:^7.27.4" + "@jest/types": "npm:30.4.1" + "@jridgewell/trace-mapping": "npm:^0.3.25" + babel-plugin-istanbul: "npm:^7.0.1" + chalk: "npm:^4.1.2" + convert-source-map: "npm:^2.0.0" + fast-json-stable-stringify: "npm:^2.1.0" + graceful-fs: "npm:^4.2.11" + jest-haste-map: "npm:30.4.1" + jest-regex-util: "npm:30.4.0" + jest-util: "npm:30.4.1" + pirates: "npm:^4.0.7" + slash: "npm:^3.0.0" + write-file-atomic: "npm:^5.0.1" + checksum: 10/7b570451f6c26360f1b852c2281dcc4e36fe685dbc159cf5eabf83d49d6aae4569f444d38f3afb5b3b6e0b809eb41b65f3145c0cac5fee3eec9c9b178fb1f0ea + languageName: node + linkType: hard + "@jest/transform@npm:^29.7.0": version: 29.7.0 resolution: "@jest/transform@npm:29.7.0" @@ -4917,6 +5201,21 @@ __metadata: languageName: node linkType: hard +"@jest/types@npm:30.4.1": + version: 30.4.1 + resolution: "@jest/types@npm:30.4.1" + dependencies: + "@jest/pattern": "npm:30.4.0" + "@jest/schemas": "npm:30.4.1" + "@types/istanbul-lib-coverage": "npm:^2.0.6" + "@types/istanbul-reports": "npm:^3.0.4" + "@types/node": "npm:*" + "@types/yargs": "npm:^17.0.33" + chalk: "npm:^4.1.2" + checksum: 10/cc0999508613487c6d0f55661cd342ebe7cfe579fa9917534b94310204358f03f94524f70f00b4fe3c6dd2ccd0fd44657615a1b9f420ab310d68b43964bff87c + languageName: node + linkType: hard + "@jest/types@npm:^26.6.2": version: 26.6.2 resolution: "@jest/types@npm:26.6.2" @@ -4988,7 +5287,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": version: 0.3.31 resolution: "@jridgewell/trace-mapping@npm:0.3.31" dependencies: @@ -5439,12 +5738,12 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" fast-deep-equal: "npm:^3.1.3" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5476,14 +5775,14 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/readable-stream": "npm:^2.3.0" deepmerge: "npm:^4.2.2" ethereum-cryptography: "npm:^2.1.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5517,10 +5816,10 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5538,10 +5837,10 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5558,12 +5857,12 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5582,11 +5881,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5602,10 +5901,10 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5628,10 +5927,10 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5649,11 +5948,11 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nanoid: "npm:^3.3.8" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5661,7 +5960,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/assets-controller@npm:^11.1.0, @metamask/assets-controller@workspace:packages/assets-controller": +"@metamask/assets-controller@npm:^11.2.0, @metamask/assets-controller@workspace:packages/assets-controller": version: 0.0.0-use.local resolution: "@metamask/assets-controller@workspace:packages/assets-controller" dependencies: @@ -5670,12 +5969,12 @@ __metadata: "@ethersproject/providers": "npm:^5.7.0" "@metamask/account-tree-controller": "npm:^7.5.5" "@metamask/accounts-controller": "npm:^39.0.5" - "@metamask/assets-controllers": "npm:^109.4.1" + "@metamask/assets-controllers": "npm:^110.0.0" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/client-controller": "npm:^1.0.1" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/core-backend": "npm:^6.5.0" + "@metamask/core-backend": "npm:^7.0.0" "@metamask/keyring-api": "npm:^23.5.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/keyring-internal-api": "npm:^11.0.1" @@ -5690,18 +5989,18 @@ __metadata: "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/snaps-controllers": "npm:^19.0.0" "@metamask/snaps-utils": "npm:^12.1.2" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/lodash": "npm:^4.14.191" async-mutex: "npm:^0.5.0" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" p-limit: "npm:^3.1.0" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5709,7 +6008,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/assets-controllers@npm:^109.4.1, @metamask/assets-controllers@workspace:packages/assets-controllers": +"@metamask/assets-controllers@npm:^110.0.0, @metamask/assets-controllers@workspace:packages/assets-controllers": version: 0.0.0-use.local resolution: "@metamask/assets-controllers@workspace:packages/assets-controllers" dependencies: @@ -5729,7 +6028,7 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/contract-metadata": "npm:^2.4.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/core-backend": "npm:^6.5.0" + "@metamask/core-backend": "npm:^7.0.0" "@metamask/eth-query": "npm:^4.0.0" "@metamask/ethjs-provider-http": "npm:^0.3.0" "@metamask/keyring-api": "npm:^23.5.0" @@ -5752,12 +6051,12 @@ __metadata: "@metamask/snaps-sdk": "npm:^11.0.0" "@metamask/snaps-utils": "npm:^12.1.2" "@metamask/storage-service": "npm:^1.0.2" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/bn.js": "npm:^5.1.5" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/lodash": "npm:^4.14.191" "@types/node": "npm:^16.18.54" "@types/uuid": "npm:^8.3.0" @@ -5766,14 +6065,14 @@ __metadata: bn.js: "npm:^5.2.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" lodash: "npm:^4.17.21" multiformats: "npm:^9.9.0" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" single-call-balance-checker-abi: "npm:^1.0.0" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5831,11 +6130,11 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5874,11 +6173,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5896,15 +6195,15 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/lodash": "npm:^4.14.191" cockatiel: "npm:^3.1.2" deepmerge: "npm:^4.2.2" fast-deep-equal: "npm:^3.1.3" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5919,10 +6218,10 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/local-node-utils": "npm:^1.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5932,7 +6231,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/bridge-controller@npm:^77.6.0, @metamask/bridge-controller@workspace:packages/bridge-controller": +"@metamask/bridge-controller@npm:^77.8.0, @metamask/bridge-controller@workspace:packages/bridge-controller": version: 0.0.0-use.local resolution: "@metamask/bridge-controller@workspace:packages/bridge-controller" dependencies: @@ -5942,13 +6241,13 @@ __metadata: "@ethersproject/contracts": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.0" "@metamask/accounts-controller": "npm:^39.0.5" - "@metamask/assets-controller": "npm:^11.1.0" - "@metamask/assets-controllers": "npm:^109.4.1" + "@metamask/assets-controller": "npm:^11.2.0" + "@metamask/assets-controllers": "npm:^110.0.0" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" "@metamask/eth-json-rpc-provider": "npm:^6.0.1" - "@metamask/gas-fee-controller": "npm:^26.2.4" + "@metamask/gas-fee-controller": "npm:^26.3.0" "@metamask/keyring-api": "npm:^23.5.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" @@ -5959,18 +6258,18 @@ __metadata: "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/snaps-controllers": "npm:^19.0.0" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -5986,9 +6285,9 @@ __metadata: "@metamask/accounts-controller": "npm:^39.0.5" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/bridge-controller": "npm:^77.6.0" + "@metamask/bridge-controller": "npm:^77.8.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/gas-fee-controller": "npm:^26.2.4" + "@metamask/gas-fee-controller": "npm:^26.3.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" @@ -5996,17 +6295,17 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/snaps-controllers": "npm:^19.0.0" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6032,10 +6331,10 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/eslint": "npm:^8.44.7" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6055,11 +6354,11 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6067,7 +6366,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/chomp-api-service@npm:^3.1.0, @metamask/chomp-api-service@workspace:packages/chomp-api-service": +"@metamask/chomp-api-service@npm:^4.0.0, @metamask/chomp-api-service@workspace:packages/chomp-api-service": version: 0.0.0-use.local resolution: "@metamask/chomp-api-service@workspace:packages/chomp-api-service" dependencies: @@ -6079,11 +6378,11 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6103,10 +6402,10 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6122,11 +6421,11 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6142,15 +6441,15 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/contract-metadata": "npm:^2.4.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/core-backend": "npm:^6.5.0" + "@metamask/core-backend": "npm:^7.0.0" "@metamask/keyring-api": "npm:^23.5.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6169,12 +6468,12 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6191,11 +6490,11 @@ __metadata: "@metamask/json-rpc-engine": "npm:^10.5.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6203,7 +6502,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/config-registry-controller@workspace:packages/config-registry-controller": +"@metamask/config-registry-controller@npm:^0.4.1, @metamask/config-registry-controller@workspace:packages/config-registry-controller": version: 0.0.0-use.local resolution: "@metamask/config-registry-controller@workspace:packages/config-registry-controller" dependencies: @@ -6219,12 +6518,12 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6240,11 +6539,11 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6293,7 +6592,7 @@ __metadata: "@spruceid/siwe-parser": "npm:2.1.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/bn.js": "npm:^5.1.5" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/lodash": "npm:^4.14.191" bignumber.js: "npm:^9.1.2" bn.js: "npm:^5.2.1" @@ -6301,11 +6600,11 @@ __metadata: deepmerge: "npm:^4.2.2" eth-ens-namehash: "npm:^2.0.8" fast-deep-equal: "npm:^3.1.3" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6315,7 +6614,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/core-backend@npm:^6.5.0, @metamask/core-backend@workspace:packages/core-backend": +"@metamask/core-backend@npm:^7.0.0, @metamask/core-backend@workspace:packages/core-backend": version: 0.0.0-use.local resolution: "@metamask/core-backend@workspace:packages/core-backend" dependencies: @@ -6329,12 +6628,12 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6361,13 +6660,14 @@ __metadata: "@metamask/skills": "npm:^0.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/lodash": "npm:^4.14.191" "@types/node": "npm:^16.18.54" "@types/semver": "npm:^7" "@typescript-eslint/eslint-plugin": "npm:^8.48.0" "@typescript-eslint/parser": "npm:^8.48.0" "@yarnpkg/types": "npm:^4.0.0" + bats: "npm:^1.13.0" comment-json: "npm:^4.5.1" eslint: "npm:^9.39.1" eslint-config-prettier: "npm:^9.1.0" @@ -6380,14 +6680,13 @@ __metadata: eslint-plugin-promise: "npm:^7.1.0" execa: "npm:^5.0.0" isomorphic-fetch: "npm:^3.0.0" - jest: "npm:^29.7.0" - jest-silent-reporter: "npm:^0.5.0" + jest: "npm:^30.4.2" + jest-silent-reporter: "npm:^0.6.0" knip: "npm:^6.23.0" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" oxfmt: "npm:^0.44.0" prettier: "npm:^3.3.3" - prettier-2: "npm:prettier@^2.8.8" rimraf: "npm:^5.0.5" semver: "npm:^7.6.3" simple-git-hooks: "npm:^2.8.0" @@ -6440,10 +6739,10 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6483,13 +6782,13 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/stake-sdk": "npm:^3.2.1" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6507,15 +6806,15 @@ __metadata: "@metamask/preferences-controller": "npm:^23.1.0" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" klona: "npm:^2.0.6" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6534,10 +6833,10 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6557,11 +6856,11 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6581,11 +6880,11 @@ __metadata: "@metamask/network-controller": "npm:^34.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" punycode: "npm:^2.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6666,12 +6965,12 @@ __metadata: "@metamask/safe-event-emitter": "npm:^3.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/json-rpc-random-id": "npm:^1.0.1" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" json-rpc-random-id: "npm:^1.0.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -6738,10 +7037,10 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deep-freeze-strict: "npm:^1.1.1" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" klona: "npm:^2.0.6" safe-stable-stringify: "npm:^2.4.3" tsx: "npm:^4.20.5" @@ -6775,12 +7074,12 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" ethers: "npm:^6.12.0" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nanoid: "npm:^3.3.8" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typescript: "npm:~5.3.3" @@ -7016,16 +7315,16 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^6.1.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/unzipper": "npm:^0.10.10" "@types/yargs": "npm:^17.0.32" "@types/yargs-parser": "npm:^21.0.3" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" minipass: "npm:^7.1.2" nock: "npm:^13.3.1" tar: "npm:^7.4.3" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7039,7 +7338,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/gas-fee-controller@npm:^26.2.4, @metamask/gas-fee-controller@workspace:packages/gas-fee-controller": +"@metamask/gas-fee-controller@npm:^26.3.0, @metamask/gas-fee-controller@workspace:packages/gas-fee-controller": version: 0.0.0-use.local resolution: "@metamask/gas-fee-controller@workspace:packages/gas-fee-controller" dependencies: @@ -7055,15 +7354,15 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/bn.js": "npm:^5.1.5" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/jest-when": "npm:^2.7.3" "@types/uuid": "npm:^8.3.0" bn.js: "npm:^5.2.1" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - jest-when: "npm:^3.4.2" + jest: "npm:^30.4.2" + jest-when: "npm:^3.7.0" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7091,13 +7390,13 @@ __metadata: "@metamask/snaps-controllers": "npm:^19.0.0" "@metamask/snaps-sdk": "npm:^11.0.0" "@metamask/snaps-utils": "npm:^12.1.2" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7114,10 +7413,10 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7132,10 +7431,10 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/local-node-utils": "npm:^1.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7158,12 +7457,12 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deep-freeze-strict: "npm:^1.1.1" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" klona: "npm:^2.0.6" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typescript: "npm:~5.3.3" @@ -7179,13 +7478,13 @@ __metadata: "@metamask/safe-event-emitter": "npm:^3.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/readable-stream": "npm:^2.3.0" deepmerge: "npm:^4.2.2" extension-port-stream: "npm:^3.0.0" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" readable-stream: "npm:^3.6.2" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7242,15 +7541,15 @@ __metadata: "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" ethereumjs-wallet: "npm:^1.0.1" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - jest-environment-node: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-node: "npm:^30.4.1" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7383,11 +7682,11 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^6.1.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7405,10 +7704,10 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7428,12 +7727,12 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" jsonschema: "npm:^1.4.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7449,14 +7748,14 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/yargs": "npm:^17.0.32" deepmerge: "npm:^4.2.2" execa: "npm:^5.0.0" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" oxfmt: "npm:^0.44.0" prettier: "npm:^3.3.3" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typescript: "npm:~5.3.3" yargs: "npm:^17.7.2" @@ -7495,11 +7794,11 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7516,7 +7815,7 @@ __metadata: languageName: node linkType: hard -"@metamask/money-account-api-data-service@npm:^0.2.0, @metamask/money-account-api-data-service@workspace:packages/money-account-api-data-service": +"@metamask/money-account-api-data-service@npm:^0.3.0, @metamask/money-account-api-data-service@workspace:packages/money-account-api-data-service": version: 0.0.0-use.local resolution: "@metamask/money-account-api-data-service@workspace:packages/money-account-api-data-service" dependencies: @@ -7528,11 +7827,11 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7551,17 +7850,17 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/money-account-api-data-service": "npm:^0.2.0" + "@metamask/money-account-api-data-service": "npm:^0.3.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7583,11 +7882,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7602,7 +7901,7 @@ __metadata: "@metamask/authenticated-user-storage": "npm:^3.0.1" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/chomp-api-service": "npm:^3.1.0" + "@metamask/chomp-api-service": "npm:^4.0.0" "@metamask/delegation-controller": "npm:^3.0.2" "@metamask/delegation-core": "npm:^2.2.1" "@metamask/delegation-deployments": "npm:^1.4.0" @@ -7611,11 +7910,11 @@ __metadata: "@metamask/network-controller": "npm:^34.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - jest-environment-node: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + jest-environment-node: "npm:^30.4.1" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7650,13 +7949,13 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/uuid": "npm:^8.3.0" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7691,11 +7990,11 @@ __metadata: "@open-rpc/meta-schema": "npm:^1.14.6" "@open-rpc/schema-utils-js": "npm:^2.0.5" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" jsonschema: "npm:^1.4.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7720,15 +8019,15 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@solana/addresses": "npm:^2.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/lodash": "npm:^4.14.191" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7754,12 +8053,12 @@ __metadata: "@metamask/snaps-utils": "npm:^12.1.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7778,11 +8077,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7804,11 +8103,11 @@ __metadata: "@metamask/network-enablement-controller": "npm:^5.6.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7838,7 +8137,7 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/jest-when": "npm:^2.7.3" "@types/lodash": "npm:^4.14.191" "@types/node-fetch": "npm:^2.6.12" @@ -7847,15 +8146,15 @@ __metadata: deepmerge: "npm:^4.2.2" fast-deep-equal: "npm:^3.1.3" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" - jest-when: "npm:^3.4.2" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" + jest-when: "npm:^3.7.0" lodash: "npm:^4.17.21" loglevel: "npm:^1.8.1" nock: "npm:^13.3.1" node-fetch: "npm:^2.7.0" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7871,20 +8170,21 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" + "@metamask/config-registry-controller": "npm:^0.4.1" "@metamask/controller-utils": "npm:^12.3.0" "@metamask/keyring-api": "npm:^23.5.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/multichain-network-controller": "npm:^3.2.1" "@metamask/network-controller": "npm:^34.0.0" "@metamask/slip44": "npm:^4.3.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7920,7 +8220,7 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/lodash": "npm:^4.14.191" "@types/readable-stream": "npm:^2.3.0" "@types/semver": "npm:^7" @@ -7928,13 +8228,13 @@ __metadata: contentful: "npm:^10.15.0" deepmerge: "npm:^4.2.2" firebase: "npm:^11.2.0" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" lodash: "npm:^4.17.21" loglevel: "npm:^1.8.1" nock: "npm:^13.3.1" semver: "npm:^7.6.3" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -7977,12 +8277,12 @@ __metadata: "@noble/curves": "npm:^1.9.2" "@noble/hashes": "npm:^1.8.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - jest-environment-node: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + jest-environment-node: "npm:^30.4.1" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8023,13 +8323,13 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deep-freeze-strict: "npm:^1.1.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nanoid: "npm:^3.3.8" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8048,12 +8348,12 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/deep-freeze-strict": "npm:^1.1.0" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deep-freeze-strict: "npm:^1.1.1" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nanoid: "npm:^3.3.8" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8079,18 +8379,18 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@myx-trade/sdk": "npm:^0.1.265" "@nktkas/hyperliquid": "npm:^0.33.1" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/uuid": "npm:^8.3.0" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8111,18 +8411,18 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@noble/hashes": "npm:^1.8.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/punycode": "npm:^2.1.0" deepmerge: "npm:^4.2.2" ethereum-cryptography: "npm:^2.1.2" fastest-levenshtein: "npm:^1.0.16" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" punycode: "npm:^2.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8144,7 +8444,7 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/node": "npm:^16.18.54" "@types/npm-which": "npm:^3" "@types/react": "npm:^19.0.0" @@ -8152,12 +8452,12 @@ __metadata: deepmerge: "npm:^4.2.2" execa: "npm:^5.0.0" glob: "npm:^13.0.6" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" npm-which: "npm:^3.0.1" prism-react-renderer: "npm:^2.4.1" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" ts-morph: "npm:^28.0.0" tsx: "npm:^4.20.5" typescript: "npm:~5.3.3" @@ -8177,12 +8477,12 @@ __metadata: "@metamask/network-controller": "npm:^34.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/uuid": "npm:^8.3.0" deepmerge: "npm:^4.2.2" fast-json-stable-stringify: "npm:^2.1.0" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8210,10 +8510,10 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8238,15 +8538,15 @@ __metadata: "@metamask/snaps-sdk": "npm:^11.0.0" "@metamask/snaps-utils": "npm:^12.1.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8276,16 +8576,16 @@ __metadata: "@noble/ciphers": "npm:^1.3.0" "@noble/hashes": "npm:^1.8.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" ethers: "npm:^6.12.0" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" loglevel: "npm:^1.8.1" nock: "npm:^13.3.1" siwe: "npm:^2.3.2" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8329,11 +8629,11 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8351,10 +8651,10 @@ __metadata: "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8370,13 +8670,13 @@ __metadata: "@metamask/base-data-service": "npm:^0.1.3" "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" - "@tanstack/react-query": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" + "@tanstack/react-query": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8404,11 +8704,11 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8448,11 +8748,11 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8470,7 +8770,7 @@ __metadata: languageName: node linkType: hard -"@metamask/seedless-onboarding-controller@npm:^10.0.3, @metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller": +"@metamask/seedless-onboarding-controller@npm:^10.1.0, @metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller": version: 0.0.0-use.local resolution: "@metamask/seedless-onboarding-controller@workspace:packages/seedless-onboarding-controller" dependencies: @@ -8489,14 +8789,14 @@ __metadata: "@noble/hashes": "npm:^1.8.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/elliptic": "npm:^6" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/json-stable-stringify-without-jsonify": "npm:^1.0.2" async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - jest-environment-node: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-node: "npm:^30.4.1" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8517,14 +8817,14 @@ __metadata: "@metamask/swappable-obj-proxy": "npm:^2.3.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8532,7 +8832,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/sentinel-api-service@workspace:packages/sentinel-api-service": +"@metamask/sentinel-api-service@npm:^1.0.0, @metamask/sentinel-api-service@workspace:packages/sentinel-api-service": version: 0.0.0-use.local resolution: "@metamask/sentinel-api-service@workspace:packages/sentinel-api-service" dependencies: @@ -8544,11 +8844,11 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8568,15 +8868,15 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/signature-controller": "npm:^39.2.7" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" cockatiel: "npm:^3.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8602,12 +8902,12 @@ __metadata: "@metamask/network-controller": "npm:^34.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" jsonschema: "npm:^1.4.1" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8652,16 +8952,16 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" reselect: "npm:^5.1.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8686,11 +8986,11 @@ __metadata: "@metamask/snaps-sdk": "npm:^11.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8827,10 +9127,10 @@ __metadata: "@metamask/profile-sync-controller": "npm:^28.3.0" "@metamask/superstruct": "npm:^3.1.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8845,10 +9145,10 @@ __metadata: "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/local-node-utils": "npm:^1.0.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8871,10 +9171,10 @@ __metadata: dependencies: "@metamask/auto-changelog": "npm:^6.1.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8890,10 +9190,10 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8911,14 +9211,14 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/polling-controller": "npm:^16.0.8" "@metamask/profile-sync-controller": "npm:^28.3.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" bignumber.js: "npm:^9.1.2" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -8958,7 +9258,7 @@ __metadata: languageName: node linkType: hard -"@metamask/transaction-controller@npm:^69.2.0, @metamask/transaction-controller@workspace:packages/transaction-controller": +"@metamask/transaction-controller@npm:^69.2.1, @metamask/transaction-controller@workspace:packages/transaction-controller": version: 0.0.0-use.local resolution: "@metamask/transaction-controller@workspace:packages/transaction-controller" dependencies: @@ -8975,11 +9275,11 @@ __metadata: "@metamask/base-controller": "npm:^9.1.0" "@metamask/connectivity-controller": "npm:^0.3.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/core-backend": "npm:^6.5.0" + "@metamask/core-backend": "npm:^7.0.0" "@metamask/eth-block-tracker": "npm:^15.0.0" "@metamask/eth-json-rpc-provider": "npm:^6.0.1" "@metamask/ethjs-provider-http": "npm:^0.3.0" - "@metamask/gas-fee-controller": "npm:^26.2.4" + "@metamask/gas-fee-controller": "npm:^26.3.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/network-controller": "npm:^34.0.0" @@ -8989,7 +9289,7 @@ __metadata: "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" "@types/bn.js": "npm:^5.1.5" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/node": "npm:^16.18.54" async-mutex: "npm:^0.5.0" bignumber.js: "npm:^9.1.2" @@ -8998,11 +9298,11 @@ __metadata: eth-method-registry: "npm:^4.0.0" fast-json-patch: "npm:^3.1.1" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - jest-environment-jsdom: "npm:^29.7.0" + jest: "npm:^30.4.2" + jest-environment-jsdom: "npm:^30.4.1" lodash: "npm:^4.17.21" nock: "npm:^13.3.1" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -9021,29 +9321,30 @@ __metadata: "@ethersproject/abi": "npm:^5.7.0" "@ethersproject/contracts": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.0" - "@metamask/assets-controller": "npm:^11.1.0" - "@metamask/assets-controllers": "npm:^109.4.1" + "@metamask/assets-controller": "npm:^11.2.0" + "@metamask/assets-controllers": "npm:^110.0.0" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" - "@metamask/gas-fee-controller": "npm:^26.2.4" + "@metamask/gas-fee-controller": "npm:^26.3.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/network-controller": "npm:^34.0.0" "@metamask/ramps-controller": "npm:^17.0.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/sentinel-api-service": "npm:^1.0.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" bignumber.js: "npm:^9.1.2" bn.js: "npm:^5.2.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -9061,23 +9362,23 @@ __metadata: "@metamask/controller-utils": "npm:^12.3.0" "@metamask/eth-block-tracker": "npm:^15.0.0" "@metamask/eth-query": "npm:^4.0.0" - "@metamask/gas-fee-controller": "npm:^26.2.4" + "@metamask/gas-fee-controller": "npm:^26.3.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/polling-controller": "npm:^16.0.8" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/superstruct": "npm:^3.1.0" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" bn.js: "npm:^5.2.1" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" lodash: "npm:^4.17.21" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -9138,17 +9439,17 @@ __metadata: "@metamask/storage-service": "npm:^1.0.2" "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" - "@metamask/wallet": "npm:^7.0.1" + "@metamask/wallet": "npm:^8.1.0" "@oclif/core": "npm:^4.10.5" "@ts-bridge/cli": "npm:^0.6.4" "@types/better-sqlite3": "npm:^7.6.13" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" better-sqlite3: "npm:^12.9.0" deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" - jest: "npm:^29.7.0" - jest-environment-node: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + jest-environment-node: "npm:^30.4.1" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typescript: "npm:~5.3.3" bin: @@ -9174,22 +9475,22 @@ __metadata: "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.11.0" "@tanstack/query-core": "npm:^4.43.0" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" "@types/react": "npm:^19.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" + jest: "npm:^30.4.2" nock: "npm:^13.3.1" prism-react-renderer: "npm:^2.4.1" raw-loader: "npm:^4.0.2" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" - ts-jest: "npm:^29.2.5" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typescript: "npm:~5.3.3" languageName: unknown linkType: soft -"@metamask/wallet@npm:^7.0.1, @metamask/wallet@workspace:packages/wallet": +"@metamask/wallet@npm:^8.1.0, @metamask/wallet@workspace:packages/wallet": version: 0.0.0-use.local resolution: "@metamask/wallet@workspace:packages/wallet" dependencies: @@ -9201,21 +9502,22 @@ __metadata: "@metamask/browser-passworder": "npm:^6.0.0" "@metamask/connectivity-controller": "npm:^0.3.0" "@metamask/controller-utils": "npm:^12.3.0" + "@metamask/gas-fee-controller": "npm:^26.3.0" "@metamask/keyring-controller": "npm:^27.1.0" "@metamask/messenger": "npm:^2.0.0" "@metamask/network-controller": "npm:^34.0.0" "@metamask/passkey-controller": "npm:^3.0.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/scure-bip39": "npm:^2.1.1" - "@metamask/seedless-onboarding-controller": "npm:^10.0.3" + "@metamask/seedless-onboarding-controller": "npm:^10.1.0" "@metamask/storage-service": "npm:^1.0.2" - "@metamask/transaction-controller": "npm:^69.2.0" + "@metamask/transaction-controller": "npm:^69.2.1" "@metamask/utils": "npm:^11.11.0" "@ts-bridge/cli": "npm:^0.6.4" - "@types/jest": "npm:^29.5.14" + "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" - jest: "npm:^29.7.0" - ts-jest: "npm:^29.2.5" + jest: "npm:^30.4.2" + ts-jest: "npm:^29.4.11" tsx: "npm:^4.20.5" typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" @@ -9245,7 +9547,7 @@ __metadata: languageName: node linkType: hard -"@napi-rs/wasm-runtime@npm:^0.2.3": +"@napi-rs/wasm-runtime@npm:^0.2.11, @napi-rs/wasm-runtime@npm:^0.2.3": version: 0.2.12 resolution: "@napi-rs/wasm-runtime@npm:0.2.12" dependencies: @@ -10411,6 +10713,13 @@ __metadata: languageName: node linkType: hard +"@pkgr/core@npm:^0.2.9": + version: 0.2.9 + resolution: "@pkgr/core@npm:0.2.9" + checksum: 10/bb2fb86977d63f836f8f5b09015d74e6af6488f7a411dcd2bfdca79d76b5a681a9112f41c45bdf88a9069f049718efc6f3900d7f1de66a2ec966068308ae517f + languageName: node + linkType: hard + "@pnpm/config.env-replace@npm:^1.1.0": version: 1.1.0 resolution: "@pnpm/config.env-replace@npm:1.1.0" @@ -10611,6 +10920,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.34.0": + version: 0.34.48 + resolution: "@sinclair/typebox@npm:0.34.48" + checksum: 10/186eebb338255db7cfd77c2f94be0ad91816c7b5ee994c3adb95e0474ae98b769574c2b6b1f26a81613d7148ed20b11e02528f4263d8d95e3ca8dcf8faaf5306 + languageName: node + linkType: hard + "@sindresorhus/is@npm:^4.6.0": version: 4.6.0 resolution: "@sindresorhus/is@npm:4.6.0" @@ -10625,7 +10941,7 @@ __metadata: languageName: node linkType: hard -"@sinonjs/commons@npm:^3.0.0": +"@sinonjs/commons@npm:^3.0.0, @sinonjs/commons@npm:^3.0.1": version: 3.0.1 resolution: "@sinonjs/commons@npm:3.0.1" dependencies: @@ -10643,6 +10959,15 @@ __metadata: languageName: node linkType: hard +"@sinonjs/fake-timers@npm:^15.4.0": + version: 15.4.0 + resolution: "@sinonjs/fake-timers@npm:15.4.0" + dependencies: + "@sinonjs/commons": "npm:^3.0.1" + checksum: 10/3960a9fe065f38a4228c66d184eeb101e8a6af9cbfc8454dd5d45ac397201da72134048d4e808a25993494885b172dd6deecdad9949bbf4c1d3a220ef561f6cc + languageName: node + linkType: hard + "@slorber/remark-comment@npm:^1.0.0": version: 1.0.0 resolution: "@slorber/remark-comment@npm:1.0.0" @@ -10952,43 +11277,28 @@ __metadata: languageName: node linkType: hard -"@tanstack/query-core@npm:4.43.0, @tanstack/query-core@npm:^4.43.0": - version: 4.43.0 - resolution: "@tanstack/query-core@npm:4.43.0" - checksum: 10/c2a5a151c7adaea8311e01a643255f31946ae3164a71567ba80048242821ae14043f13f5516b695baebe5ea7e4b2cf717fd60908a929d18a5c5125fee925ff67 +"@tanstack/query-core@npm:5.101.2, @tanstack/query-core@npm:^5.62.16": + version: 5.101.2 + resolution: "@tanstack/query-core@npm:5.101.2" + checksum: 10/567af5e3c21628745a08c1a5054d838597bc620dcbbeabe20cdd6ccffdcca85a8d38128f6e80829b6e04d10f79ca793a7dd8fddc8a3d6bef10c2d580ae214c7d languageName: node linkType: hard -"@tanstack/query-core@npm:^5.62.16": - version: 5.90.20 - resolution: "@tanstack/query-core@npm:5.90.20" - checksum: 10/25e38f4382442bc15e0f6cce8d787e9df8d8822c61d3f3e9427e89e01b1e2506f848292e086dae29aeb55f8ce71b097c34221f3c5eda37fb4a688b5ceca5d1b3 +"@tanstack/query-core@npm:^4.43.0": + version: 4.43.0 + resolution: "@tanstack/query-core@npm:4.43.0" + checksum: 10/c2a5a151c7adaea8311e01a643255f31946ae3164a71567ba80048242821ae14043f13f5516b695baebe5ea7e4b2cf717fd60908a929d18a5c5125fee925ff67 languageName: node linkType: hard -"@tanstack/react-query@npm:^4.43.0": - version: 4.43.0 - resolution: "@tanstack/react-query@npm:4.43.0" +"@tanstack/react-query@npm:^5.62.16": + version: 5.101.2 + resolution: "@tanstack/react-query@npm:5.101.2" dependencies: - "@tanstack/query-core": "npm:4.43.0" - use-sync-external-store: "npm:^1.6.0" + "@tanstack/query-core": "npm:5.101.2" peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-native: "*" - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - checksum: 10/23f9d18d130fa2a1238d8fba8bc914c67e33753b7fc3a3c7856354a9873c4cbc5d18ce24dbf6364ecf86b8ea787575e1e60998ea75baa2b9e9647ad4b9127e10 - languageName: node - linkType: hard - -"@tootallnate/once@npm:2": - version: 2.0.0 - resolution: "@tootallnate/once@npm:2.0.0" - checksum: 10/ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 + react: ^18 || ^19 + checksum: 10/0837c176b6afb01e3632010bbd8bdfeee7f46c4a25a50ed2ba9ddddce11c34431161d80dd8e53a2f55190c2313bbfb22a84a77308e6ccf29877b4818de68c4b3 languageName: node linkType: hard @@ -11104,7 +11414,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.1.14": +"@types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.20.5": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" dependencies: @@ -11368,7 +11678,7 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": +"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1, @types/istanbul-lib-coverage@npm:^2.0.6": version: 2.0.6 resolution: "@types/istanbul-lib-coverage@npm:2.0.6" checksum: 10/3feac423fd3e5449485afac999dcfcb3d44a37c830af898b689fadc65d26526460bedb889db278e0d4d815a670331796494d073a10ee6e3a6526301fe7415778 @@ -11384,7 +11694,7 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-reports@npm:^3.0.0": +"@types/istanbul-reports@npm:^3.0.0, @types/istanbul-reports@npm:^3.0.4": version: 3.0.4 resolution: "@types/istanbul-reports@npm:3.0.4" dependencies: @@ -11402,7 +11712,17 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*, @types/jest@npm:^29.5.14": +"@types/jest@npm:*, @types/jest@npm:^30.0.0": + version: 30.0.0 + resolution: "@types/jest@npm:30.0.0" + dependencies: + expect: "npm:^30.0.0" + pretty-format: "npm:^30.0.0" + checksum: 10/cdeaa924c68b5233d9ff92861a89e7042df2b0f197633729bcf3a31e65bd4e9426e751c5665b5ac2de0b222b33f100a5502da22aefce3d2c62931c715e88f209 + languageName: node + linkType: hard + +"@types/jest@npm:^29.5.14": version: 29.5.14 resolution: "@types/jest@npm:29.5.14" dependencies: @@ -11412,14 +11732,14 @@ __metadata: languageName: node linkType: hard -"@types/jsdom@npm:^20.0.0": - version: 20.0.1 - resolution: "@types/jsdom@npm:20.0.1" +"@types/jsdom@npm:^21.1.7": + version: 21.1.7 + resolution: "@types/jsdom@npm:21.1.7" dependencies: "@types/node": "npm:*" "@types/tough-cookie": "npm:*" parse5: "npm:^7.0.0" - checksum: 10/15fbb9a0bfb4a5845cf6e795f2fd12400aacfca53b8c7e5bca4a3e5e8fa8629f676327964d64258aefb127d2d8a2be86dad46359efbfca0e8c9c2b790e7f8a88 + checksum: 10/a5ee54aec813ac928ef783f69828213af4d81325f584e1fe7573a9ae139924c40768d1d5249237e62d51b9a34ed06bde059c86c6b0248d627457ec5e5d532dfa languageName: node linkType: hard @@ -11701,7 +12021,7 @@ __metadata: languageName: node linkType: hard -"@types/stack-utils@npm:^2.0.0": +"@types/stack-utils@npm:^2.0.0, @types/stack-utils@npm:^2.0.3": version: 2.0.3 resolution: "@types/stack-utils@npm:2.0.3" checksum: 10/72576cc1522090fe497337c2b99d9838e320659ac57fa5560fcbdcbafcf5d0216c6b3a0a8a4ee4fdb3b1f5e3420aa4f6223ab57b82fef3578bec3206425c6cf5 @@ -11777,12 +12097,12 @@ __metadata: languageName: node linkType: hard -"@types/yargs@npm:^17.0.32, @types/yargs@npm:^17.0.8": - version: 17.0.33 - resolution: "@types/yargs@npm:17.0.33" +"@types/yargs@npm:^17.0.32, @types/yargs@npm:^17.0.33, @types/yargs@npm:^17.0.8": + version: 17.0.35 + resolution: "@types/yargs@npm:17.0.35" dependencies: "@types/yargs-parser": "npm:*" - checksum: 10/16f6681bf4d99fb671bf56029141ed01db2862e3db9df7fc92d8bea494359ac96a1b4b1c35a836d1e95e665fb18ad753ab2015fc0db663454e8fd4e5d5e2ef91 + checksum: 10/47bcd4476a4194ea11617ea71cba8a1eddf5505fc39c44336c1a08d452a0de4486aedbc13f47a017c8efbcb5a8aa358d976880663732ebcbc6dbcbbecadb0581 languageName: node linkType: hard @@ -11921,13 +12241,148 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.0.0": +"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.3.0": version: 1.3.1 resolution: "@ungap/structured-clone@npm:1.3.1" checksum: 10/64df206f50aef71c176f9059c1b29e1694821419c6728c446ecf39c80a811eeef156668bf51421b676494a12fd0129ccf09a44f0c641f13c27f50d5f0db6de4e languageName: node linkType: hard +"@unrs/resolver-binding-android-arm-eabi@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.11.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@unrs/resolver-binding-android-arm64@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-android-arm64@npm:1.11.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-darwin-arm64@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.11.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-darwin-x64@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-darwin-x64@npm:1.11.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-freebsd-x64@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-freebsd-x64@npm:1.11.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.11.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-arm-musleabihf@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-arm-musleabihf@npm:1.11.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-arm64-gnu@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-arm64-gnu@npm:1.11.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-arm64-musl@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-arm64-musl@npm:1.11.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-ppc64-gnu@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-ppc64-gnu@npm:1.11.1" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-riscv64-gnu@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-riscv64-gnu@npm:1.11.1" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-riscv64-musl@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-riscv64-musl@npm:1.11.1" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-s390x-gnu@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-s390x-gnu@npm:1.11.1" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-x64-gnu@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-x64-gnu@npm:1.11.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-x64-musl@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-linux-x64-musl@npm:1.11.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@unrs/resolver-binding-wasm32-wasi@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.11.1" + dependencies: + "@napi-rs/wasm-runtime": "npm:^0.2.11" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.11.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.11.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1": + version: 1.11.1 + resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.11.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@vercel/stega@npm:^0.1.2": version: 0.1.2 resolution: "@vercel/stega@npm:0.1.2" @@ -12116,13 +12571,6 @@ __metadata: languageName: node linkType: hard -"abab@npm:^2.0.6": - version: 2.0.6 - resolution: "abab@npm:2.0.6" - checksum: 10/ebe95d7278999e605823fc515a3b05d689bc72e7f825536e73c95ebf621636874c6de1b749b3c4bf866b96ccd4b3a2802efa313d0e45ad51a413c8c73247db20 - languageName: node - linkType: hard - "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -12179,16 +12627,6 @@ __metadata: languageName: node linkType: hard -"acorn-globals@npm:^7.0.0": - version: 7.0.1 - resolution: "acorn-globals@npm:7.0.1" - dependencies: - acorn: "npm:^8.1.0" - acorn-walk: "npm:^8.0.2" - checksum: 10/2a2998a547af6d0db5f0cdb90acaa7c3cbca6709010e02121fb8b8617c0fbd8bab0b869579903fde358ac78454356a14fadcc1a672ecb97b04b1c2ccba955ce8 - languageName: node - linkType: hard - "acorn-import-phases@npm:^1.0.3": version: 1.0.4 resolution: "acorn-import-phases@npm:1.0.4" @@ -12207,7 +12645,7 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.0.0, acorn-walk@npm:^8.0.2": +"acorn-walk@npm:^8.0.0": version: 8.3.5 resolution: "acorn-walk@npm:8.3.5" dependencies: @@ -12216,7 +12654,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.0.0, acorn@npm:^8.0.4, acorn@npm:^8.1.0, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.16.0, acorn@npm:^8.8.1": +"acorn@npm:^8.0.0, acorn@npm:^8.0.4, acorn@npm:^8.11.0, acorn@npm:^8.15.0, acorn@npm:^8.16.0": version: 8.16.0 resolution: "acorn@npm:8.16.0" bin: @@ -12253,21 +12691,10 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:6": - version: 6.0.2 - resolution: "agent-base@npm:6.0.2" - dependencies: - debug: "npm:4" - checksum: 10/21fb903e0917e5cb16591b4d0ef6a028a54b83ac30cd1fca58dece3d4e0990512a8723f9f83130d88a41e2af8b1f7be1386fda3ea2d181bb1a62155e75e95e23 - languageName: node - linkType: hard - -"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": - version: 7.1.1 - resolution: "agent-base@npm:7.1.1" - dependencies: - debug: "npm:^4.3.4" - checksum: 10/c478fec8f79953f118704d007a38f2a185458853f5c45579b9669372bd0e12602e88dc2ad0233077831504f7cd6fcc8251c383375bba5eaaf563b102938bda26 +"agent-base@npm:^7.1.0, agent-base@npm:^7.1.1, agent-base@npm:^7.1.2": + version: 7.1.4 + resolution: "agent-base@npm:7.1.4" + checksum: 10/79bef167247789f955aaba113bae74bf64aa1e1acca4b1d6bb444bdf91d82c3e07e9451ef6a6e2e35e8f71a6f97ce33e3d855a5328eb9fad1bc3cc4cfd031ed8 languageName: node linkType: hard @@ -12445,7 +12872,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^5.0.0": +"ansi-styles@npm:^5.0.0, ansi-styles@npm:^5.2.0": version: 5.2.0 resolution: "ansi-styles@npm:5.2.0" checksum: 10/d7f4e97ce0623aea6bc0d90dcd28881ee04cba06c570b97fd3391bd7a268eedfd9d5e2dd4fdcbdd82b8105df5faf6f24aaedc08eaf3da898e702db5948f63469 @@ -12466,7 +12893,7 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:^3.0.3, anymatch@npm:~3.1.2": +"anymatch@npm:^3.0.3, anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" dependencies: @@ -12635,6 +13062,23 @@ __metadata: languageName: node linkType: hard +"babel-jest@npm:30.4.1": + version: 30.4.1 + resolution: "babel-jest@npm:30.4.1" + dependencies: + "@jest/transform": "npm:30.4.1" + "@types/babel__core": "npm:^7.20.5" + babel-plugin-istanbul: "npm:^7.0.1" + babel-preset-jest: "npm:30.4.0" + chalk: "npm:^4.1.2" + graceful-fs: "npm:^4.2.11" + slash: "npm:^3.0.0" + peerDependencies: + "@babel/core": ^7.11.0 || ^8.0.0-0 + checksum: 10/f739152bee60b368b27676441c54e235b49bca10329bb6395b54cca5982ced1f7a5a2c504e406e1082aac3dc68ab518771c9de62cf66ffe00993ad071a58fd1b + languageName: node + linkType: hard + "babel-jest@npm:^29.7.0": version: 29.7.0 resolution: "babel-jest@npm:29.7.0" @@ -12687,6 +13131,28 @@ __metadata: languageName: node linkType: hard +"babel-plugin-istanbul@npm:^7.0.1": + version: 7.0.1 + resolution: "babel-plugin-istanbul@npm:7.0.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.0.0" + "@istanbuljs/load-nyc-config": "npm:^1.0.0" + "@istanbuljs/schema": "npm:^0.1.3" + istanbul-lib-instrument: "npm:^6.0.2" + test-exclude: "npm:^6.0.0" + checksum: 10/fe9f865f975aaa7a033de9ccb2b63fdcca7817266c5e98d3e02ac7ffd774c695093d215302796cb3770a71ef4574e7a9b298504c3c0c104cf4b48c8eda67b2a6 + languageName: node + linkType: hard + +"babel-plugin-jest-hoist@npm:30.4.0": + version: 30.4.0 + resolution: "babel-plugin-jest-hoist@npm:30.4.0" + dependencies: + "@types/babel__core": "npm:^7.20.5" + checksum: 10/112f984b3b4315f7ff15d5d17df7f5aa4b500e562c67c2eafcd9974af4369c17d50feed2f9c95cbcec32faba8ccb04f8b62828aca41a47d5fdedc532b49fbf19 + languageName: node + linkType: hard + "babel-plugin-jest-hoist@npm:^29.6.3": version: 29.6.3 resolution: "babel-plugin-jest-hoist@npm:29.6.3" @@ -12747,9 +13213,9 @@ __metadata: languageName: node linkType: hard -"babel-preset-current-node-syntax@npm:^1.0.0": - version: 1.1.0 - resolution: "babel-preset-current-node-syntax@npm:1.1.0" +"babel-preset-current-node-syntax@npm:^1.0.0, babel-preset-current-node-syntax@npm:^1.2.0": + version: 1.2.0 + resolution: "babel-preset-current-node-syntax@npm:1.2.0" dependencies: "@babel/plugin-syntax-async-generators": "npm:^7.8.4" "@babel/plugin-syntax-bigint": "npm:^7.8.3" @@ -12767,8 +13233,20 @@ __metadata: "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" peerDependencies: - "@babel/core": ^7.0.0 - checksum: 10/46331111ae72b7121172fd9e6a4a7830f651ad44bf26dbbf77b3c8a60a18009411a3eacb5e72274004290c110371230272109957d5224d155436b4794ead2f1b + "@babel/core": ^7.0.0 || ^8.0.0-0 + checksum: 10/3608fa671cfa46364ea6ec704b8fcdd7514b7b70e6ec09b1199e13ae73ed346c51d5ce2cb6d4d5b295f6a3f2cad1fdeec2308aa9e037002dd7c929194cc838ea + languageName: node + linkType: hard + +"babel-preset-jest@npm:30.4.0": + version: 30.4.0 + resolution: "babel-preset-jest@npm:30.4.0" + dependencies: + babel-plugin-jest-hoist: "npm:30.4.0" + babel-preset-current-node-syntax: "npm:^1.2.0" + peerDependencies: + "@babel/core": ^7.11.0 || ^8.0.0-beta.1 + checksum: 10/7fbdcaa1f24b2efbc1b658220df849a375858bd5e208cefcf53b116bca972b28565a0715521cc20bec41adbd20ff73b9dbbdea3634bd71f50062f0ce694a7159 languageName: node linkType: hard @@ -12861,6 +13339,15 @@ __metadata: languageName: node linkType: hard +"bats@npm:^1.13.0": + version: 1.13.0 + resolution: "bats@npm:1.13.0" + bin: + bats: bin/bats + checksum: 10/cc120033073f5ed798accfc27d2f45863204d03673d985fdb5d850246df6adf8007be4678511ec206e5a47b46f15801fa612ca7d8e16ccf6afa7a009286c1627 + languageName: node + linkType: hard + "bech32@npm:1.1.4": version: 1.1.4 resolution: "bech32@npm:1.1.4" @@ -13079,11 +13566,11 @@ __metadata: linkType: hard "brace-expansion@npm:^2.0.1, brace-expansion@npm:^2.0.2": - version: 2.1.1 - resolution: "brace-expansion@npm:2.1.1" + version: 2.1.2 + resolution: "brace-expansion@npm:2.1.2" dependencies: balanced-match: "npm:^1.0.0" - checksum: 10/4681c533dc4e6c77b3ad795b38683d297fd03c739a17bfb2a338529fa7dcf4540683a79dcd662905f4c5b0db7cfda18daafcd18dd1bbf7c3b076fe0c9c3487eb + checksum: 10/0e0f9b0df1f0809e9c326470e022eeb24334ddb54c43b1ac39f1d38f3cbaeb940794de1db826f5491843ac00d7932c43f10350a65ccd51fe7d05da627152f204 languageName: node linkType: hard @@ -13317,7 +13804,7 @@ __metadata: languageName: node linkType: hard -"callsites@npm:^3.0.0": +"callsites@npm:^3.0.0, callsites@npm:^3.1.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" checksum: 10/072d17b6abb459c2ba96598918b55868af677154bec7e73d222ef95a8fdb9bbf7dae96a8421085cdad8cd190d86653b5b6dc55a4484f2e5b2e27d5e0c3fc15b3 @@ -13341,7 +13828,7 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^6.2.0": +"camelcase@npm:^6.2.0, camelcase@npm:^6.3.0": version: 6.3.0 resolution: "camelcase@npm:6.3.0" checksum: 10/8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d @@ -13563,6 +14050,13 @@ __metadata: languageName: node linkType: hard +"ci-info@npm:^4.2.0": + version: 4.4.0 + resolution: "ci-info@npm:4.4.0" + checksum: 10/dfded0c630267d89660c8abb988ac8395a382bdfefedcc03e3e2858523312c5207db777c239c34774e3fcff11f015477c19d2ac8a58ea58aa476614a2e64f434 + languageName: node + linkType: hard + "cipher-base@npm:^1.0.0, cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3": version: 1.0.4 resolution: "cipher-base@npm:1.0.4" @@ -13573,13 +14067,27 @@ __metadata: languageName: node linkType: hard -"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.3.1": +"cjs-module-lexer@npm:^1.0.0": + version: 1.4.3 + resolution: "cjs-module-lexer@npm:1.4.3" + checksum: 10/d2b92f919a2dedbfd61d016964fce8da0035f827182ed6839c97cac56e8a8077cfa6a59388adfe2bc588a19cef9bbe830d683a76a6e93c51f65852062cfe2591 + languageName: node + linkType: hard + +"cjs-module-lexer@npm:^1.3.1": version: 1.4.0 resolution: "cjs-module-lexer@npm:1.4.0" checksum: 10/b041096749792526120d8b8756929f8ef5dd4596502a0e1013f857e3027acd6091915fea77037921d70ee1a99988a100d994d3d3c2e323b04dd4c5ffd516cf13 languageName: node linkType: hard +"cjs-module-lexer@npm:^2.1.0": + version: 2.2.0 + resolution: "cjs-module-lexer@npm:2.2.0" + checksum: 10/fc8eb5c1919504366d8260a150d93c4e857740e770467dc59ca0cc34de4b66c93075559a5af65618f359187866b1be40e036f4e1a1bab2f1e06001c216415f74 + languageName: node + linkType: hard + "clean-css@npm:^5.2.2, clean-css@npm:^5.3.3, clean-css@npm:~5.3.2": version: 5.3.3 resolution: "clean-css@npm:5.3.3" @@ -13714,10 +14222,10 @@ __metadata: languageName: node linkType: hard -"collect-v8-coverage@npm:^1.0.0": - version: 1.0.2 - resolution: "collect-v8-coverage@npm:1.0.2" - checksum: 10/30ea7d5c9ee51f2fdba4901d4186c5b7114a088ef98fd53eda3979da77eed96758a2cae81cc6d97e239aaea6065868cf908b24980663f7b7e96aa291b3e12fa4 +"collect-v8-coverage@npm:^1.0.0, collect-v8-coverage@npm:^1.0.2": + version: 1.0.3 + resolution: "collect-v8-coverage@npm:1.0.3" + checksum: 10/656443261fb7b79cf79e89cba4b55622b07c1d4976c630829d7c5c585c73cda1c2ff101f316bfb19bb9e2c58d724c7db1f70a21e213dcd14099227c5e6019860 languageName: node linkType: hard @@ -14435,26 +14943,13 @@ __metadata: languageName: node linkType: hard -"cssom@npm:^0.5.0": - version: 0.5.0 - resolution: "cssom@npm:0.5.0" - checksum: 10/b502a315b1ce020a692036cc38cb36afa44157219b80deadfa040ab800aa9321fcfbecf02fd2e6ec87db169715e27978b4ab3701f916461e9cf7808899f23b54 - languageName: node - linkType: hard - -"cssom@npm:~0.3.6": - version: 0.3.8 - resolution: "cssom@npm:0.3.8" - checksum: 10/49eacc88077555e419646c0ea84ddc73c97e3a346ad7cb95e22f9413a9722d8964b91d781ce21d378bd5ae058af9a745402383fa4e35e9cdfd19654b63f892a9 - languageName: node - linkType: hard - -"cssstyle@npm:^2.3.0": - version: 2.3.0 - resolution: "cssstyle@npm:2.3.0" +"cssstyle@npm:^4.2.1": + version: 4.6.0 + resolution: "cssstyle@npm:4.6.0" dependencies: - cssom: "npm:~0.3.6" - checksum: 10/46f7f05a153446c4018b0454ee1464b50f606cb1803c90d203524834b7438eb52f3b173ba0891c618f380ced34ee12020675dc0052a7f1be755fe4ebc27ee977 + "@asamuzakjp/css-color": "npm:^3.2.0" + rrweb-cssom: "npm:^0.8.0" + checksum: 10/1cb25c9d66b87adb165f978b75cdeb6f225d7e31ba30a8934666046a0be037e4e7200d359bfa79d4f1a4aef1083ea09633b81bcdb36a2f2ac888e8c73ea3a289 languageName: node linkType: hard @@ -14465,14 +14960,13 @@ __metadata: languageName: node linkType: hard -"data-urls@npm:^3.0.2": - version: 3.0.2 - resolution: "data-urls@npm:3.0.2" +"data-urls@npm:^5.0.0": + version: 5.0.0 + resolution: "data-urls@npm:5.0.0" dependencies: - abab: "npm:^2.0.6" - whatwg-mimetype: "npm:^3.0.0" - whatwg-url: "npm:^11.0.0" - checksum: 10/033fc3dd0fba6d24bc9a024ddcf9923691dd24f90a3d26f6545d6a2f71ec6956f93462f2cdf2183cc46f10dc01ed3bcb36731a8208456eb1a08147e571fe2a76 + whatwg-mimetype: "npm:^4.0.0" + whatwg-url: "npm:^14.0.0" + checksum: 10/5c40568c31b02641a70204ff233bc4e42d33717485d074244a98661e5f2a1e80e38fe05a5755dfaf2ee549f2ab509d6a3af2a85f4b2ad2c984e5d176695eaf46 languageName: node linkType: hard @@ -14527,7 +15021,7 @@ __metadata: languageName: node linkType: hard -"decimal.js@npm:^10.4.2, decimal.js@npm:^10.6.0": +"decimal.js@npm:^10.5.0, decimal.js@npm:^10.6.0": version: 10.6.0 resolution: "decimal.js@npm:10.6.0" checksum: 10/c0d45842d47c311d11b38ce7ccc911121953d4df3ebb1465d92b31970eb4f6738a065426a06094af59bee4b0d64e42e7c8984abd57b6767c64ea90cf90bb4a69 @@ -14553,6 +15047,18 @@ __metadata: linkType: hard "dedent@npm:^1.0.0": + version: 1.7.2 + resolution: "dedent@npm:1.7.2" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: 10/30b9062290dca72b0f5a6cd3667633448cef8cd0dec602eab61015741269ad49df90cabf0521f9a32d134ceab4e21aa7f097258c55cc3baadef94874686d6480 + languageName: node + linkType: hard + +"dedent@npm:^1.6.0": version: 1.7.1 resolution: "dedent@npm:1.7.1" peerDependencies: @@ -14708,7 +15214,7 @@ __metadata: languageName: node linkType: hard -"detect-newline@npm:^3.0.0": +"detect-newline@npm:^3.0.0, detect-newline@npm:^3.1.0": version: 3.1.0 resolution: "detect-newline@npm:3.1.0" checksum: 10/ae6cd429c41ad01b164c59ea36f264a2c479598e61cba7c99da24175a7ab80ddf066420f2bec9a1c57a6bead411b4655ff15ad7d281c000a89791f48cbe939e7 @@ -14823,15 +15329,6 @@ __metadata: languageName: node linkType: hard -"domexception@npm:^4.0.0": - version: 4.0.0 - resolution: "domexception@npm:4.0.0" - dependencies: - webidl-conversions: "npm:^7.0.0" - checksum: 10/4ed443227d2871d76c58d852b2e93c68e0443815b2741348f20881bedee8c1ad4f9bfc5d30c7dec433cd026b57da63407c010260b1682fef4c8847e7181ea43f - languageName: node - linkType: hard - "domhandler@npm:^4.0.0, domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": version: 4.3.1 resolution: "domhandler@npm:4.3.1" @@ -15302,24 +15799,6 @@ __metadata: languageName: node linkType: hard -"escodegen@npm:^2.0.0": - version: 2.1.0 - resolution: "escodegen@npm:2.1.0" - dependencies: - esprima: "npm:^4.0.1" - estraverse: "npm:^5.2.0" - esutils: "npm:^2.0.2" - source-map: "npm:~0.6.1" - dependenciesMeta: - source-map: - optional: true - bin: - escodegen: bin/escodegen.js - esgenerate: bin/esgenerate.js - checksum: 10/47719a65b2888b4586e3fa93769068b275961c13089e90d5d01a96a6e8e95871b1c3893576814c8fbf08a4a31a496f37e7b2c937cf231270f4d81de012832c7c - languageName: node - linkType: hard - "eslint-compat-utils@npm:^0.5.1": version: 0.5.1 resolution: "eslint-compat-utils@npm:0.5.1" @@ -15947,6 +16426,13 @@ __metadata: languageName: node linkType: hard +"exit-x@npm:^0.2.2": + version: 0.2.2 + resolution: "exit-x@npm:0.2.2" + checksum: 10/ee043053e6c1e237adf5ad9c4faf9f085b606f64a4ff859e2b138fab63fe642711d00c9af452a9134c4c92c55f752e818bfabab78c24d345022db163f3137027 + languageName: node + linkType: hard + "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -15961,6 +16447,20 @@ __metadata: languageName: node linkType: hard +"expect@npm:30.4.1, expect@npm:^30.0.0": + version: 30.4.1 + resolution: "expect@npm:30.4.1" + dependencies: + "@jest/expect-utils": "npm:30.4.1" + "@jest/get-type": "npm:30.1.0" + jest-matcher-utils: "npm:30.4.1" + jest-message-util: "npm:30.4.1" + jest-mock: "npm:30.4.1" + jest-util: "npm:30.4.1" + checksum: 10/f25051e5073c55369199ec3108ac01c60074bd09dad0b5a6c9fe40596438051cb52607e0e97505126422535b8d0dacab13aa29bb14e9fac71630bc710201a3f1 + languageName: node + linkType: hard + "expect@npm:^29.0.0, expect@npm:^29.7.0": version: 29.7.0 resolution: "expect@npm:29.7.0" @@ -16214,7 +16714,7 @@ __metadata: languageName: node linkType: hard -"fb-watchman@npm:^2.0.0": +"fb-watchman@npm:^2.0.0, fb-watchman@npm:^2.0.2": version: 2.0.2 resolution: "fb-watchman@npm:2.0.2" dependencies: @@ -16563,7 +17063,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": +"fsevents@npm:^2.3.2, fsevents@npm:^2.3.3, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -16573,7 +17073,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": +"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A^2.3.3#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -16733,9 +17233,9 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7": - version: 10.4.5 - resolution: "glob@npm:10.4.5" +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7, glob@npm:^10.5.0": + version: 10.5.0 + resolution: "glob@npm:10.5.0" dependencies: foreground-child: "npm:^3.1.0" jackspeak: "npm:^3.1.2" @@ -16745,7 +17245,7 @@ __metadata: path-scurry: "npm:^1.11.1" bin: glob: dist/esm/bin.mjs - checksum: 10/698dfe11828b7efd0514cd11e573eaed26b2dff611f0400907281ce3eab0c1e56143ef9b35adc7c77ecc71fba74717b510c7c223d34ca8a98ec81777b293d4ac + checksum: 10/ab3bccfefcc0afaedbd1f480cd0c4a2c0e322eb3f0aa7ceaa31b3f00b825069f17cf0f1fc8b6f256795074b903f37c0ade37ddda6a176aa57f1c2bbfe7240653 languageName: node linkType: hard @@ -16892,9 +17392,9 @@ __metadata: languageName: node linkType: hard -"handlebars@npm:^4.7.8": - version: 4.7.8 - resolution: "handlebars@npm:4.7.8" +"handlebars@npm:^4.7.9": + version: 4.7.9 + resolution: "handlebars@npm:4.7.9" dependencies: minimist: "npm:^1.2.5" neo-async: "npm:^2.6.2" @@ -16906,7 +17406,7 @@ __metadata: optional: true bin: handlebars: bin/handlebars - checksum: 10/bd528f4dd150adf67f3f857118ef0fa43ff79a153b1d943fa0a770f2599e38b25a7a0dbac1a3611a4ec86970fd2325a81310fb788b5c892308c9f8743bd02e11 + checksum: 10/e755433d652e8a15fc02f83d7478e652359e7a4d354c4328818853ed4f8a39d4a09e1d22dad3c7213c5240864a65b3c840970b8b181745575dd957dd258f2b8d languageName: node linkType: hard @@ -17164,12 +17664,12 @@ __metadata: languageName: node linkType: hard -"html-encoding-sniffer@npm:^3.0.0": - version: 3.0.0 - resolution: "html-encoding-sniffer@npm:3.0.0" +"html-encoding-sniffer@npm:^4.0.0": + version: 4.0.0 + resolution: "html-encoding-sniffer@npm:4.0.0" dependencies: - whatwg-encoding: "npm:^2.0.0" - checksum: 10/707a812ec2acaf8bb5614c8618dc81e2fb6b4399d03e95ff18b65679989a072f4e919b9bef472039301a1bbfba64063ba4c79ea6e851c653ac9db80dbefe8fe5 + whatwg-encoding: "npm:^3.1.1" + checksum: 10/e86efd493293a5671b8239bd099d42128433bb3c7b0fdc7819282ef8e118a21f5dead0ad6f358e024a4e5c84f17ebb7a9b36075220fac0a6222b207248bede6f languageName: node linkType: hard @@ -17332,18 +17832,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "http-proxy-agent@npm:5.0.0" - dependencies: - "@tootallnate/once": "npm:2" - agent-base: "npm:6" - debug: "npm:4" - checksum: 10/5ee19423bc3e0fd5f23ce991b0755699ad2a46a440ce9cec99e8126bb98448ad3479d2c0ea54be5519db5b19a4ffaa69616bac01540db18506dd4dac3dc418f0 - languageName: node - linkType: hard - -"http-proxy-agent@npm:^7.0.0": +"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.2": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" dependencies: @@ -17392,23 +17881,13 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.1": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: "npm:6" - debug: "npm:4" - checksum: 10/f0dce7bdcac5e8eaa0be3c7368bb8836ed010fb5b6349ffb412b172a203efe8f807d9a6681319105ea1b6901e1972c7b5ea899672a7b9aad58309f766dcbe0df - languageName: node - linkType: hard - -"https-proxy-agent@npm:^7.0.1": - version: 7.0.5 - resolution: "https-proxy-agent@npm:7.0.5" +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.6": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" dependencies: - agent-base: "npm:^7.0.2" + agent-base: "npm:^7.1.2" debug: "npm:4" - checksum: 10/6679d46159ab3f9a5509ee80c3a3fc83fba3a920a5e18d32176c3327852c3c00ad640c0c4210a8fd70ea3c4a6d3a1b375bf01942516e7df80e2646bdc77658ab + checksum: 10/784b628cbd55b25542a9d85033bdfd03d4eda630fb8b3c9477959367f3be95dc476ed2ecbb9836c359c7c698027fc7b45723a302324433590f45d6c1706e8c13 languageName: node linkType: hard @@ -17546,7 +18025,7 @@ __metadata: languageName: node linkType: hard -"import-local@npm:^3.0.2": +"import-local@npm:^3.0.2, import-local@npm:^3.2.0": version: 3.2.0 resolution: "import-local@npm:3.2.0" dependencies: @@ -17810,7 +18289,7 @@ __metadata: languageName: node linkType: hard -"is-generator-fn@npm:^2.0.0": +"is-generator-fn@npm:^2.0.0, is-generator-fn@npm:^2.1.0": version: 2.1.0 resolution: "is-generator-fn@npm:2.1.0" checksum: 10/a6ad5492cf9d1746f73b6744e0c43c0020510b59d56ddcb78a91cbc173f09b5e6beff53d75c9c5a29feb618bfef2bf458e025ecf3a57ad2268e2fb2569f56215 @@ -18074,7 +18553,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-instrument@npm:^6.0.0": +"istanbul-lib-instrument@npm:^6.0.0, istanbul-lib-instrument@npm:^6.0.2": version: 6.0.3 resolution: "istanbul-lib-instrument@npm:6.0.3" dependencies: @@ -18109,6 +18588,17 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-source-maps@npm:^5.0.0": + version: 5.0.6 + resolution: "istanbul-lib-source-maps@npm:5.0.6" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.23" + debug: "npm:^4.1.1" + istanbul-lib-coverage: "npm:^3.0.0" + checksum: 10/569dd0a392ee3464b1fe1accbaef5cc26de3479eacb5b91d8c67ebb7b425d39fd02247d85649c3a0e9c29b600809fa60b5af5a281a75a89c01f385b1e24823a2 + languageName: node + linkType: hard + "istanbul-reports@npm:^3.1.3": version: 3.1.7 resolution: "istanbul-reports@npm:3.1.7" @@ -18145,6 +18635,17 @@ __metadata: languageName: node linkType: hard +"jest-changed-files@npm:30.4.1": + version: 30.4.1 + resolution: "jest-changed-files@npm:30.4.1" + dependencies: + execa: "npm:^5.1.1" + jest-util: "npm:30.4.1" + p-limit: "npm:^3.1.0" + checksum: 10/e566af0d6c53115edf34fd1d9bda3b857fcc6626bd032516a480b60ec208d515558eda1e693e76ef992633d71828a4c38a3e91a236142459855483cbd03ff4c4 + languageName: node + linkType: hard + "jest-changed-files@npm:^29.7.0": version: 29.7.0 resolution: "jest-changed-files@npm:29.7.0" @@ -18156,6 +18657,34 @@ __metadata: languageName: node linkType: hard +"jest-circus@npm:30.4.2": + version: 30.4.2 + resolution: "jest-circus@npm:30.4.2" + dependencies: + "@jest/environment": "npm:30.4.1" + "@jest/expect": "npm:30.4.1" + "@jest/test-result": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + chalk: "npm:^4.1.2" + co: "npm:^4.6.0" + dedent: "npm:^1.6.0" + is-generator-fn: "npm:^2.1.0" + jest-each: "npm:30.4.1" + jest-matcher-utils: "npm:30.4.1" + jest-message-util: "npm:30.4.1" + jest-runtime: "npm:30.4.2" + jest-snapshot: "npm:30.4.1" + jest-util: "npm:30.4.1" + p-limit: "npm:^3.1.0" + pretty-format: "npm:30.4.1" + pure-rand: "npm:^7.0.0" + slash: "npm:^3.0.0" + stack-utils: "npm:^2.0.6" + checksum: 10/b210db2cd3ab595c6053deee4c11e7eb5ea293b9af16fc87021288125dd42e8d13fdc77be21eca71f1636c6fe104edc26e32d6e707168763e97b0d1718c11203 + languageName: node + linkType: hard + "jest-circus@npm:^29.7.0": version: 29.7.0 resolution: "jest-circus@npm:29.7.0" @@ -18184,6 +18713,31 @@ __metadata: languageName: node linkType: hard +"jest-cli@npm:30.4.2": + version: 30.4.2 + resolution: "jest-cli@npm:30.4.2" + dependencies: + "@jest/core": "npm:30.4.2" + "@jest/test-result": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + chalk: "npm:^4.1.2" + exit-x: "npm:^0.2.2" + import-local: "npm:^3.2.0" + jest-config: "npm:30.4.2" + jest-util: "npm:30.4.1" + jest-validate: "npm:30.4.1" + yargs: "npm:^17.7.2" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: ./bin/jest.js + checksum: 10/dd33f8ee6500298639d5ec4d5f641306d9876fa182042ee40e9c63c59bdf9a82dc46da5bf38fae0783f6ac874df7f7f099006b263022954cf494f8468dfe583c + languageName: node + linkType: hard + "jest-cli@npm:^29.7.0": version: 29.7.0 resolution: "jest-cli@npm:29.7.0" @@ -18210,6 +18764,48 @@ __metadata: languageName: node linkType: hard +"jest-config@npm:30.4.2": + version: 30.4.2 + resolution: "jest-config@npm:30.4.2" + dependencies: + "@babel/core": "npm:^7.27.4" + "@jest/get-type": "npm:30.1.0" + "@jest/pattern": "npm:30.4.0" + "@jest/test-sequencer": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + babel-jest: "npm:30.4.1" + chalk: "npm:^4.1.2" + ci-info: "npm:^4.2.0" + deepmerge: "npm:^4.3.1" + glob: "npm:^10.5.0" + graceful-fs: "npm:^4.2.11" + jest-circus: "npm:30.4.2" + jest-docblock: "npm:30.4.0" + jest-environment-node: "npm:30.4.1" + jest-regex-util: "npm:30.4.0" + jest-resolve: "npm:30.4.1" + jest-runner: "npm:30.4.2" + jest-util: "npm:30.4.1" + jest-validate: "npm:30.4.1" + parse-json: "npm:^5.2.0" + pretty-format: "npm:30.4.1" + slash: "npm:^3.0.0" + strip-json-comments: "npm:^3.1.1" + peerDependencies: + "@types/node": "*" + esbuild-register: ">=3.4.0" + ts-node: ">=9.0.0" + peerDependenciesMeta: + "@types/node": + optional: true + esbuild-register: + optional: true + ts-node: + optional: true + checksum: 10/d37d923a5b3e815b73bbefe09c2f7fb092d6933f5f4ec0555fd9cacf97c58e8adb73fc4780a99e6a49e798444b1bb0097c9d5232feb1b8ffa31af589f9c02d9a + languageName: node + linkType: hard + "jest-config@npm:^29.7.0": version: 29.7.0 resolution: "jest-config@npm:29.7.0" @@ -18248,6 +18844,18 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:30.4.1": + version: 30.4.1 + resolution: "jest-diff@npm:30.4.1" + dependencies: + "@jest/diff-sequences": "npm:30.4.0" + "@jest/get-type": "npm:30.1.0" + chalk: "npm:^4.1.2" + pretty-format: "npm:30.4.1" + checksum: 10/594212df96bf101170afdb7eebd188d6d7d27241cbdd18b61d95f1142a3c94ae3b270377d15e719fb3c5efe4458d32acba8ad13dd6230dd7d6917a9eebb32625 + languageName: node + linkType: hard + "jest-diff@npm:^29.7.0": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" @@ -18260,6 +18868,15 @@ __metadata: languageName: node linkType: hard +"jest-docblock@npm:30.4.0": + version: 30.4.0 + resolution: "jest-docblock@npm:30.4.0" + dependencies: + detect-newline: "npm:^3.1.0" + checksum: 10/0ee25351ef941e832e53d10e74f34b38941b8f5fe750584661f6c5a115771818b081b8e39830c49d152fd361af81c7800c2d3a3c3a0e2dc742f7bfdbb6b1baaa + languageName: node + linkType: hard + "jest-docblock@npm:^29.7.0": version: 29.7.0 resolution: "jest-docblock@npm:29.7.0" @@ -18269,6 +18886,19 @@ __metadata: languageName: node linkType: hard +"jest-each@npm:30.4.1": + version: 30.4.1 + resolution: "jest-each@npm:30.4.1" + dependencies: + "@jest/get-type": "npm:30.1.0" + "@jest/types": "npm:30.4.1" + chalk: "npm:^4.1.2" + jest-util: "npm:30.4.1" + pretty-format: "npm:30.4.1" + checksum: 10/077365c3fd0dc0d74aaa180fe4e3728533685413db58e7a30c8502d19a60a0b08259825d8aa36c569c8175a9d0cf901dd69c0a4eb63567c22c07bd0b566ddf89 + languageName: node + linkType: hard + "jest-each@npm:^29.7.0": version: 29.7.0 resolution: "jest-each@npm:29.7.0" @@ -18281,25 +18911,35 @@ __metadata: checksum: 10/bd1a077654bdaa013b590deb5f7e7ade68f2e3289180a8c8f53bc8a49f3b40740c0ec2d3a3c1aee906f682775be2bebbac37491d80b634d15276b0aa0f2e3fda languageName: node linkType: hard - -"jest-environment-jsdom@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-environment-jsdom@npm:29.7.0" - dependencies: - "@jest/environment": "npm:^29.7.0" - "@jest/fake-timers": "npm:^29.7.0" - "@jest/types": "npm:^29.6.3" - "@types/jsdom": "npm:^20.0.0" - "@types/node": "npm:*" - jest-mock: "npm:^29.7.0" - jest-util: "npm:^29.7.0" - jsdom: "npm:^20.0.0" + +"jest-environment-jsdom@npm:^30.4.1": + version: 30.4.1 + resolution: "jest-environment-jsdom@npm:30.4.1" + dependencies: + "@jest/environment": "npm:30.4.1" + "@jest/environment-jsdom-abstract": "npm:30.4.1" + jsdom: "npm:^26.1.0" peerDependencies: - canvas: ^2.5.0 + canvas: ^3.0.0 peerDependenciesMeta: canvas: optional: true - checksum: 10/23bbfc9bca914baef4b654f7983175a4d49b0f515a5094ebcb8f819f28ec186f53c0ba06af1855eac04bab1457f4ea79dae05f70052cf899863e8096daa6e0f5 + checksum: 10/2ed0860e257d404622306823f18c15f0fe881fc1d16e1245434b54eb690fa0784cf54cd7eef74b2e9373e1699b1726a0fb69d60ae388cc78eb36968fe3fd0bf6 + languageName: node + linkType: hard + +"jest-environment-node@npm:30.4.1, jest-environment-node@npm:^30.4.1": + version: 30.4.1 + resolution: "jest-environment-node@npm:30.4.1" + dependencies: + "@jest/environment": "npm:30.4.1" + "@jest/fake-timers": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + jest-mock: "npm:30.4.1" + jest-util: "npm:30.4.1" + jest-validate: "npm:30.4.1" + checksum: 10/b93157061c6fa4b62808741bdda5fbc0372a9d2a3db844f0ffb819ed104b3b5c6ff73375d9f57c0d8485a0ad6aed07d4cfbb98aca985c38e1a29f64096b940bc languageName: node linkType: hard @@ -18324,6 +18964,28 @@ __metadata: languageName: node linkType: hard +"jest-haste-map@npm:30.4.1": + version: 30.4.1 + resolution: "jest-haste-map@npm:30.4.1" + dependencies: + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + anymatch: "npm:^3.1.3" + fb-watchman: "npm:^2.0.2" + fsevents: "npm:^2.3.3" + graceful-fs: "npm:^4.2.11" + jest-regex-util: "npm:30.4.0" + jest-util: "npm:30.4.1" + jest-worker: "npm:30.4.1" + picomatch: "npm:^4.0.3" + walker: "npm:^1.0.8" + dependenciesMeta: + fsevents: + optional: true + checksum: 10/d26404c7258d03fa423604191bca39707438ca1e62a9a471c92fcd468fa386cbdce2c50b3834fb830b25836e3eee34e3070d22b016b42f0ab626c157f5726eeb + languageName: node + linkType: hard + "jest-haste-map@npm:^29.7.0": version: 29.7.0 resolution: "jest-haste-map@npm:29.7.0" @@ -18347,6 +19009,16 @@ __metadata: languageName: node linkType: hard +"jest-leak-detector@npm:30.4.1": + version: 30.4.1 + resolution: "jest-leak-detector@npm:30.4.1" + dependencies: + "@jest/get-type": "npm:30.1.0" + pretty-format: "npm:30.4.1" + checksum: 10/8c0945d1c73f6a2abde8660f7fc5693b344cd1f5fd66153c0c2d13007f8429486eb99ecf15ac68d3d4410534fd0fad1ed430c32a641cdac66e021dbd33204c9a + languageName: node + linkType: hard + "jest-leak-detector@npm:^29.7.0": version: 29.7.0 resolution: "jest-leak-detector@npm:29.7.0" @@ -18357,6 +19029,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:30.4.1": + version: 30.4.1 + resolution: "jest-matcher-utils@npm:30.4.1" + dependencies: + "@jest/get-type": "npm:30.1.0" + chalk: "npm:^4.1.2" + jest-diff: "npm:30.4.1" + pretty-format: "npm:30.4.1" + checksum: 10/4da6e5c7fe5903fae7394233ea4b892567fb027065670c03096d01be0b389f858055c5ade20d59e82fedec6f3287e6f1720de526cd9a9ad3495432320adb9194 + languageName: node + linkType: hard + "jest-matcher-utils@npm:^29.7.0": version: 29.7.0 resolution: "jest-matcher-utils@npm:29.7.0" @@ -18369,6 +19053,24 @@ __metadata: languageName: node linkType: hard +"jest-message-util@npm:30.4.1": + version: 30.4.1 + resolution: "jest-message-util@npm:30.4.1" + dependencies: + "@babel/code-frame": "npm:^7.27.1" + "@jest/types": "npm:30.4.1" + "@types/stack-utils": "npm:^2.0.3" + chalk: "npm:^4.1.2" + graceful-fs: "npm:^4.2.11" + jest-util: "npm:30.4.1" + picomatch: "npm:^4.0.3" + pretty-format: "npm:30.4.1" + slash: "npm:^3.0.0" + stack-utils: "npm:^2.0.6" + checksum: 10/f83894efa37aa9c61c0a559b1027ecdb0d0cd8afd3e8ea74e797c707d58daea814e72f04b6db0bb6a148c12ae203e9c6e6c5544832ca5fae286c4f80c18ddc3f + languageName: node + linkType: hard + "jest-message-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-message-util@npm:29.7.0" @@ -18386,6 +19088,17 @@ __metadata: languageName: node linkType: hard +"jest-mock@npm:30.4.1": + version: 30.4.1 + resolution: "jest-mock@npm:30.4.1" + dependencies: + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + jest-util: "npm:30.4.1" + checksum: 10/8d0c2794130217b9030b888ce380fe57d82388eec19351bd666440ba46f1e24a7e2bdf42cbe9bcfda2b881d4c0ea09db3c80131b9ab788fb5224af2a1339b422 + languageName: node + linkType: hard + "jest-mock@npm:^29.7.0": version: 29.7.0 resolution: "jest-mock@npm:29.7.0" @@ -18397,7 +19110,7 @@ __metadata: languageName: node linkType: hard -"jest-pnp-resolver@npm:^1.2.2": +"jest-pnp-resolver@npm:^1.2.2, jest-pnp-resolver@npm:^1.2.3": version: 1.2.3 resolution: "jest-pnp-resolver@npm:1.2.3" peerDependencies: @@ -18409,6 +19122,13 @@ __metadata: languageName: node linkType: hard +"jest-regex-util@npm:30.4.0": + version: 30.4.0 + resolution: "jest-regex-util@npm:30.4.0" + checksum: 10/8664fcc1d07c8236a3bd012c0f06ae9d14d96e758b32ee340a3a7c4c326d0b5052d8c4ae4f4c4184f08bf78723d905352f22923647df9658ace3604f03bf074f + languageName: node + linkType: hard + "jest-regex-util@npm:^29.6.3": version: 29.6.3 resolution: "jest-regex-util@npm:29.6.3" @@ -18416,6 +19136,16 @@ __metadata: languageName: node linkType: hard +"jest-resolve-dependencies@npm:30.4.2": + version: 30.4.2 + resolution: "jest-resolve-dependencies@npm:30.4.2" + dependencies: + jest-regex-util: "npm:30.4.0" + jest-snapshot: "npm:30.4.1" + checksum: 10/ec7e27d1abf67dfe9ec1a2887b5fa883e1e6ca38eb45506a82f93e29d8df62c18cb40ec07af43fe0fa65d568051a878b6649745f6c032d8962a8dad6323763ad + languageName: node + linkType: hard + "jest-resolve-dependencies@npm:^29.7.0": version: 29.7.0 resolution: "jest-resolve-dependencies@npm:29.7.0" @@ -18426,6 +19156,22 @@ __metadata: languageName: node linkType: hard +"jest-resolve@npm:30.4.1": + version: 30.4.1 + resolution: "jest-resolve@npm:30.4.1" + dependencies: + chalk: "npm:^4.1.2" + graceful-fs: "npm:^4.2.11" + jest-haste-map: "npm:30.4.1" + jest-pnp-resolver: "npm:^1.2.3" + jest-util: "npm:30.4.1" + jest-validate: "npm:30.4.1" + slash: "npm:^3.0.0" + unrs-resolver: "npm:^1.7.11" + checksum: 10/197ca741df92c1006c2367142b5e44827d995f062e94a923f574e87ce04f966634851bb31f54ea377ca163a8362613947cd2311abf8a5712fe879b1ac15f662f + languageName: node + linkType: hard + "jest-resolve@npm:^29.7.0": version: 29.7.0 resolution: "jest-resolve@npm:29.7.0" @@ -18443,6 +19189,36 @@ __metadata: languageName: node linkType: hard +"jest-runner@npm:30.4.2": + version: 30.4.2 + resolution: "jest-runner@npm:30.4.2" + dependencies: + "@jest/console": "npm:30.4.1" + "@jest/environment": "npm:30.4.1" + "@jest/test-result": "npm:30.4.1" + "@jest/transform": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + chalk: "npm:^4.1.2" + emittery: "npm:^0.13.1" + exit-x: "npm:^0.2.2" + graceful-fs: "npm:^4.2.11" + jest-docblock: "npm:30.4.0" + jest-environment-node: "npm:30.4.1" + jest-haste-map: "npm:30.4.1" + jest-leak-detector: "npm:30.4.1" + jest-message-util: "npm:30.4.1" + jest-resolve: "npm:30.4.1" + jest-runtime: "npm:30.4.2" + jest-util: "npm:30.4.1" + jest-watcher: "npm:30.4.1" + jest-worker: "npm:30.4.1" + p-limit: "npm:^3.1.0" + source-map-support: "npm:0.5.13" + checksum: 10/3ed8ee70019f1f5a63faaf07a15e522ec878601dc6eccbde7eb4d0529e4d1a7314e7041160075458257e3103f41d6e9bbb2df8698806805ae2768a7e90228103 + languageName: node + linkType: hard + "jest-runner@npm:^29.7.0": version: 29.7.0 resolution: "jest-runner@npm:29.7.0" @@ -18472,6 +19248,36 @@ __metadata: languageName: node linkType: hard +"jest-runtime@npm:30.4.2": + version: 30.4.2 + resolution: "jest-runtime@npm:30.4.2" + dependencies: + "@jest/environment": "npm:30.4.1" + "@jest/fake-timers": "npm:30.4.1" + "@jest/globals": "npm:30.4.1" + "@jest/source-map": "npm:30.0.1" + "@jest/test-result": "npm:30.4.1" + "@jest/transform": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + chalk: "npm:^4.1.2" + cjs-module-lexer: "npm:^2.1.0" + collect-v8-coverage: "npm:^1.0.2" + glob: "npm:^10.5.0" + graceful-fs: "npm:^4.2.11" + jest-haste-map: "npm:30.4.1" + jest-message-util: "npm:30.4.1" + jest-mock: "npm:30.4.1" + jest-regex-util: "npm:30.4.0" + jest-resolve: "npm:30.4.1" + jest-snapshot: "npm:30.4.1" + jest-util: "npm:30.4.1" + slash: "npm:^3.0.0" + strip-bom: "npm:^4.0.0" + checksum: 10/bc2612a17650e7187d2c778aa66fc07926f828eeb3a445e47ffa757f65a4fb29628a43c6e792196b0a3a06d099b0405b6654a2c314fc5092013690d01483fd75 + languageName: node + linkType: hard + "jest-runtime@npm:^29.7.0": version: 29.7.0 resolution: "jest-runtime@npm:29.7.0" @@ -18502,13 +19308,42 @@ __metadata: languageName: node linkType: hard -"jest-silent-reporter@npm:^0.5.0": - version: 0.5.0 - resolution: "jest-silent-reporter@npm:0.5.0" +"jest-silent-reporter@npm:^0.6.0": + version: 0.6.0 + resolution: "jest-silent-reporter@npm:0.6.0" dependencies: chalk: "npm:^4.0.0" jest-util: "npm:^26.0.0" - checksum: 10/54853eb1fd342eb074ff97bbd2f177c3425e635deb008ce1a5feede398930b6472549797342e3ea536f8e2e538d8718d6f281f3d44dec2ae4d32f61da0eb547e + checksum: 10/443e0abaf5a6dc8c17da1e8495b7a55f813224adc39b6d1954bf49ff7fe70533b1020571453180dbb8388ace87f8e1dfc79610a4554bb93334f6c4154231c292 + languageName: node + linkType: hard + +"jest-snapshot@npm:30.4.1": + version: 30.4.1 + resolution: "jest-snapshot@npm:30.4.1" + dependencies: + "@babel/core": "npm:^7.27.4" + "@babel/generator": "npm:^7.27.5" + "@babel/plugin-syntax-jsx": "npm:^7.27.1" + "@babel/plugin-syntax-typescript": "npm:^7.27.1" + "@babel/types": "npm:^7.27.3" + "@jest/expect-utils": "npm:30.4.1" + "@jest/get-type": "npm:30.1.0" + "@jest/snapshot-utils": "npm:30.4.1" + "@jest/transform": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + babel-preset-current-node-syntax: "npm:^1.2.0" + chalk: "npm:^4.1.2" + expect: "npm:30.4.1" + graceful-fs: "npm:^4.2.11" + jest-diff: "npm:30.4.1" + jest-matcher-utils: "npm:30.4.1" + jest-message-util: "npm:30.4.1" + jest-util: "npm:30.4.1" + pretty-format: "npm:30.4.1" + semver: "npm:^7.7.2" + synckit: "npm:^0.11.8" + checksum: 10/6135108d3e0e9fb93ed10fd9ad91d8dbe56f90a9ea84c32a0b551518f8c71f363299dcc301717f3ed82cfe2a276d7993d2b3ccfabea3e8020d49ae8b0f9b6cd8 languageName: node linkType: hard @@ -18540,6 +19375,20 @@ __metadata: languageName: node linkType: hard +"jest-util@npm:30.4.1": + version: 30.4.1 + resolution: "jest-util@npm:30.4.1" + dependencies: + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + chalk: "npm:^4.1.2" + ci-info: "npm:^4.2.0" + graceful-fs: "npm:^4.2.11" + picomatch: "npm:^4.0.3" + checksum: 10/603093e12076906afcf28be514d5b7ac4e3c0e26997b0047614cf2a308b65d773137304a1fb011d747517e881aeed067f6606b9937f5b838d67f6e5734b49ebe + languageName: node + linkType: hard + "jest-util@npm:^26.0.0": version: 26.6.2 resolution: "jest-util@npm:26.6.2" @@ -18568,6 +19417,20 @@ __metadata: languageName: node linkType: hard +"jest-validate@npm:30.4.1": + version: 30.4.1 + resolution: "jest-validate@npm:30.4.1" + dependencies: + "@jest/get-type": "npm:30.1.0" + "@jest/types": "npm:30.4.1" + camelcase: "npm:^6.3.0" + chalk: "npm:^4.1.2" + leven: "npm:^3.1.0" + pretty-format: "npm:30.4.1" + checksum: 10/527fe8ad02df9a4f7f467ecc4e3ac2a37a27b7b30345e7bc3cb9c2ead33fbc8ed1290c0827baa06471281012c38abb96cb268af274a0a2350548e50db20a434f + languageName: node + linkType: hard + "jest-validate@npm:^29.7.0": version: 29.7.0 resolution: "jest-validate@npm:29.7.0" @@ -18582,6 +19445,22 @@ __metadata: languageName: node linkType: hard +"jest-watcher@npm:30.4.1": + version: 30.4.1 + resolution: "jest-watcher@npm:30.4.1" + dependencies: + "@jest/test-result": "npm:30.4.1" + "@jest/types": "npm:30.4.1" + "@types/node": "npm:*" + ansi-escapes: "npm:^4.3.2" + chalk: "npm:^4.1.2" + emittery: "npm:^0.13.1" + jest-util: "npm:30.4.1" + string-length: "npm:^4.0.2" + checksum: 10/05350ed3d5643e87e22cc5faee14f912dfc06ba63d56944006d9837f2070ed509a1d124c7e7be3e3a9a6a382bd31d491146da6fda4483acd4b8292091888e9bd + languageName: node + linkType: hard + "jest-watcher@npm:^29.7.0": version: 29.7.0 resolution: "jest-watcher@npm:29.7.0" @@ -18598,12 +19477,25 @@ __metadata: languageName: node linkType: hard -"jest-when@npm:^3.4.2": - version: 3.6.0 - resolution: "jest-when@npm:3.6.0" +"jest-when@npm:^3.7.0": + version: 3.7.0 + resolution: "jest-when@npm:3.7.0" peerDependencies: jest: ">= 25" - checksum: 10/0cb92738ccfa5711a685107f4437f18aefbe3cda120c912a9d49b612eeef03a910481ab40fe753fd42c4e617ffbb3d84c6bd66a76d963dac7f1ad9e9e5059359 + checksum: 10/b5b88d077ed467aab220c71c885dbc5f448604f06e68f761ce9f479c99bb74e0dbf553d1cc980751d88401b034a578e1c44eec5e4095743b7586f02bb7c6313d + languageName: node + linkType: hard + +"jest-worker@npm:30.4.1": + version: 30.4.1 + resolution: "jest-worker@npm:30.4.1" + dependencies: + "@types/node": "npm:*" + "@ungap/structured-clone": "npm:^1.3.0" + jest-util: "npm:30.4.1" + merge-stream: "npm:^2.0.0" + supports-color: "npm:^8.1.1" + checksum: 10/ff6af73c9097fc07e90490d3e1e354c702390ef66f7f40054a15dd6d56809a25634179969ff80bde782a6c645f49fa48bf3aacfe7d05af7315c48020f9b2b1cd languageName: node linkType: hard @@ -18649,6 +19541,25 @@ __metadata: languageName: node linkType: hard +"jest@npm:^30.4.2": + version: 30.4.2 + resolution: "jest@npm:30.4.2" + dependencies: + "@jest/core": "npm:30.4.2" + "@jest/types": "npm:30.4.1" + import-local: "npm:^3.2.0" + jest-cli: "npm:30.4.2" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: ./bin/jest.js + checksum: 10/3690f4009a46781b480fbd4c8c60dd910a3cf8af76626f29971003c28eb17dfa322e86f3203e3da168edcc9f7ba973f3b1fbf15ca12393cc5f86bc61f3289fef + languageName: node + linkType: hard + "jiti@npm:^1.20.0": version: 1.21.7 resolution: "jiti@npm:1.21.7" @@ -18752,42 +19663,36 @@ __metadata: languageName: node linkType: hard -"jsdom@npm:^20.0.0": - version: 20.0.3 - resolution: "jsdom@npm:20.0.3" +"jsdom@npm:^26.1.0": + version: 26.1.0 + resolution: "jsdom@npm:26.1.0" dependencies: - abab: "npm:^2.0.6" - acorn: "npm:^8.8.1" - acorn-globals: "npm:^7.0.0" - cssom: "npm:^0.5.0" - cssstyle: "npm:^2.3.0" - data-urls: "npm:^3.0.2" - decimal.js: "npm:^10.4.2" - domexception: "npm:^4.0.0" - escodegen: "npm:^2.0.0" - form-data: "npm:^4.0.0" - html-encoding-sniffer: "npm:^3.0.0" - http-proxy-agent: "npm:^5.0.0" - https-proxy-agent: "npm:^5.0.1" + cssstyle: "npm:^4.2.1" + data-urls: "npm:^5.0.0" + decimal.js: "npm:^10.5.0" + html-encoding-sniffer: "npm:^4.0.0" + http-proxy-agent: "npm:^7.0.2" + https-proxy-agent: "npm:^7.0.6" is-potential-custom-element-name: "npm:^1.0.1" - nwsapi: "npm:^2.2.2" - parse5: "npm:^7.1.1" + nwsapi: "npm:^2.2.16" + parse5: "npm:^7.2.1" + rrweb-cssom: "npm:^0.8.0" saxes: "npm:^6.0.0" symbol-tree: "npm:^3.2.4" - tough-cookie: "npm:^4.1.2" - w3c-xmlserializer: "npm:^4.0.0" + tough-cookie: "npm:^5.1.1" + w3c-xmlserializer: "npm:^5.0.0" webidl-conversions: "npm:^7.0.0" - whatwg-encoding: "npm:^2.0.0" - whatwg-mimetype: "npm:^3.0.0" - whatwg-url: "npm:^11.0.0" - ws: "npm:^8.11.0" - xml-name-validator: "npm:^4.0.0" + whatwg-encoding: "npm:^3.1.1" + whatwg-mimetype: "npm:^4.0.0" + whatwg-url: "npm:^14.1.1" + ws: "npm:^8.18.0" + xml-name-validator: "npm:^5.0.0" peerDependencies: - canvas: ^2.5.0 + canvas: ^3.0.0 peerDependenciesMeta: canvas: optional: true - checksum: 10/a4cdcff5b07eed87da90b146b82936321533b5efe8124492acf7160ebd5b9cf2b3c2435683592bf1cffb479615245756efb6c173effc1906f845a86ed22af985 + checksum: 10/39d78c4889cac20826393400dce1faed1666e9244fe0c8342a8f08c315375878e6be7fcfe339a33d6ff1a083bfe9e71b16d56ecf4d9a87db2da8c795925ea8c1 languageName: node linkType: hard @@ -19199,7 +20104,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0, lru-cache@npm:^10.4.3": version: 10.4.3 resolution: "lru-cache@npm:10.4.3" checksum: 10/e6e90267360476720fa8e83cc168aa2bf0311f3f2eea20a6ba78b90a885ae72071d9db132f40fda4129c803e7dcec3a6b6a6fbb44ca90b081630b810b5d6a41a @@ -20529,6 +21434,15 @@ __metadata: languageName: node linkType: hard +"napi-postinstall@npm:^0.3.0": + version: 0.3.4 + resolution: "napi-postinstall@npm:0.3.4" + bin: + napi-postinstall: lib/cli.js + checksum: 10/5541381508f9e1051ff3518701c7130ebac779abb3a1ffe9391fcc3cab4cc0569b0ba0952357db3f6b12909c3bb508359a7a60261ffd795feebbdab967175832 + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -20832,7 +21746,7 @@ __metadata: languageName: node linkType: hard -"nwsapi@npm:^2.2.2": +"nwsapi@npm:^2.2.16": version: 2.2.23 resolution: "nwsapi@npm:2.2.23" checksum: 10/aa4a570039c33d70b51436d1bb533f3e2c33c488ccbe9b09285c46a6cee5ef266fd60103461085c6954ba52460786a8138f042958328c7c1b4763898eb3dadfa @@ -21427,7 +22341,7 @@ __metadata: languageName: node linkType: hard -"parse5@npm:^7.0.0, parse5@npm:^7.1.1, parse5@npm:^7.3.0": +"parse5@npm:^7.0.0, parse5@npm:^7.2.1, parse5@npm:^7.3.0": version: 7.3.0 resolution: "parse5@npm:7.3.0" dependencies: @@ -21600,10 +22514,10 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^4.0.4": - version: 4.0.4 - resolution: "picomatch@npm:4.0.4" - checksum: 10/f6ef80a3590827ce20378ae110ac78209cc4f74d39236370f1780f957b7ee41c12acde0e4651b90f39983506fd2f5e449994716f516db2e9752924aff8de93ce +"picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": + version: 4.0.5 + resolution: "picomatch@npm:4.0.5" + checksum: 10/8bad770af9dcdb7f94ad1a893adcbe08a97d75a18872f8fed37161d3124217471c402b3929f051532f795f4ff2e53dcebb1644df4c1a5f3a9c476fef3b9f8c51 languageName: node linkType: hard @@ -21614,10 +22528,10 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.4": - version: 4.0.6 - resolution: "pirates@npm:4.0.6" - checksum: 10/d02dda76f4fec1cbdf395c36c11cf26f76a644f9f9a1bfa84d3167d0d3154d5289aacc72677aa20d599bb4a6937a471de1b65c995e2aea2d8687cbcd7e43ea5f +"pirates@npm:^4.0.4, pirates@npm:^4.0.7": + version: 4.0.7 + resolution: "pirates@npm:4.0.7" + checksum: 10/2427f371366081ae42feb58214f04805d6b41d6b84d74480ebcc9e0ddbd7105a139f7c653daeaf83ad8a1a77214cf07f64178e76de048128fec501eab3305a96 languageName: node linkType: hard @@ -22514,15 +23428,6 @@ __metadata: languageName: node linkType: hard -"prettier-2@npm:prettier@^2.8.8": - version: 2.8.8 - resolution: "prettier@npm:2.8.8" - bin: - prettier: bin-prettier.js - checksum: 10/00cdb6ab0281f98306cd1847425c24cbaaa48a5ff03633945ab4c701901b8e96ad558eb0777364ffc312f437af9b5a07d0f45346266e8245beaf6247b9c62b24 - languageName: node - linkType: hard - "prettier-linter-helpers@npm:^1.0.0": version: 1.0.0 resolution: "prettier-linter-helpers@npm:1.0.0" @@ -22551,6 +23456,18 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:30.4.1, pretty-format@npm:^30.0.0": + version: 30.4.1 + resolution: "pretty-format@npm:30.4.1" + dependencies: + "@jest/schemas": "npm:30.4.1" + ansi-styles: "npm:^5.2.0" + react-is-18: "npm:react-is@^18.3.1" + react-is-19: "npm:react-is@^19.2.5" + checksum: 10/60311ef47a646eeaec0432efe66290cb6f0d2eccb123a28ad4ab6d7e53087bc62db91cfd54c3cc00c89d6875aefb2bf6264381b6c9411ce6bff3d6aa8280abad + languageName: node + linkType: hard + "pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" @@ -22715,15 +23632,6 @@ __metadata: languageName: node linkType: hard -"psl@npm:^1.1.33": - version: 1.15.0 - resolution: "psl@npm:1.15.0" - dependencies: - punycode: "npm:^2.3.1" - checksum: 10/5e7467eb5196eb7900d156783d12907d445c0122f76c73203ce96b148a6ccf8c5450cc805887ffada38ff92d634afcf33720c24053cb01d5b6598d1c913c5caf - languageName: node - linkType: hard - "pump@npm:^3.0.0": version: 3.0.4 resolution: "pump@npm:3.0.4" @@ -22764,6 +23672,13 @@ __metadata: languageName: node linkType: hard +"pure-rand@npm:^7.0.0": + version: 7.0.1 + resolution: "pure-rand@npm:7.0.1" + checksum: 10/c61a576fda5032ec9763ecb000da4a8f19263b9e2f9ae9aa2759c8fbd9dc6b192b2ce78391ebd41abb394a5fedb7bcc4b03c9e6141ac8ab20882dd5717698b80 + languageName: node + linkType: hard + "pvtsutils@npm:^1.3.6": version: 1.3.6 resolution: "pvtsutils@npm:1.3.6" @@ -22789,13 +23704,6 @@ __metadata: languageName: node linkType: hard -"querystringify@npm:^2.1.1": - version: 2.2.0 - resolution: "querystringify@npm:2.2.0" - checksum: 10/46ab16f252fd892fc29d6af60966d338cdfeea68a231e9457631ffd22d67cec1e00141e0a5236a2eb16c0d7d74175d9ec1d6f963660c6f2b1c2fc85b194c5680 - languageName: node - linkType: hard - "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -22912,6 +23820,20 @@ __metadata: languageName: node linkType: hard +"react-is-18@npm:react-is@^18.3.1, react-is@npm:^18.0.0": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: 10/d5f60c87d285af24b1e1e7eaeb123ec256c3c8bdea7061ab3932e3e14685708221bf234ec50b21e10dd07f008f1b966a2730a0ce4ff67905b3872ff2042aec22 + languageName: node + linkType: hard + +"react-is-19@npm:react-is@^19.2.5": + version: 19.2.7 + resolution: "react-is@npm:19.2.7" + checksum: 10/ae0d3ae7638aa2fa2a82a78a817daf2806e57fa0aef357d07e1e8d1e9a301902e5967168e9334b5816db0df8fa980a725cd57569ba5a9e6e76a71e117b18be04 + languageName: node + linkType: hard + "react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -22919,13 +23841,6 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.0.0": - version: 18.3.1 - resolution: "react-is@npm:18.3.1" - checksum: 10/d5f60c87d285af24b1e1e7eaeb123ec256c3c8bdea7061ab3932e3e14685708221bf234ec50b21e10dd07f008f1b966a2730a0ce4ff67905b3872ff2042aec22 - languageName: node - linkType: hard - "react-json-view-lite@npm:^2.3.0": version: 2.5.0 resolution: "react-json-view-lite@npm:2.5.0" @@ -23569,6 +24484,13 @@ __metadata: languageName: node linkType: hard +"rrweb-cssom@npm:^0.8.0": + version: 0.8.0 + resolution: "rrweb-cssom@npm:0.8.0" + checksum: 10/07521ee36fb6569c17906afad1ac7ff8f099d49ade9249e190693ac36cdf27f88d9acf0cc66978935d5d0a23fca105643d7e9125b9a9d91ed9db9e02d31d7d80 + languageName: node + linkType: hard + "rtlcss@npm:^4.1.0": version: 4.3.0 resolution: "rtlcss@npm:4.3.0" @@ -23760,12 +24682,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.1, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.3, semver@npm:^7.7.3, semver@npm:^7.8.1": - version: 7.8.2 - resolution: "semver@npm:7.8.2" +"semver@npm:^7.1.1, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.3, semver@npm:^7.7.2, semver@npm:^7.7.3, semver@npm:^7.8.0, semver@npm:^7.8.1": + version: 7.8.5 + resolution: "semver@npm:7.8.5" bin: semver: bin/semver.js - checksum: 10/52221d8f1cadacda3cc3f0a2e7f7146e0442c7f4219acb25970bed055f5d0a6afbba5f22e293b078c2e93fca3dce0a08b088485e8b75d32a165f16c3627091c8 + checksum: 10/9b01d2ff11e6e4a4539b7ca3c5f280c8704cb397a28504469f2ed4f00ad2194748d756647362a9712fff30984d15772ab7f083108c2fb508e2096ae9e708f22c languageName: node linkType: hard @@ -24226,7 +25148,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": +"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0": version: 0.6.1 resolution: "source-map@npm:0.6.1" checksum: 10/59ef7462f1c29d502b3057e822cdbdae0b0e565302c4dd1a95e11e793d8d9d62006cdc10e0fd99163ca33ff2071360cf50ee13f90440806e7ed57d81cba2f7ff @@ -24355,7 +25277,7 @@ __metadata: languageName: node linkType: hard -"stack-utils@npm:^2.0.3": +"stack-utils@npm:^2.0.3, stack-utils@npm:^2.0.6": version: 2.0.6 resolution: "stack-utils@npm:2.0.6" dependencies: @@ -24400,7 +25322,7 @@ __metadata: languageName: node linkType: hard -"string-length@npm:^4.0.1": +"string-length@npm:^4.0.1, string-length@npm:^4.0.2": version: 4.0.2 resolution: "string-length@npm:4.0.2" dependencies: @@ -24613,7 +25535,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8, supports-color@npm:^8.0.0": +"supports-color@npm:^8, supports-color@npm:^8.0.0, supports-color@npm:^8.1.1": version: 8.1.1 resolution: "supports-color@npm:8.1.1" dependencies: @@ -24660,6 +25582,15 @@ __metadata: languageName: node linkType: hard +"synckit@npm:^0.11.8": + version: 0.11.12 + resolution: "synckit@npm:0.11.12" + dependencies: + "@pkgr/core": "npm:^0.2.9" + checksum: 10/2f51978bfed81aaf0b093f596709a72c49b17909020f42b43c5549f9c0fe18b1fe29f82e41ef771172d729b32e9ce82900a85d2b87fa14d59f886d4df8d7a329 + languageName: node + linkType: hard + "synckit@npm:^0.9.1": version: 0.9.2 resolution: "synckit@npm:0.9.2" @@ -24868,6 +25799,24 @@ __metadata: languageName: node linkType: hard +"tldts-core@npm:^6.1.86": + version: 6.1.86 + resolution: "tldts-core@npm:6.1.86" + checksum: 10/cb5dff9cc15661ac773a2099e98c99a5cb3cebc35909c23cc4261ff7992032c7501995ae995de3574dbbf3431e59c47496534d52f5e96abcb231f0e72144c020 + languageName: node + linkType: hard + +"tldts@npm:^6.1.32": + version: 6.1.86 + resolution: "tldts@npm:6.1.86" + dependencies: + tldts-core: "npm:^6.1.86" + bin: + tldts: bin/cli.js + checksum: 10/f7e66824e44479ccdda55ea556af14ce61c4d27708be403e3f90631defde49f82a580e1ca07187cc7e3b349e257a30c2808a22903f3a0548e136ebb609ccc109 + languageName: node + linkType: hard + "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -24898,24 +25847,21 @@ __metadata: languageName: node linkType: hard -"tough-cookie@npm:^4.1.2": - version: 4.1.4 - resolution: "tough-cookie@npm:4.1.4" +"tough-cookie@npm:^5.1.1": + version: 5.1.2 + resolution: "tough-cookie@npm:5.1.2" dependencies: - psl: "npm:^1.1.33" - punycode: "npm:^2.1.1" - universalify: "npm:^0.2.0" - url-parse: "npm:^1.5.3" - checksum: 10/75663f4e2cd085f16af0b217e4218772adf0617fb3227171102618a54ce0187a164e505d61f773ed7d65988f8ff8a8f935d381f87da981752c1171b076b4afac + tldts: "npm:^6.1.32" + checksum: 10/de430e6e6d34b794137e05b8ac2aa6b74ebbe6cdceb4126f168cf1e76101162a4b2e0e7587c3b70e728bd8654fc39958b2035be7619ee6f08e7257610ba4cd04 languageName: node linkType: hard -"tr46@npm:^3.0.0": - version: 3.0.0 - resolution: "tr46@npm:3.0.0" +"tr46@npm:^5.1.0": + version: 5.1.1 + resolution: "tr46@npm:5.1.1" dependencies: - punycode: "npm:^2.1.1" - checksum: 10/b09a15886cbfaee419a3469081223489051ce9dca3374dd9500d2378adedbee84a3c73f83bfdd6bb13d53657753fc0d4e20a46bfcd3f1b9057ef528426ad7ce4 + punycode: "npm:^2.3.1" + checksum: 10/833a0e1044574da5790148fd17866d4ddaea89e022de50279967bcd6b28b4ce0d30d59eb3acf9702b60918975b3bad481400337e3a2e6326cffa5c77b874753d languageName: node linkType: hard @@ -24958,17 +25904,17 @@ __metadata: languageName: node linkType: hard -"ts-jest@npm:^29.2.5": - version: 29.4.6 - resolution: "ts-jest@npm:29.4.6" +"ts-jest@npm:^29.2.5, ts-jest@npm:^29.4.11": + version: 29.4.11 + resolution: "ts-jest@npm:29.4.11" dependencies: bs-logger: "npm:^0.2.6" fast-json-stable-stringify: "npm:^2.1.0" - handlebars: "npm:^4.7.8" + handlebars: "npm:^4.7.9" json5: "npm:^2.2.3" lodash.memoize: "npm:^4.1.2" make-error: "npm:^1.3.6" - semver: "npm:^7.7.3" + semver: "npm:^7.8.0" type-fest: "npm:^4.41.0" yargs-parser: "npm:^21.1.1" peerDependencies: @@ -24978,7 +25924,7 @@ __metadata: babel-jest: ^29.0.0 || ^30.0.0 jest: ^29.0.0 || ^30.0.0 jest-util: ^29.0.0 || ^30.0.0 - typescript: ">=4.3 <6" + typescript: ">=4.3 <7" peerDependenciesMeta: "@babel/core": optional: true @@ -24994,7 +25940,7 @@ __metadata: optional: true bin: ts-jest: cli.js - checksum: 10/e0ff9e13f684166d5331808b288043b8054f49a1c2970480a92ba3caec8d0ff20edd092f2a4e7a3ad8fcb9ba4d674bee10ec7ee75046d8066bbe43a7d16cf72e + checksum: 10/8a76800cac1c102c789429a82655adde1d540d7ff025fc888006f4f458afe07ca4d3f73622a10a604edea06aa12c298605cd1a208a6380c0e3d591a3daed8af6 languageName: node linkType: hard @@ -25383,13 +26329,6 @@ __metadata: languageName: node linkType: hard -"universalify@npm:^0.2.0": - version: 0.2.0 - resolution: "universalify@npm:0.2.0" - checksum: 10/e86134cb12919d177c2353196a4cc09981524ee87abf621f7bc8d249dbbbebaec5e7d1314b96061497981350df786e4c5128dbf442eba104d6e765bc260678b5 - languageName: node - linkType: hard - "universalify@npm:^2.0.0": version: 2.0.1 resolution: "universalify@npm:2.0.1" @@ -25404,6 +26343,73 @@ __metadata: languageName: node linkType: hard +"unrs-resolver@npm:^1.7.11": + version: 1.11.1 + resolution: "unrs-resolver@npm:1.11.1" + dependencies: + "@unrs/resolver-binding-android-arm-eabi": "npm:1.11.1" + "@unrs/resolver-binding-android-arm64": "npm:1.11.1" + "@unrs/resolver-binding-darwin-arm64": "npm:1.11.1" + "@unrs/resolver-binding-darwin-x64": "npm:1.11.1" + "@unrs/resolver-binding-freebsd-x64": "npm:1.11.1" + "@unrs/resolver-binding-linux-arm-gnueabihf": "npm:1.11.1" + "@unrs/resolver-binding-linux-arm-musleabihf": "npm:1.11.1" + "@unrs/resolver-binding-linux-arm64-gnu": "npm:1.11.1" + "@unrs/resolver-binding-linux-arm64-musl": "npm:1.11.1" + "@unrs/resolver-binding-linux-ppc64-gnu": "npm:1.11.1" + "@unrs/resolver-binding-linux-riscv64-gnu": "npm:1.11.1" + "@unrs/resolver-binding-linux-riscv64-musl": "npm:1.11.1" + "@unrs/resolver-binding-linux-s390x-gnu": "npm:1.11.1" + "@unrs/resolver-binding-linux-x64-gnu": "npm:1.11.1" + "@unrs/resolver-binding-linux-x64-musl": "npm:1.11.1" + "@unrs/resolver-binding-wasm32-wasi": "npm:1.11.1" + "@unrs/resolver-binding-win32-arm64-msvc": "npm:1.11.1" + "@unrs/resolver-binding-win32-ia32-msvc": "npm:1.11.1" + "@unrs/resolver-binding-win32-x64-msvc": "npm:1.11.1" + napi-postinstall: "npm:^0.3.0" + dependenciesMeta: + "@unrs/resolver-binding-android-arm-eabi": + optional: true + "@unrs/resolver-binding-android-arm64": + optional: true + "@unrs/resolver-binding-darwin-arm64": + optional: true + "@unrs/resolver-binding-darwin-x64": + optional: true + "@unrs/resolver-binding-freebsd-x64": + optional: true + "@unrs/resolver-binding-linux-arm-gnueabihf": + optional: true + "@unrs/resolver-binding-linux-arm-musleabihf": + optional: true + "@unrs/resolver-binding-linux-arm64-gnu": + optional: true + "@unrs/resolver-binding-linux-arm64-musl": + optional: true + "@unrs/resolver-binding-linux-ppc64-gnu": + optional: true + "@unrs/resolver-binding-linux-riscv64-gnu": + optional: true + "@unrs/resolver-binding-linux-riscv64-musl": + optional: true + "@unrs/resolver-binding-linux-s390x-gnu": + optional: true + "@unrs/resolver-binding-linux-x64-gnu": + optional: true + "@unrs/resolver-binding-linux-x64-musl": + optional: true + "@unrs/resolver-binding-wasm32-wasi": + optional: true + "@unrs/resolver-binding-win32-arm64-msvc": + optional: true + "@unrs/resolver-binding-win32-ia32-msvc": + optional: true + "@unrs/resolver-binding-win32-x64-msvc": + optional: true + checksum: 10/4de653508cbaae47883a896bd5cdfef0e5e87b428d62620d16fd35cd534beaebf08ebf0cf2f8b4922aa947b2fe745180facf6cc3f39ba364f7ce0f974cb06a70 + languageName: node + linkType: hard + "unzipper@npm:^0.12.3": version: 0.12.3 resolution: "unzipper@npm:0.12.3" @@ -25479,25 +26485,6 @@ __metadata: languageName: node linkType: hard -"url-parse@npm:^1.5.3": - version: 1.5.10 - resolution: "url-parse@npm:1.5.10" - dependencies: - querystringify: "npm:^2.1.1" - requires-port: "npm:^1.0.0" - checksum: 10/c9e96bc8c5b34e9f05ddfeffc12f6aadecbb0d971b3cc26015b58d5b44676a99f50d5aeb1e5c9e61fa4d49961ae3ab1ae997369ed44da51b2f5ac010d188e6ad - languageName: node - linkType: hard - -"use-sync-external-store@npm:^1.6.0": - version: 1.6.0 - resolution: "use-sync-external-store@npm:1.6.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10/b40ad2847ba220695bff2d4ba4f4d60391c0fb4fb012faa7a4c18eb38b69181936f5edc55a522c4d20a788d1a879b73c3810952c9d0fd128d01cb3f22042c09e - languageName: node - linkType: hard - "utf8@npm:^3.0.0": version: 3.0.0 resolution: "utf8@npm:3.0.0" @@ -25677,12 +26664,12 @@ __metadata: languageName: node linkType: hard -"w3c-xmlserializer@npm:^4.0.0": - version: 4.0.0 - resolution: "w3c-xmlserializer@npm:4.0.0" +"w3c-xmlserializer@npm:^5.0.0": + version: 5.0.0 + resolution: "w3c-xmlserializer@npm:5.0.0" dependencies: - xml-name-validator: "npm:^4.0.0" - checksum: 10/9a00c412b5496f4f040842c9520bc0aaec6e0c015d06412a91a723cd7d84ea605ab903965f546b4ecdb3eae267f5145ba08565222b1d6cb443ee488cda9a0aee + xml-name-validator: "npm:^5.0.0" + checksum: 10/d78f59e6b4f924aa53b6dfc56949959229cae7fe05ea9374eb38d11edcec01398b7f5d7a12576bd5acc57ff446abb5c9115cd83b9d882555015437cf858d42f0 languageName: node linkType: hard @@ -25954,15 +26941,6 @@ __metadata: languageName: node linkType: hard -"whatwg-encoding@npm:^2.0.0": - version: 2.0.0 - resolution: "whatwg-encoding@npm:2.0.0" - dependencies: - iconv-lite: "npm:0.6.3" - checksum: 10/162d712d88fd134a4fe587e53302da812eb4215a1baa4c394dfd86eff31d0a079ff932c05233857997de07481093358d6e7587997358f49b8a580a777be22089 - languageName: node - linkType: hard - "whatwg-encoding@npm:^3.1.1": version: 3.1.1 resolution: "whatwg-encoding@npm:3.1.1" @@ -25979,13 +26957,6 @@ __metadata: languageName: node linkType: hard -"whatwg-mimetype@npm:^3.0.0": - version: 3.0.0 - resolution: "whatwg-mimetype@npm:3.0.0" - checksum: 10/96f9f628c663c2ae05412c185ca81b3df54bcb921ab52fe9ebc0081c1720f25d770665401eb2338ab7f48c71568133845638e18a81ed52ab5d4dcef7d22b40ef - languageName: node - linkType: hard - "whatwg-mimetype@npm:^4.0.0": version: 4.0.0 resolution: "whatwg-mimetype@npm:4.0.0" @@ -25993,13 +26964,13 @@ __metadata: languageName: node linkType: hard -"whatwg-url@npm:^11.0.0": - version: 11.0.0 - resolution: "whatwg-url@npm:11.0.0" +"whatwg-url@npm:^14.0.0, whatwg-url@npm:^14.1.1": + version: 14.2.0 + resolution: "whatwg-url@npm:14.2.0" dependencies: - tr46: "npm:^3.0.0" + tr46: "npm:^5.1.0" webidl-conversions: "npm:^7.0.0" - checksum: 10/dfcd51c6f4bfb54685528fb10927f3fd3d7c809b5671beef4a8cdd7b1408a7abf3343a35bc71dab83a1424f1c1e92cc2700d7930d95d231df0fac361de0c7648 + checksum: 10/f0a95b0601c64f417c471536a2d828b4c16fe37c13662483a32f02f183ed0f441616609b0663fb791e524e8cd56d9a86dd7366b1fc5356048ccb09b576495e7c languageName: node linkType: hard @@ -26172,7 +27143,7 @@ __metadata: languageName: node linkType: hard -"write-file-atomic@npm:^5.0.0": +"write-file-atomic@npm:^5.0.0, write-file-atomic@npm:^5.0.1": version: 5.0.1 resolution: "write-file-atomic@npm:5.0.1" dependencies: @@ -26242,7 +27213,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0, ws@npm:^8.18.0, ws@npm:^8.18.3": +"ws@npm:^8.18.0, ws@npm:^8.18.3": version: 8.21.0 resolution: "ws@npm:8.21.0" peerDependencies: @@ -26291,10 +27262,10 @@ __metadata: languageName: node linkType: hard -"xml-name-validator@npm:^4.0.0": - version: 4.0.0 - resolution: "xml-name-validator@npm:4.0.0" - checksum: 10/f9582a3f281f790344a471c207516e29e293c6041b2c20d84dd6e58832cd7c19796c47e108fd4fd4b164a5e72ad94f2268f8ace8231cde4a2c6428d6aa220f92 +"xml-name-validator@npm:^5.0.0": + version: 5.0.0 + resolution: "xml-name-validator@npm:5.0.0" + checksum: 10/43f30f3f6786e406dd665acf08cd742d5f8a46486bd72517edb04b27d1bcd1599664c2a4a99fc3f1e56a3194bff588b12f178b7972bc45c8047bdc4c3ac8d4a1 languageName: node linkType: hard @@ -26373,7 +27344,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.0.1, yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": +"yargs@npm:17.7.2, yargs@npm:^17.0.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: @@ -26406,6 +27377,21 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^17.3.1": + version: 17.7.3 + resolution: "yargs@npm:17.7.3" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10/a3826798c03b159e139d0580a3b2733953889a9a1bac8e4e1ca7a1a249b55315b213c323a6a1dbdb305f6e59496a9eaa810742c87e34abcf1a0584d8f59212a1 + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" From 3de671394bb36667d04d8d6428e53dafb106cdd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 18:51:01 +0200 Subject: [PATCH 08/19] fix: issue with old accessToken and state update while controller has been reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- .../kyc-controller/src/KycController.test.ts | 78 +++++++++++++++++++ packages/kyc-controller/src/KycController.ts | 29 +++++++ 2 files changed, 107 insertions(+) diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index e62d5a3dbe0..50328ab9430 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -257,6 +257,46 @@ describe('KycController', () => { ); }); + it('clears stale auth tokens when a new session is created', async () => { + await withController( + { + options: { + state: { + email: 'a@b.co', + sessionToken: 'old-session', + accessToken: 'stale-access', + disclaimers: [{ id: '1', display_name: 'T', url: 'u' }], + }, + }, + }, + async ({ controller, handlers }) => { + handlers.createSession.mockResolvedValue({ + sessionToken: 'new-session', + }); + + // Establish an auth-frame client token from a prior authentication. + const envelope = envelopeFor(controller, { clientToken: 'old-client' }); + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'connectionRequired', credentials: envelope }, + }, + }); + expect(controller.buildAuthFrameUrl()).toContain( + 'clientToken=old-client', + ); + + // Creating a new session must invalidate the carried-over auth. + await controller.acceptTermsAndStartSession(); + + expect(controller.state.accessToken).toBeNull(); + expect(controller.buildAuthFrameUrl()).toBeNull(); + expect(controller.state.sessionToken).toBe('new-session'); + }, + ); + }); + it('reverts to terms when session creation fails', async () => { await withController( { @@ -779,6 +819,44 @@ describe('KycController', () => { }, ); }); + + it('discards a successful result when reset() runs while the check is in flight', async () => { + await withController( + { options: { state: { accessToken: 'a', geoCountry: 'USA' } } }, + async ({ controller, handlers }) => { + handlers.checkKycRequired.mockImplementation(async () => { + // Simulate a reset() landing while the HTTP call is in flight. + controller.reset(); + return { kycRequired: true }; + }); + + const result = await controller.checkKycRequired({ product: 'ramps' }); + + expect(result).toBe(false); + expect(controller.state.phase).toBe('idle'); + expect(controller.state.kycRequiredByProduct.ramps).toBeUndefined(); + expect(controller.state.lastCheckedAt).toBeNull(); + }, + ); + }); + + it('discards an error when reset() runs while the check is in flight', async () => { + await withController( + { options: { state: { accessToken: 'a', geoCountry: 'USA' } } }, + async ({ controller, handlers }) => { + handlers.checkKycRequired.mockImplementation(async () => { + controller.reset(); + throw new Error('down'); + }); + + const result = await controller.checkKycRequired({ product: 'ramps' }); + + expect(result).toBe(false); + expect(controller.state.phase).toBe('idle'); + expect(controller.state.error).toBeNull(); + }, + ); + }); }); describe('getKycStatus', () => { diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index 08696b5a089..9b26fa728e0 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -323,6 +323,13 @@ export class KycController extends BaseController< /** Auth-frame client token, kept out of state. */ #authClientToken: string | null = null; + /** + * Monotonic flow generation. Incremented by {@link reset} so in-flight async + * work (e.g. the KYC-required check) can detect that it was superseded and + * avoid writing stale results onto a reset controller. + */ + #generation = 0; + /** * Constructs a new {@link KycController}. * @@ -477,10 +484,18 @@ export class KycController extends BaseController< return; } + // A new session invalidates any authentication carried over from a prior + // session. Clear the stale access token and auth-frame client token so + // `buildAuthFrameUrl` cannot return a URL tied to an old client token and + // `checkKycRequired` cannot run with an access token from an earlier + // authentication. The Check/Auth frames re-populate these for the new + // session. + this.#authClientToken = null; this.update((state) => { state.error = null; state.phase = 'session'; state.statusMessage = 'Creating session...'; + state.accessToken = null; }); try { @@ -763,6 +778,10 @@ export class KycController extends BaseController< return false; } + // Capture the flow generation so we can detect a `reset()` that happens + // while the HTTP call is in flight and avoid writing stale results. + const generation = this.#generation; + this.update((state) => { state.phase = 'submit'; state.statusMessage = 'Checking KYC status...'; @@ -773,6 +792,11 @@ export class KycController extends BaseController< 'KycService:checkKycRequired', { accessToken, country, capabilities: [{ product: params.product }] }, ); + // The flow was reset while the check was in flight; discard the result + // rather than resurrecting a done/cached state on an idle controller. + if (this.#generation !== generation) { + return false; + } this.update((state) => { state.kycRequiredByProduct[params.product] = kycRequired; state.lastCheckedAt = new Date().toISOString(); @@ -781,6 +805,9 @@ export class KycController extends BaseController< }); return kycRequired; } catch (error) { + if (this.#generation !== generation) { + return false; + } this.#fail(`KYC check failed: ${String(error)}`); return false; } @@ -911,6 +938,8 @@ export class KycController extends BaseController< */ reset(): void { this.#authClientToken = null; + // Invalidate any in-flight async work started before this reset. + this.#generation += 1; this.update((state) => { state.phase = 'idle'; state.statusMessage = ''; From 49ec4f1d71765b0315c17c323e6b9de115e9b961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 22:12:20 +0200 Subject: [PATCH 09/19] fix: another race condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- .../kyc-controller/src/KycController.test.ts | 43 +++++++++++++++++++ packages/kyc-controller/src/KycController.ts | 40 +++++++++++++++-- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index 50328ab9430..5c87b6fc75a 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -961,6 +961,49 @@ describe('KycController', () => { expect(controller.state.sumsub.status).toBe('failed'); }); }); + + it('aborts without launching the SDK when reset() runs while in flight', async () => { + await withController(async ({ controller, handlers, launcher }) => { + // Simulate a reset() landing while the UKYC session is being created. + handlers.createUkycSession.mockImplementation(async () => { + controller.reset(); + return { + sessionId: 'sid', + wrappingPublicKey: 'wpk', + idosSessionId: 'idos', + }; + }); + + const result = await controller.startSumSub(); + + expect(result).toStrictEqual({}); + expect(launcher.launch).not.toHaveBeenCalled(); + // The interrupted step must not write stale sub-flow state. + expect(controller.state.sumsub.status).toBe('idle'); + expect(controller.state.sumsub.sessionId).toBeNull(); + expect(controller.state.phase).toBe('idle'); + }); + }); + + it('suppresses status and terminal writes when reset() runs during the SDK launch', async () => { + await withController(async ({ controller, launcher }) => { + launcher.launch.mockImplementation(async ({ onStatusChange }) => { + // First status arrives on the active flow, then a reset() lands and + // a later status + the resolved result must not resurrect state. + onStatusChange?.('idle', 'InProgress'); + controller.reset(); + onStatusChange?.('InProgress', 'Completed'); + return { ok: true }; + }); + + const result = await controller.startSumSub(); + + expect(result).toStrictEqual({ ok: true }); + expect(controller.state.sumsub.status).toBe('idle'); + expect(controller.state.sumsub.result).toBeNull(); + expect(controller.state.phase).toBe('idle'); + }); + }); }); describe('reset', () => { diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index 9b26fa728e0..c388338a75a 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -847,6 +847,11 @@ export class KycController extends BaseController< throw new Error(error); } + // Capture the flow generation so each async step can detect a `reset()` + // that lands mid-flight and avoid writing stale sub-flow state (or, worse, + // presenting the SDK) on a controller that is now idle. + const generation = this.#generation; + try { this.update((state) => { state.sumsub.status = 'creatingSession'; @@ -872,7 +877,7 @@ export class KycController extends BaseController< jwtToken, }; - this.update((state) => { + this.#updateIfCurrent(generation, (state) => { state.sumsub.status = 'fetchingToken'; state.sumsub.sessionId = sessionId; }); @@ -882,6 +887,12 @@ export class KycController extends BaseController< exchange, ); + // A reset() landed while the session/token was being prepared; abort + // before presenting the SDK rather than launching on an idle controller. + if (this.#generation !== generation) { + return {}; + } + this.update((state) => { state.sumsub.status = 'launching'; state.sumsub.applicantAccessToken = applicantAccessToken; @@ -905,7 +916,7 @@ export class KycController extends BaseController< if (next === SUMSUB_COMPLETED_STATUS) { reachedCompletion = true; } - this.update((state) => { + this.#updateIfCurrent(generation, (state) => { state.sumsub.status = next === SUMSUB_COMPLETED_STATUS ? 'complete' : 'inProgress'; }); @@ -917,14 +928,14 @@ export class KycController extends BaseController< // Only record `complete` when the SDK actually reported completion; // otherwise treat the resolved-but-unfinished flow as `failed` so // consumers and UI do not mistake it for a finished verification. - this.update((state) => { + this.#updateIfCurrent(generation, (state) => { state.sumsub.status = reachedCompletion ? 'complete' : 'failed'; state.sumsub.result = result as Json; }); return result; } catch (error) { const result = { error: String(error) }; - this.update((state) => { + this.#updateIfCurrent(generation, (state) => { state.sumsub.status = 'failed'; state.sumsub.result = result; }); @@ -959,6 +970,27 @@ export class KycController extends BaseController< }); } + /** + * Applies a state update only when the flow has not been reset since + * `generation` was captured. Prevents an in-flight async step from writing + * stale results onto a controller that a concurrent {@link reset} has + * returned to idle. + * + * @param generation - The flow generation captured before the async work. + * @param updater - The state mutation to apply when still current. + * @returns `true` if the update was applied, `false` if it was superseded. + */ + #updateIfCurrent( + generation: number, + updater: (state: KycControllerState) => void, + ): boolean { + if (this.#generation !== generation) { + return false; + } + this.update(updater); + return true; + } + /** * Transitions to the error phase with a message. * From 20cff98d95f94a316a81271375efd6590668940c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Thu, 23 Jul 2026 23:15:04 +0200 Subject: [PATCH 10/19] fix: add links in changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- packages/kyc-controller/CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kyc-controller/CHANGELOG.md b/packages/kyc-controller/CHANGELOG.md index 22e08cf4f2a..29f5a436616 100644 --- a/packages/kyc-controller/CHANGELOG.md +++ b/packages/kyc-controller/CHANGELOG.md @@ -9,13 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial release of `KycController` and `KycService`, a shared, platform-agnostic KYC / identity-verification controller used across MetaMask clients +- Initial release of `KycController` and `KycService`, a shared, platform-agnostic KYC / identity-verification controller used across MetaMask clients ([#9615](https://github.com/MetaMask/core/pull/9615)) - `KycController` (`BaseController`) owns the flow state machine, the Check/Auth frame message protocol, X25519 credential decryption, and SumSub orchestration via an injected `KycSumSubLauncher` adapter. - `KycService` performs the Universal KYC (UKYC) HTTP calls via an injected `fetch`, sourcing the auth bearer token and geolocation through the messenger. - Exposes a vendor-neutral, per-product surface (`ramps`, `card`) plus reselect selectors. -- Add automatic post-authentication continuation to `KycController` +- Add automatic post-authentication continuation to `KycController` ([#9615](https://github.com/MetaMask/core/pull/9615)) - `initialize` and `acceptTermsAndStartSession` now accept an optional `product` (`ramps` | `card`), tracked in new `activeProduct` state. - When a `product` is set, reaching the `form` phase automatically runs the KYC-required check and, when KYC is required, launches the SumSub document-verification sub-flow — no extra `checkKycRequired` / `startSumSub` calls needed. When no `product` is set, the flow stops at `form` for the consumer to drive manually (unchanged behavior). -- Add optional `baseUrl` option to `KycService` constructor that overrides the base URL derived from `env`, enabling clients to target a custom (e.g. local or staging) KYC API +- Add optional `baseUrl` option to `KycService` constructor that overrides the base URL derived from `env`, enabling clients to target a custom (e.g. local or staging) KYC API ([#9615](https://github.com/MetaMask/core/pull/9615)) [Unreleased]: https://github.com/MetaMask/core/ From abc47a6b6507299416c151608889ac15285f3f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Fri, 24 Jul 2026 10:46:30 +0200 Subject: [PATCH 11/19] fix: lint issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- packages/kyc-controller/src/KycService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kyc-controller/src/KycService.ts b/packages/kyc-controller/src/KycService.ts index 48117c65407..fec9a4baa2b 100644 --- a/packages/kyc-controller/src/KycService.ts +++ b/packages/kyc-controller/src/KycService.ts @@ -208,9 +208,8 @@ export class KycService { const location = await this.#messenger.call( 'GeolocationController:getGeolocation', ); - const alpha2 = String(location ?? '') - .split('-')[0] - .toUpperCase(); + assert(location, string()); + const alpha2 = location.split('-')[0].toUpperCase(); if (!alpha2 || alpha2 === 'UNKNOWN') { throw new Error( `Unable to determine country from geolocation (got "${location}").`, @@ -390,6 +389,7 @@ export class KycService { const bearerToken = await this.#messenger.call( 'AuthenticationController:getBearerToken', ); + assert(bearerToken, string()); if (!bearerToken) { throw new Error( 'Unable to obtain an authentication bearer token — is the wallet signed in?', From 461473d2aed46c1709506ec71e6bb83f99d94900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Fri, 24 Jul 2026 11:06:52 +0200 Subject: [PATCH 12/19] fix: dedupe dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- yarn.lock | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/yarn.lock b/yarn.lock index a78434321db..f8b525f11af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14067,20 +14067,13 @@ __metadata: languageName: node linkType: hard -"cjs-module-lexer@npm:^1.0.0": +"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.3.1": version: 1.4.3 resolution: "cjs-module-lexer@npm:1.4.3" checksum: 10/d2b92f919a2dedbfd61d016964fce8da0035f827182ed6839c97cac56e8a8077cfa6a59388adfe2bc588a19cef9bbe830d683a76a6e93c51f65852062cfe2591 languageName: node linkType: hard -"cjs-module-lexer@npm:^1.3.1": - version: 1.4.0 - resolution: "cjs-module-lexer@npm:1.4.0" - checksum: 10/b041096749792526120d8b8756929f8ef5dd4596502a0e1013f857e3027acd6091915fea77037921d70ee1a99988a100d994d3d3c2e323b04dd4c5ffd516cf13 - languageName: node - linkType: hard - "cjs-module-lexer@npm:^2.1.0": version: 2.2.0 resolution: "cjs-module-lexer@npm:2.2.0" @@ -15046,7 +15039,7 @@ __metadata: languageName: node linkType: hard -"dedent@npm:^1.0.0": +"dedent@npm:^1.0.0, dedent@npm:^1.6.0": version: 1.7.2 resolution: "dedent@npm:1.7.2" peerDependencies: @@ -15058,18 +15051,6 @@ __metadata: languageName: node linkType: hard -"dedent@npm:^1.6.0": - version: 1.7.1 - resolution: "dedent@npm:1.7.1" - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - checksum: 10/78785ef592e37e0b1ca7a7a5964c8f3dee1abdff46c5bb49864168579c122328f6bb55c769bc7e005046a7381c3372d3859f0f78ab083950fa146e1c24873f4f - languageName: node - linkType: hard - "deep-extend@npm:^0.6.0": version: 0.6.0 resolution: "deep-extend@npm:0.6.0" @@ -27344,7 +27325,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.0.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": +"yargs@npm:17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: @@ -27377,7 +27358,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.3.1": +"yargs@npm:^17.0.1, yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": version: 17.7.3 resolution: "yargs@npm:17.7.3" dependencies: From 41275fc7b5cf6596c4f58d411e4bc1eabab37db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Fri, 24 Jul 2026 12:17:04 +0200 Subject: [PATCH 13/19] fix: cursor comments + state management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- .../kyc-controller/src/KycController.test.ts | 173 +++++++++++++++++- packages/kyc-controller/src/KycController.ts | 153 ++++++++++++---- packages/kyc-controller/src/KycService.ts | 18 +- 3 files changed, 299 insertions(+), 45 deletions(-) diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index 5c87b6fc75a..5e3e4797c14 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -275,7 +275,9 @@ describe('KycController', () => { }); // Establish an auth-frame client token from a prior authentication. - const envelope = envelopeFor(controller, { clientToken: 'old-client' }); + const envelope = envelopeFor(controller, { + clientToken: 'old-client', + }); await controller.handleFrameMessage({ message: { kind: 'complete', @@ -320,6 +322,31 @@ describe('KycController', () => { ); }); + it('clears the active product when session creation fails', async () => { + await withController( + { + options: { + state: { + email: 'a@b.co', + activeProduct: 'card', + disclaimers: [{ id: '1', display_name: 'T', url: 'u' }], + }, + }, + }, + async ({ controller, handlers }) => { + handlers.createSession.mockRejectedValue(new Error('nope')); + handlers.fetchDisclaimers.mockResolvedValue([]); + + await controller.acceptTermsAndStartSession({ product: 'ramps' }); + + // The failed flow must not leave a lingering product behind that a + // later product-less `acceptTermsAndStartSession` would auto-run. + expect(controller.state.phase).toBe('terms'); + expect(controller.state.activeProduct).toBeNull(); + }, + ); + }); + it('fails when no email is available', async () => { await withController( { @@ -687,6 +714,114 @@ describe('KycController', () => { ); }); + it('ignores a duplicate completion while a prior continuation is in flight', async () => { + await withController( + { + options: { + state: { + sessionToken: 'tok', + activeProduct: 'card', + geoCountry: 'FRA', + }, + }, + }, + async ({ controller, handlers, launcher }) => { + // Hold the KYC-required check open so the first continuation is still + // in flight when the second (duplicate) completion arrives. + let releaseCheck: (value: { kycRequired: boolean }) => void = () => { + // no-op placeholder until the deferred promise is wired up + }; + handlers.checkKycRequired.mockReturnValue( + new Promise<{ kycRequired: boolean }>((resolve) => { + releaseCheck = resolve; + }), + ); + launcher.launch.mockImplementation(async ({ onStatusChange }) => { + onStatusChange?.('InProgress', 'Completed'); + return { ok: true }; + }); + const envelope = envelopeFor(controller, { accessToken: 'access-1' }); + const message = { + kind: 'complete', + meta: { channelId: 'ch_2' }, + payload: { status: 'active', credentials: envelope }, + }; + + const first = controller.handleFrameMessage({ message }); + const second = controller.handleFrameMessage({ message }); + + releaseCheck({ kycRequired: true }); + await Promise.all([first, second]); + + expect(handlers.checkKycRequired).toHaveBeenCalledTimes(1); + expect(launcher.launch).toHaveBeenCalledTimes(1); + expect(controller.state.sumsub.status).toBe('complete'); + }, + ); + }); + + it('allows a fresh flow to continue after a reset interrupts an in-flight continuation', async () => { + await withController( + { + options: { + state: { + sessionToken: 'tok', + activeProduct: 'ramps', + geoCountry: 'USA', + }, + }, + }, + async ({ controller, handlers }) => { + // The keypair is stable across reset, so both envelopes can be built + // up front while the session token (used only to derive the public + // key here) is still present. + const envelope1 = envelopeFor(controller, { + accessToken: 'access-1', + }); + const envelope2 = envelopeFor(controller, { + accessToken: 'access-2', + }); + const messageFor = (credentials: unknown) => ({ + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { status: 'active', credentials }, + }); + + // Hold the first continuation open so a reset can land while it is + // still in flight. + let releaseCheck: (value: { kycRequired: boolean }) => void = () => { + // no-op placeholder until the deferred promise is wired up + }; + handlers.checkKycRequired.mockReturnValueOnce( + new Promise<{ kycRequired: boolean }>((resolve) => { + releaseCheck = resolve; + }), + ); + + const first = controller.handleFrameMessage({ + message: messageFor(envelope1), + }); + + // Reset while the continuation is awaiting the check. Its `finally` + // must not clear the guard (it belongs to the superseded generation); + // `reset` clears it instead. + controller.reset(); + releaseCheck({ kycRequired: false }); + await first; + + // Re-establish a product-scoped flow and confirm the next completion + // continues again rather than being blocked forever by a stuck guard. + await controller.initialize({ email: 'a@b.co', product: 'ramps' }); + handlers.checkKycRequired.mockResolvedValue({ kycRequired: false }); + await controller.handleFrameMessage({ + message: messageFor(envelope2), + }); + + expect(handlers.checkKycRequired).toHaveBeenCalledTimes(2); + }, + ); + }); + it('does not launch verification when the auto-run check fails', async () => { await withController( { @@ -830,7 +965,9 @@ describe('KycController', () => { return { kycRequired: true }; }); - const result = await controller.checkKycRequired({ product: 'ramps' }); + const result = await controller.checkKycRequired({ + product: 'ramps', + }); expect(result).toBe(false); expect(controller.state.phase).toBe('idle'); @@ -849,7 +986,9 @@ describe('KycController', () => { throw new Error('down'); }); - const result = await controller.checkKycRequired({ product: 'ramps' }); + const result = await controller.checkKycRequired({ + product: 'ramps', + }); expect(result).toBe(false); expect(controller.state.phase).toBe('idle'); @@ -985,6 +1124,34 @@ describe('KycController', () => { }); }); + it('refuses to refresh the token via onTokenExpiration after a reset', async () => { + await withController(async ({ controller, handlers, launcher }) => { + let refreshError: unknown; + launcher.launch.mockImplementation(async ({ onTokenExpiration }) => { + // The SDK stays open across a reset, then asks for a fresh token. + controller.reset(); + // Only the initial submitWrappedKey (session setup) should have run. + const callsBeforeRefresh = + handlers.submitWrappedKey.mock.calls.length; + try { + await onTokenExpiration(); + } catch (error) { + refreshError = error; + } + // The refresh must not hit the stale UKYC session. + expect(handlers.submitWrappedKey.mock.calls).toHaveLength( + callsBeforeRefresh, + ); + return { ok: true }; + }); + + await controller.startSumSub(); + + expect(refreshError).toBeInstanceOf(Error); + expect((refreshError as Error).message).toMatch(/flow was reset/u); + }); + }); + it('suppresses status and terminal writes when reset() runs during the SDK launch', async () => { await withController(async ({ controller, launcher }) => { launcher.launch.mockImplementation(async ({ onStatusChange }) => { diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index c388338a75a..95a925e90e4 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -330,6 +330,17 @@ export class KycController extends BaseController< */ #generation = 0; + /** + * Guards the automatic post-authentication continuation. The Check/Auth + * frames can post more than one `complete` message (duplicate or late), each + * of which resolves to an `active` outcome with an access token. Without this + * flag, every such message would re-enter {@link #continueAfterAuthentication} + * and run the KYC-required check / SumSub sub-flow again while a prior run is + * still in flight. Set for the duration of a continuation and cleared by + * {@link reset} so a fresh flow can continue again. + */ + #continuationInFlight = false; + /** * Constructs a new {@link KycController}. * @@ -375,7 +386,7 @@ export class KycController extends BaseController< // this call's product (or `null`). Otherwise a prior run's product could // linger and cause `#continueAfterAuthentication` to auto-run the check / // sub-flow when the caller intended the manual (product-less) flow. - this.update((state) => { + this.#applyUpdate((state) => { if (params?.email) { state.email = params.email; } @@ -385,7 +396,7 @@ export class KycController extends BaseController< // Resolve country for display; non-blocking. try { const country = await this.messenger.call('KycService:getGeoCountry'); - this.update((state) => { + this.#applyUpdate((state) => { state.geoCountry = country; }); } catch { @@ -401,7 +412,7 @@ export class KycController extends BaseController< return; } - this.update((state) => { + this.#applyUpdate((state) => { state.phase = 'terms'; }); await this.loadDisclaimers(); @@ -420,7 +431,7 @@ export class KycController extends BaseController< this.state.geoCountry ?? (await this.messenger.call('KycService:getGeoCountry')); if (country !== this.state.geoCountry) { - this.update((state) => { + this.#applyUpdate((state) => { state.geoCountry = country; }); } @@ -428,12 +439,12 @@ export class KycController extends BaseController< 'KycService:fetchDisclaimers', { country }, ); - this.update((state) => { + this.#applyUpdate((state) => { state.disclaimers = disclaimers; state.disclaimersError = null; }); } catch (error) { - this.update((state) => { + this.#applyUpdate((state) => { state.disclaimersError = `Failed to load disclaimers: ${String(error)}`; }); } @@ -457,7 +468,7 @@ export class KycController extends BaseController< const disclaimerIds = this.state.disclaimers.map( (disclaimer) => disclaimer.id, ); - this.update((state) => { + this.#applyUpdate((state) => { if (params?.email) { state.email = params.email; } @@ -491,7 +502,7 @@ export class KycController extends BaseController< // authentication. The Check/Auth frames re-populate these for the new // session. this.#authClientToken = null; - this.update((state) => { + this.#applyUpdate((state) => { state.error = null; state.phase = 'session'; state.statusMessage = 'Creating session...'; @@ -503,16 +514,19 @@ export class KycController extends BaseController< 'KycService:createSession', { email, termsAcceptedAt, disclaimerIds: acceptedDisclaimerIds }, ); - this.update((state) => { + this.#applyUpdate((state) => { state.sessionToken = sessionToken; state.phase = 'check'; state.statusMessage = 'Authenticating via Check frame...'; }); } catch (error) { - // Invalidate the stored acceptance so the customer can retry. - this.update((state) => { - state.termsAcceptedAt = null; - state.acceptedDisclaimerIds = []; + // Invalidate the stored acceptance so the customer can retry. Also clear + // `activeProduct` so a later `acceptTermsAndStartSession` that omits a + // product cannot auto-run the KYC check / SumSub chain for this failed + // flow's product — matching how `initialize` starts from a clean product. + this.#applyUpdate((state) => { + this.#clearAcceptedTerms(state); + state.activeProduct = null; state.error = `Session creation failed: ${String(error)}`; state.statusMessage = 'Session creation failed — accept the terms to try again.'; @@ -526,12 +540,25 @@ export class KycController extends BaseController< * Clears the persisted terms acceptance. */ clearSavedTerms(): void { - this.update((state) => { - state.termsAcceptedAt = null; - state.acceptedDisclaimerIds = []; + this.#applyUpdate((state) => { + this.#clearAcceptedTerms(state); }); } + /** + * Clears the stored terms acceptance on the given draft state. Shared by the + * paths that must invalidate acceptance — explicit clear, vendor terms + * update, and session-creation failure — so they stay in sync. This is a + * targeted invalidation and, unlike {@link reset}, deliberately leaves the + * rest of the flow (geolocation, disclaimers, phase) untouched. + * + * @param state - The state to mutate. + */ + #clearAcceptedTerms(state: KycControllerState): void { + state.termsAcceptedAt = null; + state.acceptedDisclaimerIds = []; + } + /** * Handles a message posted by a Check/Auth frame and advances the flow. * @@ -566,7 +593,7 @@ export class KycController extends BaseController< const customerId = payload.payload?.customer?.id ?? null; if (customerId) { - this.update((state) => { + this.#applyUpdate((state) => { state.moonpayCustomerId = customerId; }); } @@ -617,7 +644,7 @@ export class KycController extends BaseController< clientToken?: string, ): Promise { if (status === 'active' && accessToken) { - this.update((state) => { + this.#applyUpdate((state) => { state.accessToken = accessToken; state.phase = 'form'; state.statusMessage = 'Already authenticated. Review to submit.'; @@ -627,7 +654,7 @@ export class KycController extends BaseController< } if (status === 'connectionRequired' && clientToken) { this.#authClientToken = clientToken; - this.update((state) => { + this.#applyUpdate((state) => { state.phase = 'auth'; state.statusMessage = 'Verify your email via OTP in the Auth frame.'; }); @@ -651,7 +678,7 @@ export class KycController extends BaseController< accessToken?: string, ): Promise { if (status === 'active' && accessToken) { - this.update((state) => { + this.#applyUpdate((state) => { state.accessToken = accessToken; state.phase = 'form'; state.statusMessage = 'Authenticated. Review to submit.'; @@ -684,17 +711,35 @@ export class KycController extends BaseController< return; } - const kycRequired = await this.checkKycRequired({ product }); - if (!kycRequired) { + // A duplicate or late `complete` message can re-enter here while a prior + // continuation is still running; ignore it so the check / sub-flow does not + // run twice concurrently. + if (this.#continuationInFlight) { return; } + this.#continuationInFlight = true; + + // Capture the generation so a `reset()` landing mid-continuation does not + // let the `finally` clear a flag that belongs to a newer flow. + const generation = this.#generation; try { - await this.startSumSub(); - } catch { - // `startSumSub` already records `sumsub.status = 'failed'`; swallow the - // rethrown error (e.g. SDK unavailable) so the awaited continuation - // resolves cleanly rather than surfacing as an unhandled rejection. + const kycRequired = await this.checkKycRequired({ product }); + if (!kycRequired) { + return; + } + + try { + await this.startSumSub(); + } catch { + // `startSumSub` already records `sumsub.status = 'failed'`; swallow the + // rethrown error (e.g. SDK unavailable) so the awaited continuation + // resolves cleanly rather than surfacing as an unhandled rejection. + } + } finally { + if (this.#generation === generation) { + this.#continuationInFlight = false; + } } } @@ -702,9 +747,8 @@ export class KycController extends BaseController< * Invalidates stored terms and returns to the terms phase. */ #requireTermsReacceptance(): void { - this.update((state) => { - state.termsAcceptedAt = null; - state.acceptedDisclaimerIds = []; + this.#applyUpdate((state) => { + this.#clearAcceptedTerms(state); state.phase = 'terms'; state.statusMessage = 'The vendor updated its Terms of Use — please re-accept.'; @@ -782,7 +826,7 @@ export class KycController extends BaseController< // while the HTTP call is in flight and avoid writing stale results. const generation = this.#generation; - this.update((state) => { + this.#applyUpdate((state) => { state.phase = 'submit'; state.statusMessage = 'Checking KYC status...'; }); @@ -797,7 +841,7 @@ export class KycController extends BaseController< if (this.#generation !== generation) { return false; } - this.update((state) => { + this.#applyUpdate((state) => { state.kycRequiredByProduct[params.product] = kycRequired; state.lastCheckedAt = new Date().toISOString(); state.phase = 'done'; @@ -840,7 +884,7 @@ export class KycController extends BaseController< }): Promise> { if (!this.#sumsubLauncher.isAvailable()) { const error = 'SumSub SDK is not available in this runtime.'; - this.update((state) => { + this.#applyUpdate((state) => { state.sumsub.status = 'failed'; state.sumsub.result = { error }; }); @@ -853,7 +897,7 @@ export class KycController extends BaseController< const generation = this.#generation; try { - this.update((state) => { + this.#applyUpdate((state) => { state.sumsub.status = 'creatingSession'; state.sumsub.result = null; }); @@ -893,7 +937,7 @@ export class KycController extends BaseController< return {}; } - this.update((state) => { + this.#applyUpdate((state) => { state.sumsub.status = 'launching'; state.sumsub.applicantAccessToken = applicantAccessToken; }); @@ -906,6 +950,14 @@ export class KycController extends BaseController< const result = await this.#sumsubLauncher.launch({ applicantAccessToken, onTokenExpiration: async () => { + // A reset() may have superseded this flow while the SDK stayed open. + // Refuse to refresh against the now-stale UKYC session rather than + // silently keeping an orphaned SDK alive. + if (this.#generation !== generation) { + throw new Error( + 'KYC flow was reset; SumSub session is no longer active.', + ); + } const refreshed = await this.messenger.call( 'KycService:submitWrappedKey', exchange, @@ -951,7 +1003,11 @@ export class KycController extends BaseController< this.#authClientToken = null; // Invalidate any in-flight async work started before this reset. this.#generation += 1; - this.update((state) => { + // Allow the next authenticated flow to auto-continue; any continuation from + // the superseded generation will no longer clear this flag (see the + // generation guard in `#continueAfterAuthentication`). + this.#continuationInFlight = false; + this.#applyUpdate((state) => { state.phase = 'idle'; state.statusMessage = ''; state.error = null; @@ -987,17 +1043,38 @@ export class KycController extends BaseController< if (this.#generation !== generation) { return false; } - this.update(updater); + this.#applyUpdate(updater); return true; } + /** + * The single state-update path for this controller. All mutations go through + * here (rather than calling `this.update` directly) so the mechanism stays + * consistent and one subtlety is handled in a single place: + * + * `sumsub.result` is typed as the recursive `Json`, and expanding + * `Draft` (which happens whenever an updater touches `sumsub.result`) + * trips TypeScript's "type instantiation is excessively deep" guard. By + * typing the callback parameter as the plain {@link KycControllerState} + * instead of Immer's `Draft`, we avoid expanding the draft type while keeping + * the same mutate-in-place semantics (the underlying value is still the Immer + * draft at runtime). + * + * @param updater - The state mutation to apply. + */ + #applyUpdate(updater: (state: KycControllerState) => void): void { + this.update((state) => { + updater(state as unknown as KycControllerState); + }); + } + /** * Transitions to the error phase with a message. * * @param message - The error message. */ #fail(message: string): void { - this.update((state) => { + this.#applyUpdate((state) => { state.error = message; state.phase = 'error'; }); diff --git a/packages/kyc-controller/src/KycService.ts b/packages/kyc-controller/src/KycService.ts index fec9a4baa2b..98a994c93b0 100644 --- a/packages/kyc-controller/src/KycService.ts +++ b/packages/kyc-controller/src/KycService.ts @@ -208,11 +208,17 @@ export class KycService { const location = await this.#messenger.call( 'GeolocationController:getGeolocation', ); - assert(location, string()); - const alpha2 = location.split('-')[0].toUpperCase(); + // Guard nullish/empty geolocation with the documented domain error rather + // than letting `assert(location, string())` surface a superstruct + // assertion error (which would change how the failure reads in + // `disclaimersError`). + const alpha2 = + typeof location === 'string' ? location.split('-')[0].toUpperCase() : ''; if (!alpha2 || alpha2 === 'UNKNOWN') { throw new Error( - `Unable to determine country from geolocation (got "${location}").`, + `Unable to determine country from geolocation (got "${String( + location, + )}").`, ); } const alpha3 = alpha2ToAlpha3(alpha2); @@ -261,7 +267,11 @@ export class KycService { method: 'POST', body: JSON.stringify(params), }); - return this.#validateResponse(data, CreateSessionResponseStruct, 'sessions'); + return this.#validateResponse( + data, + CreateSessionResponseStruct, + 'sessions', + ); } /** From cb246eddea3c2b618721a100c8bbb316ac085b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Fri, 24 Jul 2026 12:25:07 +0200 Subject: [PATCH 14/19] fix: session state issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- .../kyc-controller/src/KycController.test.ts | 47 +++++++++++++++++++ packages/kyc-controller/src/KycController.ts | 14 ++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index 5e3e4797c14..d4c21cd6a70 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -299,12 +299,55 @@ describe('KycController', () => { ); }); + it('clears the old session token while a new session is being created', async () => { + await withController( + { + options: { + state: { + email: 'a@b.co', + sessionToken: 'old-session', + disclaimers: [{ id: '1', display_name: 'T', url: 'u' }], + }, + }, + }, + async ({ controller, handlers }) => { + let releaseSession: (value: { + sessionToken: string; + }) => void = () => { + // no-op placeholder until the deferred promise is wired up + }; + handlers.createSession.mockReturnValue( + new Promise<{ sessionToken: string }>((resolve) => { + releaseSession = resolve; + }), + ); + + const pending = controller.acceptTermsAndStartSession(); + + // While the request is in flight (phase `session`) the stale token + // must already be gone so no Check frame URL can be built for it. + expect(controller.state.phase).toBe('session'); + expect(controller.state.sessionToken).toBeNull(); + expect(controller.buildCheckFrameUrl()).toBeNull(); + + releaseSession({ sessionToken: 'new-session' }); + await pending; + + expect(controller.state.sessionToken).toBe('new-session'); + expect(controller.buildCheckFrameUrl()).toContain( + 'sessionToken=new-session', + ); + }, + ); + }); + it('reverts to terms when session creation fails', async () => { await withController( { options: { state: { email: 'a@b.co', + sessionToken: 'old-session', disclaimers: [{ id: '1', display_name: 'T', url: 'u' }], }, }, @@ -318,6 +361,10 @@ describe('KycController', () => { expect(controller.state.phase).toBe('terms'); expect(controller.state.termsAcceptedAt).toBeNull(); expect(controller.state.error).toMatch(/Session creation failed/u); + // A failed creation must not leave the old session token behind, so + // the Check frame cannot be built against an invalid session. + expect(controller.state.sessionToken).toBeNull(); + expect(controller.buildCheckFrameUrl()).toBeNull(); }, ); }); diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index 95a925e90e4..3348659a7b3 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -496,16 +496,20 @@ export class KycController extends BaseController< } // A new session invalidates any authentication carried over from a prior - // session. Clear the stale access token and auth-frame client token so - // `buildAuthFrameUrl` cannot return a URL tied to an old client token and - // `checkKycRequired` cannot run with an access token from an earlier - // authentication. The Check/Auth frames re-populate these for the new - // session. + // session. Clear the stale session token, access token, and auth-frame + // client token so `buildCheckFrameUrl` cannot return a URL bound to an old + // (or, on failure, invalid) session token, `buildAuthFrameUrl` cannot + // return a URL tied to an old client token, and `checkKycRequired` cannot + // run with an access token from an earlier authentication. The Check/Auth + // frames re-populate these for the new session. Because `sessionToken` is + // cleared here and only re-set on success, a failed creation leaves it + // `null` rather than resurrecting the previous session. this.#authClientToken = null; this.#applyUpdate((state) => { state.error = null; state.phase = 'session'; state.statusMessage = 'Creating session...'; + state.sessionToken = null; state.accessToken = null; }); From 194d216fb7d30a2ea8cb2dfc36bde751cd224546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Fri, 24 Jul 2026 12:28:42 +0200 Subject: [PATCH 15/19] fix: lockfile after rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index f8b525f11af..949feb617aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6231,7 +6231,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/bridge-controller@npm:^77.8.0, @metamask/bridge-controller@workspace:packages/bridge-controller": +"@metamask/bridge-controller@npm:^78.0.0, @metamask/bridge-controller@workspace:packages/bridge-controller": version: 0.0.0-use.local resolution: "@metamask/bridge-controller@workspace:packages/bridge-controller" dependencies: @@ -6285,7 +6285,7 @@ __metadata: "@metamask/accounts-controller": "npm:^39.0.5" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" - "@metamask/bridge-controller": "npm:^77.8.0" + "@metamask/bridge-controller": "npm:^78.0.0" "@metamask/controller-utils": "npm:^12.3.0" "@metamask/gas-fee-controller": "npm:^26.3.0" "@metamask/keyring-controller": "npm:^27.1.0" @@ -8618,7 +8618,7 @@ __metadata: languageName: node linkType: hard -"@metamask/ramps-controller@npm:^17.0.0, @metamask/ramps-controller@workspace:packages/ramps-controller": +"@metamask/ramps-controller@npm:^17.1.0, @metamask/ramps-controller@workspace:packages/ramps-controller": version: 0.0.0-use.local resolution: "@metamask/ramps-controller@workspace:packages/ramps-controller" dependencies: @@ -9331,7 +9331,7 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" "@metamask/network-controller": "npm:^34.0.0" - "@metamask/ramps-controller": "npm:^17.0.0" + "@metamask/ramps-controller": "npm:^17.1.0" "@metamask/remote-feature-flag-controller": "npm:^4.2.2" "@metamask/sentinel-api-service": "npm:^1.0.0" "@metamask/transaction-controller": "npm:^69.2.1" From 85a764f5cd22753d0b15395115d577c46dac4409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Fri, 24 Jul 2026 14:09:31 +0200 Subject: [PATCH 16/19] fix: review again state management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- .../kyc-controller/src/KycController.test.ts | 239 ++++++++++++++---- packages/kyc-controller/src/KycController.ts | 155 +++++++----- 2 files changed, 287 insertions(+), 107 deletions(-) diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index d4c21cd6a70..1bcd8efd701 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -129,6 +129,35 @@ describe('KycController', () => { ); }); + it('does not restart an in-progress session flow', async () => { + await withController( + { + options: { + state: { + phase: 'check', + email: 'a@b.co', + sessionToken: 'live-session', + termsAcceptedAt: 't', + acceptedDisclaimerIds: ['1'], + activeProduct: 'ramps', + }, + }, + }, + async ({ controller, handlers }) => { + await controller.initialize({ email: 'other@b.co', product: 'card' }); + + // A repeat initialize mid-flow must be a no-op: no new session, no + // token/phase teardown, and no clobbering of the active product. + expect(handlers.createSession).not.toHaveBeenCalled(); + expect(handlers.getGeoCountry).not.toHaveBeenCalled(); + expect(controller.state.phase).toBe('check'); + expect(controller.state.sessionToken).toBe('live-session'); + expect(controller.state.activeProduct).toBe('ramps'); + expect(controller.state.email).toBe('a@b.co'); + }, + ); + }); + it('stays on terms when terms exist but no email is available', async () => { await withController( { @@ -262,6 +291,7 @@ describe('KycController', () => { { options: { state: { + phase: 'check', email: 'a@b.co', sessionToken: 'old-session', accessToken: 'stale-access', @@ -369,6 +399,43 @@ describe('KycController', () => { ); }); + it('leaves the controller idle when reset() runs before session creation fails', async () => { + await withController( + { + options: { + state: { + email: 'a@b.co', + sessionToken: 'old-session', + disclaimers: [{ id: '1', display_name: 'T', url: 'u' }], + }, + }, + }, + async ({ controller, handlers }) => { + let rejectSession: (reason: Error) => void = () => { + // no-op placeholder until the deferred promise is wired up + }; + handlers.createSession.mockReturnValue( + new Promise<{ sessionToken: string }>((_resolve, reject) => { + rejectSession = reject; + }), + ); + + const pending = controller.acceptTermsAndStartSession(); + + // Reset while the create request is in flight, then let it fail. The + // superseded flow must not force the now-idle controller back to + // `terms` or re-run disclaimer loading. + controller.reset(); + rejectSession(new Error('nope')); + await pending; + + expect(controller.state.phase).toBe('idle'); + expect(controller.state.error).toBeNull(); + expect(handlers.fetchDisclaimers).not.toHaveBeenCalled(); + }, + ); + }); + it('clears the active product when session creation fails', async () => { await withController( { @@ -461,17 +528,20 @@ describe('KycController', () => { }); it('captures the customer id and ignores a status-less complete message', async () => { - await withController(async ({ controller }) => { - const result = await controller.handleFrameMessage({ - message: { - kind: 'complete', - meta: { channelId: 'ch_1' }, - payload: { customer: { id: 'cust-1' } }, - }, - }); - expect(result).toStrictEqual({}); - expect(controller.state.moonpayCustomerId).toBe('cust-1'); - }); + await withController( + { options: { state: { phase: 'check' } } }, + async ({ controller }) => { + const result = await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { customer: { id: 'cust-1' } }, + }, + }); + expect(result).toStrictEqual({}); + expect(controller.state.moonpayCustomerId).toBe('cust-1'); + }, + ); }); it('ignores messages on an unknown channel', async () => { @@ -487,9 +557,36 @@ describe('KycController', () => { }); }); + it('ignores a stale completion for a frame the flow is no longer waiting on', async () => { + // Phase `done` (e.g. after a completed flow or a `reset()` that returns + // to an idle phase) means the Check frame is no longer active; a late or + // duplicate `ch_1` completion must not resurrect tokens or rewind phase. + await withController( + { options: { state: { phase: 'done', sessionToken: 'tok' } } }, + async ({ controller }) => { + const envelope = envelopeFor(controller, { accessToken: 'access-1' }); + const result = await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_1' }, + payload: { + status: 'active', + credentials: envelope, + customer: { id: 'cust-late' }, + }, + }, + }); + expect(result).toStrictEqual({}); + expect(controller.state.phase).toBe('done'); + expect(controller.state.accessToken).toBeNull(); + expect(controller.state.moonpayCustomerId).toBeNull(); + }, + ); + }); + it('fails when credential decryption throws', async () => { await withController( - { options: { state: { sessionToken: 'tok' } } }, + { options: { state: { phase: 'check', sessionToken: 'tok' } } }, async ({ controller }) => { await controller.handleFrameMessage({ message: { @@ -507,7 +604,7 @@ describe('KycController', () => { describe('check frame', () => { it('moves to form on an active status with an access token', async () => { await withController( - { options: { state: { sessionToken: 'tok' } } }, + { options: { state: { phase: 'check', sessionToken: 'tok' } } }, async ({ controller }) => { const envelope = envelopeFor(controller, { accessToken: 'access-1', @@ -527,7 +624,7 @@ describe('KycController', () => { it('moves to auth on connectionRequired and enables the auth frame URL', async () => { await withController( - { options: { state: { sessionToken: 'tok' } } }, + { options: { state: { phase: 'check', sessionToken: 'tok' } } }, async ({ controller }) => { const envelope = envelopeFor(controller, { clientToken: 'client-1', @@ -555,6 +652,7 @@ describe('KycController', () => { { options: { state: { + phase: 'check', sessionToken: 'tok', termsAcceptedAt: 't', acceptedDisclaimerIds: ['1'], @@ -577,7 +675,7 @@ describe('KycController', () => { it('fails on an unexpected status', async () => { await withController( - { options: { state: { sessionToken: 'tok' } } }, + { options: { state: { phase: 'check', sessionToken: 'tok' } } }, async ({ controller }) => { await controller.handleFrameMessage({ message: { @@ -595,7 +693,7 @@ describe('KycController', () => { describe('auth frame', () => { it('moves to form on an active status with an access token', async () => { await withController( - { options: { state: { sessionToken: 'tok' } } }, + { options: { state: { phase: 'auth', sessionToken: 'tok' } } }, async ({ controller }) => { const envelope = envelopeFor(controller, { accessToken: 'access-2', @@ -614,29 +712,35 @@ describe('KycController', () => { }); it('requires re-acceptance on termsAcceptanceRequired', async () => { - await withController(async ({ controller }) => { - await controller.handleFrameMessage({ - message: { - kind: 'complete', - meta: { channelId: 'ch_2' }, - payload: { status: 'termsAcceptanceRequired' }, - }, - }); - expect(controller.state.phase).toBe('terms'); - }); + await withController( + { options: { state: { phase: 'auth' } } }, + async ({ controller }) => { + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_2' }, + payload: { status: 'termsAcceptanceRequired' }, + }, + }); + expect(controller.state.phase).toBe('terms'); + }, + ); }); it('fails on an unexpected status', async () => { - await withController(async ({ controller }) => { - await controller.handleFrameMessage({ - message: { - kind: 'complete', - meta: { channelId: 'ch_2' }, - payload: { status: 'unavailable' }, - }, - }); - expect(controller.state.phase).toBe('error'); - }); + await withController( + { options: { state: { phase: 'auth' } } }, + async ({ controller }) => { + await controller.handleFrameMessage({ + message: { + kind: 'complete', + meta: { channelId: 'ch_2' }, + payload: { status: 'unavailable' }, + }, + }); + expect(controller.state.phase).toBe('error'); + }, + ); }); }); }); @@ -644,7 +748,11 @@ describe('KycController', () => { describe('automatic post-authentication continuation', () => { it('stays at form and does not run the check when no product is set', async () => { await withController( - { options: { state: { sessionToken: 'tok', geoCountry: 'USA' } } }, + { + options: { + state: { phase: 'check', sessionToken: 'tok', geoCountry: 'USA' }, + }, + }, async ({ controller, handlers }) => { const envelope = envelopeFor(controller, { accessToken: 'access-1' }); @@ -667,6 +775,7 @@ describe('KycController', () => { { options: { state: { + phase: 'check', sessionToken: 'tok', activeProduct: 'ramps', geoCountry: 'USA', @@ -702,6 +811,7 @@ describe('KycController', () => { { options: { state: { + phase: 'auth', sessionToken: 'tok', activeProduct: 'card', geoCountry: 'FRA', @@ -736,6 +846,7 @@ describe('KycController', () => { { options: { state: { + phase: 'check', sessionToken: 'tok', activeProduct: 'ramps', geoCountry: 'USA', @@ -766,6 +877,7 @@ describe('KycController', () => { { options: { state: { + phase: 'auth', sessionToken: 'tok', activeProduct: 'card', geoCountry: 'FRA', @@ -774,7 +886,9 @@ describe('KycController', () => { }, async ({ controller, handlers, launcher }) => { // Hold the KYC-required check open so the first continuation is still - // in flight when the second (duplicate) completion arrives. + // in flight when the second (duplicate) completion arrives. The first + // completion moves `phase` to `form` synchronously, so the duplicate + // is dropped by the frame-phase guard before it can re-run the check. let releaseCheck: (value: { kycRequired: boolean }) => void = () => { // no-op placeholder until the deferred promise is wired up }; @@ -812,9 +926,15 @@ describe('KycController', () => { { options: { state: { + phase: 'check', + email: 'a@b.co', sessionToken: 'tok', activeProduct: 'ramps', geoCountry: 'USA', + // Persisted terms so a post-reset `initialize` auto-recreates the + // session (reaching phase `check`) for the second completion. + termsAcceptedAt: 't', + acceptedDisclaimerIds: ['1'], }, }, }, @@ -828,7 +948,13 @@ describe('KycController', () => { const envelope2 = envelopeFor(controller, { accessToken: 'access-2', }); - const messageFor = (credentials: unknown) => ({ + const messageFor = ( + credentials: unknown, + ): { + kind: string; + meta: { channelId: string }; + payload: { status: string; credentials: unknown }; + } => ({ kind: 'complete', meta: { channelId: 'ch_1' }, payload: { status: 'active', credentials }, @@ -849,16 +975,17 @@ describe('KycController', () => { message: messageFor(envelope1), }); - // Reset while the continuation is awaiting the check. Its `finally` - // must not clear the guard (it belongs to the superseded generation); - // `reset` clears it instead. + // Reset while the continuation is awaiting the check. Its result is + // discarded by the generation guard (the check belongs to the + // superseded generation) rather than written onto the idle flow. controller.reset(); releaseCheck({ kycRequired: false }); await first; - // Re-establish a product-scoped flow and confirm the next completion - // continues again rather than being blocked forever by a stuck guard. - await controller.initialize({ email: 'a@b.co', product: 'ramps' }); + // Re-establish a product-scoped flow (auto-creates a session and + // returns to phase `check`) and confirm the next completion continues + // again rather than being blocked forever by a stuck guard. + await controller.initialize({ product: 'ramps' }); handlers.checkKycRequired.mockResolvedValue({ kycRequired: false }); await controller.handleFrameMessage({ message: messageFor(envelope2), @@ -874,6 +1001,7 @@ describe('KycController', () => { { options: { state: { + phase: 'check', sessionToken: 'tok', activeProduct: 'ramps', geoCountry: 'USA', @@ -1171,6 +1299,27 @@ describe('KycController', () => { }); }); + it('aborts without launching the SDK when reset() runs just before launch', async () => { + await withController(async ({ controller, handlers, launcher }) => { + // A reset() lands during the final token exchange, i.e. after the + // session is prepared but before the SDK is presented. + handlers.submitWrappedKey.mockImplementation(async () => { + controller.reset(); + return { status: 'ok', applicantAccessToken: 'aat' }; + }); + + const result = await controller.startSumSub(); + + expect(result).toStrictEqual({}); + // The SDK must not be opened on a flow that was reset to idle, and the + // `launching` status must not be written. + expect(launcher.launch).not.toHaveBeenCalled(); + expect(controller.state.sumsub.status).toBe('idle'); + expect(controller.state.sumsub.applicantAccessToken).toBeNull(); + expect(controller.state.phase).toBe('idle'); + }); + }); + it('refuses to refresh the token via onTokenExpiration after a reset', async () => { await withController(async ({ controller, handlers, launcher }) => { let refreshError: unknown; diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index 3348659a7b3..a6436492679 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -37,6 +37,17 @@ const MOCK_JWT_TOKEN = 'mock-jwt-token'; // outcome) must not be recorded as `complete`. const SUMSUB_COMPLETED_STATUS = 'Completed'; +// Phases that represent an active vendor-session flow (tokens issued and/or +// Check/Auth frames in progress). A repeat `initialize` while in one of these +// must not restart the session and disrupt the in-flight flow. +const IN_PROGRESS_PHASES: KycPhase[] = [ + 'session', + 'check', + 'auth', + 'form', + 'submit', +]; + // === STATE === /** @@ -330,17 +341,6 @@ export class KycController extends BaseController< */ #generation = 0; - /** - * Guards the automatic post-authentication continuation. The Check/Auth - * frames can post more than one `complete` message (duplicate or late), each - * of which resolves to an `active` outcome with an access token. Without this - * flag, every such message would re-enter {@link #continueAfterAuthentication} - * and run the KYC-required check / SumSub sub-flow again while a prior run is - * still in flight. Set for the duration of a continuation and cleared by - * {@link reset} so a fresh flow can continue again. - */ - #continuationInFlight = false; - /** * Constructs a new {@link KycController}. * @@ -382,6 +382,15 @@ export class KycController extends BaseController< email?: string; product?: KycProduct; }): Promise { + // A repeat `initialize` while a session flow is already in progress must + // not tear it down: creating a new vendor session clears the tokens and + // forces `phase` back through `session`/`check`, breaking an in-flight + // Check/Auth frame flow. Leave the active flow untouched and let the + // consumer drive it (or call `reset` first to start over). + if (IN_PROGRESS_PHASES.includes(this.state.phase)) { + return; + } + // `initialize` starts a fresh flow, so `activeProduct` is always reset to // this call's product (or `null`). Otherwise a prior run's product could // linger and cause `#continueAfterAuthentication` to auto-run the check / @@ -393,10 +402,15 @@ export class KycController extends BaseController< state.activeProduct = params?.product ?? null; }); + // Capture the flow generation so a `reset()` landing while the async + // geolocation / session steps below are in flight cannot write results + // onto an idle controller. + const generation = this.#generation; + // Resolve country for display; non-blocking. try { const country = await this.messenger.call('KycService:getGeoCountry'); - this.#applyUpdate((state) => { + this.#updateIfCurrent(generation, (state) => { state.geoCountry = country; }); } catch { @@ -425,13 +439,17 @@ export class KycController extends BaseController< * @param params.country - ISO 3166-1 alpha-3 country code override. */ async loadDisclaimers(params?: { country?: string }): Promise { + // Capture the flow generation so a `reset()` landing while the geo / + // disclaimers requests are in flight cannot write results onto an idle + // controller. + const generation = this.#generation; try { const country = params?.country ?? this.state.geoCountry ?? (await this.messenger.call('KycService:getGeoCountry')); if (country !== this.state.geoCountry) { - this.#applyUpdate((state) => { + this.#updateIfCurrent(generation, (state) => { state.geoCountry = country; }); } @@ -439,12 +457,12 @@ export class KycController extends BaseController< 'KycService:fetchDisclaimers', { country }, ); - this.#applyUpdate((state) => { + this.#updateIfCurrent(generation, (state) => { state.disclaimers = disclaimers; state.disclaimersError = null; }); } catch (error) { - this.#applyUpdate((state) => { + this.#updateIfCurrent(generation, (state) => { state.disclaimersError = `Failed to load disclaimers: ${String(error)}`; }); } @@ -504,6 +522,11 @@ export class KycController extends BaseController< // frames re-populate these for the new session. Because `sessionToken` is // cleared here and only re-set on success, a failed creation leaves it // `null` rather than resurrecting the previous session. + // Capture the flow generation so a `reset()` landing while the create + // request is in flight cannot resurrect a session (success) or overwrite + // the now-idle controller (failure). The synchronous update below runs + // before any `await`, so it needs no guard. + const generation = this.#generation; this.#authClientToken = null; this.#applyUpdate((state) => { state.error = null; @@ -518,12 +541,17 @@ export class KycController extends BaseController< 'KycService:createSession', { email, termsAcceptedAt, disclaimerIds: acceptedDisclaimerIds }, ); - this.#applyUpdate((state) => { + this.#updateIfCurrent(generation, (state) => { state.sessionToken = sessionToken; state.phase = 'check'; state.statusMessage = 'Authenticating via Check frame...'; }); } catch (error) { + // A reset() superseded this flow while the request was in flight; leave + // the idle controller alone rather than forcing it back to `terms`. + if (this.#generation !== generation) { + return; + } // Invalidate the stored acceptance so the customer can retry. Also clear // `activeProduct` so a later `acceptTermsAndStartSession` that omits a // product cannot auto-run the KYC check / SumSub chain for this failed @@ -592,6 +620,23 @@ export class KycController extends BaseController< } const channelId = payload.meta?.channelId; + + // Only honor a Check/Auth `complete` for the frame the flow is currently + // waiting on. This drops stale or duplicate messages — e.g. a late post + // after `reset()` (phase `idle`) or after the flow already advanced past + // this frame — so they cannot resurrect tokens or rewind `phase` on a + // controller that has moved on. Frame messages are external input and, + // unlike the async steps, are not covered by the `#generation` guard. + let expectedPhase: KycPhase | null = null; + if (channelId === CHANNEL_CHECK) { + expectedPhase = 'check'; + } else if (channelId === CHANNEL_AUTH) { + expectedPhase = 'auth'; + } + if (!expectedPhase || this.state.phase !== expectedPhase) { + return {}; + } + const status = payload.payload?.status; const credsEnvelope = payload.payload?.credentials; @@ -627,11 +672,8 @@ export class KycController extends BaseController< return {}; } - if (channelId === CHANNEL_AUTH) { - await this.#handleAuthOutcome(status, accessToken); - return {}; - } - + // channelId === CHANNEL_AUTH, guaranteed by the expectedPhase guard above. + await this.#handleAuthOutcome(status, accessToken); return {}; } @@ -715,35 +757,24 @@ export class KycController extends BaseController< return; } - // A duplicate or late `complete` message can re-enter here while a prior - // continuation is still running; ignore it so the check / sub-flow does not - // run twice concurrently. - if (this.#continuationInFlight) { + // Re-entry protection lives at the frame boundary: `handleFrameMessage` + // only honors a Check/Auth `complete` while `phase` matches, and both + // outcome handlers move `phase` to `form` before awaiting this method. A + // duplicate or late `complete` therefore lands after the phase moved on and + // is dropped before it can start a second continuation. Any writes here are + // additionally guarded by `#generation` (see `checkKycRequired` / + // `startSumSub`) so a `reset()` mid-continuation cannot corrupt state. + const kycRequired = await this.checkKycRequired({ product }); + if (!kycRequired) { return; } - this.#continuationInFlight = true; - - // Capture the generation so a `reset()` landing mid-continuation does not - // let the `finally` clear a flag that belongs to a newer flow. - const generation = this.#generation; try { - const kycRequired = await this.checkKycRequired({ product }); - if (!kycRequired) { - return; - } - - try { - await this.startSumSub(); - } catch { - // `startSumSub` already records `sumsub.status = 'failed'`; swallow the - // rethrown error (e.g. SDK unavailable) so the awaited continuation - // resolves cleanly rather than surfacing as an unhandled rejection. - } - } finally { - if (this.#generation === generation) { - this.#continuationInFlight = false; - } + await this.startSumSub(); + } catch { + // `startSumSub` already records `sumsub.status = 'failed'`; swallow the + // rethrown error (e.g. SDK unavailable) so the awaited continuation + // resolves cleanly rather than surfacing as an unhandled rejection. } } @@ -842,15 +873,15 @@ export class KycController extends BaseController< ); // The flow was reset while the check was in flight; discard the result // rather than resurrecting a done/cached state on an idle controller. - if (this.#generation !== generation) { - return false; - } - this.#applyUpdate((state) => { + const applied = this.#updateIfCurrent(generation, (state) => { state.kycRequiredByProduct[params.product] = kycRequired; state.lastCheckedAt = new Date().toISOString(); state.phase = 'done'; state.statusMessage = 'KYC check complete.'; }); + if (!applied) { + return false; + } return kycRequired; } catch (error) { if (this.#generation !== generation) { @@ -935,16 +966,19 @@ export class KycController extends BaseController< exchange, ); - // A reset() landed while the session/token was being prepared; abort - // before presenting the SDK rather than launching on an idle controller. - if (this.#generation !== generation) { - return {}; - } - - this.#applyUpdate((state) => { + // A reset() may have landed while the session/token was being prepared. + // Gate the `launching` write and the decision to open the SDK behind a + // single generation check: `#updateIfCurrent` only writes when still + // current and reports whether it did. Since there is no `await` between + // this check and `launch` below, a successful result guarantees the SDK + // is never presented on a flow that a concurrent reset() returned to idle. + const stillCurrent = this.#updateIfCurrent(generation, (state) => { state.sumsub.status = 'launching'; state.sumsub.applicantAccessToken = applicantAccessToken; }); + if (!stillCurrent) { + return {}; + } // Track whether the SDK ever reported a successful completion. A resolved // `launch` alone does not imply success — the applicant may have @@ -1005,12 +1039,9 @@ export class KycController extends BaseController< */ reset(): void { this.#authClientToken = null; - // Invalidate any in-flight async work started before this reset. + // Invalidate any in-flight async work started before this reset so its + // results are discarded rather than written onto the now-idle controller. this.#generation += 1; - // Allow the next authenticated flow to auto-continue; any continuation from - // the superseded generation will no longer clear this flag (see the - // generation guard in `#continueAfterAuthentication`). - this.#continuationInFlight = false; this.#applyUpdate((state) => { state.phase = 'idle'; state.statusMessage = ''; From ed8497cdbcb2122c154f28c46dc31e190f25bf5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Fri, 24 Jul 2026 14:23:45 +0200 Subject: [PATCH 17/19] feat: update architecture section of readme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- packages/kyc-controller/README.md | 155 ++++++++++++++++++------------ 1 file changed, 93 insertions(+), 62 deletions(-) diff --git a/packages/kyc-controller/README.md b/packages/kyc-controller/README.md index 5c9d3a83308..a8d4ad915f0 100644 --- a/packages/kyc-controller/README.md +++ b/packages/kyc-controller/README.md @@ -22,7 +22,6 @@ This watches `src/**/*.ts` and re-runs the build on each change (it also perform This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme). - ## Architecture `@metamask/kyc-controller` is a shared, **platform-agnostic** package that owns @@ -47,13 +46,13 @@ This document explains: The package is built around a few deliberate constraints: -| Principle | How it shows up in the code | -| --- | --- | -| **Vendor-neutral surface** | Consumers deal with `KycProduct` (`'ramps' \| 'card'`) and a phase machine, never with MoonPay/SumSub specifics. `KycVendor` is internal. | -| **Platform-agnostic core** | No React, no `Buffer`/`atob`, no native SDK imports. Crypto uses `@noble/*` + `@scure/base`. WebView/iframe presentation and the SumSub SDK are **injected** by each client. | -| **Controller owns orchestration; clients own presentation** | `KycController` owns all state, HTTP orchestration, crypto and the frame protocol. Clients only render frames, forward raw messages, and present the SumSub SDK. | -| **Stateless service** | `KycService` performs HTTP only; it holds no state and derives auth/geolocation from other controllers via the messenger. | -| **Everything through the messenger** | Both classes register their public methods as messenger actions, and reach external capabilities (auth token, geolocation) via delegated actions. | +| Principle | How it shows up in the code | +| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Vendor-neutral surface** | Consumers deal with `KycProduct` (`'ramps' \| 'card'`) and a phase machine, never with MoonPay/SumSub specifics. `KycVendor` is internal. | +| **Platform-agnostic core** | No React, no `Buffer`/`atob`, no native SDK imports. Crypto uses `@noble/*` + `@scure/base`. WebView/iframe presentation and the SumSub SDK are **injected** by each client. | +| **Controller owns orchestration; clients own presentation** | `KycController` owns all state, HTTP orchestration, crypto and the frame protocol. Clients only render frames, forward raw messages, and present the SumSub SDK. | +| **Stateless service** | `KycService` performs HTTP only; it holds no state and derives auth/geolocation from other controllers via the messenger. | +| **Everything through the messenger** | Both classes register their public methods as messenger actions, and reach external capabilities (auth token, geolocation) via delegated actions. | --- @@ -150,14 +149,14 @@ Exposed messenger actions (`MESSENGER_EXPOSED_METHODS`): Endpoints: -| Method | HTTP | Endpoint | Purpose | -| --- | --- | --- | --- | -| `getGeoCountry` | — | (geolocation action) | Resolve alpha-3 country | -| `fetchDisclaimers` | `GET` | `/vendors/moonpay/disclaimers?country=` | Terms to accept | -| `createSession` | `POST` | `/vendors/moonpay/sessions` | Create vendor session | -| `checkKycRequired` | `POST` | `/vendors/moonpay/kyc-required` | Is KYC required? (normalizes `required` → `kycRequired`) | -| `createUkycSession` | `POST` | `/sessions` | Start SumSub sub-flow | -| `submitWrappedKey` | `POST` | `/sessions/{id}/wrapped-key` | Exchange wrapped key → applicant token | +| Method | HTTP | Endpoint | Purpose | +| ------------------- | ------ | --------------------------------------- | -------------------------------------------------------- | +| `getGeoCountry` | — | (geolocation action) | Resolve alpha-3 country | +| `fetchDisclaimers` | `GET` | `/vendors/moonpay/disclaimers?country=` | Terms to accept | +| `createSession` | `POST` | `/vendors/moonpay/sessions` | Create vendor session | +| `checkKycRequired` | `POST` | `/vendors/moonpay/kyc-required` | Is KYC required? (normalizes `required` → `kycRequired`) | +| `createUkycSession` | `POST` | `/sessions` | Start SumSub sub-flow | +| `submitWrappedKey` | `POST` | `/sessions/{id}/wrapped-key` | Exchange wrapped key → applicant token | ### 2.3 `crypto.ts` @@ -242,7 +241,7 @@ stateDiagram-v2 terms --> session : acceptTermsAndStartSession() session --> check : createSession() ok - session --> terms : createSession() fails (clears saved terms) + session --> terms : createSession() fails
(clears saved terms, activeProduct + stale tokens) check --> form : Check frame → active (already authenticated) check --> auth : Check frame → connectionRequired (needs OTP) @@ -269,19 +268,31 @@ stateDiagram-v2 > sub-flow (see [§7](#7-sumsub-sub-flow)). When no product is set the flow stops > at `form` and the consumer drives `checkKycRequired` / `startSumSub` manually. +> **`initialize` never tears down an active flow.** If `phase` is already one of +> the in-progress phases (`session`, `check`, `auth`, `form`, `submit`), a +> repeat `initialize` is a **no-op** — it will not create a new session, clear +> tokens, or reset `activeProduct`. Call `reset()` first to start over. + +> **`reset()` is callable from any phase and supersedes in-flight work.** In +> addition to returning `phase` to `idle` (and clearing tokens, `activeProduct`, +> and the `sumsub` sub-tree), `reset()` bumps an internal flow generation so any +> still-pending async step (geolocation, disclaimers, session creation, the +> KYC-required check, or the SumSub sub-flow) discards its result instead of +> writing it onto the now-idle controller. + Phase meanings (from `types.ts`): -| Phase | Meaning | -| --- | --- | -| `idle` | Nothing started. | -| `terms` | Waiting for the customer to accept vendor terms. | -| `session` | Creating the vendor session. | -| `check` | Running the **invisible** connection-check frame. | -| `auth` | Running the **visible** authentication (email OTP) frame. | -| `form` | Authenticated. Auto-runs the KYC-required check when a product is set; otherwise waits for the consumer. | -| `submit` | Submitting the KYC-required check. | -| `done` | Complete — see `kycRequiredByProduct` / `sumsub`. Document verification auto-launches when KYC is required. | -| `error` | Halted — see `error`. | +| Phase | Meaning | +| --------- | ----------------------------------------------------------------------------------------------------------- | +| `idle` | Nothing started. | +| `terms` | Waiting for the customer to accept vendor terms. | +| `session` | Creating the vendor session. | +| `check` | Running the **invisible** connection-check frame. | +| `auth` | Running the **visible** authentication (email OTP) frame. | +| `form` | Authenticated. Auto-runs the KYC-required check when a product is set; otherwise waits for the consumer. | +| `submit` | Submitting the KYC-required check. | +| `done` | Complete — see `kycRequiredByProduct` / `sumsub`. Document verification auto-launches when KYC is required. | +| `error` | Halted — see `error`. | --- @@ -381,12 +392,22 @@ sequenceDiagram Frame->>UI: { kind:"complete", meta:{channelId},
payload:{ status, credentials, customer } } UI->>Ctrl: handleFrameMessage({ message }) - Note over Ctrl: 1. store customer.id (moonpayCustomerId)
2. decryptCredentials(envelope, privKey)
3. route by channelId (ch_1 Check / ch_2 Auth) + Note over Ctrl: 1. phase guard: only honor ch_1 in `check`,
ch_2 in `auth` — else drop the message
2. store customer.id (moonpayCustomerId)
3. decryptCredentials(envelope, privKey)
4. route by channelId (ch_1 Check / ch_2 Auth) Ctrl->>Ctrl: apply outcome → next phase ``` Channels: `ch_1` = Check, `ch_2` = Auth, `ch_reset` = Reset. +> **Phase-guarded intake.** A `complete` is only processed when the flow is +> actually waiting on that frame — `ch_1` while `phase === 'check'`, `ch_2` +> while `phase === 'auth'`. Because both outcome handlers advance `phase` to +> `form` synchronously, a stale, duplicate, or post-`reset()` `complete` +> (delivered once the flow has moved on) is dropped before any state is touched, +> so it cannot resurrect tokens, re-store `customer.id`, or rewind `phase`. +> Frame messages are external input and are not covered by the `#generation` +> guard used for the controller's own async steps, so this boundary check is how +> late frame posts are neutralized. + Credential decryption (`crypto.ts`): ```mermaid @@ -424,14 +445,23 @@ stateDiagram-v2 idle --> creatingSession : startSumSub() creatingSession --> fetchingToken : createUkycSession() ok fetchingToken --> launching : submitWrappedKey() ok - launching --> inProgress : launcher.onStatusChange - inProgress --> complete : status = Completed - launching --> complete : SDK resolves + launching --> inProgress : onStatusChange (non-Completed) + launching --> complete : onStatusChange = Completed + inProgress --> complete : onStatusChange = Completed + launching --> failed : resolves without a Completed status + inProgress --> failed : resolves without a Completed status creatingSession --> failed : error fetchingToken --> failed : error launching --> failed : launcher unavailable / error ``` +> **Completion is status-driven, not resolution-driven.** A resolved `launch` +> is only recorded as `complete` when the SDK reported the `Completed` status +> via `onStatusChange` at least once. If `launch` resolves without ever having +> reported `Completed` (e.g. the applicant abandoned the flow, or a non-success +> outcome), the controller records `failed` — so consumers never mistake an +> unfinished flow for a verified one. + The `KycSumSubLauncher` interface (injected per client): ```ts @@ -442,8 +472,10 @@ type KycSumSubLauncher = { ``` `launch` receives `applicantAccessToken`, an `onTokenExpiration` callback (the -controller re-runs `submitWrappedKey` to refresh), and an `onStatusChange` -callback that the controller maps into `sumsub.status`. +controller re-runs `submitWrappedKey` to refresh — but **refuses to refresh +after a `reset()`**, throwing instead so a still-open SDK cannot keep an +orphaned UKYC session alive), and an `onStatusChange` callback that the +controller maps into `sumsub.status`. --- @@ -609,40 +641,39 @@ graph LR shared -. injected adapters .- client ``` -| Concern | Owner | -| --- | --- | -| Flow phase machine & state | `KycController` (shared) | -| UKYC HTTP + validation + retries | `KycService` (shared) | -| Credential decryption / key exchange | `crypto.ts` (shared) | -| Frame message semantics | `KycController.handleFrameMessage` (shared) | -| Frame **transport** (WebView/iframe) | Client | -| SumSub SDK presentation | Client (via `KycSumSubLauncher`) | -| Auth bearer token / geolocation | Other controllers (via messenger) | -| Persistence of state | Client (base-controller persistence) | +| Concern | Owner | +| ------------------------------------ | ------------------------------------------- | +| Flow phase machine & state | `KycController` (shared) | +| UKYC HTTP + validation + retries | `KycService` (shared) | +| Credential decryption / key exchange | `crypto.ts` (shared) | +| Frame message semantics | `KycController.handleFrameMessage` (shared) | +| Frame **transport** (WebView/iframe) | Client | +| SumSub SDK presentation | Client (via `KycSumSubLauncher`) | +| Auth bearer token / geolocation | Other controllers (via messenger) | +| Persistence of state | Client (base-controller persistence) | --- ### Appendix — key source files -| File | Responsibility | -| --- | --- | +| File | Responsibility | +| ---------------------- | ----------------------------------------------------- | | `src/KycController.ts` | Stateful orchestrator, phase machine, frame protocol. | -| `src/KycService.ts` | Stateless UKYC HTTP client + superstruct validation. | -| `src/crypto.ts` | X25519 ECDH + AES-256-GCM credential decryption. | -| `src/selectors.ts` | Memoized selectors over controller state. | -| `src/types.ts` | `KycPhase`, `KycProduct`, `KycSumSubLauncher`, etc. | -| `src/countryCodes.ts` | ISO alpha-2 → alpha-3 mapping. | -| `src/index.ts` | Public exports (no barrel wildcards). | +| `src/KycService.ts` | Stateless UKYC HTTP client + superstruct validation. | +| `src/crypto.ts` | X25519 ECDH + AES-256-GCM credential decryption. | +| `src/selectors.ts` | Memoized selectors over controller state. | +| `src/types.ts` | `KycPhase`, `KycProduct`, `KycSumSubLauncher`, etc. | +| `src/countryCodes.ts` | ISO alpha-2 → alpha-3 mapping. | +| `src/index.ts` | Public exports (no barrel wildcards). | Reference client (metamask-mobile): -| File | Responsibility | -| --- | --- | -| `app/core/Engine/controllers/kyc/kyc-controller-init.ts` | Construct controller + inject launcher. | -| `app/core/Engine/controllers/kyc/kyc-service-init.ts` | Construct service. | -| `app/core/Engine/controllers/kyc/reactNativeSumSubLauncher.ts` | Native SumSub adapter. | -| `app/core/Engine/messengers/kyc/*.ts` | Messenger delegation. | -| `app/components/Views/MoonpayDemo/useKycFlow.ts` | React ↔ controller binding. | -| `app/components/Views/MoonpayDemo/useMoonpayFrame.ts` | WebView postMessage bridge. | -| `app/selectors/kycController.ts` | Redux selectors. | - +| File | Responsibility | +| -------------------------------------------------------------- | --------------------------------------- | +| `app/core/Engine/controllers/kyc/kyc-controller-init.ts` | Construct controller + inject launcher. | +| `app/core/Engine/controllers/kyc/kyc-service-init.ts` | Construct service. | +| `app/core/Engine/controllers/kyc/reactNativeSumSubLauncher.ts` | Native SumSub adapter. | +| `app/core/Engine/messengers/kyc/*.ts` | Messenger delegation. | +| `app/components/Views/MoonpayDemo/useKycFlow.ts` | React ↔ controller binding. | +| `app/components/Views/MoonpayDemo/useMoonpayFrame.ts` | WebView postMessage bridge. | +| `app/selectors/kycController.ts` | Redux selectors. | From 3d2cf99249793abaf64eb0430c93aa0852879d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Fri, 24 Jul 2026 17:36:42 +0200 Subject: [PATCH 18/19] feat: update codeowner files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- .github/CODEOWNERS | 7 ++++++- codeowners.ts | 8 ++++++++ teams.json | 3 ++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 360764abba6..d535abd6be9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -75,6 +75,9 @@ ## Product Safety Team /packages/phishing-controller @MetaMask/product-safety +## Universal KYC Team +/packages/kyc-controller @MetaMask/universal-kyc + ## Swaps-Bridge Team /packages/bridge-controller @MetaMask/swaps-engineers /packages/bridge-status-controller @MetaMask/swaps-engineers @@ -286,4 +289,6 @@ /packages/money-account-upgrade-controller/package.json @MetaMask/earn @MetaMask/delegation @MetaMask/core-platform /packages/money-account-upgrade-controller/CHANGELOG.md @MetaMask/earn @MetaMask/delegation @MetaMask/core-platform /packages/snap-account-service/package.json @MetaMask/accounts-engineers @MetaMask/core-platform -/packages/snap-account-service/CHANGELOG.md @MetaMask/accounts-engineers @MetaMask/core-platform \ No newline at end of file +/packages/snap-account-service/CHANGELOG.md @MetaMask/accounts-engineers @MetaMask/core-platform +/packages/kyc-controller/package.json @MetaMask/universal-kyc @MetaMask/core-platform +/packages/kyc-controller/CHANGELOG.md @MetaMask/universal-kyc @MetaMask/core-platform \ No newline at end of file diff --git a/codeowners.ts b/codeowners.ts index 2a864d499ba..3adf343309c 100644 --- a/codeowners.ts +++ b/codeowners.ts @@ -185,6 +185,9 @@ const PACKAGES: Record = { teams: ['@MetaMask/accounts-engineers', '@MetaMask/core-platform'], initializationPath: 'keyring-controller', }, + 'kyc-controller': { + teams: ['@MetaMask/universal-kyc'], + }, 'local-node-utils': { teams: [ '@MetaMask/mobile-platform', @@ -492,6 +495,10 @@ function buildTeamSections(): CodeownersSection[] { title: 'Product Safety Team', rules: [buildRuleForPackage('phishing-controller')], }, + { + title: 'Universal KYC Team', + rules: [buildRuleForPackage('kyc-controller')], + }, { title: 'Swaps-Bridge Team', rules: [ @@ -690,6 +697,7 @@ function buildPackageReleaseSection(): CodeownersSection { 'chomp-api-service', 'money-account-upgrade-controller', 'snap-account-service', + 'kyc-controller', ] as const satisfies (keyof typeof PACKAGES)[]; return { diff --git a/teams.json b/teams.json index b20ada66d97..8c95f3e411b 100644 --- a/teams.json +++ b/teams.json @@ -94,5 +94,6 @@ "metamask/money-account-upgrade-controller": "team-earn", "metamask/snap-account-service": "team-accounts-framework", "metamask/platform-api-docs": "team-core-platform", - "metamask/smart-transactions-controller": "team-transactions" + "metamask/smart-transactions-controller": "team-transactions", + "metamask/kyc-controller": "team-universal-kyc" } From 2c4805df0289b36c23dfa242a51aa6fb286ed940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Van=20Eyck?= Date: Fri, 24 Jul 2026 18:08:35 +0200 Subject: [PATCH 19/19] fix: lint issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Van Eyck --- packages/kyc-controller/README.md | 2 +- packages/kyc-controller/src/KycService.test.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/kyc-controller/README.md b/packages/kyc-controller/README.md index a8d4ad915f0..84906e80e89 100644 --- a/packages/kyc-controller/README.md +++ b/packages/kyc-controller/README.md @@ -674,6 +674,6 @@ Reference client (metamask-mobile): | `app/core/Engine/controllers/kyc/kyc-service-init.ts` | Construct service. | | `app/core/Engine/controllers/kyc/reactNativeSumSubLauncher.ts` | Native SumSub adapter. | | `app/core/Engine/messengers/kyc/*.ts` | Messenger delegation. | -| `app/components/Views/MoonpayDemo/useKycFlow.ts` | React ↔ controller binding. | +| `app/components/Views/MoonpayDemo/useKycFlow.ts` | React ↔ controller binding. | | `app/components/Views/MoonpayDemo/useMoonpayFrame.ts` | WebView postMessage bridge. | | `app/selectors/kycController.ts` | Redux selectors. | diff --git a/packages/kyc-controller/src/KycService.test.ts b/packages/kyc-controller/src/KycService.test.ts index c359add4e76..86f582538ab 100644 --- a/packages/kyc-controller/src/KycService.test.ts +++ b/packages/kyc-controller/src/KycService.test.ts @@ -255,7 +255,9 @@ describe('KycService', () => { describe('baseUrl override', () => { it('uses the provided baseUrl instead of the env-derived URL', async () => { const customUrl = 'https://kyc-api.local.test'; - const disclaimers = [{ id: '1', display_name: 'Terms', url: 'https://t' }]; + const disclaimers = [ + { id: '1', display_name: 'Terms', url: 'https://t' }, + ]; nock(customUrl) .get('/vendors/moonpay/disclaimers') .query({ country: 'USA' })