import { Subscription } from "rxjs" ; import { filter , take } from "rxjs/operators" ; import { CommunicationManager , Controller , CoreTypes , Identity , OperatingState } from ".." ; import { IController , IControllerStatic } from "../controller/controller" ; import { Configuration , ControllerOptions } from "./configuration" ; import { Runtime } from "./runtime" ; /**
 * Defines the application-specific container components to be registered with a
 * Coaty Container.
 *
 * The configuration options for the container component classes are specified
 * in the `controllers` options of a Configuration object.
 */ export interface Components { /**
     * Application-specific controller classes to be registered with the
     * container (optional).
     *
     * The configuration options for a controller class listed here are
     * specified in the controller configuration under a key that matches the
     * associated name of the controller.
     */ controllers ? : { [ controllerName ] : IControllerStatic < Controller > ; } ; } /**
 * An IoC container that uses constructor dependency injection to create
 * container components and to resolve dependencies. This container defines the
 * entry and exit points for any Coaty application providing lifecycle
 * management for its components.
 */ export class Container { private _identity : Identity ; private _runtime : Runtime ; private _comManager : CommunicationManager ; private _controllers = new Map < string , [ IController , IControllerStatic < IController > ] > ( ) ; private _isShutdown = false ; private _operatingStateSubscription : Subscription ; param components param configuration param configTransformer returns throws static resolve ( components , configuration , configTransformer ? ) { const container = new Container ( ) ; if ( configTransformer ) { configuration = configTransformer ( configuration ) ; } container . _resolveComponents ( components || { } , configuration ) ; return container ; } param components param configuration param configTransformer returns static resolveAsync ( components , configuration , configTransformer ? ) { return new Promise < Container > ( ( resolve , reject ) => { configuration . then ( config => { resolve ( Container . resolve ( components , config , configTransformer ) ) ; } , reason => { reject ( new Error ( `Couldn't fetch async configuration: ${ reason }` ) ) ; } ) ; } ) ; } param controllerName param controllerType param controllerOptions returns registerController < T extends IController > ( controllerName , controllerType , controllerOptions ? ) { if ( this . _isShutdown ) { return ; } const ctrl = this . _resolveController ( controllerName , controllerType , controllerOptions ) ; if ( ctrl ) { ctrl . onInit ( ) ; this . _comManager . observeOperatingState ( ) . pipe ( take ( 1 ) ) . subscribe ( opState => { if ( opState === OperatingState . Started ) { this . _operatingStateCallback ( OperatingState . Started , ctrl ) ; } } ) ; } return ctrl as T ; } /**
     * Gets the identity object of this container.
     *
     * The identity can be initialized in the common configuration option
     * `agentIdentity`.
     */ get identity ( ) : Readonly < Identity > { return this . _identity ; } /**
     * Gets the runtime object of this container.
     */ get runtime ( ) { return this . _runtime ; } /**
     * Gets the communication manager of this container.
     */ get communicationManager ( ) { return this . _comManager ; } param controllerName getController < T extends IController > ( controllerName ) { if ( this . _controllers ) { const ctrl = this . _controllers . get ( controllerName ) ; if ( ctrl ) { return ctrl [ 0 ] as T ; } } return undefined ; } param callback mapControllers < T > ( callback ) { const results = [ ] ; this . _controllers ?. forEach ( ( value , name ) => results . push ( callback ( name , value [ 1 ] , value [ 0 ] ) ) ) ; return results ; } /**
     * The exit point for a Coaty applicaton. Releases all registered container
     * components and its associated system resources. This container should no
     * longer be used afterwards.
     */ shutdown ( ) { if ( this . _isShutdown ) { return ; } this . _isShutdown = true ; this . _releaseComponents ( ) ; } private _createIdentity ( props ) { const defaultIdentity = { objectId : Runtime . newUuid ( ) , name : "Coaty Agent" , } ; return Object . assign ( defaultIdentity , props || { } , { objectType : CoreTypes . OBJECT_TYPE_IDENTITY , coreType : "Identity" , } ) as Identity ; } private _resolveComponents ( components , config ) { if ( ! config ) { throw new Error ( "Container: Configuration is not defined" ) ; } if ( ! config . communication ) { throw new Error ( "Container: Configuration communication options not defined" ) ; } this . _identity = this . _createIdentity ( config . common ?. agentIdentity ) ; this . _runtime = new Runtime ( config . common , config . databases ) ; const comManager = this . _comManager = new CommunicationManager ( this , config . communication ) ; components . controllers && Object . keys ( components . controllers ) . forEach ( controllerName => { const controllerType = components . controllers [ controllerName ] ; const controllerOptions = ( config . controllers && config . controllers [ controllerName ] ) ; this . _resolveController ( controllerName , controllerType , controllerOptions ) ; } ) ; this . _controllers ?. forEach ( ctrl => ctrl [ 0 ] . onInit ( ) ) ; this . _operatingStateSubscription = comManager . observeOperatingState ( ) . pipe ( filter ( ( state , index ) => index > 0 || state !== OperatingState . Stopped ) ) . subscribe ( state => this . _controllers ?. forEach ( ctrl => this . _operatingStateCallback ( state , ctrl [ 0 ] ) ) ) ; if ( config . communication . shouldAutoStart ) { comManager . start ( ) ; } } private _resolveController < T extends IController > ( controllerName , controllerType , controllerOptions ) { if ( controllerName && controllerType ) { const ctrl = new controllerType ( this , controllerOptions || { } , controllerName ) ; this . _controllers . set ( controllerName , [ ctrl , controllerType ] ) ; return ctrl ; } return undefined ; } private _releaseComponents ( ) { this . _comManager . onDispose ( ) ; this . _controllers . forEach ( ctrl => ctrl [ 0 ] . onDispose ( ) ) ; this . _operatingStateSubscription ?. unsubscribe ( ) ; this . _controllers = undefined ; this . _comManager = undefined ; this . _runtime = undefined ; this . _identity = undefined ; } private _operatingStateCallback ( state , ctrl ) { switch ( state ) { case OperatingState . Started : ctrl . onCommunicationManagerStarting ( ) ; break ; case OperatingState . Stopped : ctrl . onCommunicationManagerStopping ( ) ; break ; default : break ; } } }