Switch from yarn to pnpm (#6779)
* switch from yarn to pnpm * Remove full-nym-wasm (#6796) * Remove nym-browser-extension (#6798) * Remove nym-browser-extension * remove unused from makefile * Remove Node tester (#6800) * Remove dom-utils (#6801) * gh-actions: remove pnpm version * nuke dist and pkg * add missing dependency * set node version to 24 and pnpm version to 11 * upgrade lock file from pnpm version 9 to 11 * pnpm add approved builds * yarn -> pnpm * upgrade jest version * yarn -> pnpm * Remove unused cfg; clippy! * pnpm: when dev mode is on, unfreeze the lock file * pnpm approve more scripts * pnpm syntax error * add `pnpm i` * disable eslint temporarily while switching to biome in later PR --------- Co-authored-by: Mark Sinclair <mmsinclair@users.noreply.github.com> Co-authored-by: mfahampshire <maxhampshire@pm.me>
This commit is contained in:
@@ -23,10 +23,10 @@ jobs:
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v5.0.0
|
||||
with:
|
||||
version: 9
|
||||
version: 11.1.2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
@@ -17,13 +17,16 @@ jobs:
|
||||
run: sudo apt-get install rsync
|
||||
continue-on-error: true
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v5.0.0
|
||||
with:
|
||||
version: 11.1.2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
- name: Build
|
||||
run: yarn && yarn build && yarn build:ci:storybook
|
||||
run: pnpm install && pnpm build && pnpm build:ci:storybook
|
||||
- name: Deploy branch to CI www (storybook)
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
|
||||
@@ -23,7 +23,6 @@ on:
|
||||
- 'sdk/ffi/**'
|
||||
- 'sdk/rust/**'
|
||||
- 'service-providers/**'
|
||||
- 'nym-browser-extension/storage/**'
|
||||
- 'tools/**'
|
||||
- 'wasm/**'
|
||||
- 'Cargo.toml'
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
node-version: 24
|
||||
|
||||
- name: Validate version format
|
||||
run: |
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
node-version: 24
|
||||
|
||||
- name: Validate version format
|
||||
run: |
|
||||
|
||||
@@ -30,10 +30,10 @@ jobs:
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v5.0.0
|
||||
with:
|
||||
version: 9
|
||||
version: 11.1.2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
@@ -20,12 +20,14 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v5.0.0
|
||||
with:
|
||||
version: 11.1.2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -44,16 +46,16 @@ jobs:
|
||||
go-version: "1.24.6"
|
||||
|
||||
- name: Install
|
||||
run: yarn
|
||||
run: pnpm i
|
||||
|
||||
- name: Build packages
|
||||
run: yarn build:ci
|
||||
run: pnpm build:ci
|
||||
|
||||
- name: Install again
|
||||
run: yarn
|
||||
run: pnpm i
|
||||
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
run: pnpm lint
|
||||
|
||||
- name: Typecheck with tsc
|
||||
run: yarn tsc
|
||||
run: pnpm tsc
|
||||
|
||||
@@ -12,30 +12,34 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v5.0.0
|
||||
with:
|
||||
version: 11.1.2
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: nym-wallet/.nvmrc
|
||||
cache: yarn
|
||||
cache-dependency-path: yarn.lock
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --network-timeout 100000
|
||||
run: pnpm install
|
||||
|
||||
- name: Build TypeScript packages (wallet depends on @nymproject/types, etc.)
|
||||
run: yarn build:types
|
||||
run: pnpm build:types
|
||||
|
||||
- name: Build @nymproject/mui-theme and @nymproject/react (wallet imports subpaths)
|
||||
run: yarn build:packages
|
||||
run: pnpm build:packages
|
||||
|
||||
- name: Typecheck nym-wallet
|
||||
run: yarn --cwd nym-wallet tsc
|
||||
run: pnpm --filter @nymproject/nym-wallet-app tsc
|
||||
|
||||
- name: Lint nym-wallet
|
||||
run: yarn --cwd nym-wallet lint
|
||||
run: pnpm --filter @nymproject/nym-wallet-app lint
|
||||
|
||||
- name: Yarn audit (workspace lockfile; informational)
|
||||
run: yarn audit --level critical
|
||||
- name: pnpm audit (workspace lockfile; informational)
|
||||
run: pnpm audit --audit-level critical
|
||||
continue-on-error: true
|
||||
|
||||
- name: Unit tests (nym-wallet)
|
||||
run: yarn --cwd nym-wallet test
|
||||
run: pnpm --filter @nymproject/nym-wallet-app test
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
@@ -23,10 +23,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Node
|
||||
uses: actions/setup-node@v4
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v5.0.0
|
||||
with:
|
||||
node-version: 22.13.0
|
||||
version: 11.1.2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@@ -68,17 +71,17 @@ jobs:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Yarn cache clean
|
||||
- name: pnpm cache clean
|
||||
shell: bash
|
||||
run: cd .. && yarn cache clean
|
||||
run: cd .. && pnpm cache delete
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
run: cd .. && pnpm i
|
||||
|
||||
- name: Yarn build
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: cd .. && yarn build
|
||||
run: cd .. && pnpm build
|
||||
|
||||
- name: Install dependencies and build it
|
||||
env:
|
||||
@@ -97,7 +100,7 @@ jobs:
|
||||
TAURI_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
TAURI_NOTARIZATION_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
run: |
|
||||
yarn build-macx86
|
||||
pnpm build-macx86
|
||||
|
||||
- name: Create app tarball
|
||||
run: |
|
||||
|
||||
@@ -26,12 +26,17 @@ jobs:
|
||||
libwebkit2gtk-4.1-dev build-essential curl wget libssl-dev jq \
|
||||
libgtk-3-dev squashfs-tools libayatana-appindicator3-dev make libfuse2 unzip librsvg2-dev file \
|
||||
libsoup-3.0-dev libjavascriptcoregtk-4.1-dev
|
||||
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v5.0.0
|
||||
with:
|
||||
version: 11.1.2
|
||||
|
||||
- name: Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22.13.0
|
||||
cache: 'yarn'
|
||||
node-version: 24
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@@ -40,10 +45,10 @@ jobs:
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
run: cd .. && pnpm i
|
||||
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
run: pnpm
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
@@ -52,7 +57,7 @@ jobs:
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Build app
|
||||
run: yarn build
|
||||
run: pnpm build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
|
||||
@@ -40,16 +40,13 @@ jobs:
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v3
|
||||
|
||||
# No cache:yarn here: setup-node needs yarn on PATH to populate the cache, but this runner
|
||||
# only gets yarn from the step below.
|
||||
- name: Node
|
||||
uses: actions/setup-node@v4
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v5.0.0
|
||||
with:
|
||||
node-version: 22.13.0
|
||||
|
||||
- name: Install Yarn (classic)
|
||||
shell: bash
|
||||
run: npm install -g yarn@1.22.22
|
||||
version: 11.1.2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
- name: Strip Authenticode thumbprint (avoid signtool on runner)
|
||||
working-directory: nym-wallet/src-tauri
|
||||
@@ -118,11 +115,11 @@ jobs:
|
||||
' tauri.conf.json
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
run: cd .. && pnpm i
|
||||
|
||||
- name: Install app dependencies
|
||||
shell: bash
|
||||
run: yarn --network-timeout 100000
|
||||
run: pnpm i
|
||||
|
||||
- name: Build and sign it
|
||||
shell: bash
|
||||
@@ -136,7 +133,7 @@ jobs:
|
||||
SSL_COM_TOTP_SECRET: ${{ env.SIGN_WINDOWS == 'true' && secrets.SSL_COM_TOTP_SECRET }}
|
||||
run: |
|
||||
echo "Starting build process..."
|
||||
yarn build
|
||||
pnpm build
|
||||
|
||||
- name: Check bundle directory
|
||||
shell: bash
|
||||
|
||||
@@ -8,15 +8,17 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v5.0.0
|
||||
with:
|
||||
version: 11.1.2
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -40,10 +42,10 @@ jobs:
|
||||
run: ./wasm/mix-fetch/go-mix-conn/scripts/update-root-certs.sh
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
run: pnpm i
|
||||
|
||||
- name: Build WASM and Typescript SDK
|
||||
run: yarn sdk:build
|
||||
run: pnpm sdk:build
|
||||
|
||||
- name: Publish to NPM
|
||||
env:
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 24
|
||||
- uses: nymtech/nym/.github/actions/nym-hash-releases@develop
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -79,3 +79,7 @@ CLAUDE.md
|
||||
/notes
|
||||
/target-otel
|
||||
test-tutorials/
|
||||
|
||||
# pnpm
|
||||
.pnpm-store/
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
shamefully-hoist=false
|
||||
prefer-workspace-packages=true
|
||||
hoist-pattern[]=*eslint*
|
||||
hoist-pattern[]=*prettier*
|
||||
hoist-pattern[]=*typescript*
|
||||
hoist-pattern[]=*@types*
|
||||
|
||||
auto-install-peers=true
|
||||
strict-peer-dependencies=false
|
||||
Generated
-38
@@ -2965,22 +2965,6 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extension-storage"
|
||||
version = "1.4.1"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
"nym-wasm-storage",
|
||||
"nym-wasm-utils",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"thiserror 2.0.18",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.12"
|
||||
@@ -6312,8 +6296,6 @@ dependencies = [
|
||||
"js-sys",
|
||||
"nym-bin-common",
|
||||
"nym-gateway-requests",
|
||||
"nym-node-tester-utils",
|
||||
"nym-node-tester-wasm",
|
||||
"nym-wasm-client-core",
|
||||
"nym-wasm-utils",
|
||||
"once_cell",
|
||||
@@ -7897,26 +7879,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-tester-wasm"
|
||||
version = "1.3.1"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
"nym-node-tester-utils",
|
||||
"nym-wasm-client-core",
|
||||
"nym-wasm-utils",
|
||||
"rand 0.8.6",
|
||||
"serde",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tsify",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-noise"
|
||||
version = "1.21.0"
|
||||
|
||||
-12
@@ -130,7 +130,6 @@ members = [
|
||||
"nym-api",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-authenticator-client",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-credential-proxy/nym-credential-proxy",
|
||||
"nym-credential-proxy/nym-credential-proxy-requests",
|
||||
"nym-data-observatory",
|
||||
@@ -174,9 +173,7 @@ members = [
|
||||
"tools/nymvisor",
|
||||
"tools/ts-rs-cli",
|
||||
"wasm/client",
|
||||
# "wasm/full-nym-wasm", # If we uncomment this again, remember to also uncomment the profile settings below
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
]
|
||||
|
||||
@@ -589,16 +586,7 @@ opt-level = 3
|
||||
# lto = true
|
||||
opt-level = 'z'
|
||||
|
||||
[profile.release.package.nym-node-tester-wasm]
|
||||
# lto = true
|
||||
opt-level = 'z'
|
||||
|
||||
# Commented out since the crate is also commented out from the inclusion in the
|
||||
# workspace above. We should uncomment this if we re-include it in the
|
||||
# workspace
|
||||
#[profile.release.package.nym-wasm-sdk]
|
||||
## lto = true
|
||||
#opt-level = 'z'
|
||||
|
||||
[profile.release.package.mix-fetch-wasm]
|
||||
# lto = true
|
||||
|
||||
@@ -104,23 +104,19 @@ $(eval $(call add_cargo_workspace,wallet,nym-wallet))
|
||||
sdk-wasm: sdk-wasm-build sdk-wasm-test sdk-wasm-lint
|
||||
|
||||
sdk-wasm-build:
|
||||
# $(MAKE) -C nym-browser-extension/storage wasm-pack
|
||||
$(MAKE) -C wasm/client
|
||||
$(MAKE) -C wasm/node-tester
|
||||
$(MAKE) -C wasm/mix-fetch
|
||||
# $(MAKE) -C wasm/zknym-lib
|
||||
# $(MAKE) -C wasm/full-nym-wasm
|
||||
|
||||
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
|
||||
sdk-typescript-build:
|
||||
npx lerna run --scope @nymproject/sdk build --stream
|
||||
npx lerna run --scope @nymproject/mix-fetch build --stream
|
||||
npx lerna run --scope @nymproject/node-tester build --stream
|
||||
yarn --cwd sdk/typescript/codegen/contract-clients build
|
||||
pnpm --pwd sdk/typescript/codegen/contract-clients build
|
||||
|
||||
# NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown)
|
||||
# WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zknym-lib
|
||||
WASM_CRATES = nym-client-wasm nym-node-tester-wasm
|
||||
|
||||
WASM_CRATES = nym-client-wasm
|
||||
|
||||
sdk-wasm-test:
|
||||
#cargo test $(addprefix -p , $(WASM_CRATES)) --target wasm32-unknown-unknown -- -Dwarnings
|
||||
@@ -223,7 +219,7 @@ build-nym-cli:
|
||||
|
||||
generate-typescript:
|
||||
cd tools/ts-rs-cli && cargo run && cd ../..
|
||||
yarn types:lint:fix
|
||||
pnpm types:lint:fix
|
||||
|
||||
# Run the integration tests for public nym-api endpoints
|
||||
run-api-tests:
|
||||
|
||||
@@ -74,9 +74,9 @@ Nym Node Operators and Validators Terms and Conditions can be found [here](https
|
||||
## Getting Started
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
pnpm install
|
||||
```
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
pnpm build
|
||||
```
|
||||
|
||||
+20
-12
@@ -4,7 +4,7 @@
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"build:prod": "yarn --cwd .. build && next build",
|
||||
"build:prod": "pnpm --dir .. build && next build",
|
||||
"start": "next start",
|
||||
"lint": "biome check --fix"
|
||||
},
|
||||
@@ -13,21 +13,28 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@chain-registry/types": "^0.50.36",
|
||||
"@cosmjs/cosmwasm-stargate": "catalog:",
|
||||
"@cosmjs/stargate": "catalog:",
|
||||
"axios": "catalog:",
|
||||
"big.js": "catalog:",
|
||||
"long": "catalog:",
|
||||
"@cosmos-kit/keplr-extension": "^2.14.0",
|
||||
"@cosmos-kit/react": "^2.20.1",
|
||||
"@emotion/cache": "^11.13.5",
|
||||
"@emotion/react": "^11.13.5",
|
||||
"@emotion/styled": "^11.13.5",
|
||||
"@emotion/cache": "catalog:",
|
||||
"@emotion/react": "catalog:",
|
||||
"@emotion/styled": "catalog:",
|
||||
"@interchain-ui/react": "^1.26.1",
|
||||
"@keplr-wallet/types": "^0.12.211",
|
||||
"@mui/icons-material": "^5.16.11",
|
||||
"@mui/icons-material": "catalog:",
|
||||
"@mui/material": "^6.1.10",
|
||||
"@mui/system": "^6.1.10",
|
||||
"@mui/material-nextjs": "^6.1.9",
|
||||
"@mui/x-date-pickers": "^7.23.2",
|
||||
"@nivo/line": "^0.88.0",
|
||||
"@nymproject/contract-clients": "^1.4.1",
|
||||
"@nymproject/react": "1.0.0",
|
||||
"@tanstack/react-query": "^5.64.2",
|
||||
"@nymproject/react": "workspace:*",
|
||||
"@nymproject/types": "workspace:*",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@tanstack/react-query-devtools": "^5.64.2",
|
||||
"@tanstack/react-query-next-experimental": "^5.66.0",
|
||||
"@tanstack/react-table": "^8.20.6",
|
||||
@@ -44,8 +51,8 @@
|
||||
"openapi-fetch": "^0.13.4",
|
||||
"qrcode.react": "^4.1.0",
|
||||
"qs": "^6.14.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"react-i18next": "^15.4.0",
|
||||
"react-markdown": "^9.0.3",
|
||||
"react-random-avatars": "^1.3.1",
|
||||
@@ -54,10 +61,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/big.js": "catalog:",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^9",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"eslint": "catalog:",
|
||||
"eslint-config-next": "15.0.3",
|
||||
"lefthook": "^1.8.5",
|
||||
"typescript": "^5"
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Stack, Typography, useTheme } from "@mui/material";
|
||||
import Flag from "react-world-flags";
|
||||
import type React from "react";
|
||||
|
||||
interface ICountryFlag {
|
||||
countryCode: string;
|
||||
countryName?: string | JSX.Element;
|
||||
countryName?: string | React.JSX.Element;
|
||||
}
|
||||
|
||||
const CountryFlag = ({ countryCode, countryName }: ICountryFlag) => {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"use client";
|
||||
import type React from "react";
|
||||
import { icons } from "@/utils/getIconByName";
|
||||
import Image from "next/image";
|
||||
|
||||
// import { useMainContext } from "@/context";
|
||||
export const SocialIcon = ({ channel }: { channel: string }): JSX.Element => {
|
||||
export const SocialIcon = ({ channel }: { channel: string }): React.JSX.Element => {
|
||||
// const { mode } = useMainContext();
|
||||
const modeType = "light";
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ export const DarkLightSwitch = styled(Switch)(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export const DarkLightSwitchDesktop = (): JSX.Element => {
|
||||
export const DarkLightSwitchDesktop = (): React.JSX.Element => {
|
||||
const [mode, setMode] = useLocalStorage<PaletteMode>("mode", "dark");
|
||||
|
||||
const toggleMode = () => setMode((m) => (m !== "light" ? "light" : "dark"));
|
||||
|
||||
+2
-2
@@ -43,9 +43,9 @@
|
||||
pre-commit:
|
||||
commands:
|
||||
check:
|
||||
root: "explorer-nextjs/"
|
||||
root: "explorer-v2/"
|
||||
glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc,css}"
|
||||
run: yarn biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files}
|
||||
run: pnpm biome check --write --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files}
|
||||
stage_fixed: true
|
||||
rust-lint:
|
||||
glob: "*.rs"
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"version": "independent"
|
||||
"version": "independent",
|
||||
"npmClient": "pnpm"
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
dist/
|
||||
build/
|
||||
**/*.wasm
|
||||
**/*.js.map
|
||||
.env*
|
||||
!.env.example
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
@@ -1,125 +0,0 @@
|
||||
# Nym Browser Extension Storage
|
||||
|
||||
A WebAssembly-based storage component for securely managing mnemonics in extensions. This component provides encrypted storage functionality using IndexedDB with password-based encryption.
|
||||
|
||||
## Overview
|
||||
|
||||
This storage component is built in Rust and compiled to WebAssembly, providing:
|
||||
|
||||
- **Secure mnemonic storage**: Password-encrypted storage of BIP39 mnemonics
|
||||
- **IndexedDB integration**: Browser-native persistent storage
|
||||
- **Multiple account support**: Store and manage multiple mnemonic phrases with custom names
|
||||
- **Type-safe API**: Promise-based JavaScript API with proper error handling
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Rust (latest stable)
|
||||
- `wasm-pack` tool for building WebAssembly
|
||||
- Node.js (for the demo server)
|
||||
|
||||
### Building
|
||||
|
||||
```bash
|
||||
cd storage
|
||||
make wasm-pack
|
||||
```
|
||||
|
||||
This will compile the Rust code to WebAssembly and generate the necessary JavaScript bindings.
|
||||
|
||||
### Example Usage
|
||||
|
||||
See the [internal-dev example](./storage/internal-dev/index.js) for complete usage examples.
|
||||
|
||||
Basic usage:
|
||||
|
||||
```javascript
|
||||
import init, { ExtensionStorage, set_panic_hook } from "@nymproject/extension-storage"
|
||||
|
||||
// Initialize the WASM module first
|
||||
await init();
|
||||
|
||||
// Set up better error handling
|
||||
set_panic_hook();
|
||||
|
||||
// Create storage instance with password
|
||||
const storage = await new ExtensionStorage("your-secure-password");
|
||||
|
||||
// Store a mnemonic
|
||||
const mnemonic = "your twenty four word mnemonic phrase goes here...";
|
||||
await storage.store_mnemonic("my-wallet", mnemonic);
|
||||
|
||||
// Read a mnemonic
|
||||
const retrievedMnemonic = await storage.read_mnemonic("my-wallet");
|
||||
|
||||
// Check if a mnemonic exists
|
||||
const exists = await storage.has_mnemonic("my-wallet");
|
||||
|
||||
// Get all stored mnemonic keys
|
||||
const allKeys = await storage.get_all_mnemonic_keys();
|
||||
|
||||
// Remove a mnemonic
|
||||
await storage.remove_mnemonic("my-wallet");
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
To run the internal development example:
|
||||
|
||||
```bash
|
||||
cd storage
|
||||
|
||||
make demo
|
||||
|
||||
# Option 2: Manual server setup
|
||||
cd internal-dev && node serve.js
|
||||
|
||||
# Then open http://localhost:8000 in your browser
|
||||
```
|
||||
|
||||
**Note**: The demo requires a server that properly serves WASM files with `application/wasm` MIME type. The Node.js server is recommended as it handles MIME types more reliably.
|
||||
|
||||
## API Reference
|
||||
|
||||
### Initialization
|
||||
- `init()` - **Required**: Initialize the WASM module before using other functions
|
||||
|
||||
### Constructor
|
||||
- `new ExtensionStorage(password: string)` - Creates a new storage instance with the given password
|
||||
|
||||
### Static Methods
|
||||
- `ExtensionStorage.exists()` - Check if storage database exists
|
||||
|
||||
### Instance Methods
|
||||
- `store_mnemonic(name: string, mnemonic: string)` - Store a mnemonic with the given name
|
||||
- `read_mnemonic(name: string)` - Retrieve a mnemonic by name (returns null if not found)
|
||||
- `has_mnemonic(name: string)` - Check if a mnemonic with the given name exists
|
||||
- `get_all_mnemonic_keys()` - Get all stored mnemonic names
|
||||
- `remove_mnemonic(name: string)` - Remove a mnemonic by name
|
||||
|
||||
### Error Handling
|
||||
- `set_panic_hook()` - Set up better stack traces for Rust panics in development
|
||||
|
||||
## Security Features
|
||||
|
||||
- **Password-based encryption**: All data is encrypted using the provided password
|
||||
- **BIP39 validation**: Mnemonics are validated before storage
|
||||
- **Secure memory handling**: Sensitive data is zeroed from memory when no longer needed
|
||||
- **Browser sandbox**: Runs within the browser's security model
|
||||
|
||||
## Architecture
|
||||
|
||||
The storage component consists of:
|
||||
|
||||
- **Rust core** (`src/storage.rs`): Main storage implementation with encryption
|
||||
- **WASM bindings** (`src/lib.rs`): WebAssembly interface layer
|
||||
- **Error handling** (`src/error.rs`): Comprehensive error types
|
||||
- **Build configuration** (`Cargo.toml`, `Makefile`): Build and dependency management
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **WASM Initialization**: Always call `await init()` before using any other functions
|
||||
2. **MIME Types**: The demo requires a server that properly serves WASM files
|
||||
3. **Browser Compatibility**: Requires modern browsers with WebAssembly support
|
||||
4. **Module Loading**: Uses ES modules - ensure your build system supports them
|
||||
@@ -1,4 +0,0 @@
|
||||
[build]
|
||||
target = "wasm32-unknown-unknown"
|
||||
target_arch = "wasm32"
|
||||
rustflags = ["--cfg=getrandom_backend=\"wasm_js\""]
|
||||
@@ -1,36 +0,0 @@
|
||||
pkg
|
||||
target
|
||||
Cargo.lock
|
||||
|
||||
*.wasm
|
||||
*.js
|
||||
*.ts
|
||||
*.d.ts
|
||||
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
|
||||
internal-dev/wasm/
|
||||
@@ -1,37 +0,0 @@
|
||||
[package]
|
||||
name = "extension-storage"
|
||||
description = "WebAssembly-based secure storage for browser extension mnemonics"
|
||||
version = "1.4.1"
|
||||
authors = ["Nym Technologies SA <contact@nymtech.net>"]
|
||||
edition = "2024"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/nymtech/nym"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
bip39 = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
js-sys = { workspace = true }
|
||||
wasm-bindgen = { workspace = true }
|
||||
wasm-bindgen-futures = { workspace = true }
|
||||
serde-wasm-bindgen = { workspace = true }
|
||||
|
||||
thiserror = { workspace = true }
|
||||
|
||||
console_error_panic_hook = { workspace = true, optional = true }
|
||||
|
||||
nym-wasm-utils = { workspace = true }
|
||||
nym-wasm-storage = { workspace = true }
|
||||
|
||||
|
||||
#[package.metadata.wasm-pack.profile.release]
|
||||
#wasm-opt = false
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
@@ -1,65 +0,0 @@
|
||||
# Nym Extension Storage - Build Configuration
|
||||
#
|
||||
# This Makefile helps build the WebAssembly storage component
|
||||
# and run development examples.
|
||||
|
||||
wasm-pack:
|
||||
@echo "🔨 Building WebAssembly package..."
|
||||
unset RUSTC_WRAPPER && wasm-pack build --target web --scope nymproject --out-dir ../../dist/wasm/extension-storage
|
||||
@echo "✅ Build complete! Output in ../../dist/wasm/extension-storage"
|
||||
|
||||
wasm-pack-dev:
|
||||
@echo "🔨 Building WebAssembly package (development mode)..."
|
||||
unset RUSTC_WRAPPER && wasm-pack build --dev --target web --scope nymproject --out-dir ../../dist/wasm/extension-storage
|
||||
@echo "✅ Development build complete!"
|
||||
|
||||
|
||||
copy-wasm-to-demo:
|
||||
@echo "📁 Copying WASM files to demo directory..."
|
||||
@mkdir -p internal-dev/wasm
|
||||
@cp ../../dist/wasm/extension-storage/extension_storage.js internal-dev/wasm/
|
||||
@cp ../../dist/wasm/extension-storage/extension_storage_bg.wasm internal-dev/wasm/
|
||||
@cp ../../dist/wasm/extension-storage/extension_storage.d.ts internal-dev/wasm/
|
||||
@echo "✅ WASM files copied to internal-dev/wasm/"
|
||||
|
||||
|
||||
clean:
|
||||
@echo "🧹 Cleaning build artifacts..."
|
||||
cargo clean
|
||||
rm -rf ../../dist/wasm/extension-storage
|
||||
rm -rf pkg
|
||||
rm -rf internal-dev/wasm
|
||||
@echo "✅ Clean complete!"
|
||||
|
||||
demo: wasm-pack-dev copy-wasm-to-demo
|
||||
cd internal-dev && node serve.js
|
||||
|
||||
|
||||
check-deps:
|
||||
@echo "🔍 Checking dependencies..."
|
||||
@command -v cargo >/dev/null 2>&1 || { echo "❌ cargo is required but not installed. Please install Rust."; exit 1; }
|
||||
@command -v wasm-pack >/dev/null 2>&1 || { echo "❌ wasm-pack is required but not installed. Run: cargo install wasm-pack"; exit 1; }
|
||||
@echo "✅ All dependencies are installed!"
|
||||
|
||||
help:
|
||||
@echo "Nym Extension Storage Build Commands:"
|
||||
@echo ""
|
||||
@echo "📦 Building:"
|
||||
@echo " make wasm-pack - Build optimized WASM package for production"
|
||||
@echo " make wasm-pack-dev - Build WASM package with debug symbols"
|
||||
@echo " make copy-wasm-to-demo - Copy WASM files to demo directory"
|
||||
@echo ""
|
||||
@echo "🧪 Development:"
|
||||
@echo " make demo - Build and serve the interactive demo (Node.js)"
|
||||
@echo " make check-deps - Verify all required tools are installed"
|
||||
@echo ""
|
||||
@echo "🧹 Maintenance:"
|
||||
@echo " make clean - Remove all build artifacts"
|
||||
@echo " make help - Show this help message"
|
||||
@echo ""
|
||||
@echo "💡 Quick start: make demo"
|
||||
|
||||
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
.PHONY: wasm-pack wasm-pack-dev copy-wasm-to-demo clean demo check-deps help
|
||||
@@ -1,161 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nym Extension Storage - Demo</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
line-height: 1.6;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.container {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #6750A4;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.warning {
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffeaa7;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.info {
|
||||
background: #d1ecf1;
|
||||
border: 1px solid #bee5eb;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.success {
|
||||
background: #d4edda;
|
||||
border: 1px solid #c3e6cb;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
code {
|
||||
background: #f8f9fa;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
.console-output {
|
||||
background: #000;
|
||||
color: #00ff00;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 12px;
|
||||
margin-top: 20px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.button-group {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
button {
|
||||
background: #6750A4;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
button:hover {
|
||||
background: #5a45a0;
|
||||
}
|
||||
button:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🚀 Nym Extension Storage Demo</h1>
|
||||
|
||||
<div class="warning">
|
||||
<strong>⚠️ Demo Environment</strong><br>
|
||||
This is a development demo. The mnemonics used here are for testing only and should never be used in production.
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<strong>📋 How to use this demo:</strong><br>
|
||||
1. Open your browser's developer console (F12)<br>
|
||||
2. The demo will run automatically when the page loads<br>
|
||||
3. You can also use the interactive functions shown below<br>
|
||||
4. Check the IndexedDB in your browser's dev tools to see stored data
|
||||
</div>
|
||||
|
||||
<div class="success">
|
||||
<strong>✅ What this demo shows:</strong><br>
|
||||
• Creating encrypted storage instances<br>
|
||||
• Storing and retrieving BIP39 mnemonics<br>
|
||||
• Managing multiple wallets<br>
|
||||
• Error handling for invalid data<br>
|
||||
• Cleaning up stored data
|
||||
</div>
|
||||
|
||||
<h2>Interactive Functions</h2>
|
||||
<p>Open the browser console and try these commands:</p>
|
||||
|
||||
<div class="console-output">
|
||||
// Create a storage instance<br/>
|
||||
const storage = await window.nymStorageDemo.createStorage();<br/>
|
||||
<br/>
|
||||
// Run a quick test<br/>
|
||||
await window.nymStorageDemo.quickTest(storage);<br/>
|
||||
<br/>
|
||||
// Access test mnemonics<br/>
|
||||
console.log(window.nymStorageDemo.mnemonics);<br/>
|
||||
<br/>
|
||||
// Manual operations<br/>
|
||||
await storage.store_mnemonic("my-wallet", "your mnemonic here...");<br/>
|
||||
<br/>
|
||||
const mnemonic = await storage.read_mnemonic("my-wallet");<br/>
|
||||
const exists = await storage.has_mnemonic("my-wallet");<br/>
|
||||
const allKeys = await storage.get_all_mnemonic_keys();<br/>
|
||||
await storage.remove_mnemonic("my-wallet");
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button onclick="location.reload()">🔄 Restart Demo</button>
|
||||
<button onclick="window.open('https://github.com/nymtech/nym', '_blank')">📖 Nym Documentation</button>
|
||||
</div>
|
||||
|
||||
<h2>Architecture Overview</h2>
|
||||
<p>This storage component uses:</p>
|
||||
<ul>
|
||||
<li><strong>Rust/WASM</strong> - Core storage logic compiled to WebAssembly</li>
|
||||
<li><strong>IndexedDB</strong> - Browser-native persistent storage</li>
|
||||
<li><strong>AES Encryption</strong> - Password-based encryption for sensitive data</li>
|
||||
<li><strong>BIP39 Validation</strong> - Ensures mnemonic phrases are valid</li>
|
||||
</ul>
|
||||
|
||||
<div class="info">
|
||||
<strong>🔒 Security Notes:</strong><br>
|
||||
• All data is encrypted with your password before storage<br>
|
||||
• Mnemonics are validated against BIP39 standards<br>
|
||||
• Sensitive data is zeroed from memory when no longer needed<br>
|
||||
• Storage is isolated per browser origin
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Load the WASM module and demo -->
|
||||
<script type="module" src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,303 +0,0 @@
|
||||
/**
|
||||
* Nym Extension Storage - Internal Development Example
|
||||
*
|
||||
* This file demonstrates how to use the ExtensionStorage WASM module
|
||||
* for securely storing and managing BIP39 mnemonics in browser extensions.
|
||||
*
|
||||
* To run this example:
|
||||
* 1. Build the WASM module: `cd .. && make wasm-pack`
|
||||
* 2. Serve this directory: `python3 -m http.server 8000`
|
||||
* 3. Open http://localhost:8000 in your browser
|
||||
* 4. Open the browser's developer console to see the output
|
||||
*/
|
||||
|
||||
import init, {
|
||||
ExtensionStorage,
|
||||
set_panic_hook
|
||||
} from "./wasm/extension_storage.js"
|
||||
|
||||
/**
|
||||
* Test mnemonics for demonstration
|
||||
* Note: These are for testing only - never use these in production!
|
||||
*/
|
||||
const TEST_MNEMONICS = {
|
||||
valid: {
|
||||
wallet1: "figure aspect pill salute review sponsor army city muffin engine army kid rival chunk unit insect blouse paddle velvet shallow box crawl grace never",
|
||||
wallet2: "salmon picture danger pill tomato hour hand chaos tray bargain frequent fuel scheme coil divert season lucky ginger mom stem mistake blanket lake suffer",
|
||||
wallet3: "cat quiz circle letter trade unhappy quarter garlic sting gravity zone stock scatter merge account barrel forward fame club chest camp under crop connect",
|
||||
wallet4: "mammal fashion rice two marble high brain achieve first harsh infant timber flush cloud hunt address brand immune tip identify aspect call beyond once"
|
||||
},
|
||||
invalid: "this is not a valid mnemonic phrase"
|
||||
};
|
||||
|
||||
const STORAGE_PASSWORD = "my-super-secure-password-123";
|
||||
|
||||
/**
|
||||
* Helper function to log results with better formatting
|
||||
*/
|
||||
function logResult(operation, result, error = null) {
|
||||
const timestamp = new Date().toISOString();
|
||||
console.group(`🔍 [${timestamp}] ${operation}`);
|
||||
|
||||
if (error) {
|
||||
console.error("❌ Error:", error);
|
||||
} else {
|
||||
console.log("✅ Result:", result);
|
||||
}
|
||||
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic storage operations
|
||||
*/
|
||||
async function testBasicOperations(storage) {
|
||||
console.log("\n📦 Testing Basic Storage Operations");
|
||||
console.log("=" .repeat(50));
|
||||
|
||||
// Test storing a valid mnemonic
|
||||
try {
|
||||
await storage.store_mnemonic("test-wallet", TEST_MNEMONICS.valid.wallet1);
|
||||
logResult("Store valid mnemonic", "Successfully stored");
|
||||
} catch (error) {
|
||||
logResult("Store valid mnemonic", null, error);
|
||||
}
|
||||
|
||||
// Test reading the stored mnemonic
|
||||
try {
|
||||
const result = await storage.read_mnemonic("test-wallet");
|
||||
logResult("Read stored mnemonic", result);
|
||||
} catch (error) {
|
||||
logResult("Read stored mnemonic", null, error);
|
||||
}
|
||||
|
||||
// Test reading a non-existent mnemonic
|
||||
try {
|
||||
const result = await storage.read_mnemonic("non-existent");
|
||||
logResult("Read non-existent mnemonic", result);
|
||||
} catch (error) {
|
||||
logResult("Read non-existent mnemonic", null, error);
|
||||
}
|
||||
|
||||
// Test storing an invalid mnemonic
|
||||
try {
|
||||
await storage.store_mnemonic("invalid-wallet", TEST_MNEMONICS.invalid);
|
||||
logResult("Store invalid mnemonic", "Should not reach here");
|
||||
} catch (error) {
|
||||
logResult("Store invalid mnemonic", null, error.toString());
|
||||
}
|
||||
|
||||
// Clean up
|
||||
try {
|
||||
await storage.remove_mnemonic("test-wallet");
|
||||
logResult("Remove test wallet", "Successfully removed");
|
||||
} catch (error) {
|
||||
logResult("Remove test wallet", null, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test multiple wallet management
|
||||
*/
|
||||
async function testMultipleWallets(storage) {
|
||||
console.log("\n👥 Testing Multiple Wallet Management");
|
||||
console.log("=" .repeat(50));
|
||||
|
||||
// Store multiple wallets
|
||||
const walletNames = Object.keys(TEST_MNEMONICS.valid);
|
||||
const walletMnemonics = Object.values(TEST_MNEMONICS.valid);
|
||||
|
||||
for (let i = 0; i < walletNames.length; i++) {
|
||||
try {
|
||||
await storage.store_mnemonic(walletNames[i], walletMnemonics[i]);
|
||||
logResult(`Store wallet: ${walletNames[i]}`, "Success");
|
||||
} catch (error) {
|
||||
logResult(`Store wallet: ${walletNames[i]}`, null, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Get all wallet keys
|
||||
try {
|
||||
const allKeys = await storage.get_all_mnemonic_keys();
|
||||
logResult("Get all wallet keys", allKeys);
|
||||
} catch (error) {
|
||||
logResult("Get all wallet keys", null, error);
|
||||
}
|
||||
|
||||
// Check if specific wallets exist
|
||||
for (const walletName of walletNames) {
|
||||
try {
|
||||
const exists = await storage.has_mnemonic(walletName);
|
||||
logResult(`Check wallet exists: ${walletName}`, exists);
|
||||
} catch (error) {
|
||||
logResult(`Check wallet exists: ${walletName}`, null, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Read each wallet
|
||||
for (const walletName of walletNames) {
|
||||
try {
|
||||
const mnemonic = await storage.read_mnemonic(walletName);
|
||||
logResult(`Read wallet: ${walletName}`, `${mnemonic.substring(0, 20)}...`);
|
||||
} catch (error) {
|
||||
logResult(`Read wallet: ${walletName}`, null, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test wallet removal and cleanup
|
||||
*/
|
||||
async function testWalletRemoval(storage) {
|
||||
console.log("\n🗑️ Testing Wallet Removal");
|
||||
console.log("=" .repeat(50));
|
||||
|
||||
const walletNames = Object.keys(TEST_MNEMONICS.valid);
|
||||
|
||||
// Remove wallets one by one
|
||||
for (const walletName of walletNames) {
|
||||
try {
|
||||
await storage.remove_mnemonic(walletName);
|
||||
logResult(`Remove wallet: ${walletName}`, "Success");
|
||||
} catch (error) {
|
||||
logResult(`Remove wallet: ${walletName}`, null, error);
|
||||
}
|
||||
|
||||
// Verify removal
|
||||
try {
|
||||
const exists = await storage.has_mnemonic(walletName);
|
||||
logResult(`Verify removal: ${walletName}`, `Exists: ${exists}`);
|
||||
} catch (error) {
|
||||
logResult(`Verify removal: ${walletName}`, null, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check final state
|
||||
try {
|
||||
const allKeys = await storage.get_all_mnemonic_keys();
|
||||
logResult("Final wallet count", `${allKeys.length} wallets remaining`);
|
||||
} catch (error) {
|
||||
logResult("Final wallet count", null, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test storage existence check
|
||||
*/
|
||||
async function testStorageExistence() {
|
||||
console.log("\n💾 Testing Storage Existence");
|
||||
console.log("=" .repeat(50));
|
||||
|
||||
try {
|
||||
const exists = await ExtensionStorage.exists();
|
||||
logResult("Storage database exists", exists);
|
||||
} catch (error) {
|
||||
logResult("Storage database exists", null, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main demonstration function
|
||||
*/
|
||||
async function runDemo() {
|
||||
console.log("🚀 Nym Extension Storage Demo");
|
||||
console.log("=" .repeat(60));
|
||||
console.log("This demo shows how to use the ExtensionStorage WASM module");
|
||||
console.log("Check the browser console for detailed output\n");
|
||||
|
||||
try {
|
||||
// Initialize the WASM module
|
||||
console.log("🔧 Initializing WASM module...");
|
||||
await init();
|
||||
console.log("✅ WASM module initialized successfully\n");
|
||||
|
||||
// Set up better stack traces for Rust panics
|
||||
set_panic_hook();
|
||||
} catch (error) {
|
||||
console.error("💥 Failed to initialize WASM module:", error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Test storage existence before creating instance
|
||||
await testStorageExistence();
|
||||
|
||||
try {
|
||||
// Create storage instance with password
|
||||
console.log("🔐 Creating storage instance with password...");
|
||||
const storage = await new ExtensionStorage(STORAGE_PASSWORD);
|
||||
console.log("✅ Storage instance created successfully\n");
|
||||
|
||||
// Run all tests
|
||||
await testBasicOperations(storage);
|
||||
await testMultipleWallets(storage);
|
||||
await testWalletRemoval(storage);
|
||||
|
||||
// Test storage existence after operations
|
||||
await testStorageExistence();
|
||||
|
||||
console.log("\n🎉 Demo completed successfully!");
|
||||
console.log("Check the IndexedDB in your browser's developer tools to see the stored data.");
|
||||
|
||||
} catch (error) {
|
||||
console.error("💥 Fatal error during demo:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional utility functions for interactive testing
|
||||
*/
|
||||
window.nymStorageDemo = {
|
||||
/**
|
||||
* Create a new storage instance for manual testing
|
||||
*/
|
||||
async createStorage(password = STORAGE_PASSWORD) {
|
||||
try {
|
||||
await init();
|
||||
set_panic_hook();
|
||||
return await new ExtensionStorage(password);
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize WASM or create storage:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Quick test with a storage instance
|
||||
*/
|
||||
async quickTest(storage, name = "test", mnemonic = TEST_MNEMONICS.valid.wallet1) {
|
||||
console.log(`Testing with wallet: ${name}`);
|
||||
|
||||
await storage.store_mnemonic(name, mnemonic);
|
||||
console.log("✅ Stored");
|
||||
|
||||
const retrieved = await storage.read_mnemonic(name);
|
||||
console.log("✅ Retrieved:", retrieved);
|
||||
|
||||
const exists = await storage.has_mnemonic(name);
|
||||
console.log("✅ Exists:", exists);
|
||||
|
||||
await storage.remove_mnemonic(name);
|
||||
console.log("✅ Removed");
|
||||
|
||||
return "Test completed";
|
||||
},
|
||||
|
||||
/**
|
||||
* Available test mnemonics
|
||||
*/
|
||||
mnemonics: TEST_MNEMONICS
|
||||
};
|
||||
|
||||
// Start the demo when the page loads
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log("📚 Interactive Demo Functions Available:");
|
||||
console.log("- window.nymStorageDemo.createStorage() - Create storage instance");
|
||||
console.log("- window.nymStorageDemo.quickTest(storage) - Run quick test");
|
||||
console.log("- window.nymStorageDemo.mnemonics - Test mnemonic phrases");
|
||||
console.log("\nStarting automated demo...\n");
|
||||
|
||||
runDemo();
|
||||
});
|
||||
|
||||
// Export for module usage
|
||||
export { runDemo, TEST_MNEMONICS, STORAGE_PASSWORD };
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_wasm_storage::error::StorageError;
|
||||
use nym_wasm_utils::wasm_error;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtensionStorageError {
|
||||
#[error("serialization failure: {source}")]
|
||||
JsonError {
|
||||
#[from]
|
||||
source: serde_wasm_bindgen::Error,
|
||||
},
|
||||
|
||||
#[error("failed to use the storage: {source}")]
|
||||
StorageError {
|
||||
#[from]
|
||||
source: StorageError,
|
||||
},
|
||||
|
||||
#[error("there's already a stored mnemonic with name {name}")]
|
||||
DuplicateMnemonic { name: String },
|
||||
|
||||
#[error("the provided mnemonic is malformed: {source}")]
|
||||
InvalidMnemonic {
|
||||
#[from]
|
||||
source: bip39::Error,
|
||||
},
|
||||
}
|
||||
|
||||
wasm_error!(ExtensionStorageError);
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod error;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod storage;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use error::ExtensionStorageError;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use storage::ExtensionStorage;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use nym_wasm_utils::set_panic_hook;
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// I'm not convinced by the lint (on Arc<WasmStorage>).
|
||||
// Sure. wasm is currently single threaded and does not require `Send` or `Sync`
|
||||
// but this data is moved across futures, so imo we should leave the Arc as it is,
|
||||
// because it might cause us headache in the future
|
||||
#![allow(clippy::arc_with_non_send_sync)]
|
||||
|
||||
use crate::ExtensionStorageError;
|
||||
use js_sys::Promise;
|
||||
use nym_wasm_storage::RawDbResult;
|
||||
use nym_wasm_storage::{Build, Database, VersionChangeEvent, WasmStorage};
|
||||
use nym_wasm_utils::check_promise_result;
|
||||
use nym_wasm_utils::error::{PromisableResult, PromisableResultError};
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
const STORAGE_NAME: &str = "nym-extension-storage";
|
||||
const STORAGE_VERSION: u32 = 1;
|
||||
|
||||
// v1 tables
|
||||
mod v1 {
|
||||
// stores
|
||||
pub const MNEMONICS_STORE: &str = "mnemonics";
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Clone)]
|
||||
pub struct ExtensionStorage {
|
||||
inner: Arc<WasmStorage>,
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn db_migration() -> Box<dyn Fn(VersionChangeEvent, Database) -> RawDbResult<()>> {
|
||||
Box::new(|evt: VersionChangeEvent, db: Database| -> RawDbResult<()> {
|
||||
// Even if the web-sys bindings expose the version as a f64, the IndexedDB API
|
||||
// works with an unsigned integer.
|
||||
// See <https://github.com/rustwasm/wasm-bindgen/issues/1149>
|
||||
let old_version = evt.old_version() as u32;
|
||||
|
||||
if old_version < 1 {
|
||||
// migrating to version 1
|
||||
db.create_object_store(v1::MNEMONICS_STORE).build()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl ExtensionStorage {
|
||||
pub(crate) async fn new_async(passphrase: String) -> Result<Self, ExtensionStorageError> {
|
||||
// make sure the password is zeroized when no longer used, especially if we error out.
|
||||
// special care must be taken on JS side to ensure it's correctly used there.
|
||||
let passphrase = Zeroizing::new(passphrase);
|
||||
|
||||
let pass_ref: &str = passphrase.as_ref();
|
||||
|
||||
let inner = WasmStorage::new(
|
||||
STORAGE_NAME,
|
||||
STORAGE_VERSION,
|
||||
Some(db_migration()),
|
||||
Some(pass_ref.as_bytes()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(ExtensionStorage {
|
||||
inner: Arc::new(inner),
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen(constructor)]
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(passphrase: String) -> Promise {
|
||||
future_to_promise(async move { Self::new_async(passphrase).await.into_promise_result() })
|
||||
}
|
||||
|
||||
pub(crate) async fn exists_async() -> Result<bool, ExtensionStorageError> {
|
||||
Ok(WasmStorage::exists(STORAGE_NAME).await?)
|
||||
}
|
||||
|
||||
pub fn exists() -> Promise {
|
||||
future_to_promise(async move { Self::exists_async().await.into_promise_result() })
|
||||
}
|
||||
|
||||
async fn store_mnemonic_async(
|
||||
&self,
|
||||
name: String,
|
||||
value: &bip39::Mnemonic,
|
||||
) -> Result<(), ExtensionStorageError> {
|
||||
self.inner
|
||||
.store_value(v1::MNEMONICS_STORE, JsValue::from_str(&name), value)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn read_mnemonic_async(
|
||||
&self,
|
||||
name: String,
|
||||
) -> Result<Option<bip39::Mnemonic>, ExtensionStorageError> {
|
||||
self.inner
|
||||
.read_value(v1::MNEMONICS_STORE, JsValue::from_str(&name))
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn remove_mnemonic_async(&self, name: String) -> Result<(), ExtensionStorageError> {
|
||||
self.inner
|
||||
.remove_value(v1::MNEMONICS_STORE, JsValue::from_str(&name))
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn has_mnemonic_async(&self, name: String) -> Result<bool, ExtensionStorageError> {
|
||||
self.inner
|
||||
.has_value(v1::MNEMONICS_STORE, JsValue::from_str(&name))
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
async fn get_all_mnemonic_keys_async(&self) -> Result<Vec<JsValue>, ExtensionStorageError> {
|
||||
self.inner
|
||||
.get_all_keys(v1::MNEMONICS_STORE)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn store_mnemonic(&self, name: String, value: String) -> Promise {
|
||||
let wrapped = Zeroizing::new(value);
|
||||
let inner: &str = wrapped.as_ref();
|
||||
|
||||
let mnemonic = check_promise_result!(
|
||||
bip39::Mnemonic::parse(inner).map_err(ExtensionStorageError::from)
|
||||
);
|
||||
|
||||
// this clones the Arc pointer
|
||||
let this = self.clone();
|
||||
future_to_promise(async move {
|
||||
this.store_mnemonic_async(name, &mnemonic)
|
||||
.await
|
||||
.map(|_| JsValue::null())
|
||||
.map_promise_err()
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn read_mnemonic(&self, name: String) -> Promise {
|
||||
// this clones the Arc pointer
|
||||
let this = self.clone();
|
||||
future_to_promise(async move {
|
||||
let maybe_mnemonic = this.read_mnemonic_async(name).await?;
|
||||
Ok(serde_wasm_bindgen::to_value(&maybe_mnemonic)?)
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn remove_mnemonic(&self, name: String) -> Promise {
|
||||
// this clones the Arc pointer
|
||||
let this = self.clone();
|
||||
future_to_promise(async move {
|
||||
this.remove_mnemonic_async(name)
|
||||
.await
|
||||
.map(|_| JsValue::null())
|
||||
.map_promise_err()
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn has_mnemonic(&self, name: String) -> Promise {
|
||||
// this clones the Arc pointer
|
||||
let this = self.clone();
|
||||
future_to_promise(async move { this.has_mnemonic_async(name).await.into_promise_result() })
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn get_all_mnemonic_keys(&self) -> Promise {
|
||||
// this clones the Arc pointer
|
||||
let this = self.clone();
|
||||
future_to_promise(async move {
|
||||
this.get_all_mnemonic_keys_async()
|
||||
.await
|
||||
.into_promise_result()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
"test:prod": "TEST_ENV=prod jest --forceExit --detectOpenHandles --passWithNoTests",
|
||||
"build": "tsc",
|
||||
"lint": "eslint --fix --ext .js,.ts,.tsx .",
|
||||
"cleanup": "rm -rf node_modules; rm -rf dist; yarn install"
|
||||
"cleanup": "rm -rf node_modules; rm -rf dist; pnpm install"
|
||||
},
|
||||
"author": "Nymtech",
|
||||
"license": "MIT",
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
22.13.0
|
||||
24
|
||||
+173
-111
@@ -10,133 +10,195 @@
|
||||
"dev": "run-p tauri:dev webpack:dev",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix",
|
||||
"prebuild": "yarn --cwd .. build",
|
||||
"prewebpack:dev": "yarn --cwd .. build",
|
||||
"prewebpack:prod": "yarn check:singletons",
|
||||
"prebuild": "pnpm --dir .. run build",
|
||||
"prewebpack:dev": "pnpm --dir .. run build",
|
||||
"prewebpack:prod": "pnpm run check:singletons",
|
||||
"check:singletons": "node scripts/check-mui-singletons.js",
|
||||
"tauri:build": "yarn tauri build",
|
||||
"tauri:build:no-sign": "yarn tauri build --no-sign -b app",
|
||||
"tauri:build:adhoc": "APPLE_SIGNING_IDENTITY=- yarn tauri build -b app",
|
||||
"tauri:dev": "yarn tauri dev",
|
||||
"tauri:buildx86": "yarn tauri build --target x86_64-apple-darwin",
|
||||
"tauri:build": "tauri build",
|
||||
"tauri:build:no-sign": "tauri build --no-sign -b app",
|
||||
"tauri:build:adhoc": "APPLE_SIGNING_IDENTITY=- tauri build -b app",
|
||||
"tauri:dev": "tauri dev",
|
||||
"tauri:buildx86": "tauri build --target x86_64-apple-darwin",
|
||||
"test": "jest --config jest.config.cjs",
|
||||
"tsc": "tsc --noEmit true",
|
||||
"tsc:watch": "tsc --noEmit true --watch",
|
||||
"webpack:dev": "yarn webpack serve --config webpack.dev.js",
|
||||
"webpack:prod": "yarn webpack --progress --config webpack.prod.js"
|
||||
"webpack:dev": "webpack serve --config webpack.dev.js",
|
||||
"webpack:prod": "webpack --progress --config webpack.prod.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/helper-simple-access": "^7.25.9",
|
||||
"@emotion/cache": "^11.14.0",
|
||||
"@emotion/react": "^11.7.0",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
|
||||
"@hookform/resolvers": "^2.8.0",
|
||||
"@mui/icons-material": "^5.2.0",
|
||||
"@mui/material": "^5.2.2",
|
||||
"@mui/styles": "^5.18.0",
|
||||
"@mui/utils": "^5.7.0",
|
||||
"@nymproject/node-tester": "^1.3.1",
|
||||
"@nymproject/react": "^1.0.0",
|
||||
"@nymproject/types": "^1.0.0",
|
||||
"@tanstack/react-query": "^5.62.0",
|
||||
"@tauri-apps/api": "^2.10.1",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
|
||||
"@tauri-apps/plugin-opener": "^2.5.3",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
|
||||
"big.js": "^6.2.1",
|
||||
"bs58": "^4.0.1",
|
||||
"clsx": "^1.1.1",
|
||||
"date-fns": "^2.28.0",
|
||||
"joi": "^17.11.0",
|
||||
"lodash": "^4.17.21",
|
||||
"notistack": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"@babel/helper-simple-access": "catalog:",
|
||||
"@babel/runtime": "catalog:",
|
||||
"@cosmjs/math": "catalog:",
|
||||
"@emotion/cache": "catalog:",
|
||||
"@emotion/hash": "catalog:",
|
||||
"@emotion/is-prop-valid": "catalog:",
|
||||
"@emotion/memoize": "catalog:",
|
||||
"@emotion/react": "catalog:",
|
||||
"@emotion/serialize": "catalog:",
|
||||
"@emotion/sheet": "catalog:",
|
||||
"@emotion/styled": "catalog:",
|
||||
"@emotion/unitless": "catalog:",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "catalog:",
|
||||
"@emotion/utils": "catalog:",
|
||||
"@emotion/weak-memoize": "catalog:",
|
||||
"@hookform/resolvers": "catalog:",
|
||||
"@mui/base": "catalog:",
|
||||
"@mui/lab": "catalog:",
|
||||
"@mui/material": "catalog:",
|
||||
"@mui/private-theming": "catalog:",
|
||||
"@mui/styles": "catalog:",
|
||||
"@mui/system": "catalog:",
|
||||
"@mui/icons-material": "catalog:",
|
||||
"@mui/utils": "catalog:",
|
||||
"@nymproject/react": "workspace:*",
|
||||
"@nymproject/types": "workspace:*",
|
||||
"@popperjs/core": "catalog:",
|
||||
"@remix-run/router": "catalog:",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@tanstack/query-core": "catalog:",
|
||||
"@tauri-apps/api": "catalog:",
|
||||
"@tauri-apps/plugin-clipboard-manager": "catalog:",
|
||||
"@tauri-apps/plugin-opener": "catalog:",
|
||||
"@tauri-apps/plugin-process": "catalog:",
|
||||
"@tauri-apps/plugin-updater": "catalog:",
|
||||
"@tauri-apps/tauri-forage": "catalog:",
|
||||
"base-x": "catalog:",
|
||||
"base64-js": "catalog:",
|
||||
"bech32": "catalog:",
|
||||
"big.js": "catalog:",
|
||||
"bn.js": "catalog:",
|
||||
"bs58": "catalog:",
|
||||
"buffer": "catalog:",
|
||||
"clsx": "catalog:",
|
||||
"clipboard-copy": "catalog:",
|
||||
"colornames": "catalog:",
|
||||
"d3-array": "catalog:",
|
||||
"d3-color": "catalog:",
|
||||
"d3-format": "catalog:",
|
||||
"d3-interpolate": "catalog:",
|
||||
"d3-path": "catalog:",
|
||||
"d3-scale": "catalog:",
|
||||
"d3-shape": "catalog:",
|
||||
"d3-time": "catalog:",
|
||||
"d3-time-format": "catalog:",
|
||||
"date-fns": "catalog:",
|
||||
"decimal.js-light": "catalog:",
|
||||
"dom-helpers": "catalog:",
|
||||
"eventemitter3": "catalog:",
|
||||
"fast-equals": "catalog:",
|
||||
"hex-rgb": "catalog:",
|
||||
"hoist-non-react-statics": "catalog:",
|
||||
"ieee754": "catalog:",
|
||||
"internmap": "catalog:",
|
||||
"joi": "catalog:",
|
||||
"localforage": "catalog:",
|
||||
"lodash": "catalog:",
|
||||
"lodash.padend": "catalog:",
|
||||
"lodash.trimstart": "catalog:",
|
||||
"lodash.words": "catalog:",
|
||||
"nanoclone": "catalog:",
|
||||
"notistack": "catalog:",
|
||||
"npm-run-all": "catalog:",
|
||||
"prop-types": "catalog:",
|
||||
"property-expr": "catalog:",
|
||||
"qr.js": "catalog:",
|
||||
"qrcode.react": "^1.0.1",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-error-boundary": "^3.1.3",
|
||||
"react-hook-form": "^7.14.2",
|
||||
"react-router-dom": "6",
|
||||
"recharts": "^2.1.13",
|
||||
"semver": "^6.3.0",
|
||||
"string-to-color": "^2.2.2",
|
||||
"use-clipboard-copy": "^0.2.0",
|
||||
"uuid": "^8.3.2",
|
||||
"yup": "^0.32.9",
|
||||
"zxcvbn": "^4.4.2"
|
||||
"ramda": "catalog:",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"react-error-boundary": "catalog:",
|
||||
"react-hook-form": "catalog:",
|
||||
"react-is": "catalog:",
|
||||
"react-router": "catalog:",
|
||||
"react-router-dom": "catalog:",
|
||||
"react-smooth": "catalog:",
|
||||
"react-transition-group": "catalog:",
|
||||
"recharts": "catalog:",
|
||||
"recharts-scale": "catalog:",
|
||||
"rgb-hex": "catalog:",
|
||||
"safe-buffer": "catalog:",
|
||||
"scheduler": "catalog:",
|
||||
"semver": "catalog:",
|
||||
"string-to-color": "catalog:",
|
||||
"stylis": "catalog:",
|
||||
"tiny-invariant": "catalog:",
|
||||
"toposort": "catalog:",
|
||||
"tweetnacl": "catalog:",
|
||||
"tweetnacl-util": "catalog:",
|
||||
"use-clipboard-copy": "catalog:",
|
||||
"uuid": "catalog:",
|
||||
"victory-vendor": "catalog:",
|
||||
"yup": "catalog:",
|
||||
"zxcvbn": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.14.5",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@nymproject/eslint-config-react-typescript": "^1.0.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
|
||||
"@svgr/webpack": "^6.1.1",
|
||||
"@tauri-apps/cli": "^2.10.1",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/big.js": "^6.1.6",
|
||||
"@types/bs58": "^4.0.1",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/minimatch": "5.1.2",
|
||||
"@babel/core": "catalog:",
|
||||
"@babel/plugin-transform-async-to-generator": "catalog:",
|
||||
"@babel/preset-env": "catalog:",
|
||||
"@babel/preset-react": "catalog:",
|
||||
"@babel/preset-typescript": "catalog:",
|
||||
"@nymproject/webpack": "workspace:*",
|
||||
"@nymproject/eslint-config-react-typescript": "workspace:*",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "catalog:",
|
||||
"@svgr/webpack": "catalog:",
|
||||
"@tauri-apps/cli": "catalog:",
|
||||
"@testing-library/dom": "catalog:",
|
||||
"@testing-library/jest-dom": "catalog:",
|
||||
"@testing-library/react": "catalog:",
|
||||
"@types/big.js": "catalog:",
|
||||
"@types/bs58": "catalog:",
|
||||
"@types/jest": "catalog:",
|
||||
"@types/lodash": "catalog:",
|
||||
"@types/minimatch": "catalog:",
|
||||
"@types/node": "^22.15.29",
|
||||
"@types/qrcode.react": "^1.0.2",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/semver": "^7.3.8",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/zxcvbn": "^4.4.1",
|
||||
"@types/qrcode.react": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@types/semver": "catalog:",
|
||||
"@types/uuid": "catalog:",
|
||||
"@types/zxcvbn": "catalog:",
|
||||
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
||||
"@typescript-eslint/parser": "^8.56.1",
|
||||
"babel-loader": "^8.3.0",
|
||||
"babel-loader": "catalog:",
|
||||
"babel-plugin-root-import": "^6.6.0",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
"clean-webpack-plugin": "catalog:",
|
||||
"css-loader": "catalog:",
|
||||
"css-minimizer-webpack-plugin": "catalog:",
|
||||
"dotenv-webpack": "catalog:",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-root-import": "^1.0.4",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jest": "^26.1.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.29.2",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"favicons": "^7.0.2",
|
||||
"favicons-webpack-plugin": "^5.0.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.1",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"jest": "^30.3.0",
|
||||
"mini-css-extract-plugin": "^2.2.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.7",
|
||||
"react-refresh": "^0.10.0",
|
||||
"react-refresh-typescript": "^2.0.2",
|
||||
"style-loader": "^3.3.1",
|
||||
"thread-loader": "^3.0.4",
|
||||
"eslint-config-airbnb": "catalog:",
|
||||
"eslint-config-airbnb-typescript": "catalog:",
|
||||
"eslint-config-prettier": "catalog:",
|
||||
"eslint-import-resolver-root-import": "catalog:",
|
||||
"eslint-plugin-import": "catalog:",
|
||||
"eslint-plugin-jest": "catalog:",
|
||||
"eslint-plugin-jsx-a11y": "catalog:",
|
||||
"eslint-plugin-prettier": "catalog:",
|
||||
"eslint-plugin-react": "catalog:",
|
||||
"eslint-plugin-react-hooks": "catalog:",
|
||||
"favicons": "catalog:",
|
||||
"favicons-webpack-plugin": "catalog:",
|
||||
"file-loader": "catalog:",
|
||||
"fork-ts-checker-webpack-plugin": "catalog:",
|
||||
"html-webpack-plugin": "catalog:",
|
||||
"jest": "^29.7.0",
|
||||
"mini-css-extract-plugin": "catalog:",
|
||||
"npm-run-all": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"react-refresh": "catalog:",
|
||||
"react-refresh-typescript": "catalog:",
|
||||
"style-loader": "catalog:",
|
||||
"thread-loader": "catalog:",
|
||||
"ts-jest": "^29.4.9",
|
||||
"ts-loader": "^9.4.2",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"ts-loader": "catalog:",
|
||||
"tsconfig-paths-webpack-plugin": "catalog:",
|
||||
"typescript": "^5.9.3",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.5.0",
|
||||
"webpack-favicons": "^1.3.8",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/minimatch": "5.1.2"
|
||||
"url-loader": "catalog:",
|
||||
"webpack": "catalog:",
|
||||
"webpack-cli": "catalog:",
|
||||
"webpack-dev-server": "catalog:",
|
||||
"webpack-favicons": "catalog:",
|
||||
"webpack-merge": "catalog:"
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
SRC="$ROOT/src-tauri/icons/app-icon-source.png"
|
||||
yarn --cwd "$ROOT" tauri icon "$SRC" -o "$ROOT/src-tauri/icons"
|
||||
pnpm --dir "$ROOT" exec tauri icon "$SRC" -o "$ROOT/src-tauri/icons"
|
||||
rm -rf "$ROOT/src-tauri/icons/android" "$ROOT/src-tauri/icons/ios"
|
||||
rm -f "$ROOT/src-tauri/icons"/Square*.png "$ROOT/src-tauri/icons/StoreLogo.png"
|
||||
python3 - <<PY
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -429,10 +429,10 @@
|
||||
"markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`",
|
||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`",
|
||||
"type": "string",
|
||||
"const": "core:app:default",
|
||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`"
|
||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the app_hide command without any pre-configured scope.",
|
||||
@@ -506,6 +506,12 @@
|
||||
"const": "core:app:allow-set-dock-visibility",
|
||||
"markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the supports_multiple_windows command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:allow-supports-multiple-windows",
|
||||
"markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the tauri_version command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -590,6 +596,12 @@
|
||||
"const": "core:app:deny-set-dock-visibility",
|
||||
"markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the supports_multiple_windows command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:deny-supports-multiple-windows",
|
||||
"markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the tauri_version command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -1113,10 +1125,10 @@
|
||||
"markdownDescription": "Denies the close command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`",
|
||||
"description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`",
|
||||
"type": "string",
|
||||
"const": "core:tray:default",
|
||||
"markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`"
|
||||
"markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the get_by_id command without any pre-configured scope.",
|
||||
@@ -1148,6 +1160,12 @@
|
||||
"const": "core:tray:allow-set-icon-as-template",
|
||||
"markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_icon_with_as_template command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:tray:allow-set-icon-with-as-template",
|
||||
"markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_menu command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -1214,6 +1232,12 @@
|
||||
"const": "core:tray:deny-set-icon-as-template",
|
||||
"markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_icon_with_as_template command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:tray:deny-set-icon-with-as-template",
|
||||
"markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_menu command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -1473,10 +1497,16 @@
|
||||
"markdownDescription": "Denies the webview_size command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`",
|
||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`",
|
||||
"type": "string",
|
||||
"const": "core:window:default",
|
||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`"
|
||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the activity_name command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:allow-activity-name",
|
||||
"markdownDescription": "Enables the activity_name command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the available_monitors command without any pre-configured scope.",
|
||||
@@ -1670,6 +1700,12 @@
|
||||
"const": "core:window:allow-scale-factor",
|
||||
"markdownDescription": "Enables the scale_factor command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the scene_identifier command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:allow-scene-identifier",
|
||||
"markdownDescription": "Enables the scene_identifier command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_always_on_bottom command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -1934,6 +1970,12 @@
|
||||
"const": "core:window:allow-unminimize",
|
||||
"markdownDescription": "Enables the unminimize command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the activity_name command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:deny-activity-name",
|
||||
"markdownDescription": "Denies the activity_name command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the available_monitors command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -2126,6 +2168,12 @@
|
||||
"const": "core:window:deny-scale-factor",
|
||||
"markdownDescription": "Denies the scale_factor command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the scene_identifier command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:deny-scene-identifier",
|
||||
"markdownDescription": "Denies the scene_identifier command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_always_on_bottom command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
|
||||
@@ -429,10 +429,10 @@
|
||||
"markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`",
|
||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`",
|
||||
"type": "string",
|
||||
"const": "core:app:default",
|
||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`"
|
||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the app_hide command without any pre-configured scope.",
|
||||
@@ -506,6 +506,12 @@
|
||||
"const": "core:app:allow-set-dock-visibility",
|
||||
"markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the supports_multiple_windows command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:allow-supports-multiple-windows",
|
||||
"markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the tauri_version command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -590,6 +596,12 @@
|
||||
"const": "core:app:deny-set-dock-visibility",
|
||||
"markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the supports_multiple_windows command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:deny-supports-multiple-windows",
|
||||
"markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the tauri_version command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -1113,10 +1125,10 @@
|
||||
"markdownDescription": "Denies the close command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`",
|
||||
"description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`",
|
||||
"type": "string",
|
||||
"const": "core:tray:default",
|
||||
"markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`"
|
||||
"markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the get_by_id command without any pre-configured scope.",
|
||||
@@ -1148,6 +1160,12 @@
|
||||
"const": "core:tray:allow-set-icon-as-template",
|
||||
"markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_icon_with_as_template command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:tray:allow-set-icon-with-as-template",
|
||||
"markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_menu command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -1214,6 +1232,12 @@
|
||||
"const": "core:tray:deny-set-icon-as-template",
|
||||
"markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_icon_with_as_template command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:tray:deny-set-icon-with-as-template",
|
||||
"markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_menu command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -1473,10 +1497,16 @@
|
||||
"markdownDescription": "Denies the webview_size command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`",
|
||||
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`",
|
||||
"type": "string",
|
||||
"const": "core:window:default",
|
||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`"
|
||||
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`"
|
||||
},
|
||||
{
|
||||
"description": "Enables the activity_name command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:allow-activity-name",
|
||||
"markdownDescription": "Enables the activity_name command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the available_monitors command without any pre-configured scope.",
|
||||
@@ -1670,6 +1700,12 @@
|
||||
"const": "core:window:allow-scale-factor",
|
||||
"markdownDescription": "Enables the scale_factor command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the scene_identifier command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:allow-scene-identifier",
|
||||
"markdownDescription": "Enables the scene_identifier command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_always_on_bottom command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -1934,6 +1970,12 @@
|
||||
"const": "core:window:allow-unminimize",
|
||||
"markdownDescription": "Enables the unminimize command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the activity_name command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:deny-activity-name",
|
||||
"markdownDescription": "Denies the activity_name command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the available_monitors command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -2126,6 +2168,12 @@
|
||||
"const": "core:window:deny-scale-factor",
|
||||
"markdownDescription": "Denies the scale_factor command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the scene_identifier command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:deny-scene-identifier",
|
||||
"markdownDescription": "Denies the scene_identifier command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_always_on_bottom command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
|
||||
@@ -87,7 +87,7 @@ export const BondingContext = createContext<TBondingContext>({
|
||||
isVestingAccount: false,
|
||||
});
|
||||
|
||||
export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Element => {
|
||||
export const BondingContextProvider: FCWithChildren = ({ children }): React.JSX.Element => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ export const MockBondingContextProvider = ({
|
||||
}: {
|
||||
network?: Network;
|
||||
children?: React.ReactNode;
|
||||
}): JSX.Element => {
|
||||
}): React.JSX.Element => {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [feeLoading, setFeeLoading] = useState(false);
|
||||
const [fee, setFee] = useState<FeeDetails | undefined>();
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { Box, Button } from '@mui/material';
|
||||
import { Download } from '@mui/icons-material';
|
||||
import { NodeTestResultResponse, NodeTester, createNodeTesterClient } from '@nymproject/node-tester';
|
||||
import { format } from 'date-fns';
|
||||
import { AppContext, useBondingContext } from 'src/context';
|
||||
import { LoadingModal } from 'src/components/Modals/LoadingModal';
|
||||
import { Results } from 'src/components/TestNode/Results';
|
||||
import { ErrorModal } from 'src/components/Modals/ErrorModal';
|
||||
import { PrintResults } from 'src/components/TestNode/PrintResults';
|
||||
import { MAINNET_VALIDATOR_URL } from 'src/constants';
|
||||
import { TestStatus } from 'src/components/TestNode/types';
|
||||
import { isMixnode } from 'src/types';
|
||||
|
||||
export const NodeTestPage = () => {
|
||||
const [nodeTestClient, setNodeTestClient] = useState<NodeTester>();
|
||||
const [error, setError] = useState<string>();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [results, setResults] = useState<NodeTestResultResponse>();
|
||||
const [printResults, setPrintResults] = useState(false);
|
||||
const [testDate, setTestDate] = useState<string>();
|
||||
|
||||
const testStateRef = useRef<TestStatus>('Stopped');
|
||||
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
const { network } = useContext(AppContext);
|
||||
const { bondedNode } = useBondingContext();
|
||||
|
||||
const handleTestTimeout = () => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current);
|
||||
}
|
||||
timerRef.current = setTimeout(() => {
|
||||
if (testStateRef.current === 'Running') {
|
||||
setIsLoading(false);
|
||||
setError('Test has timed out, please try again');
|
||||
testStateRef.current = 'Stopped';
|
||||
}
|
||||
}, 15000);
|
||||
};
|
||||
|
||||
const handleTestNode = async () => {
|
||||
if (nodeTestClient && bondedNode && isMixnode(bondedNode)) {
|
||||
setResults(undefined);
|
||||
setTestDate(format(new Date(), 'dd/MM/yyyy HH:mm'));
|
||||
setIsLoading(true);
|
||||
setError(undefined);
|
||||
testStateRef.current = 'Running';
|
||||
handleTestTimeout();
|
||||
try {
|
||||
const result = await nodeTestClient.tester.startTest(bondedNode.identityKey);
|
||||
setResults(result);
|
||||
testStateRef.current = 'Complete';
|
||||
} catch (e) {
|
||||
setError('Node test failed, please try again');
|
||||
testStateRef.current = 'Stopped';
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(e);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loadNodeTestClient = useCallback(async () => {
|
||||
try {
|
||||
const nodeTesterId = new Date().toISOString(); // make a new tester id for each session
|
||||
const validator = network === 'MAINNET' ? MAINNET_VALIDATOR_URL : 'https://rpc.nymtech.net/api/';
|
||||
const client = await createNodeTesterClient();
|
||||
await client.tester.init(validator, nodeTesterId);
|
||||
setNodeTestClient(client);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(e);
|
||||
setError('Failed to load node tester client, please try again');
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadNodeTestClient();
|
||||
|
||||
return () => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current);
|
||||
}
|
||||
if (nodeTestClient) {
|
||||
nodeTestClient.tester.disconnectFromGateway();
|
||||
nodeTestClient.terminate();
|
||||
}
|
||||
};
|
||||
}, [loadNodeTestClient]);
|
||||
|
||||
return (
|
||||
<Box p={4}>
|
||||
{isLoading && <LoadingModal text="Testing mixnode, please wait.." />}
|
||||
{error && <ErrorModal open title="Node test failed" message={error} onClose={() => setError(undefined)} />}
|
||||
{printResults && results && bondedNode && isMixnode(bondedNode) && (
|
||||
<PrintResults
|
||||
mixnodeId={bondedNode?.identityKey || '-'}
|
||||
mixnodeName={bondedNode?.name || '-'}
|
||||
packetsSent={results.sentPackets}
|
||||
packetsReceived={results.receivedPackets}
|
||||
score={results.score}
|
||||
date={testDate}
|
||||
OnPrintRequestComplete={() => setPrintResults(false)}
|
||||
/>
|
||||
)}
|
||||
<Results
|
||||
packetsSent={results?.sentPackets}
|
||||
packetsReceived={results?.receivedPackets}
|
||||
score={results?.score}
|
||||
status={testStateRef.current}
|
||||
date={testDate}
|
||||
onStartTest={handleTestNode}
|
||||
/>
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<Button onClick={() => setPrintResults(true)} startIcon={<Download />} disabled={!results}>
|
||||
Save test results as PDF
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -41,7 +41,7 @@ const operatorCostHint = `This is your (operator) rewards including the PM and c
|
||||
const profitMarginHint =
|
||||
'PM is the percentage of the node rewards that you as the node operator take before rewards are distributed to the delegators.';
|
||||
|
||||
export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode }): JSX.Element => {
|
||||
export const ParametersSettings = ({ bondedNode }: { bondedNode: TBondedMixnode }): React.JSX.Element => {
|
||||
const [openConfirmationModal, setOpenConfirmationModal] = useState<boolean>(false);
|
||||
const [intervalTime, setIntervalTime] = useState<string>();
|
||||
const [pendingUpdates, setPendingUpdates] = useState<NodeCostParams>();
|
||||
|
||||
+28
-19
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"name": "@nymproject/nymsphere",
|
||||
"version": "1.0.1",
|
||||
"packageManager": "pnpm@11.1.2",
|
||||
"license": "Apache 2.0",
|
||||
"scripts": {
|
||||
"audit:fix": "npm_config_yes=true npx yarn-audit-fix -- --dry-run",
|
||||
"audit:fix": "pnpm audit --fix",
|
||||
"build": "run-s build:types build:packages",
|
||||
"build:ci": "run-s build:types build:packages build:wasm build:ci:sdk",
|
||||
"build:ci:sdk": "lerna run --scope '{@nymproject/sdk,@nymproject/node-tester,@nymproject/sdk-react,@nymproject/mix-fetch,@nymproject/nodejs-client,@nymproject/mix-fetch-node}' build --stream",
|
||||
"build:ci:storybook": "yarn build && yarn dev:on && run-p build:playground && yarn build:ci:storybook:collect-artifacts ",
|
||||
"build:ci:sdk": "lerna run --scope '{@nymproject/sdk,@nymproject/sdk-react,@nymproject/mix-fetch,@nymproject/nodejs-client,@nymproject/mix-fetch-node}' build --stream",
|
||||
"build:ci:storybook": "pnpm build && pnpm dev:on && run-p build:playground && pnpm build:ci:storybook:collect-artifacts ",
|
||||
"build:ci:storybook:collect-artifacts": "mkdir -p ts-packages/dist && mv sdk/typescript/packages/react-components/storybook-static ts-packages/dist/storybook ",
|
||||
"build:packages": "run-s build:packages:theme build:packages:react",
|
||||
"build:packages:react": "lerna run --scope @nymproject/react build",
|
||||
@@ -20,11 +21,12 @@
|
||||
"dev:on": "node sdk/typescript/scripts/dev-mode-add.mjs",
|
||||
"docs:prod:build": "run-s docs:prod:build:ws",
|
||||
"docs:prod:build:ws": "lerna run docs:prod:build --stream",
|
||||
"lint": "lerna run lint --stream",
|
||||
"lint": "echo 'linting disabled in preparation of switch to biome'",
|
||||
"eslint:lint": "lerna run lint --stream",
|
||||
"lint:fix": "lerna run lint:fix --stream",
|
||||
"nuke": "npx rimraf **/node_modules node_modules",
|
||||
"postbuild:ci": "yarn dev:off",
|
||||
"prebuild:ci": "yarn dev:on && yarn",
|
||||
"nuke": "npx rimraf **/node_modules node_modules dist pkg",
|
||||
"postbuild:ci": "pnpm dev:off",
|
||||
"prebuild:ci": "pnpm dev:on && pnpm install --frozen-lockfile false",
|
||||
"scrub": "npx rimraf **/dist dist",
|
||||
"sdk:build": "./sdk/typescript/scripts/build-prod-sdk.sh",
|
||||
"sdk:publish": "./sdk/typescript/scripts/publish.sh",
|
||||
@@ -46,7 +48,24 @@
|
||||
"tslog": "3.3.3"
|
||||
},
|
||||
"private": true,
|
||||
"resolutions": {
|
||||
"packageExtensions": {
|
||||
"@cosmos-kit/core@*": {
|
||||
"dependencies": {
|
||||
"axios": "*"
|
||||
}
|
||||
},
|
||||
"@cosmos-kit/keplr-extension@*": {
|
||||
"dependencies": {
|
||||
"long": "*"
|
||||
}
|
||||
},
|
||||
"@nivo/line@*": {
|
||||
"dependencies": {
|
||||
"lodash": "*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"overrides": {
|
||||
"eslint": "8.57.1",
|
||||
"@cosmjs/amino": "^0.32.4",
|
||||
"@cosmjs/cosmwasm-stargate": "^0.32.4",
|
||||
@@ -62,15 +81,5 @@
|
||||
"@typescript-eslint/typescript-estree": "5.62.0",
|
||||
"@typescript-eslint/utils": "5.62.0",
|
||||
"cosmjs-types": "^0.9.0"
|
||||
},
|
||||
"workspaces": [
|
||||
"ts-packages/*",
|
||||
"nym-wallet",
|
||||
"explorer-nextjs",
|
||||
"explorer-v2",
|
||||
"types",
|
||||
"sdk/typescript/packages/mix-fetch/internal-dev",
|
||||
"sdk/typescript/packages/react-components",
|
||||
"sdk/typescript/packages/mui-theme"
|
||||
]
|
||||
}
|
||||
}
|
||||
Generated
+31489
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,244 @@
|
||||
packages:
|
||||
- 'ts-packages/*'
|
||||
- 'nym-wallet'
|
||||
- 'explorer-v2'
|
||||
- 'types'
|
||||
- 'sdk/typescript/packages/mix-fetch/internal-dev'
|
||||
- 'sdk/typescript/packages/react-components'
|
||||
- 'sdk/typescript/packages/mui-theme'
|
||||
|
||||
allowBuilds:
|
||||
'@biomejs/biome': true
|
||||
'@parcel/watcher': true
|
||||
'@swc/core': true
|
||||
core-js: true
|
||||
core-js-pure: true
|
||||
es5-ext: true
|
||||
esbuild: true
|
||||
fsevents: true
|
||||
lefthook: true
|
||||
lmdb: true
|
||||
msgpackr-extract: true
|
||||
nx: true
|
||||
protobufjs: true
|
||||
sharp: true
|
||||
tiny-secp256k1: true
|
||||
unrs-resolver: true
|
||||
|
||||
catalog:
|
||||
# babel
|
||||
"@babel/core": "^7.22.10"
|
||||
"@babel/helper-simple-access": "^7.25.9"
|
||||
"@babel/runtime": "^7.29.2"
|
||||
"@babel/plugin-transform-async-to-generator": "^7.22.5"
|
||||
"@babel/preset-env": "^7.22.10"
|
||||
"@babel/preset-react": "^7.14.5"
|
||||
"@babel/preset-typescript": "^7.22.5"
|
||||
|
||||
# emotion
|
||||
"@emotion/cache": "^11.14.0"
|
||||
"@emotion/hash": "^0.9.2"
|
||||
"@emotion/is-prop-valid": "^1.4.0"
|
||||
"@emotion/memoize": "^0.9.0"
|
||||
"@emotion/react": "^11.13.5"
|
||||
"@emotion/serialize": "^1.3.3"
|
||||
"@emotion/sheet": "^1.4.0"
|
||||
"@emotion/styled": "^11.13.5"
|
||||
"@emotion/unitless": "^0.10.0"
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0"
|
||||
"@emotion/utils": "^1.4.2"
|
||||
"@emotion/weak-memoize": "^0.4.0"
|
||||
|
||||
# mui
|
||||
"@mui/base": "5.0.0-beta.40"
|
||||
"@mui/icons-material": "^5.2.0"
|
||||
"@mui/lab": "5.0.0-alpha.170"
|
||||
"@mui/material": "^5.2.2"
|
||||
"@mui/private-theming": "^5.17.1"
|
||||
"@mui/styles": "^5.18.0"
|
||||
"@mui/system": "^5.18.0"
|
||||
"@mui/utils": "^5.7.0"
|
||||
"@mui/x-tree-view": "^7.11.1"
|
||||
|
||||
# tanstack
|
||||
"@tanstack/react-query": "^5.64.2"
|
||||
"@tanstack/query-core": "^5.64.2"
|
||||
|
||||
# tauri
|
||||
"@tauri-apps/api": "^2.10.1"
|
||||
"@tauri-apps/cli": "^2.10.1"
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.2"
|
||||
"@tauri-apps/plugin-opener": "^2.5.3"
|
||||
"@tauri-apps/plugin-process": "^2.3.1"
|
||||
"@tauri-apps/plugin-updater": "^2.10.1"
|
||||
"@tauri-apps/tauri-forage": "^1.0.0-beta.2"
|
||||
|
||||
# types
|
||||
"@types/big.js": "^6.1.6"
|
||||
"@types/bs58": "^4.0.1"
|
||||
"@types/flat": "^5.0.2"
|
||||
"@types/jest": "^29.5.14"
|
||||
"@types/lodash": "^4.17.21"
|
||||
"@types/minimatch": "5.1.2"
|
||||
"@types/qrcode.react": "^1.0.2"
|
||||
"@types/react": "^19.2.14"
|
||||
"@types/react-dom": "^19.2.3"
|
||||
"@types/semver": "^7.3.8"
|
||||
"@types/uuid": "^8.3.4"
|
||||
"@types/ws": "^8.18.1"
|
||||
"@types/zxcvbn": "^4.4.1"
|
||||
|
||||
# react ecosystem
|
||||
"@popperjs/core": "^2.11.8"
|
||||
"hoist-non-react-statics": "^3.3.2"
|
||||
"prop-types": "^15.8.1"
|
||||
"react": "^19.2.6"
|
||||
"react-dom": "^19.2.6"
|
||||
"react-error-boundary": "^3.1.3"
|
||||
"react-hook-form": "^7.14.2"
|
||||
"react-is": "^19.2.6"
|
||||
"react-refresh": "^0.10.0"
|
||||
"react-refresh-typescript": "^2.0.2"
|
||||
"react-smooth": "^4.0.4"
|
||||
"react-transition-group": "^4.4.5"
|
||||
"scheduler": "^0.27.0"
|
||||
"stylis": "^4.2.0"
|
||||
|
||||
# react router
|
||||
"@remix-run/router": "^1.23.2"
|
||||
"react-router": "^6.30.3"
|
||||
"react-router-dom": "6"
|
||||
"tiny-invariant": "^1.3.3"
|
||||
|
||||
# storybook
|
||||
"@storybook/addon-docs": "^6.5.8"
|
||||
"@storybook/addon-actions": "^6.5.8"
|
||||
"@storybook/addon-essentials": "^6.5.8"
|
||||
"@storybook/addon-interactions": "^6.5.8"
|
||||
"@storybook/addon-links": "^6.5.8"
|
||||
"@storybook/builder-webpack5": "^6.5.8"
|
||||
"@storybook/manager-webpack5": "^6.5.8"
|
||||
"@storybook/react": "^6.5.15"
|
||||
"@storybook/testing-library": "^0.0.9"
|
||||
|
||||
# testing
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4"
|
||||
"@testing-library/dom": "^10.4.1"
|
||||
"@testing-library/jest-dom": "^6.9.1"
|
||||
"@testing-library/react": "^16.3.2"
|
||||
|
||||
# crypto / cosmjs
|
||||
"@cosmjs/cosmwasm-stargate": "^0.32.4"
|
||||
"@cosmjs/math": "^0.32.4"
|
||||
"@cosmjs/stargate": "^0.32.4"
|
||||
"axios": "^1.16.0"
|
||||
"long": "^4.0.0"
|
||||
"base-x": "^3.0.11"
|
||||
"base64-js": "^1.5.1"
|
||||
"bech32": "^1.1.4"
|
||||
"bn.js": "^5.2.3"
|
||||
"bs58": "^4.0.1"
|
||||
"buffer": "^6.0.3"
|
||||
"ieee754": "^1.2.1"
|
||||
"safe-buffer": "^5.2.1"
|
||||
"tweetnacl": "^1.0.3"
|
||||
"tweetnacl-util": "^0.15.1"
|
||||
|
||||
# d3 / recharts
|
||||
"d3-array": "^3.2.4"
|
||||
"d3-color": "^3.1.0"
|
||||
"d3-format": "^1.4.5"
|
||||
"d3-interpolate": "^3.0.1"
|
||||
"d3-path": "^3.1.0"
|
||||
"d3-scale": "^4.0.2"
|
||||
"d3-shape": "^3.2.0"
|
||||
"d3-time": "^3.1.0"
|
||||
"d3-time-format": "^3.0.0"
|
||||
"decimal.js-light": "^2.5.1"
|
||||
"eventemitter3": "^4.0.7"
|
||||
"fast-equals": "^5.4.0"
|
||||
"internmap": "^2.0.3"
|
||||
"ramda": "^0.28.0"
|
||||
"recharts": "^2.1.13"
|
||||
"recharts-scale": "^0.4.5"
|
||||
"victory-vendor": "^36.9.2"
|
||||
|
||||
# typescript-eslint (v5 line — nym-wallet pins v8 separately)
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0"
|
||||
"@typescript-eslint/parser": "^5.13.0"
|
||||
|
||||
# webpack helpers
|
||||
"@svgr/webpack": "^6.1.1"
|
||||
"babel-loader": "^8.3.0"
|
||||
"clean-webpack-plugin": "^4.0.0"
|
||||
"css-loader": "^6.8.1"
|
||||
"css-minimizer-webpack-plugin": "^3.0.2"
|
||||
"dotenv-webpack": "^7.0.3"
|
||||
"favicons": "^7.0.2"
|
||||
"favicons-webpack-plugin": "^5.0.2"
|
||||
"file-loader": "^6.2.0"
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.1"
|
||||
"html-webpack-plugin": "^5.5.3"
|
||||
"mini-css-extract-plugin": "^2.7.6"
|
||||
"style-loader": "^3.3.3"
|
||||
"thread-loader": "^3.0.4"
|
||||
"ts-loader": "^9.4.4"
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2"
|
||||
"url-loader": "^4.1.1"
|
||||
"webpack": "^5.88.2"
|
||||
"webpack-cli": "^4.8.0"
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
"webpack-favicons": "^1.3.8"
|
||||
"webpack-merge": "^5.9.0"
|
||||
|
||||
# eslint ecosystem
|
||||
"eslint": "^9.26.0"
|
||||
"eslint-config-airbnb": "^19.0.4"
|
||||
"eslint-config-airbnb-typescript": "^16.1.0"
|
||||
"eslint-config-prettier": "^8.5.0"
|
||||
"eslint-import-resolver-root-import": "^1.0.4"
|
||||
"eslint-plugin-import": "^2.25.4"
|
||||
"eslint-plugin-jest": "^26.1.1"
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1"
|
||||
"eslint-plugin-prettier": "^4.0.0"
|
||||
"eslint-plugin-react": "^7.29.2"
|
||||
"eslint-plugin-react-hooks": "^4.3.0"
|
||||
"eslint-plugin-storybook": "^0.5.12"
|
||||
|
||||
# test
|
||||
"jest": "^27.1.0"
|
||||
"ts-jest": "^27.0.5"
|
||||
|
||||
# misc shared
|
||||
"@hookform/resolvers": "^2.8.0"
|
||||
"big.js": "^6.2.1"
|
||||
"clipboard-copy": "^3.2.0"
|
||||
"clsx": "^1.1.1"
|
||||
"colornames": "^1.1.1"
|
||||
"date-fns": "^2.28.0"
|
||||
"dom-helpers": "^5.2.1"
|
||||
"flat": "^5.0.2"
|
||||
"glob": "^10.5.0"
|
||||
"hex-rgb": "^4.3.0"
|
||||
"joi": "^17.11.0"
|
||||
"localforage": "^1.10.0"
|
||||
"lodash": "^4.17.21"
|
||||
"lodash.padend": "^4.6.1"
|
||||
"lodash.trimstart": "^4.5.1"
|
||||
"lodash.words": "^4.2.0"
|
||||
"nanoclone": "^0.2.1"
|
||||
"notistack": "^2.0.3"
|
||||
"npm-run-all": "^4.1.5"
|
||||
"prettier": "^2.8.7"
|
||||
"property-expr": "^2.0.6"
|
||||
"qr.js": "0.0.0"
|
||||
"rgb-hex": "^3.0.0"
|
||||
"rimraf": "^3.0.2"
|
||||
"semver": "^6.3.0"
|
||||
"string-to-color": "^2.2.2"
|
||||
"toposort": "^2.0.2"
|
||||
"tslib": "^2.8.1"
|
||||
"use-clipboard-copy": "^0.2.0"
|
||||
"uuid": "^8.3.2"
|
||||
"yup": "^0.32.9"
|
||||
"zxcvbn": "^4.4.2"
|
||||
@@ -7,7 +7,7 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/contract-clients": "file:.."
|
||||
"@nymproject/contract-clients": "workspace:*"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/contract-clients/",
|
||||
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
|
||||
"docs:serve": "reload -b -d ./docs -p 3000",
|
||||
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\""
|
||||
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"pnpm docs:generate\""
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cosmwasm/ts-codegen": "^1.13.3",
|
||||
"nodemon": "3.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"npm-run-all": "catalog:",
|
||||
"reload": "^3.2.1",
|
||||
"typedoc": "^0.24.8",
|
||||
"typescript": "^4.6.2"
|
||||
|
||||
@@ -7,7 +7,7 @@ set -o pipefail
|
||||
rm -rf ../../../../dist/ts/docs/tsdoc/nymproject/contract-clients || true
|
||||
|
||||
# run the build
|
||||
yarn docs:generate:prod
|
||||
pnpm docs:generate:prod
|
||||
|
||||
# move the output outside of the yarn/npm workspaces
|
||||
mkdir -p ../../../../dist/ts/docs/tsdoc/nymproject
|
||||
|
||||
@@ -20,22 +20,22 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.13",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"@typescript-eslint/eslint-plugin": "catalog:",
|
||||
"@typescript-eslint/parser": "catalog:",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-root-import": "^1.0.4",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jest": "^26.1.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.1.0",
|
||||
"mini-css-extract-plugin": "^2.2.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.7",
|
||||
"ts-jest": "^27.0.5",
|
||||
"eslint-config-airbnb": "catalog:",
|
||||
"eslint-config-airbnb-typescript": "catalog:",
|
||||
"eslint-config-prettier": "catalog:",
|
||||
"eslint-import-resolver-root-import": "catalog:",
|
||||
"eslint-plugin-import": "catalog:",
|
||||
"eslint-plugin-jest": "catalog:",
|
||||
"eslint-plugin-jsx-a11y": "catalog:",
|
||||
"eslint-plugin-prettier": "catalog:",
|
||||
"jest": "catalog:",
|
||||
"mini-css-extract-plugin": "catalog:",
|
||||
"npm-run-all": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"ts-jest": "catalog:",
|
||||
"typescript": "^4.6.2"
|
||||
},
|
||||
"private": false,
|
||||
|
||||
@@ -19,47 +19,47 @@
|
||||
"@nymproject/sdk": ">=1.4.1-rc1 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.14.5",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@babel/core": "catalog:",
|
||||
"@babel/plugin-transform-async-to-generator": "catalog:",
|
||||
"@babel/preset-env": "catalog:",
|
||||
"@babel/preset-typescript": "catalog:",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.13",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"babel-loader": "^8.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "catalog:",
|
||||
"@typescript-eslint/parser": "catalog:",
|
||||
"babel-loader": "catalog:",
|
||||
"babel-plugin-root-import": "^5.1.0",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"clean-webpack-plugin": "catalog:",
|
||||
"css-loader": "catalog:",
|
||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
"dotenv-webpack": "catalog:",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-root-import": "^1.0.4",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jest": "^26.1.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.1",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"jest": "^27.1.0",
|
||||
"mini-css-extract-plugin": "^2.2.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.7",
|
||||
"style-loader": "^3.3.1",
|
||||
"eslint-config-airbnb": "catalog:",
|
||||
"eslint-config-airbnb-typescript": "catalog:",
|
||||
"eslint-config-prettier": "catalog:",
|
||||
"eslint-import-resolver-root-import": "catalog:",
|
||||
"eslint-plugin-import": "catalog:",
|
||||
"eslint-plugin-jest": "catalog:",
|
||||
"eslint-plugin-jsx-a11y": "catalog:",
|
||||
"eslint-plugin-prettier": "catalog:",
|
||||
"file-loader": "catalog:",
|
||||
"fork-ts-checker-webpack-plugin": "catalog:",
|
||||
"html-webpack-plugin": "catalog:",
|
||||
"jest": "catalog:",
|
||||
"mini-css-extract-plugin": "catalog:",
|
||||
"npm-run-all": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"style-loader": "catalog:",
|
||||
"thread-loader": "^3.0.4",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-loader": "^9.4.2",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"ts-jest": "catalog:",
|
||||
"ts-loader": "catalog:",
|
||||
"tsconfig-paths-webpack-plugin": "catalog:",
|
||||
"typescript": "^4.6.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.5.0",
|
||||
"webpack-merge": "^5.8.0"
|
||||
"url-loader": "catalog:",
|
||||
"webpack": "catalog:",
|
||||
"webpack-cli": "catalog:",
|
||||
"webpack-dev-server": "catalog:",
|
||||
"webpack-merge": "catalog:"
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
|
||||
@@ -16,78 +16,78 @@
|
||||
"tsc:watch": "tsc --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mui/icons-material": "^5.5.0",
|
||||
"@mui/icons-material": "catalog:",
|
||||
"@mui/lab": "^5.0.0-alpha.72",
|
||||
"@mui/material": "^5.0.1",
|
||||
"@mui/styles": "^5.0.1",
|
||||
"@nymproject/sdk": ">=1.4.1-rc1 || ^1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-mui-dropzone": "^4.0.6",
|
||||
"use-clipboard-copy": "^0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.14.5",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@babel/core": "catalog:",
|
||||
"@babel/plugin-transform-async-to-generator": "catalog:",
|
||||
"@babel/preset-env": "catalog:",
|
||||
"@babel/preset-react": "catalog:",
|
||||
"@babel/preset-typescript": "catalog:",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
|
||||
"@svgr/webpack": "^6.1.1",
|
||||
"@svgr/webpack": "catalog:",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.13",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"babel-loader": "^8.3.0",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@typescript-eslint/eslint-plugin": "catalog:",
|
||||
"@typescript-eslint/parser": "catalog:",
|
||||
"babel-loader": "catalog:",
|
||||
"babel-plugin-root-import": "^5.1.0",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"clean-webpack-plugin": "catalog:",
|
||||
"css-loader": "catalog:",
|
||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
"dotenv-webpack": "catalog:",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-root-import": "^1.0.4",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jest": "^26.1.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.29.2",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"favicons": "^7.0.2",
|
||||
"favicons-webpack-plugin": "^5.0.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.1",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"jest": "^27.1.0",
|
||||
"mini-css-extract-plugin": "^2.2.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.7",
|
||||
"eslint-config-airbnb": "catalog:",
|
||||
"eslint-config-airbnb-typescript": "catalog:",
|
||||
"eslint-config-prettier": "catalog:",
|
||||
"eslint-import-resolver-root-import": "catalog:",
|
||||
"eslint-plugin-import": "catalog:",
|
||||
"eslint-plugin-jest": "catalog:",
|
||||
"eslint-plugin-jsx-a11y": "catalog:",
|
||||
"eslint-plugin-prettier": "catalog:",
|
||||
"eslint-plugin-react": "catalog:",
|
||||
"eslint-plugin-react-hooks": "catalog:",
|
||||
"favicons": "catalog:",
|
||||
"favicons-webpack-plugin": "catalog:",
|
||||
"file-loader": "catalog:",
|
||||
"fork-ts-checker-webpack-plugin": "catalog:",
|
||||
"html-webpack-plugin": "catalog:",
|
||||
"jest": "catalog:",
|
||||
"mini-css-extract-plugin": "catalog:",
|
||||
"npm-run-all": "catalog:",
|
||||
"prettier": "catalog:",
|
||||
"react-refresh-typescript": "^2.0.3",
|
||||
"style-loader": "^3.3.1",
|
||||
"style-loader": "catalog:",
|
||||
"thread-loader": "^3.0.4",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-loader": "^9.4.2",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"ts-jest": "catalog:",
|
||||
"ts-loader": "catalog:",
|
||||
"tsconfig-paths-webpack-plugin": "catalog:",
|
||||
"typescript": "^4.6.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.5.0",
|
||||
"webpack-favicons": "^1.3.8",
|
||||
"webpack-merge": "^5.8.0"
|
||||
"url-loader": "catalog:",
|
||||
"webpack": "catalog:",
|
||||
"webpack-cli": "catalog:",
|
||||
"webpack-dev-server": "catalog:",
|
||||
"webpack-favicons": "catalog:",
|
||||
"webpack-merge": "catalog:"
|
||||
},
|
||||
"private": false,
|
||||
"overrides": {
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"react": "^19.2.6",
|
||||
"react-dom": "^19.2.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# Nym Chrome Extension Example
|
||||
|
||||
This is an example of how Nym can be used within the context of a Chrome extension.
|
||||
|
||||
## Running the example
|
||||
|
||||
1. Copy a build of the Nym TypeScript SDK (ESM version) into `./sdk`.
|
||||
2. Navigate to `chrome://extensions` in Google Chrome.
|
||||
3. Enable "Developer mode" (top right of the page).
|
||||
4. Click on "Load unpacked" (top left of the page).
|
||||
5. Load this extension folder.
|
||||
|
||||
## How does it work?
|
||||
|
||||
The Nym Mixnet Client runs a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) that wraps
|
||||
a WASM library that builds and encrypts Sphinx packets in the browser to send over the Nym mixnet:
|
||||
|
||||

|
||||
|
||||
The WASM code encrypts each layer of the Sphinx packet in the browser, before sending the Sphinx packet over a websocket to the ingress gateway:
|
||||
|
||||

|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Nym Chrome Extension Example",
|
||||
"description": "An example demonstrating how to integrate the Nym TypeScript SDK in the context of a Google Chrome browser extension.",
|
||||
"version": "1.0",
|
||||
"manifest_version": 3,
|
||||
"icons": {
|
||||
"48": "icon.png"
|
||||
},
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
|
||||
},
|
||||
"action": {
|
||||
"default_title": "Nym Chrome Extension Example",
|
||||
"default_icon": "icon.png",
|
||||
"default_popup": "popup.html"
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "@nymproject/sdk-example-chrome-extension",
|
||||
"version": "1.0.5",
|
||||
"description": "This is an example of how Nym can be used within the context of a Chrome extension.",
|
||||
"license": "ISC",
|
||||
"author": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "webpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": ">=1.4.1-rc1 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"webpack": "^5.88.1",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
body {
|
||||
width: 800px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
#editdialog input {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<script type="module" src="main.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p><label>Sender:</label><input disabled="true" size="85" id="sender" value="" /></p>
|
||||
<p><label>Recipient:</label><input size="85" id="recipient" value="" /></p>
|
||||
<p><label>Message:</label><input id="message" value="Hello mixnet!" /></p>
|
||||
<p><button id="send-button">Send</button></p>
|
||||
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
|
||||
<p>
|
||||
<span style="color: blue">Sent</span> messages show in blue, <span style="color: green">received</span> messages
|
||||
show in green.
|
||||
</p>
|
||||
<hr />
|
||||
<p></p>
|
||||
<div id="output"></div>
|
||||
<p></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,66 +0,0 @@
|
||||
// dom-utils.js
|
||||
// Contains utility functionality to help manipulate the DOM elements necessary to demonstrate the Nym example.
|
||||
|
||||
/**
|
||||
* Create a Sphinx packet and send it to the mixnet through the gateway node.
|
||||
*
|
||||
* Message and recipient are taken from the values in the user interface.
|
||||
*
|
||||
* @param {Client} nymClient the nym client to use for message sending
|
||||
*/
|
||||
async function sendMessageTo(nym) {
|
||||
const message = document.getElementById('message').value;
|
||||
const recipient = document.getElementById('recipient').value;
|
||||
await nym.client.send({
|
||||
payload: {
|
||||
message,
|
||||
mimeType: 'text/plain'
|
||||
},
|
||||
recipient
|
||||
});
|
||||
displaySend(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display messages that have been sent up the websocket. Colours them blue.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
function displaySend(message) {
|
||||
const timestamp = new Date().toISOString().substr(11, 12);
|
||||
const sendDiv = document.createElement('div');
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.setAttribute('style', 'color: blue');
|
||||
const paragraphContent = document.createTextNode(`${timestamp} sent >>> ${message}`);
|
||||
paragraph.appendChild(paragraphContent);
|
||||
sendDiv.appendChild(paragraph);
|
||||
document.getElementById('output')?.appendChild(sendDiv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display received text messages in the browser. Colour them green.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
function displayReceived(message) {
|
||||
const content = message;
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const receivedDiv = document.createElement('div');
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.setAttribute('style', 'color: green');
|
||||
const paragraphContent = document.createTextNode(`${timestamp} received >>> ${content}`);
|
||||
paragraph.appendChild(paragraphContent);
|
||||
receivedDiv.appendChild(paragraph);
|
||||
document.getElementById('output')?.appendChild(receivedDiv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the nymClient's sender address in the user interface
|
||||
*
|
||||
* @param {Client} nymClient
|
||||
*/
|
||||
function displaySenderAddress(address) {
|
||||
document.getElementById('sender').value = address;
|
||||
}
|
||||
|
||||
export { sendMessageTo, displaySend, displayReceived , displaySenderAddress }
|
||||
@@ -1,53 +0,0 @@
|
||||
// main.js
|
||||
// Simple example of how to load Nym's TypeScript SDK and bind it to a DOM.
|
||||
// Look at dom-utils.js for the DOM utility functionality referenced here.
|
||||
|
||||
// Import the Nym mixnet ESM module.
|
||||
import { createNymMixnetClient } from '@nymproject/sdk';
|
||||
|
||||
// Import the DOM utility functionality.
|
||||
import { displaySenderAddress, displayReceived, sendMessageTo } from './dom-utils.js';
|
||||
|
||||
async function main() {
|
||||
// Initialize the Nym mixnet client.
|
||||
let nymClient = await createNymMixnetClient();
|
||||
if (!nymClient) {
|
||||
console.error('Oh no! Could not create client');
|
||||
return;
|
||||
}
|
||||
|
||||
const nymApiUrl = 'https://validator.nymtech.net/api';
|
||||
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
|
||||
|
||||
// subscribe to connect event, so that we can show the client's address
|
||||
nymClient.events.subscribeToConnected((e) => {
|
||||
if (e.args.address) {
|
||||
displaySenderAddress(e.args.address);
|
||||
}
|
||||
});
|
||||
|
||||
// subscribe to message received events and show any string messages received
|
||||
nymClient.events.subscribeToTextMessageReceivedEvent((e) => {
|
||||
displayReceived(e.args.payload);
|
||||
});
|
||||
|
||||
const sendButton = document.querySelector('#send-button');
|
||||
if (sendButton) {
|
||||
sendButton.onclick = function () {
|
||||
if (nymClient) {
|
||||
sendMessageTo(nymClient);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
nymClient.events.subscribeToRawMessageReceivedEvent((e) => console.log('Received: ', e.args.payload));
|
||||
await nymClient.client.start({
|
||||
clientId: 'My awesome client',
|
||||
nymApiUrl,
|
||||
preferredGatewayIdentityKey,
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
main();
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Webpack configuration for the Chrome extension example
|
||||
|
||||
const path = require('path');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
entry: {
|
||||
main: './src/main.js',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
'manifest.json',
|
||||
'popup.html',
|
||||
{ from: path.resolve(__dirname, '../../../../assets/favicon/favicon.png'), to: 'icon.png' },
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
sdk/index.js
|
||||
@@ -1,35 +0,0 @@
|
||||
# Nym Firefox Extension Example
|
||||
|
||||
This is an example of how Nym can be used within the context of a Mozilla Firefox extension.
|
||||
|
||||
## Running the example
|
||||
|
||||
First, build the Nym SDK:
|
||||
|
||||
From the SDK directory `sdk/typescript/packages/sdk` run:
|
||||
|
||||
```js
|
||||
npm run build:local
|
||||
```
|
||||
|
||||
Then, from the example directory `sdk/typescript/examples/firefox-extension` run:
|
||||
|
||||
```js
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Workers
|
||||
|
||||
Firefox browser extensions cannot run inline web workers. In order to overcome this limitation, the Nym Firefox Extension Example imports workers from the SDK and uses Webpack's `worker-loader` to allow the worker's to be bundled inline into the extension. In order for webpack to include the workers in the build, they are imported as modules in the `src/index.js` file:
|
||||
|
||||
## How does it work?
|
||||
|
||||
The Nym Mixnet Client runs a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) that wraps
|
||||
a WASM library that builds and encrypts Sphinx packets in the browser to send over the Nym mixnet:
|
||||
|
||||

|
||||
|
||||
The WASM code encrypts each layer of the Sphinx packet in the browser, before sending the Sphinx packet over a websocket to the ingress gateway:
|
||||
|
||||

|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Nym Firefox Extension Example",
|
||||
"version": "1.0",
|
||||
"description": "An example demonstrating how to integrate the Nym TypeScript SDK in the context of a Mozilla Firefox browser extension.",
|
||||
"icons": {
|
||||
"48": "icon.png"
|
||||
},
|
||||
"permissions": [],
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self' 'wasm-unsafe-eval';"
|
||||
},
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
"action": {
|
||||
"default_icon": {
|
||||
"32": "icon.png"
|
||||
},
|
||||
"default_title": "Nym Firefox Extension Example",
|
||||
"default_popup": "popup.html"
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "@nymproject/sdk-example-firefox-extension",
|
||||
"version": "1.0.5",
|
||||
"description": "This is an example of how Nym can be used within the context of a Firefox extension.",
|
||||
"license": "ISC",
|
||||
"author": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "yarn webpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": ">=1.4.1-rc1 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"webpack": "^5.88.1",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"worker-loader": "^3.0.8"
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
body {
|
||||
width: 800px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
#editdialog input {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<script type="module" src="popup.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p><label>Sender:</label><input disabled="true" size="85" id="sender" value="" /></p>
|
||||
<p><label>Recipient:</label><input size="85" id="recipient" value="" /></p>
|
||||
<p><label>Message:</label><input id="message" value="Hello mixnet!" /></p>
|
||||
<p><button id="send-button">Send</button></p>
|
||||
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
|
||||
<p>
|
||||
<span style="color: blue">Sent</span> messages show in blue, <span style="color: green">received</span> messages
|
||||
show in green.
|
||||
</p>
|
||||
<hr />
|
||||
<p></p>
|
||||
<div id="output"></div>
|
||||
<p></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,107 +0,0 @@
|
||||
// main.js
|
||||
// Simple example of how to load Nym's TypeScript SDK and bind it to a DOM.
|
||||
// Look at dom-utils.js for the DOM utility functionality referenced here.
|
||||
|
||||
// Import the Nym mixnet ESM module.
|
||||
// Import The web workers for the Nym mixnet ESM module.These are required for to run the Nym mixnet client.
|
||||
|
||||
import { createNymMixnetClient } from '../../../packages/sdk/dist/full-fat/index.js';
|
||||
import '../../../packages/sdk/dist/full-fat/web-worker-0.js';
|
||||
import '../../../packages/sdk/dist/full-fat/web-worker-1.js';
|
||||
|
||||
const backgroundState = {
|
||||
isReady: false,
|
||||
address: '',
|
||||
recipient: '',
|
||||
messageLog: [],
|
||||
};
|
||||
|
||||
async function initBackground() {
|
||||
// Initialize the Nym mixnet client.
|
||||
let nymClient = await createNymMixnetClient().catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
if (!nymClient) {
|
||||
console.error('Oh no! Could not create client');
|
||||
return;
|
||||
}
|
||||
const nymApiUrl = 'https://validator.nymtech.net/api';
|
||||
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
|
||||
|
||||
// subscribe to connect event, so that we can show the client's address
|
||||
nymClient.events.subscribeToConnected((e) => {
|
||||
if (e.args.address) {
|
||||
backgroundState.address = e.args.address;
|
||||
browser.runtime.sendMessage({
|
||||
type: 'displaySenderAddress',
|
||||
message: backgroundState.address,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// subscribe to message received events and show any string messages received
|
||||
nymClient.events.subscribeToTextMessageReceivedEvent((e) => {
|
||||
backgroundState.messageLog.push({
|
||||
type: 'received',
|
||||
message: e.args.payload,
|
||||
});
|
||||
browser.runtime.sendMessage({
|
||||
type: 'displayReceived',
|
||||
message: e.args.payload,
|
||||
});
|
||||
});
|
||||
|
||||
nymClient.events.subscribeToRawMessageReceivedEvent((e) => console.log('Received: ', e.args.payload));
|
||||
await nymClient.client.start({
|
||||
clientId: 'My awesome client',
|
||||
nymApiUrl,
|
||||
preferredGatewayIdentityKey,
|
||||
});
|
||||
browser.runtime.onMessage.addListener(async (data) => {
|
||||
switch (data.type) {
|
||||
case 'nymClientSendMessage':
|
||||
if (nymClient) {
|
||||
await nymClient.client.send({
|
||||
payload: {
|
||||
message: data.message,
|
||||
mimeType: 'text/plain',
|
||||
},
|
||||
recipient: data.recipient,
|
||||
});
|
||||
backgroundState.messageLog.push({
|
||||
type: 'sent',
|
||||
message: data.message,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
backgroundState.isReady = true;
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
browser.runtime.onMessage.addListener((data) => {
|
||||
switch (data.type) {
|
||||
case 'popupReady':
|
||||
if (backgroundState.isReady) {
|
||||
browser.runtime.sendMessage({
|
||||
type: 'displaySenderAddress',
|
||||
message: backgroundState.address,
|
||||
});
|
||||
browser.runtime.sendMessage({
|
||||
type: 'displayMessageLog',
|
||||
message: backgroundState.messageLog,
|
||||
});
|
||||
browser.runtime.sendMessage({
|
||||
type: 'updateRecipient',
|
||||
message: backgroundState.recipient,
|
||||
});
|
||||
} else {
|
||||
initBackground();
|
||||
}
|
||||
break;
|
||||
case 'updateRecipient':
|
||||
backgroundState.recipient = data.message;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,114 +0,0 @@
|
||||
// main.js
|
||||
// Simple example of how to load Nym's TypeScript SDK and bind it to a DOM.
|
||||
// Look at dom-utils.js for the DOM utility functionality referenced here.
|
||||
|
||||
// Import the Nym mixnet ESM module.
|
||||
// Import The web workers for the Nym mixnet ESM module.These are required for to run the Nym mixnet client.
|
||||
|
||||
import { createNymMixnetClient } from '../../../packages/sdk/dist/full-fat/index.js';
|
||||
import '../../../packages/sdk/dist/full-fat/web-worker-0.js';
|
||||
import '../../../packages/sdk/dist/full-fat/web-worker-1.js';
|
||||
|
||||
export type BackgroundState = {
|
||||
isReady: boolean;
|
||||
address: string;
|
||||
recipient: string;
|
||||
messageLog: { type: string; message: string }[];
|
||||
};
|
||||
|
||||
const backgroundState: BackgroundState = {
|
||||
isReady: false,
|
||||
address: '',
|
||||
recipient: '',
|
||||
messageLog: [],
|
||||
};
|
||||
|
||||
async function initBackground() {
|
||||
// Initialize the Nym mixnet client.
|
||||
let nymClient = await createNymMixnetClient().catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
if (!nymClient) {
|
||||
console.error('Oh no! Could not create client');
|
||||
return;
|
||||
}
|
||||
const nymApiUrl = 'https://validator.nymtech.net/api';
|
||||
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
|
||||
|
||||
// subscribe to connect event, so that we can show the client's address
|
||||
nymClient.events.subscribeToConnected((e) => {
|
||||
if (e.args.address) {
|
||||
backgroundState.address = e.args.address;
|
||||
browser.runtime.sendMessage({
|
||||
type: 'displaySenderAddress',
|
||||
message: backgroundState.address,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// subscribe to message received events and show any string messages received
|
||||
nymClient.events.subscribeToTextMessageReceivedEvent((e) => {
|
||||
backgroundState.messageLog.push({
|
||||
type: 'received',
|
||||
message: e.args.payload,
|
||||
});
|
||||
browser.runtime.sendMessage({
|
||||
type: 'displayReceived',
|
||||
message: e.args.payload,
|
||||
});
|
||||
});
|
||||
|
||||
nymClient.events.subscribeToRawMessageReceivedEvent((e) => console.log('Received: ', e.args.payload));
|
||||
await nymClient.client.start({
|
||||
clientId: 'My awesome client',
|
||||
nymApiUrl,
|
||||
preferredGatewayIdentityKey,
|
||||
});
|
||||
browser.runtime.onMessage.addListener(async (data) => {
|
||||
switch (data.type) {
|
||||
case 'nymClientSendMessage':
|
||||
if (nymClient) {
|
||||
await nymClient.client.send({
|
||||
payload: {
|
||||
message: data.message,
|
||||
mimeType: 'text/plain',
|
||||
},
|
||||
recipient: data.recipient,
|
||||
});
|
||||
backgroundState.messageLog.push({
|
||||
type: 'sent',
|
||||
message: data.message,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
backgroundState.isReady = true;
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
browser.runtime.onMessage.addListener((data) => {
|
||||
switch (data.type) {
|
||||
case 'popupReady':
|
||||
if (backgroundState.isReady) {
|
||||
browser.runtime.sendMessage({
|
||||
type: 'displaySenderAddress',
|
||||
message: backgroundState.address,
|
||||
});
|
||||
browser.runtime.sendMessage({
|
||||
type: 'displayMessageLog',
|
||||
message: backgroundState.messageLog,
|
||||
});
|
||||
browser.runtime.sendMessage({
|
||||
type: 'updateRecipient',
|
||||
message: backgroundState.recipient,
|
||||
});
|
||||
} else {
|
||||
initBackground();
|
||||
}
|
||||
break;
|
||||
case 'updateRecipient':
|
||||
backgroundState.recipient = data.message;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,74 +0,0 @@
|
||||
// dom-utils.js
|
||||
// Contains utility functionality to help manipulate the DOM elements necessary to demonstrate the Nym example.
|
||||
|
||||
/**
|
||||
* Create a Sphinx packet and send it to the mixnet through the gateway node.
|
||||
*
|
||||
* Message and recipient are taken from the values in the user interface.
|
||||
*
|
||||
* @param {Client} nymClient the nym client to use for message sending
|
||||
*/
|
||||
async function sendMessageTo() {
|
||||
const message = document.getElementById('message').value;
|
||||
const recipient = document.getElementById('recipient').value;
|
||||
browser.runtime.sendMessage({
|
||||
type: 'nymClientSendMessage',
|
||||
message,
|
||||
recipient,
|
||||
});
|
||||
displaySend(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display messages that have been sent up the websocket. Colours them blue.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
function displaySend(message) {
|
||||
const timestamp = new Date().toISOString().substr(11, 12);
|
||||
const sendDiv = document.createElement('div');
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.setAttribute('style', 'color: blue');
|
||||
const paragraphContent = document.createTextNode(`${timestamp} sent >>> ${message}`);
|
||||
paragraph.appendChild(paragraphContent);
|
||||
sendDiv.appendChild(paragraph);
|
||||
document.getElementById('output')?.appendChild(sendDiv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display received text messages in the browser. Colour them green.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
function displayReceived(message) {
|
||||
const content = message;
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const receivedDiv = document.createElement('div');
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.setAttribute('style', 'color: green');
|
||||
const paragraphContent = document.createTextNode(`${timestamp} received >>> ${content}`);
|
||||
paragraph.appendChild(paragraphContent);
|
||||
receivedDiv.appendChild(paragraph);
|
||||
document.getElementById('output')?.appendChild(receivedDiv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the nymClient's sender address in the user interface
|
||||
*
|
||||
* @param {Client} nymClient
|
||||
*/
|
||||
function displaySenderAddress(address) {
|
||||
document.getElementById('sender').value = address;
|
||||
}
|
||||
|
||||
function displayMessageLog(messageLog) {
|
||||
for (let i = 0; i < messageLog.length; i++) {
|
||||
if (messageLog[i].type === 'sent') {
|
||||
displaySend(messageLog[i].message);
|
||||
} else if (messageLog[i].type === 'received') {
|
||||
displayReceived(messageLog[i].message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { sendMessageTo, displaySend, displayReceived, displaySenderAddress, displayMessageLog };
|
||||
@@ -1,35 +0,0 @@
|
||||
// dom-utils.js
|
||||
// Contains utility functionality to help manipulate the DOM elements necessary to demonstrate the Nym example.
|
||||
|
||||
import { BackgroundState } from './background';
|
||||
import { displayReceived, displaySend, displaySenderAddress } from '../../shared/dom-utils';
|
||||
|
||||
/**
|
||||
* Create a Sphinx packet and send it to the mixnet through the gateway node.
|
||||
*
|
||||
* Message and recipient are taken from the values in the user interface.
|
||||
*
|
||||
* @param {Client} nymClient the nym client to use for message sending
|
||||
*/
|
||||
async function sendMessageTo() {
|
||||
const message = (document.getElementById('message') as HTMLFormElement).value;
|
||||
const recipient = (document.getElementById('recipient') as HTMLFormElement).value;
|
||||
browser.runtime.sendMessage({
|
||||
type: 'nymClientSendMessage',
|
||||
message,
|
||||
recipient,
|
||||
});
|
||||
displaySend(message);
|
||||
}
|
||||
|
||||
function displayMessageLog(messageLog: BackgroundState['messageLog']) {
|
||||
for (let i = 0; i < messageLog.length; i++) {
|
||||
if (messageLog[i].type === 'sent') {
|
||||
displaySend(messageLog[i].message);
|
||||
} else if (messageLog[i].type === 'received') {
|
||||
displayReceived(messageLog[i].message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { sendMessageTo, displaySend, displayReceived, displaySenderAddress, displayMessageLog };
|
||||
@@ -1,40 +0,0 @@
|
||||
// Import the DOM utility functionality.
|
||||
import { displaySenderAddress, displayReceived, sendMessageTo, displayMessageLog } from './dom-utils.js';
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const sendButton = document.querySelector('#send-button');
|
||||
if (sendButton) {
|
||||
sendButton.onclick = function () {
|
||||
sendMessageTo();
|
||||
};
|
||||
}
|
||||
const recipient = document.getElementById('recipient');
|
||||
recipient.onchange = () => {
|
||||
browser.runtime.sendMessage({
|
||||
type: 'updateRecipient',
|
||||
message: recipient.value,
|
||||
});
|
||||
};
|
||||
browser.runtime.onMessage.addListener((data) => {
|
||||
switch (data.type) {
|
||||
case 'displaySenderAddress':
|
||||
displaySenderAddress(data.message);
|
||||
break;
|
||||
case 'displayReceived':
|
||||
displayReceived(data.message);
|
||||
break;
|
||||
case 'sendMessageTo':
|
||||
sendMessageTo(data.message);
|
||||
break;
|
||||
case 'displayMessageLog':
|
||||
displayMessageLog(data.message);
|
||||
break;
|
||||
case 'updateRecipient':
|
||||
recipient.value = data.message;
|
||||
}
|
||||
});
|
||||
browser.runtime.sendMessage({
|
||||
type: 'popupReady',
|
||||
message: '',
|
||||
});
|
||||
});
|
||||
@@ -1,40 +0,0 @@
|
||||
// Import the DOM utility functionality.
|
||||
import { displaySenderAddress, displayReceived, sendMessageTo, displayMessageLog } from './dom-utils';
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const sendButton = document.querySelector('#send-button') as HTMLButtonElement;
|
||||
if (sendButton) {
|
||||
sendButton.onclick = function () {
|
||||
sendMessageTo();
|
||||
};
|
||||
}
|
||||
const recipient = document.getElementById('recipient') as HTMLFormElement;
|
||||
recipient.onchange = () => {
|
||||
browser.runtime.sendMessage({
|
||||
type: 'updateRecipient',
|
||||
message: recipient.value,
|
||||
});
|
||||
};
|
||||
browser.runtime.onMessage.addListener((data) => {
|
||||
switch (data.type) {
|
||||
case 'displaySenderAddress':
|
||||
displaySenderAddress(data.message);
|
||||
break;
|
||||
case 'displayReceived':
|
||||
displayReceived(data.message);
|
||||
break;
|
||||
// case 'sendMessageTo':
|
||||
// sendMessageTo(data.message);
|
||||
// break;
|
||||
case 'displayMessageLog':
|
||||
displayMessageLog(data.message);
|
||||
break;
|
||||
case 'updateRecipient':
|
||||
recipient.value = data.message;
|
||||
}
|
||||
});
|
||||
browser.runtime.sendMessage({
|
||||
type: 'popupReady',
|
||||
message: '',
|
||||
});
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Webpack configuration for the Firefox extension example
|
||||
|
||||
const path = require('path');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
entry: {
|
||||
background: './src/background.js',
|
||||
popup: './src/popup.js',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
'manifest.json',
|
||||
'popup.html',
|
||||
{ from: path.resolve(__dirname, '../../../../assets/favicon/favicon.png'), to: 'icon.png' },
|
||||
],
|
||||
}),
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /web-worker.*\.js$/,
|
||||
loader: 'worker-loader',
|
||||
options: {
|
||||
filename: '[name].js',
|
||||
inline: 'fallback',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
# Nym Node Tester - Parcel bundler
|
||||
|
||||
This is an example of using the Nym Mixnet node tester.
|
||||
|
||||
You can use this example as a seed for a new project.
|
||||
|
||||
## Running the example
|
||||
|
||||
Try out the node tester app by running:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "@nymproject/sdk-example-node-tester-plain-html-parcel",
|
||||
"version": "1.0.5",
|
||||
"description": "An example project that uses WASM and plain HTML bundled with Parcel v2",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"build": "npx parcel build",
|
||||
"build:serve": "npx serve dist",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix",
|
||||
"start": "npx parcel",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"tsc": "tsc",
|
||||
"tsc:watch": "tsc --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": ">=1.4.1-rc1 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.13",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-root-import": "^1.0.4",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jest": "^26.1.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.1.0",
|
||||
"mini-css-extract-plugin": "^2.2.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.7",
|
||||
"ts-jest": "^27.0.5",
|
||||
"typescript": "^4.6.2"
|
||||
},
|
||||
"private": false,
|
||||
"browserslist": "> 0.5%, last 2 versions, not dead",
|
||||
"source": "src/index.html"
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nym Node Tester Demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>
|
||||
<label>Mixnode Id: </label><input size="85" type="text" id="mixnodeId" value="">
|
||||
</p>
|
||||
<p>
|
||||
<button id="test-button">Test</button>
|
||||
<button id="disconnect-button">Disconnect from Gateway</button>
|
||||
<button id="reconnect-button">Reconnect to Gateway</button>
|
||||
<button id="terminate-button">Terminate worker</button>
|
||||
</p>
|
||||
|
||||
<p>Test a mixnode by entering the mixnode id above and click the Test button</p>
|
||||
|
||||
<hr>
|
||||
<p>
|
||||
<div id="output"></div>
|
||||
</p>
|
||||
<script src="./index.ts" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,118 +0,0 @@
|
||||
import { createNodeTesterClient, NodeTester } from '@nymproject/sdk';
|
||||
|
||||
let nodeTester: NodeTester | null = null;
|
||||
|
||||
/**
|
||||
* Display messages that have been sent up the websocket. Colours them blue.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
function displayOutput(message: string, color?: string) {
|
||||
const timestamp = new Date().toISOString().substr(11, 12);
|
||||
|
||||
const sendDiv = document.createElement('div');
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.setAttribute('style', `color: ${color || 'blue'}`);
|
||||
const paragraphContent = document.createTextNode(`${timestamp} >>> ${message}`);
|
||||
paragraph.appendChild(paragraphContent);
|
||||
|
||||
sendDiv.appendChild(paragraph);
|
||||
document.getElementById('output')?.appendChild(sendDiv);
|
||||
}
|
||||
|
||||
/**
|
||||
* The main entry point
|
||||
*/
|
||||
async function main() {
|
||||
nodeTester = await createNodeTesterClient();
|
||||
|
||||
// add node tester to the Window globally, so that it can be used from the dev tools console
|
||||
(window as any).nodeTester = nodeTester;
|
||||
|
||||
if (!nodeTester) {
|
||||
console.error('Oh no! Could not the node test');
|
||||
return;
|
||||
}
|
||||
|
||||
const nymApiUrl = 'https://validator.nymtech.net/api';
|
||||
const nodeTesterId = new Date().toISOString(); // make a new tester id for each session
|
||||
await nodeTester.tester.init(nymApiUrl, nodeTesterId);
|
||||
|
||||
const mixnodes = await (await fetch(`${nymApiUrl}/v1/mixnodes/active`)).json();
|
||||
|
||||
const exampleMixnodeIdentityKey = mixnodes[0].bond_information.mix_node.identity_key;
|
||||
|
||||
const testButton: HTMLButtonElement = document.querySelector('#test-button') as HTMLButtonElement;
|
||||
const reconnectButton: HTMLButtonElement = document.querySelector('#reconnect-button') as HTMLButtonElement;
|
||||
const disconnectButton: HTMLButtonElement = document.querySelector('#disconnect-button') as HTMLButtonElement;
|
||||
const terminateButton: HTMLButtonElement = document.querySelector('#terminate-button') as HTMLButtonElement;
|
||||
|
||||
const mixnodeIdInput = document.getElementById('mixnodeId') as HTMLFormElement;
|
||||
|
||||
mixnodeIdInput.value = exampleMixnodeIdentityKey;
|
||||
|
||||
reconnectButton.onclick = async function () {
|
||||
try {
|
||||
await nodeTester?.tester.reconnectToGateway();
|
||||
} catch (e: any) {
|
||||
console.error('Error', e);
|
||||
displayOutput(`ERROR: ${e.message}`, 'red');
|
||||
}
|
||||
};
|
||||
|
||||
disconnectButton.onclick = async function () {
|
||||
try {
|
||||
await nodeTester?.tester.disconnectFromGateway();
|
||||
} catch (e: any) {
|
||||
console.error('Error', e);
|
||||
displayOutput(`ERROR: ${e.message}`, 'red');
|
||||
}
|
||||
};
|
||||
|
||||
terminateButton.onclick = async function () {
|
||||
try {
|
||||
await nodeTester?.terminate();
|
||||
} catch (e: any) {
|
||||
console.error('Error', e);
|
||||
displayOutput(`ERROR: ${e.message}`, 'red');
|
||||
}
|
||||
};
|
||||
|
||||
if (testButton) {
|
||||
testButton.onclick = async function () {
|
||||
console.log('clicked');
|
||||
|
||||
const mixnodeId = mixnodeIdInput.value;
|
||||
if (!nodeTester) {
|
||||
displayOutput('ERROR: The node tester is not defined');
|
||||
console.error('The node tester is not defined');
|
||||
return;
|
||||
}
|
||||
if (!mixnodeId) {
|
||||
displayOutput('ERROR: No mix id specified');
|
||||
console.error('No mix id specified');
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodeTester && mixnodeId) {
|
||||
displayOutput('Starting test...');
|
||||
try {
|
||||
const response = await nodeTester.tester.startTest(mixnodeId);
|
||||
displayOutput('Done!');
|
||||
if (response) {
|
||||
displayOutput(JSON.stringify(response, null, 2), 'green');
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('Error', e);
|
||||
displayOutput(`ERROR: ${e.message}`, 'red');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// wait for the html to load
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
// let's do this!
|
||||
main();
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules", "build", "dist"]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
# Nym Node Tester - HTML
|
||||
|
||||
This is an example of using the Nym Mixnet node tester.
|
||||
|
||||
You can use this example as a seed for a new project.
|
||||
|
||||
## Running the example
|
||||
|
||||
Try out the node tester app by running:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"name": "@nymproject/sdk-example-node-tester-plain-html",
|
||||
"version": "1.0.5",
|
||||
"description": "An example project that uses WASM node tester and plain HTML",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"build": "webpack build --progress --config webpack.prod.js",
|
||||
"build:dev": "webpack build --progress",
|
||||
"build:serve": "npx serve dist",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix",
|
||||
"start": "webpack serve --progress --port 3000",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"tsc": "tsc",
|
||||
"tsc:watch": "tsc --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": ">=1.4.1-rc1 || ^1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.14.5",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.7.13",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"babel-loader": "^8.3.0",
|
||||
"babel-plugin-root-import": "^5.1.0",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-root-import": "^1.0.4",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jest": "^26.1.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.1",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"jest": "^27.1.0",
|
||||
"mini-css-extract-plugin": "^2.2.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.8.7",
|
||||
"style-loader": "^3.3.1",
|
||||
"thread-loader": "^3.0.4",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-loader": "^9.4.2",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"typescript": "^4.6.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.5.0",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nym Node Tester Demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>
|
||||
<label>Mixnode Id: </label><input size="85" type="text" id="mixnodeId" value="">
|
||||
</p>
|
||||
<p>
|
||||
<button id="send-button">Test</button>
|
||||
<button id="disconnect-button">Disconnect from Gateway</button>
|
||||
<button id="reconnect-button">Reconnect to Gateway</button>
|
||||
<button id="terminate-button">Terminate worker</button>
|
||||
</p>
|
||||
|
||||
<p>Test a mixnode by entering the mixnode id above and click the Test button</p>
|
||||
|
||||
<hr>
|
||||
<p>
|
||||
<div id="output"></div>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,116 +0,0 @@
|
||||
import { createNodeTesterClient, NodeTester } from '@nymproject/sdk';
|
||||
|
||||
let nodeTester: NodeTester | null = null;
|
||||
|
||||
/**
|
||||
* Display messages that have been sent up the websocket. Colours them blue.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
function displayOutput(message: string, color?: string) {
|
||||
const timestamp = new Date().toISOString().substr(11, 12);
|
||||
|
||||
const sendDiv = document.createElement('div');
|
||||
const paragraph = document.createElement('p');
|
||||
paragraph.setAttribute('style', `color: ${color || 'blue'}`);
|
||||
const paragraphContent = document.createTextNode(`${timestamp} >>> ${message}`);
|
||||
paragraph.appendChild(paragraphContent);
|
||||
|
||||
sendDiv.appendChild(paragraph);
|
||||
document.getElementById('output')?.appendChild(sendDiv);
|
||||
}
|
||||
|
||||
/**
|
||||
* The main entry point
|
||||
*/
|
||||
async function main() {
|
||||
nodeTester = await createNodeTesterClient();
|
||||
|
||||
// add node tester to the Window globally, so that it can be used from the dev tools console
|
||||
(window as any).nodeTester = nodeTester;
|
||||
|
||||
if (!nodeTester) {
|
||||
console.error('Oh no! Could not the node test');
|
||||
return;
|
||||
}
|
||||
|
||||
const nymApiUrl = 'https://validator.nymtech.net/api';
|
||||
const nodeTesterId = new Date().toISOString(); // make a new tester id for each session
|
||||
await nodeTester.tester.init(nymApiUrl, nodeTesterId);
|
||||
|
||||
const mixnodes = await (await fetch(`${nymApiUrl}/v1/mixnodes/active`)).json();
|
||||
|
||||
const exampleMixnodeIdentityKey = mixnodes[0].bond_information.mix_node.identity_key;
|
||||
|
||||
const sendButton: HTMLButtonElement = document.querySelector('#send-button') as HTMLButtonElement;
|
||||
const reconnectButton: HTMLButtonElement = document.querySelector('#reconnect-button') as HTMLButtonElement;
|
||||
const disconnectButton: HTMLButtonElement = document.querySelector('#disconnect-button') as HTMLButtonElement;
|
||||
const terminateButton: HTMLButtonElement = document.querySelector('#terminate-button') as HTMLButtonElement;
|
||||
|
||||
const mixnodeIdInput = document.getElementById('mixnodeId') as HTMLFormElement;
|
||||
|
||||
mixnodeIdInput.value = exampleMixnodeIdentityKey;
|
||||
|
||||
reconnectButton.onclick = async function () {
|
||||
try {
|
||||
await nodeTester?.tester.reconnectToGateway();
|
||||
} catch (e: any) {
|
||||
console.error('Error', e);
|
||||
displayOutput(`ERROR: ${e.message}`, 'red');
|
||||
}
|
||||
};
|
||||
|
||||
disconnectButton.onclick = async function () {
|
||||
try {
|
||||
await nodeTester?.tester.disconnectFromGateway();
|
||||
} catch (e: any) {
|
||||
console.error('Error', e);
|
||||
displayOutput(`ERROR: ${e.message}`, 'red');
|
||||
}
|
||||
};
|
||||
|
||||
terminateButton.onclick = async function () {
|
||||
try {
|
||||
await nodeTester?.terminate();
|
||||
} catch (e: any) {
|
||||
console.error('Error', e);
|
||||
displayOutput(`ERROR: ${e.message}`, 'red');
|
||||
}
|
||||
};
|
||||
|
||||
if (sendButton) {
|
||||
sendButton.onclick = async function () {
|
||||
const mixnodeId = mixnodeIdInput.value;
|
||||
if (!nodeTester) {
|
||||
displayOutput('ERROR: The node tester is not defined');
|
||||
console.error('The node tester is not defined');
|
||||
return;
|
||||
}
|
||||
if (!mixnodeId) {
|
||||
displayOutput('ERROR: No mix id specified');
|
||||
console.error('No mix id specified');
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodeTester && mixnodeId) {
|
||||
displayOutput('Starting test...');
|
||||
try {
|
||||
const response = await nodeTester.tester.startTest(mixnodeId);
|
||||
displayOutput('Done!');
|
||||
if (response) {
|
||||
displayOutput(JSON.stringify(response, null, 2), 'green');
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('Error', e);
|
||||
displayOutput(`ERROR: ${e.message}`, 'red');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// wait for the html to load
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
// let's do this!
|
||||
main();
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules", "build", "dist"]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"outDir": "./dist",
|
||||
"declaration": false
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules", "build", "dist", "**/*.stories.*", "**/*.test.*", "**/*.spec.*"]
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
const path = require('path');
|
||||
const { mergeWithRules } = require('webpack-merge');
|
||||
const { webpackCommon } = require('../../.webpack/webpack.base');
|
||||
|
||||
module.exports = mergeWithRules({
|
||||
module: {
|
||||
rules: {
|
||||
test: 'match',
|
||||
use: 'replace',
|
||||
},
|
||||
},
|
||||
})(
|
||||
webpackCommon(__dirname, [
|
||||
{
|
||||
inject: true,
|
||||
filename: 'index.html',
|
||||
template: path.resolve(__dirname, 'src/index.html'),
|
||||
chunks: ['index'],
|
||||
},
|
||||
]),
|
||||
{
|
||||
entry: {
|
||||
index: path.resolve(__dirname, 'src/index.ts'),
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
publicPath: '/',
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -1,67 +0,0 @@
|
||||
const { mergeWithRules } = require('webpack-merge');
|
||||
const webpack = require('webpack');
|
||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||
const ReactRefreshTypeScript = require('react-refresh-typescript');
|
||||
const commonConfig = require('./webpack.common');
|
||||
|
||||
module.exports = mergeWithRules({
|
||||
module: {
|
||||
rules: {
|
||||
test: 'match',
|
||||
use: 'replace',
|
||||
},
|
||||
},
|
||||
})(commonConfig, {
|
||||
mode: 'development',
|
||||
devtool: 'inline-source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/,
|
||||
options: {
|
||||
getCustomTransformers: () => ({
|
||||
before: [ReactRefreshTypeScript()],
|
||||
}),
|
||||
// `ts-loader` does not work with HMR unless `transpileOnly` is used.
|
||||
// If you need type checking, `ForkTsCheckerWebpackPlugin` is an alternative.
|
||||
transpileOnly: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new ReactRefreshWebpackPlugin(),
|
||||
|
||||
// this can be included automatically by the dev server, however build mode fails if missing
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
],
|
||||
|
||||
target: 'web',
|
||||
|
||||
devServer: {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
},
|
||||
historyApiFallback: true,
|
||||
},
|
||||
|
||||
// recommended for faster rebuild
|
||||
optimization: {
|
||||
runtimeChunk: true,
|
||||
removeAvailableModules: false,
|
||||
removeEmptyChunks: false,
|
||||
splitChunks: false,
|
||||
},
|
||||
|
||||
cache: {
|
||||
type: 'filesystem',
|
||||
buildDependencies: {
|
||||
// restart on config change
|
||||
config: ['./webpack.config.js'],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
const { mergeWithRules } = require('webpack-merge');
|
||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const commonConfig = require('./webpack.common');
|
||||
|
||||
module.exports = mergeWithRules({
|
||||
module: {
|
||||
rules: {
|
||||
test: 'match',
|
||||
use: 'replace',
|
||||
},
|
||||
},
|
||||
})(commonConfig, {
|
||||
mode: 'production',
|
||||
|
||||
// TODO: no source maps, add back
|
||||
devtool: false,
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [MiniCssExtractPlugin.loader, 'css-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: {
|
||||
minimizer: ['...', new CssMinimizerPlugin()],
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].[contenthash].css',
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
pathinfo: false,
|
||||
filename: '[name].[contenthash].js',
|
||||
},
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
# Nym Node Tester - React
|
||||
|
||||
This is an example of using the Nym Mixnet node tester.
|
||||
|
||||
You can use this example as a seed for a new project.
|
||||
|
||||
## Running the example
|
||||
|
||||
Try out the node tester app by running:
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="shortcut icon" href="../../../../../assets/favicon/favicon.png" />
|
||||
<title>Nym Node Tester Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="src/index.tsx" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "@nymproject/sdk-example-node-tester-react",
|
||||
"version": "1.0.5",
|
||||
"description": "An example project that uses WASM node tester and React",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"start": "parcel index.html"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.14.0",
|
||||
"@mui/material": "^5.14.0",
|
||||
"@nymproject/sdk": ">=1.4.1-rc1 || ^1",
|
||||
"parcel": "^2.9.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"private": false
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardActions,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CircularProgress,
|
||||
Grid,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { NodeTestResultResponse } from '@nymproject/sdk';
|
||||
import { ScoreIndicator } from 'src/components/ScoreIndicator';
|
||||
import { useNodeTesterClient } from 'src/hooks/useNodeTesterClient';
|
||||
import { BasicPageLayout } from 'src/layouts';
|
||||
import { TestStatusLabel } from 'src/components/TestStatusLabel';
|
||||
import Icon from '../../../../../../assets/appicon/appicon.png';
|
||||
|
||||
export const App = () => {
|
||||
const { testState, error, testNode, disconnectFromGateway, reconnectToGateway } = useNodeTesterClient();
|
||||
const [mixnodeIdentity, setMixnodeIdentity] = useState<string>('');
|
||||
const [results, setResults] = React.useState<NodeTestResultResponse>();
|
||||
|
||||
console.log({ testState, error, testNode });
|
||||
|
||||
const handleTestNode = async () => {
|
||||
setResults(undefined);
|
||||
try {
|
||||
const result = await testNode(mixnodeIdentity);
|
||||
setResults(result);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<BasicPageLayout>
|
||||
<Card variant="outlined" sx={{ mt: 15, p: 4 }}>
|
||||
<CardHeader
|
||||
title={<Typography variant="h6">Nym Mixnode Testnet Node Tester</Typography>}
|
||||
action={<TestStatusLabel state={testState} />}
|
||||
avatar={<img src={Icon} width={40} />}
|
||||
/>
|
||||
<CardContent sx={{ mb: 2 }}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<ScoreIndicator score={results?.score || 0} />
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemText primary="Packets sent" secondary={results?.sentPackets.toString() || '-'} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText primary="Packets received" secondary={results?.receivedPackets.toString() || '-'} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary="Duplicate packets received"
|
||||
secondary={results?.duplicatePackets.toString() || '-'}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
label="Enter a Mixnode Identity to test"
|
||||
value={mixnodeIdentity}
|
||||
onChange={(e) => {
|
||||
setMixnodeIdentity(e.target.value);
|
||||
}}
|
||||
fullWidth
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<Button
|
||||
disabled={!disconnectFromGateway || testState === 'Disconnected' || testState === 'Testing'}
|
||||
onClick={disconnectFromGateway}
|
||||
variant="outlined"
|
||||
disableElevation
|
||||
size="large"
|
||||
fullWidth
|
||||
sx={{ mr: 2 }}
|
||||
>
|
||||
Disconnect
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<Button
|
||||
disabled={!reconnectToGateway || testState === 'Ready' || testState === 'Testing'}
|
||||
onClick={reconnectToGateway}
|
||||
variant="outlined"
|
||||
disableElevation
|
||||
size="large"
|
||||
fullWidth
|
||||
sx={{ mr: 2 }}
|
||||
>
|
||||
Reconnect
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<Button
|
||||
disabled={!testNode || !mixnodeIdentity || testState === 'Testing' || testState === 'Disconnected'}
|
||||
onClick={handleTestNode}
|
||||
variant="contained"
|
||||
disableElevation
|
||||
fullWidth
|
||||
size="large"
|
||||
endIcon={testState === 'Testing' && <CircularProgress size={25} />}
|
||||
>
|
||||
Start test
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</BasicPageLayout>
|
||||
);
|
||||
};
|
||||
@@ -1,61 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box, CircularProgress, CircularProgressProps, Stack, Typography } from '@mui/material';
|
||||
|
||||
const getPerformanceDescriptionAndColor = (score: number) => {
|
||||
const res: { description: string; color: CircularProgressProps['color'] } = { description: '', color: 'warning' };
|
||||
|
||||
if (score >= 90) {
|
||||
res.description = 'Reliable node';
|
||||
res.color = 'success';
|
||||
}
|
||||
|
||||
if (score >= 75 && score < 90) {
|
||||
res.description = 'Average node';
|
||||
res.color = 'warning';
|
||||
}
|
||||
|
||||
if (score > 0 && score < 75) {
|
||||
res.description = 'Unreliable node';
|
||||
res.color = 'error';
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const ScoreIndicator = ({ score }) => {
|
||||
const { color } = getPerformanceDescriptionAndColor(score);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
position: 'relative',
|
||||
width: 250,
|
||||
height: 250,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
mx: 'auto',
|
||||
mt: 4,
|
||||
}}
|
||||
>
|
||||
<CircularProgress
|
||||
variant="determinate"
|
||||
value={100}
|
||||
size={250}
|
||||
sx={{ position: 'absolute', top: 0, left: 0, color: 'grey.200' }}
|
||||
/>
|
||||
<CircularProgress
|
||||
variant="determinate"
|
||||
value={score}
|
||||
size={250}
|
||||
sx={{ position: 'absolute', top: 0, left: 0 }}
|
||||
color={color}
|
||||
/>
|
||||
<Stack alignItems="center" gap={1}>
|
||||
<Typography fontWeight="bold" variant="h4">
|
||||
{Math.round(score)}%
|
||||
</Typography>
|
||||
<Typography>Performance Score</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Chip } from '@mui/material';
|
||||
import { HourglassTop, ErrorOutline, CheckCircleOutline, WarningAmber } from '@mui/icons-material';
|
||||
import { TestState } from 'src/hooks/useNodeTesterClient';
|
||||
|
||||
const getColor = (state: TestState) => {
|
||||
switch (state) {
|
||||
case 'Connecting':
|
||||
return 'warning';
|
||||
case 'Error':
|
||||
return 'error';
|
||||
case 'Ready':
|
||||
return 'success';
|
||||
default:
|
||||
return 'warning';
|
||||
}
|
||||
};
|
||||
|
||||
const getIcon = (state: TestState) => {
|
||||
switch (state) {
|
||||
case 'Connecting':
|
||||
return <HourglassTop />;
|
||||
case 'Error':
|
||||
return <ErrorOutline />;
|
||||
case 'Ready':
|
||||
return <CheckCircleOutline />;
|
||||
default:
|
||||
return <WarningAmber />;
|
||||
}
|
||||
};
|
||||
|
||||
export const TestStatusLabel = ({ state }: { state: TestState }) => (
|
||||
<Chip label={state} color={getColor(state)} icon={getIcon(state)} sx={{ color: 'white' }} />
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user