@metamask/snaps-controllers 0.24.1 → 0.26.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.
Files changed (88) hide show
  1. package/dist/cronjob/CronjobController.d.ts +3 -2
  2. package/dist/cronjob/CronjobController.js +37 -23
  3. package/dist/cronjob/CronjobController.js.map +1 -1
  4. package/dist/fsm.js +1 -1
  5. package/dist/fsm.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/multichain/MultiChainController.d.ts +3 -1
  9. package/dist/multichain/MultiChainController.js +8 -8
  10. package/dist/multichain/MultiChainController.js.map +1 -1
  11. package/dist/multichain/middleware.d.ts +1 -1
  12. package/dist/multichain/middleware.js +2 -2
  13. package/dist/multichain/middleware.js.map +1 -1
  14. package/dist/services/AbstractExecutionService.d.ts +3 -3
  15. package/dist/services/AbstractExecutionService.js +13 -12
  16. package/dist/services/AbstractExecutionService.js.map +1 -1
  17. package/dist/services/ExecutionService.d.ts +1 -1
  18. package/dist/services/ExecutionService.js.map +1 -1
  19. package/dist/services/iframe/IframeExecutionService.js +1 -1
  20. package/dist/services/iframe/IframeExecutionService.js.map +1 -1
  21. package/dist/services/iframe/test/server.js +6 -6
  22. package/dist/services/iframe/test/server.js.map +1 -1
  23. package/dist/services/node/NodeProcessExecutionService.d.ts +1 -1
  24. package/dist/services/node/NodeProcessExecutionService.js +2 -2
  25. package/dist/services/node/NodeProcessExecutionService.js.map +1 -1
  26. package/dist/services/node/NodeThreadExecutionService.d.ts +2 -2
  27. package/dist/services/node/NodeThreadExecutionService.js +5 -4
  28. package/dist/services/node/NodeThreadExecutionService.js.map +1 -1
  29. package/dist/snaps/SnapController.d.ts +37 -47
  30. package/dist/snaps/SnapController.js +306 -228
  31. package/dist/snaps/SnapController.js.map +1 -1
  32. package/dist/snaps/endowments/cronjob.d.ts +2 -2
  33. package/dist/snaps/endowments/cronjob.js +3 -3
  34. package/dist/snaps/endowments/cronjob.js.map +1 -1
  35. package/dist/snaps/endowments/enum.d.ts +2 -1
  36. package/dist/snaps/endowments/enum.js +1 -0
  37. package/dist/snaps/endowments/enum.js.map +1 -1
  38. package/dist/snaps/endowments/ethereum-provider.d.ts +1 -1
  39. package/dist/snaps/endowments/ethereum-provider.js +2 -2
  40. package/dist/snaps/endowments/ethereum-provider.js.map +1 -1
  41. package/dist/snaps/endowments/index.d.ts +33 -19
  42. package/dist/snaps/endowments/index.js +16 -4
  43. package/dist/snaps/endowments/index.js.map +1 -1
  44. package/dist/snaps/endowments/keyring.d.ts +1 -1
  45. package/dist/snaps/endowments/keyring.js +2 -2
  46. package/dist/snaps/endowments/keyring.js.map +1 -1
  47. package/dist/snaps/endowments/long-running.d.ts +1 -1
  48. package/dist/snaps/endowments/long-running.js +2 -2
  49. package/dist/snaps/endowments/long-running.js.map +1 -1
  50. package/dist/snaps/endowments/network-access.d.ts +1 -1
  51. package/dist/snaps/endowments/network-access.js +2 -2
  52. package/dist/snaps/endowments/network-access.js.map +1 -1
  53. package/dist/snaps/endowments/rpc.d.ts +35 -0
  54. package/dist/snaps/endowments/rpc.js +91 -0
  55. package/dist/snaps/endowments/rpc.js.map +1 -0
  56. package/dist/snaps/endowments/transaction-insight.d.ts +3 -3
  57. package/dist/snaps/endowments/transaction-insight.js +3 -3
  58. package/dist/snaps/endowments/transaction-insight.js.map +1 -1
  59. package/dist/snaps/index.d.ts +0 -1
  60. package/dist/snaps/index.js +0 -1
  61. package/dist/snaps/index.js.map +1 -1
  62. package/dist/snaps/location/http.d.ts +21 -0
  63. package/dist/snaps/location/http.js +71 -0
  64. package/dist/snaps/location/http.js.map +1 -0
  65. package/dist/snaps/location/index.d.ts +4 -0
  66. package/dist/snaps/{utils → location}/index.js +3 -1
  67. package/dist/snaps/location/index.js.map +1 -0
  68. package/dist/snaps/location/local.d.ts +10 -0
  69. package/dist/snaps/location/local.js +51 -0
  70. package/dist/snaps/location/local.js.map +1 -0
  71. package/dist/snaps/location/location.d.ts +32 -0
  72. package/dist/snaps/location/location.js +33 -0
  73. package/dist/snaps/location/location.js.map +1 -0
  74. package/dist/snaps/location/npm.d.ts +33 -0
  75. package/dist/snaps/location/npm.js +232 -0
  76. package/dist/snaps/location/npm.js.map +1 -0
  77. package/dist/utils.d.ts +10 -3
  78. package/dist/utils.js +17 -1
  79. package/dist/utils.js.map +1 -1
  80. package/package.json +25 -20
  81. package/dist/snaps/utils/index.d.ts +0 -2
  82. package/dist/snaps/utils/index.js.map +0 -1
  83. package/dist/snaps/utils/npm.d.ts +0 -14
  84. package/dist/snaps/utils/npm.js +0 -81
  85. package/dist/snaps/utils/npm.js.map +0 -1
  86. package/dist/snaps/utils/stream.d.ts +0 -30
  87. package/dist/snaps/utils/stream.js +0 -124
  88. package/dist/snaps/utils/stream.js.map +0 -1
@@ -21,25 +21,24 @@ var __rest = (this && this.__rest) || function (s, e) {
21
21
  }
22
22
  return t;
23
23
  };
24
- var __importDefault = (this && this.__importDefault) || function (mod) {
25
- return (mod && mod.__esModule) ? mod : { "default": mod };
26
- };
27
- var _SnapController_instances, _SnapController_closeAllConnections, _SnapController_environmentEndowmentPermissions, _SnapController_featureFlags, _SnapController_fetchFunction, _SnapController_idleTimeCheckInterval, _SnapController_checkSnapBlockList, _SnapController_maxIdleTime, _SnapController_npmRegistryUrl, _SnapController_getAppKey, _SnapController_timeoutForLastRequestStatus, _SnapController_statusMachine, _SnapController_initializeStateMachine, _SnapController_registerMessageHandlers, _SnapController_pollForLastRequestStatus, _SnapController_blockSnap, _SnapController_unblockSnap, _SnapController_assertIsUnblocked, _SnapController_stopSnapsLastRequestPastMax, _SnapController_transition, _SnapController_terminateSnap, _SnapController_getEncryptionKey, _SnapController_encryptSnapState, _SnapController_decryptSnapState, _SnapController_add, _SnapController_startSnap, _SnapController_getEndowments, _SnapController_set, _SnapController_fetchNpmSnap, _SnapController_fetchLocalSnap, _SnapController_processSnapPermissions, _SnapController_getRpcRequestHandler, _SnapController_executeWithTimeout, _SnapController_recordSnapRpcRequestStart, _SnapController_recordSnapRpcRequestFinish, _SnapController_getRuntime, _SnapController_getRuntimeExpect, _SnapController_setupRuntime, _SnapController_calculatePermissionsChange;
24
+ var _SnapController_instances, _SnapController_closeAllConnections, _SnapController_environmentEndowmentPermissions, _SnapController_featureFlags, _SnapController_fetchFunction, _SnapController_idleTimeCheckInterval, _SnapController_checkSnapBlockList, _SnapController_maxIdleTime, _SnapController_npmRegistryUrl, _SnapController_detectSnapLocation, _SnapController_rollbackSnapshots, _SnapController_getAppKey, _SnapController_timeoutForLastRequestStatus, _SnapController_statusMachine, _SnapController_initializeStateMachine, _SnapController_registerMessageHandlers, _SnapController_pollForLastRequestStatus, _SnapController_blockSnap, _SnapController_unblockSnap, _SnapController_assertIsUnblocked, _SnapController_stopSnapsLastRequestPastMax, _SnapController_transition, _SnapController_terminateSnap, _SnapController_getEncryptionKey, _SnapController_encryptSnapState, _SnapController_decryptSnapState, _SnapController_add, _SnapController_startSnap, _SnapController_getEndowments, _SnapController_set, _SnapController_fetchSnap, _SnapController_processSnapPermissions, _SnapController_getRpcRequestHandler, _SnapController_executeWithTimeout, _SnapController_recordSnapRpcRequestStart, _SnapController_recordSnapRpcRequestFinish, _SnapController_getRollbackSnapshot, _SnapController_createRollbackSnapshot, _SnapController_rollbackSnap, _SnapController_rollbackSnaps, _SnapController_getRuntime, _SnapController_getRuntimeExpect, _SnapController_setupRuntime, _SnapController_calculatePermissionsChange, _SnapController_isValidUpdate;
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  exports.SnapController = exports.AppKeyType = exports.SNAP_APPROVAL_UPDATE = exports.SNAP_APPROVAL_INSTALL = exports.controllerName = void 0;
30
- const browser_passworder_1 = __importDefault(require("@metamask/browser-passworder"));
31
- const controllers_1 = require("@metamask/controllers");
27
+ const base_controller_1 = require("@metamask/base-controller");
28
+ const browser_passworder_1 = require("@metamask/browser-passworder");
29
+ const rpc_methods_1 = require("@metamask/rpc-methods");
32
30
  const snaps_utils_1 = require("@metamask/snaps-utils");
