diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 88bdd3d76c..5c038f613b 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -43,8 +43,8 @@ jobs: } >> "$GITHUB_OUTPUT" shell: bash - build-source: - name: Build source + build: + name: Build runs-on: ubuntu-latest needs: prepare steps: @@ -55,14 +55,13 @@ jobs: node-version-file: '.nvmrc' cache: yarn - run: yarn --immutable --immutable-cache - - name: Build source - run: yarn build:source + - name: Build + run: yarn build - name: Cache build files uses: actions/cache@v3 with: path: | - packages/*/dist/esm - packages/*/dist/cjs + packages/*/dist key: build-source-${{ runner.os }}-${{ github.sha }} - name: Require clean working directory shell: bash @@ -72,40 +71,11 @@ jobs: exit 1 fi - build-types: - name: Build types - runs-on: ubuntu-latest - needs: prepare - steps: - - uses: actions/checkout@v3 - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version-file: '.nvmrc' - cache: yarn - - run: yarn --immutable --immutable-cache - - name: Build types - run: yarn build:types - - name: Cache build files - uses: actions/cache@v3 - with: - path: | - packages/*/dist/types - key: build-types-${{ runner.os }}-${{ github.sha }} - - name: Require clean working directory - shell: bash - run: | - if ! git diff --exit-code; then - echo "Working tree dirty at end of job" - exit 1 - fi - post-build: name: Post-build runs-on: ubuntu-latest needs: - - build-source - - build-types + - build steps: - uses: actions/checkout@v3 - name: Setup Node @@ -118,17 +88,9 @@ jobs: uses: actions/cache@v3 with: path: | - packages/*/dist/esm - packages/*/dist/cjs + packages/*/dist key: build-source-${{ runner.os }}-${{ github.sha }} fail-on-cache-miss: true - - name: Restore types files - uses: actions/cache@v3 - with: - path: | - packages/*/dist/types - key: build-types-${{ runner.os }}-${{ github.sha }} - fail-on-cache-miss: true - name: Post-build run: yarn build:post-tsc:ci @@ -318,8 +280,7 @@ jobs: runs-on: ubuntu-latest needs: - prepare - - build-source - - build-types + - build strategy: fail-fast: false matrix: @@ -343,17 +304,9 @@ jobs: uses: actions/cache@v3 with: path: | - packages/*/dist/esm - packages/*/dist/cjs + packages/*/dist key: build-source-${{ runner.os }}-${{ github.sha }} fail-on-cache-miss: true - - name: Restore types files - uses: actions/cache@v3 - with: - path: | - packages/*/dist/types - key: build-types-${{ runner.os }}-${{ github.sha }} - fail-on-cache-miss: true - run: yarn --immutable --immutable-cache - name: Build snap run: yarn workspace ${{ matrix.package-name }} run build diff --git a/constraints.pro b/constraints.pro index b5997c9bed..f1bf81a41c 100644 --- a/constraints.pro +++ b/constraints.pro @@ -157,15 +157,51 @@ gen_enforced_field(WorkspaceCwd, 'types', './dist/types/index.d.ts') :- \+ is_example(WorkspaceCwd), \+ workspace_field(WorkspaceCwd, 'private', true), WorkspaceCwd \= '.'. +gen_enforced_field(WorkspaceCwd, 'exports["."].types', './dist/types/index.d.ts') :- + \+ is_example(WorkspaceCwd), + \+ workspace_field(WorkspaceCwd, 'private', true), + WorkspaceCwd \= '.'. % The entrypoint for the dependency must be `./dist/cjs/index.js`. -gen_enforced_field(WorkspaceCwd, 'main', './dist/cjs/index.js') :- +gen_enforced_field(WorkspaceCwd, 'main', './dist/index.js') :- + \+ is_example(WorkspaceCwd), + \+ workspace_field(WorkspaceCwd, 'private', true), + WorkspaceCwd \= '.'. +gen_enforced_field(WorkspaceCwd, 'exports["."].require', './dist/index.js') :- \+ is_example(WorkspaceCwd), \+ workspace_field(WorkspaceCwd, 'private', true), WorkspaceCwd \= '.'. % The module entrypoint for the dependency must be `./dist/esm/index.js`. -gen_enforced_field(WorkspaceCwd, 'module', './dist/esm/index.js') :- +gen_enforced_field(WorkspaceCwd, 'module', './dist/index.mjs') :- + \+ is_example(WorkspaceCwd), + \+ workspace_field(WorkspaceCwd, 'private', true), + WorkspaceCwd \= '.'. +gen_enforced_field(WorkspaceCwd, 'exports["."].import', './dist/index.mjs') :- + \+ is_example(WorkspaceCwd), + \+ workspace_field(WorkspaceCwd, 'private', true), + WorkspaceCwd \= '.'. + +% `package.json` must be exported. +gen_enforced_field(WorkspaceCwd, 'exports["./package.json"]', './package.json') :- + \+ is_example(WorkspaceCwd), + \+ workspace_field(WorkspaceCwd, 'private', true), + WorkspaceCwd \= '.'. + +% The list of files included in the package must only include files generated +% during the build step. +gen_enforced_field(WorkspaceCwd, 'files', ['dist']) :- + \+ is_example(WorkspaceCwd), + \+ workspace_field(WorkspaceCwd, 'private', true), + WorkspaceCwd \= '.'. + +% Dependencies must have a build script. +gen_enforced_field(WorkspaceCwd, 'scripts.build', 'tsup --clean && yarn build:types') :- + \+ is_example(WorkspaceCwd), + \+ workspace_field(WorkspaceCwd, 'private', true), + WorkspaceCwd \= '.', + WorkspaceCwd \= 'packages/snaps-simulator'. +gen_enforced_field(WorkspaceCwd, 'build:types', 'tsc --project tsconfig.build.json') :- \+ is_example(WorkspaceCwd), \+ workspace_field(WorkspaceCwd, 'private', true), WorkspaceCwd \= '.'. @@ -175,6 +211,7 @@ gen_enforced_field(WorkspaceCwd, 'scripts.publish:preview', 'yarn npm publish -- \+ workspace_field(WorkspaceCwd, 'private', true), WorkspaceCwd \= '.'. +% Dependencies must have a "publishConfig" field. gen_enforced_field(WorkspaceCwd, 'publishConfig.access', 'public') :- \+ workspace_field(WorkspaceCwd, 'private', true), WorkspaceCwd \= '.'. diff --git a/package.json b/package.json index 19629e09a1..2a48997029 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "lint:ci": "yarn lint:eslint && yarn lint:misc --check && yarn lint:tsconfig && yarn constraints && yarn lint:dependencies", "build": "yarn build:source && yarn build:types && yarn build:post-tsc", "build:clean": "yarn clean && yarn build", - "build:source": "yarn workspaces foreach --parallel --verbose run build:source", + "build:source": "yarn workspaces foreach --parallel --topological --topological-dev --verbose --exclude root --exclude \"@metamask/example-snaps\" --exclude \"@metamask/invoke-snap-example-snap\" run build", "build:types": "tsc --build tsconfig.build.json", "build:examples": "yarn workspace @metamask/example-snaps build", "build:post-tsc": "yarn workspaces foreach --parallel --topological --topological-dev --verbose run build:post-tsc", @@ -73,7 +73,6 @@ "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", "@metamask/utils": "^8.3.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@types/jest": "^27.5.1", "@types/node": "18.14.2", @@ -101,6 +100,7 @@ "semver": "^7.5.4", "simple-git-hooks": "^2.7.0", "ts-node": "^10.9.1", + "tsup": "^8.0.1", "typescript": "~4.8.4" }, "engines": { @@ -116,7 +116,8 @@ "geckodriver": true, "ts-node>@swc/core": true, "@swc/core": true, - "favicons>sharp": true + "favicons>sharp": true, + "tsup>esbuild": true } }, "packageManager": "yarn@3.6.0" diff --git a/packages/create-snap/package.json b/packages/create-snap/package.json index 1925065c86..9e4a5f176c 100644 --- a/packages/create-snap/package.json +++ b/packages/create-snap/package.json @@ -8,23 +8,25 @@ }, "license": "ISC", "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", - "bin": "./dist/cjs/main.js", + "bin": "./dist/main.js", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**" + "dist" ], "scripts": { - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", - "build:post-tsc": "yarn build:chmod", - "build:chmod": "chmod +x ./dist/esm/main.js && chmod +x ./dist/cjs/main.js", + "build:chmod": "chmod +x ./dist/main.mjs && chmod +x ./dist/main.js", "build:clean": "yarn clean && yarn build", "build:watch": "tsc-watch --onSuccess 'yarn build:chmod'", "clean": "rimraf '*.tsbuildinfo' 'dist'", @@ -54,7 +56,6 @@ "@metamask/eslint-config-jest": "^12.1.0", "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", "@types/jest": "^27.5.1", @@ -81,6 +82,7 @@ "rimraf": "^4.1.2", "ts-node": "^10.9.1", "tsc-watch": "^4.5.0", + "tsup": "^8.0.1", "typescript": "~4.8.4" }, "engines": { @@ -90,6 +92,7 @@ "access": "public", "registry": "https://registry.npmjs.org/" }, + "build:types": "tsc --project tsconfig.build.json", "lavamoat": { "allowScripts": { "@lavamoat/preinstall-always-fail": false diff --git a/packages/create-snap/tsconfig.json b/packages/create-snap/tsconfig.json index 28cf1b8272..85adfc60aa 100644 --- a/packages/create-snap/tsconfig.json +++ b/packages/create-snap/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "include": ["./src", "./src/**/*.json"], + "include": ["./src", "./src/**/*.json", "package.json", "tsup.config.ts"], "references": [{ "path": "../snaps-cli" }, { "path": "../snaps-utils" }] } diff --git a/packages/create-snap/tsup.config.ts b/packages/create-snap/tsup.config.ts new file mode 100644 index 0000000000..3eaf645296 --- /dev/null +++ b/packages/create-snap/tsup.config.ts @@ -0,0 +1,14 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/examples/package.json b/packages/examples/package.json index b7af6b155a..c4d75cc66b 100644 --- a/packages/examples/package.json +++ b/packages/examples/package.json @@ -14,7 +14,6 @@ "scripts": { "build": "yarn workspaces foreach --parallel --verbose --no-private run build", "build:clean": "yarn clean && yarn build", - "build:post-tsc": "yarn build", "clean": "yarn workspaces foreach --parallel --verbose --no-private run clean", "start": "yarn workspaces foreach --parallel --verbose --interlaced --no-private --jobs unlimited run start", "start:test": "yarn start", diff --git a/packages/examples/packages/bip32/snap.manifest.json b/packages/examples/packages/bip32/snap.manifest.json index 9280175070..1f5d5383f3 100644 --- a/packages/examples/packages/bip32/snap.manifest.json +++ b/packages/examples/packages/bip32/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "g9dovKbbtrEsUwNqIdvLjVB6yBqYLvciPhNtAwoOr/E=", + "shasum": "RPvQGK2QFnwSOXKP7f48dafkWVdhQnG5wZU0yyLdSzE=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/bip44/snap.manifest.json b/packages/examples/packages/bip44/snap.manifest.json index f182f4e6a4..d9415f7601 100644 --- a/packages/examples/packages/bip44/snap.manifest.json +++ b/packages/examples/packages/bip44/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "K9S/uTI/uhNefkhpo3hgA4ON3P0/z2QT6QUPFJQWfW4=", + "shasum": "5AgPsNhFEGOqnPyheAkJQtsnQn0ZbgTZp47na++jsJs=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/browserify/snap.manifest.json b/packages/examples/packages/browserify/snap.manifest.json index c63386af0a..3c044eb08d 100644 --- a/packages/examples/packages/browserify/snap.manifest.json +++ b/packages/examples/packages/browserify/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "3lhWUOj8f2t2WRLwzzD3dJyDz0DNT6+UeSBM5vMIUI0=", + "shasum": "2qnG8o5QU4UL2GvdOiNuTggQXgojdeI0Q6NacfqhQUY=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/cronjobs/snap.manifest.json b/packages/examples/packages/cronjobs/snap.manifest.json index d218aa2aae..2617e4d794 100644 --- a/packages/examples/packages/cronjobs/snap.manifest.json +++ b/packages/examples/packages/cronjobs/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "luuLZglOmJHdIswy2Bl32uZH1eA0FppZpDalgF6OkeI=", + "shasum": "GgF2/QBRs5tRs27IKZN1qnYCd5GsXDXlRCWXpfQ1e8g=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/dialogs/snap.manifest.json b/packages/examples/packages/dialogs/snap.manifest.json index 407369ac09..4b844419c8 100644 --- a/packages/examples/packages/dialogs/snap.manifest.json +++ b/packages/examples/packages/dialogs/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "dtsOY/wB20ppXkdAdHARtTsAgpAC2wUaFZLOkdiR7TU=", + "shasum": "TrYdhg2QUWMW7Axi7Uibycj2XTkqtoPljnurfQlCBe4=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/dialogs/src/index.test.ts b/packages/examples/packages/dialogs/src/index.test.ts index 08f7946c02..cf53bc4475 100644 --- a/packages/examples/packages/dialogs/src/index.test.ts +++ b/packages/examples/packages/dialogs/src/index.test.ts @@ -61,7 +61,7 @@ describe('onRpcRequest', () => { panel([ heading('Confirmation Dialog'), text( - 'This is a confirmation dialog. It has two buttons: "OK" and "Cancel," letting the user choose whether to proceed with an action.', + 'This is a confirmation dialog. It has two buttons: "OK" and "Cancel," letting the user choose whether to proceed with an action. [That](https://snaps.metamask.io/) is a what a link looks like.', ), ]), ); diff --git a/packages/examples/packages/dialogs/src/index.ts b/packages/examples/packages/dialogs/src/index.ts index 01fd7999d3..7e7428a8c1 100644 --- a/packages/examples/packages/dialogs/src/index.ts +++ b/packages/examples/packages/dialogs/src/index.ts @@ -47,7 +47,7 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => { content: panel([ heading('Confirmation Dialog'), text( - 'This is a confirmation dialog. It has two buttons: "OK" and "Cancel," letting the user choose whether to proceed with an action.', + 'This is a confirmation dialog. It has two buttons: "OK" and "Cancel," letting the user choose whether to proceed with an action. [That](https://snaps.metamask.io/) is a what a link looks like.', ), ]), }, diff --git a/packages/examples/packages/ethers-js/snap.manifest.json b/packages/examples/packages/ethers-js/snap.manifest.json index 2b860d3883..3237887177 100644 --- a/packages/examples/packages/ethers-js/snap.manifest.json +++ b/packages/examples/packages/ethers-js/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "wpOaVTHz5XjVo+DqyqE5rN0d4RwYVP7KFWtVfcO3fag=", + "shasum": "CYsAxtTrUueDZL0n+Mq22JINzY7hG/9FykS3Rj2+o8k=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/get-entropy/snap.manifest.json b/packages/examples/packages/get-entropy/snap.manifest.json index 9a8c94f184..07ea59614e 100644 --- a/packages/examples/packages/get-entropy/snap.manifest.json +++ b/packages/examples/packages/get-entropy/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "Ye7dhCSYl8wNvsIQ/pmqYSXpReZhXOvDIF7MLAgtpFA=", + "shasum": "nGOcTGdXNGFRGwVwvaYfLAGvnoxZMykiilfLh3tLVGc=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/home-page/snap.manifest.json b/packages/examples/packages/home-page/snap.manifest.json index 505437ee92..24e06b8a07 100644 --- a/packages/examples/packages/home-page/snap.manifest.json +++ b/packages/examples/packages/home-page/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "7scwSs/Bdh9PMt1oPqPIHUcddyXM/RkcIkezfPIHCnM=", + "shasum": "LSht8go8uOrxg23D0VT3ZKImqrEMm3eIlHicdS2q2SY=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/images/snap.manifest.json b/packages/examples/packages/images/snap.manifest.json index 292bc8a8ae..501d92b6bf 100644 --- a/packages/examples/packages/images/snap.manifest.json +++ b/packages/examples/packages/images/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "6vcH4bTn+u+0+7uc8u3tWxORSvwvahksB/DIMBLOdRA=", + "shasum": "Zogo7Gg2LZl6xGnOdfdIZzwq5Z0NsHHkp1MbaHLjcU4=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json index 9f388ecfa5..7a84d4918d 100644 --- a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json +++ b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "a3M4IIiS6AFIfspxd912aQYQsH4WZbTm5gVpm+YPJoM=", + "shasum": "hmP+MksZ1p4ItQsOZrSeuQi5d+ma+kY3NAzj+NCzPD8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/lifecycle-hooks/snap.manifest.json b/packages/examples/packages/lifecycle-hooks/snap.manifest.json index 98370fd4d3..a4bd40848c 100644 --- a/packages/examples/packages/lifecycle-hooks/snap.manifest.json +++ b/packages/examples/packages/lifecycle-hooks/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "SCwQ+EogB8nFafEwO9BGSTv+nfdWGflWyMh/f//O8RE=", + "shasum": "Yt6/Wj6walhJxPGtsZBilElQLd+rhHWKGvc3dFSx9Bw=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/manage-state/snap.manifest.json b/packages/examples/packages/manage-state/snap.manifest.json index 8016e7b9d6..3bbe73f408 100644 --- a/packages/examples/packages/manage-state/snap.manifest.json +++ b/packages/examples/packages/manage-state/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "vDBH7zu4BBYMX5Gq0SPo28FLhqYcDaV6PFPmOP75HYA=", + "shasum": "MeQ3pS30EFXKx4X8FHFhokWuqubPrn/8Dro89IU1Y1k=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/notifications/snap.manifest.json b/packages/examples/packages/notifications/snap.manifest.json index dc1591d146..6b5c89169e 100644 --- a/packages/examples/packages/notifications/snap.manifest.json +++ b/packages/examples/packages/notifications/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "rUSTK83ra7N9u4X75BrZh2sTkRJI0EgNh5FiwLwTIPo=", + "shasum": "UzbKhKmoKFg/cEC2TlySweyIuF4X+ptvbcuCqxpJaeQ=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/signature-insights/snap.manifest.json b/packages/examples/packages/signature-insights/snap.manifest.json index 3fbe281818..bdbdb224d4 100644 --- a/packages/examples/packages/signature-insights/snap.manifest.json +++ b/packages/examples/packages/signature-insights/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "79WsMc0xmXW3Fi9pepRMAzxkYgv3bY535tYUe/rbh6s=", + "shasum": "DR7WvXDe5KkCk1Q8hMfQy8TsbYJt28chK/Dr5tkUKzA=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/signature-insights/src/index.test.ts b/packages/examples/packages/signature-insights/src/index.test.ts index 4aa40c825e..a616456486 100644 --- a/packages/examples/packages/signature-insights/src/index.test.ts +++ b/packages/examples/packages/signature-insights/src/index.test.ts @@ -1,23 +1,21 @@ -import { describe, it } from '@jest/globals'; -import { panel, text, row, SeverityLevel } from '@metamask/snaps-sdk'; - -import { onSignature } from '.'; +import { expect, describe, it } from '@jest/globals'; +import { installSnap } from '@metamask/snaps-jest'; +import { panel, text, row } from '@metamask/snaps-sdk'; // The Snaps E2E testing framework doesn't currently support onSignature, so we unit // test it instead. describe('onSignature', () => { it('returns custom UI', async () => { - expect( - await onSignature({ - signature: { - from: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', - data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0', - signatureMethod: 'personal_sign', - }, - }), - ).toStrictEqual({ - content: panel([ - row('From:', text('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045')), + const { onSignature } = await installSnap(); + + const response = await onSignature({ + from: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', + data: '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0', + }); + + expect(response).toRender( + panel([ + row('From:', text('0xd8da6bf26964af9d7eed9e03e53415d37aa96045')), row( 'Data:', text( @@ -25,7 +23,6 @@ describe('onSignature', () => { ), ), ]), - severity: SeverityLevel.Critical, - }); + ); }); }); diff --git a/packages/examples/packages/transaction-insights/snap.manifest.json b/packages/examples/packages/transaction-insights/snap.manifest.json index 27db137d94..ba41eb7a02 100644 --- a/packages/examples/packages/transaction-insights/snap.manifest.json +++ b/packages/examples/packages/transaction-insights/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "8x0X/zk2MKFaVMnZ3ZhD7qgyVzhIJ2swntckLnYur+I=", + "shasum": "9PTaj+pvBOwZOUJcXb83msRTH2Y/zwry8auBsakPsm8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/wasm/snap.manifest.json b/packages/examples/packages/wasm/snap.manifest.json index e3ae2d1956..7bedfff90a 100644 --- a/packages/examples/packages/wasm/snap.manifest.json +++ b/packages/examples/packages/wasm/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "V5yGxj/OvkdAjHz6MN0ardR3Sbf1A4OEEPrLbB/N8v4=", + "shasum": "IAG28rbZSwC9MyJfQeS/1X71vWhaof5WKdM9dugfRQA=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snaps-browserify-plugin/package.json b/packages/snaps-browserify-plugin/package.json index 0dfccdf9c6..07c9185f6d 100644 --- a/packages/snaps-browserify-plugin/package.json +++ b/packages/snaps-browserify-plugin/package.json @@ -9,13 +9,19 @@ "url": "https://github.com/MetaMask/snaps.git" }, "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**" + "dist" ], "scripts": { "test": "jest && yarn posttest", @@ -26,12 +32,8 @@ "lint": "yarn lint:eslint && yarn lint:misc --check && yarn lint:changelog && yarn lint:dependencies", "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write", "lint:changelog": "../../scripts/validate-changelog.sh @metamask/snaps-browserify-plugin", - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", - "build:clean": "yarn clean && yarn build", "clean": "rimraf '*.tsbuildinfo' 'dist'", "publish:preview": "yarn npm publish --tag preview", "lint:ci": "yarn lint", @@ -49,7 +51,6 @@ "@metamask/eslint-config-jest": "^12.1.0", "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", "@types/browserify": "^12.0.37", @@ -75,6 +76,7 @@ "prettier": "^2.7.1", "prettier-plugin-packagejson": "^2.2.11", "rimraf": "^4.1.2", + "tsup": "^8.0.1", "typescript": "~4.8.4" }, "engines": { @@ -83,5 +85,6 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" - } + }, + "build:types": "tsc --project tsconfig.build.json" } diff --git a/packages/snaps-browserify-plugin/tsconfig.json b/packages/snaps-browserify-plugin/tsconfig.json index 4a3ba29e4e..29291d0908 100644 --- a/packages/snaps-browserify-plugin/tsconfig.json +++ b/packages/snaps-browserify-plugin/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "baseUrl": "./" }, - "include": ["./src"], + "include": ["./src", "package.json", "tsup.config.ts"], "references": [ { "path": "../snaps-utils" diff --git a/packages/snaps-browserify-plugin/tsup.config.ts b/packages/snaps-browserify-plugin/tsup.config.ts new file mode 100644 index 0000000000..3eaf645296 --- /dev/null +++ b/packages/snaps-browserify-plugin/tsup.config.ts @@ -0,0 +1,14 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-cli/package.json b/packages/snaps-cli/package.json index 8306f845f2..2b915211fe 100644 --- a/packages/snaps-cli/package.json +++ b/packages/snaps-cli/package.json @@ -8,28 +8,29 @@ }, "license": "ISC", "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", "bin": { - "mm-snap": "./dist/cjs/main.js" + "mm-snap": "./dist/main.js" }, "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**", - ".browserslistrc" + "dist" ], "scripts": { - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", "build:post-tsc": "yarn build:chmod && yarn build:readme", - "build:chmod": "chmod +x ./dist/esm/main.js && chmod +x ./dist/cjs/main.js", + "build:chmod": "chmod +x ./dist/main.mjs && chmod +x ./dist/main.js", "build:readme": "node ./scripts/updateReadme.js", - "build:clean": "yarn clean && yarn build", "build:watch": "tsc-watch --onSuccess 'yarn build:chmod'", "clean": "rimraf '*.tsbuildinfo' 'dist'", "test": "jest --runInBand && yarn posttest", @@ -65,7 +66,6 @@ "browserify-zlib": "^0.2.0", "buffer": "^6.0.3", "chalk": "^4.1.2", - "chokidar": "^3.5.2", "console-browserify": "^1.2.0", "constants-browserify": "^1.0.0", "crypto-browserify": "^3.12.0", @@ -103,7 +103,6 @@ "@metamask/eslint-config-jest": "^12.1.0", "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", - "@swc/cli": "^0.1.62", "@swc/jest": "^0.2.26", "@types/browserify": "^12.0.37", "@types/jest": "^27.5.1", @@ -133,6 +132,7 @@ "rimraf": "^4.1.2", "ts-node": "^10.9.1", "tsc-watch": "^4.5.0", + "tsup": "^8.0.1", "typescript": "~4.8.4" }, "engines": { @@ -142,6 +142,7 @@ "access": "public", "registry": "https://registry.npmjs.org/" }, + "build:types": "tsc --project tsconfig.build.json", "lavamoat": { "allowScripts": { "@lavamoat/preinstall-always-fail": false, diff --git a/packages/snaps-cli/src/test-utils/webpack.ts b/packages/snaps-cli/src/test-utils/webpack.ts index 272e6239e2..0d8bf316f0 100644 --- a/packages/snaps-cli/src/test-utils/webpack.ts +++ b/packages/snaps-cli/src/test-utils/webpack.ts @@ -134,6 +134,16 @@ export function normalizeConfig(config: Configuration): Configuration { }; } + if (rule.use.loader.includes('function.ts')) { + return { + ...rule, + use: { + ...rule.use, + loader: '/foo/bar/src/webpack/loaders/function.ts', + }, + }; + } + return rule; }); diff --git a/packages/snaps-cli/src/webpack/__snapshots__/config.test.ts.snap b/packages/snaps-cli/src/webpack/__snapshots__/config.test.ts.snap index e66217cd4f..708821c319 100644 --- a/packages/snaps-cli/src/webpack/__snapshots__/config.test.ts.snap +++ b/packages/snaps-cli/src/webpack/__snapshots__/config.test.ts.snap @@ -597,7 +597,10 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t { "test": /\\\\\\.wasm\\$/u, "use": { - "loader": "/foo/bar/loaders/wasm", + "loader": "/foo/bar/src/webpack/loaders/function.ts", + "options": { + "fn": [Function], + }, }, }, ], @@ -2028,12 +2031,13 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "exclude": /node_modules/u, "test": /\\\\\\.\\[tj\\]sx\\?\\$/u, "use": { - "loader": "/foo/bar/loaders/browserify", + "loader": "/Users/morten/Development/MetaMask/snaps/packages/snaps-cli/src/webpack/loaders/function.ts", "options": { "bundlerCustomizer": undefined, "depsToTranspile": [], "dist": "/foo/bar/dist", "eval": true, + "fn": [Function], "manifest": true, "outfileName": "bundle.js", "port": 8081, @@ -2162,12 +2166,13 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "exclude": /node_modules/u, "test": /\\\\\\.\\[tj\\]sx\\?\\$/u, "use": { - "loader": "/foo/bar/loaders/browserify", + "loader": "/Users/morten/Development/MetaMask/snaps/packages/snaps-cli/src/webpack/loaders/function.ts", "options": { "bundlerCustomizer": undefined, "depsToTranspile": [], "dist": "/foo/bar/dist", "eval": true, + "fn": [Function], "manifest": true, "outfileName": "bundle.js", "port": 8081, @@ -2296,12 +2301,13 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "exclude": /node_modules/u, "test": /\\\\\\.\\[tj\\]sx\\?\\$/u, "use": { - "loader": "/foo/bar/loaders/browserify", + "loader": "/Users/morten/Development/MetaMask/snaps/packages/snaps-cli/src/webpack/loaders/function.ts", "options": { "bundlerCustomizer": undefined, "depsToTranspile": [], "dist": "/foo/bar/dist", "eval": true, + "fn": [Function], "manifest": true, "outfileName": "bundle.js", "port": 8081, diff --git a/packages/snaps-cli/src/webpack/config.ts b/packages/snaps-cli/src/webpack/config.ts index 8e539943bf..67b80dc9a1 100644 --- a/packages/snaps-cli/src/webpack/config.ts +++ b/packages/snaps-cli/src/webpack/config.ts @@ -6,6 +6,7 @@ import type { Configuration } from 'webpack'; import { EnvironmentPlugin, ProgressPlugin, ProvidePlugin } from 'webpack'; import type { ProcessedWebpackConfig } from '../config'; +import { getFunctionLoader, wasm } from './loaders'; import { SnapsBuiltInResolver, SnapsBundleWarningsPlugin, @@ -195,9 +196,7 @@ export async function getDefaultConfiguration( config.experimental.wasm && { test: /\.wasm$/u, - use: { - loader: resolve(__dirname, 'loaders', 'wasm'), - }, + use: getFunctionLoader(wasm), }, ], }, diff --git a/packages/snaps-cli/src/webpack/loaders/function.test.ts b/packages/snaps-cli/src/webpack/loaders/function.test.ts new file mode 100644 index 0000000000..3574a4d242 --- /dev/null +++ b/packages/snaps-cli/src/webpack/loaders/function.test.ts @@ -0,0 +1,38 @@ +import { resolve } from 'path'; + +import loader, { getFunctionLoader } from './function'; + +describe('getFunctionLoader', () => { + it('returns a loader definition', () => { + const fn = jest.fn(); + expect( + getFunctionLoader(fn, { + foo: 'bar', + }), + ).toStrictEqual({ + loader: resolve(__dirname, 'function.ts'), + options: { + fn, + foo: 'bar', + }, + }); + }); +}); + +describe('loader', () => { + it('executes the function', async () => { + const fn = jest.fn(); + + await loader.call( + // @ts-expect-error - Partial `this` object. + { + getOptions: () => ({ + fn, + }), + }, + 'test', + ); + + expect(fn).toHaveBeenCalledWith('test'); + }); +}); diff --git a/packages/snaps-cli/src/webpack/loaders/function.ts b/packages/snaps-cli/src/webpack/loaders/function.ts new file mode 100644 index 0000000000..3350448a2c --- /dev/null +++ b/packages/snaps-cli/src/webpack/loaders/function.ts @@ -0,0 +1,61 @@ +import type { LoaderDefinitionFunction } from 'webpack'; + +/** + * Options for the function loader. + */ +export type FunctionLoaderOptions = { + /** + * The function to execute. This is bound to the loader context, so it can + * access the loader options and other properties. + */ + fn: LoaderDefinitionFunction; +}; + +/** + * A loader that executes a function. See {@link getFunctionLoader} for more + * information. + * + * @param content - The input file contents as a `Uint8Array`. + * @returns The output of the function. + */ +const loader: LoaderDefinitionFunction = function ( + content, +) { + const { fn } = this.getOptions(); + return fn.bind(this)(content); +}; + +export default loader; + +/** + * Get a loader that executes the given function. This is useful for executing + * loaders without needing to pass a file to Webpack. + * + * @param fn - The function to execute. + * @param options - The options to pass to the loader. + * @returns The loader definition. + */ +export function getFunctionLoader( + fn: LoaderDefinitionFunction, + options: Options, +) { + return { + // We use `__filename` as the loader, so Webpack will execute the loader in + // this file, with the actual function in the options. + loader: __filename, + options: { + fn, + ...options, + }, + }; +} + +// When running as CJS, we need to export the loader as a default export, since +// `tsup` exports it as `loader_default`. +// istanbul ignore next 3 +// eslint-disable-next-line n/no-process-env +if (typeof module !== 'undefined' && process?.env?.NODE_ENV !== 'test') { + module.exports = loader; + module.exports.getFunctionLoader = getFunctionLoader; + module.exports.raw = true; +} diff --git a/packages/snaps-cli/src/webpack/loaders/index.ts b/packages/snaps-cli/src/webpack/loaders/index.ts new file mode 100644 index 0000000000..7c3da56aff --- /dev/null +++ b/packages/snaps-cli/src/webpack/loaders/index.ts @@ -0,0 +1,3 @@ +export * from './function'; +export { default as browserify } from './browserify'; +export { default as wasm } from './wasm'; diff --git a/packages/snaps-cli/src/webpack/utils.test.ts b/packages/snaps-cli/src/webpack/utils.test.ts index c89f64a888..245fd164ce 100644 --- a/packages/snaps-cli/src/webpack/utils.test.ts +++ b/packages/snaps-cli/src/webpack/utils.test.ts @@ -2,6 +2,7 @@ import { dim } from 'chalk'; import type { ProcessedWebpackConfig } from '../config'; import { getMockConfig } from '../test-utils'; +import { browserify } from './loaders'; import { WEBPACK_FALLBACKS, getBrowserslistTargets, @@ -16,8 +17,11 @@ describe('getDefaultLoader', () => { it('returns the Browserify loader if `legacy` is set', async () => { const config = getMockConfig('browserify'); expect(await getDefaultLoader(config)).toStrictEqual({ - loader: expect.stringContaining('browserify'), - options: config.legacy, + loader: expect.stringContaining('function'), + options: { + ...config.legacy, + fn: browserify, + }, }); }); diff --git a/packages/snaps-cli/src/webpack/utils.ts b/packages/snaps-cli/src/webpack/utils.ts index 74283ef631..235b07929a 100644 --- a/packages/snaps-cli/src/webpack/utils.ts +++ b/packages/snaps-cli/src/webpack/utils.ts @@ -6,6 +6,7 @@ import { dirname, resolve } from 'path'; import type { Configuration } from 'webpack'; import type { ProcessedWebpackConfig } from '../config'; +import { browserify, getFunctionLoader } from './loaders'; export const BROWSERSLIST_FILE = resolve( dirname( @@ -71,21 +72,13 @@ export async function getDefaultLoader({ sourceMap, }: ProcessedWebpackConfig) { if (legacy) { - return { - /** - * If the snap uses the legacy config, we use the custom `browserify` - * loader. This uses the legacy Browserify config to transpile the code. - * This is necessary for backwards compatibility with the - * `bundlerCustomizer` function. - */ - loader: resolve(__dirname, 'loaders', 'browserify'), - - /** - * The options for the `browserify` loader. These can be overridden in the - * snap config. - */ - options: legacy, - }; + /** + * If the snap uses the legacy config, we use the custom `browserify` + * loader. This uses the legacy Browserify config to transpile the code. + * This is necessary for backwards compatibility with the + * `bundlerCustomizer` function. + */ + return getFunctionLoader(browserify, legacy); } const targets = await getBrowserslistTargets(); diff --git a/packages/snaps-cli/tsconfig.json b/packages/snaps-cli/tsconfig.json index 526029de4f..cf0ed65956 100644 --- a/packages/snaps-cli/tsconfig.json +++ b/packages/snaps-cli/tsconfig.json @@ -3,7 +3,13 @@ "compilerOptions": { "baseUrl": "./" }, - "include": ["./src", "./src/**/*.json", "jest.setup.ts"], + "include": [ + "./src", + "./src/**/*.json", + "jest.setup.ts", + "package.json", + "tsup.config.ts" + ], "references": [ { "path": "../snaps-utils" }, { "path": "../snaps-browserify-plugin" }, diff --git a/packages/snaps-cli/tsup.config.ts b/packages/snaps-cli/tsup.config.ts new file mode 100644 index 0000000000..5af30151f5 --- /dev/null +++ b/packages/snaps-cli/tsup.config.ts @@ -0,0 +1,15 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, + external: ['@metamask/snaps-cli'], +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index d7bee98103..2dc329b489 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { - "branches": 90.77, - "functions": 96.6, - "lines": 97.84, - "statements": 97.54 + "branches": 91.15, + "functions": 96.86, + "lines": 97.95, + "statements": 97.67 } diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index 5e22907fe4..1b1c6bd682 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -7,17 +7,23 @@ "url": "https://github.com/MetaMask/snaps.git" }, "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "browser": { "./dist/cjs/services": "./dist/cjs/services/browser.js", "./dist/esm/services": "./dist/esm/services/browser.js" }, "types": "./dist/types/index.d.ts", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**" + "dist" ], "scripts": { "test:prepare": "yarn mkdirp test/fixtures && ./scripts/generate-fixtures.sh", @@ -25,12 +31,8 @@ "posttest": "ts-node scripts/coverage.ts && rimraf coverage/jest coverage/wdio", "test:browser": "wdio run wdio.config.js", "test:ci": "yarn test", - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", - "build:clean": "yarn clean && yarn build", "clean": "rimraf '*.tsbuildinfo' 'dist'", "lint:eslint": "eslint . --cache --ext js,ts,jsx,tsx", "lint:misc": "prettier --no-error-on-unmatched-pattern --loglevel warn \"**/*.json\" \"**/*.md\" \"**/*.html\" \"!CHANGELOG.md\" --ignore-path ../../.gitignore", @@ -76,7 +78,6 @@ "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", "@metamask/template-snap": "^0.7.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", "@types/chrome": "^0.0.237", @@ -117,6 +118,7 @@ "prettier-plugin-packagejson": "^2.2.11", "rimraf": "^4.1.2", "ts-node": "^10.9.1", + "tsup": "^8.0.1", "typescript": "~4.8.4", "vite": "^4.3.9", "vite-tsconfig-paths": "^4.0.5", @@ -138,5 +140,6 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" - } + }, + "build:types": "tsc --project tsconfig.build.json" } diff --git a/packages/snaps-controllers/src/index.ts b/packages/snaps-controllers/src/index.ts index e0f567c7eb..490f768ccc 100644 --- a/packages/snaps-controllers/src/index.ts +++ b/packages/snaps-controllers/src/index.ts @@ -3,3 +3,4 @@ export * from './services'; export * from './snaps'; export * from './utils'; export * from './cronjob'; +export * from './interface'; diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.test.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.ts new file mode 100644 index 0000000000..2053371ea8 --- /dev/null +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.test.ts @@ -0,0 +1,292 @@ +import type { SnapId } from '@metamask/snaps-sdk'; +import { form, input } from '@metamask/snaps-sdk'; +import { MOCK_SNAP_ID } from '@metamask/snaps-utils/test-utils'; + +import { + getRestrictedSnapInterfaceControllerMessenger, + getRootSnapInterfaceControllerMessenger, +} from '../test-utils'; +import { SnapInterfaceController } from './SnapInterfaceController'; + +describe('SnapInterfaceController', () => { + describe('createInterface', () => { + it('can create a new interface', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const components = form({ + name: 'foo', + children: [input({ name: 'bar' })], + }); + + const id = rootMessenger.call( + 'SnapInterfaceController:createInterface', + MOCK_SNAP_ID, + components, + ); + + const { content, state } = rootMessenger.call( + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + id, + ); + + expect(content).toStrictEqual(components); + expect(state).toStrictEqual({ foo: { bar: null } }); + }); + }); + + describe('getInterface', () => { + it('gets the interface', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const components = form({ + name: 'foo', + children: [input({ name: 'bar' })], + }); + + const id = rootMessenger.call( + 'SnapInterfaceController:createInterface', + MOCK_SNAP_ID, + components, + ); + + const { content } = rootMessenger.call( + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + id, + ); + expect(content).toStrictEqual(components); + }); + + it('throws if the snap requesting the interface is not the one that created it', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const components = form({ + name: 'foo', + children: [input({ name: 'bar' })], + }); + + const id = rootMessenger.call( + 'SnapInterfaceController:createInterface', + MOCK_SNAP_ID, + components, + ); + + expect(() => + rootMessenger.call( + 'SnapInterfaceController:getInterface', + 'foo' as SnapId, + id, + ), + ).toThrow(`Interface not created by foo.`); + }); + + it('throws if the interface does not exist', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + expect(() => + rootMessenger.call( + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + 'test', + ), + ).toThrow(`Interface with id 'test' not found.`); + }); + }); + + describe('updateInterface', () => { + it('can update an interface', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const components = form({ + name: 'foo', + children: [input({ name: 'bar' })], + }); + + const newContent = form({ + name: 'foo', + children: [input({ name: 'baz' })], + }); + + const id = rootMessenger.call( + 'SnapInterfaceController:createInterface', + MOCK_SNAP_ID, + components, + ); + + rootMessenger.call( + 'SnapInterfaceController:updateInterface', + MOCK_SNAP_ID, + id, + newContent, + ); + + const { content, state } = rootMessenger.call( + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + id, + ); + + expect(content).toStrictEqual(newContent); + expect(state).toStrictEqual({ foo: { baz: null } }); + }); + + it('throws if the interface does not exist', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const content = form({ name: 'foo', children: [input({ name: 'bar' })] }); + + expect(() => + rootMessenger.call( + 'SnapInterfaceController:updateInterface', + MOCK_SNAP_ID, + 'foo', + content, + ), + ).toThrow("Interface with id 'foo' not found."); + }); + + it('throws if the interface is updated by another snap', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const content = form({ name: 'foo', children: [input({ name: 'bar' })] }); + + const newContent = form({ + name: 'foo', + children: [input({ name: 'baz' })], + }); + + const id = rootMessenger.call( + 'SnapInterfaceController:createInterface', + MOCK_SNAP_ID, + content, + ); + + expect(() => + rootMessenger.call( + 'SnapInterfaceController:updateInterface', + 'foo' as SnapId, + id, + newContent, + ), + ).toThrow('Interface not created by foo.'); + }); + }); + + describe('updateInterfaceState', () => { + it('updates the interface state', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const content = form({ name: 'foo', children: [input({ name: 'bar' })] }); + + const newState = { foo: { bar: 'baz' } }; + + const id = rootMessenger.call( + 'SnapInterfaceController:createInterface', + MOCK_SNAP_ID, + content, + ); + + rootMessenger.call( + 'SnapInterfaceController:updateInterfaceState', + id, + newState, + ); + + const { state } = rootMessenger.call( + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + id, + ); + + expect(state).toStrictEqual(newState); + }); + }); + + describe('deleteInterface', () => { + it('can delete an interface', () => { + const rootMessenger = getRootSnapInterfaceControllerMessenger(); + const controllerMessenger = + getRestrictedSnapInterfaceControllerMessenger(rootMessenger); + + /* eslint-disable-next-line no-new */ + new SnapInterfaceController({ + messenger: controllerMessenger, + }); + + const content = form({ name: 'foo', children: [input({ name: 'bar' })] }); + + const id = rootMessenger.call( + 'SnapInterfaceController:createInterface', + MOCK_SNAP_ID, + content, + ); + + rootMessenger.call('SnapInterfaceController:deleteInterface', id); + + expect(() => + rootMessenger.call( + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + id, + ), + ).toThrow(`Interface with id '${id}' not found.`); + }); + }); +}); diff --git a/packages/snaps-controllers/src/interface/SnapInterfaceController.ts b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts new file mode 100644 index 0000000000..c32cc46b21 --- /dev/null +++ b/packages/snaps-controllers/src/interface/SnapInterfaceController.ts @@ -0,0 +1,215 @@ +import type { RestrictedControllerMessenger } from '@metamask/base-controller'; +import { BaseController } from '@metamask/base-controller'; +import type { Component, InterfaceState, SnapId } from '@metamask/snaps-sdk'; +import { assert } from '@metamask/utils'; +import { nanoid } from 'nanoid'; + +import { constructState } from './utils'; + +const controllerName = 'SnapInterfaceController'; + +export type CreateInterface = { + type: `${typeof controllerName}:createInterface`; + handler: SnapInterfaceController['createInterface']; +}; + +export type GetInterface = { + type: `${typeof controllerName}:getInterface`; + handler: SnapInterfaceController['getInterface']; +}; + +export type UpdateInterface = { + type: `${typeof controllerName}:updateInterface`; + handler: SnapInterfaceController['updateInterface']; +}; + +export type DeleteInterface = { + type: `${typeof controllerName}:deleteInterface`; + handler: SnapInterfaceController['deleteInterface']; +}; + +export type UpdateInterfaceState = { + type: `${typeof controllerName}:updateInterfaceState`; + handler: SnapInterfaceController['updateInterfaceState']; +}; + +export type SnapInterfaceControllerActions = + | CreateInterface + | GetInterface + | UpdateInterface + | DeleteInterface + | UpdateInterfaceState; + +export type SnapInterfaceControllerMessenger = RestrictedControllerMessenger< + typeof controllerName, + SnapInterfaceControllerActions, + never, + SnapInterfaceControllerActions['type'], + never +>; + +export type StoredInterface = { + snapId: SnapId; + content: Component; + state: InterfaceState; +}; + +export type SnapInterfaceControllerState = { + interfaces: Record; +}; + +export type SnapInterfaceControllerArgs = { + messenger: SnapInterfaceControllerMessenger; + state?: SnapInterfaceControllerState; +}; + +/** + * Use this controller to manage snaps UI interfaces using RPC method hooks. + */ +export class SnapInterfaceController extends BaseController< + typeof controllerName, + SnapInterfaceControllerState, + SnapInterfaceControllerMessenger +> { + constructor({ messenger, state }: SnapInterfaceControllerArgs) { + super({ + messenger, + metadata: { + interfaces: { persist: false, anonymous: false }, + }, + name: controllerName, + state: { interfaces: {}, ...state }, + }); + + this.#registerMessageHandlers(); + } + + /** + * Constructor helper for registering this controller's messaging system + * actions. + */ + #registerMessageHandlers() { + this.messagingSystem.registerActionHandler( + `${controllerName}:createInterface`, + this.createInterface.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:getInterface`, + this.getInterface.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:updateInterface`, + this.updateInterface.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:deleteInterface`, + this.deleteInterface.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:updateInterfaceState`, + this.updateInterfaceState.bind(this), + ); + } + + /** + * Create an interface in the controller state with the associated data. + * + * @param snapId - The snap id that created the interface. + * @param content - The interface content. + * @returns The newly interface id. + */ + createInterface(snapId: SnapId, content: Component) { + const id = nanoid(); + + const componentState = constructState({}, content); + + this.update((draftState) => { + draftState.interfaces[id] = { + snapId, + content, + state: componentState, + }; + }); + + return id; + } + + /** + * Get the data of a given interface id. + * + * @param snapId - The snap id requesting the interface data. + * @param id - The interface id. + * @returns The interface state. + */ + getInterface(snapId: SnapId, id: string) { + this.#validateArgs(snapId, id); + + return this.state.interfaces[id]; + } + + /** + * Update the interface with the given content. + * + * @param snapId - The snap id requesting the update. + * @param id - The interface id. + * @param content - The new content. + */ + updateInterface(snapId: SnapId, id: string, content: Component) { + this.#validateArgs(snapId, id); + + const oldState = this.state.interfaces[id].state; + + const newState = constructState(oldState, content); + + this.update((draftState) => { + draftState.interfaces[id].state = newState; + draftState.interfaces[id].content = content; + }); + } + + /** + * Delete an interface from state. + * + * @param id - The interface id. + */ + deleteInterface(id: string) { + this.update((draftState) => { + delete draftState.interfaces[id]; + }); + } + + /** + * Update the interface state. + * + * @param id - The interface id. + * @param state - The new state. + */ + updateInterfaceState(id: string, state: InterfaceState) { + this.update((draftState) => { + draftState.interfaces[id].state = state; + }); + } + + /** + * Utility function to validate the args passed to the other methods. + * + * @param snapId - The snap id. + * @param id - The interface id. + */ + #validateArgs(snapId: SnapId, id: string) { + const existingInterface = this.state.interfaces[id]; + + assert( + existingInterface !== undefined, + `Interface with id '${id}' not found.`, + ); + assert( + existingInterface.snapId === snapId, + `Interface not created by ${snapId}.`, + ); + } +} diff --git a/packages/snaps-controllers/src/interface/index.ts b/packages/snaps-controllers/src/interface/index.ts new file mode 100644 index 0000000000..e3f329b66e --- /dev/null +++ b/packages/snaps-controllers/src/interface/index.ts @@ -0,0 +1 @@ +export * from './SnapInterfaceController'; diff --git a/packages/snaps-controllers/src/interface/utils.test.ts b/packages/snaps-controllers/src/interface/utils.test.ts new file mode 100644 index 0000000000..9438e3fad1 --- /dev/null +++ b/packages/snaps-controllers/src/interface/utils.test.ts @@ -0,0 +1,286 @@ +import { form, input, panel, text } from '@metamask/snaps-sdk'; + +import { + assertNameIsUnique, + constructFormInputState, + constructInputState, + constructState, +} from './utils'; + +describe('constructInputState', () => { + it('returns null if the input has no previous value', () => { + const component = input('bar'); + + const result = constructInputState({}, component); + + expect(result).toBeNull(); + }); + + it('returns the previous state value of an input', () => { + const state = { bar: 'foo' }; + + const component = input('bar'); + + const result = constructInputState(state, component); + + expect(result).toBe('foo'); + }); + + it('returns the defined component value if it is set', () => { + const state = { bar: 'foo' }; + + const component = input({ name: 'bar', value: 'baz' }); + + const result = constructInputState(state, component); + + expect(result).toBe('baz'); + }); +}); + +describe('constructFormInputState', () => { + it('returns null if the input has no previous value', () => { + const component = input('bar'); + + const result = constructFormInputState({}, component, 'foo'); + + expect(result).toBeNull(); + }); + + it('returns the previous state value of an input', () => { + const state = { baz: { bar: 'foo' } }; + + const component = input('bar'); + + const result = constructFormInputState(state, component, 'baz'); + + expect(result).toBe('foo'); + }); + + it('returns the defined component value if it is set', () => { + const state = { baz: { bar: 'foo' } }; + + const component = input({ name: 'bar', value: 'baz' }); + + const result = constructFormInputState(state, component, 'baz'); + + expect(result).toBe('baz'); + }); +}); + +describe('assertNameIsUnique', () => { + it('throws an error if a name is not unique', () => { + const state = { test: 'foo' }; + + expect(() => assertNameIsUnique(state, 'test')).toThrow( + `Duplicate component names are not allowed, found multiple instances of: "test".`, + ); + }); + + it('passes if there is no duplicate name', () => { + const state = { test: 'foo' }; + + expect(() => assertNameIsUnique(state, 'bar')).not.toThrow(); + }); +}); + +describe('constructState', () => { + it('can construct a new component state', () => { + const components = panel([ + text('text'), + form({ name: 'foo', children: [input({ name: 'bar' })] }), + ]); + + const result = constructState({}, components); + + expect(result).toStrictEqual({ foo: { bar: null } }); + }); + + it('merges two states', () => { + const state = { foo: { bar: 'test' } }; + + const components = panel([ + text('text'), + form({ + name: 'foo', + children: [input({ name: 'bar' }), input({ name: 'baz' })], + }), + ]); + + const result = constructState(state, components); + + expect(result).toStrictEqual({ foo: { bar: 'test', baz: null } }); + }); + + it('deletes unused state', () => { + const state = { form: { foo: null, bar: 'test' } }; + + const components = panel([ + text('text'), + form({ + name: 'form', + children: [input({ name: 'bar' }), input({ name: 'baz' })], + }), + ]); + + const result = constructState(state, components); + + expect(result).toStrictEqual({ form: { bar: 'test', baz: null } }); + }); + + it('handles multiple forms', () => { + const state = { + form1: { foo: null, bar: 'test' }, + form2: { foo: 'abc', bar: 'def' }, + }; + + const components = panel([ + text('text'), + form({ + name: 'form1', + children: [input({ name: 'bar' }), input({ name: 'baz' })], + }), + form({ + name: 'form2', + children: [input({ name: 'bar' }), input({ name: 'baz' })], + }), + ]); + + const result = constructState(state, components); + + expect(result).toStrictEqual({ + form1: { bar: 'test', baz: null }, + form2: { bar: 'def', baz: null }, + }); + }); + + it('deletes unused form', () => { + const state = { + form1: { foo: null, bar: 'test' }, + form2: { foo: 'abc', bar: 'def' }, + }; + + const components = panel([ + text('text'), + form({ + name: 'form1', + children: [input({ name: 'bar' }), input({ name: 'baz' })], + }), + ]); + + const result = constructState(state, components); + + expect(result).toStrictEqual({ + form1: { bar: 'test', baz: null }, + }); + }); + + it('handles nesting forms', () => { + const state = { + form1: { foo: null, bar: 'test' }, + form2: { foo: 'abc', bar: 'def' }, + }; + + const components = panel([ + text('text'), + panel([ + form({ + name: 'form1', + children: [input({ name: 'bar' }), input({ name: 'baz' })], + }), + ]), + panel([ + form({ + name: 'form2', + children: [input({ name: 'bar' }), input({ name: 'baz' })], + }), + ]), + ]); + + const result = constructState(state, components); + + expect(result).toStrictEqual({ + form1: { bar: 'test', baz: null }, + form2: { bar: 'def', baz: null }, + }); + }); + + it('handles root level inputs with value', () => { + const components = panel([input({ name: 'foo', value: 'bar' })]); + + const result = constructState({}, components); + + expect(result).toStrictEqual({ + foo: 'bar', + }); + }); + + it('handles root level inputs without value', () => { + const components = panel([input({ name: 'foo' })]); + + const result = constructState({}, components); + + expect(result).toStrictEqual({ + foo: null, + }); + }); + + it('deletes unused root level values', () => { + const components = panel([input({ name: 'foo' })]); + + const result = constructState({ foo: null, bar: null }, components); + + expect(result).toStrictEqual({ + foo: null, + }); + }); + + it('merges root level inputs from old state', () => { + const state = { + foo: 'bar', + }; + + const components = panel([input({ name: 'foo' })]); + + const result = constructState(state, components); + + expect(result).toStrictEqual({ + foo: 'bar', + }); + }); + + it('throws if a name is not unique in a form', () => { + const components = form({ + name: 'test', + children: [input({ name: 'foo' }), input({ name: 'foo' })], + }); + + expect(() => constructState({}, components)).toThrow( + `Duplicate component names are not allowed, found multiple instances of: "foo".`, + ); + }); + + it('throws if a name is not unique at the root', () => { + const components = panel([ + input({ name: 'test' }), + input({ name: 'test' }), + ]); + + expect(() => constructState({}, components)).toThrow( + `Duplicate component names are not allowed, found multiple instances of: "test".`, + ); + }); + + it('throws if a form has the same name as an input', () => { + const components = panel([ + input({ name: 'test' }), + form({ + name: 'test', + children: [input({ name: 'foo' })], + }), + ]); + + expect(() => constructState({}, components)).toThrow( + `Duplicate component names are not allowed, found multiple instances of: "test".`, + ); + }); +}); diff --git a/packages/snaps-controllers/src/interface/utils.ts b/packages/snaps-controllers/src/interface/utils.ts new file mode 100644 index 0000000000..63cab85f52 --- /dev/null +++ b/packages/snaps-controllers/src/interface/utils.ts @@ -0,0 +1,102 @@ +import { NodeType, assert } from '@metamask/snaps-sdk'; +import type { + Component, + Input, + FormState, + InterfaceState, +} from '@metamask/snaps-sdk'; + +/** + * Construct the state for a stray input (not enclosed in a form). + * + * @param state - The interface state. + * @param component - The Input component. + * @returns The input state. + */ +export const constructInputState = ( + state: InterfaceState, + component: Input, +) => { + return component.value ?? state[component.name] ?? null; +}; + +/** + * Construct the state for a form input. + * + * Sets the state to either the specified component value, the previous value from the old state or null. + * + * @param state - The interface state. + * @param component - The Input component. + * @param form - The parent form name of the input. + * @returns The input state. + */ +export const constructFormInputState = ( + state: InterfaceState, + component: Input, + form: string, +) => { + const oldFormState = state[form] as FormState; + const oldInputState = oldFormState?.[component.name]; + return component.value ?? oldInputState ?? null; +}; + +/** + * Assert that the component name is unique in state. + * + * @param state - The interface state to verify against. + * @param name - The component name to verify. + */ +export const assertNameIsUnique = (state: InterfaceState, name: string) => { + assert( + state[name] === undefined, + `Duplicate component names are not allowed, found multiple instances of: "${name}".`, + ); +}; + +/** + * Construct the interface state for a given component tree while preserving values for matching stateful components in the old state. + * + * @param oldState - The previous state. + * @param component - The UI component to construct state from. + * @param newState - The state that is being constructed. + * @returns The interface state of the passed component. + */ +export const constructState = ( + oldState: InterfaceState, + component: Component, + newState: InterfaceState = {}, +): InterfaceState => { + const { type } = component; + if (type === NodeType.Panel) { + return component.children.reduce( + (acc, node) => constructState(oldState, node, acc), + newState, + ); + } + + if (type === NodeType.Form) { + assertNameIsUnique(newState, component.name); + newState[component.name] = component.children.reduce( + (acc, node) => { + if (node.type === NodeType.Input) { + assertNameIsUnique(acc, node.name); + acc[node.name] = constructFormInputState( + oldState, + node, + component.name, + ); + } + + return acc; + }, + {}, + ); + } + + if (type === NodeType.Input) { + assertNameIsUnique(newState, component.name); + newState[component.name] = constructInputState(oldState, component); + } + + return newState; +}; diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.ts b/packages/snaps-controllers/src/snaps/SnapController.test.ts index 35bac6303f..d3a5d25eae 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.test.ts @@ -671,7 +671,7 @@ describe('SnapController', () => { it('supports non-snap permissions', async () => { const messenger = getSnapControllerMessenger(); const initialPermissions: SnapPermissions = { - [handlerEndowments.onRpcRequest]: { snaps: false, dapps: true }, + [handlerEndowments.onRpcRequest as string]: { snaps: false, dapps: true }, // @ts-expect-error Current type only expects snap permissions // eslint-disable-next-line @typescript-eslint/naming-convention eth_accounts: { @@ -1516,7 +1516,11 @@ describe('SnapController', () => { }); describe('handleRequest', () => { - it.each(Object.keys(handlerEndowments) as HandlerType[])( + it.each( + Object.keys(handlerEndowments).filter( + (handler) => handlerEndowments[handler as HandlerType], + ) as HandlerType[], + )( 'throws if the snap does not have permission for the handler', async (handler) => { const rootMessenger = getControllerMessenger(); @@ -1544,13 +1548,45 @@ describe('SnapController', () => { request: { jsonrpc: '2.0', method: 'test' }, }), ).rejects.toThrow( - `Snap "${snap.id}" is not permitted to use "${handlerEndowments[handler]}".`, + `Snap "${snap.id}" is not permitted to use "${ + handlerEndowments[handler] as string + }".`, ); snapController.destroy(); }, ); + it('does not throw if the snap uses a permitted handler', async () => { + const rootMessenger = getControllerMessenger(); + const messenger = getSnapControllerMessenger(rootMessenger); + const snapController = getSnapController( + getSnapControllerOptions({ + messenger, + state: { + snaps: getPersistedSnapsState(), + }, + }), + ); + + rootMessenger.registerActionHandler( + 'PermissionController:hasPermission', + () => false, + ); + + const snap = snapController.getExpect(MOCK_SNAP_ID); + expect( + await snapController.handleRequest({ + snapId: snap.id, + origin: 'foo.com', + handler: HandlerType.OnUserInput, + request: { jsonrpc: '2.0', method: 'test' }, + }), + ).toBeUndefined(); + + snapController.destroy(); + }); + it('allows MetaMask to send a JSON-RPC request', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); @@ -2590,6 +2626,71 @@ describe('SnapController', () => { snapController.destroy(); }); + it('gets the interface content if the result is an interface id', async () => { + const rootMessenger = getControllerMessenger(); + const messenger = getSnapControllerMessenger(rootMessenger); + const snapController = getSnapController( + getSnapControllerOptions({ + messenger, + state: { + snaps: getPersistedSnapsState(), + }, + }), + ); + + const handlerResponse = { id: 'foo' }; + + rootMessenger.registerActionHandler( + 'PermissionController:getPermissions', + () => ({ + [SnapEndowments.HomePage]: { + caveats: null, + date: 1664187844588, + id: 'izn0WGUO8cvq_jqvLQuQP', + invoker: MOCK_SNAP_ID, + parentCapability: SnapEndowments.HomePage, + }, + }), + ); + + rootMessenger.registerActionHandler( + 'SubjectMetadataController:getSubjectMetadata', + () => MOCK_SNAP_SUBJECT_METADATA, + ); + + rootMessenger.registerActionHandler( + 'ExecutionService:handleRpcRequest', + async () => Promise.resolve(handlerResponse), + ); + + rootMessenger.registerActionHandler( + 'SnapInterfaceController:getInterface', + () => ({ snapId: MOCK_SNAP_ID, state: {}, content: text('hello') }), + ); + + const result = await snapController.handleRequest({ + snapId: MOCK_SNAP_ID, + origin: 'foo.com', + handler: HandlerType.OnHomePage, + request: { + jsonrpc: '2.0', + method: ' ', + params: {}, + id: 1, + }, + }); + + expect(rootMessenger.call).toHaveBeenNthCalledWith( + 5, + 'SnapInterfaceController:getInterface', + MOCK_SNAP_ID, + 'foo', + ); + expect(result).toBe(handlerResponse); + + snapController.destroy(); + }); + it("doesn't throw if onHomePage return value is valid", async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); @@ -3990,7 +4091,10 @@ describe('SnapController', () => { it('throws an error if a forbidden permission is requested', async () => { const initialPermissions = { - [handlerEndowments.onRpcRequest]: { snaps: false, dapps: true }, + [handlerEndowments.onRpcRequest as string]: { + snaps: false, + dapps: true, + }, // eslint-disable-next-line @typescript-eslint/naming-convention 'endowment:webassembly': {}, }; @@ -4110,7 +4214,7 @@ describe('SnapController', () => { requestState: { loading: false, permissions: { - [handlerEndowments.onRpcRequest]: { + [handlerEndowments.onRpcRequest as string]: { caveats: [ { type: SnapCaveatType.RpcOrigin, @@ -4140,7 +4244,7 @@ describe('SnapController', () => { 'PermissionController:grantPermissions', { approvedPermissions: { - [handlerEndowments.onRpcRequest]: { + [handlerEndowments.onRpcRequest as string]: { caveats: [ { type: SnapCaveatType.RpcOrigin, @@ -4178,7 +4282,10 @@ describe('SnapController', () => { it('maps endowment permission caveats to the proper format', async () => { const initialPermissions = { - [handlerEndowments.onRpcRequest]: { snaps: false, dapps: true }, + [handlerEndowments.onRpcRequest as string]: { + snaps: false, + dapps: true, + }, }; const { manifest, sourceCode, svgIcon } = await getMockSnapFilesWithUpdatedChecksum({ @@ -4270,7 +4377,10 @@ describe('SnapController', () => { it('maps permission caveats to the proper format when updating snaps', async () => { const initialPermissions = { - [handlerEndowments.onRpcRequest]: { snaps: false, dapps: true }, + [handlerEndowments.onRpcRequest as string]: { + snaps: false, + dapps: true, + }, // eslint-disable-next-line @typescript-eslint/naming-convention snap_getBip32Entropy: [ { path: ['m', "44'", "1'"], curve: 'secp256k1' as const }, @@ -4315,7 +4425,7 @@ describe('SnapController', () => { 'PermissionController:grantPermissions', { approvedPermissions: { - [handlerEndowments.onRpcRequest]: { + [handlerEndowments.onRpcRequest as string]: { caveats: [ { type: SnapCaveatType.RpcOrigin, @@ -5556,7 +5666,10 @@ describe('SnapController', () => { /* eslint-disable @typescript-eslint/naming-convention */ const initialPermissions = { - [handlerEndowments.onRpcRequest]: { snaps: false, dapps: true }, + [handlerEndowments.onRpcRequest as string]: { + snaps: false, + dapps: true, + }, snap_dialog: {}, snap_manageState: {}, }; @@ -5578,7 +5691,7 @@ describe('SnapController', () => { date: 1, invoker: MOCK_SNAP_ID, }, - [handlerEndowments.onRpcRequest]: { + [handlerEndowments.onRpcRequest as string]: { caveats: [ { type: SnapCaveatType.RpcOrigin, @@ -5588,7 +5701,7 @@ describe('SnapController', () => { }, }, ], - parentCapability: handlerEndowments.onRpcRequest, + parentCapability: handlerEndowments.onRpcRequest as string, id: '3', date: 1, invoker: MOCK_SNAP_ID, @@ -5606,7 +5719,10 @@ describe('SnapController', () => { manifest: getSnapManifest({ version: '1.1.0' as SemVerRange, initialPermissions: { - [handlerEndowments.onRpcRequest]: { snaps: false, dapps: true }, + [handlerEndowments.onRpcRequest as string]: { + snaps: false, + dapps: true, + }, snap_dialog: {}, 'endowment:network-access': {}, }, @@ -5710,8 +5826,8 @@ describe('SnapController', () => { newVersion: '1.1.0', newPermissions: { 'endowment:network-access': {} }, approvedPermissions: { - [handlerEndowments.onRpcRequest]: - approvedPermissions[handlerEndowments.onRpcRequest], + [handlerEndowments.onRpcRequest as string]: + approvedPermissions[handlerEndowments.onRpcRequest as string], snap_dialog: approvedPermissions.snap_dialog, }, unusedPermissions: { @@ -5910,7 +6026,10 @@ describe('SnapController', () => { /* eslint-disable @typescript-eslint/naming-convention */ const initialPermissions = { - [handlerEndowments.onRpcRequest]: { snaps: false, dapps: true }, + [handlerEndowments.onRpcRequest as string]: { + snaps: false, + dapps: true, + }, snap_dialog: {}, snap_manageState: {}, }; @@ -5931,7 +6050,7 @@ describe('SnapController', () => { date: 1, invoker: MOCK_SNAP_ID, }, - [handlerEndowments.onRpcRequest]: { + [handlerEndowments.onRpcRequest as string]: { caveats: [ { type: SnapCaveatType.RpcOrigin, @@ -5941,7 +6060,7 @@ describe('SnapController', () => { }, }, ], - parentCapability: handlerEndowments.onRpcRequest, + parentCapability: handlerEndowments.onRpcRequest as string, id: '3', date: 1, invoker: MOCK_SNAP_ID, @@ -5959,7 +6078,7 @@ describe('SnapController', () => { manifest: getSnapManifest({ version: '1.1.0' as SemVerRange, initialPermissions: { - [handlerEndowments.onRpcRequest]: { + [handlerEndowments.onRpcRequest as string]: { snaps: false, dapps: true, }, diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index 677ddd1310..519bb56e8a 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -36,6 +36,7 @@ import type { RequestSnapsParams, RequestSnapsResult, SnapId, + Component, } from '@metamask/snaps-sdk'; import { AuxiliaryFileEncoding, getErrorMessage } from '@metamask/snaps-sdk'; import type { @@ -96,6 +97,7 @@ import type { Patch } from 'immer'; import { nanoid } from 'nanoid'; import { forceStrict, validateMachine } from '../fsm'; +import type { GetInterface } from '../interface'; import { log } from '../logging'; import type { ExecuteSnapAction, @@ -514,7 +516,8 @@ export type AllowedActions = | Update | ResolveVersion | TestOrigin - | MaybeUpdateState; + | MaybeUpdateState + | GetInterface; export type AllowedEvents = | ExecutionServiceEvents @@ -2588,9 +2591,9 @@ export class SnapController extends BaseController< assert( permissionKeys.some((key) => handlerPermissions.includes(key)), - `A snap must request at least one of the following permissions: ${handlerPermissions.join( - ', ', - )}.`, + `A snap must request at least one of the following permissions: ${handlerPermissions + .filter((handler) => handler !== null) + .join(', ')}.`, ); const excludedPermissionErrors = permissionKeys.reduce( @@ -2719,16 +2722,24 @@ export class SnapController extends BaseController< assertIsJsonRpcRequest(request); const permissionName = handlerEndowments[handlerType]; - const hasPermission = this.messagingSystem.call( - 'PermissionController:hasPermission', - snapId, - permissionName, + + assert( + typeof permissionName === 'string' || permissionName === null, + "'permissionName' must be either a string or null.", ); - if (!hasPermission) { - throw new Error( - `Snap "${snapId}" is not permitted to use "${permissionName}".`, + if (permissionName) { + const hasPermission = this.messagingSystem.call( + 'PermissionController:hasPermission', + snapId, + permissionName, ); + + if (!hasPermission) { + throw new Error( + `Snap "${snapId}" is not permitted to use "${permissionName}".`, + ); + } } if ( @@ -2849,7 +2860,7 @@ export class SnapController extends BaseController< timer, ); - await this.#assertSnapRpcRequestResult(handlerType, result); + await this.#assertSnapRpcRequestResult(handlerType, result, snapId); return result; } catch (error) { @@ -2878,13 +2889,45 @@ export class SnapController extends BaseController< .result; } + #getInterface(snapId: SnapId, interfaceId: string) { + return this.messagingSystem.call( + 'SnapInterfaceController:getInterface', + snapId, + interfaceId, + ); + } + /** - * Asserts that the returned result of a Snap RPC call is the expected shape. + * Get the UI components from the interface if the response contains an interface id + * otherwise return the UI components of the response. + * + * @param snapId - The ID of the snap that returned the result. + * @param result - The result of the RPC request. + * @returns The UI components. + */ + #getResponseContent( + snapId: SnapId, + result: { content: Component } | { id: string }, + ) { + if (hasProperty(result, 'id')) { + const { content } = this.#getInterface(snapId, result.id as string); + return content; + } + return result.content; + } + + /** + * Assert that the returned result of a Snap RPC call is the expected shape. * * @param handlerType - The handler type of the RPC Request. * @param result - The result of the RPC request. + * @param snapId - The ID of the snap that returned the result. */ - async #assertSnapRpcRequestResult(handlerType: HandlerType, result: unknown) { + async #assertSnapRpcRequestResult( + handlerType: HandlerType, + result: unknown, + snapId: SnapId, + ) { switch (handlerType) { case HandlerType.OnTransaction: { assertStruct(result, OnTransactionResponseStruct); @@ -2917,14 +2960,15 @@ export class SnapController extends BaseController< break; } case HandlerType.OnHomePage: - assertStruct(result, OnHomePageResponseStruct); + { + assertStruct(result, OnHomePageResponseStruct); - await this.#triggerPhishingListUpdate(); + await this.#triggerPhishingListUpdate(); - validateComponentLinks( - result.content, - this.#checkPhishingList.bind(this), - ); + const content = this.#getResponseContent(snapId, result); + + validateComponentLinks(content, this.#checkPhishingList.bind(this)); + } break; default: break; @@ -3230,6 +3274,9 @@ export class SnapController extends BaseController< */ async #callLifecycleHook(snapId: SnapId, handler: HandlerType) { const permissionName = handlerEndowments[handler]; + + assert(permissionName, 'Lifecycle hook must have an endowment.'); + const hasPermission = this.messagingSystem.call( 'PermissionController:hasPermission', snapId, diff --git a/packages/snaps-controllers/src/snaps/endowments/caveats/generic.test.ts b/packages/snaps-controllers/src/snaps/endowments/caveats/generic.test.ts new file mode 100644 index 0000000000..f42cfaa4f1 --- /dev/null +++ b/packages/snaps-controllers/src/snaps/endowments/caveats/generic.test.ts @@ -0,0 +1,96 @@ +import type { PermissionConstraint } from '@metamask/permission-controller'; +import { SnapCaveatType } from '@metamask/snaps-utils'; +import { MOCK_ORIGIN } from '@metamask/snaps-utils/test-utils'; + +import { createGenericPermissionValidator } from './generic'; + +const MOCK_PERMISSION: PermissionConstraint = { + caveats: null, + date: 1664187844588, + id: 'izn0WGUO8cvq_jqvLQuQP', + invoker: MOCK_ORIGIN, + parentCapability: 'snap_dialog', +}; + +describe('createGenericPermissionValidator', () => { + it('fails if required caveats are not specified', () => { + const validator = createGenericPermissionValidator([ + { type: SnapCaveatType.ChainIds }, + { type: SnapCaveatType.RpcOrigin }, + ]); + + expect(() => + validator({ + ...MOCK_PERMISSION, + caveats: [{ type: SnapCaveatType.ChainIds, value: null }], + }), + ).toThrow('Expected the following caveats: "chainIds", "rpcOrigin".'); + }); + + it('fails if caveats are of the wrong type', () => { + const validator = createGenericPermissionValidator([ + { type: SnapCaveatType.ChainIds }, + { type: SnapCaveatType.RpcOrigin }, + ]); + + expect(() => + validator({ + ...MOCK_PERMISSION, + caveats: [ + { type: SnapCaveatType.KeyringOrigin, value: null }, + { type: SnapCaveatType.SnapIds, value: null }, + ], + }), + ).toThrow( + 'Expected the following caveats: "chainIds", "rpcOrigin", received "keyringOrigin", "snapIds".', + ); + }); + + it('fails if too many caveats specified', () => { + const validator = createGenericPermissionValidator([ + { type: SnapCaveatType.ChainIds }, + { type: SnapCaveatType.RpcOrigin }, + ]); + + expect(() => + validator({ + ...MOCK_PERMISSION, + caveats: [ + { type: SnapCaveatType.ChainIds, value: null }, + { type: SnapCaveatType.ChainIds, value: null }, + { type: SnapCaveatType.RpcOrigin, value: null }, + ], + }), + ).toThrow('Duplicate caveats are not allowed.'); + }); + + it('does not fail if optional caveat is missing', () => { + const validator = createGenericPermissionValidator([ + { type: SnapCaveatType.ChainIds }, + { type: SnapCaveatType.RpcOrigin, optional: true }, + ]); + + expect(() => + validator({ + ...MOCK_PERMISSION, + caveats: [{ type: SnapCaveatType.ChainIds, value: null }], + }), + ).not.toThrow(); + }); + + it('does not fail if optional caveats is missing', () => { + const validator = createGenericPermissionValidator([ + { type: SnapCaveatType.ChainIds, optional: true }, + { type: SnapCaveatType.RpcOrigin, optional: true }, + ]); + + expect(() => + validator({ + ...MOCK_PERMISSION, + caveats: [{ type: SnapCaveatType.ChainIds, value: null }], + }), + ).not.toThrow(); + + expect(() => validator(MOCK_PERMISSION)).not.toThrow(); + }); +}); diff --git a/packages/snaps-controllers/src/snaps/endowments/caveats/generic.ts b/packages/snaps-controllers/src/snaps/endowments/caveats/generic.ts new file mode 100644 index 0000000000..debc9184f4 --- /dev/null +++ b/packages/snaps-controllers/src/snaps/endowments/caveats/generic.ts @@ -0,0 +1,60 @@ +import type { PermissionValidatorConstraint } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; + +/** + * Create a generic permission validator that validates the presence of certain caveats. + * + * This validator only validates the types of the caveats, not the values. + * + * @param caveatsToValidate - A list of objects that represent caveats. + * @param caveatsToValidate.type - The string defining the caveat type. + * @param caveatsToValidate.optional - An optional boolean flag that defines + * whether the caveat is optional or not. + * @returns A function that validates a permission. + */ +export function createGenericPermissionValidator( + caveatsToValidate: { + type: string; + optional?: boolean; + }[], +): PermissionValidatorConstraint { + const validCaveatTypes = new Set( + caveatsToValidate.map((caveat) => caveat.type), + ); + const requiredCaveats = caveatsToValidate.filter( + (caveat) => !caveat.optional, + ); + + return function ({ caveats }) { + const actualCaveats = caveats ?? []; + const passedCaveatTypes = actualCaveats.map((caveat) => caveat.type); + const passedCaveatsSet = new Set(passedCaveatTypes); + + // Disallow duplicates + if (passedCaveatsSet.size !== passedCaveatTypes.length) { + throw rpcErrors.invalidParams({ + message: 'Duplicate caveats are not allowed.', + }); + } + + // Disallow caveats that don't match expected types + if (!actualCaveats.every((caveat) => validCaveatTypes.has(caveat.type))) { + throw rpcErrors.invalidParams({ + message: `Expected the following caveats: ${caveatsToValidate + .map((caveat) => `"${caveat.type}"`) + .join(', ')}, received ${actualCaveats + .map((caveat) => `"${caveat.type}"`) + .join(', ')}.`, + }); + } + + // Fail if not all required caveats are specified + if (!requiredCaveats.every((caveat) => passedCaveatsSet.has(caveat.type))) { + throw rpcErrors.invalidParams({ + message: `Expected the following caveats: ${requiredCaveats + .map((caveat) => `"${caveat.type}"`) + .join(', ')}.`, + }); + } + }; +} diff --git a/packages/snaps-controllers/src/snaps/endowments/caveats/index.ts b/packages/snaps-controllers/src/snaps/endowments/caveats/index.ts new file mode 100644 index 0000000000..bb93111a6c --- /dev/null +++ b/packages/snaps-controllers/src/snaps/endowments/caveats/index.ts @@ -0,0 +1 @@ +export * from './generic'; diff --git a/packages/snaps-controllers/src/snaps/endowments/cronjob.test.ts b/packages/snaps-controllers/src/snaps/endowments/cronjob.test.ts index 1f28f7a72d..f16bd7ae04 100644 --- a/packages/snaps-controllers/src/snaps/endowments/cronjob.test.ts +++ b/packages/snaps-controllers/src/snaps/endowments/cronjob.test.ts @@ -2,13 +2,13 @@ import type { Caveat } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; import { SnapCaveatType } from '@metamask/snaps-utils'; -import { SnapEndowments } from '.'; import { getCronjobCaveatMapper, cronjobEndowmentBuilder, validateCronjobCaveat, cronjobCaveatSpecifications, } from './cronjob'; +import { SnapEndowments } from './enum'; describe('endowment:cronjob', () => { describe('specificationBuilder', () => { diff --git a/packages/snaps-controllers/src/snaps/endowments/index.ts b/packages/snaps-controllers/src/snaps/endowments/index.ts index 8546b4c537..e18d43e808 100644 --- a/packages/snaps-controllers/src/snaps/endowments/index.ts +++ b/packages/snaps-controllers/src/snaps/endowments/index.ts @@ -78,7 +78,8 @@ export const endowmentCaveatMappers: Record< getSignatureInsightCaveatMapper, }; -export const handlerEndowments: Record = { +// We allow null because a permitted handler does not have an endowment +export const handlerEndowments: Record = { [HandlerType.OnRpcRequest]: rpcEndowmentBuilder.targetName, [HandlerType.OnTransaction]: transactionInsightEndowmentBuilder.targetName, [HandlerType.OnCronjob]: cronjobEndowmentBuilder.targetName, @@ -88,6 +89,7 @@ export const handlerEndowments: Record = { [HandlerType.OnKeyringRequest]: keyringEndowmentBuilder.targetName, [HandlerType.OnHomePage]: homePageEndowmentBuilder.targetName, [HandlerType.OnSignature]: signatureInsightEndowmentBuilder.targetName, + [HandlerType.OnUserInput]: null, }; export * from './enum'; diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index 9b5ac87144..4d362271ed 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -25,6 +25,7 @@ import type { CronjobControllerActions, CronjobControllerEvents, } from '../cronjob'; +import type { SnapInterfaceControllerActions } from '../interface/SnapInterfaceController'; import type { AllowedActions, AllowedEvents, @@ -380,6 +381,7 @@ export const getSnapControllerMessenger = ( 'SnapController:revokeDynamicPermissions', 'SnapController:getFile', 'SnapsRegistry:resolveVersion', + 'SnapInterfaceController:getInterface', ], }); @@ -568,3 +570,31 @@ export const getRestrictedSnapsRegistryControllerMessenger = ( return controllerMessenger; }; + +// Mock controller messenger for Interface Controller +export const getRootSnapInterfaceControllerMessenger = () => { + const messenger = new MockControllerMessenger< + SnapInterfaceControllerActions, + never + >(); + + jest.spyOn(messenger, 'call'); + + return messenger; +}; + +export const getRestrictedSnapInterfaceControllerMessenger = ( + messenger: ReturnType< + typeof getRootSnapInterfaceControllerMessenger + > = getRootSnapInterfaceControllerMessenger(), +) => { + const snapInterfaceControllerMessenger = messenger.getRestricted< + 'SnapInterfaceController', + never, + never + >({ + name: 'SnapInterfaceController', + }); + + return snapInterfaceControllerMessenger; +}; diff --git a/packages/snaps-controllers/tsconfig.json b/packages/snaps-controllers/tsconfig.json index f2434f8276..24bec24e1b 100644 --- a/packages/snaps-controllers/tsconfig.json +++ b/packages/snaps-controllers/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "baseUrl": "./" }, - "include": ["./src", "scripts"], + "include": ["./src", "scripts", "package.json", "tsup.config.ts"], "references": [ { "path": "../snaps-execution-environments" }, { "path": "../snaps-rpc-methods" }, diff --git a/packages/snaps-controllers/tsup.config.ts b/packages/snaps-controllers/tsup.config.ts new file mode 100644 index 0000000000..3eaf645296 --- /dev/null +++ b/packages/snaps-controllers/tsup.config.ts @@ -0,0 +1,14 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-execution-environments/coverage.json b/packages/snaps-execution-environments/coverage.json index 354392a95a..acbbac3cc4 100644 --- a/packages/snaps-execution-environments/coverage.json +++ b/packages/snaps-execution-environments/coverage.json @@ -1,6 +1,6 @@ { - "branches": 80.4, - "functions": 89.79, - "lines": 90.9, - "statements": 90.37 + "branches": 81.2, + "functions": 89.86, + "lines": 91.15, + "statements": 90.48 } diff --git a/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json index 1f39dcffdf..55a842b28b 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json @@ -43,10 +43,10 @@ "TextEncoder": true }, "packages": { - "@swc/cli>semver": true, "browserify>buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/providers": { @@ -156,10 +156,10 @@ "@metamask/utils>@noble/hashes": true, "@metamask/utils>@scure/base": true, "@metamask/utils>pony-cause": true, - "@swc/cli>semver": true, "browserify>buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/utils>@noble/hashes": { @@ -174,20 +174,6 @@ "TextEncoder": true } }, - "@swc/cli>semver": { - "globals": { - "console.error": true - }, - "packages": { - "@swc/cli>semver>lru-cache": true, - "browserify>process": true - } - }, - "@swc/cli>semver>lru-cache": { - "packages": { - "@swc/cli>semver>lru-cache>yallist": true - } - }, "browserify>browser-pack>safe-buffer": { "packages": { "browserify>buffer": true @@ -218,17 +204,18 @@ "browserify>browser-pack>safe-buffer": true } }, - "eslint>debug": { + "depcheck>semver": { "globals": { - "console": true, - "document": true, - "localStorage": true, - "navigator": true, - "process": true + "console.error": true }, "packages": { "browserify>process": true, - "eslint>debug>ms": true + "depcheck>semver>lru-cache": true + } + }, + "depcheck>semver>lru-cache": { + "packages": { + "depcheck>semver>lru-cache>yallist": true } }, "external:../snaps-sdk/src/error-wrappers.ts": { @@ -294,7 +281,13 @@ "external:../snaps-sdk/src/types/handlers/name-lookup.ts": true, "external:../snaps-sdk/src/types/handlers/rpc-request.ts": true, "external:../snaps-sdk/src/types/handlers/signature.ts": true, - "external:../snaps-sdk/src/types/handlers/transaction.ts": true + "external:../snaps-sdk/src/types/handlers/transaction.ts": true, + "external:../snaps-sdk/src/types/handlers/user-input.ts": true + } + }, + "external:../snaps-sdk/src/types/handlers/user-input.ts": { + "packages": { + "superstruct": true } }, "external:../snaps-sdk/src/types/index.ts": { @@ -302,14 +295,21 @@ "external:../snaps-sdk/src/types/caip.ts": true, "external:../snaps-sdk/src/types/global.ts": true, "external:../snaps-sdk/src/types/handlers/index.ts": true, + "external:../snaps-sdk/src/types/interface.ts": true, "external:../snaps-sdk/src/types/methods/index.ts": true, "external:../snaps-sdk/src/types/permissions.ts": true, "external:../snaps-sdk/src/types/provider.ts": true, "external:../snaps-sdk/src/types/snap.ts": true } }, + "external:../snaps-sdk/src/types/interface.ts": { + "packages": { + "superstruct": true + } + }, "external:../snaps-sdk/src/types/methods/index.ts": { "packages": { + "external:../snaps-sdk/src/types/methods/create-interface.ts": true, "external:../snaps-sdk/src/types/methods/dialog.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-public-key.ts": true, @@ -317,6 +317,7 @@ "external:../snaps-sdk/src/types/methods/get-client-status.ts": true, "external:../snaps-sdk/src/types/methods/get-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-file.ts": true, + "external:../snaps-sdk/src/types/methods/get-interface-state.ts": true, "external:../snaps-sdk/src/types/methods/get-locale.ts": true, "external:../snaps-sdk/src/types/methods/get-snaps.ts": true, "external:../snaps-sdk/src/types/methods/invoke-keyring.ts": true, @@ -325,7 +326,8 @@ "external:../snaps-sdk/src/types/methods/manage-state.ts": true, "external:../snaps-sdk/src/types/methods/methods.ts": true, "external:../snaps-sdk/src/types/methods/notify.ts": true, - "external:../snaps-sdk/src/types/methods/request-snaps.ts": true + "external:../snaps-sdk/src/types/methods/request-snaps.ts": true, + "external:../snaps-sdk/src/types/methods/update-interface.ts": true } }, "external:../snaps-sdk/src/ui/builder.ts": { @@ -347,6 +349,14 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/button.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/copyable.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -361,6 +371,15 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/form.ts": { + "packages": { + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/heading.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -379,24 +398,38 @@ "external:../snaps-sdk/src/ui/components/index.ts": { "packages": { "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/panel.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true } }, + "external:../snaps-sdk/src/ui/components/input.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/panel.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true, @@ -524,6 +557,19 @@ "console.warn": true, "define": true } + }, + "tsup>debug": { + "globals": { + "console": true, + "document": true, + "localStorage": true, + "navigator": true, + "process": true + }, + "packages": { + "browserify>process": true, + "tsup>debug>ms": true + } } } } \ No newline at end of file diff --git a/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json index 864b7c52a8..1c90c5b797 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json @@ -53,10 +53,10 @@ "TextEncoder": true }, "packages": { - "@swc/cli>semver": true, "buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/providers": { @@ -194,10 +194,10 @@ "@metamask/utils>@noble/hashes": true, "@metamask/utils>@scure/base": true, "@metamask/utils>pony-cause": true, - "@swc/cli>semver": true, "buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/utils>@noble/hashes": { @@ -212,20 +212,6 @@ "TextEncoder": true } }, - "@swc/cli>semver": { - "globals": { - "console.error": true, - "process": true - }, - "packages": { - "@swc/cli>semver>lru-cache": true - } - }, - "@swc/cli>semver>lru-cache": { - "packages": { - "@swc/cli>semver>lru-cache>yallist": true - } - }, "@wdio/mocha-framework>mocha>supports-color": { "builtin": { "os.release": true, @@ -267,25 +253,18 @@ "browserify>browser-pack>safe-buffer": true } }, - "eslint>debug": { - "builtin": { - "tty.isatty": true, - "util.deprecate": true, - "util.format": true, - "util.inspect": true - }, + "depcheck>semver": { "globals": { - "console": true, - "document": true, - "localStorage": true, - "navigator": true, + "console.error": true, "process": true }, "packages": { - "@wdio/mocha-framework>mocha>supports-color": true, - "eslint>debug>ms": true, - "tty": true, - "util": true + "depcheck>semver>lru-cache": true + } + }, + "depcheck>semver>lru-cache": { + "packages": { + "depcheck>semver>lru-cache>yallist": true } }, "external:../snaps-sdk/src/error-wrappers.ts": { @@ -351,7 +330,13 @@ "external:../snaps-sdk/src/types/handlers/name-lookup.ts": true, "external:../snaps-sdk/src/types/handlers/rpc-request.ts": true, "external:../snaps-sdk/src/types/handlers/signature.ts": true, - "external:../snaps-sdk/src/types/handlers/transaction.ts": true + "external:../snaps-sdk/src/types/handlers/transaction.ts": true, + "external:../snaps-sdk/src/types/handlers/user-input.ts": true + } + }, + "external:../snaps-sdk/src/types/handlers/user-input.ts": { + "packages": { + "superstruct": true } }, "external:../snaps-sdk/src/types/index.ts": { @@ -359,14 +344,21 @@ "external:../snaps-sdk/src/types/caip.ts": true, "external:../snaps-sdk/src/types/global.ts": true, "external:../snaps-sdk/src/types/handlers/index.ts": true, + "external:../snaps-sdk/src/types/interface.ts": true, "external:../snaps-sdk/src/types/methods/index.ts": true, "external:../snaps-sdk/src/types/permissions.ts": true, "external:../snaps-sdk/src/types/provider.ts": true, "external:../snaps-sdk/src/types/snap.ts": true } }, + "external:../snaps-sdk/src/types/interface.ts": { + "packages": { + "superstruct": true + } + }, "external:../snaps-sdk/src/types/methods/index.ts": { "packages": { + "external:../snaps-sdk/src/types/methods/create-interface.ts": true, "external:../snaps-sdk/src/types/methods/dialog.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-public-key.ts": true, @@ -374,6 +366,7 @@ "external:../snaps-sdk/src/types/methods/get-client-status.ts": true, "external:../snaps-sdk/src/types/methods/get-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-file.ts": true, + "external:../snaps-sdk/src/types/methods/get-interface-state.ts": true, "external:../snaps-sdk/src/types/methods/get-locale.ts": true, "external:../snaps-sdk/src/types/methods/get-snaps.ts": true, "external:../snaps-sdk/src/types/methods/invoke-keyring.ts": true, @@ -382,7 +375,8 @@ "external:../snaps-sdk/src/types/methods/manage-state.ts": true, "external:../snaps-sdk/src/types/methods/methods.ts": true, "external:../snaps-sdk/src/types/methods/notify.ts": true, - "external:../snaps-sdk/src/types/methods/request-snaps.ts": true + "external:../snaps-sdk/src/types/methods/request-snaps.ts": true, + "external:../snaps-sdk/src/types/methods/update-interface.ts": true } }, "external:../snaps-sdk/src/ui/builder.ts": { @@ -404,6 +398,14 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/button.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/copyable.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -418,6 +420,15 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/form.ts": { + "packages": { + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/heading.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -436,24 +447,38 @@ "external:../snaps-sdk/src/ui/components/index.ts": { "packages": { "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/panel.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true } }, + "external:../snaps-sdk/src/ui/components/input.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/panel.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true, @@ -599,6 +624,27 @@ "console.warn": true, "define": true } + }, + "tsup>debug": { + "builtin": { + "tty.isatty": true, + "util.deprecate": true, + "util.format": true, + "util.inspect": true + }, + "globals": { + "console": true, + "document": true, + "localStorage": true, + "navigator": true, + "process": true + }, + "packages": { + "@wdio/mocha-framework>mocha>supports-color": true, + "tsup>debug>ms": true, + "tty": true, + "util": true + } } } } \ No newline at end of file diff --git a/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json index 864b7c52a8..1c90c5b797 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json @@ -53,10 +53,10 @@ "TextEncoder": true }, "packages": { - "@swc/cli>semver": true, "buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/providers": { @@ -194,10 +194,10 @@ "@metamask/utils>@noble/hashes": true, "@metamask/utils>@scure/base": true, "@metamask/utils>pony-cause": true, - "@swc/cli>semver": true, "buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/utils>@noble/hashes": { @@ -212,20 +212,6 @@ "TextEncoder": true } }, - "@swc/cli>semver": { - "globals": { - "console.error": true, - "process": true - }, - "packages": { - "@swc/cli>semver>lru-cache": true - } - }, - "@swc/cli>semver>lru-cache": { - "packages": { - "@swc/cli>semver>lru-cache>yallist": true - } - }, "@wdio/mocha-framework>mocha>supports-color": { "builtin": { "os.release": true, @@ -267,25 +253,18 @@ "browserify>browser-pack>safe-buffer": true } }, - "eslint>debug": { - "builtin": { - "tty.isatty": true, - "util.deprecate": true, - "util.format": true, - "util.inspect": true - }, + "depcheck>semver": { "globals": { - "console": true, - "document": true, - "localStorage": true, - "navigator": true, + "console.error": true, "process": true }, "packages": { - "@wdio/mocha-framework>mocha>supports-color": true, - "eslint>debug>ms": true, - "tty": true, - "util": true + "depcheck>semver>lru-cache": true + } + }, + "depcheck>semver>lru-cache": { + "packages": { + "depcheck>semver>lru-cache>yallist": true } }, "external:../snaps-sdk/src/error-wrappers.ts": { @@ -351,7 +330,13 @@ "external:../snaps-sdk/src/types/handlers/name-lookup.ts": true, "external:../snaps-sdk/src/types/handlers/rpc-request.ts": true, "external:../snaps-sdk/src/types/handlers/signature.ts": true, - "external:../snaps-sdk/src/types/handlers/transaction.ts": true + "external:../snaps-sdk/src/types/handlers/transaction.ts": true, + "external:../snaps-sdk/src/types/handlers/user-input.ts": true + } + }, + "external:../snaps-sdk/src/types/handlers/user-input.ts": { + "packages": { + "superstruct": true } }, "external:../snaps-sdk/src/types/index.ts": { @@ -359,14 +344,21 @@ "external:../snaps-sdk/src/types/caip.ts": true, "external:../snaps-sdk/src/types/global.ts": true, "external:../snaps-sdk/src/types/handlers/index.ts": true, + "external:../snaps-sdk/src/types/interface.ts": true, "external:../snaps-sdk/src/types/methods/index.ts": true, "external:../snaps-sdk/src/types/permissions.ts": true, "external:../snaps-sdk/src/types/provider.ts": true, "external:../snaps-sdk/src/types/snap.ts": true } }, + "external:../snaps-sdk/src/types/interface.ts": { + "packages": { + "superstruct": true + } + }, "external:../snaps-sdk/src/types/methods/index.ts": { "packages": { + "external:../snaps-sdk/src/types/methods/create-interface.ts": true, "external:../snaps-sdk/src/types/methods/dialog.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-public-key.ts": true, @@ -374,6 +366,7 @@ "external:../snaps-sdk/src/types/methods/get-client-status.ts": true, "external:../snaps-sdk/src/types/methods/get-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-file.ts": true, + "external:../snaps-sdk/src/types/methods/get-interface-state.ts": true, "external:../snaps-sdk/src/types/methods/get-locale.ts": true, "external:../snaps-sdk/src/types/methods/get-snaps.ts": true, "external:../snaps-sdk/src/types/methods/invoke-keyring.ts": true, @@ -382,7 +375,8 @@ "external:../snaps-sdk/src/types/methods/manage-state.ts": true, "external:../snaps-sdk/src/types/methods/methods.ts": true, "external:../snaps-sdk/src/types/methods/notify.ts": true, - "external:../snaps-sdk/src/types/methods/request-snaps.ts": true + "external:../snaps-sdk/src/types/methods/request-snaps.ts": true, + "external:../snaps-sdk/src/types/methods/update-interface.ts": true } }, "external:../snaps-sdk/src/ui/builder.ts": { @@ -404,6 +398,14 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/button.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/copyable.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -418,6 +420,15 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/form.ts": { + "packages": { + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/heading.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -436,24 +447,38 @@ "external:../snaps-sdk/src/ui/components/index.ts": { "packages": { "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/panel.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true } }, + "external:../snaps-sdk/src/ui/components/input.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/panel.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true, @@ -599,6 +624,27 @@ "console.warn": true, "define": true } + }, + "tsup>debug": { + "builtin": { + "tty.isatty": true, + "util.deprecate": true, + "util.format": true, + "util.inspect": true + }, + "globals": { + "console": true, + "document": true, + "localStorage": true, + "navigator": true, + "process": true + }, + "packages": { + "@wdio/mocha-framework>mocha>supports-color": true, + "tsup>debug>ms": true, + "tty": true, + "util": true + } } } } \ No newline at end of file diff --git a/packages/snaps-execution-environments/lavamoat/browserify/offscreen/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/offscreen/policy.json index 26d2265580..cc68df7ed8 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/offscreen/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/offscreen/policy.json @@ -22,10 +22,10 @@ "TextEncoder": true }, "packages": { - "@swc/cli>semver": true, "browserify>buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/rpc-errors": { @@ -57,10 +57,10 @@ "@metamask/utils>@noble/hashes": true, "@metamask/utils>@scure/base": true, "@metamask/utils>pony-cause": true, - "@swc/cli>semver": true, "browserify>buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/utils>@noble/hashes": { @@ -75,20 +75,6 @@ "TextEncoder": true } }, - "@swc/cli>semver": { - "globals": { - "console.error": true - }, - "packages": { - "@swc/cli>semver>lru-cache": true, - "browserify>process": true - } - }, - "@swc/cli>semver>lru-cache": { - "packages": { - "@swc/cli>semver>lru-cache>yallist": true - } - }, "browserify>browser-pack>safe-buffer": { "packages": { "browserify>buffer": true @@ -119,17 +105,18 @@ "browserify>browser-pack>safe-buffer": true } }, - "eslint>debug": { + "depcheck>semver": { "globals": { - "console": true, - "document": true, - "localStorage": true, - "navigator": true, - "process": true + "console.error": true }, "packages": { "browserify>process": true, - "eslint>debug>ms": true + "depcheck>semver>lru-cache": true + } + }, + "depcheck>semver>lru-cache": { + "packages": { + "depcheck>semver>lru-cache>yallist": true } }, "external:../snaps-sdk/src/error-wrappers.ts": { @@ -195,7 +182,13 @@ "external:../snaps-sdk/src/types/handlers/name-lookup.ts": true, "external:../snaps-sdk/src/types/handlers/rpc-request.ts": true, "external:../snaps-sdk/src/types/handlers/signature.ts": true, - "external:../snaps-sdk/src/types/handlers/transaction.ts": true + "external:../snaps-sdk/src/types/handlers/transaction.ts": true, + "external:../snaps-sdk/src/types/handlers/user-input.ts": true + } + }, + "external:../snaps-sdk/src/types/handlers/user-input.ts": { + "packages": { + "superstruct": true } }, "external:../snaps-sdk/src/types/index.ts": { @@ -203,14 +196,21 @@ "external:../snaps-sdk/src/types/caip.ts": true, "external:../snaps-sdk/src/types/global.ts": true, "external:../snaps-sdk/src/types/handlers/index.ts": true, + "external:../snaps-sdk/src/types/interface.ts": true, "external:../snaps-sdk/src/types/methods/index.ts": true, "external:../snaps-sdk/src/types/permissions.ts": true, "external:../snaps-sdk/src/types/provider.ts": true, "external:../snaps-sdk/src/types/snap.ts": true } }, + "external:../snaps-sdk/src/types/interface.ts": { + "packages": { + "superstruct": true + } + }, "external:../snaps-sdk/src/types/methods/index.ts": { "packages": { + "external:../snaps-sdk/src/types/methods/create-interface.ts": true, "external:../snaps-sdk/src/types/methods/dialog.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-public-key.ts": true, @@ -218,6 +218,7 @@ "external:../snaps-sdk/src/types/methods/get-client-status.ts": true, "external:../snaps-sdk/src/types/methods/get-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-file.ts": true, + "external:../snaps-sdk/src/types/methods/get-interface-state.ts": true, "external:../snaps-sdk/src/types/methods/get-locale.ts": true, "external:../snaps-sdk/src/types/methods/get-snaps.ts": true, "external:../snaps-sdk/src/types/methods/invoke-keyring.ts": true, @@ -226,7 +227,8 @@ "external:../snaps-sdk/src/types/methods/manage-state.ts": true, "external:../snaps-sdk/src/types/methods/methods.ts": true, "external:../snaps-sdk/src/types/methods/notify.ts": true, - "external:../snaps-sdk/src/types/methods/request-snaps.ts": true + "external:../snaps-sdk/src/types/methods/request-snaps.ts": true, + "external:../snaps-sdk/src/types/methods/update-interface.ts": true } }, "external:../snaps-sdk/src/ui/builder.ts": { @@ -248,6 +250,14 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/button.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/copyable.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -262,6 +272,15 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/form.ts": { + "packages": { + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/heading.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -280,24 +299,38 @@ "external:../snaps-sdk/src/ui/components/index.ts": { "packages": { "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/panel.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true } }, + "external:../snaps-sdk/src/ui/components/input.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/panel.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true, @@ -425,6 +458,19 @@ "console.warn": true, "define": true } + }, + "tsup>debug": { + "globals": { + "console": true, + "document": true, + "localStorage": true, + "navigator": true, + "process": true + }, + "packages": { + "browserify>process": true, + "tsup>debug>ms": true + } } } } \ No newline at end of file diff --git a/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json index 1f39dcffdf..55a842b28b 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json @@ -43,10 +43,10 @@ "TextEncoder": true }, "packages": { - "@swc/cli>semver": true, "browserify>buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/providers": { @@ -156,10 +156,10 @@ "@metamask/utils>@noble/hashes": true, "@metamask/utils>@scure/base": true, "@metamask/utils>pony-cause": true, - "@swc/cli>semver": true, "browserify>buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/utils>@noble/hashes": { @@ -174,20 +174,6 @@ "TextEncoder": true } }, - "@swc/cli>semver": { - "globals": { - "console.error": true - }, - "packages": { - "@swc/cli>semver>lru-cache": true, - "browserify>process": true - } - }, - "@swc/cli>semver>lru-cache": { - "packages": { - "@swc/cli>semver>lru-cache>yallist": true - } - }, "browserify>browser-pack>safe-buffer": { "packages": { "browserify>buffer": true @@ -218,17 +204,18 @@ "browserify>browser-pack>safe-buffer": true } }, - "eslint>debug": { + "depcheck>semver": { "globals": { - "console": true, - "document": true, - "localStorage": true, - "navigator": true, - "process": true + "console.error": true }, "packages": { "browserify>process": true, - "eslint>debug>ms": true + "depcheck>semver>lru-cache": true + } + }, + "depcheck>semver>lru-cache": { + "packages": { + "depcheck>semver>lru-cache>yallist": true } }, "external:../snaps-sdk/src/error-wrappers.ts": { @@ -294,7 +281,13 @@ "external:../snaps-sdk/src/types/handlers/name-lookup.ts": true, "external:../snaps-sdk/src/types/handlers/rpc-request.ts": true, "external:../snaps-sdk/src/types/handlers/signature.ts": true, - "external:../snaps-sdk/src/types/handlers/transaction.ts": true + "external:../snaps-sdk/src/types/handlers/transaction.ts": true, + "external:../snaps-sdk/src/types/handlers/user-input.ts": true + } + }, + "external:../snaps-sdk/src/types/handlers/user-input.ts": { + "packages": { + "superstruct": true } }, "external:../snaps-sdk/src/types/index.ts": { @@ -302,14 +295,21 @@ "external:../snaps-sdk/src/types/caip.ts": true, "external:../snaps-sdk/src/types/global.ts": true, "external:../snaps-sdk/src/types/handlers/index.ts": true, + "external:../snaps-sdk/src/types/interface.ts": true, "external:../snaps-sdk/src/types/methods/index.ts": true, "external:../snaps-sdk/src/types/permissions.ts": true, "external:../snaps-sdk/src/types/provider.ts": true, "external:../snaps-sdk/src/types/snap.ts": true } }, + "external:../snaps-sdk/src/types/interface.ts": { + "packages": { + "superstruct": true + } + }, "external:../snaps-sdk/src/types/methods/index.ts": { "packages": { + "external:../snaps-sdk/src/types/methods/create-interface.ts": true, "external:../snaps-sdk/src/types/methods/dialog.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-public-key.ts": true, @@ -317,6 +317,7 @@ "external:../snaps-sdk/src/types/methods/get-client-status.ts": true, "external:../snaps-sdk/src/types/methods/get-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-file.ts": true, + "external:../snaps-sdk/src/types/methods/get-interface-state.ts": true, "external:../snaps-sdk/src/types/methods/get-locale.ts": true, "external:../snaps-sdk/src/types/methods/get-snaps.ts": true, "external:../snaps-sdk/src/types/methods/invoke-keyring.ts": true, @@ -325,7 +326,8 @@ "external:../snaps-sdk/src/types/methods/manage-state.ts": true, "external:../snaps-sdk/src/types/methods/methods.ts": true, "external:../snaps-sdk/src/types/methods/notify.ts": true, - "external:../snaps-sdk/src/types/methods/request-snaps.ts": true + "external:../snaps-sdk/src/types/methods/request-snaps.ts": true, + "external:../snaps-sdk/src/types/methods/update-interface.ts": true } }, "external:../snaps-sdk/src/ui/builder.ts": { @@ -347,6 +349,14 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/button.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/copyable.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -361,6 +371,15 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/form.ts": { + "packages": { + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/heading.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -379,24 +398,38 @@ "external:../snaps-sdk/src/ui/components/index.ts": { "packages": { "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/panel.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true } }, + "external:../snaps-sdk/src/ui/components/input.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/panel.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true, @@ -524,6 +557,19 @@ "console.warn": true, "define": true } + }, + "tsup>debug": { + "globals": { + "console": true, + "document": true, + "localStorage": true, + "navigator": true, + "process": true + }, + "packages": { + "browserify>process": true, + "tsup>debug>ms": true + } } } } \ No newline at end of file diff --git a/packages/snaps-execution-environments/lavamoat/browserify/worker-pool/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/worker-pool/policy.json index 26d2265580..cc68df7ed8 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/worker-pool/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/worker-pool/policy.json @@ -22,10 +22,10 @@ "TextEncoder": true }, "packages": { - "@swc/cli>semver": true, "browserify>buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/rpc-errors": { @@ -57,10 +57,10 @@ "@metamask/utils>@noble/hashes": true, "@metamask/utils>@scure/base": true, "@metamask/utils>pony-cause": true, - "@swc/cli>semver": true, "browserify>buffer": true, - "eslint>debug": true, - "superstruct": true + "depcheck>semver": true, + "superstruct": true, + "tsup>debug": true } }, "@metamask/utils>@noble/hashes": { @@ -75,20 +75,6 @@ "TextEncoder": true } }, - "@swc/cli>semver": { - "globals": { - "console.error": true - }, - "packages": { - "@swc/cli>semver>lru-cache": true, - "browserify>process": true - } - }, - "@swc/cli>semver>lru-cache": { - "packages": { - "@swc/cli>semver>lru-cache>yallist": true - } - }, "browserify>browser-pack>safe-buffer": { "packages": { "browserify>buffer": true @@ -119,17 +105,18 @@ "browserify>browser-pack>safe-buffer": true } }, - "eslint>debug": { + "depcheck>semver": { "globals": { - "console": true, - "document": true, - "localStorage": true, - "navigator": true, - "process": true + "console.error": true }, "packages": { "browserify>process": true, - "eslint>debug>ms": true + "depcheck>semver>lru-cache": true + } + }, + "depcheck>semver>lru-cache": { + "packages": { + "depcheck>semver>lru-cache>yallist": true } }, "external:../snaps-sdk/src/error-wrappers.ts": { @@ -195,7 +182,13 @@ "external:../snaps-sdk/src/types/handlers/name-lookup.ts": true, "external:../snaps-sdk/src/types/handlers/rpc-request.ts": true, "external:../snaps-sdk/src/types/handlers/signature.ts": true, - "external:../snaps-sdk/src/types/handlers/transaction.ts": true + "external:../snaps-sdk/src/types/handlers/transaction.ts": true, + "external:../snaps-sdk/src/types/handlers/user-input.ts": true + } + }, + "external:../snaps-sdk/src/types/handlers/user-input.ts": { + "packages": { + "superstruct": true } }, "external:../snaps-sdk/src/types/index.ts": { @@ -203,14 +196,21 @@ "external:../snaps-sdk/src/types/caip.ts": true, "external:../snaps-sdk/src/types/global.ts": true, "external:../snaps-sdk/src/types/handlers/index.ts": true, + "external:../snaps-sdk/src/types/interface.ts": true, "external:../snaps-sdk/src/types/methods/index.ts": true, "external:../snaps-sdk/src/types/permissions.ts": true, "external:../snaps-sdk/src/types/provider.ts": true, "external:../snaps-sdk/src/types/snap.ts": true } }, + "external:../snaps-sdk/src/types/interface.ts": { + "packages": { + "superstruct": true + } + }, "external:../snaps-sdk/src/types/methods/index.ts": { "packages": { + "external:../snaps-sdk/src/types/methods/create-interface.ts": true, "external:../snaps-sdk/src/types/methods/dialog.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-bip32-public-key.ts": true, @@ -218,6 +218,7 @@ "external:../snaps-sdk/src/types/methods/get-client-status.ts": true, "external:../snaps-sdk/src/types/methods/get-entropy.ts": true, "external:../snaps-sdk/src/types/methods/get-file.ts": true, + "external:../snaps-sdk/src/types/methods/get-interface-state.ts": true, "external:../snaps-sdk/src/types/methods/get-locale.ts": true, "external:../snaps-sdk/src/types/methods/get-snaps.ts": true, "external:../snaps-sdk/src/types/methods/invoke-keyring.ts": true, @@ -226,7 +227,8 @@ "external:../snaps-sdk/src/types/methods/manage-state.ts": true, "external:../snaps-sdk/src/types/methods/methods.ts": true, "external:../snaps-sdk/src/types/methods/notify.ts": true, - "external:../snaps-sdk/src/types/methods/request-snaps.ts": true + "external:../snaps-sdk/src/types/methods/request-snaps.ts": true, + "external:../snaps-sdk/src/types/methods/update-interface.ts": true } }, "external:../snaps-sdk/src/ui/builder.ts": { @@ -248,6 +250,14 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/button.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/copyable.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -262,6 +272,15 @@ "superstruct": true } }, + "external:../snaps-sdk/src/ui/components/form.ts": { + "packages": { + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/heading.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, @@ -280,24 +299,38 @@ "external:../snaps-sdk/src/ui/components/index.ts": { "packages": { "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/panel.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true } }, + "external:../snaps-sdk/src/ui/components/input.ts": { + "packages": { + "external:../snaps-sdk/src/internals/index.ts": true, + "external:../snaps-sdk/src/ui/builder.ts": true, + "external:../snaps-sdk/src/ui/nodes.ts": true, + "superstruct": true + } + }, "external:../snaps-sdk/src/ui/components/panel.ts": { "packages": { "external:../snaps-sdk/src/ui/builder.ts": true, "external:../snaps-sdk/src/ui/components/address.ts": true, + "external:../snaps-sdk/src/ui/components/button.ts": true, "external:../snaps-sdk/src/ui/components/copyable.ts": true, "external:../snaps-sdk/src/ui/components/divider.ts": true, + "external:../snaps-sdk/src/ui/components/form.ts": true, "external:../snaps-sdk/src/ui/components/heading.ts": true, "external:../snaps-sdk/src/ui/components/image.ts": true, + "external:../snaps-sdk/src/ui/components/input.ts": true, "external:../snaps-sdk/src/ui/components/row.ts": true, "external:../snaps-sdk/src/ui/components/spinner.ts": true, "external:../snaps-sdk/src/ui/components/text.ts": true, @@ -425,6 +458,19 @@ "console.warn": true, "define": true } + }, + "tsup>debug": { + "globals": { + "console": true, + "document": true, + "localStorage": true, + "navigator": true, + "process": true + }, + "packages": { + "browserify>process": true, + "tsup>debug>ms": true + } } } } \ No newline at end of file diff --git a/packages/snaps-execution-environments/lavamoat/build-system/policy.json b/packages/snaps-execution-environments/lavamoat/build-system/policy.json index ec83941697..54367c2881 100644 --- a/packages/snaps-execution-environments/lavamoat/build-system/policy.json +++ b/packages/snaps-execution-environments/lavamoat/build-system/policy.json @@ -36,7 +36,7 @@ "depcheck>@babel/parser": true, "depcheck>@babel/traverse": true, "depcheck>json5": true, - "eslint>debug": true + "tsup>debug": true } }, "@babel/core>@ampproject/remapping": { @@ -53,8 +53,8 @@ "define": true }, "packages": { - "terser>@jridgewell/source-map>@jridgewell/gen-mapping>@jridgewell/set-array": true, - "terser>@jridgewell/source-map>@jridgewell/gen-mapping>@jridgewell/sourcemap-codec": true + "tsup>sucrase>@jridgewell/gen-mapping>@jridgewell/set-array": true, + "tsup>sucrase>@jridgewell/gen-mapping>@jridgewell/sourcemap-codec": true } }, "@babel/core>@babel/code-frame": { @@ -164,8 +164,8 @@ "packages": { "@babel/core>@babel/generator>jsesc": true, "@babel/core>@babel/types": true, - "terser>@jridgewell/source-map>@jridgewell/gen-mapping": true, - "terser>@jridgewell/source-map>@jridgewell/trace-mapping": true + "terser>@jridgewell/source-map>@jridgewell/trace-mapping": true, + "tsup>sucrase>@jridgewell/gen-mapping": true } }, "@babel/core>@babel/generator>jsesc": { @@ -1886,7 +1886,7 @@ "depcheck>@babel/traverse>@babel/helper-hoist-variables": true, "depcheck>@babel/traverse>@babel/helper-split-export-declaration": true, "depcheck>@babel/traverse>globals": true, - "eslint>debug": true + "tsup>debug": true } }, "depcheck>@babel/traverse>@babel/helper-function-name": { @@ -1999,25 +1999,6 @@ "eslint>chalk>ansi-styles>color-convert>color-name": true } }, - "eslint>debug": { - "builtin": { - "tty.isatty": true, - "util.deprecate": true, - "util.format": true, - "util.inspect": true - }, - "globals": { - "console": true, - "document": true, - "localStorage": true, - "navigator": true, - "process": true - }, - "packages": { - "@wdio/mocha-framework>mocha>supports-color": true, - "eslint>debug>ms": true - } - }, "eslint>espree": { "packages": { "eslint>eslint-visitor-keys": true, @@ -2298,58 +2279,77 @@ "define": true } }, - "terser>@jridgewell/source-map>@jridgewell/gen-mapping": { + "terser>@jridgewell/source-map>@jridgewell/trace-mapping": { "globals": { "define": true }, "packages": { - "terser>@jridgewell/source-map>@jridgewell/gen-mapping>@jridgewell/set-array": true, - "terser>@jridgewell/source-map>@jridgewell/gen-mapping>@jridgewell/sourcemap-codec": true, - "terser>@jridgewell/source-map>@jridgewell/trace-mapping": true + "terser>@jridgewell/source-map>@jridgewell/trace-mapping>@jridgewell/resolve-uri": true, + "terser>@jridgewell/source-map>@jridgewell/trace-mapping>@jridgewell/sourcemap-codec": true } }, - "terser>@jridgewell/source-map>@jridgewell/gen-mapping>@jridgewell/set-array": { + "terser>@jridgewell/source-map>@jridgewell/trace-mapping>@jridgewell/resolve-uri": { "globals": { "define": true } }, - "terser>@jridgewell/source-map>@jridgewell/gen-mapping>@jridgewell/sourcemap-codec": { + "terser>@jridgewell/source-map>@jridgewell/trace-mapping>@jridgewell/sourcemap-codec": { "globals": { "Buffer": true, "TextDecoder": true, "define": true } }, - "terser>@jridgewell/source-map>@jridgewell/trace-mapping": { + "terser>acorn": { "globals": { + "console": true, "define": true - }, - "packages": { - "terser>@jridgewell/source-map>@jridgewell/trace-mapping>@jridgewell/resolve-uri": true, - "terser>@jridgewell/source-map>@jridgewell/trace-mapping>@jridgewell/sourcemap-codec": true } }, - "terser>@jridgewell/source-map>@jridgewell/trace-mapping>@jridgewell/resolve-uri": { + "terser>source-map-support>buffer-from": { "globals": { - "define": true + "Buffer": true } }, - "terser>@jridgewell/source-map>@jridgewell/trace-mapping>@jridgewell/sourcemap-codec": { + "tsup>debug": { + "builtin": { + "tty.isatty": true, + "util.deprecate": true, + "util.format": true, + "util.inspect": true + }, + "globals": { + "console": true, + "document": true, + "localStorage": true, + "navigator": true, + "process": true + }, + "packages": { + "@wdio/mocha-framework>mocha>supports-color": true, + "tsup>debug>ms": true + } + }, + "tsup>sucrase>@jridgewell/gen-mapping": { "globals": { - "Buffer": true, - "TextDecoder": true, "define": true + }, + "packages": { + "terser>@jridgewell/source-map>@jridgewell/trace-mapping": true, + "tsup>sucrase>@jridgewell/gen-mapping>@jridgewell/set-array": true, + "tsup>sucrase>@jridgewell/gen-mapping>@jridgewell/sourcemap-codec": true } }, - "terser>acorn": { + "tsup>sucrase>@jridgewell/gen-mapping>@jridgewell/set-array": { "globals": { - "console": true, "define": true } }, - "terser>source-map-support>buffer-from": { + "tsup>sucrase>@jridgewell/gen-mapping>@jridgewell/sourcemap-codec": { "globals": { - "Buffer": true + "Buffer": true, + "TextDecoder": true, + "define": true } }, "yargs": { diff --git a/packages/snaps-execution-environments/package.json b/packages/snaps-execution-environments/package.json index c7ba670322..f8af98ff0a 100644 --- a/packages/snaps-execution-environments/package.json +++ b/packages/snaps-execution-environments/package.json @@ -7,14 +7,19 @@ "url": "https://github.com/MetaMask/snaps.git" }, "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**", - "dist/browserify/**" + "dist" ], "scripts": { "test": "rimraf coverage && jest && yarn test:browser && yarn posttest", @@ -28,12 +33,8 @@ "lint:changelog": "../../scripts/validate-changelog.sh @metamask/snaps-execution-environments", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn lint:changelog && yarn lint:dependencies", "clean": "rimraf '*.tsbuildinfo' 'dist' 'src/__GENERATED__/' 'coverage/*' '__test__/*'", - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", - "build:clean": "yarn clean && yarn build", "build:post-tsc": "yarn build:lavamoat", "build:lavamoat": "lavamoat scripts/build.js --policy lavamoat/build-system/policy.json --policyOverride lavamoat/build-system/policy-override.json", "build:lavamoat:policy": "yarn build:lavamoat --writeAutoPolicy && node scripts/build.js --writeAutoPolicy", @@ -70,7 +71,6 @@ "@metamask/eslint-config-jest": "^12.1.0", "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", "@types/express": "^4.17.17", @@ -114,6 +114,7 @@ "ses": "^1.1.0", "terser": "^5.17.7", "ts-node": "^10.9.1", + "tsup": "^8.0.1", "typescript": "~4.8.4", "vite": "^4.3.9", "vite-tsconfig-paths": "^4.0.5", @@ -128,5 +129,6 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" - } + }, + "build:types": "tsc --project tsconfig.build.json" } diff --git a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts index 322c90e34b..bdf74d29bc 100644 --- a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts +++ b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-shadow, import/no-unassigned-import */ +import { UserInputEventType } from '@metamask/snaps-sdk'; import { HandlerType } from '@metamask/snaps-utils'; import { MOCK_ORIGIN, @@ -1504,6 +1505,47 @@ describe('BaseSnapExecutor', () => { }); }); + it('supports onUserInput export', async () => { + const CODE = ` + module.exports.onUserInput = ({ id, event }) => {} + `; + + const executor = new TestSnapExecutor(); + await executor.executeSnap(1, MOCK_SNAP_ID, CODE, []); + + expect(await executor.readCommand()).toStrictEqual({ + jsonrpc: '2.0', + id: 1, + result: 'OK', + }); + + const params = { + id: 'foo', + event: { + type: UserInputEventType.ButtonClickEvent, + name: 'bar', + }, + }; + + await executor.writeCommand({ + jsonrpc: '2.0', + id: 2, + method: 'snapRpc', + params: [ + MOCK_SNAP_ID, + HandlerType.OnUserInput, + MOCK_ORIGIN, + { jsonrpc: '2.0', method: 'foo', params }, + ], + }); + + expect(await executor.readCommand()).toStrictEqual({ + id: 2, + jsonrpc: '2.0', + result: null, + }); + }); + describe('lifecycle hooks', () => { const LIFECYCLE_HOOKS = [HandlerType.OnInstall, HandlerType.OnUpdate]; diff --git a/packages/snaps-execution-environments/src/common/commands.test.ts b/packages/snaps-execution-environments/src/common/commands.test.ts index d523956411..2f10b780ef 100644 --- a/packages/snaps-execution-environments/src/common/commands.test.ts +++ b/packages/snaps-execution-environments/src/common/commands.test.ts @@ -30,6 +30,17 @@ describe('getHandlerArguments', () => { ).toThrow('Invalid request params'); }); + it('validates the request params for the OnUserInput handler', () => { + expect(() => + getHandlerArguments(MOCK_ORIGIN, HandlerType.OnUserInput, { + id: 1, + jsonrpc: '2.0', + method: 'foo', + params: {}, + }), + ).toThrow('Invalid request params'); + }); + it('throws for invalid handler types', () => { expect(() => // @ts-expect-error Invalid handler type. diff --git a/packages/snaps-execution-environments/src/common/commands.ts b/packages/snaps-execution-environments/src/common/commands.ts index 1f357d82d1..4edea95e4a 100644 --- a/packages/snaps-execution-environments/src/common/commands.ts +++ b/packages/snaps-execution-environments/src/common/commands.ts @@ -14,6 +14,7 @@ import { assertIsOnTransactionRequestArguments, assertIsOnSignatureRequestArguments, assertIsOnNameLookupRequestArguments, + assertIsOnUserInputRequestArguments, } from './validation'; export type CommandMethodsMapping = { @@ -84,6 +85,12 @@ export function getHandlerArguments( case HandlerType.OnHomePage: return {}; + case HandlerType.OnUserInput: { + assertIsOnUserInputRequestArguments(request.params); + + const { id, event } = request.params; + return { id, event }; + } default: return assertExhaustive(handler); diff --git a/packages/snaps-execution-environments/src/common/validation.test.ts b/packages/snaps-execution-environments/src/common/validation.test.ts index d0ef606743..b9b9ec766a 100644 --- a/packages/snaps-execution-environments/src/common/validation.test.ts +++ b/packages/snaps-execution-environments/src/common/validation.test.ts @@ -1,7 +1,10 @@ +import { UserInputEventType } from '@metamask/snaps-sdk'; + import { assertIsOnNameLookupRequestArguments, assertIsOnSignatureRequestArguments, assertIsOnTransactionRequestArguments, + assertIsOnUserInputRequestArguments, isEndowment, isEndowmentsArray, } from './validation'; @@ -147,3 +150,69 @@ describe('assertIsOnNameLookupRequestArguments', () => { }, ); }); + +describe('assertIsOnUserInputRequestArguments', () => { + it.each([ + { + id: 'foo', + event: { type: UserInputEventType.ButtonClickEvent, name: 'foo' }, + }, + { + id: 'foo', + event: { + type: UserInputEventType.FormSubmitEvent, + name: 'foo', + value: { foo: 'bar' }, + }, + }, + ])('does not throw for a valid user input param object', (value) => { + expect(() => assertIsOnUserInputRequestArguments(value)).not.toThrow(); + }); + + it.each([ + true, + false, + null, + undefined, + 0, + 1, + '', + 'foo', + [], + {}, + { id: 2, event: { foo: 'bar' } }, + { + id: 'foo', + event: { type: UserInputEventType.ButtonClickEvent, name: 'foo' }, + foo: 'bar', + }, + { + id: 'foo', + event: { type: UserInputEventType.ButtonClickEvent, name: 2 }, + }, + { + id: 'foo', + event: { type: 4, name: 'foo' }, + }, + { + id: 'foo', + event: { + type: 'baz', + name: 'foo', + value: 'bar', + }, + }, + { + id: 'foo', + event: { + type: UserInputEventType.FormSubmitEvent, + name: 'foo', + value: 'bar', + }, + }, + ])('throws if the value is not a valid user input params object', (value) => { + expect(() => assertIsOnUserInputRequestArguments(value as any)).toThrow( + 'Invalid request params:', + ); + }); +}); diff --git a/packages/snaps-execution-environments/src/common/validation.ts b/packages/snaps-execution-environments/src/common/validation.ts index 2c19a8367e..6deb3bd983 100644 --- a/packages/snaps-execution-environments/src/common/validation.ts +++ b/packages/snaps-execution-environments/src/common/validation.ts @@ -1,4 +1,5 @@ import { rpcErrors } from '@metamask/rpc-errors'; +import { UserInputEventStruct } from '@metamask/snaps-sdk'; import { ChainIdStruct, HandlerType } from '@metamask/snaps-utils'; import type { Json, JsonRpcSuccess } from '@metamask/utils'; import { @@ -208,6 +209,32 @@ export function assertIsOnNameLookupRequestArguments( ); } +export const OnUserInputArgumentsStruct = object({ + id: string(), + event: UserInputEventStruct, +}); + +export type OnUserInputArguments = Infer; + +/** + * Asserts that the given value is a valid {@link OnUserInputArguments} + * object. + * + * @param value - The value to validate. + * @throws If the value is not a valid {@link OnUserInputArguments} + * object. + */ +export function assertIsOnUserInputRequestArguments( + value: unknown, +): asserts value is OnUserInputArguments { + assertStruct( + value, + OnUserInputArgumentsStruct, + 'Invalid request params', + rpcErrors.invalidParams, + ); +} + const OkResponseStruct = object({ id: JsonRpcIdStruct, jsonrpc: JsonRpcVersionStruct, diff --git a/packages/snaps-execution-environments/tsconfig.json b/packages/snaps-execution-environments/tsconfig.json index ce93b29b10..9bb5b593d8 100644 --- a/packages/snaps-execution-environments/tsconfig.json +++ b/packages/snaps-execution-environments/tsconfig.json @@ -5,7 +5,13 @@ "typeRoots": ["../../node_modules/@types", "./node_modules/@types"], "allowJs": true }, - "include": ["./src", "webpack.config.js", "scripts"], + "include": [ + "./src", + "webpack.config.js", + "scripts", + "package.json", + "tsup.config.ts" + ], "references": [ { "path": "../snaps-sdk" diff --git a/packages/snaps-execution-environments/tsup.config.ts b/packages/snaps-execution-environments/tsup.config.ts new file mode 100644 index 0000000000..b341957eee --- /dev/null +++ b/packages/snaps-execution-environments/tsup.config.ts @@ -0,0 +1,15 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, + external: ['@metamask/snaps-execution-environments'], +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-jest/README.md b/packages/snaps-jest/README.md index 2d56f07eed..f9fa642e0e 100644 --- a/packages/snaps-jest/README.md +++ b/packages/snaps-jest/README.md @@ -11,6 +11,7 @@ currently experimental, and the API may change in the future. - [Install a snap](#install-a-snap) - [`snap.request`](#snaprequest) - [`snap.sendTransaction`](#snapsendtransaction) + - [`snap.onSignature`](#snaponsignature) - [`snap.runCronjob`](#snapruncronjob) - [Jest matchers](#jest-matchers) - [Interacting with user interfaces](#interacting-with-user-interfaces) @@ -219,6 +220,40 @@ describe('MySnap', () => { }); ``` +### `snap.onSignature` + +The `onSignature` function can be used to send a signature request to the snap. It +takes a single argument, which is an object with the following properties: + +- `origin`: The origin of the signature request. +- `from`: The address of the signer. +- `data`: The data of the signature. +- `signatureMethod`: The signature method being used in the request. + +All properties are optional, and have sensible defaults. The addresses are +randomly generated by default. Most values can be specified as a hex string, or +a decimal number. + +It returns an object with the user interface that was shown by the snap, in the +[onSignature](https://docs.metamask.io/snaps/reference/exports/#onsignature) +function. + +```js +import { installSnap } from '@metamask/snaps-jest'; +import { panel, text } from '@metamask/snaps-sdk'; + +describe('MySnap', () => { + it('should do something', async () => { + const { onSignature } = await installSnap(/* optional snap ID */); + const response = await onSignature(); + + expect(response).toRender( + panel([text('You are using the personal_sign method')]), + ); + }); +}); +``` + ### `snap.runCronjob` The `runCronjob` function can be used to run a cronjob in the snap. It takes @@ -300,8 +335,9 @@ assert that a response from a snap matches an expected value: - `toRender(expectedInterface)`: Check if a snap rendered an interface. This is useful for testing the UI of a snap, either for a [`snap_dialog`](https://docs.metamask.io/snaps/reference/rpc-api/#snap_dialog), - or a UI rendered by - [the transaction insights API](https://docs.metamask.io/snaps/reference/exports/#ontransaction). + UI rendered by + [the transaction insights API](https://docs.metamask.io/snaps/reference/exports/#ontransaction) or + [the signature insights API](https://docs.metamask.io/snaps/reference/exports/#onsignature). ### Interacting with user interfaces diff --git a/packages/snaps-jest/package.json b/packages/snaps-jest/package.json index 61f0e3c2ff..b055db1be4 100644 --- a/packages/snaps-jest/package.json +++ b/packages/snaps-jest/package.json @@ -3,14 +3,19 @@ "version": "4.0.1", "description": "A Jest preset for end-to-end testing MetaMask Snaps, including a Jest environment, and a set of Jest matchers.", "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**", - "jest-preset.js" + "dist" ], "scripts": { "test": "jest --passWithNoTests && yarn posttest", @@ -22,12 +27,8 @@ "lint:ci": "yarn lint", "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write", "lint:changelog": "../../scripts/validate-changelog.sh @metamask/snaps-jest", - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", - "build:clean": "yarn clean && yarn build", "clean": "rimraf '*.tsbuildinfo' 'dist'", "publish:preview": "yarn npm publish --tag preview", "lint:dependencies": "depcheck" @@ -65,7 +66,6 @@ "@metamask/eslint-config-jest": "^12.1.0", "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", "@types/jest": "^27.5.1", @@ -87,6 +87,7 @@ "prettier": "^2.7.1", "prettier-plugin-packagejson": "^2.2.11", "rimraf": "^4.1.2", + "tsup": "^8.0.1", "typescript": "~4.8.4" }, "engines": { @@ -96,5 +97,6 @@ "access": "public", "registry": "https://registry.npmjs.org/" }, + "build:types": "tsc --project tsconfig.build.json", "packageManager": "yarn@3.4.1" } diff --git a/packages/snaps-jest/src/helpers.test.ts b/packages/snaps-jest/src/helpers.test.ts index 236049a078..eedf390009 100644 --- a/packages/snaps-jest/src/helpers.test.ts +++ b/packages/snaps-jest/src/helpers.test.ts @@ -615,6 +615,51 @@ describe('installSnap', () => { }); }); + describe('onSignature', () => { + it('sends a signature request and returns the result', async () => { + jest.spyOn(console, 'log').mockImplementation(); + + const { snapId, close: closeServer } = await getMockServer({ + sourceCode: ` + module.exports.onSignature = async ({ signature }) => { + return { + content: { + type: 'text', + value: 'You are using the ' + signature.signatureMethod + ' method.', + }, + severity: 'critical', + }; + }; + `, + }); + + const { onSignature, close } = await installSnap(snapId); + const response = await onSignature({ + signatureMethod: 'personal_sign', + }); + + expect(response).toStrictEqual( + expect.objectContaining({ + response: { + result: { + content: { + type: 'text', + value: 'You are using the personal_sign method.', + }, + severity: 'critical', + }, + }, + }), + ); + + // `close` is deprecated because the Jest environment will automatically + // close the Snap when the test finishes. However, we still need to close + // the Snap in this test because it's run outside the Jest environment. + await close(); + await closeServer(); + }); + }); + describe('runCronjob', () => { it('runs a cronjob and returns the result', async () => { jest.spyOn(console, 'log').mockImplementation(); diff --git a/packages/snaps-jest/src/helpers.ts b/packages/snaps-jest/src/helpers.ts index 5cea5667e1..b64f0ccfa1 100644 --- a/packages/snaps-jest/src/helpers.ts +++ b/packages/snaps-jest/src/helpers.ts @@ -9,6 +9,7 @@ import { TransactionOptionsStruct, getEnvironment, JsonRpcMockOptionsStruct, + SignatureOptionsStruct, } from './internals'; import type { InstallSnapOptions } from './internals'; import { @@ -252,6 +253,30 @@ export async function installSnap< onTransaction, sendTransaction: onTransaction, + onSignature: async (request: unknown): Promise => { + log('Requesting signature %o.', request); + + const { origin: signatureOrigin, ...signature } = create( + request, + SignatureOptionsStruct, + ); + + return handleRequest({ + snapId: installedSnapId, + store, + executionService, + runSaga, + handler: HandlerType.OnSignature, + request: { + method: '', + params: { + signature, + signatureOrigin, + }, + }, + }); + }, + onCronjob, runCronjob: onCronjob, diff --git a/packages/snaps-jest/src/internals/structs.test.ts b/packages/snaps-jest/src/internals/structs.test.ts index e05899bdc2..700d0736a2 100644 --- a/packages/snaps-jest/src/internals/structs.test.ts +++ b/packages/snaps-jest/src/internals/structs.test.ts @@ -4,6 +4,7 @@ import { create } from 'superstruct'; import { InterfaceStruct, JsonRpcMockOptionsStruct, + SignatureOptionsStruct, SnapOptionsStruct, SnapResponseStruct, TransactionOptionsStruct, @@ -86,6 +87,43 @@ describe('TransactionOptionsStruct', () => { }); }); +describe('SignatureOptionsStruct', () => { + it('accepts an empty object', () => { + const signature = create({}, SignatureOptionsStruct); + + expect(signature).toStrictEqual({ + data: '0x', + from: expect.any(String), + origin: 'metamask.io', + signatureMethod: 'personal_sign', + }); + }); + + it('accepts a valid object', () => { + const signature = create( + { + data: { foo: 'bar' }, + from: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', + origin: 'metamask.io', + signatureMethod: 'eth_signTypedData_v3', + }, + SignatureOptionsStruct, + ); + + expect(signature).toStrictEqual({ + data: { foo: 'bar' }, + from: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045', + origin: 'metamask.io', + signatureMethod: 'eth_signTypedData_v3', + }); + }); + + it.each(INVALID_VALUES)('throws for invalid value: %p', (value) => { + // eslint-disable-next-line jest/require-to-throw-message + expect(() => create(value, TransactionOptionsStruct)).toThrow(); + }); +}); + describe('SnapOptionsStruct', () => { it('accepts an empty object', () => { const options = create({}, SnapOptionsStruct); diff --git a/packages/snaps-jest/src/internals/structs.ts b/packages/snaps-jest/src/internals/structs.ts index f262f3b2f3..61a0fa37df 100644 --- a/packages/snaps-jest/src/internals/structs.ts +++ b/packages/snaps-jest/src/internals/structs.ts @@ -24,6 +24,8 @@ import { string, type, union, + record, + any, } from 'superstruct'; // TODO: Export this from `@metamask/utils` instead. @@ -140,6 +142,54 @@ export const TransactionOptionsStruct = object({ ), }); +export const SignatureOptionsStruct = object({ + /** + * The origin making the signature request. + */ + origin: defaulted(string(), 'metamask.io'), + + /** + * The address signing the signature request. Defaults to a randomly generated + * address. + */ + from: coerce(StrictHexStruct, optional(BytesLikeStruct), (value) => { + if (value) { + return bytesToHex(valueToBytes(value)); + } + + return bytesToHex(randomBytes(20)); + }), + + /** + * The data to send with the transaction. The data may be specified as a + * `string`, an object, or an array of objects. This covers the data types + * for the supported signature methods. Defaults to `0x`. + */ + data: defaulted( + union([ + StrictHexStruct, + literal('0x'), + record(string(), any()), + array(record(string(), any())), + ]), + '0x', + ), + + /** + * The signature method being used. + */ + signatureMethod: defaulted( + union([ + literal('eth_sign'), + literal('personal_sign'), + literal('eth_signTypedData'), + literal('eth_signTypedData_v3'), + literal('eth_signTypedData_v4'), + ]), + 'personal_sign', + ), +}); + export const SnapOptionsStruct = object({ /** * The timeout in milliseconds to use for requests to the snap. Defaults to diff --git a/packages/snaps-jest/src/types.ts b/packages/snaps-jest/src/types.ts index ffe11a3c0f..cbff45d265 100644 --- a/packages/snaps-jest/src/types.ts +++ b/packages/snaps-jest/src/types.ts @@ -7,6 +7,7 @@ import type { Json, JsonRpcId, JsonRpcParams } from '@metamask/utils'; import type { Infer } from 'superstruct'; import type { + SignatureOptionsStruct, SnapOptionsStruct, SnapResponseStruct, TransactionOptionsStruct, @@ -91,6 +92,18 @@ export type CronjobOptions = Omit; */ export type TransactionOptions = Infer; +/** + * The options to use for signature requests. + * + * @property origin - The origin to send the signature request from. Defaults to + * `metamask.io`. + * @property from - The address to send the signature from. Defaults to a + * randomly generated address. + * @property data - The data to sign. Defaults to `0x`. + * @property signatureMethod - The signature method. + */ +export type SignatureOptions = Infer; + /** * The options to use for requests to the snap. * @@ -257,6 +270,16 @@ export type Snap = { transaction?: Partial, ): Promise; + /** + * Send a signature request to the snap. + * + * @param signature - The signature request object. Contains the params from + * the various signature methods, but has an extra `origin` and `signatureMethod` field. + * Any missing fields will be filled in with default values. + * @returns The response. + */ + onSignature(signature?: Partial): Promise; + /** * Run a cronjob in the snap. This is similar to {@link request}, but the * request will be sent to the `onCronjob` method of the snap. diff --git a/packages/snaps-jest/tsconfig.json b/packages/snaps-jest/tsconfig.json index 81d407e968..8fafac73fc 100644 --- a/packages/snaps-jest/tsconfig.json +++ b/packages/snaps-jest/tsconfig.json @@ -4,7 +4,7 @@ "baseUrl": "./", "jsx": "preserve" }, - "include": ["./src"], + "include": ["./src", "package.json", "tsup.config.ts"], "references": [ { "path": "../snaps-utils" diff --git a/packages/snaps-jest/tsup.config.ts b/packages/snaps-jest/tsup.config.ts new file mode 100644 index 0000000000..3eaf645296 --- /dev/null +++ b/packages/snaps-jest/tsup.config.ts @@ -0,0 +1,14 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-rollup-plugin/package.json b/packages/snaps-rollup-plugin/package.json index a4c2d21db0..130c482268 100644 --- a/packages/snaps-rollup-plugin/package.json +++ b/packages/snaps-rollup-plugin/package.json @@ -10,13 +10,19 @@ "url": "https://github.com/MetaMask/snaps.git" }, "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**" + "dist" ], "scripts": { "test": "jest && yarn posttest", @@ -27,12 +33,8 @@ "lint": "yarn lint:eslint && yarn lint:misc --check && yarn lint:changelog && yarn lint:dependencies", "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write", "lint:changelog": "../../scripts/validate-changelog.sh @metamask/snaps-rollup-plugin", - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", - "build:clean": "yarn clean && yarn build", "clean": "rimraf '*.tsbuildinfo' 'dist'", "publish:preview": "yarn npm publish --tag preview", "lint:ci": "yarn lint", @@ -49,7 +51,6 @@ "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", "@rollup/plugin-virtual": "^2.1.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", "@types/jest": "^27.5.1", @@ -72,6 +73,7 @@ "prettier-plugin-packagejson": "^2.2.11", "rimraf": "^4.1.2", "rollup": "^2.73.0", + "tsup": "^8.0.1", "typescript": "~4.8.4" }, "engines": { @@ -80,5 +82,6 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" - } + }, + "build:types": "tsc --project tsconfig.build.json" } diff --git a/packages/snaps-rollup-plugin/tsconfig.json b/packages/snaps-rollup-plugin/tsconfig.json index a4f2b5a12e..dc42b0cdc0 100644 --- a/packages/snaps-rollup-plugin/tsconfig.json +++ b/packages/snaps-rollup-plugin/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "include": ["./src"], + "include": ["./src", "package.json", "tsup.config.ts"], "references": [{ "path": "../snaps-utils" }] } diff --git a/packages/snaps-rollup-plugin/tsup.config.ts b/packages/snaps-rollup-plugin/tsup.config.ts new file mode 100644 index 0000000000..3eaf645296 --- /dev/null +++ b/packages/snaps-rollup-plugin/tsup.config.ts @@ -0,0 +1,14 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-rpc-methods/jest.config.js b/packages/snaps-rpc-methods/jest.config.js index 6241a6ae1c..efa8226dbc 100644 --- a/packages/snaps-rpc-methods/jest.config.js +++ b/packages/snaps-rpc-methods/jest.config.js @@ -10,10 +10,10 @@ module.exports = deepmerge(baseConfig, { ], coverageThreshold: { global: { - branches: 92.91, + branches: 93.07, functions: 100, lines: 99.2, - statements: 97.52, + statements: 97.77, }, }, }); diff --git a/packages/snaps-rpc-methods/package.json b/packages/snaps-rpc-methods/package.json index f2a3577967..abd0d9c789 100644 --- a/packages/snaps-rpc-methods/package.json +++ b/packages/snaps-rpc-methods/package.json @@ -7,13 +7,19 @@ "url": "https://github.com/MetaMask/snaps.git" }, "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**" + "dist" ], "scripts": { "test": "jest && yarn posttest", @@ -24,12 +30,8 @@ "lint": "yarn lint:eslint && yarn lint:misc --check && yarn lint:changelog && yarn lint:dependencies", "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write", "lint:changelog": "../../scripts/validate-changelog.sh @metamask/snaps-rpc-methods", - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", - "build:clean": "yarn clean && yarn build", "clean": "rimraf '*.tsbuildinfo' 'dist'", "publish:preview": "yarn npm publish --tag preview", "lint:ci": "yarn lint", @@ -54,7 +56,6 @@ "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", "@metamask/json-rpc-engine": "^7.3.1", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", "@types/node": "18.14.2", @@ -75,6 +76,7 @@ "prettier": "^2.7.1", "prettier-plugin-packagejson": "^2.2.11", "rimraf": "^4.1.2", + "tsup": "^8.0.1", "typescript": "~4.8.4" }, "engines": { @@ -83,5 +85,6 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" - } + }, + "build:types": "tsc --project tsconfig.build.json" } diff --git a/packages/snaps-rpc-methods/src/permitted/createInterface.test.ts b/packages/snaps-rpc-methods/src/permitted/createInterface.test.ts new file mode 100644 index 0000000000..4c2f5da0a9 --- /dev/null +++ b/packages/snaps-rpc-methods/src/permitted/createInterface.test.ts @@ -0,0 +1,101 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; +import { text, type CreateInterfaceResult } from '@metamask/snaps-sdk'; +import type { JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; + +import type { CreateInterfaceParameters } from './createInterface'; +import { createInterfaceHandler } from './createInterface'; + +describe('snap_createInterface', () => { + describe('createInterfaceHandler', () => { + it('has the expected shape', () => { + expect(createInterfaceHandler).toMatchObject({ + methodNames: ['snap_createInterface'], + implementation: expect.any(Function), + hookNames: { + createInterface: true, + }, + }); + }); + }); + + describe('implementation', () => { + it('returns the result from the `createInterface` hook', async () => { + const { implementation } = createInterfaceHandler; + + const createInterface = jest.fn().mockReturnValue('foo'); + + const hooks = { + createInterface, + }; + + const engine = new JsonRpcEngine(); + + engine.push((request, response, next, end) => { + const result = implementation( + request as JsonRpcRequest, + response as PendingJsonRpcResponse, + next, + end, + hooks, + ); + + result?.catch(end); + }); + + const response = await engine.handle({ + jsonrpc: '2.0', + id: 1, + method: 'snap_createInterface', + params: { + ui: text('foo'), + }, + }); + + expect(response).toStrictEqual({ jsonrpc: '2.0', id: 1, result: 'foo' }); + }); + }); + + it('throws on invalid params', async () => { + const { implementation } = createInterfaceHandler; + + const createInterface = jest.fn().mockReturnValue('foo'); + + const hooks = { + createInterface, + }; + + const engine = new JsonRpcEngine(); + + engine.push((request, response, next, end) => { + const result = implementation( + request as JsonRpcRequest, + response as PendingJsonRpcResponse, + next, + end, + hooks, + ); + + result?.catch(end); + }); + + const response = await engine.handle({ + jsonrpc: '2.0', + id: 1, + method: 'snap_createInterface', + params: { + ui: 'foo', + }, + }); + + expect(response).toStrictEqual({ + error: { + code: -32602, + message: + 'Invalid params: At path: ui -- Expected the value to satisfy a union of `object | object | object | object | object | object | object | object | object | object | object | object`, but received: "foo".', + stack: expect.any(String), + }, + id: 1, + jsonrpc: '2.0', + }); + }); +}); diff --git a/packages/snaps-rpc-methods/src/permitted/createInterface.ts b/packages/snaps-rpc-methods/src/permitted/createInterface.ts new file mode 100644 index 0000000000..a3aff14e8b --- /dev/null +++ b/packages/snaps-rpc-methods/src/permitted/createInterface.ts @@ -0,0 +1,101 @@ +import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; +import type { PermittedHandlerExport } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; +import type { + Component, + CreateInterfaceParams, + CreateInterfaceResult, + JsonRpcRequest, +} from '@metamask/snaps-sdk'; +import { ComponentStruct } from '@metamask/snaps-sdk'; +import { type InferMatching } from '@metamask/snaps-utils'; +import type { PendingJsonRpcResponse } from '@metamask/utils'; +import { StructError, create, object } from 'superstruct'; + +import type { MethodHooksObject } from '../utils'; + +const hookNames: MethodHooksObject = { + createInterface: true, +}; + +export type CreateInterfaceMethodHooks = { + /** + * @param ui - The UI components. + * @returns The unique identifier of the interface. + */ + createInterface: (ui: Component) => string; +}; + +export const createInterfaceHandler: PermittedHandlerExport< + CreateInterfaceMethodHooks, + CreateInterfaceParameters, + CreateInterfaceResult +> = { + methodNames: ['snap_createInterface'], + implementation: getCreateInterfaceImplementation, + hookNames, +}; + +const CreateInterfaceParametersStruct = object({ + ui: ComponentStruct, +}); + +export type CreateInterfaceParameters = InferMatching< + typeof CreateInterfaceParametersStruct, + CreateInterfaceParams +>; + +/** + * The `snap_createInterface` method implementation. + * + * @param req - The JSON-RPC request object. + * @param res - The JSON-RPC response object. + * @param _next - The `json-rpc-engine` "next" callback. Not used by this + * function. + * @param end - The `json-rpc-engine` "end" callback. + * @param hooks - The RPC method hooks. + * @param hooks.createInterface - The function to create the interface. + * @returns Nothing. + */ +function getCreateInterfaceImplementation( + req: JsonRpcRequest, + res: PendingJsonRpcResponse, + _next: unknown, + end: JsonRpcEngineEndCallback, + { createInterface }: CreateInterfaceMethodHooks, +): void { + const { params } = req; + + try { + const validatedParams = getValidatedParams(params); + + const { ui } = validatedParams; + + res.result = createInterface(ui); + } catch (error) { + return end(error); + } + + return end(); +} + +/** + * Validate the createInterface method `params` and returns them cast to the correct + * type. Throws if validation fails. + * + * @param params - The unvalidated params object from the method request. + * @returns The validated createInterface method parameter object. + */ +function getValidatedParams(params: unknown): CreateInterfaceParameters { + try { + return create(params, CreateInterfaceParametersStruct); + } catch (error) { + if (error instanceof StructError) { + throw rpcErrors.invalidParams({ + message: `Invalid params: ${error.message}.`, + }); + } + /* istanbul ignore next */ + throw rpcErrors.internal(); + } +} diff --git a/packages/snaps-rpc-methods/src/permitted/getFile.ts b/packages/snaps-rpc-methods/src/permitted/getFile.ts index e523b4d739..a50295dd68 100644 --- a/packages/snaps-rpc-methods/src/permitted/getFile.ts +++ b/packages/snaps-rpc-methods/src/permitted/getFile.ts @@ -56,7 +56,7 @@ export type GetFileHooks = { * function. * @param end - The `json-rpc-engine` "end" callback. * @param hooks - The RPC method hooks. - * @param hooks.getSnapFile - The funnction to load a static snap file. + * @param hooks.getSnapFile - The function to load a static snap file. * @returns Nothing. */ async function implementation( diff --git a/packages/snaps-rpc-methods/src/permitted/getInterfaceState.test.ts b/packages/snaps-rpc-methods/src/permitted/getInterfaceState.test.ts new file mode 100644 index 0000000000..9d336a55d3 --- /dev/null +++ b/packages/snaps-rpc-methods/src/permitted/getInterfaceState.test.ts @@ -0,0 +1,105 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; +import { type GetInterfaceStateResult } from '@metamask/snaps-sdk'; +import type { JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; + +import { getInterfaceStateHandler } from './getInterfaceState'; +import type { UpdateInterfaceParameters } from './updateInterface'; + +describe('snap_getInterfaceState', () => { + describe('getInterfaceStateHandler', () => { + it('has the expected shape', () => { + expect(getInterfaceStateHandler).toMatchObject({ + methodNames: ['snap_getInterfaceState'], + implementation: expect.any(Function), + hookNames: { + getInterfaceState: true, + }, + }); + }); + }); + + describe('implementation', () => { + it('returns the result from the `getInterfaceState` hook', async () => { + const { implementation } = getInterfaceStateHandler; + + const getInterfaceState = jest.fn().mockReturnValue({ foo: 'bar' }); + + const hooks = { + getInterfaceState, + }; + + const engine = new JsonRpcEngine(); + + engine.push((request, response, next, end) => { + const result = implementation( + request as JsonRpcRequest, + response as PendingJsonRpcResponse, + next, + end, + hooks, + ); + + result?.catch(end); + }); + + const response = await engine.handle({ + jsonrpc: '2.0', + id: 1, + method: 'snap_getInterfaceState', + params: { + id: 'foo', + }, + }); + + expect(response).toStrictEqual({ + jsonrpc: '2.0', + id: 1, + result: { foo: 'bar' }, + }); + }); + + it('throws on invalid params', async () => { + const { implementation } = getInterfaceStateHandler; + + const getInterfaceState = jest.fn().mockReturnValue({ foo: 'bar' }); + + const hooks = { + getInterfaceState, + }; + + const engine = new JsonRpcEngine(); + + engine.push((request, response, next, end) => { + const result = implementation( + request as JsonRpcRequest, + response as PendingJsonRpcResponse, + next, + end, + hooks, + ); + + result?.catch(end); + }); + + const response = await engine.handle({ + jsonrpc: '2.0', + id: 1, + method: 'snap_getInterfaceState', + params: { + id: 42, + }, + }); + + expect(response).toStrictEqual({ + error: { + code: -32602, + message: + 'Invalid params: At path: id -- Expected a string, but received: 42.', + stack: expect.any(String), + }, + id: 1, + jsonrpc: '2.0', + }); + }); + }); +}); diff --git a/packages/snaps-rpc-methods/src/permitted/getInterfaceState.ts b/packages/snaps-rpc-methods/src/permitted/getInterfaceState.ts new file mode 100644 index 0000000000..55a2fe22f5 --- /dev/null +++ b/packages/snaps-rpc-methods/src/permitted/getInterfaceState.ts @@ -0,0 +1,100 @@ +import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; +import type { PermittedHandlerExport } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; +import type { + GetInterfaceStateParams, + GetInterfaceStateResult, + InterfaceState, + JsonRpcRequest, +} from '@metamask/snaps-sdk'; +import { type InferMatching } from '@metamask/snaps-utils'; +import type { PendingJsonRpcResponse } from '@metamask/utils'; +import { StructError, create, object, string } from 'superstruct'; + +import type { MethodHooksObject } from '../utils'; + +const hookNames: MethodHooksObject = { + getInterfaceState: true, +}; + +export type GetInterfaceStateMethodHooks = { + /** + * @param id - The interface ID. + * @returns The interface state. + */ + getInterfaceState: (id: string) => InterfaceState; +}; + +export const getInterfaceStateHandler: PermittedHandlerExport< + GetInterfaceStateMethodHooks, + GetInterfaceStateParameters, + GetInterfaceStateResult +> = { + methodNames: ['snap_getInterfaceState'], + implementation: getGetInterfaceStateImplementation, + hookNames, +}; + +const GetInterfaceStateParametersStruct = object({ + id: string(), +}); + +export type GetInterfaceStateParameters = InferMatching< + typeof GetInterfaceStateParametersStruct, + GetInterfaceStateParams +>; + +/** + * The `snap_getInterfaceState` method implementation. + * + * @param req - The JSON-RPC request object. + * @param res - The JSON-RPC response object. + * @param _next - The `json-rpc-engine` "next" callback. Not used by this + * function. + * @param end - The `json-rpc-engine` "end" callback. + * @param hooks - The RPC method hooks. + * @param hooks.getInterfaceState - The function to get the interface state. + * @returns Noting. + */ +function getGetInterfaceStateImplementation( + req: JsonRpcRequest, + res: PendingJsonRpcResponse, + _next: unknown, + end: JsonRpcEngineEndCallback, + { getInterfaceState }: GetInterfaceStateMethodHooks, +): void { + const { params } = req; + + try { + const validatedParams = getValidatedParams(params); + + const { id } = validatedParams; + + res.result = getInterfaceState(id); + } catch (error) { + return end(error); + } + + return end(); +} + +/** + * Validate the getInterfaceState method `params` and returns them cast to the correct + * type. Throws if validation fails. + * + * @param params - The unvalidated params object from the method request. + * @returns The validated getInterfaceState method parameter object. + */ +function getValidatedParams(params: unknown): GetInterfaceStateParameters { + try { + return create(params, GetInterfaceStateParametersStruct); + } catch (error) { + if (error instanceof StructError) { + throw rpcErrors.invalidParams({ + message: `Invalid params: ${error.message}.`, + }); + } + /* istanbul ignore next */ + throw rpcErrors.internal(); + } +} diff --git a/packages/snaps-rpc-methods/src/permitted/handlers.ts b/packages/snaps-rpc-methods/src/permitted/handlers.ts index 6218b3e1d2..b0b707364b 100644 --- a/packages/snaps-rpc-methods/src/permitted/handlers.ts +++ b/packages/snaps-rpc-methods/src/permitted/handlers.ts @@ -1,10 +1,13 @@ +import { createInterfaceHandler } from './createInterface'; import { getAllSnapsHandler } from './getAllSnaps'; import { getClientStatusHandler } from './getClientStatus'; import { getFileHandler } from './getFile'; +import { getInterfaceStateHandler } from './getInterfaceState'; import { getSnapsHandler } from './getSnaps'; import { invokeKeyringHandler } from './invokeKeyring'; import { invokeSnapSugarHandler } from './invokeSnapSugar'; import { requestSnapsHandler } from './requestSnaps'; +import { updateInterfaceHandler } from './updateInterface'; /* eslint-disable @typescript-eslint/naming-convention */ export const methodHandlers = { @@ -15,6 +18,9 @@ export const methodHandlers = { wallet_invokeKeyring: invokeKeyringHandler, snap_getClientStatus: getClientStatusHandler, snap_getFile: getFileHandler, + snap_createInterface: createInterfaceHandler, + snap_updateInterface: updateInterfaceHandler, + snap_getInterfaceState: getInterfaceStateHandler, }; /* eslint-enable @typescript-eslint/naming-convention */ diff --git a/packages/snaps-rpc-methods/src/permitted/index.ts b/packages/snaps-rpc-methods/src/permitted/index.ts index 480155aec9..7cb264f1b2 100644 --- a/packages/snaps-rpc-methods/src/permitted/index.ts +++ b/packages/snaps-rpc-methods/src/permitted/index.ts @@ -1,12 +1,18 @@ +import type { CreateInterfaceMethodHooks } from './createInterface'; import type { GetAllSnapsHooks } from './getAllSnaps'; import type { GetClientStatusHooks } from './getClientStatus'; +import type { GetInterfaceStateMethodHooks } from './getInterfaceState'; import type { GetSnapsHooks } from './getSnaps'; import type { RequestSnapsHooks } from './requestSnaps'; +import type { UpdateInterfaceMethodHooks } from './updateInterface'; export type PermittedRpcMethodHooks = GetAllSnapsHooks & GetClientStatusHooks & GetSnapsHooks & - RequestSnapsHooks; + RequestSnapsHooks & + CreateInterfaceMethodHooks & + UpdateInterfaceMethodHooks & + GetInterfaceStateMethodHooks; export * from './handlers'; export * from './middleware'; diff --git a/packages/snaps-rpc-methods/src/permitted/updateInterface.test.ts b/packages/snaps-rpc-methods/src/permitted/updateInterface.test.ts new file mode 100644 index 0000000000..e85774207d --- /dev/null +++ b/packages/snaps-rpc-methods/src/permitted/updateInterface.test.ts @@ -0,0 +1,102 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; +import type { UpdateInterfaceParams } from '@metamask/snaps-sdk'; +import { text, type UpdateInterfaceResult } from '@metamask/snaps-sdk'; +import type { JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; + +import { updateInterfaceHandler } from './updateInterface'; + +describe('snap_updateInterface', () => { + describe('updateInterfaceHandler', () => { + it('has the expected shape', () => { + expect(updateInterfaceHandler).toMatchObject({ + methodNames: ['snap_updateInterface'], + implementation: expect.any(Function), + hookNames: { + updateInterface: true, + }, + }); + }); + }); + + describe('implementation', () => { + it('returns the result from the `updateInterface` hook', async () => { + const { implementation } = updateInterfaceHandler; + + const updateInterface = jest.fn(); + + const hooks = { + updateInterface, + }; + + const engine = new JsonRpcEngine(); + + engine.push((request, response, next, end) => { + const result = implementation( + request as JsonRpcRequest, + response as PendingJsonRpcResponse, + next, + end, + hooks, + ); + + result?.catch(end); + }); + + const response = await engine.handle({ + jsonrpc: '2.0', + id: 1, + method: 'snap_updateInterface', + params: { + id: 'foo', + ui: text('foo'), + }, + }); + + expect(response).toStrictEqual({ jsonrpc: '2.0', id: 1, result: null }); + }); + }); + + it('throws on invalid params', async () => { + const { implementation } = updateInterfaceHandler; + + const updateInterface = jest.fn(); + + const hooks = { + updateInterface, + }; + + const engine = new JsonRpcEngine(); + + engine.push((request, response, next, end) => { + const result = implementation( + request as JsonRpcRequest, + response as PendingJsonRpcResponse, + next, + end, + hooks, + ); + + result?.catch(end); + }); + + const response = await engine.handle({ + jsonrpc: '2.0', + id: 1, + method: 'snap_updateInterface', + params: { + id: 42, + }, + }); + + expect(response).toStrictEqual({ + error: { + code: -32602, + message: + 'Invalid params: At path: id -- Expected a string, but received: 42.', + stack: expect.any(String), + }, + id: 1, + jsonrpc: '2.0', + }); + }); +}); diff --git a/packages/snaps-rpc-methods/src/permitted/updateInterface.ts b/packages/snaps-rpc-methods/src/permitted/updateInterface.ts new file mode 100644 index 0000000000..cacc77c58e --- /dev/null +++ b/packages/snaps-rpc-methods/src/permitted/updateInterface.ts @@ -0,0 +1,103 @@ +import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; +import type { PermittedHandlerExport } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; +import type { + Component, + UpdateInterfaceParams, + UpdateInterfaceResult, + JsonRpcRequest, +} from '@metamask/snaps-sdk'; +import { ComponentStruct } from '@metamask/snaps-sdk'; +import { type InferMatching } from '@metamask/snaps-utils'; +import type { PendingJsonRpcResponse } from '@metamask/utils'; +import { StructError, create, object, string } from 'superstruct'; + +import type { MethodHooksObject } from '../utils'; + +const hookNames: MethodHooksObject = { + updateInterface: true, +}; + +export type UpdateInterfaceMethodHooks = { + /** + * @param id - The interface ID. + * @param ui - The UI components. + */ + updateInterface: (id: string, ui: Component) => void; +}; + +export const updateInterfaceHandler: PermittedHandlerExport< + UpdateInterfaceMethodHooks, + UpdateInterfaceParameters, + UpdateInterfaceResult +> = { + methodNames: ['snap_updateInterface'], + implementation: getUpdateInterfaceImplementation, + hookNames, +}; + +const UpdateInterfaceParametersStruct = object({ + id: string(), + ui: ComponentStruct, +}); + +export type UpdateInterfaceParameters = InferMatching< + typeof UpdateInterfaceParametersStruct, + UpdateInterfaceParams +>; + +/** + * The `snap_updateInterface` method implementation. + * + * @param req - The JSON-RPC request object. + * @param res - The JSON-RPC response object. + * @param _next - The `json-rpc-engine` "next" callback. Not used by this + * function. + * @param end - The `json-rpc-engine` "end" callback. + * @param hooks - The RPC method hooks. + * @param hooks.updateInterface - The function to update the interface. + * @returns Nothing. + */ +function getUpdateInterfaceImplementation( + req: JsonRpcRequest, + res: PendingJsonRpcResponse, + _next: unknown, + end: JsonRpcEngineEndCallback, + { updateInterface }: UpdateInterfaceMethodHooks, +): void { + const { params } = req; + + try { + const validatedParams = getValidatedParams(params); + + const { id, ui } = validatedParams; + + updateInterface(id, ui); + res.result = null; + } catch (error) { + return end(error); + } + + return end(); +} + +/** + * Validate the updateInterface method `params` and returns them cast to the correct + * type. Throws if validation fails. + * + * @param params - The unvalidated params object from the method request. + * @returns The validated updateInterface method parameter object. + */ +function getValidatedParams(params: unknown): UpdateInterfaceParameters { + try { + return create(params, UpdateInterfaceParametersStruct); + } catch (error) { + if (error instanceof StructError) { + throw rpcErrors.invalidParams({ + message: `Invalid params: ${error.message}.`, + }); + } + /* istanbul ignore next */ + throw rpcErrors.internal(); + } +} diff --git a/packages/snaps-rpc-methods/tsconfig.json b/packages/snaps-rpc-methods/tsconfig.json index 2db16954a8..87b8b62915 100644 --- a/packages/snaps-rpc-methods/tsconfig.json +++ b/packages/snaps-rpc-methods/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "include": ["./src"], + "include": ["./src", "package.json", "tsup.config.ts"], "references": [{ "path": "../snaps-sdk" }, { "path": "../snaps-utils" }] } diff --git a/packages/snaps-rpc-methods/tsup.config.ts b/packages/snaps-rpc-methods/tsup.config.ts new file mode 100644 index 0000000000..3eaf645296 --- /dev/null +++ b/packages/snaps-rpc-methods/tsup.config.ts @@ -0,0 +1,14 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-sdk/package.json b/packages/snaps-sdk/package.json index 4e48dae19b..acc65b343b 100644 --- a/packages/snaps-sdk/package.json +++ b/packages/snaps-sdk/package.json @@ -6,13 +6,19 @@ "url": "https://github.com/MetaMask/snaps.git" }, "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**" + "dist" ], "scripts": { "test": "jest && yarn posttest", @@ -23,12 +29,8 @@ "lint": "yarn lint:eslint && yarn lint:misc --check && yarn lint:changelog && yarn lint:dependencies", "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write", "lint:changelog": "../../scripts/validate-changelog.sh @metamask/snaps-sdk", - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", - "build:clean": "yarn clean && yarn build", "clean": "rimraf '*.tsbuildinfo' 'dist'", "publish:preview": "yarn npm publish --tag preview", "lint:ci": "yarn lint", @@ -49,7 +51,6 @@ "@metamask/eslint-config-jest": "^12.1.0", "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@types/jest": "^27.5.1", "@typescript-eslint/eslint-plugin": "^5.42.1", @@ -72,6 +73,7 @@ "prettier-plugin-packagejson": "^2.2.11", "rimraf": "^4.1.2", "ts-jest": "^29.1.1", + "tsup": "^8.0.1", "typescript": "~4.8.4" }, "engines": { @@ -80,5 +82,6 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" - } + }, + "build:types": "tsc --project tsconfig.build.json" } diff --git a/packages/snaps-sdk/src/types/handlers/home-page.ts b/packages/snaps-sdk/src/types/handlers/home-page.ts index c80835a74b..965c2d3eb8 100644 --- a/packages/snaps-sdk/src/types/handlers/home-page.ts +++ b/packages/snaps-sdk/src/types/handlers/home-page.ts @@ -15,7 +15,10 @@ export type OnHomePageHandler = () => Promise; * The content to display on the home page. * * @property content - A custom UI component, that will be shown in MetaMask. + * @property id - A custom UI interface ID, that will be shown in MetaMask. */ -export type OnHomePageResponse = { - content: Component; -}; +export type OnHomePageResponse = + | { + content: Component; + } + | { id: string }; diff --git a/packages/snaps-sdk/src/types/handlers/index.ts b/packages/snaps-sdk/src/types/handlers/index.ts index 0bf0b5b921..1e4fc63634 100644 --- a/packages/snaps-sdk/src/types/handlers/index.ts +++ b/packages/snaps-sdk/src/types/handlers/index.ts @@ -6,3 +6,4 @@ export * from './name-lookup'; export * from './rpc-request'; export * from './transaction'; export * from './signature'; +export * from './user-input'; diff --git a/packages/snaps-sdk/src/types/handlers/user-input.test.ts b/packages/snaps-sdk/src/types/handlers/user-input.test.ts new file mode 100644 index 0000000000..d7be7575f9 --- /dev/null +++ b/packages/snaps-sdk/src/types/handlers/user-input.test.ts @@ -0,0 +1,9 @@ +import { UserInputEventType } from './user-input'; + +describe('UserInputEventType', () => { + it('has the correct values', () => { + expect(Object.values(UserInputEventType)).toHaveLength(2); + expect(UserInputEventType.ButtonClickEvent).toBe('ButtonClickEvent'); + expect(UserInputEventType.FormSubmitEvent).toBe('FormSubmitEvent'); + }); +}); diff --git a/packages/snaps-sdk/src/types/handlers/user-input.ts b/packages/snaps-sdk/src/types/handlers/user-input.ts new file mode 100644 index 0000000000..89d5d44c93 --- /dev/null +++ b/packages/snaps-sdk/src/types/handlers/user-input.ts @@ -0,0 +1,70 @@ +import type { Infer } from 'superstruct'; +import { + assign, + literal, + object, + optional, + record, + string, + union, +} from 'superstruct'; + +/** + * The type of user input event fired. + * Currently only two events are supported: + * + * - `ButtonClickEvent` - A button has been clicked in the UI. + * - `FormSubmitEvent` - A Form has been submitted in the UI. + */ +export enum UserInputEventType { + ButtonClickEvent = 'ButtonClickEvent', + FormSubmitEvent = 'FormSubmitEvent', +} + +export const GenericEventStruct = object({ + type: string(), + name: optional(string()), +}); + +export const ButtonClickEventStruct = assign( + GenericEventStruct, + object({ + type: literal(UserInputEventType.ButtonClickEvent), + }), +); + +export const FormSubmitEventStruct = assign( + GenericEventStruct, + object({ + type: literal(UserInputEventType.FormSubmitEvent), + value: record(string(), string()), + name: string(), + }), +); + +export const UserInputEventStruct = union([ + ButtonClickEventStruct, + FormSubmitEventStruct, +]); + +/** + * A user input event fired in the UI. This is passed to the params of the `onUserInput` handler. + * + * @property type - The type of event fired. See {@link UserInputEventType} for the different types. + * @property name - The component name that fired the event. It is optional for an {@link UserInputEventType.ButtonClickEvent}. + * @property value - The value associated with the event. Only available when an {@link UserInputEventType.FormSubmitEvent} is fired. + * It contains the form values submitted. + */ +type UserInputEvent = Infer; + +/** + * The `onUserInput` handler. This is called when an user input event is fired in the UI. + * + * @param args - The user input event. + * @param args.id - The user interface id. + * @param args.event - The {@link UserInputEvent} object, containing the data about the fired event. + */ +export type OnUserInputHandler = (args: { + id: string; + event: UserInputEvent; +}) => Promise; diff --git a/packages/snaps-sdk/src/types/index.ts b/packages/snaps-sdk/src/types/index.ts index afd6730061..a297aeca0c 100644 --- a/packages/snaps-sdk/src/types/index.ts +++ b/packages/snaps-sdk/src/types/index.ts @@ -8,3 +8,4 @@ export * from './methods'; export * from './permissions'; export * from './provider'; export * from './snap'; +export * from './interface'; diff --git a/packages/snaps-sdk/src/types/interface.test.ts b/packages/snaps-sdk/src/types/interface.test.ts new file mode 100644 index 0000000000..1bce8da22b --- /dev/null +++ b/packages/snaps-sdk/src/types/interface.test.ts @@ -0,0 +1,17 @@ +import { assert } from 'superstruct'; + +import { FormStateStruct, InterfaceStateStruct } from './interface'; + +describe('FormStateStruct', () => { + it('passes for a valid form state', () => { + expect(() => assert({ foo: 'bar' }, FormStateStruct)).not.toThrow(); + }); +}); + +describe('InterfaceStateStruct', () => { + it('passes for a valid form state', () => { + expect(() => + assert({ test: { bar: 'baz' }, foo: 'bar' }, InterfaceStateStruct), + ).not.toThrow(); + }); +}); diff --git a/packages/snaps-sdk/src/types/interface.ts b/packages/snaps-sdk/src/types/interface.ts new file mode 100644 index 0000000000..5a9edccaca --- /dev/null +++ b/packages/snaps-sdk/src/types/interface.ts @@ -0,0 +1,18 @@ +import type { Infer } from 'superstruct'; +import { nullable, record, string, union } from 'superstruct'; + +/** + * To avoid typing problems with the interface state when manipulating it we have to differentiate the state of + * a form (that will be contained inside the root state) and the root state since a key in the root stat can contain + * either the value of an input or a sub-state of a form. + */ + +export const FormStateStruct = record(string(), nullable(string())); + +export const InterfaceStateStruct = record( + string(), + union([FormStateStruct, nullable(string())]), +); + +export type FormState = Infer; +export type InterfaceState = Infer; diff --git a/packages/snaps-sdk/src/types/methods/create-interface.ts b/packages/snaps-sdk/src/types/methods/create-interface.ts new file mode 100644 index 0000000000..9dfe7c4f39 --- /dev/null +++ b/packages/snaps-sdk/src/types/methods/create-interface.ts @@ -0,0 +1,15 @@ +import type { Component } from '../../ui'; + +/** + * The request parameters for the `snap_createInterface` method. + * + * @property ui - The components to display in the interface. + */ +export type CreateInterfaceParams = { + ui: Component; +}; + +/** + * The result returned by the `snap_createInterface` method, which is the id of the created interface. + */ +export type CreateInterfaceResult = string; diff --git a/packages/snaps-sdk/src/types/methods/get-interface-state.ts b/packages/snaps-sdk/src/types/methods/get-interface-state.ts new file mode 100644 index 0000000000..7c2253de06 --- /dev/null +++ b/packages/snaps-sdk/src/types/methods/get-interface-state.ts @@ -0,0 +1,15 @@ +import type { InterfaceState } from '../interface'; + +/** + * The request parameters for the `snap_getInterfaceState` method. + * + * @property id - The interface id. + */ +export type GetInterfaceStateParams = { + id: string; +}; + +/** + * The result returned by the `snap_getInterfaceState` method, which is the state of the interface. + */ +export type GetInterfaceStateResult = InterfaceState; diff --git a/packages/snaps-sdk/src/types/methods/index.ts b/packages/snaps-sdk/src/types/methods/index.ts index d975341af3..d3722bc9d6 100644 --- a/packages/snaps-sdk/src/types/methods/index.ts +++ b/packages/snaps-sdk/src/types/methods/index.ts @@ -1,3 +1,4 @@ +export * from './create-interface'; export * from './dialog'; export * from './get-bip32-entropy'; export * from './get-bip32-public-key'; @@ -5,6 +6,7 @@ export * from './get-bip44-entropy'; export * from './get-client-status'; export * from './get-entropy'; export * from './get-file'; +export * from './get-interface-state'; export * from './get-locale'; export * from './get-snaps'; export * from './invoke-keyring'; @@ -14,3 +16,4 @@ export * from './manage-state'; export * from './methods'; export * from './notify'; export * from './request-snaps'; +export * from './update-interface'; diff --git a/packages/snaps-sdk/src/types/methods/methods.ts b/packages/snaps-sdk/src/types/methods/methods.ts index 3461a384c9..c812ca8c40 100644 --- a/packages/snaps-sdk/src/types/methods/methods.ts +++ b/packages/snaps-sdk/src/types/methods/methods.ts @@ -1,4 +1,8 @@ import type { Method } from '../../internals'; +import type { + CreateInterfaceParams, + CreateInterfaceResult, +} from './create-interface'; import type { DialogParams, DialogResult } from './dialog'; import type { GetBip32EntropyParams, @@ -18,6 +22,10 @@ import type { } from './get-client-status'; import type { GetEntropyParams, GetEntropyResult } from './get-entropy'; import type { GetFileParams, GetFileResult } from './get-file'; +import type { + GetInterfaceStateParams, + GetInterfaceStateResult, +} from './get-interface-state'; import type { GetLocaleParams, GetLocaleResult } from './get-locale'; import type { GetSnapsParams, GetSnapsResult } from './get-snaps'; import type { @@ -32,6 +40,10 @@ import type { import type { ManageStateParams, ManageStateResult } from './manage-state'; import type { NotifyParams, NotifyResult } from './notify'; import type { RequestSnapsParams, RequestSnapsResult } from './request-snaps'; +import type { + UpdateInterfaceParams, + UpdateInterfaceResult, +} from './update-interface'; /** * The methods that are available to the Snap. Each method is a tuple of the @@ -50,6 +62,9 @@ export type SnapMethods = { snap_manageAccounts: [ManageAccountsParams, ManageAccountsResult]; snap_manageState: [ManageStateParams, ManageStateResult]; snap_notify: [NotifyParams, NotifyResult]; + snap_createInterface: [CreateInterfaceParams, CreateInterfaceResult]; + snap_updateInterface: [UpdateInterfaceParams, UpdateInterfaceResult]; + snap_getInterfaceState: [GetInterfaceStateParams, GetInterfaceStateResult]; wallet_getSnaps: [GetSnapsParams, GetSnapsResult]; wallet_invokeKeyring: [InvokeKeyringParams, InvokeKeyringResult]; wallet_invokeSnap: [InvokeSnapParams, InvokeSnapResult]; diff --git a/packages/snaps-sdk/src/types/methods/update-interface.ts b/packages/snaps-sdk/src/types/methods/update-interface.ts new file mode 100644 index 0000000000..dfd6af9d98 --- /dev/null +++ b/packages/snaps-sdk/src/types/methods/update-interface.ts @@ -0,0 +1,17 @@ +import type { Component } from '../../ui'; + +/** + * The request parameters for the `snap_createInterface` method. + * + * @property id - The interface id. + * @property ui - The components to display in the interface. + */ +export type UpdateInterfaceParams = { + id: string; + ui: Component; +}; + +/** + * The result returned by the `snap_updateInterface` method. + */ +export type UpdateInterfaceResult = null; diff --git a/packages/snaps-sdk/src/ui/component.test.ts b/packages/snaps-sdk/src/ui/component.test.ts index 0c4a01e919..a13de382c6 100644 --- a/packages/snaps-sdk/src/ui/component.test.ts +++ b/packages/snaps-sdk/src/ui/component.test.ts @@ -1,11 +1,16 @@ import { assertIsComponent, isComponent } from './component'; -import type { - Divider, - Heading, - Image, - Panel, - Spinner, - Text, +import type { Input, Form } from './components'; +import { + ButtonVariant, + type Button, + type Divider, + type Heading, + type Image, + type Panel, + type Spinner, + type Text, + ButtonType, + InputType, } from './components'; import { NodeType } from './nodes'; @@ -86,6 +91,75 @@ describe('isComponent', () => { expect(isComponent(image)).toBe(true); }); + it('returns true for a button component', () => { + const button: Button = { + type: NodeType.Button, + variant: ButtonVariant.Primary, + buttonType: ButtonType.Button, + name: 'myButton', + value: 'Hello, world!', + }; + + expect(isComponent(button)).toBe(true); + }); + + it('returns true for a button component without optional fields', () => { + const button: Button = { + type: NodeType.Button, + variant: undefined, + buttonType: undefined, + name: undefined, + value: 'Hello, world!', + }; + + expect(isComponent(button)).toBe(true); + }); + + it('returns true for an input component', () => { + const input: Input = { + type: NodeType.Input, + value: 'Hello, world!', + name: 'myInput', + inputType: InputType.Text, + placeholder: 'Type here...', + label: 'Hello', + }; + + expect(isComponent(input)).toBe(true); + }); + + it('returns true for an input component without optional fields', () => { + const input: Input = { + type: NodeType.Input, + name: 'myInput', + value: undefined, + inputType: undefined, + placeholder: undefined, + label: undefined, + }; + + expect(isComponent(input)).toBe(true); + }); + + it('returns true for a form component', () => { + const form: Form = { + type: NodeType.Form, + name: 'myForm', + children: [ + { + type: NodeType.Input, + name: 'myInput', + inputType: undefined, + placeholder: undefined, + value: undefined, + label: undefined, + }, + ], + }; + + expect(isComponent(form)).toBe(true); + }); + it.each([ true, false, @@ -173,6 +247,75 @@ describe('assertIsComponent', () => { expect(() => assertIsComponent(text)).not.toThrow(); }); + it('does not throw for a button component', () => { + const button: Button = { + type: NodeType.Button, + variant: ButtonVariant.Primary, + buttonType: ButtonType.Button, + name: 'myButton', + value: 'Hello, world!', + }; + + expect(() => assertIsComponent(button)).not.toThrow(); + }); + + it('does not throw for a button component without optional fields', () => { + const button: Button = { + type: NodeType.Button, + variant: undefined, + buttonType: undefined, + name: undefined, + value: 'Hello, world!', + }; + + expect(() => assertIsComponent(button)).not.toThrow(); + }); + + it('does not throw for an input component', () => { + const input: Input = { + type: NodeType.Input, + value: 'Hello, world!', + name: 'myInput', + inputType: InputType.Text, + placeholder: 'Type here...', + label: 'Hello', + }; + + expect(() => assertIsComponent(input)).not.toThrow(); + }); + + it('does not throw for an input component without optional fields', () => { + const input: Input = { + type: NodeType.Input, + name: 'myInput', + value: undefined, + inputType: undefined, + placeholder: undefined, + label: undefined, + }; + + expect(() => assertIsComponent(input)).not.toThrow(); + }); + + it('does not throw for a form component', () => { + const form: Form = { + type: NodeType.Form, + name: 'myForm', + children: [ + { + type: NodeType.Input, + name: 'myInput', + inputType: undefined, + placeholder: undefined, + value: undefined, + label: undefined, + }, + ], + }; + + expect(isComponent(form)).toBe(true); + }); + it.each([ true, false, diff --git a/packages/snaps-sdk/src/ui/components/button.test.ts b/packages/snaps-sdk/src/ui/components/button.test.ts new file mode 100644 index 0000000000..70ee5e5be5 --- /dev/null +++ b/packages/snaps-sdk/src/ui/components/button.test.ts @@ -0,0 +1,52 @@ +import { NodeType } from '../nodes'; +import { ButtonType, ButtonVariant, button } from './button'; + +describe('button', () => { + it('creates a button component', () => { + expect( + button({ + variant: ButtonVariant.Primary, + value: 'Hello, world!', + name: 'myButton', + buttonType: ButtonType.Button, + }), + ).toStrictEqual({ + type: NodeType.Button, + variant: ButtonVariant.Primary, + buttonType: ButtonType.Button, + name: 'myButton', + value: 'Hello, world!', + }); + + expect( + button({ + value: 'Hello, world!', + }), + ).toStrictEqual({ + type: NodeType.Button, + value: 'Hello, world!', + }); + }); + + it('creates a button component using the shorthand form', () => { + expect( + button( + 'Hello, world!', + ButtonType.Button, + 'myButton', + ButtonVariant.Primary, + ), + ).toStrictEqual({ + type: NodeType.Button, + value: 'Hello, world!', + buttonType: ButtonType.Button, + variant: ButtonVariant.Primary, + name: 'myButton', + }); + + expect(button('foo bar')).toStrictEqual({ + type: NodeType.Button, + value: 'foo bar', + }); + }); +}); diff --git a/packages/snaps-sdk/src/ui/components/button.ts b/packages/snaps-sdk/src/ui/components/button.ts new file mode 100644 index 0000000000..4d92771032 --- /dev/null +++ b/packages/snaps-sdk/src/ui/components/button.ts @@ -0,0 +1,68 @@ +import type { Infer } from 'superstruct'; +import { assign, literal, object, optional, string, union } from 'superstruct'; + +import { enumValue } from '../../internals'; +import { createBuilder } from '../builder'; +import { LiteralStruct, NodeType } from '../nodes'; + +export enum ButtonVariant { + Primary = 'primary', + Secondary = 'secondary', +} + +export enum ButtonType { + Button = 'button', + Submit = 'submit', +} + +export const ButtonStruct = assign( + LiteralStruct, + object({ + type: literal(NodeType.Button), + value: string(), + variant: optional( + union([ + enumValue(ButtonVariant.Primary), + enumValue(ButtonVariant.Secondary), + ]), + ), + buttonType: optional( + union([enumValue(ButtonType.Button), enumValue(ButtonType.Submit)]), + ), + name: optional(string()), + }), +); + +/** + * A button node, that renders either a primary or a secondary button. + * + * @property type - The type of the node, must be the string 'button'. + * @property variant - The style variant of the node, must be either 'primary' or 'secondary'. + * @property value - The text content of the node as plain text. + * @property buttonType - The type of the button, must be either 'button' or 'submit'. + * @property name - An optional name to identify the button. + */ +export type Button = Infer; + +/** + * Create a {@link Button} node. + * + * @param args - The node arguments. This can be either a string, or an object + * with a `value` property. A set of optional properties can be passed. + * @param args.variant - The optional variant of the button. + * @param args.value - The text content of the node. + * @param args.name - The optional name of the button. + * @returns The text node as object. + * @example + * ```typescript + * const node = button({ variant: 'primary', text: 'Hello, world!', name: 'myButton' }); + * const node = button('Hello, world!', 'button', 'myButton', 'primary'); + * const node = button('Hello, world!'); + * ``` + */ +export const button = createBuilder(NodeType.Button, ButtonStruct, [ + 'value', + 'buttonType', + 'name', + 'variant', +]); diff --git a/packages/snaps-sdk/src/ui/components/form.test.ts b/packages/snaps-sdk/src/ui/components/form.test.ts new file mode 100644 index 0000000000..4ec9a11850 --- /dev/null +++ b/packages/snaps-sdk/src/ui/components/form.test.ts @@ -0,0 +1,50 @@ +import { NodeType } from '../nodes'; +import { form } from './form'; +import { input } from './input'; + +describe('Form', () => { + it('creates a form component', () => { + expect( + form({ + name: 'myForm', + children: [input('myInput')], + }), + ).toStrictEqual({ + type: NodeType.Form, + name: 'myForm', + children: [ + { + type: NodeType.Input, + name: 'myInput', + }, + ], + }); + }); + + it('creates a form component using the shorthand form', () => { + expect(form('myForm', [input('myInput')])).toStrictEqual({ + type: NodeType.Form, + name: 'myForm', + children: [ + { + type: NodeType.Input, + name: 'myInput', + }, + ], + }); + }); + + it('validates the args', () => { + expect(() => + // @ts-expect-error - Invalid args. + form({ name: 'foo', children: [input('myInput')], bar: 'baz' }), + ).toThrow( + 'Invalid form component: At path: bar -- Expected a value of type `never`, but received: `"baz"`.', + ); + + // @ts-expect-error - Invalid args. + expect(() => form({})).toThrow( + 'Invalid form component: At path: children -- Expected an array value, but received: undefined.', + ); + }); +}); diff --git a/packages/snaps-sdk/src/ui/components/form.ts b/packages/snaps-sdk/src/ui/components/form.ts new file mode 100644 index 0000000000..833e1c7ecd --- /dev/null +++ b/packages/snaps-sdk/src/ui/components/form.ts @@ -0,0 +1,57 @@ +import type { Infer } from 'superstruct'; +import { array, assign, literal, object, string, union } from 'superstruct'; + +import { createBuilder } from '../builder'; +import { NodeStruct, NodeType } from '../nodes'; +import { ButtonStruct } from './button'; +import { InputStruct } from './input'; + +export const FormComponentStruct = union([InputStruct, ButtonStruct]); + +/** + * The subset of nodes allowed as children in the {@link Form} node. + */ +export type FormComponent = Infer; + +export const FormStruct = assign( + NodeStruct, + object({ + type: literal(NodeType.Form), + children: array(FormComponentStruct), + name: string(), + }), +); + +/** + * A form node that takes children {@link FormComponent} nodes and renders a form. + * + * @property type - The type of the node. Must be the string `form`. + * @property children - The children of the node. Only {@link FormComponent} nodes are allowed. + * @property name - The form name used to identify it. + */ +export type Form = Infer; + +/** + * Create a {@link Form} node. + * + * @param args - The node arguments. This can be either an array of children and a string, or + * an object with a `name` and `children` property. + * @param args.name - The form name used to identify it. + * @param args.children - The child nodes of the form. This can be any valid + * {@link FormComponent}. + * @returns The form node as object. + * @example + * const node = form({ + * name: 'myForm', + * children: [ + * input({ name: 'myInput' }), + * button({ value: 'Hello, world!' }), + * ], + * }); + * + * const node = form('myForm', [input('myInput'), button('Hello, world!')]); + */ +export const form = createBuilder(NodeType.Form, FormStruct, [ + 'name', + 'children', +]); diff --git a/packages/snaps-sdk/src/ui/components/index.ts b/packages/snaps-sdk/src/ui/components/index.ts index fa1e8820dc..3b3b053716 100644 --- a/packages/snaps-sdk/src/ui/components/index.ts +++ b/packages/snaps-sdk/src/ui/components/index.ts @@ -9,3 +9,6 @@ export { ComponentStruct, panel, PanelStruct } from './panel'; export * from './spinner'; export * from './text'; export * from './row'; +export * from './button'; +export * from './input'; +export * from './form'; diff --git a/packages/snaps-sdk/src/ui/components/input.test.ts b/packages/snaps-sdk/src/ui/components/input.test.ts new file mode 100644 index 0000000000..08fbe3d4a3 --- /dev/null +++ b/packages/snaps-sdk/src/ui/components/input.test.ts @@ -0,0 +1,62 @@ +import { NodeType } from '../nodes'; +import { input, InputType } from './input'; + +describe('Input', () => { + it('creates an input component', () => { + expect( + input({ + value: 'Hello, world!', + name: 'myInput', + inputType: InputType.Text, + placeholder: 'Type here...', + label: 'Hello', + }), + ).toStrictEqual({ + type: NodeType.Input, + value: 'Hello, world!', + name: 'myInput', + inputType: InputType.Text, + placeholder: 'Type here...', + label: 'Hello', + }); + + expect( + input({ + name: 'myInput', + }), + ).toStrictEqual({ + type: NodeType.Input, + name: 'myInput', + }); + }); + + it('creates an input component using the shorthand form', () => { + expect( + input('myInput', InputType.Text, 'type here...', 'foo bar', 'input'), + ).toStrictEqual({ + type: NodeType.Input, + inputType: InputType.Text, + placeholder: 'type here...', + value: 'foo bar', + name: 'myInput', + label: 'input', + }); + + expect(input('myInput')).toStrictEqual({ + type: NodeType.Input, + name: 'myInput', + }); + }); + + it('validates the args', () => { + // @ts-expect-error - Invalid args. + expect(() => input({ name: 'foo', bar: 'baz' })).toThrow( + 'Invalid input component: At path: bar -- Expected a value of type `never`, but received: `"baz"`.', + ); + + // @ts-expect-error - Invalid args. + expect(() => input({})).toThrow( + 'Invalid input component: At path: name -- Expected a string, but received: undefined.', + ); + }); +}); diff --git a/packages/snaps-sdk/src/ui/components/input.ts b/packages/snaps-sdk/src/ui/components/input.ts new file mode 100644 index 0000000000..915b24fed5 --- /dev/null +++ b/packages/snaps-sdk/src/ui/components/input.ts @@ -0,0 +1,73 @@ +import type { Infer } from 'superstruct'; +import { assign, literal, object, optional, string, union } from 'superstruct'; + +import { enumValue } from '../../internals'; +import { createBuilder } from '../builder'; +import { LiteralStruct, NodeType } from '../nodes'; + +/** + * This replicates the available input types from the metamask extension. + * https://github.com/MetaMask/metamask-extension/develop/ui/components/component-library/input/input.constants.js + */ +export enum InputType { + /* eslint-disable @typescript-eslint/no-shadow */ + Text = 'text', + Number = 'number', + /* eslint-enable @typescript-eslint/no-shadow */ + Password = 'password', +} + +export const InputStruct = assign( + LiteralStruct, + object({ + type: literal(NodeType.Input), + value: optional(string()), + name: string(), + inputType: optional( + union([ + enumValue(InputType.Text), + enumValue(InputType.Password), + enumValue(InputType.Number), + ]), + ), + placeholder: optional(string()), + label: optional(string()), + }), +); + +/** + * An input node, that renders an input. + * + * @property type - The type of the node, must be the string 'input'. + * @property name - The name for the input. + * @property value - The value of the input. + * @property inputType - An optional type, either `text`, `password` or `number`. + * @property placeholder - An optional input placeholder. + * @property label - An optional input label. + */ +export type Input = Infer; + +/** + * Create a {@link Input} node. + * + * @param args - The node arguments. This can either be a name and an optional variant, value and placeholder or an object + * with the properties: `inputType`, `value`, `variant`, `placeholder` and `name`. + * @param args.name - The name for the input. + * @param args.value - The value of the input. + * @param args.inputType - An optional type, either `text`, `password` or `number`. + * @param args.placeholder - An optional input placeholder. + * @param args.label - An optional input label. + * @returns The input node as an object. + * @example + * const node = input('myInput'); + * const node = input('myInput', InputType.Text, 'my placeholder', 'myValue', 'myLabel'); + * const node = input({ name: 'myInput' }); + * const node = input({name: 'myInput', value: 'myValue', inputType: InputType.Password, placeholder: 'placeholder'}) + */ +export const input = createBuilder(NodeType.Input, InputStruct, [ + 'name', + 'inputType', + 'placeholder', + 'value', + 'label', +]); diff --git a/packages/snaps-sdk/src/ui/components/panel.ts b/packages/snaps-sdk/src/ui/components/panel.ts index c7e9d5e43c..64d98c4856 100644 --- a/packages/snaps-sdk/src/ui/components/panel.ts +++ b/packages/snaps-sdk/src/ui/components/panel.ts @@ -4,10 +4,13 @@ import { array, assign, lazy, literal, object, union } from 'superstruct'; import { createBuilder } from '../builder'; import { NodeStruct, NodeType } from '../nodes'; import { AddressStruct } from './address'; +import { ButtonStruct } from './button'; import { CopyableStruct } from './copyable'; import { DividerStruct } from './divider'; +import { FormStruct } from './form'; import { HeadingStruct } from './heading'; import { ImageStruct } from './image'; +import { InputStruct } from './input'; import { RowStruct } from './row'; import { SpinnerStruct } from './spinner'; import { TextStruct } from './text'; @@ -91,6 +94,9 @@ export const ComponentStruct = union([ TextStruct, RowStruct, AddressStruct, + InputStruct, + FormStruct, + ButtonStruct, ]); /** diff --git a/packages/snaps-sdk/src/ui/nodes.ts b/packages/snaps-sdk/src/ui/nodes.ts index dc7a4557b3..6daa430153 100644 --- a/packages/snaps-sdk/src/ui/nodes.ts +++ b/packages/snaps-sdk/src/ui/nodes.ts @@ -17,6 +17,9 @@ export enum NodeType { Image = 'image', Row = 'row', Address = 'address', + Button = 'button', + Input = 'input', + Form = 'form', } /** diff --git a/packages/snaps-sdk/tsconfig.json b/packages/snaps-sdk/tsconfig.json index 7d6092a4d3..c3cd554d5a 100644 --- a/packages/snaps-sdk/tsconfig.json +++ b/packages/snaps-sdk/tsconfig.json @@ -3,5 +3,5 @@ "compilerOptions": { "baseUrl": "./" }, - "include": ["./src"] + "include": ["./src", "package.json", "tsup.config.ts"] } diff --git a/packages/snaps-sdk/tsup.config.ts b/packages/snaps-sdk/tsup.config.ts new file mode 100644 index 0000000000..3eaf645296 --- /dev/null +++ b/packages/snaps-sdk/tsup.config.ts @@ -0,0 +1,14 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-simulator/package.json b/packages/snaps-simulator/package.json index e6d629b9ff..fb5f1b5808 100644 --- a/packages/snaps-simulator/package.json +++ b/packages/snaps-simulator/package.json @@ -11,24 +11,25 @@ "url": "https://github.com/MetaMask/snaps.git" }, "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/src/**", - "dist/webpack/**" + "dist" ], "scripts": { - "build": "yarn build:source && yarn build:types && yarn build:webpack", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types && yarn build:webpack", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", "build:webpack": "yarn webpack --config-name main --config-name test --mode production --progress", "build:vendor": "webpack --config-name vendor --mode production --progress", - "build:clean": "rimraf dist && yarn build", "build:post-tsc": "yarn build:vendor && yarn build:webpack", "lint": "yarn lint:eslint && yarn lint:misc --check && yarn lint:changelog && yarn lint:dependencies", "lint:changelog": "../../scripts/validate-changelog.sh @metamask/snaps-simulator", @@ -91,7 +92,6 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@redux-saga/is": "^1.1.3", "@redux-saga/symbols": "^1.1.3", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@testing-library/react": "^14.0.0", "@types/express": "^4.17.17", @@ -140,6 +140,7 @@ "terser-webpack-plugin": "^5.3.9", "ts-node": "^10.9.1", "tsconfig-paths-webpack-plugin": "^4.0.1", + "tsup": "^8.0.1", "typescript": "~4.8.4", "webpack": "^5.88.0", "webpack-cli": "^5.1.4", @@ -153,5 +154,6 @@ "access": "public", "registry": "https://registry.npmjs.org/" }, + "build:types": "tsc --project tsconfig.build.json", "packageManager": "yarn@3.2.1" } diff --git a/packages/snaps-simulator/src/features/renderer/Renderer.tsx b/packages/snaps-simulator/src/features/renderer/Renderer.tsx index b6ee630677..c21a513ccc 100644 --- a/packages/snaps-simulator/src/features/renderer/Renderer.tsx +++ b/packages/snaps-simulator/src/features/renderer/Renderer.tsx @@ -22,6 +22,11 @@ export const components: Partial< [NodeType.Spinner]: Spinner, [NodeType.Text]: Text, [NodeType.Image]: Image, + // @todo: Create a button + // TODO(@guillaumerx): Quick fix to build, update those later + [NodeType.Button]: Text, + [NodeType.Input]: Text, + [NodeType.Form]: Text, }; type RendererProps = { diff --git a/packages/snaps-simulator/tsconfig.json b/packages/snaps-simulator/tsconfig.json index ff5642e437..ab6982a118 100644 --- a/packages/snaps-simulator/tsconfig.json +++ b/packages/snaps-simulator/tsconfig.json @@ -5,7 +5,7 @@ "jsx": "react-jsx", "resolveJsonModule": true }, - "include": ["./src", "webpack.config.ts", "package.json"], + "include": ["./src", "webpack.config.ts", "package.json", "tsup.config.ts"], "references": [ { "path": "../snaps-rpc-methods" }, { "path": "../snaps-controllers" }, diff --git a/packages/snaps-simulator/tsup.config.ts b/packages/snaps-simulator/tsup.config.ts new file mode 100644 index 0000000000..3eaf645296 --- /dev/null +++ b/packages/snaps-simulator/tsup.config.ts @@ -0,0 +1,14 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-utils/coverage.json b/packages/snaps-utils/coverage.json index 8143112d0f..32444bd846 100644 --- a/packages/snaps-utils/coverage.json +++ b/packages/snaps-utils/coverage.json @@ -1,6 +1,6 @@ { "branches": 96.05, - "functions": 98.55, - "lines": 98.62, - "statements": 95.36 + "functions": 98.56, + "lines": 98.63, + "statements": 95.2 } diff --git a/packages/snaps-utils/package.json b/packages/snaps-utils/package.json index f97c2fb94f..80f9aaf2e1 100644 --- a/packages/snaps-utils/package.json +++ b/packages/snaps-utils/package.json @@ -9,39 +9,29 @@ "exports": { ".": { "browser": { - "import": "./dist/esm/index.browser.js", - "require": "./dist/cjs/index.browser.js" + "import": "./dist/index.browser.mjs", + "require": "./dist/index.browser.js" }, - "import": "./dist/esm/index.js", - "require": "./dist/cjs/index.js" + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" }, "./test-utils": { - "import": "./dist/esm/test-utils/index.js", - "require": "./dist/cjs/test-utils/index.js", + "import": "./dist/test-utils/index.mjs", + "require": "./dist/test-utils/index.js", "types": "./dist/types/test-utils/index.d.ts" - } + }, + "./package.json": "./package.json" }, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "main": "./dist/index.js", + "module": "./dist/index.mjs", "browser": { - "./dist/cjs/index.js": "./dist/cjs/index.browser.js", - "./dist/esm/index.js": "./dist/esm/index.browser.js" + "./dist/index.js": "./dist/index.browser.js", + "./dist/index.mjs": "./dist/index.browser.mjs" }, "types": "./dist/types/index.d.ts", - "typesVersions": { - "*": { - "*": [ - "./dist/types/index.d.ts" - ], - "test-utils": [ - "./dist/types/test-utils/index.d.ts" - ] - } - }, "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**" + "dist" ], "scripts": { "test": "rimraf coverage && jest && yarn test:browser && yarn posttest", @@ -53,11 +43,8 @@ "lint": "yarn lint:eslint && yarn lint:misc --check && yarn lint:changelog && yarn lint:dependencies", "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write", "lint:changelog": "../../scripts/validate-changelog.sh @metamask/snaps-utils", - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", "build:clean": "yarn clean && yarn build", "clean": "rimraf '*.tsbuildinfo' 'dist'", "publish:preview": "yarn npm publish --tag preview", @@ -98,7 +85,6 @@ "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", "@metamask/post-message-stream": "^7.0.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", "@types/jest": "^27.5.1", @@ -136,6 +122,7 @@ "prettier-plugin-packagejson": "^2.2.11", "rimraf": "^4.1.2", "ts-node": "^10.9.1", + "tsup": "^8.0.1", "typescript": "~4.8.4", "vite": "^4.3.9", "vite-tsconfig-paths": "^4.0.5", @@ -149,5 +136,6 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" - } + }, + "build:types": "tsc --project tsconfig.build.json" } diff --git a/packages/snaps-utils/src/handler-types.ts b/packages/snaps-utils/src/handler-types.ts index d85f847870..642c201fec 100644 --- a/packages/snaps-utils/src/handler-types.ts +++ b/packages/snaps-utils/src/handler-types.ts @@ -8,6 +8,7 @@ export enum HandlerType { OnNameLookup = 'onNameLookup', OnKeyringRequest = 'onKeyringRequest', OnHomePage = 'onHomePage', + OnUserInput = 'onUserInput', } export type SnapHandler = { diff --git a/packages/snaps-utils/src/handlers.ts b/packages/snaps-utils/src/handlers.ts index f987828c7e..3d8516f8eb 100644 --- a/packages/snaps-utils/src/handlers.ts +++ b/packages/snaps-utils/src/handlers.ts @@ -8,9 +8,17 @@ import type { OnSignatureHandler, OnTransactionHandler, OnUpdateHandler, + OnUserInputHandler, } from '@metamask/snaps-sdk'; import { SeverityLevel, ComponentStruct } from '@metamask/snaps-sdk'; -import { literal, nullable, object, optional } from 'superstruct'; +import { + literal, + nullable, + object, + optional, + string, + union, +} from 'superstruct'; import type { SnapHandler } from './handler-types'; import { HandlerType } from './handler-types'; @@ -85,6 +93,13 @@ export const SNAP_EXPORTS = { return typeof snapExport === 'function'; }, }, + [HandlerType.OnUserInput]: { + type: HandlerType.OnUserInput, + required: true, + validator: (snapExport: unknown): snapExport is OnUserInputHandler => { + return typeof snapExport === 'function'; + }, + }, } as const; export const OnTransactionResponseStruct = nullable( @@ -96,10 +111,18 @@ export const OnTransactionResponseStruct = nullable( export const OnSignatureResponseStruct = OnTransactionResponseStruct; -export const OnHomePageResponseStruct = object({ +export const OnHomePageResponseWithContentStruct = object({ content: ComponentStruct, }); +export const OnHomePageResponseWithIdStruct = object({ + id: string(), +}); + +export const OnHomePageResponseStruct = union([ + OnHomePageResponseWithContentStruct, + OnHomePageResponseWithIdStruct, +]); /** * Utility type for getting the handler function type from a handler type. */ diff --git a/packages/snaps-utils/tsconfig.json b/packages/snaps-utils/tsconfig.json index fd3d195748..0d410cef02 100644 --- a/packages/snaps-utils/tsconfig.json +++ b/packages/snaps-utils/tsconfig.json @@ -3,6 +3,6 @@ "compilerOptions": { "baseUrl": "./" }, - "include": ["./src", "scripts"], + "include": ["./src", "scripts", "package.json", "tsup.config.ts"], "references": [{ "path": "../snaps-sdk" }] } diff --git a/packages/snaps-utils/tsup.config.ts b/packages/snaps-utils/tsup.config.ts new file mode 100644 index 0000000000..c21ce6b3b1 --- /dev/null +++ b/packages/snaps-utils/tsup.config.ts @@ -0,0 +1,15 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, + entry: ['!./src/eval-worker.js'], +}; + +export default deepmerge(baseConfig, config); diff --git a/packages/snaps-webpack-plugin/package.json b/packages/snaps-webpack-plugin/package.json index 883c608d76..a0a337a3d2 100644 --- a/packages/snaps-webpack-plugin/package.json +++ b/packages/snaps-webpack-plugin/package.json @@ -10,13 +10,19 @@ "url": "https://github.com/MetaMask/snaps.git" }, "sideEffects": false, - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/types/index.d.ts" + }, + "./package.json": "./package.json" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", "types": "./dist/types/index.d.ts", "files": [ - "dist/cjs/**", - "dist/esm/**", - "dist/types/**" + "dist" ], "scripts": { "test": "jest && yarn posttest", @@ -27,12 +33,8 @@ "lint": "yarn lint:eslint && yarn lint:misc --check && yarn lint:changelog && yarn lint:dependencies", "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write", "lint:changelog": "../../scripts/validate-changelog.sh @metamask/snaps-webpack-plugin", - "build": "yarn build:source && yarn build:types", - "build:source": "yarn build:esm && yarn build:cjs", + "build": "tsup --clean && yarn build:types", "build:types": "tsc --project tsconfig.build.json", - "build:esm": "swc src --out-dir dist/esm --config-file ../../.swcrc.build.json --config module.type=es6", - "build:cjs": "swc src --out-dir dist/cjs --config-file ../../.swcrc.build.json --config module.type=commonjs", - "build:clean": "yarn clean && yarn build", "clean": "rimraf '*.tsbuildinfo' 'dist'", "publish:preview": "yarn npm publish --tag preview", "lint:ci": "yarn lint", @@ -51,7 +53,6 @@ "@metamask/eslint-config-jest": "^12.1.0", "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", - "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", "@types/jest": "^27.5.1", @@ -74,6 +75,7 @@ "prettier": "^2.7.1", "prettier-plugin-packagejson": "^2.2.11", "rimraf": "^4.1.2", + "tsup": "^8.0.1", "typescript": "~4.8.4", "webpack": "^5.88.0" }, @@ -83,5 +85,6 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" - } + }, + "build:types": "tsc --project tsconfig.build.json" } diff --git a/packages/snaps-webpack-plugin/tsconfig.json b/packages/snaps-webpack-plugin/tsconfig.json index b0bf6cc0b3..7fdd0d0331 100644 --- a/packages/snaps-webpack-plugin/tsconfig.json +++ b/packages/snaps-webpack-plugin/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "baseUrl": "./" }, - "include": ["./src"], + "include": ["./src", "package.json", "tsup.config.ts"], "references": [ { "path": "../snaps-sdk" diff --git a/packages/snaps-webpack-plugin/tsup.config.ts b/packages/snaps-webpack-plugin/tsup.config.ts new file mode 100644 index 0000000000..278ecb28b1 --- /dev/null +++ b/packages/snaps-webpack-plugin/tsup.config.ts @@ -0,0 +1,15 @@ +import deepmerge from 'deepmerge'; +import type { Options } from 'tsup'; + +import packageJson from './package.json'; + +// `tsup.config.ts` is not under `rootDir`, so we need to use `require` instead. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const { default: baseConfig } = require('../../tsup.config'); + +const config: Options = { + name: packageJson.name, + external: ['@swc/core', '@swc/wasm', 'pnpapi', 'uglify-js', 'webpack'], +}; + +export default deepmerge(baseConfig, config); diff --git a/tsconfig.json b/tsconfig.json index 7842684d4f..39d06b3601 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,5 +19,5 @@ "resolveJsonModule": true }, "files": [], - "include": ["scripts"] + "include": ["scripts", "tsup.config.ts"] } diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000000..785addc8d6 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,43 @@ +import type { Options } from 'tsup'; + +const config: Options = { + // The entry to bundle. + entry: [ + 'src/**/*.ts', + '!src/**/__fixtures__/**/*', + '!src/**/__mocks__/**/*', + '!src/**/__test__/**/*', + '!src/**/__tests__/**/*', + '!src/**/__snapshots__/**/*', + '!src/**/test-utils/**/*', + '!src/**/*.test.ts', + '!src/**/*.test-d.ts', + '!src/**/*.test.*.ts', + ], + + // The output formats. We want to generate both CommonJS and ESM bundles. + // https://tsup.egoist.dev/#bundle-formats + format: ['cjs', 'esm'], + + // Generate sourcemaps as separate files. + // https://tsup.egoist.dev/#generate-sourcemap-file + sourcemap: true, + + // Clean the dist folder before bundling. + clean: true, + + // Enables shimming of `__dirname` and `import.meta.url`, so that they work + // in both CommonJS and ESM. + // https://tsup.egoist.dev/#inject-cjs-and-esm-shims + shims: true, + + // Hide unnecessary logs from the console. Warnings and errors will still be + // shown. + silent: true, + + // Split the output into chunks. This is useful for tree-shaking. + // https://tsup.egoist.dev/#code-splitting + splitting: true, +}; + +export default config; diff --git a/yarn.lock b/yarn.lock index 8e426f7b36..e2b683cd98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2864,6 +2864,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/aix-ppc64@npm:0.19.11" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm64@npm:0.18.20" @@ -2871,6 +2878,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/android-arm64@npm:0.19.11" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm@npm:0.18.20" @@ -2878,6 +2892,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/android-arm@npm:0.19.11" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-x64@npm:0.18.20" @@ -2885,6 +2906,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/android-x64@npm:0.19.11" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-arm64@npm:0.18.20" @@ -2892,6 +2920,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/darwin-arm64@npm:0.19.11" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-x64@npm:0.18.20" @@ -2899,6 +2934,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/darwin-x64@npm:0.19.11" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-arm64@npm:0.18.20" @@ -2906,6 +2948,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/freebsd-arm64@npm:0.19.11" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-x64@npm:0.18.20" @@ -2913,6 +2962,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/freebsd-x64@npm:0.19.11" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm64@npm:0.18.20" @@ -2920,6 +2976,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/linux-arm64@npm:0.19.11" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm@npm:0.18.20" @@ -2927,6 +2990,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/linux-arm@npm:0.19.11" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ia32@npm:0.18.20" @@ -2934,6 +3004,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/linux-ia32@npm:0.19.11" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.14.54": version: 0.14.54 resolution: "@esbuild/linux-loong64@npm:0.14.54" @@ -2948,6 +3025,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/linux-loong64@npm:0.19.11" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-mips64el@npm:0.18.20" @@ -2955,6 +3039,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/linux-mips64el@npm:0.19.11" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ppc64@npm:0.18.20" @@ -2962,6 +3053,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/linux-ppc64@npm:0.19.11" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-riscv64@npm:0.18.20" @@ -2969,6 +3067,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/linux-riscv64@npm:0.19.11" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-s390x@npm:0.18.20" @@ -2976,6 +3081,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/linux-s390x@npm:0.19.11" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-x64@npm:0.18.20" @@ -2983,6 +3095,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/linux-x64@npm:0.19.11" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/netbsd-x64@npm:0.18.20" @@ -2990,6 +3109,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/netbsd-x64@npm:0.19.11" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/openbsd-x64@npm:0.18.20" @@ -2997,6 +3123,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/openbsd-x64@npm:0.19.11" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/sunos-x64@npm:0.18.20" @@ -3004,6 +3137,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/sunos-x64@npm:0.19.11" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-arm64@npm:0.18.20" @@ -3011,6 +3151,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/win32-arm64@npm:0.19.11" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-ia32@npm:0.18.20" @@ -3018,6 +3165,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/win32-ia32@npm:0.19.11" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-x64@npm:0.18.20" @@ -3025,6 +3179,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.19.11": + version: 0.19.11 + resolution: "@esbuild/win32-x64@npm:0.19.11" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint/eslintrc@npm:^1.3.3": version: 1.3.3 resolution: "@eslint/eslintrc@npm:1.3.3" @@ -4033,7 +4194,6 @@ __metadata: "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/snaps-utils": "workspace:^" "@metamask/utils": ^8.3.0 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/jest": ^27.5.1 @@ -4060,10 +4220,11 @@ __metadata: rimraf: ^4.1.2 ts-node: ^10.9.1 tsc-watch: ^4.5.0 + tsup: ^8.0.1 typescript: ~4.8.4 yargs: ^17.7.1 bin: - create-snap: ./dist/cjs/main.js + create-snap: ./dist/main.js languageName: unknown linkType: soft @@ -5115,7 +5276,6 @@ __metadata: "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/snaps-utils": "workspace:^" - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/browserify": ^12.0.37 @@ -5143,6 +5303,7 @@ __metadata: prettier-plugin-packagejson: ^2.2.11 readable-stream: ^3.6.2 rimraf: ^4.1.2 + tsup: ^8.0.1 typescript: ~4.8.4 languageName: unknown linkType: soft @@ -5169,7 +5330,6 @@ __metadata: "@metamask/snaps-utils": "workspace:^" "@metamask/snaps-webpack-plugin": "workspace:^" "@metamask/utils": ^8.3.0 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/browserify": ^12.0.37 @@ -5186,7 +5346,6 @@ __metadata: browserify-zlib: ^0.2.0 buffer: ^6.0.3 chalk: ^4.1.2 - chokidar: ^3.5.2 console-browserify: ^1.2.0 constants-browserify: ^1.0.0 cross-fetch: ^3.1.5 @@ -5229,6 +5388,7 @@ __metadata: timers-browserify: ^2.0.12 ts-node: ^10.9.1 tsc-watch: ^4.5.0 + tsup: ^8.0.1 tty-browserify: ^0.0.1 typescript: ~4.8.4 url: ^0.11.1 @@ -5238,7 +5398,7 @@ __metadata: webpack-merge: ^5.9.0 yargs: ^17.7.1 bin: - mm-snap: ./dist/cjs/main.js + mm-snap: ./dist/main.js languageName: unknown linkType: soft @@ -5268,7 +5428,6 @@ __metadata: "@metamask/snaps-utils": "workspace:^" "@metamask/template-snap": ^0.7.0 "@metamask/utils": ^8.3.0 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/chrome": ^0.0.237 @@ -5319,6 +5478,7 @@ __metadata: rimraf: ^4.1.2 tar-stream: ^3.1.7 ts-node: ^10.9.1 + tsup: ^8.0.1 typescript: ~4.8.4 vite: ^4.3.9 vite-tsconfig-paths: ^4.0.5 @@ -5358,7 +5518,6 @@ __metadata: "@metamask/snaps-sdk": "workspace:^" "@metamask/snaps-utils": "workspace:^" "@metamask/utils": ^8.3.0 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/express": ^4.17.17 @@ -5405,6 +5564,7 @@ __metadata: superstruct: ^1.0.3 terser: ^5.17.7 ts-node: ^10.9.1 + tsup: ^8.0.1 typescript: ~4.8.4 vite: ^4.3.9 vite-tsconfig-paths: ^4.0.5 @@ -5442,7 +5602,6 @@ __metadata: "@metamask/snaps-utils": "workspace:^" "@metamask/utils": ^8.3.0 "@reduxjs/toolkit": ^1.9.5 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/jest": ^27.5.1 @@ -5471,6 +5630,7 @@ __metadata: redux-saga: ^1.2.3 rimraf: ^4.1.2 superstruct: ^1.0.3 + tsup: ^8.0.1 typescript: ~4.8.4 languageName: unknown linkType: soft @@ -5499,7 +5659,6 @@ __metadata: "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/snaps-utils": "workspace:^" "@rollup/plugin-virtual": ^2.1.0 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/jest": ^27.5.1 @@ -5522,6 +5681,7 @@ __metadata: prettier-plugin-packagejson: ^2.2.11 rimraf: ^4.1.2 rollup: ^2.73.0 + tsup: ^8.0.1 typescript: ~4.8.4 languageName: unknown linkType: soft @@ -5545,7 +5705,6 @@ __metadata: "@metamask/snaps-utils": "workspace:^" "@metamask/utils": ^8.3.0 "@noble/hashes": ^1.3.1 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/node": 18.14.2 @@ -5567,6 +5726,7 @@ __metadata: prettier-plugin-packagejson: ^2.2.11 rimraf: ^4.1.2 superstruct: ^1.0.3 + tsup: ^8.0.1 typescript: ~4.8.4 languageName: unknown linkType: soft @@ -5585,7 +5745,6 @@ __metadata: "@metamask/providers": ^14.0.2 "@metamask/rpc-errors": ^6.1.0 "@metamask/utils": ^8.3.0 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@types/jest": ^27.5.1 "@typescript-eslint/eslint-plugin": ^5.42.1 @@ -5610,6 +5769,7 @@ __metadata: rimraf: ^4.1.2 superstruct: ^1.0.3 ts-jest: ^29.1.1 + tsup: ^8.0.1 typescript: ~4.8.4 languageName: unknown linkType: soft @@ -5646,7 +5806,6 @@ __metadata: "@redux-saga/is": ^1.1.3 "@redux-saga/symbols": ^1.1.3 "@reduxjs/toolkit": ^1.9.5 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@testing-library/react": ^14.0.0 "@types/express": ^4.17.17 @@ -5710,6 +5869,7 @@ __metadata: terser-webpack-plugin: ^5.3.9 ts-node: ^10.9.1 tsconfig-paths-webpack-plugin: ^4.0.1 + tsup: ^8.0.1 typescript: ~4.8.4 webpack: ^5.88.0 webpack-cli: ^5.1.4 @@ -5743,7 +5903,6 @@ __metadata: "@metamask/utils": ^8.3.0 "@noble/hashes": ^1.3.1 "@scure/base": ^1.1.1 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/jest": ^27.5.1 @@ -5790,6 +5949,7 @@ __metadata: ses: ^1.1.0 superstruct: ^1.0.3 ts-node: ^10.9.1 + tsup: ^8.0.1 typescript: ~4.8.4 validate-npm-package-name: ^5.0.0 vite: ^4.3.9 @@ -5813,7 +5973,6 @@ __metadata: "@metamask/snaps-sdk": "workspace:^" "@metamask/snaps-utils": "workspace:^" "@metamask/utils": ^8.3.0 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 "@types/jest": ^27.5.1 @@ -5836,6 +5995,7 @@ __metadata: prettier: ^2.7.1 prettier-plugin-packagejson: ^2.2.11 rimraf: ^4.1.2 + tsup: ^8.0.1 typescript: ~4.8.4 webpack: ^5.88.0 webpack-sources: ^3.2.3 @@ -6071,22 +6231,6 @@ __metadata: languageName: node linkType: hard -"@mole-inc/bin-wrapper@npm:^8.0.1": - version: 8.0.1 - resolution: "@mole-inc/bin-wrapper@npm:8.0.1" - dependencies: - bin-check: ^4.1.0 - bin-version-check: ^5.0.0 - content-disposition: ^0.5.4 - ext-name: ^5.0.0 - file-type: ^17.1.6 - filenamify: ^5.0.2 - got: ^11.8.5 - os-filter-obj: ^2.0.0 - checksum: 696bf652d37732d46d6b48c5a741f07958c2144df5b4a4bef3a235582e84e3687184cfa139c0e12443d03f123869819a0b7f2a7b0edc23daaebb94d70cc2e56c - languageName: node - linkType: hard - "@motionone/animation@npm:^10.12.0": version: 10.15.1 resolution: "@motionone/animation@npm:10.15.1" @@ -6645,6 +6789,97 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.9.6" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-android-arm64@npm:4.9.6" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-darwin-arm64@npm:4.9.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-darwin-x64@npm:4.9.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.9.6" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.9.6" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.9.6" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.9.6" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.9.6" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.9.6" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.9.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.9.6" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.9.6": + version: 4.9.6 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.9.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@scure/base@npm:^1.0.0, @scure/base@npm:^1.1.1, @scure/base@npm:^1.1.3, @scure/base@npm:~1.1.0, @scure/base@npm:~1.1.3": version: 1.1.3 resolution: "@scure/base@npm:1.1.3" @@ -6714,30 +6949,6 @@ __metadata: languageName: node linkType: hard -"@swc/cli@npm:^0.1.62": - version: 0.1.62 - resolution: "@swc/cli@npm:0.1.62" - dependencies: - "@mole-inc/bin-wrapper": ^8.0.1 - commander: ^7.1.0 - fast-glob: ^3.2.5 - semver: ^7.3.8 - slash: 3.0.0 - source-map: ^0.7.3 - peerDependencies: - "@swc/core": ^1.2.66 - chokidar: ^3.5.1 - peerDependenciesMeta: - chokidar: - optional: true - bin: - spack: bin/spack.js - swc: bin/swc.js - swcx: bin/swcx.js - checksum: d44e88a724ba32d4f63856f15899f8eff78d90d0c5452e4882412307bf6353a64b25e7dc9992fd6e3975c87e6c0d7b494d859a0d8badb9f3c6103623ca89ddc2 - languageName: node - linkType: hard - "@swc/core-darwin-arm64@npm:1.3.78": version: 1.3.78 resolution: "@swc/core-darwin-arm64@npm:1.3.78" @@ -6919,13 +7130,6 @@ __metadata: languageName: node linkType: hard -"@tokenizer/token@npm:^0.3.0": - version: 0.3.0 - resolution: "@tokenizer/token@npm:0.3.0" - checksum: 1d575d02d2a9f0c5a4ca5180635ebd2ad59e0f18b42a65f3d04844148b49b3db35cf00b6012a1af2d59c2ab3caca59451c5689f747ba8667ee586ad717ee58e1 - languageName: node - linkType: hard - "@tootallnate/once@npm:2": version: 2.0.0 resolution: "@tootallnate/once@npm:2.0.0" @@ -7135,10 +7339,10 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0": - version: 1.0.1 - resolution: "@types/estree@npm:1.0.1" - checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d +"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": + version: 1.0.5 + resolution: "@types/estree@npm:1.0.5" + checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a languageName: node linkType: hard @@ -8728,6 +8932,13 @@ __metadata: languageName: node linkType: hard +"any-promise@npm:^1.0.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 0ee8a9bdbe882c90464d75d1f55cf027f5458650c4bd1f0467e65aec38ccccda07ca5844969ee77ed46d04e7dded3eaceb027e8d32f385688523fe305fa7e1de + languageName: node + linkType: hard + "anymatch@npm:^3.0.3, anymatch@npm:~3.1.2": version: 3.1.2 resolution: "anymatch@npm:3.1.2" @@ -8752,13 +8963,6 @@ __metadata: languageName: node linkType: hard -"arch@npm:^2.1.0": - version: 2.2.0 - resolution: "arch@npm:2.2.0" - checksum: e21b7635029fe8e9cdd5a026f9a6c659103e63fff423834323cdf836a1bb240a72d0c39ca8c470f84643385cf581bd8eda2cad8bf493e27e54bd9783abe9101f - languageName: node - linkType: hard - "archiver-utils@npm:^4.0.1": version: 4.0.1 resolution: "archiver-utils@npm:4.0.1" @@ -9296,16 +9500,6 @@ __metadata: languageName: node linkType: hard -"bin-check@npm:^4.1.0": - version: 4.1.0 - resolution: "bin-check@npm:4.1.0" - dependencies: - execa: ^0.7.0 - executable: ^4.1.0 - checksum: 16f6d5d86df9365dab682c7dd238f93678b773a908b3bccea4b1acb82b9b4e49fcfa24c99b99180a8e4cdd89a8f15f03700b09908ed5ae651f52fd82488a3507 - languageName: node - linkType: hard - "bin-links@npm:4.0.1": version: 4.0.1 resolution: "bin-links@npm:4.0.1" @@ -9318,27 +9512,6 @@ __metadata: languageName: node linkType: hard -"bin-version-check@npm:^5.0.0": - version: 5.0.0 - resolution: "bin-version-check@npm:5.0.0" - dependencies: - bin-version: ^6.0.0 - semver: ^7.3.5 - semver-truncate: ^2.0.0 - checksum: 1d3dc92847f8ecd5e07109f5f44727f0cb3b17c00be5ae2a2e105b86bf161bc4e5c10ee2e2c21d5d28e6382994d8416b5e06048191a485be909a1e49a959c3c3 - languageName: node - linkType: hard - -"bin-version@npm:^6.0.0": - version: 6.0.0 - resolution: "bin-version@npm:6.0.0" - dependencies: - execa: ^5.0.0 - find-versions: ^5.0.0 - checksum: 78c29422ea9597eb4c8d4f0eff96df60d09aa82b53a87925bc403efbe5c55251b1a07baac538381d9096377f92d27e3c03963efa86db5bc0d6431b9563946229 - languageName: node - linkType: hard - "binary-extensions@npm:^2.0.0": version: 2.2.0 resolution: "binary-extensions@npm:2.2.0" @@ -9807,6 +9980,17 @@ __metadata: languageName: node linkType: hard +"bundle-require@npm:^4.0.0": + version: 4.0.2 + resolution: "bundle-require@npm:4.0.2" + dependencies: + load-tsconfig: ^0.2.3 + peerDependencies: + esbuild: ">=0.17" + checksum: 13a78ac0aee0f33614c24f2747167c7faebef6c9d1d5453b464fc85fa164a3a3aab657b2b31b7b5d2a088e4958676fef0454328ff7baddd6bfb03a8ff8d8b928 + languageName: node + linkType: hard + "bytes@npm:3.0.0": version: 3.0.0 resolution: "bytes@npm:3.0.0" @@ -9836,6 +10020,13 @@ __metadata: languageName: node linkType: hard +"cac@npm:^6.7.12": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 45a2496a9443abbe7f52a49b22fbe51b1905eff46e03fd5e6c98e3f85077be3f8949685a1849b1a9cd2bc3e5567dfebcf64f01ce01847baf918f1b37c839791a + languageName: node + linkType: hard + "cacache@npm:^16.0.2": version: 16.0.7 resolution: "cacache@npm:16.0.7" @@ -10048,7 +10239,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:3.5.3, chokidar@npm:^3.5.2, chokidar@npm:^3.5.3": +"chokidar@npm:3.5.3, chokidar@npm:^3.5.1, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -10404,10 +10595,10 @@ __metadata: languageName: node linkType: hard -"commander@npm:^7.1.0": - version: 7.2.0 - resolution: "commander@npm:7.2.0" - checksum: 53501cbeee61d5157546c0bef0fedb6cdfc763a882136284bed9a07225f09a14b82d2a84e7637edfd1a679fb35ed9502fd58ef1d091e6287f60d790147f68ddc +"commander@npm:^4.0.0": + version: 4.1.1 + resolution: "commander@npm:4.1.1" + checksum: d7b9913ff92cae20cb577a4ac6fcc121bd6223319e54a40f51a14740a681ad5c574fd29a57da478a5f234a6fa6c52cbf0b7c641353e03c648b1ae85ba670b977 languageName: node linkType: hard @@ -10562,7 +10753,7 @@ __metadata: languageName: node linkType: hard -"content-disposition@npm:0.5.4, content-disposition@npm:^0.5.4": +"content-disposition@npm:0.5.4": version: 0.5.4 resolution: "content-disposition@npm:0.5.4" dependencies: @@ -10786,17 +10977,6 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^5.0.1": - version: 5.1.0 - resolution: "cross-spawn@npm:5.1.0" - dependencies: - lru-cache: ^4.0.1 - shebang-command: ^1.2.0 - which: ^1.2.9 - checksum: 726939c9954fc70c20e538923feaaa33bebc253247d13021737c3c7f68cdc3e0a57f720c0fe75057c0387995349f3f12e20e9bfdbf12274db28019c7ea4ec166 - languageName: node - linkType: hard - "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -12166,6 +12346,86 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.19.2": + version: 0.19.11 + resolution: "esbuild@npm:0.19.11" + dependencies: + "@esbuild/aix-ppc64": 0.19.11 + "@esbuild/android-arm": 0.19.11 + "@esbuild/android-arm64": 0.19.11 + "@esbuild/android-x64": 0.19.11 + "@esbuild/darwin-arm64": 0.19.11 + "@esbuild/darwin-x64": 0.19.11 + "@esbuild/freebsd-arm64": 0.19.11 + "@esbuild/freebsd-x64": 0.19.11 + "@esbuild/linux-arm": 0.19.11 + "@esbuild/linux-arm64": 0.19.11 + "@esbuild/linux-ia32": 0.19.11 + "@esbuild/linux-loong64": 0.19.11 + "@esbuild/linux-mips64el": 0.19.11 + "@esbuild/linux-ppc64": 0.19.11 + "@esbuild/linux-riscv64": 0.19.11 + "@esbuild/linux-s390x": 0.19.11 + "@esbuild/linux-x64": 0.19.11 + "@esbuild/netbsd-x64": 0.19.11 + "@esbuild/openbsd-x64": 0.19.11 + "@esbuild/sunos-x64": 0.19.11 + "@esbuild/win32-arm64": 0.19.11 + "@esbuild/win32-ia32": 0.19.11 + "@esbuild/win32-x64": 0.19.11 + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: ae949a796d1d06b55275ae7491ce137857468f69a93d8cc9c0943d2a701ac54e14dbb250a2ba56f2ad98283669578f1ec3bd85a4681910a5ff29a2470c3bd62c + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -12736,21 +12996,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:^0.7.0": - version: 0.7.0 - resolution: "execa@npm:0.7.0" - dependencies: - cross-spawn: ^5.0.1 - get-stream: ^3.0.0 - is-stream: ^1.1.0 - npm-run-path: ^2.0.0 - p-finally: ^1.0.0 - signal-exit: ^3.0.0 - strip-eof: ^1.0.0 - checksum: dd70206d74b7217bf678ec9f04dddedc82f425df4c1d70e34c9f429d630ec407819e4bd42e3af2618981a4a3a1be000c9b651c0637be486cdab985160c20337c - languageName: node - linkType: hard - "execa@npm:^5.0.0, execa@npm:^5.1.1": version: 5.1.1 resolution: "execa@npm:5.1.1" @@ -12785,15 +13030,6 @@ __metadata: languageName: node linkType: hard -"executable@npm:^4.1.0": - version: 4.1.1 - resolution: "executable@npm:4.1.1" - dependencies: - pify: ^2.2.0 - checksum: f01927ce59bccec804e171bf859a26e362c1f50aa9ebc69f7cafdcce3859d29d4b6267fd47237c18b0a1830614bd3f0ee14b7380d9bad18a4e7af9b5f0b6984f - languageName: node - linkType: hard - "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -12894,25 +13130,6 @@ __metadata: languageName: node linkType: hard -"ext-list@npm:^2.0.0": - version: 2.2.2 - resolution: "ext-list@npm:2.2.2" - dependencies: - mime-db: ^1.28.0 - checksum: 9b2426bea312e674eeced62c5f18407ab9a8653bbdfbde36492331c7973dab7fbf9e11d6c38605786168b42da333910314988097ca06eee61f1b9b57efae3f18 - languageName: node - linkType: hard - -"ext-name@npm:^5.0.0": - version: 5.0.0 - resolution: "ext-name@npm:5.0.0" - dependencies: - ext-list: ^2.0.0 - sort-keys-length: ^1.0.0 - checksum: f598269bd5de4295540ea7d6f8f6a01d82a7508f148b7700a05628ef6121648d26e6e5e942049e953b3051863df6b54bd8fe951e7877f185e34ace5d44370b33 - languageName: node - linkType: hard - "extend@npm:^3.0.0": version: 3.0.2 resolution: "extend@npm:3.0.2" @@ -12986,7 +13203,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.5, fast-glob@npm:^3.2.9": +"fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.11, fast-glob@npm:^3.2.9": version: 3.2.12 resolution: "fast-glob@npm:3.2.12" dependencies: @@ -13141,17 +13358,6 @@ __metadata: languageName: node linkType: hard -"file-type@npm:^17.1.6": - version: 17.1.6 - resolution: "file-type@npm:17.1.6" - dependencies: - readable-web-to-node-stream: ^3.0.2 - strtok3: ^7.0.0-alpha.9 - token-types: ^5.0.0-alpha.2 - checksum: 797e0d155ecaf4b575d4569a0188bfed85af19d18cf3d93ec8bb66d797172a1fde9f13d56135c6a0b471cacd7ecc1adb0c9a45c6e3a19436f682a275d0be16cc - languageName: node - linkType: hard - "file-uri-to-path@npm:1.0.0": version: 1.0.0 resolution: "file-uri-to-path@npm:1.0.0" @@ -13168,24 +13374,6 @@ __metadata: languageName: node linkType: hard -"filename-reserved-regex@npm:^3.0.0": - version: 3.0.0 - resolution: "filename-reserved-regex@npm:3.0.0" - checksum: 1803e19ce64d7cb88ee5a1bd3ce282470a5c263987269222426d889049fc857e302284fa71937de9582eba7a9f39539557d45e0562f2fa51cade8efc68c65dd9 - languageName: node - linkType: hard - -"filenamify@npm:^5.0.2": - version: 5.1.1 - resolution: "filenamify@npm:5.1.1" - dependencies: - filename-reserved-regex: ^3.0.0 - strip-outer: ^2.0.0 - trim-repeated: ^2.0.0 - checksum: 55a7ed0858eb2655bb1bb1e945a59e3fb30ba4767f6924fa064ccd731bff07678aac3cb4f3899ae0e1621fe81d6472b5688232bb6afd4eeb989ade785fc1c6f1 - languageName: node - linkType: hard - "fill-range@npm:^7.0.1": version: 7.0.1 resolution: "fill-range@npm:7.0.1" @@ -13276,15 +13464,6 @@ __metadata: languageName: node linkType: hard -"find-versions@npm:^5.0.0": - version: 5.1.0 - resolution: "find-versions@npm:5.1.0" - dependencies: - semver-regex: ^4.0.5 - checksum: 680bdb0081f631f7bfb6f0f8edcfa0b74ab8cabc82097a4527a37b0d042aabc56685bf459ff27991eab0baddc04eb8e3bba8a2869f5004ecf7cdd2779b6e51de - languageName: node - linkType: hard - "findup-sync@npm:^5.0.0": version: 5.0.0 resolution: "findup-sync@npm:5.0.0" @@ -13711,13 +13890,6 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^3.0.0": - version: 3.0.0 - resolution: "get-stream@npm:3.0.0" - checksum: 36142f46005ed74ce3a45c55545ec4e7da8e243554179e345a786baf144e5c4a35fb7bdc49fadfa9f18bd08000589b6fe364abdadfc4e1eb0e1b9914a6bb9c56 - languageName: node - linkType: hard - "get-stream@npm:^5.1.0": version: 5.2.0 resolution: "get-stream@npm:5.2.0" @@ -13823,18 +13995,18 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2": - version: 10.2.6 - resolution: "glob@npm:10.2.6" +"glob@npm:^10.2.2, glob@npm:^10.3.10": + version: 10.3.10 + resolution: "glob@npm:10.3.10" dependencies: foreground-child: ^3.1.0 - jackspeak: ^2.0.3 + jackspeak: ^2.3.5 minimatch: ^9.0.1 - minipass: ^5.0.0 || ^6.0.2 - path-scurry: ^1.7.0 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + path-scurry: ^1.10.1 bin: - glob: dist/cjs/src/bin.js - checksum: 94c5964bfa9df95207a69a3bd9b07b99ea7b5ba1f36dd73a8914378cee9436a205b9b5bdff58872abc238684ea7f4b4936e932155b8885250818bcc8d5321ddf + glob: dist/esm/bin.mjs + checksum: 4f2fe2511e157b5a3f525a54092169a5f92405f24d2aed3142f4411df328baca13059f4182f1db1bf933e2c69c0bd89e57ae87edd8950cba8c7ccbe84f721cf3 languageName: node linkType: hard @@ -13935,7 +14107,7 @@ __metadata: languageName: node linkType: hard -"globby@npm:^11.1.0": +"globby@npm:^11.0.3, globby@npm:^11.1.0": version: 11.1.0 resolution: "globby@npm:11.1.0" dependencies: @@ -14987,13 +15159,6 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^1.0.0": - version: 1.1.0 - resolution: "is-plain-obj@npm:1.1.0" - checksum: 0ee04807797aad50859652a7467481816cbb57e5cc97d813a7dcd8915da8195dc68c436010bf39d195226cde6a2d352f4b815f16f26b7bf486a5754290629931 - languageName: node - linkType: hard - "is-plain-obj@npm:^3.0.0": version: 3.0.0 resolution: "is-plain-obj@npm:3.0.0" @@ -15059,13 +15224,6 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^1.1.0": - version: 1.1.0 - resolution: "is-stream@npm:1.1.0" - checksum: 063c6bec9d5647aa6d42108d4c59723d2bd4ae42135a2d4db6eadbd49b7ea05b750fd69d279e5c7c45cf9da753ad2c00d8978be354d65aa9f6bb434969c6a2ae - languageName: node - linkType: hard - "is-stream@npm:^2.0.0": version: 2.0.0 resolution: "is-stream@npm:2.0.0" @@ -15281,16 +15439,16 @@ __metadata: languageName: node linkType: hard -"jackspeak@npm:^2.0.3": - version: 2.2.1 - resolution: "jackspeak@npm:2.2.1" +"jackspeak@npm:^2.3.5": + version: 2.3.6 + resolution: "jackspeak@npm:2.3.6" dependencies: "@isaacs/cliui": ^8.0.2 "@pkgjs/parseargs": ^0.11.0 dependenciesMeta: "@pkgjs/parseargs": optional: true - checksum: e29291c0d0f280a063fa18fbd1e891ab8c2d7519fd34052c0ebde38538a15c603140d60c2c7f432375ff7ee4c5f1c10daa8b2ae19a97c3d4affe308c8360c1df + checksum: 57d43ad11eadc98cdfe7496612f6bbb5255ea69fe51ea431162db302c2a11011642f50cfad57288bd0aea78384a0612b16e131944ad8ecd09d619041c8531b54 languageName: node linkType: hard @@ -15874,6 +16032,13 @@ __metadata: languageName: node linkType: hard +"joycon@npm:^3.0.1": + version: 3.1.1 + resolution: "joycon@npm:3.1.1" + checksum: 8003c9c3fc79c5c7602b1c7e9f7a2df2e9916f046b0dbad862aa589be78c15734d11beb9fe846f5e06138df22cb2ad29961b6a986ba81c4920ce2b15a7f11067 + languageName: node + linkType: hard + "js-sdsl@npm:^4.1.4": version: 4.1.5 resolution: "js-sdsl@npm:4.1.5" @@ -16350,6 +16515,13 @@ __metadata: languageName: node linkType: hard +"lilconfig@npm:^3.0.0": + version: 3.0.0 + resolution: "lilconfig@npm:3.0.0" + checksum: a155f1cd24d324ab20dd6974db9ebcf3fb6f2b60175f7c052d917ff8a746b590bc1ee550f6fc3cb1e8716c8b58304e22fe2193febebc0cf16fa86d85e6f896c5 + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.1.6 resolution: "lines-and-columns@npm:1.1.6" @@ -16429,6 +16601,13 @@ __metadata: languageName: node linkType: hard +"load-tsconfig@npm:^0.2.3": + version: 0.2.5 + resolution: "load-tsconfig@npm:0.2.5" + checksum: 631740833c4a7157bb7b6eeae6e1afb6a6fac7416b7ba91bd0944d5c5198270af2d68bf8347af3cc2ba821adc4d83ef98f66278bd263bc284c863a09ec441503 + languageName: node + linkType: hard + "loader-runner@npm:^4.2.0": version: 4.2.0 resolution: "loader-runner@npm:4.2.0" @@ -16572,6 +16751,13 @@ __metadata: languageName: node linkType: hard +"lodash.sortby@npm:^4.7.0": + version: 4.7.0 + resolution: "lodash.sortby@npm:4.7.0" + checksum: db170c9396d29d11fe9a9f25668c4993e0c1331bcb941ddbd48fb76f492e732add7f2a47cfdf8e9d740fa59ac41bbfaf931d268bc72aab3ab49e9f89354d718c + languageName: node + linkType: hard + "lodash.union@npm:^4.6.0": version: 4.6.0 resolution: "lodash.union@npm:4.6.0" @@ -16663,10 +16849,10 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.1": - version: 10.0.1 - resolution: "lru-cache@npm:10.0.1" - checksum: 06f8d0e1ceabd76bb6f644a26dbb0b4c471b79c7b514c13c6856113879b3bf369eb7b497dad4ff2b7e2636db202412394865b33c332100876d838ad1372f0181 +"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": + version: 10.1.0 + resolution: "lru-cache@npm:10.1.0" + checksum: 58056d33e2500fbedce92f8c542e7c11b50d7d086578f14b7074d8c241422004af0718e08a6eaae8705cee09c77e39a61c1c79e9370ba689b7010c152e6a76ab languageName: node linkType: hard @@ -16705,13 +16891,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^9.1.1": - version: 9.1.1 - resolution: "lru-cache@npm:9.1.1" - checksum: 4d703bb9b66216bbee55ead82a9682820a2b6acbdfca491b235390b1ef1056000a032d56dfb373fdf9ad4492f1fa9d04cc9a05a77f25bd7ce6901d21ad9b68b7 - languageName: node - linkType: hard - "luxon@npm:3.3.0": version: 3.3.0 resolution: "luxon@npm:3.3.0" @@ -16982,7 +17161,7 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2, mime-db@npm:^1.28.0": +"mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2": version: 1.52.0 resolution: "mime-db@npm:1.52.0" checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f @@ -17186,10 +17365,10 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^5.0.0 || ^6.0.2": - version: 6.0.2 - resolution: "minipass@npm:6.0.2" - checksum: d140b91f4ab2e5ce5a9b6c468c0e82223504acc89114c1a120d4495188b81fedf8cade72a9f4793642b4e66672f990f1e0d902dd858485216a07cd3c8a62fac9 +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0": + version: 7.0.4 + resolution: "minipass@npm:7.0.4" + checksum: 87585e258b9488caf2e7acea242fd7856bbe9a2c84a7807643513a338d66f368c7d518200ad7b70a508664d408aa000517647b2930c259a8b1f9f0984f344a21 languageName: node linkType: hard @@ -17421,6 +17600,17 @@ __metadata: languageName: node linkType: hard +"mz@npm:^2.7.0": + version: 2.7.0 + resolution: "mz@npm:2.7.0" + dependencies: + any-promise: ^1.0.0 + object-assign: ^4.0.1 + thenify-all: ^1.0.0 + checksum: 8427de0ece99a07e9faed3c0c6778820d7543e3776f9a84d22cf0ec0a8eb65f6e9aee9c9d353ff9a105ff62d33a9463c6ca638974cc652ee8140cd1e35951c87 + languageName: node + linkType: hard + "n12@npm:0.4.0": version: 0.4.0 resolution: "n12@npm:0.4.0" @@ -17703,15 +17893,6 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^2.0.0": - version: 2.0.2 - resolution: "npm-run-path@npm:2.0.2" - dependencies: - path-key: ^2.0.0 - checksum: acd5ad81648ba4588ba5a8effb1d98d2b339d31be16826a118d50f182a134ac523172101b82eab1d01cb4c2ba358e857d54cfafd8163a1ffe7bd52100b741125 - languageName: node - linkType: hard - "npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -17768,7 +17949,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.1.1": +"object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f @@ -17962,15 +18143,6 @@ __metadata: languageName: node linkType: hard -"os-filter-obj@npm:^2.0.0": - version: 2.0.0 - resolution: "os-filter-obj@npm:2.0.0" - dependencies: - arch: ^2.1.0 - checksum: 08808a109b2dba9be8686cc006e082a0f6595e6d87e2a30e4147cb1d22b62a30a6e5f4fd78226aee76d9158c84db3cea292adec02e6591452e93cb33bf5da877 - languageName: node - linkType: hard - "os-tmpdir@npm:~1.0.2": version: 1.0.2 resolution: "os-tmpdir@npm:1.0.2" @@ -17985,13 +18157,6 @@ __metadata: languageName: node linkType: hard -"p-finally@npm:^1.0.0": - version: 1.0.0 - resolution: "p-finally@npm:1.0.0" - checksum: 93a654c53dc805dd5b5891bab16eb0ea46db8f66c4bfd99336ae929323b1af2b70a8b0654f8f1eae924b2b73d037031366d645f1fd18b3d30cbd15950cc4b1d4 - languageName: node - linkType: hard - "p-limit@npm:^2.0.0, p-limit@npm:^2.2.0": version: 2.3.0 resolution: "p-limit@npm:2.3.0" @@ -18304,13 +18469,6 @@ __metadata: languageName: node linkType: hard -"path-key@npm:^2.0.0": - version: 2.0.1 - resolution: "path-key@npm:2.0.1" - checksum: f7ab0ad42fe3fb8c7f11d0c4f849871e28fbd8e1add65c370e422512fc5887097b9cf34d09c1747d45c942a8c1e26468d6356e2df3f740bf177ab8ca7301ebfd - languageName: node - linkType: hard - "path-key@npm:^3.0.0, path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" @@ -18339,13 +18497,13 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^1.7.0": - version: 1.9.2 - resolution: "path-scurry@npm:1.9.2" +"path-scurry@npm:^1.10.1": + version: 1.10.1 + resolution: "path-scurry@npm:1.10.1" dependencies: - lru-cache: ^9.1.1 - minipass: ^5.0.0 || ^6.0.2 - checksum: 92888dfb68e285043c6d3291c8e971d5d2bc2f5082f4d7b5392896f34be47024c9d0a8b688dd7ae6d125acc424699195474927cb4f00049a9b1ec7c4256fa8e0 + lru-cache: ^9.1.1 || ^10.0.0 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + checksum: e2557cff3a8fb8bc07afdd6ab163a92587884f9969b05bbbaf6fe7379348bfb09af9ed292af12ed32398b15fb443e81692047b786d1eeb6d898a51eb17ed7d90 languageName: node linkType: hard @@ -18410,13 +18568,6 @@ __metadata: languageName: node linkType: hard -"peek-readable@npm:^5.0.0": - version: 5.0.0 - resolution: "peek-readable@npm:5.0.0" - checksum: bef5ceb50586eb42e14efba274ac57ffe97f0ed272df9239ce029f688f495d9bf74b2886fa27847c706a9db33acda4b7d23bbd09a2d21eb4c2a54da915117414 - languageName: node - linkType: hard - "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -18454,7 +18605,7 @@ __metadata: languageName: node linkType: hard -"pify@npm:^2.0.0, pify@npm:^2.2.0": +"pify@npm:^2.0.0": version: 2.3.0 resolution: "pify@npm:2.3.0" checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba @@ -18484,10 +18635,10 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.4": - version: 4.0.5 - resolution: "pirates@npm:4.0.5" - checksum: c9994e61b85260bec6c4fc0307016340d9b0c4f4b6550a957afaaff0c9b1ad58fbbea5cfcf083860a25cb27a375442e2b0edf52e2e1e40e69934e08dcc52d227 +"pirates@npm:^4.0.1, pirates@npm:^4.0.4": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: 46a65fefaf19c6f57460388a5af9ab81e3d7fd0e7bc44ca59d753cb5c4d0df97c6c6e583674869762101836d68675f027d60f841c105d72734df9dfca97cbcc6 languageName: node linkType: hard @@ -18548,6 +18699,24 @@ __metadata: languageName: node linkType: hard +"postcss-load-config@npm:^4.0.1": + version: 4.0.2 + resolution: "postcss-load-config@npm:4.0.2" + dependencies: + lilconfig: ^3.0.0 + yaml: ^2.3.4 + peerDependencies: + postcss: ">=8.0.9" + ts-node: ">=9.0.0" + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + checksum: 7c27dd3801db4eae207a5116fed2db6b1ebb780b40c3dd62a3e57e087093a8e6a14ee17ada729fee903152d6ef4826c6339eb135bee6208e0f3140d7e8090185 + languageName: node + linkType: hard + "postcss-modules-extract-imports@npm:^3.0.0": version: 3.0.0 resolution: "postcss-modules-extract-imports@npm:3.0.0" @@ -20021,6 +20190,60 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.0.2": + version: 4.9.6 + resolution: "rollup@npm:4.9.6" + dependencies: + "@rollup/rollup-android-arm-eabi": 4.9.6 + "@rollup/rollup-android-arm64": 4.9.6 + "@rollup/rollup-darwin-arm64": 4.9.6 + "@rollup/rollup-darwin-x64": 4.9.6 + "@rollup/rollup-linux-arm-gnueabihf": 4.9.6 + "@rollup/rollup-linux-arm64-gnu": 4.9.6 + "@rollup/rollup-linux-arm64-musl": 4.9.6 + "@rollup/rollup-linux-riscv64-gnu": 4.9.6 + "@rollup/rollup-linux-x64-gnu": 4.9.6 + "@rollup/rollup-linux-x64-musl": 4.9.6 + "@rollup/rollup-win32-arm64-msvc": 4.9.6 + "@rollup/rollup-win32-ia32-msvc": 4.9.6 + "@rollup/rollup-win32-x64-msvc": 4.9.6 + "@types/estree": 1.0.5 + fsevents: ~2.3.2 + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: cdc0bdd41ee2d3fe7f01df26f5a85921caf46ffe0ae118b2f3deebdf569e8b1c1800b8eee04960425e67aecbd9ccdd37bcdb92595866adb3968d223a07e9b7e6 + languageName: node + linkType: hard + "root@workspace:.": version: 0.0.0-use.local resolution: "root@workspace:." @@ -20033,7 +20256,6 @@ __metadata: "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/utils": ^8.3.0 - "@swc/cli": ^0.1.62 "@swc/core": 1.3.78 "@types/jest": ^27.5.1 "@types/node": 18.14.2 @@ -20061,6 +20283,7 @@ __metadata: semver: ^7.5.4 simple-git-hooks: ^2.7.0 ts-node: ^10.9.1 + tsup: ^8.0.1 typescript: ~4.8.4 languageName: unknown linkType: soft @@ -20231,22 +20454,6 @@ __metadata: languageName: node linkType: hard -"semver-regex@npm:^4.0.5": - version: 4.0.5 - resolution: "semver-regex@npm:4.0.5" - checksum: b9e5c0573c4a997fb7e6e76321385d254797e86c8dba5e23f3cd8cf8f40b40414097a51514e5fead61dcb88ff10d3676355c01e2040f3c68f6c24bfd2073da2e - languageName: node - linkType: hard - -"semver-truncate@npm:^2.0.0": - version: 2.0.0 - resolution: "semver-truncate@npm:2.0.0" - dependencies: - semver: ^6.0.0 - checksum: 713c2bd49add098c3fd6271091e7c8c13908ab3f052d58a19b68920da9f101d34eb6a0c60ef4bd5fa3c345f001e0df37bb831602082441bb35ba857cac42e0f4 - languageName: node - linkType: hard - "semver@npm:2 || 3 || 4 || 5": version: 5.7.2 resolution: "semver@npm:5.7.2" @@ -20463,15 +20670,6 @@ __metadata: languageName: node linkType: hard -"shebang-command@npm:^1.2.0": - version: 1.2.0 - resolution: "shebang-command@npm:1.2.0" - dependencies: - shebang-regex: ^1.0.0 - checksum: 9eed1750301e622961ba5d588af2212505e96770ec376a37ab678f965795e995ade7ed44910f5d3d3cb5e10165a1847f52d3348c64e146b8be922f7707958908 - languageName: node - linkType: hard - "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -20481,13 +20679,6 @@ __metadata: languageName: node linkType: hard -"shebang-regex@npm:^1.0.0": - version: 1.0.0 - resolution: "shebang-regex@npm:1.0.0" - checksum: 404c5a752cd40f94591dfd9346da40a735a05139dac890ffc229afba610854d8799aaa52f87f7e0c94c5007f2c6af55bdcaeb584b56691926c5eaf41dc8f1372 - languageName: node - linkType: hard - "shebang-regex@npm:^3.0.0": version: 3.0.0 resolution: "shebang-regex@npm:3.0.0" @@ -20513,7 +20704,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -20577,7 +20768,7 @@ __metadata: languageName: node linkType: hard -"slash@npm:3.0.0, slash@npm:^3.0.0": +"slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" checksum: 94a93fff615f25a999ad4b83c9d5e257a7280c90a32a7cb8b4a87996e4babf322e469c42b7f649fd5796edd8687652f3fb452a86dc97a816f01113183393f11c @@ -20680,24 +20871,6 @@ __metadata: languageName: node linkType: hard -"sort-keys-length@npm:^1.0.0": - version: 1.0.1 - resolution: "sort-keys-length@npm:1.0.1" - dependencies: - sort-keys: ^1.0.0 - checksum: f9acac5fb31580a9e3d43b419dc86a1b75e85b79036a084d95dd4d1062b621c9589906588ac31e370a0dd381be46d8dbe900efa306d087ca9c912d7a59b5a590 - languageName: node - linkType: hard - -"sort-keys@npm:^1.0.0": - version: 1.1.2 - resolution: "sort-keys@npm:1.1.2" - dependencies: - is-plain-obj: ^1.0.0 - checksum: 5963fd191a2a185a5ec86f06e47721e8e04713eda43bb04ae60d2a8afb21241553dd5bc9d863ed2bd7c3d541b609b0c8d0e58836b1a3eb6764c09c094bcc8b00 - languageName: node - linkType: hard - "sort-object-keys@npm:^1.1.3": version: 1.1.3 resolution: "sort-object-keys@npm:1.1.3" @@ -20755,6 +20928,15 @@ __metadata: languageName: node linkType: hard +"source-map@npm:0.8.0-beta.0": + version: 0.8.0-beta.0 + resolution: "source-map@npm:0.8.0-beta.0" + dependencies: + whatwg-url: ^7.0.0 + checksum: e94169be6461ab0ac0913313ad1719a14c60d402bd22b0ad96f4a6cffd79130d91ab5df0a5336a326b04d2df131c1409f563c9dc0d21a6ca6239a44b6c8dbd92 + languageName: node + linkType: hard + "source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": version: 0.6.1 resolution: "source-map@npm:0.6.1" @@ -21142,13 +21324,6 @@ __metadata: languageName: node linkType: hard -"strip-eof@npm:^1.0.0": - version: 1.0.0 - resolution: "strip-eof@npm:1.0.0" - checksum: 40bc8ddd7e072f8ba0c2d6d05267b4e0a4800898c3435b5fb5f5a21e6e47dfaff18467e7aa0d1844bb5d6274c3097246595841fbfeb317e541974ee992cac506 - languageName: node - linkType: hard - "strip-final-newline@npm:^2.0.0": version: 2.0.0 resolution: "strip-final-newline@npm:2.0.0" @@ -21186,13 +21361,6 @@ __metadata: languageName: node linkType: hard -"strip-outer@npm:^2.0.0": - version: 2.0.0 - resolution: "strip-outer@npm:2.0.0" - checksum: 14ef9fe861e59a5f1555f1860982ae4edce2edb4ed34ab1b37cb62a8ba2f7c3540cbca6c884eabe4006e6cd729ab5d708a631169dd5b66fda570836e7e3b6589 - languageName: node - linkType: hard - "strnum@npm:^1.0.5": version: 1.0.5 resolution: "strnum@npm:1.0.5" @@ -21200,16 +21368,6 @@ __metadata: languageName: node linkType: hard -"strtok3@npm:^7.0.0-alpha.9": - version: 7.0.0 - resolution: "strtok3@npm:7.0.0" - dependencies: - "@tokenizer/token": ^0.3.0 - peek-readable: ^5.0.0 - checksum: 2ebe7ad8f2aea611dec6742cf6a42e82764892a362907f7ce493faf334501bf981ce21c828dcc300457e6d460dc9c34d644ededb3b01dcb9e37559203cf1748c - languageName: node - linkType: hard - "style-loader@npm:^3.3.2": version: 3.3.3 resolution: "style-loader@npm:3.3.3" @@ -21254,6 +21412,24 @@ __metadata: languageName: node linkType: hard +"sucrase@npm:^3.20.3": + version: 3.35.0 + resolution: "sucrase@npm:3.35.0" + dependencies: + "@jridgewell/gen-mapping": ^0.3.2 + commander: ^4.0.0 + glob: ^10.3.10 + lines-and-columns: ^1.1.6 + mz: ^2.7.0 + pirates: ^4.0.1 + ts-interface-checker: ^0.1.9 + bin: + sucrase: bin/sucrase + sucrase-node: bin/sucrase-node + checksum: 9fc5792a9ab8a14dcf9c47dcb704431d35c1cdff1d17d55d382a31c2e8e3063870ad32ce120a80915498486246d612e30cda44f1624d9d9a10423e1a43487ad1 + languageName: node + linkType: hard + "suffix@npm:^0.1.0": version: 0.1.1 resolution: "suffix@npm:0.1.1" @@ -21474,6 +21650,24 @@ __metadata: languageName: node linkType: hard +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: ">= 3.1.0 < 4" + checksum: dba7cc8a23a154cdcb6acb7f51d61511c37a6b077ec5ab5da6e8b874272015937788402fd271fdfc5f187f8cb0948e38d0a42dcc89d554d731652ab458f5343e + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: ^1.0.0 + checksum: 84e1b804bfec49f3531215f17b4a6e50fd4397b5f7c1bccc427b9c656e1ecfb13ea79d899930184f78bc2f57285c54d9a50a590c8868f4f0cef5c1d9f898b05e + languageName: node + linkType: hard + "through2@npm:2.0.5, through2@npm:^2.0.0": version: 2.0.5 resolution: "through2@npm:2.0.5" @@ -21576,16 +21770,6 @@ __metadata: languageName: node linkType: hard -"token-types@npm:^5.0.0-alpha.2": - version: 5.0.1 - resolution: "token-types@npm:5.0.1" - dependencies: - "@tokenizer/token": ^0.3.0 - ieee754: ^1.2.1 - checksum: 32780123bc6ce8b6a2231d860445c994a02a720abf38df5583ea957aa6626873cd1c4dd8af62314da4cf16ede00c379a765707a3b06f04b8808c38efdae1c785 - languageName: node - linkType: hard - "tough-cookie@npm:^4.1.2": version: 4.1.3 resolution: "tough-cookie@npm:4.1.3" @@ -21598,6 +21782,15 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^1.0.1": + version: 1.0.1 + resolution: "tr46@npm:1.0.1" + dependencies: + punycode: ^2.1.0 + checksum: 96d4ed46bc161db75dbf9247a236ea0bfcaf5758baae6749e92afab0bc5a09cb59af21788ede7e55080f2bf02dce3e4a8f2a484cc45164e29f4b5e68f7cbcc1a + languageName: node + linkType: hard + "tr46@npm:^3.0.0": version: 3.0.0 resolution: "tr46@npm:3.0.0" @@ -21621,12 +21814,12 @@ __metadata: languageName: node linkType: hard -"trim-repeated@npm:^2.0.0": - version: 2.0.0 - resolution: "trim-repeated@npm:2.0.0" - dependencies: - escape-string-regexp: ^5.0.0 - checksum: 4086eb0bc560f3da0370f427f423db4e3fc0a8e1560ecffc3b68512071319fe82dc9dd86d76b981d36ada76d7d49c3f8897ac054c87bc177e7a25abfd29e2bcd +"tree-kill@npm:^1.2.2": + version: 1.2.2 + resolution: "tree-kill@npm:1.2.2" + bin: + tree-kill: cli.js + checksum: 49117f5f410d19c84b0464d29afb9642c863bc5ba40fcb9a245d474c6d5cc64d1b177a6e6713129eb346b40aebb9d4631d967517f9fbe8251c35b21b13cd96c7 languageName: node linkType: hard @@ -21637,6 +21830,13 @@ __metadata: languageName: node linkType: hard +"ts-interface-checker@npm:^0.1.9": + version: 0.1.13 + resolution: "ts-interface-checker@npm:0.1.13" + checksum: 20c29189c2dd6067a8775e07823ddf8d59a33e2ffc47a1bd59a5cb28bb0121a2969a816d5e77eda2ed85b18171aa5d1c4005a6b88ae8499ec7cc49f78571cb5e + languageName: node + linkType: hard + "ts-jest@npm:^29.1.1": version: 29.1.1 resolution: "ts-jest@npm:29.1.1" @@ -21794,6 +21994,45 @@ __metadata: languageName: node linkType: hard +"tsup@npm:^8.0.1": + version: 8.0.1 + resolution: "tsup@npm:8.0.1" + dependencies: + bundle-require: ^4.0.0 + cac: ^6.7.12 + chokidar: ^3.5.1 + debug: ^4.3.1 + esbuild: ^0.19.2 + execa: ^5.0.0 + globby: ^11.0.3 + joycon: ^3.0.1 + postcss-load-config: ^4.0.1 + resolve-from: ^5.0.0 + rollup: ^4.0.2 + source-map: 0.8.0-beta.0 + sucrase: ^3.20.3 + tree-kill: ^1.2.2 + peerDependencies: + "@microsoft/api-extractor": ^7.36.0 + "@swc/core": ^1 + postcss: ^8.4.12 + typescript: ">=4.5.0" + peerDependenciesMeta: + "@microsoft/api-extractor": + optional: true + "@swc/core": + optional: true + postcss: + optional: true + typescript: + optional: true + bin: + tsup: dist/cli-default.js + tsup-node: dist/cli-node.js + checksum: 7b9e7a412247e374be1f22d9aa68eec64e9bdebfdf36ac915fc24be995fc7b855d74cf210431122cec26351e4c22c0b87f0400181b1de1915a80531f4797d84a + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -22697,6 +22936,13 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^4.0.2": + version: 4.0.2 + resolution: "webidl-conversions@npm:4.0.2" + checksum: c93d8dfe908a0140a4ae9c0ebc87a33805b416a33ee638a605b551523eec94a9632165e54632f6d57a39c5f948c4bab10e0e066525e9a4b87a79f0d04fbca374 + languageName: node + linkType: hard + "webidl-conversions@npm:^7.0.0": version: 7.0.0 resolution: "webidl-conversions@npm:7.0.0" @@ -22906,6 +23152,17 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^7.0.0": + version: 7.1.0 + resolution: "whatwg-url@npm:7.1.0" + dependencies: + lodash.sortby: ^4.7.0 + tr46: ^1.0.1 + webidl-conversions: ^4.0.2 + checksum: fecb07c87290b47d2ec2fb6d6ca26daad3c9e211e0e531dd7566e7ff95b5b3525a57d4f32640ad4adf057717e0c215731db842ad761e61d947e81010e05cf5fd + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.0.2": version: 1.0.2 resolution: "which-boxed-primitive@npm:1.0.2" @@ -23171,10 +23428,10 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.2.2": - version: 2.3.1 - resolution: "yaml@npm:2.3.1" - checksum: 2c7bc9a7cd4c9f40d3b0b0a98e370781b68b8b7c4515720869aced2b00d92f5da1762b4ffa947f9e795d6cd6b19f410bd4d15fdd38aca7bd96df59bd9486fb54 +"yaml@npm:^2.2.2, yaml@npm:^2.3.4": + version: 2.3.4 + resolution: "yaml@npm:2.3.4" + checksum: e6d1dae1c6383bcc8ba11796eef3b8c02d5082911c6723efeeb5ba50fc8e881df18d645e64de68e421b577296000bea9c75d6d9097c2f6699da3ae0406c030d8 languageName: node linkType: hard