@@ -3,7 +3,7 @@ import { mkdir, rm, symlink } from 'node:fs/promises';
33
44import { fileURLToPath } from 'node:url' ;
55
6- import { suite , test , assert } from 'vitest' ;
6+ import { suite , test , assert , expect } from 'vitest' ;
77
88import { transpile } from '../src/index.js' ;
99
@@ -251,4 +251,55 @@ suite('Host Import Async (JSPI)', () => {
251251 await rm ( outFile ) ;
252252 } catch { }
253253 } ) ;
254+
255+ // A failing JSPI-suspended import must trap the guest and reject the
256+ // export, rather than leave the guest suspended forever.
257+ test . concurrent ( 'Transpile async, import rejects (NodeJS, JSPI)' , async ( ) => {
258+ if ( typeof WebAssembly ?. Suspending !== 'function' ) {
259+ return ;
260+ }
261+ const tmpDir = await getTmpDir ( ) ;
262+ const modulesDir = resolve ( tmpDir , 'node_modules' , '@bytecodealliance' ) ;
263+ await mkdir ( modulesDir , { recursive : true } ) ;
264+ await symlink (
265+ fileURLToPath ( new URL ( '../packages/preview2-shim' , import . meta. url ) ) ,
266+ resolve ( modulesDir , 'preview2-shim' ) ,
267+ 'dir' ,
268+ ) ;
269+
270+ const inducedError = new Error ( 'induced host rejection' ) ;
271+ const { instance, cleanup } = await setupAsyncTest ( {
272+ asyncMode : 'jspi' ,
273+ component : {
274+ name : 'async_call' ,
275+ path : resolve ( 'test/fixtures/components/runtime/async_call.component.wasm' ) ,
276+ imports : {
277+ 'something:test/test-interface' : {
278+ callAsync : async ( ) => {
279+ throw inducedError ;
280+ } ,
281+ callSync : ( ) => 'called sync' ,
282+ } ,
283+ } ,
284+ } ,
285+ jco : {
286+ transpile : {
287+ extraArgs : {
288+ asyncImports : [ 'something:test/test-interface#call-async' ] ,
289+ asyncExports : [ 'run-async' ] ,
290+ } ,
291+ } ,
292+ } ,
293+ } ) ;
294+
295+ try {
296+ const exportCall = instance . runAsync ( ) ;
297+ const timeout = new Promise ( ( _ , reject ) =>
298+ setTimeout ( ( ) => reject ( new Error ( 'export call timed out' ) ) , 5_000 ) ,
299+ ) ;
300+ await expect ( Promise . race ( [ exportCall , timeout ] ) ) . rejects . toBe ( inducedError ) ;
301+ } finally {
302+ await cleanup ( ) ;
303+ }
304+ } ) ;
254305} ) ;
0 commit comments