@metamask/snaps-controllers 3.2.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -1
- package/dist/cjs/cronjob/CronjobController.js.map +1 -1
- package/dist/cjs/services/ExecutionService.js.map +1 -1
- package/dist/cjs/snaps/SnapController.js +63 -25
- package/dist/cjs/snaps/SnapController.js.map +1 -1
- package/dist/cjs/snaps/endowments/enum.js +1 -0
- package/dist/cjs/snaps/endowments/enum.js.map +1 -1
- package/dist/cjs/snaps/endowments/home-page.js +37 -0
- package/dist/cjs/snaps/endowments/home-page.js.map +1 -0
- package/dist/cjs/snaps/endowments/index.js +5 -2
- package/dist/cjs/snaps/endowments/index.js.map +1 -1
- package/dist/cjs/snaps/registry/json.js.map +1 -1
- package/dist/cjs/snaps/registry/registry.js.map +1 -1
- package/dist/cjs/utils.js +9 -0
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/cronjob/CronjobController.js.map +1 -1
- package/dist/esm/services/ExecutionService.js.map +1 -1
- package/dist/esm/snaps/SnapController.js +61 -23
- package/dist/esm/snaps/SnapController.js.map +1 -1
- package/dist/esm/snaps/endowments/enum.js +1 -0
- package/dist/esm/snaps/endowments/enum.js.map +1 -1
- package/dist/esm/snaps/endowments/home-page.js +27 -0
- package/dist/esm/snaps/endowments/home-page.js.map +1 -0
- package/dist/esm/snaps/endowments/index.js +5 -2
- package/dist/esm/snaps/endowments/index.js.map +1 -1
- package/dist/esm/snaps/registry/json.js.map +1 -1
- package/dist/esm/snaps/registry/registry.js.map +1 -1
- package/dist/esm/utils.js +12 -0
- package/dist/esm/utils.js.map +1 -1
- package/dist/types/cronjob/CronjobController.d.ts +5 -4
- package/dist/types/services/ExecutionService.d.ts +4 -4
- package/dist/types/snaps/SnapController.d.ts +40 -35
- package/dist/types/snaps/endowments/enum.d.ts +2 -1
- package/dist/types/snaps/endowments/home-page.d.ts +15 -0
- package/dist/types/snaps/endowments/index.d.ts +9 -0
- package/dist/types/snaps/registry/registry.d.ts +3 -3
- package/dist/types/utils.d.ts +9 -0
- package/package.json +6 -6
|
@@ -19,6 +19,7 @@ var SnapEndowments;
|
|
|
19
19
|
SnapEndowments["NameLookup"] = 'endowment:name-lookup';
|
|
20
20
|
SnapEndowments["LifecycleHooks"] = 'endowment:lifecycle-hooks';
|
|
21
21
|
SnapEndowments["Keyring"] = 'endowment:keyring';
|
|
22
|
+
SnapEndowments["HomePage"] = 'endowment:page-home';
|
|
22
23
|
})(SnapEndowments || (SnapEndowments = {}));
|
|
23
24
|
|
|
24
25
|
//# sourceMappingURL=enum.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/snaps/endowments/enum.ts"],"sourcesContent":["export enum SnapEndowments {\n NetworkAccess = 'endowment:network-access',\n TransactionInsight = 'endowment:transaction-insight',\n Cronjob = 'endowment:cronjob',\n EthereumProvider = 'endowment:ethereum-provider',\n Rpc = 'endowment:rpc',\n WebAssemblyAccess = 'endowment:webassembly',\n NameLookup = 'endowment:name-lookup',\n LifecycleHooks = 'endowment:lifecycle-hooks',\n Keyring = 'endowment:keyring',\n}\n"],"names":["SnapEndowments","NetworkAccess","TransactionInsight","Cronjob","EthereumProvider","Rpc","WebAssemblyAccess","NameLookup","LifecycleHooks","Keyring"],"mappings":";;;;;;;;;;IAAO;UAAKA,cAAc;IAAdA,eACVC,mBAAgB;IADND,eAEVE,wBAAqB;IAFXF,eAGVG,aAAU;IAHAH,eAIVI,sBAAmB;IAJTJ,eAKVK,SAAM;IALIL,eAMVM,uBAAoB;IANVN,eAOVO,gBAAa;IAPHP,eAQVQ,oBAAiB;IARPR,eASVS,aAAU;
|
|
1
|
+
{"version":3,"sources":["../../../../src/snaps/endowments/enum.ts"],"sourcesContent":["export enum SnapEndowments {\n NetworkAccess = 'endowment:network-access',\n TransactionInsight = 'endowment:transaction-insight',\n Cronjob = 'endowment:cronjob',\n EthereumProvider = 'endowment:ethereum-provider',\n Rpc = 'endowment:rpc',\n WebAssemblyAccess = 'endowment:webassembly',\n NameLookup = 'endowment:name-lookup',\n LifecycleHooks = 'endowment:lifecycle-hooks',\n Keyring = 'endowment:keyring',\n HomePage = 'endowment:page-home',\n}\n"],"names":["SnapEndowments","NetworkAccess","TransactionInsight","Cronjob","EthereumProvider","Rpc","WebAssemblyAccess","NameLookup","LifecycleHooks","Keyring","HomePage"],"mappings":";;;;;;;;;;IAAO;UAAKA,cAAc;IAAdA,eACVC,mBAAgB;IADND,eAEVE,wBAAqB;IAFXF,eAGVG,aAAU;IAHAH,eAIVI,sBAAmB;IAJTJ,eAKVK,SAAM;IALIL,eAMVM,uBAAoB;IANVN,eAOVO,gBAAa;IAPHP,eAQVQ,oBAAiB;IARPR,eASVS,aAAU;IATAT,eAUVU,cAAW;GAVDV,mBAAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "homePageEndowmentBuilder", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return homePageEndowmentBuilder;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _permissioncontroller = require("@metamask/permission-controller");
|
|
12
|
+
const _enum = require("./enum");
|
|
13
|
+
const permissionName = _enum.SnapEndowments.HomePage;
|
|
14
|
+
/**
|
|
15
|
+
* `endowment:page-home` returns nothing; it is intended to be used as a
|
|
16
|
+
* flag by the snap controller to detect whether the snap has the capability to
|
|
17
|
+
* use the snap home page feature.
|
|
18
|
+
*
|
|
19
|
+
* @param _builderOptions - Optional specification builder options.
|
|
20
|
+
* @returns The specification for the `snap-pages` endowment.
|
|
21
|
+
*/ const specificationBuilder = (_builderOptions)=>{
|
|
22
|
+
return {
|
|
23
|
+
permissionType: _permissioncontroller.PermissionType.Endowment,
|
|
24
|
+
targetName: permissionName,
|
|
25
|
+
allowedCaveats: null,
|
|
26
|
+
endowmentGetter: (_getterOptions)=>undefined,
|
|
27
|
+
subjectTypes: [
|
|
28
|
+
_permissioncontroller.SubjectType.Snap
|
|
29
|
+
]
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const homePageEndowmentBuilder = Object.freeze({
|
|
33
|
+
targetName: permissionName,
|
|
34
|
+
specificationBuilder
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=home-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/snaps/endowments/home-page.ts"],"sourcesContent":["import type {\n PermissionSpecificationBuilder,\n EndowmentGetterParams,\n ValidPermissionSpecification,\n} from '@metamask/permission-controller';\nimport { PermissionType, SubjectType } from '@metamask/permission-controller';\nimport type { NonEmptyArray } from '@metamask/utils';\n\nimport { SnapEndowments } from './enum';\n\nconst permissionName = SnapEndowments.HomePage;\n\ntype HomePageEndowmentSpecification = ValidPermissionSpecification<{\n permissionType: PermissionType.Endowment;\n targetName: typeof permissionName;\n endowmentGetter: (_options?: EndowmentGetterParams) => undefined;\n allowedCaveats: Readonly<NonEmptyArray<string>> | null;\n}>;\n\n/**\n * `endowment:page-home` returns nothing; it is intended to be used as a\n * flag by the snap controller to detect whether the snap has the capability to\n * use the snap home page feature.\n *\n * @param _builderOptions - Optional specification builder options.\n * @returns The specification for the `snap-pages` endowment.\n */\nconst specificationBuilder: PermissionSpecificationBuilder<\n PermissionType.Endowment,\n any,\n HomePageEndowmentSpecification\n> = (_builderOptions?: unknown) => {\n return {\n permissionType: PermissionType.Endowment,\n targetName: permissionName,\n allowedCaveats: null,\n endowmentGetter: (_getterOptions?: EndowmentGetterParams) => undefined,\n subjectTypes: [SubjectType.Snap],\n };\n};\n\nexport const homePageEndowmentBuilder = Object.freeze({\n targetName: permissionName,\n specificationBuilder,\n} as const);\n"],"names":["homePageEndowmentBuilder","permissionName","SnapEndowments","HomePage","specificationBuilder","_builderOptions","permissionType","PermissionType","Endowment","targetName","allowedCaveats","endowmentGetter","_getterOptions","undefined","subjectTypes","SubjectType","Snap","Object","freeze"],"mappings":";;;;+BAyCaA;;;eAAAA;;;sCApC+B;sBAGb;AAE/B,MAAMC,iBAAiBC,oBAAc,CAACC,QAAQ;AAS9C;;;;;;;CAOC,GACD,MAAMC,uBAIF,CAACC;IACH,OAAO;QACLC,gBAAgBC,oCAAc,CAACC,SAAS;QACxCC,YAAYR;QACZS,gBAAgB;QAChBC,iBAAiB,CAACC,iBAA2CC;QAC7DC,cAAc;YAACC,iCAAW,CAACC,IAAI;SAAC;IAClC;AACF;AAEO,MAAMhB,2BAA2BiB,OAAOC,MAAM,CAAC;IACpDT,YAAYR;IACZG;AACF"}
|
|
@@ -37,6 +37,7 @@ _export(exports, {
|
|
|
37
37
|
const _snapsutils = require("@metamask/snaps-utils");
|
|
38
38
|
const _cronjob = require("./cronjob");
|
|
39
39
|
const _ethereumprovider = require("./ethereum-provider");
|
|
40
|
+
const _homepage = require("./home-page");
|
|
40
41
|
const _keyring = require("./keyring");
|
|
41
42
|
const _lifecyclehooks = require("./lifecycle-hooks");
|
|
42
43
|
const _namelookup = require("./name-lookup");
|
|
@@ -67,7 +68,8 @@ const endowmentPermissionBuilders = {
|
|
|
67
68
|
[_webassembly.webAssemblyEndowmentBuilder.targetName]: _webassembly.webAssemblyEndowmentBuilder,
|
|
68
69
|
[_namelookup.nameLookupEndowmentBuilder.targetName]: _namelookup.nameLookupEndowmentBuilder,
|
|
69
70
|
[_lifecyclehooks.lifecycleHooksEndowmentBuilder.targetName]: _lifecyclehooks.lifecycleHooksEndowmentBuilder,
|
|
70
|
-
[_keyring.keyringEndowmentBuilder.targetName]: _keyring.keyringEndowmentBuilder
|
|
71
|
+
[_keyring.keyringEndowmentBuilder.targetName]: _keyring.keyringEndowmentBuilder,
|
|
72
|
+
[_homepage.homePageEndowmentBuilder.targetName]: _homepage.homePageEndowmentBuilder
|
|
71
73
|
};
|
|
72
74
|
const endowmentCaveatSpecifications = {
|
|
73
75
|
..._cronjob.cronjobCaveatSpecifications,
|
|
@@ -90,7 +92,8 @@ const handlerEndowments = {
|
|
|
90
92
|
[_snapsutils.HandlerType.OnNameLookup]: _namelookup.nameLookupEndowmentBuilder.targetName,
|
|
91
93
|
[_snapsutils.HandlerType.OnInstall]: _lifecyclehooks.lifecycleHooksEndowmentBuilder.targetName,
|
|
92
94
|
[_snapsutils.HandlerType.OnUpdate]: _lifecyclehooks.lifecycleHooksEndowmentBuilder.targetName,
|
|
93
|
-
[_snapsutils.HandlerType.OnKeyringRequest]: _keyring.keyringEndowmentBuilder.targetName
|
|
95
|
+
[_snapsutils.HandlerType.OnKeyringRequest]: _keyring.keyringEndowmentBuilder.targetName,
|
|
96
|
+
[_snapsutils.HandlerType.OnHomePage]: _homepage.homePageEndowmentBuilder.targetName
|
|
94
97
|
};
|
|
95
98
|
|
|
96
99
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/snaps/endowments/index.ts"],"sourcesContent":["import type { PermissionConstraint } from '@metamask/permission-controller';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type { Json } from '@metamask/utils';\n\nimport {\n cronjobCaveatSpecifications,\n cronjobEndowmentBuilder,\n getCronjobCaveatMapper,\n} from './cronjob';\nimport { ethereumProviderEndowmentBuilder } from './ethereum-provider';\nimport {\n getKeyringCaveatMapper,\n keyringCaveatSpecifications,\n keyringEndowmentBuilder,\n} from './keyring';\nimport { lifecycleHooksEndowmentBuilder } from './lifecycle-hooks';\nimport {\n getNameLookupCaveatMapper,\n nameLookupCaveatSpecifications,\n nameLookupEndowmentBuilder,\n} from './name-lookup';\nimport { networkAccessEndowmentBuilder } from './network-access';\nimport {\n getRpcCaveatMapper,\n rpcCaveatSpecifications,\n rpcEndowmentBuilder,\n} from './rpc';\nimport {\n getTransactionInsightCaveatMapper,\n transactionInsightCaveatSpecifications,\n transactionInsightEndowmentBuilder,\n} from './transaction-insight';\nimport { webAssemblyEndowmentBuilder } from './web-assembly';\n\nexport const endowmentPermissionBuilders = {\n [networkAccessEndowmentBuilder.targetName]: networkAccessEndowmentBuilder,\n [transactionInsightEndowmentBuilder.targetName]:\n transactionInsightEndowmentBuilder,\n [cronjobEndowmentBuilder.targetName]: cronjobEndowmentBuilder,\n [ethereumProviderEndowmentBuilder.targetName]:\n ethereumProviderEndowmentBuilder,\n [rpcEndowmentBuilder.targetName]: rpcEndowmentBuilder,\n [webAssemblyEndowmentBuilder.targetName]: webAssemblyEndowmentBuilder,\n [nameLookupEndowmentBuilder.targetName]: nameLookupEndowmentBuilder,\n [lifecycleHooksEndowmentBuilder.targetName]: lifecycleHooksEndowmentBuilder,\n [keyringEndowmentBuilder.targetName]: keyringEndowmentBuilder,\n} as const;\n\nexport const endowmentCaveatSpecifications = {\n ...cronjobCaveatSpecifications,\n ...transactionInsightCaveatSpecifications,\n ...rpcCaveatSpecifications,\n ...nameLookupCaveatSpecifications,\n ...keyringCaveatSpecifications,\n};\n\nexport const endowmentCaveatMappers: Record<\n string,\n (value: Json) => Pick<PermissionConstraint, 'caveats'>\n> = {\n [cronjobEndowmentBuilder.targetName]: getCronjobCaveatMapper,\n [transactionInsightEndowmentBuilder.targetName]:\n getTransactionInsightCaveatMapper,\n [rpcEndowmentBuilder.targetName]: getRpcCaveatMapper,\n [nameLookupEndowmentBuilder.targetName]: getNameLookupCaveatMapper,\n [keyringEndowmentBuilder.targetName]: getKeyringCaveatMapper,\n};\n\nexport const handlerEndowments: Record<HandlerType, string> = {\n [HandlerType.OnRpcRequest]: rpcEndowmentBuilder.targetName,\n [HandlerType.OnTransaction]: transactionInsightEndowmentBuilder.targetName,\n [HandlerType.OnCronjob]: cronjobEndowmentBuilder.targetName,\n [HandlerType.OnNameLookup]: nameLookupEndowmentBuilder.targetName,\n [HandlerType.OnInstall]: lifecycleHooksEndowmentBuilder.targetName,\n [HandlerType.OnUpdate]: lifecycleHooksEndowmentBuilder.targetName,\n [HandlerType.OnKeyringRequest]: keyringEndowmentBuilder.targetName,\n};\n\nexport * from './enum';\nexport { getRpcCaveatOrigins } from './rpc';\nexport { getTransactionOriginCaveat } from './transaction-insight';\nexport { getChainIdsCaveat } from './name-lookup';\nexport { getKeyringCaveatOrigins } from './keyring';\n"],"names":["endowmentPermissionBuilders","endowmentCaveatSpecifications","endowmentCaveatMappers","handlerEndowments","getRpcCaveatOrigins","getTransactionOriginCaveat","getChainIdsCaveat","getKeyringCaveatOrigins","networkAccessEndowmentBuilder","targetName","transactionInsightEndowmentBuilder","cronjobEndowmentBuilder","ethereumProviderEndowmentBuilder","rpcEndowmentBuilder","webAssemblyEndowmentBuilder","nameLookupEndowmentBuilder","lifecycleHooksEndowmentBuilder","keyringEndowmentBuilder","cronjobCaveatSpecifications","transactionInsightCaveatSpecifications","rpcCaveatSpecifications","nameLookupCaveatSpecifications","keyringCaveatSpecifications","getCronjobCaveatMapper","getTransactionInsightCaveatMapper","getRpcCaveatMapper","getNameLookupCaveatMapper","getKeyringCaveatMapper","HandlerType","OnRpcRequest","OnTransaction","OnCronjob","OnNameLookup","OnInstall","OnUpdate","OnKeyringRequest"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../../../src/snaps/endowments/index.ts"],"sourcesContent":["import type { PermissionConstraint } from '@metamask/permission-controller';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type { Json } from '@metamask/utils';\n\nimport {\n cronjobCaveatSpecifications,\n cronjobEndowmentBuilder,\n getCronjobCaveatMapper,\n} from './cronjob';\nimport { ethereumProviderEndowmentBuilder } from './ethereum-provider';\nimport { homePageEndowmentBuilder } from './home-page';\nimport {\n getKeyringCaveatMapper,\n keyringCaveatSpecifications,\n keyringEndowmentBuilder,\n} from './keyring';\nimport { lifecycleHooksEndowmentBuilder } from './lifecycle-hooks';\nimport {\n getNameLookupCaveatMapper,\n nameLookupCaveatSpecifications,\n nameLookupEndowmentBuilder,\n} from './name-lookup';\nimport { networkAccessEndowmentBuilder } from './network-access';\nimport {\n getRpcCaveatMapper,\n rpcCaveatSpecifications,\n rpcEndowmentBuilder,\n} from './rpc';\nimport {\n getTransactionInsightCaveatMapper,\n transactionInsightCaveatSpecifications,\n transactionInsightEndowmentBuilder,\n} from './transaction-insight';\nimport { webAssemblyEndowmentBuilder } from './web-assembly';\n\nexport const endowmentPermissionBuilders = {\n [networkAccessEndowmentBuilder.targetName]: networkAccessEndowmentBuilder,\n [transactionInsightEndowmentBuilder.targetName]:\n transactionInsightEndowmentBuilder,\n [cronjobEndowmentBuilder.targetName]: cronjobEndowmentBuilder,\n [ethereumProviderEndowmentBuilder.targetName]:\n ethereumProviderEndowmentBuilder,\n [rpcEndowmentBuilder.targetName]: rpcEndowmentBuilder,\n [webAssemblyEndowmentBuilder.targetName]: webAssemblyEndowmentBuilder,\n [nameLookupEndowmentBuilder.targetName]: nameLookupEndowmentBuilder,\n [lifecycleHooksEndowmentBuilder.targetName]: lifecycleHooksEndowmentBuilder,\n [keyringEndowmentBuilder.targetName]: keyringEndowmentBuilder,\n [homePageEndowmentBuilder.targetName]: homePageEndowmentBuilder,\n} as const;\n\nexport const endowmentCaveatSpecifications = {\n ...cronjobCaveatSpecifications,\n ...transactionInsightCaveatSpecifications,\n ...rpcCaveatSpecifications,\n ...nameLookupCaveatSpecifications,\n ...keyringCaveatSpecifications,\n};\n\nexport const endowmentCaveatMappers: Record<\n string,\n (value: Json) => Pick<PermissionConstraint, 'caveats'>\n> = {\n [cronjobEndowmentBuilder.targetName]: getCronjobCaveatMapper,\n [transactionInsightEndowmentBuilder.targetName]:\n getTransactionInsightCaveatMapper,\n [rpcEndowmentBuilder.targetName]: getRpcCaveatMapper,\n [nameLookupEndowmentBuilder.targetName]: getNameLookupCaveatMapper,\n [keyringEndowmentBuilder.targetName]: getKeyringCaveatMapper,\n};\n\nexport const handlerEndowments: Record<HandlerType, string> = {\n [HandlerType.OnRpcRequest]: rpcEndowmentBuilder.targetName,\n [HandlerType.OnTransaction]: transactionInsightEndowmentBuilder.targetName,\n [HandlerType.OnCronjob]: cronjobEndowmentBuilder.targetName,\n [HandlerType.OnNameLookup]: nameLookupEndowmentBuilder.targetName,\n [HandlerType.OnInstall]: lifecycleHooksEndowmentBuilder.targetName,\n [HandlerType.OnUpdate]: lifecycleHooksEndowmentBuilder.targetName,\n [HandlerType.OnKeyringRequest]: keyringEndowmentBuilder.targetName,\n [HandlerType.OnHomePage]: homePageEndowmentBuilder.targetName,\n};\n\nexport * from './enum';\nexport { getRpcCaveatOrigins } from './rpc';\nexport { getTransactionOriginCaveat } from './transaction-insight';\nexport { getChainIdsCaveat } from './name-lookup';\nexport { getKeyringCaveatOrigins } from './keyring';\n"],"names":["endowmentPermissionBuilders","endowmentCaveatSpecifications","endowmentCaveatMappers","handlerEndowments","getRpcCaveatOrigins","getTransactionOriginCaveat","getChainIdsCaveat","getKeyringCaveatOrigins","networkAccessEndowmentBuilder","targetName","transactionInsightEndowmentBuilder","cronjobEndowmentBuilder","ethereumProviderEndowmentBuilder","rpcEndowmentBuilder","webAssemblyEndowmentBuilder","nameLookupEndowmentBuilder","lifecycleHooksEndowmentBuilder","keyringEndowmentBuilder","homePageEndowmentBuilder","cronjobCaveatSpecifications","transactionInsightCaveatSpecifications","rpcCaveatSpecifications","nameLookupCaveatSpecifications","keyringCaveatSpecifications","getCronjobCaveatMapper","getTransactionInsightCaveatMapper","getRpcCaveatMapper","getNameLookupCaveatMapper","getKeyringCaveatMapper","HandlerType","OnRpcRequest","OnTransaction","OnCronjob","OnNameLookup","OnInstall","OnUpdate","OnKeyringRequest","OnHomePage"],"mappings":";;;;;;;;;;;IAmCaA,2BAA2B;eAA3BA;;IAeAC,6BAA6B;eAA7BA;;IAQAC,sBAAsB;eAAtBA;;IAYAC,iBAAiB;eAAjBA;;IAYJC,mBAAmB;eAAnBA,wBAAmB;;IACnBC,0BAA0B;eAA1BA,8CAA0B;;IAC1BC,iBAAiB;eAAjBA,6BAAiB;;IACjBC,uBAAuB;eAAvBA,gCAAuB;;;4BApFJ;yBAOrB;kCAC0C;0BACR;yBAKlC;gCACwC;4BAKxC;+BACuC;qBAKvC;oCAKA;6BACqC;qBAgD9B;;;;;;;;;;;;;;AA9CP,MAAMP,8BAA8B;IACzC,CAACQ,4CAA6B,CAACC,UAAU,CAAC,EAAED,4CAA6B;IACzE,CAACE,sDAAkC,CAACD,UAAU,CAAC,EAC7CC,sDAAkC;IACpC,CAACC,gCAAuB,CAACF,UAAU,CAAC,EAAEE,gCAAuB;IAC7D,CAACC,kDAAgC,CAACH,UAAU,CAAC,EAC3CG,kDAAgC;IAClC,CAACC,wBAAmB,CAACJ,UAAU,CAAC,EAAEI,wBAAmB;IACrD,CAACC,wCAA2B,CAACL,UAAU,CAAC,EAAEK,wCAA2B;IACrE,CAACC,sCAA0B,CAACN,UAAU,CAAC,EAAEM,sCAA0B;IACnE,CAACC,8CAA8B,CAACP,UAAU,CAAC,EAAEO,8CAA8B;IAC3E,CAACC,gCAAuB,CAACR,UAAU,CAAC,EAAEQ,gCAAuB;IAC7D,CAACC,kCAAwB,CAACT,UAAU,CAAC,EAAES,kCAAwB;AACjE;AAEO,MAAMjB,gCAAgC;IAC3C,GAAGkB,oCAA2B;IAC9B,GAAGC,0DAAsC;IACzC,GAAGC,4BAAuB;IAC1B,GAAGC,0CAA8B;IACjC,GAAGC,oCAA2B;AAChC;AAEO,MAAMrB,yBAGT;IACF,CAACS,gCAAuB,CAACF,UAAU,CAAC,EAAEe,+BAAsB;IAC5D,CAACd,sDAAkC,CAACD,UAAU,CAAC,EAC7CgB,qDAAiC;IACnC,CAACZ,wBAAmB,CAACJ,UAAU,CAAC,EAAEiB,uBAAkB;IACpD,CAACX,sCAA0B,CAACN,UAAU,CAAC,EAAEkB,qCAAyB;IAClE,CAACV,gCAAuB,CAACR,UAAU,CAAC,EAAEmB,+BAAsB;AAC9D;AAEO,MAAMzB,oBAAiD;IAC5D,CAAC0B,uBAAW,CAACC,YAAY,CAAC,EAAEjB,wBAAmB,CAACJ,UAAU;IAC1D,CAACoB,uBAAW,CAACE,aAAa,CAAC,EAAErB,sDAAkC,CAACD,UAAU;IAC1E,CAACoB,uBAAW,CAACG,SAAS,CAAC,EAAErB,gCAAuB,CAACF,UAAU;IAC3D,CAACoB,uBAAW,CAACI,YAAY,CAAC,EAAElB,sCAA0B,CAACN,UAAU;IACjE,CAACoB,uBAAW,CAACK,SAAS,CAAC,EAAElB,8CAA8B,CAACP,UAAU;IAClE,CAACoB,uBAAW,CAACM,QAAQ,CAAC,EAAEnB,8CAA8B,CAACP,UAAU;IACjE,CAACoB,uBAAW,CAACO,gBAAgB,CAAC,EAAEnB,gCAAuB,CAACR,UAAU;IAClE,CAACoB,uBAAW,CAACQ,UAAU,CAAC,EAAEnB,kCAAwB,CAACT,UAAU;AAC/D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/snaps/registry/json.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseControllerV2 as BaseController } from '@metamask/base-controller';\nimport type { SnapsRegistryDatabase } from '@metamask/snaps-registry';\nimport { verify } from '@metamask/snaps-registry';\nimport { getTargetVersion, type SnapId } from '@metamask/snaps-utils';\nimport type { Hex, SemVerRange, SemVerVersion } from '@metamask/utils';\nimport {\n assert,\n assertIsSemVerRange,\n Duration,\n inMilliseconds,\n satisfiesVersionRange,\n} from '@metamask/utils';\n\nimport type {\n SnapsRegistry,\n SnapsRegistryInfo,\n SnapsRegistryMetadata,\n SnapsRegistryRequest,\n SnapsRegistryResult,\n} from './registry';\nimport { SnapsRegistryStatus } from './registry';\n\n// TODO: Replace with a Codefi URL\nconst SNAP_REGISTRY_URL =\n 'https://cdn.jsdelivr.net/gh/MetaMask/snaps-registry@gh-pages/latest/registry.json';\n\nconst SNAP_REGISTRY_SIGNATURE_URL =\n 'https://cdn.jsdelivr.net/gh/MetaMask/snaps-registry@gh-pages/latest/signature.json';\n\ntype JsonSnapsRegistryUrl = {\n registry: string;\n signature: string;\n};\n\nexport type JsonSnapsRegistryArgs = {\n messenger: SnapsRegistryMessenger;\n state?: SnapsRegistryState;\n fetchFunction?: typeof fetch;\n url?: JsonSnapsRegistryUrl;\n recentFetchThreshold?: number;\n refetchOnAllowlistMiss?: boolean;\n failOnUnavailableRegistry?: boolean;\n publicKey?: Hex;\n};\n\nexport type GetResult = {\n type: `${typeof controllerName}:get`;\n handler: SnapsRegistry['get'];\n};\n\nexport type ResolveVersion = {\n type: `${typeof controllerName}:resolveVersion`;\n handler: SnapsRegistry['resolveVersion'];\n};\n\nexport type GetMetadata = {\n type: `${typeof controllerName}:getMetadata`;\n handler: SnapsRegistry['getMetadata'];\n};\n\nexport type Update = {\n type: `${typeof controllerName}:update`;\n handler: SnapsRegistry['update'];\n};\n\nexport type SnapsRegistryActions =\n | GetResult\n | GetMetadata\n | Update\n | ResolveVersion;\n\nexport type SnapsRegistryEvents = never;\n\nexport type SnapsRegistryMessenger = RestrictedControllerMessenger<\n 'SnapsRegistry',\n SnapsRegistryActions,\n SnapsRegistryEvents,\n SnapsRegistryActions['type'],\n SnapsRegistryEvents['type']\n>;\n\nexport type SnapsRegistryState = {\n database: SnapsRegistryDatabase | null;\n lastUpdated: number | null;\n};\n\nconst controllerName = 'SnapsRegistry';\n\nconst defaultState = {\n database: null,\n lastUpdated: null,\n};\n\nexport class JsonSnapsRegistry extends BaseController<\n typeof controllerName,\n SnapsRegistryState,\n SnapsRegistryMessenger\n> {\n #url: JsonSnapsRegistryUrl;\n\n #publicKey?: Hex;\n\n #fetchFunction: typeof fetch;\n\n #recentFetchThreshold: number;\n\n #refetchOnAllowlistMiss: boolean;\n\n #failOnUnavailableRegistry: boolean;\n\n #currentUpdate: Promise<void> | null;\n\n constructor({\n messenger,\n state,\n url = {\n registry: SNAP_REGISTRY_URL,\n signature: SNAP_REGISTRY_SIGNATURE_URL,\n },\n publicKey,\n fetchFunction = globalThis.fetch.bind(globalThis),\n recentFetchThreshold = inMilliseconds(5, Duration.Minute),\n failOnUnavailableRegistry = true,\n refetchOnAllowlistMiss = true,\n }: JsonSnapsRegistryArgs) {\n super({\n messenger,\n metadata: {\n database: { persist: true, anonymous: false },\n lastUpdated: { persist: true, anonymous: false },\n },\n name: controllerName,\n state: {\n ...defaultState,\n ...state,\n },\n });\n this.#url = url;\n this.#publicKey = publicKey;\n this.#fetchFunction = fetchFunction;\n this.#recentFetchThreshold = recentFetchThreshold;\n this.#refetchOnAllowlistMiss = refetchOnAllowlistMiss;\n this.#failOnUnavailableRegistry = failOnUnavailableRegistry;\n this.#currentUpdate = null;\n\n this.messagingSystem.registerActionHandler(\n 'SnapsRegistry:get',\n async (...args) => this.#get(...args),\n );\n\n this.messagingSystem.registerActionHandler(\n 'SnapsRegistry:getMetadata',\n async (...args) => this.#getMetadata(...args),\n );\n\n this.messagingSystem.registerActionHandler(\n 'SnapsRegistry:resolveVersion',\n async (...args) => this.#resolveVersion(...args),\n );\n\n this.messagingSystem.registerActionHandler(\n 'SnapsRegistry:update',\n async () => this.#triggerUpdate(),\n );\n }\n\n #wasRecentlyFetched() {\n return (\n this.state.lastUpdated &&\n Date.now() - this.state.lastUpdated < this.#recentFetchThreshold\n );\n }\n\n /**\n * Triggers an update of the registry database.\n *\n * If an existing update is in progress this function will await that update.\n */\n async #triggerUpdate() {\n // If an update is ongoing, wait for that.\n if (this.#currentUpdate) {\n await this.#currentUpdate;\n return;\n }\n // If no update exists, create promise and store globally.\n if (this.#currentUpdate === null) {\n this.#currentUpdate = this.#update();\n }\n await this.#currentUpdate;\n this.#currentUpdate = null;\n }\n\n /**\n * Updates the registry database if the registry hasn't been updated recently.\n *\n * NOTE: SHOULD NOT be called directly, instead `triggerUpdate` should be used.\n */\n async #update() {\n // No-op if we recently fetched the registry.\n if (this.#wasRecentlyFetched()) {\n return;\n }\n\n try {\n const database = await this.#safeFetch(this.#url.registry);\n\n if (this.#publicKey) {\n const signature = await this.#safeFetch(this.#url.signature);\n await this.#verifySignature(database, signature);\n }\n\n this.update((state) => {\n state.database = JSON.parse(database);\n state.lastUpdated = Date.now();\n });\n } catch {\n // Ignore\n }\n }\n\n async #getDatabase(): Promise<SnapsRegistryDatabase | null> {\n if (this.state.database === null) {\n await this.#triggerUpdate();\n }\n\n // If the database is still null and we require it, throw.\n if (this.#failOnUnavailableRegistry && this.state.database === null) {\n throw new Error('Snaps registry is unavailable, installation blocked.');\n }\n return this.state.database;\n }\n\n async #getSingle(\n snapId: SnapId,\n snapInfo: SnapsRegistryInfo,\n refetch = false,\n ): Promise<SnapsRegistryResult> {\n const database = await this.#getDatabase();\n\n const blockedEntry = database?.blockedSnaps.find((blocked) => {\n if ('id' in blocked) {\n return (\n blocked.id === snapId &&\n satisfiesVersionRange(snapInfo.version, blocked.versionRange)\n );\n }\n\n return blocked.checksum === snapInfo.checksum;\n });\n\n if (blockedEntry) {\n return {\n status: SnapsRegistryStatus.Blocked,\n reason: blockedEntry.reason,\n };\n }\n\n const verified = database?.verifiedSnaps[snapId];\n const version = verified?.versions?.[snapInfo.version];\n if (version && version.checksum === snapInfo.checksum) {\n return { status: SnapsRegistryStatus.Verified };\n }\n // For now, if we have an allowlist miss, we can refetch once and try again.\n if (this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#getSingle(snapId, snapInfo, true);\n }\n return { status: SnapsRegistryStatus.Unverified };\n }\n\n async #get(\n snaps: SnapsRegistryRequest,\n ): Promise<Record<SnapId, SnapsRegistryResult>> {\n return Object.entries(snaps).reduce<\n Promise<Record<SnapId, SnapsRegistryResult>>\n >(async (previousPromise, [snapId, snapInfo]) => {\n const result = await this.#getSingle(snapId, snapInfo);\n const acc = await previousPromise;\n acc[snapId] = result;\n return acc;\n }, Promise.resolve({}));\n }\n\n /**\n * Find an allowlisted version within a specified version range.\n *\n * @param snapId - The ID of the snap we are trying to resolve a version for.\n * @param versionRange - The version range.\n * @param refetch - An optional flag used to determine if we are refetching the registry.\n * @returns An allowlisted version within the specified version range.\n * @throws If an allowlisted version does not exist within the version range.\n */\n async #resolveVersion(\n snapId: SnapId,\n versionRange: SemVerRange,\n refetch = false,\n ): Promise<SemVerRange> {\n const database = await this.#getDatabase();\n const versions = database?.verifiedSnaps[snapId]?.versions ?? null;\n\n if (!versions && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n assert(versions, 'The snap is not on the allowlist');\n\n const targetVersion = getTargetVersion(\n Object.keys(versions) as SemVerVersion[],\n versionRange,\n );\n\n if (!targetVersion && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n assert(\n targetVersion,\n 'No matching versions of the snap are on the allowlist',\n );\n\n // A semver version is technically also a valid semver range.\n assertIsSemVerRange(targetVersion);\n return targetVersion;\n }\n\n /**\n * Get metadata for the given snap ID.\n *\n * @param snapId - The ID of the snap to get metadata for.\n * @returns The metadata for the given snap ID, or `null` if the snap is not\n * verified.\n */\n async #getMetadata(snapId: SnapId): Promise<SnapsRegistryMetadata | null> {\n const database = await this.#getDatabase();\n return database?.verifiedSnaps[snapId]?.metadata ?? null;\n }\n\n /**\n * Verify the signature of the registry.\n *\n * @param database - The registry database.\n * @param signature - The signature of the registry.\n * @throws If the signature is invalid.\n * @private\n */\n async #verifySignature(database: string, signature: string) {\n assert(this.#publicKey, 'No public key provided.');\n\n const valid = await verify({\n registry: database,\n signature: JSON.parse(signature),\n publicKey: this.#publicKey,\n });\n\n assert(valid, 'Invalid registry signature.');\n }\n\n /**\n * Fetch the given URL, throwing if the response is not OK.\n *\n * @param url - The URL to fetch.\n * @returns The response body.\n * @private\n */\n async #safeFetch(url: string) {\n const response = await this.#fetchFunction(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}.`);\n }\n\n return await response.text();\n }\n}\n"],"names":["JsonSnapsRegistry","SNAP_REGISTRY_URL","SNAP_REGISTRY_SIGNATURE_URL","controllerName","defaultState","database","lastUpdated","BaseController","constructor","messenger","state","url","registry","signature","publicKey","fetchFunction","globalThis","fetch","bind","recentFetchThreshold","inMilliseconds","Duration","Minute","failOnUnavailableRegistry","refetchOnAllowlistMiss","metadata","persist","anonymous","name","currentUpdate","messagingSystem","registerActionHandler","args","get","getMetadata","resolveVersion","triggerUpdate","Date","now","update","wasRecentlyFetched","safeFetch","verifySignature","JSON","parse","Error","snapId","snapInfo","refetch","getDatabase","blockedEntry","blockedSnaps","find","blocked","id","satisfiesVersionRange","version","versionRange","checksum","status","SnapsRegistryStatus","Blocked","reason","verified","verifiedSnaps","versions","Verified","getSingle","Unverified","snaps","Object","entries","reduce","previousPromise","result","acc","Promise","resolve","assert","targetVersion","getTargetVersion","keys","assertIsSemVerRange","valid","verify","response","ok","text"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/snaps/registry/json.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseControllerV2 as BaseController } from '@metamask/base-controller';\nimport type { SnapsRegistryDatabase } from '@metamask/snaps-registry';\nimport { verify } from '@metamask/snaps-registry';\nimport { getTargetVersion } from '@metamask/snaps-utils';\nimport type { Hex, SemVerRange, SemVerVersion } from '@metamask/utils';\nimport {\n assert,\n assertIsSemVerRange,\n Duration,\n inMilliseconds,\n satisfiesVersionRange,\n} from '@metamask/utils';\n\nimport type {\n SnapsRegistry,\n SnapsRegistryInfo,\n SnapsRegistryMetadata,\n SnapsRegistryRequest,\n SnapsRegistryResult,\n} from './registry';\nimport { SnapsRegistryStatus } from './registry';\n\n// TODO: Replace with a Codefi URL\nconst SNAP_REGISTRY_URL =\n 'https://cdn.jsdelivr.net/gh/MetaMask/snaps-registry@gh-pages/latest/registry.json';\n\nconst SNAP_REGISTRY_SIGNATURE_URL =\n 'https://cdn.jsdelivr.net/gh/MetaMask/snaps-registry@gh-pages/latest/signature.json';\n\ntype JsonSnapsRegistryUrl = {\n registry: string;\n signature: string;\n};\n\nexport type JsonSnapsRegistryArgs = {\n messenger: SnapsRegistryMessenger;\n state?: SnapsRegistryState;\n fetchFunction?: typeof fetch;\n url?: JsonSnapsRegistryUrl;\n recentFetchThreshold?: number;\n refetchOnAllowlistMiss?: boolean;\n failOnUnavailableRegistry?: boolean;\n publicKey?: Hex;\n};\n\nexport type GetResult = {\n type: `${typeof controllerName}:get`;\n handler: SnapsRegistry['get'];\n};\n\nexport type ResolveVersion = {\n type: `${typeof controllerName}:resolveVersion`;\n handler: SnapsRegistry['resolveVersion'];\n};\n\nexport type GetMetadata = {\n type: `${typeof controllerName}:getMetadata`;\n handler: SnapsRegistry['getMetadata'];\n};\n\nexport type Update = {\n type: `${typeof controllerName}:update`;\n handler: SnapsRegistry['update'];\n};\n\nexport type SnapsRegistryActions =\n | GetResult\n | GetMetadata\n | Update\n | ResolveVersion;\n\nexport type SnapsRegistryEvents = never;\n\nexport type SnapsRegistryMessenger = RestrictedControllerMessenger<\n 'SnapsRegistry',\n SnapsRegistryActions,\n SnapsRegistryEvents,\n SnapsRegistryActions['type'],\n SnapsRegistryEvents['type']\n>;\n\nexport type SnapsRegistryState = {\n database: SnapsRegistryDatabase | null;\n lastUpdated: number | null;\n};\n\nconst controllerName = 'SnapsRegistry';\n\nconst defaultState = {\n database: null,\n lastUpdated: null,\n};\n\nexport class JsonSnapsRegistry extends BaseController<\n typeof controllerName,\n SnapsRegistryState,\n SnapsRegistryMessenger\n> {\n #url: JsonSnapsRegistryUrl;\n\n #publicKey?: Hex;\n\n #fetchFunction: typeof fetch;\n\n #recentFetchThreshold: number;\n\n #refetchOnAllowlistMiss: boolean;\n\n #failOnUnavailableRegistry: boolean;\n\n #currentUpdate: Promise<void> | null;\n\n constructor({\n messenger,\n state,\n url = {\n registry: SNAP_REGISTRY_URL,\n signature: SNAP_REGISTRY_SIGNATURE_URL,\n },\n publicKey,\n fetchFunction = globalThis.fetch.bind(globalThis),\n recentFetchThreshold = inMilliseconds(5, Duration.Minute),\n failOnUnavailableRegistry = true,\n refetchOnAllowlistMiss = true,\n }: JsonSnapsRegistryArgs) {\n super({\n messenger,\n metadata: {\n database: { persist: true, anonymous: false },\n lastUpdated: { persist: true, anonymous: false },\n },\n name: controllerName,\n state: {\n ...defaultState,\n ...state,\n },\n });\n this.#url = url;\n this.#publicKey = publicKey;\n this.#fetchFunction = fetchFunction;\n this.#recentFetchThreshold = recentFetchThreshold;\n this.#refetchOnAllowlistMiss = refetchOnAllowlistMiss;\n this.#failOnUnavailableRegistry = failOnUnavailableRegistry;\n this.#currentUpdate = null;\n\n this.messagingSystem.registerActionHandler(\n 'SnapsRegistry:get',\n async (...args) => this.#get(...args),\n );\n\n this.messagingSystem.registerActionHandler(\n 'SnapsRegistry:getMetadata',\n async (...args) => this.#getMetadata(...args),\n );\n\n this.messagingSystem.registerActionHandler(\n 'SnapsRegistry:resolveVersion',\n async (...args) => this.#resolveVersion(...args),\n );\n\n this.messagingSystem.registerActionHandler(\n 'SnapsRegistry:update',\n async () => this.#triggerUpdate(),\n );\n }\n\n #wasRecentlyFetched() {\n return (\n this.state.lastUpdated &&\n Date.now() - this.state.lastUpdated < this.#recentFetchThreshold\n );\n }\n\n /**\n * Triggers an update of the registry database.\n *\n * If an existing update is in progress this function will await that update.\n */\n async #triggerUpdate() {\n // If an update is ongoing, wait for that.\n if (this.#currentUpdate) {\n await this.#currentUpdate;\n return;\n }\n // If no update exists, create promise and store globally.\n if (this.#currentUpdate === null) {\n this.#currentUpdate = this.#update();\n }\n await this.#currentUpdate;\n this.#currentUpdate = null;\n }\n\n /**\n * Updates the registry database if the registry hasn't been updated recently.\n *\n * NOTE: SHOULD NOT be called directly, instead `triggerUpdate` should be used.\n */\n async #update() {\n // No-op if we recently fetched the registry.\n if (this.#wasRecentlyFetched()) {\n return;\n }\n\n try {\n const database = await this.#safeFetch(this.#url.registry);\n\n if (this.#publicKey) {\n const signature = await this.#safeFetch(this.#url.signature);\n await this.#verifySignature(database, signature);\n }\n\n this.update((state) => {\n state.database = JSON.parse(database);\n state.lastUpdated = Date.now();\n });\n } catch {\n // Ignore\n }\n }\n\n async #getDatabase(): Promise<SnapsRegistryDatabase | null> {\n if (this.state.database === null) {\n await this.#triggerUpdate();\n }\n\n // If the database is still null and we require it, throw.\n if (this.#failOnUnavailableRegistry && this.state.database === null) {\n throw new Error('Snaps registry is unavailable, installation blocked.');\n }\n return this.state.database;\n }\n\n async #getSingle(\n snapId: string,\n snapInfo: SnapsRegistryInfo,\n refetch = false,\n ): Promise<SnapsRegistryResult> {\n const database = await this.#getDatabase();\n\n const blockedEntry = database?.blockedSnaps.find((blocked) => {\n if ('id' in blocked) {\n return (\n blocked.id === snapId &&\n satisfiesVersionRange(snapInfo.version, blocked.versionRange)\n );\n }\n\n return blocked.checksum === snapInfo.checksum;\n });\n\n if (blockedEntry) {\n return {\n status: SnapsRegistryStatus.Blocked,\n reason: blockedEntry.reason,\n };\n }\n\n const verified = database?.verifiedSnaps[snapId];\n const version = verified?.versions?.[snapInfo.version];\n if (version && version.checksum === snapInfo.checksum) {\n return { status: SnapsRegistryStatus.Verified };\n }\n // For now, if we have an allowlist miss, we can refetch once and try again.\n if (this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#getSingle(snapId, snapInfo, true);\n }\n return { status: SnapsRegistryStatus.Unverified };\n }\n\n async #get(\n snaps: SnapsRegistryRequest,\n ): Promise<Record<string, SnapsRegistryResult>> {\n return Object.entries(snaps).reduce<\n Promise<Record<string, SnapsRegistryResult>>\n >(async (previousPromise, [snapId, snapInfo]) => {\n const result = await this.#getSingle(snapId, snapInfo);\n const acc = await previousPromise;\n acc[snapId] = result;\n return acc;\n }, Promise.resolve({}));\n }\n\n /**\n * Find an allowlisted version within a specified version range.\n *\n * @param snapId - The ID of the snap we are trying to resolve a version for.\n * @param versionRange - The version range.\n * @param refetch - An optional flag used to determine if we are refetching the registry.\n * @returns An allowlisted version within the specified version range.\n * @throws If an allowlisted version does not exist within the version range.\n */\n async #resolveVersion(\n snapId: string,\n versionRange: SemVerRange,\n refetch = false,\n ): Promise<SemVerRange> {\n const database = await this.#getDatabase();\n const versions = database?.verifiedSnaps[snapId]?.versions ?? null;\n\n if (!versions && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n assert(versions, 'The snap is not on the allowlist');\n\n const targetVersion = getTargetVersion(\n Object.keys(versions) as SemVerVersion[],\n versionRange,\n );\n\n if (!targetVersion && this.#refetchOnAllowlistMiss && !refetch) {\n await this.#triggerUpdate();\n return this.#resolveVersion(snapId, versionRange, true);\n }\n\n assert(\n targetVersion,\n 'No matching versions of the snap are on the allowlist',\n );\n\n // A semver version is technically also a valid semver range.\n assertIsSemVerRange(targetVersion);\n return targetVersion;\n }\n\n /**\n * Get metadata for the given snap ID.\n *\n * @param snapId - The ID of the snap to get metadata for.\n * @returns The metadata for the given snap ID, or `null` if the snap is not\n * verified.\n */\n async #getMetadata(snapId: string): Promise<SnapsRegistryMetadata | null> {\n const database = await this.#getDatabase();\n return database?.verifiedSnaps[snapId]?.metadata ?? null;\n }\n\n /**\n * Verify the signature of the registry.\n *\n * @param database - The registry database.\n * @param signature - The signature of the registry.\n * @throws If the signature is invalid.\n * @private\n */\n async #verifySignature(database: string, signature: string) {\n assert(this.#publicKey, 'No public key provided.');\n\n const valid = await verify({\n registry: database,\n signature: JSON.parse(signature),\n publicKey: this.#publicKey,\n });\n\n assert(valid, 'Invalid registry signature.');\n }\n\n /**\n * Fetch the given URL, throwing if the response is not OK.\n *\n * @param url - The URL to fetch.\n * @returns The response body.\n * @private\n */\n async #safeFetch(url: string) {\n const response = await this.#fetchFunction(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}.`);\n }\n\n return await response.text();\n }\n}\n"],"names":["JsonSnapsRegistry","SNAP_REGISTRY_URL","SNAP_REGISTRY_SIGNATURE_URL","controllerName","defaultState","database","lastUpdated","BaseController","constructor","messenger","state","url","registry","signature","publicKey","fetchFunction","globalThis","fetch","bind","recentFetchThreshold","inMilliseconds","Duration","Minute","failOnUnavailableRegistry","refetchOnAllowlistMiss","metadata","persist","anonymous","name","currentUpdate","messagingSystem","registerActionHandler","args","get","getMetadata","resolveVersion","triggerUpdate","Date","now","update","wasRecentlyFetched","safeFetch","verifySignature","JSON","parse","Error","snapId","snapInfo","refetch","getDatabase","blockedEntry","blockedSnaps","find","blocked","id","satisfiesVersionRange","version","versionRange","checksum","status","SnapsRegistryStatus","Blocked","reason","verified","verifiedSnaps","versions","Verified","getSingle","Unverified","snaps","Object","entries","reduce","previousPromise","result","acc","Promise","resolve","assert","targetVersion","getTargetVersion","keys","assertIsSemVerRange","valid","verify","response","ok","text"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/snaps/registry/registry.ts"],"sourcesContent":["import type {\n BlockReason,\n SnapsRegistryDatabase,\n} from '@metamask/snaps-registry';\nimport type { SnapId
|
|
1
|
+
{"version":3,"sources":["../../../../src/snaps/registry/registry.ts"],"sourcesContent":["import type {\n BlockReason,\n SnapsRegistryDatabase,\n} from '@metamask/snaps-registry';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport type { SemVerRange, SemVerVersion } from '@metamask/utils';\n\nexport type SnapsRegistryInfo = { version: SemVerVersion; checksum: string };\nexport type SnapsRegistryRequest = Record<SnapId, SnapsRegistryInfo>;\nexport type SnapsRegistryMetadata =\n SnapsRegistryDatabase['verifiedSnaps'][SnapId]['metadata'];\n\n// TODO: Decide on names for these\nexport enum SnapsRegistryStatus {\n Unverified = 0,\n Blocked = 1,\n Verified = 2,\n}\n\nexport type SnapsRegistryResult = {\n status: SnapsRegistryStatus;\n reason?: BlockReason;\n};\n\nexport type SnapsRegistry = {\n get(\n snaps: SnapsRegistryRequest,\n ): Promise<Record<SnapId, SnapsRegistryResult>>;\n\n update(): Promise<void>;\n\n /**\n * Find an allowlisted version within a specified version range.\n *\n * @param snapId - The ID of the snap we are trying to resolve a version for.\n * @param versionRange - The version range.\n * @param refetch - An optional flag used to determine if we are refetching the registry.\n * @returns An allowlisted version within the specified version range.\n * @throws If an allowlisted version does not exist within the version range.\n */\n resolveVersion(\n snapId: SnapId,\n versionRange: SemVerRange,\n ): Promise<SemVerRange>;\n\n /**\n * Get metadata for the given snap ID.\n *\n * @param snapId - The ID of the snap to get metadata for.\n * @returns The metadata for the given snap ID, or `null` if the snap is not\n * verified.\n */\n getMetadata(snapId: SnapId): Promise<SnapsRegistryMetadata | null>;\n};\n"],"names":["SnapsRegistryStatus","Unverified","Blocked","Verified"],"mappings":";;;;;;;;;;IAaO;UAAKA,mBAAmB;IAAnBA,oBAAAA,oBACVC,gBAAa,KAAbA;IADUD,oBAAAA,oBAEVE,aAAU,KAAVA;IAFUF,oBAAAA,oBAGVG,cAAW,KAAXA;GAHUH,wBAAAA"}
|
package/dist/cjs/utils.js
CHANGED
|
@@ -23,6 +23,9 @@ _export(exports, {
|
|
|
23
23
|
},
|
|
24
24
|
withTimeout: function() {
|
|
25
25
|
return withTimeout;
|
|
26
|
+
},
|
|
27
|
+
getSnapFiles: function() {
|
|
28
|
+
return getSnapFiles;
|
|
26
29
|
}
|
|
27
30
|
});
|
|
28
31
|
const _Timer = require("./snaps/Timer");
|
|
@@ -66,5 +69,11 @@ async function withTimeout(promise, timerOrMs) {
|
|
|
66
69
|
delayPromise.cancel();
|
|
67
70
|
}
|
|
68
71
|
}
|
|
72
|
+
async function getSnapFiles(location, files) {
|
|
73
|
+
if (!files || files.length === 0) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
return await Promise.all(files.map(async (filePath)=>location.fetch(filePath)));
|
|
77
|
+
}
|
|
69
78
|
|
|
70
79
|
//# sourceMappingURL=utils.js.map
|
package/dist/cjs/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["import { Timer } from './snaps/Timer';\n\n/**\n * Takes two objects and does a Set Difference of them.\n * Set Difference is generally defined as follows:\n * ```\n * 𝑥 ∈ A ∖ B ⟺ 𝑥 ∈ A ∧ 𝑥 ∉ B\n * ```\n * Meaning that the returned object contains all properties of A expect those that also\n * appear in B. Notice that properties that appear in B, but not in A, have no effect.\n *\n * @see [Set Difference]{@link https://proofwiki.org/wiki/Definition:Set_Difference}\n * @param objectA - The object on which the difference is being calculated.\n * @param objectB - The object whose properties will be removed from objectA.\n * @returns The objectA without properties from objectB.\n */\nexport function setDiff<\n ObjectA extends Record<any, unknown>,\n ObjectB extends Record<any, unknown>,\n>(objectA: ObjectA, objectB: ObjectB): Diff<ObjectA, ObjectB> {\n return Object.entries(objectA).reduce<Record<any, unknown>>(\n (acc, [key, value]) => {\n if (!(key in objectB)) {\n acc[key] = value;\n }\n return acc;\n },\n {},\n ) as Diff<ObjectA, ObjectB>;\n}\n\n/**\n * A Promise that delays its return for a given amount of milliseconds.\n *\n * @param ms - Milliseconds to delay the execution for.\n * @param result - The result to return from the Promise after delay.\n * @returns A promise that is void if no result provided, result otherwise.\n * @template Result - The `result`.\n */\nexport function delay<Result = void>(\n ms: number,\n result?: Result,\n): Promise<Result> & { cancel: () => void } {\n return delayWithTimer(new Timer(ms), result);\n}\n\n/**\n * A Promise that delays it's return by using a pausable Timer.\n *\n * @param timer - Timer used to control the delay.\n * @param result - The result to return from the Promise after delay.\n * @returns A promise that is void if no result provided, result otherwise.\n * @template Result - The `result`.\n */\nexport function delayWithTimer<Result = void>(\n timer: Timer,\n result?: Result,\n): Promise<Result> & { cancel: () => void } {\n let rejectFunc: (reason: Error) => void;\n const promise: any = new Promise<Result>((resolve: any, reject) => {\n timer.start(() => {\n result === undefined ? resolve() : resolve(result);\n });\n rejectFunc = reject;\n });\n\n promise.cancel = () => {\n if (timer.status !== 'finished') {\n timer.cancel();\n rejectFunc(new Error('The delay has been canceled.'));\n }\n };\n return promise;\n}\n\n/*\n * We use a Symbol instead of rejecting the promise so that Errors thrown\n * by the main promise will propagate.\n */\nexport const hasTimedOut = Symbol(\n 'Used to check if the requested promise has timeout (see withTimeout)',\n);\n\n/**\n * Executes the given Promise, if the Timer expires before the Promise settles, we return earlier.\n *\n * NOTE:** The given Promise is not cancelled or interrupted, and will continue to execute uninterrupted. We will just discard its result if it does not complete before the timeout.\n *\n * @param promise - The promise that you want to execute.\n * @param timerOrMs - The timer controlling the timeout or a ms value.\n * @returns The resolved `PromiseValue`, or the hasTimedOut symbol if\n * returning early.\n * @template PromiseValue - The value of the Promise.\n */\nexport async function withTimeout<PromiseValue = void>(\n promise: Promise<PromiseValue>,\n timerOrMs: Timer | number,\n): Promise<PromiseValue | typeof hasTimedOut> {\n const timer =\n typeof timerOrMs === 'number' ? new Timer(timerOrMs) : timerOrMs;\n const delayPromise = delayWithTimer(timer, hasTimedOut);\n try {\n return await Promise.race([promise, delayPromise]);\n } finally {\n delayPromise.cancel();\n }\n}\n\n/**\n * Checks whether the type is composed of literal types\n *\n * @returns @type {true} if whole type is composed of literals, @type {false} if whole type is not literals, @type {boolean} if mixed\n * @example\n * ```\n * type t1 = IsLiteral<1 | 2 | \"asd\" | true>;\n * // t1 = true\n *\n * type t2 = IsLiteral<number | string>;\n * // t2 = false\n *\n * type t3 = IsLiteral<1 | string>;\n * // t3 = boolean\n *\n * const s = Symbol();\n * type t4 = IsLiteral<typeof s>;\n * // t4 = true\n *\n * type t5 = IsLiteral<symbol>\n * // t5 = false;\n * ```\n */\ntype IsLiteral<Type> = Type extends string | number | boolean | symbol\n ? Extract<string | number | boolean | symbol, Type> extends never\n ? true\n : false\n : false;\n\n/**\n * Returns all keys of an object, that are literal, as an union\n *\n * @example\n * ```\n * type t1 = _LiteralKeys<{a: number, b: 0, c: 'foo', d: string}>\n * // t1 = 'b' | 'c'\n * ```\n * @see [Literal types]{@link https://www.typescriptlang.org/docs/handbook/literal-types.html}\n */\ntype LiteralKeys<Type> = NonNullable<\n {\n [Key in keyof Type]: IsLiteral<Key> extends true ? Key : never;\n }[keyof Type]\n>;\n\n/**\n * Returns all keys of an object, that are not literal, as an union\n *\n * @example\n * ```\n * type t1 = _NonLiteralKeys<{a: number, b: 0, c: 'foo', d: string}>\n * // t1 = 'a' | 'd'\n * ```\n * @see [Literal types]{@link https://www.typescriptlang.org/docs/handbook/literal-types.html}\n */\ntype NonLiteralKeys<Type> = NonNullable<\n {\n [Key in keyof Type]: IsLiteral<Key> extends false ? Key : never;\n }[keyof Type]\n>;\n\n/**\n * A set difference of two objects based on their keys\n *\n * @example\n * ```\n * type t1 = Diff<{a: string, b: string}, {a: number}>\n * // t1 = {b: string};\n * type t2 = Diff<{a: string, 0: string}, Record<string, unknown>>;\n * // t2 = { a?: string, 0: string};\n * type t3 = Diff<{a: string, 0: string, 1: string}, Record<1 | string, unknown>>;\n * // t3 = {a?: string, 0: string}\n * ```\n * @see {@link setDiff} for the main use-case\n */\nexport type Diff<First, Second> = Omit<First, LiteralKeys<Second>> &\n Partial<Pick<First, Extract<keyof First, NonLiteralKeys<Second>>>>;\n\n/**\n * Makes every specified property of the specified object type mutable.\n *\n * @template Type - The object whose readonly properties to make mutable.\n * @template TargetKey - The property key(s) to make mutable.\n */\nexport type Mutable<\n Type extends Record<string, unknown>,\n TargetKey extends string,\n> = {\n -readonly [Key in keyof Pick<Type, TargetKey>]: Type[Key];\n} & {\n [Key in keyof Omit<Type, TargetKey>]: Type[Key];\n};\n"],"names":["setDiff","delay","delayWithTimer","hasTimedOut","withTimeout","objectA","objectB","Object","entries","reduce","acc","key","value","ms","result","Timer","timer","rejectFunc","promise","Promise","resolve","reject","start","undefined","cancel","status","Error","Symbol","timerOrMs","delayPromise","race"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../src/utils.ts"],"sourcesContent":["import type { SnapLocation } from './snaps';\nimport { Timer } from './snaps/Timer';\n\n/**\n * Takes two objects and does a Set Difference of them.\n * Set Difference is generally defined as follows:\n * ```\n * 𝑥 ∈ A ∖ B ⟺ 𝑥 ∈ A ∧ 𝑥 ∉ B\n * ```\n * Meaning that the returned object contains all properties of A expect those that also\n * appear in B. Notice that properties that appear in B, but not in A, have no effect.\n *\n * @see [Set Difference]{@link https://proofwiki.org/wiki/Definition:Set_Difference}\n * @param objectA - The object on which the difference is being calculated.\n * @param objectB - The object whose properties will be removed from objectA.\n * @returns The objectA without properties from objectB.\n */\nexport function setDiff<\n ObjectA extends Record<any, unknown>,\n ObjectB extends Record<any, unknown>,\n>(objectA: ObjectA, objectB: ObjectB): Diff<ObjectA, ObjectB> {\n return Object.entries(objectA).reduce<Record<any, unknown>>(\n (acc, [key, value]) => {\n if (!(key in objectB)) {\n acc[key] = value;\n }\n return acc;\n },\n {},\n ) as Diff<ObjectA, ObjectB>;\n}\n\n/**\n * A Promise that delays its return for a given amount of milliseconds.\n *\n * @param ms - Milliseconds to delay the execution for.\n * @param result - The result to return from the Promise after delay.\n * @returns A promise that is void if no result provided, result otherwise.\n * @template Result - The `result`.\n */\nexport function delay<Result = void>(\n ms: number,\n result?: Result,\n): Promise<Result> & { cancel: () => void } {\n return delayWithTimer(new Timer(ms), result);\n}\n\n/**\n * A Promise that delays it's return by using a pausable Timer.\n *\n * @param timer - Timer used to control the delay.\n * @param result - The result to return from the Promise after delay.\n * @returns A promise that is void if no result provided, result otherwise.\n * @template Result - The `result`.\n */\nexport function delayWithTimer<Result = void>(\n timer: Timer,\n result?: Result,\n): Promise<Result> & { cancel: () => void } {\n let rejectFunc: (reason: Error) => void;\n const promise: any = new Promise<Result>((resolve: any, reject) => {\n timer.start(() => {\n result === undefined ? resolve() : resolve(result);\n });\n rejectFunc = reject;\n });\n\n promise.cancel = () => {\n if (timer.status !== 'finished') {\n timer.cancel();\n rejectFunc(new Error('The delay has been canceled.'));\n }\n };\n return promise;\n}\n\n/*\n * We use a Symbol instead of rejecting the promise so that Errors thrown\n * by the main promise will propagate.\n */\nexport const hasTimedOut = Symbol(\n 'Used to check if the requested promise has timeout (see withTimeout)',\n);\n\n/**\n * Executes the given Promise, if the Timer expires before the Promise settles, we return earlier.\n *\n * NOTE:** The given Promise is not cancelled or interrupted, and will continue to execute uninterrupted. We will just discard its result if it does not complete before the timeout.\n *\n * @param promise - The promise that you want to execute.\n * @param timerOrMs - The timer controlling the timeout or a ms value.\n * @returns The resolved `PromiseValue`, or the hasTimedOut symbol if\n * returning early.\n * @template PromiseValue - The value of the Promise.\n */\nexport async function withTimeout<PromiseValue = void>(\n promise: Promise<PromiseValue>,\n timerOrMs: Timer | number,\n): Promise<PromiseValue | typeof hasTimedOut> {\n const timer =\n typeof timerOrMs === 'number' ? new Timer(timerOrMs) : timerOrMs;\n const delayPromise = delayWithTimer(timer, hasTimedOut);\n try {\n return await Promise.race([promise, delayPromise]);\n } finally {\n delayPromise.cancel();\n }\n}\n\n/**\n * Checks whether the type is composed of literal types\n *\n * @returns @type {true} if whole type is composed of literals, @type {false} if whole type is not literals, @type {boolean} if mixed\n * @example\n * ```\n * type t1 = IsLiteral<1 | 2 | \"asd\" | true>;\n * // t1 = true\n *\n * type t2 = IsLiteral<number | string>;\n * // t2 = false\n *\n * type t3 = IsLiteral<1 | string>;\n * // t3 = boolean\n *\n * const s = Symbol();\n * type t4 = IsLiteral<typeof s>;\n * // t4 = true\n *\n * type t5 = IsLiteral<symbol>\n * // t5 = false;\n * ```\n */\ntype IsLiteral<Type> = Type extends string | number | boolean | symbol\n ? Extract<string | number | boolean | symbol, Type> extends never\n ? true\n : false\n : false;\n\n/**\n * Returns all keys of an object, that are literal, as an union\n *\n * @example\n * ```\n * type t1 = _LiteralKeys<{a: number, b: 0, c: 'foo', d: string}>\n * // t1 = 'b' | 'c'\n * ```\n * @see [Literal types]{@link https://www.typescriptlang.org/docs/handbook/literal-types.html}\n */\ntype LiteralKeys<Type> = NonNullable<\n {\n [Key in keyof Type]: IsLiteral<Key> extends true ? Key : never;\n }[keyof Type]\n>;\n\n/**\n * Returns all keys of an object, that are not literal, as an union\n *\n * @example\n * ```\n * type t1 = _NonLiteralKeys<{a: number, b: 0, c: 'foo', d: string}>\n * // t1 = 'a' | 'd'\n * ```\n * @see [Literal types]{@link https://www.typescriptlang.org/docs/handbook/literal-types.html}\n */\ntype NonLiteralKeys<Type> = NonNullable<\n {\n [Key in keyof Type]: IsLiteral<Key> extends false ? Key : never;\n }[keyof Type]\n>;\n\n/**\n * A set difference of two objects based on their keys\n *\n * @example\n * ```\n * type t1 = Diff<{a: string, b: string}, {a: number}>\n * // t1 = {b: string};\n * type t2 = Diff<{a: string, 0: string}, Record<string, unknown>>;\n * // t2 = { a?: string, 0: string};\n * type t3 = Diff<{a: string, 0: string, 1: string}, Record<1 | string, unknown>>;\n * // t3 = {a?: string, 0: string}\n * ```\n * @see {@link setDiff} for the main use-case\n */\nexport type Diff<First, Second> = Omit<First, LiteralKeys<Second>> &\n Partial<Pick<First, Extract<keyof First, NonLiteralKeys<Second>>>>;\n\n/**\n * Makes every specified property of the specified object type mutable.\n *\n * @template Type - The object whose readonly properties to make mutable.\n * @template TargetKey - The property key(s) to make mutable.\n */\nexport type Mutable<\n Type extends Record<string, unknown>,\n TargetKey extends string,\n> = {\n -readonly [Key in keyof Pick<Type, TargetKey>]: Type[Key];\n} & {\n [Key in keyof Omit<Type, TargetKey>]: Type[Key];\n};\n\n/**\n * Get all files in a Snap from an array of file paths.\n *\n * @param location - The location of the Snap.\n * @param files - The array of file paths.\n * @returns The array of files as {@link VirtualFile}s.\n */\nexport async function getSnapFiles(\n location: SnapLocation,\n files?: string[] | undefined,\n) {\n if (!files || files.length === 0) {\n return [];\n }\n\n return await Promise.all(\n files.map(async (filePath) => location.fetch(filePath)),\n );\n}\n"],"names":["setDiff","delay","delayWithTimer","hasTimedOut","withTimeout","getSnapFiles","objectA","objectB","Object","entries","reduce","acc","key","value","ms","result","Timer","timer","rejectFunc","promise","Promise","resolve","reject","start","undefined","cancel","status","Error","Symbol","timerOrMs","delayPromise","race","location","files","length","all","map","filePath","fetch"],"mappings":";;;;;;;;;;;IAiBgBA,OAAO;eAAPA;;IAuBAC,KAAK;eAALA;;IAeAC,cAAc;eAAdA;;IAyBHC,WAAW;eAAXA;;IAeSC,WAAW;eAAXA;;IAkHAC,YAAY;eAAZA;;;uBAhNA;AAgBf,SAASL,QAGdM,OAAgB,EAAEC,OAAgB;IAClC,OAAOC,OAAOC,OAAO,CAACH,SAASI,MAAM,CACnC,CAACC,KAAK,CAACC,KAAKC,MAAM;QAChB,IAAI,CAAED,CAAAA,OAAOL,OAAM,GAAI;YACrBI,GAAG,CAACC,IAAI,GAAGC;QACb;QACA,OAAOF;IACT,GACA,CAAC;AAEL;AAUO,SAASV,MACda,EAAU,EACVC,MAAe;IAEf,OAAOb,eAAe,IAAIc,YAAK,CAACF,KAAKC;AACvC;AAUO,SAASb,eACde,KAAY,EACZF,MAAe;IAEf,IAAIG;IACJ,MAAMC,UAAe,IAAIC,QAAgB,CAACC,SAAcC;QACtDL,MAAMM,KAAK,CAAC;YACVR,WAAWS,YAAYH,YAAYA,QAAQN;QAC7C;QACAG,aAAaI;IACf;IAEAH,QAAQM,MAAM,GAAG;QACf,IAAIR,MAAMS,MAAM,KAAK,YAAY;YAC/BT,MAAMQ,MAAM;YACZP,WAAW,IAAIS,MAAM;QACvB;IACF;IACA,OAAOR;AACT;AAMO,MAAMhB,cAAcyB,OACzB;AAcK,eAAexB,YACpBe,OAA8B,EAC9BU,SAAyB;IAEzB,MAAMZ,QACJ,OAAOY,cAAc,WAAW,IAAIb,YAAK,CAACa,aAAaA;IACzD,MAAMC,eAAe5B,eAAee,OAAOd;IAC3C,IAAI;QACF,OAAO,MAAMiB,QAAQW,IAAI,CAAC;YAACZ;YAASW;SAAa;IACnD,SAAU;QACRA,aAAaL,MAAM;IACrB;AACF;AAsGO,eAAepB,aACpB2B,QAAsB,EACtBC,KAA4B;IAE5B,IAAI,CAACA,SAASA,MAAMC,MAAM,KAAK,GAAG;QAChC,OAAO,EAAE;IACX;IAEA,OAAO,MAAMd,QAAQe,GAAG,CACtBF,MAAMG,GAAG,CAAC,OAAOC,WAAaL,SAASM,KAAK,CAACD;AAEjD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cronjob/CronjobController.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseControllerV2 as BaseController } from '@metamask/base-controller';\nimport type { GetPermissions } from '@metamask/permission-controller';\nimport type {\n SnapId,\n ValidatedSnapId,\n TruncatedSnap,\n CronjobSpecification,\n} from '@metamask/snaps-utils';\nimport {\n HandlerType,\n parseCronExpression,\n logError,\n} from '@metamask/snaps-utils';\nimport { Duration, inMilliseconds } from '@metamask/utils';\n\nimport type {\n GetAllSnaps,\n HandleSnapRequest,\n SnapDisabled,\n SnapEnabled,\n SnapInstalled,\n SnapRemoved,\n SnapUpdated,\n} from '..';\nimport { getRunnableSnaps, SnapEndowments } from '..';\nimport { getCronjobCaveatJobs } from '../snaps/endowments/cronjob';\nimport { Timer } from '../snaps/Timer';\n\nexport type CronjobControllerActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions;\n\nexport type CronjobControllerEvents =\n | SnapInstalled\n | SnapRemoved\n | SnapUpdated\n | SnapEnabled\n | SnapDisabled;\n\nexport type CronjobControllerMessenger = RestrictedControllerMessenger<\n 'CronjobController',\n CronjobControllerActions,\n CronjobControllerEvents,\n CronjobControllerActions['type'],\n CronjobControllerEvents['type']\n>;\n\nexport const DAILY_TIMEOUT = inMilliseconds(24, Duration.Hour);\n\nexport type CronjobControllerArgs = {\n messenger: CronjobControllerMessenger;\n /**\n * Persisted state that will be used for rehydration.\n */\n state?: CronjobControllerState;\n};\n\nexport type Cronjob = {\n timer?: Timer;\n id: string;\n snapId: ValidatedSnapId;\n} & CronjobSpecification;\n\nexport type StoredJobInformation = {\n lastRun: number;\n};\n\nexport type CronjobControllerState = {\n jobs: Record<string, StoredJobInformation>;\n};\n\nconst controllerName = 'CronjobController';\n\n/**\n * Use this controller to register and schedule periodically executed jobs\n * using RPC method hooks.\n */\nexport class CronjobController extends BaseController<\n typeof controllerName,\n CronjobControllerState,\n CronjobControllerMessenger\n> {\n #messenger: CronjobControllerMessenger;\n\n #dailyTimer!: Timer;\n\n #timers: Map<string, Timer>;\n\n // Mapping from jobId to snapId\n #snapIds: Map<string, string>;\n\n constructor({ messenger, state }: CronjobControllerArgs) {\n super({\n messenger,\n metadata: {\n jobs: { persist: true, anonymous: false },\n },\n name: controllerName,\n state: {\n jobs: {},\n ...state,\n },\n });\n this.#timers = new Map();\n this.#snapIds = new Map();\n this.#messenger = messenger;\n\n this._handleSnapRegisterEvent = this._handleSnapRegisterEvent.bind(this);\n this._handleSnapUnregisterEvent =\n this._handleSnapUnregisterEvent.bind(this);\n this._handleEventSnapUpdated = this._handleEventSnapUpdated.bind(this);\n\n // Subscribe to Snap events\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.subscribe(\n 'SnapController:snapInstalled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapRemoved',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapEnabled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapDisabled',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.dailyCheckIn().catch((error) => {\n logError(error);\n });\n }\n\n /**\n * Retrieve all cronjob specifications for all runnable snaps.\n *\n * @returns Array of Cronjob specifications.\n */\n private getAllJobs(): Cronjob[] {\n const snaps = this.messagingSystem.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(snaps);\n\n const jobs = filteredSnaps.map((snap) => this.getSnapJobs(snap.id));\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n return jobs.flat().filter((job) => job !== undefined) as Cronjob[];\n }\n\n /**\n * Retrieve all Cronjob specifications for a Snap.\n *\n * @param snapId - ID of a Snap.\n * @returns Array of Cronjob specifications.\n */\n private getSnapJobs(snapId: ValidatedSnapId): Cronjob[] | undefined {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snapId,\n );\n\n const permission = permissions?.[SnapEndowments.Cronjob];\n const definitions = getCronjobCaveatJobs(permission);\n\n return definitions?.map((definition, idx) => {\n return { ...definition, id: `${snapId}-${idx}`, snapId };\n });\n }\n\n /**\n * Register cron jobs for a given snap by getting specification from a permission caveats.\n * Once registered, each job will be scheduled.\n *\n * @param snapId - ID of a snap.\n */\n register(snapId: ValidatedSnapId) {\n const jobs = this.getSnapJobs(snapId);\n jobs?.forEach((job) => this.schedule(job));\n }\n\n /**\n * Schedule a new job.\n * This will interpret the cron expression and tell the timer to execute the job\n * at the next suitable point in time.\n * Job last run state will be initialized afterwards.\n *\n * Note: Schedule will be skipped if the job's execution time is too far in the future and\n * will be revisited on a daily check.\n *\n * @param job - Cronjob specification.\n */\n private schedule(job: Cronjob) {\n if (this.#timers.has(job.id)) {\n return;\n }\n\n const parsed = parseCronExpression(job.expression);\n const next = parsed.next();\n const now = new Date();\n const ms = next.getTime() - now.getTime();\n\n // Don't schedule this job yet as it is too far in the future\n if (ms > DAILY_TIMEOUT) {\n return;\n }\n\n const timer = new Timer(ms);\n timer.start(() => {\n this.executeCronjob(job).catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n\n this.#timers.delete(job.id);\n this.schedule(job);\n });\n\n if (!this.state.jobs[job.id]?.lastRun) {\n this.updateJobLastRunState(job.id, 0); // 0 for init, never ran actually\n }\n\n this.#timers.set(job.id, timer);\n this.#snapIds.set(job.id, job.snapId);\n }\n\n /**\n * Execute job.\n *\n * @param job - Cronjob specification.\n */\n private async executeCronjob(job: Cronjob) {\n this.updateJobLastRunState(job.id, Date.now());\n await this.#messenger.call('SnapController:handleRequest', {\n snapId: job.snapId,\n origin: '',\n handler: HandlerType.OnCronjob,\n request: job.request,\n });\n }\n\n /**\n * Unregister all jobs related to the given snapId.\n *\n * @param snapId - ID of a snap.\n */\n unregister(snapId: SnapId) {\n const jobs = [...this.#snapIds.entries()].filter(\n ([_, jobSnapId]) => jobSnapId === snapId,\n );\n\n if (jobs.length) {\n jobs.forEach(([id]) => {\n const timer = this.#timers.get(id);\n if (timer) {\n timer.cancel();\n this.#timers.delete(id);\n this.#snapIds.delete(id);\n }\n });\n }\n }\n\n /**\n * Update time of a last run for the Cronjob specified by ID.\n *\n * @param jobId - ID of a cron job.\n * @param lastRun - Unix timestamp when the job was last ran.\n */\n private updateJobLastRunState(jobId: string, lastRun: number) {\n this.update((state) => {\n state.jobs[jobId] = {\n lastRun,\n };\n });\n }\n\n /**\n * Runs every 24 hours to check if new jobs need to be scheduled.\n *\n * This is necessary for longer running jobs that execute with more than 24 hours between them.\n */\n async dailyCheckIn() {\n const jobs = this.getAllJobs();\n\n for (const job of jobs) {\n const parsed = parseCronExpression(job.expression);\n const lastRun = this.state.jobs[job.id]?.lastRun;\n // If a job was supposed to run while we were shut down but wasn't we run it now\n if (\n lastRun !== undefined &&\n parsed.hasPrev() &&\n parsed.prev().getTime() > lastRun\n ) {\n await this.executeCronjob(job);\n }\n\n // Try scheduling, will fail if an existing scheduled job is found\n this.schedule(job);\n }\n\n this.#dailyTimer = new Timer(DAILY_TIMEOUT);\n this.#dailyTimer.start(() => {\n this.dailyCheckIn().catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n });\n }\n\n /**\n * Run controller teardown process and unsubscribe from Snap events.\n */\n destroy() {\n super.destroy();\n\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.unsubscribe(\n 'SnapController:snapInstalled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapRemoved',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapEnabled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapDisabled',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.#snapIds.forEach((snapId) => {\n this.unregister(snapId);\n });\n }\n\n /**\n * Handle events that should cause cronjobs to be registered.\n *\n * @param snap - Basic Snap information.\n */\n private _handleSnapRegisterEvent(snap: TruncatedSnap) {\n this.register(snap.id);\n }\n\n /**\n * Handle events that should cause cronjobs to be unregistered.\n *\n * @param snap - Basic Snap information.\n */\n private _handleSnapUnregisterEvent(snap: TruncatedSnap) {\n this.unregister(snap.id);\n }\n\n /**\n * Handle cron jobs on 'snapUpdated' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapUpdated(snap: TruncatedSnap) {\n this.unregister(snap.id);\n this.register(snap.id);\n }\n}\n"],"names":["BaseControllerV2","BaseController","HandlerType","parseCronExpression","logError","Duration","inMilliseconds","getRunnableSnaps","SnapEndowments","getCronjobCaveatJobs","Timer","DAILY_TIMEOUT","Hour","controllerName","CronjobController","getAllJobs","snaps","messagingSystem","call","filteredSnaps","jobs","map","snap","getSnapJobs","id","flat","filter","job","undefined","snapId","permissions","messenger","permission","Cronjob","definitions","definition","idx","register","forEach","schedule","timers","has","parsed","expression","next","now","Date","ms","getTime","timer","start","executeCronjob","catch","error","delete","state","lastRun","updateJobLastRunState","set","snapIds","origin","handler","OnCronjob","request","unregister","entries","_","jobSnapId","length","get","cancel","jobId","update","dailyCheckIn","hasPrev","prev","dailyTimer","destroy","unsubscribe","_handleSnapRegisterEvent","_handleSnapUnregisterEvent","_handleEventSnapUpdated","constructor","metadata","persist","anonymous","name","Map","bind","subscribe"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/cronjob/CronjobController.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport { BaseControllerV2 as BaseController } from '@metamask/base-controller';\nimport type { GetPermissions } from '@metamask/permission-controller';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport type {\n TruncatedSnap,\n CronjobSpecification,\n} from '@metamask/snaps-utils';\nimport {\n HandlerType,\n parseCronExpression,\n logError,\n} from '@metamask/snaps-utils';\nimport { Duration, inMilliseconds } from '@metamask/utils';\n\nimport type {\n GetAllSnaps,\n HandleSnapRequest,\n SnapDisabled,\n SnapEnabled,\n SnapInstalled,\n SnapRemoved,\n SnapUpdated,\n} from '..';\nimport { getRunnableSnaps, SnapEndowments } from '..';\nimport { getCronjobCaveatJobs } from '../snaps/endowments/cronjob';\nimport { Timer } from '../snaps/Timer';\n\nexport type CronjobControllerActions =\n | GetAllSnaps\n | HandleSnapRequest\n | GetPermissions;\n\nexport type CronjobControllerEvents =\n | SnapInstalled\n | SnapRemoved\n | SnapUpdated\n | SnapEnabled\n | SnapDisabled;\n\nexport type CronjobControllerMessenger = RestrictedControllerMessenger<\n 'CronjobController',\n CronjobControllerActions,\n CronjobControllerEvents,\n CronjobControllerActions['type'],\n CronjobControllerEvents['type']\n>;\n\nexport const DAILY_TIMEOUT = inMilliseconds(24, Duration.Hour);\n\nexport type CronjobControllerArgs = {\n messenger: CronjobControllerMessenger;\n /**\n * Persisted state that will be used for rehydration.\n */\n state?: CronjobControllerState;\n};\n\nexport type Cronjob = {\n timer?: Timer;\n id: string;\n snapId: SnapId;\n} & CronjobSpecification;\n\nexport type StoredJobInformation = {\n lastRun: number;\n};\n\nexport type CronjobControllerState = {\n jobs: Record<string, StoredJobInformation>;\n};\n\nconst controllerName = 'CronjobController';\n\n/**\n * Use this controller to register and schedule periodically executed jobs\n * using RPC method hooks.\n */\nexport class CronjobController extends BaseController<\n typeof controllerName,\n CronjobControllerState,\n CronjobControllerMessenger\n> {\n #messenger: CronjobControllerMessenger;\n\n #dailyTimer!: Timer;\n\n #timers: Map<string, Timer>;\n\n // Mapping from jobId to snapId\n #snapIds: Map<string, string>;\n\n constructor({ messenger, state }: CronjobControllerArgs) {\n super({\n messenger,\n metadata: {\n jobs: { persist: true, anonymous: false },\n },\n name: controllerName,\n state: {\n jobs: {},\n ...state,\n },\n });\n this.#timers = new Map();\n this.#snapIds = new Map();\n this.#messenger = messenger;\n\n this._handleSnapRegisterEvent = this._handleSnapRegisterEvent.bind(this);\n this._handleSnapUnregisterEvent =\n this._handleSnapUnregisterEvent.bind(this);\n this._handleEventSnapUpdated = this._handleEventSnapUpdated.bind(this);\n\n // Subscribe to Snap events\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.subscribe(\n 'SnapController:snapInstalled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapRemoved',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapEnabled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapDisabled',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.subscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.dailyCheckIn().catch((error) => {\n logError(error);\n });\n }\n\n /**\n * Retrieve all cronjob specifications for all runnable snaps.\n *\n * @returns Array of Cronjob specifications.\n */\n private getAllJobs(): Cronjob[] {\n const snaps = this.messagingSystem.call('SnapController:getAll');\n const filteredSnaps = getRunnableSnaps(snaps);\n\n const jobs = filteredSnaps.map((snap) => this.getSnapJobs(snap.id));\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n return jobs.flat().filter((job) => job !== undefined) as Cronjob[];\n }\n\n /**\n * Retrieve all Cronjob specifications for a Snap.\n *\n * @param snapId - ID of a Snap.\n * @returns Array of Cronjob specifications.\n */\n private getSnapJobs(snapId: SnapId): Cronjob[] | undefined {\n const permissions = this.#messenger.call(\n 'PermissionController:getPermissions',\n snapId,\n );\n\n const permission = permissions?.[SnapEndowments.Cronjob];\n const definitions = getCronjobCaveatJobs(permission);\n\n return definitions?.map((definition, idx) => {\n return { ...definition, id: `${snapId}-${idx}`, snapId };\n });\n }\n\n /**\n * Register cron jobs for a given snap by getting specification from a permission caveats.\n * Once registered, each job will be scheduled.\n *\n * @param snapId - ID of a snap.\n */\n register(snapId: SnapId) {\n const jobs = this.getSnapJobs(snapId);\n jobs?.forEach((job) => this.schedule(job));\n }\n\n /**\n * Schedule a new job.\n * This will interpret the cron expression and tell the timer to execute the job\n * at the next suitable point in time.\n * Job last run state will be initialized afterwards.\n *\n * Note: Schedule will be skipped if the job's execution time is too far in the future and\n * will be revisited on a daily check.\n *\n * @param job - Cronjob specification.\n */\n private schedule(job: Cronjob) {\n if (this.#timers.has(job.id)) {\n return;\n }\n\n const parsed = parseCronExpression(job.expression);\n const next = parsed.next();\n const now = new Date();\n const ms = next.getTime() - now.getTime();\n\n // Don't schedule this job yet as it is too far in the future\n if (ms > DAILY_TIMEOUT) {\n return;\n }\n\n const timer = new Timer(ms);\n timer.start(() => {\n this.executeCronjob(job).catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n\n this.#timers.delete(job.id);\n this.schedule(job);\n });\n\n if (!this.state.jobs[job.id]?.lastRun) {\n this.updateJobLastRunState(job.id, 0); // 0 for init, never ran actually\n }\n\n this.#timers.set(job.id, timer);\n this.#snapIds.set(job.id, job.snapId);\n }\n\n /**\n * Execute job.\n *\n * @param job - Cronjob specification.\n */\n private async executeCronjob(job: Cronjob) {\n this.updateJobLastRunState(job.id, Date.now());\n await this.#messenger.call('SnapController:handleRequest', {\n snapId: job.snapId,\n origin: '',\n handler: HandlerType.OnCronjob,\n request: job.request,\n });\n }\n\n /**\n * Unregister all jobs related to the given snapId.\n *\n * @param snapId - ID of a snap.\n */\n unregister(snapId: string) {\n const jobs = [...this.#snapIds.entries()].filter(\n ([_, jobSnapId]) => jobSnapId === snapId,\n );\n\n if (jobs.length) {\n jobs.forEach(([id]) => {\n const timer = this.#timers.get(id);\n if (timer) {\n timer.cancel();\n this.#timers.delete(id);\n this.#snapIds.delete(id);\n }\n });\n }\n }\n\n /**\n * Update time of a last run for the Cronjob specified by ID.\n *\n * @param jobId - ID of a cron job.\n * @param lastRun - Unix timestamp when the job was last ran.\n */\n private updateJobLastRunState(jobId: string, lastRun: number) {\n this.update((state) => {\n state.jobs[jobId] = {\n lastRun,\n };\n });\n }\n\n /**\n * Runs every 24 hours to check if new jobs need to be scheduled.\n *\n * This is necessary for longer running jobs that execute with more than 24 hours between them.\n */\n async dailyCheckIn() {\n const jobs = this.getAllJobs();\n\n for (const job of jobs) {\n const parsed = parseCronExpression(job.expression);\n const lastRun = this.state.jobs[job.id]?.lastRun;\n // If a job was supposed to run while we were shut down but wasn't we run it now\n if (\n lastRun !== undefined &&\n parsed.hasPrev() &&\n parsed.prev().getTime() > lastRun\n ) {\n await this.executeCronjob(job);\n }\n\n // Try scheduling, will fail if an existing scheduled job is found\n this.schedule(job);\n }\n\n this.#dailyTimer = new Timer(DAILY_TIMEOUT);\n this.#dailyTimer.start(() => {\n this.dailyCheckIn().catch((error) => {\n // TODO: Decide how to handle errors.\n logError(error);\n });\n });\n }\n\n /**\n * Run controller teardown process and unsubscribe from Snap events.\n */\n destroy() {\n super.destroy();\n\n /* eslint-disable @typescript-eslint/unbound-method */\n this.messagingSystem.unsubscribe(\n 'SnapController:snapInstalled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapRemoved',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapEnabled',\n this._handleSnapRegisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapDisabled',\n this._handleSnapUnregisterEvent,\n );\n\n this.messagingSystem.unsubscribe(\n 'SnapController:snapUpdated',\n this._handleEventSnapUpdated,\n );\n /* eslint-enable @typescript-eslint/unbound-method */\n\n this.#snapIds.forEach((snapId) => {\n this.unregister(snapId);\n });\n }\n\n /**\n * Handle events that should cause cronjobs to be registered.\n *\n * @param snap - Basic Snap information.\n */\n private _handleSnapRegisterEvent(snap: TruncatedSnap) {\n this.register(snap.id);\n }\n\n /**\n * Handle events that should cause cronjobs to be unregistered.\n *\n * @param snap - Basic Snap information.\n */\n private _handleSnapUnregisterEvent(snap: TruncatedSnap) {\n this.unregister(snap.id);\n }\n\n /**\n * Handle cron jobs on 'snapUpdated' event.\n *\n * @param snap - Basic Snap information.\n */\n private _handleEventSnapUpdated(snap: TruncatedSnap) {\n this.unregister(snap.id);\n this.register(snap.id);\n }\n}\n"],"names":["BaseControllerV2","BaseController","HandlerType","parseCronExpression","logError","Duration","inMilliseconds","getRunnableSnaps","SnapEndowments","getCronjobCaveatJobs","Timer","DAILY_TIMEOUT","Hour","controllerName","CronjobController","getAllJobs","snaps","messagingSystem","call","filteredSnaps","jobs","map","snap","getSnapJobs","id","flat","filter","job","undefined","snapId","permissions","messenger","permission","Cronjob","definitions","definition","idx","register","forEach","schedule","timers","has","parsed","expression","next","now","Date","ms","getTime","timer","start","executeCronjob","catch","error","delete","state","lastRun","updateJobLastRunState","set","snapIds","origin","handler","OnCronjob","request","unregister","entries","_","jobSnapId","length","get","cancel","jobId","update","dailyCheckIn","hasPrev","prev","dailyTimer","destroy","unsubscribe","_handleSnapRegisterEvent","_handleSnapUnregisterEvent","_handleEventSnapUpdated","constructor","metadata","persist","anonymous","name","Map","bind","subscribe"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/services/ExecutionService.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport type {
|
|
1
|
+
{"version":3,"sources":["../../../src/services/ExecutionService.ts"],"sourcesContent":["import type { RestrictedControllerMessenger } from '@metamask/base-controller';\nimport type { SnapRpcHookArgs } from '@metamask/snaps-utils';\nimport type { Json } from '@metamask/utils';\n\ntype TerminateSnap = (snapId: string) => Promise<void>;\ntype TerminateAll = () => Promise<void>;\ntype ExecuteSnap = (snapData: SnapExecutionData) => Promise<unknown>;\n\ntype HandleRpcRequest = (\n snapId: string,\n options: SnapRpcHookArgs,\n) => Promise<unknown>;\n\nexport interface ExecutionService {\n terminateSnap: TerminateSnap;\n terminateAllSnaps: TerminateAll;\n executeSnap: ExecuteSnap;\n handleRpcRequest: HandleRpcRequest;\n}\n\nexport type SnapExecutionData = {\n snapId: string;\n sourceCode: string;\n endowments?: Json;\n};\n\nexport type SnapErrorJson = {\n message: string;\n code: number;\n data?: Json;\n};\n\nconst controllerName = 'ExecutionService';\n\nexport type ErrorMessageEvent = {\n type: 'ExecutionService:unhandledError';\n payload: [string, SnapErrorJson];\n};\n\nexport type OutboundRequest = {\n type: 'ExecutionService:outboundRequest';\n payload: [string];\n};\n\nexport type OutboundResponse = {\n type: 'ExecutionService:outboundResponse';\n payload: [string];\n};\n\nexport type ExecutionServiceEvents =\n | ErrorMessageEvent\n | OutboundRequest\n | OutboundResponse;\n\n/**\n * Handles RPC request.\n */\nexport type HandleRpcRequestAction = {\n type: `${typeof controllerName}:handleRpcRequest`;\n handler: ExecutionService['handleRpcRequest'];\n};\n\n/**\n * Executes a given snap.\n */\nexport type ExecuteSnapAction = {\n type: `${typeof controllerName}:executeSnap`;\n handler: ExecutionService['executeSnap'];\n};\n\n/**\n * Terminates a given snap.\n */\nexport type TerminateSnapAction = {\n type: `${typeof controllerName}:terminateSnap`;\n handler: ExecutionService['terminateSnap'];\n};\n\n/**\n * Terminates all snaps.\n */\nexport type TerminateAllSnapsAction = {\n type: `${typeof controllerName}:terminateAllSnaps`;\n handler: ExecutionService['terminateAllSnaps'];\n};\n\nexport type ExecutionServiceActions =\n | HandleRpcRequestAction\n | ExecuteSnapAction\n | TerminateSnapAction\n | TerminateAllSnapsAction;\n\nexport type ExecutionServiceMessenger = RestrictedControllerMessenger<\n 'ExecutionService',\n ExecutionServiceActions,\n ExecutionServiceEvents,\n ExecutionServiceActions['type'],\n ExecutionServiceEvents['type']\n>;\n"],"names":["controllerName"],"mappings":"AAgCA,MAAMA,iBAAiB;AAhCvB,WAkGE"}
|
|
@@ -65,14 +65,14 @@ import { BaseControllerV2 as BaseController } from '@metamask/base-controller';
|
|
|
65
65
|
import { SubjectType } from '@metamask/permission-controller';
|
|
66
66
|
import { rpcErrors } from '@metamask/rpc-errors';
|
|
67
67
|
import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/snaps-rpc-methods';
|
|
68
|
-
import {
|
|
69
|
-
import { assertIsSnapManifest, assertIsValidSnapId,
|
|
68
|
+
import { AuxiliaryFileEncoding, getErrorMessage } from '@metamask/snaps-sdk';
|
|
69
|
+
import { validateComponentLinks, assertIsSnapManifest, assertIsValidSnapId, DEFAULT_ENDOWMENTS, DEFAULT_REQUESTED_SNAP_VERSION, encodeAuxiliaryFile, HandlerType, isOriginAllowed, logError, normalizeRelative, OnTransactionResponseStruct, resolveVersionRange, SnapCaveatType, SnapStatus, SnapStatusEvents, validateFetchedSnap, unwrapError, OnHomePageResponseStruct, getValidatedLocalizationFiles } from '@metamask/snaps-utils';
|
|
70
70
|
import { assert, assertIsJsonRpcRequest, assertStruct, Duration, gtRange, gtVersion, hasProperty, inMilliseconds, isNonEmptyArray, isValidSemVerRange, satisfiesVersionRange, timeSince } from '@metamask/utils';
|
|
71
71
|
import { createMachine, interpret } from '@xstate/fsm';
|
|
72
72
|
import { nanoid } from 'nanoid';
|
|
73
73
|
import { forceStrict, validateMachine } from '../fsm';
|
|
74
74
|
import { log } from '../logging';
|
|
75
|
-
import { hasTimedOut, setDiff, withTimeout } from '../utils';
|
|
75
|
+
import { getSnapFiles, hasTimedOut, setDiff, withTimeout } from '../utils';
|
|
76
76
|
import { handlerEndowments, SnapEndowments } from './endowments';
|
|
77
77
|
import { getKeyringCaveatOrigins } from './endowments/keyring';
|
|
78
78
|
import { getRpcCaveatOrigins } from './endowments/rpc';
|
|
@@ -95,7 +95,8 @@ const TRUNCATED_SNAP_PROPERTIES = new Set([
|
|
|
95
95
|
]);
|
|
96
96
|
const defaultState = {
|
|
97
97
|
snaps: {},
|
|
98
|
-
snapStates: {}
|
|
98
|
+
snapStates: {},
|
|
99
|
+
unencryptedSnapStates: {}
|
|
99
100
|
};
|
|
100
101
|
/**
|
|
101
102
|
* Truncates the properties of a snap to only ones that are easily serializable.
|
|
@@ -167,7 +168,7 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
167
168
|
*
|
|
168
169
|
* @param snapId - The id of the Snap whose message handler to get.
|
|
169
170
|
* @returns The RPC handler for the given snap.
|
|
170
|
-
*/ _getRpcRequestHandler = /*#__PURE__*/ new WeakSet(), _assertSnapRpcRequestResult = /*#__PURE__*/ new WeakSet(), _executeWithTimeout = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestStart = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestFinish = /*#__PURE__*/ new WeakSet(), /**
|
|
171
|
+
*/ _getRpcRequestHandler = /*#__PURE__*/ new WeakSet(), _triggerPhishingListUpdate = /*#__PURE__*/ new WeakSet(), _checkPhishingList = /*#__PURE__*/ new WeakSet(), _assertSnapRpcRequestResult = /*#__PURE__*/ new WeakSet(), _executeWithTimeout = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestStart = /*#__PURE__*/ new WeakSet(), _recordSnapRpcRequestFinish = /*#__PURE__*/ new WeakSet(), /**
|
|
171
172
|
* Retrieves the rollback snapshot of a snap.
|
|
172
173
|
*
|
|
173
174
|
* @param snapId - The snap id.
|
|
@@ -381,9 +382,14 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
381
382
|
*
|
|
382
383
|
* @param snapId - The id of the Snap whose state should be updated.
|
|
383
384
|
* @param newSnapState - The new state of the snap.
|
|
384
|
-
|
|
385
|
+
* @param encrypted - A flag to indicate whether to use encrypted storage or not.
|
|
386
|
+
*/ updateSnapState(snapId, newSnapState, encrypted) {
|
|
385
387
|
this.update((state)=>{
|
|
386
|
-
|
|
388
|
+
if (encrypted) {
|
|
389
|
+
state.snapStates[snapId] = newSnapState;
|
|
390
|
+
} else {
|
|
391
|
+
state.unencryptedSnapStates[snapId] = newSnapState;
|
|
392
|
+
}
|
|
387
393
|
});
|
|
388
394
|
}
|
|
389
395
|
/**
|
|
@@ -391,9 +397,14 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
391
397
|
* This is distinct from the state MetaMask uses to manage snaps.
|
|
392
398
|
*
|
|
393
399
|
* @param snapId - The id of the Snap whose state should be cleared.
|
|
394
|
-
|
|
400
|
+
* @param encrypted - A flag to indicate whether to use encrypted storage or not.
|
|
401
|
+
*/ clearSnapState(snapId, encrypted) {
|
|
395
402
|
this.update((state)=>{
|
|
396
|
-
|
|
403
|
+
if (encrypted) {
|
|
404
|
+
state.snapStates[snapId] = null;
|
|
405
|
+
} else {
|
|
406
|
+
state.unencryptedSnapStates[snapId] = null;
|
|
407
|
+
}
|
|
397
408
|
});
|
|
398
409
|
}
|
|
399
410
|
/**
|
|
@@ -401,10 +412,10 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
401
412
|
* This is distinct from the state MetaMask uses to manage snaps.
|
|
402
413
|
*
|
|
403
414
|
* @param snapId - The id of the Snap whose state to get.
|
|
404
|
-
* @
|
|
405
|
-
* @
|
|
406
|
-
*/
|
|
407
|
-
const state = this.state.snapStates[snapId];
|
|
415
|
+
* @param encrypted - A flag to indicate whether to use encrypted storage or not.
|
|
416
|
+
* @returns The requested snap state or null if no state exists.
|
|
417
|
+
*/ getSnapState(snapId, encrypted) {
|
|
418
|
+
const state = encrypted ? this.state.snapStates[snapId] : this.state.unencryptedSnapStates[snapId];
|
|
408
419
|
return state ?? null;
|
|
409
420
|
}
|
|
410
421
|
/**
|
|
@@ -921,6 +932,10 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
921
932
|
persist: true,
|
|
922
933
|
anonymous: false
|
|
923
934
|
},
|
|
935
|
+
unencryptedSnapStates: {
|
|
936
|
+
persist: true,
|
|
937
|
+
anonymous: false
|
|
938
|
+
},
|
|
924
939
|
snaps: {
|
|
925
940
|
persist: (snaps)=>{
|
|
926
941
|
return Object.values(snaps)// We should not persist snaps that are in the installing state,
|
|
@@ -1000,6 +1015,8 @@ _initializeStateMachine = /*#__PURE__*/ new WeakSet(), /**
|
|
|
1000
1015
|
*/ _class_private_method_init(this, _fetchSnap);
|
|
1001
1016
|
_class_private_method_init(this, _validateSnapPermissions);
|
|
1002
1017
|
_class_private_method_init(this, _getRpcRequestHandler);
|
|
1018
|
+
_class_private_method_init(this, _triggerPhishingListUpdate);
|
|
1019
|
+
_class_private_method_init(this, _checkPhishingList);
|
|
1003
1020
|
/**
|
|
1004
1021
|
* Asserts that the returned result of a Snap RPC call is the expected shape.
|
|
1005
1022
|
*
|
|
@@ -1198,11 +1215,11 @@ function initializeStateMachine() {
|
|
|
1198
1215
|
function registerMessageHandlers() {
|
|
1199
1216
|
this.messagingSystem.registerActionHandler(`${controllerName}:clearSnapState`, (...args)=>this.clearSnapState(...args));
|
|
1200
1217
|
this.messagingSystem.registerActionHandler(`${controllerName}:get`, (...args)=>this.get(...args));
|
|
1201
|
-
this.messagingSystem.registerActionHandler(`${controllerName}:getSnapState`,
|
|
1218
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:getSnapState`, (...args)=>this.getSnapState(...args));
|
|
1202
1219
|
this.messagingSystem.registerActionHandler(`${controllerName}:handleRequest`, async (...args)=>this.handleRequest(...args));
|
|
1203
1220
|
this.messagingSystem.registerActionHandler(`${controllerName}:has`, (...args)=>this.has(...args));
|
|
1204
1221
|
this.messagingSystem.registerActionHandler(`${controllerName}:updateBlockedSnaps`, async ()=>this.updateBlockedSnaps());
|
|
1205
|
-
this.messagingSystem.registerActionHandler(`${controllerName}:updateSnapState`,
|
|
1222
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:updateSnapState`, (...args)=>this.updateSnapState(...args));
|
|
1206
1223
|
this.messagingSystem.registerActionHandler(`${controllerName}:enable`, (...args)=>this.enableSnap(...args));
|
|
1207
1224
|
this.messagingSystem.registerActionHandler(`${controllerName}:disable`, async (...args)=>this.disableSnap(...args));
|
|
1208
1225
|
this.messagingSystem.registerActionHandler(`${controllerName}:remove`, async (...args)=>this.removeSnap(...args));
|
|
@@ -1411,7 +1428,7 @@ async function getEndowments(snapId) {
|
|
|
1411
1428
|
}
|
|
1412
1429
|
function set(args) {
|
|
1413
1430
|
const { id: snapId, origin, files, isUpdate = false } = args;
|
|
1414
|
-
const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles } = files;
|
|
1431
|
+
const { manifest, sourceCode: sourceCodeFile, svgIcon, auxiliaryFiles: rawAuxiliaryFiles, localizationFiles } = files;
|
|
1415
1432
|
assertIsSnapManifest(manifest.result);
|
|
1416
1433
|
const { version } = manifest.result;
|
|
1417
1434
|
const sourceCode = sourceCodeFile.toString();
|
|
@@ -1445,7 +1462,8 @@ function set(args) {
|
|
|
1445
1462
|
sourceCode,
|
|
1446
1463
|
version,
|
|
1447
1464
|
versionHistory,
|
|
1448
|
-
auxiliaryFiles
|
|
1465
|
+
auxiliaryFiles,
|
|
1466
|
+
localizationFiles: localizationFiles.map((file)=>file.result)
|
|
1449
1467
|
};
|
|
1450
1468
|
// If the snap was blocked, it isn't any longer
|
|
1451
1469
|
delete snap.blockInformation;
|
|
@@ -1473,14 +1491,17 @@ async function fetchSnap(snapId, location) {
|
|
|
1473
1491
|
const sourceCode = await location.fetch(manifest.result.source.location.npm.filePath);
|
|
1474
1492
|
const { iconPath } = manifest.result.source.location.npm;
|
|
1475
1493
|
const svgIcon = iconPath ? await location.fetch(iconPath) : undefined;
|
|
1476
|
-
const auxiliaryFiles =
|
|
1494
|
+
const auxiliaryFiles = await getSnapFiles(location, manifest.result.source.files);
|
|
1495
|
+
const localizationFiles = await getSnapFiles(location, manifest.result.source.locales);
|
|
1496
|
+
const validatedLocalizationFiles = getValidatedLocalizationFiles(localizationFiles);
|
|
1477
1497
|
const files = {
|
|
1478
1498
|
manifest,
|
|
1479
1499
|
sourceCode,
|
|
1480
1500
|
svgIcon,
|
|
1481
|
-
auxiliaryFiles
|
|
1501
|
+
auxiliaryFiles,
|
|
1502
|
+
localizationFiles: validatedLocalizationFiles
|
|
1482
1503
|
};
|
|
1483
|
-
validateFetchedSnap(files);
|
|
1504
|
+
await validateFetchedSnap(files);
|
|
1484
1505
|
return {
|
|
1485
1506
|
files,
|
|
1486
1507
|
location
|
|
@@ -1562,12 +1583,29 @@ function getRpcRequestHandler(snapId) {
|
|
|
1562
1583
|
runtime.rpcHandler = rpcHandler;
|
|
1563
1584
|
return rpcHandler;
|
|
1564
1585
|
}
|
|
1586
|
+
async function triggerPhishingListUpdate() {
|
|
1587
|
+
return this.messagingSystem.call('PhishingController:maybeUpdateState');
|
|
1588
|
+
}
|
|
1589
|
+
function checkPhishingList(origin) {
|
|
1590
|
+
return this.messagingSystem.call('PhishingController:testOrigin', origin).result;
|
|
1591
|
+
}
|
|
1565
1592
|
async function assertSnapRpcRequestResult(handlerType, result) {
|
|
1566
1593
|
switch(handlerType){
|
|
1567
1594
|
case HandlerType.OnTransaction:
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1595
|
+
{
|
|
1596
|
+
assertStruct(result, OnTransactionResponseStruct);
|
|
1597
|
+
// Null is an allowed return value here
|
|
1598
|
+
if (result === null) {
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
|
|
1602
|
+
validateComponentLinks(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
|
|
1603
|
+
break;
|
|
1604
|
+
}
|
|
1605
|
+
case HandlerType.OnHomePage:
|
|
1606
|
+
assertStruct(result, OnHomePageResponseStruct);
|
|
1607
|
+
await _class_private_method_get(this, _triggerPhishingListUpdate, triggerPhishingListUpdate).call(this);
|
|
1608
|
+
validateComponentLinks(result.content, _class_private_method_get(this, _checkPhishingList, checkPhishingList).bind(this));
|
|
1571
1609
|
break;
|
|
1572
1610
|
default:
|
|
1573
1611
|
break;
|