Compiling to a Single Binary
This page covers platformaticWasmPlugin(), the Bun bundler plugin required when compiling an application that imports any Kafka helper into a standalone bun build --compile executable.
The problem
A compiled binary crashes on startup if the application imports any Kafka helper and the build did not register the plugin:
ENOENT: no such file or directory, open '/$bunfs/dist/native.wasm'The crash happens before your code runs. It fires while the module graph is still loading - before the IGNIS application boots, so no log line, no lifecycle hook, and no error handler of yours ever executes.
Why it happens
The codecs are WebAssembly, not native bindings.
@platformatic/kafkacomputes Kafka's CRC32C checksums and lz4/snappy compression in WebAssembly, through@platformatic/wasm-utils.The default entrypoint reads the wasm payload from disk at module load time:
javascript// @platformatic/wasm-utils/dist/index.js const wasm = readFileSync(new URL('../dist/native.wasm', import.meta.url));bun build --compileembeds JavaScript modules only. Assets such asnative.wasmare never carried into the executable. Inside the binary,import.meta.urlresolves against the virtual/$bunfsfilesystem, the file isn't there, and the read throws.Running from source is unaffected.
bun run/bun .keepnode_moduleson disk, so the read succeeds - the bug only exists in compiled binaries.
The fix
- A second entrypoint ships the payload inline.
@platformatic/wasm-utils/bundledexposes the same API with the wasm payload inlined as base64 - no filesystem read. platformaticWasmPlugin()swaps one entrypoint for the other at bundle time, so the rest of your code and@platformatic/kafkaitself need no changes.- Compile through a Bun build script, not the CLI.
bun build --compileon the command line cannot register plugins:
// scripts/compile.ts
import { platformaticWasmPlugin } from '@venizia/ignis-helpers/kafka';
const built = await Bun.build({
entrypoints: ['./dist/index.js'],
target: 'bun',
minify: { whitespace: true, syntax: true },
sourcemap: 'linked',
compile: {
target: process.env.BUN_TARGET ?? 'bun-linux-x64',
outfile: './dist/bin',
},
plugins: [platformaticWasmPlugin()],
});
if (!built.success) {
console.error(built.logs);
process.exit(1);
}{
"scripts": {
"compile": "bun run ./scripts/compile.ts"
}
}- The plugin resolves relative to the importing module's own directory (
Bun.resolveSyncfromdirname(args.importer)), so it works with hoisted and isolatednode_moduleslayouts alike, and pins no package version.
Verifying
A correctly built binary contains no reference to the wasm file on disk:
grep -c 'native.wasm' ./dist/bin # 0 -- the payload is inlined
./dist/bin # boots instead of throwing ENOENTThe binary grows by roughly 76 KB, the base64 form of the 57 KB wasm module.
Notes
- Upgrading
@platformatic/kafkadoes not remove the need for the plugin. Every release to date imports the default@platformatic/wasm-utilsentrypoint. - Apps that never import a Kafka helper need no plugin. Nothing pulls in
@platformatic/wasm-utils, and the plugin's resolver never fires. - Patching
node_modulesduring the build achieves the same result, but mutates a dependency in place, pins the store path to one version, and leaves the tree dirty when a build fails. The plugin needs neither.
See also
- Kafka Overview - the four helpers this plugin protects
- Producer / Consumer / Admin - any of these importing into a compiled binary triggers this caveat
- Examples & Troubleshooting - other Kafka connection and configuration errors
Files:
packages/helpers/src/modules/queue/kafka/bundler/platformatic-wasm.plugin.ts-platformaticWasmPluginpackages/helpers/src/modules/queue/kafka/bundler/common/constants.ts-KafkaBundlerPluginNames,PlatformaticWasmSpecifiers