@metamask/snaps-controllers 3.0.0 → 3.1.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 (54) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/dist/cjs/cronjob/CronjobController.js +1 -1
  3. package/dist/cjs/cronjob/CronjobController.js.map +1 -1
  4. package/dist/cjs/services/AbstractExecutionService.js +6 -7
  5. package/dist/cjs/services/AbstractExecutionService.js.map +1 -1
  6. package/dist/cjs/snaps/SnapController.js +73 -82
  7. package/dist/cjs/snaps/SnapController.js.map +1 -1
  8. package/dist/cjs/snaps/endowments/cronjob.js +4 -4
  9. package/dist/cjs/snaps/endowments/cronjob.js.map +1 -1
  10. package/dist/cjs/snaps/endowments/keyring.js +4 -4
  11. package/dist/cjs/snaps/endowments/keyring.js.map +1 -1
  12. package/dist/cjs/snaps/endowments/name-lookup.js +3 -3
  13. package/dist/cjs/snaps/endowments/name-lookup.js.map +1 -1
  14. package/dist/cjs/snaps/endowments/rpc.js +4 -4
  15. package/dist/cjs/snaps/endowments/rpc.js.map +1 -1
  16. package/dist/cjs/snaps/endowments/transaction-insight.js +3 -3
  17. package/dist/cjs/snaps/endowments/transaction-insight.js.map +1 -1
  18. package/dist/cjs/snaps/location/npm.js +41 -14
  19. package/dist/cjs/snaps/location/npm.js.map +1 -1
  20. package/dist/cjs/snaps/permissions.js +5 -5
  21. package/dist/cjs/snaps/permissions.js.map +1 -1
  22. package/dist/cjs/snaps/registry/json.js +30 -1
  23. package/dist/cjs/snaps/registry/json.js.map +1 -1
  24. package/dist/cjs/snaps/registry/registry.js.map +1 -1
  25. package/dist/esm/cronjob/CronjobController.js +1 -1
  26. package/dist/esm/cronjob/CronjobController.js.map +1 -1
  27. package/dist/esm/services/AbstractExecutionService.js +6 -7
  28. package/dist/esm/services/AbstractExecutionService.js.map +1 -1
  29. package/dist/esm/snaps/SnapController.js +70 -79
  30. package/dist/esm/snaps/SnapController.js.map +1 -1
  31. package/dist/esm/snaps/endowments/cronjob.js +4 -4
  32. package/dist/esm/snaps/endowments/cronjob.js.map +1 -1
  33. package/dist/esm/snaps/endowments/keyring.js +4 -4
  34. package/dist/esm/snaps/endowments/keyring.js.map +1 -1
  35. package/dist/esm/snaps/endowments/name-lookup.js +3 -3
  36. package/dist/esm/snaps/endowments/name-lookup.js.map +1 -1
  37. package/dist/esm/snaps/endowments/rpc.js +4 -4
  38. package/dist/esm/snaps/endowments/rpc.js.map +1 -1
  39. package/dist/esm/snaps/endowments/transaction-insight.js +3 -3
  40. package/dist/esm/snaps/endowments/transaction-insight.js.map +1 -1
  41. package/dist/esm/snaps/location/npm.js +42 -15
  42. package/dist/esm/snaps/location/npm.js.map +1 -1
  43. package/dist/esm/snaps/permissions.js +1 -1
  44. package/dist/esm/snaps/permissions.js.map +1 -1
  45. package/dist/esm/snaps/registry/json.js +31 -2
  46. package/dist/esm/snaps/registry/json.js.map +1 -1
  47. package/dist/esm/snaps/registry/registry.js.map +1 -1
  48. package/dist/types/cronjob/CronjobController.d.ts +1 -1
  49. package/dist/types/services/AbstractExecutionService.d.ts +1 -1
  50. package/dist/types/snaps/SnapController.d.ts +20 -32
  51. package/dist/types/snaps/location/npm.d.ts +1 -1
  52. package/dist/types/snaps/registry/json.d.ts +5 -1
  53. package/dist/types/snaps/registry/registry.d.ts +11 -1
  54. package/package.json +29 -22
