@metamask/snaps-utils 9.0.1 → 9.2.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 (151) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/dist/account.cjs +3 -0
  3. package/dist/account.cjs.map +1 -0
  4. package/dist/account.d.cts +22 -0
  5. package/dist/account.d.cts.map +1 -0
  6. package/dist/account.d.mts +22 -0
  7. package/dist/account.d.mts.map +1 -0
  8. package/dist/account.mjs +2 -0
  9. package/dist/account.mjs.map +1 -0
  10. package/dist/cronjob.d.cts +9 -9
  11. package/dist/cronjob.d.mts +9 -9
  12. package/dist/eval-worker.cjs +2 -2
  13. package/dist/eval-worker.cjs.map +1 -1
  14. package/dist/eval-worker.mjs +1 -1
  15. package/dist/eval-worker.mjs.map +1 -1
  16. package/dist/handlers/asset-historical-price.cjs +25 -0
  17. package/dist/handlers/asset-historical-price.cjs.map +1 -0
  18. package/dist/handlers/asset-historical-price.d.cts +37 -0
  19. package/dist/handlers/asset-historical-price.d.cts.map +1 -0
  20. package/dist/handlers/asset-historical-price.d.mts +37 -0
  21. package/dist/handlers/asset-historical-price.d.mts.map +1 -0
  22. package/dist/handlers/asset-historical-price.mjs +22 -0
  23. package/dist/handlers/asset-historical-price.mjs.map +1 -0
  24. package/dist/handlers/assets-conversion.cjs +38 -0
  25. package/dist/handlers/assets-conversion.cjs.map +1 -0
  26. package/dist/handlers/assets-conversion.d.cts +90 -0
  27. package/dist/handlers/assets-conversion.d.cts.map +1 -0
  28. package/dist/handlers/assets-conversion.d.mts +90 -0
  29. package/dist/handlers/assets-conversion.d.mts.map +1 -0
  30. package/dist/handlers/assets-conversion.mjs +35 -0
  31. package/dist/handlers/assets-conversion.mjs.map +1 -0
  32. package/dist/handlers/exports.cjs +113 -0
  33. package/dist/handlers/exports.cjs.map +1 -0
  34. package/dist/handlers/exports.d.cts +81 -0
  35. package/dist/handlers/exports.d.cts.map +1 -0
  36. package/dist/handlers/exports.d.mts +81 -0
  37. package/dist/handlers/exports.d.mts.map +1 -0
  38. package/dist/{handlers.mjs → handlers/exports.mjs} +10 -45
  39. package/dist/handlers/exports.mjs.map +1 -0
  40. package/dist/handlers/home-page.cjs +16 -0
  41. package/dist/handlers/home-page.cjs.map +1 -0
  42. package/dist/handlers/home-page.d.cts +268 -0
  43. package/dist/handlers/home-page.d.cts.map +1 -0
  44. package/dist/handlers/home-page.d.mts +268 -0
  45. package/dist/handlers/home-page.d.mts.map +1 -0
  46. package/dist/handlers/home-page.mjs +13 -0
  47. package/dist/handlers/home-page.mjs.map +1 -0
  48. package/dist/handlers/index.cjs +26 -0
  49. package/dist/handlers/index.cjs.map +1 -0
  50. package/dist/handlers/index.d.cts +10 -0
  51. package/dist/handlers/index.d.cts.map +1 -0
  52. package/dist/handlers/index.d.mts +10 -0
  53. package/dist/handlers/index.d.mts.map +1 -0
  54. package/dist/handlers/index.mjs +10 -0
  55. package/dist/handlers/index.mjs.map +1 -0
  56. package/dist/handlers/name-lookup.cjs +21 -0
  57. package/dist/handlers/name-lookup.cjs.map +1 -0
  58. package/dist/handlers/name-lookup.d.cts +67 -0
  59. package/dist/handlers/name-lookup.d.cts.map +1 -0
  60. package/dist/handlers/name-lookup.d.mts +67 -0
  61. package/dist/handlers/name-lookup.d.mts.map +1 -0
  62. package/dist/handlers/name-lookup.mjs +18 -0
  63. package/dist/handlers/name-lookup.mjs.map +1 -0
  64. package/dist/handlers/settings-page.cjs +6 -0
  65. package/dist/handlers/settings-page.cjs.map +1 -0
  66. package/dist/handlers/settings-page.d.cts +90 -0
  67. package/dist/handlers/settings-page.d.cts.map +1 -0
  68. package/dist/handlers/settings-page.d.mts +90 -0
  69. package/dist/handlers/settings-page.d.mts.map +1 -0
  70. package/dist/handlers/settings-page.mjs +3 -0
  71. package/dist/handlers/settings-page.mjs.map +1 -0
  72. package/dist/handlers/signature.cjs +6 -0
  73. package/dist/handlers/signature.cjs.map +1 -0
  74. package/dist/handlers/signature.d.cts +92 -0
  75. package/dist/handlers/signature.d.cts.map +1 -0
  76. package/dist/handlers/signature.d.mts +92 -0
  77. package/dist/handlers/signature.d.mts.map +1 -0
  78. package/dist/handlers/signature.mjs +3 -0
  79. package/dist/handlers/signature.mjs.map +1 -0
  80. package/dist/handlers/transaction.cjs +19 -0
  81. package/dist/handlers/transaction.cjs.map +1 -0
  82. package/dist/handlers/transaction.d.cts +280 -0
  83. package/dist/handlers/transaction.d.cts.map +1 -0
  84. package/dist/handlers/transaction.d.mts +280 -0
  85. package/dist/handlers/transaction.d.mts.map +1 -0
  86. package/dist/handlers/transaction.mjs +16 -0
  87. package/dist/handlers/transaction.mjs.map +1 -0
  88. package/dist/{handler-types.cjs → handlers/types.cjs} +3 -3
  89. package/dist/handlers/types.cjs.map +1 -0
  90. package/dist/{handler-types.d.mts → handlers/types.d.cts} +22 -2
  91. package/dist/handlers/types.d.cts.map +1 -0
  92. package/dist/{handler-types.d.cts → handlers/types.d.mts} +22 -2
  93. package/dist/handlers/types.d.mts.map +1 -0
  94. package/dist/{handler-types.mjs → handlers/types.mjs} +2 -2
  95. package/dist/handlers/types.mjs.map +1 -0
  96. package/dist/iframe.test.browser.cjs +20 -19
  97. package/dist/iframe.test.browser.cjs.map +1 -1
  98. package/dist/iframe.test.browser.mjs +2 -1
  99. package/dist/iframe.test.browser.mjs.map +1 -1
  100. package/dist/index.cjs +2 -2
  101. package/dist/index.cjs.map +1 -1
  102. package/dist/index.d.cts +3 -2
  103. package/dist/index.d.cts.map +1 -1
  104. package/dist/index.d.mts +3 -2
  105. package/dist/index.d.mts.map +1 -1
  106. package/dist/index.executionenv.cjs +2 -2
  107. package/dist/index.executionenv.cjs.map +1 -1
  108. package/dist/index.executionenv.d.cts +2 -2
  109. package/dist/index.executionenv.d.cts.map +1 -1
  110. package/dist/index.executionenv.d.mts +2 -2
  111. package/dist/index.executionenv.d.mts.map +1 -1
  112. package/dist/index.executionenv.mjs +2 -2
  113. package/dist/index.executionenv.mjs.map +1 -1
  114. package/dist/index.mjs +2 -2
  115. package/dist/index.mjs.map +1 -1
  116. package/dist/localization.d.cts +5 -5
  117. package/dist/localization.d.mts +5 -5
  118. package/dist/manifest/validation.d.cts +12 -12
  119. package/dist/manifest/validation.d.mts +12 -12
  120. package/dist/time.cjs +34 -0
  121. package/dist/time.cjs.map +1 -0
  122. package/dist/time.d.cts +9 -0
  123. package/dist/time.d.cts.map +1 -0
  124. package/dist/time.d.mts +9 -0
  125. package/dist/time.d.mts.map +1 -0
  126. package/dist/time.mjs +31 -0
  127. package/dist/time.mjs.map +1 -0
  128. package/dist/types.d.cts +2 -2
  129. package/dist/types.d.cts.map +1 -1
  130. package/dist/types.d.mts +2 -2
  131. package/dist/types.d.mts.map +1 -1
  132. package/dist/ui.cjs +34 -10
  133. package/dist/ui.cjs.map +1 -1
  134. package/dist/ui.d.cts +22 -6
  135. package/dist/ui.d.cts.map +1 -1
  136. package/dist/ui.d.mts +22 -6
  137. package/dist/ui.d.mts.map +1 -1
  138. package/dist/ui.mjs +31 -8
  139. package/dist/ui.mjs.map +1 -1
  140. package/package.json +16 -24
  141. package/dist/handler-types.cjs.map +0 -1
  142. package/dist/handler-types.d.cts.map +0 -1
  143. package/dist/handler-types.d.mts.map +0 -1
  144. package/dist/handler-types.mjs.map +0 -1
  145. package/dist/handlers.cjs +0 -148
  146. package/dist/handlers.cjs.map +0 -1
  147. package/dist/handlers.d.cts +0 -887
  148. package/dist/handlers.d.cts.map +0 -1
  149. package/dist/handlers.d.mts +0 -887
  150. package/dist/handlers.d.mts.map +0 -1
  151. package/dist/handlers.mjs.map +0 -1
package/dist/ui.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serialiseJsx = exports.walkJsx = exports.getJsxChildren = exports.hasChildren = exports.getTotalTextLength = exports.validateJsxLinks = exports.validateTextLinks = exports.validateLink = exports.getJsxElementFromComponent = exports.getTextChildren = void 0;
3
+ exports.serialiseJsx = exports.walkJsx = exports.getJsxChildren = exports.hasChildren = exports.getTotalTextLength = exports.validateJsxElements = exports.validateAssetSelector = exports.validateTextLinks = exports.validateLink = exports.getJsxElementFromComponent = exports.getTextChildren = void 0;
4
4
  const jsx_runtime_1 = require("@metamask/snaps-sdk/jsx-runtime");
5
5
  const snaps_sdk_1 = require("@metamask/snaps-sdk");
6
6
  const jsx_1 = require("@metamask/snaps-sdk/jsx");
@@ -254,23 +254,47 @@ function validateTextLinks(text, isOnPhishingList, getSnap) {
254
254
  }
255
255
  exports.validateTextLinks = validateTextLinks;
256
256
  /**
257
- * Walk a JSX tree and validate each {@link LinkElement} node against the
258
- * phishing list.
257
+ * Validate the asset selector component.
258
+ *
259
+ * @param address - The address of the account to pull the assets from.
260
+ * @param getAccountByAddress - A function to get an account by its address.
261
+ *
262
+ * @throws If the asset selector is invalid.
263
+ */
264
+ function validateAssetSelector(address, getAccountByAddress) {
265
+ const account = getAccountByAddress(address);
266
+ (0, utils_1.assert)(account, `Could not find account for address: ${address}`);
267
+ }
268
+ exports.validateAssetSelector = validateAssetSelector;
269
+ /**
270
+ * Walk a JSX tree and validate elements.
271
+ * This function validates Links and AssetSelectors.
259
272
  *
260
273
  * @param node - The JSX node to walk.
261
- * @param isOnPhishingList - The function that checks the link against the
274
+ * @param hooks - The hooks to use for validation.
275
+ * @param hooks.isOnPhishingList - The function that checks the link against the
262
276
  * phishing list.
263
- * @param getSnap - The function that returns a snap if installed, undefined otherwise.
277
+ * @param hooks.getSnap - The function that returns a snap if installed, undefined otherwise.
278
+ * @param hooks.getAccountByAddress - The function that returns an account by address.
264
279
  */
