@metamask/snaps-controllers 2.0.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/dist/cjs/cronjob/CronjobController.js +1 -1
  3. package/dist/cjs/cronjob/CronjobController.js.map +1 -1
  4. package/dist/cjs/fsm.js.map +1 -1
  5. package/dist/cjs/services/AbstractExecutionService.js +6 -7
  6. package/dist/cjs/services/AbstractExecutionService.js.map +1 -1
  7. package/dist/cjs/snaps/SnapController.js +80 -89
  8. package/dist/cjs/snaps/SnapController.js.map +1 -1
  9. package/dist/cjs/snaps/endowments/cronjob.js +4 -4
  10. package/dist/cjs/snaps/endowments/cronjob.js.map +1 -1
  11. package/dist/cjs/snaps/endowments/enum.js +1 -0
  12. package/dist/cjs/snaps/endowments/enum.js.map +1 -1
  13. package/dist/cjs/snaps/endowments/index.js +12 -4
  14. package/dist/cjs/snaps/endowments/index.js.map +1 -1
  15. package/dist/cjs/snaps/endowments/keyring.js +100 -0
  16. package/dist/cjs/snaps/endowments/keyring.js.map +1 -0
  17. package/dist/cjs/snaps/endowments/name-lookup.js +3 -3
  18. package/dist/cjs/snaps/endowments/name-lookup.js.map +1 -1
  19. package/dist/cjs/snaps/endowments/rpc.js +4 -4
  20. package/dist/cjs/snaps/endowments/rpc.js.map +1 -1
  21. package/dist/cjs/snaps/endowments/transaction-insight.js +3 -3
  22. package/dist/cjs/snaps/endowments/transaction-insight.js.map +1 -1
  23. package/dist/cjs/snaps/location/npm.js +41 -14
  24. package/dist/cjs/snaps/location/npm.js.map +1 -1
  25. package/dist/cjs/snaps/permissions.js +5 -5
  26. package/dist/cjs/snaps/permissions.js.map +1 -1
  27. package/dist/cjs/snaps/registry/json.js +30 -1
  28. package/dist/cjs/snaps/registry/json.js.map +1 -1
  29. package/dist/cjs/snaps/registry/registry.js.map +1 -1
  30. package/dist/esm/cronjob/CronjobController.js +1 -1
  31. package/dist/esm/cronjob/CronjobController.js.map +1 -1
  32. package/dist/esm/fsm.js.map +1 -1
  33. package/dist/esm/services/AbstractExecutionService.js +6 -7
  34. package/dist/esm/services/AbstractExecutionService.js.map +1 -1
  35. package/dist/esm/snaps/SnapController.js +77 -86
  36. package/dist/esm/snaps/SnapController.js.map +1 -1
  37. package/dist/esm/snaps/endowments/cronjob.js +4 -4
  38. package/dist/esm/snaps/endowments/cronjob.js.map +1 -1
  39. package/dist/esm/snaps/endowments/enum.js +1 -0
  40. package/dist/esm/snaps/endowments/enum.js.map +1 -1
  41. package/dist/esm/snaps/endowments/index.js +10 -4
  42. package/dist/esm/snaps/endowments/index.js.map +1 -1
  43. package/dist/esm/snaps/endowments/keyring.js +91 -0
  44. package/dist/esm/snaps/endowments/keyring.js.map +1 -0
  45. package/dist/esm/snaps/endowments/name-lookup.js +3 -3
  46. package/dist/esm/snaps/endowments/name-lookup.js.map +1 -1
  47. package/dist/esm/snaps/endowments/rpc.js +4 -4
  48. package/dist/esm/snaps/endowments/rpc.js.map +1 -1
  49. package/dist/esm/snaps/endowments/transaction-insight.js +3 -3
  50. package/dist/esm/snaps/endowments/transaction-insight.js.map +1 -1
  51. package/dist/esm/snaps/location/npm.js +42 -15
  52. package/dist/esm/snaps/location/npm.js.map +1 -1
  53. package/dist/esm/snaps/permissions.js +1 -1
  54. package/dist/esm/snaps/permissions.js.map +1 -1
  55. package/dist/esm/snaps/registry/json.js +31 -2
  56. package/dist/esm/snaps/registry/json.js.map +1 -1
  57. package/dist/esm/snaps/registry/registry.js.map +1 -1
  58. package/dist/types/cronjob/CronjobController.d.ts +1 -1
  59. package/dist/types/services/AbstractExecutionService.d.ts +1 -1
  60. package/dist/types/snaps/SnapController.d.ts +20 -32
  61. package/dist/types/snaps/endowments/enum.d.ts +2 -1
  62. package/dist/types/snaps/endowments/index.d.ts +13 -0
  63. package/dist/types/snaps/endowments/keyring.d.ts +39 -0
  64. package/dist/types/snaps/location/npm.d.ts +1 -1
  65. package/dist/types/snaps/registry/json.d.ts +5 -1
  66. package/dist/types/snaps/registry/registry.d.ts +11 -1
  67. package/package.json +32 -25
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/snaps/location/npm.ts"],"sourcesContent":["import type { SnapManifest } from '@metamask/snaps-utils';\nimport {\n createSnapManifest,\n DEFAULT_REQUESTED_SNAP_VERSION,\n getTargetVersion,\n isValidUrl,\n NpmSnapIdStruct,\n VirtualFile,\n normalizeRelative,\n parseJson,\n} from '@metamask/snaps-utils';\nimport type { SemVerRange, SemVerVersion } from '@metamask/utils';\nimport {\n assert,\n assertIsSemVerVersion,\n assertStruct,\n isObject,\n} from '@metamask/utils';\nimport concat from 'concat-stream';\nimport createGunzipStream from 'gunzip-maybe';\nimport { ReadableWebToNodeStream } from 'readable-web-to-node-stream';\nimport { pipeline } from 'stream';\nimport type { Readable, Writable } from 'stream';\nimport { extract as tarExtract } from 'tar-stream';\n\nimport type { DetectSnapLocationOptions, SnapLocation } from './location';\n\nexport const DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org';\n\ninterface NpmMeta {\n registry: URL;\n packageName: string;\n requestedRange: SemVerRange;\n version?: string;\n fetch: typeof fetch;\n}\nexport interface NpmOptions {\n /**\n * @default DEFAULT_REQUESTED_SNAP_VERSION\n */\n versionRange?: SemVerRange;\n /**\n * Whether to allow custom NPM registries outside of {@link DEFAULT_NPM_REGISTRY}.\n *\n * @default false\n */\n allowCustomRegistries?: boolean;\n}\n\nexport class NpmLocation implements SnapLocation {\n private readonly meta: NpmMeta;\n\n private validatedManifest?: VirtualFile<SnapManifest>;\n\n private files?: Map<string, VirtualFile>;\n\n constructor(url: URL, opts: DetectSnapLocationOptions = {}) {\n const allowCustomRegistries = opts.allowCustomRegistries ?? false;\n const fetchFunction = opts.fetch ?? globalThis.fetch.bind(globalThis);\n const requestedRange = opts.versionRange ?? DEFAULT_REQUESTED_SNAP_VERSION;\n\n assertStruct(url.toString(), NpmSnapIdStruct, 'Invalid Snap Id: ');\n\n let registry: string | URL;\n if (\n url.host === '' &&\n url.port === '' &&\n url.username === '' &&\n url.password === ''\n ) {\n registry = new URL(DEFAULT_NPM_REGISTRY);\n } else {\n registry = 'https://';\n if (url.username) {\n registry += url.username;\n if (url.password) {\n registry += `:${url.password}`;\n }\n registry += '@';\n }\n registry += url.host;\n registry = new URL(registry);\n assert(\n allowCustomRegistries,\n new TypeError(\n `Custom NPM registries are disabled, tried to use \"${registry.toString()}\".`,\n ),\n );\n }\n\n assert(\n registry.pathname === '/' &&\n registry.search === '' &&\n registry.hash === '',\n );\n\n assert(\n url.pathname !== '' && url.pathname !== '/',\n new TypeError('The package name in NPM location is empty.'),\n );\n let packageName = url.pathname;\n if (packageName.startsWith('/')) {\n packageName = packageName.slice(1);\n }\n\n this.meta = {\n requestedRange,\n registry,\n packageName,\n fetch: fetchFunction,\n };\n }\n\n async manifest(): Promise<VirtualFile<SnapManifest>> {\n if (this.validatedManifest) {\n return this.validatedManifest.clone();\n }\n\n const vfile = await this.fetch('snap.manifest.json');\n const result = parseJson(vfile.toString());\n vfile.result = createSnapManifest(result);\n this.validatedManifest = vfile as VirtualFile<SnapManifest>;\n\n return this.manifest();\n }\n\n async fetch(path: string): Promise<VirtualFile> {\n const relativePath = normalizeRelative(path);\n if (!this.files) {\n await this.#lazyInit();\n assert(this.files !== undefined);\n }\n const vfile = this.files.get(relativePath);\n assert(\n vfile !== undefined,\n new TypeError(`File \"${path}\" not found in package.`),\n );\n return vfile.clone();\n }\n\n get packageName(): string {\n return this.meta.packageName;\n }\n\n get version(): string {\n assert(\n this.meta.version !== undefined,\n 'Tried to access version without first fetching NPM package.',\n );\n return this.meta.version;\n }\n\n get registry(): URL {\n return this.meta.registry;\n }\n\n get versionRange(): SemVerRange {\n return this.meta.requestedRange;\n }\n\n async #lazyInit() {\n assert(this.files === undefined);\n const [tarballResponse, actualVersion] = await fetchNpmTarball(\n this.meta.packageName,\n this.meta.requestedRange,\n this.meta.registry,\n this.meta.fetch,\n );\n this.meta.version = actualVersion;\n\n let canonicalBase = 'npm://';\n if (this.meta.registry.username !== '') {\n canonicalBase += this.meta.registry.username;\n if (this.meta.registry.password !== '') {\n canonicalBase += `:${this.meta.registry.password}`;\n }\n canonicalBase += '@';\n }\n canonicalBase += this.meta.registry.host;\n\n // TODO(ritave): Lazily extract files instead of up-front extracting all of them\n // We would need to replace tar-stream package because it requires immediate consumption of streams.\n await new Promise<void>((resolve, reject) => {\n this.files = new Map();\n pipeline(\n getNodeStream(tarballResponse),\n // The \"gz\" in \"tgz\" stands for \"gzip\". The tarball needs to be decompressed\n // before we can actually grab any files from it.\n // To prevent recursion-based zip bombs, we set a maximum recursion depth of 1.\n createGunzipStream(1),\n createTarballStream(\n `${canonicalBase}/${this.meta.packageName}/`,\n this.files,\n ),\n (error) => {\n error ? reject(error) : resolve();\n },\n );\n });\n }\n}\n\n// Safety limit for tarballs, 250 MB in bytes\nconst TARBALL_SIZE_SAFETY_LIMIT = 262144000;\n\n// Incomplete type\nexport type PartialNpmMetadata = {\n versions: Record<string, { dist: { tarball: string } }>;\n};\n\n/**\n * Fetches the NPM metadata of the specified package from\n * the public npm registry.\n *\n * @param packageName - The name of the package whose metadata to fetch.\n * @param registryUrl - The URL of the npm registry to fetch the metadata from.\n * @param fetchFunction - The fetch function to use. Defaults to the global\n * {@link fetch}. Useful for Node.js compatibility.\n * @returns The NPM metadata object.\n * @throws If fetching the metadata fails.\n */\nexport async function fetchNpmMetadata(\n packageName: string,\n registryUrl: URL | string,\n fetchFunction: typeof fetch,\n): Promise<PartialNpmMetadata> {\n const packageResponse = await fetchFunction(\n new URL(packageName, registryUrl).toString(),\n );\n if (!packageResponse.ok) {\n throw new Error(\n `Failed to fetch NPM registry entry. Status code: ${packageResponse.status}.`,\n );\n }\n const packageMetadata = await packageResponse.json();\n\n if (!isObject(packageMetadata)) {\n throw new Error(\n `Failed to fetch package \"${packageName}\" metadata from npm.`,\n );\n }\n\n return packageMetadata as PartialNpmMetadata;\n}\n\n/**\n * Fetches the tarball (`.tgz` file) of the specified package and version from\n * the public npm registry.\n *\n * @param packageName - The name of the package whose tarball to fetch.\n * @param versionRange - The SemVer range of the package to fetch. The highest\n * version satisfying the range will be fetched.\n * @param registryUrl - The URL of the npm registry to fetch the tarball from.\n * @param fetchFunction - The fetch function to use. Defaults to the global\n * {@link fetch}. Useful for Node.js compatibility.\n * @returns A tuple of the {@link Response} for the package tarball and the\n * actual version of the package.\n * @throws If fetching the tarball fails.\n */\nasync function fetchNpmTarball(\n packageName: string,\n versionRange: SemVerRange,\n registryUrl: URL | string,\n fetchFunction: typeof fetch,\n): Promise<[ReadableStream, SemVerVersion]> {\n const packageMetadata = await fetchNpmMetadata(\n packageName,\n registryUrl,\n fetchFunction,\n );\n\n const versions = Object.keys(packageMetadata?.versions ?? {}).map(\n (version) => {\n assertIsSemVerVersion(version);\n return version;\n },\n );\n\n const targetVersion = getTargetVersion(versions, versionRange);\n\n if (targetVersion === null) {\n throw new Error(\n `Failed to find a matching version in npm metadata for package \"${packageName}\" and requested semver range \"${versionRange}\".`,\n );\n }\n\n const tarballUrlString =\n packageMetadata?.versions?.[targetVersion]?.dist?.tarball;\n\n if (\n !isValidUrl(tarballUrlString) ||\n !tarballUrlString.toString().endsWith('.tgz')\n ) {\n throw new Error(\n `Failed to find valid tarball URL in NPM metadata for package \"${packageName}\".`,\n );\n }\n\n // Override the tarball hostname/protocol with registryUrl hostname/protocol\n const newRegistryUrl = new URL(registryUrl);\n const newTarballUrl = new URL(tarballUrlString);\n newTarballUrl.hostname = newRegistryUrl.hostname;\n newTarballUrl.protocol = newRegistryUrl.protocol;\n\n // Perform a raw fetch because we want the Response object itself.\n const tarballResponse = await fetchFunction(newTarballUrl.toString());\n if (!tarballResponse.ok || !tarballResponse.body) {\n throw new Error(`Failed to fetch tarball for package \"${packageName}\".`);\n }\n // We assume that NPM is a good actor and provides us with a valid `content-length` header.\n const tarballSizeString = tarballResponse.headers.get('content-length');\n assert(tarballSizeString, 'Snap tarball has invalid content-length');\n const tarballSize = parseInt(tarballSizeString, 10);\n assert(\n tarballSize <= TARBALL_SIZE_SAFETY_LIMIT,\n 'Snap tarball exceeds size limit',\n );\n return [tarballResponse.body, targetVersion];\n}\n\n/**\n * The paths of files within npm tarballs appear to always be prefixed with\n * \"package/\".\n */\nconst NPM_TARBALL_PATH_PREFIX = /^package\\//u;\n\n/**\n * Converts a {@link ReadableStream} to a Node.js {@link Readable}\n * stream. Returns the stream directly if it is already a Node.js stream.\n * We can't use the native Web {@link ReadableStream} directly because the\n * other stream libraries we use expect Node.js streams.\n *\n * @param stream - The stream to convert.\n * @returns The given stream as a Node.js Readable stream.\n */\nfunction getNodeStream(stream: ReadableStream): Readable {\n if (typeof stream.getReader !== 'function') {\n return stream as unknown as Readable;\n }\n\n return new ReadableWebToNodeStream(stream);\n}\n\n/**\n * Creates a `tar-stream` that will get the necessary files from an npm Snap\n * package tarball (`.tgz` file).\n *\n * @param canonicalBase - A base URI as specified in {@link https://github.com/MetaMask/SIPs/blob/main/SIPS/sip-8.md SIP-8}. Starting with 'npm:'. Will be used for canonicalPath vfile argument.\n * @param files - An object to write target file contents to.\n * @returns The {@link Writable} tarball extraction stream.\n */\nfunction createTarballStream(\n canonicalBase: string,\n files: Map<string, VirtualFile>,\n): Writable {\n assert(\n canonicalBase.endsWith('/'),\n \"Base needs to end with '/' for relative paths to be added as children instead of siblings.\",\n );\n\n assert(\n canonicalBase.startsWith('npm:'),\n 'Protocol mismatch, expected \"npm:\".',\n );\n // `tar-stream` is pretty old-school, so we create it first and then\n // instrument it by adding event listeners.\n const extractStream = tarExtract();\n\n let totalSize = 0;\n\n // \"entry\" is fired for every discreet entity in the tarball. This includes\n // files and folders.\n extractStream.on('entry', (header, entryStream, next) => {\n const { name: headerName, type: headerType } = header;\n if (headerType === 'file') {\n // The name is a path if the header type is \"file\".\n const path = headerName.replace(NPM_TARBALL_PATH_PREFIX, '');\n return entryStream.pipe(\n concat({ encoding: 'uint8array' }, (data) => {\n try {\n totalSize += data.byteLength;\n // To prevent zip bombs, we set a safety limit for the total size of tarballs.\n assert(\n totalSize < TARBALL_SIZE_SAFETY_LIMIT,\n `Snap tarball exceeds limit of ${TARBALL_SIZE_SAFETY_LIMIT} bytes.`,\n );\n const vfile = new VirtualFile({\n value: data,\n path,\n data: {\n canonicalPath: new URL(path, canonicalBase).toString(),\n },\n });\n // We disallow files having identical paths as it may confuse our checksum calculations.\n assert(\n !files.has(path),\n 'Malformed tarball, multiple files with the same path.',\n );\n files.set(path, vfile);\n return next();\n } catch (error) {\n return extractStream.destroy(error);\n }\n }),\n );\n }\n\n // If we get here, the entry is not a file, and we want to ignore. The entry\n // stream must be drained, or the extractStream will stop reading. This is\n // effectively a no-op for the current entry.\n entryStream.on('end', () => next());\n return entryStream.resume();\n });\n return extractStream;\n}\n"],"names":["createSnapManifest","DEFAULT_REQUESTED_SNAP_VERSION","getTargetVersion","isValidUrl","NpmSnapIdStruct","VirtualFile","normalizeRelative","parseJson","assert","assertIsSemVerVersion","assertStruct","isObject","concat","createGunzipStream","ReadableWebToNodeStream","pipeline","extract","tarExtract","DEFAULT_NPM_REGISTRY","NpmLocation","manifest","validatedManifest","clone","vfile","fetch","result","toString","path","relativePath","files","lazyInit","undefined","get","TypeError","packageName","meta","version","registry","versionRange","requestedRange","constructor","url","opts","allowCustomRegistries","fetchFunction","globalThis","bind","host","port","username","password","URL","pathname","search","hash","startsWith","slice","tarballResponse","actualVersion","fetchNpmTarball","canonicalBase","Promise","resolve","reject","Map","getNodeStream","createTarballStream","error","TARBALL_SIZE_SAFETY_LIMIT","fetchNpmMetadata","registryUrl","packageResponse","ok","Error","status","packageMetadata","json","versions","Object","keys","map","targetVersion","tarballUrlString","dist","tarball","endsWith","newRegistryUrl","newTarballUrl","hostname","protocol","body","tarballSizeString","headers","tarballSize","parseInt","NPM_TARBALL_PATH_PREFIX","stream","getReader","extractStream","totalSize","on","header","entryStream","next","name","headerName","type","headerType","replace","pipe","encoding","data","byteLength","value","canonicalPath","has","set","destroy","resume"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/snaps/location/npm.ts"],"sourcesContent":["import type { SnapManifest } from '@metamask/snaps-utils';\nimport {\n createSnapManifest,\n DEFAULT_REQUESTED_SNAP_VERSION,\n getTargetVersion,\n isValidUrl,\n NpmSnapIdStruct,\n VirtualFile,\n normalizeRelative,\n parseJson,\n} from '@metamask/snaps-utils';\nimport type { SemVerRange, SemVerVersion } from '@metamask/utils';\nimport {\n assert,\n assertIsSemVerVersion,\n assertStruct,\n isObject,\n isValidSemVerVersion,\n} from '@metamask/utils';\nimport concat from 'concat-stream';\nimport getNpmTarballUrl from 'get-npm-tarball-url';\nimport createGunzipStream from 'gunzip-maybe';\nimport { ReadableWebToNodeStream } from 'readable-web-to-node-stream';\nimport { pipeline } from 'stream';\nimport type { Readable, Writable } from 'stream';\nimport { extract as tarExtract } from 'tar-stream';\n\nimport type { DetectSnapLocationOptions, SnapLocation } from './location';\n\nexport const DEFAULT_NPM_REGISTRY = new URL('https://registry.npmjs.org');\n\ninterface NpmMeta {\n registry: URL;\n packageName: string;\n requestedRange: SemVerRange;\n version?: string;\n fetch: typeof fetch;\n}\nexport interface NpmOptions {\n /**\n * @default DEFAULT_REQUESTED_SNAP_VERSION\n */\n versionRange?: SemVerRange;\n /**\n * Whether to allow custom NPM registries outside of {@link DEFAULT_NPM_REGISTRY}.\n *\n * @default false\n */\n allowCustomRegistries?: boolean;\n}\n\nexport class NpmLocation implements SnapLocation {\n private readonly meta: NpmMeta;\n\n private validatedManifest?: VirtualFile<SnapManifest>;\n\n private files?: Map<string, VirtualFile>;\n\n constructor(url: URL, opts: DetectSnapLocationOptions = {}) {\n const allowCustomRegistries = opts.allowCustomRegistries ?? false;\n const fetchFunction = opts.fetch ?? globalThis.fetch.bind(globalThis);\n const requestedRange = opts.versionRange ?? DEFAULT_REQUESTED_SNAP_VERSION;\n\n assertStruct(url.toString(), NpmSnapIdStruct, 'Invalid Snap Id: ');\n\n let registry: string | URL;\n if (\n url.host === '' &&\n url.port === '' &&\n url.username === '' &&\n url.password === ''\n ) {\n registry = DEFAULT_NPM_REGISTRY;\n } else {\n registry = 'https://';\n if (url.username) {\n registry += url.username;\n if (url.password) {\n registry += `:${url.password}`;\n }\n registry += '@';\n }\n registry += url.host;\n registry = new URL(registry);\n assert(\n allowCustomRegistries,\n new TypeError(\n `Custom NPM registries are disabled, tried to use \"${registry.toString()}\".`,\n ),\n );\n }\n\n assert(\n registry.pathname === '/' &&\n registry.search === '' &&\n registry.hash === '',\n );\n\n assert(\n url.pathname !== '' && url.pathname !== '/',\n new TypeError('The package name in NPM location is empty.'),\n );\n let packageName = url.pathname;\n if (packageName.startsWith('/')) {\n packageName = packageName.slice(1);\n }\n\n this.meta = {\n requestedRange,\n registry,\n packageName,\n fetch: fetchFunction,\n };\n }\n\n async manifest(): Promise<VirtualFile<SnapManifest>> {\n if (this.validatedManifest) {\n return this.validatedManifest.clone();\n }\n\n const vfile = await this.fetch('snap.manifest.json');\n const result = parseJson(vfile.toString());\n vfile.result = createSnapManifest(result);\n this.validatedManifest = vfile as VirtualFile<SnapManifest>;\n\n return this.manifest();\n }\n\n async fetch(path: string): Promise<VirtualFile> {\n const relativePath = normalizeRelative(path);\n if (!this.files) {\n await this.#lazyInit();\n assert(this.files !== undefined);\n }\n const vfile = this.files.get(relativePath);\n assert(\n vfile !== undefined,\n new TypeError(`File \"${path}\" not found in package.`),\n );\n return vfile.clone();\n }\n\n get packageName(): string {\n return this.meta.packageName;\n }\n\n get version(): string {\n assert(\n this.meta.version !== undefined,\n 'Tried to access version without first fetching NPM package.',\n );\n return this.meta.version;\n }\n\n get registry(): URL {\n return this.meta.registry;\n }\n\n get versionRange(): SemVerRange {\n return this.meta.requestedRange;\n }\n\n async #lazyInit() {\n assert(this.files === undefined);\n const [tarballResponse, actualVersion] = await fetchNpmTarball(\n this.meta.packageName,\n this.meta.requestedRange,\n this.meta.registry,\n this.meta.fetch,\n );\n this.meta.version = actualVersion;\n\n let canonicalBase = 'npm://';\n if (this.meta.registry.username !== '') {\n canonicalBase += this.meta.registry.username;\n if (this.meta.registry.password !== '') {\n canonicalBase += `:${this.meta.registry.password}`;\n }\n canonicalBase += '@';\n }\n canonicalBase += this.meta.registry.host;\n\n // TODO(ritave): Lazily extract files instead of up-front extracting all of them\n // We would need to replace tar-stream package because it requires immediate consumption of streams.\n await new Promise<void>((resolve, reject) => {\n this.files = new Map();\n pipeline(\n getNodeStream(tarballResponse),\n // The \"gz\" in \"tgz\" stands for \"gzip\". The tarball needs to be decompressed\n // before we can actually grab any files from it.\n // To prevent recursion-based zip bombs, we set a maximum recursion depth of 1.\n createGunzipStream(1),\n createTarballStream(\n `${canonicalBase}/${this.meta.packageName}/`,\n this.files,\n ),\n (error) => {\n error ? reject(error) : resolve();\n },\n );\n });\n }\n}\n\n// Safety limit for tarballs, 250 MB in bytes\nconst TARBALL_SIZE_SAFETY_LIMIT = 262144000;\n\n// Incomplete type\nexport type PartialNpmMetadata = {\n versions: Record<string, { dist: { tarball: string } }>;\n};\n\n/**\n * Fetches the NPM metadata of the specified package from\n * the public npm registry.\n *\n * @param packageName - The name of the package whose metadata to fetch.\n * @param registryUrl - The URL of the npm registry to fetch the metadata from.\n * @param fetchFunction - The fetch function to use. Defaults to the global\n * {@link fetch}. Useful for Node.js compatibility.\n * @returns The NPM metadata object.\n * @throws If fetching the metadata fails.\n */\nexport async function fetchNpmMetadata(\n packageName: string,\n registryUrl: URL | string,\n fetchFunction: typeof fetch,\n): Promise<PartialNpmMetadata> {\n const packageResponse = await fetchFunction(\n new URL(packageName, registryUrl).toString(),\n );\n if (!packageResponse.ok) {\n throw new Error(\n `Failed to fetch NPM registry entry. Status code: ${packageResponse.status}.`,\n );\n }\n const packageMetadata = await packageResponse.json();\n\n if (!isObject(packageMetadata)) {\n throw new Error(\n `Failed to fetch package \"${packageName}\" metadata from npm.`,\n );\n }\n\n return packageMetadata as PartialNpmMetadata;\n}\n\n/**\n * Resolves a version range to a version using the NPM registry.\n *\n * Unless the version range is already a version, then the NPM registry is skipped.\n *\n * @param packageName - The name of the package whose metadata to fetch.\n * @param versionRange - The version range of the package.\n * @param registryUrl - The URL of the npm registry to fetch the metadata from.\n * @param fetchFunction - The fetch function to use. Defaults to the global\n * {@link fetch}. Useful for Node.js compatibility.\n * @returns An object containing the resolved version and a URL for its tarball.\n * @throws If fetching the metadata fails.\n */\nasync function resolveNpmVersion(\n packageName: string,\n versionRange: SemVerRange,\n registryUrl: URL,\n fetchFunction: typeof fetch,\n): Promise<{ tarballURL: string; targetVersion: SemVerVersion }> {\n // If the version range is already a static version we don't need to look for the metadata.\n if (\n registryUrl.toString() === DEFAULT_NPM_REGISTRY.toString() &&\n isValidSemVerVersion(versionRange)\n ) {\n return {\n tarballURL: getNpmTarballUrl(packageName, versionRange),\n targetVersion: versionRange,\n };\n }\n\n const packageMetadata = await fetchNpmMetadata(\n packageName,\n registryUrl,\n fetchFunction,\n );\n\n const versions = Object.keys(packageMetadata?.versions ?? {}).map(\n (version) => {\n assertIsSemVerVersion(version);\n return version;\n },\n );\n\n const targetVersion = getTargetVersion(versions, versionRange);\n\n if (targetVersion === null) {\n throw new Error(\n `Failed to find a matching version in npm metadata for package \"${packageName}\" and requested semver range \"${versionRange}\".`,\n );\n }\n\n const tarballURL = packageMetadata?.versions?.[targetVersion]?.dist?.tarball;\n\n return { tarballURL, targetVersion };\n}\n\n/**\n * Fetches the tarball (`.tgz` file) of the specified package and version from\n * the public npm registry.\n *\n * @param packageName - The name of the package whose tarball to fetch.\n * @param versionRange - The SemVer range of the package to fetch. The highest\n * version satisfying the range will be fetched.\n * @param registryUrl - The URL of the npm registry to fetch the tarball from.\n * @param fetchFunction - The fetch function to use. Defaults to the global\n * {@link fetch}. Useful for Node.js compatibility.\n * @returns A tuple of the {@link Response} for the package tarball and the\n * actual version of the package.\n * @throws If fetching the tarball fails.\n */\nasync function fetchNpmTarball(\n packageName: string,\n versionRange: SemVerRange,\n registryUrl: URL,\n fetchFunction: typeof fetch,\n): Promise<[ReadableStream, SemVerVersion]> {\n const { tarballURL, targetVersion } = await resolveNpmVersion(\n packageName,\n versionRange,\n registryUrl,\n fetchFunction,\n );\n\n if (!isValidUrl(tarballURL) || !tarballURL.toString().endsWith('.tgz')) {\n throw new Error(\n `Failed to find valid tarball URL in NPM metadata for package \"${packageName}\".`,\n );\n }\n\n // Override the tarball hostname/protocol with registryUrl hostname/protocol\n const newRegistryUrl = new URL(registryUrl);\n const newTarballUrl = new URL(tarballURL);\n newTarballUrl.hostname = newRegistryUrl.hostname;\n newTarballUrl.protocol = newRegistryUrl.protocol;\n\n // Perform a raw fetch because we want the Response object itself.\n const tarballResponse = await fetchFunction(newTarballUrl.toString());\n if (!tarballResponse.ok || !tarballResponse.body) {\n throw new Error(`Failed to fetch tarball for package \"${packageName}\".`);\n }\n // We assume that NPM is a good actor and provides us with a valid `content-length` header.\n const tarballSizeString = tarballResponse.headers.get('content-length');\n assert(tarballSizeString, 'Snap tarball has invalid content-length');\n const tarballSize = parseInt(tarballSizeString, 10);\n assert(\n tarballSize <= TARBALL_SIZE_SAFETY_LIMIT,\n 'Snap tarball exceeds size limit',\n );\n return [tarballResponse.body, targetVersion];\n}\n\n/**\n * The paths of files within npm tarballs appear to always be prefixed with\n * \"package/\".\n */\nconst NPM_TARBALL_PATH_PREFIX = /^package\\//u;\n\n/**\n * Converts a {@link ReadableStream} to a Node.js {@link Readable}\n * stream. Returns the stream directly if it is already a Node.js stream.\n * We can't use the native Web {@link ReadableStream} directly because the\n * other stream libraries we use expect Node.js streams.\n *\n * @param stream - The stream to convert.\n * @returns The given stream as a Node.js Readable stream.\n */\nfunction getNodeStream(stream: ReadableStream): Readable {\n if (typeof stream.getReader !== 'function') {\n return stream as unknown as Readable;\n }\n\n return new ReadableWebToNodeStream(stream);\n}\n\n/**\n * Creates a `tar-stream` that will get the necessary files from an npm Snap\n * package tarball (`.tgz` file).\n *\n * @param canonicalBase - A base URI as specified in {@link https://github.com/MetaMask/SIPs/blob/main/SIPS/sip-8.md SIP-8}. Starting with 'npm:'. Will be used for canonicalPath vfile argument.\n * @param files - An object to write target file contents to.\n * @returns The {@link Writable} tarball extraction stream.\n */\nfunction createTarballStream(\n canonicalBase: string,\n files: Map<string, VirtualFile>,\n): Writable {\n assert(\n canonicalBase.endsWith('/'),\n \"Base needs to end with '/' for relative paths to be added as children instead of siblings.\",\n );\n\n assert(\n canonicalBase.startsWith('npm:'),\n 'Protocol mismatch, expected \"npm:\".',\n );\n // `tar-stream` is pretty old-school, so we create it first and then\n // instrument it by adding event listeners.\n const extractStream = tarExtract();\n\n let totalSize = 0;\n\n // \"entry\" is fired for every discreet entity in the tarball. This includes\n // files and folders.\n extractStream.on('entry', (header, entryStream, next) => {\n const { name: headerName, type: headerType } = header;\n if (headerType === 'file') {\n // The name is a path if the header type is \"file\".\n const path = headerName.replace(NPM_TARBALL_PATH_PREFIX, '');\n return entryStream.pipe(\n concat({ encoding: 'uint8array' }, (data) => {\n try {\n totalSize += data.byteLength;\n // To prevent zip bombs, we set a safety limit for the total size of tarballs.\n assert(\n totalSize < TARBALL_SIZE_SAFETY_LIMIT,\n `Snap tarball exceeds limit of ${TARBALL_SIZE_SAFETY_LIMIT} bytes.`,\n );\n const vfile = new VirtualFile({\n value: data,\n path,\n data: {\n canonicalPath: new URL(path, canonicalBase).toString(),\n },\n });\n // We disallow files having identical paths as it may confuse our checksum calculations.\n assert(\n !files.has(path),\n 'Malformed tarball, multiple files with the same path.',\n );\n files.set(path, vfile);\n return next();\n } catch (error) {\n return extractStream.destroy(error);\n }\n }),\n );\n }\n\n // If we get here, the entry is not a file, and we want to ignore. The entry\n // stream must be drained, or the extractStream will stop reading. This is\n // effectively a no-op for the current entry.\n entryStream.on('end', () => next());\n return entryStream.resume();\n });\n return extractStream;\n}\n"],"names":["createSnapManifest","DEFAULT_REQUESTED_SNAP_VERSION","getTargetVersion","isValidUrl","NpmSnapIdStruct","VirtualFile","normalizeRelative","parseJson","assert","assertIsSemVerVersion","assertStruct","isObject","isValidSemVerVersion","concat","getNpmTarballUrl","createGunzipStream","ReadableWebToNodeStream","pipeline","extract","tarExtract","DEFAULT_NPM_REGISTRY","URL","NpmLocation","manifest","validatedManifest","clone","vfile","fetch","result","toString","path","relativePath","files","lazyInit","undefined","get","TypeError","packageName","meta","version","registry","versionRange","requestedRange","constructor","url","opts","allowCustomRegistries","fetchFunction","globalThis","bind","host","port","username","password","pathname","search","hash","startsWith","slice","tarballResponse","actualVersion","fetchNpmTarball","canonicalBase","Promise","resolve","reject","Map","getNodeStream","createTarballStream","error","TARBALL_SIZE_SAFETY_LIMIT","fetchNpmMetadata","registryUrl","packageResponse","ok","Error","status","packageMetadata","json","resolveNpmVersion","tarballURL","targetVersion","versions","Object","keys","map","dist","tarball","endsWith","newRegistryUrl","newTarballUrl","hostname","protocol","body","tarballSizeString","headers","tarballSize","parseInt","NPM_TARBALL_PATH_PREFIX","stream","getReader","extractStream","totalSize","on","header","entryStream","next","name","headerName","type","headerType","replace","pipe","encoding","data","byteLength","value","canonicalPath","has","set","destroy","resume"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { caveatMappers, restrictedMethodPermissionBuilders, selectHooks } from '@metamask/rpc-methods';
1
+ import { caveatMappers, restrictedMethodPermissionBuilders, selectHooks } from '@metamask/snaps-rpc-methods';
2
2
  import { hasProperty } from '@metamask/utils';
