import { OpenYoloError , OpenYoloErrorType , OpenYoloExposedErrorData , OpenYoloInternalError } from '../protocol/errors' ; import { RpcMessageArgumentTypes , RpcMessageData , RpcMessageType } from '../protocol/rpc_messages' ; import { SecureChannel } from '../protocol/secure_channel' ; import { generateId , PromiseResolver , startTimeoutRacer , TimeoutRacer } from '../protocol/utils' ; import { ProviderFrameElement } from './provider_frame_elem' ; export type RpcMessageHandler < T extends RpcMessageType > = ( data : RpcMessageArgumentTypes [ T ] , type : T , ev : MessageEvent ) => void ; template T template O export interface RelayRequest < T , O > { /**
     * Sends the specific request to the relay, with the given options.
     */ dispatch ( options , timeoutRacer ? ) : Promise < T > ; } template ResultT template OptionsT export abstract class BaseRequest < ResultT , OptionsT > implements RelayRequest < ResultT , OptionsT > { private promiseResolver = new PromiseResolver < ResultT > ( ) ; private listenerKeys : number [ ] = [ ] ; private disposed = false ; private timeoutRacer : TimeoutRacer | null = null ; constructor ( protected frame , protected channel , public id = generateId ( ) ) { } async dispatch ( options , timeoutRacer ? ) { this . timeoutRacer = timeoutRacer || startTimeoutRacer ( 0 ) ; this . registerBaseHandlers ( ) ; this . dispatchInternal ( options ) ; try { return await this . timeoutRacer . race ( this . getPromise ( ) ) ; } catch ( error ) { this . timeoutRacer . rethrowUnlessTimeoutError ( error ) ; this . frame . hide ( ) ; this . dispose ( ) ; throw OpenYoloInternalError . requestTimeout ( ) . toExposedError ( ) ; } } /**
     * Method to implement in subclasses that handle the request.
     */ abstract dispatchInternal ( options ) ; /**
     * Registers the base handlers for the request. To be called by subclasses for
     * proper initialization.
     */ private registerBaseHandlers ( ) { this . registerHandler ( RpcMessageType . error , ( data ) => { let error ; if ( data ) { error = OpenYoloError . fromData ( data ) ; } else { error = OpenYoloInternalError . unknownError ( ) . toExposedError ( ) ; } this . reject ( error ) ; this . dispose ( ) ; } ) ; this . registerHandler ( RpcMessageType . showProvider , ( options ) => { this . clearTimeout ( ) ; this . frame . display ( options ) ; } ) ; } protected getPromise ( ) { return this . promiseResolver . promise ; } resolve ( response ? ) { this . promiseResolver . resolve ( response ) ; this . frame . hide ( ) ; } reject ( reason ) { this . promiseResolver . reject ( reason ) ; if ( reason . type !== OpenYoloErrorType . illegalConcurrentRequest ) { this . frame . hide ( ) ; } } /**
     * Registers a message handler for this request. It will forward messages
     * of the specified type that match the ID of this request.
     */ registerHandler < T extends RpcMessageType > ( type , handler ) { let filter = ( data , t , e ) : boolean => { if ( data . id !== this . id ) { return false ; } handler ( data . args , type , e ) ; return true ; } ; filter . toString = ( ) => `${ type } message handler` ; this . listenerKeys . push ( this . channel . listen ( type , filter ) ) ; } /**
     * Clears all listeners.
     */ protected clearListeners ( ) { this . listenerKeys . forEach ( ( key ) => { this . channel . unlisten ( key ) ; } ) ; this . listenerKeys = [ ] ; } /**
     * Clear the timeout racer.
     */ protected clearTimeout ( ) { if ( this . timeoutRacer !== null ) { this . timeoutRacer . stop ( ) ; } } /**
     * Clears timeouts and listeners.
     */ dispose ( ) { if ( this . disposed ) { return ; } this . clearTimeout ( ) ; this . clearListeners ( ) ; this . promiseResolver . dispose ( ) ; this . disposed = true ; } }