@metamask/snaps-controllers 3.4.0 → 3.5.0
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 +20 -1
- package/dist/cjs/cronjob/CronjobController.js +1 -1
- package/dist/cjs/cronjob/CronjobController.js.map +1 -1
- package/dist/cjs/services/AbstractExecutionService.js +4 -6
- package/dist/cjs/services/AbstractExecutionService.js.map +1 -1
- package/dist/cjs/snaps/SnapController.js +22 -19
- package/dist/cjs/snaps/SnapController.js.map +1 -1
- package/dist/cjs/snaps/location/npm.js +30 -7
- package/dist/cjs/snaps/location/npm.js.map +1 -1
- package/dist/cjs/snaps/registry/json.js +1 -1
- package/dist/cjs/snaps/registry/json.js.map +1 -1
- package/dist/esm/cronjob/CronjobController.js +1 -1
- package/dist/esm/cronjob/CronjobController.js.map +1 -1
- package/dist/esm/services/AbstractExecutionService.js +3 -5
- package/dist/esm/services/AbstractExecutionService.js.map +1 -1
- package/dist/esm/snaps/SnapController.js +23 -20
- package/dist/esm/snaps/SnapController.js.map +1 -1
- package/dist/esm/snaps/location/npm.js +30 -7
- package/dist/esm/snaps/location/npm.js.map +1 -1
- package/dist/esm/snaps/registry/json.js +1 -1
- package/dist/esm/snaps/registry/json.js.map +1 -1
- package/dist/types/cronjob/CronjobController.d.ts +1 -1
- package/dist/types/services/AbstractExecutionService.d.ts +1 -1
- package/dist/types/snaps/SnapController.d.ts +2 -2
- package/dist/types/snaps/location/npm.d.ts +1 -1
- package/dist/types/snaps/registry/json.d.ts +1 -1
- package/package.json +15 -14
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [3.5.0]
|
|
10
|
+
### Changed
|
|
11
|
+
- Reduce memory usage by removing source code and state from runtime ([#2009](https://github.com/MetaMask/snaps/pull/2009))
|
|
12
|
+
- Improve base64 encoding/decoding speeds ([#1985](https://github.com/MetaMask/snaps/pull/1985))
|
|
13
|
+
- Use `DecompressionStream` for NPM fetching when available ([#1971](https://github.com/MetaMask/snaps/pull/1971))
|
|
14
|
+
- Bump several MetaMask dependencies ([#1989](https://github.com/MetaMask/snaps/pull/1989), [#1993](https://github.com/MetaMask/snaps/pull/1993), [#1987](https://github.com/MetaMask/snaps/pull/1987), [#1983](https://github.com/MetaMask/snaps/pull/1983))
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Fix idle snap timeout for unused snap ([#2010](https://github.com/MetaMask/snaps/pull/2010))
|
|
18
|
+
|
|
19
|
+
## [3.4.1]
|
|
20
|
+
### Changed
|
|
21
|
+
- Bump several MetaMask dependencies ([#1964](https://github.com/MetaMask/snaps/pull/1964), [#1961](https://github.com/MetaMask/snaps/pull/1961))
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- Fix a few issues with passing non-JSON-serializable values ([#1974](https://github.com/MetaMask/snaps/pull/1974))
|
|
25
|
+
|
|
9
26
|
## [3.4.0]
|
|
10
27
|
### Changed
|
|
11
28
|
- Use `SubtleCrypto` for checksum calculation if available ([#1953](https://github.com/MetaMask/snaps/pull/1953))
|
|
@@ -119,7 +136,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
119
136
|
- The version of the package no longer needs to match the version of all other
|
|
120
137
|
MetaMask Snaps packages.
|
|
121
138
|
|
|
122
|
-
[Unreleased]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@3.
|
|
139
|
+
[Unreleased]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@3.5.0...HEAD
|
|
140
|
+
[3.5.0]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@3.4.1...@metamask/snaps-controllers@3.5.0
|
|
141
|
+
[3.4.1]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@3.4.0...@metamask/snaps-controllers@3.4.1
|
|
123
142
|
[3.4.0]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@3.3.0...@metamask/snaps-controllers@3.4.0
|
|
124
143
|
[3.3.0]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@3.2.0...@metamask/snaps-controllers@3.3.0
|
|
125
144
|
[3.2.0]: https://github.com/MetaMask/snaps/compare/@metamask/snaps-controllers@3.1.1...@metamask/snaps-controllers@3.2.0
|
|
@@ -66,7 +66,7 @@ const DAILY_TIMEOUT = (0, _utils.inMilliseconds)(24, _utils.Duration.Hour);
|
|
|
66
66
|
const controllerName = 'CronjobController';
|
|
67
67
|
var _messenger = /*#__PURE__*/ new WeakMap(), _dailyTimer = /*#__PURE__*/ new WeakMap(), _timers = /*#__PURE__*/ new WeakMap(), // Mapping from jobId to snapId
|
|
68
68
|
_snapIds = /*#__PURE__*/ new WeakMap();
|
|
69
|
-
class CronjobController extends _basecontroller.
|
|
69
|
+
class CronjobController extends _basecontroller.BaseController {
|
|
70
70
|
/**
|
|
71
71
|
* Retrieve all cronjob specifications for all runnable snaps.
|
|
72
72
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cronjob/CronjobController.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseControllerV2 as BaseController } from '@metamask/base-controller';\nimport type { GetPermissions } from '@metamask/permission-controller';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport type {\n TruncatedSnap,\n CronjobSpecification,\n} from '@metamask/snaps-utils';\nimport {\n HandlerType,\n parseCronExpression,\n logError,\n} from '@metamask/snaps-utils';\nimport { Duration, inMilliseconds } from '@metamask/utils';\n\nimport type {\n GetAllSnaps,\n HandleSnapRequest,\n SnapDisabled,\n SnapEnabled,\n SnapInstalled,\n SnapRemoved,\n SnapUpdated,\n} from '..';\nimport { getRunnableSnaps, SnapEndowments } from '..';\nimport { getCronjobCaveatJobs } from '../snaps/endowments/cronjob';\nimport { Timer } from '../snaps/Timer';\n\nexport type CronjobControllerActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions;\n\nexport type CronjobControllerEvents =\n | SnapInstalled\n | SnapRemoved\n | SnapUpdated\n | SnapEnabled\n | SnapDisabled;\n\nexport type CronjobControllerMessenger = RestrictedControllerMessenger<\n 'CronjobController',\n CronjobControllerActions,\n CronjobControllerEvents,\n CronjobControllerActions['type'],\n CronjobControllerEvents['type']\n>;\n\nexport const DAILY_TIMEOUT = inMilliseconds(24, Duration.Hour);\n\nexport type CronjobControllerArgs = {\n messenger: CronjobControllerMessenger;\n /**\n * Persisted state that will be used for rehydration.\n */\n state?: CronjobControllerState;\n};\n\nexport type Cronjob = {\n timer?: Timer;\n id: string;\n snapId: SnapId;\n} & CronjobSpecification;\n\nexport type StoredJobInformation = {\n lastRun: number;\n};\n\nexport type CronjobControllerState = {\n jobs: Record<string, StoredJobInformation>;\n};\n\nconst controllerName = 'CronjobController';\n\n/**\n * Use this controller to register and schedule periodically executed jobs\n * using RPC method hooks.\n */\nexport class CronjobController extends BaseController<\n typeof controllerName,\n CronjobControllerState,\n CronjobControllerMessenger\n> {\n #messenger: CronjobControllerMessenger;\n\n #dailyTimer!: Timer;\n\n #timers: Map<string, Timer>;\n\n // Mapping from jobId to snapId\n #snapIds: Map<string, string>;\n\n constructor({ messenger, state }: CronjobControllerArgs) {\n super({\n messenger,\n metadata: {\n jobs: { persist: true, anonymous: false },\n },\n name: controllerName,\n state: {\n jobs: {},\n ...state,\n },\n });\n this.#timers = new Map();\n this.#snapIds = new Map();\n this.#messenger = messenger;\n\n this._handleSnapRegisterEvent = this._handleSnapRegisterEvent.bind(this);\n this._handleSnapUnregisterEvent =\n this._handleSnapUnregisterEvent.bind(this);\n this._handleEventSnapUpdated = this._handleEventSnapUpdated.bind(this);\n\n // Subscribe to Snap events\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.subscribe(\n 'SnapController:snapInstalled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapRemoved',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapEnabled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapDisabled',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.dailyCheckIn().catch((error) => {\n logError(error);\n });\n }\n\n /**\n * Retrieve all cronjob specifications for all runnable snaps.\n *\n * @returns Array of Cronjob specifications.\n */\n private getAllJobs(): Cronjob[] {\n const snaps = this.messagingSystem.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(snaps);\n\n const jobs = filteredSnaps.map((snap) => this.getSnapJobs(snap.id));\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n return jobs.flat().filter((job) => job !== undefined) as Cronjob[];\n }\n\n /**\n * Retrieve all Cronjob specifications for a Snap.\n *\n * @param snapId - ID of a Snap.\n * @returns Array of Cronjob specifications.\n */\n private getSnapJobs(snapId: SnapId): Cronjob[] | undefined {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snapId,\n );\n\n const permission = permissions?.[SnapEndowments.Cronjob];\n const definitions = getCronjobCaveatJobs(permission);\n\n return definitions?.map((definition, idx) => {\n return { ...definition, id: `${snapId}-${idx}`, snapId };\n });\n }\n\n /**\n * Register cron jobs for a given snap by getting specification from a permission caveats.\n * Once registered, each job will be scheduled.\n *\n * @param snapId - ID of a snap.\n */\n register(snapId: SnapId) {\n const jobs = this.getSnapJobs(snapId);\n jobs?.forEach((job) => this.schedule(job));\n }\n\n /**\n * Schedule a new job.\n * This will interpret the cron expression and tell the timer to execute the job\n * at the next suitable point in time.\n * Job last run state will be initialized afterwards.\n *\n * Note: Schedule will be skipped if the job's execution time is too far in the future and\n * will be revisited on a daily check.\n *\n * @param job - Cronjob specification.\n */\n private schedule(job: Cronjob) {\n if (this.#timers.has(job.id)) {\n return;\n }\n\n const parsed = parseCronExpression(job.expression);\n const next = parsed.next();\n const now = new Date();\n const ms = next.getTime() - now.getTime();\n\n // Don't schedule this job yet as it is too far in the future\n if (ms > DAILY_TIMEOUT) {\n return;\n }\n\n const timer = new Timer(ms);\n timer.start(() => {\n this.executeCronjob(job).catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n\n this.#timers.delete(job.id);\n this.schedule(job);\n });\n\n if (!this.state.jobs[job.id]?.lastRun) {\n this.updateJobLastRunState(job.id, 0); // 0 for init, never ran actually\n }\n\n this.#timers.set(job.id, timer);\n this.#snapIds.set(job.id, job.snapId);\n }\n\n /**\n * Execute job.\n *\n * @param job - Cronjob specification.\n */\n private async executeCronjob(job: Cronjob) {\n this.updateJobLastRunState(job.id, Date.now());\n await this.#messenger.call('SnapController:handleRequest', {\n snapId: job.snapId,\n origin: '',\n handler: HandlerType.OnCronjob,\n request: job.request,\n });\n }\n\n /**\n * Unregister all jobs related to the given snapId.\n *\n * @param snapId - ID of a snap.\n */\n unregister(snapId: string) {\n const jobs = [...this.#snapIds.entries()].filter(\n ([_, jobSnapId]) => jobSnapId === snapId,\n );\n\n if (jobs.length) {\n jobs.forEach(([id]) => {\n const timer = this.#timers.get(id);\n if (timer) {\n timer.cancel();\n this.#timers.delete(id);\n this.#snapIds.delete(id);\n }\n });\n }\n }\n\n /**\n * Update time of a last run for the Cronjob specified by ID.\n *\n * @param jobId - ID of a cron job.\n * @param lastRun - Unix timestamp when the job was last ran.\n */\n private updateJobLastRunState(jobId: string, lastRun: number) {\n this.update((state) => {\n state.jobs[jobId] = {\n lastRun,\n };\n });\n }\n\n /**\n * Runs every 24 hours to check if new jobs need to be scheduled.\n *\n * This is necessary for longer running jobs that execute with more than 24 hours between them.\n */\n async dailyCheckIn() {\n const jobs = this.getAllJobs();\n\n for (const job of jobs) {\n const parsed = parseCronExpression(job.expression);\n const lastRun = this.state.jobs[job.id]?.lastRun;\n // If a job was supposed to run while we were shut down but wasn't we run it now\n if (\n lastRun !== undefined &&\n parsed.hasPrev() &&\n parsed.prev().getTime() > lastRun\n ) {\n await this.executeCronjob(job);\n }\n\n // Try scheduling, will fail if an existing scheduled job is found\n this.schedule(job);\n }\n\n this.#dailyTimer = new Timer(DAILY_TIMEOUT);\n this.#dailyTimer.start(() => {\n this.dailyCheckIn().catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n });\n }\n\n /**\n * Run controller teardown process and unsubscribe from Snap events.\n */\n destroy() {\n super.destroy();\n\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.unsubscribe(\n 'SnapController:snapInstalled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapRemoved',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapEnabled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapDisabled',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.#snapIds.forEach((snapId) => {\n this.unregister(snapId);\n });\n }\n\n /**\n * Handle events that should cause cronjobs to be registered.\n *\n * @param snap - Basic Snap information.\n */\n private _handleSnapRegisterEvent(snap: TruncatedSnap) {\n this.register(snap.id);\n }\n\n /**\n * Handle events that should cause cronjobs to be unregistered.\n *\n * @param snap - Basic Snap information.\n */\n private _handleSnapUnregisterEvent(snap: TruncatedSnap) {\n this.unregister(snap.id);\n }\n\n /**\n * Handle cron jobs on 'snapUpdated' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapUpdated(snap: TruncatedSnap) {\n this.unregister(snap.id);\n this.register(snap.id);\n }\n}\n"],"names":["DAILY_TIMEOUT","CronjobController","inMilliseconds","Duration","Hour","controllerName","BaseController","getAllJobs","snaps","messagingSystem","call","filteredSnaps","getRunnableSnaps","jobs","map","snap","getSnapJobs","id","flat","filter","job","undefined","snapId","permissions","messenger","permission","SnapEndowments","Cronjob","definitions","getCronjobCaveatJobs","definition","idx","register","forEach","schedule","timers","has","parsed","parseCronExpression","expression","next","now","Date","ms","getTime","timer","Timer","start","executeCronjob","catch","error","logError","delete","state","lastRun","updateJobLastRunState","set","snapIds","origin","handler","HandlerType","OnCronjob","request","unregister","entries","_","jobSnapId","length","get","cancel","jobId","update","dailyCheckIn","hasPrev","prev","dailyTimer","destroy","unsubscribe","_handleSnapRegisterEvent","_handleSnapUnregisterEvent","_handleEventSnapUpdated","constructor","metadata","persist","anonymous","name","Map","bind","subscribe"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/cronjob/CronjobController.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { GetPermissions } from '@metamask/permission-controller';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport type {\n TruncatedSnap,\n CronjobSpecification,\n} from '@metamask/snaps-utils';\nimport {\n HandlerType,\n parseCronExpression,\n logError,\n} from '@metamask/snaps-utils';\nimport { Duration, inMilliseconds } from '@metamask/utils';\n\nimport type {\n GetAllSnaps,\n HandleSnapRequest,\n SnapDisabled,\n SnapEnabled,\n SnapInstalled,\n SnapRemoved,\n SnapUpdated,\n} from '..';\nimport { getRunnableSnaps, SnapEndowments } from '..';\nimport { getCronjobCaveatJobs } from '../snaps/endowments/cronjob';\nimport { Timer } from '../snaps/Timer';\n\nexport type CronjobControllerActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions;\n\nexport type CronjobControllerEvents =\n | SnapInstalled\n | SnapRemoved\n | SnapUpdated\n | SnapEnabled\n | SnapDisabled;\n\nexport type CronjobControllerMessenger = RestrictedControllerMessenger<\n 'CronjobController',\n CronjobControllerActions,\n CronjobControllerEvents,\n CronjobControllerActions['type'],\n CronjobControllerEvents['type']\n>;\n\nexport const DAILY_TIMEOUT = inMilliseconds(24, Duration.Hour);\n\nexport type CronjobControllerArgs = {\n messenger: CronjobControllerMessenger;\n /**\n * Persisted state that will be used for rehydration.\n */\n state?: CronjobControllerState;\n};\n\nexport type Cronjob = {\n timer?: Timer;\n id: string;\n snapId: SnapId;\n} & CronjobSpecification;\n\nexport type StoredJobInformation = {\n lastRun: number;\n};\n\nexport type CronjobControllerState = {\n jobs: Record<string, StoredJobInformation>;\n};\n\nconst controllerName = 'CronjobController';\n\n/**\n * Use this controller to register and schedule periodically executed jobs\n * using RPC method hooks.\n */\nexport class CronjobController extends BaseController<\n typeof controllerName,\n CronjobControllerState,\n CronjobControllerMessenger\n> {\n #messenger: CronjobControllerMessenger;\n\n #dailyTimer!: Timer;\n\n #timers: Map<string, Timer>;\n\n // Mapping from jobId to snapId\n #snapIds: Map<string, string>;\n\n constructor({ messenger, state }: CronjobControllerArgs) {\n super({\n messenger,\n metadata: {\n jobs: { persist: true, anonymous: false },\n },\n name: controllerName,\n state: {\n jobs: {},\n ...state,\n },\n });\n this.#timers = new Map();\n this.#snapIds = new Map();\n this.#messenger = messenger;\n\n this._handleSnapRegisterEvent = this._handleSnapRegisterEvent.bind(this);\n this._handleSnapUnregisterEvent =\n this._handleSnapUnregisterEvent.bind(this);\n this._handleEventSnapUpdated = this._handleEventSnapUpdated.bind(this);\n\n // Subscribe to Snap events\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.subscribe(\n 'SnapController:snapInstalled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapRemoved',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapEnabled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapDisabled',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.dailyCheckIn().catch((error) => {\n logError(error);\n });\n }\n\n /**\n * Retrieve all cronjob specifications for all runnable snaps.\n *\n * @returns Array of Cronjob specifications.\n */\n private getAllJobs(): Cronjob[] {\n const snaps = this.messagingSystem.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(snaps);\n\n const jobs = filteredSnaps.map((snap) => this.getSnapJobs(snap.id));\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n return jobs.flat().filter((job) => job !== undefined) as Cronjob[];\n }\n\n /**\n * Retrieve all Cronjob specifications for a Snap.\n *\n * @param snapId - ID of a Snap.\n * @returns Array of Cronjob specifications.\n */\n private getSnapJobs(snapId: SnapId): Cronjob[] | undefined {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snapId,\n );\n\n const permission = permissions?.[SnapEndowments.Cronjob];\n const definitions = getCronjobCaveatJobs(permission);\n\n return definitions?.map((definition, idx) => {\n return { ...definition, id: `${snapId}-${idx}`, snapId };\n });\n }\n\n /**\n * Register cron jobs for a given snap by getting specification from a permission caveats.\n * Once registered, each job will be scheduled.\n *\n * @param snapId - ID of a snap.\n */\n register(snapId: SnapId) {\n const jobs = this.getSnapJobs(snapId);\n jobs?.forEach((job) => this.schedule(job));\n }\n\n /**\n * Schedule a new job.\n * This will interpret the cron expression and tell the timer to execute the job\n * at the next suitable point in time.\n * Job last run state will be initialized afterwards.\n *\n * Note: Schedule will be skipped if the job's execution time is too far in the future and\n * will be revisited on a daily check.\n *\n * @param job - Cronjob specification.\n */\n private schedule(job: Cronjob) {\n if (this.#timers.has(job.id)) {\n return;\n }\n\n const parsed = parseCronExpression(job.expression);\n const next = parsed.next();\n const now = new Date();\n const ms = next.getTime() - now.getTime();\n\n // Don't schedule this job yet as it is too far in the future\n if (ms > DAILY_TIMEOUT) {\n return;\n }\n\n const timer = new Timer(ms);\n timer.start(() => {\n this.executeCronjob(job).catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n\n this.#timers.delete(job.id);\n this.schedule(job);\n });\n\n if (!this.state.jobs[job.id]?.lastRun) {\n this.updateJobLastRunState(job.id, 0); // 0 for init, never ran actually\n }\n\n this.#timers.set(job.id, timer);\n this.#snapIds.set(job.id, job.snapId);\n }\n\n /**\n * Execute job.\n *\n * @param job - Cronjob specification.\n */\n private async executeCronjob(job: Cronjob) {\n this.updateJobLastRunState(job.id, Date.now());\n await this.#messenger.call('SnapController:handleRequest', {\n snapId: job.snapId,\n origin: '',\n handler: HandlerType.OnCronjob,\n request: job.request,\n });\n }\n\n /**\n * Unregister all jobs related to the given snapId.\n *\n * @param snapId - ID of a snap.\n */\n unregister(snapId: string) {\n const jobs = [...this.#snapIds.entries()].filter(\n ([_, jobSnapId]) => jobSnapId === snapId,\n );\n\n if (jobs.length) {\n jobs.forEach(([id]) => {\n const timer = this.#timers.get(id);\n if (timer) {\n timer.cancel();\n this.#timers.delete(id);\n this.#snapIds.delete(id);\n }\n });\n }\n }\n\n /**\n * Update time of a last run for the Cronjob specified by ID.\n *\n * @param jobId - ID of a cron job.\n * @param lastRun - Unix timestamp when the job was last ran.\n */\n private updateJobLastRunState(jobId: string, lastRun: number) {\n this.update((state) => {\n state.jobs[jobId] = {\n lastRun,\n };\n });\n }\n\n /**\n * Runs every 24 hours to check if new jobs need to be scheduled.\n *\n * This is necessary for longer running jobs that execute with more than 24 hours between them.\n */\n async dailyCheckIn() {\n const jobs = this.getAllJobs();\n\n for (const job of jobs) {\n const parsed = parseCronExpression(job.expression);\n const lastRun = this.state.jobs[job.id]?.lastRun;\n // If a job was supposed to run while we were shut down but wasn't we run it now\n if (\n lastRun !== undefined &&\n parsed.hasPrev() &&\n parsed.prev().getTime() > lastRun\n ) {\n await this.executeCronjob(job);\n }\n\n // Try scheduling, will fail if an existing scheduled job is found\n this.schedule(job);\n }\n\n this.#dailyTimer = new Timer(DAILY_TIMEOUT);\n this.#dailyTimer.start(() => {\n this.dailyCheckIn().catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n });\n }\n\n /**\n * Run controller teardown process and unsubscribe from Snap events.\n */\n destroy() {\n super.destroy();\n\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.unsubscribe(\n 'SnapController:snapInstalled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapRemoved',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapEnabled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapDisabled',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.#snapIds.forEach((snapId) => {\n this.unregister(snapId);\n });\n }\n\n /**\n * Handle events that should cause cronjobs to be registered.\n *\n * @param snap - Basic Snap information.\n */\n private _handleSnapRegisterEvent(snap: TruncatedSnap) {\n this.register(snap.id);\n }\n\n /**\n * Handle events that should cause cronjobs to be unregistered.\n *\n * @param snap - Basic Snap information.\n */\n private _handleSnapUnregisterEvent(snap: TruncatedSnap) {\n this.unregister(snap.id);\n }\n\n /**\n * Handle cron jobs on 'snapUpdated' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapUpdated(snap: TruncatedSnap) {\n this.unregister(snap.id);\n this.register(snap.id);\n }\n}\n"],"names":["DAILY_TIMEOUT","CronjobController","inMilliseconds","Duration","Hour","controllerName","BaseController","getAllJobs","snaps","messagingSystem","call","filteredSnaps","getRunnableSnaps","jobs","map","snap","getSnapJobs","id","flat","filter","job","undefined","snapId","permissions","messenger","permission","SnapEndowments","Cronjob","definitions","getCronjobCaveatJobs","definition","idx","register","forEach","schedule","timers","has","parsed","parseCronExpression","expression","next","now","Date","ms","getTime","timer","Timer","start","executeCronjob","catch","error","logError","delete","state","lastRun","updateJobLastRunState","set","snapIds","origin","handler","HandlerType","OnCronjob","request","unregister","entries","_","jobSnapId","length","get","cancel","jobId","update","dailyCheckIn","hasPrev","prev","dailyTimer","destroy","unsubscribe","_handleSnapRegisterEvent","_handleSnapUnregisterEvent","_handleEventSnapUpdated","constructor","metadata","persist","anonymous","name","Map","bind","subscribe"],"mappings":"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"}
|
|
@@ -23,7 +23,7 @@ const _snapsutils = require("@metamask/snaps-utils");
|
|
|
23
23
|
const _utils = require("@metamask/utils");
|
|
24
24
|
const _jsonrpcmiddlewarestream = require("json-rpc-middleware-stream");
|
|
25
25
|
const _nanoid = require("nanoid");
|
|
26
|
-
const
|
|
26
|
+
const _readablestream = require("readable-stream");
|
|
27
27
|
const _logging = require("../logging");
|
|
28
28
|
const _utils1 = require("../utils");
|
|
29
29
|
function _check_private_redeclaration(obj, privateCollection) {
|
|
@@ -157,7 +157,7 @@ class AbstractExecutionService {
|
|
|
157
157
|
const { streams, worker } = await this.initStreams(jobId);
|
|
158
158
|
const rpcEngine = new _jsonrpcengine.JsonRpcEngine();
|
|
159
159
|
const jsonRpcConnection = (0, _jsonrpcmiddlewarestream.createStreamMiddleware)();
|
|
160
|
-
(0,
|
|
160
|
+
(0, _readablestream.pipeline)(jsonRpcConnection.stream, streams.command, jsonRpcConnection.stream, (error)=>{
|
|
161
161
|
if (error) {
|
|
162
162
|
(0, _snapsutils.logError)(`Command stream failure.`, error);
|
|
163
163
|
}
|
|
@@ -278,9 +278,7 @@ class AbstractExecutionService {
|
|
|
278
278
|
}
|
|
279
279
|
// Cannot be hash private yet because of tests.
|
|
280
280
|
async command(jobId, message) {
|
|
281
|
-
|
|
282
|
-
throw new Error('Must send object.');
|
|
283
|
-
}
|
|
281
|
+
(0, _utils.assertIsJsonRpcRequest)(message);
|
|
284
282
|
const job = this.jobs.get(jobId);
|
|
285
283
|
if (!job) {
|
|
286
284
|
throw new Error(`Job with id "${jobId}" not found.`);
|
|
@@ -378,7 +376,7 @@ function removeSnapAndJobMapping(jobId) {
|
|
|
378
376
|
}
|
|
379
377
|
function setupMultiplex(connectionStream, streamName) {
|
|
380
378
|
const mux = new _objectmultiplex.default();
|
|
381
|
-
(0,
|
|
379
|
+
(0, _readablestream.pipeline)(connectionStream, mux, connectionStream, (error)=>{
|
|
382
380
|
if (error) {
|
|
383
381
|
streamName ? (0, _snapsutils.logError)(`"${streamName}" stream failure.`, error) : (0, _snapsutils.logError)(error);
|
|
384
382
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/services/AbstractExecutionService.ts"],"sourcesContent":["import { JsonRpcEngine } from '@metamask/json-rpc-engine';\nimport ObjectMultiplex from '@metamask/object-multiplex';\nimport type { BasePostMessageStream } from '@metamask/post-message-stream';\nimport { JsonRpcError } from '@metamask/rpc-errors';\nimport type { SnapRpcHook, SnapRpcHookArgs } from '@metamask/snaps-utils';\nimport { SNAP_STREAM_NAMES, logError } from '@metamask/snaps-utils';\nimport type {\n Json,\n JsonRpcNotification,\n JsonRpcRequest,\n PendingJsonRpcResponse,\n} from '@metamask/utils';\nimport { Duration, isJsonRpcNotification, isObject } from '@metamask/utils';\nimport { createStreamMiddleware } from 'json-rpc-middleware-stream';\nimport { nanoid } from 'nanoid';\nimport { pipeline } from 'stream';\nimport type { Duplex } from 'stream';\n\nimport { log } from '../logging';\nimport { hasTimedOut, withTimeout } from '../utils';\nimport type {\n ExecutionService,\n ExecutionServiceMessenger,\n SnapErrorJson,\n SnapExecutionData,\n} from './ExecutionService';\n\nconst controllerName = 'ExecutionService';\n\nexport type SetupSnapProvider = (snapId: string, stream: Duplex) => void;\n\nexport type ExecutionServiceArgs = {\n setupSnapProvider: SetupSnapProvider;\n messenger: ExecutionServiceMessenger;\n terminationTimeout?: number;\n};\n\nexport type JobStreams = {\n command: Duplex;\n rpc: Duplex;\n _connection: BasePostMessageStream;\n};\n\nexport type Job<WorkerType> = {\n id: string;\n streams: JobStreams;\n rpcEngine: JsonRpcEngine;\n worker: WorkerType;\n};\n\nexport abstract class AbstractExecutionService<WorkerType>\n implements ExecutionService\n{\n #snapRpcHooks: Map<string, SnapRpcHook>;\n\n // Cannot be hash private yet because of tests.\n protected jobs: Map<string, Job<WorkerType>>;\n\n // Cannot be hash private yet because of tests.\n private readonly setupSnapProvider: SetupSnapProvider;\n\n #snapToJobMap: Map<string, string>;\n\n #jobToSnapMap: Map<string, string>;\n\n #messenger: ExecutionServiceMessenger;\n\n #terminationTimeout: number;\n\n constructor({\n setupSnapProvider,\n messenger,\n terminationTimeout = Duration.Second,\n }: ExecutionServiceArgs) {\n this.#snapRpcHooks = new Map();\n this.jobs = new Map();\n this.setupSnapProvider = setupSnapProvider;\n this.#snapToJobMap = new Map();\n this.#jobToSnapMap = new Map();\n this.#messenger = messenger;\n this.#terminationTimeout = terminationTimeout;\n\n this.registerMessageHandlers();\n }\n\n /**\n * Constructor helper for registering the controller's messaging system\n * actions.\n */\n private registerMessageHandlers(): void {\n this.#messenger.registerActionHandler(\n `${controllerName}:handleRpcRequest`,\n async (snapId: string, options: SnapRpcHookArgs) =>\n this.handleRpcRequest(snapId, options),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:executeSnap`,\n async (snapData: SnapExecutionData) => this.executeSnap(snapData),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateSnap`,\n async (snapId: string) => this.terminateSnap(snapId),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateAllSnaps`,\n async () => this.terminateAllSnaps(),\n );\n }\n\n /**\n * Performs additional necessary work during job termination. **MUST** be\n * implemented by concrete implementations. See\n * {@link AbstractExecutionService.terminate} for details.\n *\n * @param job - The object corresponding to the job to be terminated.\n */\n protected abstract terminateJob(job: Job<WorkerType>): void;\n\n /**\n * Terminates the job with the specified ID and deletes all its associated\n * data. Any subsequent messages targeting the job will fail with an error.\n * Throws an error if the specified job does not exist, or if termination\n * fails unexpectedly.\n *\n * @param jobId - The id of the job to be terminated.\n */\n public async terminate(jobId: string): Promise<void> {\n const jobWrapper = this.jobs.get(jobId);\n if (!jobWrapper) {\n throw new Error(`Job with id \"${jobId}\" not found.`);\n }\n\n // Ping worker and tell it to run teardown, continue with termination if it takes too long\n const result = await withTimeout(\n this.command(jobId, {\n jsonrpc: '2.0',\n method: 'terminate',\n params: [],\n id: nanoid(),\n }),\n this.#terminationTimeout,\n );\n\n if (result === hasTimedOut || result !== 'OK') {\n // We tried to shutdown gracefully but failed. This probably means the Snap is in infinite loop and\n // hogging down the whole JS process.\n // TODO(ritave): It might be doing weird things such as posting a lot of setTimeouts. Add a test to ensure that this behaviour\n // doesn't leak into other workers. Especially important in IframeExecutionEnvironment since they all share the same\n // JS process.\n logError(`Job \"${jobId}\" failed to terminate gracefully.`, result);\n }\n\n Object.values(jobWrapper.streams).forEach((stream) => {\n try {\n !stream.destroyed && stream.destroy();\n stream.removeAllListeners();\n } catch (error) {\n logError('Error while destroying stream', error);\n }\n });\n\n this.terminateJob(jobWrapper);\n\n this.#removeSnapAndJobMapping(jobId);\n this.jobs.delete(jobId);\n log(`Job \"${jobId}\" terminated.`);\n }\n\n /**\n * Initiates a job for a snap.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n *\n * @returns Information regarding the created job.\n */\n protected async initJob(): Promise<Job<WorkerType>> {\n const jobId = nanoid();\n const { streams, worker } = await this.initStreams(jobId);\n const rpcEngine = new JsonRpcEngine();\n\n const jsonRpcConnection = createStreamMiddleware();\n\n pipeline(\n jsonRpcConnection.stream,\n streams.command,\n jsonRpcConnection.stream,\n (error) => {\n if (error) {\n logError(`Command stream failure.`, error);\n }\n },\n );\n\n rpcEngine.push(jsonRpcConnection.middleware);\n\n const envMetadata = {\n id: jobId,\n streams,\n rpcEngine,\n worker,\n };\n this.jobs.set(jobId, envMetadata);\n\n return envMetadata;\n }\n\n /**\n * Sets up the streams for an initiated job.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n *\n * @param jobId - The id of the job.\n * @returns The streams to communicate with the worker and the worker itself.\n */\n protected async initStreams(\n jobId: string,\n ): Promise<{ streams: JobStreams; worker: WorkerType }> {\n const { worker, stream: envStream } = await this.initEnvStream(jobId);\n const mux = setupMultiplex(envStream, `Job: \"${jobId}\"`);\n const commandStream = mux.createStream(SNAP_STREAM_NAMES.COMMAND);\n\n // Handle out-of-band errors, i.e. errors thrown from the snap outside of the req/res cycle.\n // Also keep track of outbound request/responses\n const notificationHandler = (\n message:\n | JsonRpcRequest\n | JsonRpcNotification<Json[] | Record<string, Json>>,\n ) => {\n if (!isJsonRpcNotification(message)) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const snapId = this.#jobToSnapMap.get(jobId)!;\n if (message.method === 'OutboundRequest') {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n } else if (message.method === 'OutboundResponse') {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n } else if (message.method === 'UnhandledError') {\n if (isObject(message.params) && message.params.error) {\n this.#messenger.publish(\n 'ExecutionService:unhandledError',\n snapId,\n message.params.error as SnapErrorJson,\n );\n commandStream.removeListener('data', notificationHandler);\n } else {\n logError(\n new Error(\n `Received malformed \"${message.method}\" command stream notification.`,\n ),\n );\n }\n } else {\n logError(\n new Error(\n `Received unexpected command stream notification \"${message.method}\".`,\n ),\n );\n }\n };\n\n commandStream.on('data', notificationHandler);\n const rpcStream = mux.createStream(SNAP_STREAM_NAMES.JSON_RPC);\n\n // Typecast: stream type mismatch\n return {\n streams: {\n command: commandStream,\n rpc: rpcStream,\n // eslint-disable-next-line @typescript-eslint/naming-convention\n _connection: envStream,\n },\n worker,\n };\n }\n\n /**\n * Abstract function implemented by implementing class that spins up a new worker for a job.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n */\n protected abstract initEnvStream(jobId: string): Promise<{\n worker: WorkerType;\n stream: BasePostMessageStream;\n }>;\n\n /**\n * Terminates the Snap with the specified ID. May throw an error if\n * termination unexpectedly fails, but will not fail if no job for the snap\n * with the specified ID is found.\n *\n * @param snapId - The ID of the snap to terminate.\n */\n async terminateSnap(snapId: string) {\n const jobId = this.#snapToJobMap.get(snapId);\n if (jobId) {\n await this.terminate(jobId);\n }\n }\n\n async terminateAllSnaps() {\n await Promise.all(\n [...this.jobs.keys()].map(async (jobId) => this.terminate(jobId)),\n );\n this.#snapRpcHooks.clear();\n }\n\n /**\n * Gets the RPC request handler for the given snap.\n *\n * @param snapId - The id of the Snap whose message handler to get.\n * @returns The RPC request handler for the snap.\n */\n private getRpcRequestHandler(snapId: string) {\n return this.#snapRpcHooks.get(snapId);\n }\n\n /**\n * Initializes and executes a snap, setting up the communication channels to the snap etc.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n *\n * @param snapData - Data needed for Snap execution.\n * @returns A string `OK` if execution succeeded.\n * @throws If the execution service returns an error.\n */\n async executeSnap(snapData: SnapExecutionData): Promise<string> {\n if (this.#snapToJobMap.has(snapData.snapId)) {\n throw new Error(`Snap \"${snapData.snapId}\" is already being executed.`);\n }\n\n const job = await this.initJob();\n this.#mapSnapAndJob(snapData.snapId, job.id);\n\n // Ping the worker to ensure that it started up\n await this.command(job.id, {\n jsonrpc: '2.0',\n method: 'ping',\n id: nanoid(),\n });\n\n const rpcStream = job.streams.rpc;\n\n this.setupSnapProvider(snapData.snapId, rpcStream);\n\n const result = await this.command(job.id, {\n jsonrpc: '2.0',\n method: 'executeSnap',\n params: snapData,\n id: nanoid(),\n });\n this.#createSnapHooks(snapData.snapId, job.id);\n return result as string;\n }\n\n // Cannot be hash private yet because of tests.\n private async command(\n jobId: string,\n message: JsonRpcRequest,\n ): Promise<Json | undefined> {\n if (typeof message !== 'object') {\n throw new Error('Must send object.');\n }\n\n const job = this.jobs.get(jobId);\n if (!job) {\n throw new Error(`Job with id \"${jobId}\" not found.`);\n }\n\n log('Parent: Sending Command', message);\n const response: PendingJsonRpcResponse<Json> = await job.rpcEngine.handle(\n message,\n );\n\n if (response.error) {\n throw new JsonRpcError(\n response.error.code,\n response.error.message,\n response.error.data,\n );\n }\n\n return response.result;\n }\n\n #removeSnapHooks(snapId: string) {\n this.#snapRpcHooks.delete(snapId);\n }\n\n #createSnapHooks(snapId: string, workerId: string) {\n const rpcHook = async ({ origin, handler, request }: SnapRpcHookArgs) => {\n return await this.command(workerId, {\n id: nanoid(),\n jsonrpc: '2.0',\n method: 'snapRpc',\n params: {\n origin,\n handler,\n request: request as JsonRpcRequest,\n target: snapId,\n },\n });\n };\n\n this.#snapRpcHooks.set(snapId, rpcHook);\n }\n\n #mapSnapAndJob(snapId: string, jobId: string): void {\n this.#snapToJobMap.set(snapId, jobId);\n this.#jobToSnapMap.set(jobId, snapId);\n }\n\n #removeSnapAndJobMapping(jobId: string): void {\n const snapId = this.#jobToSnapMap.get(jobId);\n if (!snapId) {\n throw new Error(`job: \"${jobId}\" has no mapped snap.`);\n }\n\n this.#jobToSnapMap.delete(jobId);\n this.#snapToJobMap.delete(snapId);\n this.#removeSnapHooks(snapId);\n }\n\n /**\n * Handle RPC request.\n *\n * @param snapId - The ID of the recipient snap.\n * @param options - Bag of options to pass to the RPC handler.\n * @returns Promise that can handle the request.\n */\n public async handleRpcRequest(\n snapId: string,\n options: SnapRpcHookArgs,\n ): Promise<unknown> {\n const rpcRequestHandler = this.getRpcRequestHandler(snapId);\n\n if (!rpcRequestHandler) {\n throw new Error(\n `Snap execution service returned no RPC handler for running snap \"${snapId}\".`,\n );\n }\n\n return rpcRequestHandler(options);\n }\n}\n\n/**\n * Sets up stream multiplexing for the given stream.\n *\n * @param connectionStream - The stream to mux.\n * @param streamName - The name of the stream, for identification in errors.\n * @returns The multiplexed stream.\n */\nexport function setupMultiplex(\n connectionStream: Duplex,\n streamName: string,\n): ObjectMultiplex {\n const mux = new ObjectMultiplex();\n pipeline(connectionStream, mux, connectionStream, (error) => {\n if (error) {\n streamName\n ? logError(`\"${streamName}\" stream failure.`, error)\n : logError(error);\n }\n });\n return mux;\n}\n"],"names":["AbstractExecutionService","setupMultiplex","controllerName","registerMessageHandlers","messenger","registerActionHandler","snapId","options","handleRpcRequest","snapData","executeSnap","terminateSnap","terminateAllSnaps","terminate","jobId","jobWrapper","jobs","get","Error","result","withTimeout","command","jsonrpc","method","params","id","nanoid","terminationTimeout","hasTimedOut","logError","Object","values","streams","forEach","stream","destroyed","destroy","removeAllListeners","error","terminateJob","removeSnapAndJobMapping","delete","log","initJob","worker","initStreams","rpcEngine","JsonRpcEngine","jsonRpcConnection","createStreamMiddleware","pipeline","push","middleware","envMetadata","set","envStream","initEnvStream","mux","commandStream","createStream","SNAP_STREAM_NAMES","COMMAND","notificationHandler","message","isJsonRpcNotification","jobToSnapMap","publish","isObject","removeListener","on","rpcStream","JSON_RPC","rpc","_connection","snapToJobMap","Promise","all","keys","map","snapRpcHooks","clear","getRpcRequestHandler","has","job","mapSnapAndJob","setupSnapProvider","createSnapHooks","response","handle","JsonRpcError","code","data","rpcRequestHandler","constructor","Duration","Second","Map","workerId","rpcHook","origin","handler","request","target","removeSnapHooks","connectionStream","streamName","ObjectMultiplex"],"mappings":";;;;;;;;;;;IAkDsBA,wBAAwB;eAAxBA;;IAuZNC,cAAc;eAAdA;;;+BAzcc;wEACF;2BAEC;4BAEe;uBAOc;yCACnB;wBAChB;wBACE;yBAGL;wBACqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQzC,MAAMC,iBAAiB;IA0BrB,6CAQA,6CAEA,6CAEA,0CAEA,mDAkUA,gDAIA,gDAkBA,8CAKA;AA9WK,MAAeF;IAmCpB;;;GAGC,GACD,AAAQG,0BAAgC;QACtC,yBAAA,IAAI,EAAEC,YAAUC,qBAAqB,CACnC,CAAC,EAAEH,eAAe,iBAAiB,CAAC,EACpC,OAAOI,QAAgBC,UACrB,IAAI,CAACC,gBAAgB,CAACF,QAAQC;QAGlC,yBAAA,IAAI,EAAEH,YAAUC,qBAAqB,CACnC,CAAC,EAAEH,eAAe,YAAY,CAAC,EAC/B,OAAOO,WAAgC,IAAI,CAACC,WAAW,CAACD;QAG1D,yBAAA,IAAI,EAAEL,YAAUC,qBAAqB,CACnC,CAAC,EAAEH,eAAe,cAAc,CAAC,EACjC,OAAOI,SAAmB,IAAI,CAACK,aAAa,CAACL;QAG/C,yBAAA,IAAI,EAAEF,YAAUC,qBAAqB,CACnC,CAAC,EAAEH,eAAe,kBAAkB,CAAC,EACrC,UAAY,IAAI,CAACU,iBAAiB;IAEtC;IAWA;;;;;;;GAOC,GACD,MAAaC,UAAUC,KAAa,EAAiB;QACnD,MAAMC,aAAa,IAAI,CAACC,IAAI,CAACC,GAAG,CAACH;QACjC,IAAI,CAACC,YAAY;YACf,MAAM,IAAIG,MAAM,CAAC,aAAa,EAAEJ,MAAM,YAAY,CAAC;QACrD;QAEA,0FAA0F;QAC1F,MAAMK,SAAS,MAAMC,IAAAA,mBAAW,EAC9B,IAAI,CAACC,OAAO,CAACP,OAAO;YAClBQ,SAAS;YACTC,QAAQ;YACRC,QAAQ,EAAE;YACVC,IAAIC,IAAAA,cAAM;QACZ,6BACA,IAAI,EAAEC;QAGR,IAAIR,WAAWS,mBAAW,IAAIT,WAAW,MAAM;YAC7C,mGAAmG;YACnG,qCAAqC;YACrC,8HAA8H;YAC9H,kIAAkI;YAClI,4BAA4B;YAC5BU,IAAAA,oBAAQ,EAAC,CAAC,KAAK,EAAEf,MAAM,iCAAiC,CAAC,EAAEK;QAC7D;QAEAW,OAAOC,MAAM,CAAChB,WAAWiB,OAAO,EAAEC,OAAO,CAAC,CAACC;YACzC,IAAI;gBACF,CAACA,OAAOC,SAAS,IAAID,OAAOE,OAAO;gBACnCF,OAAOG,kBAAkB;YAC3B,EAAE,OAAOC,OAAO;gBACdT,IAAAA,oBAAQ,EAAC,iCAAiCS;YAC5C;QACF;QAEA,IAAI,CAACC,YAAY,CAACxB;QAElB,0BAAA,IAAI,EAAEyB,0BAAAA,8BAAN,IAAI,EAA0B1B;QAC9B,IAAI,CAACE,IAAI,CAACyB,MAAM,CAAC3B;QACjB4B,IAAAA,YAAG,EAAC,CAAC,KAAK,EAAE5B,MAAM,aAAa,CAAC;IAClC;IAEA;;;;;;GAMC,GACD,MAAgB6B,UAAoC;QAClD,MAAM7B,QAAQY,IAAAA,cAAM;QACpB,MAAM,EAAEM,OAAO,EAAEY,MAAM,EAAE,GAAG,MAAM,IAAI,CAACC,WAAW,CAAC/B;QACnD,MAAMgC,YAAY,IAAIC,4BAAa;QAEnC,MAAMC,oBAAoBC,IAAAA,+CAAsB;QAEhDC,IAAAA,gBAAQ,EACNF,kBAAkBd,MAAM,EACxBF,QAAQX,OAAO,EACf2B,kBAAkBd,MAAM,EACxB,CAACI;YACC,IAAIA,OAAO;gBACTT,IAAAA,oBAAQ,EAAC,CAAC,uBAAuB,CAAC,EAAES;YACtC;QACF;QAGFQ,UAAUK,IAAI,CAACH,kBAAkBI,UAAU;QAE3C,MAAMC,cAAc;YAClB5B,IAAIX;YACJkB;YACAc;YACAF;QACF;QACA,IAAI,CAAC5B,IAAI,CAACsC,GAAG,CAACxC,OAAOuC;QAErB,OAAOA;IACT;IAEA;;;;;;;GAOC,GACD,MAAgBR,YACd/B,KAAa,EACyC;QACtD,MAAM,EAAE8B,MAAM,EAAEV,QAAQqB,SAAS,EAAE,GAAG,MAAM,IAAI,CAACC,aAAa,CAAC1C;QAC/D,MAAM2C,MAAMxD,eAAesD,WAAW,CAAC,MAAM,EAAEzC,MAAM,CAAC,CAAC;QACvD,MAAM4C,gBAAgBD,IAAIE,YAAY,CAACC,6BAAiB,CAACC,OAAO;QAEhE,4FAA4F;QAC5F,gDAAgD;QAChD,MAAMC,sBAAsB,CAC1BC;YAIA,IAAI,CAACC,IAAAA,4BAAqB,EAACD,UAAU;gBACnC;YACF;YAEA,oEAAoE;YACpE,MAAMzD,SAAS,yBAAA,IAAI,EAAE2D,eAAahD,GAAG,CAACH;YACtC,IAAIiD,QAAQxC,MAAM,KAAK,mBAAmB;gBACxC,yBAAA,IAAI,EAAEnB,YAAU8D,OAAO,CAAC,oCAAoC5D;YAC9D,OAAO,IAAIyD,QAAQxC,MAAM,KAAK,oBAAoB;gBAChD,yBAAA,IAAI,EAAEnB,YAAU8D,OAAO,CAAC,qCAAqC5D;YAC/D,OAAO,IAAIyD,QAAQxC,MAAM,KAAK,kBAAkB;gBAC9C,IAAI4C,IAAAA,eAAQ,EAACJ,QAAQvC,MAAM,KAAKuC,QAAQvC,MAAM,CAACc,KAAK,EAAE;oBACpD,yBAAA,IAAI,EAAElC,YAAU8D,OAAO,CACrB,mCACA5D,QACAyD,QAAQvC,MAAM,CAACc,KAAK;oBAEtBoB,cAAcU,cAAc,CAAC,QAAQN;gBACvC,OAAO;oBACLjC,IAAAA,oBAAQ,EACN,IAAIX,MACF,CAAC,oBAAoB,EAAE6C,QAAQxC,MAAM,CAAC,8BAA8B,CAAC;gBAG3E;YACF,OAAO;gBACLM,IAAAA,oBAAQ,EACN,IAAIX,MACF,CAAC,iDAAiD,EAAE6C,QAAQxC,MAAM,CAAC,EAAE,CAAC;YAG5E;QACF;QAEAmC,cAAcW,EAAE,CAAC,QAAQP;QACzB,MAAMQ,YAAYb,IAAIE,YAAY,CAACC,6BAAiB,CAACW,QAAQ;QAE7D,iCAAiC;QACjC,OAAO;YACLvC,SAAS;gBACPX,SAASqC;gBACTc,KAAKF;gBACL,gEAAgE;gBAChEG,aAAalB;YACf;YACAX;QACF;IACF;IAYA;;;;;;GAMC,GACD,MAAMjC,cAAcL,MAAc,EAAE;QAClC,MAAMQ,QAAQ,yBAAA,IAAI,EAAE4D,eAAazD,GAAG,CAACX;QACrC,IAAIQ,OAAO;YACT,MAAM,IAAI,CAACD,SAAS,CAACC;QACvB;IACF;IAEA,MAAMF,oBAAoB;QACxB,MAAM+D,QAAQC,GAAG,CACf;eAAI,IAAI,CAAC5D,IAAI,CAAC6D,IAAI;SAAG,CAACC,GAAG,CAAC,OAAOhE,QAAU,IAAI,CAACD,SAAS,CAACC;QAE5D,yBAAA,IAAI,EAAEiE,eAAaC,KAAK;IAC1B;IAEA;;;;;GAKC,GACD,AAAQC,qBAAqB3E,MAAc,EAAE;QAC3C,OAAO,yBAAA,IAAI,EAAEyE,eAAa9D,GAAG,CAACX;IAChC;IAEA;;;;;;;;GAQC,GACD,MAAMI,YAAYD,QAA2B,EAAmB;QAC9D,IAAI,yBAAA,IAAI,EAAEiE,eAAaQ,GAAG,CAACzE,SAASH,MAAM,GAAG;YAC3C,MAAM,IAAIY,MAAM,CAAC,MAAM,EAAET,SAASH,MAAM,CAAC,4BAA4B,CAAC;QACxE;QAEA,MAAM6E,MAAM,MAAM,IAAI,CAACxC,OAAO;QAC9B,0BAAA,IAAI,EAAEyC,gBAAAA,oBAAN,IAAI,EAAgB3E,SAASH,MAAM,EAAE6E,IAAI1D,EAAE;QAE3C,+CAA+C;QAC/C,MAAM,IAAI,CAACJ,OAAO,CAAC8D,IAAI1D,EAAE,EAAE;YACzBH,SAAS;YACTC,QAAQ;YACRE,IAAIC,IAAAA,cAAM;QACZ;QAEA,MAAM4C,YAAYa,IAAInD,OAAO,CAACwC,GAAG;QAEjC,IAAI,CAACa,iBAAiB,CAAC5E,SAASH,MAAM,EAAEgE;QAExC,MAAMnD,SAAS,MAAM,IAAI,CAACE,OAAO,CAAC8D,IAAI1D,EAAE,EAAE;YACxCH,SAAS;YACTC,QAAQ;YACRC,QAAQf;YACRgB,IAAIC,IAAAA,cAAM;QACZ;QACA,0BAAA,IAAI,EAAE4D,kBAAAA,sBAAN,IAAI,EAAkB7E,SAASH,MAAM,EAAE6E,IAAI1D,EAAE;QAC7C,OAAON;IACT;IAEA,+CAA+C;IAC/C,MAAcE,QACZP,KAAa,EACbiD,OAAuB,EACI;QAC3B,IAAI,OAAOA,YAAY,UAAU;YAC/B,MAAM,IAAI7C,MAAM;QAClB;QAEA,MAAMiE,MAAM,IAAI,CAACnE,IAAI,CAACC,GAAG,CAACH;QAC1B,IAAI,CAACqE,KAAK;YACR,MAAM,IAAIjE,MAAM,CAAC,aAAa,EAAEJ,MAAM,YAAY,CAAC;QACrD;QAEA4B,IAAAA,YAAG,EAAC,2BAA2BqB;QAC/B,MAAMwB,WAAyC,MAAMJ,IAAIrC,SAAS,CAAC0C,MAAM,CACvEzB;QAGF,IAAIwB,SAASjD,KAAK,EAAE;YAClB,MAAM,IAAImD,uBAAY,CACpBF,SAASjD,KAAK,CAACoD,IAAI,EACnBH,SAASjD,KAAK,CAACyB,OAAO,EACtBwB,SAASjD,KAAK,CAACqD,IAAI;QAEvB;QAEA,OAAOJ,SAASpE,MAAM;IACxB;IAwCA;;;;;;GAMC,GACD,MAAaX,iBACXF,MAAc,EACdC,OAAwB,EACN;QAClB,MAAMqF,oBAAoB,IAAI,CAACX,oBAAoB,CAAC3E;QAEpD,IAAI,CAACsF,mBAAmB;YACtB,MAAM,IAAI1E,MACR,CAAC,iEAAiE,EAAEZ,OAAO,EAAE,CAAC;QAElF;QAEA,OAAOsF,kBAAkBrF;IAC3B;IA1XAsF,YAAY,EACVR,iBAAiB,EACjBjF,SAAS,EACTuB,qBAAqBmE,eAAQ,CAACC,MAAM,EACf,CAAE;QA4TzB,iCAAA;QAIA,iCAAA;QAkBA,iCAAA;QAKA,iCAAA;QA3WA,gCAAA;;mBAAA,KAAA;;QAEA,+CAA+C;QAC/C,uBAAU/E,QAAV,KAAA;QAEA,+CAA+C;QAC/C,uBAAiBqE,qBAAjB,KAAA;QAEA,gCAAA;;mBAAA,KAAA;;QAEA,gCAAA;;mBAAA,KAAA;;QAEA,gCAAA;;mBAAA,KAAA;;QAEA,gCAAA;;mBAAA,KAAA;;uCAOQN,eAAe,IAAIiB;QACzB,IAAI,CAAChF,IAAI,GAAG,IAAIgF;QAChB,IAAI,CAACX,iBAAiB,GAAGA;uCACnBX,eAAe,IAAIsB;uCACnB/B,eAAe,IAAI+B;uCACnB5F,YAAYA;uCACZuB,qBAAqBA;QAE3B,IAAI,CAACxB,uBAAuB;IAC9B;AA6WF;AA3DE,SAAA,gBAAiBG,MAAc;IAC7B,yBAAA,IAAI,EAAEyE,eAAatC,MAAM,CAACnC;AAC5B;AAEA,SAAA,gBAAiBA,MAAc,EAAE2F,QAAgB;IAC/C,MAAMC,UAAU,OAAO,EAAEC,MAAM,EAAEC,OAAO,EAAEC,OAAO,EAAmB;QAClE,OAAO,MAAM,IAAI,CAAChF,OAAO,CAAC4E,UAAU;YAClCxE,IAAIC,IAAAA,cAAM;YACVJ,SAAS;YACTC,QAAQ;YACRC,QAAQ;gBACN2E;gBACAC;gBACAC,SAASA;gBACTC,QAAQhG;YACV;QACF;IACF;IAEA,yBAAA,IAAI,EAAEyE,eAAazB,GAAG,CAAChD,QAAQ4F;AACjC;AAEA,SAAA,cAAe5F,MAAc,EAAEQ,KAAa;IAC1C,yBAAA,IAAI,EAAE4D,eAAapB,GAAG,CAAChD,QAAQQ;IAC/B,yBAAA,IAAI,EAAEmD,eAAaX,GAAG,CAACxC,OAAOR;AAChC;AAEA,SAAA,wBAAyBQ,KAAa;IACpC,MAAMR,SAAS,yBAAA,IAAI,EAAE2D,eAAahD,GAAG,CAACH;IACtC,IAAI,CAACR,QAAQ;QACX,MAAM,IAAIY,MAAM,CAAC,MAAM,EAAEJ,MAAM,qBAAqB,CAAC;IACvD;IAEA,yBAAA,IAAI,EAAEmD,eAAaxB,MAAM,CAAC3B;IAC1B,yBAAA,IAAI,EAAE4D,eAAajC,MAAM,CAACnC;IAC1B,0BAAA,IAAI,EAAEiG,kBAAAA,sBAAN,IAAI,EAAkBjG;AACxB;AAgCK,SAASL,eACduG,gBAAwB,EACxBC,UAAkB;IAElB,MAAMhD,MAAM,IAAIiD,wBAAe;IAC/BxD,IAAAA,gBAAQ,EAACsD,kBAAkB/C,KAAK+C,kBAAkB,CAAClE;QACjD,IAAIA,OAAO;YACTmE,aACI5E,IAAAA,oBAAQ,EAAC,CAAC,CAAC,EAAE4E,WAAW,iBAAiB,CAAC,EAAEnE,SAC5CT,IAAAA,oBAAQ,EAACS;QACf;IACF;IACA,OAAOmB;AACT"}
|
|
1
|
+
{"version":3,"sources":["../../../src/services/AbstractExecutionService.ts"],"sourcesContent":["import { JsonRpcEngine } from '@metamask/json-rpc-engine';\nimport ObjectMultiplex from '@metamask/object-multiplex';\nimport type { BasePostMessageStream } from '@metamask/post-message-stream';\nimport { JsonRpcError } from '@metamask/rpc-errors';\nimport type { SnapRpcHook, SnapRpcHookArgs } from '@metamask/snaps-utils';\nimport { SNAP_STREAM_NAMES, logError } from '@metamask/snaps-utils';\nimport type {\n Json,\n JsonRpcNotification,\n JsonRpcRequest,\n PendingJsonRpcResponse,\n} from '@metamask/utils';\nimport {\n Duration,\n assertIsJsonRpcRequest,\n isJsonRpcNotification,\n isObject,\n} from '@metamask/utils';\nimport { createStreamMiddleware } from 'json-rpc-middleware-stream';\nimport { nanoid } from 'nanoid';\nimport { pipeline } from 'readable-stream';\nimport type { Duplex } from 'readable-stream';\n\nimport { log } from '../logging';\nimport { hasTimedOut, withTimeout } from '../utils';\nimport type {\n ExecutionService,\n ExecutionServiceMessenger,\n SnapErrorJson,\n SnapExecutionData,\n} from './ExecutionService';\n\nconst controllerName = 'ExecutionService';\n\nexport type SetupSnapProvider = (snapId: string, stream: Duplex) => void;\n\nexport type ExecutionServiceArgs = {\n setupSnapProvider: SetupSnapProvider;\n messenger: ExecutionServiceMessenger;\n terminationTimeout?: number;\n};\n\nexport type JobStreams = {\n command: Duplex;\n rpc: Duplex;\n _connection: BasePostMessageStream;\n};\n\nexport type Job<WorkerType> = {\n id: string;\n streams: JobStreams;\n rpcEngine: JsonRpcEngine;\n worker: WorkerType;\n};\n\nexport abstract class AbstractExecutionService<WorkerType>\n implements ExecutionService\n{\n #snapRpcHooks: Map<string, SnapRpcHook>;\n\n // Cannot be hash private yet because of tests.\n protected jobs: Map<string, Job<WorkerType>>;\n\n // Cannot be hash private yet because of tests.\n private readonly setupSnapProvider: SetupSnapProvider;\n\n #snapToJobMap: Map<string, string>;\n\n #jobToSnapMap: Map<string, string>;\n\n #messenger: ExecutionServiceMessenger;\n\n #terminationTimeout: number;\n\n constructor({\n setupSnapProvider,\n messenger,\n terminationTimeout = Duration.Second,\n }: ExecutionServiceArgs) {\n this.#snapRpcHooks = new Map();\n this.jobs = new Map();\n this.setupSnapProvider = setupSnapProvider;\n this.#snapToJobMap = new Map();\n this.#jobToSnapMap = new Map();\n this.#messenger = messenger;\n this.#terminationTimeout = terminationTimeout;\n\n this.registerMessageHandlers();\n }\n\n /**\n * Constructor helper for registering the controller's messaging system\n * actions.\n */\n private registerMessageHandlers(): void {\n this.#messenger.registerActionHandler(\n `${controllerName}:handleRpcRequest`,\n async (snapId: string, options: SnapRpcHookArgs) =>\n this.handleRpcRequest(snapId, options),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:executeSnap`,\n async (snapData: SnapExecutionData) => this.executeSnap(snapData),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateSnap`,\n async (snapId: string) => this.terminateSnap(snapId),\n );\n\n this.#messenger.registerActionHandler(\n `${controllerName}:terminateAllSnaps`,\n async () => this.terminateAllSnaps(),\n );\n }\n\n /**\n * Performs additional necessary work during job termination. **MUST** be\n * implemented by concrete implementations. See\n * {@link AbstractExecutionService.terminate} for details.\n *\n * @param job - The object corresponding to the job to be terminated.\n */\n protected abstract terminateJob(job: Job<WorkerType>): void;\n\n /**\n * Terminates the job with the specified ID and deletes all its associated\n * data. Any subsequent messages targeting the job will fail with an error.\n * Throws an error if the specified job does not exist, or if termination\n * fails unexpectedly.\n *\n * @param jobId - The id of the job to be terminated.\n */\n public async terminate(jobId: string): Promise<void> {\n const jobWrapper = this.jobs.get(jobId);\n if (!jobWrapper) {\n throw new Error(`Job with id \"${jobId}\" not found.`);\n }\n\n // Ping worker and tell it to run teardown, continue with termination if it takes too long\n const result = await withTimeout(\n this.command(jobId, {\n jsonrpc: '2.0',\n method: 'terminate',\n params: [],\n id: nanoid(),\n }),\n this.#terminationTimeout,\n );\n\n if (result === hasTimedOut || result !== 'OK') {\n // We tried to shutdown gracefully but failed. This probably means the Snap is in infinite loop and\n // hogging down the whole JS process.\n // TODO(ritave): It might be doing weird things such as posting a lot of setTimeouts. Add a test to ensure that this behaviour\n // doesn't leak into other workers. Especially important in IframeExecutionEnvironment since they all share the same\n // JS process.\n logError(`Job \"${jobId}\" failed to terminate gracefully.`, result);\n }\n\n Object.values(jobWrapper.streams).forEach((stream) => {\n try {\n !stream.destroyed && stream.destroy();\n stream.removeAllListeners();\n } catch (error) {\n logError('Error while destroying stream', error);\n }\n });\n\n this.terminateJob(jobWrapper);\n\n this.#removeSnapAndJobMapping(jobId);\n this.jobs.delete(jobId);\n log(`Job \"${jobId}\" terminated.`);\n }\n\n /**\n * Initiates a job for a snap.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n *\n * @returns Information regarding the created job.\n */\n protected async initJob(): Promise<Job<WorkerType>> {\n const jobId = nanoid();\n const { streams, worker } = await this.initStreams(jobId);\n const rpcEngine = new JsonRpcEngine();\n\n const jsonRpcConnection = createStreamMiddleware();\n\n pipeline(\n jsonRpcConnection.stream,\n streams.command,\n jsonRpcConnection.stream,\n (error) => {\n if (error) {\n logError(`Command stream failure.`, error);\n }\n },\n );\n\n rpcEngine.push(jsonRpcConnection.middleware);\n\n const envMetadata = {\n id: jobId,\n streams,\n rpcEngine,\n worker,\n };\n this.jobs.set(jobId, envMetadata);\n\n return envMetadata;\n }\n\n /**\n * Sets up the streams for an initiated job.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n *\n * @param jobId - The id of the job.\n * @returns The streams to communicate with the worker and the worker itself.\n */\n protected async initStreams(\n jobId: string,\n ): Promise<{ streams: JobStreams; worker: WorkerType }> {\n const { worker, stream: envStream } = await this.initEnvStream(jobId);\n const mux = setupMultiplex(envStream, `Job: \"${jobId}\"`);\n const commandStream = mux.createStream(SNAP_STREAM_NAMES.COMMAND);\n\n // Handle out-of-band errors, i.e. errors thrown from the snap outside of the req/res cycle.\n // Also keep track of outbound request/responses\n const notificationHandler = (\n message:\n | JsonRpcRequest\n | JsonRpcNotification<Json[] | Record<string, Json>>,\n ) => {\n if (!isJsonRpcNotification(message)) {\n return;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const snapId = this.#jobToSnapMap.get(jobId)!;\n if (message.method === 'OutboundRequest') {\n this.#messenger.publish('ExecutionService:outboundRequest', snapId);\n } else if (message.method === 'OutboundResponse') {\n this.#messenger.publish('ExecutionService:outboundResponse', snapId);\n } else if (message.method === 'UnhandledError') {\n if (isObject(message.params) && message.params.error) {\n this.#messenger.publish(\n 'ExecutionService:unhandledError',\n snapId,\n message.params.error as SnapErrorJson,\n );\n commandStream.removeListener('data', notificationHandler);\n } else {\n logError(\n new Error(\n `Received malformed \"${message.method}\" command stream notification.`,\n ),\n );\n }\n } else {\n logError(\n new Error(\n `Received unexpected command stream notification \"${message.method}\".`,\n ),\n );\n }\n };\n\n commandStream.on('data', notificationHandler);\n const rpcStream = mux.createStream(SNAP_STREAM_NAMES.JSON_RPC);\n\n // Typecast: stream type mismatch\n return {\n streams: {\n command: commandStream,\n rpc: rpcStream,\n // eslint-disable-next-line @typescript-eslint/naming-convention\n _connection: envStream,\n },\n worker,\n };\n }\n\n /**\n * Abstract function implemented by implementing class that spins up a new worker for a job.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n */\n protected abstract initEnvStream(jobId: string): Promise<{\n worker: WorkerType;\n stream: BasePostMessageStream;\n }>;\n\n /**\n * Terminates the Snap with the specified ID. May throw an error if\n * termination unexpectedly fails, but will not fail if no job for the snap\n * with the specified ID is found.\n *\n * @param snapId - The ID of the snap to terminate.\n */\n async terminateSnap(snapId: string) {\n const jobId = this.#snapToJobMap.get(snapId);\n if (jobId) {\n await this.terminate(jobId);\n }\n }\n\n async terminateAllSnaps() {\n await Promise.all(\n [...this.jobs.keys()].map(async (jobId) => this.terminate(jobId)),\n );\n this.#snapRpcHooks.clear();\n }\n\n /**\n * Gets the RPC request handler for the given snap.\n *\n * @param snapId - The id of the Snap whose message handler to get.\n * @returns The RPC request handler for the snap.\n */\n private getRpcRequestHandler(snapId: string) {\n return this.#snapRpcHooks.get(snapId);\n }\n\n /**\n * Initializes and executes a snap, setting up the communication channels to the snap etc.\n *\n * Depending on the execution environment, this may run forever if the Snap fails to start up properly, therefore any call to this function should be wrapped in a timeout.\n *\n * @param snapData - Data needed for Snap execution.\n * @returns A string `OK` if execution succeeded.\n * @throws If the execution service returns an error.\n */\n async executeSnap(snapData: SnapExecutionData): Promise<string> {\n if (this.#snapToJobMap.has(snapData.snapId)) {\n throw new Error(`Snap \"${snapData.snapId}\" is already being executed.`);\n }\n\n const job = await this.initJob();\n this.#mapSnapAndJob(snapData.snapId, job.id);\n\n // Ping the worker to ensure that it started up\n await this.command(job.id, {\n jsonrpc: '2.0',\n method: 'ping',\n id: nanoid(),\n });\n\n const rpcStream = job.streams.rpc;\n\n this.setupSnapProvider(snapData.snapId, rpcStream);\n\n const result = await this.command(job.id, {\n jsonrpc: '2.0',\n method: 'executeSnap',\n params: snapData,\n id: nanoid(),\n });\n this.#createSnapHooks(snapData.snapId, job.id);\n return result as string;\n }\n\n // Cannot be hash private yet because of tests.\n private async command(\n jobId: string,\n message: JsonRpcRequest,\n ): Promise<Json | undefined> {\n assertIsJsonRpcRequest(message);\n\n const job = this.jobs.get(jobId);\n if (!job) {\n throw new Error(`Job with id \"${jobId}\" not found.`);\n }\n\n log('Parent: Sending Command', message);\n const response: PendingJsonRpcResponse<Json> = await job.rpcEngine.handle(\n message,\n );\n\n if (response.error) {\n throw new JsonRpcError(\n response.error.code,\n response.error.message,\n response.error.data,\n );\n }\n\n return response.result;\n }\n\n #removeSnapHooks(snapId: string) {\n this.#snapRpcHooks.delete(snapId);\n }\n\n #createSnapHooks(snapId: string, workerId: string) {\n const rpcHook = async ({ origin, handler, request }: SnapRpcHookArgs) => {\n return await this.command(workerId, {\n id: nanoid(),\n jsonrpc: '2.0',\n method: 'snapRpc',\n params: {\n origin,\n handler,\n request: request as JsonRpcRequest,\n target: snapId,\n },\n });\n };\n\n this.#snapRpcHooks.set(snapId, rpcHook);\n }\n\n #mapSnapAndJob(snapId: string, jobId: string): void {\n this.#snapToJobMap.set(snapId, jobId);\n this.#jobToSnapMap.set(jobId, snapId);\n }\n\n #removeSnapAndJobMapping(jobId: string): void {\n const snapId = this.#jobToSnapMap.get(jobId);\n if (!snapId) {\n throw new Error(`job: \"${jobId}\" has no mapped snap.`);\n }\n\n this.#jobToSnapMap.delete(jobId);\n this.#snapToJobMap.delete(snapId);\n this.#removeSnapHooks(snapId);\n }\n\n /**\n * Handle RPC request.\n *\n * @param snapId - The ID of the recipient snap.\n * @param options - Bag of options to pass to the RPC handler.\n * @returns Promise that can handle the request.\n */\n public async handleRpcRequest(\n snapId: string,\n options: SnapRpcHookArgs,\n ): Promise<unknown> {\n const rpcRequestHandler = this.getRpcRequestHandler(snapId);\n\n if (!rpcRequestHandler) {\n throw new Error(\n `Snap execution service returned no RPC handler for running snap \"${snapId}\".`,\n );\n }\n\n return rpcRequestHandler(options);\n }\n}\n\n/**\n * Sets up stream multiplexing for the given stream.\n *\n * @param connectionStream - The stream to mux.\n * @param streamName - The name of the stream, for identification in errors.\n * @returns The multiplexed stream.\n */\nexport function setupMultiplex(\n connectionStream: Duplex,\n streamName: string,\n): ObjectMultiplex {\n const mux = new ObjectMultiplex();\n pipeline(connectionStream, mux, connectionStream, (error) => {\n if (error) {\n streamName\n ? logError(`\"${streamName}\" stream failure.`, error)\n : logError(error);\n }\n });\n return mux;\n}\n"],"names":["AbstractExecutionService","setupMultiplex","controllerName","registerMessageHandlers","messenger","registerActionHandler","snapId","options","handleRpcRequest","snapData","executeSnap","terminateSnap","terminateAllSnaps","terminate","jobId","jobWrapper","jobs","get","Error","result","withTimeout","command","jsonrpc","method","params","id","nanoid","terminationTimeout","hasTimedOut","logError","Object","values","streams","forEach","stream","destroyed","destroy","removeAllListeners","error","terminateJob","removeSnapAndJobMapping","delete","log","initJob","worker","initStreams","rpcEngine","JsonRpcEngine","jsonRpcConnection","createStreamMiddleware","pipeline","push","middleware","envMetadata","set","envStream","initEnvStream","mux","commandStream","createStream","SNAP_STREAM_NAMES","COMMAND","notificationHandler","message","isJsonRpcNotification","jobToSnapMap","publish","isObject","removeListener","on","rpcStream","JSON_RPC","rpc","_connection","snapToJobMap","Promise","all","keys","map","snapRpcHooks","clear","getRpcRequestHandler","has","job","mapSnapAndJob","setupSnapProvider","createSnapHooks","assertIsJsonRpcRequest","response","handle","JsonRpcError","code","data","rpcRequestHandler","constructor","Duration","Second","Map","workerId","rpcHook","origin","handler","request","target","removeSnapHooks","connectionStream","streamName","ObjectMultiplex"],"mappings":"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"}
|
|
@@ -221,7 +221,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
221
221
|
* @param newVersionRange - The new version range being requsted.
|
|
222
222
|
* @returns `true` if validation checks pass and `false` if they do not.
|
|
223
223
|
*/ _isValidUpdate = /*#__PURE__*/ new WeakSet(), _callLifecycleHook = /*#__PURE__*/ new WeakSet();
|
|
224
|
-
class SnapController extends _basecontroller.
|
|
224
|
+
class SnapController extends _basecontroller.BaseController {
|
|
225
225
|
/**
|
|
226
226
|
* Checks all installed snaps against the block list and
|
|
227
227
|
* blocks/unblocks snaps as appropriate. See {@link SnapController.blockSnap}
|
|
@@ -446,7 +446,7 @@ class SnapController extends _basecontroller.BaseControllerV2 {
|
|
|
446
446
|
* @param path - The path to the requested file.
|
|
447
447
|
* @param encoding - An optional requested file encoding.
|
|
448
448
|
* @returns The file requested in the chosen file encoding or null if the file is not found.
|
|
449
|
-
*/ getSnapFile(snapId, path, encoding = _snapssdk.AuxiliaryFileEncoding.Base64) {
|
|
449
|
+
*/ async getSnapFile(snapId, path, encoding = _snapssdk.AuxiliaryFileEncoding.Base64) {
|
|
450
450
|
const snap = this.getExpect(snapId);
|
|
451
451
|
const normalizedPath = (0, _snapsutils.normalizeRelative)(path);
|
|
452
452
|
const value = snap.auxiliaryFiles?.find((file)=>file.path === normalizedPath)?.value;
|
|
@@ -1174,10 +1174,7 @@ class SnapController extends _basecontroller.BaseControllerV2 {
|
|
|
1174
1174
|
});
|
|
1175
1175
|
_class_private_method_get(this, _initializeStateMachine, initializeStateMachine).call(this);
|
|
1176
1176
|
_class_private_method_get(this, _registerMessageHandlers, registerMessageHandlers).call(this);
|
|
1177
|
-
Object.values(state?.snaps ?? {}).forEach((snap)=>_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snap.id
|
|
1178
|
-
sourceCode: snap.sourceCode,
|
|
1179
|
-
state: state?.snapStates?.[snap.id] ?? null
|
|
1180
|
-
}));
|
|
1177
|
+
Object.values(state?.snaps ?? {}).forEach((snap)=>_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snap.id));
|
|
1181
1178
|
}
|
|
1182
1179
|
}
|
|
1183
1180
|
function initializeStateMachine() {
|
|
@@ -1252,7 +1249,7 @@ function registerMessageHandlers() {
|
|
|
1252
1249
|
this.messagingSystem.registerActionHandler(`${controllerName}:getRegistryMetadata`, async (...args)=>this.getRegistryMetadata(...args));
|
|
1253
1250
|
this.messagingSystem.registerActionHandler(`${controllerName}:disconnectOrigin`, (...args)=>this.removeSnapFromSubject(...args));
|
|
1254
1251
|
this.messagingSystem.registerActionHandler(`${controllerName}:revokeDynamicPermissions`, (...args)=>this.revokeDynamicSnapPermissions(...args));
|
|
1255
|
-
this.messagingSystem.registerActionHandler(`${controllerName}:getFile`, (...args)=>this.getSnapFile(...args));
|
|
1252
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:getFile`, async (...args)=>this.getSnapFile(...args));
|
|
1256
1253
|
}
|
|
1257
1254
|
function pollForLastRequestStatus() {
|
|
1258
1255
|
_class_private_field_set(this, _timeoutForLastRequestStatus, setTimeout(()=>{
|
|
@@ -1303,8 +1300,7 @@ async function stopSnapsLastRequestPastMax() {
|
|
|
1303
1300
|
const entries = [
|
|
1304
1301
|
..._class_private_field_get(this, _snapsRuntimeData).entries()
|
|
1305
1302
|
];
|
|
1306
|
-
return Promise.all(entries.filter(([_snapId, runtime])=>runtime.activeReferences === 0 && runtime.pendingInboundRequests.length === 0 &&
|
|
1307
|
-
runtime.lastRequest && _class_private_field_get(this, _maxIdleTime) && (0, _utils.timeSince)(runtime.lastRequest) > _class_private_field_get(this, _maxIdleTime)).map(async ([snapId])=>this.stopSnap(snapId, _snapsutils.SnapStatusEvents.Stop)));
|
|
1303
|
+
return Promise.all(entries.filter(([_snapId, runtime])=>runtime.activeReferences === 0 && runtime.pendingInboundRequests.length === 0 && runtime.lastRequest && _class_private_field_get(this, _maxIdleTime) && (0, _utils.timeSince)(runtime.lastRequest) > _class_private_field_get(this, _maxIdleTime)).map(async ([snapId])=>this.stopSnap(snapId, _snapsutils.SnapStatusEvents.Stop)));
|
|
1308
1304
|
}
|
|
1309
1305
|
function transition(snapId, event) {
|
|
1310
1306
|
const { interpreter } = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
|
|
@@ -1367,10 +1363,7 @@ async function resolveAllowlistVersion(snapId, versionRange) {
|
|
|
1367
1363
|
}
|
|
1368
1364
|
async function add(args) {
|
|
1369
1365
|
const { id: snapId, location, versionRange } = args;
|
|
1370
|
-
_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snapId
|
|
1371
|
-
sourceCode: null,
|
|
1372
|
-
state: null
|
|
1373
|
-
});
|
|
1366
|
+
_class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snapId);
|
|
1374
1367
|
const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
|
|
1375
1368
|
if (!runtime.installPromise) {
|
|
1376
1369
|
(0, _logging.log)(`Adding snap: ${snapId}`);
|
|
@@ -1408,11 +1401,14 @@ async function startSnap(snapData) {
|
|
|
1408
1401
|
throw new Error(`Snap "${snapId}" is already started.`);
|
|
1409
1402
|
}
|
|
1410
1403
|
try {
|
|
1404
|
+
const runtime = _class_private_method_get(this, _getRuntimeExpect, getRuntimeExpect).call(this, snapId);
|
|
1411
1405
|
const result = await _class_private_method_get(this, _executeWithTimeout, executeWithTimeout).call(this, this.messagingSystem.call('ExecutionService:executeSnap', {
|
|
1412
1406
|
...snapData,
|
|
1413
1407
|
endowments: await _class_private_method_get(this, _getEndowments, getEndowments).call(this, snapId)
|
|
1414
1408
|
}));
|
|
1415
1409
|
_class_private_method_get(this, _transition, transition).call(this, snapId, _snapsutils.SnapStatusEvents.Start);
|
|
1410
|
+
// We treat the initialization of the snap as the first request, for idle timing purposes.
|
|
1411
|
+
runtime.lastRequest = Date.now();
|
|
1416
1412
|
return result;
|
|
1417
1413
|
} catch (error) {
|
|
1418
1414
|
await _class_private_method_get(this, _terminateSnap, terminateSnap).call(this, snapId);
|
|
@@ -1454,10 +1450,13 @@ function set(args) {
|
|
|
1454
1450
|
const { version } = manifest.result;
|
|
1455
1451
|
const sourceCode = sourceCodeFile.toString();
|
|
1456
1452
|
(0, _utils.assert)(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
|
|
1457
|
-
const auxiliaryFiles = rawAuxiliaryFiles.map((file)=>
|
|
1453
|
+
const auxiliaryFiles = rawAuxiliaryFiles.map((file)=>{
|
|
1454
|
+
(0, _utils.assert)(typeof file.data.base64 === 'string');
|
|
1455
|
+
return {
|
|
1458
1456
|
path: file.path,
|
|
1459
|
-
value: file.
|
|
1460
|
-
}
|
|
1457
|
+
value: file.data.base64
|
|
1458
|
+
};
|
|
1459
|
+
});
|
|
1461
1460
|
const snapsState = this.state.snaps;
|
|
1462
1461
|
const existingSnap = snapsState[snapId];
|
|
1463
1462
|
const previousVersionHistory = existingSnap?.versionHistory ?? [];
|
|
@@ -1513,6 +1512,11 @@ async function fetchSnap(snapId, location) {
|
|
|
1513
1512
|
const { iconPath } = manifest.result.source.location.npm;
|
|
1514
1513
|
const svgIcon = iconPath ? await location.fetch(iconPath) : undefined;
|
|
1515
1514
|
const auxiliaryFiles = await (0, _utils1.getSnapFiles)(location, manifest.result.source.files);
|
|
1515
|
+
await Promise.all(auxiliaryFiles.map(async (file)=>{
|
|
1516
|
+
// This should still be safe
|
|
1517
|
+
// eslint-disable-next-line require-atomic-updates
|
|
1518
|
+
file.data.base64 = await (0, _snapsutils.encodeBase64)(file);
|
|
1519
|
+
}));
|
|
1516
1520
|
const localizationFiles = await (0, _utils1.getSnapFiles)(location, manifest.result.source.locales);
|
|
1517
1521
|
const validatedLocalizationFiles = (0, _snapsutils.getValidatedLocalizationFiles)(localizationFiles);
|
|
1518
1522
|
const files = {
|
|
@@ -1724,7 +1728,7 @@ function getRuntimeExpect(snapId) {
|
|
|
1724
1728
|
(0, _utils.assert)(runtime !== undefined, new Error(`Snap "${snapId}" runtime data not found`));
|
|
1725
1729
|
return runtime;
|
|
1726
1730
|
}
|
|
1727
|
-
function setupRuntime(snapId
|
|
1731
|
+
function setupRuntime(snapId) {
|
|
1728
1732
|
if (_class_private_field_get(this, _snapsRuntimeData).has(snapId)) {
|
|
1729
1733
|
return;
|
|
1730
1734
|
}
|
|
@@ -1744,8 +1748,7 @@ function setupRuntime(snapId, data) {
|
|
|
1744
1748
|
activeReferences: 0,
|
|
1745
1749
|
pendingInboundRequests: [],
|
|
1746
1750
|
pendingOutboundRequests: 0,
|
|
1747
|
-
interpreter
|
|
1748
|
-
...data
|
|
1751
|
+
interpreter
|
|
1749
1752
|
});
|
|
1750
1753
|
}
|
|
1751
1754
|
function calculatePermissionsChange(snapId, desiredPermissionsSet) {
|