diff --git a/.eslintrc.js b/.eslintrc.js index c378adac..c0316560 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -66,6 +66,16 @@ module.exports = { '@typescript-eslint/naming-convention': 'warn', }, }, + // @metamask/keyring-utils + { + files: ['packages/keyring-utils/src/**/*.ts'], + extends: ['@metamask/eslint-config-typescript'], + parserOptions, + rules: { + // TODO: re-lint everything once the migration is done + '@typescript-eslint/no-explicit-any': 'off', + }, + }, // @metamask/keyring-api { files: ['packages/keyring-api/src/**/*.ts'], @@ -77,6 +87,18 @@ module.exports = { 'jsdoc/newline-after-description': 'off', }, }, + // @metamask/keyring-internal-api + { + files: ['packages/keyring-internal-api/src/**/*.ts'], + extends: ['@metamask/eslint-config-typescript'], + parserOptions, + rules: { + // FIXME: for some reason, it seems eslint is not able to infere those (this + // works on the original repository, so there might be some side-effects now that + // we are building in a monorepo) + '@typescript-eslint/restrict-template-expressions': 'off', + }, + }, // @metamask/keyring-eth-hd { files: ['packages/keyring-eth-hd/**/*.js'], @@ -169,6 +191,32 @@ module.exports = { '@typescript-eslint/restrict-template-expressions': 'off', }, }, + // @metamask/keyring-snap-sdk + { + files: ['packages/keyring-snap-sdk/src/**/*.test.ts'], + extends: ['@metamask/eslint-config-typescript'], + parserOptions, + rules: { + // FIXME: for some reason, it seems eslint is not able to infere those (this + // works on the original repository, so there might be some side-effects now that + // we are building in a monorepo) + '@typescript-eslint/restrict-template-expressions': 'off', + }, + }, + // @metamask/keyring-snap-client + { + files: ['packages/keyring-snap-client/src/**/*.ts'], + extends: ['@metamask/eslint-config-typescript'], + parserOptions, + rules: { + // TODO: re-lint everything once the migration is done + '@typescript-eslint/no-explicit-any': 'off', + // FIXME: for some reason, it seems eslint is not able to infere those (this + // works on the original repository, so there might be some side-effects now that + // we are building in a monorepo) + '@typescript-eslint/restrict-template-expressions': 'off', + }, + }, ], rules: { 'jsdoc/match-description': [ diff --git a/.syncpackrc b/.syncpackrc index 5be04059..e5485a42 100644 --- a/.syncpackrc +++ b/.syncpackrc @@ -26,6 +26,36 @@ "dependencyTypes": ["!local"], "dependencies": ["@metamask/keyring-api"], "pinVersion": "workspace:^" - } + }, + { + "label": "use workspace version of the keyring-internal-api", + "dependencyTypes": ["!local"], + "dependencies": ["@metamask/keyring-internal-api"], + "pinVersion": "workspace:^" + }, + { + "label": "use workspace version of the keyring-snap-sdk", + "dependencyTypes": ["!local"], + "dependencies": ["@metamask/keyring-snap-sdk"], + "pinVersion": "workspace:^" + }, + { + "label": "use workspace version of the keyring-snap-client", + "dependencyTypes": ["!local"], + "dependencies": ["@metamask/keyring-snap-client"], + "pinVersion": "workspace:^" + }, + { + "label": "use workspace version of the keyring-snap-internal-client", + "dependencyTypes": ["!local"], + "dependencies": ["@metamask/keyring-snap-internal-client"], + "pinVersion": "workspace:^" + }, + { + "label": "use workspace version of the keyring-utils", + "dependencyTypes": ["!local"], + "dependencies": ["@metamask/keyring-utils"], + "pinVersion": "workspace:^" + }, ] } diff --git a/README.md b/README.md index 2a7d36fb..9a24b689 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,14 @@ This repository contains the following packages [^fn1]: - [`@metamask/eth-hd-keyring`](packages/keyring-eth-hd) - [`@metamask/eth-ledger-bridge-keyring`](packages/keyring-eth-ledger-bridge) - [`@metamask/eth-simple-keyring`](packages/keyring-eth-simple) -- [`@metamask/eth-snap-keyring`](packages/keyring-snap) +- [`@metamask/eth-snap-keyring`](packages/keyring-snap-bridge) - [`@metamask/eth-trezor-keyring`](packages/keyring-eth-trezor) - [`@metamask/keyring-api`](packages/keyring-api) +- [`@metamask/keyring-internal-api`](packages/keyring-internal-api) +- [`@metamask/keyring-snap-client`](packages/keyring-snap-client) +- [`@metamask/keyring-snap-internal-client`](packages/keyring-snap-internal-client) +- [`@metamask/keyring-snap-sdk`](packages/keyring-snap-sdk) +- [`@metamask/keyring-utils`](packages/keyring-utils) @@ -34,8 +39,25 @@ linkStyle default opacity:0.5 eth_ledger_bridge_keyring(["@metamask/eth-ledger-bridge-keyring"]); eth_simple_keyring(["@metamask/eth-simple-keyring"]); eth_trezor_keyring(["@metamask/eth-trezor-keyring"]); + keyring_internal_api(["@metamask/keyring-internal-api"]); eth_snap_keyring(["@metamask/eth-snap-keyring"]); + keyring_snap_client(["@metamask/keyring-snap-client"]); + keyring_snap_internal_client(["@metamask/keyring-snap-internal-client"]); + keyring_snap_sdk(["@metamask/keyring-snap-sdk"]); + keyring_utils(["@metamask/keyring-utils"]); + keyring_api --> keyring_utils; + keyring_internal_api --> keyring_api; + keyring_internal_api --> keyring_utils; eth_snap_keyring --> keyring_api; + eth_snap_keyring --> keyring_internal_api; + eth_snap_keyring --> keyring_snap_internal_client; + keyring_snap_client --> keyring_api; + keyring_snap_client --> keyring_utils; + keyring_snap_internal_client --> keyring_api; + keyring_snap_internal_client --> keyring_snap_client; + keyring_snap_internal_client --> keyring_utils; + keyring_snap_sdk --> keyring_utils; + keyring_snap_sdk --> keyring_api; ``` diff --git a/jest.config.packages.js b/jest.config.packages.js index 402cd2e0..87280d8d 100644 --- a/jest.config.packages.js +++ b/jest.config.packages.js @@ -22,7 +22,13 @@ module.exports = { collectCoverage: true, // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: ['./src/**/*.ts', '!./src/**/*.test-d.ts'], + collectCoverageFrom: [ + './src/**/*.ts', + // Ignore typing test files + '!./src/**/*.test-d.ts', + // Ignore index of subdirectories + '!./src/**/*/index.ts', + ], // The directory where Jest should output its coverage files coverageDirectory: 'coverage', @@ -80,14 +86,6 @@ module.exports = { // Here we ensure that Jest resolves `@metamask/*` imports to the uncompiled source code for packages that live in this repo. // NOTE: This must be synchronized with the `paths` option in `tsconfig.packages.json`. moduleNameMapper: { - // FIXME: For now we do require to build some packages (keyring-api) that is then used - // in the keyring-snap package. This might be fixed after splitting the keyring-api into - // smaller internal packages! - // TODO: Remove this after the split of the keyring-api - // { - '^@metamask/keyring-api/dist/(.*)$': ['/../keyring-api/dist/$1'], - // } - '^jest-environment-jsdom$': ['/../keyring-api/dist/$1'], '^@metamask/(.+)$': [ '/../$1/src', // Some @metamask/* packages we are referencing aren't in this monorepo, diff --git a/package.json b/package.json index b9fe8e72..8a86e54e 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,10 @@ "lint:eslint": "eslint . --cache --ext js,cjs,mjs,ts", "lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write && yarn constraints --fix && yarn lint:dependencies:fix", "lint:misc": "prettier '**/*.json' '**/*.md' '!**/CHANGELOG.old.md' '**/*.yml' '!.yarnrc.yml' '!merged-packages/**' --ignore-path .gitignore", - "prepare:preview": "ts-node scripts/prepare-preview-builds.ts", + "prepare:preview": "ts-node --project tsconfig.scripts.json scripts/prepare-preview-builds.ts", "prepare:preview:local": "yarn prepare:preview @metamask-previews $(git rev-parse --short HEAD)", "publish:preview": "yarn workspaces foreach --all --no-private --parallel --verbose run publish:preview", - "readme:update": "ts-node scripts/update-readme-content.ts", + "readme:update": "ts-node --project tsconfig.scripts.json scripts/update-readme-content.ts", "release": "./scripts/release.sh", "setup": "yarn install", "test": "yarn workspaces foreach --all --parallel --verbose run test", diff --git a/packages/keyring-api/package.json b/packages/keyring-api/package.json index f43e4d45..ce5a97dd 100644 --- a/packages/keyring-api/package.json +++ b/packages/keyring-api/package.json @@ -42,23 +42,20 @@ "test": "yarn test:source && yarn test:types", "test:clean": "jest --clearCache", "test:source": "jest && jest-it-up", - "test:types": "tsd", + "test:types": "../../scripts/tsd-test.sh ./src", "test:watch": "jest --watch" }, "dependencies": { - "@metamask/snaps-sdk": "^6.7.0", + "@metamask/keyring-utils": "workspace:^", "@metamask/superstruct": "^3.1.0", "@metamask/utils": "^9.3.0", - "@types/uuid": "^9.0.8", - "bech32": "^2.0.0", - "uuid": "^9.0.1", - "webextension-polyfill": "^0.12.0" + "bech32": "^2.0.0" }, "devDependencies": { "@lavamoat/allow-scripts": "^3.2.1", "@lavamoat/preinstall-always-fail": "^2.1.0", "@metamask/auto-changelog": "^3.4.4", - "@metamask/providers": "^18.1.0", + "@metamask/keyring-utils": "workspace:^", "@ts-bridge/cli": "^0.6.0", "@types/jest": "^29.5.12", "@types/node": "^20.12.12", @@ -73,9 +70,6 @@ "typedoc": "^0.25.13", "typescript": "~5.6.3" }, - "peerDependencies": { - "@metamask/providers": "^18.1.0" - }, "engines": { "node": "^18.18 || >=20" }, @@ -91,6 +85,9 @@ } }, "tsd": { - "directory": "src" + "directory": "src", + "compilerOptions": { + "composite": "false" + } } } diff --git a/packages/keyring-api/src/api/account.ts b/packages/keyring-api/src/api/account.ts index 68a48524..52f465e9 100644 --- a/packages/keyring-api/src/api/account.ts +++ b/packages/keyring-api/src/api/account.ts @@ -1,10 +1,8 @@ +import { object, UuidStruct } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { array, enums, record, string } from '@metamask/superstruct'; import { JsonStruct } from '@metamask/utils'; -import { object } from '../superstruct'; -import { UuidStruct } from '../utils'; - /** * Supported Ethereum account types. */ diff --git a/packages/keyring-api/src/api/asset.ts b/packages/keyring-api/src/api/asset.ts index 009f459f..f46623a7 100644 --- a/packages/keyring-api/src/api/asset.ts +++ b/packages/keyring-api/src/api/asset.ts @@ -1,3 +1,8 @@ +import { + object, + selectiveUnion, + StringNumberStruct, +} from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { literal, string } from '@metamask/superstruct'; import { @@ -6,9 +11,6 @@ import { isPlainObject, } from '@metamask/utils'; -import { object, selectiveUnion } from '../superstruct'; -import { StringNumberStruct } from '../utils'; - /** * Fungible asset struct. */ diff --git a/packages/keyring-api/src/api/balance.test.ts b/packages/keyring-api/src/api/balance.test.ts new file mode 100644 index 00000000..27ce466f --- /dev/null +++ b/packages/keyring-api/src/api/balance.test.ts @@ -0,0 +1,28 @@ +import { is } from '@metamask/superstruct'; + +import { BalanceStruct } from './balance'; + +describe('BalanceStruct', () => { + it.each([ + // Valid + { balance: { amount: '1.0', unit: 'ETH' }, expected: true }, + { balance: { amount: '0.1', unit: 'BTC' }, expected: true }, + // Missing amount + { balance: { unit: 'ETH' }, expected: false }, + // Missing unit + { balance: { amount: '1.0' }, expected: false }, + // Invalid amount type + { balance: { amount: 1, unit: 'ETH' }, expected: false }, + { balance: { amount: true, unit: 'ETH' }, expected: false }, + { balance: { amount: null, unit: 'ETH' }, expected: false }, + // Invalid unit type + { balance: { amount: '1.0', unit: 1 }, expected: false }, + { balance: { amount: '1.0', unit: true }, expected: false }, + { balance: { amount: '1.0', unit: null }, expected: false }, + ])( + 'returns $expected for is($balance, BalanceStruct)', + ({ balance, expected }) => { + expect(is(balance, BalanceStruct)).toBe(expected); + }, + ); +}); diff --git a/packages/keyring-api/src/api/balance.ts b/packages/keyring-api/src/api/balance.ts index 609108e8..32b7573f 100644 --- a/packages/keyring-api/src/api/balance.ts +++ b/packages/keyring-api/src/api/balance.ts @@ -1,9 +1,7 @@ +import { object, StringNumberStruct } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { string } from '@metamask/superstruct'; -import { object } from '../superstruct'; -import { StringNumberStruct } from '../utils'; - export const BalanceStruct = object({ amount: StringNumberStruct, unit: string(), diff --git a/packages/keyring-api/src/api/caip.ts b/packages/keyring-api/src/api/caip.ts index 2008200d..dd41a8b8 100644 --- a/packages/keyring-api/src/api/caip.ts +++ b/packages/keyring-api/src/api/caip.ts @@ -1,7 +1,6 @@ +import { definePattern } from '@metamask/keyring-utils'; import { is, type Infer } from '@metamask/superstruct'; -import { definePattern } from '../superstruct'; - const CAIP_ASSET_TYPE_REGEX = /^(?(?[-a-z0-9]{3,8}):(?[-_a-zA-Z0-9]{1,32}))\/(?[-a-z0-9]{3,8}):(?[-.%a-zA-Z0-9]{1,128})$/u; diff --git a/packages/keyring-api/src/api/export.test.ts b/packages/keyring-api/src/api/export.test.ts new file mode 100644 index 00000000..e22a27d8 --- /dev/null +++ b/packages/keyring-api/src/api/export.test.ts @@ -0,0 +1,29 @@ +import { is } from '@metamask/superstruct'; + +import { KeyringAccountDataStruct } from './export'; + +describe('KeyringAccountDataStruct', () => { + const sym = Symbol('Unique symbol for testing purposes'); + it.each([ + // Valid + { data: { foo: 'bar' }, expected: true }, + { data: { foo: 'bar', bar: 1 }, expected: true }, + // Undefined is not allowed in JSON + { data: { foo: undefined, bar: null }, expected: false }, + // Invalid + { data: 0, expected: false }, + { + data: '34a0b893b66e312a8b0f7dc4bc4c7930b67f8823513aff5444fb5c64aa060c5a', + expected: false, + }, + { data: sym, expected: false }, + // FIXME: Not sure why this one works, maybe the array is + // mapped as: { 0: 0xdead, 1: 0xbeef, 2: '!' }? + { data: [0xdead, 0xbeef, '!'], expected: true }, + ])( + 'returns $expected for is($data, KeyringAccountDataStruct)', + ({ data, expected }) => { + expect(is(data, KeyringAccountDataStruct)).toBe(expected); + }, + ); +}); diff --git a/packages/keyring-api/src/api/index.ts b/packages/keyring-api/src/api/index.ts index 5e418c7f..a1288a7a 100644 --- a/packages/keyring-api/src/api/index.ts +++ b/packages/keyring-api/src/api/index.ts @@ -6,4 +6,5 @@ export * from './export'; export * from './request'; export * from './response'; export * from './transaction'; +export * from './pagination'; export type * from './keyring'; diff --git a/packages/keyring-api/src/api/keyring.ts b/packages/keyring-api/src/api/keyring.ts index 0c56a19b..44187dac 100644 --- a/packages/keyring-api/src/api/keyring.ts +++ b/packages/keyring-api/src/api/keyring.ts @@ -1,13 +1,16 @@ +/* eslint-disable @typescript-eslint/no-redundant-type-constituents */ +// This rule seems to be triggering a false positive on the `KeyringAccount`. + import type { Json } from '@metamask/utils'; import type { KeyringAccount } from './account'; import type { Balance } from './balance'; import type { CaipAssetType } from './caip'; import type { KeyringAccountData } from './export'; +import type { Paginated, Pagination } from './pagination'; import type { KeyringRequest } from './request'; import type { KeyringResponse } from './response'; import type { Transaction } from './transaction'; -import type { Paginated, Pagination } from '../utils'; /** * Keyring interface. diff --git a/packages/keyring-api/src/api/pagination.test.ts b/packages/keyring-api/src/api/pagination.test.ts new file mode 100644 index 00000000..08e7f24e --- /dev/null +++ b/packages/keyring-api/src/api/pagination.test.ts @@ -0,0 +1,36 @@ +import { is } from '@metamask/superstruct'; + +import { PaginationStruct } from './pagination'; + +describe('PaginationStruct', () => { + it.each([ + // Valid + { + pagination: { + limit: 1, + next: 'next-cursor', + }, + expected: true, + }, + { + pagination: { + limit: 1, + next: null, + }, + expected: true, + }, + { + pagination: { + limit: 1, + }, + expected: true, + }, + // Missing `limit` + { pagination: { next: 'next-cursor' }, expected: false }, + ])( + 'returns $expected for is($asset, PaginationStruct)', + ({ pagination, expected }) => { + expect(is(pagination, PaginationStruct)).toBe(expected); + }, + ); +}); diff --git a/packages/keyring-api/src/utils/pagination.ts b/packages/keyring-api/src/api/pagination.ts similarity index 94% rename from packages/keyring-api/src/utils/pagination.ts rename to packages/keyring-api/src/api/pagination.ts index 8b3bebb8..035756bd 100644 --- a/packages/keyring-api/src/utils/pagination.ts +++ b/packages/keyring-api/src/api/pagination.ts @@ -1,8 +1,7 @@ +import { exactOptional, object } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { nullable, number, string } from '@metamask/superstruct'; -import { exactOptional, object } from '../superstruct'; - /** * Pagination struct. This struct is used to specify the limit of items to * return and the next cursor to iterate over the results. diff --git a/packages/keyring-api/src/api/request.test.ts b/packages/keyring-api/src/api/request.test.ts new file mode 100644 index 00000000..53fe0db2 --- /dev/null +++ b/packages/keyring-api/src/api/request.test.ts @@ -0,0 +1,105 @@ +import { is } from '@metamask/superstruct'; + +import { KeyringRequestStruct } from './request'; + +describe('KeyringRequest', () => { + it.each([ + // Valid + { + request: { + id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e', + scope: 'eip155:1', + account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797', + request: { + method: 'eth_personalSign', + params: { + data: '0x00...', + }, + }, + }, + expected: true, + }, + { + request: { + id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e', + scope: 'eip155:1', + account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797', + request: { + method: 'eth_somethingElseWithNoParameters', + }, + }, + expected: true, + }, + // Invalid: + // Missing id + { + request: { + scope: 'eip155:1', + account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797', + request: { + method: 'eth_personalSign', + params: { + data: '0x00...', + }, + }, + }, + expected: false, + }, + // Missing scope + { + request: { + id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e', + account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797', + request: { + method: 'eth_personalSign', + params: { + data: '0x00...', + }, + }, + }, + expected: false, + }, + // Missing account + { + request: { + id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e', + scope: 'eip155:1', + request: { + method: 'eth_personalSign', + params: { + data: '0x00...', + }, + }, + }, + expected: false, + }, + // Missing request + { + request: { + id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e', + scope: 'eip155:1', + account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797', + }, + expected: false, + }, + // Missing request.method + { + request: { + id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e', + scope: 'eip155:1', + account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797', + request: { + params: { + data: '0x00...', + }, + }, + }, + expected: false, + }, + ])( + 'returns $expected for is($data, KeyringRequestStruct)', + ({ request, expected }) => { + expect(is(request, KeyringRequestStruct)).toBe(expected); + }, + ); +}); diff --git a/packages/keyring-api/src/api/request.ts b/packages/keyring-api/src/api/request.ts index db2609af..3b3c4c72 100644 --- a/packages/keyring-api/src/api/request.ts +++ b/packages/keyring-api/src/api/request.ts @@ -1,10 +1,8 @@ +import { exactOptional, object, UuidStruct } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { array, record, string, union } from '@metamask/superstruct'; import { JsonStruct } from '@metamask/utils'; -import { exactOptional, object } from '../superstruct'; -import { UuidStruct } from '../utils'; - export const KeyringRequestStruct = object({ /** * Keyring request ID (UUIDv4). diff --git a/packages/keyring-api/src/api/response.test.ts b/packages/keyring-api/src/api/response.test.ts new file mode 100644 index 00000000..861edff2 --- /dev/null +++ b/packages/keyring-api/src/api/response.test.ts @@ -0,0 +1,73 @@ +import { is } from '@metamask/superstruct'; + +import { KeyringResponseStruct } from './response'; + +describe('KeyringResponseStruct', () => { + it.each([ + // Valid + { + response: { + pending: false, + result: {}, + }, + expected: true, + }, + { + response: { + pending: true, + redirect: { + message: 'success', + url: 'http://dapp.example.com/continue', + }, + }, + expected: true, + }, + { + response: { + pending: true, + redirect: { + url: 'http://dapp.example.com/continue', + }, + }, + expected: true, + }, + { + response: { + pending: true, + redirect: { + message: 'success', + }, + }, + expected: true, + }, + { + response: { + pending: true, + }, + expected: true, + }, + // Invalid + { + response: { + pending: true, + result: {}, + }, + expected: false, + }, + { + response: { + pending: false, + redirect: { + message: 'success', + url: 'http://dapp.example.com/continue', + }, + }, + expected: false, + }, + ])( + 'returns $expected for is($data, KeyringResponseStruct)', + ({ response, expected }) => { + expect(is(response, KeyringResponseStruct)).toBe(expected); + }, + ); +}); diff --git a/packages/keyring-api/src/api/response.ts b/packages/keyring-api/src/api/response.ts index dbe2e0b4..154dd815 100644 --- a/packages/keyring-api/src/api/response.ts +++ b/packages/keyring-api/src/api/response.ts @@ -1,9 +1,8 @@ +import { exactOptional, object } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { literal, string, union } from '@metamask/superstruct'; import { JsonStruct } from '@metamask/utils'; -import { exactOptional, object } from '../superstruct'; - export const KeyringResponseStruct = union([ object({ /** diff --git a/packages/keyring-api/src/api/transaction.ts b/packages/keyring-api/src/api/transaction.ts index 9e806c9b..ac0f787d 100644 --- a/packages/keyring-api/src/api/transaction.ts +++ b/packages/keyring-api/src/api/transaction.ts @@ -1,12 +1,11 @@ +import type { InferEquals } from '@metamask/keyring-utils'; +import { object, UuidStruct } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { array, enums, nullable, number, string } from '@metamask/superstruct'; import { CaipChainIdStruct } from '@metamask/utils'; import { AssetStruct } from './asset'; -import type { InferEquals } from '../superstruct'; -import { object } from '../superstruct'; -import type { Paginated } from '../utils'; -import { UuidStruct } from '../utils'; +import type { Paginated } from './pagination'; /** * This struct represents a participant in a transaction. diff --git a/packages/keyring-api/src/btc/types.test-d.ts b/packages/keyring-api/src/btc/types.test-d.ts index 74367be1..3ea67d0a 100644 --- a/packages/keyring-api/src/btc/types.test-d.ts +++ b/packages/keyring-api/src/btc/types.test-d.ts @@ -1,7 +1,8 @@ +import type { Extends } from '@metamask/keyring-utils'; +import { expectTrue } from '@metamask/keyring-utils'; + import type { BtcP2wpkhAccount } from './types'; import type { KeyringAccount } from '../api'; -import type { Extends } from '../utils'; -import { expectTrue } from '../utils'; // `BtcP2wpkhAccount` extends `KeyringAccount` expectTrue>(); diff --git a/packages/keyring-api/src/btc/types.ts b/packages/keyring-api/src/btc/types.ts index 44c213cb..f610293d 100644 --- a/packages/keyring-api/src/btc/types.ts +++ b/packages/keyring-api/src/btc/types.ts @@ -1,9 +1,9 @@ +import { object } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { string, array, enums, refine, literal } from '@metamask/superstruct'; import { bech32 } from 'bech32'; -import { KeyringAccountStruct, BtcAccountType } from '../api'; -import { object } from '../superstruct'; +import { BtcAccountType, KeyringAccountStruct } from '../api'; export const BtcP2wpkhAddressStruct = refine( string(), diff --git a/packages/keyring-api/src/eth/erc4337/types.ts b/packages/keyring-api/src/eth/erc4337/types.ts index ba92a384..c9fd8aab 100644 --- a/packages/keyring-api/src/eth/erc4337/types.ts +++ b/packages/keyring-api/src/eth/erc4337/types.ts @@ -1,7 +1,6 @@ +import { exactOptional, object, UrlStruct } from '@metamask/keyring-utils'; import { type Infer } from '@metamask/superstruct'; -import { exactOptional, object } from '../../superstruct'; -import { UrlStruct } from '../../utils'; import { EthAddressStruct, EthBytesStruct, EthUint256Struct } from '../types'; /** diff --git a/packages/keyring-api/src/eth/types.test-d.ts b/packages/keyring-api/src/eth/types.test-d.ts index 250f7eb6..69edac4f 100644 --- a/packages/keyring-api/src/eth/types.test-d.ts +++ b/packages/keyring-api/src/eth/types.test-d.ts @@ -1,11 +1,11 @@ +import type { Extends } from '@metamask/keyring-utils'; +import { expectTrue } from '@metamask/keyring-utils'; import { expectAssignable, expectNotAssignable } from 'tsd'; import type { EthEoaAccount, EthErc4337Account } from './types'; import { EthMethod } from './types'; -import type { KeyringAccount } from '../api'; import { EthAccountType } from '../api'; -import type { Extends } from '../utils'; -import { expectTrue } from '../utils'; +import type { KeyringAccount } from '../api'; const id = '606a7759-b0fb-48e4-9874-bab62ff8e7eb'; const address = '0x000'; diff --git a/packages/keyring-api/src/eth/types.test.ts b/packages/keyring-api/src/eth/types.test.ts deleted file mode 100644 index 5afc8012..00000000 --- a/packages/keyring-api/src/eth/types.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { UrlStruct } from '../utils'; - -describe('types', () => { - it('is a valid BundlerUrl', () => { - const url = 'https://api.example.com'; - expect(() => UrlStruct.assert(url)).not.toThrow(); - }); - - it('is a valid BundlerUrl with query parameters', () => { - const url = 'https://api.example.com?foo=bar'; - expect(() => UrlStruct.assert(url)).not.toThrow(); - }); - - it('accepts path parameters', () => { - const url = 'https://api.example.com/foo/bar'; - expect(() => UrlStruct.assert(url)).not.toThrow(); - }); - - it('fails if it does not start with http or https', () => { - const url = 'ftp://api.example.com'; - expect(() => UrlStruct.assert(url)).toThrow( - 'Expected a value of type `Url`, but received: `"ftp://api.example.com"`', - ); - }); - - it('has to start with http or https', () => { - const url = 'http://api.example.com'; - expect(() => UrlStruct.assert(url)).not.toThrow(); - }); -}); diff --git a/packages/keyring-api/src/eth/types.ts b/packages/keyring-api/src/eth/types.ts index 2d28926b..892bcc5e 100644 --- a/packages/keyring-api/src/eth/types.ts +++ b/packages/keyring-api/src/eth/types.ts @@ -1,8 +1,8 @@ +import { object, definePattern } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { array, enums, literal } from '@metamask/superstruct'; import { EthAccountType, KeyringAccountStruct } from '../api'; -import { object, definePattern } from '../superstruct'; export const EthBytesStruct = definePattern('EthBytes', /^0x[0-9a-f]*$/iu); diff --git a/packages/keyring-api/src/eth/utils.ts b/packages/keyring-api/src/eth/utils.ts index e686e858..6a0005ff 100644 --- a/packages/keyring-api/src/eth/utils.ts +++ b/packages/keyring-api/src/eth/utils.ts @@ -1,5 +1,5 @@ -import type { KeyringAccountType } from '../api'; import { EthAccountType } from '../api'; +import type { KeyringAccountType } from '../api'; /** * Checks if the given type is an EVM account type. diff --git a/packages/keyring-api/src/internal/events.test.ts b/packages/keyring-api/src/events.test.ts similarity index 98% rename from packages/keyring-api/src/internal/events.test.ts rename to packages/keyring-api/src/events.test.ts index e2e1873d..8748aa5a 100644 --- a/packages/keyring-api/src/internal/events.test.ts +++ b/packages/keyring-api/src/events.test.ts @@ -1,14 +1,14 @@ import { is } from '@metamask/superstruct'; +import { EthAccountType } from './api'; import { AccountCreatedEventStruct, AccountDeletedEventStruct, AccountUpdatedEventStruct, RequestApprovedEventStruct, RequestRejectedEventStruct, + KeyringEvent, } from './events'; -import { EthAccountType } from '../api'; -import { KeyringEvent } from '../events'; describe('events', () => { describe('AccountCreatedEventStruct', () => { diff --git a/packages/keyring-api/src/events.ts b/packages/keyring-api/src/events.ts index d3f26d59..b5e433f6 100644 --- a/packages/keyring-api/src/events.ts +++ b/packages/keyring-api/src/events.ts @@ -1,3 +1,9 @@ +import { exactOptional, object, UuidStruct } from '@metamask/keyring-utils'; +import { boolean, literal, string } from '@metamask/superstruct'; +import { JsonStruct } from '@metamask/utils'; + +import { KeyringAccountStruct } from './api'; + /** * Supported keyring events. */ @@ -11,3 +17,73 @@ export enum KeyringEvent { RequestApproved = 'notify:requestApproved', RequestRejected = 'notify:requestRejected', } + +export const AccountCreatedEventStruct = object({ + method: literal(`${KeyringEvent.AccountCreated}`), + params: object({ + /** + * New account object. + */ + account: KeyringAccountStruct, + + /** + * Account name suggestion provided to the MetaMask client. + * + * The keyring can suggest a name for the account, but it's up to the + * client to decide whether to use it. The keyring won't be informed if the + * client decides to use a different name. + */ + accountNameSuggestion: exactOptional(string()), + + /** + * Instructs MetaMask to display the add account confirmation dialog in the UI. + * **Note:** This is not guaranteed to be honored by the MetaMask client. + */ + displayConfirmation: exactOptional(boolean()), + }), +}); + +export const AccountUpdatedEventStruct = object({ + method: literal(`${KeyringEvent.AccountUpdated}`), + params: object({ + /** + * Updated account object. + */ + account: KeyringAccountStruct, + }), +}); + +export const AccountDeletedEventStruct = object({ + method: literal(`${KeyringEvent.AccountDeleted}`), + params: object({ + /** + * Deleted account ID. + */ + id: UuidStruct, + }), +}); + +export const RequestApprovedEventStruct = object({ + method: literal(`${KeyringEvent.RequestApproved}`), + params: object({ + /** + * Request ID. + */ + id: UuidStruct, + + /** + * Request result. + */ + result: JsonStruct, + }), +}); + +export const RequestRejectedEventStruct = object({ + method: literal(`${KeyringEvent.RequestRejected}`), + params: object({ + /** + * Request ID. + */ + id: UuidStruct, + }), +}); diff --git a/packages/keyring-api/src/index.ts b/packages/keyring-api/src/index.ts index f81807db..42d55f74 100644 --- a/packages/keyring-api/src/index.ts +++ b/packages/keyring-api/src/index.ts @@ -1,13 +1,7 @@ export * from './api'; export * from './btc'; -export type * from './contexts'; -export * from './eth'; export * from './sol'; +export * from './eth'; +export type * from './contexts'; +export * from './rpc'; export * from './events'; -export * from './internal'; -export * from './JsonRpcRequest'; -export * from './KeyringClient'; -export * from './KeyringSnapRpcClient'; -export * from './rpc-handler'; -export * from './snap-utils'; -export * from './superstruct'; diff --git a/packages/keyring-api/src/internal/events.ts b/packages/keyring-api/src/internal/events.ts deleted file mode 100644 index 9047ae26..00000000 --- a/packages/keyring-api/src/internal/events.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { boolean, literal, string } from '@metamask/superstruct'; -import { JsonStruct } from '@metamask/utils'; - -import { KeyringAccountStruct } from '../api'; -import { KeyringEvent } from '../events'; -import { exactOptional, object } from '../superstruct'; -import { UuidStruct } from '../utils'; - -export const AccountCreatedEventStruct = object({ - method: literal(`${KeyringEvent.AccountCreated}`), - params: object({ - /** - * New account object. - */ - account: KeyringAccountStruct, - - /** - * Account name suggestion provided to the MetaMask client. - * - * The keyring can suggest a name for the account, but it's up to the - * client to decide whether to use it. The keyring won't be informed if the - * client decides to use a different name. - */ - accountNameSuggestion: exactOptional(string()), - - /** - * Instructs MetaMask to display the add account confirmation dialog in the UI. - * **Note:** This is not guaranteed to be honored by the MetaMask client. - */ - displayConfirmation: exactOptional(boolean()), - }), -}); - -export const AccountUpdatedEventStruct = object({ - method: literal(`${KeyringEvent.AccountUpdated}`), - params: object({ - /** - * Updated account object. - */ - account: KeyringAccountStruct, - }), -}); - -export const AccountDeletedEventStruct = object({ - method: literal(`${KeyringEvent.AccountDeleted}`), - params: object({ - /** - * Deleted account ID. - */ - id: UuidStruct, - }), -}); - -export const RequestApprovedEventStruct = object({ - method: literal(`${KeyringEvent.RequestApproved}`), - params: object({ - /** - * Request ID. - */ - id: UuidStruct, - - /** - * Request result. - */ - result: JsonStruct, - }), -}); - -export const RequestRejectedEventStruct = object({ - method: literal(`${KeyringEvent.RequestRejected}`), - params: object({ - /** - * Request ID. - */ - id: UuidStruct, - }), -}); diff --git a/packages/keyring-api/src/internal/index.ts b/packages/keyring-api/src/internal/index.ts deleted file mode 100644 index 2ec237b3..00000000 --- a/packages/keyring-api/src/internal/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './api'; -export type * from './eth'; -export * from './events'; -export * from './rpc'; -export * from './types'; diff --git a/packages/keyring-api/src/internal/rpc.ts b/packages/keyring-api/src/internal/rpc.ts deleted file mode 100644 index e74161dd..00000000 --- a/packages/keyring-api/src/internal/rpc.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Keyring RPC methods used by the API. - */ -export enum KeyringRpcMethod { - ListAccounts = 'keyring_listAccounts', - GetAccount = 'keyring_getAccount', - CreateAccount = 'keyring_createAccount', - ListAccountTransactions = 'keyring_listAccountTransactions', - GetAccountBalances = 'keyring_getAccountBalances', - FilterAccountChains = 'keyring_filterAccountChains', - UpdateAccount = 'keyring_updateAccount', - DeleteAccount = 'keyring_deleteAccount', - ExportAccount = 'keyring_exportAccount', - ListRequests = 'keyring_listRequests', - GetRequest = 'keyring_getRequest', - SubmitRequest = 'keyring_submitRequest', - ApproveRequest = 'keyring_approveRequest', - RejectRequest = 'keyring_rejectRequest', -} - -/** - * Check if a method is a keyring RPC method. - * - * @param method - Method to check. - * @returns Whether the method is a keyring RPC method. - */ -export function isKeyringRpcMethod(method: string): boolean { - return Object.values(KeyringRpcMethod).includes(method as KeyringRpcMethod); -} diff --git a/packages/keyring-api/src/rpc.test.ts b/packages/keyring-api/src/rpc.test.ts new file mode 100644 index 00000000..e2f0afde --- /dev/null +++ b/packages/keyring-api/src/rpc.test.ts @@ -0,0 +1,14 @@ +import { KeyringRpcMethod, isKeyringRpcMethod } from './rpc'; + +describe('isKeyringRpcMethod', () => { + it.each(Object.values(KeyringRpcMethod))( + 'returns true for: KeyringRpcMethod.$s', + (method) => { + expect(isKeyringRpcMethod(method)).toBe(true); + }, + ); + + it('returns false for unknown method', () => { + expect(isKeyringRpcMethod('keyring_unknownMethod')).toBe(false); + }); +}); diff --git a/packages/keyring-api/src/internal/api.ts b/packages/keyring-api/src/rpc.ts similarity index 86% rename from packages/keyring-api/src/internal/api.ts rename to packages/keyring-api/src/rpc.ts index 0b59aff2..f4c65697 100644 --- a/packages/keyring-api/src/internal/api.ts +++ b/packages/keyring-api/src/rpc.ts @@ -1,3 +1,4 @@ +import { object, UuidStruct } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { array, @@ -9,7 +10,6 @@ import { } from '@metamask/superstruct'; import { JsonStruct } from '@metamask/utils'; -import { KeyringRpcMethod } from './rpc'; import { BalanceStruct, CaipAssetTypeStruct, @@ -18,9 +18,40 @@ import { KeyringRequestStruct, KeyringResponseStruct, TransactionsPageStruct, -} from '../api'; -import { object } from '../superstruct'; -import { PaginationStruct, UuidStruct } from '../utils'; + PaginationStruct, +} from './api'; + +/** + * Keyring RPC methods used by the API. + */ +export enum KeyringRpcMethod { + ListAccounts = 'keyring_listAccounts', + GetAccount = 'keyring_getAccount', + CreateAccount = 'keyring_createAccount', + ListAccountTransactions = 'keyring_listAccountTransactions', + GetAccountBalances = 'keyring_getAccountBalances', + FilterAccountChains = 'keyring_filterAccountChains', + UpdateAccount = 'keyring_updateAccount', + DeleteAccount = 'keyring_deleteAccount', + ExportAccount = 'keyring_exportAccount', + ListRequests = 'keyring_listRequests', + GetRequest = 'keyring_getRequest', + SubmitRequest = 'keyring_submitRequest', + ApproveRequest = 'keyring_approveRequest', + RejectRequest = 'keyring_rejectRequest', +} + +/** + * Check if a method is a keyring RPC method. + * + * @param method - Method to check. + * @returns Whether the method is a keyring RPC method. + */ +export function isKeyringRpcMethod(method: string): boolean { + return Object.values(KeyringRpcMethod).includes(method as KeyringRpcMethod); +} + +// ---------------------------------------------------------------------------- const CommonHeader = { jsonrpc: literal('2.0'), diff --git a/packages/keyring-api/src/sol/types.test-d.ts b/packages/keyring-api/src/sol/types.test-d.ts index 8647f89e..6dae109a 100644 --- a/packages/keyring-api/src/sol/types.test-d.ts +++ b/packages/keyring-api/src/sol/types.test-d.ts @@ -1,7 +1,8 @@ +import type { Extends } from '@metamask/keyring-utils'; +import { expectTrue } from '@metamask/keyring-utils'; + import type { SolDataAccount } from './types'; import type { KeyringAccount } from '../api'; -import type { Extends } from '../utils'; -import { expectTrue } from '../utils'; // `SolDataAccount` extends `KeyringAccount` expectTrue>(); diff --git a/packages/keyring-api/src/sol/types.ts b/packages/keyring-api/src/sol/types.ts index d01d9415..64dcaa66 100644 --- a/packages/keyring-api/src/sol/types.ts +++ b/packages/keyring-api/src/sol/types.ts @@ -1,8 +1,8 @@ +import { object, definePattern } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { array, enums, literal } from '@metamask/superstruct'; import { KeyringAccountStruct, SolAccountType } from '../api'; -import { object, definePattern } from '../superstruct'; /** * Solana addresses are represented in the format of a 256-bit ed25519 public key and diff --git a/packages/keyring-api/src/utils/index.ts b/packages/keyring-api/src/utils/index.ts deleted file mode 100644 index 15a9960f..00000000 --- a/packages/keyring-api/src/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './pagination'; -export * from './types'; -export * from './typing'; diff --git a/packages/keyring-api/src/utils/types.test.ts b/packages/keyring-api/src/utils/types.test.ts deleted file mode 100644 index 7cf685ce..00000000 --- a/packages/keyring-api/src/utils/types.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { is } from '@metamask/superstruct'; - -import { StringNumberStruct } from './types'; - -describe('StringNumber', () => { - it.each(['0', '0.0', '0.1', '0.19', '00.19', '0.000000000000000000000'])( - 'validates basic number: %s', - (input: string) => { - expect(is(input, StringNumberStruct)).toBe(true); - }, - ); - - it.each(['foobar', 'NaN', '0.123.4', '1e3', undefined, null, 1, true])( - 'fails to validate wrong number: %s', - (input: any) => { - expect(is(input, StringNumberStruct)).toBe(false); - }, - ); -}); diff --git a/packages/keyring-api/tsconfig.build.json b/packages/keyring-api/tsconfig.build.json index 054c9a64..b3a9f737 100644 --- a/packages/keyring-api/tsconfig.build.json +++ b/packages/keyring-api/tsconfig.build.json @@ -3,11 +3,9 @@ "compilerOptions": { "baseUrl": "./", "outDir": "dist", - "rootDir": "src", - // FIXME: We should investigate how to get rid of this flag, as this will turn off type - // checking for all *.d.ts files - "skipLibCheck": true + "rootDir": "src" }, + "references": [{ "path": "../keyring-utils/tsconfig.build.json" }], "include": ["./src/**/*.ts"], "exclude": ["./src/**/*.test.ts", "./src/**/*.test-d.ts"] } diff --git a/packages/keyring-internal-api/CHANGELOG.md b/packages/keyring-internal-api/CHANGELOG.md new file mode 100644 index 00000000..f4eb4815 --- /dev/null +++ b/packages/keyring-internal-api/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +[Unreleased]: https://github.com/MetaMask/accounts/ diff --git a/packages/keyring-internal-api/README.md b/packages/keyring-internal-api/README.md new file mode 100644 index 00000000..ff4e4bbf --- /dev/null +++ b/packages/keyring-internal-api/README.md @@ -0,0 +1,15 @@ +# MetaMask Keyring Internal API + +Keyring internal API that holds internal types and implementations. + +## Installation + +`yarn add @metamask/keyring-internal-api` + +or + +`npm install @metamask/keyring-internal-api` + +## Contributing + +This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/accounts#readme). diff --git a/packages/keyring-internal-api/jest.config.js b/packages/keyring-internal-api/jest.config.js new file mode 100644 index 00000000..d6e04ca7 --- /dev/null +++ b/packages/keyring-internal-api/jest.config.js @@ -0,0 +1,28 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +const merge = require('deepmerge'); +const path = require('path'); + +const baseConfig = require('../../jest.config.packages'); + +const displayName = path.basename(__dirname); + +module.exports = merge(baseConfig, { + // The display name when running multiple projects + displayName, + + coveragePathIgnorePatterns: ['./src/tests'], + + // An object that configures minimum threshold enforcement for coverage results + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); diff --git a/packages/keyring-internal-api/package.json b/packages/keyring-internal-api/package.json new file mode 100644 index 00000000..356e7629 --- /dev/null +++ b/packages/keyring-internal-api/package.json @@ -0,0 +1,89 @@ +{ + "name": "@metamask/keyring-internal-api", + "version": "0.0.1", + "description": "MetaMask Keyring Internal API", + "keywords": [ + "metamask", + "keyring" + ], + "homepage": "https://github.com/MetaMask/accounts/tree/main/packages/keyring-internal-api#readme", + "bugs": { + "url": "https://github.com/MetaMask/accounts/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/accounts.git" + }, + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "main": "./dist/index.cjs", + "types": "./dist/index.d.cts", + "files": [ + "dist/" + ], + "scripts": { + "build": "ts-bridge --project tsconfig.build.json --no-references", + "build:clean": "yarn build --clean", + "build:docs": "typedoc", + "changelog:update": "../../scripts/update-changelog.sh @metamask/keyring-internal-api", + "changelog:validate": "../../scripts/validate-changelog.sh @metamask/keyring-internal-api", + "publish:preview": "yarn npm publish --tag preview", + "test": "yarn test:source && yarn test:types", + "test:clean": "jest --clearCache", + "test:source": "jest && jest-it-up", + "test:types": "../../scripts/tsd-test.sh ./src", + "test:watch": "jest --watch" + }, + "dependencies": { + "@metamask/keyring-api": "workspace:^", + "@metamask/keyring-utils": "workspace:^", + "@metamask/superstruct": "^3.1.0", + "@metamask/utils": "^9.3.0" + }, + "devDependencies": { + "@lavamoat/allow-scripts": "^3.2.1", + "@lavamoat/preinstall-always-fail": "^2.1.0", + "@metamask/auto-changelog": "^3.4.4", + "@ts-bridge/cli": "^0.6.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.12", + "deepmerge": "^4.2.2", + "depcheck": "^1.4.7", + "jest": "^29.5.0", + "jest-it-up": "^3.1.0", + "rimraf": "^5.0.7", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.2", + "tsd": "^0.31.0", + "typedoc": "^0.25.13", + "typescript": "~5.6.3" + }, + "engines": { + "node": "^18.18 || >=20" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "lavamoat": { + "allowScripts": { + "@lavamoat/preinstall-always-fail": false + } + }, + "tsd": { + "directory": "src", + "compilerOptions": { + "composite": "false" + } + } +} diff --git a/packages/keyring-api/src/internal/eth/EthKeyring.ts b/packages/keyring-internal-api/src/eth/EthKeyring.ts similarity index 94% rename from packages/keyring-api/src/internal/eth/EthKeyring.ts rename to packages/keyring-internal-api/src/eth/EthKeyring.ts index 34cc4909..9f583deb 100644 --- a/packages/keyring-api/src/internal/eth/EthKeyring.ts +++ b/packages/keyring-internal-api/src/eth/EthKeyring.ts @@ -1,12 +1,11 @@ -import type { Json, Keyring } from '@metamask/utils'; - -import type { KeyringExecutionContext } from '../../contexts'; import type { + KeyringExecutionContext, EthBaseTransaction, EthBaseUserOperation, EthUserOperation, EthUserOperationPatch, -} from '../../eth'; +} from '@metamask/keyring-api'; +import type { Json, Keyring } from '@metamask/utils'; export type EthKeyring = Keyring & { /** diff --git a/packages/keyring-api/src/internal/eth/index.ts b/packages/keyring-internal-api/src/eth/index.ts similarity index 100% rename from packages/keyring-api/src/internal/eth/index.ts rename to packages/keyring-internal-api/src/eth/index.ts diff --git a/packages/keyring-internal-api/src/index.ts b/packages/keyring-internal-api/src/index.ts new file mode 100644 index 00000000..5286ea07 --- /dev/null +++ b/packages/keyring-internal-api/src/index.ts @@ -0,0 +1,2 @@ +export type * from './eth'; +export * from './types'; diff --git a/packages/keyring-api/src/internal/types.test.ts b/packages/keyring-internal-api/src/types.test.ts similarity index 100% rename from packages/keyring-api/src/internal/types.test.ts rename to packages/keyring-internal-api/src/types.test.ts diff --git a/packages/keyring-api/src/internal/types.ts b/packages/keyring-internal-api/src/types.ts similarity index 84% rename from packages/keyring-api/src/internal/types.ts rename to packages/keyring-internal-api/src/types.ts index 401cc649..a6fa4e9d 100644 --- a/packages/keyring-api/src/internal/types.ts +++ b/packages/keyring-internal-api/src/types.ts @@ -1,16 +1,21 @@ -import type { Infer, Struct } from '@metamask/superstruct'; -import { boolean, string, number } from '@metamask/superstruct'; +/* eslint-disable @typescript-eslint/no-redundant-type-constituents */ +/* eslint-disable @typescript-eslint/no-duplicate-type-constituents */ +// FIXME: Those rules seem to be triggering a false positive on the `InternalAccountStructs` +// and `InternalAccountTypes`. import { BtcAccountType, EthAccountType, KeyringAccountStruct, SolAccountType, -} from '../api'; -import { BtcP2wpkhAccountStruct } from '../btc/types'; -import { EthEoaAccountStruct, EthErc4337AccountStruct } from '../eth/types'; -import { SolDataAccountStruct } from '../sol/types'; -import { exactOptional, object } from '../superstruct'; + BtcP2wpkhAccountStruct, + EthEoaAccountStruct, + EthErc4337AccountStruct, + SolDataAccountStruct, +} from '@metamask/keyring-api'; +import { exactOptional, object } from '@metamask/keyring-utils'; +import type { Infer, Struct } from '@metamask/superstruct'; +import { boolean, string, number } from '@metamask/superstruct'; export type InternalAccountType = | EthAccountType diff --git a/packages/keyring-internal-api/tsconfig.build.json b/packages/keyring-internal-api/tsconfig.build.json new file mode 100644 index 00000000..27b6fc27 --- /dev/null +++ b/packages/keyring-internal-api/tsconfig.build.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.packages.build.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "rootDir": "src" + }, + "references": [ + { + "path": "../keyring-api/tsconfig.build.json" + } + ], + "include": ["./src/**/*.ts"], + "exclude": ["./src/**/*.test.ts", "./src/**/*.test-d.ts"] +} diff --git a/packages/keyring-internal-api/tsconfig.json b/packages/keyring-internal-api/tsconfig.json new file mode 100644 index 00000000..b4fcb337 --- /dev/null +++ b/packages/keyring-internal-api/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "baseUrl": "./" + }, + "references": [ + { + "path": "../keyring-api/tsconfig.build.json" + } + ], + "include": ["./src"], + "exclude": ["./dist/**/*"] +} diff --git a/packages/keyring-internal-api/typedoc.json b/packages/keyring-internal-api/typedoc.json new file mode 100644 index 00000000..b527b625 --- /dev/null +++ b/packages/keyring-internal-api/typedoc.json @@ -0,0 +1,6 @@ +{ + "entryPoints": ["./src/index.ts"], + "excludePrivate": true, + "hideGenerator": true, + "out": "docs" +} diff --git a/packages/keyring-snap-bridge/package.json b/packages/keyring-snap-bridge/package.json index 34adc791..50b5c210 100644 --- a/packages/keyring-snap-bridge/package.json +++ b/packages/keyring-snap-bridge/package.json @@ -24,7 +24,7 @@ "dist/" ], "scripts": { - "build": "yarn workspace $(jq -r '.name' ../keyring-api/package.json) build:force && ts-bridge --project tsconfig.build.json --no-references", + "build": "ts-bridge --project tsconfig.build.json --no-references", "build:clean": "yarn build --clean", "build:docs": "typedoc", "changelog:update": "../../scripts/update-changelog.sh @metamask/eth-snap-keyring", @@ -37,6 +37,9 @@ "dependencies": { "@ethereumjs/tx": "^4.2.0", "@metamask/eth-sig-util": "^8.0.0", + "@metamask/keyring-api": "workspace:^", + "@metamask/keyring-internal-api": "workspace:^", + "@metamask/keyring-snap-internal-client": "workspace:^", "@metamask/snaps-controllers": "^9.10.0", "@metamask/snaps-sdk": "^6.7.0", "@metamask/snaps-utils": "^8.3.0", @@ -49,6 +52,7 @@ "@lavamoat/allow-scripts": "^3.2.1", "@lavamoat/preinstall-always-fail": "^2.1.0", "@metamask/auto-changelog": "^3.4.4", + "@metamask/providers": "^18.1.0", "@ts-bridge/cli": "^0.6.0", "@types/jest": "^29.5.12", "@types/node": "^20.12.12", @@ -62,7 +66,8 @@ "typescript": "~5.6.3" }, "peerDependencies": { - "@metamask/keyring-api": "workspace:^" + "@metamask/keyring-api": "workspace:^", + "@metamask/providers": "^18.1.0" }, "engines": { "node": "^18.18 || >=20" diff --git a/packages/keyring-snap-bridge/src/SnapKeyring.test.ts b/packages/keyring-snap-bridge/src/SnapKeyring.test.ts index 57fb9daa..a4552236 100644 --- a/packages/keyring-snap-bridge/src/SnapKeyring.test.ts +++ b/packages/keyring-snap-bridge/src/SnapKeyring.test.ts @@ -1,18 +1,18 @@ import { TransactionFactory } from '@ethereumjs/tx'; import { SignTypedDataVersion } from '@metamask/eth-sig-util'; import type { + KeyringAccount, + KeyringExecutionContext, EthBaseUserOperation, EthUserOperation, EthUserOperationPatch, - KeyringAccount, - KeyringExecutionContext, } from '@metamask/keyring-api'; import { BtcAccountType, - BtcMethod, EthAccountType, - EthMethod, SolAccountType, + BtcMethod, + EthMethod, SolMethod, KeyringEvent, } from '@metamask/keyring-api'; diff --git a/packages/keyring-snap-bridge/src/SnapKeyring.ts b/packages/keyring-snap-bridge/src/SnapKeyring.ts index b3524369..a2c7e3aa 100644 --- a/packages/keyring-snap-bridge/src/SnapKeyring.ts +++ b/packages/keyring-snap-bridge/src/SnapKeyring.ts @@ -1,22 +1,11 @@ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */ // This rule seems to be triggering a false positive. Possibly eslint is not -// inferring the EthMethod, BtcMethod, and InternalAccount types correctly. +// inferring the `EthMethod`, `BtcMethod`, and `InternalAccount` types correctly. import type { TypedTransaction } from '@ethereumjs/tx'; import { TransactionFactory } from '@ethereumjs/tx'; import type { TypedDataV1, TypedMessage } from '@metamask/eth-sig-util'; import { SignTypedDataVersion } from '@metamask/eth-sig-util'; -import type { - BtcMethod, - EthBaseTransaction, - EthBaseUserOperation, - EthUserOperation, - EthUserOperationPatch, - InternalAccount, - KeyringAccount, - KeyringExecutionContext, - KeyringResponse, -} from '@metamask/keyring-api'; import { AccountCreatedEventStruct, AccountDeletedEventStruct, @@ -30,6 +19,18 @@ import { RequestApprovedEventStruct, RequestRejectedEventStruct, } from '@metamask/keyring-api'; +import type { + KeyringAccount, + KeyringExecutionContext, + KeyringResponse, + BtcMethod, + EthBaseTransaction, + EthBaseUserOperation, + EthUserOperation, + EthUserOperationPatch, +} from '@metamask/keyring-api'; +import type { InternalAccount } from '@metamask/keyring-internal-api'; +import { KeyringSnapControllerClient } from '@metamask/keyring-snap-internal-client'; import type { SnapController } from '@metamask/snaps-controllers'; import type { SnapId } from '@metamask/snaps-sdk'; import type { Snap } from '@metamask/snaps-utils'; @@ -44,7 +45,6 @@ import { EventEmitter } from 'events'; import { v4 as uuid } from 'uuid'; import { DeferredPromise } from './DeferredPromise'; -import { KeyringSnapControllerClient } from './KeyringSnapControllerClient'; import { projectLogger as log } from './logger'; import { SnapIdMap } from './SnapIdMap'; import type { SnapMessage } from './types'; diff --git a/packages/keyring-snap-bridge/src/index.ts b/packages/keyring-snap-bridge/src/index.ts index dab23fcf..d01d020e 100644 --- a/packages/keyring-snap-bridge/src/index.ts +++ b/packages/keyring-snap-bridge/src/index.ts @@ -1,3 +1,2 @@ export * from './types'; export * from './SnapKeyring'; -export * from './KeyringSnapControllerClient'; diff --git a/packages/keyring-snap-bridge/tsconfig.build.json b/packages/keyring-snap-bridge/tsconfig.build.json index 9a6ec1d7..5886d6ea 100644 --- a/packages/keyring-snap-bridge/tsconfig.build.json +++ b/packages/keyring-snap-bridge/tsconfig.build.json @@ -10,7 +10,11 @@ "lib": ["ES2020"], "target": "es2020" }, - "references": [{ "path": "../keyring-api/tsconfig.build.json" }], + "references": [ + { "path": "../keyring-api/tsconfig.build.json" }, + { "path": "../keyring-internal-api/tsconfig.build.json" }, + { "path": "../keyring-snap-internal-client/tsconfig.build.json" } + ], "include": ["./src/**/*.ts"], "exclude": ["./src/**/*.test.ts"] } diff --git a/packages/keyring-snap-client/CHANGELOG.md b/packages/keyring-snap-client/CHANGELOG.md new file mode 100644 index 00000000..f4eb4815 --- /dev/null +++ b/packages/keyring-snap-client/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +[Unreleased]: https://github.com/MetaMask/accounts/ diff --git a/packages/keyring-snap-client/README.md b/packages/keyring-snap-client/README.md new file mode 100644 index 00000000..43c20887 --- /dev/null +++ b/packages/keyring-snap-client/README.md @@ -0,0 +1,15 @@ +# MetaMask Keyring Snap Clients + +Clients that can be used to communicate with a Snap Account using various providers. + +## Installation + +`yarn add @metamask/keyring-snap-client` + +or + +`npm install @metamask/keyring-snap-client` + +## Contributing + +This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/accounts#readme). diff --git a/packages/keyring-snap-client/jest.config.js b/packages/keyring-snap-client/jest.config.js new file mode 100644 index 00000000..d6e04ca7 --- /dev/null +++ b/packages/keyring-snap-client/jest.config.js @@ -0,0 +1,28 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +const merge = require('deepmerge'); +const path = require('path'); + +const baseConfig = require('../../jest.config.packages'); + +const displayName = path.basename(__dirname); + +module.exports = merge(baseConfig, { + // The display name when running multiple projects + displayName, + + coveragePathIgnorePatterns: ['./src/tests'], + + // An object that configures minimum threshold enforcement for coverage results + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); diff --git a/packages/keyring-snap-client/package.json b/packages/keyring-snap-client/package.json new file mode 100644 index 00000000..4f6c1cd3 --- /dev/null +++ b/packages/keyring-snap-client/package.json @@ -0,0 +1,93 @@ +{ + "name": "@metamask/keyring-snap-client", + "version": "0.0.1", + "description": "MetaMask Keyring Snap clients", + "keywords": [ + "metamask", + "keyring" + ], + "homepage": "https://github.com/MetaMask/accounts/tree/main/packages/keyring-snap-client#readme", + "bugs": { + "url": "https://github.com/MetaMask/accounts/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/accounts.git" + }, + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "main": "./dist/index.cjs", + "types": "./dist/index.d.cts", + "files": [ + "dist/" + ], + "scripts": { + "build": "ts-bridge --project tsconfig.build.json --no-references", + "build:clean": "yarn build --clean", + "build:docs": "typedoc", + "changelog:update": "../../scripts/update-changelog.sh @metamask/keyring-snap-client", + "changelog:validate": "../../scripts/validate-changelog.sh @metamask/keyring-snap-client", + "publish:preview": "yarn npm publish --tag preview", + "test": "yarn test:source && yarn test:types", + "test:clean": "jest --clearCache", + "test:source": "jest && jest-it-up", + "test:types": "echo 'No tests to run for now'", + "test:watch": "jest --watch" + }, + "dependencies": { + "@metamask/keyring-api": "workspace:^", + "@metamask/keyring-utils": "workspace:^", + "@metamask/superstruct": "^3.1.0", + "@types/uuid": "^9.0.8", + "uuid": "^9.0.1", + "webextension-polyfill": "^0.12.0" + }, + "devDependencies": { + "@lavamoat/allow-scripts": "^3.2.1", + "@lavamoat/preinstall-always-fail": "^2.1.0", + "@metamask/auto-changelog": "^3.4.4", + "@metamask/providers": "^18.1.0", + "@metamask/utils": "^9.3.0", + "@ts-bridge/cli": "^0.6.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.12", + "deepmerge": "^4.2.2", + "depcheck": "^1.4.7", + "jest": "^29.5.0", + "jest-it-up": "^3.1.0", + "rimraf": "^5.0.7", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.2", + "tsd": "^0.31.0", + "typedoc": "^0.25.13", + "typescript": "~5.6.3" + }, + "peerDependencies": { + "@metamask/providers": "^18.1.0" + }, + "engines": { + "node": "^18.18 || >=20" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "lavamoat": { + "allowScripts": { + "@lavamoat/preinstall-always-fail": false + } + }, + "tsd": { + "directory": "src" + } +} diff --git a/packages/keyring-api/src/KeyringClient.test.ts b/packages/keyring-snap-client/src/KeyringClient.test.ts similarity index 98% rename from packages/keyring-api/src/KeyringClient.test.ts rename to packages/keyring-snap-client/src/KeyringClient.test.ts index cca714fc..99da8230 100644 --- a/packages/keyring-api/src/KeyringClient.test.ts +++ b/packages/keyring-snap-client/src/KeyringClient.test.ts @@ -1,10 +1,11 @@ -import { - type KeyringAccount, - type KeyringRequest, - type KeyringResponse, - KeyringClient, - KeyringRpcMethod, -} from '.'; // Import from `index.ts` to test the public API +import type { + KeyringAccount, + KeyringRequest, + KeyringResponse, +} from '@metamask/keyring-api'; +import { KeyringRpcMethod } from '@metamask/keyring-api'; + +import { KeyringClient } from '.'; // Import from `index.ts` to test the public API describe('KeyringClient', () => { const mockSender = { diff --git a/packages/keyring-api/src/KeyringClient.ts b/packages/keyring-snap-client/src/KeyringClient.ts similarity index 95% rename from packages/keyring-api/src/KeyringClient.ts rename to packages/keyring-snap-client/src/KeyringClient.ts index 4de45ac0..ad74d41a 100644 --- a/packages/keyring-api/src/KeyringClient.ts +++ b/packages/keyring-snap-client/src/KeyringClient.ts @@ -1,7 +1,3 @@ -import { assert } from '@metamask/superstruct'; -import type { Json } from '@metamask/utils'; -import { v4 as uuid } from 'uuid'; - import type { Keyring, KeyringAccount, @@ -11,7 +7,8 @@ import type { CaipAssetType, Balance, TransactionsPage, -} from './api'; + Pagination, +} from '@metamask/keyring-api'; import { ApproveRequestResponseStruct, CreateAccountResponseStruct, @@ -27,11 +24,13 @@ import { RejectRequestResponseStruct, SubmitRequestResponseStruct, UpdateAccountResponseStruct, -} from './internal/api'; -import { KeyringRpcMethod } from './internal/rpc'; -import type { JsonRpcRequest } from './JsonRpcRequest'; -import { strictMask } from './superstruct'; -import type { Pagination } from './utils'; + KeyringRpcMethod, +} from '@metamask/keyring-api'; +import type { JsonRpcRequest } from '@metamask/keyring-utils'; +import { strictMask } from '@metamask/keyring-utils'; +import { assert } from '@metamask/superstruct'; +import type { Json } from '@metamask/utils'; +import { v4 as uuid } from 'uuid'; export type Sender = { send(request: JsonRpcRequest): Promise; diff --git a/packages/keyring-api/src/KeyringSnapRpcClient.test.ts b/packages/keyring-snap-client/src/KeyringSnapRpcClient.test.ts similarity index 95% rename from packages/keyring-api/src/KeyringSnapRpcClient.test.ts rename to packages/keyring-snap-client/src/KeyringSnapRpcClient.test.ts index ce2d9b7e..b1b471ea 100644 --- a/packages/keyring-api/src/KeyringSnapRpcClient.test.ts +++ b/packages/keyring-snap-client/src/KeyringSnapRpcClient.test.ts @@ -1,6 +1,6 @@ +import type { KeyringAccount } from '@metamask/keyring-api'; import type { MetaMaskInpageProvider } from '@metamask/providers'; -import type { KeyringAccount } from './api'; import { KeyringSnapRpcClient } from './KeyringSnapRpcClient'; describe('KeyringSnapRpcClient', () => { diff --git a/packages/keyring-api/src/KeyringSnapRpcClient.ts b/packages/keyring-snap-client/src/KeyringSnapRpcClient.ts similarity index 96% rename from packages/keyring-api/src/KeyringSnapRpcClient.ts rename to packages/keyring-snap-client/src/KeyringSnapRpcClient.ts index 17f27d8b..b70019ff 100644 --- a/packages/keyring-api/src/KeyringSnapRpcClient.ts +++ b/packages/keyring-snap-client/src/KeyringSnapRpcClient.ts @@ -1,7 +1,7 @@ +import type { JsonRpcRequest } from '@metamask/keyring-utils'; import type { MetaMaskInpageProvider } from '@metamask/providers'; import type { Json } from '@metamask/utils'; -import type { JsonRpcRequest } from './JsonRpcRequest'; import type { Sender } from './KeyringClient'; import { KeyringClient } from './KeyringClient'; diff --git a/packages/keyring-snap-client/src/index.ts b/packages/keyring-snap-client/src/index.ts new file mode 100644 index 00000000..a2792ea9 --- /dev/null +++ b/packages/keyring-snap-client/src/index.ts @@ -0,0 +1,2 @@ +export * from './KeyringClient'; +export * from './KeyringSnapRpcClient'; diff --git a/packages/keyring-snap-client/tsconfig.build.json b/packages/keyring-snap-client/tsconfig.build.json new file mode 100644 index 00000000..b52bdfe2 --- /dev/null +++ b/packages/keyring-snap-client/tsconfig.build.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.packages.build.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "rootDir": "src", + // FIXME: We should investigate how to get rid of this flag, as this will turn off type + // checking for all *.d.ts files + "skipLibCheck": true + }, + "references": [ + { + "path": "../keyring-api/tsconfig.build.json" + }, + { + "path": "../keyring-utils/tsconfig.build.json" + } + ], + "include": ["./src/**/*.ts"], + "exclude": ["./src/**/*.test.ts", "./src/**/*.test-d.ts"] +} diff --git a/packages/keyring-snap-client/tsconfig.json b/packages/keyring-snap-client/tsconfig.json new file mode 100644 index 00000000..bba26c34 --- /dev/null +++ b/packages/keyring-snap-client/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["./src"], + "exclude": ["./dist/**/*"] +} diff --git a/packages/keyring-snap-client/typedoc.json b/packages/keyring-snap-client/typedoc.json new file mode 100644 index 00000000..b527b625 --- /dev/null +++ b/packages/keyring-snap-client/typedoc.json @@ -0,0 +1,6 @@ +{ + "entryPoints": ["./src/index.ts"], + "excludePrivate": true, + "hideGenerator": true, + "out": "docs" +} diff --git a/packages/keyring-snap-internal-client/CHANGELOG.md b/packages/keyring-snap-internal-client/CHANGELOG.md new file mode 100644 index 00000000..f4eb4815 --- /dev/null +++ b/packages/keyring-snap-internal-client/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +[Unreleased]: https://github.com/MetaMask/accounts/ diff --git a/packages/keyring-snap-internal-client/README.md b/packages/keyring-snap-internal-client/README.md new file mode 100644 index 00000000..270b3d25 --- /dev/null +++ b/packages/keyring-snap-internal-client/README.md @@ -0,0 +1,15 @@ +# MetaMask Keyring Snap Clients + +Clients that can be used to communicate with a Snap Account using various providers. + +## Installation + +`yarn add @metamask/keyring-snap-internal-client` + +or + +`npm install @metamask/keyring-snap-internal-client` + +## Contributing + +This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/accounts#readme). diff --git a/packages/keyring-snap-internal-client/jest.config.js b/packages/keyring-snap-internal-client/jest.config.js new file mode 100644 index 00000000..d6e04ca7 --- /dev/null +++ b/packages/keyring-snap-internal-client/jest.config.js @@ -0,0 +1,28 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +const merge = require('deepmerge'); +const path = require('path'); + +const baseConfig = require('../../jest.config.packages'); + +const displayName = path.basename(__dirname); + +module.exports = merge(baseConfig, { + // The display name when running multiple projects + displayName, + + coveragePathIgnorePatterns: ['./src/tests'], + + // An object that configures minimum threshold enforcement for coverage results + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); diff --git a/packages/keyring-snap-internal-client/package.json b/packages/keyring-snap-internal-client/package.json new file mode 100644 index 00000000..b118568c --- /dev/null +++ b/packages/keyring-snap-internal-client/package.json @@ -0,0 +1,97 @@ +{ + "name": "@metamask/keyring-snap-internal-client", + "version": "0.0.1", + "description": "MetaMask Keyring Snap internal clients", + "keywords": [ + "metamask", + "keyring" + ], + "homepage": "https://github.com/MetaMask/accounts/tree/main/packages/keyring-snap-internal-client#readme", + "bugs": { + "url": "https://github.com/MetaMask/accounts/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/accounts.git" + }, + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "main": "./dist/index.cjs", + "types": "./dist/index.d.cts", + "files": [ + "dist/" + ], + "scripts": { + "build": "ts-bridge --project tsconfig.build.json --no-references", + "build:clean": "yarn build --clean", + "build:docs": "typedoc", + "changelog:update": "../../scripts/update-changelog.sh @metamask/keyring-snap-internal-client", + "changelog:validate": "../../scripts/validate-changelog.sh @metamask/keyring-snap-internal-client", + "publish:preview": "yarn npm publish --tag preview", + "test": "yarn test:source && yarn test:types", + "test:clean": "jest --clearCache", + "test:source": "jest && jest-it-up", + "test:types": "echo 'No tests to run for now'", + "test:watch": "jest --watch" + }, + "dependencies": { + "@metamask/keyring-api": "workspace:^", + "@metamask/keyring-snap-client": "workspace:^", + "@metamask/keyring-utils": "workspace:^", + "@metamask/snaps-controllers": "^9.10.0", + "@metamask/snaps-sdk": "^6.7.0", + "@metamask/snaps-utils": "^8.3.0", + "@metamask/superstruct": "^3.1.0", + "@types/uuid": "^9.0.8", + "uuid": "^9.0.1", + "webextension-polyfill": "^0.12.0" + }, + "devDependencies": { + "@lavamoat/allow-scripts": "^3.2.1", + "@lavamoat/preinstall-always-fail": "^2.1.0", + "@metamask/auto-changelog": "^3.4.4", + "@metamask/providers": "^18.1.0", + "@metamask/utils": "^9.3.0", + "@ts-bridge/cli": "^0.6.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.12", + "deepmerge": "^4.2.2", + "depcheck": "^1.4.7", + "jest": "^29.5.0", + "jest-it-up": "^3.1.0", + "rimraf": "^5.0.7", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.2", + "tsd": "^0.31.0", + "typedoc": "^0.25.13", + "typescript": "~5.6.3" + }, + "peerDependencies": { + "@metamask/providers": "^18.1.0" + }, + "engines": { + "node": "^18.18 || >=20" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "lavamoat": { + "allowScripts": { + "@lavamoat/preinstall-always-fail": false + } + }, + "tsd": { + "directory": "src" + } +} diff --git a/packages/keyring-snap-bridge/src/KeyringSnapControllerClient.test.ts b/packages/keyring-snap-internal-client/src/KeyringSnapControllerClient.test.ts similarity index 100% rename from packages/keyring-snap-bridge/src/KeyringSnapControllerClient.test.ts rename to packages/keyring-snap-internal-client/src/KeyringSnapControllerClient.test.ts diff --git a/packages/keyring-snap-bridge/src/KeyringSnapControllerClient.ts b/packages/keyring-snap-internal-client/src/KeyringSnapControllerClient.ts similarity index 95% rename from packages/keyring-snap-bridge/src/KeyringSnapControllerClient.ts rename to packages/keyring-snap-internal-client/src/KeyringSnapControllerClient.ts index c369470f..c5d8d3b0 100644 --- a/packages/keyring-snap-bridge/src/KeyringSnapControllerClient.ts +++ b/packages/keyring-snap-internal-client/src/KeyringSnapControllerClient.ts @@ -1,5 +1,5 @@ -import type { JsonRpcRequest, Sender } from '@metamask/keyring-api'; -import { KeyringClient } from '@metamask/keyring-api'; +import { KeyringClient, type Sender } from '@metamask/keyring-snap-client'; +import type { JsonRpcRequest } from '@metamask/keyring-utils'; import type { SnapController } from '@metamask/snaps-controllers'; import type { SnapId } from '@metamask/snaps-sdk'; import type { HandlerType } from '@metamask/snaps-utils'; @@ -27,7 +27,7 @@ class SnapControllerSender implements Sender { * @param handler - The handler type. */ constructor( - controller: any, + controller: SnapController, snapId: SnapId, origin: string, handler: HandlerType, diff --git a/packages/keyring-snap-internal-client/src/index.ts b/packages/keyring-snap-internal-client/src/index.ts new file mode 100644 index 00000000..bd358351 --- /dev/null +++ b/packages/keyring-snap-internal-client/src/index.ts @@ -0,0 +1 @@ +export * from './KeyringSnapControllerClient'; diff --git a/packages/keyring-snap-internal-client/tsconfig.build.json b/packages/keyring-snap-internal-client/tsconfig.build.json new file mode 100644 index 00000000..d54bf4a4 --- /dev/null +++ b/packages/keyring-snap-internal-client/tsconfig.build.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.packages.build.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "rootDir": "src", + // FIXME: We should investigate how to get rid of this flag, as this will turn off type + // checking for all *.d.ts files + "skipLibCheck": true + }, + "references": [ + { + "path": "../keyring-api/tsconfig.build.json" + }, + { + "path": "../keyring-snap-client/tsconfig.build.json" + } + ], + "include": ["./src/**/*.ts"], + "exclude": ["./src/**/*.test.ts", "./src/**/*.test-d.ts"] +} diff --git a/packages/keyring-snap-internal-client/tsconfig.json b/packages/keyring-snap-internal-client/tsconfig.json new file mode 100644 index 00000000..bba26c34 --- /dev/null +++ b/packages/keyring-snap-internal-client/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["./src"], + "exclude": ["./dist/**/*"] +} diff --git a/packages/keyring-snap-internal-client/typedoc.json b/packages/keyring-snap-internal-client/typedoc.json new file mode 100644 index 00000000..b527b625 --- /dev/null +++ b/packages/keyring-snap-internal-client/typedoc.json @@ -0,0 +1,6 @@ +{ + "entryPoints": ["./src/index.ts"], + "excludePrivate": true, + "hideGenerator": true, + "out": "docs" +} diff --git a/packages/keyring-snap-sdk/CHANGELOG.md b/packages/keyring-snap-sdk/CHANGELOG.md new file mode 100644 index 00000000..f4eb4815 --- /dev/null +++ b/packages/keyring-snap-sdk/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +[Unreleased]: https://github.com/MetaMask/accounts/ diff --git a/packages/keyring-snap-sdk/README.md b/packages/keyring-snap-sdk/README.md new file mode 100644 index 00000000..7e1c372c --- /dev/null +++ b/packages/keyring-snap-sdk/README.md @@ -0,0 +1,15 @@ +# MetaMask Keyring Snap SDK + +Various JavaScript/TypeScript utilities that can be used by Snap Accounts. + +## Installation + +`yarn add @metamask/keyring-snap-sdk` + +or + +`npm install @metamask/keyring-snap-sdk` + +## Contributing + +This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/accounts#readme). diff --git a/packages/keyring-snap-sdk/jest.config.js b/packages/keyring-snap-sdk/jest.config.js new file mode 100644 index 00000000..d6e04ca7 --- /dev/null +++ b/packages/keyring-snap-sdk/jest.config.js @@ -0,0 +1,28 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +const merge = require('deepmerge'); +const path = require('path'); + +const baseConfig = require('../../jest.config.packages'); + +const displayName = path.basename(__dirname); + +module.exports = merge(baseConfig, { + // The display name when running multiple projects + displayName, + + coveragePathIgnorePatterns: ['./src/tests'], + + // An object that configures minimum threshold enforcement for coverage results + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); diff --git a/packages/keyring-snap-sdk/package.json b/packages/keyring-snap-sdk/package.json new file mode 100644 index 00000000..7b337af4 --- /dev/null +++ b/packages/keyring-snap-sdk/package.json @@ -0,0 +1,91 @@ +{ + "name": "@metamask/keyring-snap-sdk", + "version": "0.0.1", + "description": "MetaMask Keyring Snap SDK", + "keywords": [ + "metamask", + "keyring" + ], + "homepage": "https://github.com/MetaMask/accounts/tree/main/packages/keyring-snap-sdk#readme", + "bugs": { + "url": "https://github.com/MetaMask/accounts/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/accounts.git" + }, + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "main": "./dist/index.cjs", + "types": "./dist/index.d.cts", + "files": [ + "dist/" + ], + "scripts": { + "build": "ts-bridge --project tsconfig.build.json --no-references", + "build:clean": "yarn build --clean", + "build:docs": "typedoc", + "changelog:update": "../../scripts/update-changelog.sh @metamask/keyring-snap-sdk", + "changelog:validate": "../../scripts/validate-changelog.sh @metamask/keyring-snap-sdk", + "publish:preview": "yarn npm publish --tag preview", + "test": "yarn test:source && yarn test:types", + "test:clean": "jest --clearCache", + "test:source": "jest && jest-it-up", + "test:types": "echo 'No tests to run for now'", + "test:watch": "jest --watch" + }, + "dependencies": { + "@metamask/keyring-utils": "workspace:^", + "@metamask/snaps-sdk": "^6.7.0", + "@metamask/superstruct": "^3.1.0", + "@metamask/utils": "^9.3.0" + }, + "devDependencies": { + "@lavamoat/allow-scripts": "^3.2.1", + "@lavamoat/preinstall-always-fail": "^2.1.0", + "@metamask/auto-changelog": "^3.4.4", + "@metamask/keyring-api": "workspace:^", + "@metamask/providers": "^18.1.0", + "@ts-bridge/cli": "^0.6.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.12", + "deepmerge": "^4.2.2", + "depcheck": "^1.4.7", + "jest": "^29.5.0", + "jest-it-up": "^3.1.0", + "rimraf": "^5.0.7", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.2", + "tsd": "^0.31.0", + "typedoc": "^0.25.13", + "typescript": "~5.6.3" + }, + "peerDependencies": { + "@metamask/providers": "^18.1.0" + }, + "engines": { + "node": "^18.18 || >=20" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "lavamoat": { + "allowScripts": { + "@lavamoat/preinstall-always-fail": false + } + }, + "tsd": { + "directory": "src" + } +} diff --git a/packages/keyring-snap-sdk/src/index.ts b/packages/keyring-snap-sdk/src/index.ts new file mode 100644 index 00000000..4391239d --- /dev/null +++ b/packages/keyring-snap-sdk/src/index.ts @@ -0,0 +1,2 @@ +export * from './rpc-handler'; +export * from './snap-utils'; diff --git a/packages/keyring-api/src/rpc-handler.test.ts b/packages/keyring-snap-sdk/src/rpc-handler.test.ts similarity index 98% rename from packages/keyring-api/src/rpc-handler.test.ts rename to packages/keyring-snap-sdk/src/rpc-handler.test.ts index 5e340f42..980e85ae 100644 --- a/packages/keyring-api/src/rpc-handler.test.ts +++ b/packages/keyring-snap-sdk/src/rpc-handler.test.ts @@ -1,7 +1,7 @@ -import type { Keyring } from './api'; -import type { GetAccountBalancesRequest } from './internal'; -import { KeyringRpcMethod, isKeyringRpcMethod } from './internal/rpc'; -import type { JsonRpcRequest } from './JsonRpcRequest'; +import { KeyringRpcMethod, isKeyringRpcMethod } from '@metamask/keyring-api'; +import type { Keyring, GetAccountBalancesRequest } from '@metamask/keyring-api'; +import type { JsonRpcRequest } from '@metamask/keyring-utils'; + import { handleKeyringRequest } from './rpc-handler'; describe('handleKeyringRequest', () => { diff --git a/packages/keyring-api/src/rpc-handler.ts b/packages/keyring-snap-sdk/src/rpc-handler.ts similarity index 95% rename from packages/keyring-api/src/rpc-handler.ts rename to packages/keyring-snap-sdk/src/rpc-handler.ts index a943acae..f3ed5254 100644 --- a/packages/keyring-api/src/rpc-handler.ts +++ b/packages/keyring-snap-sdk/src/rpc-handler.ts @@ -1,8 +1,6 @@ -import { assert } from '@metamask/superstruct'; -import type { Json } from '@metamask/utils'; - -import type { Keyring } from './api'; +import type { Keyring } from '@metamask/keyring-api'; import { + KeyringRpcMethod, GetAccountRequestStruct, CreateAccountRequestStruct, ListAccountTransactionsRequestStruct, @@ -17,10 +15,11 @@ import { ListAccountsRequestStruct, ListRequestsRequestStruct, GetAccountBalancesRequestStruct, -} from './internal/api'; -import { KeyringRpcMethod } from './internal/rpc'; -import type { JsonRpcRequest } from './JsonRpcRequest'; -import { JsonRpcRequestStruct } from './JsonRpcRequest'; +} from '@metamask/keyring-api'; +import type { JsonRpcRequest } from '@metamask/keyring-utils'; +import { JsonRpcRequestStruct } from '@metamask/keyring-utils'; +import { assert } from '@metamask/superstruct'; +import type { Json } from '@metamask/utils'; /** * Error thrown when a keyring JSON-RPC method is not supported. diff --git a/packages/keyring-api/src/snap-utils.test.ts b/packages/keyring-snap-sdk/src/snap-utils.test.ts similarity index 91% rename from packages/keyring-api/src/snap-utils.test.ts rename to packages/keyring-snap-sdk/src/snap-utils.test.ts index cd7694fb..77814d09 100644 --- a/packages/keyring-api/src/snap-utils.test.ts +++ b/packages/keyring-snap-sdk/src/snap-utils.test.ts @@ -1,4 +1,5 @@ -import { KeyringEvent } from './events'; +import { KeyringEvent } from '@metamask/keyring-api'; + import { emitSnapKeyringEvent } from './snap-utils'; describe('emitSnapKeyringEvent', () => { diff --git a/packages/keyring-api/src/snap-utils.ts b/packages/keyring-snap-sdk/src/snap-utils.ts similarity index 90% rename from packages/keyring-api/src/snap-utils.ts rename to packages/keyring-snap-sdk/src/snap-utils.ts index c77d0cc9..58734668 100644 --- a/packages/keyring-api/src/snap-utils.ts +++ b/packages/keyring-snap-sdk/src/snap-utils.ts @@ -1,8 +1,7 @@ +import type { KeyringEvent } from '@metamask/keyring-api'; import type { SnapsProvider } from '@metamask/snaps-sdk'; import type { Json } from '@metamask/utils'; -import type { KeyringEvent } from './events'; - /** * Emit a keyring event from a snap. * diff --git a/packages/keyring-snap-sdk/tsconfig.build.json b/packages/keyring-snap-sdk/tsconfig.build.json new file mode 100644 index 00000000..29973f41 --- /dev/null +++ b/packages/keyring-snap-sdk/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.packages.build.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "rootDir": "src", + // FIXME: We should investigate how to get rid of this flag, as this will turn off type + // checking for all *.d.ts files + "skipLibCheck": true + }, + "references": [ + { + "path": "../keyring-api/tsconfig.build.json" + } + ], + "include": ["./src/**/*.ts"], + "exclude": ["./src/**/*.test.ts", "./src/**/*.test-d.ts"] +} diff --git a/packages/keyring-snap-sdk/tsconfig.json b/packages/keyring-snap-sdk/tsconfig.json new file mode 100644 index 00000000..bba26c34 --- /dev/null +++ b/packages/keyring-snap-sdk/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["./src"], + "exclude": ["./dist/**/*"] +} diff --git a/packages/keyring-snap-sdk/typedoc.json b/packages/keyring-snap-sdk/typedoc.json new file mode 100644 index 00000000..b527b625 --- /dev/null +++ b/packages/keyring-snap-sdk/typedoc.json @@ -0,0 +1,6 @@ +{ + "entryPoints": ["./src/index.ts"], + "excludePrivate": true, + "hideGenerator": true, + "out": "docs" +} diff --git a/packages/keyring-utils/CHANGELOG.md b/packages/keyring-utils/CHANGELOG.md new file mode 100644 index 00000000..f4eb4815 --- /dev/null +++ b/packages/keyring-utils/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +[Unreleased]: https://github.com/MetaMask/accounts/ diff --git a/packages/keyring-utils/README.md b/packages/keyring-utils/README.md new file mode 100644 index 00000000..210958b3 --- /dev/null +++ b/packages/keyring-utils/README.md @@ -0,0 +1,15 @@ +# MetaMask Keyring utils + +Various JavaScript/TypeScript utilities required by Keyrings. + +## Installation + +`yarn add @metamask/keyring-utils` + +or + +`npm install @metamask/keyring-utils` + +## Contributing + +This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/accounts#readme). diff --git a/packages/keyring-utils/jest.config.js b/packages/keyring-utils/jest.config.js new file mode 100644 index 00000000..d6e04ca7 --- /dev/null +++ b/packages/keyring-utils/jest.config.js @@ -0,0 +1,28 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +const merge = require('deepmerge'); +const path = require('path'); + +const baseConfig = require('../../jest.config.packages'); + +const displayName = path.basename(__dirname); + +module.exports = merge(baseConfig, { + // The display name when running multiple projects + displayName, + + coveragePathIgnorePatterns: ['./src/tests'], + + // An object that configures minimum threshold enforcement for coverage results + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); diff --git a/packages/keyring-utils/package.json b/packages/keyring-utils/package.json new file mode 100644 index 00000000..862f4690 --- /dev/null +++ b/packages/keyring-utils/package.json @@ -0,0 +1,87 @@ +{ + "name": "@metamask/keyring-utils", + "version": "0.0.1", + "description": "MetaMask Keyring utils", + "keywords": [ + "metamask", + "keyring" + ], + "homepage": "https://github.com/MetaMask/accounts/tree/main/packages/keyring-utils#readme", + "bugs": { + "url": "https://github.com/MetaMask/accounts/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/accounts.git" + }, + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "main": "./dist/index.cjs", + "types": "./dist/index.d.cts", + "files": [ + "dist/" + ], + "scripts": { + "build": "ts-bridge --project tsconfig.build.json --no-references", + "build:clean": "yarn build --clean", + "build:docs": "typedoc", + "changelog:update": "../../scripts/update-changelog.sh @metamask/keyring-utils", + "changelog:validate": "../../scripts/validate-changelog.sh @metamask/keyring-utils", + "publish:preview": "yarn npm publish --tag preview", + "test": "yarn test:source && yarn test:types", + "test:clean": "jest --clearCache", + "test:source": "jest && jest-it-up", + "test:types": "../../scripts/tsd-test.sh ./src", + "test:watch": "jest --watch" + }, + "dependencies": { + "@metamask/superstruct": "^3.1.0", + "@metamask/utils": "^9.3.0" + }, + "devDependencies": { + "@lavamoat/allow-scripts": "^3.2.1", + "@lavamoat/preinstall-always-fail": "^2.1.0", + "@metamask/auto-changelog": "^3.4.4", + "@ts-bridge/cli": "^0.6.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.12", + "deepmerge": "^4.2.2", + "depcheck": "^1.4.7", + "jest": "^29.5.0", + "jest-it-up": "^3.1.0", + "rimraf": "^5.0.7", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.2", + "tsd": "^0.31.0", + "typedoc": "^0.25.13", + "typescript": "~5.6.3" + }, + "engines": { + "node": "^18.18 || >=20" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "lavamoat": { + "allowScripts": { + "@lavamoat/preinstall-always-fail": false + } + }, + "tsd": { + "directory": "src", + "compilerOptions": { + "composite": "false" + } + } +} diff --git a/packages/keyring-api/src/JsonRpcRequest.test.ts b/packages/keyring-utils/src/JsonRpcRequest.test.ts similarity index 100% rename from packages/keyring-api/src/JsonRpcRequest.test.ts rename to packages/keyring-utils/src/JsonRpcRequest.test.ts diff --git a/packages/keyring-api/src/JsonRpcRequest.ts b/packages/keyring-utils/src/JsonRpcRequest.ts similarity index 90% rename from packages/keyring-api/src/JsonRpcRequest.ts rename to packages/keyring-utils/src/JsonRpcRequest.ts index 16f80c81..f4fc170d 100644 --- a/packages/keyring-api/src/JsonRpcRequest.ts +++ b/packages/keyring-utils/src/JsonRpcRequest.ts @@ -9,7 +9,7 @@ import { import type { Infer } from '@metamask/superstruct'; import { JsonStruct } from '@metamask/utils'; -import { exactOptional, object } from './superstruct'; +import { exactOptional, object } from '.'; export const JsonRpcRequestStruct = object({ jsonrpc: literal('2.0'), diff --git a/packages/keyring-utils/src/index.ts b/packages/keyring-utils/src/index.ts new file mode 100644 index 00000000..695ccde5 --- /dev/null +++ b/packages/keyring-utils/src/index.ts @@ -0,0 +1,4 @@ +export * from './types'; +export * from './typing'; +export * from './superstruct'; +export * from './JsonRpcRequest'; diff --git a/packages/keyring-api/src/superstruct.test-d.ts b/packages/keyring-utils/src/superstruct.test-d.ts similarity index 100% rename from packages/keyring-api/src/superstruct.test-d.ts rename to packages/keyring-utils/src/superstruct.test-d.ts diff --git a/packages/keyring-api/src/superstruct.test.ts b/packages/keyring-utils/src/superstruct.test.ts similarity index 83% rename from packages/keyring-api/src/superstruct.test.ts rename to packages/keyring-utils/src/superstruct.test.ts index a0846944..906ab0bf 100644 --- a/packages/keyring-api/src/superstruct.test.ts +++ b/packages/keyring-utils/src/superstruct.test.ts @@ -12,7 +12,7 @@ import { } from '@metamask/superstruct'; import { isPlainObject } from '@metamask/utils'; -import { exactOptional, object, selectiveUnion } from '.'; +import { exactOptional, object, strictMask, selectiveUnion } from '.'; describe('exactOptional', () => { const simpleStruct = object({ @@ -131,3 +131,26 @@ describe('selectiveUnion', () => { expect(create(obj, struct)).toStrictEqual(want); }); }); + +describe('strictMask', () => { + const struct = object({ + foo: string(), + bar: number(), + }); + + it('is valid', () => { + expect(() => strictMask({ foo: 'foo', bar: 1 }, struct)).not.toThrow(); + }); + + it('fails if the object is not strictly matching', () => { + expect(() => strictMask({ foo: 'foo', bar: 1, zzz: [] }, struct)).toThrow( + 'At path: zzz -- Expected a value of type `never`, but received: ``', + ); + expect(() => strictMask({ foo: 'foo' }, struct)).toThrow( + 'At path: bar -- Expected a number, but received: undefined', + ); + expect(() => strictMask({ bar: 1 }, struct)).toThrow( + 'At path: foo -- Expected a string, but received: undefined', + ); + }); +}); diff --git a/packages/keyring-api/src/superstruct.ts b/packages/keyring-utils/src/superstruct.ts similarity index 99% rename from packages/keyring-api/src/superstruct.ts rename to packages/keyring-utils/src/superstruct.ts index 370ebd0d..26582cba 100644 --- a/packages/keyring-api/src/superstruct.ts +++ b/packages/keyring-utils/src/superstruct.ts @@ -15,7 +15,7 @@ import type { AnyStruct, } from '@metamask/superstruct'; -import type { Equals } from './utils'; +import type { Equals } from './types'; // eslint-disable-next-line @typescript-eslint/no-unused-vars declare const ExactOptionalSymbol: unique symbol; diff --git a/packages/keyring-api/src/utils/types.test-d.ts b/packages/keyring-utils/src/types.test-d.ts similarity index 100% rename from packages/keyring-api/src/utils/types.test-d.ts rename to packages/keyring-utils/src/types.test-d.ts diff --git a/packages/keyring-utils/src/types.test.ts b/packages/keyring-utils/src/types.test.ts new file mode 100644 index 00000000..3a11fd17 --- /dev/null +++ b/packages/keyring-utils/src/types.test.ts @@ -0,0 +1,73 @@ +import { is, assert } from '@metamask/superstruct'; + +import { StringNumberStruct, UrlStruct, UuidStruct } from './types'; + +describe('UuidStruct', () => { + it('is a valid UUID', () => { + const uuid = '47d782ac-15c8-4c81-8bfe-759ae1be4a3e'; + expect(() => assert(uuid, UuidStruct)).not.toThrow(); + }); + + it.each([ + '', + 'invalid-uuid', + '47d782ac_15c8_4c81_8bfe_759ae1be4a3e', + '47d782ac15c84c818bfe759ae1be4a3e', + ])('fails if the UUID is a valid', (uuid) => { + expect(() => assert(uuid, UuidStruct)).toThrow( + `Expected a value of type \`UuidV4\`, but received: \`"${uuid}"\``, + ); + }); +}); + +describe('UrlStruct', () => { + it('is a valid URL', () => { + const url = 'https://api.example.com'; + expect(() => assert(url, UrlStruct)).not.toThrow(); + }); + + it('is a valid URL with query parameters', () => { + const url = 'https://api.example.com?foo=bar'; + expect(() => assert(url, UrlStruct)).not.toThrow(); + }); + + it('accepts path parameters', () => { + const url = 'https://api.example.com/foo/bar'; + expect(() => assert(url, UrlStruct)).not.toThrow(); + }); + + it('fails if it does not start with http or https', () => { + const url = 'ftp://api.example.com'; + expect(() => assert(url, UrlStruct)).toThrow( + 'Expected a value of type `Url`, but received: `"ftp://api.example.com"`', + ); + }); + + it('fails if the URL is not valid', () => { + const url = 'api.example.com'; // No protocol. + expect(() => assert(url, UrlStruct)).toThrow( + 'Expected a value of type `Url`, but received: `"api.example.com"`', + ); + }); + + it('has to start with http or https', () => { + const url = 'http://api.example.com'; + expect(() => assert(url, UrlStruct)).not.toThrow(); + }); +}); + +describe('StringNumber', () => { + it.each(['0', '0.0', '0.1', '0.19', '00.19', '0.000000000000000000000'])( + 'validates basic number: %s', + (input: string) => { + expect(is(input, StringNumberStruct)).toBe(true); + }, + ); + + it.each(['foobar', 'NaN', '0.123.4', '1e3', undefined, null, 1, true])( + 'fails to validate wrong number: %s', + (input: any) => { + expect(is(input, StringNumberStruct)).toBe(false); + }, + ); +}); diff --git a/packages/keyring-api/src/utils/types.ts b/packages/keyring-utils/src/types.ts similarity index 97% rename from packages/keyring-api/src/utils/types.ts rename to packages/keyring-utils/src/types.ts index d0f17309..f00db535 100644 --- a/packages/keyring-api/src/utils/types.ts +++ b/packages/keyring-utils/src/types.ts @@ -1,6 +1,6 @@ import { define, type Infer } from '@metamask/superstruct'; -import { definePattern } from '../superstruct'; +import { definePattern } from './superstruct'; /** * UUIDv4 struct. diff --git a/packages/keyring-api/src/utils/typing.test-d.ts b/packages/keyring-utils/src/typing.test-d.ts similarity index 100% rename from packages/keyring-api/src/utils/typing.test-d.ts rename to packages/keyring-utils/src/typing.test-d.ts diff --git a/packages/keyring-api/src/utils/typing.test.ts b/packages/keyring-utils/src/typing.test.ts similarity index 100% rename from packages/keyring-api/src/utils/typing.test.ts rename to packages/keyring-utils/src/typing.test.ts diff --git a/packages/keyring-api/src/utils/typing.ts b/packages/keyring-utils/src/typing.ts similarity index 100% rename from packages/keyring-api/src/utils/typing.ts rename to packages/keyring-utils/src/typing.ts diff --git a/packages/keyring-utils/tsconfig.build.json b/packages/keyring-utils/tsconfig.build.json new file mode 100644 index 00000000..dee2ad8c --- /dev/null +++ b/packages/keyring-utils/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.packages.build.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "dist", + "rootDir": "src" + }, + "include": ["./src/**/*.ts"], + "exclude": ["./src/**/*.test.ts", "./src/**/*.test-d.ts"] +} diff --git a/packages/keyring-utils/tsconfig.json b/packages/keyring-utils/tsconfig.json new file mode 100644 index 00000000..bba26c34 --- /dev/null +++ b/packages/keyring-utils/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["./src"], + "exclude": ["./dist/**/*"] +} diff --git a/packages/keyring-utils/typedoc.json b/packages/keyring-utils/typedoc.json new file mode 100644 index 00000000..b527b625 --- /dev/null +++ b/packages/keyring-utils/typedoc.json @@ -0,0 +1,6 @@ +{ + "entryPoints": ["./src/index.ts"], + "excludePrivate": true, + "hideGenerator": true, + "out": "docs" +} diff --git a/scripts/prepare-preview-builds.ts b/scripts/prepare-preview-builds.ts index 65531fdd..d96a1f19 100755 --- a/scripts/prepare-preview-builds.ts +++ b/scripts/prepare-preview-builds.ts @@ -23,6 +23,8 @@ type WorkspacePreviewPackage = WorkspacePackage & { version: string; }; +type DependenciesRecord = Record; + class UsageError extends Error { constructor(message: string) { // 1 because `ts-node` is being used as a launcher, so argv[0] is ts-node "bin.js" @@ -148,8 +150,8 @@ async function updateWorkspacePackagesWithPreviewInfo( const peerDepKey = 'peerDependencies'; if (peerDepKey in pkgJson.content) { const depKey = 'dependencies'; - const deps = pkgJson.content[depKey]; - const peerDeps = pkgJson.content[peerDepKey]; + const deps = pkgJson.content[depKey] as DependenciesRecord; + const peerDeps = pkgJson.content[peerDepKey] as DependenciesRecord; for (const { name, version } of previewPkgs) { // Only consider dependenc that refers to a local workspace package @@ -171,7 +173,7 @@ async function updateWorkspacePackagesWithPreviewInfo( // of that package instead, and `yarn` will resolve this using the global resolutions // (see `updateWorkspaceResolutions`) for (const depKey of ['dependencies', 'devDependencies']) { - const deps = pkgJson.content[depKey]; + const deps = pkgJson.content[depKey] as DependenciesRecord; for (const { name, version } of previewPkgs) { // Only consider dependenc that refers to a local workspace package diff --git a/scripts/tsd-test.sh b/scripts/tsd-test.sh new file mode 100755 index 00000000..1fdfff26 --- /dev/null +++ b/scripts/tsd-test.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -euo pipefail + +# Get the current package test directory +if [[ $# -eq 0 ]]; then + echo "Missing package test directory." + exit 1 +fi + +package_test_dir="$1" + +# For some reason, just running `tsd` with no arguments does not work properly. If you have some +# static errors in your *.test-d.ts, they might not be evaluated. However, specifying each tests +# with `--files` works everytime. So for now, we just use this wrapper for our typing tests. +# NOTE: This directive is expected since we want the output to be splitted: +# shellcheck disable=SC2046 +package_test_files="$(find "${package_test_dir}" -name "*.test-d.ts" -exec echo -n "--files {} " \;)" +if [ "${package_test_files}" == "" ]; then + # If there's no files, we prevent from calling `tsd` with no arguments, otherwise it fallsback + # to the original behavior which is to test every `*d.ts` files. + echo "Nothing to test with tsd." +else + tsd "${package_test_files}" +fi diff --git a/tsconfig.build.json b/tsconfig.build.json index e7fb4d72..47db6edc 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,11 +1,16 @@ { "references": [ { "path": "./packages/keyring-api/tsconfig.build.json" }, + { "path": "./packages/keyring-internal-api/tsconfig.build.json" }, { "path": "./packages/keyring-eth-ledger-bridge/tsconfig.build.json" }, { "path": "./packages/keyring-eth-simple/tsconfig.build.json" }, { "path": "./packages/keyring-eth-trezor/tsconfig.build.json" }, + { "path": "./packages/keyring-eth-hd/tsconfig.build.json" }, { "path": "./packages/keyring-snap-bridge/tsconfig.build.json" }, - { "path": "./packages/keyring-eth-hd/tsconfig.build.json" } + { "path": "./packages/keyring-snap-sdk/tsconfig.build.json" }, + { "path": "./packages/keyring-snap-client/tsconfig.build.json" }, + { "path": "./packages/keyring-snap-internal-client/tsconfig.build.json" }, + { "path": "./packages/keyring-utils/tsconfig.build.json" } ], "files": [], "include": [] diff --git a/tsconfig.json b/tsconfig.json index de576d01..cf093be8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,11 +2,16 @@ "compilerOptions": { "esModuleInterop": true, "noEmit": true }, "references": [ { "path": "./packages/keyring-api" }, + { "path": "./packages/keyring-internal-api" }, { "path": "./packages/keyring-eth-ledger-bridge" }, { "path": "./packages/keyring-eth-simple" }, { "path": "./packages/keyring-eth-trezor" }, + { "path": "./packages/keyring-eth-hd" }, { "path": "./packages/keyring-snap-bridge" }, - { "path": "./packages/keyring-eth-hd" } + { "path": "./packages/keyring-snap-client" }, + { "path": "./packages/keyring-snap-internal-client" }, + { "path": "./packages/keyring-snap-sdk" }, + { "path": "./packages/keyring-utils" } ], "files": [], "include": [] diff --git a/tsconfig.packages.json b/tsconfig.packages.json index 84712874..847976f5 100644 --- a/tsconfig.packages.json +++ b/tsconfig.packages.json @@ -1,5 +1,9 @@ { "compilerOptions": { + // NOTE: For some reason, this flag has to be disabled for tsd. See: + // https://stackoverflow.com/a/70964056 + // It is required to build the entire project though, so this flag will be disabled explicitly + // in every package.json that uses tsd for their typing tests. "composite": true, "esModuleInterop": true, "exactOptionalPropertyTypes": true, diff --git a/tsconfig.scripts.json b/tsconfig.scripts.json new file mode 100644 index 00000000..faac62b4 --- /dev/null +++ b/tsconfig.scripts.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "target": "es6" + }, + "include": ["./scripts/**/*.ts"], + "exclude": ["**/node_modules"] +} diff --git a/yarn.lock b/yarn.lock index 03e59f2d..b0c6d446 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1997,6 +1997,10 @@ __metadata: "@lavamoat/preinstall-always-fail": "npm:^2.1.0" "@metamask/auto-changelog": "npm:^3.4.4" "@metamask/eth-sig-util": "npm:^8.0.0" + "@metamask/keyring-api": "workspace:^" + "@metamask/keyring-internal-api": "workspace:^" + "@metamask/keyring-snap-internal-client": "workspace:^" + "@metamask/providers": "npm:^18.1.0" "@metamask/snaps-controllers": "npm:^9.10.0" "@metamask/snaps-sdk": "npm:^6.7.0" "@metamask/snaps-utils": "npm:^8.3.0" @@ -2017,6 +2021,7 @@ __metadata: uuid: "npm:^9.0.1" peerDependencies: "@metamask/keyring-api": "workspace:^" + "@metamask/providers": ^18.1.0 languageName: unknown linkType: soft @@ -2128,27 +2133,118 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-api@workspace:packages/keyring-api": +"@metamask/keyring-api@workspace:^, @metamask/keyring-api@workspace:packages/keyring-api": version: 0.0.0-use.local resolution: "@metamask/keyring-api@workspace:packages/keyring-api" dependencies: "@lavamoat/allow-scripts": "npm:^3.2.1" "@lavamoat/preinstall-always-fail": "npm:^2.1.0" "@metamask/auto-changelog": "npm:^3.4.4" + "@metamask/keyring-utils": "workspace:^" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^9.3.0" + "@ts-bridge/cli": "npm:^0.6.0" + "@types/jest": "npm:^29.5.12" + "@types/node": "npm:^20.12.12" + "@types/webextension-polyfill": "npm:^0.12.1" + bech32: "npm:^2.0.0" + deepmerge: "npm:^4.2.2" + depcheck: "npm:^1.4.7" + jest: "npm:^29.5.0" + jest-it-up: "npm:^3.1.0" + ts-jest: "npm:^29.0.5" + ts-node: "npm:^10.9.2" + tsd: "npm:^0.31.0" + typedoc: "npm:^0.25.13" + typescript: "npm:~5.6.3" + languageName: unknown + linkType: soft + +"@metamask/keyring-internal-api@workspace:^, @metamask/keyring-internal-api@workspace:packages/keyring-internal-api": + version: 0.0.0-use.local + resolution: "@metamask/keyring-internal-api@workspace:packages/keyring-internal-api" + dependencies: + "@lavamoat/allow-scripts": "npm:^3.2.1" + "@lavamoat/preinstall-always-fail": "npm:^2.1.0" + "@metamask/auto-changelog": "npm:^3.4.4" + "@metamask/keyring-api": "workspace:^" + "@metamask/keyring-utils": "workspace:^" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^9.3.0" + "@ts-bridge/cli": "npm:^0.6.0" + "@types/jest": "npm:^29.5.12" + "@types/node": "npm:^20.12.12" + deepmerge: "npm:^4.2.2" + depcheck: "npm:^1.4.7" + jest: "npm:^29.5.0" + jest-it-up: "npm:^3.1.0" + rimraf: "npm:^5.0.7" + ts-jest: "npm:^29.0.5" + ts-node: "npm:^10.9.2" + tsd: "npm:^0.31.0" + typedoc: "npm:^0.25.13" + typescript: "npm:~5.6.3" + languageName: unknown + linkType: soft + +"@metamask/keyring-snap-client@workspace:^, @metamask/keyring-snap-client@workspace:packages/keyring-snap-client": + version: 0.0.0-use.local + resolution: "@metamask/keyring-snap-client@workspace:packages/keyring-snap-client" + dependencies: + "@lavamoat/allow-scripts": "npm:^3.2.1" + "@lavamoat/preinstall-always-fail": "npm:^2.1.0" + "@metamask/auto-changelog": "npm:^3.4.4" + "@metamask/keyring-api": "workspace:^" + "@metamask/keyring-utils": "workspace:^" "@metamask/providers": "npm:^18.1.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^9.3.0" + "@ts-bridge/cli": "npm:^0.6.0" + "@types/jest": "npm:^29.5.12" + "@types/node": "npm:^20.12.12" + "@types/uuid": "npm:^9.0.8" + deepmerge: "npm:^4.2.2" + depcheck: "npm:^1.4.7" + jest: "npm:^29.5.0" + jest-it-up: "npm:^3.1.0" + rimraf: "npm:^5.0.7" + ts-jest: "npm:^29.0.5" + ts-node: "npm:^10.9.2" + tsd: "npm:^0.31.0" + typedoc: "npm:^0.25.13" + typescript: "npm:~5.6.3" + uuid: "npm:^9.0.1" + webextension-polyfill: "npm:^0.12.0" + peerDependencies: + "@metamask/providers": ^18.1.0 + languageName: unknown + linkType: soft + +"@metamask/keyring-snap-internal-client@workspace:^, @metamask/keyring-snap-internal-client@workspace:packages/keyring-snap-internal-client": + version: 0.0.0-use.local + resolution: "@metamask/keyring-snap-internal-client@workspace:packages/keyring-snap-internal-client" + dependencies: + "@lavamoat/allow-scripts": "npm:^3.2.1" + "@lavamoat/preinstall-always-fail": "npm:^2.1.0" + "@metamask/auto-changelog": "npm:^3.4.4" + "@metamask/keyring-api": "workspace:^" + "@metamask/keyring-snap-client": "workspace:^" + "@metamask/keyring-utils": "workspace:^" + "@metamask/providers": "npm:^18.1.0" + "@metamask/snaps-controllers": "npm:^9.10.0" "@metamask/snaps-sdk": "npm:^6.7.0" + "@metamask/snaps-utils": "npm:^8.3.0" "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^9.3.0" "@ts-bridge/cli": "npm:^0.6.0" "@types/jest": "npm:^29.5.12" "@types/node": "npm:^20.12.12" "@types/uuid": "npm:^9.0.8" - "@types/webextension-polyfill": "npm:^0.12.1" - bech32: "npm:^2.0.0" deepmerge: "npm:^4.2.2" depcheck: "npm:^1.4.7" jest: "npm:^29.5.0" jest-it-up: "npm:^3.1.0" + rimraf: "npm:^5.0.7" ts-jest: "npm:^29.0.5" ts-node: "npm:^10.9.2" tsd: "npm:^0.31.0" @@ -2161,6 +2257,62 @@ __metadata: languageName: unknown linkType: soft +"@metamask/keyring-snap-sdk@workspace:packages/keyring-snap-sdk": + version: 0.0.0-use.local + resolution: "@metamask/keyring-snap-sdk@workspace:packages/keyring-snap-sdk" + dependencies: + "@lavamoat/allow-scripts": "npm:^3.2.1" + "@lavamoat/preinstall-always-fail": "npm:^2.1.0" + "@metamask/auto-changelog": "npm:^3.4.4" + "@metamask/keyring-api": "workspace:^" + "@metamask/keyring-utils": "workspace:^" + "@metamask/providers": "npm:^18.1.0" + "@metamask/snaps-sdk": "npm:^6.7.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^9.3.0" + "@ts-bridge/cli": "npm:^0.6.0" + "@types/jest": "npm:^29.5.12" + "@types/node": "npm:^20.12.12" + deepmerge: "npm:^4.2.2" + depcheck: "npm:^1.4.7" + jest: "npm:^29.5.0" + jest-it-up: "npm:^3.1.0" + rimraf: "npm:^5.0.7" + ts-jest: "npm:^29.0.5" + ts-node: "npm:^10.9.2" + tsd: "npm:^0.31.0" + typedoc: "npm:^0.25.13" + typescript: "npm:~5.6.3" + peerDependencies: + "@metamask/providers": ^18.1.0 + languageName: unknown + linkType: soft + +"@metamask/keyring-utils@workspace:^, @metamask/keyring-utils@workspace:packages/keyring-utils": + version: 0.0.0-use.local + resolution: "@metamask/keyring-utils@workspace:packages/keyring-utils" + dependencies: + "@lavamoat/allow-scripts": "npm:^3.2.1" + "@lavamoat/preinstall-always-fail": "npm:^2.1.0" + "@metamask/auto-changelog": "npm:^3.4.4" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^9.3.0" + "@ts-bridge/cli": "npm:^0.6.0" + "@types/jest": "npm:^29.5.12" + "@types/node": "npm:^20.12.12" + deepmerge: "npm:^4.2.2" + depcheck: "npm:^1.4.7" + jest: "npm:^29.5.0" + jest-it-up: "npm:^3.1.0" + rimraf: "npm:^5.0.7" + ts-jest: "npm:^29.0.5" + ts-node: "npm:^10.9.2" + tsd: "npm:^0.31.0" + typedoc: "npm:^0.25.13" + typescript: "npm:~5.6.3" + languageName: unknown + linkType: soft + "@metamask/number-to-bn@npm:^1.7.1": version: 1.7.1 resolution: "@metamask/number-to-bn@npm:1.7.1" @@ -6769,7 +6921,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10": +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7": version: 10.4.5 resolution: "glob@npm:10.4.5" dependencies: @@ -10041,6 +10193,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:^5.0.7": + version: 5.0.10 + resolution: "rimraf@npm:5.0.10" + dependencies: + glob: "npm:^10.3.7" + bin: + rimraf: dist/esm/bin.mjs + checksum: 10/f3b8ce81eecbde4628b07bdf9e2fa8b684e0caea4999acb1e3b0402c695cd41f28cd075609a808e61ce2672f528ca079f675ab1d8e8d5f86d56643a03e0b8d2e + languageName: node + linkType: hard + "ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1, ripemd160@npm:^2.0.2": version: 2.0.2 resolution: "ripemd160@npm:2.0.2"