import { IMiddlewareEvent , IMiddlewareHandler , IActionContext } from "../internal" ; type Omit < T , K > = Pick < T , Exclude < keyof T , K > > ; export interface IActionTrackingMiddleware2Call < TEnv > extends Readonly < IActionContext > { env : TEnv | undefined ; readonly parentCall ? : IActionTrackingMiddleware2Call < TEnv > ; } export interface IActionTrackingMiddleware2Hooks < TEnv > { filter ? : ( call : IActionTrackingMiddleware2Call < TEnv > ) => boolean ; onStart : ( call : IActionTrackingMiddleware2Call < TEnv > ) => void ; onFinish : ( call : IActionTrackingMiddleware2Call < TEnv > , error ? : any ) => void ; } class RunningAction { private flowsPending = 0 ; private running = true ; constructor ( public readonly hooks , readonly call ) { if ( hooks ) { hooks . onStart ( call ) ; } } finish ( error ? ) { if ( this . running ) { this . running = false ; if ( this . hooks ) { this . hooks . onFinish ( this . call , error ) ; } } } incFlowsPending ( ) { this . flowsPending ++ ; } decFlowsPending ( ) { this . flowsPending -- ; } get hasFlowsPending ( ) { return this . flowsPending > 0 ; } } param hooks returns export function createActionTrackingMiddleware2 < TEnv = any > ( middlewareHooks ) { const runningActions = new WeakMap < IMiddlewareEvent , RunningAction > ( ) ; return function actionTrackingMiddleware ( call , next ) { const parentRunningAction = call . parentActionEvent ? runningActions . get ( call . parentActionEvent ) : undefined ; if ( call . type === "action" ) { const newCall = { ... call , env : parentRunningAction && parentRunningAction . call . env , parentCall : parentRunningAction && parentRunningAction . call } ; const passesFilter = ! middlewareHooks . filter || middlewareHooks . filter ( newCall ) ; const hooks = passesFilter ? middlewareHooks : undefined ; const runningAction = new RunningAction ( hooks , newCall ) ; runningActions . set ( call , runningAction ) ; let res ; try { res = next ( call ) ; } catch ( e ) { runningAction . finish ( e ) ; throw e ; } if ( ! runningAction . hasFlowsPending ) { runningAction . finish ( ) ; } return res ; } else { if ( ! parentRunningAction ) { return next ( call ) ; } switch ( call . type ) { case "flow_spawn" : { parentRunningAction . incFlowsPending ( ) ; return next ( call ) ; } case "flow_resume" : case "flow_resume_error" : { return next ( call ) ; } case "flow_throw" : { const error = call . args [ 0 ] ; try { return next ( call ) ; } finally { parentRunningAction . decFlowsPending ( ) ; if ( ! parentRunningAction . hasFlowsPending ) { parentRunningAction . finish ( error ) ; } } } case "flow_return" : { try { return next ( call ) ; } finally { parentRunningAction . decFlowsPending ( ) ; if ( ! parentRunningAction . hasFlowsPending ) { parentRunningAction . finish ( ) ; } } } } } } ; }