3
3
  import { endowmentCaveatMappers, endowmentPermissionBuilders } from './endowments';
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/snaps/permissions.ts"],"sourcesContent":["import type {\n PermissionConstraint,\n PermissionSpecificationConstraint,\n} from '@metamask/permission-controller';\nimport {\n caveatMappers,\n restrictedMethodPermissionBuilders,\n selectHooks,\n} from '@metamask/rpc-methods';\nimport type { SnapPermissions } from '@metamask/snaps-utils';\nimport { hasProperty } from '@metamask/utils';\n\nimport {\n endowmentCaveatMappers,\n endowmentPermissionBuilders,\n} from './endowments';\n\n/**\n * Map initial permissions as defined in a Snap manifest to something that can\n * be processed by the PermissionsController. Each caveat mapping function\n * should return a valid permission caveat value.\n *\n * This function does not validate the caveat values, since that is done by\n * the PermissionsController itself, upon requesting the permissions.\n *\n * @param initialPermissions - The initial permissions to process.\n * @returns The processed permissions.\n */\nexport function processSnapPermissions(\n initialPermissions: SnapPermissions,\n): Record<string, Pick<PermissionConstraint, 'caveats'>> {\n return Object.fromEntries(\n Object.entries(initialPermissions).map(([initialPermission, value]) => {\n if (hasProperty(caveatMappers, initialPermission)) {\n return [initialPermission, caveatMappers[initialPermission](value)];\n } else if (hasProperty(endowmentCaveatMappers, initialPermission)) {\n return [\n initialPermission,\n endowmentCaveatMappers[initialPermission](value),\n ];\n }\n\n // If we have no mapping, this may be a non-snap permission, return as-is\n return [\n initialPermission,\n value as Pick<PermissionConstraint, 'caveats'>,\n ];\n }),\n );\n}\n\nexport const buildSnapEndowmentSpecifications = (\n excludedEndowments: string[],\n) =>\n Object.values(endowmentPermissionBuilders).reduce<\n Record<string, PermissionSpecificationConstraint>\n >((allSpecifications, { targetName, specificationBuilder }) => {\n if (!excludedEndowments.includes(targetName)) {\n allSpecifications[targetName] = specificationBuilder({});\n }\n return allSpecifications;\n }, {});\n\nexport const buildSnapRestrictedMethodSpecifications = (\n excludedPermissions: string[],\n hooks: Record<string, unknown>,\n) =>\n Object.values(restrictedMethodPermissionBuilders).reduce<\n Record<string, PermissionSpecificationConstraint>\n >((specifications, { targetName, specificationBuilder, methodHooks }) => {\n if (!excludedPermissions.includes(targetName)) {\n specifications[targetName] = specificationBuilder({\n // @ts-expect-error The selectHooks type is wonky\n methodHooks: selectHooks<typeof hooks, keyof typeof methodHooks>(\n hooks,\n methodHooks,\n ) as Pick<typeof hooks, keyof typeof methodHooks>,\n });\n }\n return specifications;\n }, {});\n"],"names":["caveatMappers","restrictedMethodPermissionBuilders","selectHooks","hasProperty","endowmentCaveatMappers","endowmentPermissionBuilders","processSnapPermissions","initialPermissions","Object","fromEntries","entries","map","initialPermission","value","buildSnapEndowmentSpecifications","excludedEndowments","values","reduce","allSpecifications","targetName","specificationBuilder","includes","buildSnapRestrictedMethodSpecifications","excludedPermissions","hooks","specifications","methodHooks"],"mappings":"AAIA,SACEA,aAAa,EACbC,kCAAkC,EAClCC,WAAW,QACN,wBAAwB;AAE/B,SAASC,WAAW,QAAQ,kBAAkB;AAE9C,SACEC,sBAAsB,EACtBC,2BAA2B,QACtB,eAAe;AAEtB;;;;;;;;;;CAUC,GACD,OAAO,SAASC,uBACdC,kBAAmC;IAEnC,OAAOC,OAAOC,WAAW,CACvBD,OAAOE,OAAO,CAACH,oBAAoBI,GAAG,CAAC,CAAC,CAACC,mBAAmBC,MAAM;QAChE,IAAIV,YAAYH,eAAeY,oBAAoB;YACjD,OAAO;gBAACA;gBAAmBZ,aAAa,CAACY,kBAAkB,CAACC;aAAO;QACrE,OAAO,IAAIV,YAAYC,wBAAwBQ,oBAAoB;YACjE,OAAO;gBACLA;gBACAR,sBAAsB,CAACQ,kBAAkB,CAACC;aAC3C;QACH;QAEA,yEAAyE;QACzE,OAAO;YACLD;YACAC;SACD;IACH;AAEJ;AAEA,OAAO,MAAMC,mCAAmC,CAC9CC,qBAEAP,OAAOQ,MAAM,CAACX,6BAA6BY,MAAM,CAE/C,CAACC,mBAAmB,EAAEC,UAAU,EAAEC,oBAAoB,EAAE;QACxD,IAAI,CAACL,mBAAmBM,QAAQ,CAACF,aAAa;YAC5CD,iBAAiB,CAACC,WAAW,GAAGC,qBAAqB,CAAC;QACxD;QACA,OAAOF;IACT,GAAG,CAAC,GAAG;AAET,OAAO,MAAMI,0CAA0C,CACrDC,qBACAC,QAEAhB,OAAOQ,MAAM,CAACf,oCAAoCgB,MAAM,CAEtD,CAACQ,gBAAgB,EAAEN,UAAU,EAAEC,oBAAoB,EAAEM,WAAW,EAAE;QAClE,IAAI,CAACH,oBAAoBF,QAAQ,CAACF,aAAa;YAC7CM,cAAc,CAACN,WAAW,GAAGC,qBAAqB;gBAChD,iDAAiD;gBACjDM,aAAaxB,YACXsB,OACAE;YAEJ;QACF;QACA,OAAOD;IACT,GAAG,CAAC,GAAG"}
1
+ {"version":3,"sources":["../../../src/snaps/permissions.ts"],"sourcesContent":["import type {\n PermissionConstraint,\n PermissionSpecificationConstraint,\n} from '@metamask/permission-controller';\nimport {\n caveatMappers,\n restrictedMethodPermissionBuilders,\n selectHooks,\n} from '@metamask/snaps-rpc-methods';\nimport type { SnapPermissions } from '@metamask/snaps-utils';\nimport { hasProperty } from '@metamask/utils';\n\nimport {\n endowmentCaveatMappers,\n endowmentPermissionBuilders,\n} from './endowments';\n\n/**\n * Map initial permissions as defined in a Snap manifest to something that can\n * be processed by the PermissionsController. Each caveat mapping function\n * should return a valid permission caveat value.\n *\n * This function does not validate the caveat values, since that is done by\n * the PermissionsController itself, upon requesting the permissions.\n *\n * @param initialPermissions - The initial permissions to process.\n * @returns The processed permissions.\n */\nexport function processSnapPermissions(\n initialPermissions: SnapPermissions,\n): Record<string, Pick<PermissionConstraint, 'caveats'>> {\n return Object.fromEntries(\n Object.entries(initialPermissions).map(([initialPermission, value]) => {\n if (hasProperty(caveatMappers, initialPermission)) {\n return [initialPermission, caveatMappers[initialPermission](value)];\n } else if (hasProperty(endowmentCaveatMappers, initialPermission)) {\n return [\n initialPermission,\n endowmentCaveatMappers[initialPermission](value),\n ];\n }\n\n // If we have no mapping, this may be a non-snap permission, return as-is\n return [\n initialPermission,\n value as Pick<PermissionConstraint, 'caveats'>,\n ];\n }),\n );\n}\n\nexport const buildSnapEndowmentSpecifications = (\n excludedEndowments: string[],\n) =>\n Object.values(endowmentPermissionBuilders).reduce<\n Record<string, PermissionSpecificationConstraint>\n >((allSpecifications, { targetName, specificationBuilder }) => {\n if (!excludedEndowments.includes(targetName)) {\n allSpecifications[targetName] = specificationBuilder({});\n }\n return allSpecifications;\n }, {});\n\nexport const buildSnapRestrictedMethodSpecifications = (\n excludedPermissions: string[],\n hooks: Record<string, unknown>,\n) =>\n Object.values(restrictedMethodPermissionBuilders).reduce<\n Record<string, PermissionSpecificationConstraint>\n >((specifications, { targetName, specificationBuilder, methodHooks }) => {\n if (!excludedPermissions.includes(targetName)) {\n specifications[targetName] = specificationBuilder({\n // @ts-expect-error The selectHooks type is wonky\n methodHooks: selectHooks<typeof hooks, keyof typeof methodHooks>(\n hooks,\n methodHooks,\n ) as Pick<typeof hooks, keyof typeof methodHooks>,\n });\n }\n return specifications;\n }, {});\n"],"names":["caveatMappers","restrictedMethodPermissionBuilders","selectHooks","hasProperty","endowmentCaveatMappers","endowmentPermissionBuilders","processSnapPermissions","initialPermissions","Object","fromEntries","entries","map","initialPermission","value","buildSnapEndowmentSpecifications","excludedEndowments","values","reduce","allSpecifications","targetName","specificationBuilder","includes","buildSnapRestrictedMethodSpecifications","excludedPermissions","hooks","specifications","methodHooks"],"mappings":"AAIA,SACEA,aAAa,EACbC,kCAAkC,EAClCC,WAAW,QACN,8BAA8B;AAErC,SAASC,WAAW,QAAQ,kBAAkB;AAE9C,SACEC,sBAAsB,EACtBC,2BAA2B,QACtB,eAAe;AAEtB;;;;;;;;;;CAUC,GACD,OAAO,SAASC,uBACdC,kBAAmC;IAEnC,OAAOC,OAAOC,WAAW,CACvBD,OAAOE,OAAO,CAACH,oBAAoBI,GAAG,CAAC,CAAC,CAACC,mBAAmBC,MAAM;QAChE,IAAIV,YAAYH,eAAeY,oBAAoB;YACjD,OAAO;gBAACA;gBAAmBZ,aAAa,CAACY,kBAAkB,CAACC;aAAO;QACrE,OAAO,IAAIV,YAAYC,wBAAwBQ,oBAAoB;YACjE,OAAO;gBACLA;gBACAR,sBAAsB,CAACQ,kBAAkB,CAACC;aAC3C;QACH;QAEA,yEAAyE;QACzE,OAAO;YACLD;YACAC;SACD;IACH;AAEJ;AAEA,OAAO,MAAMC,mCAAmC,CAC9CC,qBAEAP,OAAOQ,MAAM,CAACX,6BAA6BY,MAAM,CAE/C,CAACC,mBAAmB,EAAEC,UAAU,EAAEC,oBAAoB,EAAE;QACxD,IAAI,CAACL,mBAAmBM,QAAQ,CAACF,aAAa;YAC5CD,iBAAiB,CAACC,WAAW,GAAGC,qBAAqB,CAAC;QACxD;QACA,OAAOF;IACT,GAAG,CAAC,GAAG;AAET,OAAO,MAAMI,0CAA0C,CACrDC,qBACAC,QAEAhB,OAAOQ,MAAM,CAACf,oCAAoCgB,MAAM,CAEtD,CAACQ,gBAAgB,EAAEN,UAAU,EAAEC,oBAAoB,EAAEM,WAAW,EAAE;QAClE,IAAI,CAACH,oBAAoBF,QAAQ,CAACF,aAAa;YAC7CM,cAAc,CAACN,WAAW,GAAGC,qBAAqB;gBAChD,iDAAiD;gBACjDM,aAAaxB,YACXsB,OACAE;YAEJ;QACF;QACA,OAAOD;IACT,GAAG,CAAC,GAAG"}
@@ -50,7 +50,8 @@ function _class_private_method_init(obj, privateSet) {
50
50
  }
51
51
  import { BaseControllerV2 as BaseController } from '@metamask/base-controller';
52
52
  import { verify } from '@metamask/snaps-registry';
53
- import { assert, Duration, inMilliseconds, satisfiesVersionRange } from '@metamask/utils';
53
+ import { getTargetVersion } from '@metamask/snaps-utils';
54
+ import { assert, assertIsSemVerRange, Duration, inMilliseconds, satisfiesVersionRange } from '@metamask/utils';
54
55
  import { SnapsRegistryStatus } from './registry';
55
56
  // TODO: Replace with a Codefi URL
56
57
  const SNAP_REGISTRY_URL = 'https://cdn.jsdelivr.net/gh/MetaMask/snaps-registry@gh-pages/latest/registry.json';
@@ -60,7 +61,7 @@ const defaultState = {
60
61
  database: null,
61
62
  lastUpdated: null
62
63
  };
63
- var _url = /*#__PURE__*/ new WeakMap(), _publicKey = /*#__PURE__*/ new WeakMap(), _fetchFunction = /*#__PURE__*/ new WeakMap(), _recentFetchThreshold = /*#__PURE__*/ new WeakMap(), _refetchOnAllowlistMiss = /*#__PURE__*/ new WeakMap(), _failOnUnavailableRegistry = /*#__PURE__*/ new WeakMap(), _currentUpdate = /*#__PURE__*/ new WeakMap(), _wasRecentlyFetched = /*#__PURE__*/ new WeakSet(), _triggerUpdate = /*#__PURE__*/ new WeakSet(), _update = /*#__PURE__*/ new WeakSet(), _getDatabase = /*#__PURE__*/ new WeakSet(), _getSingle = /*#__PURE__*/ new WeakSet(), _get = /*#__PURE__*/ new WeakSet(), _getMetadata = /*#__PURE__*/ new WeakSet(), _verifySignature = /*#__PURE__*/ new WeakSet(), _safeFetch = /*#__PURE__*/ new WeakSet();
64
+ var _url = /*#__PURE__*/ new WeakMap(), _publicKey = /*#__PURE__*/ new WeakMap(), _fetchFunction = /*#__PURE__*/ new WeakMap(), _recentFetchThreshold = /*#__PURE__*/ new WeakMap(), _refetchOnAllowlistMiss = /*#__PURE__*/ new WeakMap(), _failOnUnavailableRegistry = /*#__PURE__*/ new WeakMap(), _currentUpdate = /*#__PURE__*/ new WeakMap(), _wasRecentlyFetched = /*#__PURE__*/ new WeakSet(), _triggerUpdate = /*#__PURE__*/ new WeakSet(), _update = /*#__PURE__*/ new WeakSet(), _getDatabase = /*#__PURE__*/ new WeakSet(), _getSingle = /*#__PURE__*/ new WeakSet(), _get = /*#__PURE__*/ new WeakSet(), _resolveVersion = /*#__PURE__*/ new WeakSet(), _getMetadata = /*#__PURE__*/ new WeakSet(), _verifySignature = /*#__PURE__*/ new WeakSet(), _safeFetch = /*#__PURE__*/ new WeakSet();
64
65
  export class JsonSnapsRegistry extends BaseController {
65
66
  constructor({ messenger, state, url = {
66
67
  registry: SNAP_REGISTRY_URL,
@@ -99,6 +100,15 @@ export class JsonSnapsRegistry extends BaseController {
99
100
  _class_private_method_init(this, _getSingle);
100
101
  _class_private_method_init(this, _get);
101
102
  /**
103
+ * Find an allowlisted version within a specified version range.
104
+ *
105
+ * @param snapId - The ID of the snap we are trying to resolve a version for.
106
+ * @param versionRange - The version range.
107
+ * @param refetch - An optional flag used to determine if we are refetching the registry.
108
+ * @returns An allowlisted version within the specified version range.
109
+ * @throws If an allowlisted version does not exist within the version range.
110
+ */ _class_private_method_init(this, _resolveVersion);
111
+ /**
102
112
  * Get metadata for the given snap ID.
103
113
  *
104
114
  * @param snapId - The ID of the snap to get metadata for.
@@ -157,6 +167,7 @@ export class JsonSnapsRegistry extends BaseController {
157
167
  _class_private_field_set(this, _currentUpdate, null);
158
168
  this.messagingSystem.registerActionHandler('SnapsRegistry:get', async (...args)=>_class_private_method_get(this, _get, get).call(this, ...args));
159
169
  this.messagingSystem.registerActionHandler('SnapsRegistry:getMetadata', async (...args)=>_class_private_method_get(this, _getMetadata, getMetadata).call(this, ...args));
170
+ this.messagingSystem.registerActionHandler('SnapsRegistry:resolveVersion', async (...args)=>_class_private_method_get(this, _resolveVersion, resolveVersion).call(this, ...args));
160
171
  this.messagingSystem.registerActionHandler('SnapsRegistry:update', async ()=>_class_private_method_get(this, _triggerUpdate, triggerUpdate).call(this));
161
172
  }
162
173
  }
@@ -243,6 +254,24 @@ async function get(snaps) {
243
254
  return acc;
244
255
  }, Promise.resolve({}));
245
256
  }
257
+ async function resolveVersion(snapId, versionRange, refetch = false) {
258
+ const database = await _class_private_method_get(this, _getDatabase, getDatabase).call(this);
259
+ const versions = database?.verifiedSnaps[snapId]?.versions ?? null;
260
+ if (!versions && _class_private_field_get(this, _refetchOnAllowlistMiss) && !refetch) {
261
+ await _class_private_method_get(this, _triggerUpdate, triggerUpdate).call(this);
262
+ return _class_private_method_get(this, _resolveVersion, resolveVersion).call(this, snapId, versionRange, true);
263
+ }
264
+ assert(versions, `Cannot install snap "${snapId}": The snap is not on the allowlist.`);
265
+ const targetVersion = getTargetVersion(Object.keys(versions), versionRange);
266
+ if (!targetVersion && _class_private_field_get(this, _refetchOnAllowlistMiss) && !refetch) {
267
+ await _class_private_method_get(this, _triggerUpdate, triggerUpdate).call(this);
268
+ return _class_private_method_get(this, _resolveVersion, resolveVersion).call(this, snapId, versionRange, true);
269
+ }
270
+ assert(targetVersion, `Cannot install version "${versionRange}" of snap "${snapId}": No matching versions of the snap are on the allowlist.`);
271
+ // A semver version is technically also a valid semver range.
272
+ assertIsSemVerRange(targetVersion);
273
+ return targetVersion;
274
+ }
246
275
  async function getMetadata(snapId) {
247
276
  const database = await _class_private_method_get(this, _getDatabase, getDatabase).call(this);
248
277
  return database?.verifiedSnaps[snapId]?.metadata ?? null;
@@ -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 type { SnapId } from '@metamask/snaps-utils';\nimport type { Hex } from '@metamask/utils';\nimport {\n assert,\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 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 = GetResult | GetMetadata | Update;\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 this.messagingSystem.registerActionHandler(\n 'SnapsRegistry:getMetadata',\n async (...args) => this.#getMetadata(...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 * 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":["BaseControllerV2","BaseController","verify","assert","Duration","inMilliseconds","satisfiesVersionRange","SnapsRegistryStatus","SNAP_REGISTRY_URL","SNAP_REGISTRY_SIGNATURE_URL","controllerName","defaultState","database","lastUpdated","JsonSnapsRegistry","constructor","messenger","state","url","registry","signature","publicKey","fetchFunction","globalThis","fetch","bind","recentFetchThreshold","Minute","failOnUnavailableRegistry","refetchOnAllowlistMiss","metadata","persist","anonymous","name","currentUpdate","messagingSystem","registerActionHandler","args","get","getMetadata","triggerUpdate","Date","now","update","wasRecentlyFetched","safeFetch","verifySignature","JSON","parse","Error","snapId","snapInfo","refetch","getDatabase","blockedEntry","blockedSnaps","find","blocked","id","version","versionRange","checksum","status","Blocked","reason","verified","verifiedSnaps","versions","Verified","getSingle","Unverified","snaps","Object","entries","reduce","previousPromise","result","acc","Promise","resolve","valid","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, 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(\n versions,\n `Cannot install snap \"${snapId}\": The snap is not on the allowlist.`,\n );\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 `Cannot install version \"${versionRange}\" of snap \"${snapId}\": 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":["BaseControllerV2","BaseController","verify","getTargetVersion","assert","assertIsSemVerRange","Duration","inMilliseconds","satisfiesVersionRange","SnapsRegistryStatus","SNAP_REGISTRY_URL","SNAP_REGISTRY_SIGNATURE_URL","controllerName","defaultState","database","lastUpdated","JsonSnapsRegistry","constructor","messenger","state","url","registry","signature","publicKey","fetchFunction","globalThis","fetch","bind","recentFetchThreshold","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","version","versionRange","checksum","status","Blocked","reason","verified","verifiedSnaps","versions","Verified","getSingle","Unverified","snaps","Object","entries","reduce","previousPromise","result","acc","Promise","resolve","targetVersion","keys","valid","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, ValidatedSnapId } from '@metamask/snaps-utils';\nimport type { SemVerVersion } from '@metamask/utils';\n\nexport type SnapsRegistryInfo = { version: SemVerVersion; checksum: string };\nexport type SnapsRegistryRequest = Record<SnapId, SnapsRegistryInfo>;\nexport type SnapsRegistryMetadata =\n SnapsRegistryDatabase['verifiedSnaps'][ValidatedSnapId]['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<ValidatedSnapId, SnapsRegistryResult>>;\n\n update(): Promise<void>;\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":"WAaO;UAAKA,mBAAmB;IAAnBA,oBAAAA,oBACVC,gBAAa,KAAbA;IADUD,oBAAAA,oBAEVE,aAAU,KAAVA;IAFUF,oBAAAA,oBAGVG,cAAW,KAAXA;GAHUH,wBAAAA"}
1
+ {"version":3,"sources":["../../../../src/snaps/registry/registry.ts"],"sourcesContent":["import type {\n BlockReason,\n SnapsRegistryDatabase,\n} from '@metamask/snaps-registry';\nimport type { SnapId, ValidatedSnapId } from '@metamask/snaps-utils';\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'][ValidatedSnapId]['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<ValidatedSnapId, 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":"WAaO;UAAKA,mBAAmB;IAAnBA,oBAAAA,oBACVC,gBAAa,KAAbA;IADUD,oBAAAA,oBAEVE,aAAU,KAAVA;IAFUF,oBAAAA,oBAGVG,cAAW,KAAXA;GAHUH,wBAAAA"}
@@ -88,7 +88,7 @@ export declare class CronjobController extends BaseController<typeof controllerN
88
88
  /**
89
89
  * Runs every 24 hours to check if new jobs need to be scheduled.
90
90
  *
91
- * This is necesary for longer running jobs that execute with more than 24 hours between them.
91
+ * This is necessary for longer running jobs that execute with more than 24 hours between them.
92
92
  */
93
93
  dailyCheckIn(): Promise<void>;
94
94
  /**
@@ -1,8 +1,8 @@
1
1
  /// <reference types="node" />
2
+ import { JsonRpcEngine } from '@metamask/json-rpc-engine';
2
3
  import ObjectMultiplex from '@metamask/object-multiplex';
3
4
  import type { BasePostMessageStream } from '@metamask/post-message-stream';
4
5
  import type { SnapRpcHookArgs } from '@metamask/snaps-utils';
5
- import { JsonRpcEngine } from 'json-rpc-engine';
6
6
  import type { Duplex } from 'stream';
7
7
  import type { ExecutionService, ExecutionServiceMessenger, SnapExecutionData } from './ExecutionService';
8
8
  export declare type SetupSnapProvider = (snapId: string, stream: Duplex) => void;
@@ -4,14 +4,14 @@ import { BaseControllerV2 as BaseController } from '@metamask/base-controller';
4
4
  import type { GetEndowments, GetPermissions, GetSubjectMetadata, GetSubjects, GrantPermissions, HasPermission, HasPermissions, RevokeAllPermissions, RevokePermissionForAllSubjects, RevokePermissions, UpdateCaveat } from '@metamask/permission-controller';
5
5
  import type { BlockReason } from '@metamask/snaps-registry';
6
6
  import type { InstallSnapsResult, PersistedSnap, RequestedSnapPermissions, Snap, SnapId, SnapRpcHook, SnapRpcHookArgs, StatusContext, StatusEvents, StatusStates, TruncatedSnap, ValidatedSnapId } from '@metamask/snaps-utils';
7
- import { SnapStatusEvents } from '@metamask/snaps-utils';
7
+ import { AuxiliaryFileEncoding, SnapStatusEvents } from '@metamask/snaps-utils';
8
8
  import type { Json, NonEmptyArray } from '@metamask/utils';
9
9
  import type { StateMachine } from '@xstate/fsm';
10
10
  import type { Patch } from 'immer';
11
11
  import type { ExecuteSnapAction, ExecutionServiceEvents, HandleRpcRequestAction, SnapErrorJson, TerminateAllSnapsAction, TerminateSnapAction } from '../services';
12
12
  import type { SnapLocation } from './location';
13
13
  import { detectSnapLocation } from './location';
14
- import type { GetMetadata, GetResult, SnapsRegistryMetadata, Update } from './registry';
14
+ import type { GetMetadata, GetResult, ResolveVersion, SnapsRegistryMetadata, Update } from './registry';
15
15
  import { Timer } from './Timer';
16
16
  export declare const controllerName = "SnapController";
17
17
  export declare const SNAP_APPROVAL_INSTALL = "wallet_installSnap";
@@ -70,11 +70,6 @@ declare type StoredSnaps = Record<ValidatedSnapId, Snap>;
70
70
  export declare type SnapControllerState = {
71
71
  snaps: StoredSnaps;
72
72
  snapStates: Record<ValidatedSnapId, string | null>;
73
- snapErrors: {
74
- [internalID: string]: SnapError & {
75
- internalID: string;
76
- };
77
- };
78
73
  };
79
74
  export declare type PersistedSnapControllerState = SnapControllerState & {
80
75
  snaps: Record<ValidatedSnapId, PersistedSnap>;
@@ -161,10 +156,6 @@ export declare type InstallSnaps = {
161
156
  type: `${typeof controllerName}:install`;
162
157
  handler: SnapController['installSnaps'];
163
158
  };
164
- export declare type RemoveSnapError = {
165
- type: `${typeof controllerName}:removeSnapError`;
166
- handler: SnapController['removeSnapError'];
167
- };
168
159
  export declare type GetRegistryMetadata = {
169
160
  type: `${typeof controllerName}:getRegistryMetadata`;
170
161
  handler: SnapController['getRegistryMetadata'];
@@ -177,7 +168,11 @@ export declare type RevokeDynamicPermissions = {
177
168
  type: `${typeof controllerName}:revokeDynamicPermissions`;
178
169
  handler: SnapController['revokeDynamicSnapPermissions'];
179
170
  };
180
- export declare type SnapControllerActions = ClearSnapState | GetSnap | GetSnapState | HandleSnapRequest | HasSnap | UpdateBlockedSnaps | UpdateSnapState | EnableSnap | DisableSnap | RemoveSnap | GetPermittedSnaps | InstallSnaps | RemoveSnapError | GetAllSnaps | IncrementActiveReferences | DecrementActiveReferences | GetRegistryMetadata | DisconnectOrigin | RevokeDynamicPermissions;
171
+ export declare type GetSnapFile = {
172
+ type: `${typeof controllerName}:getFile`;
173
+ handler: SnapController['getSnapFile'];
174
+ };
175
+ export declare type SnapControllerActions = ClearSnapState | GetSnap | GetSnapState | HandleSnapRequest | HasSnap | UpdateBlockedSnaps | UpdateSnapState | EnableSnap | DisableSnap | RemoveSnap | GetPermittedSnaps | InstallSnaps | GetAllSnaps | IncrementActiveReferences | DecrementActiveReferences | GetRegistryMetadata | DisconnectOrigin | RevokeDynamicPermissions | GetSnapFile;
181
176
  export declare type SnapStateChange = {
182
177
  type: `${typeof controllerName}:stateChange`;
183
178
  payload: [SnapControllerState, Patch[]];
@@ -202,7 +197,7 @@ export declare type SnapBlocked = {
202
197
  */
203
198
  export declare type SnapInstalled = {
204
199
  type: `${typeof controllerName}:snapInstalled`;
205
- payload: [snap: TruncatedSnap];
200
+ payload: [snap: TruncatedSnap, origin: string];
206
201
  };
207
202
  /**
208
203
  * Emitted when a snap that has previously been fully installed, is uninstalled.
@@ -231,7 +226,7 @@ export declare type SnapUnblocked = {
231
226
  */
232
227
  export declare type SnapUpdated = {
233
228
  type: `${typeof controllerName}:snapUpdated`;
234
- payload: [snap: TruncatedSnap, oldVersion: string];
229
+ payload: [snap: TruncatedSnap, oldVersion: string, origin: string];
235
230
  };
236
231
  /**
237
232
  * Emitted when a snap is rolled back.
@@ -264,7 +259,7 @@ export declare type SnapDisabled = {
264
259
  payload: [snap: TruncatedSnap];
265
260
  };
266
261
  export declare type SnapControllerEvents = SnapAdded | SnapBlocked | SnapInstalled | SnapUninstalled | SnapRemoved | SnapStateChange | SnapUnblocked | SnapUpdated | SnapRolledback | SnapTerminated | SnapEnabled | SnapDisabled;
267
- export declare type AllowedActions = GetEndowments | GetPermissions | GetSubjects | GetSubjectMetadata | HasPermission | HasPermissions | RevokePermissions | RevokeAllPermissions | RevokePermissionForAllSubjects | GrantPermissions | AddApprovalRequest | HandleRpcRequestAction | ExecuteSnapAction | TerminateAllSnapsAction | TerminateSnapAction | UpdateCaveat | UpdateRequestState | GetResult | GetMetadata | Update;
262
+ export declare type AllowedActions = GetEndowments | GetPermissions | GetSubjects | GetSubjectMetadata | HasPermission | HasPermissions | RevokePermissions | RevokeAllPermissions | RevokePermissionForAllSubjects | GrantPermissions | AddApprovalRequest | HandleRpcRequestAction | ExecuteSnapAction | TerminateAllSnapsAction | TerminateSnapAction | UpdateCaveat | UpdateRequestState | GetResult | GetMetadata | Update | ResolveVersion;
268
263
  export declare type AllowedEvents = ExecutionServiceEvents | SnapInstalled | SnapUpdated;
269
264
  declare type SnapControllerMessenger = RestrictedControllerMessenger<typeof controllerName, SnapControllerActions | AllowedActions, SnapControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
270
265
  declare type FeatureFlags = {
@@ -342,7 +337,7 @@ export declare class SnapController extends BaseController<string, SnapControlle
342
337
  * for more information.
343
338
  */
344
339
  updateBlockedSnaps(): Promise<void>;
345
- _onUnhandledSnapError(snapId: SnapId, error: SnapErrorJson): void;
340
+ _onUnhandledSnapError(snapId: SnapId, _error: SnapErrorJson): void;
346
341
  _onOutboundRequest(snapId: SnapId): void;
347
342
  _onOutboundResponse(snapId: SnapId): void;
348
343
  /**
@@ -441,22 +436,6 @@ export declare class SnapController extends BaseController<string, SnapControlle
441
436
  * @param snapId - The id of the Snap whose state should be cleared.
442
437
  */
443
438
  clearSnapState(snapId: ValidatedSnapId): void;
444
- /**
445
- * Adds error from a snap to the SnapController state.
446
- *
447
- * @param snapError - The error to store on the SnapController.
448
- */
449
- addSnapError(snapError: SnapError): void;
450
- /**
451
- * Removes an error by internalID from the SnapControllers state.
452
- *
453
- * @param internalID - The internal error ID to remove on the SnapController.
454
- */
455
- removeSnapError(internalID: string): void;
456
- /**
457
- * Clears all errors from the SnapControllers state.
458
- */
459
- clearSnapErrors(): void;
460
439
  /**
461
440
  * Gets the own state of the snap with the given id.
462
441
  * This is distinct from the state MetaMask uses to manage snaps.
@@ -466,6 +445,15 @@ export declare class SnapController extends BaseController<string, SnapControlle
466
445
  * @throws If the snap state decryption fails.
467
446
  */
468
447
  getSnapState(snapId: ValidatedSnapId): Promise<Json>;
448
+ /**
449
+ * Gets a static auxiliary snap file in a chosen file encoding.
450
+ *
451
+ * @param snapId - The id of the Snap whose state to get.
452
+ * @param path - The path to the requested file.
453
+ * @param encoding - An optional requested file encoding.
454
+ * @returns The file requested in the chosen file encoding or null if the file is not found.
455
+ */
456
+ getSnapFile(snapId: ValidatedSnapId, path: string, encoding?: AuxiliaryFileEncoding): string | null;
469
457
  /**
470
458
  * Completely clear the controller's state: delete all associated data,
471
459
  * handlers, event listeners, and permissions; tear down all snap providers.
@@ -6,5 +6,6 @@ export declare enum SnapEndowments {
6
6
  Rpc = "endowment:rpc",
7
7
  WebAssemblyAccess = "endowment:webassembly",
8
8
  NameLookup = "endowment:name-lookup",
9
- LifecycleHooks = "endowment:lifecycle-hooks"
9
+ LifecycleHooks = "endowment:lifecycle-hooks",
10
+ Keyring = "endowment:keyring"
10
11
  }
@@ -78,8 +78,20 @@ export declare const endowmentPermissionBuilders: {
78
78
  allowedCaveats: readonly [string, ...string[]] | null;
79
79
  }>;
80
80
  }>;
81
+ readonly "endowment:keyring": Readonly<{
82
+ readonly targetName: import("./enum").SnapEndowments.Keyring;
83
+ readonly specificationBuilder: import("@metamask/permission-controller").PermissionSpecificationBuilder<import("@metamask/permission-controller").PermissionType.Endowment, any, {
84
+ permissionType: import("@metamask/permission-controller").PermissionType.Endowment;
85
+ targetName: import("./enum").SnapEndowments.Keyring;
86
+ endowmentGetter: (_options?: import("@metamask/permission-controller").EndowmentGetterParams | undefined) => undefined;
87
+ allowedCaveats: readonly [string, ...string[]] | null;
88
+ validator: import("@metamask/permission-controller").PermissionValidatorConstraint;
89
+ subjectTypes: readonly import("@metamask/permission-controller").SubjectType[];
90
+ }>;
91
+ }>;
81
92
  };
82
93
  export declare const endowmentCaveatSpecifications: {
94
+ keyringOrigin: import("@metamask/permission-controller").CaveatSpecificationConstraint;
83
95
  chainIds: import("@metamask/permission-controller").CaveatSpecificationConstraint;
84
96
  rpcOrigin: import("@metamask/permission-controller").CaveatSpecificationConstraint;
85
97
  transactionOrigin: import("@metamask/permission-controller").CaveatSpecificationConstraint;
@@ -91,3 +103,4 @@ export * from './enum';
91
103
  export { getRpcCaveatOrigins } from './rpc';
92
104
  export { getTransactionOriginCaveat } from './transaction-insight';
93
105
  export { getChainIdsCaveat } from './name-lookup';
106
+ export { getKeyringCaveatOrigins } from './keyring';
@@ -0,0 +1,39 @@
1
+ import type { CaveatSpecificationConstraint, EndowmentGetterParams, PermissionConstraint, PermissionSpecificationBuilder, PermissionValidatorConstraint } from '@metamask/permission-controller';
2
+ import { PermissionType, SubjectType } from '@metamask/permission-controller';
3
+ import type { KeyringOrigins } from '@metamask/snaps-utils';
4
+ import { SnapCaveatType } from '@metamask/snaps-utils';
5
+ import type { Json, NonEmptyArray } from '@metamask/utils';
6
+ import { SnapEndowments } from './enum';
7
+ declare const permissionName = SnapEndowments.Keyring;
8
+ export declare const keyringEndowmentBuilder: Readonly<{
9
+ readonly targetName: SnapEndowments.Keyring;
10
+ readonly specificationBuilder: PermissionSpecificationBuilder<PermissionType.Endowment, any, {
11
+ permissionType: PermissionType.Endowment;
12
+ targetName: typeof permissionName;
13
+ endowmentGetter: (_options?: EndowmentGetterParams) => undefined;
14
+ allowedCaveats: Readonly<NonEmptyArray<string>> | null;
15
+ validator: PermissionValidatorConstraint;
16
+ subjectTypes: readonly SubjectType[];
17
+ }>;
18
+ }>;
19
+ /**
20
+ * Map a raw value from the `initialPermissions` to a caveat specification.
21
+ * Note that this function does not do any validation, that's handled by the
22
+ * PermissionsController when the permission is requested.
23
+ *
24
+ * @param value - The raw value from the `initialPermissions`.
25
+ * @returns The caveat specification.
26
+ */
27
+ export declare function getKeyringCaveatMapper(value: Json): Pick<PermissionConstraint, 'caveats'>;
28
+ /**
29
+ * Getter function to get the {@link KeyringOrigins} caveat value from a
30
+ * permission.
31
+ *
32
+ * @param permission - The permission to get the caveat value from.
33
+ * @returns The caveat value.
34
+ * @throws If the permission does not have a valid {@link KeyringOrigins}
35
+ * caveat.
36
+ */
37
+ export declare function getKeyringCaveatOrigins(permission?: PermissionConstraint): KeyringOrigins | null;
38
+ export declare const keyringCaveatSpecifications: Record<SnapCaveatType.KeyringOrigin, CaveatSpecificationConstraint>;
39
+ export {};
@@ -2,7 +2,7 @@ import type { SnapManifest } from '@metamask/snaps-utils';
2
2
  import { VirtualFile } from '@metamask/snaps-utils';
3
3
  import type { SemVerRange } from '@metamask/utils';
4
4
  import type { DetectSnapLocationOptions, SnapLocation } from './location';
5
- export declare const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
5
+ export declare const DEFAULT_NPM_REGISTRY: URL;
6
6
  export interface NpmOptions {
7
7
  /**
8
8
  * @default DEFAULT_REQUESTED_SNAP_VERSION
@@ -21,6 +21,10 @@ export declare type GetResult = {
21
21
  type: `${typeof controllerName}:get`;
22
22
  handler: SnapsRegistry['get'];
23
23
  };
24
+ export declare type ResolveVersion = {
25
+ type: `${typeof controllerName}:resolveVersion`;
26
+ handler: SnapsRegistry['resolveVersion'];
27
+ };
24
28
  export declare type GetMetadata = {
25
29
  type: `${typeof controllerName}:getMetadata`;
26
30
  handler: SnapsRegistry['getMetadata'];
@@ -29,7 +33,7 @@ export declare type Update = {
29
33
  type: `${typeof controllerName}:update`;
30
34
  handler: SnapsRegistry['update'];
31
35
  };
32
- export declare type SnapsRegistryActions = GetResult | GetMetadata | Update;
36
+ export declare type SnapsRegistryActions = GetResult | GetMetadata | Update | ResolveVersion;
33
37
  export declare type SnapsRegistryEvents = never;
34
38
  export declare type SnapsRegistryMessenger = RestrictedControllerMessenger<'SnapsRegistry', SnapsRegistryActions, SnapsRegistryEvents, SnapsRegistryActions['type'], SnapsRegistryEvents['type']>;
35
39
  export declare type SnapsRegistryState = {
@@ -1,6 +1,6 @@
1
1
  import type { BlockReason, SnapsRegistryDatabase } from '@metamask/snaps-registry';
2
2
  import type { SnapId, ValidatedSnapId } from '@metamask/snaps-utils';
3
- import type { SemVerVersion } from '@metamask/utils';
3
+ import type { SemVerRange, SemVerVersion } from '@metamask/utils';
4
4
  export declare type SnapsRegistryInfo = {
5
5
  version: SemVerVersion;
6
6
  checksum: string;
@@ -19,6 +19,16 @@ export declare type SnapsRegistryResult = {
19
19
  export declare type SnapsRegistry = {
20
20
  get(snaps: SnapsRegistryRequest): Promise<Record<ValidatedSnapId, SnapsRegistryResult>>;
21
21
  update(): Promise<void>;
22
+ /**
23
+ * Find an allowlisted version within a specified version range.
24
+ *
25
+ * @param snapId - The ID of the snap we are trying to resolve a version for.
26
+ * @param versionRange - The version range.
27
+ * @param refetch - An optional flag used to determine if we are refetching the registry.
28
+ * @returns An allowlisted version within the specified version range.
29
+ * @throws If an allowlisted version does not exist within the version range.
30
+ */
31
+ resolveVersion(snapId: SnapId, versionRange: SemVerRange): Promise<SemVerRange>;
22
32
  /**
23
33
  * Get metadata for the given snap ID.
24
34
  *