From 22245e673d0d714251a99d60c4ee4510f9f1a654 Mon Sep 17 00:00:00 2001 From: Alois Date: Sat, 27 Jun 2026 02:32:43 +0200 Subject: [PATCH] (feat): add package.json for easy wasm installs (feat): add code quality control (fix): ci rewritten for forgejo (qol): move docs to dedicated docs/ folder --- .forgejo/workflows/ci.yml | 216 ++++++++++++++++++++++ .github/workflows/ci.yml | 65 ------- .gitignore | 1 + Cargo.lock | 17 -- bun.lock | 27 +++ client/Cargo.toml | 1 - crypto/Cargo.toml | 3 + CONNECTOR.md => docs/CONNECTOR.md | 0 NATIVE-CLIENT.md => docs/NATIVE-CLIENT.md | 0 NATIVE-HOST.md => docs/NATIVE-HOST.md | 0 TYPE-MAP.md => docs/TYPE-MAP.md | 0 WASM-CLIENT.md => docs/WASM-CLIENT.md | 0 example-usage/server/Cargo.toml | 1 - flake.nix | 92 +++++++-- host/Cargo.toml | 1 - host/src/lib.rs | 2 +- package.json | 29 +++ transport/Cargo.toml | 5 - wasm/Cargo.toml | 6 +- wasm/pkg/mtp_wasm_bg.wasm | Bin 377214 -> 377146 bytes 20 files changed, 354 insertions(+), 112 deletions(-) create mode 100644 .forgejo/workflows/ci.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 bun.lock rename CONNECTOR.md => docs/CONNECTOR.md (100%) rename NATIVE-CLIENT.md => docs/NATIVE-CLIENT.md (100%) rename NATIVE-HOST.md => docs/NATIVE-HOST.md (100%) rename TYPE-MAP.md => docs/TYPE-MAP.md (100%) rename WASM-CLIENT.md => docs/WASM-CLIENT.md (100%) create mode 100644 package.json diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml new file mode 100644 index 0000000..3f62f86 --- /dev/null +++ b/.forgejo/workflows/ci.yml @@ -0,0 +1,216 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +env: + CARGO_TERM_COLOR: always + +jobs: + fmt: + name: rustfmt + runs-on: docker + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Install system dependencies + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates curl build-essential pkg-config libssl-dev + + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --component rustfmt + + - name: Check formatting + run: | + export PATH="$HOME/.cargo/bin:$PATH" + cargo fmt --all --check + + clippy: + name: clippy + runs-on: docker + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Install system dependencies + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates curl build-essential pkg-config libssl-dev + + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --component clippy + + - name: Run clippy + run: | + export PATH="$HOME/.cargo/bin:$PATH" + export MTP_TYPE_MAPS="$PWD/example-usage/type-maps.yaml" + cargo clippy --workspace --exclude mtp-wasm --all-targets --all-features -- -D warnings -W unreachable-pub + + test: + name: test + runs-on: docker + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Install system dependencies + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates curl build-essential pkg-config libssl-dev + + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal + + - name: Run tests + run: | + export PATH="$HOME/.cargo/bin:$PATH" + export MTP_TYPE_MAPS="$PWD/example-usage/type-maps.yaml" + cargo test --workspace --exclude mtp-wasm --all-features + + wasm: + name: wasm build + runs-on: docker + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Install system dependencies + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates curl build-essential pkg-config libssl-dev lld + + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --target wasm32-unknown-unknown + + - name: Build wasm crate + run: | + export PATH="$HOME/.cargo/bin:$PATH" + export MTP_TYPE_MAPS="$PWD/example-usage/type-maps.yaml" + cargo build -p mtp-wasm --target wasm32-unknown-unknown + env: + RUSTFLAGS: --cfg web_sys_unstable_apis + + example-usage: + name: example usage + runs-on: docker + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Install system dependencies + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates curl build-essential pkg-config libssl-dev + + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal + + - name: Check example usage workspace + working-directory: example-usage + run: | + export PATH="$HOME/.cargo/bin:$PATH" + export MTP_TYPE_MAPS="$PWD/type-maps.yaml" + cargo check --workspace --all-targets --all-features + + deny: + name: cargo-deny + runs-on: docker + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Install system dependencies + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates curl build-essential pkg-config libssl-dev + + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal + + - name: Install cargo-deny + run: | + export PATH="$HOME/.cargo/bin:$PATH" + cargo install cargo-deny --locked + + - name: Run cargo-deny + run: | + export PATH="$HOME/.cargo/bin:$PATH" + cargo deny check + + machete: + name: cargo-machete + runs-on: docker + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Install system dependencies + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates curl build-essential pkg-config libssl-dev + + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal + + - name: Install cargo-machete + run: | + export PATH="$HOME/.cargo/bin:$PATH" + cargo install cargo-machete --locked + + - name: Run cargo-machete + run: | + export PATH="$HOME/.cargo/bin:$PATH" + cargo machete + + duplicates: + name: duplicate code + runs-on: docker + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Install system dependencies + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates curl unzip + + - name: Install Bun + run: | + curl -fsSL https://bun.sh/install | bash + + - name: Install dependencies + run: | + export PATH="$HOME/.bun/bin:$PATH" + bun install --frozen-lockfile + + - name: Run duplicate detector + run: | + export PATH="$HOME/.bun/bin:$PATH" + bun run dup + + web-client: + name: web client + runs-on: docker + steps: + - uses: https://data.forgejo.org/actions/checkout@v4 + + - name: Install system dependencies + run: apt-get update && apt-get install -y --no-install-recommends ca-certificates curl unzip build-essential pkg-config libssl-dev lld + + - name: Install Rust + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --target wasm32-unknown-unknown + + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Build wasm package + run: | + export PATH="$HOME/.cargo/bin:$PATH" + export MTP_TYPE_MAPS="$PWD/example-usage/type-maps.yaml" + wasm-pack build wasm --target web + env: + RUSTFLAGS: --cfg web_sys_unstable_apis + + - name: Install Bun + run: | + curl -fsSL https://bun.sh/install | bash + + - name: Install dependencies + working-directory: example-usage/web-client + run: | + export PATH="$HOME/.bun/bin:$PATH" + bun install --frozen-lockfile + + - name: Build web client + working-directory: example-usage/web-client + run: | + export PATH="$HOME/.bun/bin:$PATH" + bun run build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 61c41cc..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: CI - -on: - push: - branches: [master] - pull_request: - -env: - CARGO_TERM_COLOR: always - # The example workspace config points the type-map build script at this file. - MTP_TYPE_MAPS: example-type-maps.yaml - -jobs: - fmt: - name: rustfmt - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - run: cargo fmt --all --check - - clippy: - name: clippy - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - uses: Swatinem/rust-cache@v2 - - run: cargo clippy --workspace --exclude mtp-wasm --all-targets --all-features -- -D warnings - - test: - name: test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - run: cargo test --workspace --exclude mtp-wasm --all-features - - wasm: - name: wasm build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - targets: wasm32-unknown-unknown - - uses: Swatinem/rust-cache@v2 - # The WebTransport bindings in web-sys are still gated behind this cfg. - - run: cargo build -p mtp-wasm --target wasm32-unknown-unknown - env: - RUSTFLAGS: --cfg=web_sys_unstable_apis - - deny: - name: cargo-deny - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v2 - with: - command: check diff --git a/.gitignore b/.gitignore index fae72c5..3c93af0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ **target/ *.pem *.key +node_modules/ diff --git a/Cargo.lock b/Cargo.lock index 4283ec3..346bb2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -990,7 +990,6 @@ dependencies = [ "mtp-crypto", "mtp-transport", "rand 0.8.6", - "tokio", ] [[package]] @@ -1042,7 +1041,6 @@ dependencies = [ "mtp-crypto", "mtp-transport", "rand 0.8.6", - "tokio", ] [[package]] @@ -1052,11 +1050,9 @@ dependencies = [ "log", "mtp-codec", "mtp-common", - "quinn", "rcgen", "rustls", "rustls-native-certs", - "thiserror", "tokio", "wtransport", ] @@ -1082,8 +1078,6 @@ dependencies = [ "mtp-common", "mtp-crypto", "mtp-type-map", - "serde", - "serde-wasm-bindgen", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", @@ -1642,17 +1636,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - [[package]] name = "serde_core" version = "1.0.228" diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..08856ef --- /dev/null +++ b/bun.lock @@ -0,0 +1,27 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "mtp-wasm", + "devDependencies": { + "jscpd": "5.0.11", + }, + }, + }, + "packages": { + "cpd-darwin-arm64": ["cpd-darwin-arm64@5.0.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-3QvH+4Dv7A7esVFM2tsRVWN3kn9EDu8dMYog6gYAVsCtxEf4xyxAwS/ef6LjC7/dh4+ATADFbg3H09A2fD//Qw=="], + + "cpd-darwin-x64": ["cpd-darwin-x64@5.0.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-OvgM2ps0OFR5jUzx7+FK9URdJGxUzzM5KKk2F1V3vf1LooGDKwkivfIDyKsqEwp37zcbyUo7COvBpJXOT0dZmQ=="], + + "cpd-linux-arm64-gnu": ["cpd-linux-arm64-gnu@5.0.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-pXMINibAeruglni8ZajlXEefZHDs7QFSG+vPtkBDu7uiIMpNU8aoktgO2vP+PbIRFD0vHkqTMb64kDtIOqQcwQ=="], + + "cpd-linux-x64-gnu": ["cpd-linux-x64-gnu@5.0.11", "", { "os": "linux", "cpu": "x64" }, "sha512-rQ7DuF0lH1HLzjGxlE0aEP2ycfhXgZH/CLSeS7FXNJ38lRVp+iXkrlcrrY6mC4WW/NgbL+DkF7/0lv3tFvGmvg=="], + + "cpd-linux-x64-musl": ["cpd-linux-x64-musl@5.0.11", "", { "os": "linux", "cpu": "x64" }, "sha512-Yh+7Go5+fA++I5ssAZg7gUkDCT5CxnzCPvrspbwDrfnwaY6nNM5g1C6Vs0+GJhsspuAKwydJl4nf7jkxzMwRQw=="], + + "cpd-windows-x64-msvc": ["cpd-windows-x64-msvc@5.0.11", "", { "os": "win32", "cpu": "x64" }, "sha512-uV6w85qdfE0WJsrLcGw9A4Kv9ovSnlXZCybMK0esvuiJ7clgaZmDiDPozt5PvrOOShkK/NxzTZSORBSdVnquHA=="], + + "jscpd": ["jscpd@5.0.11", "", { "optionalDependencies": { "cpd-darwin-arm64": "5.0.11", "cpd-darwin-x64": "5.0.11", "cpd-linux-arm64-gnu": "5.0.11", "cpd-linux-x64-gnu": "5.0.11", "cpd-linux-x64-musl": "5.0.11", "cpd-windows-x64-msvc": "5.0.11" }, "bin": { "jscpd": "run-jscpd.js" } }, "sha512-NfLrFJHRM6rIf3oVcdZ4sfhMVop1qxi5r8aC99lpj55YC8hiWaN4VmzU2wcXTwoAo+NS4npXPs9EVEQJ6jyRlg=="], + } +} diff --git a/client/Cargo.toml b/client/Cargo.toml index 770adb8..561715b 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -8,7 +8,6 @@ mtp-common = { path = "../common" } mtp-codec = { path = "../codec" } mtp-transport = { path = "../transport" } mtp-crypto = { path = "../crypto", optional = true } -tokio = { version = "1", features = ["full"] } rand = "0.8" [features] diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index c46618c..867596b 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -3,6 +3,9 @@ name = "mtp-crypto" version = "0.1.0" edition = "2024" +[package.metadata.cargo-machete] +ignored = ["getrandom"] + [dependencies] chacha20poly1305 = { version = "0.10", optional = true } aes-gcm = { version = "0.10", optional = true } diff --git a/CONNECTOR.md b/docs/CONNECTOR.md similarity index 100% rename from CONNECTOR.md rename to docs/CONNECTOR.md diff --git a/NATIVE-CLIENT.md b/docs/NATIVE-CLIENT.md similarity index 100% rename from NATIVE-CLIENT.md rename to docs/NATIVE-CLIENT.md diff --git a/NATIVE-HOST.md b/docs/NATIVE-HOST.md similarity index 100% rename from NATIVE-HOST.md rename to docs/NATIVE-HOST.md diff --git a/TYPE-MAP.md b/docs/TYPE-MAP.md similarity index 100% rename from TYPE-MAP.md rename to docs/TYPE-MAP.md diff --git a/WASM-CLIENT.md b/docs/WASM-CLIENT.md similarity index 100% rename from WASM-CLIENT.md rename to docs/WASM-CLIENT.md diff --git a/example-usage/server/Cargo.toml b/example-usage/server/Cargo.toml index d94132e..7a12004 100644 --- a/example-usage/server/Cargo.toml +++ b/example-usage/server/Cargo.toml @@ -13,5 +13,4 @@ rcgen = "0.14" tokio = { version = "1", features = ["full"] } serde_json = { version = "1" } hex = "0.4" -serde_core = "1.0.228" base64 = "0.22" diff --git a/flake.nix b/flake.nix index b1d8196..568f0e7 100644 --- a/flake.nix +++ b/flake.nix @@ -26,6 +26,52 @@ ]; targets = ["wasm32-unknown-unknown"]; }; + + clippyCheck = pkgs.writeShellApplication { + name = "mtp-clippy"; + runtimeInputs = [rustToolchain]; + text = '' + export MTP_TYPE_MAPS="''${MTP_TYPE_MAPS:-$PWD/example-usage/type-maps.yaml}" + cargo clippy --workspace --exclude mtp-wasm --all-targets --all-features -- -D warnings -W unreachable-pub + ''; + }; + + macheteCheck = pkgs.writeShellApplication { + name = "mtp-machete"; + runtimeInputs = [pkgs.cargo-machete]; + text = '' + cargo machete "$@" + ''; + }; + + buildAll = pkgs.writeShellApplication { + name = "mtp-build-all"; + runtimeInputs = [rustToolchain pkgs.wasm-pack pkgs.bun clippyCheck macheteCheck]; + text = '' + export MTP_TYPE_MAPS="''${MTP_TYPE_MAPS:-$PWD/example-usage/type-maps.yaml}" + + bun install --frozen-lockfile + cargo fmt --all --check + cargo b + cargo test --workspace --exclude mtp-wasm --all-features + cargo check --manifest-path example-usage/Cargo.toml --workspace --all-targets --all-features + mtp-clippy + mtp-machete + bun run dup + RUSTFLAGS='--cfg web_sys_unstable_apis' wasm-pack build wasm --target web --out-dir pkg --release + bun --cwd example-usage/web-client install --frozen-lockfile + bun --cwd example-usage/web-client run build + ''; + }; + + healthCheck = pkgs.writeShellApplication { + name = "mtp-health"; + runtimeInputs = [clippyCheck macheteCheck]; + text = '' + mtp-clippy + mtp-machete + ''; + }; in { devShells = { default = pkgs.mkShell { @@ -33,6 +79,7 @@ buildInputs = with pkgs; [ rustToolchain + cargo-machete wasm-pack pkg-config openssl @@ -77,15 +124,6 @@ export MTP_DEV_CERT="$cert_pem" export MTP_DEV_KEY="$cert_key" export MTP_DEV_CERT_HASH="$(cat "$cert_hash")" - - echo "MTP dev shell" - echo " rustc : $(rustc --version)" - echo " cargo : $(cargo --version)" - echo " wasm-pack : $(wasm-pack --version 2>/dev/null || echo 'not found')" - echo " targets: $(rustc --print target-list | grep wasm32 | tr '\n' ' ')" - echo " MTP_TYPE_MAPS = $MTP_TYPE_MAPS" - echo " dev cert: $MTP_DEV_CERT ($cert_status)" - echo " cert sha256: $MTP_DEV_CERT_HASH" ''; }; autoStart = pkgs.mkShell { @@ -101,15 +139,33 @@ }; # Ad-hoc WASM build using wasm-pack - apps.wasm-build = { - type = "app"; - program = "${pkgs.wasm-pack}/bin/wasm-pack"; - args = [ - "build" - "wasm" - "--target" - "web" - ]; + apps = { + clippy = { + type = "app"; + program = "${clippyCheck}/bin/mtp-clippy"; + }; + machete = { + type = "app"; + program = "${macheteCheck}/bin/mtp-machete"; + }; + build-all = { + type = "app"; + program = "${buildAll}/bin/mtp-build-all"; + }; + health = { + type = "app"; + program = "${healthCheck}/bin/mtp-health"; + }; + wasm-build = { + type = "app"; + program = "${pkgs.wasm-pack}/bin/wasm-pack"; + args = [ + "build" + "wasm" + "--target" + "web" + ]; + }; }; } ); diff --git a/host/Cargo.toml b/host/Cargo.toml index 9e3d9f0..7a8a44e 100644 --- a/host/Cargo.toml +++ b/host/Cargo.toml @@ -8,7 +8,6 @@ mtp-common = { path = "../common" } mtp-codec = { path = "../codec", features = ["registry"] } mtp-transport = { path = "../transport", features = ["host"] } mtp-crypto = { path = "../crypto", optional = true } -tokio = { version = "1", features = ["full"] } rand = "0.8" log = "0.4" diff --git a/host/src/lib.rs b/host/src/lib.rs index fb7b455..f3b62cf 100644 --- a/host/src/lib.rs +++ b/host/src/lib.rs @@ -124,7 +124,7 @@ impl MTPHost { } }; - let negotiated = match self.registry.negotiate(&[client_version.clone()]) { + let negotiated = match self.registry.negotiate(std::slice::from_ref(&client_version)) { Some(v) => v, None => { log::warn!("accept: negotiate failed for client version {client_version:?}"); diff --git a/package.json b/package.json new file mode 100644 index 0000000..80cea8e --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "mtp-wasm", + "version": "0.1.0", + "description": "Browser WASM interface for MTP.", + "type": "module", + "module": "./wasm/pkg/mtp_wasm.js", + "types": "./wasm/pkg/mtp_wasm.d.ts", + "exports": { + ".": { + "types": "./wasm/pkg/mtp_wasm.d.ts", + "import": "./wasm/pkg/mtp_wasm.js", + "default": "./wasm/pkg/mtp_wasm.js" + } + }, + "files": [ + "wasm/pkg/mtp_wasm.js", + "wasm/pkg/mtp_wasm.d.ts", + "wasm/pkg/mtp_wasm_bg.wasm", + "wasm/pkg/mtp_wasm_bg.wasm.d.ts" + ], + "scripts": { + "build": "MTP_TYPE_MAPS=$PWD/example-usage/type-maps.yaml RUSTFLAGS='--cfg web_sys_unstable_apis' wasm-pack build wasm --target web --out-dir pkg --release", + "build:all": "nix run .#build-all", + "dup": "jscpd --pattern '**/*.rs' --ignore 'target/**' --ignore 'wasm/pkg/**' --ignore '.git/**' --min-lines 8 --min-tokens 80 --threshold 4 --reporters console --no-tips ." + }, + "devDependencies": { + "jscpd": "5.0.11" + } +} diff --git a/transport/Cargo.toml b/transport/Cargo.toml index 6db802f..7a60a2a 100644 --- a/transport/Cargo.toml +++ b/transport/Cargo.toml @@ -12,12 +12,7 @@ wtransport = { version = "0.7.1", default-features = false, features = [ "self-signed", ] } rustls = { version = "0.23.41" } -quinn = { version = "0.11.11", default-features = false, features = [ - "rustls-aws-lc-rs", - "rustls", -] } tokio = { version = "1", features = ["full"] } -thiserror = "2.0.18" rustls-native-certs = "0.8.4" log = "0.4" diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index c76b05e..6ea67d1 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -3,6 +3,9 @@ name = "mtp-wasm" version = "0.1.0" edition = "2024" +[package.metadata.cargo-machete] +ignored = ["getrandom-v02"] + [lib] crate-type = ["cdylib"] @@ -30,9 +33,6 @@ console_error_panic_hook = "0.1" hex = "0.4" -serde = { version = "1", features = ["derive"] } -serde-wasm-bindgen = "0.6" - getrandom = { version = "0.4", features = ["wasm_js"] } getrandom-v02 = { package = "getrandom", version = "0.2", features = ["js"] } diff --git a/wasm/pkg/mtp_wasm_bg.wasm b/wasm/pkg/mtp_wasm_bg.wasm index 9a6b28c530119acce98145046e10ced03761d23f..379e5b2ae1d4bec30eece01ca38bbbac468bab26 100644 GIT binary patch delta 19067 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 delta 19098 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