From 9edfafe2a68753edb5a90ce2fc388765653bf4af Mon Sep 17 00:00:00 2001 From: Eren Yeager Date: Fri, 16 Aug 2024 11:54:08 +0000 Subject: [PATCH] feat: introducing hub, our new wallet management --- package.json | 2 +- wallets/core/package.json | 27 +- wallets/core/src/builders/action.ts | 89 ++ wallets/core/src/builders/mod.ts | 5 + wallets/core/src/builders/namespace.ts | 229 +++ wallets/core/src/builders/provider.ts | 61 + wallets/core/src/builders/types.ts | 29 + wallets/core/src/hub/helpers.ts | 11 + wallets/core/src/hub/hub.ts | 122 ++ wallets/core/src/hub/mod.ts | 10 + wallets/core/src/hub/namespaces/errors.ts | 8 + wallets/core/src/hub/namespaces/mod.ts | 9 + .../core/src/hub/namespaces/namespace.test.ts | 235 +++ wallets/core/src/hub/namespaces/namespace.ts | 340 ++++ wallets/core/src/hub/namespaces/types.ts | 54 + wallets/core/src/hub/provider/mod.ts | 9 + .../core/src/hub/provider/provider.test.ts | 186 +++ wallets/core/src/hub/provider/provider.ts | 234 +++ wallets/core/src/hub/provider/types.ts | 40 + wallets/core/src/hub/store/hub.ts | 18 + wallets/core/src/hub/store/mod.ts | 8 + wallets/core/src/hub/store/namespaces.ts | 90 ++ wallets/core/src/hub/store/providers.ts | 97 ++ wallets/core/src/hub/store/selectors.ts | 59 + wallets/core/src/hub/store/store.test.ts | 32 + wallets/core/src/hub/store/store.ts | 26 + wallets/core/src/mod.ts | 36 + wallets/core/src/namespaces/common/actions.ts | 11 + wallets/core/src/namespaces/common/after.ts | 8 + wallets/core/src/namespaces/common/and.ts | 39 + wallets/core/src/namespaces/common/before.ts | 9 + .../core/src/namespaces/common/builders.ts | 19 + wallets/core/src/namespaces/common/helpers.ts | 10 + wallets/core/src/namespaces/common/mod.ts | 13 + wallets/core/src/namespaces/common/types.ts | 48 + wallets/core/src/namespaces/cosmos/types.ts | 10 + wallets/core/src/namespaces/evm/actions.ts | 99 ++ wallets/core/src/namespaces/evm/after.ts | 3 + wallets/core/src/namespaces/evm/and.ts | 5 + wallets/core/src/namespaces/evm/before.ts | 3 + wallets/core/src/namespaces/evm/builders.ts | 10 + wallets/core/src/namespaces/evm/constants.ts | 2 + wallets/core/src/namespaces/evm/eip1193.ts | 1414 +++++++++++++++++ wallets/core/src/namespaces/evm/mod.ts | 9 + wallets/core/src/namespaces/evm/types.ts | 28 + wallets/core/src/namespaces/evm/utils.ts | 52 + wallets/core/src/namespaces/solana/actions.ts | 73 + wallets/core/src/namespaces/solana/after.ts | 3 + wallets/core/src/namespaces/solana/and.ts | 5 + wallets/core/src/namespaces/solana/before.ts | 3 + .../core/src/namespaces/solana/builders.ts | 10 + .../core/src/namespaces/solana/constants.ts | 2 + wallets/core/src/namespaces/solana/mod.ts | 8 + wallets/core/src/namespaces/solana/types.ts | 20 + wallets/core/src/test-utils/fixtures.ts | 9 + wallets/core/src/utils/mod.ts | 8 + wallets/core/src/utils/versions.test.ts | 22 + wallets/core/src/utils/versions.ts | 63 + wallets/core/tests/hub.test.ts | 63 + wallets/core/tests/providers.test.ts | 210 +++ wallets/core/tsconfig.build.json | 4 +- yarn.lock | 809 ++++++---- 62 files changed, 4844 insertions(+), 326 deletions(-) create mode 100644 wallets/core/src/builders/action.ts create mode 100644 wallets/core/src/builders/mod.ts create mode 100644 wallets/core/src/builders/namespace.ts create mode 100644 wallets/core/src/builders/provider.ts create mode 100644 wallets/core/src/builders/types.ts create mode 100644 wallets/core/src/hub/helpers.ts create mode 100644 wallets/core/src/hub/hub.ts create mode 100644 wallets/core/src/hub/mod.ts create mode 100644 wallets/core/src/hub/namespaces/errors.ts create mode 100644 wallets/core/src/hub/namespaces/mod.ts create mode 100644 wallets/core/src/hub/namespaces/namespace.test.ts create mode 100644 wallets/core/src/hub/namespaces/namespace.ts create mode 100644 wallets/core/src/hub/namespaces/types.ts create mode 100644 wallets/core/src/hub/provider/mod.ts create mode 100644 wallets/core/src/hub/provider/provider.test.ts create mode 100644 wallets/core/src/hub/provider/provider.ts create mode 100644 wallets/core/src/hub/provider/types.ts create mode 100644 wallets/core/src/hub/store/hub.ts create mode 100644 wallets/core/src/hub/store/mod.ts create mode 100644 wallets/core/src/hub/store/namespaces.ts create mode 100644 wallets/core/src/hub/store/providers.ts create mode 100644 wallets/core/src/hub/store/selectors.ts create mode 100644 wallets/core/src/hub/store/store.test.ts create mode 100644 wallets/core/src/hub/store/store.ts create mode 100644 wallets/core/src/namespaces/common/actions.ts create mode 100644 wallets/core/src/namespaces/common/after.ts create mode 100644 wallets/core/src/namespaces/common/and.ts create mode 100644 wallets/core/src/namespaces/common/before.ts create mode 100644 wallets/core/src/namespaces/common/builders.ts create mode 100644 wallets/core/src/namespaces/common/helpers.ts create mode 100644 wallets/core/src/namespaces/common/mod.ts create mode 100644 wallets/core/src/namespaces/common/types.ts create mode 100644 wallets/core/src/namespaces/cosmos/types.ts create mode 100644 wallets/core/src/namespaces/evm/actions.ts create mode 100644 wallets/core/src/namespaces/evm/after.ts create mode 100644 wallets/core/src/namespaces/evm/and.ts create mode 100644 wallets/core/src/namespaces/evm/before.ts create mode 100644 wallets/core/src/namespaces/evm/builders.ts create mode 100644 wallets/core/src/namespaces/evm/constants.ts create mode 100644 wallets/core/src/namespaces/evm/eip1193.ts create mode 100644 wallets/core/src/namespaces/evm/mod.ts create mode 100644 wallets/core/src/namespaces/evm/types.ts create mode 100644 wallets/core/src/namespaces/evm/utils.ts create mode 100644 wallets/core/src/namespaces/solana/actions.ts create mode 100644 wallets/core/src/namespaces/solana/after.ts create mode 100644 wallets/core/src/namespaces/solana/and.ts create mode 100644 wallets/core/src/namespaces/solana/before.ts create mode 100644 wallets/core/src/namespaces/solana/builders.ts create mode 100644 wallets/core/src/namespaces/solana/constants.ts create mode 100644 wallets/core/src/namespaces/solana/mod.ts create mode 100644 wallets/core/src/namespaces/solana/types.ts create mode 100644 wallets/core/src/test-utils/fixtures.ts create mode 100644 wallets/core/src/utils/mod.ts create mode 100644 wallets/core/src/utils/versions.test.ts create mode 100644 wallets/core/src/utils/versions.ts create mode 100644 wallets/core/tests/hub.test.ts create mode 100644 wallets/core/tests/providers.test.ts diff --git a/package.json b/package.json index 0d4cec98c3..a8c882c669 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "stream-browserify": "^3.0.0", "tty-browserify": "^0.0.1", "util": "^0.12.3", - "vitest": "^0.33.0", + "vitest": "^2.0.5", "vm-browserify": "^1.1.2" }, "resolutions": { diff --git a/wallets/core/package.json b/wallets/core/package.json index ac5f18ce0a..160a89b7f1 100644 --- a/wallets/core/package.json +++ b/wallets/core/package.json @@ -14,6 +14,22 @@ "./legacy": { "types": "./dist/legacy/mod.d.ts", "default": "./dist/legacy/mod.js" + }, + "./utils": { + "types": "./dist/utils/mod.d.ts", + "default": "./dist/utils/mod.js" + }, + "./namespaces/common": { + "types": "./dist/namespaces/common/mod.d.ts", + "default": "./dist/namespaces/common/mod.js" + }, + "./namespaces/evm": { + "types": "./dist/namespaces/evm/mod.d.ts", + "default": "./dist/namespaces/evm/mod.js" + }, + "./namespaces/solana": { + "types": "./dist/namespaces/solana/mod.d.ts", + "default": "./dist/namespaces/solana/mod.js" } }, "files": [ @@ -21,11 +37,13 @@ "src" ], "scripts": { - "build": "node ../../scripts/build/command.mjs --path wallets/core --inputs src/mod.ts,src/legacy/mod.ts", + "build": "node ../../scripts/build/command.mjs --path wallets/core --inputs src/mod.ts,src/utils/mod.ts,src/legacy/mod.ts,src/namespaces/evm/mod.ts,src/namespaces/solana/mod.ts,src/namespaces/common/mod.ts", "ts-check": "tsc --declaration --emitDeclarationOnly -p ./tsconfig.json", "clean": "rimraf dist", "format": "prettier --write '{.,src}/**/*.{ts,tsx}'", - "lint": "eslint \"**/*.{ts,tsx}\" --ignore-path ../../.eslintignore" + "lint": "eslint \"**/*.{ts,tsx}\" --ignore-path ../../.eslintignore", + "test": "vitest", + "coverage": "vitest run --coverage" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -33,7 +51,10 @@ "react-dom": "^17.0.0 || ^18.0.0" }, "dependencies": { - "rango-types": "^0.1.69" + "caip": "^1.1.1", + "immer": "^10.0.4", + "rango-types": "^0.1.69", + "zustand": "^4.5.2" }, "publishConfig": { "access": "public" diff --git a/wallets/core/src/builders/action.ts b/wallets/core/src/builders/action.ts new file mode 100644 index 0000000000..014238545e --- /dev/null +++ b/wallets/core/src/builders/action.ts @@ -0,0 +1,89 @@ +import type { Actions, Context, HookActions } from '../hub/namespaces/types.js'; +import type { + AnyFunction, + FunctionWithContext, +} from '../namespaces/common/types.js'; + +export interface ActionByBuilder { + actionName: keyof T; + and: HookActions; + or: HookActions; + after: HookActions; + before: HookActions; + action: FunctionWithContext; +} + +/* + * TODO: + * Currently, to use this builder you will write something like this: + * new ActionBuilder('disconnect').after(....) + * + * I couldn't figure it out to be able typescript infer the constructor value as key of actions. + * Ideal usage: + * new ActionBuilder('disconnect').after(....) + * + */ +export class ActionBuilder, K extends keyof T> { + readonly name: K; + #and: HookActions = new Map(); + #or: HookActions = new Map(); + #after: HookActions = new Map(); + #before: HookActions = new Map(); + #action: FunctionWithContext> | undefined; + + constructor(name: K) { + this.name = name; + } + + public and(action: FunctionWithContext>) { + if (!this.#and.has(this.name)) { + this.#and.set(this.name, []); + } + this.#and.get(this.name)?.push(action); + return this; + } + + public or(action: FunctionWithContext>) { + if (!this.#or.has(this.name)) { + this.#or.set(this.name, []); + } + this.#or.get(this.name)?.push(action); + return this; + } + + public before(action: FunctionWithContext>) { + if (!this.#before.has(this.name)) { + this.#before.set(this.name, []); + } + this.#before.get(this.name)?.push(action); + return this; + } + + public after(action: FunctionWithContext>) { + if (!this.#after.has(this.name)) { + this.#after.set(this.name, []); + } + this.#after.get(this.name)?.push(action); + return this; + } + + public action(action: FunctionWithContext>) { + this.#action = action; + return this; + } + + public build(): ActionByBuilder> { + if (!this.#action) { + throw new Error('Your action builder should includes an action.'); + } + + return { + actionName: this.name, + action: this.#action, + before: this.#before, + after: this.#after, + and: this.#and, + or: this.#or, + }; + } +} diff --git a/wallets/core/src/builders/mod.ts b/wallets/core/src/builders/mod.ts new file mode 100644 index 0000000000..ce8394e228 --- /dev/null +++ b/wallets/core/src/builders/mod.ts @@ -0,0 +1,5 @@ +export type { ProxiedNamespace, FindProxiedNamespace } from './types.js'; + +export { NamespaceBuilder } from './namespace.js'; +export { ProviderBuilder } from './provider.js'; +export { ActionBuilder } from './action.js'; diff --git a/wallets/core/src/builders/namespace.ts b/wallets/core/src/builders/namespace.ts new file mode 100644 index 0000000000..bb3de60dae --- /dev/null +++ b/wallets/core/src/builders/namespace.ts @@ -0,0 +1,229 @@ +import type { ActionByBuilder } from './action.js'; +import type { ProxiedNamespace } from './types.js'; +import type { Actions, ActionsMap, Context } from '../hub/namespaces/mod.js'; +import type { NamespaceConfig } from '../hub/store/mod.js'; +import type { FunctionWithContext } from '../namespaces/common/types.js'; + +import { Namespace } from '../hub/mod.js'; + +/** + * There are Namespace's methods that should be called directly on Proxy object. + * The Proxy object is creating in `.build`. + */ +export const allowedMethods = [ + 'init', + 'state', + 'after', + 'before', + 'and', + 'or', + 'store', +] as const; + +export class NamespaceBuilder> { + #id: string; + #providerId: string; + #actions: ActionsMap = new Map(); + /* + * We keep a list of `ActionBuilder` outputs here to use them in separate phases. + * Actually, `ActionBuilder` is packing action and its hooks in one place, here we should expand them and them in appropriate places. + * Eventually, action will be added to `#actions` and its hooks will be added to `Namespace`. + */ + #actionBuilders: ActionByBuilder>[] = []; + #configs: NamespaceConfig; + + constructor(id: string, providerId: string) { + this.#id = id; + this.#providerId = providerId; + this.#configs = {}; + } + + /** There are some predefined configs that can be set for each namespace separately */ + public config( + name: K, + value: NamespaceConfig[K] + ) { + this.#configs[name] = value; + return this; + } + + /** + * Getting a list of actions. + * + * e.g.: + * ```ts + * .action([ + * ["connect", () => {}], + * ["disconnect", () => {}] + * ]) + * ``` + * + */ + public action( + action: (readonly [K, FunctionWithContext>])[] + ): NamespaceBuilder; + + /** + * + * Add a single action + * + * e.g.: + * ```ts + * .action( ["connect", () => {}] ) + * ``` + */ + public action( + action: K, + actionFn: FunctionWithContext> + ): NamespaceBuilder; + + public action(action: ActionByBuilder>): NamespaceBuilder; + + /** + * + * Actions are piece of functionality that a namespace can have, for example it can be a `connect` function + * or a sign function or even a function for updating namespace's internal state. Actions are flexible and can be anything. + * + * Generally, each standard namespace (e.g. evm) has an standard interface defined in `src/namespaces/` + * and provider (which includes namespaces) authors will implement those actions. + * + * You can call this function by a list of actions or a single action. + * + */ + public action( + action: (readonly [K, FunctionWithContext>])[] | K, + actionFn?: FunctionWithContext> + ) { + // List mode + if (Array.isArray(action)) { + action.forEach(([name, actionFnForItem]) => { + this.#actions.set(name, actionFnForItem); + }); + return this; + } + + // Action builder mode + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (typeof action === 'object' && !!action?.actionName) { + this.#actionBuilders.push(action); + return this; + } + + // Single action mode + if (!!actionFn) { + this.#actions.set(action, actionFn); + } + + return this; + } + + /** + * By calling build, an instance of Namespace will be built. + * + * Note: it's not exactly a `Namespace`, it returns a Proxy which add more convenient use like `namespace.connect()` instead of `namespace.run("connect")` + */ + public build(): ProxiedNamespace { + if (this.#isConfigsValid(this.#configs)) { + return this.#buildApi(this.#configs); + } + + throw new Error(`You namespace config isn't valid.`); + } + + // Currently, namespace doesn't has any config. + #isConfigsValid(_config: NamespaceConfig): boolean { + return true; + } + + /* + * Extracting hooks and add them to `Namespace` for the action. + * + * Note: this should be called after `addActionsFromActionBuilders` to ensure the action is added first. + */ + #addHooksFromActionBuilders(namespace: Namespace) { + this.#actionBuilders.forEach((actionByBuild) => { + actionByBuild.after.forEach((afterHooks) => { + afterHooks.map((action) => { + namespace.after(actionByBuild.actionName, action); + }); + }); + + actionByBuild.before.forEach((beforeHooks) => { + beforeHooks.map((action) => { + namespace.before(actionByBuild.actionName, action); + }); + }); + + actionByBuild.and.forEach((andHooks) => { + andHooks.map((action) => { + namespace.and(actionByBuild.actionName, action); + }); + }); + + actionByBuild.or.forEach((orHooks) => { + orHooks.map((action) => { + namespace.or(actionByBuild.actionName, action); + }); + }); + }); + } + + /** + * Iterate over `actionBuilders` and add them to exists `actions`. + * Note: Hooks will be added in a separate phase. + */ + #addActionsFromActionBuilders() { + this.#actionBuilders.forEach((actionByBuild) => { + this.#actions.set(actionByBuild.actionName, actionByBuild.action); + }); + } + + /** + * Build a Proxy object to call actions in a more convenient way. e.g `.connect()` instead of `.run(connect)` + */ + #buildApi(configs: NamespaceConfig): ProxiedNamespace { + this.#addActionsFromActionBuilders(); + const namespace = new Namespace(this.#id, this.#providerId, { + configs, + actions: this.#actions, + }); + this.#addHooksFromActionBuilders(namespace); + + const api = new Proxy(namespace, { + get: (_, property) => { + if (typeof property !== 'string') { + throw new Error( + 'You can use string as your property on Namespace instance.' + ); + } + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore-next-line + const targetValue = namespace[property]; + + if ( + allowedMethods.includes(property as (typeof allowedMethods)[number]) + ) { + return targetValue.bind(namespace); + } + + /* + * This is useful accessing values like `version`, If we don't do this, we should whitelist + * All the values as well, So it can be confusing for someone that only wants to add a public value to `Namespace` + */ + const allowedPublicValues = ['string', 'number']; + if (allowedPublicValues.includes(typeof targetValue)) { + return targetValue; + } + + return namespace.run.bind(namespace, property as keyof T); + }, + set: () => { + throw new Error('You can not set anything on this object.'); + }, + }); + + return api as unknown as ProxiedNamespace; + } +} diff --git a/wallets/core/src/builders/provider.ts b/wallets/core/src/builders/provider.ts new file mode 100644 index 0000000000..0e6646c05a --- /dev/null +++ b/wallets/core/src/builders/provider.ts @@ -0,0 +1,61 @@ +import type { FindProxiedNamespace } from './types.js'; +import type { + CommonNamespaces, + ExtendableInternalActions, + ProviderBuilderOptions, +} from '../hub/provider/mod.js'; +import type { ProviderConfig } from '../hub/store/mod.js'; + +import { Provider } from '../hub/provider/mod.js'; + +export class ProviderBuilder { + #id: string; + #namespaces = new Map(); + #methods: ExtendableInternalActions = {}; + #configs: Partial = {}; + #options: Partial; + + constructor(id: string, options?: ProviderBuilderOptions) { + this.#id = id; + this.#options = options || {}; + } + + public add( + id: K, + namespace: FindProxiedNamespace + ) { + if (this.#options.store) { + namespace.store(this.#options.store); + } + this.#namespaces.set(id, namespace); + return this; + } + + public config( + name: K, + value: ProviderConfig[K] + ) { + this.#configs[name] = value; + return this; + } + + public init(cb: Exclude) { + this.#methods.init = cb; + return this; + } + + public build(): Provider { + if (this.#isConfigsValid(this.#configs)) { + return new Provider(this.#id, this.#namespaces, this.#configs, { + extendInternalActions: this.#methods, + store: this.#options.store, + }); + } + + throw new Error('You need to set all required configs.'); + } + + #isConfigsValid(config: Partial): config is ProviderConfig { + return !!config.info; + } +} diff --git a/wallets/core/src/builders/types.ts b/wallets/core/src/builders/types.ts new file mode 100644 index 0000000000..a70cf9fc04 --- /dev/null +++ b/wallets/core/src/builders/types.ts @@ -0,0 +1,29 @@ +import type { allowedMethods } from './namespace.js'; +import type { Actions, Namespace } from '../hub/namespaces/mod.js'; + +// These should be matched with `/hub/namespace.ts` public values. +type NamespacePublicValues = { + namespaceId: string; + providerId: string; +}; + +/** + * NamespaceBuilder is creating a proxy instead of return Namespace instance. + * The reason is improving access to actions. e.g `.connect()` instead of `.run('connect')` + */ +export type ProxiedNamespace> = T & + Pick, (typeof allowedMethods)[number]> & + NamespacePublicValues; + +/** + * This is useful when you gave a list of namespaces and want to map a key to corresponding namespace. + * + * e.g: + * Type List = { evm: EvmActions, solana: SolanaActions}; + * FindProxiedNamespace<"solana", List> + */ +export type FindProxiedNamespace = T[K] extends Actions< + T[K] +> + ? ProxiedNamespace + : never; diff --git a/wallets/core/src/hub/helpers.ts b/wallets/core/src/hub/helpers.ts new file mode 100644 index 0000000000..f0c0dfc9a8 --- /dev/null +++ b/wallets/core/src/hub/helpers.ts @@ -0,0 +1,11 @@ +/** + * Note: This only works native async, if we are going to support for old transpilers like Babel. + */ +// eslint-disable-next-line @typescript-eslint/ban-types +export function isAsync(fn: Function) { + return fn.constructor.name === 'AsyncFunction'; +} + +export function generateStoreId(providerId: string, namespace: string) { + return `${providerId}$$${namespace}`; +} diff --git a/wallets/core/src/hub/hub.ts b/wallets/core/src/hub/hub.ts new file mode 100644 index 0000000000..760dab26c0 --- /dev/null +++ b/wallets/core/src/hub/hub.ts @@ -0,0 +1,122 @@ +import type { Namespace, State as NamespaceState } from './namespaces/mod.js'; +import type { Provider, State as ProviderState } from './provider/mod.js'; +import type { Store } from './store/mod.js'; + +type HubState = { + [key in string]: ProviderState & { + namespaces: NamespaceState[]; + }; +}; + +type RunAllResult = { + id: string; + provider: unknown; + namespaces: unknown[]; +}; + +interface HubOptions { + store?: Store; +} +export class Hub { + #providers = new Map(); + #options: HubOptions; + + constructor(options?: HubOptions) { + this.#options = options ?? {}; + /* + * TODO: + * config: + * isEagerConnectEnabled? (warning if explicitly calls eagerConnect) + * + */ + this.init(); + } + + init() { + this.runAll('init'); + } + + /* + * Running a specific action (e.g. init) on all namespaces and providers one by one. + * + * TODO: Some of methods may accepts args, with this implementation we only limit to those one without any argument. + */ + runAll(action: string): RunAllResult[] { + const output: RunAllResult[] = []; + + // run action on all providers eagerConnect, disconnect + const providers = this.#providers.values(); + + for (const provider of providers) { + // Calling `action` on `Provider` if exists. + const providerOutput: RunAllResult = { + id: provider.id, + provider: undefined, + namespaces: [], + }; + + const providerMethod = provider[action as keyof Provider]; + if (typeof providerMethod === 'function') { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore-next-line + providerOutput.provider = providerMethod.call(provider); + } + + // Namespace instances can have their own `action` as well. we will call them as well. + const namespaces = provider.getAll().values(); + for (const namespace of namespaces) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore-next-line + const namespaceMethod = namespace[action]; + if (typeof namespaceMethod === 'function') { + const result = namespaceMethod(); + providerOutput.namespaces.push(result); + } + } + + output.push(providerOutput); + } + + return output; + } + + add(id: string, provider: Provider) { + if (this.#options.store) { + provider.store(this.#options.store); + } + this.#providers.set(id, provider); + return this; + } + + get(providerId: string): Provider | undefined { + return this.#providers.get(providerId); + } + + getAll() { + return this.#providers; + } + + state(): HubState { + const output = this.runAll('state'); + const res: HubState = {}; + + output.forEach((result) => { + const namespaces: NamespaceState[] = []; + result.namespaces.forEach((b) => { + const [getNamespaceState] = b as ReturnType['state']>; + + namespaces.push(getNamespaceState()); + }); + + const [getProviderState] = result.provider as ReturnType< + Provider['state'] + >; + + res[result.id] = { + ...(getProviderState() || {}), + namespaces: namespaces, + }; + }); + return res; + } +} diff --git a/wallets/core/src/hub/mod.ts b/wallets/core/src/hub/mod.ts new file mode 100644 index 0000000000..0845450dc9 --- /dev/null +++ b/wallets/core/src/hub/mod.ts @@ -0,0 +1,10 @@ +export { Namespace } from './namespaces/mod.js'; +export { Provider } from './provider/mod.js'; +export { Hub } from './hub.js'; +export type { Store, State, ProviderInfo } from './store/mod.js'; +export { + createStore, + guessProviderStateSelector, + namespaceStateSelector, +} from './store/mod.js'; +export { generateStoreId } from './helpers.js'; diff --git a/wallets/core/src/hub/namespaces/errors.ts b/wallets/core/src/hub/namespaces/errors.ts new file mode 100644 index 0000000000..1607c14f4a --- /dev/null +++ b/wallets/core/src/hub/namespaces/errors.ts @@ -0,0 +1,8 @@ +export const ACTION_NOT_FOUND_ERROR = (name: string) => + `Couldn't find "${name}" action. Are you sure you've added the action?`; + +export const OR_ACTION_FAILED_ERROR = (name: string) => + `An error occurred during running ${name}`; + +export const NO_STORE_FOUND_ERROR = + 'For setup store, you should set `store` first.'; diff --git a/wallets/core/src/hub/namespaces/mod.ts b/wallets/core/src/hub/namespaces/mod.ts new file mode 100644 index 0000000000..e4b7fbfeae --- /dev/null +++ b/wallets/core/src/hub/namespaces/mod.ts @@ -0,0 +1,9 @@ +export type { + Subscriber, + State, + ActionsMap, + Context, + Actions, +} from './types.js'; + +export { Namespace } from './namespace.js'; diff --git a/wallets/core/src/hub/namespaces/namespace.test.ts b/wallets/core/src/hub/namespaces/namespace.test.ts new file mode 100644 index 0000000000..012a5c146f --- /dev/null +++ b/wallets/core/src/hub/namespaces/namespace.test.ts @@ -0,0 +1,235 @@ +import { describe, expect, test, vi } from 'vitest'; + +import { NamespaceBuilder } from '../../builders/mod.js'; + +import { Namespace } from './namespace.js'; + +describe('check NamespaceBuilder works as expected', () => { + const NAMESPACE = 'bip122'; + const PROVIDER_ID = 'garbage provider'; + + test.todo('ensure namespace without any hooks running correctly'); + + test('add actions and run them.', () => { + const builder = new NamespaceBuilder<{ + hello: () => void; + bye: () => void; + chainable: () => void; + chain2: () => void; + }>(NAMESPACE, PROVIDER_ID); + builder.action('hello', () => 'hello world'); + builder.action('bye', () => 'bye bye'); + builder + .action('chainable', () => "it's also chainable") + .action('chain2', () => 'chain2'); + const blockchain = builder.build(); + + expect(blockchain.hello()).toBe('hello world'); + expect(blockchain.bye()).toBe('bye bye'); + + expect(() => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore-next-line + return blockchain.some_action_name_that_has_not_added(); + }).toThrowError(); + }); + + test('call .init only once.', () => { + const builder = new NamespaceBuilder(NAMESPACE, PROVIDER_ID); + let count = 0; + builder.action('init', () => { + count++; + }); + const blockchain = builder.build(); + blockchain.init(); + blockchain.init(); + + expect(count).toBe(1); + }); + + test('should be called before/after target action correctly even with multiple hook assigned to an action name', () => { + const beforeAction = vi.fn(); + const anotherBeforeAction = vi.fn(); + const afterAction = vi.fn(); + const anotherAfterAction = vi.fn(); + const connectAction = vi.fn(); + const disconnectAction = vi.fn(); + const builder = new NamespaceBuilder<{ + connect: () => void; + disconnect: () => void; + }>(NAMESPACE, PROVIDER_ID); + builder.action('connect', connectAction); + builder.action('disconnect', disconnectAction); + const blockchain = builder.build(); + + blockchain.connect(); + expect(connectAction).toBeCalledTimes(1); + expect(beforeAction).toBeCalledTimes(0); + expect(anotherBeforeAction).toBeCalledTimes(0); + expect(afterAction).toBeCalledTimes(0); + expect(anotherAfterAction).toBeCalledTimes(0); + + blockchain.before('connect', beforeAction); + blockchain.before('connect', anotherBeforeAction); + blockchain.connect(); + expect(connectAction).toBeCalledTimes(2); + expect(beforeAction).toBeCalledTimes(1); + expect(anotherBeforeAction).toBeCalledTimes(1); + expect(afterAction).toBeCalledTimes(0); + expect(anotherAfterAction).toBeCalledTimes(0); + + blockchain.after('connect', afterAction); + blockchain.after('connect', anotherAfterAction); + blockchain.connect(); + expect(beforeAction).toBeCalledTimes(2); + expect(anotherBeforeAction).toBeCalledTimes(2); + expect(afterAction).toBeCalledTimes(1); + expect(anotherAfterAction).toBeCalledTimes(1); + }); + + test('should call `and` sequentially.', () => { + const andActionFirst = vi.fn((_ctx, result) => result + 1); + const andActionSecond = vi.fn((_ctx, result) => result + 1); + + const connectAction = vi.fn(() => 0); + const disconnectAction = vi.fn(); + + const actions = new Map(); + actions.set('connect', connectAction); + actions.set('disconnect', disconnectAction); + + const ns = new Namespace<{ + connect: () => void; + disconnect: () => void; + }>(NAMESPACE, PROVIDER_ID, { + actions: actions, + configs: {}, + }); + ns.and('connect', andActionFirst); + ns.and('connect', andActionSecond); + + const result = ns.run('connect'); + + expect(connectAction).toBeCalledTimes(1); + expect(andActionFirst).toBeCalledTimes(1); + expect(andActionSecond).toBeCalledTimes(1); + + expect(result).toBe(2); + + ns.run('connect'); + expect(connectAction).toBeCalledTimes(2); + expect(andActionFirst).toBeCalledTimes(2); + expect(andActionSecond).toBeCalledTimes(2); + }); + + test("shouldn't run other `and` hooks if one of them fails then fallback to `or`.", () => { + const andActionFirst = vi.fn((_ctx, _result) => { + throw new Error('Oops!'); + }); + const andActionSecond = vi.fn((_ctx, result) => result + 1); + + const orAction = vi.fn((_ctx, e) => e instanceof Error); + + const connectAction = vi.fn(() => 0); + const actions = new Map(); + actions.set('connect', connectAction); + + const ns = new Namespace<{ + connect: () => void; + }>(NAMESPACE, PROVIDER_ID, { + actions: actions, + configs: {}, + }); + ns.and('connect', andActionFirst); + ns.and('connect', andActionSecond); + + ns.or('connect', orAction); + + const result = ns.run('connect'); + + expect(connectAction).toBeCalledTimes(1); + expect(andActionFirst).toBeCalledTimes(1); + expect(andActionSecond).toBeCalledTimes(0); + expect(orAction).toBeCalledTimes(1); + expect(result).toBe(true); + }); + + test('should throw error if there are no `or` to handle error', () => { + const andActionFirst = vi.fn((_ctx, _result) => { + throw new Error('Oops!'); + }); + const andActionSecond = vi.fn((_ctx, result) => result + 1); + + const connectAction = vi.fn(() => 0); + const actions = new Map(); + actions.set('connect', connectAction); + + const ns = new Namespace<{ + connect: () => void; + }>(NAMESPACE, PROVIDER_ID, { + actions: actions, + configs: {}, + }); + + ns.and('connect', andActionFirst); + ns.and('connect', andActionSecond); + + expect(() => ns.run('connect')).toThrowError(); + }); + + test('ensure `or` has access to error', () => { + const actions = new Map(); + actions.set('connect', () => { + throw new Error('Oops!'); + }); + + const ns = new Namespace<{ + connect: () => void; + }>(NAMESPACE, PROVIDER_ID, { + actions: actions, + configs: {}, + }); + + ns.or('connect', (_ctx: any, err: any) => { + return err instanceof Error; + }); + + const result = ns.run('connect'); + expect(result).toBe(true); + }); + + test('should call `or` sequentially.', () => { + const orActionFirst = vi.fn((_ctx, _err) => 1); + const orActionSecond = vi.fn((_ctx, _err) => _err + 1); + + const connectAction = vi.fn(() => { + throw new Error('Oops!'); + }); + + const actions = new Map(); + actions.set('connect', connectAction); + + const ns = new Namespace<{ + connect: () => void; + }>(NAMESPACE, PROVIDER_ID, { + actions: actions, + configs: {}, + }); + + ns.or('connect', orActionFirst); + ns.or('connect', orActionSecond); + + const result = ns.run('connect'); + + expect(connectAction).toBeCalledTimes(1); + expect(orActionFirst).toBeCalledTimes(1); + expect(orActionSecond).toBeCalledTimes(1); + + expect(result).toBe(2); + + ns.run('connect'); + expect(connectAction).toBeCalledTimes(2); + expect(orActionFirst).toBeCalledTimes(2); + expect(orActionSecond).toBeCalledTimes(2); + }); +}); diff --git a/wallets/core/src/hub/namespaces/namespace.ts b/wallets/core/src/hub/namespaces/namespace.ts new file mode 100644 index 0000000000..b7e2e8bf7b --- /dev/null +++ b/wallets/core/src/hub/namespaces/namespace.ts @@ -0,0 +1,340 @@ +import type { + Actions, + ActionsMap, + Context, + GetState, + HookActions, + HookActionsWithOptions, + SetState, + State, +} from './types.js'; +import type { + AndFunction, + AnyFunction, + FunctionWithContext, +} from '../../namespaces/common/types.js'; +import type { NamespaceConfig, Store } from '../store/mod.js'; + +import { generateStoreId, isAsync } from '../helpers.js'; + +import { + ACTION_NOT_FOUND_ERROR, + NO_STORE_FOUND_ERROR, + OR_ACTION_FAILED_ERROR, +} from './errors.js'; + +class Namespace> { + public readonly namespaceId: string; + public readonly providerId: string; + + #actions: ActionsMap; + #andActions: HookActions = new Map(); + #orActions: HookActions = new Map(); + // `context` for these two can be Namespace context or Provider context + #beforeActions: HookActionsWithOptions = new Map(); + #afterActions: HookActionsWithOptions = new Map(); + #initiated = false; + #store: Store | undefined; + // Namespace doesn't has any configs now, but we will need the feature in future + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore noUnusedParameters + #configs: NamespaceConfig; + + constructor( + id: string, + providerId: string, + options: { + configs: NamespaceConfig; + actions: ActionsMap; + } + ) { + const { configs, actions } = options; + + this.namespaceId = id; + this.providerId = providerId; + + this.#configs = configs; + this.#actions = actions; + } + + public state(): [GetState, SetState] { + const store = this.#store; + if (!store) { + throw new Error( + 'You need to set your store using `.store` method first.' + ); + } + + const id = this.#storeId(); + const setState: SetState = (name, value) => { + store.getState().namespaces.updateStatus(id, name, value); + }; + + const getState: GetState = (name?: K) => { + const state: State = store.getState().namespaces.getNamespaceData(id); + + if (!name) { + return state; + } + + return state[name]; + }; + + return [getState, setState]; + } + + /* + * This hook helps us to run a sync function if action ran successfully. + * For example, if we have a `connect` action, we can add function to be run after `connect` if it ran successfully. + * + */ + public and( + name: K, + action: FunctionWithContext, Context> + ) { + const currentAndActions = this.#andActions.get(name) || []; + this.#andActions.set(name, currentAndActions.concat(action)); + + return this; + } + + /* + * If an action fails, a hook action can be called after that which `or`. + * For example, if we have a `connect` action, we can add function to be run when `connect` fails (throw an error). + * + */ + public or( + name: K, + action: FunctionWithContext + ) { + const currentOrActions = this.#orActions.get(name) || []; + this.#orActions.set(name, currentOrActions.concat(action)); + + return this; + } + + /** + * Running a function after a specific action + * + * Note: the context can be set from outside as well. this is useful for Provider to set its context instead of namespace context. + */ + public after( + name: K, + action: FunctionWithContext, + options?: { context?: C } + ) { + const currentAfterActions = this.#afterActions.get(name) || []; + const actionWithOptions = { + action: action, + options: { + context: options?.context, + }, + }; + + this.#afterActions.set(name, currentAfterActions.concat(actionWithOptions)); + return this; + } + + /** + * Running a function before a specific action + * + * Note: the context can be set from outside as well. this is useful for Provider to set its context instead of using namespace context. + */ + public before( + name: K, + action: FunctionWithContext, + options?: { context?: C } + ) { + const currentBeforeActions = this.#beforeActions.get(name) || []; + const actionWithOptions = { + action: action, + options: { + context: options?.context, + }, + }; + this.#beforeActions.set( + name, + currentBeforeActions.concat(actionWithOptions) + ); + + return this; + } + + public run( + name: K, + ...args: any[] + ): unknown | Promise { + const action = this.#actions.get(name); + if (!action) { + throw new Error(ACTION_NOT_FOUND_ERROR(name.toString())); + } + + /* + * Action can be both, sync or async. To simplify the process we can not make `sync` mode to async + * Since every user's sync action will be an async function and affect what user expect, + * it makes all the actions async and it doesn't match with Namespace interface (e.g. EvmActions) + * + * To avoid this issue and also not duplicating code, I broke the process into smaller methods + * and two main methods to run actions: tryRunAsyncAction & tryRunAction. + */ + const result = isAsync(action) + ? this.#tryRunAsyncAction(name, args) + : this.#tryRunAction(name, args); + + return result; + } + + public init() { + if (this.#initiated) { + console.log('[Namespace] initiated already.'); + return; + } + + const definedInitByUser = this.#actions + .get('init') + ?.bind(null, this.#context()); + if (definedInitByUser) { + definedInitByUser(); + } else { + console.debug( + "[Namespace] this namespace doesn't have any `init` implemented." + ); + } + + this.#initiated = true; + console.debug('[Namespace] initiated successfully.'); + } + + public store(store: Store) { + if (this.#store) { + console.warn( + "You've already set an store for your Namespace. Old store will be replaced by the new one." + ); + } + this.#store = store; + this.#setupStore(); + + return this; + } + + #tryRunAction(name: K, params: any[]): unknown { + this.#tryRunBeforeActions(name); + + const action = this.#actions.get(name); + if (!action) { + throw new Error(ACTION_NOT_FOUND_ERROR(name.toString())); + } + + const context = this.#context(); + + let result; + try { + result = action(context, ...params); + result = this.#tryRunAndAction(result, name); + } catch (e) { + result = this.#tryRunOrAction(e, name); + } finally { + this.#tryRunAfterActions(name); + } + + return result; + } + + async #tryRunAsyncAction( + name: K, + params: any[] + ): Promise { + this.#tryRunBeforeActions(name); + + const action = this.#actions.get(name); + if (!action) { + throw new Error(ACTION_NOT_FOUND_ERROR(name.toString())); + } + + const context = this.#context(); + return await action(context, ...params) + .then((result: unknown) => this.#tryRunAndAction(result, name)) + .catch((e: unknown) => this.#tryRunOrAction(e, name)) + .finally(() => this.#tryRunAfterActions(name)); + } + + #tryRunAfterActions(actionName: K) { + const afterActions = this.#afterActions.get(actionName); + + if (afterActions) { + afterActions.forEach((afterAction) => { + const context = afterAction.options?.context || this.#context(); + afterAction.action(context); + }); + } + } + + #tryRunBeforeActions(actionName: K): void { + const beforeActions = this.#beforeActions.get(actionName); + if (beforeActions) { + beforeActions.forEach((beforeAction) => { + const context = beforeAction.options?.context || this.#context(); + beforeAction.action(context); + }); + } + } + + #tryRunAndAction(result: unknown, actionName: K): unknown { + const andActions = this.#andActions.get(actionName); + + if (andActions) { + const context = this.#context(); + result = andActions.reduce((prev, andAction) => { + return andAction(context, prev); + }, result); + } + return result; + } + + #tryRunOrAction( + actionError: unknown, + actionName: K + ): unknown { + const orActions = this.#orActions.get(actionName); + + if (orActions) { + try { + const context = this.#context(); + return orActions.reduce((prev, orAction) => { + return orAction(context, prev); + }, actionError); + } catch (orError) { + throw new Error(OR_ACTION_FAILED_ERROR(actionName.toString()), { + cause: actionError, + }); + } + } else { + throw actionError; + } + } + + #setupStore(): void { + const store = this.#store; + if (!store) { + throw new Error(NO_STORE_FOUND_ERROR); + } + const id = this.#storeId(); + store.getState().namespaces.addNamespace(id, { + namespaceId: this.namespaceId, + providerId: this.providerId, + }); + } + + #storeId() { + return generateStoreId(this.providerId, this.namespaceId); + } + + #context(): Context { + return { + state: this.state.bind(this), + action: this.run.bind(this), + }; + } +} + +export { Namespace }; diff --git a/wallets/core/src/hub/namespaces/types.ts b/wallets/core/src/hub/namespaces/types.ts new file mode 100644 index 0000000000..5ad1520b9a --- /dev/null +++ b/wallets/core/src/hub/namespaces/types.ts @@ -0,0 +1,54 @@ +import type { + AnyFunction, + FunctionWithContext, +} from '../../namespaces/common/types.js'; +import type { SolanaActions } from '../../namespaces/solana/types.js'; +import type { NamespaceData } from '../store/mod.js'; + +type ActionName = K | Omit; + +export type Subscriber = ( + context: Context, + ...args: any[] +) => void; +export type SubscriberCleanUp = ( + context: Context, + ...args: any[] +) => void; +export type State = NamespaceData; +export type SetState = ( + name: K, + value: State[K] +) => void; +export type GetState = { + (): State; + (name: K): State[K]; +}; +export type ActionsMap> = Map< + ActionName, + FunctionWithContext> +>; + +export type AndUseActions = Map; +export type HookActions = Map; +export type HookActionsWithOptions = Map< + keyof T, + { + action: AnyFunction; + options?: { + context?: unknown; + }; + }[] +>; +export type Context = object> = { + state: () => [GetState, SetState]; + action: (name: keyof T, ...args: any[]) => any; +}; + +/** + * This actually define what kind of action will be implemented in namespaces. + * For example evm namespace will have .connect(chain: string) and .switchNetwork + * But solana namespace only have: `.connect()`. + * This actions will be passed to this generic. + */ +export type Actions = Record; diff --git a/wallets/core/src/hub/provider/mod.ts b/wallets/core/src/hub/provider/mod.ts new file mode 100644 index 0000000000..92da36f4d7 --- /dev/null +++ b/wallets/core/src/hub/provider/mod.ts @@ -0,0 +1,9 @@ +export type { + ExtendableInternalActions, + CommonNamespaces, + State, + Context, + ProviderBuilderOptions, +} from './types.js'; + +export { Provider } from './provider.js'; diff --git a/wallets/core/src/hub/provider/provider.test.ts b/wallets/core/src/hub/provider/provider.test.ts new file mode 100644 index 0000000000..65d2e627f4 --- /dev/null +++ b/wallets/core/src/hub/provider/provider.test.ts @@ -0,0 +1,186 @@ +import type { EvmActions } from '../../namespaces/evm/types.js'; +import type { SolanaActions } from '../../namespaces/solana/types.js'; +import type { Store } from '../store/mod.js'; + +import { beforeEach, describe, expect, test, vi } from 'vitest'; + +import { NamespaceBuilder } from '../../builders/namespace.js'; +import { ProviderBuilder } from '../../builders/provider.js'; +import { garbageWalletInfo } from '../../test-utils/fixtures.js'; +import { createStore } from '../store/mod.js'; + +describe('providers', () => { + let namespaces: { + evm: NamespaceBuilder; + solana: NamespaceBuilder; + }; + let store: Store; + + beforeEach(() => { + store = createStore(); + const evmBlockchain = new NamespaceBuilder('eip155', 'garbage'); + const solanaBlockchain = new NamespaceBuilder( + 'solana', + 'garbage' + ); + + namespaces = { + evm: evmBlockchain, + solana: solanaBlockchain, + }; + + return () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore-next-line + (store = undefined), (namespaces = undefined); + }; + }); + + test('Initialize providers correctly', () => { + const builder = new ProviderBuilder('garbage').config( + 'info', + garbageWalletInfo + ); + const { evm, solana } = namespaces; + builder.add('evm', evm.build()).add('solana', solana.build()); + const provider = builder.build(); + + const allNamespaces = provider.getAll(); + expect(allNamespaces.size).toBe(2); + }); + + test('updating states', () => { + const builder = new ProviderBuilder('garbage', { store }).config( + 'info', + garbageWalletInfo + ); + const { evm, solana } = namespaces; + builder.add('evm', evm.build()).add('solana', solana.build()); + + const provider = builder.build(); + const [getState, setState] = provider.state(); + setState('installed', true); + const isInstalled = getState('installed'); + expect(isInstalled).toBe(true); + }); + + test('run actions', async () => { + const builder = new ProviderBuilder('garbage', { store }).config( + 'info', + garbageWalletInfo + ); + const { evm, solana } = namespaces; + solana.action('connect', async () => [ + 'solana:mainnet:0x000000000000000000000000000000000000dead', + ]); + builder.add('evm', evm.build()).add('solana', solana.build()); + + const provider = builder.build(); + const result = await provider.get('solana')?.connect(); + + expect(result).toStrictEqual([ + 'solana:mainnet:0x000000000000000000000000000000000000dead', + ]); + // Since we didn't add any action regarding connect for `evm` + await expect(async () => + provider.get('evm')?.connect('0x1') + ).rejects.toThrowError(); + }); + + test('sets config properly', () => { + const builder = new ProviderBuilder('garbage'); + builder.config('info', garbageWalletInfo); + const provider = builder.build().store(store); + + expect(provider.info()).toStrictEqual(garbageWalletInfo); + }); + + test('.init should works on Provider', () => { + const builder = new ProviderBuilder('garbage').config( + 'info', + garbageWalletInfo + ); + let count = 0; + builder.init(() => { + count++; + }); + const provider = builder.build().store(store); + expect(count).toBe(0); + provider.init(); + provider.init(); + provider.init(); + expect(count).toBe(1); + }); + + test(".init shouldn't do anything when use hasn't set anything", () => { + const builder = new ProviderBuilder('garbage').config( + 'info', + garbageWalletInfo + ); + const provider = builder.build().store(store); + expect(() => { + provider.init(); + provider.init(); + provider.init(); + }).not.toThrow(); + }); + + test('A provider can be found using its namespace', () => { + const builder = new ProviderBuilder('garbage', { store }).config( + 'info', + garbageWalletInfo + ); + + const { evm, solana } = namespaces; + builder.add('evm', evm.build()).add('solana', solana.build()); + const provider = builder.build(); + + const result = provider.findByNamespace('solana'); + expect(result).toBeDefined(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore-next-line + expect(result?.namespaceId).toBe('solana'); + + const result2 = provider.findByNamespace('evm'); + expect(result2).toBeUndefined(); + }); + + test('`before/after` is calling with correct context ', () => { + const connect = vi.fn(); + const before = vi.fn(function (context) { + const [, setState] = context.state(); + setState('installed', true); + }); + const after = vi.fn(function (context) { + const [, setState] = context.state(); + setState('installed', false); + }); + + const { evm } = namespaces; + const evmBlockchain = evm.action('connect', connect).build(); + + const builder = new ProviderBuilder('garbage', { store }) + .add('evm', evmBlockchain) + .config('info', garbageWalletInfo); + const provider = builder.build(); + + const [getState] = provider.state(); + const result = provider.get('evm'); + + // Adding `after` then make it will run + provider.before('connect', before); + void result?.connect('whatever'); + + expect(connect).toBeCalledTimes(1); + expect(before).toBeCalledTimes(1); + expect(getState('installed')).toBe(true); + + // Adding `after` then make it will run + provider.after('connect', after); + void result?.connect('whatever'); + expect(connect).toBeCalledTimes(2); + expect(after).toBeCalledTimes(1); + + expect(getState('installed')).toBe(false); + }); +}); diff --git a/wallets/core/src/hub/provider/provider.ts b/wallets/core/src/hub/provider/provider.ts new file mode 100644 index 0000000000..487d9a353f --- /dev/null +++ b/wallets/core/src/hub/provider/provider.ts @@ -0,0 +1,234 @@ +import type { + CommonNamespaces, + Context, + ExtendableInternalActions, + GetState, + NamespacesMap, + SetState, + State, +} from './types.js'; +import type { FindProxiedNamespace } from '../../builders/mod.js'; +import type { + AnyFunction, + FunctionWithContext, +} from '../../namespaces/common/types.js'; +import type { ProviderConfig, Store } from '../store/mod.js'; + +const VERSION = '1.0'; + +export class Provider { + public readonly id: string; + public readonly version = VERSION; + + #namespaces: NamespacesMap; + #initiated = false; + #extendInternalActions: ExtendableInternalActions = {}; + #store: Store | undefined; + #configs: ProviderConfig; + + constructor( + id: string, + namespaces: NamespacesMap, + configs: ProviderConfig, + options: { + /** + * There are some cases we need to have a behavior like initializing a provider which will be run when we are creating an instance. + * These internal steps and behaviors will be useful for library user to extend the behavior by running a specific code. + */ + extendInternalActions: ExtendableInternalActions; + store?: Store; + } + ) { + this.id = id; + this.#configs = configs; + // it should be only created here, to make sure `after/before` will work properly. + this.#extendInternalActions = options.extendInternalActions; + this.#namespaces = namespaces; + + if (options.store) { + this.#store = options.store; + this.#setupStore(); + } + } + + /** + * Getting state of a provider + * + * **Note:** + * Each namespace has it's own state as well, in Legacy we didn't have this separation and all of them was accessible through Provider itself + * To be compatible with legacy, `getState` has a logic to guess the final state to produce same state as legacy. + */ + public state(): [GetState, SetState] { + const store = this.#store; + if (!store) { + throw new Error( + `Any store detected for ${this.id}. You need to set your store using '.store' method first.` + ); + } + + /** + * State updater + */ + const setState: SetState = (name, value) => { + switch (name) { + case 'installed': + return store.getState().providers.updateStatus(this.id, name, value); + default: + throw new Error( + `Unhandled state update for provider. (provider id: ${this.id}, state name: ${name})` + ); + } + }; + + /** + * State getter + */ + const getState: GetState = (name?: K) => { + const state: State = store + .getState() + .providers.guessNamespacesState(this.id); + + if (!name) { + return state; + } + + switch (name) { + case 'installed': + case 'connected': + case 'connecting': + return state[name]; + default: + throw new Error('Unhandled state for provider'); + } + }; + + return [getState, setState]; + } + + public getAll(): NamespacesMap { + return this.#namespaces; + } + + public get( + id: K + ): FindProxiedNamespace | undefined { + return this.#namespaces.get(id) as unknown as + | FindProxiedNamespace + | undefined; + } + + public findByNamespace( + namespaceLookingFor: K | string + ): FindProxiedNamespace | undefined { + // If we didn't found any match, we will return `undefined`. + let result: object | undefined = undefined; + + this.#namespaces.forEach((namespace) => { + if (namespace.namespaceId === namespaceLookingFor) { + result = namespace; + } + }); + + return result; + } + + public info(): ProviderConfig['info'] | undefined { + const store = this.#store; + if (!store) { + throw new Error( + 'You need to set your store using `.store` method first.' + ); + } + + return store.getState().providers.list[this.id].config.info; + } + + public init(): void { + const definedInitByUser = this.#extendInternalActions.init; + if (!definedInitByUser) { + console.debug( + "[Namespace] this namespace doesn't have any `init` implemented." + ); + return; + } + + if (this.#initiated) { + console.log('[Namespace] initiated already.'); + return; + } + + definedInitByUser.bind(null, this.#context())(); + this.#initiated = true; + console.debug('[Namespace] initiated successfully.'); + } + + public before( + action: string, + cb: FunctionWithContext + ): this { + this.#addHook('before', action, cb); + return this; + } + + public after( + action: string, + cb: FunctionWithContext + ): this { + this.#addHook('after', action, cb); + return this; + } + + public store(store: Store): this { + if (this.#store) { + console.warn( + "You've already set an store for your Provider. Old store will be replaced by the new one." + ); + } + this.#store = store; + this.#setupStore(); + return this; + } + + #addHook( + hookName: 'after' | 'before', + action: string, + cb: FunctionWithContext + ): this { + const context = { + state: this.state.bind(this), + }; + + this.#namespaces.forEach((namespace) => { + if (hookName === 'after') { + namespace.after(action as any, cb, { + context, + }); + } else if (hookName === 'before') { + namespace.before(action as any, cb, { + context, + }); + } else { + throw new Error(`You hook name is invalid: ${hookName}`); + } + }); + + return this; + } + + #setupStore(): void { + const store = this.#store; + if (!store) { + throw new Error('For setup store, you should set `store` first.'); + } + store.getState().providers.addProvider(this.id, this.#configs); + this.#namespaces.forEach((provider) => { + provider.store(store); + }); + } + + #context(): Context { + return { + state: this.state.bind(this), + }; + } +} diff --git a/wallets/core/src/hub/provider/types.ts b/wallets/core/src/hub/provider/types.ts new file mode 100644 index 0000000000..f0fe289b59 --- /dev/null +++ b/wallets/core/src/hub/provider/types.ts @@ -0,0 +1,40 @@ +import type { LegacyState } from '../../legacy/mod.js'; +import type { NamespaceInterface, Store } from '../../mod.js'; +import type { + AnyFunction, + FunctionWithContext, +} from '../../namespaces/common/types.js'; +import type { CosmosActions } from '../../namespaces/cosmos/types.js'; +import type { EvmActions } from '../../namespaces/evm/types.js'; +import type { SolanaActions } from '../../namespaces/solana/types.js'; + +export type Context = { + state: () => [GetState, SetState]; +}; + +export type State = Omit; +export type SetState = >( + name: K, + value: State[K] +) => void; +export type GetState = { + (): State; + (name: K): State[K]; +}; + +export interface CommonNamespaces { + evm: EvmActions; + solana: SolanaActions; + cosmos: CosmosActions; +} + +export interface ExtendableInternalActions { + init?: FunctionWithContext; +} + +export type NamespacesMap = Map< + K, + NamespaceInterface +>; + +export type ProviderBuilderOptions = { store?: Store }; diff --git a/wallets/core/src/hub/store/hub.ts b/wallets/core/src/hub/store/hub.ts new file mode 100644 index 0000000000..569f5eeef4 --- /dev/null +++ b/wallets/core/src/hub/store/hub.ts @@ -0,0 +1,18 @@ +/************ Hub ************/ + +import type { State } from './mod.js'; +import type { StateCreator } from 'zustand'; + +type HubConfig = object; + +export interface HubStore { + config: HubConfig; +} + +type HubStateCreator = StateCreator; + +const hubStore: HubStateCreator = () => ({ + config: {}, +}); + +export { hubStore }; diff --git a/wallets/core/src/hub/store/mod.ts b/wallets/core/src/hub/store/mod.ts new file mode 100644 index 0000000000..08193d4e4a --- /dev/null +++ b/wallets/core/src/hub/store/mod.ts @@ -0,0 +1,8 @@ +export { + guessProviderStateSelector, + namespaceStateSelector, +} from './selectors.js'; +export type { Store, State } from './store.js'; +export type { ProviderInfo, ProviderConfig } from './providers.js'; +export type { NamespaceConfig, NamespaceData } from './namespaces.js'; +export { createStore } from './store.js'; diff --git a/wallets/core/src/hub/store/namespaces.ts b/wallets/core/src/hub/store/namespaces.ts new file mode 100644 index 0000000000..f2bbcc7b36 --- /dev/null +++ b/wallets/core/src/hub/store/namespaces.ts @@ -0,0 +1,90 @@ +/************ Namespace ************/ + +import type { StateCreator } from 'zustand'; + +import { produce } from 'immer'; + +import { namespaceStateSelector, type State } from './mod.js'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface NamespaceConfig { + // Currently, namespace doesn't has any config. +} + +export interface NamespaceData { + accounts: null | string[]; + network: null | string; + connected: boolean; + connecting: boolean; +} + +interface NamespaceInfo { + providerId: string; + namespaceId: string; +} + +type NamespaceState = { + list: Record< + string, + { + info: NamespaceInfo; + data: NamespaceData; + error: unknown; + } + >; +}; + +interface NamespaceActions { + addNamespace: (id: string, config: NamespaceInfo) => void; + updateStatus: ( + id: string, + key: K, + value: NamespaceData[K] + ) => void; +} +interface NamespaceSelectors { + getNamespaceData(storeId: string): NamespaceData; +} + +export type NamespaceStore = NamespaceState & + NamespaceActions & + NamespaceSelectors; +type NamespaceStateCreator = StateCreator; + +const namespacesStore: NamespaceStateCreator = (set, get) => ({ + list: {}, + addNamespace: (id, info) => { + const item = { + data: { + accounts: null, + network: null, + connected: false, + connecting: false, + }, + error: '', + info, + }; + + set( + produce((state: State) => { + state.namespaces.list[id] = item; + }) + ); + }, + updateStatus: (id, key, value) => { + if (!get().namespaces.list[id]) { + throw new Error(`No namespace with '${id}' found.`); + } + + set( + produce((state: State) => { + state.namespaces.list[id].data[key] = value; + }) + ); + }, + getNamespaceData(storeId) { + return namespaceStateSelector(get(), storeId); + }, +}); + +export { namespacesStore }; diff --git a/wallets/core/src/hub/store/providers.ts b/wallets/core/src/hub/store/providers.ts new file mode 100644 index 0000000000..470e3d20c5 --- /dev/null +++ b/wallets/core/src/hub/store/providers.ts @@ -0,0 +1,97 @@ +import type { + CommonNamespaces, + State as InternalProviderState, +} from '../provider/mod.js'; +import type { StateCreator } from 'zustand'; + +import { produce } from 'immer'; + +import { guessProviderStateSelector, type State } from './mod.js'; + +type NamespaceName = + | keyof CommonNamespaces + | Omit; + +type Browsers = 'firefox' | 'chrome' | 'edge' | 'brave' | 'homepage'; +type Property = { name: N; value: V }; +type DetachedInstances = Property<'detached', NamespaceName[]>; + +export type ProviderInfo = { + name: string; + icon: string; + extensions: Partial>; + properties?: DetachedInstances[]; +}; + +export interface ProviderConfig { + info: ProviderInfo; +} + +interface ProviderData { + installed: boolean; +} + +type ProviderState = { + list: Record< + string, + { + config: ProviderConfig; + data: ProviderData; + error: unknown; + } + >; +}; +interface ProviderActions { + addProvider: (id: string, config: ProviderConfig) => void; + updateStatus: ( + id: string, + key: K, + value: ProviderData[K] + ) => void; +} + +interface ProviderSelectors { + /** + * Provider has a limited state to itself, to be compatible with legacy, we try to produce same object as legacy + * which includes namespace state as well. + */ + guessNamespacesState: (id: string) => InternalProviderState; +} + +export type ProviderStore = ProviderState & ProviderActions & ProviderSelectors; +type ProvidersStateCreator = StateCreator; + +const providersStore: ProvidersStateCreator = (set, get) => ({ + list: {}, + addProvider: (id, config) => { + const item = { + data: { + installed: false, + }, + error: '', + config, + }; + + set( + produce((state: State) => { + state.providers.list[id] = item; + }) + ); + }, + updateStatus: (id, key, value) => { + if (!get().providers.list[id]) { + throw new Error(`No namespace namespace with '${id}' found.`); + } + + set( + produce((state: State) => { + state.providers.list[id].data[key] = value; + }) + ); + }, + guessNamespacesState: (providerId: string): InternalProviderState => { + return guessProviderStateSelector(get(), providerId); + }, +}); + +export { providersStore }; diff --git a/wallets/core/src/hub/store/selectors.ts b/wallets/core/src/hub/store/selectors.ts new file mode 100644 index 0000000000..a124032d1e --- /dev/null +++ b/wallets/core/src/hub/store/selectors.ts @@ -0,0 +1,59 @@ +/** + * Note: Zustand has some difficulty when you are trying to `previous` state on `subscribe`. + * If these selectors define inside store directly and use `get()` for accessing state, it will get the latest state + * instead of previous state which desired. + * So make them a helper will let us to reuse them both in store and outside of store in a `subscribe`. + */ +import type { State } from '../mod.js'; +import type { State as InternalProviderState } from '../provider/mod.js'; + +/** + * Legacy provider state includes `connecting` and `connected` values. It hadn't a separation layer for `namespaces`. + * For compatibility reasons, we should produce these two values somehow. + * + * Currently, We return `true` if any of namespaces return true. We are missing failed namespace here. + * But if we want to solve that, we should migrate our client code to use `namespace` state directly and not from `provider` + */ +export function guessProviderStateSelector( + state: State, + providerId: string +): InternalProviderState { + /* + * We keep namespaces in a separate branch than providers. + * We should look at all of them and find all the namespaces that for our current proivder. + */ + const allNamespaces = state.namespaces.list; + const currentProviderNamespaces = Object.keys(allNamespaces).filter( + (key) => allNamespaces[key].info.providerId === providerId + ); + + // Returning provider state value directly. + const installed = state.providers.list[providerId].data.installed; + + /* + * If any namespaces returns `true`, we consider the whole provider for this field to be `true`. + * it has a downside regarding errors which explained on top of the function. + */ + const connected = + currentProviderNamespaces.length > 0 + ? currentProviderNamespaces.some( + (key) => allNamespaces[key].data.connected + ) + : false; + const connecting = + currentProviderNamespaces.length > 0 + ? currentProviderNamespaces.some( + (key) => allNamespaces[key].data.connecting + ) + : false; + + return { + installed, + connected, + connecting, + }; +} + +export function namespaceStateSelector(state: State, storeId: string) { + return state.namespaces.list[storeId].data; +} diff --git a/wallets/core/src/hub/store/store.test.ts b/wallets/core/src/hub/store/store.test.ts new file mode 100644 index 0000000000..a1a8fc4ceb --- /dev/null +++ b/wallets/core/src/hub/store/store.test.ts @@ -0,0 +1,32 @@ +import type { Store } from './store.js'; + +import { beforeEach, describe, expect, test } from 'vitest'; + +import { createStore } from './store.js'; + +describe('checking store', () => { + let hubStore: Store; + + beforeEach(() => { + hubStore = createStore(); + }); + + test('new providers can be added to store', () => { + const id = 'sol-or-something'; + const info = { + info: { + name: 'sol grabage wallet', + icon: 'http://somewhere.world', + extensions: { + homepage: 'http://somewhere.world', + }, + }, + }; + + const { getState } = hubStore; + getState().providers.addProvider(id, info); + + expect(getState().providers.list[id]).toBeDefined(); + expect(Object.keys(getState().providers.list).length).toBe(1); + }); +}); diff --git a/wallets/core/src/hub/store/store.ts b/wallets/core/src/hub/store/store.ts new file mode 100644 index 0000000000..17211cca26 --- /dev/null +++ b/wallets/core/src/hub/store/store.ts @@ -0,0 +1,26 @@ +import type { StoreApi } from 'zustand/vanilla'; + +import { createStore as createZustandStore } from 'zustand/vanilla'; + +import { hubStore, type HubStore } from './hub.js'; +import { namespacesStore, type NamespaceStore } from './namespaces.js'; +import { providersStore, type ProviderStore } from './providers.js'; + +/************ State ************/ + +export interface State { + hub: HubStore; + providers: ProviderStore; + namespaces: NamespaceStore; +} + +export type Store = StoreApi; +export const createStore = (): Store => { + return createZustandStore((...api) => { + return { + hub: hubStore(...api), + providers: providersStore(...api), + namespaces: namespacesStore(...api), + }; + }); +}; diff --git a/wallets/core/src/mod.ts b/wallets/core/src/mod.ts index d81950716a..44b99e5e4a 100644 --- a/wallets/core/src/mod.ts +++ b/wallets/core/src/mod.ts @@ -8,3 +8,39 @@ export { LegacyEvents, LegacyNamespace, } from './legacy/mod.js'; + +export type { Store, State, ProviderInfo } from './hub/mod.js'; +export { + Hub, + Provider, + Namespace, + createStore, + guessProviderStateSelector, + namespaceStateSelector, +} from './hub/mod.js'; +export type { + ProxiedNamespace, + FindProxiedNamespace as NamespaceInterface, +} from './builders/mod.js'; +export { + NamespaceBuilder, + ProviderBuilder, + ActionBuilder, +} from './builders/mod.js'; + +/* + * Our `embedded` hasn't been migrated to NodeNext yet so it doesn't support `exports` field. + * There are two approach to make `NodeNext` which is used for our libs with old moduleResolution: + * + * 1. Use direct paths, e.g. '@rango-dev/wallets-core/dist/legacy/mod' + * 2. Add types and function that are using in `embedded` to package entry point (this file). + * + * The first one is better since we don't need to deprecate or having a breaking change in future, + * But Parcel has weird behavior on resolving ESM exports. We enabled exports for Parcel using `packageExports: true` option, + * But it will use `exports` fields whenever it finds the field in package.json and ignore `moduleResolution` in tsconfig. + * + * To make it work for Parcel, we should go with second mentioned option. + * + */ +export type { Versions } from './utils/mod.js'; +export { defineVersions, pickVersion } from './utils/mod.js'; \ No newline at end of file diff --git a/wallets/core/src/namespaces/common/actions.ts b/wallets/core/src/namespaces/common/actions.ts new file mode 100644 index 0000000000..f520932638 --- /dev/null +++ b/wallets/core/src/namespaces/common/actions.ts @@ -0,0 +1,11 @@ +import type { Context } from '../../hub/namespaces/mod.js'; + +export function disconnect(context: Context): void { + const [, setState] = context.state(); + setState('network', null); + setState('accounts', null); + setState('connected', false); + setState('connecting', false); +} + +export const recommended = [['disconnect', disconnect] as const]; diff --git a/wallets/core/src/namespaces/common/after.ts b/wallets/core/src/namespaces/common/after.ts new file mode 100644 index 0000000000..9f66c6ba23 --- /dev/null +++ b/wallets/core/src/namespaces/common/after.ts @@ -0,0 +1,8 @@ +import type { Context } from '../../hub/namespaces/mod.js'; + +export function intoConnectionFinished(context: Context) { + const [, setState] = context.state(); + setState('connecting', false); +} + +export const recommended = [['connect', intoConnectionFinished] as const]; diff --git a/wallets/core/src/namespaces/common/and.ts b/wallets/core/src/namespaces/common/and.ts new file mode 100644 index 0000000000..5d29f94e57 --- /dev/null +++ b/wallets/core/src/namespaces/common/and.ts @@ -0,0 +1,39 @@ +import type { Accounts, AccountsWithActiveChain } from './types.js'; +import type { Context } from '../../hub/namespaces/mod.js'; + +import { isValidCaipAddress } from './helpers.js'; + +export function connectAndUpdateStateForSingleNetwork( + context: Context, + accounts: Accounts +) { + if (!accounts.every(isValidCaipAddress)) { + throw new Error( + `Your provider should format account addresses in CAIP-10 format. Your provided accounts: ${accounts}` + ); + } + + const [, setState] = context.state(); + setState('accounts', accounts); + setState('connected', true); + return accounts; +} + +export function connectAndUpdateStateForMultiNetworks( + context: Context, + accounts: AccountsWithActiveChain +) { + if (!accounts.accounts.every(isValidCaipAddress)) { + throw new Error( + `Your provider should format account addresses in CAIP-10 format. Your provided accounts: ${accounts.accounts}` + ); + } + + const [, setState] = context.state(); + setState('accounts', accounts.accounts); + setState('network', accounts.network); + setState('connected', true); + return accounts; +} + +export const recommended = []; diff --git a/wallets/core/src/namespaces/common/before.ts b/wallets/core/src/namespaces/common/before.ts new file mode 100644 index 0000000000..6945b36dfc --- /dev/null +++ b/wallets/core/src/namespaces/common/before.ts @@ -0,0 +1,9 @@ +import type { Context } from '../../hub/namespaces/mod.js'; + +export function intoConnecting(context: Context) { + const [, setState] = context.state(); + setState('connecting', true); +} + +// Please consider if you are going to add something here, make sure it works on all namespaces. +export const recommended = [['connect', intoConnecting] as const]; diff --git a/wallets/core/src/namespaces/common/builders.ts b/wallets/core/src/namespaces/common/builders.ts new file mode 100644 index 0000000000..1f4f60c64e --- /dev/null +++ b/wallets/core/src/namespaces/common/builders.ts @@ -0,0 +1,19 @@ +import type { AutoImplementedActionsByRecommended } from './types.js'; +import type { Actions } from '../../hub/namespaces/types.js'; + +import { ActionBuilder } from '../../mod.js'; + +import { disconnect as disconnectAction } from './actions.js'; + +export const disconnect = < + T extends Actions & + Record<'disconnect', AutoImplementedActionsByRecommended['disconnect']> +>() => + new ActionBuilder( + 'disconnect' + ) + .after((c) => { + c; + // + }) + .action(disconnectAction) as unknown as ActionBuilder; diff --git a/wallets/core/src/namespaces/common/helpers.ts b/wallets/core/src/namespaces/common/helpers.ts new file mode 100644 index 0000000000..29f85b68a7 --- /dev/null +++ b/wallets/core/src/namespaces/common/helpers.ts @@ -0,0 +1,10 @@ +import { AccountId } from 'caip'; + +export function isValidCaipAddress(address: string): boolean { + try { + AccountId.parse(address); + return true; + } catch { + return false; + } +} diff --git a/wallets/core/src/namespaces/common/mod.ts b/wallets/core/src/namespaces/common/mod.ts new file mode 100644 index 0000000000..d4e0c9a7bd --- /dev/null +++ b/wallets/core/src/namespaces/common/mod.ts @@ -0,0 +1,13 @@ +export * as actions from './actions.js'; +export * as builders from './builders.js'; +export { + intoConnectionFinished, + recommended as afterRecommended, +} from './after.js'; +export { + connectAndUpdateStateForMultiNetworks, + connectAndUpdateStateForSingleNetwork, + recommended as andRecommended, +} from './and.js'; +export { intoConnecting, recommended as beforeRecommended } from './before.js'; +export { type CaipAccount } from './types.js'; diff --git a/wallets/core/src/namespaces/common/types.ts b/wallets/core/src/namespaces/common/types.ts new file mode 100644 index 0000000000..78cdf573b0 --- /dev/null +++ b/wallets/core/src/namespaces/common/types.ts @@ -0,0 +1,48 @@ +import type { Actions } from '../../hub/namespaces/mod.js'; + +export type AnyFunction = (...args: any[]) => any; +export type AnyPromiseFunction = (...args: any[]) => Promise; + +export type AndFunction< + T extends Record, + K extends keyof T +> = (result: Awaited>) => Awaited>; + +export type FunctionWithContext = T extends (...args: infer P) => infer R + ? (context: C, ...args: P) => R + : never; + +export type VoidReturn = T extends (...args: infer P) => any + ? (...args: P) => void + : never; + +export type Task> = readonly [ + keyof T, + FunctionWithContext +]; + +export type TaskWithVoidReturn> = readonly [ + keyof T, + VoidReturn> +]; + +type CaipNamespace = string; +type CaipChainId = string; +type CaipAccountAddress = string; + +export type CaipAccount = + `${CaipNamespace}:${CaipChainId}:${CaipAccountAddress}`; + +export type Accounts = CaipAccount[]; +export type AccountsWithActiveChain = { + accounts: Accounts; + network: string; +}; + +export interface CommonActions { + init: () => void; +} + +export interface AutoImplementedActionsByRecommended { + disconnect: () => void; +} diff --git a/wallets/core/src/namespaces/cosmos/types.ts b/wallets/core/src/namespaces/cosmos/types.ts new file mode 100644 index 0000000000..27cde09734 --- /dev/null +++ b/wallets/core/src/namespaces/cosmos/types.ts @@ -0,0 +1,10 @@ +import type { + AutoImplementedActionsByRecommended, + CommonActions, +} from '../common/types.js'; + +export interface CosmosActions + extends AutoImplementedActionsByRecommended, + CommonActions { + // TODO +} diff --git a/wallets/core/src/namespaces/evm/actions.ts b/wallets/core/src/namespaces/evm/actions.ts new file mode 100644 index 0000000000..d38bcce4f8 --- /dev/null +++ b/wallets/core/src/namespaces/evm/actions.ts @@ -0,0 +1,99 @@ +import type { EIP1193EventMap } from './eip1193.js'; +import type { EvmActions, ProviderApi } from './types.js'; +import type { Context, Subscriber } from '../../hub/namespaces/mod.js'; +import type { SubscriberCleanUp } from '../../hub/namespaces/types.js'; +import type { CaipAccount, FunctionWithContext } from '../common/types.js'; + +import { AccountId } from 'caip'; + +import { recommended as commonRecommended } from '../common/actions.js'; + +import { CAIP_NAMESPACE } from './constants.js'; +import { getAccounts, switchOrAddNetwork } from './utils.js'; + +export const recommended = [...commonRecommended]; + +export function connect( + instance: () => ProviderApi +): FunctionWithContext { + return async (_context, chain) => { + const evmInstance = instance(); + + if (!evmInstance) { + throw new Error( + 'Do your wallet injected correctly and is evm compatible?' + ); + } + + if (chain) { + await switchOrAddNetwork(evmInstance, chain); + } + + const chainId = await evmInstance.request({ method: 'eth_chainId' }); + + const result = await getAccounts(evmInstance); + + const formatAccounts = result.accounts.map( + (account) => + AccountId.format({ + address: account, + chainId: { + namespace: CAIP_NAMESPACE, + reference: chainId, + }, + }) as CaipAccount + ); + + console.log('[evm] you are a trader?', { formatAccounts }); + + return { + accounts: formatAccounts, + network: result.chainId, + }; + }; +} + +export function changeAccountSubscriber( + instance: () => ProviderApi +): [Subscriber, SubscriberCleanUp] { + let eventCallback: EIP1193EventMap['accountsChanged']; + + return [ + (context) => { + const evmInstance = instance(); + + if (!evmInstance) { + throw new Error( + 'Trying to subscribe to your EVM wallet, but seems its instance is not available.' + ); + } + + const [, setState] = context.state(); + + eventCallback = async (accounts) => { + const chainId = await evmInstance.request({ method: 'eth_chainId' }); + + const formatAccounts = accounts.map((account) => + AccountId.format({ + address: account, + chainId: { + namespace: CAIP_NAMESPACE, + reference: chainId, + }, + }) + ); + + setState('accounts', formatAccounts); + console.log('[evm] Accounts changed:', accounts, { context }); + }; + evmInstance.on('accountsChanged', eventCallback); + }, + () => { + const evmInstance = instance(); + + if (eventCallback && evmInstance) { + evmInstance.removeListener('accountsChanged', eventCallback); + } + }, + ]; +} diff --git a/wallets/core/src/namespaces/evm/after.ts b/wallets/core/src/namespaces/evm/after.ts new file mode 100644 index 0000000000..229710d3bf --- /dev/null +++ b/wallets/core/src/namespaces/evm/after.ts @@ -0,0 +1,3 @@ +import { recommended as commonRecommended } from '../common/after.js'; + +export const recommended = [...commonRecommended]; diff --git a/wallets/core/src/namespaces/evm/and.ts b/wallets/core/src/namespaces/evm/and.ts new file mode 100644 index 0000000000..b1af366a39 --- /dev/null +++ b/wallets/core/src/namespaces/evm/and.ts @@ -0,0 +1,5 @@ +import { connectAndUpdateStateForMultiNetworks } from '../common/mod.js'; + +export const recommended = [ + ['connect', connectAndUpdateStateForMultiNetworks] as const, +]; diff --git a/wallets/core/src/namespaces/evm/before.ts b/wallets/core/src/namespaces/evm/before.ts new file mode 100644 index 0000000000..ceeb4ffad1 --- /dev/null +++ b/wallets/core/src/namespaces/evm/before.ts @@ -0,0 +1,3 @@ +import { recommended as commonRecommended } from '../common/before.js'; + +export const recommended = [...commonRecommended]; diff --git a/wallets/core/src/namespaces/evm/builders.ts b/wallets/core/src/namespaces/evm/builders.ts new file mode 100644 index 0000000000..fec7dd8293 --- /dev/null +++ b/wallets/core/src/namespaces/evm/builders.ts @@ -0,0 +1,10 @@ +import type { EvmActions } from './types.js'; + +import { ActionBuilder } from '../../mod.js'; +import { intoConnectionFinished } from '../common/after.js'; +import { connectAndUpdateStateForMultiNetworks } from '../common/and.js'; + +export const connect = () => + new ActionBuilder('connect') + .and(connectAndUpdateStateForMultiNetworks) + .after(intoConnectionFinished); diff --git a/wallets/core/src/namespaces/evm/constants.ts b/wallets/core/src/namespaces/evm/constants.ts new file mode 100644 index 0000000000..13af0a2d0c --- /dev/null +++ b/wallets/core/src/namespaces/evm/constants.ts @@ -0,0 +1,2 @@ +export const CAIP_NAMESPACE = 'eip155'; +export const CAIP_ETHEREUM_CHAIN_ID = '1'; diff --git a/wallets/core/src/namespaces/evm/eip1193.ts b/wallets/core/src/namespaces/evm/eip1193.ts new file mode 100644 index 0000000000..e2634e370f --- /dev/null +++ b/wallets/core/src/namespaces/evm/eip1193.ts @@ -0,0 +1,1414 @@ +/* + * These are copied from `viem` (EIP1193Provider) + * They have an issue with `node16`, we can not directly import from their package. + * When they fixed the issue, it would better to re-export from viem instead of keeping it here and maintaining the changes. + */ + +// These are types that has so much nested types, for now i put any instead. +type Block = any; +type Log = any; +type TransactionRequest = any; +type BlockNumber = any; +type BlockTag = any; +type BlockIdentifier = any; +type RpcStateOverride = any; +type FeeHistory = any; +type Proof = any; +type Transaction = any; +type TransactionReceipt = any; +type LogTopic = any; +type Uncle = any; +type RpcUserOperation<_ = any> = any; +type RpcEstimateUserOperationGasReturnType = any; +type RpcGetUserOperationByHashReturnType = any; +type RpcUserOperationReceipt = any; + +type KeyofUnion = type extends type ? keyof type : never; + +export type OneOf< + union extends object, + fallback extends object | undefined = undefined, + /// + keys extends KeyofUnion = KeyofUnion +> = union extends infer item + ? Prettify< + item & { + [key in Exclude]?: fallback extends object + ? key extends keyof fallback + ? fallback[key] + : undefined + : undefined; + } + > + : never; + +export type AddEthereumChainParameter = { + /** A 0x-prefixed hexadecimal string */ + chainId: string; + /** The chain name. */ + chainName: string; + /** Native currency for the chain. */ + nativeCurrency?: + | { + name: string; + symbol: string; + decimals: number; + } + | undefined; + rpcUrls: readonly string[]; + blockExplorerUrls?: string[] | undefined; + iconUrls?: string[] | undefined; +}; + +export type NetworkSync = { + /** The current block number */ + currentBlock: Quantity; + /** Number of latest block on the network */ + highestBlock: Quantity; + /** Block number at which syncing started */ + startingBlock: Quantity; +}; + +export type WalletCapabilities = { + [capability: string]: any; +}; + +export type WalletCapabilitiesRecord< + capabilities extends WalletCapabilities = WalletCapabilities, + id extends string | number = Hex +> = { + [chainId in id]: capabilities; +}; + +export type WalletCallReceipt = { + logs: { + address: Hex; + data: Hex; + topics: Hex[]; + }[]; + status: status; + blockHash: Hex; + blockNumber: quantity; + gasUsed: quantity; + transactionHash: Hex; +}; + +export type WalletGrantPermissionsParameters = { + signer?: + | { + type: string; + data?: unknown | undefined; + } + | undefined; + permissions: readonly { + data: unknown; + policies: readonly { + data: unknown; + type: string; + }[]; + required?: boolean | undefined; + type: string; + }[]; + expiry: number; +}; + +export type WalletGrantPermissionsReturnType = { + expiry: number; + factory?: `0x${string}` | undefined; + factoryData?: string | undefined; + grantedPermissions: readonly { + data: unknown; + policies: readonly { + data: unknown; + type: string; + }[]; + required?: boolean | undefined; + type: string; + }[]; + permissionsContext: string; + signerData?: + | { + userOpBuilder?: `0x${string}` | undefined; + submitToAddress?: `0x${string}` | undefined; + } + | undefined; +}; + +export type WalletGetCallsStatusReturnType = { + status: 'PENDING' | 'CONFIRMED'; + receipts?: WalletCallReceipt[] | undefined; +}; + +export type WalletPermissionCaveat = { + type: string; + value: any; +}; + +export type WalletPermission = { + caveats: WalletPermissionCaveat[]; + date: number; + id: string; + invoker: `http://${string}` | `https://${string}`; + parentCapability: 'eth_accounts' | string; +}; + +export type WalletSendCallsParameters< + capabilities extends WalletCapabilities = WalletCapabilities, + chainId extends Hex | number = Hex, + quantity extends Quantity | bigint = Quantity +> = [ + { + calls: OneOf< + | { + to: Address; + data?: Hex | undefined; + value?: quantity | undefined; + } + | { + data: Hex; + } + >[]; + capabilities?: capabilities | undefined; + chainId: chainId; + from: Address; + version: string; + } +]; + +export type WatchAssetParams = { + /** Token type. */ + type: 'ERC20'; + options: { + /** The address of the token contract */ + address: string; + /** A ticker symbol or shorthand, up to 11 characters */ + symbol: string; + /** The number of token decimals */ + decimals: number; + /** A string url of the token logo */ + image?: string | undefined; + }; +}; + +export type ExactPartial = { + [key in keyof type]?: type[key] | undefined; +}; + +export type PartialBy = Omit & + ExactPartial>; + +/** TypeScript type to use for `address` values */ +type Address = `0x${string}`; + +export type ProviderConnectInfo = { + chainId: string; +}; + +export type ProviderMessage = { + type: string; + data: unknown; +}; + +class ProviderRpcError extends Error { + code: number; + details: string; + + constructor(code: number, message: string) { + super(message); + this.code = code; + this.details = message; + } +} + +export type EIP1193EventMap = { + accountsChanged(accounts: Address[]): void; + chainChanged(chainId: string): void; + connect(connectInfo: ProviderConnectInfo): void; + disconnect(error: ProviderRpcError): void; + message(message: ProviderMessage): void; +}; + +export type EIP1193Events = { + on( + event: event, + listener: EIP1193EventMap[event] + ): void; + removeListener( + event: event, + listener: EIP1193EventMap[event] + ): void; +}; + +export type Prettify = { + [K in keyof T]: T[K]; + // eslint-disable-next-line @typescript-eslint/ban-types +} & {}; + +export type RpcSchema = readonly { + Method: string; + Parameters?: unknown | undefined; + ReturnType: unknown; +}[]; + +export type RpcSchemaOverride = Omit; + +export type EIP1193Parameters< + rpcSchema extends RpcSchema | undefined = undefined +> = rpcSchema extends RpcSchema + ? { + [K in keyof rpcSchema]: Prettify< + { + method: rpcSchema[K] extends rpcSchema[number] + ? rpcSchema[K]['Method'] + : never; + } & (rpcSchema[K] extends rpcSchema[number] + ? rpcSchema[K]['Parameters'] extends undefined + ? { params?: undefined } + : { params: rpcSchema[K]['Parameters'] } + : never) + >; + }[number] + : { + method: string; + params?: unknown | undefined; + }; + +export type EIP1193RequestOptions = { + // Deduplicate in-flight requests. + dedupe?: boolean | undefined; + // The base delay (in ms) between retries. + retryDelay?: number | undefined; + // The max number of times to retry. + retryCount?: number | undefined; + /** Unique identifier for the request. */ + uid?: string | undefined; +}; + +type DerivedRpcSchema< + rpcSchema extends RpcSchema | undefined, + rpcSchemaOverride extends RpcSchemaOverride | undefined +> = rpcSchemaOverride extends RpcSchemaOverride + ? [rpcSchemaOverride & { Method: string }] + : rpcSchema; + +export type EIP1193RequestFn< + rpcSchema extends RpcSchema | undefined = undefined +> = < + rpcSchemaOverride extends RpcSchemaOverride | undefined = undefined, + _parameters extends EIP1193Parameters< + DerivedRpcSchema + > = EIP1193Parameters>, + _returnType = DerivedRpcSchema extends RpcSchema + ? Extract< + DerivedRpcSchema[number], + { Method: _parameters['method'] } + >['ReturnType'] + : unknown +>( + args: _parameters, + options?: EIP1193RequestOptions | undefined +) => Promise<_returnType>; + +export type Hex = `0x${string}`; +export type Hash = `0x${string}`; +export type Quantity = `0x${string}`; + +export type PublicRpcSchema = [ + /** + * @description Returns the version of the current client + * + * @example + * provider.request({ method: 'web3_clientVersion' }) + * // => 'MetaMask/v1.0.0' + */ + { + Method: 'web3_clientVersion'; + Parameters?: undefined; + ReturnType: string; + }, + /** + * @description Hashes data using the Keccak-256 algorithm + * + * @example + * provider.request({ method: 'web3_sha3', params: ['0x68656c6c6f20776f726c64'] }) + * // => '0xc94770007dda54cF92009BFF0dE90c06F603a09f' + */ + { + Method: 'web3_sha3'; + Parameters: [data: Hash]; + ReturnType: string; + }, + /** + * @description Determines if this client is listening for new network connections + * + * @example + * provider.request({ method: 'net_listening' }) + * // => true + */ + { + Method: 'net_listening'; + Parameters?: undefined; + ReturnType: boolean; + }, + /** + * @description Returns the number of peers currently connected to this client + * + * @example + * provider.request({ method: 'net_peerCount' }) + * // => '0x1' + */ + { + Method: 'net_peerCount'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Returns the chain ID associated with the current network + * + * @example + * provider.request({ method: 'net_version' }) + * // => '1' + */ + { + Method: 'net_version'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Returns the base fee per blob gas in wei. + * + * @example + * provider.request({ method: 'eth_blobBaseFee' }) + * // => '0x09184e72a000' + */ + { + Method: 'eth_blobBaseFee'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Returns the number of the most recent block seen by this client + * + * @example + * provider.request({ method: 'eth_blockNumber' }) + * // => '0x1b4' + */ + { + Method: 'eth_blockNumber'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Executes a new message call immediately without submitting a transaction to the network + * + * @example + * provider.request({ method: 'eth_call', params: [{ to: '0x...', data: '0x...' }] }) + * // => '0x...' + */ + { + Method: 'eth_call'; + Parameters: + | [transaction: ExactPartial] + | [ + transaction: ExactPartial, + block: BlockNumber | BlockTag | BlockIdentifier + ] + | [ + transaction: ExactPartial, + block: BlockNumber | BlockTag | BlockIdentifier, + stateOverrideSet: RpcStateOverride + ]; + ReturnType: Hex; + }, + /** + * @description Returns the chain ID associated with the current network + * @example + * provider.request({ method: 'eth_chainId' }) + * // => '1' + */ + { + Method: 'eth_chainId'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Returns the client coinbase address. + * @example + * provider.request({ method: 'eth_coinbase' }) + * // => '0x...' + */ + { + Method: 'eth_coinbase'; + Parameters?: undefined; + ReturnType: Address; + }, + /** + * @description Estimates the gas necessary to complete a transaction without submitting it to the network + * + * @example + * provider.request({ + * method: 'eth_estimateGas', + * params: [{ from: '0x...', to: '0x...', value: '0x...' }] + * }) + * // => '0x5208' + */ + { + Method: 'eth_estimateGas'; + Parameters: + | [transaction: TransactionRequest] + | [transaction: TransactionRequest, block: BlockNumber | BlockTag] + | [ + transaction: TransactionRequest, + block: BlockNumber | BlockTag, + stateOverride: RpcStateOverride + ]; + ReturnType: Quantity; + }, + /** + * @description Returns a collection of historical gas information + * + * @example + * provider.request({ + * method: 'eth_feeHistory', + * params: ['4', 'latest', ['25', '75']] + * }) + * // => { + * // oldestBlock: '0x1', + * // baseFeePerGas: ['0x1', '0x2', '0x3', '0x4'], + * // gasUsedRatio: ['0x1', '0x2', '0x3', '0x4'], + * // reward: [['0x1', '0x2'], ['0x3', '0x4'], ['0x5', '0x6'], ['0x7', '0x8']] + * // } + * + */ + { + Method: 'eth_feeHistory'; + Parameters: [ + /** Number of blocks in the requested range. Between 1 and 1024 blocks can be requested in a single query. Less than requested may be returned if not all blocks are available. */ + blockCount: Quantity, + /** Highest number block of the requested range. */ + newestBlock: BlockNumber | BlockTag, + /** A monotonically increasing list of percentile values to sample from each block's effective priority fees per gas in ascending order, weighted by gas used. */ + rewardPercentiles: number[] | undefined + ]; + ReturnType: FeeHistory; + }, + /** + * @description Returns the current price of gas expressed in wei + * + * @example + * provider.request({ method: 'eth_gasPrice' }) + * // => '0x09184e72a000' + */ + { + Method: 'eth_gasPrice'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Returns the balance of an address in wei + * + * @example + * provider.request({ method: 'eth_getBalance', params: ['0x...', 'latest'] }) + * // => '0x12a05...' + */ + { + Method: 'eth_getBalance'; + Parameters: [ + address: Address, + block: BlockNumber | BlockTag | BlockIdentifier + ]; + ReturnType: Quantity; + }, + /** + * @description Returns information about a block specified by hash + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getBlockByHash', params: ['0x...', true] }) + * // => { + * // number: '0x1b4', + * // hash: '0x...', + * // parentHash: '0x...', + * // ... + * // } + */ + { + Method: 'eth_getBlockByHash'; + Parameters: [ + /** hash of a block */ + hash: Hash, + /** true will pull full transaction objects, false will pull transaction hashes */ + includeTransactionObjects: boolean + ]; + ReturnType: Block | null; + }, + /** + * @description Returns information about a block specified by number + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getBlockByNumber', params: ['0x1b4', true] }) + * // => { + * // number: '0x1b4', + * // hash: '0x...', + * // parentHash: '0x...', + * // ... + * // } + */ + { + Method: 'eth_getBlockByNumber'; + Parameters: [ + /** block number, or one of "latest", "safe", "finalized", "earliest" or "pending" */ + block: BlockNumber | BlockTag, + /** true will pull full transaction objects, false will pull transaction hashes */ + includeTransactionObjects: boolean + ]; + ReturnType: Block | null; + }, + /** + * @description Returns the number of transactions in a block specified by block hash + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getBlockTransactionCountByHash', params: ['0x...'] }) + * // => '0x1' + */ + { + Method: 'eth_getBlockTransactionCountByHash'; + Parameters: [hash: Hash]; + ReturnType: Quantity; + }, + /** + * @description Returns the number of transactions in a block specified by block number + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getBlockTransactionCountByNumber', params: ['0x1b4'] }) + * // => '0x1' + */ + { + Method: 'eth_getBlockTransactionCountByNumber'; + Parameters: [block: BlockNumber | BlockTag]; + ReturnType: Quantity; + }, + /** + * @description Returns the contract code stored at a given address + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getCode', params: ['0x...', 'latest'] }) + * // => '0x...' + */ + { + Method: 'eth_getCode'; + Parameters: [ + address: Address, + block: BlockNumber | BlockTag | BlockIdentifier + ]; + ReturnType: Hex; + }, + /** + * @description Returns a list of all logs based on filter ID since the last log retrieval + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getFilterChanges', params: ['0x...'] }) + * // => [{ ... }, { ... }] + */ + { + Method: 'eth_getFilterChanges'; + Parameters: [filterId: Quantity]; + ReturnType: Log[] | Hex[]; + }, + /** + * @description Returns a list of all logs based on filter ID + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getFilterLogs', params: ['0x...'] }) + * // => [{ ... }, { ... }] + */ + { + Method: 'eth_getFilterLogs'; + Parameters: [filterId: Quantity]; + ReturnType: Log[]; + }, + /** + * @description Returns a list of all logs based on a filter object + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getLogs', params: [{ fromBlock: '0x...', toBlock: '0x...', address: '0x...', topics: ['0x...'] }] }) + * // => [{ ... }, { ... }] + */ + { + Method: 'eth_getLogs'; + Parameters: [ + { + address?: Address | Address[] | undefined; + topics?: LogTopic[] | undefined; + } & ( + | { + fromBlock?: BlockNumber | BlockTag | undefined; + toBlock?: BlockNumber | BlockTag | undefined; + blockHash?: undefined; + } + | { + fromBlock?: undefined; + toBlock?: undefined; + blockHash?: Hash | undefined; + } + ) + ]; + ReturnType: Log[]; + }, + /** + * @description Returns the account and storage values of the specified account including the Merkle-proof. + * @link https://eips.ethereum.org/EIPS/eip-1186 + * @example + * provider.request({ method: 'eth_getProof', params: ['0x...', ['0x...'], 'latest'] }) + * // => { + * // ... + * // } + */ + { + Method: 'eth_getProof'; + Parameters: [ + /** Address of the account. */ + address: Address, + /** An array of storage-keys that should be proofed and included. */ + storageKeys: Hash[], + block: BlockNumber | BlockTag + ]; + ReturnType: Proof; + }, + /** + * @description Returns the value from a storage position at an address + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getStorageAt', params: ['0x...', '0x...', 'latest'] }) + * // => '0x...' + */ + { + Method: 'eth_getStorageAt'; + Parameters: [ + address: Address, + index: Quantity, + block: BlockNumber | BlockTag | BlockIdentifier + ]; + ReturnType: Hex; + }, + /** + * @description Returns information about a transaction specified by block hash and transaction index + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getTransactionByBlockHashAndIndex', params: ['0x...', '0x...'] }) + * // => { ... } + */ + { + Method: 'eth_getTransactionByBlockHashAndIndex'; + Parameters: [hash: Hash, index: Quantity]; + ReturnType: Transaction | null; + }, + /** + * @description Returns information about a transaction specified by block number and transaction index + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getTransactionByBlockNumberAndIndex', params: ['0x...', '0x...'] }) + * // => { ... } + */ + { + Method: 'eth_getTransactionByBlockNumberAndIndex'; + Parameters: [block: BlockNumber | BlockTag, index: Quantity]; + ReturnType: Transaction | null; + }, + /** + * @description Returns information about a transaction specified by hash + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getTransactionByHash', params: ['0x...'] }) + * // => { ... } + */ + { + Method: 'eth_getTransactionByHash'; + Parameters: [hash: Hash]; + ReturnType: Transaction | null; + }, + /** + * @description Returns the number of transactions sent from an address + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getTransactionCount', params: ['0x...', 'latest'] }) + * // => '0x1' + */ + { + Method: 'eth_getTransactionCount'; + Parameters: [ + address: Address, + block: BlockNumber | BlockTag | BlockIdentifier + ]; + ReturnType: Quantity; + }, + /** + * @description Returns the receipt of a transaction specified by hash + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getTransactionReceipt', params: ['0x...'] }) + * // => { ... } + */ + { + Method: 'eth_getTransactionReceipt'; + Parameters: [hash: Hash]; + ReturnType: TransactionReceipt | null; + }, + /** + * @description Returns information about an uncle specified by block hash and uncle index position + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getUncleByBlockHashAndIndex', params: ['0x...', '0x...'] }) + * // => { ... } + */ + { + Method: 'eth_getUncleByBlockHashAndIndex'; + Parameters: [hash: Hash, index: Quantity]; + ReturnType: Uncle | null; + }, + /** + * @description Returns information about an uncle specified by block number and uncle index position + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getUncleByBlockNumberAndIndex', params: ['0x...', '0x...'] }) + * // => { ... } + */ + { + Method: 'eth_getUncleByBlockNumberAndIndex'; + Parameters: [block: BlockNumber | BlockTag, index: Quantity]; + ReturnType: Uncle | null; + }, + /** + * @description Returns the number of uncles in a block specified by block hash + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getUncleCountByBlockHash', params: ['0x...'] }) + * // => '0x1' + */ + { + Method: 'eth_getUncleCountByBlockHash'; + Parameters: [hash: Hash]; + ReturnType: Quantity; + }, + /** + * @description Returns the number of uncles in a block specified by block number + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_getUncleCountByBlockNumber', params: ['0x...'] }) + * // => '0x1' + */ + { + Method: 'eth_getUncleCountByBlockNumber'; + Parameters: [block: BlockNumber | BlockTag]; + ReturnType: Quantity; + }, + /** + * @description Returns the current maxPriorityFeePerGas in wei. + * @link https://ethereum.github.io/execution-apis/api-documentation/ + * @example + * provider.request({ method: 'eth_maxPriorityFeePerGas' }) + * // => '0x5f5e100' + */ + { + Method: 'eth_maxPriorityFeePerGas'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Creates a filter to listen for new blocks that can be used with `eth_getFilterChanges` + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_newBlockFilter' }) + * // => '0x1' + */ + { + Method: 'eth_newBlockFilter'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Creates a filter to listen for specific state changes that can then be used with `eth_getFilterChanges` + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_newFilter', params: [{ fromBlock: '0x...', toBlock: '0x...', address: '0x...', topics: ['0x...'] }] }) + * // => '0x1' + */ + { + Method: 'eth_newFilter'; + Parameters: [ + filter: { + fromBlock?: BlockNumber | BlockTag | undefined; + toBlock?: BlockNumber | BlockTag | undefined; + address?: Address | Address[] | undefined; + topics?: LogTopic[] | undefined; + } + ]; + ReturnType: Quantity; + }, + /** + * @description Creates a filter to listen for new pending transactions that can be used with `eth_getFilterChanges` + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_newPendingTransactionFilter' }) + * // => '0x1' + */ + { + Method: 'eth_newPendingTransactionFilter'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Returns the current Ethereum protocol version + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_protocolVersion' }) + * // => '54' + */ + { + Method: 'eth_protocolVersion'; + Parameters?: undefined; + ReturnType: string; + }, + /** + * @description Sends a **signed** transaction to the network + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_sendRawTransaction', params: ['0x...'] }) + * // => '0x...' + */ + { + Method: 'eth_sendRawTransaction'; + Parameters: [signedTransaction: Hex]; + ReturnType: Hash; + }, + /** + * @description Destroys a filter based on filter ID + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_uninstallFilter', params: ['0x1'] }) + * // => true + */ + { + Method: 'eth_uninstallFilter'; + Parameters: [filterId: Quantity]; + ReturnType: boolean; + } +]; + +export type WalletRpcSchema = [ + /** + * @description Returns a list of addresses owned by this client + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_accounts' }) + * // => ['0x0fB69...'] + */ + { + Method: 'eth_accounts'; + Parameters?: undefined; + ReturnType: Address[]; + }, + /** + * @description Returns the current chain ID associated with the wallet. + * @example + * provider.request({ method: 'eth_chainId' }) + * // => '1' + */ + { + Method: 'eth_chainId'; + Parameters?: undefined; + ReturnType: Quantity; + }, + /** + * @description Estimates the gas necessary to complete a transaction without submitting it to the network + * + * @example + * provider.request({ + * method: 'eth_estimateGas', + * params: [{ from: '0x...', to: '0x...', value: '0x...' }] + * }) + * // => '0x5208' + */ + { + Method: 'eth_estimateGas'; + Parameters: + | [transaction: TransactionRequest] + | [transaction: TransactionRequest, block: BlockNumber | BlockTag] + | [ + transaction: TransactionRequest, + block: BlockNumber | BlockTag, + stateOverride: RpcStateOverride + ]; + ReturnType: Quantity; + }, + /** + * @description Requests that the user provides an Ethereum address to be identified by. Typically causes a browser extension popup to appear. + * @link https://eips.ethereum.org/EIPS/eip-1102 + * @example + * provider.request({ method: 'eth_requestAccounts' }] }) + * // => ['0x...', '0x...'] + */ + { + Method: 'eth_requestAccounts'; + Parameters?: undefined; + ReturnType: Address[]; + }, + /** + * @description Creates, signs, and sends a new transaction to the network + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_sendTransaction', params: [{ from: '0x...', to: '0x...', value: '0x...' }] }) + * // => '0x...' + */ + { + Method: 'eth_sendTransaction'; + Parameters: [transaction: TransactionRequest]; + ReturnType: Hash; + }, + /** + * @description Sends and already-signed transaction to the network + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_sendRawTransaction', params: ['0x...'] }) + * // => '0x...' + */ + { + Method: 'eth_sendRawTransaction'; + Parameters: [signedTransaction: Hex]; + ReturnType: Hash; + }, + /** + * @description Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))` + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_sign', params: ['0x...', '0x...'] }) + * // => '0x...' + */ + { + Method: 'eth_sign'; + Parameters: [ + /** Address to use for signing */ + address: Address, + /** Data to sign */ + data: Hex + ]; + ReturnType: Hex; + }, + /** + * @description Signs a transaction that can be submitted to the network at a later time using with `eth_sendRawTransaction` + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_signTransaction', params: [{ from: '0x...', to: '0x...', value: '0x...' }] }) + * // => '0x...' + */ + { + Method: 'eth_signTransaction'; + Parameters: [request: TransactionRequest]; + ReturnType: Hex; + }, + /** + * @description Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))` + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_signTypedData_v4', params: [{ from: '0x...', data: [{ type: 'string', name: 'message', value: 'hello world' }] }] }) + * // => '0x...' + */ + { + Method: 'eth_signTypedData_v4'; + Parameters: [ + /** Address to use for signing */ + address: Address, + /** Message to sign containing type information, a domain separator, and data */ + message: string + ]; + ReturnType: Hex; + }, + /** + * @description Returns information about the status of this client’s network synchronization + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'eth_syncing' }) + * // => { startingBlock: '0x...', currentBlock: '0x...', highestBlock: '0x...' } + */ + { + Method: 'eth_syncing'; + Parameters?: undefined; + ReturnType: NetworkSync | false; + }, + /** + * @description Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))` + * @link https://eips.ethereum.org/EIPS/eip-1474 + * @example + * provider.request({ method: 'personal_sign', params: ['0x...', '0x...'] }) + * // => '0x...' + */ + { + Method: 'personal_sign'; + Parameters: [ + /** Data to sign */ + data: Hex, + /** Address to use for signing */ + address: Address + ]; + ReturnType: Hex; + }, + /** + * @description Add an Ethereum chain to the wallet. + * @link https://eips.ethereum.org/EIPS/eip-3085 + * @example + * provider.request({ method: 'wallet_addEthereumChain', params: [{ chainId: 1, rpcUrl: 'https://mainnet.infura.io/v3/...' }] }) + * // => { ... } + */ + { + Method: 'wallet_addEthereumChain'; + Parameters: [chain: AddEthereumChainParameter]; + ReturnType: null; + }, + /** + * @description Returns the status of a call batch that was sent via `wallet_sendCalls`. + * @link https://eips.ethereum.org/EIPS/eip-5792 + * @example + * provider.request({ method: 'wallet_getCallsStatus' }) + * // => { ... } + */ + { + Method: 'wallet_getCallsStatus'; + Parameters?: [string]; + ReturnType: WalletGetCallsStatusReturnType; + }, + /** + * @description Gets the connected wallet's capabilities. + * @link https://eips.ethereum.org/EIPS/eip-5792 + * @example + * provider.request({ method: 'wallet_getCapabilities' }) + * // => { ... } + */ + { + Method: 'wallet_getCapabilities'; + Parameters?: [Address]; + ReturnType: Prettify; + }, + /** + * @description Gets the wallets current permissions. + * @link https://eips.ethereum.org/EIPS/eip-2255 + * @example + * provider.request({ method: 'wallet_getPermissions' }) + * // => { ... } + */ + { + Method: 'wallet_getPermissions'; + Parameters?: undefined; + ReturnType: WalletPermission[]; + }, + /** + * @description Requests permissions from a wallet + * @link https://eips.ethereum.org/EIPS/eip-7715 + * @example + * provider.request({ method: 'wallet_grantPermissions', params: [{ ... }] }) + * // => { ... } + */ + { + Method: 'wallet_grantPermissions'; + Parameters?: [WalletGrantPermissionsParameters]; + ReturnType: Prettify; + }, + /** + * @description Requests the given permissions from the user. + * @link https://eips.ethereum.org/EIPS/eip-2255 + * @example + * provider.request({ method: 'wallet_requestPermissions', params: [{ eth_accounts: {} }] }) + * // => { ... } + */ + { + Method: 'wallet_requestPermissions'; + Parameters: [permissions: { eth_accounts: Record }]; + ReturnType: WalletPermission[]; + }, + /** + * @description Revokes the given permissions from the user. + * @link https://github.com/MetaMask/metamask-improvement-proposals/blob/main/MIPs/mip-2.md + * @example + * provider.request({ method: 'wallet_revokePermissions', params: [{ eth_accounts: {} }] }) + * // => { ... } + */ + { + Method: 'wallet_revokePermissions'; + Parameters: [permissions: { eth_accounts: Record }]; + ReturnType: null; + }, + /** + * @description Requests the connected wallet to send a batch of calls. + * @link https://eips.ethereum.org/EIPS/eip-5792 + * @example + * provider.request({ method: 'wallet_sendCalls' }) + * // => { ... } + */ + { + Method: 'wallet_sendCalls'; + Parameters?: WalletSendCallsParameters; + ReturnType: string; + }, + /** + * @description Requests for the wallet to show information about a call batch + * that was sent via `wallet_sendCalls`. + * @link https://eips.ethereum.org/EIPS/eip-5792 + * @example + * provider.request({ method: 'wallet_showCallsStatus', params: ['...'] }) + */ + { + Method: 'wallet_showCallsStatus'; + Parameters?: [string]; + ReturnType: void; + }, + /** + * @description Switch the wallet to the given Ethereum chain. + * @link https://eips.ethereum.org/EIPS/eip-3326 + * @example + * provider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0xf00' }] }) + * // => { ... } + */ + { + Method: 'wallet_switchEthereumChain'; + Parameters: [chain: { chainId: string }]; + ReturnType: null; + }, + /** + * @description Requests that the user tracks the token in their wallet. Returns a boolean indicating if the token was successfully added. + * @link https://eips.ethereum.org/EIPS/eip-747 + * @example + * provider.request({ method: 'wallet_watchAsset' }] }) + * // => true + */ + { + Method: 'wallet_watchAsset'; + Parameters: WatchAssetParams; + ReturnType: boolean; + } +]; + +export type BundlerRpcSchema = [ + /** + * @description Returns the chain ID associated with the current network + * + * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_chainid + */ + { + Method: 'eth_chainId'; + Parameters?: undefined; + ReturnType: Hex; + }, + /** + * @description Estimate the gas values for a UserOperation. + * + * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_estimateuseroperationgas + * + * @example + * provider.request({ + * method: 'eth_estimateUserOperationGas', + * params: [{ ... }] + * }) + * // => { ... } + */ + { + Method: 'eth_estimateUserOperationGas'; + Parameters: + | [userOperation: RpcUserOperation, entrypoint: Address] + | [ + userOperation: RpcUserOperation, + entrypoint: Address, + stateOverrideSet: RpcStateOverride + ]; + ReturnType: RpcEstimateUserOperationGasReturnType; + }, + /** + * @description Return a UserOperation based on a hash. + * + * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_getuseroperationbyhash + * + * @example + * provider.request({ + * method: 'eth_getUserOperationByHash', + * params: ['0x...'] + * }) + * // => { ... } + */ + { + Method: 'eth_getUserOperationByHash'; + Parameters: [hash: Hash]; + ReturnType: RpcGetUserOperationByHashReturnType | null; + }, + /** + * @description Return a UserOperation receipt based on a hash. + * + * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_getuseroperationreceipt + * + * @example + * provider.request({ + * method: 'eth_getUserOperationReceipt', + * params: ['0x...'] + * }) + * // => { ... } + */ + { + Method: 'eth_getUserOperationReceipt'; + Parameters: [hash: Hash]; + ReturnType: RpcUserOperationReceipt | null; + }, + /** + * @description Submits a User Operation object to the User Operation pool of the client. + * + * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_senduseroperation + * + * @example + * provider.request({ + * method: 'eth_sendUserOperation', + * params: [{ ... }] + * }) + * // => '0x...' + */ + { + Method: 'eth_sendUserOperation'; + Parameters: [userOperation: RpcUserOperation, entrypoint: Address]; + ReturnType: Hash; + }, + /** + * @description Return the list of supported entry points by the client. + * + * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_supportedentrypoints + */ + { + Method: 'eth_supportedEntryPoints'; + Parameters?: undefined; + ReturnType: readonly Address[]; + } +]; + +export type PaymasterRpcSchema = [ + /** + * @description Returns the chain ID associated with the current network + * + * @link https://eips.ethereum.org/EIPS/eip-4337#-eth_chainid + */ + { + Method: 'pm_getPaymasterStubData'; + Parameters?: [ + userOperation: OneOf< + | PartialBy< + Pick< + RpcUserOperation<'0.6'>, + | 'callData' + | 'callGasLimit' + | 'initCode' + | 'maxFeePerGas' + | 'maxPriorityFeePerGas' + | 'nonce' + | 'sender' + | 'preVerificationGas' + | 'verificationGasLimit' + >, + | 'callGasLimit' + | 'initCode' + | 'maxFeePerGas' + | 'maxPriorityFeePerGas' + | 'preVerificationGas' + | 'verificationGasLimit' + > + | PartialBy< + Pick< + RpcUserOperation<'0.7'>, + | 'callData' + | 'callGasLimit' + | 'factory' + | 'factoryData' + | 'maxFeePerGas' + | 'maxPriorityFeePerGas' + | 'nonce' + | 'sender' + | 'preVerificationGas' + | 'verificationGasLimit' + >, + | 'callGasLimit' + | 'factory' + | 'factoryData' + | 'maxFeePerGas' + | 'maxPriorityFeePerGas' + | 'preVerificationGas' + | 'verificationGasLimit' + > + >, + entrypoint: Address, + chainId: Hex, + context: unknown + ]; + ReturnType: OneOf< + | { paymasterAndData: Hex } + | { + paymaster: Address; + paymasterData: Hex; + paymasterVerificationGasLimit: Hex; + paymasterPostOpGasLimit: Hex; + } + > & { + sponsor?: { name: string; icon?: string | undefined } | undefined; + isFinal?: boolean | undefined; + }; + }, + /** + * @description Returns values to be used in paymaster-related fields of a signed user operation. + * + * @link https://github.com/ethereum/ERCs/blob/master/ERCS/erc-7677.md#pm_getpaymasterdata + */ + { + Method: 'pm_getPaymasterData'; + Parameters?: [ + userOperation: + | Pick< + RpcUserOperation<'0.6'>, + | 'callData' + | 'callGasLimit' + | 'initCode' + | 'maxFeePerGas' + | 'maxPriorityFeePerGas' + | 'nonce' + | 'sender' + | 'preVerificationGas' + | 'verificationGasLimit' + > + | Pick< + RpcUserOperation<'0.7'>, + | 'callData' + | 'callGasLimit' + | 'factory' + | 'factoryData' + | 'maxFeePerGas' + | 'maxPriorityFeePerGas' + | 'nonce' + | 'sender' + | 'preVerificationGas' + | 'verificationGasLimit' + >, + entrypoint: Address, + chainId: Hex, + context: unknown + ]; + ReturnType: OneOf< + | { paymasterAndData: Hex } + | { + paymaster: Address; + paymasterData: Hex; + paymasterVerificationGasLimit: Hex; + paymasterPostOpGasLimit: Hex; + } + >; + } +]; + +export type EIP1474Methods = [ + ...PublicRpcSchema, + ...WalletRpcSchema, + ...BundlerRpcSchema, + ...PaymasterRpcSchema +]; + +export type EIP1193Provider = EIP1193Events & { + request: EIP1193RequestFn; +}; diff --git a/wallets/core/src/namespaces/evm/mod.ts b/wallets/core/src/namespaces/evm/mod.ts new file mode 100644 index 0000000000..5bbacc853f --- /dev/null +++ b/wallets/core/src/namespaces/evm/mod.ts @@ -0,0 +1,9 @@ +export * as actions from './actions.js'; +export * as after from './after.js'; +export * as and from './and.js'; +export * as before from './before.js'; +export * as utils from './utils.js'; +export * as builders from './builders.js'; + +export type { EvmActions, ProviderApi } from './types.js'; +export { CAIP_NAMESPACE, CAIP_ETHEREUM_CHAIN_ID } from './constants.js'; diff --git a/wallets/core/src/namespaces/evm/types.ts b/wallets/core/src/namespaces/evm/types.ts new file mode 100644 index 0000000000..1ed4593a4d --- /dev/null +++ b/wallets/core/src/namespaces/evm/types.ts @@ -0,0 +1,28 @@ +import type { + AccountsWithActiveChain, + AutoImplementedActionsByRecommended, + CommonActions, +} from '../common/types.js'; + +export interface EvmActions + extends AutoImplementedActionsByRecommended, + CommonActions { + connect: (chain?: Chain | ChainId) => Promise; +} + +export type { EIP1193Provider as ProviderApi } from './eip1193.js'; + +// A 0x-prefixed hexadecimal string +export type ChainId = string; +export type Chain = { + chainId: ChainId; + chainName: string; + nativeCurrency: { + name: string; + symbol: string; // 2-6 characters long + decimals: number; + }; + rpcUrls: string[]; + blockExplorerUrls?: string[]; + iconUrls?: string[]; // Currently ignored. +}; diff --git a/wallets/core/src/namespaces/evm/utils.ts b/wallets/core/src/namespaces/evm/utils.ts new file mode 100644 index 0000000000..1778975416 --- /dev/null +++ b/wallets/core/src/namespaces/evm/utils.ts @@ -0,0 +1,52 @@ +import type { Chain, ChainId, ProviderApi } from './types.js'; + +export async function getAccounts(provider: ProviderApi) { + const [accounts, chainId] = await Promise.all([ + provider.request({ method: 'eth_requestAccounts' }), + provider.request({ method: 'eth_chainId' }), + ]); + + return { + accounts, + chainId, + }; +} + +export async function suggestNetwork(instance: ProviderApi, chain: Chain) { + return await instance.request({ + method: 'wallet_addEthereumChain', + params: [chain], + }); +} + +export async function switchNetwork(instance: ProviderApi, chainId: ChainId) { + return await instance.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: chainId }], + }); +} + +export async function switchOrAddNetwork( + instance: ProviderApi, + chain: ChainId | Chain +) { + try { + const chainId = typeof chain === 'string' ? chain : chain.chainId; + await switchNetwork(instance, chainId); + } catch (switchError) { + const error = switchError as { code: number }; + + const NOT_FOUND_CHAIN_ERROR_CODE = 4902; + if ( + typeof chain !== 'string' && + (error.code === NOT_FOUND_CHAIN_ERROR_CODE || !error.code) + ) { + /* + * Note: on WalletConnect `code` is undefined so we have to use !switchError.code as fallback. + * This error code indicates that the chain has not been added to wallet. + */ + await suggestNetwork(instance, chain); + } + throw switchError; + } +} diff --git a/wallets/core/src/namespaces/solana/actions.ts b/wallets/core/src/namespaces/solana/actions.ts new file mode 100644 index 0000000000..1178ae2b18 --- /dev/null +++ b/wallets/core/src/namespaces/solana/actions.ts @@ -0,0 +1,73 @@ +import type { ProviderApi } from './types.js'; +import type { Subscriber } from '../../hub/namespaces/mod.js'; +import type { SubscriberCleanUp } from '../../hub/namespaces/types.js'; +import type { AnyFunction } from '../common/types.js'; + +import { AccountId } from 'caip'; + +import { recommended as commonRecommended } from '../common/actions.js'; + +import { CAIP_NAMESPACE, CAIP_SOLANA_CHAIN_ID } from './constants.js'; + +export const recommended = [...commonRecommended]; + +export function changeAccountSubscriber( + instance: () => ProviderApi | undefined +): [Subscriber, SubscriberCleanUp] { + let eventCallback: AnyFunction; + + // subscriber can be passed to `or`, it will get the error and should rethrow error to pass the error to next `or` or throw error. + return [ + (context, err) => { + const solanaInstance = instance(); + + if (!solanaInstance) { + throw new Error( + 'Trying to subscribe to your Solana wallet, but seems its instance is not available.' + ); + } + + const [, setState] = context.state(); + + eventCallback = (publicKey) => { + /* + * In Phantom, when user is switching to an account which is not connected to dApp yet, it returns a null. + * So null means we don't have access to account and we 0 need to disconnect and let the user connect the account. + */ + if (!publicKey) { + context.action('disconnect'); + return; + } + + setState('accounts', [ + AccountId.format({ + address: publicKey.toString(), + chainId: { + namespace: CAIP_NAMESPACE, + reference: CAIP_SOLANA_CHAIN_ID, + }, + }), + ]); + console.log('[solana] Accounts changed to:', publicKey.toString(), { + context, + }); + }; + solanaInstance.on('accountChanged', eventCallback); + + if (err instanceof Error) { + throw err; + } + }, + (_context, err) => { + const solanaInstance = instance(); + + if (eventCallback && solanaInstance) { + solanaInstance.off('accountChanged', eventCallback); + } + + if (err instanceof Error) { + throw err; + } + }, + ]; +} diff --git a/wallets/core/src/namespaces/solana/after.ts b/wallets/core/src/namespaces/solana/after.ts new file mode 100644 index 0000000000..229710d3bf --- /dev/null +++ b/wallets/core/src/namespaces/solana/after.ts @@ -0,0 +1,3 @@ +import { recommended as commonRecommended } from '../common/after.js'; + +export const recommended = [...commonRecommended]; diff --git a/wallets/core/src/namespaces/solana/and.ts b/wallets/core/src/namespaces/solana/and.ts new file mode 100644 index 0000000000..0eb8ef6141 --- /dev/null +++ b/wallets/core/src/namespaces/solana/and.ts @@ -0,0 +1,5 @@ +import { connectAndUpdateStateForSingleNetwork } from '../common/mod.js'; + +export const recommended = [ + ['connect', connectAndUpdateStateForSingleNetwork] as const, +]; diff --git a/wallets/core/src/namespaces/solana/before.ts b/wallets/core/src/namespaces/solana/before.ts new file mode 100644 index 0000000000..ceeb4ffad1 --- /dev/null +++ b/wallets/core/src/namespaces/solana/before.ts @@ -0,0 +1,3 @@ +import { recommended as commonRecommended } from '../common/before.js'; + +export const recommended = [...commonRecommended]; diff --git a/wallets/core/src/namespaces/solana/builders.ts b/wallets/core/src/namespaces/solana/builders.ts new file mode 100644 index 0000000000..73f4dc1d24 --- /dev/null +++ b/wallets/core/src/namespaces/solana/builders.ts @@ -0,0 +1,10 @@ +import type { SolanaActions } from './types.js'; + +import { ActionBuilder } from '../../mod.js'; +import { intoConnectionFinished } from '../common/after.js'; +import { connectAndUpdateStateForSingleNetwork } from '../common/and.js'; + +export const connect = () => + new ActionBuilder('connect') + .and(connectAndUpdateStateForSingleNetwork) + .after(intoConnectionFinished); diff --git a/wallets/core/src/namespaces/solana/constants.ts b/wallets/core/src/namespaces/solana/constants.ts new file mode 100644 index 0000000000..31d6877448 --- /dev/null +++ b/wallets/core/src/namespaces/solana/constants.ts @@ -0,0 +1,2 @@ +export const CAIP_NAMESPACE = 'solana'; +export const CAIP_SOLANA_CHAIN_ID = '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'; diff --git a/wallets/core/src/namespaces/solana/mod.ts b/wallets/core/src/namespaces/solana/mod.ts new file mode 100644 index 0000000000..4bb7321dae --- /dev/null +++ b/wallets/core/src/namespaces/solana/mod.ts @@ -0,0 +1,8 @@ +export * as actions from './actions.js'; +export * as after from './after.js'; +export * as and from './and.js'; +export * as before from './before.js'; +export * as builders from './builders.js'; + +export type { ProviderApi, SolanaActions } from './types.js'; +export { CAIP_NAMESPACE, CAIP_SOLANA_CHAIN_ID } from './constants.js'; diff --git a/wallets/core/src/namespaces/solana/types.ts b/wallets/core/src/namespaces/solana/types.ts new file mode 100644 index 0000000000..fe8a6b8088 --- /dev/null +++ b/wallets/core/src/namespaces/solana/types.ts @@ -0,0 +1,20 @@ +import type { + Accounts, + AutoImplementedActionsByRecommended, + CommonActions, +} from '../common/types.js'; + +export interface SolanaActions + extends AutoImplementedActionsByRecommended, + CommonActions { + connect: () => Promise; +} + +/* + * + * TODO: That would be better to define a type for Solana injected wallets. + * They have something called [Wallet Standard](https://github.com/wallet-standard/wallet-standard) but not sure all the Solana wallets support that (Phantom do). + * If Phantom's interface is what Solana wallets are supporting, another option would be define that type here. + * + */ +export type ProviderApi = Record; diff --git a/wallets/core/src/test-utils/fixtures.ts b/wallets/core/src/test-utils/fixtures.ts new file mode 100644 index 0000000000..f4c15f5cf2 --- /dev/null +++ b/wallets/core/src/test-utils/fixtures.ts @@ -0,0 +1,9 @@ +import type { ProviderConfig } from '../hub/store/mod.js'; + +export const garbageWalletInfo: ProviderConfig['info'] = { + name: 'Garbage Wallet', + icon: 'https://somewhereininternet.com/icon.svg', + extensions: { + homepage: 'https://app.rango.exchange', + }, +}; diff --git a/wallets/core/src/utils/mod.ts b/wallets/core/src/utils/mod.ts new file mode 100644 index 0000000000..0870b202dd --- /dev/null +++ b/wallets/core/src/utils/mod.ts @@ -0,0 +1,8 @@ +/* + * It is not a good idea to re-export all of CAIP because if they have a breaking change, we will break as well. + * It would be better to create an abstraction over them and export our own interface to ensure it is under our control. + */ +export * as CAIP from 'caip'; + +export { generateStoreId } from '../hub/helpers.js'; +export * from './versions.js'; diff --git a/wallets/core/src/utils/versions.test.ts b/wallets/core/src/utils/versions.test.ts new file mode 100644 index 0000000000..c115b4b768 --- /dev/null +++ b/wallets/core/src/utils/versions.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, test } from 'vitest'; + +import { defineVersions, pickVersion } from './versions.js'; + +describe('Picking versions should work correctly', () => { + test("Error on picking a version doesn't exist", () => { + const versions = defineVersions().build(); + // const versions: Versions = [['1.0.0', {}]]; + expect(() => pickVersion(versions, '1.0.0')).toThrowError(); + }); + + test('Pick the correct version if it exist', () => { + const versions = defineVersions() + .version('0.0.0', {} as any) + .version('1.0.0', {} as any) + .build(); + + const target = pickVersion(versions, '1.0.0'); + expect(target).toBeDefined(); + expect(target[0]).toBe('1.0.0'); + }); +}); diff --git a/wallets/core/src/utils/versions.ts b/wallets/core/src/utils/versions.ts new file mode 100644 index 0000000000..0abee95745 --- /dev/null +++ b/wallets/core/src/utils/versions.ts @@ -0,0 +1,63 @@ +import type { Provider } from '../hub/mod.js'; +import type { LegacyProviderInterface } from '../legacy/mod.js'; + +type VersionedVLegacy = ['0.0.0', LegacyProviderInterface]; +type VersionedV1 = ['1.0.0', Provider]; +type AvailableVersions = VersionedVLegacy | VersionedV1; +export type Versions = AvailableVersions[]; +// eslint-disable-next-line @typescript-eslint/no-magic-numbers +export type VersionInterface = T[1]; + +type SemVer = T extends [infer U, any] ? U : never; +type MatchVersion = Extract< + T[number], + [Version, any] +>; + +export function pickVersion< + L extends Versions, + V extends SemVer +>(list: L, targetVersion: V): MatchVersion { + if (!targetVersion) { + throw new Error(`You should provide a valid semver, e.g 1.0.0.`); + } + + const target = list.find(([version]) => version === targetVersion); + + if (!target) { + throw new Error( + `You target version hasn't been found. Available versions: ${Object.keys( + list + ).join(', ')}` + ); + } + return target as MatchVersion; +} + +interface DefineVersionsApi { + version: >( + semver: T, + value: VersionInterface> + ) => DefineVersionsApi; + build: () => Versions; +} + +export function defineVersions(): DefineVersionsApi { + const versions: Versions = []; + const api: DefineVersionsApi = { + version: (semver, value) => { + versions.push([semver, value]); + return api; + }, + build: () => { + return versions; + }, + }; + return api; +} + +export function legacyProviderImportsToVersionsInterface( + provider: LegacyProviderInterface +): Versions { + return defineVersions().version('0.0.0', provider).build(); +} diff --git a/wallets/core/tests/hub.test.ts b/wallets/core/tests/hub.test.ts new file mode 100644 index 0000000000..118a84483c --- /dev/null +++ b/wallets/core/tests/hub.test.ts @@ -0,0 +1,63 @@ +import type { Context } from '../src/hub/namespaces/types.js'; +import type { FunctionWithContext } from '../src/namespaces/common/types.js'; +import type { EvmActions } from '../src/namespaces/evm/types.js'; + +import { beforeEach, describe, expect, test } from 'vitest'; + +import { NamespaceBuilder, ProviderBuilder } from '../src/builders/mod.js'; +import { Hub } from '../src/hub/hub.js'; +import { createStore, type Store } from '../src/hub/store/mod.js'; +import { garbageWalletInfo } from '../src/test-utils/fixtures.js'; + +describe('check hub', () => { + const walletName = 'garbage-wallet'; + let store: Store; + + beforeEach(() => { + store = createStore(); + + return () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore-next-line + store = undefined; + }; + }); + + test('connect through hub', async () => { + const evmConnect: FunctionWithContext< + EvmActions['connect'], + Context + > = async (_context, _chain) => { + return { + accounts: [ + 'eip155:0x1:0x000000000000000000000000000000000000dead', + 'eip155:0x1:0x0000000000000000000000000000000000000000', + ], + network: 'eth', + }; + }; + + const evmProvider = new NamespaceBuilder('eip155', walletName) + .action('connect', evmConnect) + .build(); + const garbageWalletBuilder = new ProviderBuilder(walletName).config( + 'info', + garbageWalletInfo + ); + garbageWalletBuilder.add('evm', evmProvider); + const garbageWallet = garbageWalletBuilder.build(); + + const myHub = new Hub({ + store, + }).add(garbageWallet.id, garbageWallet); + const wallet = myHub.get(garbageWallet.id); + // this is only for checking `.store` to has been set. + wallet?.state(); + const evmResult = await wallet?.get('evm')?.connect('0x0'); + + expect(evmResult?.accounts).toStrictEqual([ + 'eip155:0x1:0x000000000000000000000000000000000000dead', + 'eip155:0x1:0x0000000000000000000000000000000000000000', + ]); + }); +}); diff --git a/wallets/core/tests/providers.test.ts b/wallets/core/tests/providers.test.ts new file mode 100644 index 0000000000..a1b97ac489 --- /dev/null +++ b/wallets/core/tests/providers.test.ts @@ -0,0 +1,210 @@ +import type { Context } from '../src/hub/namespaces/mod.js'; +import type { + AccountsWithActiveChain, + CaipAccount, + FunctionWithContext, +} from '../src/namespaces/common/types.js'; +import type { EvmActions } from '../src/namespaces/evm/types.js'; +import type { SolanaActions } from '../src/namespaces/solana/types.js'; + +import { describe, expect, test, vi } from 'vitest'; + +import { + ActionBuilder, + NamespaceBuilder, + ProviderBuilder, +} from '../src/builders/mod.js'; +import { garbageWalletInfo } from '../src/test-utils/fixtures.js'; + +describe('check Provider works with Blockchain correctly', () => { + const walletName = 'garbage-wallet'; + + test('connect successfully when two blockchain type has been added to Provider', async () => { + // Wallet Code + const spyOnEvmConnect = vi.fn(); + const evmConnect: FunctionWithContext< + EvmActions['connect'], + Context + > = async (_context, _chain) => { + spyOnEvmConnect(); + return { + accounts: ['eip155:0x1:0x000000000000000000000000000000000000dead'], + network: 'eth', + }; + }; + + const spyOnSolanaConnect = vi.fn(); + const solanaConnect: FunctionWithContext< + SolanaActions['connect'], + Context + > = async () => { + spyOnSolanaConnect(); + return ['solana:mainnet:1nc1nerator11111111111111111111111111111111']; + }; + + const evmProvider = new NamespaceBuilder('eip155', walletName) + .action('connect', evmConnect) + .build(); + const solanaProvider = new NamespaceBuilder( + 'solana', + walletName + ) + .action('connect', solanaConnect) + .build(); + + const garbageWalletBuilder = new ProviderBuilder(walletName).config( + 'info', + garbageWalletInfo + ); + garbageWalletBuilder.add('evm', evmProvider); + garbageWalletBuilder.add('solana', solanaProvider); + + const garbageWallet = garbageWalletBuilder.build(); + const evmResult = await garbageWallet.get('evm')?.connect('0x1'); + const solanaResult = await garbageWallet.get('solana')?.connect(); + + expect(evmResult?.accounts).toStrictEqual([ + 'eip155:0x1:0x000000000000000000000000000000000000dead', + ]); + expect(solanaResult).toStrictEqual([ + 'solana:mainnet:1nc1nerator11111111111111111111111111111111', + ]); + + expect(spyOnEvmConnect).toBeCalledTimes(1); + expect(spyOnSolanaConnect).toBeCalledTimes(1); + }); + + test('check post actions to work correctly.', async () => { + const spyOnConnect = vi.fn(); + const evmConnect: FunctionWithContext = + async function (_context, _chain) { + spyOnConnect(); + return { + // `as CaipAccount` is ok here because we are going to make to `CaipAccount` in `and` hook. + accounts: [ + '0x000000000000000000000000000000000000dead' as CaipAccount, + '0x0000000000000000000000000000000000000000' as CaipAccount, + ], + network: 'eth', + }; + }; + + const andConnect = vi.fn((_context, result: AccountsWithActiveChain) => { + return { + network: result.network, + accounts: result.accounts.map((account) => `eip155:0x1:${account}`), + }; + }); + + const evmDisconnect = vi.fn(); + const afterDisconnect = vi.fn(); + + const connectAction = new ActionBuilder('connect') + .action(evmConnect) + .and(andConnect) + .build(); + + const disconnectAction = new ActionBuilder( + 'disconnect' + ) + .action(evmDisconnect) + .after(afterDisconnect) + .build(); + + const evmProvider = new NamespaceBuilder('eip155', walletName) + .action(connectAction) + .action(disconnectAction) + .build(); + + const garbageWalletBuilder = new ProviderBuilder('garbage-wallet').config( + 'info', + garbageWalletInfo + ); + garbageWalletBuilder.add('evm', evmProvider); + + const garbageWallet = garbageWalletBuilder.build(); + const evmResult = await garbageWallet.get('evm')?.connect('0x1'); + + expect(evmResult?.accounts).toStrictEqual([ + 'eip155:0x1:0x000000000000000000000000000000000000dead', + 'eip155:0x1:0x0000000000000000000000000000000000000000', + ]); + + await garbageWallet.get('evm')?.connect('0x1'); + garbageWallet.get('evm')?.disconnect(); + expect(spyOnConnect).toBeCalledTimes(2); + expect(andConnect).toBeCalledTimes(2); + + expect(evmDisconnect).toBeCalledTimes(1); + expect(afterDisconnect).toBeCalledTimes(1); + }); + + test('check action builder works with namespace correctly.', async () => { + const spyOnSuccessAndAction = vi.fn((_ctx, value) => value); + const spyOnThrowAndAction = vi.fn(); + const spyOnThrowAndActionWithOr = vi.fn(); + + const spyOnSuccessOrAction = vi.fn(); + const spyOnThrowOrAction = vi.fn(); + + interface GarbageActions { + successfulAction: () => string; + throwErrorAction: () => void; + throwErrorActionWithOr: () => void; + } + + const successfulAction = new ActionBuilder< + GarbageActions, + 'successfulAction' + >('successfulAction') + .action(() => { + return 'yay!'; + }) + .and(spyOnSuccessAndAction) + .or(spyOnSuccessOrAction) + .build(); + + const throwErrorAction = new ActionBuilder< + GarbageActions, + 'throwErrorAction' + >('throwErrorAction') + .action(() => { + throw new Error('whatever'); + }) + .and(spyOnThrowAndAction) + .build(); + + const throwErrorActionWithOr = new ActionBuilder< + GarbageActions, + 'throwErrorActionWithOr' + >('throwErrorActionWithOr') + .action(() => { + throw new Error('whatever'); + }) + .and(spyOnThrowAndActionWithOr) + .or(spyOnThrowOrAction) + .build(); + + const garbageProvider = new NamespaceBuilder<{ + successfulAction: () => string; + throwErrorAction: () => void; + throwErrorActionWithOr: () => void; + }>('eip155', walletName) + .action(successfulAction) + .action(throwErrorAction) + .action(throwErrorActionWithOr) + .build(); + + garbageProvider.successfulAction(); + expect(spyOnSuccessAndAction).toBeCalledTimes(1); + expect(spyOnSuccessAndAction).toHaveLastReturnedWith('yay!'); + + expect(() => garbageProvider.throwErrorAction()).toThrowError(); + expect(spyOnThrowAndAction).toBeCalledTimes(0); + + garbageProvider.throwErrorActionWithOr(); + expect(spyOnThrowAndActionWithOr).toBeCalledTimes(0); + expect(spyOnSuccessOrAction).toBeCalledTimes(0); + expect(spyOnThrowOrAction).toBeCalledTimes(1); + }); +}); diff --git a/wallets/core/tsconfig.build.json b/wallets/core/tsconfig.build.json index 7484fe1a80..d77c2c36b7 100644 --- a/wallets/core/tsconfig.build.json +++ b/wallets/core/tsconfig.build.json @@ -1,9 +1,7 @@ { - // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs "extends": "../../tsconfig.libnext.json", "include": ["src"], "compilerOptions": { - "outDir": "dist", - "rootDir": "./src" + "outDir": "dist" } } diff --git a/yarn.lock b/yarn.lock index 35d292ceeb..c7dbf1004f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -41,6 +41,14 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@ampproject/remapping@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + "@aw-web-design/x-default-browser@1.4.126": version "1.4.126" resolved "https://registry.yarnpkg.com/@aw-web-design/x-default-browser/-/x-default-browser-1.4.126.tgz#43e4bd8f0314ed907a8718d7e862a203af79bc16" @@ -2714,16 +2722,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + "@esbuild/android-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== -"@esbuild/android-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" - integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== - "@esbuild/android-arm64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz#68791afa389550736f682c15b963a4f37ec2f5f6" @@ -2734,16 +2742,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + "@esbuild/android-arm@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== -"@esbuild/android-arm@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" - integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== - "@esbuild/android-arm@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.1.tgz#38c91d8ee8d5196f7fbbdf4f0061415dde3a473a" @@ -2754,16 +2762,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + "@esbuild/android-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== -"@esbuild/android-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" - integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== - "@esbuild/android-x64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.1.tgz#93f6190ce997b313669c20edbf3645fc6c8d8f22" @@ -2774,16 +2782,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + "@esbuild/darwin-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== -"@esbuild/darwin-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" - integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== - "@esbuild/darwin-arm64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz#0d391f2e81fda833fe609182cc2fbb65e03a3c46" @@ -2794,16 +2802,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + "@esbuild/darwin-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== -"@esbuild/darwin-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" - integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== - "@esbuild/darwin-x64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz#92504077424584684862f483a2242cfde4055ba2" @@ -2814,16 +2822,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + "@esbuild/freebsd-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== -"@esbuild/freebsd-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" - integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== - "@esbuild/freebsd-arm64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz#a1646fa6ba87029c67ac8a102bb34384b9290774" @@ -2834,16 +2842,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + "@esbuild/freebsd-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== -"@esbuild/freebsd-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" - integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== - "@esbuild/freebsd-x64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz#41c9243ab2b3254ea7fb512f71ffdb341562e951" @@ -2854,16 +2862,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + "@esbuild/linux-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== -"@esbuild/linux-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" - integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== - "@esbuild/linux-arm64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz#f3c1e1269fbc9eedd9591a5bdd32bf707a883156" @@ -2874,16 +2882,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + "@esbuild/linux-arm@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== -"@esbuild/linux-arm@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" - integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== - "@esbuild/linux-arm@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz#4503ca7001a8ee99589c072801ce9d7540717a21" @@ -2894,16 +2902,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + "@esbuild/linux-ia32@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== -"@esbuild/linux-ia32@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" - integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== - "@esbuild/linux-ia32@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz#98c474e3e0cbb5bcbdd8561a6e65d18f5767ce48" @@ -2914,16 +2922,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + "@esbuild/linux-loong64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== -"@esbuild/linux-loong64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" - integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== - "@esbuild/linux-loong64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz#a8097d28d14b9165c725fe58fc438f80decd2f33" @@ -2934,16 +2942,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + "@esbuild/linux-mips64el@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== -"@esbuild/linux-mips64el@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" - integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== - "@esbuild/linux-mips64el@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz#c44f6f0d7d017c41ad3bb15bfdb69b690656b5ea" @@ -2954,16 +2962,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + "@esbuild/linux-ppc64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== -"@esbuild/linux-ppc64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" - integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== - "@esbuild/linux-ppc64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz#0765a55389a99237b3c84227948c6e47eba96f0d" @@ -2974,16 +2982,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + "@esbuild/linux-riscv64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== -"@esbuild/linux-riscv64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" - integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== - "@esbuild/linux-riscv64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz#e4153b032288e3095ddf4c8be07893781b309a7e" @@ -2994,16 +3002,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + "@esbuild/linux-s390x@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== -"@esbuild/linux-s390x@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" - integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== - "@esbuild/linux-s390x@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz#b9ab8af6e4b73b26d63c1c426d7669a5d53eb5a7" @@ -3014,16 +3022,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + "@esbuild/linux-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== -"@esbuild/linux-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" - integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== - "@esbuild/linux-x64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz#0b25da17ac38c3e11cdd06ca3691d4d6bef2755f" @@ -3034,16 +3042,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + "@esbuild/netbsd-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== -"@esbuild/netbsd-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" - integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== - "@esbuild/netbsd-x64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz#3148e48406cd0d4f7ba1e0bf3f4d77d548c98407" @@ -3054,16 +3062,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + "@esbuild/openbsd-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== -"@esbuild/openbsd-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" - integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== - "@esbuild/openbsd-x64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz#7b73e852986a9750192626d377ac96ac2b749b76" @@ -3074,16 +3082,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + "@esbuild/sunos-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== -"@esbuild/sunos-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" - integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== - "@esbuild/sunos-x64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz#402a441cdac2eee98d8be378c7bc23e00c1861c5" @@ -3094,16 +3102,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + "@esbuild/win32-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== -"@esbuild/win32-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" - integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== - "@esbuild/win32-arm64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz#36c4e311085806a6a0c5fc54d1ac4d7b27e94d7b" @@ -3114,16 +3122,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + "@esbuild/win32-ia32@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== -"@esbuild/win32-ia32@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" - integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== - "@esbuild/win32-ia32@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz#0cf933be3fb9dc58b45d149559fe03e9e22b54fe" @@ -3134,16 +3142,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + "@esbuild/win32-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== -"@esbuild/win32-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" - integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== - "@esbuild/win32-x64@0.20.1": version "0.20.1" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz#77583b6ea54cee7c1410ebbd54051b6a3fcbd8ba" @@ -3154,6 +3162,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -3980,6 +3993,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": version "0.3.20" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" @@ -5830,6 +5848,86 @@ estree-walker "^2.0.2" picomatch "^2.3.1" +"@rollup/rollup-android-arm-eabi@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz#c3f5660f67030c493a981ac1d34ee9dfe1d8ec0f" + integrity sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA== + +"@rollup/rollup-android-arm64@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz#64161f0b67050023a3859e723570af54a82cff5c" + integrity sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ== + +"@rollup/rollup-darwin-arm64@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz#25f3d57b1da433097cfebc89341b355901615763" + integrity sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q== + +"@rollup/rollup-darwin-x64@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz#d8ddaffb636cc2f59222c50316e27771e48966df" + integrity sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz#41bd4fcffa20fb84f3dbac6c5071638f46151885" + integrity sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA== + +"@rollup/rollup-linux-arm-musleabihf@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz#842077c5113a747eb5686f19f2f18c33ecc0acc8" + integrity sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw== + +"@rollup/rollup-linux-arm64-gnu@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz#65d1d5b6778848f55b7823958044bf3e8737e5b7" + integrity sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ== + +"@rollup/rollup-linux-arm64-musl@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz#50eef7d6e24d0fe3332200bb666cad2be8afcf86" + integrity sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q== + +"@rollup/rollup-linux-powerpc64le-gnu@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz#8837e858f53c84607f05ad0602943e96d104c6b4" + integrity sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw== + +"@rollup/rollup-linux-riscv64-gnu@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz#c894ade2300caa447757ddf45787cca246e816a4" + integrity sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA== + +"@rollup/rollup-linux-s390x-gnu@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz#5841e5390d4c82dd5cdf7b2c95a830e3c2f47dd3" + integrity sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg== + +"@rollup/rollup-linux-x64-gnu@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz#cc1f26398bf777807a99226dc13f47eb0f6c720d" + integrity sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew== + +"@rollup/rollup-linux-x64-musl@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz#1507465d9056e0502a590d4c1a00b4d7b1fda370" + integrity sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg== + +"@rollup/rollup-win32-arm64-msvc@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz#86a221f01a2c248104dd0defb4da119f2a73642e" + integrity sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA== + +"@rollup/rollup-win32-ia32-msvc@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz#8bc8f77e02760aa664694b4286d6fbea7f1331c5" + integrity sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A== + +"@rollup/rollup-win32-x64-msvc@4.20.0": + version "4.20.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz#601fffee719a1e8447f908aca97864eec23b2784" + integrity sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg== + "@safe-global/safe-apps-provider@^0.17.0": version "0.17.1" resolved "https://registry.yarnpkg.com/@safe-global/safe-apps-provider/-/safe-apps-provider-0.17.1.tgz#72df2a66be5343940ed505efe594ed3b0f2f7015" @@ -7702,18 +7800,6 @@ "@types/connect" "*" "@types/node" "*" -"@types/chai-subset@^1.3.3": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.5.tgz#3fc044451f26985f45625230a7f22284808b0a9a" - integrity sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A== - dependencies: - "@types/chai" "*" - -"@types/chai@*", "@types/chai@^4.3.5": - version "4.3.11" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.11.tgz#e95050bf79a932cb7305dd130254ccdf9bde671c" - integrity sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ== - "@types/connect@*", "@types/connect@^3.4.33": version "3.4.38" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" @@ -7774,7 +7860,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^1.0.0": +"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -8332,48 +8418,56 @@ test-exclude "^6.0.0" v8-to-istanbul "^9.1.0" -"@vitest/expect@0.33.0": - version "0.33.0" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.33.0.tgz#f48652591f3573ad6c2db828ad358d5c078845d3" - integrity sha512-sVNf+Gla3mhTCxNJx+wJLDPp/WcstOe0Ksqz4Vec51MmgMth/ia0MGFEkIZmVGeTL5HtjYR4Wl/ZxBxBXZJTzQ== +"@vitest/expect@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.0.5.tgz#f3745a6a2c18acbea4d39f5935e913f40d26fa86" + integrity sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA== dependencies: - "@vitest/spy" "0.33.0" - "@vitest/utils" "0.33.0" - chai "^4.3.7" + "@vitest/spy" "2.0.5" + "@vitest/utils" "2.0.5" + chai "^5.1.1" + tinyrainbow "^1.2.0" -"@vitest/runner@0.33.0": - version "0.33.0" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.33.0.tgz#0b1a4d04ff8bc5cdad73920eac019d99550edf9d" - integrity sha512-UPfACnmCB6HKRHTlcgCoBh6ppl6fDn+J/xR8dTufWiKt/74Y9bHci5CKB8tESSV82zKYtkBJo9whU3mNvfaisg== +"@vitest/pretty-format@2.0.5", "@vitest/pretty-format@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-2.0.5.tgz#91d2e6d3a7235c742e1a6cc50e7786e2f2979b1e" + integrity sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ== dependencies: - "@vitest/utils" "0.33.0" - p-limit "^4.0.0" - pathe "^1.1.1" + tinyrainbow "^1.2.0" -"@vitest/snapshot@0.33.0": - version "0.33.0" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-0.33.0.tgz#4400a90c48907808122b573053a2112a832b3698" - integrity sha512-tJjrl//qAHbyHajpFvr8Wsk8DIOODEebTu7pgBrP07iOepR5jYkLFiqLq2Ltxv+r0uptUb4izv1J8XBOwKkVYA== +"@vitest/runner@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-2.0.5.tgz#89197e712bb93513537d6876995a4843392b2a84" + integrity sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig== dependencies: - magic-string "^0.30.1" - pathe "^1.1.1" - pretty-format "^29.5.0" + "@vitest/utils" "2.0.5" + pathe "^1.1.2" -"@vitest/spy@0.33.0": - version "0.33.0" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.33.0.tgz#366074d3cf9cf1ed8aeaa76e50e78c799fb242eb" - integrity sha512-Kv+yZ4hnH1WdiAkPUQTpRxW8kGtH8VRTnus7ZTGovFYM1ZezJpvGtb9nPIjPnptHbsyIAxYZsEpVPYgtpjGnrg== +"@vitest/snapshot@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-2.0.5.tgz#a2346bc5013b73c44670c277c430e0334690a162" + integrity sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew== dependencies: - tinyspy "^2.1.1" + "@vitest/pretty-format" "2.0.5" + magic-string "^0.30.10" + pathe "^1.1.2" -"@vitest/utils@0.33.0": - version "0.33.0" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.33.0.tgz#6b9820cb8f128d649da6f78ecaa9b73d6222b277" - integrity sha512-pF1w22ic965sv+EN6uoePkAOTkAPWM03Ri/jXNyMIKBb/XHLDPfhLvf/Fa9g0YECevAIz56oVYXhodLvLQ/awA== +"@vitest/spy@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-2.0.5.tgz#590fc07df84a78b8e9dd976ec2090920084a2b9f" + integrity sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA== + dependencies: + tinyspy "^3.0.0" + +"@vitest/utils@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.0.5.tgz#6f8307a4b6bc6ceb9270007f73c67c915944e926" + integrity sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ== dependencies: - diff-sequences "^29.4.3" - loupe "^2.3.6" - pretty-format "^29.5.0" + "@vitest/pretty-format" "2.0.5" + estree-walker "^3.0.3" + loupe "^3.1.1" + tinyrainbow "^1.2.0" "@wallet-standard/base@^1.0.1": version "1.0.1" @@ -8969,11 +9063,6 @@ acorn-walk@^7.2.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.2.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.0.tgz#2097665af50fd0cf7a2dfccd2b9368964e66540f" - integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA== - acorn@^7.4.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -9314,10 +9403,10 @@ assert@^2.0.0, assert@^2.1.0: object.assign "^4.1.4" util "^0.12.5" -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== ast-types@^0.16.1: version "0.16.1" @@ -10047,18 +10136,16 @@ catering@^2.0.0, catering@^2.1.0: resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== -chai@^4.3.7: - version "4.3.10" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" - integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== +chai@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.1.tgz#f035d9792a22b481ead1c65908d14bb62ec1c82c" + integrity sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA== dependencies: - assertion-error "^1.1.0" - check-error "^1.0.3" - deep-eql "^4.1.3" - get-func-name "^2.0.2" - loupe "^2.3.6" - pathval "^1.1.1" - type-detect "^4.0.8" + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" chalk@5.3.0, chalk@^5.2.0: version "5.3.0" @@ -10107,12 +10194,10 @@ charenc@~0.0.1: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== -check-error@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" - integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== - dependencies: - get-func-name "^2.0.2" +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== chokidar@3.5.1: version "3.5.1" @@ -11088,6 +11173,13 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.5: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -11116,12 +11208,10 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-eql@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" - integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== - dependencies: - type-detect "^4.0.0" +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== deep-equal@^1.1.1: version "1.1.2" @@ -11313,11 +11403,6 @@ detect-port@^1.3.0: address "^1.0.1" debug "4" -diff-sequences@^29.4.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -11847,34 +11932,6 @@ esbuild@^0.17.10: "@esbuild/win32-ia32" "0.20.2" "@esbuild/win32-x64" "0.20.2" -esbuild@^0.18.10: - version "0.18.20" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6" - integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== - optionalDependencies: - "@esbuild/android-arm" "0.18.20" - "@esbuild/android-arm64" "0.18.20" - "@esbuild/android-x64" "0.18.20" - "@esbuild/darwin-arm64" "0.18.20" - "@esbuild/darwin-x64" "0.18.20" - "@esbuild/freebsd-arm64" "0.18.20" - "@esbuild/freebsd-x64" "0.18.20" - "@esbuild/linux-arm" "0.18.20" - "@esbuild/linux-arm64" "0.18.20" - "@esbuild/linux-ia32" "0.18.20" - "@esbuild/linux-loong64" "0.18.20" - "@esbuild/linux-mips64el" "0.18.20" - "@esbuild/linux-ppc64" "0.18.20" - "@esbuild/linux-riscv64" "0.18.20" - "@esbuild/linux-s390x" "0.18.20" - "@esbuild/linux-x64" "0.18.20" - "@esbuild/netbsd-x64" "0.18.20" - "@esbuild/openbsd-x64" "0.18.20" - "@esbuild/sunos-x64" "0.18.20" - "@esbuild/win32-arm64" "0.18.20" - "@esbuild/win32-ia32" "0.18.20" - "@esbuild/win32-x64" "0.18.20" - esbuild@^0.20.1: version "0.20.1" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.1.tgz#1e4cbb380ad1959db7609cb9573ee77257724a3e" @@ -11904,6 +11961,35 @@ esbuild@^0.20.1: "@esbuild/win32-ia32" "0.20.1" "@esbuild/win32-x64" "0.20.1" +esbuild@^0.21.3: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -12128,6 +12214,13 @@ estree-walker@^2.0.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -12346,6 +12439,21 @@ execa@^5.0.0, execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + express@^4.17.3: version "4.19.2" resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" @@ -12834,7 +12942,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.1, fsevents@~2.3.2: +fsevents@~2.3.1, fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -12891,7 +12999,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.1, get-func-name@^2.0.2: +get-func-name@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== @@ -12946,6 +13054,11 @@ get-stream@^6.0.0, get-stream@^6.0.1: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -13527,6 +13640,11 @@ human-signals@^4.3.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -13588,6 +13706,11 @@ immediate@~3.2.3: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" integrity sha512-RrGCXRm/fRVqMIhqXrGEX9rRADavPiDFSoMb/k64i9XMk8uH4r/Omi5Ctierj6XzNecwDbO4WuFbDD1zmpl3Tg== +immer@^10.0.4: + version "10.1.1" + resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" + integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== + immer@^9.0.19: version "9.0.21" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" @@ -14862,11 +14985,6 @@ loader-runner@^4.2.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== -local-pkg@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963" - integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -15050,10 +15168,10 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4 dependencies: js-tokens "^3.0.0 || ^4.0.0" -loupe@^2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" - integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== +loupe@^3.1.0, loupe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.1.tgz#71d038d59007d890e3247c5db97c1ec5a92edc54" + integrity sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw== dependencies: get-func-name "^2.0.1" @@ -15125,6 +15243,13 @@ magic-string@^0.30.1: dependencies: "@jridgewell/sourcemap-codec" "^1.4.15" +magic-string@^0.30.10: + version "0.30.11" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.11.tgz#301a6f93b3e8c2cb13ac1a7a673492c0dfd12954" + integrity sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -15502,7 +15627,7 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mlly@^1.2.0, mlly@^1.4.0, mlly@^1.4.2: +mlly@^1.2.0, mlly@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.4.2.tgz#7cf406aa319ff6563d25da6b36610a93f2a8007e" integrity sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg== @@ -15595,7 +15720,7 @@ nan@^2.13.2: resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== -nanoid@^3.3.6: +nanoid@^3.3.6, nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== @@ -16421,10 +16546,15 @@ pathe@^1.1.0, pathe@^1.1.1: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a" integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q== -pathval@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" - integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== pbkdf2@^3.0.16, pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9, pbkdf2@^3.1.1: version "3.1.2" @@ -16456,6 +16586,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.3.0, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -16635,7 +16770,7 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.21, postcss@^8.4.27: +postcss@^8.4.21: version "8.4.31" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== @@ -16644,6 +16779,15 @@ postcss@^8.4.21, postcss@^8.4.27: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.41: + version "8.4.41" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.41.tgz#d6104d3ba272d882fe18fc07d15dc2da62fa2681" + integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.1" + source-map-js "^1.2.0" + posthtml-parser@^0.10.1: version "0.10.2" resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.10.2.tgz#df364d7b179f2a6bf0466b56be7b98fd4e97c573" @@ -16708,7 +16852,7 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" -pretty-format@^29.5.0, pretty-format@^29.7.0: +pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== @@ -17865,11 +18009,29 @@ rlp@^2.2.3, rlp@^2.2.4: dependencies: bn.js "^5.2.0" -rollup@^3.27.1: - version "3.29.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981" - integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw== +rollup@^4.13.0: + version "4.20.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.20.0.tgz#f9d602161d29e178f0bf1d9f35f0a26f83939492" + integrity sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw== + dependencies: + "@types/estree" "1.0.5" optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.20.0" + "@rollup/rollup-android-arm64" "4.20.0" + "@rollup/rollup-darwin-arm64" "4.20.0" + "@rollup/rollup-darwin-x64" "4.20.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.20.0" + "@rollup/rollup-linux-arm-musleabihf" "4.20.0" + "@rollup/rollup-linux-arm64-gnu" "4.20.0" + "@rollup/rollup-linux-arm64-musl" "4.20.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.20.0" + "@rollup/rollup-linux-riscv64-gnu" "4.20.0" + "@rollup/rollup-linux-s390x-gnu" "4.20.0" + "@rollup/rollup-linux-x64-gnu" "4.20.0" + "@rollup/rollup-linux-x64-musl" "4.20.0" + "@rollup/rollup-win32-arm64-msvc" "4.20.0" + "@rollup/rollup-win32-ia32-msvc" "4.20.0" + "@rollup/rollup-win32-x64-msvc" "4.20.0" fsevents "~2.3.2" rpc-websockets@^7.5.1: @@ -18242,7 +18404,7 @@ 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: +signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== @@ -18345,6 +18507,11 @@ source-map-js@^1.0.1, source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== +source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + source-map-support@^0.5.16, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -18485,6 +18652,11 @@ std-env@^3.3.3, std-env@^3.4.3: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.5.0.tgz#83010c9e29bd99bf6f605df87c19012d82d63b97" integrity sha512-JGUEaALvL0Mf6JCfYnJOTcobY+Nc7sG/TemDRBqCA0wEr4DER7zDchaaixTlmOxAjG1uRJmX82EQcxwTQTkqVA== +std-env@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" + integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== + store2@^2.14.2, store2@^2.7.1: version "2.14.2" resolved "https://registry.yarnpkg.com/store2/-/store2-2.14.2.tgz#56138d200f9fe5f582ad63bc2704dbc0e4a45068" @@ -18672,13 +18844,6 @@ strip-json-comments@^3.0.1, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-literal@^1.0.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.3.0.tgz#db3942c2ec1699e6836ad230090b84bb458e3a07" - integrity sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg== - dependencies: - acorn "^8.10.0" - strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -18988,25 +19153,30 @@ tiny-secp256k1@^1.1.3, tiny-secp256k1@^1.1.6: elliptic "^6.4.0" nan "^2.13.2" -tinybench@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.1.tgz#3408f6552125e53a5a48adee31261686fd71587e" - integrity sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg== +tinybench@^2.8.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== tinycolor2@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e" integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw== -tinypool@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.6.0.tgz#c3640b851940abe2168497bb6e43b49fafb3ba7b" - integrity sha512-FdswUUo5SxRizcBc6b1GSuLpLjisa8N8qMyYoP3rl+bym+QauhtJP5bvZY1ytt8krKGmMLYIRl36HBZfeAoqhQ== +tinypool@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.0.tgz#a68965218e04f4ad9de037d2a1cd63cda9afb238" + integrity sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ== -tinyspy@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.0.tgz#9dc04b072746520b432f77ea2c2d17933de5d6ce" - integrity sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg== +tinyrainbow@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" + integrity sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ== + +tinyspy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.0.tgz#cb61644f2713cd84dee184863f4642e06ddf0585" + integrity sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA== tmp@0.0.33, tmp@^0.0.33: version "0.0.33" @@ -19189,11 +19359,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - type-fest@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" @@ -19596,6 +19761,11 @@ use-sync-external-store@1.2.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== +use-sync-external-store@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" + integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== + utf-8-validate@5.0.7: version "5.0.7" resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" @@ -19735,58 +19905,52 @@ viem@^1.0.0: isows "1.0.3" ws "8.13.0" -vite-node@0.33.0: - version "0.33.0" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.33.0.tgz#c6a3a527e0b8090da7436241bc875760ae0eef28" - integrity sha512-19FpHYbwWWxDr73ruNahC+vtEdza52kA90Qb3La98yZ0xULqV8A5JLNPUff0f5zID4984tW7l3DH2przTJUZSw== +vite-node@2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.0.5.tgz#36d909188fc6e3aba3da5fc095b3637d0d18e27b" + integrity sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q== dependencies: cac "^6.7.14" - debug "^4.3.4" - mlly "^1.4.0" - pathe "^1.1.1" - picocolors "^1.0.0" - vite "^3.0.0 || ^4.0.0" + debug "^4.3.5" + pathe "^1.1.2" + tinyrainbow "^1.2.0" + vite "^5.0.0" -"vite@^3.0.0 || ^4.0.0": - version "4.5.3" - resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.3.tgz#d88a4529ea58bae97294c7e2e6f0eab39a50fb1a" - integrity sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg== +vite@^5.0.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.1.tgz#2aa72370de824d23f53658affd807e4c9905b058" + integrity sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA== dependencies: - esbuild "^0.18.10" - postcss "^8.4.27" - rollup "^3.27.1" + esbuild "^0.21.3" + postcss "^8.4.41" + rollup "^4.13.0" optionalDependencies: - fsevents "~2.3.2" + fsevents "~2.3.3" -vitest@^0.33.0: - version "0.33.0" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.33.0.tgz#e2be6153aec1d30e3460ac6d64265bf72da2551c" - integrity sha512-1CxaugJ50xskkQ0e969R/hW47za4YXDUfWJDxip1hwbnhUjYolpfUn2AMOulqG/Dtd9WYAtkHmM/m3yKVrEejQ== - dependencies: - "@types/chai" "^4.3.5" - "@types/chai-subset" "^1.3.3" - "@types/node" "*" - "@vitest/expect" "0.33.0" - "@vitest/runner" "0.33.0" - "@vitest/snapshot" "0.33.0" - "@vitest/spy" "0.33.0" - "@vitest/utils" "0.33.0" - acorn "^8.9.0" - acorn-walk "^8.2.0" - cac "^6.7.14" - chai "^4.3.7" - debug "^4.3.4" - local-pkg "^0.4.3" - magic-string "^0.30.1" - pathe "^1.1.1" - picocolors "^1.0.0" - std-env "^3.3.3" - strip-literal "^1.0.1" - tinybench "^2.5.0" - tinypool "^0.6.0" - vite "^3.0.0 || ^4.0.0" - vite-node "0.33.0" - why-is-node-running "^2.2.2" +vitest@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.0.5.tgz#2f15a532704a7181528e399cc5b754c7f335fd62" + integrity sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA== + dependencies: + "@ampproject/remapping" "^2.3.0" + "@vitest/expect" "2.0.5" + "@vitest/pretty-format" "^2.0.5" + "@vitest/runner" "2.0.5" + "@vitest/snapshot" "2.0.5" + "@vitest/spy" "2.0.5" + "@vitest/utils" "2.0.5" + chai "^5.1.1" + debug "^4.3.5" + execa "^8.0.1" + magic-string "^0.30.10" + pathe "^1.1.2" + std-env "^3.7.0" + tinybench "^2.8.0" + tinypool "^1.0.0" + tinyrainbow "^1.2.0" + vite "^5.0.0" + vite-node "2.0.5" + why-is-node-running "^2.3.0" vm-browserify@^1.1.2: version "1.1.2" @@ -19991,10 +20155,10 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -why-is-node-running@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" - integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== dependencies: siginfo "^2.0.0" stackback "0.0.2" @@ -20232,3 +20396,10 @@ zustand@^4.3.2: integrity sha512-Rb16eW55gqL4W2XZpJh0fnrATxYEG3Apl2gfHTyDSE965x/zxslTikpNch0JgNjJA9zK6gEFW8Fl6d1rTZaqgg== dependencies: use-sync-external-store "1.2.0" + +zustand@^4.5.2: + version "4.5.5" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.5.tgz#f8c713041543715ec81a2adda0610e1dc82d4ad1" + integrity sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q== + dependencies: + use-sync-external-store "1.2.2"