import MP from './mp' ; file import { utils } from './shared' ; const { logger , getFunctionName , intercept } = utils ; /** Object describing handler that will be triggered for a given `type` of instrumentation */ interface InstrumentHandler { type : InstrumentHandlerType ; callback : InstrumentHandlerCallback ; } type InstrumentHandlerType = 'request' | 'error' | 'resource' ; type InstrumentHandlerCallback = ( data : any ) => void ; const handlers = { } ; const instrumented = { } ; /** Instruments given API */ function instrument ( type ) { const ctx = MP . instance ( ) . context ; if ( instrumented [ type ] ) { return ; } instrumented [ type ] = true ; switch ( type ) { case 'request' : instrumentRequest ( ctx ) ; break ; case 'resource' : instrumentResource ( ctx ) ; break ; case 'error' : instrumentConsoleError ( ) ; break ; default : logger . warn ( 'unknown instrumentation type:' , type ) ; } } hidden export function addInstrumentationHandler ( handler ) { if ( ! handler || typeof handler . type !== 'string' || typeof handler . callback !== 'function' ) { return ; } handlers [ handler . type ] = handlers [ handler . type ] || [ ] ; ( handlers [ handler . type ] as InstrumentHandlerCallback [ ] ) . push ( handler . callback ) ; instrument ( handler . type ) ; } /** JSDoc */ function triggerHandlers ( type , data ) { if ( ! type || ! handlers [ type ] ) { return ; } for ( const handler of handlers [ type ] || [ ] ) { try { handler ( data ) ; } catch ( e ) { logger . error ( `Error while triggering instrumentation handler.\nType: ${ type }\nName: ${ getFunctionName ( handler ) }\nError: ${ e }` ) ; } } } /** JSDoc */ function instrumentRequest ( ctx ) { intercept ( ctx , 'request' , ( originalRequest ) : ( ) => void => function ( requestOptions ? ) : void { const commonHandlerData = { ... requestOptions , startTimestamp : Date . now ( ) , } ; const originSuccess = requestOptions . success ; const originFail = requestOptions . fail ; requestOptions . success = ( response ) => { if ( originSuccess ) { originSuccess ( response ) ; } triggerHandlers ( 'request' , { ... commonHandlerData , endTimestamp : Date . now ( ) , response , } ) ; } ; requestOptions . fail = ( error ) => { if ( originFail ) { originFail ( error ) ; } triggerHandlers ( 'request' , { ... commonHandlerData , endTimestamp : Date . now ( ) , error , } ) ; } ; return originalRequest . call ( ctx , requestOptions ) ; } ) ; } /** JSDoc */ function instrumentConsoleError ( ) { intercept ( window . console , 'error' , ( originalConsole ) : ( ... args : any ) => void => function ( ... args ) : boolean { triggerHandlers ( 'error' , args ) ; return originalConsole . call ( window . console , args ) ; } ) ; } /** JSDoc */ function instrumentResource ( ctx ) { intercept ( ctx , 'downloadFile' , ( originalDownload ) : ( ) => void => function ( downloadOptions ? ) : void { const originSuccess = downloadOptions . success ; const originFail = downloadOptions . fail ; downloadOptions . success = ( res ) => { if ( originSuccess ) { originSuccess ( res ) ; } if ( res . statusCode !== 200 ) { triggerHandlers ( 'resource' , { statusCode : res . statusCode , errMsg : res . errMsg , url : downloadOptions . url || '' } ) ; } } ; downloadOptions . fail = ( error ) => { if ( originFail ) { originFail ( error ) ; } triggerHandlers ( 'resource' , { statusCode : - 1 , errMsg : error . errMsg , url : downloadOptions . url || '' } ) ; } ; return originalDownload . call ( ctx , downloadOptions ) ; } ) ; }