inline rules-engine WASM binary at build time#123
Open
bpapillon wants to merge 2 commits into
Open
Conversation
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.
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.
cbrady
approved these changes
May 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The wasm-bindgen-generated
dist/wasm/rulesengine.jsreads its.wasmsibling viafs.readFileSync(${__dirname}/...), which breaks under any downstream bundler that follows the require — webpack rewrites__dirnameto point inside the bundle output where no.wasmsibling exists. Repro in a Next.js consumer surfaces asENOENT: ... .next/dev/server/vendor-chunks/rulesengine_bg.wasmat 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.