mirror of
https://github.com/zhigang1992/wallet.git
synced 2026-04-30 13:42:31 +08:00
ci: improve set up, upgrades
This commit is contained in:
4
.github/workflows/build-extension.yml
vendored
4
.github/workflows/build-extension.yml
vendored
@@ -47,8 +47,6 @@ jobs:
|
|||||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
|
||||||
node-version: 14.x
|
|
||||||
|
|
||||||
- name: Install packages
|
- name: Install packages
|
||||||
uses: ./.github/actions/provision
|
uses: ./.github/actions/provision
|
||||||
@@ -83,8 +81,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Set Node Version
|
- name: Set Node Version
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
|
||||||
node-version: 14.x
|
|
||||||
|
|
||||||
- name: Restore cache
|
- name: Restore cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
|
|||||||
82
.github/workflows/code-checks.yml
vendored
82
.github/workflows/code-checks.yml
vendored
@@ -33,23 +33,15 @@ jobs:
|
|||||||
- name: File name checker
|
- name: File name checker
|
||||||
run: yarn lint:filename
|
run: yarn lint:filename
|
||||||
|
|
||||||
audit:
|
lint-commit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/provision
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Audit
|
- name: Lint commit message
|
||||||
run: yarn audit-ci --high --skip-dev
|
uses: wagoid/commitlint-github-action@v4
|
||||||
|
|
||||||
typecheck:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: ./.github/actions/provision
|
|
||||||
|
|
||||||
- name: Typecheck
|
|
||||||
run: yarn typecheck
|
|
||||||
|
|
||||||
lint-deps:
|
lint-deps:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -60,27 +52,6 @@ jobs:
|
|||||||
- name: Lint dependency rules
|
- name: Lint dependency rules
|
||||||
run: yarn lint:deps
|
run: yarn lint:deps
|
||||||
|
|
||||||
locked-versions:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: ./.github/actions/provision
|
|
||||||
|
|
||||||
- name: Check exact versions
|
|
||||||
uses: ./.github/actions/check-version-lock
|
|
||||||
|
|
||||||
test-unit:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: ./.github/actions/provision
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: yarn build
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: yarn test:unit
|
|
||||||
|
|
||||||
lint-message-schema:
|
lint-message-schema:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -112,7 +83,34 @@ jobs:
|
|||||||
|
|
||||||
- run: yarn web-ext lint
|
- run: yarn web-ext lint
|
||||||
|
|
||||||
build:
|
locked-versions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/provision
|
||||||
|
|
||||||
|
- name: Check exact versions
|
||||||
|
uses: ./.github/actions/check-version-lock
|
||||||
|
|
||||||
|
audit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/provision
|
||||||
|
|
||||||
|
- name: Audit
|
||||||
|
run: yarn audit-ci --high --skip-dev
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/provision
|
||||||
|
|
||||||
|
- name: Typecheck
|
||||||
|
run: yarn typecheck
|
||||||
|
|
||||||
|
test-unit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -121,6 +119,18 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: yarn build
|
run: yarn build
|
||||||
|
|
||||||
- name: Build extension
|
- name: Test
|
||||||
|
run: yarn test:unit
|
||||||
|
|
||||||
|
test-build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/provision
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: yarn build
|
||||||
|
|
||||||
|
- name: Package extension
|
||||||
run: sh build-ext.sh
|
run: sh build-ext.sh
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
13
.github/workflows/commit-lint.yml
vendored
13
.github/workflows/commit-lint.yml
vendored
@@ -1,13 +0,0 @@
|
|||||||
name: Commit lint
|
|
||||||
|
|
||||||
on: [pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
commitlint:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- uses: wagoid/commitlint-github-action@v4
|
|
||||||
46
.github/workflows/integration-tests.yml
vendored
46
.github/workflows/integration-tests.yml
vendored
@@ -10,12 +10,12 @@ jobs:
|
|||||||
directories:
|
directories:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
dir: ${{ steps.set-dirs.outputs.dir }}
|
dir: ${{ steps.set-dirs.outputs.TEST_DIRECTORIES }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- id: set-dirs
|
- id: set-dirs
|
||||||
working-directory: ./tests-legacy/integration
|
working-directory: ./tests-legacy/integration
|
||||||
run: echo "::set-output name=dir::$( ls -d */ | xargs -0 | sed 's/\///' | jq -R -s -c 'split("\n")[:-2]')"
|
run: echo "TEST_DIRECTORIES=$( ls -d */ | xargs -0 | sed 's/\///' | jq -R -s -c 'split("\n")[:-2]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
test-integration:
|
test-integration:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -28,33 +28,37 @@ jobs:
|
|||||||
- name: Check out Git repository
|
- name: Check out Git repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- run: echo ${{ fromJson(needs.directories.outputs.dir) }}
|
- name: Get installed Playwright version
|
||||||
- run: echo ${{ matrix.dir }}
|
id: playwright-version
|
||||||
|
run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package.json').devDependencies['@playwright/test'])")" >> $GITHUB_ENV
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v3
|
||||||
name: Cache node_modules
|
id: cache
|
||||||
id: cache-node-modules
|
|
||||||
with:
|
with:
|
||||||
path: '**/node_modules'
|
path: '**/node_modules'
|
||||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**/package.json') }}-${{ env.PLAYWRIGHT_VERSION }}
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- name: Install dependencies
|
||||||
name: Cache playwright
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
id: cache-playwright-browsers
|
env:
|
||||||
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||||
|
run: yarn --frozen-lockfile
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Cache playwright binaries
|
||||||
|
uses: actions/cache@v3
|
||||||
|
id: playwright-cache
|
||||||
with:
|
with:
|
||||||
path: '~/.cache/ms-playwright'
|
path: ~/.cache/ms-playwright
|
||||||
key: ${{ runner.os }}-playwright-browser
|
key: ${{ runner.os }}-playwright-cache-${{ env.PLAYWRIGHT_VERSION }}
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- name: Install Playwright browsers
|
||||||
with:
|
run: yarn playwright install chrome
|
||||||
node-version: 18
|
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||||
|
|
||||||
- name: Install packages
|
- name: Install Playwright deps
|
||||||
uses: ./.github/actions/provision
|
run: yarn playwright install-deps
|
||||||
if: steps.cache-node-modules.outputs.cache-hit != 'true'
|
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||||
|
|
||||||
- name: Install Playwright dependencies
|
|
||||||
run: npx playwright install --with-deps
|
|
||||||
|
|
||||||
- name: Build assets
|
- name: Build assets
|
||||||
run: yarn build:test
|
run: yarn build:test
|
||||||
|
|||||||
2
.github/workflows/playwright.yml
vendored
2
.github/workflows/playwright.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
|||||||
key: ${{ runner.os }}-playwright-cache-${{ env.PLAYWRIGHT_VERSION }}
|
key: ${{ runner.os }}-playwright-cache-${{ env.PLAYWRIGHT_VERSION }}
|
||||||
|
|
||||||
- name: Install Playwright browsers
|
- name: Install Playwright browsers
|
||||||
run: yarn playwright install chrome
|
run: yarn playwright install chrome chromium
|
||||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||||
|
|
||||||
- name: Install Playwright deps
|
- name: Install Playwright deps
|
||||||
|
|||||||
@@ -272,7 +272,7 @@
|
|||||||
"@types/webpack": "5.28.1",
|
"@types/webpack": "5.28.1",
|
||||||
"@types/zxcvbn": "4.4.1",
|
"@types/zxcvbn": "4.4.1",
|
||||||
"@typescript-eslint/parser": "5.59.6",
|
"@typescript-eslint/parser": "5.59.6",
|
||||||
"@vitest/coverage-istanbul": "0.31.0",
|
"@vitest/coverage-istanbul": "0.31.1",
|
||||||
"audit-ci": "6.6.1",
|
"audit-ci": "6.6.1",
|
||||||
"babel-loader": "9.1.2",
|
"babel-loader": "9.1.2",
|
||||||
"base64-loader": "1.0.0",
|
"base64-loader": "1.0.0",
|
||||||
@@ -317,7 +317,7 @@
|
|||||||
"ts-unused-exports": "7.0.3",
|
"ts-unused-exports": "7.0.3",
|
||||||
"tsconfig-paths-webpack-plugin": "4.0.1",
|
"tsconfig-paths-webpack-plugin": "4.0.1",
|
||||||
"typescript": "5.0.4",
|
"typescript": "5.0.4",
|
||||||
"vitest": "0.31.0",
|
"vitest": "0.31.1",
|
||||||
"vm-browserify": "1.1.2",
|
"vm-browserify": "1.1.2",
|
||||||
"web-ext": "7.4.0",
|
"web-ext": "7.4.0",
|
||||||
"web-ext-submit": "7.4.0",
|
"web-ext-submit": "7.4.0",
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const contentSecurityPolicyEnvironment = {
|
|||||||
development:
|
development:
|
||||||
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; frame-src 'none'; frame-ancestors 'none';",
|
"script-src 'self' 'wasm-unsafe-eval'; object-src 'self'; frame-src 'none'; frame-ancestors 'none';",
|
||||||
production:
|
production:
|
||||||
"default-src 'none'; connect-src *; style-src 'unsafe-inline'; img-src 'self' https:; script-src 'self' 'wasm-unsafe-eval'; object-src 'none'; frame-src 'none'; frame-ancestors 'none';",
|
"default-src 'none'; connect-src *; style-src 'unsafe-inline'; img-src 'self' data: https:; script-src 'self' 'wasm-unsafe-eval'; object-src 'none'; frame-src 'none'; frame-ancestors 'none';",
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultIconEnvironment = {
|
const defaultIconEnvironment = {
|
||||||
@@ -42,18 +42,9 @@ const defaultIconEnvironment = {
|
|||||||
|
|
||||||
const browserSpecificConfig = {
|
const browserSpecificConfig = {
|
||||||
firefox: {
|
firefox: {
|
||||||
manifest_version: 2,
|
|
||||||
permissions: ['contextMenus', 'storage', '*://*/*'],
|
|
||||||
background: {
|
background: {
|
||||||
page: 'background.js',
|
scripts: ['background.js'],
|
||||||
},
|
},
|
||||||
web_accessible_resources: ['inpage.js'],
|
|
||||||
browser_action: {
|
|
||||||
default_title: 'Stacks',
|
|
||||||
default_popup: 'popup.html',
|
|
||||||
default_icon: defaultIconEnvironment[NODE_ENV],
|
|
||||||
},
|
|
||||||
content_security_policy: contentSecurityPolicyEnvironment[NODE_ENV],
|
|
||||||
browser_specific_settings: {
|
browser_specific_settings: {
|
||||||
gecko: {
|
gecko: {
|
||||||
id: '{e22ae397-03d7-4622-bd8f-ecaca8c9b277}',
|
id: '{e22ae397-03d7-4622-bd8f-ecaca8c9b277}',
|
||||||
@@ -61,21 +52,9 @@ const browserSpecificConfig = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
chromium: {
|
chromium: {
|
||||||
manifest_version: 3,
|
|
||||||
host_permissions: ['*://*/*'],
|
|
||||||
permissions: ['contextMenus', 'storage'],
|
|
||||||
background: {
|
background: {
|
||||||
service_worker: 'background.js',
|
service_worker: 'background.js',
|
||||||
},
|
},
|
||||||
web_accessible_resources: [{ resources: ['inpage.js'], matches: ['*://*/*'] }],
|
|
||||||
action: {
|
|
||||||
default_title: 'Stacks',
|
|
||||||
default_popup: 'popup.html',
|
|
||||||
default_icon: defaultIconEnvironment[NODE_ENV],
|
|
||||||
},
|
|
||||||
content_security_policy: {
|
|
||||||
extension_pages: contentSecurityPolicyEnvironment[NODE_ENV],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -83,9 +62,11 @@ const browserSpecificConfig = {
|
|||||||
* @type {Manifest} manifest
|
* @type {Manifest} manifest
|
||||||
*/
|
*/
|
||||||
const manifest = {
|
const manifest = {
|
||||||
|
manifest_version: 3,
|
||||||
author: 'Hiro PBC',
|
author: 'Hiro PBC',
|
||||||
description:
|
description:
|
||||||
'Hiro Wallet is a safe way to manage your STX, sign into apps, and protect your funds while interacting with Clarity smart contracts.',
|
'Hiro Wallet is a safe way to manage your STX, sign into apps, and protect your funds while interacting with Clarity smart contracts.',
|
||||||
|
permissions: ['contextMenus', 'storage'],
|
||||||
commands: {
|
commands: {
|
||||||
_execute_browser_action: {
|
_execute_browser_action: {
|
||||||
suggested_key: {
|
suggested_key: {
|
||||||
@@ -95,6 +76,16 @@ const manifest = {
|
|||||||
description: 'Opens Stacks App',
|
description: 'Opens Stacks App',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
host_permissions: ['*://*/*'],
|
||||||
|
content_security_policy: {
|
||||||
|
extension_pages: contentSecurityPolicyEnvironment[NODE_ENV],
|
||||||
|
},
|
||||||
|
web_accessible_resources: [{ resources: ['inpage.js'], matches: ['*://*/*'] }],
|
||||||
|
action: {
|
||||||
|
default_title: 'Stacks',
|
||||||
|
default_popup: 'popup.html',
|
||||||
|
default_icon: defaultIconEnvironment[NODE_ENV],
|
||||||
|
},
|
||||||
options_ui: {
|
options_ui: {
|
||||||
page: 'index.html',
|
page: 'index.html',
|
||||||
open_in_tab: true,
|
open_in_tab: true,
|
||||||
@@ -116,12 +107,8 @@ const name = PREVIEW_RELEASE ? 'Hiro Wallet Preview' : 'Hiro Wallet';
|
|||||||
|
|
||||||
const prodManifest = {
|
const prodManifest = {
|
||||||
name,
|
name,
|
||||||
// CSP loosened to allow `wasm-eval` per
|
|
||||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1268576
|
|
||||||
content_security_policy:
|
|
||||||
"default-src 'none'; connect-src *; style-src 'unsafe-inline'; img-src 'self' data: https:; script-src 'self' 'wasm-eval'; object-src 'none'; frame-src 'none'; frame-ancestors 'none';",
|
|
||||||
icons: generateImageAssetUrlsWithSuffix(PREVIEW_RELEASE ? '-preview' : ''),
|
icons: generateImageAssetUrlsWithSuffix(PREVIEW_RELEASE ? '-preview' : ''),
|
||||||
browser_action: {
|
action: {
|
||||||
default_icon: `assets/connect-logo/Stacks128w${PREVIEW_RELEASE ? '-preview' : ''}.png`,
|
default_icon: `assets/connect-logo/Stacks128w${PREVIEW_RELEASE ? '-preview' : ''}.png`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import { useMemo } from 'react';
|
|||||||
import { generateSecretKey } from '@stacks/wallet-sdk';
|
import { generateSecretKey } from '@stacks/wallet-sdk';
|
||||||
|
|
||||||
import { logger } from '@shared/logger';
|
import { logger } from '@shared/logger';
|
||||||
import { InternalMethods } from '@shared/message-types';
|
import { clearChromeStorage } from '@shared/storage/redux-pesist';
|
||||||
import { sendMessage } from '@shared/messages';
|
|
||||||
import { clearChromeStorage } from '@shared/storage';
|
|
||||||
|
|
||||||
import { queryClient } from '@app/common/persistence';
|
import { queryClient } from '@app/common/persistence';
|
||||||
import { partiallyClearLocalStorage } from '@app/common/store-utils';
|
import { partiallyClearLocalStorage } from '@app/common/store-utils';
|
||||||
@@ -15,6 +13,7 @@ import { useBitcoinClient, useStacksClientAnchored } from '@app/store/common/api
|
|||||||
import { inMemoryKeyActions } from '@app/store/in-memory-key/in-memory-key.actions';
|
import { inMemoryKeyActions } from '@app/store/in-memory-key/in-memory-key.actions';
|
||||||
import { keyActions } from '@app/store/keys/key.actions';
|
import { keyActions } from '@app/store/keys/key.actions';
|
||||||
import { useCurrentKeyDetails } from '@app/store/keys/key.selectors';
|
import { useCurrentKeyDetails } from '@app/store/keys/key.selectors';
|
||||||
|
import { clearWalletSession } from '@app/store/session-restore';
|
||||||
|
|
||||||
import { useAnalytics } from './analytics/use-analytics';
|
import { useAnalytics } from './analytics/use-analytics';
|
||||||
|
|
||||||
@@ -37,10 +36,6 @@ export function useKeyActions() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const secretKey = generateSecretKey(256);
|
const secretKey = generateSecretKey(256);
|
||||||
sendMessage({
|
|
||||||
method: InternalMethods.ShareInMemoryKeyToBackground,
|
|
||||||
payload: { secretKey, keyId: 'default' },
|
|
||||||
});
|
|
||||||
return dispatch(inMemoryKeyActions.generateWalletKey(secretKey));
|
return dispatch(inMemoryKeyActions.generateWalletKey(secretKey));
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -57,7 +52,7 @@ export function useKeyActions() {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async signOut() {
|
async signOut() {
|
||||||
sendMessage({ method: InternalMethods.RemoveInMemoryKeys, payload: undefined });
|
await clearWalletSession();
|
||||||
dispatch(keyActions.signOut());
|
dispatch(keyActions.signOut());
|
||||||
await clearChromeStorage();
|
await clearChromeStorage();
|
||||||
partiallyClearLocalStorage();
|
partiallyClearLocalStorage();
|
||||||
@@ -65,8 +60,8 @@ export function useKeyActions() {
|
|||||||
queryClient.clear();
|
queryClient.clear();
|
||||||
},
|
},
|
||||||
|
|
||||||
lockWallet() {
|
async lockWallet() {
|
||||||
sendMessage({ method: InternalMethods.RemoveInMemoryKeys, payload: undefined });
|
await clearWalletSession();
|
||||||
return dispatch(inMemoryKeyActions.lockWallet());
|
return dispatch(inMemoryKeyActions.lockWallet());
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import toast from 'react-hot-toast';
|
|||||||
import * as reduxPersist from 'redux-persist';
|
import * as reduxPersist from 'redux-persist';
|
||||||
|
|
||||||
import { getLogsFromBrowserStorage } from '@shared/logger-storage';
|
import { getLogsFromBrowserStorage } from '@shared/logger-storage';
|
||||||
import { persistConfig } from '@shared/storage';
|
import { persistConfig } from '@shared/storage/redux-pesist';
|
||||||
|
|
||||||
import { store } from './store';
|
import { store } from './store';
|
||||||
import { stxChainSlice } from './store/chains/stx-chain.slice';
|
import { stxChainSlice } from './store/chains/stx-chain.slice';
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ export function Container() {
|
|||||||
<SwitchAccountDrawer />
|
<SwitchAccountDrawer />
|
||||||
<SettingsDropdown />
|
<SettingsDropdown />
|
||||||
<Toaster position="bottom-center" toastOptions={{ style: { fontSize: '14px' } }} />
|
<Toaster position="bottom-center" toastOptions={{ style: { fontSize: '14px' } }} />
|
||||||
|
|
||||||
<ContainerLayout header={routeHeader}>
|
<ContainerLayout header={routeHeader}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</ContainerLayout>
|
</ContainerLayout>
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import toast from 'react-hot-toast';
|
|||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { InternalMethods } from '@shared/message-types';
|
|
||||||
import { sendMessage } from '@shared/messages';
|
|
||||||
import { RouteUrls } from '@shared/route-urls';
|
import { RouteUrls } from '@shared/route-urls';
|
||||||
|
|
||||||
import { keySlice } from '@app/store/keys/key.slice';
|
import { keySlice } from '@app/store/keys/key.slice';
|
||||||
@@ -29,10 +27,6 @@ export function useTriggerLedgerDeviceRequestStacksKeys() {
|
|||||||
publicKeys,
|
publicKeys,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
// It's possible a user may have first generated a key, then decided
|
|
||||||
// they wanted to pair with Ledger. Here, we kill all in memory keys when
|
|
||||||
// a new Ledger wallet is created
|
|
||||||
sendMessage({ method: InternalMethods.RemoveInMemoryKeys, payload: undefined });
|
|
||||||
navigate(RouteUrls.Home);
|
navigate(RouteUrls.Home);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
import { InternalMethods } from '@shared/message-types';
|
|
||||||
import { initSentry } from '@shared/utils/analytics';
|
import { initSentry } from '@shared/utils/analytics';
|
||||||
import { warnUsersAboutDevToolsDangers } from '@shared/utils/dev-tools-warning-log';
|
import { warnUsersAboutDevToolsDangers } from '@shared/utils/dev-tools-warning-log';
|
||||||
|
|
||||||
import { persistAndRenderApp } from '@app/common/persistence';
|
import { persistAndRenderApp } from '@app/common/persistence';
|
||||||
|
import { restoreWalletSession } from '@app/store/session-restore';
|
||||||
|
|
||||||
import { App } from './app';
|
import { App } from './app';
|
||||||
import { setDebugOnGlobal } from './debug';
|
import { setDebugOnGlobal } from './debug';
|
||||||
import { store } from './store';
|
|
||||||
import { inMemoryKeyActions } from './store/in-memory-key/in-memory-key.actions';
|
|
||||||
|
|
||||||
initSentry();
|
initSentry();
|
||||||
warnUsersAboutDevToolsDangers();
|
warnUsersAboutDevToolsDangers();
|
||||||
@@ -23,19 +21,8 @@ declare global {
|
|||||||
|
|
||||||
window.__APP_VERSION__ = VERSION;
|
window.__APP_VERSION__ = VERSION;
|
||||||
|
|
||||||
async function checkForInMemoryKeys() {
|
|
||||||
return new Promise(resolve =>
|
|
||||||
chrome.runtime.sendMessage({ method: InternalMethods.RequestInMemoryKeys }, resp => {
|
|
||||||
if (!resp) return resolve(true);
|
|
||||||
if (Object.keys(resp).length === 0) return resolve(true);
|
|
||||||
store.dispatch(inMemoryKeyActions.setKeysInMemory(resp));
|
|
||||||
resolve(true);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function renderApp() {
|
async function renderApp() {
|
||||||
await checkForInMemoryKeys();
|
await restoreWalletSession();
|
||||||
const container = document.getElementById('app');
|
const container = document.getElementById('app');
|
||||||
return createRoot(container!).render(<App />);
|
return createRoot(container!).render(<App />);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ export function AccountGate({ children }: AccountGateProps) {
|
|||||||
const currentKeyDetails = useCurrentKeyDetails();
|
const currentKeyDetails = useCurrentKeyDetails();
|
||||||
const currentInMemorySecretKey = useDefaultWalletSecretKey();
|
const currentInMemorySecretKey = useDefaultWalletSecretKey();
|
||||||
|
|
||||||
|
// console.log('currentKeyDetails', currentKeyDetails);
|
||||||
|
// console.log('currentInMemorySecretKey', currentInMemorySecretKey);
|
||||||
|
|
||||||
if (currentKeyDetails?.type === 'ledger') return <>{children}</>;
|
if (currentKeyDetails?.type === 'ledger') return <>{children}</>;
|
||||||
|
|
||||||
if (shouldNavigateToOnboardingStartPage(currentKeyDetails))
|
if (shouldNavigateToOnboardingStartPage(currentKeyDetails))
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { keyActions } from '@app/store/keys/key.actions';
|
|||||||
|
|
||||||
export function useOnSignOut(handler: () => void) {
|
export function useOnSignOut(handler: () => void) {
|
||||||
useOnMount(() => {
|
useOnMount(() => {
|
||||||
chrome.runtime.onMessage.addListener(message => {
|
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||||
if (message?.method === keyActions.signOut.type) handler();
|
if (message?.method === keyActions.signOut.type) handler();
|
||||||
|
sendResponse();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,17 @@ export function useOnOriginTabClose(handler: () => void) {
|
|||||||
const analytics = useAnalytics();
|
const analytics = useAnalytics();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const messageHandler = (message: BackgroundMessages) => {
|
const messageHandler = (
|
||||||
|
message: BackgroundMessages,
|
||||||
|
_sender: chrome.runtime.MessageSender,
|
||||||
|
sendResponse: () => void
|
||||||
|
) => {
|
||||||
if (message.method !== InternalMethods.OriginatingTabClosed) return;
|
if (message.method !== InternalMethods.OriginatingTabClosed) return;
|
||||||
if (message.payload.tabId === tabId) {
|
if (message.payload.tabId === tabId) {
|
||||||
handler();
|
handler();
|
||||||
void analytics.track('requesting_origin_tab_closed_with_pending_action');
|
void analytics.track('requesting_origin_tab_closed_with_pending_action');
|
||||||
}
|
}
|
||||||
|
sendResponse();
|
||||||
};
|
};
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener(messageHandler);
|
chrome.runtime.onMessage.addListener(messageHandler);
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { inMemoryKeyActions } from '@app/store/in-memory-key/in-memory-key.actio
|
|||||||
|
|
||||||
export function useOnWalletLock(handler: () => void) {
|
export function useOnWalletLock(handler: () => void) {
|
||||||
useOnMount(() => {
|
useOnMount(() => {
|
||||||
chrome.runtime.onMessage.addListener(message => {
|
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||||
if (message?.method === inMemoryKeyActions.lockWallet.type) handler();
|
if (message?.method === inMemoryKeyActions.lockWallet.type) handler();
|
||||||
|
sendResponse();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { selectCurrentAccountIndex } from '../keys/key.selectors';
|
|||||||
|
|
||||||
// This is only used when there is a pending transaction request and
|
// This is only used when there is a pending transaction request and
|
||||||
// the user switches accounts during the signing process
|
// the user switches accounts during the signing process
|
||||||
export const hasSwitchedAccountsState = atom<boolean>(false);
|
export const hasSwitchedAccountsState = atom(false);
|
||||||
|
|
||||||
const hasCreatedAccountState = atom<boolean>(false);
|
const hasCreatedAccountState = atom<boolean>(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
import { Wallet } from '@stacks/wallet-sdk';
|
import { Wallet } from '@stacks/wallet-sdk';
|
||||||
import memoize from 'promise-memoize';
|
import memoize from 'promise-memoize';
|
||||||
|
|
||||||
|
import { deriveStacksAccounts } from '@shared/crypto/stacks/derive-stacks-accounts';
|
||||||
import { InternalMethods } from '@shared/message-types';
|
import { InternalMethods } from '@shared/message-types';
|
||||||
import { RequestDerivedStxAccounts } from '@shared/messages';
|
import { RequestDerivedStxAccounts } from '@shared/messages';
|
||||||
|
|
||||||
|
import { delay } from '@app/common/utils';
|
||||||
import { RootState } from '@app/store';
|
import { RootState } from '@app/store';
|
||||||
|
|
||||||
export const selectStacksChain = (state: RootState) => state.chains.stx;
|
export const selectStacksChain = (state: RootState) => state.chains.stx;
|
||||||
|
|
||||||
export const deriveWalletWithAccounts = memoize(
|
const requestDerivedStacksAccountFromBackground = memoize(async () => {
|
||||||
async (secretKey: string, highestAccountIndex: number): Promise<Wallet> => {
|
async (secretKey: string, highestAccountIndex: number): Promise<Wallet> => {
|
||||||
const message: RequestDerivedStxAccounts = {
|
const message: RequestDerivedStxAccounts = {
|
||||||
method: InternalMethods.RequestDerivedStxAccounts,
|
method: InternalMethods.RequestDerivedStxAccounts,
|
||||||
payload: { secretKey, highestAccountIndex },
|
payload: { secretKey, highestAccountIndex },
|
||||||
};
|
};
|
||||||
return new Promise(resolve => chrome.runtime.sendMessage(message, resp => resolve(resp)));
|
return new Promise(resolve => chrome.runtime.sendMessage(message, resp => resolve(resp)));
|
||||||
}
|
};
|
||||||
);
|
});
|
||||||
|
|
||||||
|
export async function deriveWalletWithAccounts(secretKey: string, highestAccountIndex: number) {
|
||||||
|
const cachedResult = await Promise.race([
|
||||||
|
requestDerivedStacksAccountFromBackground(),
|
||||||
|
delay(1000),
|
||||||
|
]);
|
||||||
|
if (cachedResult) return cachedResult as Wallet;
|
||||||
|
return deriveStacksAccounts(secretKey, highestAccountIndex);
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
import { PersistPartial } from 'redux-persist/es/persistReducer';
|
import { PersistPartial } from 'redux-persist/es/persistReducer';
|
||||||
|
|
||||||
import { IS_DEV_ENV } from '@shared/environment';
|
import { IS_DEV_ENV } from '@shared/environment';
|
||||||
import { persistConfig } from '@shared/storage';
|
import { persistConfig } from '@shared/storage/redux-pesist';
|
||||||
|
|
||||||
import { analyticsSlice } from './analytics/analytics.slice';
|
import { analyticsSlice } from './analytics/analytics.slice';
|
||||||
import { appPermissionsSlice } from './app-permissions/app-permissions.slice';
|
import { appPermissionsSlice } from './app-permissions/app-permissions.slice';
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { AddressVersion } from '@stacks/transactions';
|
|||||||
|
|
||||||
import { decryptMnemonic, encryptMnemonic } from '@shared/crypto/mnemonic-encryption';
|
import { decryptMnemonic, encryptMnemonic } from '@shared/crypto/mnemonic-encryption';
|
||||||
import { logger } from '@shared/logger';
|
import { logger } from '@shared/logger';
|
||||||
import { InternalMethods } from '@shared/message-types';
|
|
||||||
import { sendMessage } from '@shared/messages';
|
|
||||||
|
|
||||||
import { recurseAccountsForActivity } from '@app/common/account-restoration/account-restore';
|
import { recurseAccountsForActivity } from '@app/common/account-restoration/account-restore';
|
||||||
import { checkForLegacyGaiaConfigWithKnownGeneratedAccountIndex } from '@app/common/account-restoration/legacy-gaia-config-lookup';
|
import { checkForLegacyGaiaConfigWithKnownGeneratedAccountIndex } from '@app/common/account-restoration/legacy-gaia-config-lookup';
|
||||||
@@ -11,6 +9,7 @@ import { BitcoinClient } from '@app/query/bitcoin/bitcoin-client';
|
|||||||
import { fetchNamesForAddress } from '@app/query/stacks/bns/bns.utils';
|
import { fetchNamesForAddress } from '@app/query/stacks/bns/bns.utils';
|
||||||
import { StacksClient } from '@app/query/stacks/stacks-client';
|
import { StacksClient } from '@app/query/stacks/stacks-client';
|
||||||
import { AppThunk } from '@app/store';
|
import { AppThunk } from '@app/store';
|
||||||
|
import { initalizeWalletSession } from '@app/store/session-restore';
|
||||||
|
|
||||||
import { getNativeSegwitMainnetAddressFromMnemonic } from '../accounts/blockchain/bitcoin/bitcoin-keychain';
|
import { getNativeSegwitMainnetAddressFromMnemonic } from '../accounts/blockchain/bitcoin/bitcoin-keychain';
|
||||||
import { getStacksAddressByIndex } from '../accounts/blockchain/stacks/stacks-keychain';
|
import { getStacksAddressByIndex } from '../accounts/blockchain/stacks/stacks-keychain';
|
||||||
@@ -30,7 +29,13 @@ function setWalletEncryptionPassword(args: {
|
|||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const secretKey = selectDefaultWalletKey(getState());
|
const secretKey = selectDefaultWalletKey(getState());
|
||||||
if (!secretKey) throw new Error('Cannot generate wallet without first having generated a key');
|
if (!secretKey) throw new Error('Cannot generate wallet without first having generated a key');
|
||||||
const { encryptedSecretKey, salt } = await encryptMnemonic({ secretKey, password });
|
|
||||||
|
const { encryptedSecretKey, salt, encryptionKey } = await encryptMnemonic({
|
||||||
|
secretKey,
|
||||||
|
password,
|
||||||
|
});
|
||||||
|
|
||||||
|
await initalizeWalletSession(encryptionKey, secretKey);
|
||||||
|
|
||||||
const legacyAccountActivityLookup =
|
const legacyAccountActivityLookup =
|
||||||
await checkForLegacyGaiaConfigWithKnownGeneratedAccountIndex(secretKey);
|
await checkForLegacyGaiaConfigWithKnownGeneratedAccountIndex(secretKey);
|
||||||
@@ -76,12 +81,6 @@ function setWalletEncryptionPassword(args: {
|
|||||||
dispatch(stxChainSlice.actions.restoreAccountIndex(recursiveActivityIndex));
|
dispatch(stxChainSlice.actions.restoreAccountIndex(recursiveActivityIndex));
|
||||||
});
|
});
|
||||||
|
|
||||||
sendMessage({
|
|
||||||
method: InternalMethods.ShareInMemoryKeyToBackground,
|
|
||||||
payload: { secretKey, keyId: defaultKeyId },
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch(inMemoryKeySlice.actions.setKeysInMemory({ default: secretKey }));
|
|
||||||
dispatch(
|
dispatch(
|
||||||
keySlice.actions.createStacksSoftwareWalletComplete({
|
keySlice.actions.createStacksSoftwareWalletComplete({
|
||||||
type: 'software',
|
type: 'software',
|
||||||
@@ -100,11 +99,9 @@ function unlockWalletAction(password: string): AppThunk {
|
|||||||
const currentKey = selectCurrentKey(getState());
|
const currentKey = selectCurrentKey(getState());
|
||||||
if (!currentKey) return;
|
if (!currentKey) return;
|
||||||
if (currentKey.type !== 'software') return;
|
if (currentKey.type !== 'software') return;
|
||||||
const { secretKey } = await decryptMnemonic({ password, ...currentKey });
|
const { secretKey, encryptionKey } = await decryptMnemonic({ password, ...currentKey });
|
||||||
sendMessage({
|
|
||||||
method: InternalMethods.ShareInMemoryKeyToBackground,
|
await initalizeWalletSession(encryptionKey, secretKey);
|
||||||
payload: { secretKey: secretKey, keyId: defaultKeyId },
|
|
||||||
});
|
|
||||||
dispatch(inMemoryKeySlice.actions.setKeysInMemory({ default: secretKey }));
|
dispatch(inMemoryKeySlice.actions.setKeysInMemory({ default: secretKey }));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
72
src/app/store/session-restore.ts
Normal file
72
src/app/store/session-restore.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { decrypt } from '@stacks/wallet-sdk';
|
||||||
|
|
||||||
|
import { InternalMethods } from '@shared/message-types';
|
||||||
|
import { sendMessage } from '@shared/messages';
|
||||||
|
import { whenBrowserRuntime } from '@shared/utils/get-browser-runtime';
|
||||||
|
|
||||||
|
import { store } from '@app/store';
|
||||||
|
import { inMemoryKeyActions } from '@app/store/in-memory-key/in-memory-key.actions';
|
||||||
|
import { selectCurrentKey } from '@app/store/keys/key.selectors';
|
||||||
|
import { defaultKeyId } from '@app/store/keys/key.slice';
|
||||||
|
|
||||||
|
export async function initalizeWalletSession(encryptionKey: string, secretKey: string) {
|
||||||
|
return await whenBrowserRuntime({
|
||||||
|
async chromium() {
|
||||||
|
return chrome.storage.session.set({ encryptionKey });
|
||||||
|
},
|
||||||
|
async firefox() {
|
||||||
|
return sendMessage({
|
||||||
|
method: InternalMethods.ShareInMemoryKeyToBackground,
|
||||||
|
payload: { secretKey, keyId: defaultKeyId },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clearWalletSession() {
|
||||||
|
return await whenBrowserRuntime({
|
||||||
|
async chromium() {
|
||||||
|
return chrome.storage.session.remove('encryptionKey');
|
||||||
|
},
|
||||||
|
async firefox() {
|
||||||
|
return chrome.runtime.sendMessage({ method: InternalMethods.RemoveInMemoryKeys });
|
||||||
|
},
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function restoreWalletSession() {
|
||||||
|
return whenBrowserRuntime({
|
||||||
|
async chromium() {
|
||||||
|
const key = await chrome.storage.session.get(['encryptionKey']);
|
||||||
|
if (!key.encryptionKey) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentKey = selectCurrentKey(store.getState());
|
||||||
|
|
||||||
|
if (currentKey?.type === 'software') {
|
||||||
|
const secretKey = await decrypt(currentKey.encryptedSecretKey, key.encryptionKey);
|
||||||
|
store.dispatch(inMemoryKeyActions.setKeysInMemory({ default: secretKey }));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
async firefox() {
|
||||||
|
return checkForInMemoryKeys();
|
||||||
|
},
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkForInMemoryKeys() {
|
||||||
|
return new Promise(resolve =>
|
||||||
|
chrome.runtime.sendMessage({ method: InternalMethods.RequestInMemoryKeys }, resp => {
|
||||||
|
if (!resp) resolve(false);
|
||||||
|
if (Object.keys(resp).length === 0) return resolve(false);
|
||||||
|
store.dispatch(inMemoryKeyActions.setKeysInMemory(resp));
|
||||||
|
resolve(true);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -21,10 +21,6 @@ initSentry();
|
|||||||
initContextMenuActions();
|
initContextMenuActions();
|
||||||
warnUsersAboutDevToolsDangers();
|
warnUsersAboutDevToolsDangers();
|
||||||
|
|
||||||
const keepAlive = () => setInterval(async () => await chrome.runtime.getPlatformInfo(), 20_000);
|
|
||||||
chrome.runtime.onStartup.addListener(keepAlive);
|
|
||||||
keepAlive();
|
|
||||||
|
|
||||||
const IS_TEST_ENV = process.env.TEST_ENV === 'true';
|
const IS_TEST_ENV = process.env.TEST_ENV === 'true';
|
||||||
|
|
||||||
chrome.runtime.onInstalled.addListener(async details => {
|
chrome.runtime.onInstalled.addListener(async details => {
|
||||||
@@ -69,34 +65,34 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const storageArea = chrome.storage.local as chrome.storage.LocalStorageArea;
|
// const storageArea = chrome.storage.local as chrome.storage.LocalStorageArea;
|
||||||
|
|
||||||
const testIntervalMs = 10000;
|
// const testIntervalMs = 10000;
|
||||||
const storageWaitTimeMs = 100;
|
// const storageWaitTimeMs = 100;
|
||||||
|
|
||||||
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=1316588
|
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=1316588
|
||||||
async function hasChromiumIssue1316588() {
|
// async function hasChromiumIssue1316588() {
|
||||||
return new Promise(resolve => {
|
// return new Promise(resolve => {
|
||||||
let dispatched = false;
|
// let dispatched = false;
|
||||||
const testEventDispatching = () => {
|
// const testEventDispatching = () => {
|
||||||
storageArea.onChanged.removeListener(testEventDispatching);
|
// storageArea.onChanged.removeListener(testEventDispatching);
|
||||||
dispatched = true;
|
// dispatched = true;
|
||||||
};
|
// };
|
||||||
storageArea.onChanged.addListener(testEventDispatching);
|
// storageArea.onChanged.addListener(testEventDispatching);
|
||||||
void storageArea.set({ testEventDispatching: Math.random() });
|
// void storageArea.set({ testEventDispatching: Math.random() });
|
||||||
setTimeout(() => resolve(!dispatched), storageWaitTimeMs);
|
// setTimeout(() => resolve(!dispatched), storageWaitTimeMs);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
function fixChromiumIssue1316588() {
|
// function fixChromiumIssue1316588() {
|
||||||
void hasChromiumIssue1316588().then(hasIssue => {
|
// void hasChromiumIssue1316588().then(hasIssue => {
|
||||||
if (hasIssue) {
|
// if (hasIssue) {
|
||||||
chrome.runtime.reload();
|
// chrome.runtime.reload();
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
setTimeout(fixChromiumIssue1316588, testIntervalMs);
|
// setTimeout(fixChromiumIssue1316588, testIntervalMs);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
fixChromiumIssue1316588();
|
// fixChromiumIssue1316588();
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
// import { StacksMainnet } from '@stacks/network';
|
import { deriveStacksAccountsMemoized } from '@shared/crypto/stacks/derive-stacks-accounts';
|
||||||
import { generateNewAccount, generateWallet } from '@stacks/wallet-sdk';
|
|
||||||
import memoize from 'promise-memoize';
|
|
||||||
|
|
||||||
import { logger } from '@shared/logger';
|
import { logger } from '@shared/logger';
|
||||||
import { InternalMethods } from '@shared/message-types';
|
import { InternalMethods } from '@shared/message-types';
|
||||||
import { BackgroundMessages } from '@shared/messages';
|
import { BackgroundMessages } from '@shared/messages';
|
||||||
@@ -11,18 +8,6 @@ function validateMessagesAreFromExtension(sender: chrome.runtime.MessageSender)
|
|||||||
return sender.url?.startsWith(chrome.runtime.getURL(''));
|
return sender.url?.startsWith(chrome.runtime.getURL(''));
|
||||||
}
|
}
|
||||||
|
|
||||||
const deriveWalletWithAccounts = memoize(async (secretKey: string, highestAccountIndex: number) => {
|
|
||||||
// Here we only want the resulting `Wallet` objects, but the API
|
|
||||||
// requires a password (so it can also return an encrypted key)
|
|
||||||
const wallet = await generateWallet({ secretKey, password: '' });
|
|
||||||
let walWithAccounts = wallet;
|
|
||||||
for (let i = 0; i < highestAccountIndex; i++) {
|
|
||||||
walWithAccounts = generateNewAccount(walWithAccounts);
|
|
||||||
}
|
|
||||||
return walWithAccounts;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Persists keys in memory for the duration of the background scripts life
|
|
||||||
const inMemoryKeys = new Map();
|
const inMemoryKeys = new Map();
|
||||||
|
|
||||||
const inMemoryFormState = new Map<number, object>();
|
const inMemoryFormState = new Map<number, object>();
|
||||||
@@ -35,17 +20,19 @@ export async function internalBackgroundMessageHandler(
|
|||||||
sender: chrome.runtime.MessageSender,
|
sender: chrome.runtime.MessageSender,
|
||||||
sendResponse: (response?: any) => void
|
sendResponse: (response?: any) => void
|
||||||
) {
|
) {
|
||||||
// console.info('Internal msg', message, validateMessagesAreFromExtension(sender));
|
|
||||||
if (!validateMessagesAreFromExtension(sender)) {
|
if (!validateMessagesAreFromExtension(sender)) {
|
||||||
logger.error('Error: Received background script msg from ' + sender.url);
|
logger.error('Error: Received background script msg from ' + sender.url);
|
||||||
sendResponse();
|
sendResponse();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// logger.debug('Internal message', message);
|
logger.debug('Internal message', message);
|
||||||
switch (message.method) {
|
switch (message.method) {
|
||||||
case InternalMethods.RequestDerivedStxAccounts: {
|
case InternalMethods.RequestDerivedStxAccounts: {
|
||||||
const { secretKey, highestAccountIndex } = message.payload;
|
const { secretKey, highestAccountIndex } = message.payload;
|
||||||
const walletsWithAccounts = await deriveWalletWithAccounts(secretKey, highestAccountIndex);
|
const walletsWithAccounts = await deriveStacksAccountsMemoized(
|
||||||
|
secretKey,
|
||||||
|
highestAccountIndex
|
||||||
|
);
|
||||||
sendResponse(walletsWithAccounts);
|
sendResponse(walletsWithAccounts);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -59,7 +46,6 @@ export async function internalBackgroundMessageHandler(
|
|||||||
|
|
||||||
case InternalMethods.RequestInMemoryKeys: {
|
case InternalMethods.RequestInMemoryKeys: {
|
||||||
sendResponse(Object.fromEntries(inMemoryKeys));
|
sendResponse(Object.fromEntries(inMemoryKeys));
|
||||||
sendResponse();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,11 +57,13 @@ export async function internalBackgroundMessageHandler(
|
|||||||
case InternalMethods.SetActiveFormState: {
|
case InternalMethods.SetActiveFormState: {
|
||||||
const { tabId, ...state } = message.payload;
|
const { tabId, ...state } = message.payload;
|
||||||
inMemoryFormState.set(tabId, state);
|
inMemoryFormState.set(tabId, state);
|
||||||
|
sendResponse();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case InternalMethods.ClearActiveFormState: {
|
case InternalMethods.ClearActiveFormState: {
|
||||||
inMemoryFormState.delete(message.payload.tabId);
|
inMemoryFormState.delete(message.payload.tabId);
|
||||||
|
sendResponse();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +73,4 @@ export async function internalBackgroundMessageHandler(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// As browser is instructed that, if there is a response it will be
|
|
||||||
// asyncronous, we must always return a response, even if empty
|
|
||||||
sendResponse();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { WorkerScript, createWorker } from '../workers';
|
import { WorkerScript, createWorker } from '../workers';
|
||||||
|
|
||||||
const worker = createWorker(WorkerScript.DecryptionWorker);
|
const worker = createWorker(WorkerScript.DecryptionWorker);
|
||||||
interface GenerateEncryptionKeyArgs {
|
interface DeriveEncryptionKeyArgs {
|
||||||
password: string;
|
password: string;
|
||||||
salt: string;
|
salt: string;
|
||||||
}
|
}
|
||||||
export async function generateEncryptionKey(args: GenerateEncryptionKeyArgs): Promise<string> {
|
export async function deriveEncryptionKey(args: DeriveEncryptionKeyArgs): Promise<string> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const handler = (e: MessageEvent<string>) => resolve(e.data);
|
const handler = (e: MessageEvent<string>) => resolve(e.data);
|
||||||
worker.addEventListener('message', handler);
|
worker.addEventListener('message', handler);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { bytesToHex } from '@stacks/common';
|
import { bytesToHex } from '@stacks/common';
|
||||||
import { decrypt, encrypt } from '@stacks/wallet-sdk';
|
import { decrypt, encrypt } from '@stacks/wallet-sdk';
|
||||||
|
|
||||||
import { generateEncryptionKey } from './generate-encryption-key';
|
import { deriveEncryptionKey } from './generate-encryption-key';
|
||||||
import { generateRandomHexString } from './generate-random-hex';
|
import { generateRandomHexString } from './generate-random-hex';
|
||||||
|
|
||||||
interface EncryptMnemonicArgs {
|
interface EncryptMnemonicArgs {
|
||||||
@@ -10,11 +10,12 @@ interface EncryptMnemonicArgs {
|
|||||||
}
|
}
|
||||||
export async function encryptMnemonic({ secretKey, password }: EncryptMnemonicArgs) {
|
export async function encryptMnemonic({ secretKey, password }: EncryptMnemonicArgs) {
|
||||||
const salt = generateRandomHexString();
|
const salt = generateRandomHexString();
|
||||||
const argonHash = await generateEncryptionKey({ password, salt });
|
const encryptionKey = await deriveEncryptionKey({ password, salt });
|
||||||
const encryptedBuffer = await encrypt(secretKey, argonHash);
|
const encryptedBuffer = await encrypt(secretKey, encryptionKey);
|
||||||
return {
|
return {
|
||||||
salt,
|
salt,
|
||||||
encryptedSecretKey: bytesToHex(encryptedBuffer),
|
encryptedSecretKey: bytesToHex(encryptedBuffer),
|
||||||
|
encryptionKey,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,14 +37,16 @@ export async function decryptMnemonic({
|
|||||||
encryptedSecretKey: string;
|
encryptedSecretKey: string;
|
||||||
salt: string;
|
salt: string;
|
||||||
secretKey: string;
|
secretKey: string;
|
||||||
|
encryptionKey: string;
|
||||||
}> {
|
}> {
|
||||||
if (salt) {
|
if (salt) {
|
||||||
const pw = await generateEncryptionKey({ password, salt });
|
const encryptionKey = await deriveEncryptionKey({ password, salt });
|
||||||
const secretKey = await decrypt(Buffer.from(encryptedSecretKey, 'hex'), pw);
|
const secretKey = await decrypt(Buffer.from(encryptedSecretKey, 'hex'), encryptionKey);
|
||||||
return {
|
return {
|
||||||
secretKey,
|
secretKey,
|
||||||
encryptedSecretKey,
|
encryptedSecretKey,
|
||||||
salt,
|
salt,
|
||||||
|
encryptionKey,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// if there is no salt, decrypt the secret key, then re-encrypt with an argon2 hash
|
// if there is no salt, decrypt the secret key, then re-encrypt with an argon2 hash
|
||||||
@@ -53,6 +56,7 @@ export async function decryptMnemonic({
|
|||||||
secretKey,
|
secretKey,
|
||||||
encryptedSecretKey: newEncryptedKey.encryptedSecretKey,
|
encryptedSecretKey: newEncryptedKey.encryptedSecretKey,
|
||||||
salt: newEncryptedKey.salt,
|
salt: newEncryptedKey.salt,
|
||||||
|
encryptionKey: newEncryptedKey.encryptedSecretKey,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/shared/crypto/stacks/derive-stacks-accounts.ts
Normal file
15
src/shared/crypto/stacks/derive-stacks-accounts.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { generateNewAccount, generateWallet } from '@stacks/wallet-sdk';
|
||||||
|
import memoize from 'promise-memoize';
|
||||||
|
|
||||||
|
export async function deriveStacksAccounts(secretKey: string, highestAccountIndex: number) {
|
||||||
|
// Here we only want the resulting `Wallet` objects, but the API
|
||||||
|
// requires a password (so it can also return an encrypted key)
|
||||||
|
const wallet = await generateWallet({ secretKey, password: '' });
|
||||||
|
let walWithAccounts = wallet;
|
||||||
|
for (let i = 0; i < highestAccountIndex; i++) {
|
||||||
|
walWithAccounts = generateNewAccount(walWithAccounts);
|
||||||
|
}
|
||||||
|
return walWithAccounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deriveStacksAccountsMemoized = memoize(deriveStacksAccounts);
|
||||||
@@ -25,14 +25,14 @@ type ClearActiveFormState = BackgroundMessage<
|
|||||||
{ tabId: number }
|
{ tabId: number }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type ShareInMemoryKeyToBackground = BackgroundMessage<
|
type FirefoxShareInMemoryKeyToBackground = BackgroundMessage<
|
||||||
InternalMethods.ShareInMemoryKeyToBackground,
|
InternalMethods.ShareInMemoryKeyToBackground,
|
||||||
{ secretKey: string; keyId: string }
|
{ secretKey: string; keyId: string }
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type RequestInMemoryKeys = BackgroundMessage<InternalMethods.RequestInMemoryKeys>;
|
type FirefoxRequestInMemoryKeys = BackgroundMessage<InternalMethods.RequestInMemoryKeys>;
|
||||||
|
|
||||||
type RemoveInMemoryKeys = BackgroundMessage<InternalMethods.RemoveInMemoryKeys>;
|
type FirefoxRemoveInMemoryKeys = BackgroundMessage<InternalMethods.RemoveInMemoryKeys>;
|
||||||
|
|
||||||
type OriginatingTabClosed = BackgroundMessage<
|
type OriginatingTabClosed = BackgroundMessage<
|
||||||
InternalMethods.OriginatingTabClosed,
|
InternalMethods.OriginatingTabClosed,
|
||||||
@@ -44,9 +44,9 @@ export type BackgroundMessages =
|
|||||||
| GetActiveFormState
|
| GetActiveFormState
|
||||||
| SetActiveFormState
|
| SetActiveFormState
|
||||||
| ClearActiveFormState
|
| ClearActiveFormState
|
||||||
| ShareInMemoryKeyToBackground
|
| FirefoxShareInMemoryKeyToBackground
|
||||||
| RequestInMemoryKeys
|
| FirefoxRequestInMemoryKeys
|
||||||
| RemoveInMemoryKeys
|
| FirefoxRemoveInMemoryKeys
|
||||||
| OriginatingTabClosed;
|
| OriginatingTabClosed;
|
||||||
|
|
||||||
export function sendMessage(message: BackgroundMessages) {
|
export function sendMessage(message: BackgroundMessages) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { PersistConfig, createMigrate, getStoredState } from 'redux-persist';
|
|||||||
|
|
||||||
import type { RootState } from '@app/store';
|
import type { RootState } from '@app/store';
|
||||||
|
|
||||||
import { analytics } from './utils/analytics';
|
import { analytics } from '../utils/analytics';
|
||||||
|
|
||||||
export async function clearChromeStorage(): Promise<void> {
|
export async function clearChromeStorage(): Promise<void> {
|
||||||
return new Promise(resolve => chrome.storage.local.clear(resolve));
|
return new Promise(resolve => chrome.storage.local.clear(resolve));
|
||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
SENTRY_DSN,
|
SENTRY_DSN,
|
||||||
WALLET_ENVIRONMENT,
|
WALLET_ENVIRONMENT,
|
||||||
} from '@shared/environment';
|
} from '@shared/environment';
|
||||||
import { persistConfig } from '@shared/storage';
|
import { persistConfig } from '@shared/storage/redux-pesist';
|
||||||
|
|
||||||
import type { RootState } from '@app/store';
|
import type { RootState } from '@app/store';
|
||||||
|
|
||||||
|
|||||||
11
src/shared/utils/get-browser-runtime.ts
Normal file
11
src/shared/utils/get-browser-runtime.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
type BrowserRuntime = 'chromium' | 'firefox';
|
||||||
|
|
||||||
|
function getBrowserRuntime(): BrowserRuntime {
|
||||||
|
return chrome.runtime.getURL('').startsWith('moz-extension://') ? 'firefox' : 'chromium';
|
||||||
|
}
|
||||||
|
|
||||||
|
type WhenBrowserRuntimeMap<T> = Record<BrowserRuntime, T>;
|
||||||
|
|
||||||
|
export function whenBrowserRuntime<T>(runtimeMap: WhenBrowserRuntimeMap<T>) {
|
||||||
|
return runtimeMap[getBrowserRuntime()];
|
||||||
|
}
|
||||||
@@ -121,7 +121,11 @@ describe(`Onboarding integration tests`, () => {
|
|||||||
await wallet.waitForHomePage();
|
await wallet.waitForHomePage();
|
||||||
await wallet.waitForsuggestedStepsList();
|
await wallet.waitForsuggestedStepsList();
|
||||||
const stepsToStartBtns = await wallet.page.$$(wallet.$suggestedStepStartBtn);
|
const stepsToStartBtns = await wallet.page.$$(wallet.$suggestedStepStartBtn);
|
||||||
await stepsToStartBtns[1].click();
|
const [newPage] = await Promise.all([
|
||||||
|
browser.context.waitForEvent('page'),
|
||||||
|
stepsToStartBtns[1].click(),
|
||||||
|
]);
|
||||||
|
await newPage.waitForLoadState();
|
||||||
const stepsDone = await wallet.page.$$(wallet.$suggestedStepDoneBadge);
|
const stepsDone = await wallet.page.$$(wallet.$suggestedStepDoneBadge);
|
||||||
expect(stepsDone.length).toEqual(2);
|
expect(stepsDone.length).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { BrowserContext, Page } from '@playwright/test';
|
||||||
import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors';
|
import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors';
|
||||||
import { BrowserContext, Page } from 'playwright';
|
|
||||||
|
|
||||||
import { createTestSelector } from '../integration/utils';
|
import { createTestSelector } from '../integration/utils';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { Page } from '@playwright/test';
|
||||||
import { FundPageSelectors } from '@tests-legacy/page-objects/fund.selectors';
|
import { FundPageSelectors } from '@tests-legacy/page-objects/fund.selectors';
|
||||||
import { Page } from 'playwright';
|
|
||||||
|
|
||||||
import { createTestSelector } from '../integration/utils';
|
import { createTestSelector } from '../integration/utils';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { Page } from '@playwright/test';
|
||||||
import { NetworkSelectors } from '@tests-legacy/integration/network.selectors';
|
import { NetworkSelectors } from '@tests-legacy/integration/network.selectors';
|
||||||
import { Page } from 'playwright';
|
|
||||||
|
|
||||||
import { createTestSelector } from '../integration/utils';
|
import { createTestSelector } from '../integration/utils';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { Page } from '@playwright/test';
|
||||||
import { ProfileUpdatingSelectors } from '@tests-legacy/integration/profile/profile-updating.selector';
|
import { ProfileUpdatingSelectors } from '@tests-legacy/integration/profile/profile-updating.selector';
|
||||||
import { Page } from 'playwright';
|
|
||||||
|
|
||||||
import { createTestSelector } from '../integration/utils';
|
import { createTestSelector } from '../integration/utils';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { Page } from '@playwright/test';
|
||||||
import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors';
|
import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors';
|
||||||
import { Page } from 'playwright';
|
|
||||||
|
|
||||||
import { createTestSelector } from '../integration/utils';
|
import { createTestSelector } from '../integration/utils';
|
||||||
import { TransactionSigningSelectors } from './transaction-signing.selectors';
|
import { TransactionSigningSelectors } from './transaction-signing.selectors';
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import { Page } from '@playwright/test';
|
||||||
import { SettingsSelectors } from '@tests-legacy/integration/settings.selectors';
|
import { SettingsSelectors } from '@tests-legacy/integration/settings.selectors';
|
||||||
import { HomePageSelectorsLegacy } from '@tests-legacy/page-objects/home.selectors';
|
import { HomePageSelectorsLegacy } from '@tests-legacy/page-objects/home.selectors';
|
||||||
import { HomePageSelectors } from '@tests/selectors/home.selectors';
|
import { HomePageSelectors } from '@tests/selectors/home.selectors';
|
||||||
import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors';
|
import { OnboardingSelectors } from '@tests/selectors/onboarding.selectors';
|
||||||
import { SettingsMenuSelectors } from '@tests/selectors/settings.selectors';
|
import { SettingsMenuSelectors } from '@tests/selectors/settings.selectors';
|
||||||
import { Page } from 'playwright';
|
|
||||||
|
|
||||||
import { RouteUrls } from '@shared/route-urls';
|
import { RouteUrls } from '@shared/route-urls';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { Page } from '@playwright/test';
|
import { Page } from '@playwright/test';
|
||||||
import { setupMockApis } from '@tests/mocks/mock-apis';
|
import { setupMockApis } from '@tests/mocks/mock-apis';
|
||||||
|
|
||||||
// const delay = (ms: number) => new Promise(res => setTimeout(res, ms));
|
|
||||||
|
|
||||||
export class GlobalPage {
|
export class GlobalPage {
|
||||||
readonly page: Page;
|
readonly page: Page;
|
||||||
|
|
||||||
@@ -17,6 +15,7 @@ export class GlobalPage {
|
|||||||
async setupAndUseApiCalls(extensionId: string) {
|
async setupAndUseApiCalls(extensionId: string) {
|
||||||
await this.page.route(/.*/, route => route.continue());
|
await this.page.route(/.*/, route => route.continue());
|
||||||
await setupMockApis(this.page);
|
await setupMockApis(this.page);
|
||||||
|
await this.page.waitForTimeout(600);
|
||||||
await this.gotoNakedRoot(extensionId);
|
await this.gotoNakedRoot(extensionId);
|
||||||
// I've no idea why this delay is needed. Was required when switching to MV3. Without it,
|
// I've no idea why this delay is needed. Was required when switching to MV3. Without it,
|
||||||
// this error is thrown. Let's try removing with later versions of Playwright.
|
// this error is thrown. Let's try removing with later versions of Playwright.
|
||||||
@@ -25,7 +24,6 @@ export class GlobalPage {
|
|||||||
// =========================== logs ===========================
|
// =========================== logs ===========================
|
||||||
// navigating to "chrome-extension://bcokkkbghbnpbjpkoifjakaofdjfgeaj/index.html", waiting until "load"
|
// navigating to "chrome-extension://bcokkkbghbnpbjpkoifjakaofdjfgeaj/index.html", waiting until "load"
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// await delay(600);
|
|
||||||
// await this.page.goto(`chrome-extension://${extensionId}/index.html`);
|
// await this.page.goto(`chrome-extension://${extensionId}/index.html`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
90
yarn.lock
90
yarn.lock
@@ -5287,7 +5287,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/chai" "*"
|
"@types/chai" "*"
|
||||||
|
|
||||||
"@types/chai@*", "@types/chai@^4.3.4":
|
"@types/chai@*", "@types/chai@^4.3.5":
|
||||||
version "4.3.5"
|
version "4.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b"
|
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.5.tgz#ae69bcbb1bebb68c4ac0b11e9d8ed04526b3562b"
|
||||||
integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==
|
integrity sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==
|
||||||
@@ -6370,10 +6370,10 @@
|
|||||||
"@typescript-eslint/types" "5.59.6"
|
"@typescript-eslint/types" "5.59.6"
|
||||||
eslint-visitor-keys "^3.3.0"
|
eslint-visitor-keys "^3.3.0"
|
||||||
|
|
||||||
"@vitest/coverage-istanbul@0.31.0":
|
"@vitest/coverage-istanbul@0.31.1":
|
||||||
version "0.31.0"
|
version "0.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/coverage-istanbul/-/coverage-istanbul-0.31.0.tgz#57d14c5d66685f9fe0d80e5884e940c0872a7fd6"
|
resolved "https://registry.yarnpkg.com/@vitest/coverage-istanbul/-/coverage-istanbul-0.31.1.tgz#c9749bf404470c2b12dcb6ad851e89d27f88fc42"
|
||||||
integrity sha512-SaTI1PSpCRtBhJ5ihBx7Z+jgrFAQlDjuI4MFmKQ/HjyYWzEoaU+I062SquRrOLjJtVOHnwJdjVJXKi0dgFiR9Q==
|
integrity sha512-wLVROukTxWflIub4QUT5TenA2zx2ypUjRp636yiMTidN5hvRhnUNEEkRavJMEpYuWYYMN23E7EmAxo/MzRRmIA==
|
||||||
dependencies:
|
dependencies:
|
||||||
istanbul-lib-coverage "^3.2.0"
|
istanbul-lib-coverage "^3.2.0"
|
||||||
istanbul-lib-instrument "^5.2.1"
|
istanbul-lib-instrument "^5.2.1"
|
||||||
@@ -6382,45 +6382,45 @@
|
|||||||
istanbul-reports "^3.1.5"
|
istanbul-reports "^3.1.5"
|
||||||
test-exclude "^6.0.0"
|
test-exclude "^6.0.0"
|
||||||
|
|
||||||
"@vitest/expect@0.31.0":
|
"@vitest/expect@0.31.1":
|
||||||
version "0.31.0"
|
version "0.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.31.0.tgz#37ab35d4f75c12826c204f2a0290e0c2e5ef1192"
|
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.31.1.tgz#db8cb5a14a91167b948f377b9d29442229c73747"
|
||||||
integrity sha512-Jlm8ZTyp6vMY9iz9Ny9a0BHnCG4fqBa8neCF6Pk/c/6vkUk49Ls6UBlgGAU82QnzzoaUs9E/mUhq/eq9uMOv/g==
|
integrity sha512-BV1LyNvhnX+eNYzJxlHIGPWZpwJFZaCcOIzp2CNG0P+bbetenTupk6EO0LANm4QFt0TTit+yqx7Rxd1qxi/SQA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vitest/spy" "0.31.0"
|
"@vitest/spy" "0.31.1"
|
||||||
"@vitest/utils" "0.31.0"
|
"@vitest/utils" "0.31.1"
|
||||||
chai "^4.3.7"
|
chai "^4.3.7"
|
||||||
|
|
||||||
"@vitest/runner@0.31.0":
|
"@vitest/runner@0.31.1":
|
||||||
version "0.31.0"
|
version "0.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.31.0.tgz#ca830405ae4c2744ae5fb7fbe85df81b56430ebc"
|
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.31.1.tgz#fc06260d4824dde624abaeea1825d6a75bad4583"
|
||||||
integrity sha512-H1OE+Ly7JFeBwnpHTrKyCNm/oZgr+16N4qIlzzqSG/YRQDATBYmJb/KUn3GrZaiQQyL7GwpNHVZxSQd6juLCgw==
|
integrity sha512-imWuc82ngOtxdCUpXwtEzZIuc1KMr+VlQ3Ondph45VhWoQWit5yvG/fFcldbnCi8DUuFi+NmNx5ehMUw/cGLUw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vitest/utils" "0.31.0"
|
"@vitest/utils" "0.31.1"
|
||||||
concordance "^5.0.4"
|
concordance "^5.0.4"
|
||||||
p-limit "^4.0.0"
|
p-limit "^4.0.0"
|
||||||
pathe "^1.1.0"
|
pathe "^1.1.0"
|
||||||
|
|
||||||
"@vitest/snapshot@0.31.0":
|
"@vitest/snapshot@0.31.1":
|
||||||
version "0.31.0"
|
version "0.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-0.31.0.tgz#f59c4bcf0d03f1f494ee09286965e60a1e0cab64"
|
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-0.31.1.tgz#7fc3f1e48f0c4313e6cb795c17a2c1aa909a7d64"
|
||||||
integrity sha512-5dTXhbHnyUMTMOujZPB0wjFjQ6q5x9c8TvAsSPUNKjp1tVU7i9pbqcKPqntyu2oXtmVxKbuHCqrOd+Ft60r4tg==
|
integrity sha512-L3w5uU9bMe6asrNzJ8WZzN+jUTX4KSgCinEJPXyny0o90fG4FPQMV0OWsq7vrCWfQlAilMjDnOF9nP8lidsJ+g==
|
||||||
dependencies:
|
dependencies:
|
||||||
magic-string "^0.30.0"
|
magic-string "^0.30.0"
|
||||||
pathe "^1.1.0"
|
pathe "^1.1.0"
|
||||||
pretty-format "^27.5.1"
|
pretty-format "^27.5.1"
|
||||||
|
|
||||||
"@vitest/spy@0.31.0":
|
"@vitest/spy@0.31.1":
|
||||||
version "0.31.0"
|
version "0.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.31.0.tgz#98cb19046c0bd2673a73d6c90ee1533d1be82136"
|
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.31.1.tgz#1c3b6a3eec4ce81b8889e19c7fac6a603b600b14"
|
||||||
integrity sha512-IzCEQ85RN26GqjQNkYahgVLLkULOxOm5H/t364LG0JYb3Apg0PsYCHLBYGA006+SVRMWhQvHlBBCyuByAMFmkg==
|
integrity sha512-1cTpt2m9mdo3hRLDyCG2hDQvRrePTDgEJBFQQNz1ydHHZy03EiA6EpFxY+7ODaY7vMRCie+WlFZBZ0/dQWyssQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
tinyspy "^2.1.0"
|
tinyspy "^2.1.0"
|
||||||
|
|
||||||
"@vitest/utils@0.31.0":
|
"@vitest/utils@0.31.1":
|
||||||
version "0.31.0"
|
version "0.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.31.0.tgz#d0aae17150b95ebf7afdf4e5db8952ac21610ffa"
|
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.31.1.tgz#b810a458b37ef16931ab0d384ce79a9500f34e07"
|
||||||
integrity sha512-kahaRyLX7GS1urekRXN2752X4gIgOGVX4Wo8eDUGUkTWlGpXzf5ZS6N9RUUS+Re3XEE8nVGqNyxkSxF5HXlGhQ==
|
integrity sha512-yFyRD5ilwojsZfo3E0BnH72pSVSuLg2356cN1tCEe/0RtDzxTPYwOomIC+eQbot7m6DRy4tPZw+09mB7NkbMmA==
|
||||||
dependencies:
|
dependencies:
|
||||||
concordance "^5.0.4"
|
concordance "^5.0.4"
|
||||||
loupe "^2.3.6"
|
loupe "^2.3.6"
|
||||||
@@ -16868,7 +16868,7 @@ tiny-warning@^1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||||
|
|
||||||
tinybench@^2.4.0:
|
tinybench@^2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.0.tgz#4711c99bbf6f3e986f67eb722fed9cddb3a68ba5"
|
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.0.tgz#4711c99bbf6f3e986f67eb722fed9cddb3a68ba5"
|
||||||
integrity sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==
|
integrity sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==
|
||||||
@@ -17488,10 +17488,10 @@ vinyl-buffer@^1.0.1:
|
|||||||
bl "^1.2.1"
|
bl "^1.2.1"
|
||||||
through2 "^2.0.3"
|
through2 "^2.0.3"
|
||||||
|
|
||||||
vite-node@0.31.0:
|
vite-node@0.31.1:
|
||||||
version "0.31.0"
|
version "0.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.31.0.tgz#8794a98f21b0cf2394bfd2aaa5fc85d2c42be084"
|
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.31.1.tgz#9fea18cbf9552ab262b969068249a8b8e7fb8b38"
|
||||||
integrity sha512-8x1x1LNuPvE2vIvkSB7c1mApX5oqlgsxzHQesYF7l5n1gKrEmrClIiZuOFbFDQcjLsmcWSwwmrWrcGWm9Fxc/g==
|
integrity sha512-BajE/IsNQ6JyizPzu9zRgHrBwczkAs0erQf/JRpgTIESpKvNj9/Gd0vxX905klLkb0I0SJVCKbdrl5c6FnqYKA==
|
||||||
dependencies:
|
dependencies:
|
||||||
cac "^6.7.14"
|
cac "^6.7.14"
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
@@ -17511,19 +17511,19 @@ vite-node@0.31.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
vitest@0.31.0:
|
vitest@0.31.1:
|
||||||
version "0.31.0"
|
version "0.31.1"
|
||||||
resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.31.0.tgz#133e98f779aa81afbc7ee1fcb385a0c458b8c2c8"
|
resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.31.1.tgz#e3d1b68a44e76e24f142c1156fe9772ef603e52c"
|
||||||
integrity sha512-JwWJS9p3GU9GxkG7eBSmr4Q4x4bvVBSswaCFf1PBNHiPx00obfhHRJfgHcnI0ffn+NMlIh9QGvG75FlaIBdKGA==
|
integrity sha512-/dOoOgzoFk/5pTvg1E65WVaobknWREN15+HF+0ucudo3dDG/vCZoXTQrjIfEaWvQXmqScwkRodrTbM/ScMpRcQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/chai" "^4.3.4"
|
"@types/chai" "^4.3.5"
|
||||||
"@types/chai-subset" "^1.3.3"
|
"@types/chai-subset" "^1.3.3"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
"@vitest/expect" "0.31.0"
|
"@vitest/expect" "0.31.1"
|
||||||
"@vitest/runner" "0.31.0"
|
"@vitest/runner" "0.31.1"
|
||||||
"@vitest/snapshot" "0.31.0"
|
"@vitest/snapshot" "0.31.1"
|
||||||
"@vitest/spy" "0.31.0"
|
"@vitest/spy" "0.31.1"
|
||||||
"@vitest/utils" "0.31.0"
|
"@vitest/utils" "0.31.1"
|
||||||
acorn "^8.8.2"
|
acorn "^8.8.2"
|
||||||
acorn-walk "^8.2.0"
|
acorn-walk "^8.2.0"
|
||||||
cac "^6.7.14"
|
cac "^6.7.14"
|
||||||
@@ -17536,10 +17536,10 @@ vitest@0.31.0:
|
|||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
std-env "^3.3.2"
|
std-env "^3.3.2"
|
||||||
strip-literal "^1.0.1"
|
strip-literal "^1.0.1"
|
||||||
tinybench "^2.4.0"
|
tinybench "^2.5.0"
|
||||||
tinypool "^0.5.0"
|
tinypool "^0.5.0"
|
||||||
vite "^3.0.0 || ^4.0.0"
|
vite "^3.0.0 || ^4.0.0"
|
||||||
vite-node "0.31.0"
|
vite-node "0.31.1"
|
||||||
why-is-node-running "^2.2.2"
|
why-is-node-running "^2.2.2"
|
||||||
|
|
||||||
vm-browserify@1.1.2:
|
vm-browserify@1.1.2:
|
||||||
|
|||||||
Reference in New Issue
Block a user