import {Service} from '../../../di/src/decorator/Service.ts' import {Logging} from '../service/logging/Logging.ts' import LifecycleUnit from './Unit.ts' import {container, make} from '../../../di/src/global.ts' import {DependencyKey} from '../../../di/src/type/DependencyKey.ts' import RunLevelErrorHandler from '../error/RunLevelErrorHandler.ts' import {Status} from '../const/status.ts' import Instantiable from '../../../di/src/type/Instantiable.ts' import {Collection} from '../collection/Collection.ts' import {path} from '../external/std.ts' /** * Central class for Daton applications. */ @Service() export default class Application { /** * Collection of LifecycleUnits instantiated by this application. * @type Collection */ protected instantiated_units: Collection = new Collection() constructor( protected logger: Logging, protected rleh: RunLevelErrorHandler, /** * Array of unit classes to run for this application. * @type Array> */ protected units: (Instantiable)[], ) {} /** * Use the IoC container to instantiate the given dependency key. * @param {DependencyKey} token */ make(token: DependencyKey) { return make(token) } /** * Get the IoC container. * @return Container */ container() { return container } /** * Launch the application. * @return Promise */ async up() { this.logger.info('Starting Daton...', true) for ( const unit_class of this.units ) { const unit = this.make(unit_class) this.instantiated_units.push(unit) await this.start_unit(unit) } } /** * Stop the application. * @return Promise */ async down() { } /** * Run the application. * @return Promise */ async run() { try { await this.up() await this.down() } catch (e) { await this.app_error(e) } } /** * Pass an error to the top-level error handler. * @param {Error} e */ async app_error(e: Error) { this.rleh.handle(e) } /** * Launch the given lifecycle unit. * @param {LifecycleUnit} unit */ protected async start_unit(unit: LifecycleUnit) { try { unit.status = Status.Starting this.logger.info(`Starting ${unit.constructor.name}...`) await unit.up() this.logger.verbose(`Successfully started ${unit.constructor.name}`) unit.status = Status.Running } catch (e) { unit.status = Status.Error this.logger.error(`Error encountered while starting ${unit.constructor.name}. Will attempt to proceed.`) this.logger.debug(e.message) this.logger.verbose(e) } } /** * Get the root directory of the application. * @type string */ get root() { return path.resolve('.') } /** * Get the root directory of application class definitions. * @type string */ get app_root() { return path.resolve('./app') } /** * Resolve the given path within the application's root. * @param {...string} parts * @return string */ path(...parts: string[]) { return path.resolve(this.root, ...parts) } /** * Resolve the given path within the application's class definition root. * @param {...string} parts * @return string */ app_path(...parts: string[]) { return path.resolve(this.app_root, ...parts) } }