@@ -63,11 +63,11 @@ function _define_property(obj, key, value) {
63
63
  }
64
64
  import { BaseControllerV2 as BaseController } from '@metamask/base-controller';
65
65
  import { SubjectType } from '@metamask/permission-controller';
66
- import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/rpc-methods';
67
- import { assertIsSnapManifest, assertIsValidSnapId, DEFAULT_ENDOWMENTS, DEFAULT_REQUESTED_SNAP_VERSION, getErrorMessage, HandlerType, isOriginAllowed, logError, normalizeRelative, resolveVersionRange, SnapCaveatType, SnapStatus, SnapStatusEvents, validateFetchedSnap } from '@metamask/snaps-utils';
66
+ import { rpcErrors } from '@metamask/rpc-errors';
67
+ import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/snaps-rpc-methods';
68
+ import { assertIsSnapManifest, assertIsValidSnapId, AuxiliaryFileEncoding, DEFAULT_ENDOWMENTS, DEFAULT_REQUESTED_SNAP_VERSION, encodeAuxiliaryFile, getErrorMessage, HandlerType, isOriginAllowed, logError, normalizeRelative, resolveVersionRange, SnapCaveatType, SnapStatus, SnapStatusEvents, validateFetchedSnap, unwrapError } from '@metamask/snaps-utils';
68
69
  import { assert, assertIsJsonRpcRequest, Duration, gtRange, gtVersion, hasProperty, inMilliseconds, isNonEmptyArray, isValidSemVerRange, satisfiesVersionRange, timeSince } from '@metamask/utils';
69
70
  import { createMachine, interpret } from '@xstate/fsm';
70
- import { ethErrors } from 'eth-rpc-errors';
71
71
  import { nanoid } from 'nanoid';
72
72
  import { forceStrict, validateMachine } from '../fsm';
73
73
  import { log } from '../logging';
@@ -93,7 +93,6 @@ const TRUNCATED_SNAP_PROPERTIES = new Set([
93
93
  'blocked'
94
94
  ]);
95
95
  const defaultState = {
96
- snapErrors: {},
97
96
  snaps: {},
98
97
  snapStates: {}
99
98
  };
@@ -149,7 +148,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
149
148
  * Safely revokes all permissions granted to a Snap.
150
149
  *
151
150
  * @param snapId - The snap ID.
