Webassembly in libraries not correctly loading

I am currently working on a project using the npm package "@dimforge/rapier2d". The package can be used in two ways, either using a bundler that resolves the .wasm dependency it is using internally or using the package "@dimforge/rapier2d" where it loads itself from a base64 string. I am currently using the first version. The code is shared between a frontend vite project and the cloudflare worker project. It works flawlessly with vite. The recommended way of loading the library is using import("@dimforge/rapier2d") where it internally loads the .wasm file using import * as wasm from './rapier_wasm2d_bg.wasm'; (the wasm file does reside in the moment of compilation in the node_modules directory). When I try to use this library with cloudflare workers it seems to resolve the promise import("@dimforge/rapier2d") but webassembly functions are not correctly defined and I get an is not a function error for some internal (in rapier) intergration function. I also previously tried to use the compat package but only get a blocked by embeddeder error, because WebAssebmly.instantiate([...]) is not allowed for manual loaded data. How can I correctly load the webassembly parts from packages I am using or disable the eval content security policy?
3 Replies
Phisn
PhisnOP16mo ago
Update: Wrangler seems to handle the package incorrectly. At least the bundled code does look abviously wrong without any WebAssembly.intiate call. Question is why vite is able to produce correct output and how I can fix the wrangler output.
Phisn
PhisnOP16mo ago
Solution: Found a solution to my problem. Probably the most hacky thing ever but works perfectly and there seems currently to not be a better solution. Rapier is build using wasm-bindgen for js so this applies to my problem https://developers.cloudflare.com/workers/runtime-apis/webassembly/rust/#javascript-plumbing-wasm-bindgen. Its said that we get a Module instead of an Instance and have therefore to adjust the processing that is normally done by bindgen. Problem is, that the proposed solution does not work because of how vite handles wasm files. Therefor my solution was to inject a __setWasm into the _bg file and set it somewhere inside my cloudflare worker. This way the compilation for vite stays uneffected. I am using the following script as "postinstall" in my package.json:
Rust WebAssembly guide · Cloudflare Workers docs
By following this guide, you will learn how to build a Worker entirely in the Rust programming language. You will accomplish this using the workers-rs …
Phisn
PhisnOP16mo ago
import path from 'path';
import * as fs from 'fs';

// script to fix rust bindings generated by wasm-bindgen because they are incompatible with cloudflare workers.
// see: https://developers.cloudflare.com/workers/runtime-apis/webassembly/rust/#javascript-plumbing-wasm-bindgen

const scriptHead = "//! modified by custom application postprocess !"

function transformScript() {
const scriptBg = path.resolve('./node_modules/@dimforge/rapier2d/rapier_wasm2d_bg.js')
const linesScriptBg = fs.readFileSync(scriptBg, 'utf8').split('\n')

// line 0: import * as wasm from './rapier_wasm2d_bg.wasm';
const [head, ...withoutWasmImport] = linesScriptBg

if (head === scriptHead) {
return
}

const withCustomWasm = [
scriptHead,
"let wasm",
"export function __setWasm(newWasm) {",
" wasm = newWasm;",
"}",
...withoutWasmImport,
]

fs.writeFileSync(scriptBg, withCustomWasm.join('\n'))
}

function transformScriptTypes() {
const scriptTypes = path.resolve('./node_modules/@dimforge/rapier2d/rapier_wasm2d_bg.d.ts')

if (fs.existsSync(scriptTypes)) {
return
}

const lines = [
scriptHead,
"export function __setWasm(newWasm: WebAssembly.Exports)"
]

fs.writeFileSync(scriptTypes, lines.join('\n'))
}

transformScript()
transformScriptTypes()
import path from 'path';
import * as fs from 'fs';

// script to fix rust bindings generated by wasm-bindgen because they are incompatible with cloudflare workers.
// see: https://developers.cloudflare.com/workers/runtime-apis/webassembly/rust/#javascript-plumbing-wasm-bindgen

const scriptHead = "//! modified by custom application postprocess !"

function transformScript() {
const scriptBg = path.resolve('./node_modules/@dimforge/rapier2d/rapier_wasm2d_bg.js')
const linesScriptBg = fs.readFileSync(scriptBg, 'utf8').split('\n')

// line 0: import * as wasm from './rapier_wasm2d_bg.wasm';
const [head, ...withoutWasmImport] = linesScriptBg

if (head === scriptHead) {
return
}

const withCustomWasm = [
scriptHead,
"let wasm",
"export function __setWasm(newWasm) {",
" wasm = newWasm;",
"}",
...withoutWasmImport,
]

fs.writeFileSync(scriptBg, withCustomWasm.join('\n'))
}

function transformScriptTypes() {
const scriptTypes = path.resolve('./node_modules/@dimforge/rapier2d/rapier_wasm2d_bg.d.ts')

if (fs.existsSync(scriptTypes)) {
return
}

const lines = [
scriptHead,
"export function __setWasm(newWasm: WebAssembly.Exports)"
]

fs.writeFileSync(scriptTypes, lines.join('\n'))
}

transformScript()
transformScriptTypes()
Now I can simply import the wasm file inside my cloudflare worker:
// initialize rapier wasm special for cloudflare workers
import * as imports from "@dimforge/rapier2d/rapier_wasm2d_bg"
import _wasm from "../node_modules/@dimforge/rapier2d/rapier_wasm2d_bg.wasm"

imports.__setWasm(new WebAssembly.Instance(_wasm, { "./rapier_wasm2d_bg.js": imports }).exports)
// initialize rapier wasm special for cloudflare workers
import * as imports from "@dimforge/rapier2d/rapier_wasm2d_bg"
import _wasm from "../node_modules/@dimforge/rapier2d/rapier_wasm2d_bg.wasm"

imports.__setWasm(new WebAssembly.Instance(_wasm, { "./rapier_wasm2d_bg.js": imports }).exports)
Want results from more Discord servers?
Add your server