@metamask/snaps-controllers 13.1.0 → 13.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -2
- package/dist/websocket/WebSocketService.cjs +3 -1
- package/dist/websocket/WebSocketService.cjs.map +1 -1
- package/dist/websocket/WebSocketService.d.cts +2 -0
- package/dist/websocket/WebSocketService.d.cts.map +1 -1
- package/dist/websocket/WebSocketService.d.mts +2 -0
- package/dist/websocket/WebSocketService.d.mts.map +1 -1
- package/dist/websocket/WebSocketService.mjs +3 -1
- package/dist/websocket/WebSocketService.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,13 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [13.1.1]
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Always delete socket when `close` is emitted ([#3465](https://github.com/MetaMask/snaps/pull/3465))
|
|
15
|
+
- Add missing boilerplate to `WebSocketService` ([#3464](https://github.com/MetaMask/snaps/pull/3464))
|
|
16
|
+
|
|
10
17
|
## [13.1.0]
|
|
11
18
|
|
|
12
19
|
### Added
|
|
13
20
|
|
|
14
21
|
- Add `WebSocketService` for WebSockets support ([#3450](https://github.com/MetaMask/snaps/pull/3450))
|
|
15
22
|
- This service must be instantiated to invoke `onWebSocketEvent` when WebSocket
|
|
16
|
-
|
|
23
|
+
messages are received.
|
|
17
24
|
- Add `SnapController:init` to support `onStart` handler ([#3455](https://github.com/MetaMask/snaps/pull/3455))
|
|
18
25
|
- This function should be called when the client has been fully started.
|
|
19
26
|
|
|
@@ -819,7 +826,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
819
826
|
- The version of the package no longer needs to match the version of all other
|
|
820
827
|
MetaMask Snaps packages.
|
|
821
828
|
|
|
822
|
-
[Unreleased]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@13.1.
|
|
829
|
+
[Unreleased]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@13.1.1...HEAD
|
|
830
|
+
[13.1.1]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@13.1.0...@metamask/snaps-controllers@13.1.1
|
|
823
831
|
[13.1.0]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@13.0.0...@metamask/snaps-controllers@13.1.0
|
|
824
832
|
[13.0.0]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@12.3.1...@metamask/snaps-controllers@13.0.0
|
|
825
833
|
[12.3.1]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@12.3.0...@metamask/snaps-controllers@12.3.1
|
|
@@ -8,6 +8,8 @@ const nanoid_1 = require("nanoid");
|
|
|
8
8
|
const snaps_1 = require("../snaps/index.cjs");
|
|
9
9
|
const serviceName = 'WebSocketService';
|
|
10
10
|
class WebSocketService {
|
|
11
|
+
name = serviceName;
|
|
12
|
+
state = null;
|
|
11
13
|
#messenger;
|
|
12
14
|
#sockets;
|
|
13
15
|
constructor({ messenger }) {
|
|
@@ -98,6 +100,7 @@ class WebSocketService {
|
|
|
98
100
|
});
|
|
99
101
|
});
|
|
100
102
|
socket.addEventListener('close', (event) => {
|
|
103
|
+
this.#sockets.delete(id);
|
|
101
104
|
this.#handleEvent(snapId, {
|
|
102
105
|
type: 'close',
|
|
103
106
|
id,
|
|
@@ -149,7 +152,6 @@ class WebSocketService {
|
|
|
149
152
|
#close(snapId, id) {
|
|
150
153
|
const { socket } = this.#get(snapId, id);
|
|
151
154
|
socket.close();
|
|
152
|
-
this.#sockets.delete(id);
|
|
153
155
|
}
|
|
154
156
|
/**
|
|
155
157
|
* Close all open connections for a given Snap ID.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketService.cjs","sourceRoot":"","sources":["../../src/websocket/WebSocketService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedMessenger } from '@metamask/base-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n GetWebSocketsResult,\n SnapId,\n WebSocketEvent,\n} from '@metamask/snaps-sdk';\nimport { HandlerType, isEqual, logError } from '@metamask/snaps-utils';\nimport { assert, createDeferredPromise } from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport type {\n HandleSnapRequest,\n SnapInstalled,\n SnapUninstalled,\n SnapUpdated,\n} from '../snaps';\nimport { METAMASK_ORIGIN } from '../snaps';\n\nconst serviceName = 'WebSocketService';\n\nexport type WebSocketServiceOpenAction = {\n type: `${typeof serviceName}:open`;\n handler: (\n snapId: SnapId,\n url: string,\n protocols?: string[],\n ) => Promise<string>;\n};\n\nexport type WebSocketServiceCloseAction = {\n type: `${typeof serviceName}:close`;\n handler: (snapId: SnapId, id: string) => void;\n};\n\nexport type WebSocketServiceSendMessageAction = {\n type: `${typeof serviceName}:sendMessage`;\n handler: (\n snapId: SnapId,\n id: string,\n data: string | number[],\n ) => Promise<void>;\n};\n\nexport type WebSocketServiceGetAllAction = {\n type: `${typeof serviceName}:getAll`;\n handler: (snapId: SnapId) => GetWebSocketsResult;\n};\n\nexport type WebSocketServiceActions =\n | WebSocketServiceOpenAction\n | WebSocketServiceCloseAction\n | WebSocketServiceSendMessageAction\n | WebSocketServiceGetAllAction;\n\nexport type WebSocketServiceAllowedActions = HandleSnapRequest;\n\nexport type WebSocketServiceEvents =\n | SnapUninstalled\n | SnapUpdated\n | SnapInstalled;\n\nexport type WebSocketServiceMessenger = RestrictedMessenger<\n 'WebSocketService',\n WebSocketServiceActions | WebSocketServiceAllowedActions,\n WebSocketServiceEvents,\n WebSocketServiceAllowedActions['type'],\n WebSocketServiceEvents['type']\n>;\n\ntype WebSocketServiceArgs = {\n messenger: WebSocketServiceMessenger;\n};\n\ntype InternalSocket = {\n id: string;\n snapId: SnapId;\n url: string;\n protocols: string[];\n openPromise: Promise<void>;\n // eslint-disable-next-line no-restricted-globals\n socket: WebSocket;\n};\n\nexport class WebSocketService {\n readonly #messenger: WebSocketServiceMessenger;\n\n readonly #sockets: Map<string, InternalSocket>;\n\n constructor({ messenger }: WebSocketServiceArgs) {\n this.#messenger = messenger;\n this.#sockets = new Map();\n\n this.#messenger.registerActionHandler(\n `${serviceName}:open`,\n async (...args) => this.#open(...args),\n );\n\n this.#messenger.registerActionHandler(`${serviceName}:close`, (...args) =>\n this.#close(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${serviceName}:sendMessage`,\n async (...args) => this.#sendMessage(...args),\n );\n\n this.#messenger.registerActionHandler(`${serviceName}:getAll`, (...args) =>\n this.#getAll(...args),\n );\n\n this.#messenger.subscribe('SnapController:snapUpdated', (snap) => {\n this.#closeAll(snap.id);\n });\n\n this.#messenger.subscribe('SnapController:snapUninstalled', (snap) => {\n this.#closeAll(snap.id);\n });\n\n // Due to local Snaps not currently emitting snapUninstalled we also have to\n // listen to snapInstalled.\n this.#messenger.subscribe('SnapController:snapInstalled', (snap) => {\n this.#closeAll(snap.id);\n });\n }\n\n /**\n * Get information about a given WebSocket connection with an ID.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n * @returns Information abou the WebSocket connection.\n * @throws If the WebSocket connection cannot be found.\n */\n #get(snapId: SnapId, id: string) {\n const socket = this.#sockets.get(id);\n\n assert(\n socket && socket.snapId === snapId,\n `Socket with ID \"${id}\" not found.`,\n );\n\n return socket;\n }\n\n /**\n * Check whether a given Snap ID already has an open connection for a URL and protocol.\n *\n * @param snapId - The Snap ID.\n * @param url - The URL.\n * @param protocols - A protocols parameter.\n * @returns True if a matching connection already exists, otherwise false.\n */\n #exists(snapId: SnapId, url: string, protocols: string[]) {\n return this.#getAll(snapId).some(\n (socket) => socket.url === url && isEqual(socket.protocols, protocols),\n );\n }\n\n /**\n * Handle sending a specific WebSocketEvent to a Snap.\n *\n * @param snapId - The Snap ID.\n * @param event - The WebSocketEvent.\n */\n #handleEvent(snapId: SnapId, event: WebSocketEvent) {\n this.#messenger\n .call('SnapController:handleRequest', {\n origin: METAMASK_ORIGIN,\n snapId,\n handler: HandlerType.OnWebSocketEvent,\n request: { method: '', params: { event } },\n })\n .catch((error) => {\n logError(\n `An error occurred while handling a WebSocket message for Snap \"${snapId}\":`,\n error,\n );\n });\n }\n\n /**\n * Open a WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param url - The URL for the WebSocket connection.\n * @param protocols - An optional parameter for protocols.\n * @returns The identifier for the opened connection.\n * @throws If the connection fails.\n */\n async #open(snapId: SnapId, url: string, protocols: string[] = []) {\n assert(\n !this.#exists(snapId, url, protocols),\n `An open WebSocket connection to ${url} already exists.`,\n );\n\n const parsedUrl = new URL(url);\n const { origin } = parsedUrl;\n\n const id = nanoid();\n\n // eslint-disable-next-line no-restricted-globals\n const socket = new WebSocket(url, protocols);\n socket.binaryType = 'arraybuffer';\n\n const { promise, resolve, reject } = createDeferredPromise();\n\n socket.addEventListener('open', () => {\n resolve();\n this.#handleEvent(snapId, {\n type: 'open',\n id,\n origin,\n });\n });\n\n socket.addEventListener('close', (event) => {\n this.#handleEvent(snapId, {\n type: 'close',\n id,\n origin,\n code: event.code,\n reason: event.reason,\n // wasClean is not available on mobile.\n wasClean: event.wasClean ?? null,\n });\n });\n\n const errorListener = () => {\n reject(\n rpcErrors.resourceUnavailable(\n 'An error occurred while opening the WebSocket.',\n ),\n );\n };\n\n socket.addEventListener('error', errorListener);\n\n socket.addEventListener('message', (event) => {\n const isText = typeof event.data === 'string';\n const data = isText\n ? { type: 'text' as const, message: event.data }\n : {\n type: 'binary' as const,\n // We request that the WebSocket gives us an array buffer.\n message: Array.from(new Uint8Array(event.data)),\n };\n\n this.#handleEvent(snapId, {\n type: 'message',\n id,\n origin,\n data,\n });\n });\n\n this.#sockets.set(id, {\n id,\n snapId,\n url,\n protocols,\n socket,\n openPromise: promise,\n });\n\n await promise;\n\n socket.removeEventListener('error', errorListener);\n\n return id;\n }\n\n /**\n * Close a given WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n */\n #close(snapId: SnapId, id: string) {\n const { socket } = this.#get(snapId, id);\n\n socket.close();\n\n this.#sockets.delete(id);\n }\n\n /**\n * Close all open connections for a given Snap ID.\n *\n * @param snapId - The Snap ID.\n */\n #closeAll(snapId: SnapId) {\n for (const socket of this.#getAll(snapId)) {\n this.#close(snapId, socket.id);\n }\n }\n\n /**\n * Send a message from a given Snap ID to a WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n * @param data - The message to send.\n */\n async #sendMessage(snapId: SnapId, id: string, data: string | number[]) {\n const { socket, openPromise } = this.#get(snapId, id);\n\n await openPromise;\n\n const wrappedData = Array.isArray(data) ? new Uint8Array(data) : data;\n\n socket.send(wrappedData);\n }\n\n /**\n * Get a list of all open WebSocket connections for a Snap ID.\n *\n * @param snapId - The Snap ID.\n * @returns A list of WebSocket connections.\n */\n #getAll(snapId: SnapId) {\n return [...this.#sockets.values()]\n .filter((socket) => socket.snapId === snapId)\n .map((socket) => ({\n id: socket.id,\n url: socket.url,\n protocols: socket.protocols,\n }));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WebSocketService.cjs","sourceRoot":"","sources":["../../src/websocket/WebSocketService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedMessenger } from '@metamask/base-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n GetWebSocketsResult,\n SnapId,\n WebSocketEvent,\n} from '@metamask/snaps-sdk';\nimport { HandlerType, isEqual, logError } from '@metamask/snaps-utils';\nimport { assert, createDeferredPromise } from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport type {\n HandleSnapRequest,\n SnapInstalled,\n SnapUninstalled,\n SnapUpdated,\n} from '../snaps';\nimport { METAMASK_ORIGIN } from '../snaps';\n\nconst serviceName = 'WebSocketService';\n\nexport type WebSocketServiceOpenAction = {\n type: `${typeof serviceName}:open`;\n handler: (\n snapId: SnapId,\n url: string,\n protocols?: string[],\n ) => Promise<string>;\n};\n\nexport type WebSocketServiceCloseAction = {\n type: `${typeof serviceName}:close`;\n handler: (snapId: SnapId, id: string) => void;\n};\n\nexport type WebSocketServiceSendMessageAction = {\n type: `${typeof serviceName}:sendMessage`;\n handler: (\n snapId: SnapId,\n id: string,\n data: string | number[],\n ) => Promise<void>;\n};\n\nexport type WebSocketServiceGetAllAction = {\n type: `${typeof serviceName}:getAll`;\n handler: (snapId: SnapId) => GetWebSocketsResult;\n};\n\nexport type WebSocketServiceActions =\n | WebSocketServiceOpenAction\n | WebSocketServiceCloseAction\n | WebSocketServiceSendMessageAction\n | WebSocketServiceGetAllAction;\n\nexport type WebSocketServiceAllowedActions = HandleSnapRequest;\n\nexport type WebSocketServiceEvents =\n | SnapUninstalled\n | SnapUpdated\n | SnapInstalled;\n\nexport type WebSocketServiceMessenger = RestrictedMessenger<\n 'WebSocketService',\n WebSocketServiceActions | WebSocketServiceAllowedActions,\n WebSocketServiceEvents,\n WebSocketServiceAllowedActions['type'],\n WebSocketServiceEvents['type']\n>;\n\ntype WebSocketServiceArgs = {\n messenger: WebSocketServiceMessenger;\n};\n\ntype InternalSocket = {\n id: string;\n snapId: SnapId;\n url: string;\n protocols: string[];\n openPromise: Promise<void>;\n // eslint-disable-next-line no-restricted-globals\n socket: WebSocket;\n};\n\nexport class WebSocketService {\n name: typeof serviceName = serviceName;\n\n state = null;\n\n readonly #messenger: WebSocketServiceMessenger;\n\n readonly #sockets: Map<string, InternalSocket>;\n\n constructor({ messenger }: WebSocketServiceArgs) {\n this.#messenger = messenger;\n this.#sockets = new Map();\n\n this.#messenger.registerActionHandler(\n `${serviceName}:open`,\n async (...args) => this.#open(...args),\n );\n\n this.#messenger.registerActionHandler(`${serviceName}:close`, (...args) =>\n this.#close(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${serviceName}:sendMessage`,\n async (...args) => this.#sendMessage(...args),\n );\n\n this.#messenger.registerActionHandler(`${serviceName}:getAll`, (...args) =>\n this.#getAll(...args),\n );\n\n this.#messenger.subscribe('SnapController:snapUpdated', (snap) => {\n this.#closeAll(snap.id);\n });\n\n this.#messenger.subscribe('SnapController:snapUninstalled', (snap) => {\n this.#closeAll(snap.id);\n });\n\n // Due to local Snaps not currently emitting snapUninstalled we also have to\n // listen to snapInstalled.\n this.#messenger.subscribe('SnapController:snapInstalled', (snap) => {\n this.#closeAll(snap.id);\n });\n }\n\n /**\n * Get information about a given WebSocket connection with an ID.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n * @returns Information abou the WebSocket connection.\n * @throws If the WebSocket connection cannot be found.\n */\n #get(snapId: SnapId, id: string) {\n const socket = this.#sockets.get(id);\n\n assert(\n socket && socket.snapId === snapId,\n `Socket with ID \"${id}\" not found.`,\n );\n\n return socket;\n }\n\n /**\n * Check whether a given Snap ID already has an open connection for a URL and protocol.\n *\n * @param snapId - The Snap ID.\n * @param url - The URL.\n * @param protocols - A protocols parameter.\n * @returns True if a matching connection already exists, otherwise false.\n */\n #exists(snapId: SnapId, url: string, protocols: string[]) {\n return this.#getAll(snapId).some(\n (socket) => socket.url === url && isEqual(socket.protocols, protocols),\n );\n }\n\n /**\n * Handle sending a specific WebSocketEvent to a Snap.\n *\n * @param snapId - The Snap ID.\n * @param event - The WebSocketEvent.\n */\n #handleEvent(snapId: SnapId, event: WebSocketEvent) {\n this.#messenger\n .call('SnapController:handleRequest', {\n origin: METAMASK_ORIGIN,\n snapId,\n handler: HandlerType.OnWebSocketEvent,\n request: { method: '', params: { event } },\n })\n .catch((error) => {\n logError(\n `An error occurred while handling a WebSocket message for Snap \"${snapId}\":`,\n error,\n );\n });\n }\n\n /**\n * Open a WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param url - The URL for the WebSocket connection.\n * @param protocols - An optional parameter for protocols.\n * @returns The identifier for the opened connection.\n * @throws If the connection fails.\n */\n async #open(snapId: SnapId, url: string, protocols: string[] = []) {\n assert(\n !this.#exists(snapId, url, protocols),\n `An open WebSocket connection to ${url} already exists.`,\n );\n\n const parsedUrl = new URL(url);\n const { origin } = parsedUrl;\n\n const id = nanoid();\n\n // eslint-disable-next-line no-restricted-globals\n const socket = new WebSocket(url, protocols);\n socket.binaryType = 'arraybuffer';\n\n const { promise, resolve, reject } = createDeferredPromise();\n\n socket.addEventListener('open', () => {\n resolve();\n this.#handleEvent(snapId, {\n type: 'open',\n id,\n origin,\n });\n });\n\n socket.addEventListener('close', (event) => {\n this.#sockets.delete(id);\n\n this.#handleEvent(snapId, {\n type: 'close',\n id,\n origin,\n code: event.code,\n reason: event.reason,\n // wasClean is not available on mobile.\n wasClean: event.wasClean ?? null,\n });\n });\n\n const errorListener = () => {\n reject(\n rpcErrors.resourceUnavailable(\n 'An error occurred while opening the WebSocket.',\n ),\n );\n };\n\n socket.addEventListener('error', errorListener);\n\n socket.addEventListener('message', (event) => {\n const isText = typeof event.data === 'string';\n const data = isText\n ? { type: 'text' as const, message: event.data }\n : {\n type: 'binary' as const,\n // We request that the WebSocket gives us an array buffer.\n message: Array.from(new Uint8Array(event.data)),\n };\n\n this.#handleEvent(snapId, {\n type: 'message',\n id,\n origin,\n data,\n });\n });\n\n this.#sockets.set(id, {\n id,\n snapId,\n url,\n protocols,\n socket,\n openPromise: promise,\n });\n\n await promise;\n\n socket.removeEventListener('error', errorListener);\n\n return id;\n }\n\n /**\n * Close a given WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n */\n #close(snapId: SnapId, id: string) {\n const { socket } = this.#get(snapId, id);\n\n socket.close();\n }\n\n /**\n * Close all open connections for a given Snap ID.\n *\n * @param snapId - The Snap ID.\n */\n #closeAll(snapId: SnapId) {\n for (const socket of this.#getAll(snapId)) {\n this.#close(snapId, socket.id);\n }\n }\n\n /**\n * Send a message from a given Snap ID to a WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n * @param data - The message to send.\n */\n async #sendMessage(snapId: SnapId, id: string, data: string | number[]) {\n const { socket, openPromise } = this.#get(snapId, id);\n\n await openPromise;\n\n const wrappedData = Array.isArray(data) ? new Uint8Array(data) : data;\n\n socket.send(wrappedData);\n }\n\n /**\n * Get a list of all open WebSocket connections for a Snap ID.\n *\n * @param snapId - The Snap ID.\n * @returns A list of WebSocket connections.\n */\n #getAll(snapId: SnapId) {\n return [...this.#sockets.values()]\n .filter((socket) => socket.snapId === snapId)\n .map((socket) => ({\n id: socket.id,\n url: socket.url,\n protocols: socket.protocols,\n }));\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketService.d.cts","sourceRoot":"","sources":["../../src/websocket/WebSocketService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAkC;AAErE,OAAO,KAAK,EACV,mBAAmB,EACnB,MAAM,EAEP,4BAA4B;AAK7B,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,WAAW,EACZ,2BAAiB;AAGlB,QAAA,MAAM,WAAW,qBAAqB,CAAC;AAEvC,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,GAAG,OAAO,WAAW,OAAO,CAAC;IACnC,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAAE,KACjB,OAAO,CAAC,MAAM,CAAC,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,GAAG,OAAO,WAAW,QAAQ,CAAC;IACpC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,GAAG,OAAO,WAAW,cAAc,CAAC;IAC1C,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,KACpB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,EAAE,GAAG,OAAO,WAAW,SAAS,CAAC;IACrC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,mBAAmB,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B,0BAA0B,GAC1B,2BAA2B,GAC3B,iCAAiC,GACjC,4BAA4B,CAAC;AAEjC,MAAM,MAAM,8BAA8B,GAAG,iBAAiB,CAAC;AAE/D,MAAM,MAAM,sBAAsB,GAC9B,eAAe,GACf,WAAW,GACX,aAAa,CAAC;AAElB,MAAM,MAAM,yBAAyB,GAAG,mBAAmB,CACzD,kBAAkB,EAClB,uBAAuB,GAAG,8BAA8B,EACxD,sBAAsB,EACtB,8BAA8B,CAAC,MAAM,CAAC,EACtC,sBAAsB,CAAC,MAAM,CAAC,CAC/B,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,yBAAyB,CAAC;CACtC,CAAC;AAYF,qBAAa,gBAAgB;;
|
|
1
|
+
{"version":3,"file":"WebSocketService.d.cts","sourceRoot":"","sources":["../../src/websocket/WebSocketService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAkC;AAErE,OAAO,KAAK,EACV,mBAAmB,EACnB,MAAM,EAEP,4BAA4B;AAK7B,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,WAAW,EACZ,2BAAiB;AAGlB,QAAA,MAAM,WAAW,qBAAqB,CAAC;AAEvC,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,GAAG,OAAO,WAAW,OAAO,CAAC;IACnC,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAAE,KACjB,OAAO,CAAC,MAAM,CAAC,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,GAAG,OAAO,WAAW,QAAQ,CAAC;IACpC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,GAAG,OAAO,WAAW,cAAc,CAAC;IAC1C,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,KACpB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,EAAE,GAAG,OAAO,WAAW,SAAS,CAAC;IACrC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,mBAAmB,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B,0BAA0B,GAC1B,2BAA2B,GAC3B,iCAAiC,GACjC,4BAA4B,CAAC;AAEjC,MAAM,MAAM,8BAA8B,GAAG,iBAAiB,CAAC;AAE/D,MAAM,MAAM,sBAAsB,GAC9B,eAAe,GACf,WAAW,GACX,aAAa,CAAC;AAElB,MAAM,MAAM,yBAAyB,GAAG,mBAAmB,CACzD,kBAAkB,EAClB,uBAAuB,GAAG,8BAA8B,EACxD,sBAAsB,EACtB,8BAA8B,CAAC,MAAM,CAAC,EACtC,sBAAsB,CAAC,MAAM,CAAC,CAC/B,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,yBAAyB,CAAC;CACtC,CAAC;AAYF,qBAAa,gBAAgB;;IAC3B,IAAI,EAAE,OAAO,WAAW,CAAe;IAEvC,KAAK,OAAQ;gBAMD,EAAE,SAAS,EAAE,EAAE,oBAAoB;CAgPhD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketService.d.mts","sourceRoot":"","sources":["../../src/websocket/WebSocketService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAkC;AAErE,OAAO,KAAK,EACV,mBAAmB,EACnB,MAAM,EAEP,4BAA4B;AAK7B,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,WAAW,EACZ,2BAAiB;AAGlB,QAAA,MAAM,WAAW,qBAAqB,CAAC;AAEvC,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,GAAG,OAAO,WAAW,OAAO,CAAC;IACnC,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAAE,KACjB,OAAO,CAAC,MAAM,CAAC,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,GAAG,OAAO,WAAW,QAAQ,CAAC;IACpC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,GAAG,OAAO,WAAW,cAAc,CAAC;IAC1C,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,KACpB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,EAAE,GAAG,OAAO,WAAW,SAAS,CAAC;IACrC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,mBAAmB,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B,0BAA0B,GAC1B,2BAA2B,GAC3B,iCAAiC,GACjC,4BAA4B,CAAC;AAEjC,MAAM,MAAM,8BAA8B,GAAG,iBAAiB,CAAC;AAE/D,MAAM,MAAM,sBAAsB,GAC9B,eAAe,GACf,WAAW,GACX,aAAa,CAAC;AAElB,MAAM,MAAM,yBAAyB,GAAG,mBAAmB,CACzD,kBAAkB,EAClB,uBAAuB,GAAG,8BAA8B,EACxD,sBAAsB,EACtB,8BAA8B,CAAC,MAAM,CAAC,EACtC,sBAAsB,CAAC,MAAM,CAAC,CAC/B,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,yBAAyB,CAAC;CACtC,CAAC;AAYF,qBAAa,gBAAgB;;
|
|
1
|
+
{"version":3,"file":"WebSocketService.d.mts","sourceRoot":"","sources":["../../src/websocket/WebSocketService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kCAAkC;AAErE,OAAO,KAAK,EACV,mBAAmB,EACnB,MAAM,EAEP,4BAA4B;AAK7B,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,WAAW,EACZ,2BAAiB;AAGlB,QAAA,MAAM,WAAW,qBAAqB,CAAC;AAEvC,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,GAAG,OAAO,WAAW,OAAO,CAAC;IACnC,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,MAAM,EAAE,KACjB,OAAO,CAAC,MAAM,CAAC,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,GAAG,OAAO,WAAW,QAAQ,CAAC;IACpC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,IAAI,EAAE,GAAG,OAAO,WAAW,cAAc,CAAC;IAC1C,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,KACpB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,EAAE,GAAG,OAAO,WAAW,SAAS,CAAC;IACrC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,mBAAmB,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B,0BAA0B,GAC1B,2BAA2B,GAC3B,iCAAiC,GACjC,4BAA4B,CAAC;AAEjC,MAAM,MAAM,8BAA8B,GAAG,iBAAiB,CAAC;AAE/D,MAAM,MAAM,sBAAsB,GAC9B,eAAe,GACf,WAAW,GACX,aAAa,CAAC;AAElB,MAAM,MAAM,yBAAyB,GAAG,mBAAmB,CACzD,kBAAkB,EAClB,uBAAuB,GAAG,8BAA8B,EACxD,sBAAsB,EACtB,8BAA8B,CAAC,MAAM,CAAC,EACtC,sBAAsB,CAAC,MAAM,CAAC,CAC/B,CAAC;AAEF,KAAK,oBAAoB,GAAG;IAC1B,SAAS,EAAE,yBAAyB,CAAC;CACtC,CAAC;AAYF,qBAAa,gBAAgB;;IAC3B,IAAI,EAAE,OAAO,WAAW,CAAe;IAEvC,KAAK,OAAQ;gBAMD,EAAE,SAAS,EAAE,EAAE,oBAAoB;CAgPhD"}
|
|
@@ -5,6 +5,8 @@ import { nanoid } from "nanoid";
|
|
|
5
5
|
import { METAMASK_ORIGIN } from "../snaps/index.mjs";
|
|
6
6
|
const serviceName = 'WebSocketService';
|
|
7
7
|
export class WebSocketService {
|
|
8
|
+
name = serviceName;
|
|
9
|
+
state = null;
|
|
8
10
|
#messenger;
|
|
9
11
|
#sockets;
|
|
10
12
|
constructor({ messenger }) {
|
|
@@ -95,6 +97,7 @@ export class WebSocketService {
|
|
|
95
97
|
});
|
|
96
98
|
});
|
|
97
99
|
socket.addEventListener('close', (event) => {
|
|
100
|
+
this.#sockets.delete(id);
|
|
98
101
|
this.#handleEvent(snapId, {
|
|
99
102
|
type: 'close',
|
|
100
103
|
id,
|
|
@@ -146,7 +149,6 @@ export class WebSocketService {
|
|
|
146
149
|
#close(snapId, id) {
|
|
147
150
|
const { socket } = this.#get(snapId, id);
|
|
148
151
|
socket.close();
|
|
149
|
-
this.#sockets.delete(id);
|
|
150
152
|
}
|
|
151
153
|
/**
|
|
152
154
|
* Close all open connections for a given Snap ID.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketService.mjs","sourceRoot":"","sources":["../../src/websocket/WebSocketService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedMessenger } from '@metamask/base-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n GetWebSocketsResult,\n SnapId,\n WebSocketEvent,\n} from '@metamask/snaps-sdk';\nimport { HandlerType, isEqual, logError } from '@metamask/snaps-utils';\nimport { assert, createDeferredPromise } from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport type {\n HandleSnapRequest,\n SnapInstalled,\n SnapUninstalled,\n SnapUpdated,\n} from '../snaps';\nimport { METAMASK_ORIGIN } from '../snaps';\n\nconst serviceName = 'WebSocketService';\n\nexport type WebSocketServiceOpenAction = {\n type: `${typeof serviceName}:open`;\n handler: (\n snapId: SnapId,\n url: string,\n protocols?: string[],\n ) => Promise<string>;\n};\n\nexport type WebSocketServiceCloseAction = {\n type: `${typeof serviceName}:close`;\n handler: (snapId: SnapId, id: string) => void;\n};\n\nexport type WebSocketServiceSendMessageAction = {\n type: `${typeof serviceName}:sendMessage`;\n handler: (\n snapId: SnapId,\n id: string,\n data: string | number[],\n ) => Promise<void>;\n};\n\nexport type WebSocketServiceGetAllAction = {\n type: `${typeof serviceName}:getAll`;\n handler: (snapId: SnapId) => GetWebSocketsResult;\n};\n\nexport type WebSocketServiceActions =\n | WebSocketServiceOpenAction\n | WebSocketServiceCloseAction\n | WebSocketServiceSendMessageAction\n | WebSocketServiceGetAllAction;\n\nexport type WebSocketServiceAllowedActions = HandleSnapRequest;\n\nexport type WebSocketServiceEvents =\n | SnapUninstalled\n | SnapUpdated\n | SnapInstalled;\n\nexport type WebSocketServiceMessenger = RestrictedMessenger<\n 'WebSocketService',\n WebSocketServiceActions | WebSocketServiceAllowedActions,\n WebSocketServiceEvents,\n WebSocketServiceAllowedActions['type'],\n WebSocketServiceEvents['type']\n>;\n\ntype WebSocketServiceArgs = {\n messenger: WebSocketServiceMessenger;\n};\n\ntype InternalSocket = {\n id: string;\n snapId: SnapId;\n url: string;\n protocols: string[];\n openPromise: Promise<void>;\n // eslint-disable-next-line no-restricted-globals\n socket: WebSocket;\n};\n\nexport class WebSocketService {\n readonly #messenger: WebSocketServiceMessenger;\n\n readonly #sockets: Map<string, InternalSocket>;\n\n constructor({ messenger }: WebSocketServiceArgs) {\n this.#messenger = messenger;\n this.#sockets = new Map();\n\n this.#messenger.registerActionHandler(\n `${serviceName}:open`,\n async (...args) => this.#open(...args),\n );\n\n this.#messenger.registerActionHandler(`${serviceName}:close`, (...args) =>\n this.#close(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${serviceName}:sendMessage`,\n async (...args) => this.#sendMessage(...args),\n );\n\n this.#messenger.registerActionHandler(`${serviceName}:getAll`, (...args) =>\n this.#getAll(...args),\n );\n\n this.#messenger.subscribe('SnapController:snapUpdated', (snap) => {\n this.#closeAll(snap.id);\n });\n\n this.#messenger.subscribe('SnapController:snapUninstalled', (snap) => {\n this.#closeAll(snap.id);\n });\n\n // Due to local Snaps not currently emitting snapUninstalled we also have to\n // listen to snapInstalled.\n this.#messenger.subscribe('SnapController:snapInstalled', (snap) => {\n this.#closeAll(snap.id);\n });\n }\n\n /**\n * Get information about a given WebSocket connection with an ID.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n * @returns Information abou the WebSocket connection.\n * @throws If the WebSocket connection cannot be found.\n */\n #get(snapId: SnapId, id: string) {\n const socket = this.#sockets.get(id);\n\n assert(\n socket && socket.snapId === snapId,\n `Socket with ID \"${id}\" not found.`,\n );\n\n return socket;\n }\n\n /**\n * Check whether a given Snap ID already has an open connection for a URL and protocol.\n *\n * @param snapId - The Snap ID.\n * @param url - The URL.\n * @param protocols - A protocols parameter.\n * @returns True if a matching connection already exists, otherwise false.\n */\n #exists(snapId: SnapId, url: string, protocols: string[]) {\n return this.#getAll(snapId).some(\n (socket) => socket.url === url && isEqual(socket.protocols, protocols),\n );\n }\n\n /**\n * Handle sending a specific WebSocketEvent to a Snap.\n *\n * @param snapId - The Snap ID.\n * @param event - The WebSocketEvent.\n */\n #handleEvent(snapId: SnapId, event: WebSocketEvent) {\n this.#messenger\n .call('SnapController:handleRequest', {\n origin: METAMASK_ORIGIN,\n snapId,\n handler: HandlerType.OnWebSocketEvent,\n request: { method: '', params: { event } },\n })\n .catch((error) => {\n logError(\n `An error occurred while handling a WebSocket message for Snap \"${snapId}\":`,\n error,\n );\n });\n }\n\n /**\n * Open a WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param url - The URL for the WebSocket connection.\n * @param protocols - An optional parameter for protocols.\n * @returns The identifier for the opened connection.\n * @throws If the connection fails.\n */\n async #open(snapId: SnapId, url: string, protocols: string[] = []) {\n assert(\n !this.#exists(snapId, url, protocols),\n `An open WebSocket connection to ${url} already exists.`,\n );\n\n const parsedUrl = new URL(url);\n const { origin } = parsedUrl;\n\n const id = nanoid();\n\n // eslint-disable-next-line no-restricted-globals\n const socket = new WebSocket(url, protocols);\n socket.binaryType = 'arraybuffer';\n\n const { promise, resolve, reject } = createDeferredPromise();\n\n socket.addEventListener('open', () => {\n resolve();\n this.#handleEvent(snapId, {\n type: 'open',\n id,\n origin,\n });\n });\n\n socket.addEventListener('close', (event) => {\n this.#handleEvent(snapId, {\n type: 'close',\n id,\n origin,\n code: event.code,\n reason: event.reason,\n // wasClean is not available on mobile.\n wasClean: event.wasClean ?? null,\n });\n });\n\n const errorListener = () => {\n reject(\n rpcErrors.resourceUnavailable(\n 'An error occurred while opening the WebSocket.',\n ),\n );\n };\n\n socket.addEventListener('error', errorListener);\n\n socket.addEventListener('message', (event) => {\n const isText = typeof event.data === 'string';\n const data = isText\n ? { type: 'text' as const, message: event.data }\n : {\n type: 'binary' as const,\n // We request that the WebSocket gives us an array buffer.\n message: Array.from(new Uint8Array(event.data)),\n };\n\n this.#handleEvent(snapId, {\n type: 'message',\n id,\n origin,\n data,\n });\n });\n\n this.#sockets.set(id, {\n id,\n snapId,\n url,\n protocols,\n socket,\n openPromise: promise,\n });\n\n await promise;\n\n socket.removeEventListener('error', errorListener);\n\n return id;\n }\n\n /**\n * Close a given WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n */\n #close(snapId: SnapId, id: string) {\n const { socket } = this.#get(snapId, id);\n\n socket.close();\n\n this.#sockets.delete(id);\n }\n\n /**\n * Close all open connections for a given Snap ID.\n *\n * @param snapId - The Snap ID.\n */\n #closeAll(snapId: SnapId) {\n for (const socket of this.#getAll(snapId)) {\n this.#close(snapId, socket.id);\n }\n }\n\n /**\n * Send a message from a given Snap ID to a WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n * @param data - The message to send.\n */\n async #sendMessage(snapId: SnapId, id: string, data: string | number[]) {\n const { socket, openPromise } = this.#get(snapId, id);\n\n await openPromise;\n\n const wrappedData = Array.isArray(data) ? new Uint8Array(data) : data;\n\n socket.send(wrappedData);\n }\n\n /**\n * Get a list of all open WebSocket connections for a Snap ID.\n *\n * @param snapId - The Snap ID.\n * @returns A list of WebSocket connections.\n */\n #getAll(snapId: SnapId) {\n return [...this.#sockets.values()]\n .filter((socket) => socket.snapId === snapId)\n .map((socket) => ({\n id: socket.id,\n url: socket.url,\n protocols: socket.protocols,\n }));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WebSocketService.mjs","sourceRoot":"","sources":["../../src/websocket/WebSocketService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RestrictedMessenger } from '@metamask/base-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n GetWebSocketsResult,\n SnapId,\n WebSocketEvent,\n} from '@metamask/snaps-sdk';\nimport { HandlerType, isEqual, logError } from '@metamask/snaps-utils';\nimport { assert, createDeferredPromise } from '@metamask/utils';\nimport { nanoid } from 'nanoid';\n\nimport type {\n HandleSnapRequest,\n SnapInstalled,\n SnapUninstalled,\n SnapUpdated,\n} from '../snaps';\nimport { METAMASK_ORIGIN } from '../snaps';\n\nconst serviceName = 'WebSocketService';\n\nexport type WebSocketServiceOpenAction = {\n type: `${typeof serviceName}:open`;\n handler: (\n snapId: SnapId,\n url: string,\n protocols?: string[],\n ) => Promise<string>;\n};\n\nexport type WebSocketServiceCloseAction = {\n type: `${typeof serviceName}:close`;\n handler: (snapId: SnapId, id: string) => void;\n};\n\nexport type WebSocketServiceSendMessageAction = {\n type: `${typeof serviceName}:sendMessage`;\n handler: (\n snapId: SnapId,\n id: string,\n data: string | number[],\n ) => Promise<void>;\n};\n\nexport type WebSocketServiceGetAllAction = {\n type: `${typeof serviceName}:getAll`;\n handler: (snapId: SnapId) => GetWebSocketsResult;\n};\n\nexport type WebSocketServiceActions =\n | WebSocketServiceOpenAction\n | WebSocketServiceCloseAction\n | WebSocketServiceSendMessageAction\n | WebSocketServiceGetAllAction;\n\nexport type WebSocketServiceAllowedActions = HandleSnapRequest;\n\nexport type WebSocketServiceEvents =\n | SnapUninstalled\n | SnapUpdated\n | SnapInstalled;\n\nexport type WebSocketServiceMessenger = RestrictedMessenger<\n 'WebSocketService',\n WebSocketServiceActions | WebSocketServiceAllowedActions,\n WebSocketServiceEvents,\n WebSocketServiceAllowedActions['type'],\n WebSocketServiceEvents['type']\n>;\n\ntype WebSocketServiceArgs = {\n messenger: WebSocketServiceMessenger;\n};\n\ntype InternalSocket = {\n id: string;\n snapId: SnapId;\n url: string;\n protocols: string[];\n openPromise: Promise<void>;\n // eslint-disable-next-line no-restricted-globals\n socket: WebSocket;\n};\n\nexport class WebSocketService {\n name: typeof serviceName = serviceName;\n\n state = null;\n\n readonly #messenger: WebSocketServiceMessenger;\n\n readonly #sockets: Map<string, InternalSocket>;\n\n constructor({ messenger }: WebSocketServiceArgs) {\n this.#messenger = messenger;\n this.#sockets = new Map();\n\n this.#messenger.registerActionHandler(\n `${serviceName}:open`,\n async (...args) => this.#open(...args),\n );\n\n this.#messenger.registerActionHandler(`${serviceName}:close`, (...args) =>\n this.#close(...args),\n );\n\n this.#messenger.registerActionHandler(\n `${serviceName}:sendMessage`,\n async (...args) => this.#sendMessage(...args),\n );\n\n this.#messenger.registerActionHandler(`${serviceName}:getAll`, (...args) =>\n this.#getAll(...args),\n );\n\n this.#messenger.subscribe('SnapController:snapUpdated', (snap) => {\n this.#closeAll(snap.id);\n });\n\n this.#messenger.subscribe('SnapController:snapUninstalled', (snap) => {\n this.#closeAll(snap.id);\n });\n\n // Due to local Snaps not currently emitting snapUninstalled we also have to\n // listen to snapInstalled.\n this.#messenger.subscribe('SnapController:snapInstalled', (snap) => {\n this.#closeAll(snap.id);\n });\n }\n\n /**\n * Get information about a given WebSocket connection with an ID.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n * @returns Information abou the WebSocket connection.\n * @throws If the WebSocket connection cannot be found.\n */\n #get(snapId: SnapId, id: string) {\n const socket = this.#sockets.get(id);\n\n assert(\n socket && socket.snapId === snapId,\n `Socket with ID \"${id}\" not found.`,\n );\n\n return socket;\n }\n\n /**\n * Check whether a given Snap ID already has an open connection for a URL and protocol.\n *\n * @param snapId - The Snap ID.\n * @param url - The URL.\n * @param protocols - A protocols parameter.\n * @returns True if a matching connection already exists, otherwise false.\n */\n #exists(snapId: SnapId, url: string, protocols: string[]) {\n return this.#getAll(snapId).some(\n (socket) => socket.url === url && isEqual(socket.protocols, protocols),\n );\n }\n\n /**\n * Handle sending a specific WebSocketEvent to a Snap.\n *\n * @param snapId - The Snap ID.\n * @param event - The WebSocketEvent.\n */\n #handleEvent(snapId: SnapId, event: WebSocketEvent) {\n this.#messenger\n .call('SnapController:handleRequest', {\n origin: METAMASK_ORIGIN,\n snapId,\n handler: HandlerType.OnWebSocketEvent,\n request: { method: '', params: { event } },\n })\n .catch((error) => {\n logError(\n `An error occurred while handling a WebSocket message for Snap \"${snapId}\":`,\n error,\n );\n });\n }\n\n /**\n * Open a WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param url - The URL for the WebSocket connection.\n * @param protocols - An optional parameter for protocols.\n * @returns The identifier for the opened connection.\n * @throws If the connection fails.\n */\n async #open(snapId: SnapId, url: string, protocols: string[] = []) {\n assert(\n !this.#exists(snapId, url, protocols),\n `An open WebSocket connection to ${url} already exists.`,\n );\n\n const parsedUrl = new URL(url);\n const { origin } = parsedUrl;\n\n const id = nanoid();\n\n // eslint-disable-next-line no-restricted-globals\n const socket = new WebSocket(url, protocols);\n socket.binaryType = 'arraybuffer';\n\n const { promise, resolve, reject } = createDeferredPromise();\n\n socket.addEventListener('open', () => {\n resolve();\n this.#handleEvent(snapId, {\n type: 'open',\n id,\n origin,\n });\n });\n\n socket.addEventListener('close', (event) => {\n this.#sockets.delete(id);\n\n this.#handleEvent(snapId, {\n type: 'close',\n id,\n origin,\n code: event.code,\n reason: event.reason,\n // wasClean is not available on mobile.\n wasClean: event.wasClean ?? null,\n });\n });\n\n const errorListener = () => {\n reject(\n rpcErrors.resourceUnavailable(\n 'An error occurred while opening the WebSocket.',\n ),\n );\n };\n\n socket.addEventListener('error', errorListener);\n\n socket.addEventListener('message', (event) => {\n const isText = typeof event.data === 'string';\n const data = isText\n ? { type: 'text' as const, message: event.data }\n : {\n type: 'binary' as const,\n // We request that the WebSocket gives us an array buffer.\n message: Array.from(new Uint8Array(event.data)),\n };\n\n this.#handleEvent(snapId, {\n type: 'message',\n id,\n origin,\n data,\n });\n });\n\n this.#sockets.set(id, {\n id,\n snapId,\n url,\n protocols,\n socket,\n openPromise: promise,\n });\n\n await promise;\n\n socket.removeEventListener('error', errorListener);\n\n return id;\n }\n\n /**\n * Close a given WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n */\n #close(snapId: SnapId, id: string) {\n const { socket } = this.#get(snapId, id);\n\n socket.close();\n }\n\n /**\n * Close all open connections for a given Snap ID.\n *\n * @param snapId - The Snap ID.\n */\n #closeAll(snapId: SnapId) {\n for (const socket of this.#getAll(snapId)) {\n this.#close(snapId, socket.id);\n }\n }\n\n /**\n * Send a message from a given Snap ID to a WebSocket connection.\n *\n * @param snapId - The Snap ID.\n * @param id - The identifier for the WebSocket connection.\n * @param data - The message to send.\n */\n async #sendMessage(snapId: SnapId, id: string, data: string | number[]) {\n const { socket, openPromise } = this.#get(snapId, id);\n\n await openPromise;\n\n const wrappedData = Array.isArray(data) ? new Uint8Array(data) : data;\n\n socket.send(wrappedData);\n }\n\n /**\n * Get a list of all open WebSocket connections for a Snap ID.\n *\n * @param snapId - The Snap ID.\n * @returns A list of WebSocket connections.\n */\n #getAll(snapId: SnapId) {\n return [...this.#sockets.values()]\n .filter((socket) => socket.snapId === snapId)\n .map((socket) => ({\n id: socket.id,\n url: socket.url,\n protocols: socket.protocols,\n }));\n }\n}\n"]}
|