diff --git a/.gitignore b/.gitignore index 74d7485a..d4e21b49 100644 --- a/.gitignore +++ b/.gitignore @@ -124,7 +124,7 @@ dist # Stores VSCode versions used for testing VSCode extensions .vscode-test -.vscode +#.vscode # yarn v2 .yarn/cache diff --git a/.prettierignore b/.prettierignore index 1b906ac3..9b32615a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -124,7 +124,7 @@ dist # Stores VSCode versions used for testing VSCode extensions .vscode-test -.vscode +#.vscode # yarn v2 .yarn/cache diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0c4e6e1d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } +} diff --git a/package.json b/package.json index 6932d6c1..0146be13 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "description": "React hooks for using Algorand compatible wallets in dApps.", "scripts": { "dev": "yarn storybook", - "build": "rm -rf dist && rollup -c", + "build": "rimraf dist && rollup -c", "test": "jest", "lint": "eslint '**/*.{js,ts,tsx}'", "format": "prettier --check '**/*.{js,ts,tsx}'", @@ -60,6 +60,7 @@ "algosdk": "^2.1.0", "babel-jest": "^29.1.2", "babel-loader": "^9.0.0", + "buffer": "^6.0.3", "commitizen": "4.3.0", "css-loader": "^6.5.1", "cz-conventional-changelog": "3.3.0", @@ -80,6 +81,7 @@ "react-dom": "^18.2.0", "release-it": "^16.1.0", "require-from-string": "^2.0.2", + "rimraf": "^5.0.1", "rollup": "^3.3.0", "rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-dts": "^5.0.0", diff --git a/src/clients/custom/client.ts b/src/clients/custom/client.ts new file mode 100644 index 00000000..c1665a06 --- /dev/null +++ b/src/clients/custom/client.ts @@ -0,0 +1,104 @@ +import Algod, { getAlgodClient } from '../../algod' +import BaseClient from '../base' +import { DEFAULT_NETWORK, PROVIDER_ID } from '../../constants' +import { debugLog } from '../../utils/debugLog' +import { ICON } from './constants' +import type _algosdk from 'algosdk' +import type { Network } from '../../types/node' +import type { InitParams } from '../../types/providers' +import type { Metadata, Wallet } from '../../types/wallet' +import type { CustomProvider, CustomWalletClientConstructor } from './types' + +class CustomWalletClient extends BaseClient { + network: Network + providerProxy: CustomProvider + + static metadata: Metadata = { + id: PROVIDER_ID.CUSTOM, + icon: ICON, + isWalletConnect: false, + name: 'Custom' + } + + constructor({ + providerProxy, + metadata, + algosdk, + algodClient, + network + }: CustomWalletClientConstructor) { + super(metadata, algosdk, algodClient) + + this.providerProxy = providerProxy + this.network = network + } + + static async init({ + clientOptions, + algodOptions, + algosdkStatic, + network = DEFAULT_NETWORK + }: InitParams): Promise { + try { + debugLog(`${PROVIDER_ID.CUSTOM.toUpperCase()} initializing...`) + + if (!clientOptions) { + throw new Error(`Attempt to create custom wallet with no provider specified.`) + } + + const algosdk = algosdkStatic || (await Algod.init(algodOptions)).algosdk + const algodClient = getAlgodClient(algosdk, algodOptions) + + try { + return new CustomWalletClient({ + providerProxy: clientOptions.getProvider({ + algod: algodClient, + algosdkStatic: algosdk, + network + }), + metadata: { + ...CustomWalletClient.metadata, + name: clientOptions.name, + icon: clientOptions.icon ?? CustomWalletClient.metadata.icon + }, + algodClient, + algosdk, + network + }) + } finally { + debugLog(`${PROVIDER_ID.CUSTOM.toUpperCase()} initialized`, '✅') + } + } catch (e) { + console.error('Error initializing...', e) + return null + } + } + + async connect(): Promise { + return await this.providerProxy.connect(this.metadata) + } + + async disconnect() { + await this.providerProxy.disconnect() + } + + async reconnect(): Promise { + return await this.providerProxy.reconnect(this.metadata) + } + + async signTransactions( + connectedAccounts: string[], + txnGroups: Uint8Array[] | Uint8Array[][], + indexesToSign?: number[], + returnGroup = true + ) { + return await this.providerProxy.signTransactions( + connectedAccounts, + txnGroups, + indexesToSign, + returnGroup + ) + } +} + +export default CustomWalletClient diff --git a/src/clients/custom/constants.ts b/src/clients/custom/constants.ts new file mode 100644 index 00000000..f09ef090 --- /dev/null +++ b/src/clients/custom/constants.ts @@ -0,0 +1,2 @@ +export const ICON = + "data:image/svg+xml,%3Csvg fill='%23000000' width='800px' height='800px' viewBox='0 0 24 24' id='wallet' data-name='Flat Line' xmlns='http://www.w3.org/2000/svg' class='icon flat-line'%3E%3Cpath id='secondary' d='M16,12h5V8H5A2,2,0,0,1,3,6V19a1,1,0,0,0,1,1H20a1,1,0,0,0,1-1V16H16a1,1,0,0,1-1-1V13A1,1,0,0,1,16,12Z' style='fill: rgb(44, 169, 188); stroke-width: 2;'%3E%3C/path%3E%3Cpath id='primary' d='M19,4H5A2,2,0,0,0,3,6H3A2,2,0,0,0,5,8H21' style='fill: none; stroke: rgb(0, 0, 0); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;'%3E%3C/path%3E%3Cpath id='primary-2' data-name='primary' d='M21,8V19a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V6A2,2,0,0,0,5,8Zm0,4H16a1,1,0,0,0-1,1v2a1,1,0,0,0,1,1h5Z' style='fill: none; stroke: rgb(0, 0, 0); stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;'%3E%3C/path%3E%3C/svg%3E" diff --git a/src/clients/custom/index.ts b/src/clients/custom/index.ts new file mode 100644 index 00000000..43b93ed1 --- /dev/null +++ b/src/clients/custom/index.ts @@ -0,0 +1,3 @@ +import custom from './client' + +export default custom diff --git a/src/clients/custom/types.ts b/src/clients/custom/types.ts new file mode 100644 index 00000000..486d4733 --- /dev/null +++ b/src/clients/custom/types.ts @@ -0,0 +1,33 @@ +import type algosdk from 'algosdk' +import type { Network } from '../../types/node' +import type { Metadata, Wallet } from '../../types/wallet' + +export type CustomOptions = { + name: string + icon?: string + getProvider: (params: { + network?: Network + algod?: algosdk.Algodv2 + algosdkStatic?: typeof algosdk + }) => CustomProvider +} + +export type CustomProvider = { + connect(metadata: Metadata): Promise + disconnect(): Promise + reconnect(metadata: Metadata): Promise + signTransactions( + connectedAccounts: string[], + txnGroups: Uint8Array[] | Uint8Array[][], + indexesToSign?: number[], + returnGroup?: boolean + ): Promise +} + +export type CustomWalletClientConstructor = { + providerProxy: CustomProvider + metadata: Metadata + algosdk: typeof algosdk + algodClient: algosdk.Algodv2 + network: Network +} diff --git a/src/clients/index.ts b/src/clients/index.ts index 8299d942..0df3d957 100644 --- a/src/clients/index.ts +++ b/src/clients/index.ts @@ -7,8 +7,21 @@ import algosigner from './algosigner' import walletconnect from './walletconnect2' import kmd from './kmd' import mnemonic from './mnemonic' +import { CustomProvider } from './custom/types' +import custom from './custom' -export { pera, myalgo, defly, exodus, algosigner, walletconnect, kmd, mnemonic } +export { + pera, + myalgo, + defly, + exodus, + algosigner, + walletconnect, + kmd, + mnemonic, + custom, + CustomProvider +} export default { [pera.metadata.id]: pera, @@ -19,5 +32,6 @@ export default { [algosigner.metadata.id]: algosigner, [walletconnect.metadata.id]: walletconnect, [kmd.metadata.id]: kmd, - [mnemonic.metadata.id]: mnemonic + [mnemonic.metadata.id]: mnemonic, + [custom.metadata.id]: custom } diff --git a/src/components/Example/Example.tsx b/src/components/Example/Example.tsx index 202d7b83..4da7dddf 100644 --- a/src/components/Example/Example.tsx +++ b/src/components/Example/Example.tsx @@ -1,10 +1,12 @@ import React from 'react' import { DeflyWalletConnect } from '@blockshake/defly-connect' import { DaffiWalletConnect } from '@daffiwallet/connect' -import { WalletProvider, PROVIDER_ID, useInitializeProviders } from '../../index' +import { WalletProvider, PROVIDER_ID, useInitializeProviders, Network } from '../../index' import Account from './Account' import Connect from './Connect' import Transact from './Transact' +import algosdk from 'algosdk' +import { ManualGoalSigningAlertPromptProvider } from './TestManualProvider' const getDynamicPeraWalletConnect = async () => { const PeraWalletConnect = (await import('@perawallet/connect')).PeraWalletConnect @@ -17,7 +19,20 @@ export default function ConnectWallet() { { id: PROVIDER_ID.DEFLY, clientStatic: DeflyWalletConnect }, { id: PROVIDER_ID.PERA, getDynamicClient: getDynamicPeraWalletConnect }, { id: PROVIDER_ID.DAFFI, clientStatic: DaffiWalletConnect }, - { id: PROVIDER_ID.EXODUS } + { id: PROVIDER_ID.EXODUS }, + { + id: PROVIDER_ID.CUSTOM, + clientOptions: { + name: 'Manual', + getProvider: (params: { + network?: Network + algod?: algosdk.Algodv2 + algosdkStatic?: typeof algosdk + }) => { + return new ManualGoalSigningAlertPromptProvider(params.algosdkStatic ?? algosdk) + } + } + } ] }) diff --git a/src/components/Example/TestManualProvider.ts b/src/components/Example/TestManualProvider.ts new file mode 100644 index 00000000..d7a0e994 --- /dev/null +++ b/src/components/Example/TestManualProvider.ts @@ -0,0 +1,157 @@ +import { CustomProvider } from '../../clients/custom/types' +import { Buffer } from 'buffer' +import type _algosdk from 'algosdk' +import { PROVIDER_ID, Metadata } from '../../index' + +/** + * Example of a custom wallet provider that facilitates manual signing via goal CLI using alert / prompt as a UI. + */ +export class ManualGoalSigningAlertPromptProvider implements CustomProvider { + algosdk: typeof _algosdk + + constructor(algosdkStatic: typeof _algosdk) { + this.algosdk = algosdkStatic + } + + // eslint-disable-next-line @typescript-eslint/require-await + async connect(metadata: Metadata) { + let address = prompt('Enter address of your account') + if (address && !this.algosdk.isValidAddress(address)) { + alert('Invalid address; please try again') + address = null + } + const authAddress = address + ? prompt("Enter address of the signing account; leave blank if account hasn't been rekeyed") + : undefined + + return { + ...metadata, + accounts: address + ? [ + { + address, + name: address, + providerId: PROVIDER_ID.CUSTOM, + authAddr: authAddress === null || authAddress === address ? undefined : authAddress + } + ] + : [] + } + } + + async disconnect() { + // + } + + // eslint-disable-next-line @typescript-eslint/require-await + async reconnect(_metadata: Metadata) { + return null + } + + async signTransactions( + connectedAccounts: string[], + txnGroups: Uint8Array[] | Uint8Array[][], + indexesToSign?: number[] | undefined, + _returnGroup?: boolean | undefined + ): Promise { + // If txnGroups is a nested array, flatten it + const transactions: Uint8Array[] = Array.isArray(txnGroups[0]) + ? (txnGroups as Uint8Array[][]).flatMap((txn) => txn) + : (txnGroups as Uint8Array[]) + + // Decode the transactions to access their properties. + const decodedTxns = transactions.map((txn) => { + return this.algosdk.decodeObj(txn) + }) as Array<_algosdk.EncodedTransaction | _algosdk.EncodedSignedTransaction> + + const signedTxns: Array = [] + + let idx = -1 + for (const dtxn of decodedTxns) { + idx++ + const isSigned = 'txn' in dtxn + + // push the incoming txn into signed, we'll overwrite it later + signedTxns.push(transactions[idx]) + + // Its already signed, skip it + if (isSigned) { + continue + // Not specified in indexes to sign, skip it + } else if (indexesToSign && indexesToSign.length && !indexesToSign.includes(Number(idx))) { + continue + } + // Not to be signed by our signer, skip it + else if (!connectedAccounts.includes(this.algosdk.encodeAddress(dtxn.snd))) { + continue + } + + const unsignedTxn = this.algosdk.decodeUnsignedTransaction(transactions[idx]) + + const forSigning = Buffer.from( + this.algosdk.encodeObj({ + txn: unsignedTxn.get_obj_for_encoding() + }) + ).toString('base64') + alert( + `Here is the unsigned transaction bytes in base64 that needs signing, press OK to copy to clipboard for signing: ${forSigning}` + ) + + console.log('Here is the unsigned transaction bytes in base64 that need signing', forSigning) + await navigator.clipboard.writeText(forSigning).catch(async (e) => { + console.warn('Error copying from clipboard, trying again in async thread', e) + // Try async to avoid permission issue + await new Promise((resolve, reject) => + setTimeout(() => { + navigator.clipboard + .writeText(forSigning) + .then(() => { + resolve() + }) + .catch((e) => { + alert( + 'Clipboard copy failed; check you have granted permission to clipboard and/or try copying it from the developer console' + ) + reject(e) + }) + }, 1) + ) + }) + const clipboard = await navigator.clipboard.readText().catch((e) => { + alert( + 'Clipboard copy failed; check you have granted permission to clipboard and/or try copying it from the developer console' + ) + throw e + }) + if (clipboard !== forSigning) { + alert( + 'Clipboard copy failed; check you have granted permission to clipboard and/or try copying it from the developer console' + ) + } + + alert(`### Signing instructions ### + + 1. Check the value landed in your clipboard and if not check the web browser isn't waiting for you to grant permission to clipboard and either way try again or copy the value from the developer console if all else fails (F12) + 2. Load the value in the clipboard into a file e.g. unsigned.txn: + \`echo {paste value} | base64 -d > unsigned.txn\` + 3. Inspect it: + \`goal clerk inspect unsigned.txn\` + 4. Sign it e.g. + \`goal clerk sign -i unsigned.txn -o signed.txn\` + 5. Output the signed transaction e.g. + \`cat signed.txn | base64\` + 6. Copy the signed transaction output to clipboard + 7. Press OK here and then paste into the next prompt.`) + + const signed = prompt('Provide the base 64 encoded signed transaction') + if (!signed) { + throw new Error('Provided invalid signed transaction') + } + + const encoded = Buffer.from(signed, 'base64') + signedTxns[idx] = encoded + } + + return signedTxns + } +} diff --git a/src/constants/constants.ts b/src/constants/constants.ts index f172bac9..0aa4a278 100644 --- a/src/constants/constants.ts +++ b/src/constants/constants.ts @@ -2,6 +2,7 @@ import { Network } from '../types/node' export enum PROVIDER_ID { KMD = 'kmd', + CUSTOM = 'custom', PERA = 'pera', DAFFI = 'daffi', MYALGO = 'myalgo', diff --git a/src/hooks/useWallet.ts b/src/hooks/useWallet.ts index 21ad1ec2..87665dbc 100644 --- a/src/hooks/useWallet.ts +++ b/src/hooks/useWallet.ts @@ -36,31 +36,35 @@ export default function useWallet() { } const supportedClients = Object.keys(clients) as PROVIDER_ID[] - setProviders( - supportedClients.map((id) => { - return { - ...allClients[id], - accounts: getAccountsByProvider(id), - isActive: activeAccount?.providerId === id, - isConnected: connectedAccounts.some((accounts) => accounts.providerId === id), - connect: () => connect(id), - disconnect: () => disconnect(id), - reconnect: () => reconnect(id), - setActiveProvider: () => setActive(id), - setActiveAccount: (account: string) => selectActiveAccount(id, account) - } - }) + supportedClients + // Remove any clients that didn't initialise + .filter((id) => !!clients?.[id]) + .map((id) => { + return { + ...allClients[id], + // Override static details with any instance details + ...clients[id], + accounts: getAccountsByProvider(id), + isActive: activeAccount?.providerId === id, + isConnected: connectedAccounts.some((accounts) => accounts.providerId === id), + connect: () => connect(id), + disconnect: () => disconnect(id), + reconnect: () => reconnect(id), + setActiveProvider: () => setActive(id), + setActiveAccount: (account: string) => selectActiveAccount(id, account) + } + }) ) // eslint-disable-next-line react-hooks/exhaustive-deps }, [clients, connectedAccounts, connectedActiveAccounts, activeAccount]) const getClient = (id?: PROVIDER_ID): WalletClient => { - if (!id) throw new Error('Provier ID is missing.') + if (!id) throw new Error('Provider ID is missing.') const walletClient = clients?.[id] - if (!walletClient) throw new Error('Client not found for ID') + if (!walletClient) throw new Error(`Client not found for ID: ${id}`) return walletClient } diff --git a/src/testUtils/mockClients.ts b/src/testUtils/mockClients.ts index 20d955a0..285e22e1 100644 --- a/src/testUtils/mockClients.ts +++ b/src/testUtils/mockClients.ts @@ -18,9 +18,11 @@ import { PROVIDER_ID } from '../constants' import type { AlgoSigner } from '../clients/algosigner/types' import type { Exodus } from '../clients/exodus/types' import type { Account, ClientOptions } from '../types' +import CustomWalletClient from '../clients/custom/client' type ClientTypeMap = { [PROVIDER_ID.ALGOSIGNER]: AlgoSignerClient + [PROVIDER_ID.CUSTOM]: CustomWalletClient [PROVIDER_ID.DAFFI]: DaffiWalletClient [PROVIDER_ID.DEFLY]: DeflyWalletClient [PROVIDER_ID.EXODUS]: ExodusClient @@ -47,6 +49,7 @@ export const createMockClient = ( [PROVIDER_ID.DEFLY]: createDeflyMockInstance, [PROVIDER_ID.EXODUS]: createExodusMockInstance, [PROVIDER_ID.KMD]: createKmdMockInstance, + [PROVIDER_ID.CUSTOM]: createCustomMockInstance, [PROVIDER_ID.MNEMONIC]: createMnemonicMockInstance, [PROVIDER_ID.MYALGO]: createMyAlgoMockInstance, [PROVIDER_ID.PERA]: createPeraMockInstance, @@ -268,6 +271,43 @@ export const createKmdMockInstance = ( return mockKmdWalletClient } +// CUSTOM +export const createCustomMockInstance = ( + clientOptions?: ClientOptions, + accounts: Array = [] +): CustomWalletClient => { + const mockCustomWalletClient = new CustomWalletClient({ + metadata: { + id: PROVIDER_ID.CUSTOM, + name: 'CUSTOM', + icon: 'custom-icon-b64', + isWalletConnect: false + }, + providerProxy: undefined as any, + algosdk, + algodClient: { + accountInformation: () => ({ + do: () => Promise.resolve({}) + }) + } as any, + network: 'test-network', + ...(clientOptions && clientOptions) + }) + + // Mock the connect method + mockCustomWalletClient.connect = jest.fn().mockImplementation(() => + Promise.resolve({ + ...mockCustomWalletClient.metadata, + accounts + }) + ) + + // Mock the disconnect method + mockCustomWalletClient.disconnect = jest.fn().mockImplementation(() => Promise.resolve()) + + return mockCustomWalletClient +} + // MNEMONIC export const createMnemonicMockInstance = ( clientOptions?: ClientOptions, diff --git a/src/types/providers.ts b/src/types/providers.ts index 44b22f78..957c80e8 100644 --- a/src/types/providers.ts +++ b/src/types/providers.ts @@ -17,6 +17,7 @@ import type { MyAlgoConnectOptions } from '../clients/myalgo/types' import type { DaffiWalletConnectOptions } from '../clients/daffi/types' import type { NonEmptyArray } from './utilities' import type BaseClient from '../clients/base' +import type { CustomOptions } from 'src/clients/custom/types' export type ProviderConfigMapping = { [PROVIDER_ID.PERA]: { @@ -54,6 +55,11 @@ export type ProviderConfigMapping = { clientStatic?: undefined getDynamicClient?: undefined } + [PROVIDER_ID.CUSTOM]: { + clientOptions?: CustomOptions + clientStatic?: undefined + getDynamicClient?: undefined + } [PROVIDER_ID.ALGOSIGNER]: { clientOptions?: undefined clientStatic?: undefined @@ -119,10 +125,12 @@ type ProviderDef = | (ProviderConfig & OneOfStaticOrDynamicClient) | ProviderConfig | ProviderConfig + | ProviderConfig | PROVIDER_ID.EXODUS | PROVIDER_ID.KMD | PROVIDER_ID.ALGOSIGNER | PROVIDER_ID.MNEMONIC + | PROVIDER_ID.CUSTOM export type ProvidersArray = NonEmptyArray diff --git a/yarn.lock b/yarn.lock index 427e32c3..c8a73bbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1396,6 +1396,18 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1996,6 +2008,11 @@ buffer "^6.0.3" qr-code-styling "1.6.0-rc.1" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@pmmmwh/react-refresh-webpack-plugin@^0.5.3": version "0.5.10" resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8" @@ -7981,6 +7998,14 @@ foreground-child@^2.0.0: cross-spawn "^7.0.0" signal-exit "^3.0.2" +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + fork-ts-checker-webpack-plugin@^4.1.6: version "4.1.6" resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz#5055c703febcf37fa06405d400c122b905167fc5" @@ -8369,6 +8394,17 @@ glob@7.2.3, glob@^7.0.0, glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.2.5: + version "10.3.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" + integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.0.3" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + glob@^8.0.3: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" @@ -9803,6 +9839,15 @@ iterate-value@^1.0.2: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" +jackspeak@^2.0.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.0.tgz#aa228a94de830f31d4e4f0184427ce91c4ff1493" + integrity sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jest-canvas-mock@^2.5.0: version "2.5.2" resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.5.2.tgz#7e21ebd75e05ab41c890497f6ba8a77f915d2ad6" @@ -10770,6 +10815,11 @@ lru-cache@^7.14.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +"lru-cache@^9.1.1 || ^10.0.0": + version "10.0.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" + integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== + lz-string@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" @@ -11136,6 +11186,13 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -11188,6 +11245,11 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974" + integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -12143,6 +12205,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -13583,6 +13653,13 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.1.tgz#0881323ab94ad45fec7c0221f27ea1a142f3f0d0" + integrity sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg== + dependencies: + glob "^10.2.5" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -14028,6 +14105,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -14349,7 +14431,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -14440,6 +14522,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -14447,13 +14536,6 @@ strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -15792,19 +15874,19 @@ worker-rpc@^0.1.0: dependencies: microevent.ts "~0.1.1" -wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== +wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0"