@metamask/snaps-utils 8.3.0 → 8.4.1

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/dist/url.cjs CHANGED
@@ -17,10 +17,16 @@ exports.SNAP_PATHS = ['/home'];
17
17
  */
18
18
  function parseMetaMaskUrl(str) {
19
19
  const url = new URL(str);
20
- const { hostname: authority, pathname: path, protocol } = url;
20
+ const { protocol } = url;
21
21
  if (protocol !== 'metamask:') {
22
22
  throw new Error(`Unable to parse URL. Expected the protocol to be "metamask:", but received "${protocol}".`);
23
23
  }
24
+ // The browser version of URL differs from the Node version so we rely on the href
25
+ // property to grab the relevant parts of the url instead of hostname and pathname
26
+ const [authority, ...pathElements] = url.href
27
+ .replace('metamask://', '')
28
+ .split('/');
29
+ const path = `/${pathElements.join('/')}`;
24
30
  switch (authority) {
25
31
  case 'client':
26
32
  (0, utils_1.assert)(exports.CLIENT_PATHS.includes(path), `Unable to navigate to "${path}". The provided path is not allowed.`);
package/dist/url.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"url.cjs","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"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","sourcesContent":["import { type SnapId } from '@metamask/snaps-sdk';\nimport { assert } from '@metamask/utils';\n\nimport { assertIsValidSnapId, stripSnapPrefix } from './snaps';\n\nexport type Authority = 'client' | 'snap';\n\nexport const CLIENT_PATHS = ['/'];\n\nexport const SNAP_PATHS = ['/home'];\n\n/**\n * Parse a url string to an object containing the authority, path and Snap id (if snap authority).\n * This also validates the url before parsing it.\n *\n * Note: The Snap id returned from this function should always be validated to be an installed snap.\n *\n * @param str - The url string to validate and parse.\n * @returns A parsed url object.\n * @throws If the authority or path is invalid.\n */\nexport function parseMetaMaskUrl(str: string): {\n authority: Authority;\n snapId?: SnapId;\n path: string;\n} {\n const url = new URL(str);\n const { hostname: authority, pathname: path, protocol } = url;\n if (protocol !== 'metamask:') {\n throw new Error(\n `Unable to parse URL. Expected the protocol to be \"metamask:\", but received \"${protocol}\".`,\n );\n }\n switch (authority) {\n case 'client':\n assert(\n CLIENT_PATHS.includes(path),\n `Unable to navigate to \"${path}\". The provided path is not allowed.`,\n );\n return {\n authority,\n path,\n };\n case 'snap':\n return parseSnapPath(path);\n default:\n throw new Error(\n `Expected \"metamask:\" URL to start with \"client\" or \"snap\", but received \"${authority}\".`,\n );\n }\n}\n\n/**\n * Parse a snap path and throws if it is invalid, returns an object with link data otherwise.\n *\n * @param path - The snap path to be parsed.\n * @returns A parsed url object.\n * @throws If the path or Snap id is invalid.\n */\nfunction parseSnapPath(path: string): {\n authority: Authority;\n snapId: SnapId;\n path: string;\n} {\n const baseErrorMessage = 'Invalid MetaMask url:';\n const strippedPath = stripSnapPrefix(path.slice(1));\n const location = path.slice(1).startsWith('npm:') ? 'npm:' : 'local:';\n const isNameSpaced = strippedPath.startsWith('@');\n const pathTokens = strippedPath.split('/');\n const lastPathToken = `/${pathTokens[pathTokens.length - 1]}`;\n let partialSnapId;\n if (location === 'local:') {\n const [localProtocol, , ...rest] = pathTokens.slice(0, -1);\n partialSnapId = `${localProtocol}//${rest.join('/')}`;\n // we can't make assumptions of the structure of the local snap url since it can have a nested path\n // so we only check that the last path token is one of the allowed paths\n assert(\n SNAP_PATHS.includes(lastPathToken),\n `${baseErrorMessage} invalid snap path.`,\n );\n } else {\n partialSnapId = isNameSpaced\n ? `${pathTokens[0]}/${pathTokens[1]}`\n : pathTokens[0];\n assert(\n isNameSpaced\n ? pathTokens.length === 3 && SNAP_PATHS.includes(lastPathToken)\n : pathTokens.length === 2 && SNAP_PATHS.includes(lastPathToken),\n `${baseErrorMessage} invalid snap path.`,\n );\n }\n const snapId = `${location}${partialSnapId}`;\n assertIsValidSnapId(snapId);\n\n return {\n authority: 'snap' as Authority,\n snapId,\n path: lastPathToken,\n };\n}\n"]}
1
+ {"version":3,"file":"url.cjs","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"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","sourcesContent":["import { type SnapId } from '@metamask/snaps-sdk';\nimport { assert } from '@metamask/utils';\n\nimport { assertIsValidSnapId, stripSnapPrefix } from './snaps';\n\nexport type Authority = 'client' | 'snap';\n\nexport const CLIENT_PATHS = ['/'];\n\nexport const SNAP_PATHS = ['/home'];\n\n/**\n * Parse a url string to an object containing the authority, path and Snap id (if snap authority).\n * This also validates the url before parsing it.\n *\n * Note: The Snap id returned from this function should always be validated to be an installed snap.\n *\n * @param str - The url string to validate and parse.\n * @returns A parsed url object.\n * @throws If the authority or path is invalid.\n */\nexport function parseMetaMaskUrl(str: string): {\n authority: Authority;\n snapId?: SnapId;\n path: string;\n} {\n const url = new URL(str);\n const { protocol } = url;\n if (protocol !== 'metamask:') {\n throw new Error(\n `Unable to parse URL. Expected the protocol to be \"metamask:\", but received \"${protocol}\".`,\n );\n }\n\n // The browser version of URL differs from the Node version so we rely on the href\n // property to grab the relevant parts of the url instead of hostname and pathname\n const [authority, ...pathElements] = url.href\n .replace('metamask://', '')\n .split('/');\n const path = `/${pathElements.join('/')}`;\n\n switch (authority) {\n case 'client':\n assert(\n CLIENT_PATHS.includes(path),\n `Unable to navigate to \"${path}\". The provided path is not allowed.`,\n );\n return {\n authority,\n path,\n };\n case 'snap':\n return parseSnapPath(path);\n default:\n throw new Error(\n `Expected \"metamask:\" URL to start with \"client\" or \"snap\", but received \"${authority}\".`,\n );\n }\n}\n\n/**\n * Parse a snap path and throws if it is invalid, returns an object with link data otherwise.\n *\n * @param path - The snap path to be parsed.\n * @returns A parsed url object.\n * @throws If the path or Snap id is invalid.\n */\nfunction parseSnapPath(path: string): {\n authority: Authority;\n snapId: SnapId;\n path: string;\n} {\n const baseErrorMessage = 'Invalid MetaMask url:';\n const strippedPath = stripSnapPrefix(path.slice(1));\n const location = path.slice(1).startsWith('npm:') ? 'npm:' : 'local:';\n const isNameSpaced = strippedPath.startsWith('@');\n const pathTokens = strippedPath.split('/');\n const lastPathToken = `/${pathTokens[pathTokens.length - 1]}`;\n let partialSnapId;\n if (location === 'local:') {\n const [localProtocol, , ...rest] = pathTokens.slice(0, -1);\n partialSnapId = `${localProtocol}//${rest.join('/')}`;\n // we can't make assumptions of the structure of the local snap url since it can have a nested path\n // so we only check that the last path token is one of the allowed paths\n assert(\n SNAP_PATHS.includes(lastPathToken),\n `${baseErrorMessage} invalid snap path.`,\n );\n } else {\n partialSnapId = isNameSpaced\n ? `${pathTokens[0]}/${pathTokens[1]}`\n : pathTokens[0];\n assert(\n isNameSpaced\n ? pathTokens.length === 3 && SNAP_PATHS.includes(lastPathToken)\n : pathTokens.length === 2 && SNAP_PATHS.includes(lastPathToken),\n `${baseErrorMessage} invalid snap path.`,\n );\n }\n const snapId = `${location}${partialSnapId}`;\n assertIsValidSnapId(snapId);\n\n return {\n authority: 'snap' as Authority,\n snapId,\n path: lastPathToken,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"url.d.cts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,4BAA4B;AAKlD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1C,eAAO,MAAM,YAAY,UAAQ,CAAC;AAElC,eAAO,MAAM,UAAU,UAAY,CAAC;AAEpC;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAyBA"}
1
+ {"version":3,"file":"url.d.cts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,4BAA4B;AAKlD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1C,eAAO,MAAM,YAAY,UAAQ,CAAC;AAElC,eAAO,MAAM,UAAU,UAAY,CAAC;AAEpC;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAiCA"}
@@ -1 +1 @@
1
- {"version":3,"file":"url.d.mts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,4BAA4B;AAKlD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1C,eAAO,MAAM,YAAY,UAAQ,CAAC;AAElC,eAAO,MAAM,UAAU,UAAY,CAAC;AAEpC;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAyBA"}
1
+ {"version":3,"file":"url.d.mts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,4BAA4B;AAKlD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1C,eAAO,MAAM,YAAY,UAAQ,CAAC;AAElC,eAAO,MAAM,UAAU,UAAY,CAAC;AAEpC;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAiCA"}
package/dist/url.mjs CHANGED
@@ -14,10 +14,16 @@ export const SNAP_PATHS = ['/home'];
14
14
  */
15
15
  export function parseMetaMaskUrl(str) {
16
16
  const url = new URL(str);
17
- const { hostname: authority, pathname: path, protocol } = url;
17
+ const { protocol } = url;
18
18
  if (protocol !== 'metamask:') {
19
19
  throw new Error(`Unable to parse URL. Expected the protocol to be "metamask:", but received "${protocol}".`);
20
20
  }
21
+ // The browser version of URL differs from the Node version so we rely on the href
22
+ // property to grab the relevant parts of the url instead of hostname and pathname
23
+ const [authority, ...pathElements] = url.href
24
+ .replace('metamask://', '')
25
+ .split('/');
26
+ const path = `/${pathElements.join('/')}`;
21
27
  switch (authority) {
22
28
  case 'client':
23
29
  assert(CLIENT_PATHS.includes(path), `Unable to navigate to "${path}". The provided path is not allowed.`);
package/dist/url.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"url.mjs","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"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","sourcesContent":["import { type SnapId } from '@metamask/snaps-sdk';\nimport { assert } from '@metamask/utils';\n\nimport { assertIsValidSnapId, stripSnapPrefix } from './snaps';\n\nexport type Authority = 'client' | 'snap';\n\nexport const CLIENT_PATHS = ['/'];\n\nexport const SNAP_PATHS = ['/home'];\n\n/**\n * Parse a url string to an object containing the authority, path and Snap id (if snap authority).\n * This also validates the url before parsing it.\n *\n * Note: The Snap id returned from this function should always be validated to be an installed snap.\n *\n * @param str - The url string to validate and parse.\n * @returns A parsed url object.\n * @throws If the authority or path is invalid.\n */\nexport function parseMetaMaskUrl(str: string): {\n authority: Authority;\n snapId?: SnapId;\n path: string;\n} {\n const url = new URL(str);\n const { hostname: authority, pathname: path, protocol } = url;\n if (protocol !== 'metamask:') {\n throw new Error(\n `Unable to parse URL. Expected the protocol to be \"metamask:\", but received \"${protocol}\".`,\n );\n }\n switch (authority) {\n case 'client':\n assert(\n CLIENT_PATHS.includes(path),\n `Unable to navigate to \"${path}\". The provided path is not allowed.`,\n );\n return {\n authority,\n path,\n };\n case 'snap':\n return parseSnapPath(path);\n default:\n throw new Error(\n `Expected \"metamask:\" URL to start with \"client\" or \"snap\", but received \"${authority}\".`,\n );\n }\n}\n\n/**\n * Parse a snap path and throws if it is invalid, returns an object with link data otherwise.\n *\n * @param path - The snap path to be parsed.\n * @returns A parsed url object.\n * @throws If the path or Snap id is invalid.\n */\nfunction parseSnapPath(path: string): {\n authority: Authority;\n snapId: SnapId;\n path: string;\n} {\n const baseErrorMessage = 'Invalid MetaMask url:';\n const strippedPath = stripSnapPrefix(path.slice(1));\n const location = path.slice(1).startsWith('npm:') ? 'npm:' : 'local:';\n const isNameSpaced = strippedPath.startsWith('@');\n const pathTokens = strippedPath.split('/');\n const lastPathToken = `/${pathTokens[pathTokens.length - 1]}`;\n let partialSnapId;\n if (location === 'local:') {\n const [localProtocol, , ...rest] = pathTokens.slice(0, -1);\n partialSnapId = `${localProtocol}//${rest.join('/')}`;\n // we can't make assumptions of the structure of the local snap url since it can have a nested path\n // so we only check that the last path token is one of the allowed paths\n assert(\n SNAP_PATHS.includes(lastPathToken),\n `${baseErrorMessage} invalid snap path.`,\n );\n } else {\n partialSnapId = isNameSpaced\n ? `${pathTokens[0]}/${pathTokens[1]}`\n : pathTokens[0];\n assert(\n isNameSpaced\n ? pathTokens.length === 3 && SNAP_PATHS.includes(lastPathToken)\n : pathTokens.length === 2 && SNAP_PATHS.includes(lastPathToken),\n `${baseErrorMessage} invalid snap path.`,\n );\n }\n const snapId = `${location}${partialSnapId}`;\n assertIsValidSnapId(snapId);\n\n return {\n authority: 'snap' as Authority,\n snapId,\n path: lastPathToken,\n };\n}\n"]}
1
+ {"version":3,"file":"url.mjs","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"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","sourcesContent":["import { type SnapId } from '@metamask/snaps-sdk';\nimport { assert } from '@metamask/utils';\n\nimport { assertIsValidSnapId, stripSnapPrefix } from './snaps';\n\nexport type Authority = 'client' | 'snap';\n\nexport const CLIENT_PATHS = ['/'];\n\nexport const SNAP_PATHS = ['/home'];\n\n/**\n * Parse a url string to an object containing the authority, path and Snap id (if snap authority).\n * This also validates the url before parsing it.\n *\n * Note: The Snap id returned from this function should always be validated to be an installed snap.\n *\n * @param str - The url string to validate and parse.\n * @returns A parsed url object.\n * @throws If the authority or path is invalid.\n */\nexport function parseMetaMaskUrl(str: string): {\n authority: Authority;\n snapId?: SnapId;\n path: string;\n} {\n const url = new URL(str);\n const { protocol } = url;\n if (protocol !== 'metamask:') {\n throw new Error(\n `Unable to parse URL. Expected the protocol to be \"metamask:\", but received \"${protocol}\".`,\n );\n }\n\n // The browser version of URL differs from the Node version so we rely on the href\n // property to grab the relevant parts of the url instead of hostname and pathname\n const [authority, ...pathElements] = url.href\n .replace('metamask://', '')\n .split('/');\n const path = `/${pathElements.join('/')}`;\n\n switch (authority) {\n case 'client':\n assert(\n CLIENT_PATHS.includes(path),\n `Unable to navigate to \"${path}\". The provided path is not allowed.`,\n );\n return {\n authority,\n path,\n };\n case 'snap':\n return parseSnapPath(path);\n default:\n throw new Error(\n `Expected \"metamask:\" URL to start with \"client\" or \"snap\", but received \"${authority}\".`,\n );\n }\n}\n\n/**\n * Parse a snap path and throws if it is invalid, returns an object with link data otherwise.\n *\n * @param path - The snap path to be parsed.\n * @returns A parsed url object.\n * @throws If the path or Snap id is invalid.\n */\nfunction parseSnapPath(path: string): {\n authority: Authority;\n snapId: SnapId;\n path: string;\n} {\n const baseErrorMessage = 'Invalid MetaMask url:';\n const strippedPath = stripSnapPrefix(path.slice(1));\n const location = path.slice(1).startsWith('npm:') ? 'npm:' : 'local:';\n const isNameSpaced = strippedPath.startsWith('@');\n const pathTokens = strippedPath.split('/');\n const lastPathToken = `/${pathTokens[pathTokens.length - 1]}`;\n let partialSnapId;\n if (location === 'local:') {\n const [localProtocol, , ...rest] = pathTokens.slice(0, -1);\n partialSnapId = `${localProtocol}//${rest.join('/')}`;\n // we can't make assumptions of the structure of the local snap url since it can have a nested path\n // so we only check that the last path token is one of the allowed paths\n assert(\n SNAP_PATHS.includes(lastPathToken),\n `${baseErrorMessage} invalid snap path.`,\n );\n } else {\n partialSnapId = isNameSpaced\n ? `${pathTokens[0]}/${pathTokens[1]}`\n : pathTokens[0];\n assert(\n isNameSpaced\n ? pathTokens.length === 3 && SNAP_PATHS.includes(lastPathToken)\n : pathTokens.length === 2 && SNAP_PATHS.includes(lastPathToken),\n `${baseErrorMessage} invalid snap path.`,\n );\n }\n const snapId = `${location}${partialSnapId}`;\n assertIsValidSnapId(snapId);\n\n return {\n authority: 'snap' as Authority,\n snapId,\n path: lastPathToken,\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask/snaps-utils",
3
- "version": "8.3.0",
3
+ "version": "8.4.1",
4
4
  "description": "A collection of utilities for MetaMask Snaps",
5
5
  "keywords": [
6
6
  "MetaMask",
@@ -85,7 +85,7 @@
85
85
  "@metamask/rpc-errors": "^6.3.1",
86
86
  "@metamask/slip44": "^4.0.0",
87
87
  "@metamask/snaps-registry": "^3.2.1",
88
- "@metamask/snaps-sdk": "^6.7.0",
88
+ "@metamask/snaps-sdk": "^6.9.0",
89
89
  "@metamask/superstruct": "^3.1.0",
90
90
  "@metamask/utils": "^9.2.1",
91
91
  "@noble/hashes": "^1.3.1",