import type { JsonValue } from "type-fest" ; import type { Worker as NodeWorker } from "worker_threads" ; type RemoteImpl = Record < string , ( ... args : any [ ] ) => Promise < any > > ; export type RemoteCall < O extends RemoteImpl , N extends keyof O = keyof O > = { exec < T extends keyof O = N > ( func , ... args ) : ReturnType < O [ N ] > ; terminate ( ) : Promise < void > ; } ; type OffscreenCanvas = any ; type Transferrable = JsonValue | ArrayBuffer | ImageBitmap | OffscreenCanvas | MessagePort ; export type Expose < I extends RemoteImpl > = ( impl : I ) => RemoteCall < I > ; export type WorkerApi < Impl extends RemoteImpl > = RemoteCall < Impl > ; export type Cmd = string | [ name : string , transferrable ? : Transferrable [ ] ] ; const REQUEST_MARK = "m$s" ; const RESPONSE_MARK = "m$r" ; type Request = [ mark : typeof REQUEST_MARK , id : number , cmd : Cmd , ... args : Transferrable [ ] ] ; type Response = [ mark : typeof RESPONSE_MARK , id : number , error : boolean , result : Transferrable ] ; export type Adapter < Ctx = any > = [ emit : ( ctx : Ctx , data : Response | Request , transferrable ? : Array < Transferrable > ) => void , listen : ( ctx : Ctx , fn : any ) => void , terminate : ( ctx : Ctx ) => Promise < void > ] ; export const createExpose = ( [ emit , listen ] ) => ( ctx , api ) => { listen ( ctx , ( ev ) => { if ( ev . data ?. [ 0 ] !== REQUEST_MARK ) { return ; } const [ , id , cmd , ... args ] = ev . data as Request ; const [ cmdName , transferrable ] = typeof cmd === "string" ? [ cmd , [ ] ] : cmd ; const func = api [ cmdName ] ; func ( ... args ) . then ( ( result ) => { emit ( ctx , [ RESPONSE_MARK , id , false , result ] , transferrable ) ; } ) . catch ( ( e ) => emit ( ctx , [ RESPONSE_MARK , id , true , e ?. stack ?? e ?. toString ( ) ] ) ) ; } ) ; } ; const _sentIdMap = new Map ( ) ; let _cnt = 0 ; const genId = ( ) => _cnt ++ ; export const createWrap = ( [ emit , listen , terminate ] ) => < Impl extends RemoteImpl > ( ctx ) : WorkerApi < Impl > => { listen ( ctx , ( ev ) => { if ( ev . data ?. [ 0 ] !== RESPONSE_MARK ) { return ; } const [ , id , error , result ] = ev . data as Response ; const obj = _sentIdMap . get ( id ) ; if ( obj == null ) return ; _sentIdMap . delete ( id ) ; obj [ error ? 1 : 0 ] ( result ) ; } ) ; return { terminate : ( ) => terminate ( ctx ) , exec ( cmd , ... args ) { const id = genId ( ) ; return new Promise ( ( resolve , reject ) => { _sentIdMap . set ( id , [ resolve , reject ] ) ; const [ cmdName , transferrable ] = typeof cmd === "string" ? [ cmd , [ ] ] : cmd ; const req = [ REQUEST_MARK , id , cmdName , ... args ] as Request ; emit ( ctx , req , transferrable ) ; } ) ; } , } ; } ;