265
- function validateJsxLinks(node, isOnPhishingList, getSnap) {
280
+ function validateJsxElements(node, { isOnPhishingList, getSnap, getAccountByAddress, }) {
266
281
  walkJsx(node, (childNode) => {
267
- if (childNode.type !== 'Link') {
268
- return;
282
+ switch (childNode.type) {
283
+ case 'Link':
284
+ validateLink(childNode.props.href, isOnPhishingList, getSnap);
285
+ break;
286
+ case 'AssetSelector':
287
+ validateAssetSelector(
288
+ // We assume that the address part of the CAIP-10 account ID are the same, as
289
+ // that is already validated in the struct.
290
+ childNode.props.addresses[0], getAccountByAddress);
291
+ break;
292
+ default:
293
+ break;
269
294
  }
270
- validateLink(childNode.props.href, isOnPhishingList, getSnap);
271
295
  });
272
296
  }
273
- exports.validateJsxLinks = validateJsxLinks;
297
+ exports.validateJsxElements = validateJsxElements;
274
298
  /**
275
299
  * Calculate the total length of all text in the component.
276
300
  *
package/dist/ui.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.cjs","sourceRoot":"","sources":["../src/ui.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { Component } from '@metamask/snaps-sdk';\nimport { NodeType } from '@metamask/snaps-sdk';\nimport type {\n BoldChildren,\n GenericSnapElement,\n ItalicChildren,\n JSXElement,\n LinkElement,\n Nestable,\n RowChildren,\n SnapNode,\n StandardFormattingElement,\n TextChildren,\n} from '@metamask/snaps-sdk/jsx';\nimport {\n Italic,\n Link,\n Bold,\n Row,\n Text,\n Field,\n Image,\n Input,\n Heading,\n Form,\n Divider,\n Spinner,\n Copyable,\n Box,\n Button,\n Address,\n} from '@metamask/snaps-sdk/jsx';\nimport {\n assert,\n assertExhaustive,\n hasProperty,\n isPlainObject,\n} from '@metamask/utils';\nimport { lexer, walkTokens } from 'marked';\nimport type { Token, Tokens } from 'marked';\n\nimport type { Snap } from './snaps';\nimport { parseMetaMaskUrl } from './url';\n\nconst MAX_TEXT_LENGTH = 50_000; // 50 kb\nconst ALLOWED_PROTOCOLS = ['https:', 'mailto:', 'metamask:'];\n\n/**\n * Get the button variant from a legacy button component variant.\n *\n * @param variant - The legacy button component variant.\n * @returns The button variant.\n */\nfunction getButtonVariant(variant?: 'primary' | 'secondary' | undefined) {\n switch (variant) {\n case 'primary':\n return 'primary';\n case 'secondary':\n return 'destructive';\n default:\n return undefined;\n }\n}\n\n/**\n * Get the children of a JSX element. If there is only one child, the child is\n * returned directly. Otherwise, the children are returned as an array.\n *\n * @param elements - The JSX elements.\n * @returns The child or children.\n */\nfunction getChildren<Type>(elements: Type[]) {\n if (elements.length === 1) {\n return elements[0];\n }\n\n return elements;\n}\n\n/**\n * Get the text of a link token.\n *\n * @param token - The link token.\n * @returns The text of the link token.\n */\nfunction getLinkText(token: Tokens.Link | Tokens.Generic) {\n if (token.tokens && token.tokens.length > 0) {\n return getChildren(token.tokens.flatMap(getTextChildFromToken));\n }\n\n return token.href;\n}\n\n/**\n * Get the text child from a list of markdown tokens.\n *\n * @param tokens - The markdown tokens.\n * @returns The text child.\n */\nfunction getTextChildFromTokens(tokens: Token[]) {\n return getChildren(tokens.flatMap(getTextChildFromToken));\n}\n\n/**\n * Get the text child from a markdown token.\n *\n * @param token - The markdown token.\n * @returns The text child.\n */\nfunction getTextChildFromToken(token: Token): TextChildren {\n switch (token.type) {\n case 'link': {\n return <Link href={token.href} children={getLinkText(token)} />;\n }\n\n case 'text':\n return token.text;\n\n case 'strong':\n return (\n <Bold>\n {\n getTextChildFromTokens(\n // Due to the way `marked` is typed, `token.tokens` can be\n // `undefined`, but it's a required field of `Tokens.Bold`, so we\n // can safely cast it to `Token[]`.\n token.tokens as Token[],\n ) as BoldChildren\n }\n </Bold>\n );\n\n case 'em':\n return (\n <Italic>\n {\n getTextChildFromTokens(\n // Due to the way `marked` is typed, `token.tokens` can be\n // `undefined`, but it's a required field of `Tokens.Bold`, so we\n // can safely cast it to `Token[]`.\n token.tokens as Token[],\n ) as ItalicChildren\n }\n </Italic>\n );\n\n default:\n return null;\n }\n}\n\n/**\n * Get all text children from a markdown string.\n *\n * @param value - The markdown string.\n * @returns The text children.\n */\nexport function getTextChildren(\n value: string,\n): (string | StandardFormattingElement | LinkElement)[] {\n const rootTokens = lexer(value, { gfm: false });\n const children: (string | StandardFormattingElement | LinkElement | null)[] =\n [];\n\n // TODO: Either fix this lint violation or explain why it's necessary to\n // ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n walkTokens(rootTokens, (token) => {\n if (token.type === 'paragraph') {\n if (children.length > 0) {\n children.push('\\n\\n');\n }\n\n const { tokens } = token as Tokens.Paragraph;\n // We do not need to consider nesting deeper than 1 level here and we can therefore cast.\n children.push(\n ...(tokens.flatMap(getTextChildFromToken) as (\n | string\n | StandardFormattingElement\n | LinkElement\n | null\n )[]),\n );\n }\n });\n\n return children.filter((child) => child !== null) as (\n | string\n | StandardFormattingElement\n | LinkElement\n )[];\n}\n\n/**\n * Validate the text size of a component. The text size is the total length of\n * all text in the component.\n *\n * @param component - The component to validate.\n * @throws An error if the text size exceeds the maximum allowed size.\n */\nfunction validateComponentTextSize(component: Component) {\n const textSize = getTotalTextLength(component);\n assert(\n textSize <= MAX_TEXT_LENGTH,\n `The text in a Snap UI may not be larger than ${\n MAX_TEXT_LENGTH / 1000\n } kB.`,\n );\n}\n\n/**\n * Get a JSX element from a legacy UI component. This supports all legacy UI\n * components, and maps them to their JSX equivalents where possible.\n *\n * This function validates the text size of the component, but does not validate\n * the total size. The total size of the component should be validated before\n * calling this function.\n *\n * @param legacyComponent - The legacy UI component.\n * @returns The JSX element.\n */\nexport function getJsxElementFromComponent(\n legacyComponent: Component,\n): JSXElement {\n validateComponentTextSize(legacyComponent);\n\n /**\n * Get the JSX element for a component. This function is recursive and will\n * call itself for child components.\n *\n * @param component - The component to convert to a JSX element.\n * @returns The JSX element.\n */\n function getElement(component: Component) {\n switch (component.type) {\n case NodeType.Address:\n return <Address address={component.value} />;\n\n case NodeType.Button:\n return (\n <Button\n name={component.name}\n variant={getButtonVariant(component.variant)}\n type={component.buttonType}\n >\n {component.value}\n </Button>\n );\n\n case NodeType.Copyable:\n return (\n <Copyable value={component.value} sensitive={component.sensitive} />\n );\n\n case NodeType.Divider:\n return <Divider />;\n\n case NodeType.Form:\n return (\n <Form name={component.name}>\n {getChildren(component.children.map(getElement))}\n </Form>\n );\n\n case NodeType.Heading:\n return <Heading children={component.value} />;\n\n case NodeType.Image:\n // `Image` supports `alt`, but the legacy `Image` component does not.\n return <Image src={component.value} />;\n\n case NodeType.Input:\n return (\n <Field label={component.label} error={component.error}>\n <Input\n name={component.name}\n type={component.inputType}\n value={component.value}\n placeholder={component.placeholder}\n />\n </Field>\n );\n\n case NodeType.Panel:\n // `Panel` is renamed to `Box` in JSX.\n return (\n <Box children={getChildren(component.children.map(getElement))} />\n );\n\n case NodeType.Row:\n return (\n <Row label={component.label} variant={component.variant}>\n {getElement(component.value) as RowChildren}\n </Row>\n );\n\n case NodeType.Spinner:\n return <Spinner />;\n\n case NodeType.Text:\n return <Text>{getChildren(getTextChildren(component.value))}</Text>;\n\n /* istanbul ignore next 2 */\n default:\n return assertExhaustive(component);\n }\n }\n\n return getElement(legacyComponent);\n}\n\n/**\n * Extract all links from a Markdown text string using the `marked` lexer.\n *\n * @param text - The markdown text string.\n * @returns A list of URLs linked to in the string.\n */\nfunction getMarkdownLinks(text: string) {\n const tokens = lexer(text, { gfm: false });\n const links: Tokens.Link[] = [];\n\n // Walk the lexed tokens and collect all link tokens\n // TODO: Either fix this lint violation or explain why it's necessary to\n // ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n walkTokens(tokens, (token) => {\n if (token.type === 'link') {\n links.push(token as Tokens.Link);\n }\n });\n\n return links;\n}\n\n/**\n * Validate a link against the phishing list.\n *\n * @param link - The link to validate.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n * @throws If the link is invalid.\n */\nexport function validateLink(\n link: string,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n try {\n const url = new URL(link);\n assert(\n ALLOWED_PROTOCOLS.includes(url.protocol),\n `Protocol must be one of: ${ALLOWED_PROTOCOLS.join(', ')}.`,\n );\n\n if (url.protocol === 'metamask:') {\n const linkData = parseMetaMaskUrl(link);\n if (linkData.snapId) {\n assert(\n getSnap(linkData.snapId),\n 'The Snap being navigated to is not installed.',\n );\n }\n } else if (url.protocol === 'mailto:') {\n const emails = url.pathname.split(',');\n for (const email of emails) {\n const hostname = email.split('@')[1];\n assert(!hostname.includes(':'));\n const href = `https://${hostname}`;\n assert(!isOnPhishingList(href), 'The specified URL is not allowed.');\n }\n\n return;\n }\n\n assert(!isOnPhishingList(url.href), 'The specified URL is not allowed.');\n } catch (error) {\n throw new Error(\n `Invalid URL: ${\n error?.code === 'ERR_ASSERTION' ? error.message : 'Unable to parse URL.'\n }`,\n );\n }\n}\n\n/**\n * Search for Markdown links in a string and checks them against the phishing\n * list.\n *\n * @param text - The text to verify.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n * @throws If the text contains a link that is not allowed.\n */\nexport function validateTextLinks(\n text: string,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n const links = getMarkdownLinks(text);\n\n for (const link of links) {\n validateLink(link.href, isOnPhishingList, getSnap);\n }\n}\n\n/**\n * Walk a JSX tree and validate each {@link LinkElement} node against the\n * phishing list.\n *\n * @param node - The JSX node to walk.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n */\nexport function validateJsxLinks(\n node: JSXElement,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n walkJsx(node, (childNode) => {\n if (childNode.type !== 'Link') {\n return;\n }\n\n validateLink(childNode.props.href, isOnPhishingList, getSnap);\n });\n}\n\n/**\n * Calculate the total length of all text in the component.\n *\n * @param component - A custom UI component.\n * @returns The total length of all text components in the component.\n */\nexport function getTotalTextLength(component: Component): number {\n const { type } = component;\n\n switch (type) {\n case NodeType.Panel:\n return component.children.reduce<number>(\n (sum, node) => sum + getTotalTextLength(node),\n 0,\n );\n\n case NodeType.Row:\n return getTotalTextLength(component.value);\n\n case NodeType.Text:\n return component.value.length;\n\n default:\n return 0;\n }\n}\n\n/**\n * Check if a JSX element has children.\n *\n * @param element - A JSX element.\n * @returns `true` if the element has children, `false` otherwise.\n */\nexport function hasChildren<Element extends JSXElement>(\n element: Element,\n): element is Element & {\n props: { children: Nestable<JSXElement | string> };\n} {\n return hasProperty(element.props, 'children');\n}\n\n/**\n * Filter a JSX child to remove `null`, `undefined`, plain booleans, and empty\n * strings.\n *\n * @param child - The JSX child to filter.\n * @returns `true` if the child is not `null`, `undefined`, a plain boolean, or\n * an empty string, `false` otherwise.\n */\nfunction filterJsxChild(child: JSXElement | string | boolean | null): boolean {\n return Boolean(child) && child !== true;\n}\n\n/**\n * Get the children of a JSX element as an array. If the element has only one\n * child, the child is returned as an array.\n *\n * @param element - A JSX element.\n * @returns The children of the element.\n */\nexport function getJsxChildren(element: JSXElement): (JSXElement | string)[] {\n if (hasChildren(element)) {\n if (Array.isArray(element.props.children)) {\n // @ts-expect-error - Each member of the union type has signatures, but\n // none of those signatures are compatible with each other.\n return element.props.children.filter(filterJsxChild).flat(Infinity);\n }\n\n if (element.props.children) {\n return [element.props.children];\n }\n }\n\n return [];\n}\n\n/**\n * Walk a JSX tree and call a callback on each node.\n *\n * @param node - The JSX node to walk.\n * @param callback - The callback to call on each node.\n * @param depth - The current depth in the JSX tree for a walk.\n * @returns The result of the callback, if any.\n */\nexport function walkJsx<Value>(\n node: JSXElement | JSXElement[],\n callback: (node: JSXElement, depth: number) => Value | undefined,\n depth = 0,\n): Value | undefined {\n if (Array.isArray(node)) {\n for (const child of node) {\n const childResult = walkJsx(child, callback, depth);\n if (childResult !== undefined) {\n return childResult;\n }\n }\n\n return undefined;\n }\n\n const result = callback(node, depth);\n if (result !== undefined) {\n return result;\n }\n\n if (\n hasProperty(node, 'props') &&\n isPlainObject(node.props) &&\n hasProperty(node.props, 'children')\n ) {\n const children = getJsxChildren(node);\n for (const child of children) {\n if (isPlainObject(child)) {\n const childResult = walkJsx(child, callback, depth + 1);\n if (childResult !== undefined) {\n return childResult;\n }\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Serialise a JSX prop to a string.\n *\n * @param prop - The JSX prop.\n * @returns The serialised JSX prop.\n */\nfunction serialiseProp(prop: unknown): string {\n if (typeof prop === 'string') {\n return `\"${prop}\"`;\n }\n\n return `{${JSON.stringify(prop)}}`;\n}\n\n/**\n * Serialise JSX props to a string.\n *\n * @param props - The JSX props.\n * @returns The serialised JSX props.\n */\nfunction serialiseProps(props: Record<string, unknown>): string {\n return Object.entries(props)\n .filter(([key]) => key !== 'children')\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => ` ${key}=${serialiseProp(value)}`)\n .join('');\n}\n\n/**\n * Serialise a JSX node to a string.\n *\n * @param node - The JSX node.\n * @param indentation - The indentation level. Defaults to `0`. This should not\n * be set by the caller, as it is used for recursion.\n * @returns The serialised JSX node.\n */\nexport function serialiseJsx(node: SnapNode, indentation = 0): string {\n if (Array.isArray(node)) {\n return node.map((child) => serialiseJsx(child, indentation)).join('');\n }\n\n const indent = ' '.repeat(indentation);\n if (typeof node === 'string') {\n return `${indent}${node}\\n`;\n }\n\n if (!node) {\n return '';\n }\n\n const { type, props } = node as GenericSnapElement;\n const trailingNewline = indentation > 0 ? '\\n' : '';\n\n if (hasProperty(props, 'children')) {\n const children = serialiseJsx(props.children as SnapNode, indentation + 1);\n return `${indent}<${type}${serialiseProps(\n props,\n )}>\\n${children}${indent}</${type}>${trailingNewline}`;\n }\n\n return `${indent}<${type}${serialiseProps(props)} />${trailingNewline}`;\n}\n"]}
1
+ {"version":3,"file":"ui.cjs","sourceRoot":"","sources":["../src/ui.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { CaipAccountId, Component } from '@metamask/snaps-sdk';\nimport { NodeType } from '@metamask/snaps-sdk';\nimport type {\n BoldChildren,\n GenericSnapElement,\n ItalicChildren,\n JSXElement,\n LinkElement,\n Nestable,\n RowChildren,\n SnapNode,\n StandardFormattingElement,\n TextChildren,\n} from '@metamask/snaps-sdk/jsx';\nimport {\n Italic,\n Link,\n Bold,\n Row,\n Text,\n Field,\n Image,\n Input,\n Heading,\n Form,\n Divider,\n Spinner,\n Copyable,\n Box,\n Button,\n Address,\n} from '@metamask/snaps-sdk/jsx';\nimport {\n assert,\n assertExhaustive,\n hasProperty,\n isPlainObject,\n} from '@metamask/utils';\nimport { lexer, walkTokens } from 'marked';\nimport type { Token, Tokens } from 'marked';\n\nimport type { InternalAccount } from './account';\nimport type { Snap } from './snaps';\nimport { parseMetaMaskUrl } from './url';\n\nconst MAX_TEXT_LENGTH = 50_000; // 50 kb\nconst ALLOWED_PROTOCOLS = ['https:', 'mailto:', 'metamask:'];\n\n/**\n * Get the button variant from a legacy button component variant.\n *\n * @param variant - The legacy button component variant.\n * @returns The button variant.\n */\nfunction getButtonVariant(variant?: 'primary' | 'secondary' | undefined) {\n switch (variant) {\n case 'primary':\n return 'primary';\n case 'secondary':\n return 'destructive';\n default:\n return undefined;\n }\n}\n\n/**\n * Get the children of a JSX element. If there is only one child, the child is\n * returned directly. Otherwise, the children are returned as an array.\n *\n * @param elements - The JSX elements.\n * @returns The child or children.\n */\nfunction getChildren<Type>(elements: Type[]) {\n if (elements.length === 1) {\n return elements[0];\n }\n\n return elements;\n}\n\n/**\n * Get the text of a link token.\n *\n * @param token - The link token.\n * @returns The text of the link token.\n */\nfunction getLinkText(token: Tokens.Link | Tokens.Generic) {\n if (token.tokens && token.tokens.length > 0) {\n return getChildren(token.tokens.flatMap(getTextChildFromToken));\n }\n\n return token.href;\n}\n\n/**\n * Get the text child from a list of markdown tokens.\n *\n * @param tokens - The markdown tokens.\n * @returns The text child.\n */\nfunction getTextChildFromTokens(tokens: Token[]) {\n return getChildren(tokens.flatMap(getTextChildFromToken));\n}\n\n/**\n * Get the text child from a markdown token.\n *\n * @param token - The markdown token.\n * @returns The text child.\n */\nfunction getTextChildFromToken(token: Token): TextChildren {\n switch (token.type) {\n case 'link': {\n return <Link href={token.href} children={getLinkText(token)} />;\n }\n\n case 'text':\n return token.text;\n\n case 'strong':\n return (\n <Bold>\n {\n getTextChildFromTokens(\n // Due to the way `marked` is typed, `token.tokens` can be\n // `undefined`, but it's a required field of `Tokens.Bold`, so we\n // can safely cast it to `Token[]`.\n token.tokens as Token[],\n ) as BoldChildren\n }\n </Bold>\n );\n\n case 'em':\n return (\n <Italic>\n {\n getTextChildFromTokens(\n // Due to the way `marked` is typed, `token.tokens` can be\n // `undefined`, but it's a required field of `Tokens.Bold`, so we\n // can safely cast it to `Token[]`.\n token.tokens as Token[],\n ) as ItalicChildren\n }\n </Italic>\n );\n\n default:\n return null;\n }\n}\n\n/**\n * Get all text children from a markdown string.\n *\n * @param value - The markdown string.\n * @returns The text children.\n */\nexport function getTextChildren(\n value: string,\n): (string | StandardFormattingElement | LinkElement)[] {\n const rootTokens = lexer(value, { gfm: false });\n const children: (string | StandardFormattingElement | LinkElement | null)[] =\n [];\n\n // TODO: Either fix this lint violation or explain why it's necessary to\n // ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n walkTokens(rootTokens, (token) => {\n if (token.type === 'paragraph') {\n if (children.length > 0) {\n children.push('\\n\\n');\n }\n\n const { tokens } = token as Tokens.Paragraph;\n // We do not need to consider nesting deeper than 1 level here and we can therefore cast.\n children.push(\n ...(tokens.flatMap(getTextChildFromToken) as (\n | string\n | StandardFormattingElement\n | LinkElement\n | null\n )[]),\n );\n }\n });\n\n return children.filter((child) => child !== null) as (\n | string\n | StandardFormattingElement\n | LinkElement\n )[];\n}\n\n/**\n * Validate the text size of a component. The text size is the total length of\n * all text in the component.\n *\n * @param component - The component to validate.\n * @throws An error if the text size exceeds the maximum allowed size.\n */\nfunction validateComponentTextSize(component: Component) {\n const textSize = getTotalTextLength(component);\n assert(\n textSize <= MAX_TEXT_LENGTH,\n `The text in a Snap UI may not be larger than ${\n MAX_TEXT_LENGTH / 1000\n } kB.`,\n );\n}\n\n/**\n * Get a JSX element from a legacy UI component. This supports all legacy UI\n * components, and maps them to their JSX equivalents where possible.\n *\n * This function validates the text size of the component, but does not validate\n * the total size. The total size of the component should be validated before\n * calling this function.\n *\n * @param legacyComponent - The legacy UI component.\n * @returns The JSX element.\n */\nexport function getJsxElementFromComponent(\n legacyComponent: Component,\n): JSXElement {\n validateComponentTextSize(legacyComponent);\n\n /**\n * Get the JSX element for a component. This function is recursive and will\n * call itself for child components.\n *\n * @param component - The component to convert to a JSX element.\n * @returns The JSX element.\n */\n function getElement(component: Component) {\n switch (component.type) {\n case NodeType.Address:\n return <Address address={component.value} />;\n\n case NodeType.Button:\n return (\n <Button\n name={component.name}\n variant={getButtonVariant(component.variant)}\n type={component.buttonType}\n >\n {component.value}\n </Button>\n );\n\n case NodeType.Copyable:\n return (\n <Copyable value={component.value} sensitive={component.sensitive} />\n );\n\n case NodeType.Divider:\n return <Divider />;\n\n case NodeType.Form:\n return (\n <Form name={component.name}>\n {getChildren(component.children.map(getElement))}\n </Form>\n );\n\n case NodeType.Heading:\n return <Heading children={component.value} />;\n\n case NodeType.Image:\n // `Image` supports `alt`, but the legacy `Image` component does not.\n return <Image src={component.value} />;\n\n case NodeType.Input:\n return (\n <Field label={component.label} error={component.error}>\n <Input\n name={component.name}\n type={component.inputType}\n value={component.value}\n placeholder={component.placeholder}\n />\n </Field>\n );\n\n case NodeType.Panel:\n // `Panel` is renamed to `Box` in JSX.\n return (\n <Box children={getChildren(component.children.map(getElement))} />\n );\n\n case NodeType.Row:\n return (\n <Row label={component.label} variant={component.variant}>\n {getElement(component.value) as RowChildren}\n </Row>\n );\n\n case NodeType.Spinner:\n return <Spinner />;\n\n case NodeType.Text:\n return <Text>{getChildren(getTextChildren(component.value))}</Text>;\n\n /* istanbul ignore next 2 */\n default:\n return assertExhaustive(component);\n }\n }\n\n return getElement(legacyComponent);\n}\n\n/**\n * Extract all links from a Markdown text string using the `marked` lexer.\n *\n * @param text - The markdown text string.\n * @returns A list of URLs linked to in the string.\n */\nfunction getMarkdownLinks(text: string) {\n const tokens = lexer(text, { gfm: false });\n const links: Tokens.Link[] = [];\n\n // Walk the lexed tokens and collect all link tokens\n // TODO: Either fix this lint violation or explain why it's necessary to\n // ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n walkTokens(tokens, (token) => {\n if (token.type === 'link') {\n links.push(token as Tokens.Link);\n }\n });\n\n return links;\n}\n\n/**\n * Validate a link against the phishing list.\n *\n * @param link - The link to validate.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n * @throws If the link is invalid.\n */\nexport function validateLink(\n link: string,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n try {\n const url = new URL(link);\n assert(\n ALLOWED_PROTOCOLS.includes(url.protocol),\n `Protocol must be one of: ${ALLOWED_PROTOCOLS.join(', ')}.`,\n );\n\n if (url.protocol === 'metamask:') {\n const linkData = parseMetaMaskUrl(link);\n if (linkData.snapId) {\n assert(\n getSnap(linkData.snapId),\n 'The Snap being navigated to is not installed.',\n );\n }\n } else if (url.protocol === 'mailto:') {\n const emails = url.pathname.split(',');\n for (const email of emails) {\n const hostname = email.split('@')[1];\n assert(!hostname.includes(':'));\n const href = `https://${hostname}`;\n assert(!isOnPhishingList(href), 'The specified URL is not allowed.');\n }\n\n return;\n }\n\n assert(!isOnPhishingList(url.href), 'The specified URL is not allowed.');\n } catch (error) {\n throw new Error(\n `Invalid URL: ${\n error?.code === 'ERR_ASSERTION' ? error.message : 'Unable to parse URL.'\n }`,\n );\n }\n}\n\n/**\n * Search for Markdown links in a string and checks them against the phishing\n * list.\n *\n * @param text - The text to verify.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n * @throws If the text contains a link that is not allowed.\n */\nexport function validateTextLinks(\n text: string,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n const links = getMarkdownLinks(text);\n\n for (const link of links) {\n validateLink(link.href, isOnPhishingList, getSnap);\n }\n}\n\n/**\n * Validate the asset selector component.\n *\n * @param address - The address of the account to pull the assets from.\n * @param getAccountByAddress - A function to get an account by its address.\n *\n * @throws If the asset selector is invalid.\n */\nexport function validateAssetSelector(\n address: CaipAccountId,\n getAccountByAddress: (address: CaipAccountId) => InternalAccount | undefined,\n) {\n const account = getAccountByAddress(address);\n\n assert(account, `Could not find account for address: ${address}`);\n}\n\n/**\n * Walk a JSX tree and validate elements.\n * This function validates Links and AssetSelectors.\n *\n * @param node - The JSX node to walk.\n * @param hooks - The hooks to use for validation.\n * @param hooks.isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param hooks.getSnap - The function that returns a snap if installed, undefined otherwise.\n * @param hooks.getAccountByAddress - The function that returns an account by address.\n */\nexport function validateJsxElements(\n node: JSXElement,\n {\n isOnPhishingList,\n getSnap,\n getAccountByAddress,\n }: {\n isOnPhishingList: (url: string) => boolean;\n getSnap: (id: string) => Snap | undefined;\n getAccountByAddress: (\n address: CaipAccountId,\n ) => InternalAccount | undefined;\n },\n) {\n walkJsx(node, (childNode) => {\n switch (childNode.type) {\n case 'Link':\n validateLink(childNode.props.href, isOnPhishingList, getSnap);\n break;\n case 'AssetSelector':\n validateAssetSelector(\n // We assume that the address part of the CAIP-10 account ID are the same, as\n // that is already validated in the struct.\n childNode.props.addresses[0],\n getAccountByAddress,\n );\n break;\n default:\n break;\n }\n });\n}\n\n/**\n * Calculate the total length of all text in the component.\n *\n * @param component - A custom UI component.\n * @returns The total length of all text components in the component.\n */\nexport function getTotalTextLength(component: Component): number {\n const { type } = component;\n\n switch (type) {\n case NodeType.Panel:\n return component.children.reduce<number>(\n (sum, node) => sum + getTotalTextLength(node),\n 0,\n );\n\n case NodeType.Row:\n return getTotalTextLength(component.value);\n\n case NodeType.Text:\n return component.value.length;\n\n default:\n return 0;\n }\n}\n\n/**\n * Check if a JSX element has children.\n *\n * @param element - A JSX element.\n * @returns `true` if the element has children, `false` otherwise.\n */\nexport function hasChildren<Element extends JSXElement>(\n element: Element,\n): element is Element & {\n props: { children: Nestable<JSXElement | string> };\n} {\n return hasProperty(element.props, 'children');\n}\n\n/**\n * Filter a JSX child to remove `null`, `undefined`, plain booleans, and empty\n * strings.\n *\n * @param child - The JSX child to filter.\n * @returns `true` if the child is not `null`, `undefined`, a plain boolean, or\n * an empty string, `false` otherwise.\n */\nfunction filterJsxChild(child: JSXElement | string | boolean | null): boolean {\n return Boolean(child) && child !== true;\n}\n\n/**\n * Get the children of a JSX element as an array. If the element has only one\n * child, the child is returned as an array.\n *\n * @param element - A JSX element.\n * @returns The children of the element.\n */\nexport function getJsxChildren(element: JSXElement): (JSXElement | string)[] {\n if (hasChildren(element)) {\n if (Array.isArray(element.props.children)) {\n // @ts-expect-error - Each member of the union type has signatures, but\n // none of those signatures are compatible with each other.\n return element.props.children.filter(filterJsxChild).flat(Infinity);\n }\n\n if (element.props.children) {\n return [element.props.children];\n }\n }\n\n return [];\n}\n\n/**\n * Walk a JSX tree and call a callback on each node.\n *\n * @param node - The JSX node to walk.\n * @param callback - The callback to call on each node.\n * @param depth - The current depth in the JSX tree for a walk.\n * @returns The result of the callback, if any.\n */\nexport function walkJsx<Value>(\n node: JSXElement | JSXElement[],\n callback: (node: JSXElement, depth: number) => Value | undefined,\n depth = 0,\n): Value | undefined {\n if (Array.isArray(node)) {\n for (const child of node) {\n const childResult = walkJsx(child, callback, depth);\n if (childResult !== undefined) {\n return childResult;\n }\n }\n\n return undefined;\n }\n\n const result = callback(node, depth);\n if (result !== undefined) {\n return result;\n }\n\n if (\n hasProperty(node, 'props') &&\n isPlainObject(node.props) &&\n hasProperty(node.props, 'children')\n ) {\n const children = getJsxChildren(node);\n for (const child of children) {\n if (isPlainObject(child)) {\n const childResult = walkJsx(child, callback, depth + 1);\n if (childResult !== undefined) {\n return childResult;\n }\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Serialise a JSX prop to a string.\n *\n * @param prop - The JSX prop.\n * @returns The serialised JSX prop.\n */\nfunction serialiseProp(prop: unknown): string {\n if (typeof prop === 'string') {\n return `\"${prop}\"`;\n }\n\n return `{${JSON.stringify(prop)}}`;\n}\n\n/**\n * Serialise JSX props to a string.\n *\n * @param props - The JSX props.\n * @returns The serialised JSX props.\n */\nfunction serialiseProps(props: Record<string, unknown>): string {\n return Object.entries(props)\n .filter(([key]) => key !== 'children')\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => ` ${key}=${serialiseProp(value)}`)\n .join('');\n}\n\n/**\n * Serialise a JSX node to a string.\n *\n * @param node - The JSX node.\n * @param indentation - The indentation level. Defaults to `0`. This should not\n * be set by the caller, as it is used for recursion.\n * @returns The serialised JSX node.\n */\nexport function serialiseJsx(node: SnapNode, indentation = 0): string {\n if (Array.isArray(node)) {\n return node.map((child) => serialiseJsx(child, indentation)).join('');\n }\n\n const indent = ' '.repeat(indentation);\n if (typeof node === 'string') {\n return `${indent}${node}\\n`;\n }\n\n if (!node) {\n return '';\n }\n\n const { type, props } = node as GenericSnapElement;\n const trailingNewline = indentation > 0 ? '\\n' : '';\n\n if (hasProperty(props, 'children')) {\n const children = serialiseJsx(props.children as SnapNode, indentation + 1);\n return `${indent}<${type}${serialiseProps(\n props,\n )}>\\n${children}${indent}</${type}>${trailingNewline}`;\n }\n\n return `${indent}<${type}${serialiseProps(props)} />${trailingNewline}`;\n}\n"]}
package/dist/ui.d.cts CHANGED
@@ -1,5 +1,6 @@
1
- import type { Component } from "@metamask/snaps-sdk";
1
+ import type { CaipAccountId, Component } from "@metamask/snaps-sdk";
2
2
  import type { JSXElement, LinkElement, Nestable, SnapNode, StandardFormattingElement } from "@metamask/snaps-sdk/jsx";
3
+ import type { InternalAccount } from "./account.cjs";
3
4
  import type { Snap } from "./snaps.cjs";
4
5
  /**
5
6
  * Get all text children from a markdown string.
@@ -42,15 +43,30 @@ export declare function validateLink(link: string, isOnPhishingList: (url: strin
42
43
  */
43
44
  export declare function validateTextLinks(text: string, isOnPhishingList: (url: string) => boolean, getSnap: (id: string) => Snap | undefined): void;
44
45
  /**
45
- * Walk a JSX tree and validate each {@link LinkElement} node against the
46
- * phishing list.
46
+ * Validate the asset selector component.
47
+ *
48
+ * @param address - The address of the account to pull the assets from.
49
+ * @param getAccountByAddress - A function to get an account by its address.
50
+ *
51
+ * @throws If the asset selector is invalid.
52
+ */
53
+ export declare function validateAssetSelector(address: CaipAccountId, getAccountByAddress: (address: CaipAccountId) => InternalAccount | undefined): void;
54
+ /**
55
+ * Walk a JSX tree and validate elements.
56
+ * This function validates Links and AssetSelectors.
47
57
  *
48
58
  * @param node - The JSX node to walk.
49
- * @param isOnPhishingList - The function that checks the link against the
59
+ * @param hooks - The hooks to use for validation.
60
+ * @param hooks.isOnPhishingList - The function that checks the link against the
50
61
  * phishing list.
51
- * @param getSnap - The function that returns a snap if installed, undefined otherwise.
62
+ * @param hooks.getSnap - The function that returns a snap if installed, undefined otherwise.
63
+ * @param hooks.getAccountByAddress - The function that returns an account by address.
52
64
  */
53
- export declare function validateJsxLinks(node: JSXElement, isOnPhishingList: (url: string) => boolean, getSnap: (id: string) => Snap | undefined): void;
65
+ export declare function validateJsxElements(node: JSXElement, { isOnPhishingList, getSnap, getAccountByAddress, }: {
66
+ isOnPhishingList: (url: string) => boolean;
67
+ getSnap: (id: string) => Snap | undefined;
68
+ getAccountByAddress: (address: CaipAccountId) => InternalAccount | undefined;
69
+ }): void;
54
70
  /**
55
71
  * Calculate the total length of all text in the component.
56
72
  *
package/dist/ui.d.cts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.cts","sourceRoot":"","sources":["../src/ui.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,KAAK,EAIV,UAAU,EACV,WAAW,EACX,QAAQ,EAER,QAAQ,EACR,yBAAyB,EAE1B,gCAAgC;AA4BjC,OAAO,KAAK,EAAE,IAAI,EAAE,oBAAgB;AA8GpC;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,GACZ,CAAC,MAAM,GAAG,yBAAyB,GAAG,WAAW,CAAC,EAAE,CAgCtD;AAmBD;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,GACzB,UAAU,CAsFZ;AAyBD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAqC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAO1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAS1C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAmB/D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,UAAU,EACpD,OAAO,EAAE,OAAO,GACf,OAAO,IAAI,OAAO,GAAG;IACtB,KAAK,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,CAAA;KAAE,CAAC;CACpD,CAEA;AAcD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC,EAAE,CAc3E;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAC3B,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,EAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,GAAG,SAAS,EAChE,KAAK,SAAI,GACR,KAAK,GAAG,SAAS,CAkCnB;AA8BD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,SAAI,GAAG,MAAM,CAyBpE"}
1
+ {"version":3,"file":"ui.d.cts","sourceRoot":"","sources":["../src/ui.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,4BAA4B;AAEpE,OAAO,KAAK,EAIV,UAAU,EACV,WAAW,EACX,QAAQ,EAER,QAAQ,EACR,yBAAyB,EAE1B,gCAAgC;AA4BjC,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAkB;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,oBAAgB;AA8GpC;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,GACZ,CAAC,MAAM,GAAG,yBAAyB,GAAG,WAAW,CAAC,EAAE,CAgCtD;AAmBD;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,GACzB,UAAU,CAsFZ;AAyBD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAqC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAO1C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,aAAa,EACtB,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,eAAe,GAAG,SAAS,QAK7E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,UAAU,EAChB,EACE,gBAAgB,EAChB,OAAO,EACP,mBAAmB,GACpB,EAAE;IACD,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;IAC1C,mBAAmB,EAAE,CACnB,OAAO,EAAE,aAAa,KACnB,eAAe,GAAG,SAAS,CAAC;CAClC,QAmBF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAmB/D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,UAAU,EACpD,OAAO,EAAE,OAAO,GACf,OAAO,IAAI,OAAO,GAAG;IACtB,KAAK,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,CAAA;KAAE,CAAC;CACpD,CAEA;AAcD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC,EAAE,CAc3E;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAC3B,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,EAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,GAAG,SAAS,EAChE,KAAK,SAAI,GACR,KAAK,GAAG,SAAS,CAkCnB;AA8BD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,SAAI,GAAG,MAAM,CAyBpE"}
package/dist/ui.d.mts CHANGED
@@ -1,5 +1,6 @@
1
- import type { Component } from "@metamask/snaps-sdk";
1
+ import type { CaipAccountId, Component } from "@metamask/snaps-sdk";
2
2
  import type { JSXElement, LinkElement, Nestable, SnapNode, StandardFormattingElement } from "@metamask/snaps-sdk/jsx";
3
+ import type { InternalAccount } from "./account.mjs";
3
4
  import type { Snap } from "./snaps.mjs";
4
5
  /**
5
6
  * Get all text children from a markdown string.
@@ -42,15 +43,30 @@ export declare function validateLink(link: string, isOnPhishingList: (url: strin
42
43
  */
43
44
  export declare function validateTextLinks(text: string, isOnPhishingList: (url: string) => boolean, getSnap: (id: string) => Snap | undefined): void;
44
45
  /**
45
- * Walk a JSX tree and validate each {@link LinkElement} node against the
46
- * phishing list.
46
+ * Validate the asset selector component.
47
+ *
48
+ * @param address - The address of the account to pull the assets from.
49
+ * @param getAccountByAddress - A function to get an account by its address.
50
+ *
51
+ * @throws If the asset selector is invalid.
52
+ */
53
+ export declare function validateAssetSelector(address: CaipAccountId, getAccountByAddress: (address: CaipAccountId) => InternalAccount | undefined): void;
54
+ /**
55
+ * Walk a JSX tree and validate elements.
56
+ * This function validates Links and AssetSelectors.
47
57
  *
48
58
  * @param node - The JSX node to walk.
49
- * @param isOnPhishingList - The function that checks the link against the
59
+ * @param hooks - The hooks to use for validation.
60
+ * @param hooks.isOnPhishingList - The function that checks the link against the
50
61
  * phishing list.
51
- * @param getSnap - The function that returns a snap if installed, undefined otherwise.
62
+ * @param hooks.getSnap - The function that returns a snap if installed, undefined otherwise.
63
+ * @param hooks.getAccountByAddress - The function that returns an account by address.
52
64
  */
53
- export declare function validateJsxLinks(node: JSXElement, isOnPhishingList: (url: string) => boolean, getSnap: (id: string) => Snap | undefined): void;
65
+ export declare function validateJsxElements(node: JSXElement, { isOnPhishingList, getSnap, getAccountByAddress, }: {
66
+ isOnPhishingList: (url: string) => boolean;
67
+ getSnap: (id: string) => Snap | undefined;
68
+ getAccountByAddress: (address: CaipAccountId) => InternalAccount | undefined;
69
+ }): void;
54
70
  /**
55
71
  * Calculate the total length of all text in the component.
56
72
  *
package/dist/ui.d.mts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.mts","sourceRoot":"","sources":["../src/ui.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,KAAK,EAIV,UAAU,EACV,WAAW,EACX,QAAQ,EAER,QAAQ,EACR,yBAAyB,EAE1B,gCAAgC;AA4BjC,OAAO,KAAK,EAAE,IAAI,EAAE,oBAAgB;AA8GpC;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,GACZ,CAAC,MAAM,GAAG,yBAAyB,GAAG,WAAW,CAAC,EAAE,CAgCtD;AAmBD;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,GACzB,UAAU,CAsFZ;AAyBD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAqC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAO1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAS1C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAmB/D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,UAAU,EACpD,OAAO,EAAE,OAAO,GACf,OAAO,IAAI,OAAO,GAAG;IACtB,KAAK,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,CAAA;KAAE,CAAC;CACpD,CAEA;AAcD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC,EAAE,CAc3E;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAC3B,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,EAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,GAAG,SAAS,EAChE,KAAK,SAAI,GACR,KAAK,GAAG,SAAS,CAkCnB;AA8BD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,SAAI,GAAG,MAAM,CAyBpE"}
1
+ {"version":3,"file":"ui.d.mts","sourceRoot":"","sources":["../src/ui.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,4BAA4B;AAEpE,OAAO,KAAK,EAIV,UAAU,EACV,WAAW,EACX,QAAQ,EAER,QAAQ,EACR,yBAAyB,EAE1B,gCAAgC;AA4BjC,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAkB;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,oBAAgB;AA8GpC;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,GACZ,CAAC,MAAM,GAAG,yBAAyB,GAAG,WAAW,CAAC,EAAE,CAgCtD;AAmBD;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,GACzB,UAAU,CAsFZ;AAyBD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAqC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EAC1C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,QAO1C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,aAAa,EACtB,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,eAAe,GAAG,SAAS,QAK7E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,UAAU,EAChB,EACE,gBAAgB,EAChB,OAAO,EACP,mBAAmB,GACpB,EAAE;IACD,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;IAC1C,mBAAmB,EAAE,CACnB,OAAO,EAAE,aAAa,KACnB,eAAe,GAAG,SAAS,CAAC;CAClC,QAmBF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAmB/D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,UAAU,EACpD,OAAO,EAAE,OAAO,GACf,OAAO,IAAI,OAAO,GAAG;IACtB,KAAK,EAAE;QAAE,QAAQ,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,CAAA;KAAE,CAAC;CACpD,CAEA;AAcD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC,EAAE,CAc3E;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAC3B,IAAI,EAAE,UAAU,GAAG,UAAU,EAAE,EAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,GAAG,SAAS,EAChE,KAAK,SAAI,GACR,KAAK,GAAG,SAAS,CAkCnB;AA8BD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,SAAI,GAAG,MAAM,CAyBpE"}
package/dist/ui.mjs CHANGED
@@ -247,20 +247,43 @@ export function validateTextLinks(text, isOnPhishingList, getSnap) {
247
247
  }
248
248
  }
249
249
  /**
250
- * Walk a JSX tree and validate each {@link LinkElement} node against the
251
- * phishing list.
250
+ * Validate the asset selector component.
251
+ *
252
+ * @param address - The address of the account to pull the assets from.
253
+ * @param getAccountByAddress - A function to get an account by its address.
254
+ *
255
+ * @throws If the asset selector is invalid.
256
+ */
257
+ export function validateAssetSelector(address, getAccountByAddress) {
258
+ const account = getAccountByAddress(address);
259
+ assert(account, `Could not find account for address: ${address}`);
260
+ }
261
+ /**
262
+ * Walk a JSX tree and validate elements.
263
+ * This function validates Links and AssetSelectors.
252
264
  *
253
265
  * @param node - The JSX node to walk.
254
- * @param isOnPhishingList - The function that checks the link against the
266
+ * @param hooks - The hooks to use for validation.
267
+ * @param hooks.isOnPhishingList - The function that checks the link against the
255
268
  * phishing list.
256
- * @param getSnap - The function that returns a snap if installed, undefined otherwise.
269
+ * @param hooks.getSnap - The function that returns a snap if installed, undefined otherwise.
270
+ * @param hooks.getAccountByAddress - The function that returns an account by address.
257
271
  */
258
- export function validateJsxLinks(node, isOnPhishingList, getSnap) {
272
+ export function validateJsxElements(node, { isOnPhishingList, getSnap, getAccountByAddress, }) {
259
273
  walkJsx(node, (childNode) => {
260
- if (childNode.type !== 'Link') {
261
- return;
274
+ switch (childNode.type) {
275
+ case 'Link':
276
+ validateLink(childNode.props.href, isOnPhishingList, getSnap);
277
+ break;
278
+ case 'AssetSelector':
279
+ validateAssetSelector(
280
+ // We assume that the address part of the CAIP-10 account ID are the same, as
281
+ // that is already validated in the struct.
282
+ childNode.props.addresses[0], getAccountByAddress);
283
+ break;
284
+ default:
285
+ break;
262
286
  }
263
- validateLink(childNode.props.href, isOnPhishingList, getSnap);
264
287
  });
265
288
  }
266
289
  /**
package/dist/ui.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ui.mjs","sourceRoot":"","sources":["../src/ui.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { Component } from '@metamask/snaps-sdk';\nimport { NodeType } from '@metamask/snaps-sdk';\nimport type {\n BoldChildren,\n GenericSnapElement,\n ItalicChildren,\n JSXElement,\n LinkElement,\n Nestable,\n RowChildren,\n SnapNode,\n StandardFormattingElement,\n TextChildren,\n} from '@metamask/snaps-sdk/jsx';\nimport {\n Italic,\n Link,\n Bold,\n Row,\n Text,\n Field,\n Image,\n Input,\n Heading,\n Form,\n Divider,\n Spinner,\n Copyable,\n Box,\n Button,\n Address,\n} from '@metamask/snaps-sdk/jsx';\nimport {\n assert,\n assertExhaustive,\n hasProperty,\n isPlainObject,\n} from '@metamask/utils';\nimport { lexer, walkTokens } from 'marked';\nimport type { Token, Tokens } from 'marked';\n\nimport type { Snap } from './snaps';\nimport { parseMetaMaskUrl } from './url';\n\nconst MAX_TEXT_LENGTH = 50_000; // 50 kb\nconst ALLOWED_PROTOCOLS = ['https:', 'mailto:', 'metamask:'];\n\n/**\n * Get the button variant from a legacy button component variant.\n *\n * @param variant - The legacy button component variant.\n * @returns The button variant.\n */\nfunction getButtonVariant(variant?: 'primary' | 'secondary' | undefined) {\n switch (variant) {\n case 'primary':\n return 'primary';\n case 'secondary':\n return 'destructive';\n default:\n return undefined;\n }\n}\n\n/**\n * Get the children of a JSX element. If there is only one child, the child is\n * returned directly. Otherwise, the children are returned as an array.\n *\n * @param elements - The JSX elements.\n * @returns The child or children.\n */\nfunction getChildren<Type>(elements: Type[]) {\n if (elements.length === 1) {\n return elements[0];\n }\n\n return elements;\n}\n\n/**\n * Get the text of a link token.\n *\n * @param token - The link token.\n * @returns The text of the link token.\n */\nfunction getLinkText(token: Tokens.Link | Tokens.Generic) {\n if (token.tokens && token.tokens.length > 0) {\n return getChildren(token.tokens.flatMap(getTextChildFromToken));\n }\n\n return token.href;\n}\n\n/**\n * Get the text child from a list of markdown tokens.\n *\n * @param tokens - The markdown tokens.\n * @returns The text child.\n */\nfunction getTextChildFromTokens(tokens: Token[]) {\n return getChildren(tokens.flatMap(getTextChildFromToken));\n}\n\n/**\n * Get the text child from a markdown token.\n *\n * @param token - The markdown token.\n * @returns The text child.\n */\nfunction getTextChildFromToken(token: Token): TextChildren {\n switch (token.type) {\n case 'link': {\n return <Link href={token.href} children={getLinkText(token)} />;\n }\n\n case 'text':\n return token.text;\n\n case 'strong':\n return (\n <Bold>\n {\n getTextChildFromTokens(\n // Due to the way `marked` is typed, `token.tokens` can be\n // `undefined`, but it's a required field of `Tokens.Bold`, so we\n // can safely cast it to `Token[]`.\n token.tokens as Token[],\n ) as BoldChildren\n }\n </Bold>\n );\n\n case 'em':\n return (\n <Italic>\n {\n getTextChildFromTokens(\n // Due to the way `marked` is typed, `token.tokens` can be\n // `undefined`, but it's a required field of `Tokens.Bold`, so we\n // can safely cast it to `Token[]`.\n token.tokens as Token[],\n ) as ItalicChildren\n }\n </Italic>\n );\n\n default:\n return null;\n }\n}\n\n/**\n * Get all text children from a markdown string.\n *\n * @param value - The markdown string.\n * @returns The text children.\n */\nexport function getTextChildren(\n value: string,\n): (string | StandardFormattingElement | LinkElement)[] {\n const rootTokens = lexer(value, { gfm: false });\n const children: (string | StandardFormattingElement | LinkElement | null)[] =\n [];\n\n // TODO: Either fix this lint violation or explain why it's necessary to\n // ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n walkTokens(rootTokens, (token) => {\n if (token.type === 'paragraph') {\n if (children.length > 0) {\n children.push('\\n\\n');\n }\n\n const { tokens } = token as Tokens.Paragraph;\n // We do not need to consider nesting deeper than 1 level here and we can therefore cast.\n children.push(\n ...(tokens.flatMap(getTextChildFromToken) as (\n | string\n | StandardFormattingElement\n | LinkElement\n | null\n )[]),\n );\n }\n });\n\n return children.filter((child) => child !== null) as (\n | string\n | StandardFormattingElement\n | LinkElement\n )[];\n}\n\n/**\n * Validate the text size of a component. The text size is the total length of\n * all text in the component.\n *\n * @param component - The component to validate.\n * @throws An error if the text size exceeds the maximum allowed size.\n */\nfunction validateComponentTextSize(component: Component) {\n const textSize = getTotalTextLength(component);\n assert(\n textSize <= MAX_TEXT_LENGTH,\n `The text in a Snap UI may not be larger than ${\n MAX_TEXT_LENGTH / 1000\n } kB.`,\n );\n}\n\n/**\n * Get a JSX element from a legacy UI component. This supports all legacy UI\n * components, and maps them to their JSX equivalents where possible.\n *\n * This function validates the text size of the component, but does not validate\n * the total size. The total size of the component should be validated before\n * calling this function.\n *\n * @param legacyComponent - The legacy UI component.\n * @returns The JSX element.\n */\nexport function getJsxElementFromComponent(\n legacyComponent: Component,\n): JSXElement {\n validateComponentTextSize(legacyComponent);\n\n /**\n * Get the JSX element for a component. This function is recursive and will\n * call itself for child components.\n *\n * @param component - The component to convert to a JSX element.\n * @returns The JSX element.\n */\n function getElement(component: Component) {\n switch (component.type) {\n case NodeType.Address:\n return <Address address={component.value} />;\n\n case NodeType.Button:\n return (\n <Button\n name={component.name}\n variant={getButtonVariant(component.variant)}\n type={component.buttonType}\n >\n {component.value}\n </Button>\n );\n\n case NodeType.Copyable:\n return (\n <Copyable value={component.value} sensitive={component.sensitive} />\n );\n\n case NodeType.Divider:\n return <Divider />;\n\n case NodeType.Form:\n return (\n <Form name={component.name}>\n {getChildren(component.children.map(getElement))}\n </Form>\n );\n\n case NodeType.Heading:\n return <Heading children={component.value} />;\n\n case NodeType.Image:\n // `Image` supports `alt`, but the legacy `Image` component does not.\n return <Image src={component.value} />;\n\n case NodeType.Input:\n return (\n <Field label={component.label} error={component.error}>\n <Input\n name={component.name}\n type={component.inputType}\n value={component.value}\n placeholder={component.placeholder}\n />\n </Field>\n );\n\n case NodeType.Panel:\n // `Panel` is renamed to `Box` in JSX.\n return (\n <Box children={getChildren(component.children.map(getElement))} />\n );\n\n case NodeType.Row:\n return (\n <Row label={component.label} variant={component.variant}>\n {getElement(component.value) as RowChildren}\n </Row>\n );\n\n case NodeType.Spinner:\n return <Spinner />;\n\n case NodeType.Text:\n return <Text>{getChildren(getTextChildren(component.value))}</Text>;\n\n /* istanbul ignore next 2 */\n default:\n return assertExhaustive(component);\n }\n }\n\n return getElement(legacyComponent);\n}\n\n/**\n * Extract all links from a Markdown text string using the `marked` lexer.\n *\n * @param text - The markdown text string.\n * @returns A list of URLs linked to in the string.\n */\nfunction getMarkdownLinks(text: string) {\n const tokens = lexer(text, { gfm: false });\n const links: Tokens.Link[] = [];\n\n // Walk the lexed tokens and collect all link tokens\n // TODO: Either fix this lint violation or explain why it's necessary to\n // ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n walkTokens(tokens, (token) => {\n if (token.type === 'link') {\n links.push(token as Tokens.Link);\n }\n });\n\n return links;\n}\n\n/**\n * Validate a link against the phishing list.\n *\n * @param link - The link to validate.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n * @throws If the link is invalid.\n */\nexport function validateLink(\n link: string,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n try {\n const url = new URL(link);\n assert(\n ALLOWED_PROTOCOLS.includes(url.protocol),\n `Protocol must be one of: ${ALLOWED_PROTOCOLS.join(', ')}.`,\n );\n\n if (url.protocol === 'metamask:') {\n const linkData = parseMetaMaskUrl(link);\n if (linkData.snapId) {\n assert(\n getSnap(linkData.snapId),\n 'The Snap being navigated to is not installed.',\n );\n }\n } else if (url.protocol === 'mailto:') {\n const emails = url.pathname.split(',');\n for (const email of emails) {\n const hostname = email.split('@')[1];\n assert(!hostname.includes(':'));\n const href = `https://${hostname}`;\n assert(!isOnPhishingList(href), 'The specified URL is not allowed.');\n }\n\n return;\n }\n\n assert(!isOnPhishingList(url.href), 'The specified URL is not allowed.');\n } catch (error) {\n throw new Error(\n `Invalid URL: ${\n error?.code === 'ERR_ASSERTION' ? error.message : 'Unable to parse URL.'\n }`,\n );\n }\n}\n\n/**\n * Search for Markdown links in a string and checks them against the phishing\n * list.\n *\n * @param text - The text to verify.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n * @throws If the text contains a link that is not allowed.\n */\nexport function validateTextLinks(\n text: string,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n const links = getMarkdownLinks(text);\n\n for (const link of links) {\n validateLink(link.href, isOnPhishingList, getSnap);\n }\n}\n\n/**\n * Walk a JSX tree and validate each {@link LinkElement} node against the\n * phishing list.\n *\n * @param node - The JSX node to walk.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n */\nexport function validateJsxLinks(\n node: JSXElement,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n walkJsx(node, (childNode) => {\n if (childNode.type !== 'Link') {\n return;\n }\n\n validateLink(childNode.props.href, isOnPhishingList, getSnap);\n });\n}\n\n/**\n * Calculate the total length of all text in the component.\n *\n * @param component - A custom UI component.\n * @returns The total length of all text components in the component.\n */\nexport function getTotalTextLength(component: Component): number {\n const { type } = component;\n\n switch (type) {\n case NodeType.Panel:\n return component.children.reduce<number>(\n (sum, node) => sum + getTotalTextLength(node),\n 0,\n );\n\n case NodeType.Row:\n return getTotalTextLength(component.value);\n\n case NodeType.Text:\n return component.value.length;\n\n default:\n return 0;\n }\n}\n\n/**\n * Check if a JSX element has children.\n *\n * @param element - A JSX element.\n * @returns `true` if the element has children, `false` otherwise.\n */\nexport function hasChildren<Element extends JSXElement>(\n element: Element,\n): element is Element & {\n props: { children: Nestable<JSXElement | string> };\n} {\n return hasProperty(element.props, 'children');\n}\n\n/**\n * Filter a JSX child to remove `null`, `undefined`, plain booleans, and empty\n * strings.\n *\n * @param child - The JSX child to filter.\n * @returns `true` if the child is not `null`, `undefined`, a plain boolean, or\n * an empty string, `false` otherwise.\n */\nfunction filterJsxChild(child: JSXElement | string | boolean | null): boolean {\n return Boolean(child) && child !== true;\n}\n\n/**\n * Get the children of a JSX element as an array. If the element has only one\n * child, the child is returned as an array.\n *\n * @param element - A JSX element.\n * @returns The children of the element.\n */\nexport function getJsxChildren(element: JSXElement): (JSXElement | string)[] {\n if (hasChildren(element)) {\n if (Array.isArray(element.props.children)) {\n // @ts-expect-error - Each member of the union type has signatures, but\n // none of those signatures are compatible with each other.\n return element.props.children.filter(filterJsxChild).flat(Infinity);\n }\n\n if (element.props.children) {\n return [element.props.children];\n }\n }\n\n return [];\n}\n\n/**\n * Walk a JSX tree and call a callback on each node.\n *\n * @param node - The JSX node to walk.\n * @param callback - The callback to call on each node.\n * @param depth - The current depth in the JSX tree for a walk.\n * @returns The result of the callback, if any.\n */\nexport function walkJsx<Value>(\n node: JSXElement | JSXElement[],\n callback: (node: JSXElement, depth: number) => Value | undefined,\n depth = 0,\n): Value | undefined {\n if (Array.isArray(node)) {\n for (const child of node) {\n const childResult = walkJsx(child, callback, depth);\n if (childResult !== undefined) {\n return childResult;\n }\n }\n\n return undefined;\n }\n\n const result = callback(node, depth);\n if (result !== undefined) {\n return result;\n }\n\n if (\n hasProperty(node, 'props') &&\n isPlainObject(node.props) &&\n hasProperty(node.props, 'children')\n ) {\n const children = getJsxChildren(node);\n for (const child of children) {\n if (isPlainObject(child)) {\n const childResult = walkJsx(child, callback, depth + 1);\n if (childResult !== undefined) {\n return childResult;\n }\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Serialise a JSX prop to a string.\n *\n * @param prop - The JSX prop.\n * @returns The serialised JSX prop.\n */\nfunction serialiseProp(prop: unknown): string {\n if (typeof prop === 'string') {\n return `\"${prop}\"`;\n }\n\n return `{${JSON.stringify(prop)}}`;\n}\n\n/**\n * Serialise JSX props to a string.\n *\n * @param props - The JSX props.\n * @returns The serialised JSX props.\n */\nfunction serialiseProps(props: Record<string, unknown>): string {\n return Object.entries(props)\n .filter(([key]) => key !== 'children')\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => ` ${key}=${serialiseProp(value)}`)\n .join('');\n}\n\n/**\n * Serialise a JSX node to a string.\n *\n * @param node - The JSX node.\n * @param indentation - The indentation level. Defaults to `0`. This should not\n * be set by the caller, as it is used for recursion.\n * @returns The serialised JSX node.\n */\nexport function serialiseJsx(node: SnapNode, indentation = 0): string {\n if (Array.isArray(node)) {\n return node.map((child) => serialiseJsx(child, indentation)).join('');\n }\n\n const indent = ' '.repeat(indentation);\n if (typeof node === 'string') {\n return `${indent}${node}\\n`;\n }\n\n if (!node) {\n return '';\n }\n\n const { type, props } = node as GenericSnapElement;\n const trailingNewline = indentation > 0 ? '\\n' : '';\n\n if (hasProperty(props, 'children')) {\n const children = serialiseJsx(props.children as SnapNode, indentation + 1);\n return `${indent}<${type}${serialiseProps(\n props,\n )}>\\n${children}${indent}</${type}>${trailingNewline}`;\n }\n\n return `${indent}<${type}${serialiseProps(props)} />${trailingNewline}`;\n}\n"]}
1
+ {"version":3,"file":"ui.mjs","sourceRoot":"","sources":["../src/ui.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { CaipAccountId, Component } from '@metamask/snaps-sdk';\nimport { NodeType } from '@metamask/snaps-sdk';\nimport type {\n BoldChildren,\n GenericSnapElement,\n ItalicChildren,\n JSXElement,\n LinkElement,\n Nestable,\n RowChildren,\n SnapNode,\n StandardFormattingElement,\n TextChildren,\n} from '@metamask/snaps-sdk/jsx';\nimport {\n Italic,\n Link,\n Bold,\n Row,\n Text,\n Field,\n Image,\n Input,\n Heading,\n Form,\n Divider,\n Spinner,\n Copyable,\n Box,\n Button,\n Address,\n} from '@metamask/snaps-sdk/jsx';\nimport {\n assert,\n assertExhaustive,\n hasProperty,\n isPlainObject,\n} from '@metamask/utils';\nimport { lexer, walkTokens } from 'marked';\nimport type { Token, Tokens } from 'marked';\n\nimport type { InternalAccount } from './account';\nimport type { Snap } from './snaps';\nimport { parseMetaMaskUrl } from './url';\n\nconst MAX_TEXT_LENGTH = 50_000; // 50 kb\nconst ALLOWED_PROTOCOLS = ['https:', 'mailto:', 'metamask:'];\n\n/**\n * Get the button variant from a legacy button component variant.\n *\n * @param variant - The legacy button component variant.\n * @returns The button variant.\n */\nfunction getButtonVariant(variant?: 'primary' | 'secondary' | undefined) {\n switch (variant) {\n case 'primary':\n return 'primary';\n case 'secondary':\n return 'destructive';\n default:\n return undefined;\n }\n}\n\n/**\n * Get the children of a JSX element. If there is only one child, the child is\n * returned directly. Otherwise, the children are returned as an array.\n *\n * @param elements - The JSX elements.\n * @returns The child or children.\n */\nfunction getChildren<Type>(elements: Type[]) {\n if (elements.length === 1) {\n return elements[0];\n }\n\n return elements;\n}\n\n/**\n * Get the text of a link token.\n *\n * @param token - The link token.\n * @returns The text of the link token.\n */\nfunction getLinkText(token: Tokens.Link | Tokens.Generic) {\n if (token.tokens && token.tokens.length > 0) {\n return getChildren(token.tokens.flatMap(getTextChildFromToken));\n }\n\n return token.href;\n}\n\n/**\n * Get the text child from a list of markdown tokens.\n *\n * @param tokens - The markdown tokens.\n * @returns The text child.\n */\nfunction getTextChildFromTokens(tokens: Token[]) {\n return getChildren(tokens.flatMap(getTextChildFromToken));\n}\n\n/**\n * Get the text child from a markdown token.\n *\n * @param token - The markdown token.\n * @returns The text child.\n */\nfunction getTextChildFromToken(token: Token): TextChildren {\n switch (token.type) {\n case 'link': {\n return <Link href={token.href} children={getLinkText(token)} />;\n }\n\n case 'text':\n return token.text;\n\n case 'strong':\n return (\n <Bold>\n {\n getTextChildFromTokens(\n // Due to the way `marked` is typed, `token.tokens` can be\n // `undefined`, but it's a required field of `Tokens.Bold`, so we\n // can safely cast it to `Token[]`.\n token.tokens as Token[],\n ) as BoldChildren\n }\n </Bold>\n );\n\n case 'em':\n return (\n <Italic>\n {\n getTextChildFromTokens(\n // Due to the way `marked` is typed, `token.tokens` can be\n // `undefined`, but it's a required field of `Tokens.Bold`, so we\n // can safely cast it to `Token[]`.\n token.tokens as Token[],\n ) as ItalicChildren\n }\n </Italic>\n );\n\n default:\n return null;\n }\n}\n\n/**\n * Get all text children from a markdown string.\n *\n * @param value - The markdown string.\n * @returns The text children.\n */\nexport function getTextChildren(\n value: string,\n): (string | StandardFormattingElement | LinkElement)[] {\n const rootTokens = lexer(value, { gfm: false });\n const children: (string | StandardFormattingElement | LinkElement | null)[] =\n [];\n\n // TODO: Either fix this lint violation or explain why it's necessary to\n // ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n walkTokens(rootTokens, (token) => {\n if (token.type === 'paragraph') {\n if (children.length > 0) {\n children.push('\\n\\n');\n }\n\n const { tokens } = token as Tokens.Paragraph;\n // We do not need to consider nesting deeper than 1 level here and we can therefore cast.\n children.push(\n ...(tokens.flatMap(getTextChildFromToken) as (\n | string\n | StandardFormattingElement\n | LinkElement\n | null\n )[]),\n );\n }\n });\n\n return children.filter((child) => child !== null) as (\n | string\n | StandardFormattingElement\n | LinkElement\n )[];\n}\n\n/**\n * Validate the text size of a component. The text size is the total length of\n * all text in the component.\n *\n * @param component - The component to validate.\n * @throws An error if the text size exceeds the maximum allowed size.\n */\nfunction validateComponentTextSize(component: Component) {\n const textSize = getTotalTextLength(component);\n assert(\n textSize <= MAX_TEXT_LENGTH,\n `The text in a Snap UI may not be larger than ${\n MAX_TEXT_LENGTH / 1000\n } kB.`,\n );\n}\n\n/**\n * Get a JSX element from a legacy UI component. This supports all legacy UI\n * components, and maps them to their JSX equivalents where possible.\n *\n * This function validates the text size of the component, but does not validate\n * the total size. The total size of the component should be validated before\n * calling this function.\n *\n * @param legacyComponent - The legacy UI component.\n * @returns The JSX element.\n */\nexport function getJsxElementFromComponent(\n legacyComponent: Component,\n): JSXElement {\n validateComponentTextSize(legacyComponent);\n\n /**\n * Get the JSX element for a component. This function is recursive and will\n * call itself for child components.\n *\n * @param component - The component to convert to a JSX element.\n * @returns The JSX element.\n */\n function getElement(component: Component) {\n switch (component.type) {\n case NodeType.Address:\n return <Address address={component.value} />;\n\n case NodeType.Button:\n return (\n <Button\n name={component.name}\n variant={getButtonVariant(component.variant)}\n type={component.buttonType}\n >\n {component.value}\n </Button>\n );\n\n case NodeType.Copyable:\n return (\n <Copyable value={component.value} sensitive={component.sensitive} />\n );\n\n case NodeType.Divider:\n return <Divider />;\n\n case NodeType.Form:\n return (\n <Form name={component.name}>\n {getChildren(component.children.map(getElement))}\n </Form>\n );\n\n case NodeType.Heading:\n return <Heading children={component.value} />;\n\n case NodeType.Image:\n // `Image` supports `alt`, but the legacy `Image` component does not.\n return <Image src={component.value} />;\n\n case NodeType.Input:\n return (\n <Field label={component.label} error={component.error}>\n <Input\n name={component.name}\n type={component.inputType}\n value={component.value}\n placeholder={component.placeholder}\n />\n </Field>\n );\n\n case NodeType.Panel:\n // `Panel` is renamed to `Box` in JSX.\n return (\n <Box children={getChildren(component.children.map(getElement))} />\n );\n\n case NodeType.Row:\n return (\n <Row label={component.label} variant={component.variant}>\n {getElement(component.value) as RowChildren}\n </Row>\n );\n\n case NodeType.Spinner:\n return <Spinner />;\n\n case NodeType.Text:\n return <Text>{getChildren(getTextChildren(component.value))}</Text>;\n\n /* istanbul ignore next 2 */\n default:\n return assertExhaustive(component);\n }\n }\n\n return getElement(legacyComponent);\n}\n\n/**\n * Extract all links from a Markdown text string using the `marked` lexer.\n *\n * @param text - The markdown text string.\n * @returns A list of URLs linked to in the string.\n */\nfunction getMarkdownLinks(text: string) {\n const tokens = lexer(text, { gfm: false });\n const links: Tokens.Link[] = [];\n\n // Walk the lexed tokens and collect all link tokens\n // TODO: Either fix this lint violation or explain why it's necessary to\n // ignore.\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n walkTokens(tokens, (token) => {\n if (token.type === 'link') {\n links.push(token as Tokens.Link);\n }\n });\n\n return links;\n}\n\n/**\n * Validate a link against the phishing list.\n *\n * @param link - The link to validate.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n * @throws If the link is invalid.\n */\nexport function validateLink(\n link: string,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n try {\n const url = new URL(link);\n assert(\n ALLOWED_PROTOCOLS.includes(url.protocol),\n `Protocol must be one of: ${ALLOWED_PROTOCOLS.join(', ')}.`,\n );\n\n if (url.protocol === 'metamask:') {\n const linkData = parseMetaMaskUrl(link);\n if (linkData.snapId) {\n assert(\n getSnap(linkData.snapId),\n 'The Snap being navigated to is not installed.',\n );\n }\n } else if (url.protocol === 'mailto:') {\n const emails = url.pathname.split(',');\n for (const email of emails) {\n const hostname = email.split('@')[1];\n assert(!hostname.includes(':'));\n const href = `https://${hostname}`;\n assert(!isOnPhishingList(href), 'The specified URL is not allowed.');\n }\n\n return;\n }\n\n assert(!isOnPhishingList(url.href), 'The specified URL is not allowed.');\n } catch (error) {\n throw new Error(\n `Invalid URL: ${\n error?.code === 'ERR_ASSERTION' ? error.message : 'Unable to parse URL.'\n }`,\n );\n }\n}\n\n/**\n * Search for Markdown links in a string and checks them against the phishing\n * list.\n *\n * @param text - The text to verify.\n * @param isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param getSnap - The function that returns a snap if installed, undefined otherwise.\n * @throws If the text contains a link that is not allowed.\n */\nexport function validateTextLinks(\n text: string,\n isOnPhishingList: (url: string) => boolean,\n getSnap: (id: string) => Snap | undefined,\n) {\n const links = getMarkdownLinks(text);\n\n for (const link of links) {\n validateLink(link.href, isOnPhishingList, getSnap);\n }\n}\n\n/**\n * Validate the asset selector component.\n *\n * @param address - The address of the account to pull the assets from.\n * @param getAccountByAddress - A function to get an account by its address.\n *\n * @throws If the asset selector is invalid.\n */\nexport function validateAssetSelector(\n address: CaipAccountId,\n getAccountByAddress: (address: CaipAccountId) => InternalAccount | undefined,\n) {\n const account = getAccountByAddress(address);\n\n assert(account, `Could not find account for address: ${address}`);\n}\n\n/**\n * Walk a JSX tree and validate elements.\n * This function validates Links and AssetSelectors.\n *\n * @param node - The JSX node to walk.\n * @param hooks - The hooks to use for validation.\n * @param hooks.isOnPhishingList - The function that checks the link against the\n * phishing list.\n * @param hooks.getSnap - The function that returns a snap if installed, undefined otherwise.\n * @param hooks.getAccountByAddress - The function that returns an account by address.\n */\nexport function validateJsxElements(\n node: JSXElement,\n {\n isOnPhishingList,\n getSnap,\n getAccountByAddress,\n }: {\n isOnPhishingList: (url: string) => boolean;\n getSnap: (id: string) => Snap | undefined;\n getAccountByAddress: (\n address: CaipAccountId,\n ) => InternalAccount | undefined;\n },\n) {\n walkJsx(node, (childNode) => {\n switch (childNode.type) {\n case 'Link':\n validateLink(childNode.props.href, isOnPhishingList, getSnap);\n break;\n case 'AssetSelector':\n validateAssetSelector(\n // We assume that the address part of the CAIP-10 account ID are the same, as\n // that is already validated in the struct.\n childNode.props.addresses[0],\n getAccountByAddress,\n );\n break;\n default:\n break;\n }\n });\n}\n\n/**\n * Calculate the total length of all text in the component.\n *\n * @param component - A custom UI component.\n * @returns The total length of all text components in the component.\n */\nexport function getTotalTextLength(component: Component): number {\n const { type } = component;\n\n switch (type) {\n case NodeType.Panel:\n return component.children.reduce<number>(\n (sum, node) => sum + getTotalTextLength(node),\n 0,\n );\n\n case NodeType.Row:\n return getTotalTextLength(component.value);\n\n case NodeType.Text:\n return component.value.length;\n\n default:\n return 0;\n }\n}\n\n/**\n * Check if a JSX element has children.\n *\n * @param element - A JSX element.\n * @returns `true` if the element has children, `false` otherwise.\n */\nexport function hasChildren<Element extends JSXElement>(\n element: Element,\n): element is Element & {\n props: { children: Nestable<JSXElement | string> };\n} {\n return hasProperty(element.props, 'children');\n}\n\n/**\n * Filter a JSX child to remove `null`, `undefined`, plain booleans, and empty\n * strings.\n *\n * @param child - The JSX child to filter.\n * @returns `true` if the child is not `null`, `undefined`, a plain boolean, or\n * an empty string, `false` otherwise.\n */\nfunction filterJsxChild(child: JSXElement | string | boolean | null): boolean {\n return Boolean(child) && child !== true;\n}\n\n/**\n * Get the children of a JSX element as an array. If the element has only one\n * child, the child is returned as an array.\n *\n * @param element - A JSX element.\n * @returns The children of the element.\n */\nexport function getJsxChildren(element: JSXElement): (JSXElement | string)[] {\n if (hasChildren(element)) {\n if (Array.isArray(element.props.children)) {\n // @ts-expect-error - Each member of the union type has signatures, but\n // none of those signatures are compatible with each other.\n return element.props.children.filter(filterJsxChild).flat(Infinity);\n }\n\n if (element.props.children) {\n return [element.props.children];\n }\n }\n\n return [];\n}\n\n/**\n * Walk a JSX tree and call a callback on each node.\n *\n * @param node - The JSX node to walk.\n * @param callback - The callback to call on each node.\n * @param depth - The current depth in the JSX tree for a walk.\n * @returns The result of the callback, if any.\n */\nexport function walkJsx<Value>(\n node: JSXElement | JSXElement[],\n callback: (node: JSXElement, depth: number) => Value | undefined,\n depth = 0,\n): Value | undefined {\n if (Array.isArray(node)) {\n for (const child of node) {\n const childResult = walkJsx(child, callback, depth);\n if (childResult !== undefined) {\n return childResult;\n }\n }\n\n return undefined;\n }\n\n const result = callback(node, depth);\n if (result !== undefined) {\n return result;\n }\n\n if (\n hasProperty(node, 'props') &&\n isPlainObject(node.props) &&\n hasProperty(node.props, 'children')\n ) {\n const children = getJsxChildren(node);\n for (const child of children) {\n if (isPlainObject(child)) {\n const childResult = walkJsx(child, callback, depth + 1);\n if (childResult !== undefined) {\n return childResult;\n }\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Serialise a JSX prop to a string.\n *\n * @param prop - The JSX prop.\n * @returns The serialised JSX prop.\n */\nfunction serialiseProp(prop: unknown): string {\n if (typeof prop === 'string') {\n return `\"${prop}\"`;\n }\n\n return `{${JSON.stringify(prop)}}`;\n}\n\n/**\n * Serialise JSX props to a string.\n *\n * @param props - The JSX props.\n * @returns The serialised JSX props.\n */\nfunction serialiseProps(props: Record<string, unknown>): string {\n return Object.entries(props)\n .filter(([key]) => key !== 'children')\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, value]) => ` ${key}=${serialiseProp(value)}`)\n .join('');\n}\n\n/**\n * Serialise a JSX node to a string.\n *\n * @param node - The JSX node.\n * @param indentation - The indentation level. Defaults to `0`. This should not\n * be set by the caller, as it is used for recursion.\n * @returns The serialised JSX node.\n */\nexport function serialiseJsx(node: SnapNode, indentation = 0): string {\n if (Array.isArray(node)) {\n return node.map((child) => serialiseJsx(child, indentation)).join('');\n }\n\n const indent = ' '.repeat(indentation);\n if (typeof node === 'string') {\n return `${indent}${node}\\n`;\n }\n\n if (!node) {\n return '';\n }\n\n const { type, props } = node as GenericSnapElement;\n const trailingNewline = indentation > 0 ? '\\n' : '';\n\n if (hasProperty(props, 'children')) {\n const children = serialiseJsx(props.children as SnapNode, indentation + 1);\n return `${indent}<${type}${serialiseProps(\n props,\n )}>\\n${children}${indent}</${type}>${trailingNewline}`;\n }\n\n return `${indent}<${type}${serialiseProps(props)} />${trailingNewline}`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask/snaps-utils",
3
- "version": "9.0.1",
3
+ "version": "9.2.0",
4
4
  "description": "A collection of utilities for MetaMask Snaps",
5
5
  "keywords": [
6
6
  "MetaMask",
@@ -70,9 +70,10 @@
70
70
  "publish:preview": "yarn npm publish --tag preview",
71
71
  "since-latest-release": "../../scripts/since-latest-release.sh",
72
72
  "test": "jest --reporters=jest-silent-reporter && yarn test:browser",
73
- "test:browser": "wdio run wdio.config.js",
73
+ "test:browser": "vitest",
74
+ "test:browser:firefox": "vitest --config ./vitest.config.firefox.mts",
74
75
  "test:clean": "jest --clearCache",
75
- "test:post": "ts-node scripts/coverage.ts && rimraf coverage/jest coverage/wdio",
76
+ "test:post": "ts-node scripts/coverage.ts && rimraf coverage/jest coverage/vite",
76
77
  "test:verbose": "jest --verbose",
77
78
  "test:watch": "jest --watch"
78
79
  },
@@ -80,21 +81,22 @@
80
81
  "@babel/core": "^7.23.2",
81
82
  "@babel/types": "^7.23.0",
82
83
  "@metamask/base-controller": "^8.0.0",
83
- "@metamask/key-tree": "^10.0.2",
84
+ "@metamask/key-tree": "^10.1.1",
84
85
  "@metamask/permission-controller": "^11.0.6",
85
86
  "@metamask/rpc-errors": "^7.0.2",
86
87
  "@metamask/slip44": "^4.1.0",
87
88
  "@metamask/snaps-registry": "^3.2.3",
88
- "@metamask/snaps-sdk": "^6.19.0",
89
- "@metamask/superstruct": "^3.1.0",
89
+ "@metamask/snaps-sdk": "^6.22.0",
90
+ "@metamask/superstruct": "^3.2.1",
90
91
  "@metamask/utils": "^11.2.0",
91
- "@noble/hashes": "^1.3.1",
92
+ "@noble/hashes": "^1.7.1",
92
93
  "@scure/base": "^1.1.1",
93
94
  "chalk": "^4.1.2",
94
95
  "cron-parser": "^4.5.0",
95
96
  "fast-deep-equal": "^3.1.3",
96
97
  "fast-json-stable-stringify": "^2.1.0",
97
98
  "fast-xml-parser": "^4.4.1",
99
+ "luxon": "^3.5.0",
98
100
  "marked": "^12.0.1",
99
101
  "rfdc": "^1.3.0",
100
102
  "semver": "^7.5.4",
@@ -102,31 +104,22 @@
102
104
  "validate-npm-package-name": "^5.0.0"
103
105
  },
104
106
  "devDependencies": {
105
- "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
106
- "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
107
107
  "@lavamoat/allow-scripts": "^3.0.4",
108
- "@metamask/auto-changelog": "^4.1.0",
108
+ "@metamask/auto-changelog": "^5.0.1",
109
109
  "@metamask/post-message-stream": "^9.0.0",
110
110
  "@swc/core": "1.3.78",
111
111
  "@swc/jest": "^0.2.26",
112
112
  "@ts-bridge/cli": "^0.6.1",
113
113
  "@types/jest": "^27.5.1",
114
- "@types/mocha": "^10.0.1",
114
+ "@types/luxon": "^3",
115
115
  "@types/node": "18.14.2",
116
116
  "@types/semver": "^7.5.0",
117
117
  "@types/validate-npm-package-name": "^4.0.0",
118
- "@wdio/browser-runner": "^8.19.0",
119
- "@wdio/cli": "^8.19.0",
120
- "@wdio/globals": "^8.19.0",
121
- "@wdio/mocha-framework": "^8.19.0",
122
- "@wdio/spec-reporter": "^8.19.0",
123
- "@wdio/static-server-service": "^8.19.0",
124
- "@wdio/types": "^8.19.0",
118
+ "@vitest/browser": "^3.0.8",
119
+ "@vitest/coverage-istanbul": "3.0.8",
125
120
  "deepmerge": "^4.2.2",
126
121
  "depcheck": "^1.4.7",
127
- "esbuild": "^0.18.10",
128
122
  "eslint": "^9.11.0",
129
- "expect-webdriverio": "^4.4.1",
130
123
  "istanbul-lib-coverage": "^3.2.0",
131
124
  "istanbul-lib-report": "^3.0.0",
132
125
  "istanbul-reports": "^3.1.5",
@@ -137,11 +130,10 @@
137
130
  "rimraf": "^4.1.2",
138
131
  "ts-node": "^10.9.1",
139
132
  "typescript": "~5.3.3",
140
- "vite": "^4.5.6",
133
+ "vite": "^6.2.3",
134
+ "vite-plugin-node-polyfills": "^0.23.0",
141
135
  "vite-tsconfig-paths": "^4.0.5",
142
- "wdio-chromedriver-service": "^8.1.1",
143
- "wdio-geckodriver-service": "^5.0.2",
144
- "webdriverio": "^8.19.0"
136
+ "vitest": "^3.0.8"
145
137
  },
146
138
  "engines": {
147
139
  "node": "^18.16 || >=20"
@@ -1 +0,0 @@
1
- {"version":3,"file":"handler-types.cjs","sourceRoot":"","sources":["../src/handler-types.ts"],"names":[],"mappings":";;;AAAA,IAAY,WAeX;AAfD,WAAY,WAAW;IACrB,4CAA6B,CAAA;IAC7B,0CAA2B,CAAA;IAC3B,8CAA+B,CAAA;IAC/B,sCAAuB,CAAA;IACvB,sCAAuB,CAAA;IACvB,oCAAqB,CAAA;IACrB,4CAA6B,CAAA;IAC7B,oDAAqC,CAAA;IACrC,wCAAyB,CAAA;IACzB,gDAAiC,CAAA;IACjC,0CAA2B,CAAA;IAC3B,gDAAiC,CAAA;IACjC,wDAAyC,CAAA;IACzC,sDAAuC,CAAA;AACzC,CAAC,EAfW,WAAW,2BAAX,WAAW,QAetB;AA0BY,QAAA,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC","sourcesContent":["export enum HandlerType {\n OnRpcRequest = 'onRpcRequest',\n OnSignature = 'onSignature',\n OnTransaction = 'onTransaction',\n OnCronjob = 'onCronjob',\n OnInstall = 'onInstall',\n OnUpdate = 'onUpdate',\n OnNameLookup = 'onNameLookup',\n OnKeyringRequest = 'onKeyringRequest',\n OnHomePage = 'onHomePage',\n OnSettingsPage = 'onSettingsPage',\n OnUserInput = 'onUserInput',\n OnAssetsLookup = 'onAssetsLookup',\n OnAssetsConversion = 'onAssetsConversion',\n OnProtocolRequest = 'onProtocolRequest',\n}\n\nexport type SnapHandler = {\n /**\n * The type of handler.\n */\n type: HandlerType;\n\n /**\n * Whether the handler is required, i.e., whether the request will fail if the\n * handler is called, but the snap does not export it.\n *\n * This is primarily used for the lifecycle handlers, which are optional.\n */\n required: boolean;\n\n /**\n * Validate the given snap export. This should return a type guard for the\n * handler type.\n *\n * @param snapExport - The export to validate.\n * @returns Whether the export is valid.\n */\n validator: (snapExport: unknown) => boolean;\n};\n\nexport const SNAP_EXPORT_NAMES = Object.values(HandlerType);\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"handler-types.d.cts","sourceRoot":"","sources":["../src/handler-types.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IACrB,YAAY,iBAAiB;IAC7B,WAAW,gBAAgB;IAC3B,aAAa,kBAAkB;IAC/B,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,QAAQ,aAAa;IACrB,YAAY,iBAAiB;IAC7B,gBAAgB,qBAAqB;IACrC,UAAU,eAAe;IACzB,cAAc,mBAAmB;IACjC,WAAW,gBAAgB;IAC3B,cAAc,mBAAmB;IACjC,kBAAkB,uBAAuB;IACzC,iBAAiB,sBAAsB;CACxC;AAED,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,IAAI,EAAE,WAAW,CAAC;IAElB;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAC;IAElB;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,OAAO,CAAC;CAC7C,CAAC;AAEF,eAAO,MAAM,iBAAiB,eAA6B,CAAC"}