justjake / quickjs-emscripten

Safely execute untrusted Javascript in your Javascript, and execute synchronous code that uses async functions
https://www.npmjs.com/package/quickjs-emscripten
Other
1.25k stars 94 forks source link

Cloudflare + QJS + async module loader. #188

Open qbz opened 1 month ago

qbz commented 1 month ago

Hello, I am struggling making async module loader when using QJS on CF. I need setModuleLoader to be async, but this requires ASYNCified WASM build as far as I understood. When trying this, I am getting TS error (also it doesn't work after ts-ignore):

import type { QuickJSAsyncWASMModule, QuickJSWASMModule } from 'quickjs-emscripten'
import { newQuickJSWASMModule, RELEASE_ASYNC as baseVariant, newVariant, newQuickJSAsyncWASMModule } from 'quickjs-emscripten'
import cloudflareWasmModule from './wasm/RELEASE_ASYNCIFY.wasm'

export function initQuickJS(): Promise<QuickJSAsyncWASMModule> {
  // TS error
  const cloudflareVariant = newVariant<QuickJSAsyncVariant>(baseVariant, {
    wasmModule: cloudflareWasmModule,
  })
  return newQuickJSAsyncWASMModule(cloudflareVariant)
}

export const QuickJSWasmModule = initQuickJS()

error:

Argument of type 'import("/Users/project/node_modules/.pnpm/@jitl+quickjs-ffi-types@0.29.2/node_modules/@jitl/quickjs-ffi-types/dist/index").QuickJSAsyncVariant' is not assignable to parameter of type 'import("/Users/project/node_modules/@jitl/quickjs-ffi-types/dist/index").QuickJSAsyncVariant'.
  The types returned by 'importFFI()' are incompatible between these types.
    Type 'Promise<new (module: import("/Users/project/node_modules/.pnpm/@jitl+quickjs-ffi-types@0.29.2/node_modules/@jitl/quickjs-ffi-types/dist/index").QuickJSAsyncEmscriptenModule) => import("/Users/project/node_modules/.pnpm/@jitl+quickjs-ffi-types@0.29.2/node_modules/@jitl/quickjs-ffi-ty...' is not assignable to type 'Promise<new (module: import("/Users/project/node_modules/@jitl/quickjs-ffi-types/dist/index").QuickJSAsyncEmscriptenModule) => import("/Users/project/node_modules/@jitl/quickjs-ffi-types/dist/index").QuickJSAsyncFFI>'.
      Type 'new (module: import("/Users/project/node_modules/.pnpm/@jitl+quickjs-ffi-types@0.29.2/node_modules/@jitl/quickjs-ffi-types/dist/index").QuickJSAsyncEmscriptenModule) => import("/Users/project/node_modules/.pnpm/@jitl+quickjs-ffi-types@0.29.2/node_modules/@jitl/quickjs-ffi-types/dist...' is not assignable to type 'new (module: import("/Users/project/node_modules/@jitl/quickjs-ffi-types/dist/index").QuickJSAsyncEmscriptenModule) => import("/Users/project/node_modules/@jitl/quickjs-ffi-types/dist/index").QuickJSAsyncFFI'.
....

Code above was working fine when it used SYNC versions of builds:

import type { QuickJSWASMModule } from 'quickjs-emscripten'
import { newQuickJSWASMModule, RELEASE_SYNC as baseVariant, newVariant } from 'quickjs-emscripten'
import cloudflareWasmModule from './wasm/RELEASE_SYNC.wasm'

export function initQuickJS(): Promise<QuickJSWASMModule> {
  const cloudflareVariant = newVariant(baseVariant, {
    wasmModule: cloudflareWasmModule,
  })
  return newQuickJSWASMModule(cloudflareVariant)
}

export const QuickJSWasmModule = initQuickJS()

Is it possible to run such thing on CF or not?

Thanks a lot for such a great project anyway!

justjake commented 1 month ago

The type error is confusing because it seems like there are two copies of @jitl/quickjs-ffi-types at different locations inside node_modules:

'import("/Users/project/node_modules/.pnpm/@jitl+quickjs-ffi-types@0.29.2/node_modules/@jitl/quickjs-ffi-types/dist/index").QuickJSAsyncVariant' 
'import("/Users/project/node_modules/                                                  @jitl/quickjs-ffi-types/dist/index").QuickJSAsyncVariant'

Is it possible there are two incompatible versions of the quickjs-emscripten packages installed and being mixed together? I'm not sure how pnpm works but I get the sense that it is "cutting edge" and can introduce some breakages in the packages it installs.

I am more interested in a runtime error, you say:

also it doesn't work after ts-ignore

I would like to know what you mean by "it doesn't work".

qbz commented 1 month ago

Thanks for quick response!

Hm, seems like you are right. I just tried to restart VSCode and suddenly types are okay. Not sure what was that, maybe pnpm stuff or VSCode. Anyway, when trying to run this on CloudFlare, I see this (that's what I meant by "it doesnt work"):

Screenshot 2024-07-21 at 7 00 56

I tried to google this, but honestly not much was found. Maybe you have any ideas?

Update: you were right. I had different version of quickjs-emscripten and wasm builds. After fixiting this, it doesn't stuck anymore! 🎉

Nevertheless, it looks like I am facing new problem (I can create another issue if you want). I tried to use it with { type: "module" } and now it returns {}. So, in short, this snippet returns 2:

const ctx = runtime.newContext()
const result = await ctx.evalCodeAsync('1 + 1')
console.log(ctx.dump(ctx.unwrapResult(result)))

and this returns just {}

const ctx = runtime.newContext()
const result = await ctx.evalCodeAsync('1 + 1', undefined, { type: "module" })
console.log(ctx.dump(ctx.unwrapResult(result)))

I have read constructor name of what it returns from sandbox and it is Module. Can you help me with reading the value I need? Thanks!

justjake commented 2 weeks ago

Module evaluation always returns a module or a promise of a module. You will need to set a global variable you can read later, call an injected callback function, or export the result from the module to retrieve the value