From 43ce99350d959c985526ed0ca94acb6a57c032a0 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 23 Oct 2024 00:14:21 -0400 Subject: [PATCH 01/26] implement interface persistence --- .../SnapInterfaceController.test.tsx | 124 +++++++++++++++++- .../src/interface/SnapInterfaceController.ts | 60 ++++++++- .../src/snaps/SnapController.ts | 22 +++- .../src/test-utils/controller.ts | 1 + .../src/restricted/dialog.test.tsx | 30 ++++- .../src/restricted/dialog.ts | 19 +++ packages/snaps-sdk/src/types/interface.ts | 7 + 7 files changed, 255 insertions(+), 8 deletions(-) diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx index a3561840df..a7481d65f3 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx @@ -1,5 +1,13 @@ +import { getPersistentState } from '@metamask/base-controller'; import type { SnapId } from '@metamask/snaps-sdk'; -import { form, image, input, panel, text } from '@metamask/snaps-sdk'; +import { + form, + image, + input, + panel, + text, + ContentType, +} from '@metamask/snaps-sdk'; import { Box, Field, @@ -29,6 +37,40 @@ jest.mock('@metamask/snaps-utils', () => ({ })); describe('SnapInterfaceController', () => { + describe('constructor', () => { + it('persists notification interfaces', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + const controller = new SnapInterfaceController({ + messenger: controllerMessenger, + state: { + interfaces: { + // @ts-expect-error missing properties + '1': { + contentType: ContentType.Notification, + }, + // @ts-expect-error missing properties + '2': { + contentType: ContentType.Dialog, + }, + }, + }, + }); + + expect( + getPersistentState(controller.state, controller.metadata), + ).toStrictEqual({ + interfaces: { + '1': { + contentType: ContentType.Notification, + }, + }, + }); + }); + }); + describe('createInterface', () => { it('can create a new interface', async () => { const rootMessenger = getRootSnapInterfaceControllerMessenger(); @@ -160,6 +202,41 @@ describe('SnapInterfaceController', () => { expect(context).toStrictEqual({ foo: 'bar' }); }); + it('supports providing an interface content type', async () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const element = ( + + + foo + + + ); + + const id = await rootMessenger.call( + 'SnapInterfaceController:createInterface', + MOCK_SNAP_ID, + element, + { foo: 'bar' }, + ContentType.Notification, + ); + + const { contentType } = rootMessenger.call( + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + id, + ); + + expect(contentType).toStrictEqual(ContentType.Notification); + }); + it('throws if interface context is too large', async () => { const rootMessenger = getRootSnapInterfaceControllerMessenger(); const controllerMessenger = @@ -1068,6 +1145,51 @@ describe('SnapInterfaceController', () => { }); }); + describe('updateInterfaceContentType', () => { + it("can update an interface's content type", async () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const content = form({ name: 'foo', children: [input({ name: 'bar' })] }); + + let contentType; + + const id = await rootMessenger.call( + 'SnapInterfaceController:createInterface', + MOCK_SNAP_ID, + content, + ); + + contentType = rootMessenger.call( + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + id, + ).contentType; + + expect(contentType).toBeNull(); + + rootMessenger.call( + 'SnapInterfaceController:updateInterfaceContentType', + id, + ContentType.Dialog, + ); + + contentType = rootMessenger.call( + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + id, + ).contentType; + + expect(contentType).toStrictEqual(ContentType.Dialog); + }); + }); + describe('resolveInterface', () => { it('resolves the interface with the given value', async () => { const rootMessenger = getRootSnapInterfaceControllerMessenger(); diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts index cca147b9d8..807a8ea36d 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts @@ -18,6 +18,7 @@ import type { ComponentOrElement, InterfaceContext, } from '@metamask/snaps-sdk'; +import { ContentType } from '@metamask/snaps-sdk'; import type { JSXElement } from '@metamask/snaps-sdk/jsx'; import { getJsonSizeUnsafe, validateJsxLinks } from '@metamask/snaps-utils'; import type { Json } from '@metamask/utils'; @@ -61,6 +62,11 @@ export type UpdateInterfaceState = { handler: SnapInterfaceController['updateInterfaceState']; }; +export type UpdateInterfaceContentType = { + type: `${typeof controllerName}:updateInterfaceContentType`; + handler: SnapInterfaceController['updateInterfaceContentType']; +}; + export type ResolveInterface = { type: `${typeof controllerName}:resolveInterface`; handler: SnapInterfaceController['resolveInterface']; @@ -84,6 +90,7 @@ export type SnapInterfaceControllerActions = | UpdateInterface | DeleteInterface | UpdateInterfaceState + | UpdateInterfaceContentType | ResolveInterface | SnapInterfaceControllerGetStateAction; @@ -109,6 +116,7 @@ export type StoredInterface = { content: JSXElement; state: InterfaceState; context: InterfaceContext | null; + contentType: ContentType | null; }; export type SnapInterfaceControllerState = { @@ -132,7 +140,22 @@ export class SnapInterfaceController extends BaseController< super({ messenger, metadata: { - interfaces: { persist: false, anonymous: false }, + interfaces: { + persist: (interfaces: Record) => { + return Object.entries(interfaces).reduce< + Record + >((persistedInterfaces, [id, snapInterface]) => { + switch (snapInterface.contentType) { + case ContentType.Notification: + persistedInterfaces[id] = snapInterface; + return persistedInterfaces; + default: + return persistedInterfaces; + } + }, {}); + }, + anonymous: false, + }, }, name: controllerName, state: { interfaces: {}, ...state }, @@ -161,6 +184,11 @@ export class SnapInterfaceController extends BaseController< this.updateInterface.bind(this), ); + this.messagingSystem.registerActionHandler( + `${controllerName}:updateInterfaceContentType`, + this.updateInterfaceContentType.bind(this), + ); + this.messagingSystem.registerActionHandler( `${controllerName}:deleteInterface`, this.deleteInterface.bind(this), @@ -183,12 +211,14 @@ export class SnapInterfaceController extends BaseController< * @param snapId - The snap id that created the interface. * @param content - The interface content. * @param context - An optional interface context object. + * @param contentType - The type of content. * @returns The newly interface id. */ async createInterface( snapId: SnapId, content: ComponentOrElement, context?: InterfaceContext, + contentType?: ContentType, ) { const element = getJsxInterface(content); await this.#validateContent(element); @@ -205,6 +235,7 @@ export class SnapInterfaceController extends BaseController< content: castDraft(element), state: componentState, context: context ?? null, + contentType: contentType ?? null, }; }); @@ -255,6 +286,20 @@ export class SnapInterfaceController extends BaseController< }); } + /** + * Update the type of content in an interface. + * + * @param id - The interface id. + * @param contentType - The type of content. + */ + updateInterfaceContentType(id: string, contentType: ContentType) { + this.#validateContentType(contentType); + assert(this.state.interfaces[id], 'Interface does not exist.'); + this.update((draftState) => { + draftState.interfaces[id].contentType = contentType; + }); + } + /** * Delete an interface from state. * @@ -393,4 +438,17 @@ export class SnapInterfaceController extends BaseController< (id: string) => this.messagingSystem.call('SnapController:get', id), ); } + + /** + * Utility function to validate the type of interface content. + * Must be a value of the enum ContentType. + * Throws if the passed string is invalid. + * + * @param contentType - The content type. + */ + #validateContentType(contentType: string) { + if (!(contentType in ContentType)) { + throw new Error('Invalid content type.'); + } + } } diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index 532ddf41d1..d71259c0cf 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -47,7 +47,11 @@ import type { SnapId, ComponentOrElement, } from '@metamask/snaps-sdk'; -import { AuxiliaryFileEncoding, getErrorMessage } from '@metamask/snaps-sdk'; +import { + AuxiliaryFileEncoding, + getErrorMessage, + ContentType, +} from '@metamask/snaps-sdk'; import type { FetchedSnapFiles, InitialConnections, @@ -110,7 +114,7 @@ import type { Patch } from 'immer'; import { nanoid } from 'nanoid'; import { forceStrict, validateMachine } from '../fsm'; -import type { CreateInterface, GetInterface } from '../interface'; +import { type CreateInterface, type GetInterface } from '../interface'; import { log } from '../logging'; import type { ExecuteSnapAction, @@ -3311,16 +3315,20 @@ export class SnapController extends BaseController< * * @param snapId - The snap ID. * @param content - The initial interface content. + * @param contentType - The type of content. * @returns An identifier that can be used to identify the interface. */ async #createInterface( snapId: SnapId, content: ComponentOrElement, + contentType: ContentType, ): Promise { return this.messagingSystem.call( 'SnapInterfaceController:createInterface', snapId, content, + undefined, + contentType, ); } @@ -3358,10 +3366,20 @@ export class SnapController extends BaseController< // If a handler returns static content, we turn it into a dynamic UI if (castResult && hasProperty(castResult, 'content')) { const { content, ...rest } = castResult; + const getContentType = (handler: HandlerType) => { + if ( + handler === HandlerType.OnSignature || + handler === HandlerType.OnTransaction + ) { + return ContentType.Insight; + } + return ContentType.HomePage; + }; const id = await this.#createInterface( snapId, content as ComponentOrElement, + getContentType(handlerType), ); return { ...rest, id }; diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index bcb7e27708..809a4cc105 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -761,6 +761,7 @@ export const getRestrictedSnapInterfaceControllerMessenger = ( 'PhishingController:maybeUpdateState', 'ApprovalController:hasRequest', 'ApprovalController:acceptRequest', + 'SnapController:get', ], allowedEvents: [], }); diff --git a/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx b/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx index dd5d3c7fec..a5c72fcf77 100644 --- a/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx +++ b/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx @@ -1,6 +1,12 @@ import { PermissionType, SubjectType } from '@metamask/permission-controller'; import { rpcErrors } from '@metamask/rpc-errors'; -import { DialogType, heading, panel, text } from '@metamask/snaps-sdk'; +import { + ContentType, + DialogType, + heading, + panel, + text, +} from '@metamask/snaps-sdk'; import { Box, Text } from '@metamask/snaps-sdk/jsx'; import type { DialogMethodHooks } from './dialog'; @@ -30,6 +36,7 @@ describe('builder', () => { requestUserApproval: jest.fn(), createInterface: jest.fn(), getInterface: jest.fn(), + updateInterfaceContentType: jest.fn(), }, }), ).toStrictEqual({ @@ -50,6 +57,7 @@ describe('implementation', () => { getInterface: jest .fn() .mockReturnValue({ content: text('foo'), state: {}, snapId: 'foo' }), + updateInterfaceContentType: jest.fn(), } as DialogMethodHooks); it('accepts string dialog types', async () => { @@ -117,7 +125,11 @@ describe('implementation', () => { }, }); - expect(hooks.createInterface).toHaveBeenCalledWith('foo', content); + expect(hooks.createInterface).toHaveBeenCalledWith( + 'foo', + content, + ContentType.Dialog, + ); expect(hooks.requestUserApproval).toHaveBeenCalledTimes(1); expect(hooks.requestUserApproval).toHaveBeenCalledWith({ id: 'bar', @@ -137,6 +149,7 @@ describe('implementation', () => { getInterface: jest .fn() .mockReturnValue({ content: text('foo'), state: {}, snapId: 'foo' }), + updateInterfaceContentType: jest.fn(), }; const implementation = getDialogImplementation(hooks); @@ -177,7 +190,11 @@ describe('implementation', () => { }, }); - expect(hooks.createInterface).toHaveBeenCalledWith('foo', content); + expect(hooks.createInterface).toHaveBeenCalledWith( + 'foo', + content, + ContentType.Dialog, + ); expect(hooks.requestUserApproval).toHaveBeenCalledTimes(1); expect(hooks.requestUserApproval).toHaveBeenCalledWith({ id: undefined, @@ -209,7 +226,11 @@ describe('implementation', () => { }, }); - expect(hooks.createInterface).toHaveBeenCalledWith('foo', content); + expect(hooks.createInterface).toHaveBeenCalledWith( + 'foo', + content, + ContentType.Dialog, + ); expect(hooks.requestUserApproval).toHaveBeenCalledTimes(1); expect(hooks.requestUserApproval).toHaveBeenCalledWith({ id: undefined, @@ -229,6 +250,7 @@ describe('implementation', () => { getInterface: jest.fn().mockImplementation((_snapId, id) => { throw new Error(`Interface with id '${id}' not found.`); }), + updateInterfaceContentType: jest.fn(), }; const implementation = getDialogImplementation(hooks); diff --git a/packages/snaps-rpc-methods/src/restricted/dialog.ts b/packages/snaps-rpc-methods/src/restricted/dialog.ts index d4b5fb6f78..8b110edb5f 100644 --- a/packages/snaps-rpc-methods/src/restricted/dialog.ts +++ b/packages/snaps-rpc-methods/src/restricted/dialog.ts @@ -9,6 +9,7 @@ import { DialogType, enumValue, ComponentOrElementStruct, + ContentType, selectiveUnion, } from '@metamask/snaps-sdk'; import type { @@ -61,6 +62,7 @@ type RequestUserApproval = ( type CreateInterface = ( snapId: string, content: ComponentOrElement, + contentType?: ContentType, ) => Promise; type GetInterface = ( @@ -68,6 +70,11 @@ type GetInterface = ( id: string, ) => { content: ComponentOrElement; snapId: SnapId; state: InterfaceState }; +type UpdateInterfaceContentType = ( + id: string, + contentType: ContentType, +) => void; + export type DialogMethodHooks = { /** * @param opts - The `requestUserApproval` options. @@ -90,6 +97,11 @@ export type DialogMethodHooks = { * @param id - The interface ID. */ getInterface: GetInterface; + /** + * @param id - The interface ID. + * @param contentType - The type of the interface content. + */ + updateInterfaceContentType: UpdateInterfaceContentType; }; type DialogSpecificationBuilderOptions = { @@ -139,6 +151,7 @@ const methodHooks: MethodHooksObject = { requestUserApproval: true, createInterface: true, getInterface: true, + updateInterfaceContentType: true, }; export const dialogBuilder = Object.freeze({ @@ -249,6 +262,7 @@ export type DialogParameters = InferMatching< * This function should return a Promise that resolves with the appropriate value when the user has approved or rejected the request. * @param hooks.createInterface - A function that creates the interface in SnapInterfaceController. * @param hooks.getInterface - A function that gets an interface from SnapInterfaceController. + * @param hooks.updateInterfaceContentType - A function that updates an interface's content type. * @returns The method implementation which return value depends on the dialog * type, valid return types are: string, boolean, null. */ @@ -256,6 +270,7 @@ export function getDialogImplementation({ requestUserApproval, createInterface, getInterface, + updateInterfaceContentType, }: DialogMethodHooks) { return async function dialogImplementation( args: RestrictedMethodOptions, @@ -289,6 +304,7 @@ export function getDialogImplementation({ const id = await createInterface( origin, validatedParams.content as Component, + ContentType.Dialog, ); return requestUserApproval({ @@ -300,6 +316,9 @@ export function getDialogImplementation({ } validateInterface(origin, validatedParams.id, getInterface); + // we update the content type here since if we are receiving this interface + // as an id that means it was already created with a snap_createInterface call + updateInterfaceContentType(validatedParams.id, ContentType.Dialog); return requestUserApproval({ id: diff --git a/packages/snaps-sdk/src/types/interface.ts b/packages/snaps-sdk/src/types/interface.ts index 3e8290cb3c..a587a0283c 100644 --- a/packages/snaps-sdk/src/types/interface.ts +++ b/packages/snaps-sdk/src/types/interface.ts @@ -45,3 +45,10 @@ export const ComponentOrElementStruct = selectiveUnion((value) => { export const InterfaceContextStruct = record(string(), JsonStruct); export type InterfaceContext = Infer; + +export enum ContentType { + Insight = 'Insight', + Dialog = 'Dialog', + Notification = 'Notification', + HomePage = 'HomePage', +} From 7335c309ffc6c66bd5a232dc11b3ce5e89fffbe4 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 23 Oct 2024 01:18:55 -0400 Subject: [PATCH 02/26] update coverage --- packages/snaps-controllers/coverage.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index c10c3a0f6a..bba0e3300b 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { - "branches": 92.73, + "branches": 92.65, "functions": 96.65, - "lines": 97.99, - "statements": 97.69 + "lines": 97.95, + "statements": 97.65 } From 0a19daf84e0c39b1dbefe37c2cbbc8e3e8d36933 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 23 Oct 2024 01:42:02 -0400 Subject: [PATCH 03/26] test fixes --- packages/examples/packages/browserify-plugin/snap.manifest.json | 2 +- packages/examples/packages/browserify/snap.manifest.json | 2 +- packages/snaps-simulation/src/methods/hooks/interface.test.ts | 1 + packages/snaps-simulator/src/features/simulation/hooks.test.ts | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/examples/packages/browserify-plugin/snap.manifest.json b/packages/examples/packages/browserify-plugin/snap.manifest.json index 3fa785166d..3d64ffd13c 100644 --- a/packages/examples/packages/browserify-plugin/snap.manifest.json +++ b/packages/examples/packages/browserify-plugin/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "zA6fni0b6B+ELhkMRiw6vcAgKj1/9A/Rm+dxkPY/oxM=", + "shasum": "27qYDxM1vuhrXntxwo8v6GE52cJ7OsaIYo51Q4p3gq0=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/browserify/snap.manifest.json b/packages/examples/packages/browserify/snap.manifest.json index 1f1ce212a2..6bd23fe54a 100644 --- a/packages/examples/packages/browserify/snap.manifest.json +++ b/packages/examples/packages/browserify/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "diJJzn5l3lSAKQvHldlYmQXjTcO/IDnLOnxH7kGmkW0=", + "shasum": "oT2RHVQmSMHq87FYqFRPXqjERd+gOrI0RKjwNWDbSWY=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snaps-simulation/src/methods/hooks/interface.test.ts b/packages/snaps-simulation/src/methods/hooks/interface.test.ts index ffad7cdaee..0173820a2f 100644 --- a/packages/snaps-simulation/src/methods/hooks/interface.test.ts +++ b/packages/snaps-simulation/src/methods/hooks/interface.test.ts @@ -71,6 +71,7 @@ describe('getGetInterfaceImplementation', () => { state: {}, snapId: MOCK_SNAP_ID, context: null, + contentType: null, }); }); }); diff --git a/packages/snaps-simulator/src/features/simulation/hooks.test.ts b/packages/snaps-simulator/src/features/simulation/hooks.test.ts index 3efee20f20..a7afbe0434 100644 --- a/packages/snaps-simulator/src/features/simulation/hooks.test.ts +++ b/packages/snaps-simulator/src/features/simulation/hooks.test.ts @@ -336,6 +336,7 @@ describe('updateInterface', () => { snapId: snapId as SnapId, content: Box({ children: Text({ children: 'foo' }) }), context: null, + contentType: null, }), ) .silentRun(); From 5c4f5b8d6f6bb3cba08d85a360e982761b705053 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 23 Oct 2024 13:36:02 -0400 Subject: [PATCH 04/26] update coverage --- packages/snaps-controllers/coverage.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index bba0e3300b..9f6755552b 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { "branches": 92.65, - "functions": 96.65, + "functions": 96.7, "lines": 97.95, "statements": 97.65 } From bf86a94cf01148f2d60eca264bcbca95459458df Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 23 Oct 2024 13:39:11 -0400 Subject: [PATCH 05/26] update params --- packages/snaps-rpc-methods/src/restricted/dialog.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/snaps-rpc-methods/src/restricted/dialog.ts b/packages/snaps-rpc-methods/src/restricted/dialog.ts index 8b110edb5f..001dcfb476 100644 --- a/packages/snaps-rpc-methods/src/restricted/dialog.ts +++ b/packages/snaps-rpc-methods/src/restricted/dialog.ts @@ -19,6 +19,7 @@ import type { SnapId, PromptDialog, ComponentOrElement, + InterfaceContext, } from '@metamask/snaps-sdk'; import type { InferMatching } from '@metamask/snaps-utils'; import type { Infer } from '@metamask/superstruct'; @@ -62,6 +63,7 @@ type RequestUserApproval = ( type CreateInterface = ( snapId: string, content: ComponentOrElement, + context?: InterfaceContext, contentType?: ContentType, ) => Promise; @@ -304,6 +306,7 @@ export function getDialogImplementation({ const id = await createInterface( origin, validatedParams.content as Component, + undefined, ContentType.Dialog, ); From 5fd08ae13f4f5de31498c4ec9c89454d030501c8 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 23 Oct 2024 13:44:40 -0400 Subject: [PATCH 06/26] update more types --- .../snaps-rpc-methods/src/permitted/createInterface.ts | 2 ++ packages/snaps-simulation/src/methods/hooks/interface.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/snaps-rpc-methods/src/permitted/createInterface.ts b/packages/snaps-rpc-methods/src/permitted/createInterface.ts index 6dc66c53fb..1dd983036f 100644 --- a/packages/snaps-rpc-methods/src/permitted/createInterface.ts +++ b/packages/snaps-rpc-methods/src/permitted/createInterface.ts @@ -7,6 +7,7 @@ import type { JsonRpcRequest, ComponentOrElement, InterfaceContext, + ContentType, } from '@metamask/snaps-sdk'; import { ComponentOrElementStruct, @@ -30,6 +31,7 @@ export type CreateInterfaceMethodHooks = { createInterface: ( ui: ComponentOrElement, context?: InterfaceContext, + contentType?: ContentType, ) => Promise; }; diff --git a/packages/snaps-simulation/src/methods/hooks/interface.ts b/packages/snaps-simulation/src/methods/hooks/interface.ts index 50a1343117..228742a338 100644 --- a/packages/snaps-simulation/src/methods/hooks/interface.ts +++ b/packages/snaps-simulation/src/methods/hooks/interface.ts @@ -1,4 +1,9 @@ -import type { Component, InterfaceContext, SnapId } from '@metamask/snaps-sdk'; +import type { + Component, + ContentType, + InterfaceContext, + SnapId, +} from '@metamask/snaps-sdk'; import type { RootControllerMessenger } from '../../controllers'; @@ -15,12 +20,14 @@ export function getCreateInterfaceImplementation( snapId: SnapId, content: Component, context?: InterfaceContext, + contentType?: ContentType, ) => controllerMessenger.call( 'SnapInterfaceController:createInterface', snapId, content, context, + contentType, ); } From 4c84900eca41e6a65ff5bf67841a1068cc28411e Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 23 Oct 2024 13:59:50 -0400 Subject: [PATCH 07/26] fix tests --- packages/snaps-rpc-methods/src/restricted/dialog.test.tsx | 1 + packages/snaps-simulation/src/methods/hooks/interface.test.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx b/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx index a5c72fcf77..db74d7cbca 100644 --- a/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx +++ b/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx @@ -128,6 +128,7 @@ describe('implementation', () => { expect(hooks.createInterface).toHaveBeenCalledWith( 'foo', content, + undefined, ContentType.Dialog, ); expect(hooks.requestUserApproval).toHaveBeenCalledTimes(1); diff --git a/packages/snaps-simulation/src/methods/hooks/interface.test.ts b/packages/snaps-simulation/src/methods/hooks/interface.test.ts index 0173820a2f..27f434cd1b 100644 --- a/packages/snaps-simulation/src/methods/hooks/interface.test.ts +++ b/packages/snaps-simulation/src/methods/hooks/interface.test.ts @@ -1,5 +1,5 @@ import { SnapInterfaceController } from '@metamask/snaps-controllers'; -import { text } from '@metamask/snaps-sdk'; +import { ContentType, text } from '@metamask/snaps-sdk'; import { getJsxElementFromComponent } from '@metamask/snaps-utils'; import { MOCK_SNAP_ID } from '@metamask/snaps-utils/test-utils'; @@ -36,6 +36,7 @@ describe('getCreateInterfaceImplementation', () => { MOCK_SNAP_ID, content, undefined, + ContentType.Dialog, ); expect(result.content).toStrictEqual(getJsxElementFromComponent(content)); From 6f40593bf147cda59e92f2bb3b213d121d5b8934 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Wed, 23 Oct 2024 14:42:30 -0400 Subject: [PATCH 08/26] fix tests --- packages/snaps-rpc-methods/src/restricted/dialog.test.tsx | 2 ++ packages/snaps-simulation/src/methods/hooks/interface.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx b/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx index db74d7cbca..07f17a9c22 100644 --- a/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx +++ b/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx @@ -194,6 +194,7 @@ describe('implementation', () => { expect(hooks.createInterface).toHaveBeenCalledWith( 'foo', content, + undefined, ContentType.Dialog, ); expect(hooks.requestUserApproval).toHaveBeenCalledTimes(1); @@ -230,6 +231,7 @@ describe('implementation', () => { expect(hooks.createInterface).toHaveBeenCalledWith( 'foo', content, + undefined, ContentType.Dialog, ); expect(hooks.requestUserApproval).toHaveBeenCalledTimes(1); diff --git a/packages/snaps-simulation/src/methods/hooks/interface.test.ts b/packages/snaps-simulation/src/methods/hooks/interface.test.ts index 27f434cd1b..59bbe419d7 100644 --- a/packages/snaps-simulation/src/methods/hooks/interface.test.ts +++ b/packages/snaps-simulation/src/methods/hooks/interface.test.ts @@ -1,5 +1,5 @@ import { SnapInterfaceController } from '@metamask/snaps-controllers'; -import { ContentType, text } from '@metamask/snaps-sdk'; +import { text } from '@metamask/snaps-sdk'; import { getJsxElementFromComponent } from '@metamask/snaps-utils'; import { MOCK_SNAP_ID } from '@metamask/snaps-utils/test-utils'; @@ -36,7 +36,7 @@ describe('getCreateInterfaceImplementation', () => { MOCK_SNAP_ID, content, undefined, - ContentType.Dialog, + undefined, ); expect(result.content).toStrictEqual(getJsxElementFromComponent(content)); From e5b4551f4c4db7102e5845a2377d347af45d1ec9 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 24 Oct 2024 12:27:02 -0400 Subject: [PATCH 09/26] address PR comments --- .../SnapInterfaceController.test.tsx | 45 ------------------- .../src/interface/SnapInterfaceController.ts | 38 ---------------- .../src/snaps/SnapController.ts | 20 ++------- .../src/test-utils/controller.ts | 1 - .../src/restricted/dialog.test.tsx | 33 ++------------ .../src/restricted/dialog.ts | 20 +-------- 6 files changed, 8 insertions(+), 149 deletions(-) diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx index a7481d65f3..2c020e4c50 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx @@ -1145,51 +1145,6 @@ describe('SnapInterfaceController', () => { }); }); - describe('updateInterfaceContentType', () => { - it("can update an interface's content type", async () => { - const rootMessenger = getRootSnapInterfaceControllerMessenger(); - const controllerMessenger = - getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ - messenger: controllerMessenger, - }); - - const content = form({ name: 'foo', children: [input({ name: 'bar' })] }); - - let contentType; - - const id = await rootMessenger.call( - 'SnapInterfaceController:createInterface', - MOCK_SNAP_ID, - content, - ); - - contentType = rootMessenger.call( - 'SnapInterfaceController:getInterface', - MOCK_SNAP_ID, - id, - ).contentType; - - expect(contentType).toBeNull(); - - rootMessenger.call( - 'SnapInterfaceController:updateInterfaceContentType', - id, - ContentType.Dialog, - ); - - contentType = rootMessenger.call( - 'SnapInterfaceController:getInterface', - MOCK_SNAP_ID, - id, - ).contentType; - - expect(contentType).toStrictEqual(ContentType.Dialog); - }); - }); - describe('resolveInterface', () => { it('resolves the interface with the given value', async () => { const rootMessenger = getRootSnapInterfaceControllerMessenger(); diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts index 807a8ea36d..1f015355c4 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts @@ -62,11 +62,6 @@ export type UpdateInterfaceState = { handler: SnapInterfaceController['updateInterfaceState']; }; -export type UpdateInterfaceContentType = { - type: `${typeof controllerName}:updateInterfaceContentType`; - handler: SnapInterfaceController['updateInterfaceContentType']; -}; - export type ResolveInterface = { type: `${typeof controllerName}:resolveInterface`; handler: SnapInterfaceController['resolveInterface']; @@ -90,7 +85,6 @@ export type SnapInterfaceControllerActions = | UpdateInterface | DeleteInterface | UpdateInterfaceState - | UpdateInterfaceContentType | ResolveInterface | SnapInterfaceControllerGetStateAction; @@ -184,11 +178,6 @@ export class SnapInterfaceController extends BaseController< this.updateInterface.bind(this), ); - this.messagingSystem.registerActionHandler( - `${controllerName}:updateInterfaceContentType`, - this.updateInterfaceContentType.bind(this), - ); - this.messagingSystem.registerActionHandler( `${controllerName}:deleteInterface`, this.deleteInterface.bind(this), @@ -286,20 +275,6 @@ export class SnapInterfaceController extends BaseController< }); } - /** - * Update the type of content in an interface. - * - * @param id - The interface id. - * @param contentType - The type of content. - */ - updateInterfaceContentType(id: string, contentType: ContentType) { - this.#validateContentType(contentType); - assert(this.state.interfaces[id], 'Interface does not exist.'); - this.update((draftState) => { - draftState.interfaces[id].contentType = contentType; - }); - } - /** * Delete an interface from state. * @@ -438,17 +413,4 @@ export class SnapInterfaceController extends BaseController< (id: string) => this.messagingSystem.call('SnapController:get', id), ); } - - /** - * Utility function to validate the type of interface content. - * Must be a value of the enum ContentType. - * Throws if the passed string is invalid. - * - * @param contentType - The content type. - */ - #validateContentType(contentType: string) { - if (!(contentType in ContentType)) { - throw new Error('Invalid content type.'); - } - } } diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index a43c81bf9f..4fcea6f3fb 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -46,12 +46,9 @@ import type { RequestSnapsResult, SnapId, ComponentOrElement, -} from '@metamask/snaps-sdk'; -import { - AuxiliaryFileEncoding, - getErrorMessage, ContentType, } from '@metamask/snaps-sdk'; +import { AuxiliaryFileEncoding, getErrorMessage } from '@metamask/snaps-sdk'; import type { FetchedSnapFiles, InitialConnections, @@ -115,7 +112,7 @@ import { nanoid } from 'nanoid'; import semver from 'semver'; import { forceStrict, validateMachine } from '../fsm'; -import { type CreateInterface, type GetInterface } from '../interface'; +import type { CreateInterface, GetInterface } from '../interface'; import { log } from '../logging'; import type { ExecuteSnapAction, @@ -3379,7 +3376,7 @@ export class SnapController extends BaseController< async #createInterface( snapId: SnapId, content: ComponentOrElement, - contentType: ContentType, + contentType?: ContentType, ): Promise { return this.messagingSystem.call( 'SnapInterfaceController:createInterface', @@ -3424,20 +3421,9 @@ export class SnapController extends BaseController< // If a handler returns static content, we turn it into a dynamic UI if (castResult && hasProperty(castResult, 'content')) { const { content, ...rest } = castResult; - const getContentType = (handler: HandlerType) => { - if ( - handler === HandlerType.OnSignature || - handler === HandlerType.OnTransaction - ) { - return ContentType.Insight; - } - return ContentType.HomePage; - }; - const id = await this.#createInterface( snapId, content as ComponentOrElement, - getContentType(handlerType), ); return { ...rest, id }; diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index 88e6bf7c8a..52978aacaf 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -764,7 +764,6 @@ export const getRestrictedSnapInterfaceControllerMessenger = ( 'PhishingController:maybeUpdateState', 'ApprovalController:hasRequest', 'ApprovalController:acceptRequest', - 'SnapController:get', ], allowedEvents: [], }); diff --git a/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx b/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx index 07f17a9c22..dd5d3c7fec 100644 --- a/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx +++ b/packages/snaps-rpc-methods/src/restricted/dialog.test.tsx @@ -1,12 +1,6 @@ import { PermissionType, SubjectType } from '@metamask/permission-controller'; import { rpcErrors } from '@metamask/rpc-errors'; -import { - ContentType, - DialogType, - heading, - panel, - text, -} from '@metamask/snaps-sdk'; +import { DialogType, heading, panel, text } from '@metamask/snaps-sdk'; import { Box, Text } from '@metamask/snaps-sdk/jsx'; import type { DialogMethodHooks } from './dialog'; @@ -36,7 +30,6 @@ describe('builder', () => { requestUserApproval: jest.fn(), createInterface: jest.fn(), getInterface: jest.fn(), - updateInterfaceContentType: jest.fn(), }, }), ).toStrictEqual({ @@ -57,7 +50,6 @@ describe('implementation', () => { getInterface: jest .fn() .mockReturnValue({ content: text('foo'), state: {}, snapId: 'foo' }), - updateInterfaceContentType: jest.fn(), } as DialogMethodHooks); it('accepts string dialog types', async () => { @@ -125,12 +117,7 @@ describe('implementation', () => { }, }); - expect(hooks.createInterface).toHaveBeenCalledWith( - 'foo', - content, - undefined, - ContentType.Dialog, - ); + expect(hooks.createInterface).toHaveBeenCalledWith('foo', content); expect(hooks.requestUserApproval).toHaveBeenCalledTimes(1); expect(hooks.requestUserApproval).toHaveBeenCalledWith({ id: 'bar', @@ -150,7 +137,6 @@ describe('implementation', () => { getInterface: jest .fn() .mockReturnValue({ content: text('foo'), state: {}, snapId: 'foo' }), - updateInterfaceContentType: jest.fn(), }; const implementation = getDialogImplementation(hooks); @@ -191,12 +177,7 @@ describe('implementation', () => { }, }); - expect(hooks.createInterface).toHaveBeenCalledWith( - 'foo', - content, - undefined, - ContentType.Dialog, - ); + expect(hooks.createInterface).toHaveBeenCalledWith('foo', content); expect(hooks.requestUserApproval).toHaveBeenCalledTimes(1); expect(hooks.requestUserApproval).toHaveBeenCalledWith({ id: undefined, @@ -228,12 +209,7 @@ describe('implementation', () => { }, }); - expect(hooks.createInterface).toHaveBeenCalledWith( - 'foo', - content, - undefined, - ContentType.Dialog, - ); + expect(hooks.createInterface).toHaveBeenCalledWith('foo', content); expect(hooks.requestUserApproval).toHaveBeenCalledTimes(1); expect(hooks.requestUserApproval).toHaveBeenCalledWith({ id: undefined, @@ -253,7 +229,6 @@ describe('implementation', () => { getInterface: jest.fn().mockImplementation((_snapId, id) => { throw new Error(`Interface with id '${id}' not found.`); }), - updateInterfaceContentType: jest.fn(), }; const implementation = getDialogImplementation(hooks); diff --git a/packages/snaps-rpc-methods/src/restricted/dialog.ts b/packages/snaps-rpc-methods/src/restricted/dialog.ts index 001dcfb476..f9091b6c78 100644 --- a/packages/snaps-rpc-methods/src/restricted/dialog.ts +++ b/packages/snaps-rpc-methods/src/restricted/dialog.ts @@ -9,7 +9,6 @@ import { DialogType, enumValue, ComponentOrElementStruct, - ContentType, selectiveUnion, } from '@metamask/snaps-sdk'; import type { @@ -20,6 +19,7 @@ import type { PromptDialog, ComponentOrElement, InterfaceContext, + ContentType, } from '@metamask/snaps-sdk'; import type { InferMatching } from '@metamask/snaps-utils'; import type { Infer } from '@metamask/superstruct'; @@ -72,11 +72,6 @@ type GetInterface = ( id: string, ) => { content: ComponentOrElement; snapId: SnapId; state: InterfaceState }; -type UpdateInterfaceContentType = ( - id: string, - contentType: ContentType, -) => void; - export type DialogMethodHooks = { /** * @param opts - The `requestUserApproval` options. @@ -99,11 +94,6 @@ export type DialogMethodHooks = { * @param id - The interface ID. */ getInterface: GetInterface; - /** - * @param id - The interface ID. - * @param contentType - The type of the interface content. - */ - updateInterfaceContentType: UpdateInterfaceContentType; }; type DialogSpecificationBuilderOptions = { @@ -153,7 +143,6 @@ const methodHooks: MethodHooksObject = { requestUserApproval: true, createInterface: true, getInterface: true, - updateInterfaceContentType: true, }; export const dialogBuilder = Object.freeze({ @@ -264,7 +253,6 @@ export type DialogParameters = InferMatching< * This function should return a Promise that resolves with the appropriate value when the user has approved or rejected the request. * @param hooks.createInterface - A function that creates the interface in SnapInterfaceController. * @param hooks.getInterface - A function that gets an interface from SnapInterfaceController. - * @param hooks.updateInterfaceContentType - A function that updates an interface's content type. * @returns The method implementation which return value depends on the dialog * type, valid return types are: string, boolean, null. */ @@ -272,7 +260,6 @@ export function getDialogImplementation({ requestUserApproval, createInterface, getInterface, - updateInterfaceContentType, }: DialogMethodHooks) { return async function dialogImplementation( args: RestrictedMethodOptions, @@ -306,8 +293,6 @@ export function getDialogImplementation({ const id = await createInterface( origin, validatedParams.content as Component, - undefined, - ContentType.Dialog, ); return requestUserApproval({ @@ -319,9 +304,6 @@ export function getDialogImplementation({ } validateInterface(origin, validatedParams.id, getInterface); - // we update the content type here since if we are receiving this interface - // as an id that means it was already created with a snap_createInterface call - updateInterfaceContentType(validatedParams.id, ContentType.Dialog); return requestUserApproval({ id: From 2ab88f0c5290814d5610acc72bb6fe4fbd07d10e Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Fri, 25 Oct 2024 09:17:30 -0400 Subject: [PATCH 10/26] update coverage --- packages/snaps-controllers/coverage.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index 9f6755552b..cacff6a6c9 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { "branches": 92.65, - "functions": 96.7, + "functions": 96.67, "lines": 97.95, "statements": 97.65 } From e00558172cb870ba252808dd63bf616af9e9f075 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Fri, 25 Oct 2024 10:42:14 -0400 Subject: [PATCH 11/26] add logic to cleanup notification interfaces --- packages/snaps-controllers/package.json | 1 + .../src/interface/SnapInterfaceController.ts | 66 +- yarn.lock | 810 +++++++++++++++++- 3 files changed, 854 insertions(+), 23 deletions(-) diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index daf6f486a6..cd5a41ae39 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -83,6 +83,7 @@ "@metamask/base-controller": "^6.0.2", "@metamask/json-rpc-engine": "^9.0.2", "@metamask/json-rpc-middleware-stream": "^8.0.2", + "@metamask/notification-services-controller": "^0.12.0", "@metamask/object-multiplex": "^2.0.0", "@metamask/permission-controller": "^11.0.0", "@metamask/phishing-controller": "^12.0.2", diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts index 1f015355c4..aedabd2b8a 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts @@ -8,6 +8,11 @@ import type { ControllerStateChangeEvent, } from '@metamask/base-controller'; import { BaseController } from '@metamask/base-controller'; +import type { + INotification, + NotificationListUpdatedEvent, +} from '@metamask/notification-services-controller/notification-services'; +import { TRIGGER_TYPES } from '@metamask/notification-services-controller/notification-services'; import type { MaybeUpdateState, TestOrigin, @@ -95,7 +100,8 @@ export type SnapInterfaceControllerStateChangeEvent = >; export type SnapInterfaceControllerEvents = - SnapInterfaceControllerStateChangeEvent; + | SnapInterfaceControllerStateChangeEvent + | NotificationListUpdatedEvent; export type SnapInterfaceControllerMessenger = RestrictedControllerMessenger< typeof controllerName, @@ -155,6 +161,11 @@ export class SnapInterfaceController extends BaseController< state: { interfaces: {}, ...state }, }); + this.messagingSystem.subscribe( + 'NotificationServicesController:notificationsListUpdated', + /* eslint-disable @typescript-eslint/unbound-method */ + this._onNotificationsListUpdated, + ); this.#registerMessageHandlers(); } @@ -217,8 +228,6 @@ export class SnapInterfaceController extends BaseController< const componentState = constructState({}, element); this.update((draftState) => { - // @ts-expect-error - TS2589: Type instantiation is excessively deep and - // possibly infinite. draftState.interfaces[id] = { snapId, content: castDraft(element), @@ -413,4 +422,55 @@ export class SnapInterfaceController extends BaseController< (id: string) => this.messagingSystem.call('SnapController:get', id), ); } + + _onNotificationsListUpdated(notificationsList: INotification[]) { + const snapNotificationsWithInterface = notificationsList.filter( + (notification) => { + return ( + notification.type === TRIGGER_TYPES.SNAP && + // @ts-expect-error detailedView can be undefined here, type needs to be updated in the core repo + notification.data?.detailedView + ); + }, + ); + + const interfaceIdSet = new Set( + snapNotificationsWithInterface.map( + // @ts-expect-error detailedView can be undefined here, type needs to be updated in the core repo + (notification) => notification.data.detailedView.interfaceId, + ), + ); + + const updatedState = Object.entries(this.state.interfaces).reduce< + Record + >((newState, [id, snapInterface]) => { + if (snapInterface.contentType === ContentType.Notification) { + if (interfaceIdSet.has(id)) { + newState[id] = snapInterface; + } + } else { + newState[id] = snapInterface; + } + return newState; + }, {}); + + this.update((state) => { + // @ts-expect-error - TS2589: Type instantiation is excessively deep and + // possibly infinite. + state.interfaces = updatedState; + }); + } + + /** + * Run controller teardown process and unsubscribe from notification service controller events. + */ + destroy() { + super.destroy(); + + /* eslint-disable @typescript-eslint/unbound-method */ + this.messagingSystem.unsubscribe( + 'NotificationServicesController:notificationsListUpdated', + this._onNotificationsListUpdated, + ); + } } diff --git a/yarn.lock b/yarn.lock index 6e053b127b..408345b959 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2636,6 +2636,23 @@ __metadata: languageName: node linkType: hard +"@contentful/rich-text-html-renderer@npm:^16.5.2": + version: 16.6.10 + resolution: "@contentful/rich-text-html-renderer@npm:16.6.10" + dependencies: + "@contentful/rich-text-types": "npm:^16.8.5" + escape-html: "npm:^1.0.3" + checksum: 10/fdf5121524355c96cd8a4628b2cc50a61b9b9d37da167dd983cc8fa24fa822427809d00064ebca9b85b8f40d92719f0b86f0e4ea4eaa8d078656ed56c51df6bb + languageName: node + linkType: hard + +"@contentful/rich-text-types@npm:^16.8.5": + version: 16.8.5 + resolution: "@contentful/rich-text-types@npm:16.8.5" + checksum: 10/7c1ec7088cc39bbd8f5edc14867ed3d59d06a55f2c92e6c62913aa2312056e920528e6ca6d59780b32c6fe2ab89c429b9f0bce9a52f456be7260f458527ccc3b + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -3300,6 +3317,549 @@ __metadata: languageName: node linkType: hard +"@firebase/analytics-compat@npm:0.2.14": + version: 0.2.14 + resolution: "@firebase/analytics-compat@npm:0.2.14" + dependencies: + "@firebase/analytics": "npm:0.10.8" + "@firebase/analytics-types": "npm:0.8.2" + "@firebase/component": "npm:0.6.9" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/0e368159d24223076b488b27308c11e5ef50456aff49fc58e1f66616228021c61e60c3299f63ce52ddc2f7099d803e9048bc28cd952cf5c302917002c03c85ee + languageName: node + linkType: hard + +"@firebase/analytics-types@npm:0.8.2": + version: 0.8.2 + resolution: "@firebase/analytics-types@npm:0.8.2" + checksum: 10/297fb7becbc51950c7de5809fed896c391d1e87b4d8bb4bf88f4e8760b2e32f903a7dd8e92de4424b49c4e2ecb60a44d49e2f9c68ac3f7ffe3a0194f78910392 + languageName: node + linkType: hard + +"@firebase/analytics@npm:0.10.8": + version: 0.10.8 + resolution: "@firebase/analytics@npm:0.10.8" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/installations": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/152ddaf68146f02baa7060d34426c25ec13890a53942ffa2db09faa148bef35f59ee9810e6fb8f561fb3d115b71d1fb9fb111d2a0f0199aa510220782557c765 + languageName: node + linkType: hard + +"@firebase/app-check-compat@npm:0.3.15": + version: 0.3.15 + resolution: "@firebase/app-check-compat@npm:0.3.15" + dependencies: + "@firebase/app-check": "npm:0.8.8" + "@firebase/app-check-types": "npm:0.5.2" + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/ae541d324d5f91dbb7b479855d3380c4fe73e365013b80973a54620405093e6fd2f8e418549155b3a527530472a19b6edf6df1481a708f823eba42e376105b28 + languageName: node + linkType: hard + +"@firebase/app-check-interop-types@npm:0.3.2": + version: 0.3.2 + resolution: "@firebase/app-check-interop-types@npm:0.3.2" + checksum: 10/3effe656a4762c541838f4bde91b4498e51d48389046b930dc3dbb012e54b6ab0727f7c68a3e94198f633d57833346fc337a0847b6b03d2407030e1489d466fe + languageName: node + linkType: hard + +"@firebase/app-check-types@npm:0.5.2": + version: 0.5.2 + resolution: "@firebase/app-check-types@npm:0.5.2" + checksum: 10/2b33a7adfb7b6ebf5423940bf0af5909df69bf2d6184e12e989f6c76062077be16c31193795349862b4f8aab6b3059806b732a92995cae30fd77419f19a86c6e + languageName: node + linkType: hard + +"@firebase/app-check@npm:0.8.8": + version: 0.8.8 + resolution: "@firebase/app-check@npm:0.8.8" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/a3676f2143c8e438d7e8ac11bb163af30880f6ce6acc5cc54cfcc214b8efd5dabce14c040626f8a64a3967db144b99834f1108c2076a0eae8a6baf864b5a3d77 + languageName: node + linkType: hard + +"@firebase/app-compat@npm:0.2.43": + version: 0.2.43 + resolution: "@firebase/app-compat@npm:0.2.43" + dependencies: + "@firebase/app": "npm:0.10.13" + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + checksum: 10/e27340dbc9804ffd0d469cc1fa919cd61b6e04fe96599d14414aa06c3dcbe75b23c324f0bedfff4dbd5d9b829b8dde5a2e8b5464f1f686d66f9c00971d9d4c56 + languageName: node + linkType: hard + +"@firebase/app-types@npm:0.9.2": + version: 0.9.2 + resolution: "@firebase/app-types@npm:0.9.2" + checksum: 10/566b3714a4d7e8180514258e4b1549bf5b28ae0383b4ff53d3532a45e114048afdd27c1fef8688d871dd9e5ad5307e749776e23f094122655ac6b0fb550eb11a + languageName: node + linkType: hard + +"@firebase/app@npm:0.10.13": + version: 0.10.13 + resolution: "@firebase/app@npm:0.10.13" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + idb: "npm:7.1.1" + tslib: "npm:^2.1.0" + checksum: 10/54ec64b3a992c2f30c800fb5638bf586e7e7f351899887c701d5f946ad8ca445d8c1d3024007b7939a7e6ae29a51d90567552a863323594dc6fca22f1e811e0b + languageName: node + linkType: hard + +"@firebase/auth-compat@npm:0.5.14": + version: 0.5.14 + resolution: "@firebase/auth-compat@npm:0.5.14" + dependencies: + "@firebase/auth": "npm:1.7.9" + "@firebase/auth-types": "npm:0.12.2" + "@firebase/component": "npm:0.6.9" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + undici: "npm:6.19.7" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/85d5259e7b04b14b5d02dc1fb19b015d742c594c14138f33f13146ed9f6caa7ed9d19d65bb99aaca57e70ffd2a491e520d8638eadefbd00f839d37ef972cbbda + languageName: node + linkType: hard + +"@firebase/auth-interop-types@npm:0.2.3": + version: 0.2.3 + resolution: "@firebase/auth-interop-types@npm:0.2.3" + checksum: 10/e55b8ded6bd1a5e6a2845c9c7ed520bb9a8a76e4ddf90249bf685986ac7b1fb079be2fa4edcb6a3aa81d1d56870a470eadcd5a8f20b797dccd803d72ed4c80aa + languageName: node + linkType: hard + +"@firebase/auth-types@npm:0.12.2": + version: 0.12.2 + resolution: "@firebase/auth-types@npm:0.12.2" + peerDependencies: + "@firebase/app-types": 0.x + "@firebase/util": 1.x + checksum: 10/f55449381de8e2a24ffaf19f12b5c4a093c8323034253ea7a5f7afc946327d20b09f32a483c12960862a1c4814645ea80bc4343f0a9f22db5dc048ca82773132 + languageName: node + linkType: hard + +"@firebase/auth@npm:1.7.9": + version: 1.7.9 + resolution: "@firebase/auth@npm:1.7.9" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + undici: "npm:6.19.7" + peerDependencies: + "@firebase/app": 0.x + "@react-native-async-storage/async-storage": ^1.18.1 + peerDependenciesMeta: + "@react-native-async-storage/async-storage": + optional: true + checksum: 10/010013ec339c9ef7b4d9278c6cacfd8e2eb3282f27a3e4e89c42a5968955976a26277421f34fda3e9400409a22a61f632bcc03e713b3f39d71e4777bc003165d + languageName: node + linkType: hard + +"@firebase/component@npm:0.6.9": + version: 0.6.9 + resolution: "@firebase/component@npm:0.6.9" + dependencies: + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + checksum: 10/76c865d640e4b24a0e50876ecdc0e1199df38af562131a937b5a4bac924d61b6933339afb7906881dca509f38f3b0c511cd6b5008e061424c61b20876de9531e + languageName: node + linkType: hard + +"@firebase/data-connect@npm:0.1.0": + version: 0.1.0 + resolution: "@firebase/data-connect@npm:0.1.0" + dependencies: + "@firebase/auth-interop-types": "npm:0.2.3" + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/20dac7c4755a0dde17abea0c99b41e96c9f7eea6ea39c36fd85f3f5554991b718a25b4bc1f92850208ec1f0e3b1ee584b1cf1913c4ad6c41643655682c06a2b0 + languageName: node + linkType: hard + +"@firebase/database-compat@npm:1.0.8": + version: 1.0.8 + resolution: "@firebase/database-compat@npm:1.0.8" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/database": "npm:1.0.8" + "@firebase/database-types": "npm:1.0.5" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + checksum: 10/28389efcc87da77b822cb27c31707824fe98e7b0a3bf9cbf2b0c0fccd9edd72e2681a9467b76b120281464dbfc814852ebca63d99a385a9cb68fb55c7b334105 + languageName: node + linkType: hard + +"@firebase/database-types@npm:1.0.5": + version: 1.0.5 + resolution: "@firebase/database-types@npm:1.0.5" + dependencies: + "@firebase/app-types": "npm:0.9.2" + "@firebase/util": "npm:1.10.0" + checksum: 10/bdf667da0369dce8623987fc01cad8db09cfe1895130f69ab581d34a0ee043ca6113c32457629147ae1441a934d985ede9d7cbe104ac346de6d0c21629903a8b + languageName: node + linkType: hard + +"@firebase/database@npm:1.0.8": + version: 1.0.8 + resolution: "@firebase/database@npm:1.0.8" + dependencies: + "@firebase/app-check-interop-types": "npm:0.3.2" + "@firebase/auth-interop-types": "npm:0.2.3" + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + faye-websocket: "npm:0.11.4" + tslib: "npm:^2.1.0" + checksum: 10/adb199a6ad7866b418e8b319cc505e108bfc8200b5406f21857706df0849d4e5982a1b0e44e07001821edebef73c4dfffc7f96fb77a2cff10bb9ac26f17d40c3 + languageName: node + linkType: hard + +"@firebase/firestore-compat@npm:0.3.38": + version: 0.3.38 + resolution: "@firebase/firestore-compat@npm:0.3.38" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/firestore": "npm:4.7.3" + "@firebase/firestore-types": "npm:3.0.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/de9e92b5ac612ea73322407b65b1d90067f7138d2159bcfd2400535d09968ea8c8b44956282172129eca78bf951f74a6991394b2634913458927570bb4fa8cd8 + languageName: node + linkType: hard + +"@firebase/firestore-types@npm:3.0.2": + version: 3.0.2 + resolution: "@firebase/firestore-types@npm:3.0.2" + peerDependencies: + "@firebase/app-types": 0.x + "@firebase/util": 1.x + checksum: 10/81e91f836a026ecb70937407ca8699add7abb5b050d8815620cde97c3eec3f78f7dfbb366225758909f0df31d9f21e98a84ba62701bd27ee38b2609898c11acd + languageName: node + linkType: hard + +"@firebase/firestore@npm:4.7.3": + version: 4.7.3 + resolution: "@firebase/firestore@npm:4.7.3" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + "@firebase/webchannel-wrapper": "npm:1.0.1" + "@grpc/grpc-js": "npm:~1.9.0" + "@grpc/proto-loader": "npm:^0.7.8" + tslib: "npm:^2.1.0" + undici: "npm:6.19.7" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/f46a6e3c03eadfa970d8ecc17627d0d696931a19092fcf5be4b2056a209da3691be0bd040a11d333d26c15fd14329ce0fb5dfc32bf2cfa530a4d035b45ef4edf + languageName: node + linkType: hard + +"@firebase/functions-compat@npm:0.3.14": + version: 0.3.14 + resolution: "@firebase/functions-compat@npm:0.3.14" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/functions": "npm:0.11.8" + "@firebase/functions-types": "npm:0.6.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/a8d6cbcdc646d78adecfcdc1f8fa14a5d9af2394dd69cac00c6826106b923e01d246c67fb7e09025ca7cfb876f8d5df97240cc056c64ccee8899ca5f17178a6c + languageName: node + linkType: hard + +"@firebase/functions-types@npm:0.6.2": + version: 0.6.2 + resolution: "@firebase/functions-types@npm:0.6.2" + checksum: 10/5b8733f9d4bd85a617d35dd10ce296d9ec0490494e584697c4eda8098ff1e865607d7880b84194e86c35d438bbcd714977c111180502d0d1b6b2da1cde1b37ca + languageName: node + linkType: hard + +"@firebase/functions@npm:0.11.8": + version: 0.11.8 + resolution: "@firebase/functions@npm:0.11.8" + dependencies: + "@firebase/app-check-interop-types": "npm:0.3.2" + "@firebase/auth-interop-types": "npm:0.2.3" + "@firebase/component": "npm:0.6.9" + "@firebase/messaging-interop-types": "npm:0.2.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + undici: "npm:6.19.7" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/44f3e42df189f3f3cb3c366b38e93a0ffdfaa1a7b3f6dba624bcd9a7cda3d3271df66f2769b7cbe7e1e5ff01bf6ab3bef6c1e1e15c6646e34514d1e2ebb60555 + languageName: node + linkType: hard + +"@firebase/installations-compat@npm:0.2.9": + version: 0.2.9 + resolution: "@firebase/installations-compat@npm:0.2.9" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/installations": "npm:0.6.9" + "@firebase/installations-types": "npm:0.5.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/919e1a4f4b63f5fe757a3c9cefb4a36cbab92deb4a6e15f249c94d6e80d1c6d37e5e384a460af8c17fc88e3091594bf43d036c88b704516c279b5ab8401977e1 + languageName: node + linkType: hard + +"@firebase/installations-types@npm:0.5.2": + version: 0.5.2 + resolution: "@firebase/installations-types@npm:0.5.2" + peerDependencies: + "@firebase/app-types": 0.x + checksum: 10/2e795280c299d644b8c8e3fdfa5c6f20cb367dd3b7df32317211f84393fa169b33dee0cbed28de407f3b22dc8f1fb2f7a11ae5a373f8082cc570ef61ef6b91ba + languageName: node + linkType: hard + +"@firebase/installations@npm:0.6.9": + version: 0.6.9 + resolution: "@firebase/installations@npm:0.6.9" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/util": "npm:1.10.0" + idb: "npm:7.1.1" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/349c8b7e877b002fb29f274f4d239fbca4c2c266ccb66ecfb5f1762f973a7fe1be99cc3346184d1230e6e35feb2b6f9e8b7169479fa0018b53e4a83837848619 + languageName: node + linkType: hard + +"@firebase/logger@npm:0.4.2": + version: 0.4.2 + resolution: "@firebase/logger@npm:0.4.2" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10/961b4605220c0a56c5f3ccf4e6049e44c27303c1ca998c6fa1d19de785c76d93e3b1a3da455e9f40655711345d8d779912366e4f369d93eda8d08c407cc5b140 + languageName: node + linkType: hard + +"@firebase/messaging-compat@npm:0.2.12": + version: 0.2.12 + resolution: "@firebase/messaging-compat@npm:0.2.12" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/messaging": "npm:0.12.12" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/0437ba6b24327d9eb02dc87ba61146fbb9720491ad671dc554438ac87e162d5fb154c704400d55c87ce01dd5aeedada9d0367fd114d840ead0d07802475eaa60 + languageName: node + linkType: hard + +"@firebase/messaging-interop-types@npm:0.2.2": + version: 0.2.2 + resolution: "@firebase/messaging-interop-types@npm:0.2.2" + checksum: 10/547f8ebf2c5a8dcbc484991b97d76bd3dc3eb4bd9d4e6ea2ffc652097c7065d92dc68d389ddb19fba41e0ce3b5f4cd757ed22f96b4744801149b0f8dbf323af7 + languageName: node + linkType: hard + +"@firebase/messaging@npm:0.12.12": + version: 0.12.12 + resolution: "@firebase/messaging@npm:0.12.12" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/installations": "npm:0.6.9" + "@firebase/messaging-interop-types": "npm:0.2.2" + "@firebase/util": "npm:1.10.0" + idb: "npm:7.1.1" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/a00125489085782faf189ad42f75bed108c6632b9d198d114e0a8ce28d89f9455b4f78ff8f7d24d4a86ad13e1e14e0f17fa2ff3605c6dd0c8ff1b65fef23ce3d + languageName: node + linkType: hard + +"@firebase/performance-compat@npm:0.2.9": + version: 0.2.9 + resolution: "@firebase/performance-compat@npm:0.2.9" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/performance": "npm:0.6.9" + "@firebase/performance-types": "npm:0.2.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/bc4e8b0208c9bc603518e1388713ec80658ee109c6af80d429479447ccb85e8e831269383233c379ed66bf37469d13f5c234074d0c0c9e7e69e909be5fdeca4f + languageName: node + linkType: hard + +"@firebase/performance-types@npm:0.2.2": + version: 0.2.2 + resolution: "@firebase/performance-types@npm:0.2.2" + checksum: 10/d25ae06cb75ab6b44ffacf7affadc1f651881f283e58381c444eb63b62dfb74c33c544ab89843518ec1d15367ba7c4343b4d6b22de1f1df35126a1283baa578d + languageName: node + linkType: hard + +"@firebase/performance@npm:0.6.9": + version: 0.6.9 + resolution: "@firebase/performance@npm:0.6.9" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/installations": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/d682d0b1e342ed3eda1a5ddab39c8ddac33afc9edb2c7335a2f9a28eb8c268b975bbf450a3bad5443138edebaf2aa731dca0b774bcf3211a6dc215b35d86d849 + languageName: node + linkType: hard + +"@firebase/remote-config-compat@npm:0.2.9": + version: 0.2.9 + resolution: "@firebase/remote-config-compat@npm:0.2.9" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/remote-config": "npm:0.4.9" + "@firebase/remote-config-types": "npm:0.3.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/a6db7509512d8d22b7ddf1127c741715e379e04e5b3246372bb0302d7c84afb421a94550adebecddcce5def115d61729a9580940dce6e65f8d77f9af30f69fe1 + languageName: node + linkType: hard + +"@firebase/remote-config-types@npm:0.3.2": + version: 0.3.2 + resolution: "@firebase/remote-config-types@npm:0.3.2" + checksum: 10/6c91599c653825708aba9fe9e4562997f108c3e4f3eaf5d188f31c859a6ad013414aa7a213b6b021b68049dd0dd57158546dbc9fb64384652274ef7f57ce7d7d + languageName: node + linkType: hard + +"@firebase/remote-config@npm:0.4.9": + version: 0.4.9 + resolution: "@firebase/remote-config@npm:0.4.9" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/installations": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/f14189f38c8cf75db16bf8b85dd004486b1dd8242f62d697c716fa85cd32928aed549ccea8c632a528870a424fc7f04f1132a14b3b099276cd7696c78e644b28 + languageName: node + linkType: hard + +"@firebase/storage-compat@npm:0.3.12": + version: 0.3.12 + resolution: "@firebase/storage-compat@npm:0.3.12" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/storage": "npm:0.13.2" + "@firebase/storage-types": "npm:0.8.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app-compat": 0.x + checksum: 10/4eea49a57f1d7537da697e5ff8b4e035ff1af69e416e7eab14485753c39c25eaa5a71bd2bafba0985ac6a7ce803f98f2f2f83c613c78c8f74bce286e3259b8ec + languageName: node + linkType: hard + +"@firebase/storage-types@npm:0.8.2": + version: 0.8.2 + resolution: "@firebase/storage-types@npm:0.8.2" + peerDependencies: + "@firebase/app-types": 0.x + "@firebase/util": 1.x + checksum: 10/e00716932370d2004dc9f7ef6d7e3aff72305b91569fa6ec15e8bc2ec784b03a150391e8be2c063234edbbfda7796da915d48e26ce2f1f7c5d3343acd39afd99 + languageName: node + linkType: hard + +"@firebase/storage@npm:0.13.2": + version: 0.13.2 + resolution: "@firebase/storage@npm:0.13.2" + dependencies: + "@firebase/component": "npm:0.6.9" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + undici: "npm:6.19.7" + peerDependencies: + "@firebase/app": 0.x + checksum: 10/d887f80cf95ef5daa80ffb2e6d564d25abb8a3e84099bee9730c95082597a12028bbf73bfe66fca2df3cdf04eaadea8e9d74ec0a826f946bc8f002293a9983ea + languageName: node + linkType: hard + +"@firebase/util@npm:1.10.0": + version: 1.10.0 + resolution: "@firebase/util@npm:1.10.0" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10/eb161f1c6294ff097f3c40236820e9e6e29cd6582e5e1254148157143272493580535ee2cb9e7c6055d3909b3ef39d8b64086895b071c665827acb66742b63eb + languageName: node + linkType: hard + +"@firebase/vertexai-preview@npm:0.0.4": + version: 0.0.4 + resolution: "@firebase/vertexai-preview@npm:0.0.4" + dependencies: + "@firebase/app-check-interop-types": "npm:0.3.2" + "@firebase/component": "npm:0.6.9" + "@firebase/logger": "npm:0.4.2" + "@firebase/util": "npm:1.10.0" + tslib: "npm:^2.1.0" + peerDependencies: + "@firebase/app": 0.x + "@firebase/app-types": 0.x + checksum: 10/8ec48d81f48aebdcc63b65d802c67bf36880f256e5c2f5f3b152dc91c8c0e924053fba2fac5218716612f8ee720b25d0822337a214f16f5b7e51ce0247dfc4e5 + languageName: node + linkType: hard + +"@firebase/webchannel-wrapper@npm:1.0.1": + version: 1.0.1 + resolution: "@firebase/webchannel-wrapper@npm:1.0.1" + checksum: 10/22fc7e1e6dd36ab7c13f3a6c1ff51f4d405304424dc323cb146109e7a3ab3b592e2ddb29f53197ee5719a8448cdedb98d9e86a080f9365e389f8429b1c6555c2 + languageName: node + linkType: hard + "@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -3307,6 +3867,30 @@ __metadata: languageName: node linkType: hard +"@grpc/grpc-js@npm:~1.9.0": + version: 1.9.15 + resolution: "@grpc/grpc-js@npm:1.9.15" + dependencies: + "@grpc/proto-loader": "npm:^0.7.8" + "@types/node": "npm:>=12.12.47" + checksum: 10/edd45c5970046ebb1bb54856f22a41186742c77dfb7e5182ca615f690f1a320af3abeef553d8924812d56911157a04882c7d264c2de64f326f8df7d473c47b2a + languageName: node + linkType: hard + +"@grpc/proto-loader@npm:^0.7.8": + version: 0.7.13 + resolution: "@grpc/proto-loader@npm:0.7.13" + dependencies: + lodash.camelcase: "npm:^4.3.0" + long: "npm:^5.0.0" + protobufjs: "npm:^7.2.5" + yargs: "npm:^17.7.2" + bin: + proto-loader-gen-types: build/bin/proto-loader-gen-types.js + checksum: 10/7e2d842c2061cbaf6450c71da0077263be3bab165454d5c8a3e1ae4d3c6d2915f02fd27da63ff01f05e127b1221acd40705273f5d29303901e60514e852992f4 + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.6": version: 0.11.7 resolution: "@humanwhocodes/config-array@npm:0.11.7" @@ -3916,13 +4500,13 @@ __metadata: languageName: node linkType: hard -"@metamask/base-controller@npm:^7.0.0": - version: 7.0.0 - resolution: "@metamask/base-controller@npm:7.0.0" +"@metamask/base-controller@npm:^7.0.0, @metamask/base-controller@npm:^7.0.1": + version: 7.0.1 + resolution: "@metamask/base-controller@npm:7.0.1" dependencies: "@metamask/utils": "npm:^9.1.0" immer: "npm:^9.0.6" - checksum: 10/0ea307da4a7863224fd1fc83039165dbd06d2725922d4d4cf5854f5e7894e789a3c277f1e4592a38ae002de869217a26550f3b9687c259fc29153984dc5b4a4c + checksum: 10/774b6d68ac95a5ec187e890d321bede50065f8a6f1ba7b49a19f5971366274054ac0e401548b51d3b014d0bca5d650409fb554dd13ce120e7fb3495b4e8e67b1 languageName: node linkType: hard @@ -4172,9 +4756,9 @@ __metadata: languageName: unknown linkType: soft -"@metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.2.0": - version: 11.2.0 - resolution: "@metamask/controller-utils@npm:11.2.0" +"@metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.2.0, @metamask/controller-utils@npm:^11.4.0": + version: 11.4.0 + resolution: "@metamask/controller-utils@npm:11.4.0" dependencies: "@ethereumjs/util": "npm:^8.1.0" "@metamask/eth-query": "npm:^4.0.0" @@ -4185,7 +4769,7 @@ __metadata: bn.js: "npm:^5.2.1" eth-ens-namehash: "npm:^2.0.8" fast-deep-equal: "npm:^3.1.3" - checksum: 10/8c8630a635c5eeeb8ef46b14a12779a5005d8112668500bfb513186dee3f68b94dcf43ce6eddf42c38382828cd7ba28657b627d0eb617207a25b6ee78eae6b08 + checksum: 10/f34d24880eab264bddaa5bef21afaecb206db6978364565d0f7b7a54b1d411f129eb84175041df3be8a66394c2d49e83b6648b5cbde6f34662a60fc553c31458 languageName: node linkType: hard @@ -5289,6 +5873,25 @@ __metadata: languageName: unknown linkType: soft +"@metamask/notification-services-controller@npm:^0.12.0": + version: 0.12.0 + resolution: "@metamask/notification-services-controller@npm:0.12.0" + dependencies: + "@contentful/rich-text-html-renderer": "npm:^16.5.2" + "@metamask/base-controller": "npm:^7.0.1" + "@metamask/controller-utils": "npm:^11.4.0" + "@metamask/utils": "npm:^9.1.0" + bignumber.js: "npm:^9.1.2" + firebase: "npm:^10.11.0" + loglevel: "npm:^1.8.1" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/keyring-controller": ^17.0.0 + "@metamask/profile-sync-controller": ^0.0.0 + checksum: 10/715cb7c656c6c5703d8582a9bcf368ce90fb6defcd5c8d71548b6271a04f2b761ce924d12091999bba5268a19d29ff7c84c87611dfdc8d599ce959f7b8b41b2f + languageName: node + linkType: hard + "@metamask/number-to-bn@npm:^1.7.1": version: 1.7.1 resolution: "@metamask/number-to-bn@npm:1.7.1" @@ -5725,6 +6328,7 @@ __metadata: "@metamask/eslint-config-typescript": "npm:^12.1.0" "@metamask/json-rpc-engine": "npm:^9.0.2" "@metamask/json-rpc-middleware-stream": "npm:^8.0.2" + "@metamask/notification-services-controller": "npm:^0.12.0" "@metamask/object-multiplex": "npm:^2.0.0" "@metamask/permission-controller": "npm:^11.0.0" "@metamask/phishing-controller": "npm:^12.0.2" @@ -6903,6 +7507,79 @@ __metadata: languageName: node linkType: hard +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 10/8a938d84fe4889411296db66b29287bd61ea3c14c2d23e7a8325f46a2b8ce899857c5f038d65d7641805e6c1d06b495525c7faf00c44f85a7ee6476649034969 + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: 10/c71b100daeb3c9bdccab5cbc29495b906ba0ae22ceedc200e1ba49717d9c4ab15a6256839cebb6f9c6acae4ed7c25c67e0a95e734f612b258261d1a3098fe342 + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.4": + version: 2.0.4 + resolution: "@protobufjs/codegen@npm:2.0.4" + checksum: 10/c6ee5fa172a8464f5253174d3c2353ea520c2573ad7b6476983d9b1346f4d8f2b44aa29feb17a949b83c1816bc35286a5ea265ed9d8fdd2865acfa09668c0447 + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/eventemitter@npm:1.1.0" + checksum: 10/03af3e99f17ad421283d054c88a06a30a615922a817741b43ca1b13e7c6b37820a37f6eba9980fb5150c54dba6e26cb6f7b64a6f7d8afa83596fafb3afa218c3 + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/fetch@npm:1.1.0" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.1" + "@protobufjs/inquire": "npm:^1.1.0" + checksum: 10/67ae40572ad536e4ef94269199f252c024b66e3059850906bdaee161ca1d75c73d04d35cd56f147a8a5a079f5808e342b99e61942c1dae15604ff0600b09a958 + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: 10/634c2c989da0ef2f4f19373d64187e2a79f598c5fb7991afb689d29a2ea17c14b796b29725945fa34b9493c17fb799e08ac0a7ccaae460ee1757d3083ed35187 + languageName: node + linkType: hard + +"@protobufjs/inquire@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/inquire@npm:1.1.0" + checksum: 10/c09efa34a5465cb120775e1a482136f2340a58b4abce7e93d72b8b5a9324a0e879275016ef9fcd73d72a4731639c54f2bb755bb82f916e4a78892d1d840bb3d2 + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 10/bb709567935fd385a86ad1f575aea98131bbd719c743fb9b6edd6b47ede429ff71a801cecbd64fc72deebf4e08b8f1bd8062793178cdaed3713b8d15771f9b83 + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: 10/b9c7047647f6af28e92aac54f6f7c1f7ff31b201b4bfcc7a415b2861528854fce3ec666d7e7e10fd744da905f7d4aef2205bbcc8944ca0ca7a82e18134d00c46 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/utf8@npm:1.1.0" + checksum: 10/131e289c57534c1d73a0e55782d6751dd821db1583cb2f7f7e017c9d6747addaebe79f28120b2e0185395d990aad347fb14ffa73ef4096fa38508d61a0e64602 + languageName: node + linkType: hard + "@puppeteer/browsers@npm:1.7.0": version: 1.7.0 resolution: "@puppeteer/browsers@npm:1.7.0" @@ -7955,12 +8632,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:22.7.5": - version: 22.7.5 - resolution: "@types/node@npm:22.7.5" +"@types/node@npm:*, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0": + version: 22.8.0 + resolution: "@types/node@npm:22.8.0" dependencies: - undici-types: "npm:~6.19.2" - checksum: 10/e8ba102f8c1aa7623787d625389be68d64e54fcbb76d41f6c2c64e8cf4c9f4a2370e7ef5e5f1732f3c57529d3d26afdcb2edc0101c5e413a79081449825c57ac + undici-types: "npm:~6.19.8" + checksum: 10/2ffb407fd94c362a2b712cff32e59e5f988bff6cb32334f884554a1acf85ff41f4f43815e71be39a16970d2d71939f732e4981bfb515cc6f557b9face24476e0 languageName: node linkType: hard @@ -7971,6 +8648,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:22.7.5": + version: 22.7.5 + resolution: "@types/node@npm:22.7.5" + dependencies: + undici-types: "npm:~6.19.2" + checksum: 10/e8ba102f8c1aa7623787d625389be68d64e54fcbb76d41f6c2c64e8cf4c9f4a2370e7ef5e5f1732f3c57529d3d26afdcb2edc0101c5e413a79081449825c57ac + languageName: node + linkType: hard + "@types/node@npm:^20.1.0, @types/node@npm:^20.1.1": version: 20.5.7 resolution: "@types/node@npm:20.5.7" @@ -9866,6 +10552,13 @@ __metadata: languageName: node linkType: hard +"bignumber.js@npm:^9.1.2": + version: 9.1.2 + resolution: "bignumber.js@npm:9.1.2" + checksum: 10/d89b8800a987225d2c00dcbf8a69dc08e92aa0880157c851c287b307d31ceb2fc2acb0c62c3e3a3d42b6c5fcae9b004035f13eb4386e56d529d7edac18d5c9d8 + languageName: node + linkType: hard + "bin-links@npm:4.0.3": version: 4.0.3 resolution: "bin-links@npm:4.0.3" @@ -13629,7 +14322,7 @@ __metadata: languageName: node linkType: hard -"faye-websocket@npm:^0.11.3": +"faye-websocket@npm:0.11.4, faye-websocket@npm:^0.11.3": version: 0.11.4 resolution: "faye-websocket@npm:0.11.4" dependencies: @@ -13803,6 +14496,42 @@ __metadata: languageName: node linkType: hard +"firebase@npm:^10.11.0": + version: 10.14.1 + resolution: "firebase@npm:10.14.1" + dependencies: + "@firebase/analytics": "npm:0.10.8" + "@firebase/analytics-compat": "npm:0.2.14" + "@firebase/app": "npm:0.10.13" + "@firebase/app-check": "npm:0.8.8" + "@firebase/app-check-compat": "npm:0.3.15" + "@firebase/app-compat": "npm:0.2.43" + "@firebase/app-types": "npm:0.9.2" + "@firebase/auth": "npm:1.7.9" + "@firebase/auth-compat": "npm:0.5.14" + "@firebase/data-connect": "npm:0.1.0" + "@firebase/database": "npm:1.0.8" + "@firebase/database-compat": "npm:1.0.8" + "@firebase/firestore": "npm:4.7.3" + "@firebase/firestore-compat": "npm:0.3.38" + "@firebase/functions": "npm:0.11.8" + "@firebase/functions-compat": "npm:0.3.14" + "@firebase/installations": "npm:0.6.9" + "@firebase/installations-compat": "npm:0.2.9" + "@firebase/messaging": "npm:0.12.12" + "@firebase/messaging-compat": "npm:0.2.12" + "@firebase/performance": "npm:0.6.9" + "@firebase/performance-compat": "npm:0.2.9" + "@firebase/remote-config": "npm:0.4.9" + "@firebase/remote-config-compat": "npm:0.2.9" + "@firebase/storage": "npm:0.13.2" + "@firebase/storage-compat": "npm:0.3.12" + "@firebase/util": "npm:1.10.0" + "@firebase/vertexai-preview": "npm:0.0.4" + checksum: 10/1b2fd7b653f632d2bb0cf3b87e7eda0ae69d28b702a7de00d2a166c7985b8747ef9d15f7ac151847d242f91a1cb08c689d0a07197e8b80561ff1a679398bf9f8 + languageName: node + linkType: hard + "flat-cache@npm:^3.0.4": version: 3.0.4 resolution: "flat-cache@npm:3.0.4" @@ -15004,6 +15733,13 @@ __metadata: languageName: node linkType: hard +"idb@npm:7.1.1": + version: 7.1.1 + resolution: "idb@npm:7.1.1" + checksum: 10/8e33eaebf21055129864acb89932e0739b8c96788e559df24c253ce114d8c6deb977a3b30ea47a9bb8a2ae8a55964861c3df65f360d95745e341cee40d5c17f4 + languageName: node + linkType: hard + "idna-uts46-hx@npm:^2.3.1": version: 2.3.1 resolution: "idna-uts46-hx@npm:2.3.1" @@ -17013,6 +17749,13 @@ __metadata: languageName: node linkType: hard +"lodash.camelcase@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.camelcase@npm:4.3.0" + checksum: 10/c301cc379310441dc73cd6cebeb91fb254bea74e6ad3027f9346fc43b4174385153df420ffa521654e502fd34c40ef69ca4e7d40ee7129a99e06f306032bfc65 + languageName: node + linkType: hard + "lodash.clonedeep@npm:^4.5.0": version: 4.5.0 resolution: "lodash.clonedeep@npm:4.5.0" @@ -17140,14 +17883,14 @@ __metadata: languageName: node linkType: hard -"loglevel@npm:^1.6.0": - version: 1.8.1 - resolution: "loglevel@npm:1.8.1" - checksum: 10/36a786082a7e4f1d962de330122291da3a102b88dbde81a45eb92a045c38b0903783958ba39dce641440c0413da303410e7f2565f897bccad828853bd5974c86 +"loglevel@npm:^1.6.0, loglevel@npm:^1.8.1": + version: 1.9.2 + resolution: "loglevel@npm:1.9.2" + checksum: 10/6153d8db308323f7ee20130bc40309e7a976c30a10379d8666b596d9c6441965c3e074c8d7ee3347fe5cfc059c0375b6f3e8a10b93d5b813cc5547f5aa412a29 languageName: node linkType: hard -"long@npm:^5.2.1": +"long@npm:^5.0.0, long@npm:^5.2.1": version: 5.2.3 resolution: "long@npm:5.2.3" checksum: 10/9167ec6947a825b827c30da169a7384eec6c0c9ec2f0b9c74da2e93d81159bbe39fb09c3f13dae9721d4b807ccfa09797a7dd1012f5d478e3e33ca3c78b608e6 @@ -19276,6 +20019,26 @@ __metadata: languageName: node linkType: hard +"protobufjs@npm:^7.2.5": + version: 7.4.0 + resolution: "protobufjs@npm:7.4.0" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.2" + "@protobufjs/base64": "npm:^1.1.2" + "@protobufjs/codegen": "npm:^2.0.4" + "@protobufjs/eventemitter": "npm:^1.1.0" + "@protobufjs/fetch": "npm:^1.1.0" + "@protobufjs/float": "npm:^1.0.2" + "@protobufjs/inquire": "npm:^1.1.0" + "@protobufjs/path": "npm:^1.1.2" + "@protobufjs/pool": "npm:^1.1.0" + "@protobufjs/utf8": "npm:^1.1.0" + "@types/node": "npm:>=13.7.0" + long: "npm:^5.0.0" + checksum: 10/408423506610f70858d7593632f4a6aa4f05796c90fd632be9b9252457c795acc71aa6d3b54bb7f48a890141728fee4ca3906723ccea6c202ad71f21b3879b8b + languageName: node + linkType: hard + "proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -22377,13 +23140,20 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.19.2": +"undici-types@npm:~6.19.2, undici-types@npm:~6.19.8": version: 6.19.8 resolution: "undici-types@npm:6.19.8" checksum: 10/cf0b48ed4fc99baf56584afa91aaffa5010c268b8842f62e02f752df209e3dea138b372a60a963b3b2576ed932f32329ce7ddb9cb5f27a6c83040d8cd74b7a70 languageName: node linkType: hard +"undici@npm:6.19.7": + version: 6.19.7 + resolution: "undici@npm:6.19.7" + checksum: 10/77fb8b0377388f6dba8244b015841318d621031211b4f3c2273d809304b77ec44adeba4b89dfd6708bdc044190e18f068e5b231882ef15d057d4624e46f544e3 + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" From 5cbc1a7b3f2f211b3c5a7bfd8410c7518ad26ff3 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Mon, 28 Oct 2024 09:20:36 -0400 Subject: [PATCH 12/26] update tests --- packages/snaps-controllers/package.json | 1 - .../SnapInterfaceController.test.tsx | 167 ++-- .../src/interface/SnapInterfaceController.ts | 22 +- .../src/test-utils/controller.ts | 10 +- yarn.lock | 773 +----------------- 5 files changed, 128 insertions(+), 845 deletions(-) diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index cd5a41ae39..daf6f486a6 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -83,7 +83,6 @@ "@metamask/base-controller": "^6.0.2", "@metamask/json-rpc-engine": "^9.0.2", "@metamask/json-rpc-middleware-stream": "^8.0.2", - "@metamask/notification-services-controller": "^0.12.0", "@metamask/object-multiplex": "^2.0.0", "@metamask/permission-controller": "^11.0.0", "@metamask/phishing-controller": "^12.0.2", diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx index 2c020e4c50..39a9e614cd 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx @@ -37,6 +37,46 @@ jest.mock('@metamask/snaps-utils', () => ({ })); describe('SnapInterfaceController', () => { + it('handles a notificationsListUpdated event on the controller messenger', async () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + const controller = new SnapInterfaceController({ + messenger: controllerMessenger, + state: { + interfaces: { + // @ts-expect-error missing properties + '1': { + contentType: ContentType.Notification, + }, + // @ts-expect-error missing properties + '2': { + contentType: ContentType.Dialog, + }, + }, + }, + }); + + rootMessenger.publish( + 'NotificationServicesController:notificationsListUpdated', + [], + ); + + // defer + setTimeout(() => { + expect(controller.state).toStrictEqual({ + interfaces: { + '2': { + contentType: ContentType.Dialog, + }, + }, + }); + }, 1); + + controller.destroy(); + }); + describe('constructor', () => { it('persists notification interfaces', () => { const rootMessenger = getRootSnapInterfaceControllerMessenger(); @@ -68,6 +108,8 @@ describe('SnapInterfaceController', () => { }, }, }); + + controller.destroy(); }); }); @@ -77,8 +119,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -115,6 +156,7 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(getJsxElementFromComponent(components)); expect(state).toStrictEqual({ foo: { bar: null } }); + controller.destroy(); }); it('can create a new interface from JSX', async () => { @@ -122,8 +164,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -165,6 +206,7 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(element); expect(state).toStrictEqual({ foo: { bar: null } }); + controller.destroy(); }); it('supports providing interface context', async () => { @@ -172,8 +214,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -200,6 +241,7 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(element); expect(context).toStrictEqual({ foo: 'bar' }); + controller.destroy(); }); it('supports providing an interface content type', async () => { @@ -207,8 +249,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -235,6 +276,7 @@ describe('SnapInterfaceController', () => { ); expect(contentType).toStrictEqual(ContentType.Notification); + controller.destroy(); }); it('throws if interface context is too large', async () => { @@ -247,8 +289,7 @@ describe('SnapInterfaceController', () => { .mockReturnValueOnce(1) .mockReturnValueOnce(10_000_000); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -268,6 +309,7 @@ describe('SnapInterfaceController', () => { { foo: 'a'.repeat(1_000_000) }, ), ).rejects.toThrow('A Snap interface context may not be larger than 1 MB'); + controller.destroy(); }); it('throws if a link is on the phishing list', async () => { @@ -287,8 +329,7 @@ describe('SnapInterfaceController', () => { () => ({ result: true, type: 'all' }), ); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -318,6 +359,8 @@ describe('SnapInterfaceController', () => { 'PhishingController:testOrigin', 'https://foo.bar/', ); + + controller.destroy(); }); it('throws if a JSX link is on the phishing list', async () => { @@ -337,8 +380,7 @@ describe('SnapInterfaceController', () => { () => ({ result: true, type: 'all' }), ); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -368,6 +410,7 @@ describe('SnapInterfaceController', () => { 'PhishingController:testOrigin', 'https://foo.bar/', ); + controller.destroy(); }); it('throws if UI content is too large', async () => { @@ -379,8 +422,7 @@ describe('SnapInterfaceController', () => { jest.mocked(getJsonSizeUnsafe).mockReturnValueOnce(11_000_000); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -393,6 +435,7 @@ describe('SnapInterfaceController', () => { components, ), ).rejects.toThrow('A Snap UI may not be larger than 10 MB.'); + controller.destroy(); }); it('throws if JSX UI content is too large', async () => { @@ -404,8 +447,7 @@ describe('SnapInterfaceController', () => { jest.mocked(getJsonSizeUnsafe).mockReturnValueOnce(11_000_000); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -422,6 +464,7 @@ describe('SnapInterfaceController', () => { element, ), ).rejects.toThrow('A Snap UI may not be larger than 10 MB.'); + controller.destroy(); }); it('throws if text content is too large', async () => { @@ -431,8 +474,7 @@ describe('SnapInterfaceController', () => { false, ); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -445,6 +487,7 @@ describe('SnapInterfaceController', () => { components, ), ).rejects.toThrow('The text in a Snap UI may not be larger than 50 kB.'); + controller.destroy(); }); }); @@ -454,8 +497,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -477,6 +519,7 @@ describe('SnapInterfaceController', () => { ); expect(content).toStrictEqual(getJsxElementFromComponent(components)); + controller.destroy(); }); it('throws if the snap requesting the interface is not the one that created it', async () => { @@ -484,8 +527,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -507,6 +549,7 @@ describe('SnapInterfaceController', () => { id, ), ).toThrow(`Interface not created by foo.`); + controller.destroy(); }); it('throws if the interface does not exist', () => { @@ -514,8 +557,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -526,6 +568,7 @@ describe('SnapInterfaceController', () => { 'test', ), ).toThrow(`Interface with id 'test' not found.`); + controller.destroy(); }); }); @@ -535,8 +578,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -571,6 +613,7 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(getJsxElementFromComponent(newContent)); expect(state).toStrictEqual({ foo: { baz: null } }); + controller.destroy(); }); it('can update an interface using JSX', async () => { @@ -578,8 +621,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -620,6 +662,7 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(newElement); expect(state).toStrictEqual({ foo: { baz: null } }); + controller.destroy(); }); it('can update an interface and context', async () => { @@ -627,8 +670,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -674,6 +716,7 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(getJsxElementFromComponent(newContent)); expect(state).toStrictEqual({ foo: { baz: null } }); expect(interfaceContext).toStrictEqual(newContext); + controller.destroy(); }); it('does not replace context if none is provided', async () => { @@ -681,8 +724,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -725,6 +767,7 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(getJsxElementFromComponent(newContent)); expect(state).toStrictEqual({ foo: { baz: null } }); expect(interfaceContext).toStrictEqual(context); + controller.destroy(); }); it('throws if a link is on the phishing list', async () => { @@ -744,8 +787,7 @@ describe('SnapInterfaceController', () => { () => ({ result: true, type: 'all' }), ); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -787,6 +829,7 @@ describe('SnapInterfaceController', () => { 'PhishingController:testOrigin', 'https://foo.bar/', ); + controller.destroy(); }); it('throws if a JSX link is on the phishing list', async () => { @@ -806,8 +849,7 @@ describe('SnapInterfaceController', () => { () => ({ result: true, type: 'all' }), ); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -853,6 +895,7 @@ describe('SnapInterfaceController', () => { 'PhishingController:testOrigin', 'https://foo.bar/', ); + controller.destroy(); }); it('throws if UI content is too large', async () => { @@ -865,8 +908,7 @@ describe('SnapInterfaceController', () => { .mockReturnValueOnce(1) .mockReturnValueOnce(11_000_000); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -891,6 +933,7 @@ describe('SnapInterfaceController', () => { newContent, ), ).rejects.toThrow('A Snap UI may not be larger than 10 MB.'); + controller.destroy(); }); it('throws if JSX UI content is too large', async () => { @@ -903,8 +946,7 @@ describe('SnapInterfaceController', () => { .mockReturnValueOnce(1) .mockReturnValueOnce(11_000_000); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -936,6 +978,7 @@ describe('SnapInterfaceController', () => { newElement, ), ).rejects.toThrow('A Snap UI may not be larger than 10 MB.'); + controller.destroy(); }); it('throws if text content is too large', async () => { @@ -943,8 +986,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -969,6 +1011,7 @@ describe('SnapInterfaceController', () => { newContent, ), ).rejects.toThrow('The text in a Snap UI may not be larger than 50 kB.'); + controller.destroy(); }); it('throws if the interface does not exist', async () => { @@ -976,8 +1019,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -991,6 +1033,7 @@ describe('SnapInterfaceController', () => { content, ), ).rejects.toThrow("Interface with id 'foo' not found."); + controller.destroy(); }); it('throws if the interface is updated by another snap', async () => { @@ -998,8 +1041,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1024,6 +1066,7 @@ describe('SnapInterfaceController', () => { newContent, ), ).rejects.toThrow('Interface not created by foo.'); + controller.destroy(); }); }); @@ -1033,8 +1076,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1061,6 +1103,7 @@ describe('SnapInterfaceController', () => { ); expect(state).toStrictEqual(newState); + controller.destroy(); }); it('updates the interface state with a file', async () => { @@ -1068,8 +1111,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1111,6 +1153,7 @@ describe('SnapInterfaceController', () => { ); expect(state).toStrictEqual(newState); + controller.destroy(); }); }); @@ -1120,8 +1163,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1142,6 +1184,7 @@ describe('SnapInterfaceController', () => { id, ), ).toThrow(`Interface with id '${id}' not found.`); + controller.destroy(); }); }); @@ -1151,8 +1194,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1188,6 +1230,7 @@ describe('SnapInterfaceController', () => { ); expect(await approvalPromise).toBe('bar'); + controller.destroy(); }); it('throws if the interface does not exist', async () => { @@ -1195,8 +1238,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1220,6 +1262,7 @@ describe('SnapInterfaceController', () => { 'bar', ), ).rejects.toThrow(`Interface with id 'foo' not found.`); + controller.destroy(); }); it('throws if the interface is resolved by another snap', async () => { @@ -1227,8 +1270,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1265,6 +1307,7 @@ describe('SnapInterfaceController', () => { 'bar', ), ).rejects.toThrow('Interface not created by baz.'); + controller.destroy(); }); it('throws if the interface has no approval request', async () => { @@ -1272,8 +1315,7 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - /* eslint-disable-next-line no-new */ - new SnapInterfaceController({ + const controller = new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1305,6 +1347,7 @@ describe('SnapInterfaceController', () => { 'bar', ), ).rejects.toThrow(`Approval request with id '${id}' not found.`); + controller.destroy(); }); }); }); diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts index aedabd2b8a..ccd5412f4b 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts @@ -8,11 +8,6 @@ import type { ControllerStateChangeEvent, } from '@metamask/base-controller'; import { BaseController } from '@metamask/base-controller'; -import type { - INotification, - NotificationListUpdatedEvent, -} from '@metamask/notification-services-controller/notification-services'; -import { TRIGGER_TYPES } from '@metamask/notification-services-controller/notification-services'; import type { MaybeUpdateState, TestOrigin, @@ -99,6 +94,11 @@ export type SnapInterfaceControllerStateChangeEvent = SnapInterfaceControllerState >; +type NotificationListUpdatedEvent = { + type: `${'NotificationServicesController'}:notificationsListUpdated`; + payload: [Record[]]; +}; + export type SnapInterfaceControllerEvents = | SnapInterfaceControllerStateChangeEvent | NotificationListUpdatedEvent; @@ -161,6 +161,9 @@ export class SnapInterfaceController extends BaseController< state: { interfaces: {}, ...state }, }); + this._onNotificationsListUpdated = + this._onNotificationsListUpdated.bind(this); + this.messagingSystem.subscribe( 'NotificationServicesController:notificationsListUpdated', /* eslint-disable @typescript-eslint/unbound-method */ @@ -423,20 +426,15 @@ export class SnapInterfaceController extends BaseController< ); } - _onNotificationsListUpdated(notificationsList: INotification[]) { + _onNotificationsListUpdated(notificationsList: Record[]) { const snapNotificationsWithInterface = notificationsList.filter( (notification) => { - return ( - notification.type === TRIGGER_TYPES.SNAP && - // @ts-expect-error detailedView can be undefined here, type needs to be updated in the core repo - notification.data?.detailedView - ); + return notification.type === 'snap' && notification.data?.detailedView; }, ); const interfaceIdSet = new Set( snapNotificationsWithInterface.map( - // @ts-expect-error detailedView can be undefined here, type needs to be updated in the core repo (notification) => notification.data.detailedView.interfaceId, ), ); diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index 52978aacaf..4bea263330 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -45,6 +45,7 @@ import type { import type { SnapInterfaceControllerActions, SnapInterfaceControllerAllowedActions, + SnapInterfaceControllerEvents, StoredInterface, } from '../interface/SnapInterfaceController'; import type { @@ -739,7 +740,7 @@ export const getRestrictedSnapsRegistryControllerMessenger = ( export const getRootSnapInterfaceControllerMessenger = () => { const messenger = new MockControllerMessenger< SnapInterfaceControllerActions | SnapInterfaceControllerAllowedActions, - never + SnapInterfaceControllerEvents >(); jest.spyOn(messenger, 'call'); @@ -756,7 +757,7 @@ export const getRestrictedSnapInterfaceControllerMessenger = ( const snapInterfaceControllerMessenger = messenger.getRestricted< 'SnapInterfaceController', SnapInterfaceControllerAllowedActions['type'], - never + SnapInterfaceControllerEvents['type'] >({ name: 'SnapInterfaceController', allowedActions: [ @@ -765,7 +766,10 @@ export const getRestrictedSnapInterfaceControllerMessenger = ( 'ApprovalController:hasRequest', 'ApprovalController:acceptRequest', ], - allowedEvents: [], + allowedEvents: [ + 'NotificationServicesController:notificationsListUpdated', + 'SnapInterfaceController:stateChange', + ], }); if (mocked) { diff --git a/yarn.lock b/yarn.lock index 408345b959..23ca006e7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2636,23 +2636,6 @@ __metadata: languageName: node linkType: hard -"@contentful/rich-text-html-renderer@npm:^16.5.2": - version: 16.6.10 - resolution: "@contentful/rich-text-html-renderer@npm:16.6.10" - dependencies: - "@contentful/rich-text-types": "npm:^16.8.5" - escape-html: "npm:^1.0.3" - checksum: 10/fdf5121524355c96cd8a4628b2cc50a61b9b9d37da167dd983cc8fa24fa822427809d00064ebca9b85b8f40d92719f0b86f0e4ea4eaa8d078656ed56c51df6bb - languageName: node - linkType: hard - -"@contentful/rich-text-types@npm:^16.8.5": - version: 16.8.5 - resolution: "@contentful/rich-text-types@npm:16.8.5" - checksum: 10/7c1ec7088cc39bbd8f5edc14867ed3d59d06a55f2c92e6c62913aa2312056e920528e6ca6d59780b32c6fe2ab89c429b9f0bce9a52f456be7260f458527ccc3b - languageName: node - linkType: hard - "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -3317,549 +3300,6 @@ __metadata: languageName: node linkType: hard -"@firebase/analytics-compat@npm:0.2.14": - version: 0.2.14 - resolution: "@firebase/analytics-compat@npm:0.2.14" - dependencies: - "@firebase/analytics": "npm:0.10.8" - "@firebase/analytics-types": "npm:0.8.2" - "@firebase/component": "npm:0.6.9" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/0e368159d24223076b488b27308c11e5ef50456aff49fc58e1f66616228021c61e60c3299f63ce52ddc2f7099d803e9048bc28cd952cf5c302917002c03c85ee - languageName: node - linkType: hard - -"@firebase/analytics-types@npm:0.8.2": - version: 0.8.2 - resolution: "@firebase/analytics-types@npm:0.8.2" - checksum: 10/297fb7becbc51950c7de5809fed896c391d1e87b4d8bb4bf88f4e8760b2e32f903a7dd8e92de4424b49c4e2ecb60a44d49e2f9c68ac3f7ffe3a0194f78910392 - languageName: node - linkType: hard - -"@firebase/analytics@npm:0.10.8": - version: 0.10.8 - resolution: "@firebase/analytics@npm:0.10.8" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/installations": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/152ddaf68146f02baa7060d34426c25ec13890a53942ffa2db09faa148bef35f59ee9810e6fb8f561fb3d115b71d1fb9fb111d2a0f0199aa510220782557c765 - languageName: node - linkType: hard - -"@firebase/app-check-compat@npm:0.3.15": - version: 0.3.15 - resolution: "@firebase/app-check-compat@npm:0.3.15" - dependencies: - "@firebase/app-check": "npm:0.8.8" - "@firebase/app-check-types": "npm:0.5.2" - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/ae541d324d5f91dbb7b479855d3380c4fe73e365013b80973a54620405093e6fd2f8e418549155b3a527530472a19b6edf6df1481a708f823eba42e376105b28 - languageName: node - linkType: hard - -"@firebase/app-check-interop-types@npm:0.3.2": - version: 0.3.2 - resolution: "@firebase/app-check-interop-types@npm:0.3.2" - checksum: 10/3effe656a4762c541838f4bde91b4498e51d48389046b930dc3dbb012e54b6ab0727f7c68a3e94198f633d57833346fc337a0847b6b03d2407030e1489d466fe - languageName: node - linkType: hard - -"@firebase/app-check-types@npm:0.5.2": - version: 0.5.2 - resolution: "@firebase/app-check-types@npm:0.5.2" - checksum: 10/2b33a7adfb7b6ebf5423940bf0af5909df69bf2d6184e12e989f6c76062077be16c31193795349862b4f8aab6b3059806b732a92995cae30fd77419f19a86c6e - languageName: node - linkType: hard - -"@firebase/app-check@npm:0.8.8": - version: 0.8.8 - resolution: "@firebase/app-check@npm:0.8.8" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/a3676f2143c8e438d7e8ac11bb163af30880f6ce6acc5cc54cfcc214b8efd5dabce14c040626f8a64a3967db144b99834f1108c2076a0eae8a6baf864b5a3d77 - languageName: node - linkType: hard - -"@firebase/app-compat@npm:0.2.43": - version: 0.2.43 - resolution: "@firebase/app-compat@npm:0.2.43" - dependencies: - "@firebase/app": "npm:0.10.13" - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - checksum: 10/e27340dbc9804ffd0d469cc1fa919cd61b6e04fe96599d14414aa06c3dcbe75b23c324f0bedfff4dbd5d9b829b8dde5a2e8b5464f1f686d66f9c00971d9d4c56 - languageName: node - linkType: hard - -"@firebase/app-types@npm:0.9.2": - version: 0.9.2 - resolution: "@firebase/app-types@npm:0.9.2" - checksum: 10/566b3714a4d7e8180514258e4b1549bf5b28ae0383b4ff53d3532a45e114048afdd27c1fef8688d871dd9e5ad5307e749776e23f094122655ac6b0fb550eb11a - languageName: node - linkType: hard - -"@firebase/app@npm:0.10.13": - version: 0.10.13 - resolution: "@firebase/app@npm:0.10.13" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - idb: "npm:7.1.1" - tslib: "npm:^2.1.0" - checksum: 10/54ec64b3a992c2f30c800fb5638bf586e7e7f351899887c701d5f946ad8ca445d8c1d3024007b7939a7e6ae29a51d90567552a863323594dc6fca22f1e811e0b - languageName: node - linkType: hard - -"@firebase/auth-compat@npm:0.5.14": - version: 0.5.14 - resolution: "@firebase/auth-compat@npm:0.5.14" - dependencies: - "@firebase/auth": "npm:1.7.9" - "@firebase/auth-types": "npm:0.12.2" - "@firebase/component": "npm:0.6.9" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - undici: "npm:6.19.7" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/85d5259e7b04b14b5d02dc1fb19b015d742c594c14138f33f13146ed9f6caa7ed9d19d65bb99aaca57e70ffd2a491e520d8638eadefbd00f839d37ef972cbbda - languageName: node - linkType: hard - -"@firebase/auth-interop-types@npm:0.2.3": - version: 0.2.3 - resolution: "@firebase/auth-interop-types@npm:0.2.3" - checksum: 10/e55b8ded6bd1a5e6a2845c9c7ed520bb9a8a76e4ddf90249bf685986ac7b1fb079be2fa4edcb6a3aa81d1d56870a470eadcd5a8f20b797dccd803d72ed4c80aa - languageName: node - linkType: hard - -"@firebase/auth-types@npm:0.12.2": - version: 0.12.2 - resolution: "@firebase/auth-types@npm:0.12.2" - peerDependencies: - "@firebase/app-types": 0.x - "@firebase/util": 1.x - checksum: 10/f55449381de8e2a24ffaf19f12b5c4a093c8323034253ea7a5f7afc946327d20b09f32a483c12960862a1c4814645ea80bc4343f0a9f22db5dc048ca82773132 - languageName: node - linkType: hard - -"@firebase/auth@npm:1.7.9": - version: 1.7.9 - resolution: "@firebase/auth@npm:1.7.9" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - undici: "npm:6.19.7" - peerDependencies: - "@firebase/app": 0.x - "@react-native-async-storage/async-storage": ^1.18.1 - peerDependenciesMeta: - "@react-native-async-storage/async-storage": - optional: true - checksum: 10/010013ec339c9ef7b4d9278c6cacfd8e2eb3282f27a3e4e89c42a5968955976a26277421f34fda3e9400409a22a61f632bcc03e713b3f39d71e4777bc003165d - languageName: node - linkType: hard - -"@firebase/component@npm:0.6.9": - version: 0.6.9 - resolution: "@firebase/component@npm:0.6.9" - dependencies: - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - checksum: 10/76c865d640e4b24a0e50876ecdc0e1199df38af562131a937b5a4bac924d61b6933339afb7906881dca509f38f3b0c511cd6b5008e061424c61b20876de9531e - languageName: node - linkType: hard - -"@firebase/data-connect@npm:0.1.0": - version: 0.1.0 - resolution: "@firebase/data-connect@npm:0.1.0" - dependencies: - "@firebase/auth-interop-types": "npm:0.2.3" - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/20dac7c4755a0dde17abea0c99b41e96c9f7eea6ea39c36fd85f3f5554991b718a25b4bc1f92850208ec1f0e3b1ee584b1cf1913c4ad6c41643655682c06a2b0 - languageName: node - linkType: hard - -"@firebase/database-compat@npm:1.0.8": - version: 1.0.8 - resolution: "@firebase/database-compat@npm:1.0.8" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/database": "npm:1.0.8" - "@firebase/database-types": "npm:1.0.5" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - checksum: 10/28389efcc87da77b822cb27c31707824fe98e7b0a3bf9cbf2b0c0fccd9edd72e2681a9467b76b120281464dbfc814852ebca63d99a385a9cb68fb55c7b334105 - languageName: node - linkType: hard - -"@firebase/database-types@npm:1.0.5": - version: 1.0.5 - resolution: "@firebase/database-types@npm:1.0.5" - dependencies: - "@firebase/app-types": "npm:0.9.2" - "@firebase/util": "npm:1.10.0" - checksum: 10/bdf667da0369dce8623987fc01cad8db09cfe1895130f69ab581d34a0ee043ca6113c32457629147ae1441a934d985ede9d7cbe104ac346de6d0c21629903a8b - languageName: node - linkType: hard - -"@firebase/database@npm:1.0.8": - version: 1.0.8 - resolution: "@firebase/database@npm:1.0.8" - dependencies: - "@firebase/app-check-interop-types": "npm:0.3.2" - "@firebase/auth-interop-types": "npm:0.2.3" - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - faye-websocket: "npm:0.11.4" - tslib: "npm:^2.1.0" - checksum: 10/adb199a6ad7866b418e8b319cc505e108bfc8200b5406f21857706df0849d4e5982a1b0e44e07001821edebef73c4dfffc7f96fb77a2cff10bb9ac26f17d40c3 - languageName: node - linkType: hard - -"@firebase/firestore-compat@npm:0.3.38": - version: 0.3.38 - resolution: "@firebase/firestore-compat@npm:0.3.38" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/firestore": "npm:4.7.3" - "@firebase/firestore-types": "npm:3.0.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/de9e92b5ac612ea73322407b65b1d90067f7138d2159bcfd2400535d09968ea8c8b44956282172129eca78bf951f74a6991394b2634913458927570bb4fa8cd8 - languageName: node - linkType: hard - -"@firebase/firestore-types@npm:3.0.2": - version: 3.0.2 - resolution: "@firebase/firestore-types@npm:3.0.2" - peerDependencies: - "@firebase/app-types": 0.x - "@firebase/util": 1.x - checksum: 10/81e91f836a026ecb70937407ca8699add7abb5b050d8815620cde97c3eec3f78f7dfbb366225758909f0df31d9f21e98a84ba62701bd27ee38b2609898c11acd - languageName: node - linkType: hard - -"@firebase/firestore@npm:4.7.3": - version: 4.7.3 - resolution: "@firebase/firestore@npm:4.7.3" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - "@firebase/webchannel-wrapper": "npm:1.0.1" - "@grpc/grpc-js": "npm:~1.9.0" - "@grpc/proto-loader": "npm:^0.7.8" - tslib: "npm:^2.1.0" - undici: "npm:6.19.7" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/f46a6e3c03eadfa970d8ecc17627d0d696931a19092fcf5be4b2056a209da3691be0bd040a11d333d26c15fd14329ce0fb5dfc32bf2cfa530a4d035b45ef4edf - languageName: node - linkType: hard - -"@firebase/functions-compat@npm:0.3.14": - version: 0.3.14 - resolution: "@firebase/functions-compat@npm:0.3.14" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/functions": "npm:0.11.8" - "@firebase/functions-types": "npm:0.6.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/a8d6cbcdc646d78adecfcdc1f8fa14a5d9af2394dd69cac00c6826106b923e01d246c67fb7e09025ca7cfb876f8d5df97240cc056c64ccee8899ca5f17178a6c - languageName: node - linkType: hard - -"@firebase/functions-types@npm:0.6.2": - version: 0.6.2 - resolution: "@firebase/functions-types@npm:0.6.2" - checksum: 10/5b8733f9d4bd85a617d35dd10ce296d9ec0490494e584697c4eda8098ff1e865607d7880b84194e86c35d438bbcd714977c111180502d0d1b6b2da1cde1b37ca - languageName: node - linkType: hard - -"@firebase/functions@npm:0.11.8": - version: 0.11.8 - resolution: "@firebase/functions@npm:0.11.8" - dependencies: - "@firebase/app-check-interop-types": "npm:0.3.2" - "@firebase/auth-interop-types": "npm:0.2.3" - "@firebase/component": "npm:0.6.9" - "@firebase/messaging-interop-types": "npm:0.2.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - undici: "npm:6.19.7" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/44f3e42df189f3f3cb3c366b38e93a0ffdfaa1a7b3f6dba624bcd9a7cda3d3271df66f2769b7cbe7e1e5ff01bf6ab3bef6c1e1e15c6646e34514d1e2ebb60555 - languageName: node - linkType: hard - -"@firebase/installations-compat@npm:0.2.9": - version: 0.2.9 - resolution: "@firebase/installations-compat@npm:0.2.9" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/installations": "npm:0.6.9" - "@firebase/installations-types": "npm:0.5.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/919e1a4f4b63f5fe757a3c9cefb4a36cbab92deb4a6e15f249c94d6e80d1c6d37e5e384a460af8c17fc88e3091594bf43d036c88b704516c279b5ab8401977e1 - languageName: node - linkType: hard - -"@firebase/installations-types@npm:0.5.2": - version: 0.5.2 - resolution: "@firebase/installations-types@npm:0.5.2" - peerDependencies: - "@firebase/app-types": 0.x - checksum: 10/2e795280c299d644b8c8e3fdfa5c6f20cb367dd3b7df32317211f84393fa169b33dee0cbed28de407f3b22dc8f1fb2f7a11ae5a373f8082cc570ef61ef6b91ba - languageName: node - linkType: hard - -"@firebase/installations@npm:0.6.9": - version: 0.6.9 - resolution: "@firebase/installations@npm:0.6.9" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/util": "npm:1.10.0" - idb: "npm:7.1.1" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/349c8b7e877b002fb29f274f4d239fbca4c2c266ccb66ecfb5f1762f973a7fe1be99cc3346184d1230e6e35feb2b6f9e8b7169479fa0018b53e4a83837848619 - languageName: node - linkType: hard - -"@firebase/logger@npm:0.4.2": - version: 0.4.2 - resolution: "@firebase/logger@npm:0.4.2" - dependencies: - tslib: "npm:^2.1.0" - checksum: 10/961b4605220c0a56c5f3ccf4e6049e44c27303c1ca998c6fa1d19de785c76d93e3b1a3da455e9f40655711345d8d779912366e4f369d93eda8d08c407cc5b140 - languageName: node - linkType: hard - -"@firebase/messaging-compat@npm:0.2.12": - version: 0.2.12 - resolution: "@firebase/messaging-compat@npm:0.2.12" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/messaging": "npm:0.12.12" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/0437ba6b24327d9eb02dc87ba61146fbb9720491ad671dc554438ac87e162d5fb154c704400d55c87ce01dd5aeedada9d0367fd114d840ead0d07802475eaa60 - languageName: node - linkType: hard - -"@firebase/messaging-interop-types@npm:0.2.2": - version: 0.2.2 - resolution: "@firebase/messaging-interop-types@npm:0.2.2" - checksum: 10/547f8ebf2c5a8dcbc484991b97d76bd3dc3eb4bd9d4e6ea2ffc652097c7065d92dc68d389ddb19fba41e0ce3b5f4cd757ed22f96b4744801149b0f8dbf323af7 - languageName: node - linkType: hard - -"@firebase/messaging@npm:0.12.12": - version: 0.12.12 - resolution: "@firebase/messaging@npm:0.12.12" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/installations": "npm:0.6.9" - "@firebase/messaging-interop-types": "npm:0.2.2" - "@firebase/util": "npm:1.10.0" - idb: "npm:7.1.1" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/a00125489085782faf189ad42f75bed108c6632b9d198d114e0a8ce28d89f9455b4f78ff8f7d24d4a86ad13e1e14e0f17fa2ff3605c6dd0c8ff1b65fef23ce3d - languageName: node - linkType: hard - -"@firebase/performance-compat@npm:0.2.9": - version: 0.2.9 - resolution: "@firebase/performance-compat@npm:0.2.9" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/performance": "npm:0.6.9" - "@firebase/performance-types": "npm:0.2.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/bc4e8b0208c9bc603518e1388713ec80658ee109c6af80d429479447ccb85e8e831269383233c379ed66bf37469d13f5c234074d0c0c9e7e69e909be5fdeca4f - languageName: node - linkType: hard - -"@firebase/performance-types@npm:0.2.2": - version: 0.2.2 - resolution: "@firebase/performance-types@npm:0.2.2" - checksum: 10/d25ae06cb75ab6b44ffacf7affadc1f651881f283e58381c444eb63b62dfb74c33c544ab89843518ec1d15367ba7c4343b4d6b22de1f1df35126a1283baa578d - languageName: node - linkType: hard - -"@firebase/performance@npm:0.6.9": - version: 0.6.9 - resolution: "@firebase/performance@npm:0.6.9" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/installations": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/d682d0b1e342ed3eda1a5ddab39c8ddac33afc9edb2c7335a2f9a28eb8c268b975bbf450a3bad5443138edebaf2aa731dca0b774bcf3211a6dc215b35d86d849 - languageName: node - linkType: hard - -"@firebase/remote-config-compat@npm:0.2.9": - version: 0.2.9 - resolution: "@firebase/remote-config-compat@npm:0.2.9" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/remote-config": "npm:0.4.9" - "@firebase/remote-config-types": "npm:0.3.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/a6db7509512d8d22b7ddf1127c741715e379e04e5b3246372bb0302d7c84afb421a94550adebecddcce5def115d61729a9580940dce6e65f8d77f9af30f69fe1 - languageName: node - linkType: hard - -"@firebase/remote-config-types@npm:0.3.2": - version: 0.3.2 - resolution: "@firebase/remote-config-types@npm:0.3.2" - checksum: 10/6c91599c653825708aba9fe9e4562997f108c3e4f3eaf5d188f31c859a6ad013414aa7a213b6b021b68049dd0dd57158546dbc9fb64384652274ef7f57ce7d7d - languageName: node - linkType: hard - -"@firebase/remote-config@npm:0.4.9": - version: 0.4.9 - resolution: "@firebase/remote-config@npm:0.4.9" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/installations": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/f14189f38c8cf75db16bf8b85dd004486b1dd8242f62d697c716fa85cd32928aed549ccea8c632a528870a424fc7f04f1132a14b3b099276cd7696c78e644b28 - languageName: node - linkType: hard - -"@firebase/storage-compat@npm:0.3.12": - version: 0.3.12 - resolution: "@firebase/storage-compat@npm:0.3.12" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/storage": "npm:0.13.2" - "@firebase/storage-types": "npm:0.8.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app-compat": 0.x - checksum: 10/4eea49a57f1d7537da697e5ff8b4e035ff1af69e416e7eab14485753c39c25eaa5a71bd2bafba0985ac6a7ce803f98f2f2f83c613c78c8f74bce286e3259b8ec - languageName: node - linkType: hard - -"@firebase/storage-types@npm:0.8.2": - version: 0.8.2 - resolution: "@firebase/storage-types@npm:0.8.2" - peerDependencies: - "@firebase/app-types": 0.x - "@firebase/util": 1.x - checksum: 10/e00716932370d2004dc9f7ef6d7e3aff72305b91569fa6ec15e8bc2ec784b03a150391e8be2c063234edbbfda7796da915d48e26ce2f1f7c5d3343acd39afd99 - languageName: node - linkType: hard - -"@firebase/storage@npm:0.13.2": - version: 0.13.2 - resolution: "@firebase/storage@npm:0.13.2" - dependencies: - "@firebase/component": "npm:0.6.9" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - undici: "npm:6.19.7" - peerDependencies: - "@firebase/app": 0.x - checksum: 10/d887f80cf95ef5daa80ffb2e6d564d25abb8a3e84099bee9730c95082597a12028bbf73bfe66fca2df3cdf04eaadea8e9d74ec0a826f946bc8f002293a9983ea - languageName: node - linkType: hard - -"@firebase/util@npm:1.10.0": - version: 1.10.0 - resolution: "@firebase/util@npm:1.10.0" - dependencies: - tslib: "npm:^2.1.0" - checksum: 10/eb161f1c6294ff097f3c40236820e9e6e29cd6582e5e1254148157143272493580535ee2cb9e7c6055d3909b3ef39d8b64086895b071c665827acb66742b63eb - languageName: node - linkType: hard - -"@firebase/vertexai-preview@npm:0.0.4": - version: 0.0.4 - resolution: "@firebase/vertexai-preview@npm:0.0.4" - dependencies: - "@firebase/app-check-interop-types": "npm:0.3.2" - "@firebase/component": "npm:0.6.9" - "@firebase/logger": "npm:0.4.2" - "@firebase/util": "npm:1.10.0" - tslib: "npm:^2.1.0" - peerDependencies: - "@firebase/app": 0.x - "@firebase/app-types": 0.x - checksum: 10/8ec48d81f48aebdcc63b65d802c67bf36880f256e5c2f5f3b152dc91c8c0e924053fba2fac5218716612f8ee720b25d0822337a214f16f5b7e51ce0247dfc4e5 - languageName: node - linkType: hard - -"@firebase/webchannel-wrapper@npm:1.0.1": - version: 1.0.1 - resolution: "@firebase/webchannel-wrapper@npm:1.0.1" - checksum: 10/22fc7e1e6dd36ab7c13f3a6c1ff51f4d405304424dc323cb146109e7a3ab3b592e2ddb29f53197ee5719a8448cdedb98d9e86a080f9365e389f8429b1c6555c2 - languageName: node - linkType: hard - "@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -3867,30 +3307,6 @@ __metadata: languageName: node linkType: hard -"@grpc/grpc-js@npm:~1.9.0": - version: 1.9.15 - resolution: "@grpc/grpc-js@npm:1.9.15" - dependencies: - "@grpc/proto-loader": "npm:^0.7.8" - "@types/node": "npm:>=12.12.47" - checksum: 10/edd45c5970046ebb1bb54856f22a41186742c77dfb7e5182ca615f690f1a320af3abeef553d8924812d56911157a04882c7d264c2de64f326f8df7d473c47b2a - languageName: node - linkType: hard - -"@grpc/proto-loader@npm:^0.7.8": - version: 0.7.13 - resolution: "@grpc/proto-loader@npm:0.7.13" - dependencies: - lodash.camelcase: "npm:^4.3.0" - long: "npm:^5.0.0" - protobufjs: "npm:^7.2.5" - yargs: "npm:^17.7.2" - bin: - proto-loader-gen-types: build/bin/proto-loader-gen-types.js - checksum: 10/7e2d842c2061cbaf6450c71da0077263be3bab165454d5c8a3e1ae4d3c6d2915f02fd27da63ff01f05e127b1221acd40705273f5d29303901e60514e852992f4 - languageName: node - linkType: hard - "@humanwhocodes/config-array@npm:^0.11.6": version: 0.11.7 resolution: "@humanwhocodes/config-array@npm:0.11.7" @@ -4500,7 +3916,7 @@ __metadata: languageName: node linkType: hard -"@metamask/base-controller@npm:^7.0.0, @metamask/base-controller@npm:^7.0.1": +"@metamask/base-controller@npm:^7.0.0": version: 7.0.1 resolution: "@metamask/base-controller@npm:7.0.1" dependencies: @@ -4756,7 +4172,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.2.0, @metamask/controller-utils@npm:^11.4.0": +"@metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.2.0": version: 11.4.0 resolution: "@metamask/controller-utils@npm:11.4.0" dependencies: @@ -5873,25 +5289,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/notification-services-controller@npm:^0.12.0": - version: 0.12.0 - resolution: "@metamask/notification-services-controller@npm:0.12.0" - dependencies: - "@contentful/rich-text-html-renderer": "npm:^16.5.2" - "@metamask/base-controller": "npm:^7.0.1" - "@metamask/controller-utils": "npm:^11.4.0" - "@metamask/utils": "npm:^9.1.0" - bignumber.js: "npm:^9.1.2" - firebase: "npm:^10.11.0" - loglevel: "npm:^1.8.1" - uuid: "npm:^8.3.2" - peerDependencies: - "@metamask/keyring-controller": ^17.0.0 - "@metamask/profile-sync-controller": ^0.0.0 - checksum: 10/715cb7c656c6c5703d8582a9bcf368ce90fb6defcd5c8d71548b6271a04f2b761ce924d12091999bba5268a19d29ff7c84c87611dfdc8d599ce959f7b8b41b2f - languageName: node - linkType: hard - "@metamask/number-to-bn@npm:^1.7.1": version: 1.7.1 resolution: "@metamask/number-to-bn@npm:1.7.1" @@ -6328,7 +5725,6 @@ __metadata: "@metamask/eslint-config-typescript": "npm:^12.1.0" "@metamask/json-rpc-engine": "npm:^9.0.2" "@metamask/json-rpc-middleware-stream": "npm:^8.0.2" - "@metamask/notification-services-controller": "npm:^0.12.0" "@metamask/object-multiplex": "npm:^2.0.0" "@metamask/permission-controller": "npm:^11.0.0" "@metamask/phishing-controller": "npm:^12.0.2" @@ -7507,79 +6903,6 @@ __metadata: languageName: node linkType: hard -"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/aspromise@npm:1.1.2" - checksum: 10/8a938d84fe4889411296db66b29287bd61ea3c14c2d23e7a8325f46a2b8ce899857c5f038d65d7641805e6c1d06b495525c7faf00c44f85a7ee6476649034969 - languageName: node - linkType: hard - -"@protobufjs/base64@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/base64@npm:1.1.2" - checksum: 10/c71b100daeb3c9bdccab5cbc29495b906ba0ae22ceedc200e1ba49717d9c4ab15a6256839cebb6f9c6acae4ed7c25c67e0a95e734f612b258261d1a3098fe342 - languageName: node - linkType: hard - -"@protobufjs/codegen@npm:^2.0.4": - version: 2.0.4 - resolution: "@protobufjs/codegen@npm:2.0.4" - checksum: 10/c6ee5fa172a8464f5253174d3c2353ea520c2573ad7b6476983d9b1346f4d8f2b44aa29feb17a949b83c1816bc35286a5ea265ed9d8fdd2865acfa09668c0447 - languageName: node - linkType: hard - -"@protobufjs/eventemitter@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/eventemitter@npm:1.1.0" - checksum: 10/03af3e99f17ad421283d054c88a06a30a615922a817741b43ca1b13e7c6b37820a37f6eba9980fb5150c54dba6e26cb6f7b64a6f7d8afa83596fafb3afa218c3 - languageName: node - linkType: hard - -"@protobufjs/fetch@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/fetch@npm:1.1.0" - dependencies: - "@protobufjs/aspromise": "npm:^1.1.1" - "@protobufjs/inquire": "npm:^1.1.0" - checksum: 10/67ae40572ad536e4ef94269199f252c024b66e3059850906bdaee161ca1d75c73d04d35cd56f147a8a5a079f5808e342b99e61942c1dae15604ff0600b09a958 - languageName: node - linkType: hard - -"@protobufjs/float@npm:^1.0.2": - version: 1.0.2 - resolution: "@protobufjs/float@npm:1.0.2" - checksum: 10/634c2c989da0ef2f4f19373d64187e2a79f598c5fb7991afb689d29a2ea17c14b796b29725945fa34b9493c17fb799e08ac0a7ccaae460ee1757d3083ed35187 - languageName: node - linkType: hard - -"@protobufjs/inquire@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/inquire@npm:1.1.0" - checksum: 10/c09efa34a5465cb120775e1a482136f2340a58b4abce7e93d72b8b5a9324a0e879275016ef9fcd73d72a4731639c54f2bb755bb82f916e4a78892d1d840bb3d2 - languageName: node - linkType: hard - -"@protobufjs/path@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/path@npm:1.1.2" - checksum: 10/bb709567935fd385a86ad1f575aea98131bbd719c743fb9b6edd6b47ede429ff71a801cecbd64fc72deebf4e08b8f1bd8062793178cdaed3713b8d15771f9b83 - languageName: node - linkType: hard - -"@protobufjs/pool@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/pool@npm:1.1.0" - checksum: 10/b9c7047647f6af28e92aac54f6f7c1f7ff31b201b4bfcc7a415b2861528854fce3ec666d7e7e10fd744da905f7d4aef2205bbcc8944ca0ca7a82e18134d00c46 - languageName: node - linkType: hard - -"@protobufjs/utf8@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/utf8@npm:1.1.0" - checksum: 10/131e289c57534c1d73a0e55782d6751dd821db1583cb2f7f7e017c9d6747addaebe79f28120b2e0185395d990aad347fb14ffa73ef4096fa38508d61a0e64602 - languageName: node - linkType: hard - "@puppeteer/browsers@npm:1.7.0": version: 1.7.0 resolution: "@puppeteer/browsers@npm:1.7.0" @@ -8632,7 +7955,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0": +"@types/node@npm:*": version: 22.8.0 resolution: "@types/node@npm:22.8.0" dependencies: @@ -10552,13 +9875,6 @@ __metadata: languageName: node linkType: hard -"bignumber.js@npm:^9.1.2": - version: 9.1.2 - resolution: "bignumber.js@npm:9.1.2" - checksum: 10/d89b8800a987225d2c00dcbf8a69dc08e92aa0880157c851c287b307d31ceb2fc2acb0c62c3e3a3d42b6c5fcae9b004035f13eb4386e56d529d7edac18d5c9d8 - languageName: node - linkType: hard - "bin-links@npm:4.0.3": version: 4.0.3 resolution: "bin-links@npm:4.0.3" @@ -14322,7 +13638,7 @@ __metadata: languageName: node linkType: hard -"faye-websocket@npm:0.11.4, faye-websocket@npm:^0.11.3": +"faye-websocket@npm:^0.11.3": version: 0.11.4 resolution: "faye-websocket@npm:0.11.4" dependencies: @@ -14496,42 +13812,6 @@ __metadata: languageName: node linkType: hard -"firebase@npm:^10.11.0": - version: 10.14.1 - resolution: "firebase@npm:10.14.1" - dependencies: - "@firebase/analytics": "npm:0.10.8" - "@firebase/analytics-compat": "npm:0.2.14" - "@firebase/app": "npm:0.10.13" - "@firebase/app-check": "npm:0.8.8" - "@firebase/app-check-compat": "npm:0.3.15" - "@firebase/app-compat": "npm:0.2.43" - "@firebase/app-types": "npm:0.9.2" - "@firebase/auth": "npm:1.7.9" - "@firebase/auth-compat": "npm:0.5.14" - "@firebase/data-connect": "npm:0.1.0" - "@firebase/database": "npm:1.0.8" - "@firebase/database-compat": "npm:1.0.8" - "@firebase/firestore": "npm:4.7.3" - "@firebase/firestore-compat": "npm:0.3.38" - "@firebase/functions": "npm:0.11.8" - "@firebase/functions-compat": "npm:0.3.14" - "@firebase/installations": "npm:0.6.9" - "@firebase/installations-compat": "npm:0.2.9" - "@firebase/messaging": "npm:0.12.12" - "@firebase/messaging-compat": "npm:0.2.12" - "@firebase/performance": "npm:0.6.9" - "@firebase/performance-compat": "npm:0.2.9" - "@firebase/remote-config": "npm:0.4.9" - "@firebase/remote-config-compat": "npm:0.2.9" - "@firebase/storage": "npm:0.13.2" - "@firebase/storage-compat": "npm:0.3.12" - "@firebase/util": "npm:1.10.0" - "@firebase/vertexai-preview": "npm:0.0.4" - checksum: 10/1b2fd7b653f632d2bb0cf3b87e7eda0ae69d28b702a7de00d2a166c7985b8747ef9d15f7ac151847d242f91a1cb08c689d0a07197e8b80561ff1a679398bf9f8 - languageName: node - linkType: hard - "flat-cache@npm:^3.0.4": version: 3.0.4 resolution: "flat-cache@npm:3.0.4" @@ -15733,13 +15013,6 @@ __metadata: languageName: node linkType: hard -"idb@npm:7.1.1": - version: 7.1.1 - resolution: "idb@npm:7.1.1" - checksum: 10/8e33eaebf21055129864acb89932e0739b8c96788e559df24c253ce114d8c6deb977a3b30ea47a9bb8a2ae8a55964861c3df65f360d95745e341cee40d5c17f4 - languageName: node - linkType: hard - "idna-uts46-hx@npm:^2.3.1": version: 2.3.1 resolution: "idna-uts46-hx@npm:2.3.1" @@ -17749,13 +17022,6 @@ __metadata: languageName: node linkType: hard -"lodash.camelcase@npm:^4.3.0": - version: 4.3.0 - resolution: "lodash.camelcase@npm:4.3.0" - checksum: 10/c301cc379310441dc73cd6cebeb91fb254bea74e6ad3027f9346fc43b4174385153df420ffa521654e502fd34c40ef69ca4e7d40ee7129a99e06f306032bfc65 - languageName: node - linkType: hard - "lodash.clonedeep@npm:^4.5.0": version: 4.5.0 resolution: "lodash.clonedeep@npm:4.5.0" @@ -17883,14 +17149,14 @@ __metadata: languageName: node linkType: hard -"loglevel@npm:^1.6.0, loglevel@npm:^1.8.1": +"loglevel@npm:^1.6.0": version: 1.9.2 resolution: "loglevel@npm:1.9.2" checksum: 10/6153d8db308323f7ee20130bc40309e7a976c30a10379d8666b596d9c6441965c3e074c8d7ee3347fe5cfc059c0375b6f3e8a10b93d5b813cc5547f5aa412a29 languageName: node linkType: hard -"long@npm:^5.0.0, long@npm:^5.2.1": +"long@npm:^5.2.1": version: 5.2.3 resolution: "long@npm:5.2.3" checksum: 10/9167ec6947a825b827c30da169a7384eec6c0c9ec2f0b9c74da2e93d81159bbe39fb09c3f13dae9721d4b807ccfa09797a7dd1012f5d478e3e33ca3c78b608e6 @@ -20019,26 +19285,6 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:^7.2.5": - version: 7.4.0 - resolution: "protobufjs@npm:7.4.0" - dependencies: - "@protobufjs/aspromise": "npm:^1.1.2" - "@protobufjs/base64": "npm:^1.1.2" - "@protobufjs/codegen": "npm:^2.0.4" - "@protobufjs/eventemitter": "npm:^1.1.0" - "@protobufjs/fetch": "npm:^1.1.0" - "@protobufjs/float": "npm:^1.0.2" - "@protobufjs/inquire": "npm:^1.1.0" - "@protobufjs/path": "npm:^1.1.2" - "@protobufjs/pool": "npm:^1.1.0" - "@protobufjs/utf8": "npm:^1.1.0" - "@types/node": "npm:>=13.7.0" - long: "npm:^5.0.0" - checksum: 10/408423506610f70858d7593632f4a6aa4f05796c90fd632be9b9252457c795acc71aa6d3b54bb7f48a890141728fee4ca3906723ccea6c202ad71f21b3879b8b - languageName: node - linkType: hard - "proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -23147,13 +22393,6 @@ __metadata: languageName: node linkType: hard -"undici@npm:6.19.7": - version: 6.19.7 - resolution: "undici@npm:6.19.7" - checksum: 10/77fb8b0377388f6dba8244b015841318d621031211b4f3c2273d809304b77ec44adeba4b89dfd6708bdc044190e18f068e5b231882ef15d057d4624e46f544e3 - languageName: node - linkType: hard - "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" From 686b1fa2cc47c9862b940a09ba24ae21be3ca46e Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Mon, 28 Oct 2024 11:11:06 -0400 Subject: [PATCH 13/26] update yarn.lock --- yarn.lock | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/yarn.lock b/yarn.lock index 23ca006e7e..6e053b127b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3917,12 +3917,12 @@ __metadata: linkType: hard "@metamask/base-controller@npm:^7.0.0": - version: 7.0.1 - resolution: "@metamask/base-controller@npm:7.0.1" + version: 7.0.0 + resolution: "@metamask/base-controller@npm:7.0.0" dependencies: "@metamask/utils": "npm:^9.1.0" immer: "npm:^9.0.6" - checksum: 10/774b6d68ac95a5ec187e890d321bede50065f8a6f1ba7b49a19f5971366274054ac0e401548b51d3b014d0bca5d650409fb554dd13ce120e7fb3495b4e8e67b1 + checksum: 10/0ea307da4a7863224fd1fc83039165dbd06d2725922d4d4cf5854f5e7894e789a3c277f1e4592a38ae002de869217a26550f3b9687c259fc29153984dc5b4a4c languageName: node linkType: hard @@ -4173,8 +4173,8 @@ __metadata: linkType: soft "@metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.2.0": - version: 11.4.0 - resolution: "@metamask/controller-utils@npm:11.4.0" + version: 11.2.0 + resolution: "@metamask/controller-utils@npm:11.2.0" dependencies: "@ethereumjs/util": "npm:^8.1.0" "@metamask/eth-query": "npm:^4.0.0" @@ -4185,7 +4185,7 @@ __metadata: bn.js: "npm:^5.2.1" eth-ens-namehash: "npm:^2.0.8" fast-deep-equal: "npm:^3.1.3" - checksum: 10/f34d24880eab264bddaa5bef21afaecb206db6978364565d0f7b7a54b1d411f129eb84175041df3be8a66394c2d49e83b6648b5cbde6f34662a60fc553c31458 + checksum: 10/8c8630a635c5eeeb8ef46b14a12779a5005d8112668500bfb513186dee3f68b94dcf43ce6eddf42c38382828cd7ba28657b627d0eb617207a25b6ee78eae6b08 languageName: node linkType: hard @@ -7955,12 +7955,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*": - version: 22.8.0 - resolution: "@types/node@npm:22.8.0" +"@types/node@npm:*, @types/node@npm:22.7.5": + version: 22.7.5 + resolution: "@types/node@npm:22.7.5" dependencies: - undici-types: "npm:~6.19.8" - checksum: 10/2ffb407fd94c362a2b712cff32e59e5f988bff6cb32334f884554a1acf85ff41f4f43815e71be39a16970d2d71939f732e4981bfb515cc6f557b9face24476e0 + undici-types: "npm:~6.19.2" + checksum: 10/e8ba102f8c1aa7623787d625389be68d64e54fcbb76d41f6c2c64e8cf4c9f4a2370e7ef5e5f1732f3c57529d3d26afdcb2edc0101c5e413a79081449825c57ac languageName: node linkType: hard @@ -7971,15 +7971,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:22.7.5": - version: 22.7.5 - resolution: "@types/node@npm:22.7.5" - dependencies: - undici-types: "npm:~6.19.2" - checksum: 10/e8ba102f8c1aa7623787d625389be68d64e54fcbb76d41f6c2c64e8cf4c9f4a2370e7ef5e5f1732f3c57529d3d26afdcb2edc0101c5e413a79081449825c57ac - languageName: node - linkType: hard - "@types/node@npm:^20.1.0, @types/node@npm:^20.1.1": version: 20.5.7 resolution: "@types/node@npm:20.5.7" @@ -17150,9 +17141,9 @@ __metadata: linkType: hard "loglevel@npm:^1.6.0": - version: 1.9.2 - resolution: "loglevel@npm:1.9.2" - checksum: 10/6153d8db308323f7ee20130bc40309e7a976c30a10379d8666b596d9c6441965c3e074c8d7ee3347fe5cfc059c0375b6f3e8a10b93d5b813cc5547f5aa412a29 + version: 1.8.1 + resolution: "loglevel@npm:1.8.1" + checksum: 10/36a786082a7e4f1d962de330122291da3a102b88dbde81a45eb92a045c38b0903783958ba39dce641440c0413da303410e7f2565f897bccad828853bd5974c86 languageName: node linkType: hard @@ -22386,7 +22377,7 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.19.2, undici-types@npm:~6.19.8": +"undici-types@npm:~6.19.2": version: 6.19.8 resolution: "undici-types@npm:6.19.8" checksum: 10/cf0b48ed4fc99baf56584afa91aaffa5010c268b8842f62e02f752df209e3dea138b372a60a963b3b2576ed932f32329ce7ddb9cb5f27a6c83040d8cd74b7a70 From 3d89f80ddf6abc1eeeed6c3bc1551ddfea495200 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Mon, 28 Oct 2024 11:18:44 -0400 Subject: [PATCH 14/26] add ts expect error --- .../snaps-controllers/src/interface/SnapInterfaceController.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts index ccd5412f4b..e2cdce85d5 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts @@ -231,6 +231,8 @@ export class SnapInterfaceController extends BaseController< const componentState = constructState({}, element); this.update((draftState) => { + // @ts-expect-error - TS2589: Type instantiation is excessively deep and + // possibly infinite. draftState.interfaces[id] = { snapId, content: castDraft(element), From 305980c6f0e297ed9375e07faa0f81d23aa0fd7d Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Mon, 28 Oct 2024 11:39:58 -0400 Subject: [PATCH 15/26] fix coverage and add missing event --- packages/snaps-controllers/coverage.json | 8 ++++---- packages/snaps-jest/jest.config.js | 6 +++--- packages/snaps-simulation/jest.config.js | 8 ++++++++ packages/snaps-simulation/src/controllers.ts | 4 +++- packages/snaps-simulator/jest.config.js | 8 ++++---- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index cacff6a6c9..77badf064e 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { - "branches": 92.65, - "functions": 96.67, - "lines": 97.95, - "statements": 97.65 + "branches": 92.29, + "functions": 96.14, + "lines": 97.85, + "statements": 97.55 } diff --git a/packages/snaps-jest/jest.config.js b/packages/snaps-jest/jest.config.js index b809e14f94..ad629cdd28 100644 --- a/packages/snaps-jest/jest.config.js +++ b/packages/snaps-jest/jest.config.js @@ -7,9 +7,9 @@ module.exports = deepmerge(baseConfig, { coverageThreshold: { global: { branches: 100, - functions: 100, - lines: 100, - statements: 100, + functions: 97.22, + lines: 96.75, + statements: 96.83, }, }, }); diff --git a/packages/snaps-simulation/jest.config.js b/packages/snaps-simulation/jest.config.js index b187b6e932..e4966b143e 100644 --- a/packages/snaps-simulation/jest.config.js +++ b/packages/snaps-simulation/jest.config.js @@ -22,4 +22,12 @@ module.exports = deepmerge(baseConfig, { }, ], }, + coverageThreshold: { + global: { + branches: 33.67, + functions: 53.16, + lines: 54.01, + statements: 56.53, + }, + }, }); diff --git a/packages/snaps-simulation/src/controllers.ts b/packages/snaps-simulation/src/controllers.ts index 804279c36d..0877480b7d 100644 --- a/packages/snaps-simulation/src/controllers.ts +++ b/packages/snaps-simulation/src/controllers.ts @@ -85,7 +85,9 @@ export function getControllers(options: GetControllersOptions): Controllers { 'ApprovalController:hasRequest', 'ApprovalController:acceptRequest', ], - allowedEvents: [], + allowedEvents: [ + 'NotificationServicesController:notificationsListUpdated', + ], }), }); diff --git a/packages/snaps-simulator/jest.config.js b/packages/snaps-simulator/jest.config.js index d1ebf12317..39e576a6b1 100644 --- a/packages/snaps-simulator/jest.config.js +++ b/packages/snaps-simulator/jest.config.js @@ -7,10 +7,10 @@ delete baseConfig.transform; module.exports = deepmerge(baseConfig, { coverageThreshold: { global: { - branches: 54.33, - functions: 60.76, - lines: 80.54, - statements: 80.87, + branches: 48.33, + functions: 54.49, + lines: 75.63, + statements: 76.18, }, }, setupFiles: ['./jest.setup.js'], From 58fce06c781df7a2201e04b327e4f54da803ea1b Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Mon, 28 Oct 2024 11:49:47 -0400 Subject: [PATCH 16/26] add missing event --- .../src/features/simulation/test/controllers.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/snaps-simulator/src/features/simulation/test/controllers.ts b/packages/snaps-simulator/src/features/simulation/test/controllers.ts index a28673c866..fd0e581bdd 100644 --- a/packages/snaps-simulator/src/features/simulation/test/controllers.ts +++ b/packages/snaps-simulator/src/features/simulation/test/controllers.ts @@ -20,7 +20,9 @@ export function getSnapInterfaceController() { 'PhishingController:maybeUpdateState', 'PhishingController:testOrigin', ], - allowedEvents: [], + allowedEvents: [ + 'NotificationServicesController:notificationsListUpdated', + ], }), }); } From 4dc542d4f37e8cc783729930b8677f1a2915f742 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Mon, 28 Oct 2024 11:51:39 -0400 Subject: [PATCH 17/26] update coverage --- packages/snaps-jest/jest.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/snaps-jest/jest.config.js b/packages/snaps-jest/jest.config.js index ad629cdd28..b809e14f94 100644 --- a/packages/snaps-jest/jest.config.js +++ b/packages/snaps-jest/jest.config.js @@ -7,9 +7,9 @@ module.exports = deepmerge(baseConfig, { coverageThreshold: { global: { branches: 100, - functions: 97.22, - lines: 96.75, - statements: 96.83, + functions: 100, + lines: 100, + statements: 100, }, }, }); From c988b19773e5f99c7a391e7cebf0af4df7ce3d20 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Mon, 28 Oct 2024 12:12:25 -0400 Subject: [PATCH 18/26] fix more tests --- packages/snaps-simulation/src/test-utils/controller.ts | 10 +++++++--- .../snaps-simulator/src/features/simulation/sagas.ts | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/snaps-simulation/src/test-utils/controller.ts b/packages/snaps-simulation/src/test-utils/controller.ts index 6913a30eb6..ac264f7cff 100644 --- a/packages/snaps-simulation/src/test-utils/controller.ts +++ b/packages/snaps-simulation/src/test-utils/controller.ts @@ -1,5 +1,8 @@ import { PhishingDetectorResultType } from '@metamask/phishing-controller'; -import type { SnapInterfaceControllerAllowedActions } from '@metamask/snaps-controllers'; +import type { + SnapInterfaceControllerAllowedActions, + SnapInterfaceControllerEvents, +} from '@metamask/snaps-controllers'; import { MockControllerMessenger } from '@metamask/snaps-utils/test-utils'; import type { RootControllerAllowedActions } from '../controllers'; @@ -47,7 +50,8 @@ export const getRestrictedSnapInterfaceControllerMessenger = ( ) => { const snapInterfaceControllerMessenger = messenger.getRestricted< 'SnapInterfaceController', - SnapInterfaceControllerAllowedActions['type'] + SnapInterfaceControllerAllowedActions['type'], + SnapInterfaceControllerEvents['type'] >({ name: 'SnapInterfaceController', allowedActions: [ @@ -56,7 +60,7 @@ export const getRestrictedSnapInterfaceControllerMessenger = ( 'ApprovalController:hasRequest', 'ApprovalController:acceptRequest', ], - allowedEvents: [], + allowedEvents: ['NotificationServicesController:notificationsListUpdated'], }); return snapInterfaceControllerMessenger; diff --git a/packages/snaps-simulator/src/features/simulation/sagas.ts b/packages/snaps-simulator/src/features/simulation/sagas.ts index 8b0f64d7a4..f3184ae0ef 100644 --- a/packages/snaps-simulator/src/features/simulation/sagas.ts +++ b/packages/snaps-simulator/src/features/simulation/sagas.ts @@ -194,7 +194,9 @@ export function* initSaga({ payload }: PayloadAction) { `PhishingController:testOrigin`, `PhishingController:maybeUpdateState`, ], - allowedEvents: [], + allowedEvents: [ + 'NotificationServicesController:notificationsListUpdated', + ], }), }); From 89d529c7fcd637fcdd8d285339da9e1a55f38744 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Mon, 28 Oct 2024 12:22:46 -0400 Subject: [PATCH 19/26] fix coverage again --- packages/snaps-simulation/jest.config.js | 8 -------- packages/snaps-simulator/jest.config.js | 8 ++++---- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/snaps-simulation/jest.config.js b/packages/snaps-simulation/jest.config.js index e4966b143e..b187b6e932 100644 --- a/packages/snaps-simulation/jest.config.js +++ b/packages/snaps-simulation/jest.config.js @@ -22,12 +22,4 @@ module.exports = deepmerge(baseConfig, { }, ], }, - coverageThreshold: { - global: { - branches: 33.67, - functions: 53.16, - lines: 54.01, - statements: 56.53, - }, - }, }); diff --git a/packages/snaps-simulator/jest.config.js b/packages/snaps-simulator/jest.config.js index 39e576a6b1..d1ebf12317 100644 --- a/packages/snaps-simulator/jest.config.js +++ b/packages/snaps-simulator/jest.config.js @@ -7,10 +7,10 @@ delete baseConfig.transform; module.exports = deepmerge(baseConfig, { coverageThreshold: { global: { - branches: 48.33, - functions: 54.49, - lines: 75.63, - statements: 76.18, + branches: 54.33, + functions: 60.76, + lines: 80.54, + statements: 80.87, }, }, setupFiles: ['./jest.setup.js'], From a90208a4510e3b4568349a7b14b31b8140dd494e Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Sat, 2 Nov 2024 03:08:27 +0900 Subject: [PATCH 20/26] use weakmap to maintain function reference --- .../interface/SnapInterfaceController.test.tsx | 15 ++++++--------- .../src/interface/SnapInterfaceController.ts | 14 ++++++++------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx index 39a9e614cd..921860e5f2 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx @@ -63,16 +63,13 @@ describe('SnapInterfaceController', () => { [], ); - // defer - setTimeout(() => { - expect(controller.state).toStrictEqual({ - interfaces: { - '2': { - contentType: ContentType.Dialog, - }, + expect(controller.state).toStrictEqual({ + interfaces: { + '2': { + contentType: ContentType.Dialog, }, - }); - }, 1); + }, + }); controller.destroy(); }); diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts index e2cdce85d5..3c6ce61059 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts @@ -128,6 +128,8 @@ export type SnapInterfaceControllerArgs = { state?: SnapInterfaceControllerState; }; +const subscriptions = new WeakMap(); + /** * Use this controller to manage snaps UI interfaces using RPC method hooks. */ @@ -161,14 +163,14 @@ export class SnapInterfaceController extends BaseController< state: { interfaces: {}, ...state }, }); - this._onNotificationsListUpdated = - this._onNotificationsListUpdated.bind(this); + /* eslint-disable @typescript-eslint/unbound-method */ + subscriptions.set(this, this.#_onNotificationsListUpdated.bind(this)); this.messagingSystem.subscribe( 'NotificationServicesController:notificationsListUpdated', - /* eslint-disable @typescript-eslint/unbound-method */ - this._onNotificationsListUpdated, + subscriptions.get(this), ); + this.#registerMessageHandlers(); } @@ -428,7 +430,7 @@ export class SnapInterfaceController extends BaseController< ); } - _onNotificationsListUpdated(notificationsList: Record[]) { + #_onNotificationsListUpdated(notificationsList: Record[]) { const snapNotificationsWithInterface = notificationsList.filter( (notification) => { return notification.type === 'snap' && notification.data?.detailedView; @@ -470,7 +472,7 @@ export class SnapInterfaceController extends BaseController< /* eslint-disable @typescript-eslint/unbound-method */ this.messagingSystem.unsubscribe( 'NotificationServicesController:notificationsListUpdated', - this._onNotificationsListUpdated, + subscriptions.get(this), ); } } From d36157082f20f314209a358c40a401c5d1dc6517 Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Sat, 2 Nov 2024 03:21:25 +0900 Subject: [PATCH 21/26] update coverage --- packages/snaps-controllers/coverage.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index 77badf064e..58ec857bd7 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { "branches": 92.29, - "functions": 96.14, + "functions": 96.13, "lines": 97.85, "statements": 97.55 } From e05f6f07a6ffd4a87585f07fafbb66a6717bc1fa Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Sat, 2 Nov 2024 03:24:55 +0900 Subject: [PATCH 22/26] rebuild --- packages/examples/packages/browserify-plugin/snap.manifest.json | 2 +- packages/examples/packages/browserify/snap.manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/examples/packages/browserify-plugin/snap.manifest.json b/packages/examples/packages/browserify-plugin/snap.manifest.json index 3d64ffd13c..bb85919513 100644 --- a/packages/examples/packages/browserify-plugin/snap.manifest.json +++ b/packages/examples/packages/browserify-plugin/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "27qYDxM1vuhrXntxwo8v6GE52cJ7OsaIYo51Q4p3gq0=", + "shasum": "LciQX9cjRXeSErEsKRm9OEFW8jAq9doqaa9b/TfGN3Y=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/browserify/snap.manifest.json b/packages/examples/packages/browserify/snap.manifest.json index 6bd23fe54a..f3ae515fcb 100644 --- a/packages/examples/packages/browserify/snap.manifest.json +++ b/packages/examples/packages/browserify/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "oT2RHVQmSMHq87FYqFRPXqjERd+gOrI0RKjwNWDbSWY=", + "shasum": "83SohbQ4Vp/wI2lFXL6tyuvWy7bLcRSL3yZikZEZrAg=", "location": { "npm": { "filePath": "dist/bundle.js", From 0c5bd6db6923e6de027c32c97df012cc8996561a Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 7 Nov 2024 06:33:08 +0900 Subject: [PATCH 23/26] address PR comments --- .../SnapInterfaceController.test.tsx | 129 +++++++++--------- .../src/interface/SnapInterfaceController.ts | 48 ++----- 2 files changed, 73 insertions(+), 104 deletions(-) diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx index 921860e5f2..5a6c693a5c 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.tsx @@ -70,8 +70,6 @@ describe('SnapInterfaceController', () => { }, }, }); - - controller.destroy(); }); describe('constructor', () => { @@ -105,8 +103,6 @@ describe('SnapInterfaceController', () => { }, }, }); - - controller.destroy(); }); }); @@ -116,7 +112,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -153,7 +150,6 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(getJsxElementFromComponent(components)); expect(state).toStrictEqual({ foo: { bar: null } }); - controller.destroy(); }); it('can create a new interface from JSX', async () => { @@ -161,7 +157,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -203,7 +200,6 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(element); expect(state).toStrictEqual({ foo: { bar: null } }); - controller.destroy(); }); it('supports providing interface context', async () => { @@ -211,7 +207,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -238,7 +235,6 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(element); expect(context).toStrictEqual({ foo: 'bar' }); - controller.destroy(); }); it('supports providing an interface content type', async () => { @@ -246,7 +242,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -273,7 +270,6 @@ describe('SnapInterfaceController', () => { ); expect(contentType).toStrictEqual(ContentType.Notification); - controller.destroy(); }); it('throws if interface context is too large', async () => { @@ -286,7 +282,8 @@ describe('SnapInterfaceController', () => { .mockReturnValueOnce(1) .mockReturnValueOnce(10_000_000); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -306,7 +303,6 @@ describe('SnapInterfaceController', () => { { foo: 'a'.repeat(1_000_000) }, ), ).rejects.toThrow('A Snap interface context may not be larger than 1 MB'); - controller.destroy(); }); it('throws if a link is on the phishing list', async () => { @@ -326,7 +322,8 @@ describe('SnapInterfaceController', () => { () => ({ result: true, type: 'all' }), ); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -356,8 +353,6 @@ describe('SnapInterfaceController', () => { 'PhishingController:testOrigin', 'https://foo.bar/', ); - - controller.destroy(); }); it('throws if a JSX link is on the phishing list', async () => { @@ -377,7 +372,8 @@ describe('SnapInterfaceController', () => { () => ({ result: true, type: 'all' }), ); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -407,7 +403,6 @@ describe('SnapInterfaceController', () => { 'PhishingController:testOrigin', 'https://foo.bar/', ); - controller.destroy(); }); it('throws if UI content is too large', async () => { @@ -419,7 +414,8 @@ describe('SnapInterfaceController', () => { jest.mocked(getJsonSizeUnsafe).mockReturnValueOnce(11_000_000); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -432,7 +428,6 @@ describe('SnapInterfaceController', () => { components, ), ).rejects.toThrow('A Snap UI may not be larger than 10 MB.'); - controller.destroy(); }); it('throws if JSX UI content is too large', async () => { @@ -444,7 +439,8 @@ describe('SnapInterfaceController', () => { jest.mocked(getJsonSizeUnsafe).mockReturnValueOnce(11_000_000); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -461,7 +457,6 @@ describe('SnapInterfaceController', () => { element, ), ).rejects.toThrow('A Snap UI may not be larger than 10 MB.'); - controller.destroy(); }); it('throws if text content is too large', async () => { @@ -471,7 +466,8 @@ describe('SnapInterfaceController', () => { false, ); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -484,7 +480,6 @@ describe('SnapInterfaceController', () => { components, ), ).rejects.toThrow('The text in a Snap UI may not be larger than 50 kB.'); - controller.destroy(); }); }); @@ -494,7 +489,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -516,7 +512,6 @@ describe('SnapInterfaceController', () => { ); expect(content).toStrictEqual(getJsxElementFromComponent(components)); - controller.destroy(); }); it('throws if the snap requesting the interface is not the one that created it', async () => { @@ -524,7 +519,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -546,7 +542,6 @@ describe('SnapInterfaceController', () => { id, ), ).toThrow(`Interface not created by foo.`); - controller.destroy(); }); it('throws if the interface does not exist', () => { @@ -554,7 +549,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -565,7 +561,6 @@ describe('SnapInterfaceController', () => { 'test', ), ).toThrow(`Interface with id 'test' not found.`); - controller.destroy(); }); }); @@ -575,7 +570,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -610,7 +606,6 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(getJsxElementFromComponent(newContent)); expect(state).toStrictEqual({ foo: { baz: null } }); - controller.destroy(); }); it('can update an interface using JSX', async () => { @@ -618,7 +613,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -659,7 +655,6 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(newElement); expect(state).toStrictEqual({ foo: { baz: null } }); - controller.destroy(); }); it('can update an interface and context', async () => { @@ -667,7 +662,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -713,7 +709,6 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(getJsxElementFromComponent(newContent)); expect(state).toStrictEqual({ foo: { baz: null } }); expect(interfaceContext).toStrictEqual(newContext); - controller.destroy(); }); it('does not replace context if none is provided', async () => { @@ -721,7 +716,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -764,7 +760,6 @@ describe('SnapInterfaceController', () => { expect(content).toStrictEqual(getJsxElementFromComponent(newContent)); expect(state).toStrictEqual({ foo: { baz: null } }); expect(interfaceContext).toStrictEqual(context); - controller.destroy(); }); it('throws if a link is on the phishing list', async () => { @@ -784,7 +779,8 @@ describe('SnapInterfaceController', () => { () => ({ result: true, type: 'all' }), ); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -826,7 +822,6 @@ describe('SnapInterfaceController', () => { 'PhishingController:testOrigin', 'https://foo.bar/', ); - controller.destroy(); }); it('throws if a JSX link is on the phishing list', async () => { @@ -846,7 +841,8 @@ describe('SnapInterfaceController', () => { () => ({ result: true, type: 'all' }), ); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -892,7 +888,6 @@ describe('SnapInterfaceController', () => { 'PhishingController:testOrigin', 'https://foo.bar/', ); - controller.destroy(); }); it('throws if UI content is too large', async () => { @@ -905,7 +900,8 @@ describe('SnapInterfaceController', () => { .mockReturnValueOnce(1) .mockReturnValueOnce(11_000_000); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -930,7 +926,6 @@ describe('SnapInterfaceController', () => { newContent, ), ).rejects.toThrow('A Snap UI may not be larger than 10 MB.'); - controller.destroy(); }); it('throws if JSX UI content is too large', async () => { @@ -943,7 +938,8 @@ describe('SnapInterfaceController', () => { .mockReturnValueOnce(1) .mockReturnValueOnce(11_000_000); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -975,7 +971,6 @@ describe('SnapInterfaceController', () => { newElement, ), ).rejects.toThrow('A Snap UI may not be larger than 10 MB.'); - controller.destroy(); }); it('throws if text content is too large', async () => { @@ -983,7 +978,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1008,7 +1004,6 @@ describe('SnapInterfaceController', () => { newContent, ), ).rejects.toThrow('The text in a Snap UI may not be larger than 50 kB.'); - controller.destroy(); }); it('throws if the interface does not exist', async () => { @@ -1016,7 +1011,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1030,7 +1026,6 @@ describe('SnapInterfaceController', () => { content, ), ).rejects.toThrow("Interface with id 'foo' not found."); - controller.destroy(); }); it('throws if the interface is updated by another snap', async () => { @@ -1038,7 +1033,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1063,7 +1059,6 @@ describe('SnapInterfaceController', () => { newContent, ), ).rejects.toThrow('Interface not created by foo.'); - controller.destroy(); }); }); @@ -1073,7 +1068,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1100,7 +1096,6 @@ describe('SnapInterfaceController', () => { ); expect(state).toStrictEqual(newState); - controller.destroy(); }); it('updates the interface state with a file', async () => { @@ -1108,7 +1103,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1150,7 +1146,6 @@ describe('SnapInterfaceController', () => { ); expect(state).toStrictEqual(newState); - controller.destroy(); }); }); @@ -1160,7 +1155,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1181,7 +1177,6 @@ describe('SnapInterfaceController', () => { id, ), ).toThrow(`Interface with id '${id}' not found.`); - controller.destroy(); }); }); @@ -1191,7 +1186,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1227,7 +1223,6 @@ describe('SnapInterfaceController', () => { ); expect(await approvalPromise).toBe('bar'); - controller.destroy(); }); it('throws if the interface does not exist', async () => { @@ -1235,7 +1230,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1259,7 +1255,6 @@ describe('SnapInterfaceController', () => { 'bar', ), ).rejects.toThrow(`Interface with id 'foo' not found.`); - controller.destroy(); }); it('throws if the interface is resolved by another snap', async () => { @@ -1267,7 +1262,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1304,7 +1300,6 @@ describe('SnapInterfaceController', () => { 'bar', ), ).rejects.toThrow('Interface not created by baz.'); - controller.destroy(); }); it('throws if the interface has no approval request', async () => { @@ -1312,7 +1307,8 @@ describe('SnapInterfaceController', () => { const controllerMessenger = getRestrictedSnapInterfaceControllerMessenger(rootMessenger); - const controller = new SnapInterfaceController({ + // eslint-disable-next-line no-new + new SnapInterfaceController({ messenger: controllerMessenger, }); @@ -1344,7 +1340,6 @@ describe('SnapInterfaceController', () => { 'bar', ), ).rejects.toThrow(`Approval request with id '${id}' not found.`); - controller.destroy(); }); }); }); diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts index 3c6ce61059..3cc50bd317 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts @@ -95,7 +95,7 @@ export type SnapInterfaceControllerStateChangeEvent = >; type NotificationListUpdatedEvent = { - type: `${'NotificationServicesController'}:notificationsListUpdated`; + type: 'NotificationServicesController:notificationsListUpdated'; payload: [Record[]]; }; @@ -128,8 +128,6 @@ export type SnapInterfaceControllerArgs = { state?: SnapInterfaceControllerState; }; -const subscriptions = new WeakMap(); - /** * Use this controller to manage snaps UI interfaces using RPC method hooks. */ @@ -163,12 +161,9 @@ export class SnapInterfaceController extends BaseController< state: { interfaces: {}, ...state }, }); - /* eslint-disable @typescript-eslint/unbound-method */ - subscriptions.set(this, this.#_onNotificationsListUpdated.bind(this)); - this.messagingSystem.subscribe( 'NotificationServicesController:notificationsListUpdated', - subscriptions.get(this), + this.#onNotificationsListUpdated.bind(this), ); this.#registerMessageHandlers(); @@ -430,7 +425,7 @@ export class SnapInterfaceController extends BaseController< ); } - #_onNotificationsListUpdated(notificationsList: Record[]) { + #onNotificationsListUpdated(notificationsList: Record[]) { const snapNotificationsWithInterface = notificationsList.filter( (notification) => { return notification.type === 'snap' && notification.data?.detailedView; @@ -443,36 +438,15 @@ export class SnapInterfaceController extends BaseController< ), ); - const updatedState = Object.entries(this.state.interfaces).reduce< - Record - >((newState, [id, snapInterface]) => { - if (snapInterface.contentType === ContentType.Notification) { - if (interfaceIdSet.has(id)) { - newState[id] = snapInterface; - } - } else { - newState[id] = snapInterface; - } - return newState; - }, {}); - this.update((state) => { - // @ts-expect-error - TS2589: Type instantiation is excessively deep and - // possibly infinite. - state.interfaces = updatedState; + Object.entries(state.interfaces).forEach(([id, snapInterface]) => { + if ( + snapInterface.contentType === ContentType.Notification && + !interfaceIdSet.has(id) + ) { + delete state.interfaces[id]; + } + }); }); } - - /** - * Run controller teardown process and unsubscribe from notification service controller events. - */ - destroy() { - super.destroy(); - - /* eslint-disable @typescript-eslint/unbound-method */ - this.messagingSystem.unsubscribe( - 'NotificationServicesController:notificationsListUpdated', - subscriptions.get(this), - ); - } } From c5995075ea1f94f586386769f5b0e958e9dce18e Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 7 Nov 2024 07:01:38 +0900 Subject: [PATCH 24/26] update coverage --- packages/snaps-controllers/coverage.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index 58ec857bd7..3a5431d676 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { "branches": 92.29, - "functions": 96.13, + "functions": 96.11, "lines": 97.85, "statements": 97.55 } From c6bfb6996dd2276ef674b59cb211c8e90c19053d Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 7 Nov 2024 07:10:14 +0900 Subject: [PATCH 25/26] update coverage again --- packages/snaps-controllers/coverage.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index 3a5431d676..7994616f0d 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { - "branches": 92.29, + "branches": 92.48, "functions": 96.11, - "lines": 97.85, - "statements": 97.55 + "lines": 97.89, + "statements": 97.6 } From c130124a689f1474e28ce13f58d73cec6383635f Mon Sep 17 00:00:00 2001 From: Hassan Malik Date: Thu, 7 Nov 2024 23:11:37 +0900 Subject: [PATCH 26/26] update types --- .../src/interface/SnapInterfaceController.ts | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts index 3cc50bd317..0cc765ed42 100644 --- a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts @@ -22,7 +22,7 @@ import { ContentType } from '@metamask/snaps-sdk'; import type { JSXElement } from '@metamask/snaps-sdk/jsx'; import { getJsonSizeUnsafe, validateJsxLinks } from '@metamask/snaps-utils'; import type { Json } from '@metamask/utils'; -import { assert } from '@metamask/utils'; +import { assert, hasProperty } from '@metamask/utils'; import { castDraft } from 'immer'; import { nanoid } from 'nanoid'; @@ -94,9 +94,32 @@ export type SnapInterfaceControllerStateChangeEvent = SnapInterfaceControllerState >; +type OtherNotification = { type: string; [key: string]: unknown }; + +export type ExpandedView = { + title: string; + interfaceId: string; + footerLink?: { href: string; text: string }; +}; + +type RawSnapNotificationData = + | { + message: string; + origin: string; + } + | { message: string; origin: string; detailedView: ExpandedView }; + +type SnapNotification = { + type: 'snap'; + data: RawSnapNotificationData; + readDate: string | null; +}; + +type Notification = OtherNotification | SnapNotification; + type NotificationListUpdatedEvent = { type: 'NotificationServicesController:notificationsListUpdated'; - payload: [Record[]]; + payload: [Notification[]]; }; export type SnapInterfaceControllerEvents = @@ -425,15 +448,19 @@ export class SnapInterfaceController extends BaseController< ); } - #onNotificationsListUpdated(notificationsList: Record[]) { + #onNotificationsListUpdated(notificationsList: Notification[]) { const snapNotificationsWithInterface = notificationsList.filter( (notification) => { - return notification.type === 'snap' && notification.data?.detailedView; + return ( + notification.type === 'snap' && + hasProperty((notification as SnapNotification).data, 'detailedView') + ); }, ); const interfaceIdSet = new Set( snapNotificationsWithInterface.map( + // @ts-expect-error - Notification is SnapNotification here. (notification) => notification.data.detailedView.interfaceId, ), );