Skip to content

ci(wasm): guard whatsapp-rust wasm32 build and fix two #668 regressions - #671

Merged
jlucaso1 merged 2 commits into
mainfrom
ci/wasm-build-guard
Jun 1, 2026
Merged

ci(wasm): guard whatsapp-rust wasm32 build and fix two #668 regressions#671
jlucaso1 merged 2 commits into
mainfrom
ci/wasm-build-guard

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

What

Adds a CI job that builds the whatsapp-rust lib for wasm32-unknown-unknown in release, and fixes two regressions from #668 that broke that build (the one whatsapp-rust-bridge consumes).

The catch: the tokio unresolved-module error (E0433) aborts name resolution before type-checking, so it masked a second wasm regression sitting right behind it. The bridge build only ever surfaced the tokio error.

Fix 1 — tokio::time::timeout in message.rs. resolve_msg_secret_via_app called tokio::time::timeout directly, which doesn't exist under --no-default-features (no tokio-runtime feature → no tokio dep). Swapped for the runtime-agnostic wacore::runtime::timeout(&*self.runtime, ...), the same helper already used by request.rs, handshake.rs, flush_scope.rs and media_reupload.rs. The match arms are unchanged (it returns Result<T, Elapsed>, same shape as tokio's).

Fix 2 — Send + Sync bounds vs the wasm ?Send resolver. #668 added original_message_resolver: Option<Arc<dyn OriginalMessageResolver>>. That trait is, by design, ?Send on wasm (documented in msg_secret.rs — it may be backed by !Send JS handles), which makes Client !Send/!Sync on wasm. But SendContextResolver and EventHandler required Send + Sync unconditionally, so all 6 impls (context_impl.rs, send.rs, bot.rs) failed to compile on wasm. Introduced wacore::sync_marker::MaybeSendSync — a conditional bound that is Send + Sync on native (via a blanket impl over every Send + Sync type) and empty on wasm — and switched both supertraits to it.

Why

whatsapp-rust-bridge compiles whatsapp-rust to wasm32 with --no-default-features --features debug-diagnostics. Nothing in this repo's CI exercised that target, so a single accidental commit could (and did) break downstream wasm builds without turning anything red here. The new wasm.yml job mirrors the bridge's flags so this class of regression fails CI instead of reaching a consumer.

Compatibility note

The MaybeSendSync relaxation is transparent on native: a transitive supertrait Send + Sync still propagates to dyn SendContextResolver / dyn EventHandler (verified with a standalone PoC — Box<dyn _>: Send still holds), so no native consumer loses a bound. It only relaxes wasm. The crate also gains a getrandom dependency gated to cfg(target_arch = "wasm32") (enables the wasm_js backend feature so the lib builds standalone for wasm); it is a no-op on native targets.

Tests

  • cargo build -p whatsapp-rust --lib --release --target wasm32-unknown-unknown --no-default-features --features debug-diagnostics (with RUSTFLAGS='--cfg getrandom_backend="wasm_js"') — clean (was 1 error before fix 1, 6 errors after, 0 now)
  • cargo build --workspace --exclude e2e-tests (native) — clean
  • cargo clippy -p wacore -p whatsapp-rust --all-targets (native) — clean
  • cargo fmt --all

#668 added two uses that only compile on native and broke the wasm32
build that whatsapp-rust-bridge relies on. The `tokio` unresolved-module
error (E0433) aborts name resolution before type-checking, so it masked a
second regression behind it.

1. message.rs called `tokio::time::timeout` directly, which is absent under
   `--no-default-features` (wasm). Use the agnostic `wacore::runtime::timeout`
   over `self.runtime`, matching request/handshake/media_reupload.

2. the new `original_message_resolver` field holds a `dyn OriginalMessageResolver`,
   which is intentionally `?Send` on wasm (JS handles), making `Client` `!Send`.
   `SendContextResolver` and `EventHandler` required `Send + Sync`
   unconditionally, breaking every impl on wasm. Relax them to a conditional
   `wacore::sync_marker::MaybeSendSync` (still `Send + Sync` on native via a
   blanket impl, transparent there; dropped on wasm).

Add `.github/workflows/wasm.yml` building the lib for wasm32 in release,
mirroring the bridge's flags, so this class of regression fails CI.
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f247723c-188a-49c0-9398-9aacf65736c5

📥 Commits

Reviewing files that changed from the base of the PR and between 09fa7a6 and 71b89a5.

📒 Files selected for processing (1)
  • Cargo.toml

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added WebAssembly (WASM) build target so the app can run directly in web environments.
  • Infrastructure

    • CI workflow added to automatically build and validate WASM releases.
    • Platform-specific build/runtime settings configured for WASM targets.
  • Stability & Compatibility

    • Improved cross-platform compatibility and relaxed threading constraints for WASM to ensure smoother behavior in browsers.

Walkthrough

This PR adds WASM32 target support by introducing a conditional synchronization marker trait, applying it across the trait hierarchy to relax thread-safety requirements on WASM, integrating runtime-aware timeout abstractions, and configuring CI/CD and dependency management for WASM builds.

Changes

WASM32 Support with Conditional Synchronization

