Skip to content

inline rules-engine WASM binary at build time#123

Open
bpapillon wants to merge 2 commits into
mainfrom
inline-wasm-binary-at-build-time
Open

inline rules-engine WASM binary at build time#123
bpapillon wants to merge 2 commits into
mainfrom
inline-wasm-binary-at-build-time

Conversation

@bpapillon
Copy link
Copy Markdown
Contributor

The wasm-bindgen-generated dist/wasm/rulesengine.js reads its .wasm sibling via fs.readFileSync(${__dirname}/...), which breaks under any downstream bundler that follows the require — webpack rewrites __dirname to point inside the bundle output where no .wasm sibling exists. Repro in a Next.js consumer surfaces as ENOENT: ... .next/dev/server/vendor-chunks/rulesengine_bg.wasm at SDK init, and the rules engine silently falls back to API-only (DataStream + credit-lease paths disable).

Build step base64-encodes the WASM binary into the loader at publish time so initialization no longer touches the filesystem.

Tarball delta: +138 KB (base64 overhead on the ~414 KB binary). Net consumer bundle size is unchanged when the WASM is actually used; tree-shaking gains for non-users are the same in either approach (require chain is reachable from the main entry point) — would require splitting credit-leases into a separate subpath export, out of scope here.

The wasm-bindgen-generated `dist/wasm/rulesengine.js` loads its `.wasm`
sibling at runtime via `fs.readFileSync(${__dirname}/rulesengine_bg.wasm)`.
That works in plain Node, but breaks the moment a downstream bundler
follows the require chain — webpack rewrites `__dirname` to point inside
the bundle output, where the `.wasm` sibling never gets copied. Symptom
in a Next.js consumer:

  ENOENT: no such file or directory, open
    '.next/dev/server/vendor-chunks/rulesengine_bg.wasm'

…and the SDK silently falls back to API-only checks, disabling DataStream
and credit-lease paths.

This adds a build step that reads the WASM binary, base64-encodes it,
and rewrites `dist/wasm/rulesengine.js` to instantiate from an inlined
`Buffer.from(BASE64, 'base64')` instead of touching the filesystem. The
standalone `.wasm` is then removed from `dist/` since nothing reads it
at runtime anymore.

Tarball delta is +138 KB (base64 overhead on the ~414 KB binary). For
consumers that use creditLeases / DataStream the net bundle size is
unchanged — the WASM bytes are in either form. For consumers that don't,
practical tree-shaking ends up the same regardless of inlining: the
require chain is reachable from the package's main entry point and the
WASM init runs as a module-level side effect. Materially reducing the
WASM cost for non-credit-lease consumers would require splitting credit
leases into a separate entry point — out of scope here.

If wasm-bindgen output ever stops matching the regex this script keys
on, the build throws with a clear pointer instead of silently shipping
a broken loader.
@bpapillon bpapillon marked this pull request as ready for review May 22, 2026 19:13
@bpapillon bpapillon requested a review from a team as a code owner May 22, 2026 19:13
The verify-package step asserted on `dist/wasm/rulesengine_bg.wasm`
presence, which the inlining step now intentionally removes. Replace
those checks with a content-sentinel grep on the inlined comment so a
future change that silently reverts to disk-based loading still fails
CI, plus an explicit assertion that the standalone .wasm is gone.
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.

2 participants