import avvio , { Avvio , mixedInstance , Plugin } from 'avvio' ; import { kApp , kInstance , kScope } from './symbols' ; export type PluginType = Plugin < AppInstanceOpts , AppInstance > ; export type Lifecycle = '_preInit' | '_init' | '_postInit' | '_preHandler' | '_handler' | '_postHandler' | '_preRun' | '_run' | '_postRun' ; export enum PluginScope { PARENT , DEFAULT } export const LIFECYCLE = [ '_preInit' , '_init' , '_postInit' , '_preHandler' , '_handler' , '_postHandler' , '_preRun' , '_run' , '_postRun' , ] ; export class AppInstance { [ kApp ] : App ; } export class AppInstanceOpts { } export type Override = Avvio < AppInstance > [ 'override' ] ; export type Overrides = { [ name ] : Avvio < unknown > [ 'override' ] ; } ; /**
 * An App is the basic building block for a plugin system, it contains all core and lifecycle methods.
 *
 * **App Lifecycle**
 *
 *  setPreInit →  setInit →  setPostInit *
 * →  setPreHandler →  setHandler →  setPostHandler *
 * →  setPreRun →  setRun →  setPostRun export class App { protected _parent : App | undefined ; protected _apps : Map < string , App > ; protected _preInit : Map < string , Plugin < AppInstanceOpts , AppInstance > > ; protected _init : Map < string , Plugin < AppInstanceOpts , AppInstance > > ; protected _postInit : Map < string , Plugin < AppInstanceOpts , AppInstance > > ; protected _preHandler : Map < string , Plugin < AppInstanceOpts , AppInstance > > ; protected _handler : Map < string , Plugin < AppInstanceOpts , AppInstance > > ; protected _postHandler : Map < string , Plugin < AppInstanceOpts , AppInstance > > ; protected _preRun : Map < string , Plugin < AppInstanceOpts , AppInstance > > ; protected _run : Map < string , Plugin < AppInstanceOpts , AppInstance > > ; protected _postRun : Map < string , Plugin < AppInstanceOpts , AppInstance > > ; protected _instance : Avvio < AppInstance > ; protected _name : string ; protected _scope : PluginScope ; protected _overrides : Overrides ; protected _unnamedCounter : number ; opts : any ; constructor ( ) { this . _apps = new Map ( ) ; this . _preInit = new Map ( ) ; this . _init = new Map ( ) ; this . _postInit = new Map ( ) ; this . _preHandler = new Map ( ) ; this . _handler = new Map ( ) ; this . _postHandler = new Map ( ) ; this . _preRun = new Map ( ) ; this . _run = new Map ( ) ; this . _postRun = new Map ( ) ; this . _instance = avvio ( new AppInstance ( ) , { autostart : false } ) ; this . _name = 'Root' ; this . _scope = PluginScope . DEFAULT ; this . _overrides = { } ; this . opts = { } ; this . _unnamedCounter = 1 ; } get apps ( ) { return this . _apps ; } get init ( ) { return this . _init ; } get handler ( ) { return this . _handler ; } get run ( ) { return this . _run ; } get instance ( ) { return this . _instance ; } get name ( ) { return this . _name ; } set name ( newName ) { this . _name = newName ; } get scope ( ) { return this . _scope ; } set scope ( newScope ) { this . _scope = newScope ; } get parent ( ) { return this . _parent ; } get overrides ( ) { return this . _overrides ; } /**
     * Set's it's argument function as a hook during the  setPreInit point of the lifecycle
     *  param funcName param plugin setPreInit ( funcName , plugin ) { this . setHook ( '_preInit' , funcName , plugin ) ; } /**
     * Set's it's argument function as a hook during the  setInit point of the lifecycle
     *  param funcName param plugin setInit ( funcName , plugin ) { this . setHook ( '_init' , funcName , plugin ) ; } /**
     * Set's it's argument function as a hook during the  setPostInit point of the lifecycle
     *  param funcName param plugin setPostInit ( funcName , plugin ) { this . setHook ( '_postInit' , funcName , plugin ) ; } /**
     * Set's it's argument function as a hook during the  setPreHandler point of the lifecycle
     *  param funcName param plugin setPreHandler ( funcName , plugin ) { this . setHook ( '_preHandler' , funcName , plugin ) ; } /**
     * Set's it's argument function as a hook during the  setHandler point of the lifecycle
     *  param funcName param plugin setHandler ( funcName , plugin ) { this . setHook ( '_handler' , funcName , plugin ) ; } /**
     * Set's it's argument function as a hook during the  setPostHandler point of the lifecycle
     *  param funcName param plugin setPostHandler ( funcName , plugin ) { this . setHook ( '_postHandler' , funcName , plugin ) ; } /**
     * Set's it's argument function as a hook during the  setPreRun point of the lifecycle
     *  param funcName param plugin setPreRun ( funcName , plugin ) { this . setHook ( '_preRun' , funcName , plugin ) ; } /**
     * Set's it's argument function as a hook during the  setRun point of the lifecycle
     *  param funcName param plugin setRun ( funcName , plugin ) { this . setHook ( '_run' , funcName , plugin ) ; } /**
     * Set's it's argument function as a hook during the  setPostRun point of the lifecycle
     *  param funcName param plugin setPostRun ( funcName , plugin ) { this . setHook ( '_postRun' , funcName , plugin ) ; } param lifecycle param funcName removeHook ( lifecycle , funcName ) { if ( ! this [ lifecycle ] . has ( funcName ) ) { throw new Error ( `${ funcName } does not exist in ${ lifecycle }` ) ; } this [ lifecycle ] . delete ( funcName ) ; } param lifecycle param funcName param plugin setHook ( lifecycle , funcName , plugin ) { if ( this [ lifecycle ] . has ( funcName ) ) { throw new Error ( `${ funcName } already declared for ${ lifecycle }` ) ; } Object . defineProperty ( plugin , 'name' , { value : funcName } ) ; this [ lifecycle ] . set ( funcName , plugin ) ; } param app _setParent ( app ) { if ( this . _parent === undefined ) { this . _parent = app ; } else { throw new Error ( `App's parent has already been set, parents can only be set once` ) ; } } param name param newApp param opts addApp ( childApp , opts ? ) ; addApp ( name , childApp , opts ? ) ; addApp ( arg1 , arg2 ? , arg3 = { } ) { let name ; let newApp ; let opts ; if ( arg1 instanceof App && ( arg2 instanceof Object || arg2 === undefined ) ) { name = `UnnamedApp${ this . _unnamedCounter }` ; this . _unnamedCounter += 1 ; newApp = arg1 ; opts = arg2 ?? arg3 ; } else if ( typeof arg1 === 'string' && arg2 instanceof App && arg3 instanceof Object ) { name = arg1 ; newApp = arg2 as App ; opts = arg3 ; } else { throw new Error ( 'Invalid function signature for addApp()' ) ; } if ( this . _apps . has ( name ) || Object . values ( this . _apps ) . indexOf ( newApp ) !== - 1 ) { throw new Error ( `Child app ${ name } already exists` ) ; } newApp . name = name ; newApp . opts = opts ; newApp . _setParent ( this ) ; this . _apps . set ( name , newApp ) ; } getApp ( name ) { return this . apps . get ( name ) ; } param lifecycle returns getHookPlugin ( lifecycle ) { const childPlugins = Array . from ( this . apps . values ( ) ) . reduce ( ( previousPlugins , app ) => { const childPlugin = app . getHookPlugin ( lifecycle ) ; if ( childPlugin ) { previousPlugins . push ( childPlugin ) ; } return previousPlugins ; } , [ ] as Array < PluginType > ) ; if ( this [ lifecycle ] . size === 0 && childPlugins . length === 0 ) { return undefined ; } const appFunc = async ( instance , opts ) => { this [ lifecycle ] . forEach ( async ( pluginFunc ) => { if ( pluginFunc . prototype === undefined ) { pluginFunc . prototype = { } ; } pluginFunc . prototype [ kScope ] = PluginScope . PARENT ; pluginFunc . prototype [ kApp ] = this ; instance . use ( pluginFunc , opts ) ; } ) ; childPlugins . forEach ( ( childPlugin ) => { instance . use ( childPlugin , opts ) ; } ) ; } ; appFunc . prototype [ kApp ] = this ; appFunc . prototype [ kScope ] = this . scope ; Object . defineProperty ( appFunc , 'name' , { value : this . name } ) ; return appFunc ; } param opts async start ( opts ? ) { if ( this . _instance . started ) { throw new Error ( `App has been started already!` ) ; } this . _instance . override = this . override ; LIFECYCLE . forEach ( ( lifecycle ) => { const lifecyclePlugin = this . getHookPlugin ( lifecycle ) ; if ( lifecyclePlugin ) { Object . defineProperty ( lifecyclePlugin , 'name' , { value : lifecycle } ) ; this . _instance . use ( lifecyclePlugin , opts ) ; } } ) ; this . _instance . start ( ) ; await this . _instance . ready ( ) ; } param varName param override setCustomOverride ( varName , override ) { this . _overrides [ varName ] = override ; } param old param fn param opts returns protected override : Override = ( old , fn , opts ) => { let newInstance ; const parentApp = fn . prototype [ kApp ] ?? this ; if ( fn . prototype [ kScope ] === PluginScope . DEFAULT ) { if ( parentApp [ kInstance ] === undefined ) { parentApp [ kInstance ] = Object . create ( old ) ; Object . entries ( this . overrides ) . forEach ( ( [ varName , override ] ) => { if ( parentApp [ kInstance ] [ varName ] === undefined ) { return ; } parentApp [ kInstance ] [ varName ] = override ( parentApp [ kInstance ] [ varName ] , async ( instance , opts ) => { } , parentApp . opts ) ; } ) ; } newInstance = parentApp [ kInstance ] ; } else if ( fn . prototype [ kScope ] === PluginScope . PARENT ) { newInstance = old ; } else { throw new Error ( `Function scope is ${ fn . prototype [ kScope ] } for ${ fn }, but needs to be ${ PluginScope }` ) ; } newInstance [ kApp ] = parentApp ; return newInstance ; } ; }