31
+ const subject_metadata_controller_1 = require("@metamask/subject-metadata-controller");
33
32
  const utils_1 = require("@metamask/utils");
34
33
  const fsm_1 = require("@xstate/fsm");
35
34
  const eth_rpc_errors_1 = require("eth-rpc-errors");
36
35
  const nanoid_1 = require("nanoid");
37
- const rpc_methods_1 = require("@metamask/rpc-methods");
38
36
  const fsm_2 = require("../fsm");
39
37
  const utils_2 = require("../utils");
40
38
  const endowments_1 = require("./endowments");
39
+ const rpc_1 = require("./endowments/rpc");
40
+ const location_1 = require("./location");
41
41
  const RequestQueue_1 = require("./RequestQueue");
42
- const utils_3 = require("./utils");
43
42
  const Timer_1 = require("./Timer");
44
43
  exports.controllerName = 'SnapController';
45
44
  // TODO: Figure out how to name these
@@ -55,7 +54,7 @@ const TRUNCATED_SNAP_PROPERTIES = new Set([
55
54
  ]);
56
55
  var AppKeyType;
57
56
  (function (AppKeyType) {
58
- AppKeyType["stateEncryption"] = "stateEncryption";
57
+ AppKeyType["StateEncryption"] = "stateEncryption";
59
58
  })(AppKeyType = exports.AppKeyType || (exports.AppKeyType = {}));
