import { FrameCategory , FrameCategoryModel , FrameData , FrameEnd , FrameStart , FrameType } from './types' ; export abstract class StopwatchStore { public frameQueue : ( FrameStart | FrameEnd ) [ ] = [ ] ; public dataQueue : FrameData [ ] = [ ] ; protected sync ( ) { } abstract run < T > ( data , cb ) ; abstract getZone ( ) ; data ( data ) { this . dataQueue . push ( data ) ; this . sync ( ) ; } add ( frame ) { this . frameQueue . push ( frame ) ; this . sync ( ) ; return 0 ; } } export interface StopwatchFrameInterface < C extends FrameCategory & keyof FrameCategoryModel > { data ( data ) : void ; end ( ) : void ; run < T > ( data , cb ) : Promise < T > ; } export class StopwatchFrame < C extends FrameCategory & keyof FrameCategoryModel > implements StopwatchFrameInterface < C > { protected stopped = false ; constructor ( protected store , public context , public category , public id , public worker ) { } data ( data ) { this . store . data ( { id : this . id , category : this . category , worker : this . worker , data } ) ; } end ( ) { if ( this . stopped ) return ; this . stopped = true ; this . store . add ( { id : this . id , type : FrameType . end , worker : this . worker , timestamp : 0 } ) ; } run < T > ( data , cb ) { data . stopwatchContextId = this . context ; return this . store . run ( data , cb ) ; } } export class NoopStopwatchFrame < C extends FrameCategory & keyof FrameCategoryModel > implements StopwatchFrameInterface < C > { data ( data ) { } end ( ) { } run < T > ( data , cb ) { return cb ( ) ; } } let frameId = 0 ; let contextId = 0 ; export class Stopwatch { /**
     * It's active when there is a StopwatchStore attached.
     * Per default its inactive.
     */ public active = false ; constructor ( protected store ? ) { this . active = this . store !== undefined ; } /**
     * Please check Stopwatch.active before using this method.
     *
     * When a new context is created, it's important to use StopwatchFrame.run() so that all
     * sub frames are correctly assigned to the new context.
     */ public start < C extends FrameCategory & keyof FrameCategoryModel > ( label , category = FrameCategory . none as C , newContext = false ) { if ( ! this . active || ! this . store ) throw new Error ( 'Stopwatch not active' ) ; let context = 0 ; const zone = this . store . getZone ( ) ; if ( newContext ) { context = ++ contextId ; } else { if ( ! zone ) return new NoopStopwatchFrame ( ) ; context = zone . stopwatchContextId ; if ( ! context ) throw new Error ( 'No Stopwatch context given' ) ; } const id = ++ frameId ; const worker = this . store . add ( { id , type : FrameType . start , worker : 0 , category , context : context , label , timestamp : 0 , } ) ; return new StopwatchFrame ( this . store , context , category , id , worker ) ; } }