import { BehaviorSubject , Observable , combineLatest , of } from 'rxjs' ; import { map , distinctUntilChanged , switchMap , debounceTime } from 'rxjs/operators' ; import { isDeepStrictEqual } from 'util' ; import { PluginName } from '../plugins' ; import { ServiceStatus , CoreStatus } from './types' ; import { getSummaryStatus } from './get_summary_status' ; interface Deps { core$ : Observable < CoreStatus > ; pluginDependencies : ReadonlyMap < PluginName , PluginName [ ] > ; } export class PluginsStatusService { private readonly pluginStatuses = new Map < PluginName , Observable < ServiceStatus > > ( ) ; private readonly update$ = new BehaviorSubject ( true ) ; private readonly defaultInheritedStatus$ : Observable < ServiceStatus > ; constructor ( private readonly deps ) { this . defaultInheritedStatus$ = this . deps . core$ . pipe ( map ( ( coreStatus ) => { return getSummaryStatus ( Object . entries ( coreStatus ) , { allAvailableSummary : `All dependencies are available` , } ) ; } ) ) ; } public set ( plugin , status$ ) { this . pluginStatuses . set ( plugin , status$ ) ; this . update$ . next ( true ) ; } public getAll$ ( ) { return this . getPluginStatuses$ ( [ ... this . deps . pluginDependencies . keys ( ) ] ) ; } public getDependenciesStatus$ ( plugin ) { const dependencies = this . deps . pluginDependencies . get ( plugin ) ; if ( ! dependencies ) { throw new Error ( `Unknown plugin: ${ plugin }` ) ; } return this . getPluginStatuses$ ( dependencies ) . pipe ( debounceTime ( 500 ) ) ; } public getDerivedStatus$ ( plugin ) { return this . update$ . pipe ( switchMap ( ( ) => { if ( this . anyCustomStatuses ( plugin ) ) { return combineLatest ( [ this . deps . core$ , this . getDependenciesStatus$ ( plugin ) ] ) . pipe ( map ( ( [ coreStatus , pluginStatuses ] ) => { return getSummaryStatus ( [ ... Object . entries ( coreStatus ) , ... Object . entries ( pluginStatuses ) ] , { allAvailableSummary : `All dependencies are available` , } ) ; } ) ) ; } else { return this . defaultInheritedStatus$ ; } } ) ) ; } private getPluginStatuses$ ( plugins ) { if ( plugins . length === 0 ) { return of ( { } ) ; } return this . update$ . pipe ( switchMap ( ( ) => { const pluginStatuses = plugins . map ( ( depName ) => [ depName , this . pluginStatuses . get ( depName ) ?? this . getDerivedStatus$ ( depName ) ] as [ PluginName , Observable < ServiceStatus > ] ) . map ( ( [ pName , status$ ] ) => status$ . pipe ( map ( ( status ) => [ pName , status ] as [ PluginName , ServiceStatus ] ) ) ) ; return combineLatest ( pluginStatuses ) . pipe ( map ( ( statuses ) => Object . fromEntries ( statuses ) ) , distinctUntilChanged ( isDeepStrictEqual ) ) ; } ) ) ; } /**
     * Determines whether or not this plugin or any plugin in it's dependency tree have a custom status registered.
     */ private anyCustomStatuses ( plugin ) { if ( this . pluginStatuses . get ( plugin ) ) { return true ; } return this . deps . pluginDependencies . get ( plugin ) ! . reduce ( ( acc , depName ) => acc || this . anyCustomStatuses ( depName ) , false as boolean ) ; } }