60
59
  const defaultState = {
61
60
  snapErrors: {},
@@ -69,12 +68,14 @@ const defaultState = {
69
68
  * @returns Object with serializable snap properties.
70
69
  */
71
70
  function truncateSnap(snap) {
72
- return Object.keys(snap).reduce((serialized, key) => {
71
+ const truncatedSnap = Object.keys(snap).reduce((serialized, key) => {
73
72
  if (TRUNCATED_SNAP_PROPERTIES.has(key)) {
74
73
  serialized[key] = snap[key];
75
74
  }
76
75
  return serialized;
77
76
  }, {});
77
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
78
+ return truncatedSnap;
78
79
  }
79
80
  const name = 'SnapController';
80
81
  /*
@@ -83,8 +84,8 @@ const name = 'SnapController';
83
84
  * - Authorize: Requests the snap's required permissions from the user.
84
85
  * - Start: Initializes the snap in its SES realm with the authorized permissions.
85
86
  */
86
- class SnapController extends controllers_1.BaseControllerV2 {
87
- constructor({ closeAllConnections, messenger, state, getAppKey, environmentEndowmentPermissions = [], npmRegistryUrl, idleTimeCheckInterval = (0, utils_1.inMilliseconds)(5, utils_1.Duration.Second), checkBlockList, maxIdleTime = (0, utils_1.inMilliseconds)(30, utils_1.Duration.Second), maxRequestTime = (0, utils_1.inMilliseconds)(60, utils_1.Duration.Second), fetchFunction = globalThis.fetch.bind(globalThis), featureFlags = {}, }) {
87
+ class SnapController extends base_controller_1.BaseControllerV2 {
88
+ constructor({ closeAllConnections, messenger, state, getAppKey, environmentEndowmentPermissions = [], npmRegistryUrl, idleTimeCheckInterval = (0, utils_1.inMilliseconds)(5, utils_1.Duration.Second), checkBlockList, maxIdleTime = (0, utils_1.inMilliseconds)(30, utils_1.Duration.Second), maxRequestTime = (0, utils_1.inMilliseconds)(60, utils_1.Duration.Second), fetchFunction = globalThis.fetch.bind(globalThis), featureFlags = {}, detectSnapLocation: detectSnapLocationFunction = location_1.detectSnapLocation, }) {
88
89
  var _a, _b;
89
90
  super({
90
91
  messenger,
@@ -135,6 +136,8 @@ class SnapController extends controllers_1.BaseControllerV2 {
135
136
  _SnapController_checkSnapBlockList.set(this, void 0);
136
137
  _SnapController_maxIdleTime.set(this, void 0);
137
138
  _SnapController_npmRegistryUrl.set(this, void 0);
139
+ _SnapController_detectSnapLocation.set(this, void 0);
140
+ _SnapController_rollbackSnapshots.set(this, void 0);
138
141
  _SnapController_getAppKey.set(this, void 0);
139
142
  _SnapController_timeoutForLastRequestStatus.set(this, void 0);
140
143
  _SnapController_statusMachine.set(this, void 0);
@@ -148,14 +151,18 @@ class SnapController extends controllers_1.BaseControllerV2 {
148
151
  __classPrivateFieldSet(this, _SnapController_maxIdleTime, maxIdleTime, "f");
149
152
  this.maxRequestTime = maxRequestTime;
150
153
  __classPrivateFieldSet(this, _SnapController_npmRegistryUrl, npmRegistryUrl, "f");
154
+ __classPrivateFieldSet(this, _SnapController_detectSnapLocation, detectSnapLocationFunction, "f");
151
155
  this._onUnhandledSnapError = this._onUnhandledSnapError.bind(this);
152
156
  this._onOutboundRequest = this._onOutboundRequest.bind(this);
153
157
  this._onOutboundResponse = this._onOutboundResponse.bind(this);
158
+ __classPrivateFieldSet(this, _SnapController_rollbackSnapshots, new Map(), "f");
154
159
  this.snapsRuntimeData = new Map();
155
160
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_pollForLastRequestStatus).call(this);
161
+ /* eslint-disable @typescript-eslint/unbound-method */
156
162
  this.messagingSystem.subscribe('ExecutionService:unhandledError', this._onUnhandledSnapError);
157
163
  this.messagingSystem.subscribe('ExecutionService:outboundRequest', this._onOutboundRequest);
158
164
  this.messagingSystem.subscribe('ExecutionService:outboundResponse', this._onOutboundResponse);
165
+ /* eslint-enable @typescript-eslint/unbound-method */
159
166
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_initializeStateMachine).call(this);
160
167
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_registerMessageHandlers).call(this);
161
168
  Object.values((_b = state === null || state === void 0 ? void 0 : state.snaps) !== null && _b !== void 0 ? _b : {}).forEach((snap) => {
@@ -179,7 +186,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
179
186
  };
180
187
  return blockListArg;
181
188
  }, {}));
182
- await Promise.all(Object.entries(blockedSnaps).map((_a) => {
189
+ await Promise.all(Object.entries(blockedSnaps).map(async (_a) => {
183
190
  var [snapId, _b] = _a, { blocked } = _b, blockData = __rest(_b, ["blocked"]);
184
191
  if (blocked) {
185
192
  return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_blockSnap).call(this, snapId, blockData);
@@ -200,11 +207,15 @@ class SnapController extends controllers_1.BaseControllerV2 {
200
207
  });
201
208
  return result[snapId].blocked;
202
209
  }
203
- async _onUnhandledSnapError(snapId, error) {
204
- await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Crash);
205
- this.addSnapError(error);
210
+ _onUnhandledSnapError(snapId, error) {
211
+ this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Crash)
212
+ .then(() => this.addSnapError(error))
213
+ .catch((stopSnapError) => {
214
+ // TODO: Decide how to handle errors.
215
+ console.error(stopSnapError);
216
+ });
206
217
  }
207
- async _onOutboundRequest(snapId) {
218
+ _onOutboundRequest(snapId) {
208
219
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
209
220
  // Ideally we would only pause the pending request that is making the outbound request
210
221
  // but right now we don't have a way to know which request initiated the outbound request
@@ -213,7 +224,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
213
224
  .forEach((pendingRequest) => pendingRequest.timer.pause());
214
225
  runtime.pendingOutboundRequests += 1;
215
226
  }
216
- async _onOutboundResponse(snapId) {
227
+ _onOutboundResponse(snapId) {
217
228
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
218
229
  runtime.pendingOutboundRequests -= 1;
219
230
  if (runtime.pendingOutboundRequests === 0) {
@@ -260,7 +271,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
260
271
  * @param snapId - The id of the Snap to disable.
261
272
  * @returns A promise that resolves once the snap has been disabled.
262
273
  */
263
- disableSnap(snapId) {
274
+ async disableSnap(snapId) {
264
275
  if (!this.has(snapId)) {
265
276
  throw new Error(`Snap "${snapId}" not found.`);
266
277
  }
@@ -386,7 +397,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
386
397
  *
387
398
  * @param snapId - The id of the Snap whose state should be cleared.
388
399
  */
389
- async clearSnapState(snapId) {
400
+ clearSnapState(snapId) {
390
401
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
391
402
  runtime.state = null;
392
403
  }
@@ -402,20 +413,19 @@ class SnapController extends controllers_1.BaseControllerV2 {
402
413
  });
403
414
  }
404
415
  /**
405
- * Removes an error by internalID from a the SnapControllers state.
416
+ * Removes an error by internalID from the SnapControllers state.
406
417
  *
407
418
  * @param internalID - The internal error ID to remove on the SnapController.
408
419
  */
409
- async removeSnapError(internalID) {
420
+ removeSnapError(internalID) {
410
421
  this.update((state) => {
411
422
  delete state.snapErrors[internalID];
412
423
  });
413
424
  }
414
425
  /**
415
426
  * Clears all errors from the SnapControllers state.
416
- *
417
427
  */
418
- async clearSnapErrors() {
428
+ clearSnapErrors() {
419
429
  this.update((state) => {
420
430
  state.snapErrors = {};
421
431
  });
@@ -436,13 +446,13 @@ class SnapController extends controllers_1.BaseControllerV2 {
436
446
  * Completely clear the controller's state: delete all associated data,
437
447
  * handlers, event listeners, and permissions; tear down all snap providers.
438
448
  */
439
- clearState() {
449
+ async clearState() {
440
450
  const snapIds = Object.keys(this.state.snaps);
441
451
  snapIds.forEach((snapId) => {
442
452
  __classPrivateFieldGet(this, _SnapController_closeAllConnections, "f").call(this, snapId);
443
453
  });
444
- this.messagingSystem.call('ExecutionService:terminateAllSnaps');
445
- snapIds.forEach(this.revokeAllSnapPermissions);
454
+ await this.messagingSystem.call('ExecutionService:terminateAllSnaps');
455
+ snapIds.forEach((snapId) => this.revokeAllSnapPermissions(snapId));
446
456
  this.update((state) => {
447
457
  state.snaps = {};
448
458
  state.snapStates = {};
@@ -491,8 +501,8 @@ class SnapController extends controllers_1.BaseControllerV2 {
491
501
  *
492
502
  * @param snapId - The snap ID.
493
503
  */
494
- async revokeAllSnapPermissions(snapId) {
495
- if (await this.messagingSystem.call('PermissionController:hasPermissions', snapId)) {
504
+ revokeAllSnapPermissions(snapId) {
505
+ if (this.messagingSystem.call('PermissionController:hasPermissions', snapId)) {
496
506
  this.messagingSystem.call('PermissionController:revokeAllPermissions', snapId);
497
507
  }
498
508
  }
@@ -529,9 +539,9 @@ class SnapController extends controllers_1.BaseControllerV2 {
529
539
  * @param origin - The origin whose permitted snaps to retrieve.
530
540
  * @returns The serialized permitted snaps for the origin.
531
541
  */
532
- async getPermittedSnaps(origin) {
542
+ getPermittedSnaps(origin) {
533
543
  var _a;
534
- return Object.values((_a = (await this.messagingSystem.call('PermissionController:getPermissions', origin))) !== null && _a !== void 0 ? _a : {}).reduce((permittedSnaps, perm) => {
544
+ return Object.values((_a = this.messagingSystem.call('PermissionController:getPermissions', origin)) !== null && _a !== void 0 ? _a : {}).reduce((permittedSnaps, perm) => {
535
545
  if (perm.parentCapability.startsWith(snaps_utils_1.SNAP_PREFIX)) {
536
546
  const snapId = perm.parentCapability.replace(snaps_utils_1.SNAP_PREFIX_REGEX, '');
537
547
  const snap = this.get(snapId);
@@ -555,27 +565,46 @@ class SnapController extends controllers_1.BaseControllerV2 {
555
565
  */
556
566
  async installSnaps(origin, requestedSnaps) {
557
567
  const result = {};
558
- await Promise.all(Object.entries(requestedSnaps).map(async ([snapId, { version: rawVersion }]) => {
559
- const version = (0, snaps_utils_1.resolveVersion)(rawVersion);
560
- const permissionName = (0, snaps_utils_1.getSnapPermissionName)(snapId);
561
- if (!(0, snaps_utils_1.isValidSnapVersionRange)(version)) {
562
- result[snapId] = {
563
- error: eth_rpc_errors_1.ethErrors.rpc.invalidParams(`The "version" field must be a valid SemVer version range if specified. Received: "${version}".`),
564
- };
565
- return;
566
- }
567
- if (await this.messagingSystem.call('PermissionController:hasPermission', origin, permissionName)) {
568
- // Attempt to install and run the snap, storing any errors that
569
- // occur during the process.
570
- result[snapId] = Object.assign({}, (await this.processRequestedSnap(origin, snapId, version)));
571
- }
572
- else {
573
- // only allow the installation of permitted snaps
574
- result[snapId] = {
575
- error: eth_rpc_errors_1.ethErrors.provider.unauthorized(`Not authorized to install snap "${snapId}". Request the permission for the snap before attempting to install it.`),
576
- };
568
+ const snapIds = Object.keys(requestedSnaps);
569
+ // Existing snaps may need to be updated
570
+ const pendingUpdates = snapIds.filter((snapId) => this.has(snapId));
571
+ // Non-existing snaps will need to be installed
572
+ const pendingInstalls = snapIds.filter((snapId) => !pendingUpdates.includes(snapId));
573
+ try {
574
+ for (const [snapId, { version: rawVersion }] of Object.entries(requestedSnaps)) {
575
+ const [error, version] = (0, snaps_utils_1.resolveVersionRange)(rawVersion);
576
+ if (error) {
577
+ throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`The "version" field must be a valid SemVer version range if specified. Received: "${rawVersion}".`);
578
+ }
579
+ const permissionName = (0, snaps_utils_1.getSnapPermissionName)(snapId);
580
+ if (!this.messagingSystem.call('PermissionController:hasPermission', origin, permissionName)) {
581
+ throw eth_rpc_errors_1.ethErrors.provider.unauthorized(`Not authorized to install snap "${snapId}". Request the permission for the snap before attempting to install it.`);
582
+ }
583
+ const isUpdate = pendingUpdates.includes(snapId);
584
+ if (isUpdate && __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_isValidUpdate).call(this, snapId, version)) {
585
+ let rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
586
+ if (rollbackSnapshot === undefined) {
587
+ const prevSourceCode = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId).sourceCode;
588
+ rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_createRollbackSnapshot).call(this, snapId);
589
+ rollbackSnapshot.sourceCode = prevSourceCode;
590
+ rollbackSnapshot.newVersion = version;
591
+ }
592
+ else {
593
+ throw new Error('This snap is already being updated.');
594
+ }
595
+ }
596
+ result[snapId] = await this.processRequestedSnap(origin, snapId, version);
577
597
  }
578
- }));
598
+ snapIds.forEach((snapId) => __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").delete(snapId));
599
+ }
600
+ catch (error) {
601
+ const installed = pendingInstalls.filter((snapId) => this.has(snapId));
602
+ await this.removeSnaps(installed);
603
+ const snapshottedSnaps = [...__classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").keys()];
604
+ const snapsToRollback = pendingUpdates.filter((snapId) => snapshottedSnaps.includes(snapId));
605
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_rollbackSnaps).call(this, snapsToRollback);
606
+ throw error;
607
+ }
579
608
  return result;
580
609
  }
581
610
  /**
@@ -588,39 +617,22 @@ class SnapController extends controllers_1.BaseControllerV2 {
588
617
  * @returns The resulting snap object, or an error if something went wrong.
589
618
  */
590
619
  async processRequestedSnap(origin, snapId, versionRange) {
591
- try {
592
- (0, snaps_utils_1.validateSnapId)(snapId);
593
- }
594
- catch (err) {
595
- return {
596
- error: eth_rpc_errors_1.ethErrors.rpc.invalidParams(`"${snapId}" is not a valid snap id.`),
597
- };
598
- }
620
+ (0, snaps_utils_1.validateSnapId)(snapId);
621
+ const location = __classPrivateFieldGet(this, _SnapController_detectSnapLocation, "f").call(this, snapId, { versionRange });
599
622
  const existingSnap = this.getTruncated(snapId);
600
623
  // For devX we always re-install local snaps.
601
- if (existingSnap && (0, snaps_utils_1.getSnapPrefix)(snapId) !== snaps_utils_1.SnapIdPrefixes.local) {
624
+ if (existingSnap && !location.shouldAlwaysReload) {
602
625
  if ((0, snaps_utils_1.satisfiesVersionRange)(existingSnap.version, versionRange)) {
603
626
  return existingSnap;
604
627
  }
605
628
  if (__classPrivateFieldGet(this, _SnapController_featureFlags, "f").dappsCanUpdateSnaps === true) {
606
- try {
607
- const updateResult = await this.updateSnap(origin, snapId, versionRange);
608
- if (updateResult === null) {
609
- return {
610
- error: eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Snap "${snapId}@${existingSnap.version}" is already installed, couldn't update to a version inside requested "${versionRange}" range.`),
611
- };
612
- }
613
- return updateResult;
629
+ const updateResult = await this.updateSnap(origin, snapId, versionRange, location);
630
+ if (updateResult === null) {
631
+ throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Snap "${snapId}@${existingSnap.version}" is already installed. Couldn't update to a version inside requested "${versionRange}" range.`);
614
632
  }
615
- catch (err) {
616
- return { error: (0, eth_rpc_errors_1.serializeError)(err) };
617
- }
618
- }
619
- else {
620
- return {
621
- error: eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Version mismatch with already installed snap. ${snapId}@${existingSnap.version} doesn't satisfy requested version ${versionRange}`),
622
- };
633
+ return updateResult;
623
634
  }
635
+ throw eth_rpc_errors_1.ethErrors.rpc.invalidParams(`Version mismatch with already installed snap. ${snapId}@${existingSnap.version} doesn't satisfy requested version ${versionRange}.`);
624
636
  }
625
637
  // Existing snaps must be stopped before overwriting
626
638
  if (existingSnap && this.isRunning(snapId)) {
@@ -630,7 +642,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
630
642
  const { sourceCode } = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_add).call(this, {
631
643
  origin,
632
644
  id: snapId,
633
- versionRange,
645
+ location,
634
646
  });
635
647
  await this.authorize(origin, snapId);
636
648
  await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, {
@@ -641,12 +653,9 @@ class SnapController extends controllers_1.BaseControllerV2 {
641
653
  this.messagingSystem.publish(`SnapController:snapInstalled`, truncated);
642
654
  return truncated;
643
655
  }
644
- catch (err) {
645
- console.error(`Error when adding snap.`, err);
646
- if (this.has(snapId)) {
647
- this.removeSnap(snapId);
648
- }
649
- return { error: (0, eth_rpc_errors_1.serializeError)(err) };
656
+ catch (error) {
657
+ console.error(`Error when adding snap.`, error);
658
+ throw error;
650
659
  }
651
660
  }
652
661
  /**
@@ -664,27 +673,29 @@ class SnapController extends controllers_1.BaseControllerV2 {
664
673
  * @param origin - The origin requesting the snap update.
665
674
  * @param snapId - The id of the Snap to be updated.
666
675
  * @param newVersionRange - A semver version range in which the maximum version will be chosen.
676
+ * @param location - Optional location that was already used during installation flow.
667
677
  * @returns The snap metadata if updated, `null` otherwise.
668
678
  */
669
- async updateSnap(origin, snapId, newVersionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION) {
679
+ async updateSnap(origin, snapId, newVersionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION, location) {
680
+ var _a;
670
681
  const snap = this.getExpect(snapId);
671
- if (!(0, snaps_utils_1.isValidSnapVersionRange)(newVersionRange)) {
682
+ if (!(0, snaps_utils_1.isValidSemVerRange)(newVersionRange)) {
672
683
  throw new Error(`Received invalid snap version range: "${newVersionRange}".`);
673
684
  }
674
- const newSnap = await this.fetchSnap(snapId, newVersionRange);
675
- const newVersion = newSnap.manifest.version;
685
+ const newSnap = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_fetchSnap).call(this, snapId, location !== null && location !== void 0 ? location : __classPrivateFieldGet(this, _SnapController_detectSnapLocation, "f").call(this, snapId, { versionRange: newVersionRange }));
686
+ const newVersion = newSnap.manifest.result.version;
676
687
  if (!(0, snaps_utils_1.gtVersion)(newVersion, snap.version)) {
677
688
  console.warn(`Tried updating snap "${snapId}" within "${newVersionRange}" version range, but newer version "${snap.version}" is already installed`);
678
689
  return null;
679
690
  }
680
691
  await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertIsUnblocked).call(this, snapId, {
681
692
  version: newVersion,
682
- shasum: newSnap.manifest.source.shasum,
693
+ shasum: newSnap.manifest.result.source.shasum,
683
694
  });
684
- const processedPermissions = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_processSnapPermissions).call(this, newSnap.manifest.initialPermissions);
685
- const { newPermissions, unusedPermissions, approvedPermissions } = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_calculatePermissionsChange).call(this, snapId, processedPermissions);
695
+ const processedPermissions = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_processSnapPermissions).call(this, newSnap.manifest.result.initialPermissions);
696
+ const { newPermissions, unusedPermissions, approvedPermissions } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_calculatePermissionsChange).call(this, snapId, processedPermissions);
686
697
  const id = (0, nanoid_1.nanoid)();
687
- const _a = (await this.messagingSystem.call('ApprovalController:addRequest', {
698
+ const _b = (await this.messagingSystem.call('ApprovalController:addRequest', {
688
699
  origin,
689
700
  id,
690
701
  type: exports.SNAP_APPROVAL_UPDATE,
@@ -693,12 +704,12 @@ class SnapController extends controllers_1.BaseControllerV2 {
693
704
  metadata: { id, origin: snapId, dappOrigin: origin },
694
705
  permissions: newPermissions,
695
706
  snapId,
696
- newVersion: newSnap.manifest.version,
707
+ newVersion: newSnap.manifest.result.version,
697
708
  newPermissions,
698
709
  approvedPermissions,
699
710
  unusedPermissions,
700
711
  },
701
- }, true)), { permissions: approvedNewPermissions } = _a, requestData = __rest(_a, ["permissions"]);
712
+ }, true)), { permissions: approvedNewPermissions } = _b, requestData = __rest(_b, ["permissions"]);
702
713
  if (this.isRunning(snapId)) {
703
714
  await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
704
715
  }
@@ -707,53 +718,41 @@ class SnapController extends controllers_1.BaseControllerV2 {
707
718
  origin,
708
719
  id: snapId,
709
720
  manifest: newSnap.manifest,
710
- sourceCode: newSnap.sourceCode,
721
+ files: newSnap.files,
711
722
  versionRange: newVersionRange,
723
+ isUpdate: true,
712
724
  });
713
725
  const unusedPermissionsKeys = Object.keys(unusedPermissions);
714
726
  if ((0, utils_1.isNonEmptyArray)(unusedPermissionsKeys)) {
715
- await this.messagingSystem.call('PermissionController:revokePermissions', {
727
+ this.messagingSystem.call('PermissionController:revokePermissions', {
716
728
  [snapId]: unusedPermissionsKeys,
717
729
  });
718
730
  }
719
731
  if ((0, utils_1.isNonEmptyArray)(Object.keys(approvedNewPermissions))) {
720
- await this.messagingSystem.call('PermissionController:grantPermissions', {
732
+ this.messagingSystem.call('PermissionController:grantPermissions', {
721
733
  approvedPermissions: approvedNewPermissions,
722
734
  subject: { origin: snapId },
723
735
  requestData,
724
736
  });
725
737
  }
726
- await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, { snapId, sourceCode: newSnap.sourceCode });
727
- const truncatedSnap = this.getTruncatedExpect(snapId);
728
- this.messagingSystem.publish('SnapController:snapUpdated', truncatedSnap, snap.version);
729
- return truncatedSnap;
730
- }
731
- /**
732
- * Fetches the manifest and source code of a snap.
733
- *
734
- * This function is not hash private yet because of tests.
735
- *
736
- * @param snapId - The id of the Snap.
737
- * @param versionRange - The SemVer version of the Snap to fetch.
738
- * @returns A tuple of the Snap manifest object and the Snap source code.
739
- */
740
- async fetchSnap(snapId, versionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION) {
738
+ const rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
739
+ if (rollbackSnapshot !== undefined) {
740
+ rollbackSnapshot.permissions.revoked = unusedPermissions;
741
+ rollbackSnapshot.permissions.granted = Object.keys(approvedNewPermissions);
742
+ rollbackSnapshot.permissions.requestData = requestData;
743
+ }
744
+ const sourceCode = (_a = newSnap.files
745
+ .find((file) => file.path === newSnap.manifest.result.source.location.npm.filePath)) === null || _a === void 0 ? void 0 : _a.toString();
746
+ (0, utils_1.assert)(sourceCode !== undefined);
741
747
  try {
742
- const snapPrefix = (0, snaps_utils_1.getSnapPrefix)(snapId);
743
- switch (snapPrefix) {
744
- case snaps_utils_1.SnapIdPrefixes.local:
745
- return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_fetchLocalSnap).call(this, snapId.replace(snaps_utils_1.SnapIdPrefixes.local, ''));
746
- case snaps_utils_1.SnapIdPrefixes.npm:
747
- return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_fetchNpmSnap).call(this, snapId.replace(snaps_utils_1.SnapIdPrefixes.npm, ''), versionRange);
748
- /* istanbul ignore next */
749
- default:
750
- // This whill fail to compile if the above switch is not fully exhaustive
751
- return (0, utils_1.assertExhaustive)(snapPrefix);
752
- }
748
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_startSnap).call(this, { snapId, sourceCode });
753
749
  }
754
- catch (error) {
755
- throw new Error(`Failed to fetch Snap "${snapId}": ${error.message}`);
750
+ catch (_c) {
751
+ throw new Error(`Snap ${snapId} crashed with updated source code.`);
756
752
  }
753
+ const truncatedSnap = this.getTruncatedExpect(snapId);
754
+ this.messagingSystem.publish('SnapController:snapUpdated', truncatedSnap, snap.version);
755
+ return truncatedSnap;
757
756
  }
758
757
  /**
759
758
  * Initiates a request for the given snap's initial permissions.
@@ -785,7 +784,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
785
784
  },
786
785
  }, true)), { permissions: approvedPermissions } = _a, requestData = __rest(_a, ["permissions"]);
787
786
  if ((0, utils_1.isNonEmptyArray)(Object.keys(approvedPermissions))) {
788
- await this.messagingSystem.call('PermissionController:grantPermissions', {
787
+ this.messagingSystem.call('PermissionController:grantPermissions', {
789
788
  approvedPermissions,
790
789
  subject: { origin: snapId },
791
790
  requestData,
@@ -802,9 +801,11 @@ class SnapController extends controllers_1.BaseControllerV2 {
802
801
  if (__classPrivateFieldGet(this, _SnapController_timeoutForLastRequestStatus, "f")) {
803
802
  clearTimeout(__classPrivateFieldGet(this, _SnapController_timeoutForLastRequestStatus, "f"));
804
803
  }
804
+ /* eslint-disable @typescript-eslint/unbound-method */
805
805
  this.messagingSystem.unsubscribe('ExecutionService:unhandledError', this._onUnhandledSnapError);
806
806
  this.messagingSystem.unsubscribe('ExecutionService:outboundRequest', this._onOutboundRequest);
807
807
  this.messagingSystem.unsubscribe('ExecutionService:outboundResponse', this._onOutboundResponse);
808
+ /* eslint-enable @typescript-eslint/unbound-method */
808
809
  }
809
810
  /**
810
811
  * Passes a JSON-RPC request object to the RPC handler function of a snap.
@@ -817,6 +818,23 @@ class SnapController extends controllers_1.BaseControllerV2 {
817
818
  * @returns The result of the JSON-RPC request.
818
819
  */
819
820
  async handleRequest({ snapId, origin, handler: handlerType, request, }) {
821
+ const permissionName = endowments_1.handlerEndowments[handlerType];
822
+ const hasPermission = this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName);
823
+ if (!hasPermission) {
824
+ throw new Error(`Snap "${snapId}" is not permitted to use "${permissionName}".`);
825
+ }
826
+ if (permissionName === endowments_1.SnapEndowments.Rpc) {
827
+ const subject = this.messagingSystem.call('SubjectMetadataController:getSubjectMetadata', origin);
828
+ const isSnap = (subject === null || subject === void 0 ? void 0 : subject.subjectType) === subject_metadata_controller_1.SubjectType.Snap;
829
+ const permissions = this.messagingSystem.call('PermissionController:getPermissions', snapId);
830
+ const rpcPermission = permissions === null || permissions === void 0 ? void 0 : permissions[endowments_1.SnapEndowments.Rpc];
831
+ (0, utils_1.assert)(rpcPermission);
832
+ const origins = (0, rpc_1.getRpcCaveatOrigins)(rpcPermission);
833
+ (0, utils_1.assert)(origins);
834
+ if ((isSnap && !origins.snaps) || (!isSnap && !origins.dapps)) {
835
+ throw new Error(`Snap "${snapId}" is not permitted to handle JSON-RPC requests from "${origin}".`);
836
+ }
837
+ }
820
838
  const handler = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRpcRequestHandler).call(this, snapId);
821
839
  if (!handler) {
822
840
  throw new Error(`Snap RPC message handler not found for snap "${snapId}".`);
@@ -825,7 +843,7 @@ class SnapController extends controllers_1.BaseControllerV2 {
825
843
  }
826
844
  }
827
845
  exports.SnapController = SnapController;
828
- _SnapController_closeAllConnections = new WeakMap(), _SnapController_environmentEndowmentPermissions = new WeakMap(), _SnapController_featureFlags = new WeakMap(), _SnapController_fetchFunction = new WeakMap(), _SnapController_idleTimeCheckInterval = new WeakMap(), _SnapController_checkSnapBlockList = new WeakMap(), _SnapController_maxIdleTime = new WeakMap(), _SnapController_npmRegistryUrl = new WeakMap(), _SnapController_getAppKey = new WeakMap(), _SnapController_timeoutForLastRequestStatus = new WeakMap(), _SnapController_statusMachine = new WeakMap(), _SnapController_instances = new WeakSet(), _SnapController_initializeStateMachine = function _SnapController_initializeStateMachine() {
846
+ _SnapController_closeAllConnections = new WeakMap(), _SnapController_environmentEndowmentPermissions = new WeakMap(), _SnapController_featureFlags = new WeakMap(), _SnapController_fetchFunction = new WeakMap(), _SnapController_idleTimeCheckInterval = new WeakMap(), _SnapController_checkSnapBlockList = new WeakMap(), _SnapController_maxIdleTime = new WeakMap(), _SnapController_npmRegistryUrl = new WeakMap(), _SnapController_detectSnapLocation = new WeakMap(), _SnapController_rollbackSnapshots = new WeakMap(), _SnapController_getAppKey = new WeakMap(), _SnapController_timeoutForLastRequestStatus = new WeakMap(), _SnapController_statusMachine = new WeakMap(), _SnapController_instances = new WeakSet(), _SnapController_initializeStateMachine = function _SnapController_initializeStateMachine() {
829
847
  const disableGuard = ({ snapId }) => {
830
848
  return this.getExpect(snapId).enabled;
831
849
  };
@@ -878,23 +896,26 @@ _SnapController_closeAllConnections = new WeakMap(), _SnapController_environment
878
896
  }, _SnapController_registerMessageHandlers = function _SnapController_registerMessageHandlers() {
879
897
  this.messagingSystem.registerActionHandler(`${exports.controllerName}:clearSnapState`, (...args) => this.clearSnapState(...args));
880
898
  this.messagingSystem.registerActionHandler(`${exports.controllerName}:get`, (...args) => this.get(...args));
881
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:getSnapState`, (...args) => this.getSnapState(...args));
882
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:handleRequest`, (...args) => this.handleRequest(...args));
899
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:getSnapState`, async (...args) => this.getSnapState(...args));
900
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:handleRequest`, async (...args) => this.handleRequest(...args));
883
901
  this.messagingSystem.registerActionHandler(`${exports.controllerName}:has`, (...args) => this.has(...args));
884
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateBlockedSnaps`, () => this.updateBlockedSnaps());
885
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateSnapState`, (...args) => this.updateSnapState(...args));
902
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateBlockedSnaps`, async () => this.updateBlockedSnaps());
903
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:updateSnapState`, async (...args) => this.updateSnapState(...args));
886
904
  this.messagingSystem.registerActionHandler(`${exports.controllerName}:enable`, (...args) => this.enableSnap(...args));
887
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:disable`, (...args) => this.disableSnap(...args));
888
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:remove`, (...args) => this.removeSnap(...args));
905
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:disable`, async (...args) => this.disableSnap(...args));
906
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:remove`, async (...args) => this.removeSnap(...args));
889
907
  this.messagingSystem.registerActionHandler(`${exports.controllerName}:getPermitted`, (...args) => this.getPermittedSnaps(...args));
890
- this.messagingSystem.registerActionHandler(`${exports.controllerName}:install`, (...args) => this.installSnaps(...args));
908
+ this.messagingSystem.registerActionHandler(`${exports.controllerName}:install`, async (...args) => this.installSnaps(...args));
891
909
  this.messagingSystem.registerActionHandler(`${exports.controllerName}:removeSnapError`, (...args) => this.removeSnapError(...args));
892
910
  this.messagingSystem.registerActionHandler(`${exports.controllerName}:getAll`, (...args) => this.getAllSnaps(...args));
893
911
  this.messagingSystem.registerActionHandler(`${exports.controllerName}:incrementActiveReferences`, (...args) => this.incrementActiveReferences(...args));
894
912
  this.messagingSystem.registerActionHandler(`${exports.controllerName}:decrementActiveReferences`, (...args) => this.decrementActiveReferences(...args));
895
913
  }, _SnapController_pollForLastRequestStatus = function _SnapController_pollForLastRequestStatus() {
896
- __classPrivateFieldSet(this, _SnapController_timeoutForLastRequestStatus, setTimeout(async () => {
897
- await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_stopSnapsLastRequestPastMax).call(this);
914
+ __classPrivateFieldSet(this, _SnapController_timeoutForLastRequestStatus, setTimeout(() => {
915
+ __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_stopSnapsLastRequestPastMax).call(this).catch((error) => {
916
+ // TODO: Decide how to handle errors.
917
+ console.error(error);
918
+ });
898
919
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_pollForLastRequestStatus).call(this);
899
920
  }, __classPrivateFieldGet(this, _SnapController_idleTimeCheckInterval, "f")), "f");
900
921
  }, _SnapController_blockSnap =
@@ -920,15 +941,7 @@ async function _SnapController_blockSnap(snapId, blockedSnapInfo) {
920
941
  console.error(`Encountered error when stopping blocked snap "${snapId}".`, error);
921
942
  }
922
943
  this.messagingSystem.publish(`${exports.controllerName}:snapBlocked`, snapId, blockedSnapInfo);
923
- }, _SnapController_unblockSnap =
924
- /**
925
- * Unblocks a snap so that it can be enabled and started again. Emits
926
- * {@link SnapUnblocked}. Does nothing if the snap is not installed or already
927
- * unblocked.
928
- *
929
- * @param snapId - The id of the snap to unblock.
930
- */
931
- async function _SnapController_unblockSnap(snapId) {
944
+ }, _SnapController_unblockSnap = function _SnapController_unblockSnap(snapId) {
932
945
  if (!this.has(snapId) || !this.state.snaps[snapId].blocked) {
933
946
  return;
934
947
  }
@@ -958,7 +971,7 @@ async function _SnapController_assertIsUnblocked(snapId, snapInfo) {
958
971
  runtime.lastRequest &&
959
972
  __classPrivateFieldGet(this, _SnapController_maxIdleTime, "f") &&
960
973
  (0, utils_1.timeSince)(runtime.lastRequest) > __classPrivateFieldGet(this, _SnapController_maxIdleTime, "f"))
961
- .map(([snapId]) => this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop)));
974
+ .map(async ([snapId]) => this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop)));
962
975
  }, _SnapController_transition = function _SnapController_transition(snapId, event) {
963
976
  const { interpreter } = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
964
977
  interpreter.send(event);
@@ -975,16 +988,18 @@ async function _SnapController_terminateSnap(snapId) {
975
988
  await this.messagingSystem.call('ExecutionService:terminateSnap', snapId);
976
989
  this.messagingSystem.publish('SnapController:snapTerminated', this.getTruncatedExpect(snapId));
977
990
  }, _SnapController_getEncryptionKey = async function _SnapController_getEncryptionKey(snapId) {
978
- return __classPrivateFieldGet(this, _SnapController_getAppKey, "f").call(this, snapId, AppKeyType.stateEncryption);
991
+ return __classPrivateFieldGet(this, _SnapController_getAppKey, "f").call(this, snapId, AppKeyType.StateEncryption);
979
992
  }, _SnapController_encryptSnapState = async function _SnapController_encryptSnapState(snapId, state) {
980
993
  const appKey = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getEncryptionKey).call(this, snapId);
981
- return browser_passworder_1.default.encrypt(appKey, state);
994
+ return (0, browser_passworder_1.encrypt)(appKey, state);
982
995
  }, _SnapController_decryptSnapState = async function _SnapController_decryptSnapState(snapId, encrypted) {
983
996
  const appKey = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getEncryptionKey).call(this, snapId);
984
997
  try {
985
- return await browser_passworder_1.default.decrypt(appKey, encrypted);
998
+ const value = await (0, browser_passworder_1.decrypt)(appKey, encrypted);
999
+ (0, utils_1.assert)((0, utils_1.isValidJson)(value));
1000
+ return value;
986
1001
  }
987
- catch (err) {
1002
+ catch (error) {
988
1003
  throw new Error('Failed to decrypt snap state, the state must be corrupted.');
989
1004
  }
990
1005
  }, _SnapController_add =
@@ -998,15 +1013,8 @@ async function _SnapController_terminateSnap(snapId) {
998
1013
  * @returns The resulting snap object.
999
1014
  */
1000
1015
  async function _SnapController_add(args) {
1001
- const { id: snapId } = args;
1016
+ const { id: snapId, location } = args;
1002
1017
  (0, snaps_utils_1.validateSnapId)(snapId);
1003
- if (!args ||
1004
- !('origin' in args) ||
1005
- !('id' in args) ||
1006
- (!('manifest' in args) && 'sourceCode' in args) ||
1007
- ('manifest' in args && !('sourceCode' in args))) {
1008
- throw new Error(`Invalid add snap args for snap "${snapId}".`);
1009
- }
1010
1018
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_setupRuntime).call(this, snapId, { sourceCode: null, state: null });
1011
1019
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1012
1020
  if (!runtime.installPromise) {
@@ -1014,13 +1022,10 @@ async function _SnapController_add(args) {
1014
1022
  // If fetching and setting the snap succeeds, this property will be set
1015
1023
  // to null in the authorize() method.
1016
1024
  runtime.installPromise = (async () => {
1017
- if ('manifest' in args && 'sourceCode' in args) {
1018
- return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, Object.assign(Object.assign({}, args), { id: snapId }));
1019
- }
1020
- const fetchedSnap = await this.fetchSnap(snapId, args.versionRange);
1025
+ const fetchedSnap = await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_fetchSnap).call(this, snapId, location);
1021
1026
  await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_assertIsUnblocked).call(this, snapId, {
1022
- version: fetchedSnap.manifest.version,
1023
- shasum: fetchedSnap.manifest.source.shasum,
1027
+ version: fetchedSnap.manifest.result.version,
1028
+ shasum: fetchedSnap.manifest.result.source.shasum,
1024
1029
  });
1025
1030
  return __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_set).call(this, Object.assign(Object.assign(Object.assign({}, args), fetchedSnap), { id: snapId }));
1026
1031
  })();
@@ -1044,9 +1049,9 @@ async function _SnapController_add(args) {
1044
1049
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_transition).call(this, snapId, snaps_utils_1.SnapStatusEvents.Start);
1045
1050
  return result;
1046
1051
  }
1047
- catch (err) {
1052
+ catch (error) {
1048
1053
  await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_terminateSnap).call(this, snapId);
1049
- throw err;
1054
+ throw error;
1050
1055
  }
1051
1056
  }, _SnapController_getEndowments =
1052
1057
  /**
@@ -1063,7 +1068,7 @@ async function _SnapController_add(args) {
1063
1068
  async function _SnapController_getEndowments(snapId) {
1064
1069
  let allEndowments = [];
1065
1070
  for (const permissionName of __classPrivateFieldGet(this, _SnapController_environmentEndowmentPermissions, "f")) {
1066
- if (await this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName)) {
1071
+ if (this.messagingSystem.call('PermissionController:hasPermission', snapId, permissionName)) {
1067
1072
  const endowments = await this.messagingSystem.call('PermissionController:getEndowments', snapId, permissionName);
1068
1073
  if (endowments) {
1069
1074
  // We don't have any guarantees about the type of the endowments
@@ -1080,30 +1085,30 @@ async function _SnapController_getEndowments(snapId) {
1080
1085
  ...new Set([...snaps_utils_1.DEFAULT_ENDOWMENTS, ...allEndowments]),
1081
1086
  ];
1082
1087
  if (dedupedEndowments.length <
1088
+ // This is a bug in TypeScript: https://github.com/microsoft/TypeScript/issues/48313
1089
+ // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
1083
1090
  snaps_utils_1.DEFAULT_ENDOWMENTS.length + allEndowments.length) {
1084
1091
  console.error('Duplicate endowments found. Default endowments should not be requested.', allEndowments);
1085
1092
  }
1086
1093
  return dedupedEndowments;
1087
1094
  }, _SnapController_set = function _SnapController_set(args) {
1088
- var _a;
1089
- const { id: snapId, origin, manifest, sourceCode, svgIcon, versionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION, } = args;
1090
- (0, snaps_utils_1.assertIsSnapManifest)(manifest);
1091
- const { version } = manifest;
1095
+ var _a, _b;
1096
+ const { id: snapId, origin, manifest, files, versionRange = snaps_utils_1.DEFAULT_REQUESTED_SNAP_VERSION, isUpdate = false, } = args;
1097
+ (0, snaps_utils_1.assertIsSnapManifest)(manifest.result);
1098
+ const { version } = manifest.result;
1092
1099
  if (!(0, snaps_utils_1.satisfiesVersionRange)(version, versionRange)) {
1093
1100
  throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${version}" which doesn't satisfy requested version range "${versionRange}"`);
1094
1101
  }
1102
+ const sourceCode = (_a = files
1103
+ .find((file) => file.path === manifest.result.source.location.npm.filePath)) === null || _a === void 0 ? void 0 : _a.toString();
1104
+ const svgIcon = files.find((file) => file.path === manifest.result.source.location.npm.iconPath);
1105
+ (0, utils_1.assert)(sourceCode !== undefined);
1095
1106
  if (typeof sourceCode !== 'string' || sourceCode.length === 0) {
1096
1107
  throw new Error(`Invalid source code for snap "${snapId}".`);
1097
1108
  }
1098
- const initialPermissions = manifest === null || manifest === void 0 ? void 0 : manifest.initialPermissions;
1099
- if (!initialPermissions ||
1100
- typeof initialPermissions !== 'object' ||
1101
- Array.isArray(initialPermissions)) {
1102
- throw new Error(`Invalid initial permissions for snap "${snapId}".`);
1103
- }
1104
1109
  const snapsState = this.state.snaps;
1105
1110
  const existingSnap = snapsState[snapId];
1106
- const previousVersionHistory = (_a = existingSnap === null || existingSnap === void 0 ? void 0 : existingSnap.versionHistory) !== null && _a !== void 0 ? _a : [];
1111
+ const previousVersionHistory = (_b = existingSnap === null || existingSnap === void 0 ? void 0 : existingSnap.versionHistory) !== null && _b !== void 0 ? _b : [];
1107
1112
  const versionHistory = [
1108
1113
  ...previousVersionHistory,
1109
1114
  {
@@ -1117,52 +1122,54 @@ async function _SnapController_getEndowments(snapId) {
1117
1122
  // previous state.
1118
1123
  blocked: false, enabled: true,
1119
1124
  // So we can easily correlate the snap with its permission
1120
- permissionName: (0, snaps_utils_1.getSnapPermissionName)(snapId), id: snapId, initialPermissions,
1121
- manifest, status: __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial, version,
1125
+ permissionName: (0, snaps_utils_1.getSnapPermissionName)(snapId), id: snapId, initialPermissions: manifest.result.initialPermissions, manifest: manifest.result, status: __classPrivateFieldGet(this, _SnapController_statusMachine, "f").config.initial, version,
1122
1126
  versionHistory });
1123
1127
  // If the snap was blocked, it isn't any longer
1124
1128
  delete snap.blockInformation;
1125
1129
  // store the snap back in state
1126
- this.update((state) => {
1130
+ const { inversePatches } = this.update((state) => {
1127
1131
  state.snaps[snapId] = snap;
1128
1132
  });
1133
+ // checking for isUpdate here as this function is also used in
1134
+ // the install flow, we do not care to create snapshots for installs
1135
+ if (isUpdate) {
1136
+ const rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
1137
+ if (rollbackSnapshot !== undefined) {
1138
+ rollbackSnapshot.statePatches = inversePatches;
1139
+ }
1140
+ }
1129
1141
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1130
1142
  runtime.sourceCode = sourceCode;
1131
- this.messagingSystem.publish(`SnapController:snapAdded`, snap, svgIcon);
1143
+ this.messagingSystem.publish(`SnapController:snapAdded`, snap, svgIcon === null || svgIcon === void 0 ? void 0 : svgIcon.toString());
1132
1144
  return Object.assign(Object.assign({}, snap), { sourceCode });
1133
- }, _SnapController_fetchNpmSnap = async function _SnapController_fetchNpmSnap(packageName, versionRange) {
1134
- if (!(0, snaps_utils_1.isValidSnapVersionRange)(versionRange)) {
1135
- throw new Error(`Received invalid Snap version range: "${versionRange}".`);
1136
- }
1137
- const { manifest, sourceCode, svgIcon } = await (0, utils_3.fetchNpmSnap)(packageName, versionRange, __classPrivateFieldGet(this, _SnapController_npmRegistryUrl, "f"), __classPrivateFieldGet(this, _SnapController_fetchFunction, "f"));
1138
- return { manifest, sourceCode, svgIcon };
1139
- }, _SnapController_fetchLocalSnap =
1145
+ }, _SnapController_fetchSnap =
1140
1146
  /**
1141
- * Fetches the manifest and source code of a local snap.
1147
+ * Fetches the manifest and source code of a snap.
1148
+ *
1149
+ * This function is not hash private yet because of tests.
1142
1150
  *
1143
- * @param localhostUrl - The localhost URL to download from.
1144
- * @returns The validated manifest and the source code.
1151
+ * @param snapId - The id of the Snap.
1152
+ * @param location - Source from which snap will be fetched.
1153
+ * @returns A tuple of the Snap manifest object and the Snap source code.
1145
1154
  */
1146
- async function _SnapController_fetchLocalSnap(localhostUrl) {
1147
- // Local snaps are mostly used for development purposes. Fetches were cached in the browser and were not requested
1148
- // afterwards which lead to confusing development where old versions of snaps were installed.
1149
- // Thus we disable caching
1150
- const fetchOptions = { cache: 'no-cache' };
1151
- const manifestUrl = new URL(snaps_utils_1.NpmSnapFileNames.Manifest, localhostUrl);
1152
- if (!snaps_utils_1.LOCALHOST_HOSTNAMES.has(manifestUrl.hostname)) {
1153
- throw new Error(`Invalid URL: Locally hosted Snaps must be hosted on localhost. Received URL: "${manifestUrl.toString()}"`);
1154
- }
1155
- const manifest = await (await __classPrivateFieldGet(this, _SnapController_fetchFunction, "f").call(this, manifestUrl.toString(), fetchOptions)).json();
1156
- (0, snaps_utils_1.assertIsSnapManifest)(manifest);
1157
- const { source: { location: { npm: { filePath, iconPath }, }, }, } = manifest;
1158
- const [sourceCode, svgIcon] = await Promise.all([
1159
- (await __classPrivateFieldGet(this, _SnapController_fetchFunction, "f").call(this, new URL(filePath, localhostUrl).toString(), fetchOptions)).text(),
1160
- iconPath
1161
- ? (await __classPrivateFieldGet(this, _SnapController_fetchFunction, "f").call(this, new URL(iconPath, localhostUrl).toString(), fetchOptions)).text()
1162
- : undefined,
1163
- ]);
1164
- (0, snaps_utils_1.validateSnapShasum)(manifest, sourceCode);
1165
- return { manifest, sourceCode, svgIcon };
1155
+ async function _SnapController_fetchSnap(snapId, location) {
1156
+ try {
1157
+ const manifest = await location.manifest();
1158
+ const sourceCode = await location.fetch(manifest.result.source.location.npm.filePath);
1159
+ (0, snaps_utils_1.validateSnapShasum)(manifest.result, sourceCode.toString());
1160
+ const { iconPath } = manifest.result.source.location.npm;
1161
+ const files = [sourceCode];
1162
+ if (iconPath) {
1163
+ files.push(await location.fetch(iconPath));
1164
+ }
1165
+ return { manifest, files, location };
1166
+ }
1167
+ catch (error) {
1168
+ // TODO(ritave): Export `getErrorMessage()` from @metamask/utils and use it here
1169
+ // https://github.com/MetaMask/utils/blob/62d022ef83c91fa4d150e51913be4441508a0ab1/src/assert.ts
1170
+ const message = error instanceof Error ? error.message : error.toString();
1171
+ throw new Error(`Failed to fetch Snap "${snapId}": ${message}.`);
1172
+ }
1166
1173
  }, _SnapController_processSnapPermissions = function _SnapController_processSnapPermissions(initialPermissions) {
1167
1174
  return (0, snaps_utils_1.fromEntries)(Object.entries(initialPermissions).map(([initialPermission, value]) => {
1168
1175
  if ((0, utils_1.hasProperty)(rpc_methods_1.caveatMappers, initialPermission)) {
@@ -1174,17 +1181,13 @@ async function _SnapController_fetchLocalSnap(localhostUrl) {
1174
1181
  endowments_1.endowmentCaveatMappers[initialPermission](value),
1175
1182
  ];
1176
1183
  }
1177
- (0, utils_1.assert)(Object.keys(value).length === 0);
1178
- return [initialPermission, {}];
1184
+ // If we have no mapping, this may be a non-snap permission, return as-is
1185
+ return [
1186
+ initialPermission,
1187
+ value,
1188
+ ];
1179
1189
  }));
1180
- }, _SnapController_getRpcRequestHandler =
1181
- /**
1182
- * Gets the RPC message handler for the given snap.
1183
- *
1184
- * @param snapId - The id of the Snap whose message handler to get.
1185
- * @returns The RPC handler for the given snap.
1186
- */
1187
- async function _SnapController_getRpcRequestHandler(snapId) {
1190
+ }, _SnapController_getRpcRequestHandler = function _SnapController_getRpcRequestHandler(snapId) {
1188
1191
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1189
1192
  const existingHandler = runtime.rpcHandler;
1190
1193
  if (existingHandler) {
@@ -1201,7 +1204,7 @@ async function _SnapController_getRpcRequestHandler(snapId) {
1201
1204
  if (this.state.snaps[snapId].status === snaps_utils_1.SnapStatus.Installing) {
1202
1205
  throw new Error(`Snap "${snapId}" is currently being installed. Please try again later.`);
1203
1206
  }
1204
- if (this.isRunning(snapId) === false) {
1207
+ if (!this.isRunning(snapId)) {
1205
1208
  let localStartPromise = startPromises.get(snapId);
1206
1209
  if (!localStartPromise) {
1207
1210
  localStartPromise = this.startSnap(snapId);
@@ -1241,9 +1244,9 @@ async function _SnapController_getRpcRequestHandler(snapId) {
1241
1244
  __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_recordSnapRpcRequestFinish).call(this, snapId, request.id);
1242
1245
  return result;
1243
1246
  }
1244
- catch (err) {
1247
+ catch (error) {
1245
1248
  await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Crash);
1246
- throw err;
1249
+ throw error;
1247
1250
  }
1248
1251
  };
1249
1252
  runtime.rpcHandler = rpcHandler;
@@ -1260,7 +1263,7 @@ async function _SnapController_getRpcRequestHandler(snapId) {
1260
1263
  * @template PromiseValue - The value of the Promise.
1261
1264
  */
1262
1265
  async function _SnapController_executeWithTimeout(snapId, promise, timer) {
1263
- const isLongRunning = await this.messagingSystem.call('PermissionController:hasPermission', snapId, endowments_1.SnapEndowments.LongRunning);
1266
+ const isLongRunning = this.messagingSystem.call('PermissionController:hasPermission', snapId, endowments_1.SnapEndowments.LongRunning);
1264
1267
  // Long running snaps have timeouts disabled
1265
1268
  if (isLongRunning) {
1266
1269
  return promise;
@@ -1276,10 +1279,76 @@ async function _SnapController_executeWithTimeout(snapId, promise, timer) {
1276
1279
  runtime.lastRequest = null;
1277
1280
  }, _SnapController_recordSnapRpcRequestFinish = function _SnapController_recordSnapRpcRequestFinish(snapId, requestId) {
1278
1281
  const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1279
- runtime.pendingInboundRequests = runtime.pendingInboundRequests.filter((r) => r.requestId !== requestId);
1282
+ runtime.pendingInboundRequests = runtime.pendingInboundRequests.filter((request) => request.requestId !== requestId);
1280
1283
  if (runtime.pendingInboundRequests.length === 0) {
1281
1284
  runtime.lastRequest = Date.now();
1282
1285
  }
1286
+ }, _SnapController_getRollbackSnapshot = function _SnapController_getRollbackSnapshot(snapId) {
1287
+ return __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").get(snapId);
1288
+ }, _SnapController_createRollbackSnapshot = function _SnapController_createRollbackSnapshot(snapId) {
1289
+ (0, utils_1.assert)(__classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").get(snapId) === undefined, new Error(`Snap "${snapId}" rollback snapshot already exists.`));
1290
+ __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").set(snapId, {
1291
+ statePatches: [],
1292
+ sourceCode: '',
1293
+ permissions: { revoked: null, granted: [], requestData: null },
1294
+ newVersion: '',
1295
+ });
1296
+ const newRollbackSnapshot = __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").get(snapId);
1297
+ (0, utils_1.assert)(newRollbackSnapshot !== undefined, new Error(`Snapshot creation failed for ${snapId}.`));
1298
+ return newRollbackSnapshot;
1299
+ }, _SnapController_rollbackSnap =
1300
+ /**
1301
+ * Rolls back a snap to its previous state, permissions
1302
+ * and source code based on the `RollbackSnapshot` that
1303
+ * is captured during the update process. After rolling back,
1304
+ * the function also emits an event indicating that the
1305
+ * snap has been rolled back and it clears the snapshot
1306
+ * for that snap.
1307
+ *
1308
+ * @param snapId - The snap id.
1309
+ * @throws {@link Error}. If a snapshot does not exist.
1310
+ */
1311
+ async function _SnapController_rollbackSnap(snapId) {
1312
+ var _a;
1313
+ const rollbackSnapshot = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRollbackSnapshot).call(this, snapId);
1314
+ if (!rollbackSnapshot) {
1315
+ throw new Error('A snapshot does not exist for this snap.');
1316
+ }
1317
+ await this.stopSnap(snapId, snaps_utils_1.SnapStatusEvents.Stop);
1318
+ const { statePatches, sourceCode, permissions } = rollbackSnapshot;
1319
+ if (statePatches === null || statePatches === void 0 ? void 0 : statePatches.length) {
1320
+ this.applyPatches(statePatches);
1321
+ }
1322
+ if (sourceCode) {
1323
+ const runtime = __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_getRuntimeExpect).call(this, snapId);
1324
+ runtime.sourceCode = sourceCode;
1325
+ }
1326
+ if (permissions.revoked && Object.keys(permissions.revoked).length) {
1327
+ this.messagingSystem.call('PermissionController:grantPermissions', {
1328
+ approvedPermissions: permissions.revoked,
1329
+ subject: { origin: snapId },
1330
+ requestData: permissions.requestData,
1331
+ });
1332
+ }
1333
+ if ((_a = permissions.granted) === null || _a === void 0 ? void 0 : _a.length) {
1334
+ this.messagingSystem.call('PermissionController:revokePermissions', {
1335
+ [snapId]: permissions.granted,
1336
+ });
1337
+ }
1338
+ const truncatedSnap = this.getTruncatedExpect(snapId);
1339
+ this.messagingSystem.publish('SnapController:snapRolledback', truncatedSnap, rollbackSnapshot.newVersion);
1340
+ __classPrivateFieldGet(this, _SnapController_rollbackSnapshots, "f").delete(snapId);
1341
+ }, _SnapController_rollbackSnaps =
1342
+ /**
1343
+ * Iterates through an array of snap ids
1344
+ * and calls `rollbackSnap` on them.
1345
+ *
1346
+ * @param snapIds - An array of snap ids.
1347
+ */
1348
+ async function _SnapController_rollbackSnaps(snapIds) {
1349
+ for (const snapId of snapIds) {
1350
+ await __classPrivateFieldGet(this, _SnapController_instances, "m", _SnapController_rollbackSnap).call(this, snapId);
1351
+ }
1283
1352
  }, _SnapController_getRuntime = function _SnapController_getRuntime(snapId) {
1284
1353
  return this.snapsRuntimeData.get(snapId);
1285
1354
  }, _SnapController_getRuntimeExpect = function _SnapController_getRuntimeExpect(snapId) {
@@ -1299,9 +1368,9 @@ async function _SnapController_executeWithTimeout(snapId, promise, timer) {
1299
1368
  });
1300
1369
  (0, fsm_2.forceStrict)(interpreter);
1301
1370
  this.snapsRuntimeData.set(snapId, Object.assign({ lastRequest: null, rpcHandler: null, installPromise: null, activeReferences: 0, pendingInboundRequests: [], pendingOutboundRequests: 0, interpreter }, data));
1302
- }, _SnapController_calculatePermissionsChange = async function _SnapController_calculatePermissionsChange(snapId, desiredPermissionsSet) {
1371
+ }, _SnapController_calculatePermissionsChange = function _SnapController_calculatePermissionsChange(snapId, desiredPermissionsSet) {
1303
1372
  var _a;
1304
- const oldPermissions = (_a = (await this.messagingSystem.call('PermissionController:getPermissions', snapId))) !== null && _a !== void 0 ? _a : {};
1373
+ const oldPermissions = (_a = this.messagingSystem.call('PermissionController:getPermissions', snapId)) !== null && _a !== void 0 ? _a : {};
1305
1374
  const newPermissions = (0, utils_2.setDiff)(desiredPermissionsSet, oldPermissions);
1306
1375
  // TODO(ritave): The assumption that these are unused only holds so long as we do not
1307
1376
  // permit dynamic permission requests.
@@ -1310,5 +1379,14 @@ async function _SnapController_executeWithTimeout(snapId, promise, timer) {
1310
1379
  // oldPermissions ∖ (oldPermissions ∖ desiredPermissionsSet) ⟺ oldPermissions ∩ desiredPermissionsSet
1311
1380
  const approvedPermissions = (0, utils_2.setDiff)(oldPermissions, unusedPermissions);
1312
1381
  return { newPermissions, unusedPermissions, approvedPermissions };
1382
+ }, _SnapController_isValidUpdate = function _SnapController_isValidUpdate(snapId, newVersionRange) {
1383
+ const existingSnap = this.getExpect(snapId);
1384
+ if ((0, snaps_utils_1.satisfiesVersionRange)(existingSnap.version, newVersionRange)) {
1385
+ return false;
1386
+ }
1387
+ if ((0, snaps_utils_1.gtRange)(existingSnap.version, newVersionRange)) {
1388
+ return false;
1389
+ }
1390
+ return true;
1313
1391
  };
1314
1392
  //# sourceMappingURL=SnapController.js.map