Layer / File(s) Summary
Conditional synchronization marker foundation
wacore/src/sync_marker.rs, wacore/src/lib.rs
New MaybeSendSync trait enforces Send + Sync on native targets but relaxes bounds on WASM32; exported from crate root for platform-specific trait bounds.
Apply conditional bounds to trait hierarchy
wacore/src/client/context.rs, wacore/src/types/events.rs
SendContextResolver and EventHandler traits updated to depend on MaybeSendSync instead of Send + Sync, allowing different platform safety requirements while preserving all method signatures.
Runtime-aware timeout abstraction
src/message.rs
resolve_msg_secret_via_app now uses wacore::runtime::timeout bound to client runtime instead of tokio::time::timeout, keeping existing result handling and timeout/logging behavior.
Build and CI infrastructure
.github/workflows/wasm.yml, Cargo.toml
New WASM Build CI workflow installs wasm32-unknown-unknown target and builds library in release mode; Cargo.toml configures getrandom backend for wasm32-unknown-unknown target.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Runtime as wacore::runtime
  participant Resolver as msg_secret_resolver
  Client->>Runtime: timeout(&*self.runtime, duration, lookup)
  Runtime->>Resolver: poll lookup future
  Resolver-->>Runtime: Ok(Some(secret)) / Ok(None) / Err
  Runtime-->>Client: propagate result (Some/None/timeout warning)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • oxidezap/whatsapp-rust#303: Adds concrete EventHandler implementations that depend on the EventHandler trait whose bounds this PR changes.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: a CI job for wasm32 builds and fixes for two regressions from PR #668.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing the wasm CI job addition and both regression fixes with technical justification.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/wasm-build-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Cargo.toml`:
- Around line 150-154: The getrandom dependency is configured with feature
"wasm_js" but the target gate uses target_arch = "wasm32", which is too broad
and will force wasm_js for non-browser wasm (e.g., wasm32-wasi) and can break
builds; change the target predicate in Cargo.toml to the specific
browser/unknown wasm triple(s) you support (e.g., cfg(target =
"wasm32-unknown-unknown")) or enumerate supported targets, or alternatively
document that this crate only supports browser wasm; also note that getrandom's
"wasm_js" is usually intended for binaries/tests because it pulls in
wasm-bindgen/js-sys if this crate is a library and consider moving the wasm_js
selection to binary-specific Cargo.toml sections instead of the library
dependency.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e67ae5a9-c15e-49c9-88d3-ec1e945c1395

📥 Commits

Reviewing files that changed from the base of the PR and between 714ea29 and 09fa7a6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • .github/workflows/wasm.yml
  • Cargo.toml
  • src/message.rs
  • wacore/src/client/context.rs
  • wacore/src/lib.rs
  • wacore/src/sync_marker.rs
  • wacore/src/types/events.rs

Comment thread Cargo.toml Outdated
cfg(target_arch = "wasm32") also matched wasm32-wasi/emscripten, forcing the
browser backend (and wasm-bindgen/js-sys) onto targets that have their own
getrandom backend. Gate to the unknown-unknown triple per getrandom's guidance;
it's the only target without a default backend, where wasm_js is the sole valid
choice anyway.
@jlucaso1
jlucaso1 merged commit 3019778 into main Jun 1, 2026
11 checks passed
@jlucaso1
jlucaso1 deleted the ci/wasm-build-guard branch June 1, 2026 01:34
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

Benchmark Results

