Wasm problem with Sveltekit, Cloudflare Pages and Resvg.

I'm trying to implement dynamic opengraph images, and so far the most promising solution has been satori + Resvg on a server endpoint (turns into a cloudflare pages function). It works beautifully everywhere except deployed on Cloudflare. "CompileError: WebAssembly.instantiate(): Wasm code generation disallowed by embedder" I found this "workaround" which does not seem to work: https://github.com/cloudflare/workers-sdk/issues/1366 which defines a wasm Memory to a 512 pagesize to be able to work on Cloudflare.
The wasm package I'm trying to use is Resvg. which I import via fetch using Vite's ..wasm?url directive because Vite doesn't support ESM wasm modules especially in SSR.
I tried using vite plugins for directly importing wasm modules (like vite-plugin-wasm and also mentioned here https://github.com/vitejs/vite/issues/8882) with no success because then it throws a cannot find module 'wbg' error. I understand there are tons of factors frameworks and tools associated with this but if anyone can help me at all tha'd be amazing
20 Replies
James
James•9mo ago
While not directly a solution for you, I can confirm that I'm able to use resvg-wasm without issue in Workers when I import it directly via import wasm from '@resvg/resvg-wasm/index_bg.wasm' and do so in a couple projects. I don't know how/if that'll work when it interacts with Vite though.
boggin
boggin•9mo ago
Thank you, when I try that, i get some error with wbg which i know nothing about and it seems specific to how resvg is packing their wasm lemme try it and spit out the error
James
James•9mo ago
When using wrangler dev directly or via some build step with Vite? It works perfectly fine for me in a couple Workers projects, though I'm just doing them directly and using wrangler or esbuild instead of another build step
boggin
boggin•9mo ago
yeah I get this error when importing '@resvg/resvg-wasm/index_bg.wasm'
Error: Cannot find module 'wbg' imported from 'C:/Users/.../node_modules/@resvg/resvg-wasm/index_bg.wasm'
Error: Cannot find module 'wbg' imported from 'C:/Users/.../node_modules/@resvg/resvg-wasm/index_bg.wasm'
I'm not using wrangler dev. just normal build with Vite, using the sveltekit adapter for cloudflare In dev. havent tried deploying that. but dont know why this error would disappear
James
James•9mo ago
Hmm yeah okay I suspect this is some way that svelte/vite is bundling that wasm file. It definitely works fine in Workers directly, but something in the build chain here is doing something weird I'm not super familiar with Vite/Svelte internals to aid much further unfortunately, sorry
boggin
boggin•9mo ago
Well to know that you can successfully import that wasm in a normal Worker is an enormous north star so thank you. and regarding Resvg in your worker, are importing like this?:
import { Resvg, initWasm } from '@resvg/resvg-wasm';
import resvgwasm from '@resvg/resvg-wasm/index_bg.wasm'
import { Resvg, initWasm } from '@resvg/resvg-wasm';
import resvgwasm from '@resvg/resvg-wasm/index_bg.wasm'
James
James•9mo ago
Yeah, it was the example one I used in fact in a recent wasm import DX improvement I did, so I'm pretty familiar with it, haha: https://jross.me/improving-wasm-imports-with-wrangler-and-cloudflare-workers/ yep, pretty much exactly this
boggin
boggin•9mo ago
@James So setup a separate worker. Having problems :
import { Resvg, initWasm } from '@resvg/resvg-wasm';
import resvgwasm from '@resvg/resvg-wasm/index_bg.wasm'

export default {
async fetch(request: Request): Promise<Response> {

const reqBody = await request.text();

await initWasm(resvgwasm)

const resvg = new Resvg(reqBody, {
fitTo: {
mode: 'width',
value: 1200
}
});
const image = resvg.render();

return new Response(image.asPng(), {
headers: {
'content-type': 'image/png'
}
});
},
};
import { Resvg, initWasm } from '@resvg/resvg-wasm';
import resvgwasm from '@resvg/resvg-wasm/index_bg.wasm'

export default {
async fetch(request: Request): Promise<Response> {

const reqBody = await request.text();

await initWasm(resvgwasm)

const resvg = new Resvg(reqBody, {
fitTo: {
mode: 'width',
value: 1200
}
});
const image = resvg.render();

return new Response(image.asPng(), {
headers: {
'content-type': 'image/png'
}
});
},
};
I get an error :
[wrangler:err] AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:

(0, import_assert5.default)(prepareStackTrace !== void 0)

at getSourceMapper (C:\Users\...\node_modules\miniflare\dist\src\index.js:5749:30)
[wrangler:err] AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:

(0, import_assert5.default)(prepareStackTrace !== void 0)

at getSourceMapper (C:\Users\...\node_modules\miniflare\dist\src\index.js:5749:30)
This is my first time using a standalone worker, a wasm, and resvg It's importing the wasm module fine, but ...uh how did you use it? cuz Resvg's docs say to the @resvg/resvg-wasm/index.js file to use the Resvg constructor and to initiate the wasm. If you have experience using this, what did you do?
James
James•9mo ago
import { Resvg, initWasm } from '@resvg/resvg-wasm';

function wasmInit(wasm: WebAssembly.Module) {
return initWasm(wasm).catch((err) => {
if ('message' in err && err.message?.startsWith?.('Already initialized')) {
return;
}
throw err;
});
}


// and then inside a request handler:
const { default: resvgwasm } = await import('@resvg/resvg-wasm/index_bg.wasm');
await wasmInit(resvgwasm);
const resvg = new Resvg(svgString, {
fitTo: {
mode: 'width',
value: 512,
},
});
const pngData = resvg.render();
const pngBuffer = pngData.asPng();
const response = new Response(pngBuffer, {
headers: {
'content-type': 'image/png',
},
});
import { Resvg, initWasm } from '@resvg/resvg-wasm';

function wasmInit(wasm: WebAssembly.Module) {
return initWasm(wasm).catch((err) => {
if ('message' in err && err.message?.startsWith?.('Already initialized')) {
return;
}
throw err;
});
}


// and then inside a request handler:
const { default: resvgwasm } = await import('@resvg/resvg-wasm/index_bg.wasm');
await wasmInit(resvgwasm);
const resvg = new Resvg(svgString, {
fitTo: {
mode: 'width',
value: 512,
},
});
const pngData = resvg.render();
const pngBuffer = pngData.asPng();
const response = new Response(pngBuffer, {
headers: {
'content-type': 'image/png',
},
});
very similar to what you have, though I async loaded the wasm file The error you're seeing there is a current bug with wrangler and is unfortunately swallowing errors for you: https://github.com/cloudflare/workers-sdk/pull/4719
boggin
boggin•9mo ago
Ok well I'm assuming your code is working but I have other code problems. I'm assuming you're using this as a dynamic opengraph image generator right? if it's going into a meta tag, it has to be a url as far as I know, so how are you passing an SVG into the worker? putting the SVG would be too big for a search param, and I cant put satori into the worker because it exceeds the script limit on the worker. between satori and font buffers required for satori, I don't see how this would work. I really appreciate your time @James
James
James•9mo ago
I'm using it more to convert from SVG data I generate for QR codes, like at https://qrcodes.tools/, and then converting to PNG so I already have the SVG I need
boggin
boggin•9mo ago
oh I see. Using a smaller svg would make this doable probably. Think this is the nail in the coffin then
James
James•9mo ago
https://developers.cloudflare.com/browser-rendering is in beta, but perhaps trying to render as HTML first might work better for your use-case? I'm not sure I think others have got satori to work in a Worker though, hmm lemme see Perhaps this repo might be helpful? https://github.com/kvnang/workers-og Workers can be up to 10MB in size now, so Satori should be able to work fine
boggin
boggin•9mo ago
? for me it says it can't exceed 1MB gzipped
James
James•9mo ago
where do you see that? Pages build log? Ignore that Oh you do need to be on paid for 10MB
Want results from more Discord servers?
Add your server