Skip to content

Load wasm files at runtime instead of inlining as base64#16

Open
jlucaso1 wants to merge 1 commit into
masterfrom
feat/wasm-runtime-load
Open

Load wasm files at runtime instead of inlining as base64#16
jlucaso1 wants to merge 1 commit into
masterfrom
feat/wasm-runtime-load

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Drops the bun build macro that embedded both wasm variants as base64 string constants in dist/index.js. Wasms are now emitted straight into dist/ and loaded at startup via readFileSync(new URL(name, import.meta.url)).

Why

  • Base64-as-JS-constant kept ~1.4 MB of strings permanently resident in the JS heap (constants don't get GC'd). After WebAssembly.Module compiles raw bytes, the Buffer is collectable.
  • 33% size overhead from base64 encoding goes away.

Numbers

before after
dist/index.js 2.0 MB 71 KB
tarball compressed 837 KB 668 KB
tarball unpacked 2.04 MB 1.6 MB

Test plan

  • bun test — 163 pass / 0 fail
  • cargo fmt --check, cargo clippy -D warnings — clean
  • Tarball install + import in a fresh dir works (SIMD path picked correctly)

Summary by CodeRabbit

  • Refactor

    • Optimized WebAssembly module loading and initialization process.
    • Restructured build output and artifact handling.
  • Chores

    • Updated build configuration and project settings.
    • Modified build script to support variant WebAssembly artifacts.

The previous loader used a Bun build-time macro to embed both wasm
variants as base64 string constants in the bundled `dist/index.js`,
roughly tripling the JS bundle and keeping ~1.4 MB of base64 strings
permanently resident in the JS heap (constants don't get collected).

Switch to runtime loading: the build emits `simd.wasm` / `nosimd.wasm`
straight into `dist/`, and `ts/index.ts` reads whichever variant the
SIMD probe selects via `readFileSync(new URL(name, import.meta.url))`.
After `WebAssembly.Module` compiles the bytes, the Buffer becomes
GC-eligible, so the steady-state JS heap drops by the size of the
chosen wasm.

Other effects:
- `dist/index.js` shrinks from ~2.0 MB to ~71 KB (28x smaller)
- npm tarball: 837 KB → 668 KB compressed; 2.04 MB → 1.6 MB unpacked
- `ts/macro.ts` and the `assets/wasm/` staging dir are gone
- `bun build` now needs `--target=node` so it doesn't stub `node:fs`
- consumer side requires Node/Bun (no browser support, same as before
  since the previous code already relied on `process.env`)
@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e923b27f-8561-4000-ac47-0df7ae9d9a77

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/wasm-runtime-load

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.

🧹 Nitpick comments (1)
ts/index.ts (1)

11-19: ⚡ Quick win

Surface the underlying error when WASM initialization fails.

tryInit catches and discards every error, so a missing/corrupt dist/whatsapp_rust_bridge_bg.simd.wasm (e.g., a packaging regression) silently falls back to the non-SIMD variant, and when both variants fail the thrown error carries no diagnostic context (no cause, no path, no original Error.message). For a published library this makes field debugging materially harder. Consider capturing the last error and threading it into the thrown Error, and optionally warning when SIMD was supported but its variant failed to load.

♻️ Proposed refactor preserving error context
-function tryInit(filename: string): boolean {
-  try {
-    const url = new URL(filename, import.meta.url);
-    initSync({ module: readFileSync(url) });
-    return true;
-  } catch {
-    return false;
-  }
-}
+function tryInit(filename: string): Error | null {
+  try {
+    const url = new URL(filename, import.meta.url);
+    initSync({ module: readFileSync(url) });
+    return null;
+  } catch (err) {
+    return err instanceof Error ? err : new Error(String(err));
+  }
+}
@@
-let simdUsed = false;
-if (simdSupported && tryInit("whatsapp_rust_bridge_bg.simd.wasm")) {
-  simdUsed = true;
-} else if (!tryInit("whatsapp_rust_bridge_bg.nosimd.wasm")) {
-  throw new Error(
-    "whatsapp-rust-bridge: failed to load WASM module (neither SIMD nor non-SIMD variant could be initialized)",
-  );
-}
+let simdUsed = false;
+let simdErr: Error | null = null;
+if (simdSupported) {
+  simdErr = tryInit("whatsapp_rust_bridge_bg.simd.wasm");
+  if (simdErr === null) {
+    simdUsed = true;
+  } else {
+    console.warn(
+      `whatsapp-rust-bridge: SIMD variant failed to initialize, falling back to non-SIMD: ${simdErr.message}`,
+    );
+  }
+}
+if (!simdUsed) {
+  const nosimdErr = tryInit("whatsapp_rust_bridge_bg.nosimd.wasm");
+  if (nosimdErr !== null) {
+    throw new Error(
+      "whatsapp-rust-bridge: failed to load WASM module (neither SIMD nor non-SIMD variant could be initialized)",
+      { cause: simdErr ?? nosimdErr },
+    );
+  }
+}

Also applies to: 28-34

🤖 Prompt for 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.

In `@ts/index.ts` around lines 11 - 19, The tryInit function currently swallows
all exceptions; change it to capture and preserve underlying errors: when
calling new URL/readFileSync/initSync inside tryInit, catch the thrown error
into a local variable (e.g., lastError) and rethrow or return with context
instead of discarding it—specifically, if you still want a boolean API, record
lastError and expose it (or throw a new Error that sets lastError as the cause)
so callers get the original message and stack; also, when a SIMD variant fails
but SIMD is supported, emit a warning via the existing logger mentioning the
failed wasm filename and lastError; update both tryInit occurrences (lines
shown) to follow this pattern and reference initSync, readFileSync, and new URL
to locate the code to modify.
🤖 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.

Nitpick comments:
In `@ts/index.ts`:
- Around line 11-19: The tryInit function currently swallows all exceptions;
change it to capture and preserve underlying errors: when calling new
URL/readFileSync/initSync inside tryInit, catch the thrown error into a local
variable (e.g., lastError) and rethrow or return with context instead of
discarding it—specifically, if you still want a boolean API, record lastError
and expose it (or throw a new Error that sets lastError as the cause) so callers
get the original message and stack; also, when a SIMD variant fails but SIMD is
supported, emit a warning via the existing logger mentioning the failed wasm
filename and lastError; update both tryInit occurrences (lines shown) to follow
this pattern and reference initSync, readFileSync, and new URL to locate the
code to modify.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78521ceb-265d-46a7-ba02-f098fb77ccab

📥 Commits

Reviewing files that changed from the base of the PR and between 63ef2aa and c96cb69.

📒 Files selected for processing (6)
  • .gitignore
  • package.json
  • scripts/build-wasm.mjs
  • ts/index.ts
  • ts/macro.ts
  • tsconfig.json
💤 Files with no reviewable changes (2)
  • .gitignore
  • ts/macro.ts

@jlucaso1 jlucaso1 marked this pull request as ready for review May 8, 2026 20:25

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 6 files

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant