@travetto/app
Version:
Application registration/management and run support.
107 lines (88 loc) • 3.12 kB
text/typescript
import { RootIndex } from '@travetto/manifest';
import { ShutdownManager, Class, ConcreteClass, GlobalEnv } from '@travetto/base';
import { DependencyRegistry, InjectionError } from '@travetto/di';
import { BindUtil, SchemaValidator } from '@travetto/schema';
import { Configuration } from '@travetto/config';
import { AppClass, ApplicationConfig } from './types';
/**
* Registration point for all applications. Generally invoked by using
* the `@Application` decorator, but can be used directly as well.
*/
class $ApplicationRegistry {
#applications = new Map<string, ApplicationConfig>();
async init(): Promise<void> {
await RootIndex.loadSource();
}
register(app: string, config: ApplicationConfig): void {
this.#applications.set(app, config);
}
/**
* Get application by name
*/
getByName(name: string): ApplicationConfig | undefined {
return this.#applications.get(name);
}
/**
* Get all applications
*/
getAll(): ApplicationConfig[] {
return Array.from(this.#applications.values());
}
/**
* Prepare parameters for usage
*/
prepareParams(name: string, args: string[]): unknown[] {
const config = this.#applications.get(name)!;
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const cleaned = BindUtil.coerceMethodParams(config.target! as Class, 'run', args);
SchemaValidator.validateMethod(config.target!, 'run', cleaned);
return cleaned;
}
/**
* Runs the application, by name
*/
async run(name: string, args: string[]): Promise<void> {
const config = this.#applications.get(name);
if (!config) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
throw new InjectionError('Application not found', { Ⲑid: name } as Class);
}
const cleaned = this.prepareParams(name, args);
console.log('Running application', {
name: config.name,
target: config.targetId
});
// Show manifest
console.log('Manifest', {
info: RootIndex.mainDigest(),
env: GlobalEnv.toJSON()
});
// Get instance of app class
const inst = DependencyRegistry.get(config.target!) ?
await DependencyRegistry.getInstance(config.target!) :
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
new (config.target! as ConcreteClass<AppClass>)();
const rootConfig = await DependencyRegistry.getInstance(Configuration);
// Show config
console.log('Config', await rootConfig.exportActive());
const ret = await inst.run(...cleaned);
const target = ret ?? inst;
if (target) {
if ('close' in target) {
ShutdownManager.onShutdown(target, target); // Tie shutdown into app close
}
if ('wait' in target) {
await target.wait(); // Wait for close signal
} else if ('on' in target) {
await new Promise<void>(res => target.on('close', res));
}
}
}
/**
* Clear all apps on reset
*/
onReset(): void {
this.#applications.clear();
}
}
export const ApplicationRegistry = new $ApplicationRegistry();