67 unchanged benchmark(s)
Benchmark Current Baseline Change
reporting_token_benchmark::content_extraction_group::bench_content_extraction simple:setup_simple_message() 3,933 3,933 +0.0%
reporting_token_benchmark::content_extraction_group::bench_content_extraction extended:setup_extended_message() 12,038 12,038 +0.0%
reporting_token_benchmark::key_derivation_group::bench_key_derivation 43,514 43,514 +0.0%
reporting_token_benchmark::token_calculation_group::bench_token_calculation 19,365 19,365 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation simple:setup_full_gen_simple() 68,579 68,579 +0.0%
reporting_token_benchmark::full_generation_group::bench_full_token_generation extended:setup_full_gen_extended() 76,679 76,679 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding simple:setup_simple_message() 2,230 2,230 +0.0%
reporting_token_benchmark::message_encoding_group::bench_message_encoding extended:setup_extended_message() 5,988 5,988 +0.0%
send_receive_benchmark::dm_send::bench_dm_send text:setup_dm_send() 183,212 183,231 -0.0%
send_receive_benchmark::dm_recv::bench_dm_recv text:setup_dm_recv() 2,204,889 2,204,889 +0.0%
send_receive_benchmark::group_send::bench_group_send group_10:setup_group_send_10() 888,659 888,680 -0.0%
send_receive_benchmark::group_send::bench_group_send group_50:setup_group_send_50() 1,027,839 1,026,879 +0.1%
send_receive_benchmark::group_send::bench_group_send group_256:setup_group_send_256() 1,760,234 1,760,945 -0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_10:setup_group_skdm_10() 1,131,604 1,131,700 -0.0%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_50:setup_group_skdm_50() 2,133,904 2,132,759 +0.1%
send_receive_benchmark::group_send_skdm::bench_group_send_skdm skdm_256:setup_group_skdm_256() 7,298,902 7,308,482 -0.1%
send_receive_benchmark::group_recv::bench_group_recv text:setup_group_recv() 12,652,016 12,765,154 -0.9%
binary_benchmark::marshal_group::bench_marshal_allocating 71,326 71,326 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_allocating 71,379 71,379 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_allocating 98,446 98,446 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer 78,826 78,826 +0.0%
binary_benchmark::marshal_group::bench_marshal_reusing_buffer_vec_writer 71,426 71,426 +0.0%
binary_benchmark::marshal_group::bench_marshal_long_string 7,593 7,593 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_long_string 7,636 7,636 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_long_string 9,348 9,348 +0.0%
binary_benchmark::marshal_group::bench_marshal_huge_bytes_allocating 530,583 530,583 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_huge_bytes_allocating 530,151 530,151 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_huge_bytes_allocating 531,506 531,506 +0.0%
binary_benchmark::marshal_group::bench_marshal_many_children_allocating 8,506,239 8,506,239 +0.0%
binary_benchmark::marshal_group::bench_marshal_auto_many_children_allocating 8,450,491 8,450,491 +0.0%
binary_benchmark::marshal_group::bench_marshal_exact_many_children_allocating 19,678,026 19,678,026 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal small:setup_small_marshaled() 2,468 2,468 +0.0%
binary_benchmark::unmarshal_group::bench_unmarshal large:setup_large_marshaled() 33,558 33,558 +0.0%
binary_benchmark::unpack_group::bench_unpack_uncompressed 787 787 +0.0%
binary_benchmark::unpack_group::bench_unpack_compressed 526,732 526,732 +0.0%
binary_benchmark::attr_parser_group::bench_attr_parser attr_lookup:setup_attr_marshaled() 4,986 4,986 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip small:setup_small_marshaled() 5,315 5,315 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip large:setup_large_marshaled() 61,874 61,874 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto small:setup_small_marshaled() 5,347 5,347 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_auto large:setup_large_marshaled() 61,942 61,942 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact small:setup_small_marshaled() 6,734 6,734 +0.0%
binary_benchmark::roundtrip_group::bench_roundtrip_exact large:setup_large_marshaled() 85,585 85,585 +0.0%
binary_benchmark::child_iteration_group::bench_get_children_by_tag 477,570 477,570 +0.0%
binary_benchmark::jid_optimization_group::bench_jid_to_owned_access jid_access:setup_jid_heavy_marshaled() 11,563 11,563 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u32 396 396 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u32 120 120 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_u64 439 439 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_u64 153 153 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_i64 499 499 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_i64 162 162 +0.0%
numeric_attr_benchmark::bench_group::bench_baseline_loop_100_u64 44,624 44,624 +0.0%
numeric_attr_benchmark::bench_group::bench_proposed_loop_100_u64 16,424 16,424 +0.0%
libsignal_benchmark::dm_group::bench_dm_session_establishment setup:setup_dm_users() 17,258,818 17,147,900 +0.6%
libsignal_benchmark::dm_group::bench_dm_encrypt_first_message first_msg:setup_dm_session() 157,179 157,179 +0.0%
libsignal_benchmark::dm_group::bench_dm_decrypt_first_message decrypt_prekey:setup_dm_with_first_message() 5,513,975 5,513,975 +0.0%
libsignal_benchmark::dm_group::bench_dm_encrypt_subsequent_message subsequent:setup_established_dm_session() 157,539 157,539 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_create_distribution_message create:setup_group_sender() 296,767 296,767 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_encrypt_message encrypt:setup_group_with_distribution() 706,282 706,282 +0.0%
libsignal_benchmark::group_messaging_group::bench_group_decrypt_message decrypt:setup_group_with_encrypted_message() 12,543,185 12,568,681 -0.2%
libsignal_benchmark::conversation_group::bench_full_dm_conversation full:setup_conversation_data() 27,409,767 27,576,100 -0.6%
libsignal_benchmark::signature_group::bench_signature_creation sign:setup_keypair_with_message() 3,467,011 3,467,011 +0.0%
libsignal_benchmark::signature_group::bench_signature_verification verify:setup_keypair_with_message() 125,773,383 126,273,823 -0.4%
libsignal_benchmark::signature_group::bench_key_generation keygen 2,830,452 2,830,452 +0.0%
libsignal_benchmark::session_optimization_group::bench_decrypt_with_previous_session previous_session:setup_with_archived_sessions() 46,566 46,566 +0.0%
libsignal_benchmark::session_optimization_group::bench_out_of_order_decryption out_of_order:setup_out_of_order_messages() 5,197,012 5,197,012 +0.0%
libsignal_benchmark::session_optimization_group::bench_promote_matching_session promote:setup_promote_matching_session() 360,648 360,648 +0.0%
libsignal_benchmark::session_optimization_group::bench_message_key_eviction eviction:setup_message_key_eviction() 14,255,917 14,255,917 +0.0%
No significant changes detected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant