diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 079300369b2..9a38edf9c1b 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -963,7 +963,7 @@ }, "packages/ens-controller/src/EnsController.test.ts": { "@typescript-eslint/explicit-function-return-type": { - "count": 11 + "count": 10 }, "@typescript-eslint/prefer-nullish-coalescing": { "count": 1 diff --git a/packages/ens-controller/CHANGELOG.md b/packages/ens-controller/CHANGELOG.md index b24ae8e8469..a5a1844e0e2 100644 --- a/packages/ens-controller/CHANGELOG.md +++ b/packages/ens-controller/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** `DEFAULT_ENS_NETWORK_MAP` now maps Mainnet and Sepolia to the ENS Universal Resolver contract address (`0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe`) + - Previously it mapped Mainnet, Ropsten, Rinkeby, Goerli, Holesky, and Sepolia to the ENS registry contract address; the default `ensEntries` state seeded from this map (under the `.` key) changes accordingly + - Reverse resolution is no longer available on Holesky by default; Ropsten, Rinkeby, and Goerli have been shut down and could not resolve anyway +- Replace `@ethersproject/providers` with `viem` for ENS resolution + - Reverse resolution now goes through the ENS Universal Resolver (ENSIP-23), which makes the controller compatible with ENSv2 when it launches + ## [19.1.6] ### Changed diff --git a/packages/ens-controller/package.json b/packages/ens-controller/package.json index 66656af2678..886f9f911f9 100644 --- a/packages/ens-controller/package.json +++ b/packages/ens-controller/package.json @@ -55,13 +55,13 @@ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch" }, "dependencies": { - "@ethersproject/providers": "^5.7.0", "@metamask/base-controller": "^9.1.0", "@metamask/controller-utils": "^12.3.0", "@metamask/messenger": "^2.0.0", "@metamask/network-controller": "^35.0.0", "@metamask/utils": "^11.11.0", - "punycode": "^2.1.1" + "punycode": "^2.1.1", + "viem": "2.55.8" }, "devDependencies": { "@metamask/auto-changelog": "^6.1.0", diff --git a/packages/ens-controller/src/EnsController.test.ts b/packages/ens-controller/src/EnsController.test.ts index 7a93e4cf314..bdddeea507c 100644 --- a/packages/ens-controller/src/EnsController.test.ts +++ b/packages/ens-controller/src/EnsController.test.ts @@ -1,4 +1,3 @@ -import * as providersModule from '@ethersproject/providers'; import { deriveStateFromMetadata } from '@metamask/base-controller'; import { toChecksumHexAddress, @@ -16,6 +15,7 @@ import type { NetworkController, NetworkState, } from '@metamask/network-controller'; +import { getEnsAddress, getEnsName } from 'viem/actions'; import { buildMockGetNetworkClientById, @@ -44,14 +44,14 @@ for (const [cid, address] of Object.entries(DEFAULT_ENS_NETWORK_MAP)) { } Object.freeze(defaultState); -jest.mock('@ethersproject/providers', () => { - const originalModule = jest.requireActual('@ethersproject/providers'); +jest.mock('viem/actions', () => ({ + __esModule: true, + getEnsAddress: jest.fn(), + getEnsName: jest.fn(), +})); - return { - __esModule: true, - ...originalModule, - }; -}); +const getEnsNameMock = jest.mocked(getEnsName); +const getEnsAddressMock = jest.mocked(getEnsAddress); type AllEnsControllerActions = MessengerActions; @@ -135,15 +135,6 @@ function getEnsControllerMessenger( return ensControllerMessenger; } -/** - * Creates a mock provider. - * - * @returns mock provider - */ -function getProvider() { - return () => Promise.resolve(null); -} - describe('EnsController', () => { it('should set default state', () => { const rootMessenger = getRootMessenger(); @@ -154,7 +145,7 @@ describe('EnsController', () => { expect(controller.state).toStrictEqual(defaultState); }); - it('should return registry address for `.`', () => { + it('should return universal resolver address for `.`', () => { const rootMessenger = getRootMessenger(); const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); const controller = new EnsController({ @@ -162,7 +153,7 @@ describe('EnsController', () => { }); expect(controller.get('0x1', '.')).toStrictEqual({ ensName: '.', - address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + address: '0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe', chainId: '0x1', }); }); @@ -579,12 +570,8 @@ describe('EnsController', () => { it('should only resolve an ENS name once', async () => { const rootMessenger = getRootMessenger(); const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); - const ethProvider = new providersModule.Web3Provider(getProvider()); - jest.spyOn(ethProvider, 'resolveName').mockResolvedValue(address1); - jest - .spyOn(ethProvider, 'lookupAddress') - .mockResolvedValue('peaksignal.eth'); - jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider); + getEnsAddressMock.mockResolvedValue(address1); + getEnsNameMock.mockResolvedValue('peaksignal.eth'); const ens = new EnsController({ messenger: ensControllerMessenger, @@ -598,14 +585,13 @@ describe('EnsController', () => { expect(await ens.reverseResolveAddress(address1)).toBe('peaksignal.eth'); expect(await ens.reverseResolveAddress(address1)).toBe('peaksignal.eth'); + expect(getEnsNameMock).toHaveBeenCalledTimes(1); }); it('should fail if lookupAddress through an error', async () => { const rootMessenger = getRootMessenger(); const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); - const ethProvider = new providersModule.Web3Provider(getProvider()); - jest.spyOn(ethProvider, 'lookupAddress').mockRejectedValue('error'); - jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider); + getEnsNameMock.mockRejectedValue(new Error('error')); const ens = new EnsController({ messenger: ensControllerMessenger, onNetworkDidChange: (listener) => { @@ -622,9 +608,7 @@ describe('EnsController', () => { it('should fail if lookupAddress returns a null value', async () => { const rootMessenger = getRootMessenger(); const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); - const ethProvider = new providersModule.Web3Provider(getProvider()); - jest.spyOn(ethProvider, 'lookupAddress').mockResolvedValue(null); - jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider); + getEnsNameMock.mockResolvedValue(null); const ens = new EnsController({ messenger: ensControllerMessenger, onNetworkDidChange: (listener) => { @@ -641,12 +625,8 @@ describe('EnsController', () => { it('should fail if resolveName through an error', async () => { const rootMessenger = getRootMessenger(); const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); - const ethProvider = new providersModule.Web3Provider(getProvider()); - jest - .spyOn(ethProvider, 'lookupAddress') - .mockResolvedValue('peaksignal.eth'); - jest.spyOn(ethProvider, 'resolveName').mockRejectedValue('error'); - jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider); + getEnsNameMock.mockResolvedValue('peaksignal.eth'); + getEnsAddressMock.mockRejectedValue(new Error('error')); const ens = new EnsController({ messenger: ensControllerMessenger, onNetworkDidChange: (listener) => { @@ -663,12 +643,8 @@ describe('EnsController', () => { it('should fail if resolveName returns a null value', async () => { const rootMessenger = getRootMessenger(); const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); - const ethProvider = new providersModule.Web3Provider(getProvider()); - jest.spyOn(ethProvider, 'resolveName').mockResolvedValue(null); - jest - .spyOn(ethProvider, 'lookupAddress') - .mockResolvedValue('peaksignal.eth'); - jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider); + getEnsNameMock.mockResolvedValue('peaksignal.eth'); + getEnsAddressMock.mockResolvedValue(null); const ens = new EnsController({ messenger: ensControllerMessenger, onNetworkDidChange: (listener) => { @@ -685,14 +661,8 @@ describe('EnsController', () => { it('should fail if registred address is zero x error address', async () => { const rootMessenger = getRootMessenger(); const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); - const ethProvider = new providersModule.Web3Provider(getProvider()); - jest - .spyOn(ethProvider, 'resolveName') - .mockResolvedValue(ZERO_X_ERROR_ADDRESS); - jest - .spyOn(ethProvider, 'lookupAddress') - .mockResolvedValue('peaksignal.eth'); - jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider); + getEnsNameMock.mockResolvedValue('peaksignal.eth'); + getEnsAddressMock.mockResolvedValue(ZERO_X_ERROR_ADDRESS); const ens = new EnsController({ messenger: ensControllerMessenger, onNetworkDidChange: (listener) => { @@ -709,13 +679,8 @@ describe('EnsController', () => { it('should fail if the name is registered to a different address than the reverse resolved', async () => { const rootMessenger = getRootMessenger(); const ensControllerMessenger = getEnsControllerMessenger(rootMessenger); - - const ethProvider = new providersModule.Web3Provider(getProvider()); - jest.spyOn(ethProvider, 'resolveName').mockResolvedValue(address2); - jest - .spyOn(ethProvider, 'lookupAddress') - .mockResolvedValue('peaksignal.eth'); - jest.spyOn(providersModule, 'Web3Provider').mockReturnValue(ethProvider); + getEnsNameMock.mockResolvedValue('peaksignal.eth'); + getEnsAddressMock.mockResolvedValue(address2); const ens = new EnsController({ messenger: ensControllerMessenger, onNetworkDidChange: (listener) => { @@ -728,6 +693,34 @@ describe('EnsController', () => { expect(await ens.reverseResolveAddress(address1)).toBeUndefined(); }); + + it('should return undefined on a chain without a known universal resolver', async () => { + const rootMessenger = getRootMessenger(); + const getNetworkClientById = buildMockGetNetworkClientById({ + 'AAAA-AAAA-AAAA-AAAA': buildCustomNetworkClientConfiguration({ + chainId: '0x2105', + }), + }); + const ensControllerMessenger = getEnsControllerMessenger( + rootMessenger, + getNetworkClientById, + ); + const ens = new EnsController({ + messenger: ensControllerMessenger, + registriesByChainId: { + 8453: '0x74E20Bd2A1fE0cdbe45b9A1d89cb7e0a45b36376', + }, + onNetworkDidChange: (listener): void => { + listener({ + ...getDefaultNetworkControllerState(), + selectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA', + }); + }, + }); + + expect(await ens.reverseResolveAddress(address1)).toBeUndefined(); + expect(getEnsNameMock).not.toHaveBeenCalled(); + }); }); describe('metadata', () => { @@ -765,42 +758,14 @@ describe('EnsController', () => { "ensEntries": { "0x1": { ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + "address": "0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe", "chainId": "0x1", "ensName": ".", }, }, - "0x3": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x3", - "ensName": ".", - }, - }, - "0x4": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x4", - "ensName": ".", - }, - }, - "0x4268": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x4268", - "ensName": ".", - }, - }, - "0x5": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x5", - "ensName": ".", - }, - }, "0xaa36a7": { ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + "address": "0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe", "chainId": "0xaa36a7", "ensName": ".", }, @@ -829,42 +794,14 @@ describe('EnsController', () => { "ensEntries": { "0x1": { ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + "address": "0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe", "chainId": "0x1", "ensName": ".", }, }, - "0x3": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x3", - "ensName": ".", - }, - }, - "0x4": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x4", - "ensName": ".", - }, - }, - "0x4268": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x4268", - "ensName": ".", - }, - }, - "0x5": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x5", - "ensName": ".", - }, - }, "0xaa36a7": { ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + "address": "0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe", "chainId": "0xaa36a7", "ensName": ".", }, @@ -893,42 +830,14 @@ describe('EnsController', () => { "ensEntries": { "0x1": { ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + "address": "0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe", "chainId": "0x1", "ensName": ".", }, }, - "0x3": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x3", - "ensName": ".", - }, - }, - "0x4": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x4", - "ensName": ".", - }, - }, - "0x4268": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x4268", - "ensName": ".", - }, - }, - "0x5": { - ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "chainId": "0x5", - "ensName": ".", - }, - }, "0xaa36a7": { ".": { - "address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", + "address": "0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe", "chainId": "0xaa36a7", "ensName": ".", }, diff --git a/packages/ens-controller/src/EnsController.ts b/packages/ens-controller/src/EnsController.ts index 456178f4dad..48cc43ec6c1 100644 --- a/packages/ens-controller/src/EnsController.ts +++ b/packages/ens-controller/src/EnsController.ts @@ -1,17 +1,14 @@ -import { Web3Provider } from '@ethersproject/providers'; import { BaseController } from '@metamask/base-controller'; import type { StateMetadata, ControllerGetStateAction, ControllerStateChangeEvent, } from '@metamask/base-controller'; -import type { ChainId } from '@metamask/controller-utils'; import { normalizeEnsName, isValidHexAddress, isSafeDynamicKey, toChecksumHexAddress, - CHAIN_ID_TO_ETHERS_NETWORK_NAME_MAP, convertHexToDecimal, toHex, } from '@metamask/controller-utils'; @@ -24,6 +21,11 @@ import type { import type { Hex } from '@metamask/utils'; import { createProjectLogger } from '@metamask/utils'; import { toASCII } from 'punycode/punycode.js'; +import type { Address, Chain, Client } from 'viem'; +import { createClient, custom } from 'viem'; +import { getEnsAddress, getEnsName } from 'viem/actions'; +import { mainnet, sepolia } from 'viem/chains'; +import { normalize } from 'viem/ens'; import type { EnsControllerMethodActions } from './EnsController-method-action-types.js'; @@ -40,20 +42,20 @@ const MESSENGER_EXPOSED_METHODS = [ 'set', ] as const; -// Map of chainIDs and ENS registry contract addresses +// Map of chainIDs and ENS universal resolver contract addresses (ENSIP-23 +// proxy, upgraded in place by the ENS DAO to support ENSv2 on launch). export const DEFAULT_ENS_NETWORK_MAP: Record = { // Mainnet - 1: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - // Ropsten - 3: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - // Rinkeby - 4: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - // Goerli - 5: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', - // Holesky - 17000: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + 1: '0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe', // Sepolia - 11155111: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', + 11155111: '0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe', +}; + +// Chains with a viem definition that includes an `ensUniversalResolver` +// contract, used as the default source of universal resolver addresses. +const VIEM_CHAINS_BY_ID: Record = { + [mainnet.id]: mainnet, + [sepolia.id]: sepolia, }; /** @@ -143,7 +145,7 @@ export class EnsController extends BaseController< EnsControllerState, EnsControllerMessenger > { - #ethProvider: Web3Provider | null = null; + #ethClient: Client | null = null; /** * Creates an EnsController instance. @@ -335,17 +337,22 @@ export class EnsController extends BaseController< selectedNetworkClientId, ); + const chainIdDecimal = convertHexToDecimal(currentChainId); + const chain = VIEM_CHAINS_BY_ID[chainIdDecimal]; + if ( - registriesByChainId?.[parseInt(currentChainId, 16)] && - this.#getChainEnsSupport(currentChainId) + registriesByChainId?.[chainIdDecimal] && + this.#getChainEnsSupport(currentChainId) && + chain ) { - this.#ethProvider = new Web3Provider(provider, { - chainId: convertHexToDecimal(currentChainId), - name: CHAIN_ID_TO_ETHERS_NETWORK_NAME_MAP[currentChainId as ChainId], - ensAddress: registriesByChainId[parseInt(currentChainId, 16)], + // viem resolves ENS names through the `ensUniversalResolver` contract + // defined on the chain. + this.#ethClient = createClient({ + chain, + transport: custom(provider), }); } else { - this.#ethProvider = null; + this.#ethClient = null; } } @@ -366,7 +373,7 @@ export class EnsController extends BaseController< * @returns ens resolution */ async reverseResolveAddress(nonChecksummedAddress: string) { - if (!this.#ethProvider) { + if (!this.#ethClient) { return undefined; } @@ -377,7 +384,9 @@ export class EnsController extends BaseController< let domain: string | null; try { - domain = await this.#ethProvider.lookupAddress(address); + domain = await getEnsName(this.#ethClient, { + address: address as Address, + }); } catch (error) { log(error); return undefined; @@ -389,7 +398,9 @@ export class EnsController extends BaseController< let registeredAddress: string | null; try { - registeredAddress = await this.#ethProvider.resolveName(domain); + registeredAddress = await getEnsAddress(this.#ethClient, { + name: normalize(domain), + }); } catch (error) { log(error); return undefined; diff --git a/yarn.lock b/yarn.lock index 2166edbfa67..38b75ad1f9c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6898,7 +6898,6 @@ __metadata: version: 0.0.0-use.local resolution: "@metamask/ens-controller@workspace:packages/ens-controller" dependencies: - "@ethersproject/providers": "npm:^5.7.0" "@metamask/auto-changelog": "npm:^6.1.0" "@metamask/base-controller": "npm:^9.1.0" "@metamask/controller-utils": "npm:^12.3.0" @@ -6915,6 +6914,7 @@ __metadata: typedoc: "npm:^0.25.13" typedoc-plugin-missing-exports: "npm:^2.0.0" typescript: "npm:~5.3.3" + viem: "npm:2.55.8" languageName: unknown linkType: soft @@ -22510,9 +22510,9 @@ __metadata: languageName: node linkType: hard -"ox@npm:0.14.20": - version: 0.14.20 - resolution: "ox@npm:0.14.20" +"ox@npm:0.14.32": + version: 0.14.32 + resolution: "ox@npm:0.14.32" dependencies: "@adraffy/ens-normalize": "npm:^1.11.0" "@noble/ciphers": "npm:^1.3.0" @@ -22527,7 +22527,28 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/96526073193f3a6dd2ccd21bcc255e82c7226d6de63fa17a2021c75232fdc9bc969e75e2cbc0c8d5163d88c575e08dc4c75dec7333b1727f080585f07fc6c1ed + checksum: 10/d631768f48d7b1208b586df92661273019b9d95a135eeb691ce6721857acfebe6c61b3ab7f950ba8ca1b43d531a0971b4e3e39be703fbceab27e4bd0ead8a5b2 + languageName: node + linkType: hard + +"ox@npm:0.14.33": + version: 0.14.33 + resolution: "ox@npm:0.14.33" + dependencies: + "@adraffy/ens-normalize": "npm:^1.11.0" + "@noble/ciphers": "npm:^1.3.0" + "@noble/curves": "npm:1.9.1" + "@noble/hashes": "npm:^1.8.0" + "@scure/bip32": "npm:^1.7.0" + "@scure/bip39": "npm:^1.6.0" + abitype: "npm:^1.2.3" + eventemitter3: "npm:5.0.1" + peerDependencies: + typescript: ">=5.4.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/8a1390fa4799fc8629ea64c00b49fe4c5c93417c92033bad7599a1861842af202303ea1cdc85023f19145192563ebe9085609d84b0a3d0145ed1b61e579a2052 languageName: node linkType: hard @@ -27474,9 +27495,30 @@ __metadata: languageName: node linkType: hard +"viem@npm:2.55.8": + version: 2.55.8 + resolution: "viem@npm:2.55.8" + dependencies: + "@noble/curves": "npm:1.9.1" + "@noble/hashes": "npm:1.8.0" + "@scure/bip32": "npm:1.7.0" + "@scure/bip39": "npm:1.6.0" + abitype: "npm:1.2.3" + isows: "npm:1.0.7" + ox: "npm:0.14.32" + ws: "npm:8.21.0" + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10/77a5c474e185654e78f5a561d618ce2152263f126ff91aedcb24f5b1f79454494f825e1863603f12d7f4eb85f84ff1cee6b32f93dc70f32853506dde4a94c54e + languageName: node + linkType: hard + "viem@npm:^2.36.0": - version: 2.48.4 - resolution: "viem@npm:2.48.4" + version: 2.55.10 + resolution: "viem@npm:2.55.10" dependencies: "@noble/curves": "npm:1.9.1" "@noble/hashes": "npm:1.8.0" @@ -27484,14 +27526,14 @@ __metadata: "@scure/bip39": "npm:1.6.0" abitype: "npm:1.2.3" isows: "npm:1.0.7" - ox: "npm:0.14.20" - ws: "npm:8.18.3" + ox: "npm:0.14.33" + ws: "npm:8.21.0" peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: 10/79ab1c8941013e1b4d12ef0bd7fcca6108cfc078b669cc02ae5a08c94d4e3b6de182071cfb40fb4e33ddc40b3aa997f3ebb50d269c85512cefcefdce49b193a0 + checksum: 10/4da52637c5ef51a05748cb4566fc9842b39148fcd847746df239eb19a013fc9cbedeaf70331258b808ddef7c6ca984b1b5b5995a3e47869091723a9d73c245c2 languageName: node linkType: hard @@ -28011,9 +28053,9 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.18.3": - version: 8.18.3 - resolution: "ws@npm:8.18.3" +"ws@npm:8.21.0, ws@npm:^8.18.0, ws@npm:^8.18.3": + version: 8.21.0 + resolution: "ws@npm:8.21.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -28022,7 +28064,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10/725964438d752f0ab0de582cd48d6eeada58d1511c3f613485b5598a83680bedac6187c765b0fe082e2d8cc4341fc57707c813ae780feee82d0c5efe6a4c61b6 + checksum: 10/088411956432c8f876158409d5a285cb9ad1382f593391f51d3a599bd0a5b277f876609ebd00fc3596321c4a4c9064d6fffe1ebad960e8ea7fd9ae25324f35c2 languageName: node linkType: hard @@ -28041,21 +28083,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.18.0, ws@npm:^8.18.3": - version: 8.21.0 - resolution: "ws@npm:8.21.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 10/088411956432c8f876158409d5a285cb9ad1382f593391f51d3a599bd0a5b277f876609ebd00fc3596321c4a4c9064d6fffe1ebad960e8ea7fd9ae25324f35c2 - languageName: node - linkType: hard - "wsl-utils@npm:^0.1.0": version: 0.1.0 resolution: "wsl-utils@npm:0.1.0"