152
- */ _revokeAllSnapPermissions = /*#__PURE__*/ new WeakSet(), _createApproval = /*#__PURE__*/ new WeakSet(), _updateApproval = /*#__PURE__*/ new WeakSet(), _add = /*#__PURE__*/ new WeakSet(), _startSnap = /*#__PURE__*/ new WeakSet(), _getEndowments = /*#__PURE__*/ new WeakSet(), /**
151
+ */ _revokeAllSnapPermissions = /*#__PURE__*/ new WeakSet(), _createApproval = /*#__PURE__*/ new WeakSet(), _updateApproval = /*#__PURE__*/ new WeakSet(), _resolveAllowlistVersion = /*#__PURE__*/ new WeakSet(), _add = /*#__PURE__*/ new WeakSet(), _startSnap = /*#__PURE__*/ new WeakSet(), _getEndowments = /*#__PURE__*/ new WeakSet(), /**
153
152
  * Sets a snap in state. Called when a snap is installed or updated. Performs
154
153
  * various validation checks on the received arguments, and will throw if
155
154
  * validation fails.
@@ -220,8 +219,8 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
220
219
  return _class_private_method_get(this, _unblockSnap, unblockSnap).call(this, snapId);
221
220
  }));
222
221
  }
223
- _onUnhandledSnapError(snapId, error) {
224
- this.stopSnap(snapId, SnapStatusEvents.Crash).then(()=>this.addSnapError(error)).catch((stopSnapError)=>{
222
+ _onUnhandledSnapError(snapId, _error) {
223
+ this.stopSnap(snapId, SnapStatusEvents.Crash).catch((stopSnapError)=>{
225
224
  // TODO: Decide how to handle errors.
226
225
  logError(stopSnapError);
227
226
  });
@@ -397,35 +396,6 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
397
396
  });
398
397
  }
399
398
  /**
400
- * Adds error from a snap to the SnapController state.
401
- *
402
- * @param snapError - The error to store on the SnapController.
403
- */ addSnapError(snapError) {
404
- this.update((state)=>{
405
- const id = nanoid();
406
- state.snapErrors[id] = {
407
- ...snapError,
408
- internalID: id
409
- };
410
- });
411
- }
412
- /**
413
- * Removes an error by internalID from the SnapControllers state.
414
- *
415
- * @param internalID - The internal error ID to remove on the SnapController.
416
- */ removeSnapError(internalID) {
417
- this.update((state)=>{
418
- delete state.snapErrors[internalID];
419
- });
420
- }
421
- /**
422
- * Clears all errors from the SnapControllers state.
423
- */ clearSnapErrors() {
424
- this.update((state)=>{
425
- state.snapErrors = {};
426
- });
427
- }
428
- /**
429
399
  * Gets the own state of the snap with the given id.
430
400
  * This is distinct from the state MetaMask uses to manage snaps.
431
401
  *
@@ -437,6 +407,22 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
437
407
  return state ?? null;
438
408
  }
439
409
  /**
410
+ * Gets a static auxiliary snap file in a chosen file encoding.
411
+ *
412
+ * @param snapId - The id of the Snap whose state to get.
413
+ * @param path - The path to the requested file.
414
+ * @param encoding - An optional requested file encoding.
415
+ * @returns The file requested in the chosen file encoding or null if the file is not found.
416
+ */ getSnapFile(snapId, path, encoding = AuxiliaryFileEncoding.Base64) {
417
+ const snap = this.getExpect(snapId);
418
+ const normalizedPath = normalizeRelative(path);
419
+ const value = snap.auxiliaryFiles?.find((file)=>file.path === normalizedPath)?.value;
420
+ if (!value) {
421
+ return null;
422
+ }
423
+ return encodeAuxiliaryFile(value, encoding);
424
+ }
425
+ /**
440
426
  * Completely clear the controller's state: delete all associated data,
441
427
  * handlers, event listeners, and permissions; tear down all snap providers.
442
428
  */ async clearState() {
@@ -590,10 +576,12 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
590
576
  try {
591
577
  for (const [snapId, { version: rawVersion }] of Object.entries(requestedSnaps)){
592
578
  assertIsValidSnapId(snapId);
593
- const [error, version] = resolveVersionRange(rawVersion);
579
+ const [error, resolvedVersion] = resolveVersionRange(rawVersion);
594
580
  if (error) {
595
- throw ethErrors.rpc.invalidParams(`The "version" field must be a valid SemVer version range if specified. Received: "${rawVersion}".`);
581
+ throw rpcErrors.invalidParams(`The "version" field must be a valid SemVer version range if specified. Received: "${rawVersion}".`);
596
582
  }
583
+ // If we are running in allowlist mode, try to match the version with an allowlist version.
584
+ const version = _class_private_field_get(this, _featureFlags).requireAllowlist ? await _class_private_method_get(this, _resolveAllowlistVersion, resolveAllowlistVersion).call(this, snapId, resolvedVersion) : resolvedVersion;
597
585
  const location = _class_private_field_get(this, _detectSnapLocation).call(this, snapId, {
598
586
  versionRange: version,
599
587
  fetch: _class_private_field_get(this, _fetchFunction),
@@ -621,8 +609,8 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
621
609
  result[snapId] = await this.processRequestedSnap(origin, snapId, location, version);
622
610
  }
623
611
  // Once we finish all installs / updates, emit events.
624
- pendingInstalls.forEach((snapId)=>this.messagingSystem.publish(`SnapController:snapInstalled`, this.getTruncatedExpect(snapId)));
625
- pendingUpdates.forEach(({ snapId, oldVersion })=>this.messagingSystem.publish(`SnapController:snapUpdated`, this.getTruncatedExpect(snapId), oldVersion));
612
+ pendingInstalls.forEach((snapId)=>this.messagingSystem.publish(`SnapController:snapInstalled`, this.getTruncatedExpect(snapId), origin));
613
+ pendingUpdates.forEach(({ snapId, oldVersion })=>this.messagingSystem.publish(`SnapController:snapUpdated`, this.getTruncatedExpect(snapId), oldVersion, origin));
626
614
  snapIds.forEach((snapId)=>_class_private_field_get(this, _rollbackSnapshots).delete(snapId));
627
615
  } catch (error) {
628
616
  const installed = pendingInstalls.filter((snapId)=>this.has(snapId));
@@ -655,7 +643,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
655
643
  return await this.updateSnap(origin, snapId, location, versionRange, // Since we are requesting an update from within processRequestedSnap,
656
644
  // we disable the emitting of the snapUpdated event and rely on the caller
657
645
  // to publish this event after the update is complete.
658
- // This is necesary as installSnaps may be installing multiple snaps
646
+ // This is necessary as installSnaps may be installing multiple snaps
659
647
  // and we don't want to emit events prematurely.
660
648
  false);
661
649
  }
@@ -735,23 +723,25 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
735
723
  try {
736
724
  const snap = this.getExpect(snapId);
737
725
  const newSnap = await _class_private_method_get(this, _fetchSnap, fetchSnap).call(this, snapId, location);
738
- const newVersion = newSnap.manifest.result.version;
726
+ const { sourceCode: sourceCodeFile, manifest: manifestFile } = newSnap.files;
727
+ const manifest = manifestFile.result;
728
+ const newVersion = manifest.version;
739
729
  if (!gtVersion(newVersion, snap.version)) {
740
- throw ethErrors.rpc.invalidParams(`Snap "${snapId}@${snap.version}" is already installed. Couldn't update to a version inside requested "${newVersionRange}" range.`);
730
+ throw rpcErrors.invalidParams(`Snap "${snapId}@${snap.version}" is already installed. Couldn't update to a version inside requested "${newVersionRange}" range.`);
741
731
  }
742
732
  if (!satisfiesVersionRange(newVersion, newVersionRange)) {
743
733
  throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${newVersion}" which doesn't satisfy requested version range "${newVersionRange}".`);
744
734
  }
745
735
  await _class_private_method_get(this, _assertIsInstallAllowed, assertIsInstallAllowed).call(this, snapId, {
746
736
  version: newVersion,
747
- checksum: newSnap.manifest.result.source.shasum
737
+ checksum: manifest.source.shasum
748
738
  });
749
- const processedPermissions = processSnapPermissions(newSnap.manifest.result.initialPermissions);
739
+ const processedPermissions = processSnapPermissions(manifest.initialPermissions);
750
740
  _class_private_method_get(this, _validateSnapPermissions, validateSnapPermissions).call(this, processedPermissions);
751
741
  const { newPermissions, unusedPermissions, approvedPermissions } = _class_private_method_get(this, _calculatePermissionsChange, calculatePermissionsChange).call(this, snapId, processedPermissions);
752
742
  _class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
753
743
  permissions: newPermissions,
754
- newVersion: newSnap.manifest.result.version,
744
+ newVersion: manifest.version,
755
745
  newPermissions,
756
746
  approvedPermissions,
757
747
  unusedPermissions,
@@ -770,7 +760,6 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
770
760
  _class_private_method_get(this, _set, set).call(this, {
771
761
  origin,
772
762
  id: snapId,
773
- manifest: newSnap.manifest,
774
763
  files: newSnap.files,
775
764
  isUpdate: true
776
765
  });
@@ -795,8 +784,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
795
784
  rollbackSnapshot.permissions.granted = Object.keys(approvedNewPermissions);
796
785
  rollbackSnapshot.permissions.requestData = requestData;
797
786
  }
798
- const normalizedSourcePath = normalizeRelative(newSnap.manifest.result.source.location.npm.filePath);
799
- const sourceCode = newSnap.files.find((file)=>file.path === normalizedSourcePath)?.toString();
787
+ const sourceCode = sourceCodeFile.toString();
800
788
  assert(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
801
789
  try {
802
790
  await _class_private_method_get(this, _startSnap, startSnap).call(this, {
@@ -808,7 +796,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
808
796
  }
809
797
  const truncatedSnap = this.getTruncatedExpect(snapId);
810
798
  if (emitEvent) {
811
- this.messagingSystem.publish('SnapController:snapUpdated', truncatedSnap, snap.version);
799
+ this.messagingSystem.publish('SnapController:snapUpdated', truncatedSnap, snap.version, origin);
812
800
  }
813
801
  _class_private_method_get(this, _updateApproval, updateApproval).call(this, pendingApproval.id, {
814
802
  loading: false,
@@ -878,6 +866,8 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
878
866
  /* eslint-disable @typescript-eslint/unbound-method */ this.messagingSystem.unsubscribe('ExecutionService:unhandledError', this._onUnhandledSnapError);
879
867
  this.messagingSystem.unsubscribe('ExecutionService:outboundRequest', this._onOutboundRequest);
880
868
  this.messagingSystem.unsubscribe('ExecutionService:outboundResponse', this._onOutboundResponse);
869
+ this.messagingSystem.clearEventSubscriptions('SnapController:snapInstalled');
870
+ this.messagingSystem.clearEventSubscriptions('SnapController:snapUpdated');
881
871
  /* eslint-enable @typescript-eslint/unbound-method */ }
882
872
  /**
883
873
  * Passes a JSON-RPC request object to the RPC handler function of a snap.
@@ -927,17 +917,15 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
927
917
  super({
928
918
  messenger,
929
919
  metadata: {
930
- snapErrors: {
931
- persist: false,
932
- anonymous: false
933
- },
934
920
  snapStates: {
935
921
  persist: true,
936
922
  anonymous: false
937
923
  },
938
924
  snaps: {
939
925
  persist: (snaps)=>{
940
- return Object.values(snaps).map((snap)=>{
926
+ return Object.values(snaps)// We should not persist snaps that are in the installing state,
927
+ // since they haven't completed installation and would be unusable
928
+ .filter((snap)=>snap.status !== SnapStatus.Installing).map((snap)=>{
941
929
  return {
942
930
  ...snap,
943
931
  // At the time state is rehydrated, no snap will be running.
@@ -980,6 +968,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
980
968
  _class_private_method_init(this, _revokeAllSnapPermissions);
981
969
  _class_private_method_init(this, _createApproval);
982
970
  _class_private_method_init(this, _updateApproval);
971
+ _class_private_method_init(this, _resolveAllowlistVersion);
983
972
  /**
984
973
  * Returns a promise representing the complete installation of the requested snap.
985
974
  * If the snap is already being installed, the previously pending promise will be returned.
@@ -1005,8 +994,6 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
1005
994
  /**
1006
995
  * Fetches the manifest and source code of a snap.
1007
996
  *
1008
- * This function is not hash private yet because of tests.
1009
- *
1010
997
  * @param snapId - The id of the Snap.
1011
998
  * @param location - Source from which snap will be fetched.
1012
999
  * @returns A tuple of the Snap manifest object and the Snap source code.
@@ -1215,13 +1202,13 @@ function registerMessageHandlers() {
1215
1202
  this.messagingSystem.registerActionHandler(`${controllerName}:remove`, async (...args)=>this.removeSnap(...args));
1216
1203
  this.messagingSystem.registerActionHandler(`${controllerName}:getPermitted`, (...args)=>this.getPermittedSnaps(...args));
1217
1204
  this.messagingSystem.registerActionHandler(`${controllerName}:install`, async (...args)=>this.installSnaps(...args));
1218
- this.messagingSystem.registerActionHandler(`${controllerName}:removeSnapError`, (...args)=>this.removeSnapError(...args));
1219
1205
  this.messagingSystem.registerActionHandler(`${controllerName}:getAll`, (...args)=>this.getAllSnaps(...args));
1220
1206
  this.messagingSystem.registerActionHandler(`${controllerName}:incrementActiveReferences`, (...args)=>this.incrementActiveReferences(...args));
1221
1207
  this.messagingSystem.registerActionHandler(`${controllerName}:decrementActiveReferences`, (...args)=>this.decrementActiveReferences(...args));
1222
1208
  this.messagingSystem.registerActionHandler(`${controllerName}:getRegistryMetadata`, async (...args)=>this.getRegistryMetadata(...args));
1223
1209
  this.messagingSystem.registerActionHandler(`${controllerName}:disconnectOrigin`, (...args)=>this.removeSnapFromSubject(...args));
1224
1210
  this.messagingSystem.registerActionHandler(`${controllerName}:revokeDynamicPermissions`, (...args)=>this.revokeDynamicSnapPermissions(...args));
1211
+ this.messagingSystem.registerActionHandler(`${controllerName}:getFile`, (...args)=>this.getSnapFile(...args));
1225
1212
  }
1226
1213
  function pollForLastRequestStatus() {
1227
1214
  _class_private_field_set(this, _timeoutForLastRequestStatus, setTimeout(()=>{
@@ -1265,7 +1252,7 @@ async function assertIsInstallAllowed(snapId, snapInfo) {
1265
1252
  if (result.status === SnapsRegistryStatus.Blocked) {
1266
1253
  throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The version is blocked. ${result.reason?.explanation ?? ''}`);
1267
1254
  } else if (_class_private_field_get(this, _featureFlags).requireAllowlist && result.status !== SnapsRegistryStatus.Verified) {
1268
- throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The snap is not on the allow list.`);
1255
+ throw new Error(`Cannot install version "${snapInfo.version}" of snap "${snapId}": The snap is not on the allowlist.`);
1269
1256
  }
1270
1257
  }
1271
1258
  async function stopSnapsLastRequestPastMax() {
@@ -1331,6 +1318,9 @@ function updateApproval(id, requestState) {
1331
1318
  // Do nothing
1332
1319
  }
1333
1320
  }
1321
+ async function resolveAllowlistVersion(snapId, versionRange) {
1322
+ return await this.messagingSystem.call('SnapsRegistry:resolveVersion', snapId, versionRange);
1323
+ }
1334
1324
  async function add(args) {
1335
1325
  const { id: snapId, location, versionRange } = args;
1336
1326
  _class_private_method_get(this, _setupRuntime, setupRuntime).call(this, snapId, {
@@ -1344,7 +1334,7 @@ async function add(args) {
1344
1334
  // to null in the authorize() method.
1345
1335
  runtime.installPromise = (async ()=>{
1346
1336
  const fetchedSnap = await _class_private_method_get(this, _fetchSnap, fetchSnap).call(this, snapId, location);
1347
- const manifest = fetchedSnap.manifest.result;
1337
+ const manifest = fetchedSnap.files.manifest.result;
1348
1338
  if (!satisfiesVersionRange(manifest.version, versionRange)) {
1349
1339
  throw new Error(`Version mismatch. Manifest for "${snapId}" specifies version "${manifest.version}" which doesn't satisfy requested version range "${versionRange}".`);
1350
1340
  }
@@ -1414,15 +1404,16 @@ async function getEndowments(snapId) {
1414
1404
  return dedupedEndowments;
1415
1405
  }
1416
1406
  function set(args) {
1417
- const { id: snapId, origin, manifest, files, isUpdate = false } = args;
1407
+ const { id: snapId, origin, files, isUpdate = false } = args;
1408
+ const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles } = files;
1418
1409
  assertIsSnapManifest(manifest.result);
1419
1410
  const { version } = manifest.result;
1420
- const normalizedSourcePath = normalizeRelative(manifest.result.source.location.npm.filePath);
1421
- const { iconPath } = manifest.result.source.location.npm;
1422
- const normalizedIconPath = iconPath && normalizeRelative(iconPath);
1423
- const sourceCode = files.find((file)=>file.path === normalizedSourcePath)?.toString();
1424
- const svgIcon = normalizedIconPath ? files.find((file)=>file.path === normalizedIconPath) : undefined;
1411
+ const sourceCode = sourceCodeFile.toString();
1425
1412
  assert(typeof sourceCode === 'string' && sourceCode.length > 0, `Invalid source code for snap "${snapId}".`);
1413
+ const auxiliaryFiles = rawAuxiliaryFiles.map((file)=>({
1414
+ path: file.path,
1415
+ value: file.toString('base64')
1416
+ }));
1426
1417
  const snapsState = this.state.snaps;
1427
1418
  const existingSnap = snapsState[snapId];
1428
1419
  const previousVersionHistory = existingSnap?.versionHistory ?? [];
@@ -1447,7 +1438,8 @@ function set(args) {
1447
1438
  status: _class_private_field_get(this, _statusMachine).config.initial,
1448
1439
  sourceCode,
1449
1440
  version,
1450
- versionHistory
1441
+ versionHistory,
1442
+ auxiliaryFiles
1451
1443
  };
1452
1444
  // If the snap was blocked, it isn't any longer
1453
1445
  delete snap.blockInformation;
@@ -1475,19 +1467,15 @@ async function fetchSnap(snapId, location) {
1475
1467
  const sourceCode = await location.fetch(manifest.result.source.location.npm.filePath);
1476
1468
  const { iconPath } = manifest.result.source.location.npm;
1477
1469
  const svgIcon = iconPath ? await location.fetch(iconPath) : undefined;
1478
- const files = [
1479
- sourceCode
1480
- ];
1481
- if (svgIcon) {
1482
- files.push(svgIcon);
1483
- }
1484
- validateFetchedSnap({
1470
+ const auxiliaryFiles = manifest.result.source.files ? await Promise.all(manifest.result.source.files.map(async (filePath)=>location.fetch(filePath))) : [];
1471
+ const files = {
1485
1472
  manifest,
1486
1473
  sourceCode,
1487
- svgIcon
1488
- });
1474
+ svgIcon,
1475
+ auxiliaryFiles
1476
+ };
1477
+ validateFetchedSnap(files);
1489
1478
  return {
1490
- manifest,
1491
1479
  files,
1492
1480
  location
1493
1481
  };
@@ -1556,8 +1544,11 @@ function getRpcRequestHandler(snapId) {
1556
1544
  _class_private_method_get(this, _recordSnapRpcRequestFinish, recordSnapRpcRequestFinish).call(this, snapId, request.id);
1557
1545
  return result;
1558
1546
  } catch (error) {
1559
- await this.stopSnap(snapId, SnapStatusEvents.Crash);
1560
- throw error;
1547
+ const [jsonRpcError, handled] = unwrapError(error);
1548
+ if (!handled) {
1549
+ await this.stopSnap(snapId, SnapStatusEvents.Crash);
1550
+ }
1551
+ throw jsonRpcError;
1561
1552
  }
1562
1553
  };
1563
1554
  runtime.rpcHandler = rpcHandler;