UNPKG

@hapiness/core

Version:

Project to have a HapiJS (https://hapijs.com/) based framework to create easier NodeJS back-end with some awesome features

255 lines 8.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const rxjs_1 = require("rxjs"); const logger_1 = require("./logger"); const enums_1 = require("./enums"); const module_1 = require("./module"); const hook_1 = require("./hook"); const shutdown_1 = require("./shutdown"); function extensionError(error, name) { error.message = `[${name}] ${error.message}`; return error; } class Hapiness { /** * Entrypoint to bootstrap a module * will load the extentions and trigger * module's hooks * * @param {Type<any>} module * @param {Array<Type<any>|ExtensionWithConfig>} extensions? * @param {BootstrapOptions} options? * @returns Promise */ static bootstrap(module, extensions, options = {}) { if (options.shutdown !== false) { this.handleShutdownSignals(); } return new Promise((resolve, reject) => { this .checkArg(module) .flatMap(_ => module_1.ModuleManager.resolve(_)) .flatMap(_ => this.loadExtensions(extensions, _, options)) .ignoreElements() .subscribe(null, _ => { this.logger.debug(`bootstrap error caught [${_.message}]`); this .shutdown() .subscribe(() => reject(_), err => { this.logger.debug(`bootstrap error caught [${err.message}], shutting down extensions...`); reject(err); process.exit(1); }); }, () => resolve()); }); } /** * Force a shutdown * * @returns Observable */ static shutdown() { return this .getShutdownHooks() .flatMap(_ => this .shutdownUtils .shutdown(_)); } static handleShutdownSignals() { this .shutdownUtils .events$ .flatMap(_ => this.shutdown()) .subscribe(_ => { this.logger.debug('process shutdown triggered'); process.exit(0); }, _ => { errorHandler(_); process.exit(1); }); } /** * Retrieve all shutdown hooks * * @returns ExtensionShutdown[] */ static getShutdownHooks() { return rxjs_1.Observable .from([].concat(this.extensions).filter(e => !!e)) .filter(_ => !!_ && hook_1.HookManager .hasLifecycleHook(enums_1.ExtentionHooksEnum.OnShutdown.toString(), _.token)) .flatMap(_ => hook_1.HookManager .triggerHook(enums_1.ExtentionHooksEnum.OnShutdown.toString(), _.token, _.instance, [module, _.value])) .toArray(); } /** * Load extensions * * @param {Array<Type<any>|ExtensionWithConfig>} extensions * @param {CoreModule} moduleResolved * @param {BootstrapOptions} options? * @returns Observable */ static loadExtensions(extensions, moduleResolved, options) { return rxjs_1.Observable .from([].concat(extensions).filter(_ => !!_)) .map(_ => this.toExtensionWithConfig(_)) .concatMap(_ => this .loadExtention(_, moduleResolved, options) .catch(err => rxjs_1.Observable.throw(extensionError(err, _.token.name)))) .toArray() .flatMap(_ => this.instantiateModule(_, moduleResolved, options)); } /** * Instantiate module * * @param {Extension[]} extensionsLoaded * @param {CoreModule} moduleResolved * @returns Observable */ static instantiateModule(extensionsLoaded, moduleResolved, options) { return rxjs_1.Observable .from(extensionsLoaded) .map(_ => ({ provide: _.token, useValue: _.value })) .toArray() .flatMap(_ => module_1.ModuleManager.instantiate(moduleResolved, _)) .flatMap(_ => this.callRegister(_)) .flatMap(moduleInstantiated => rxjs_1.Observable .from(extensionsLoaded) .flatMap(_ => this .moduleInstantiated(_, moduleInstantiated) .timeout(options.extensionTimeout || this.defaultTimeout) .catch(err => rxjs_1.Observable.throw(extensionError(err, _.token.name)))) .toArray() .map(_ => moduleInstantiated)) .do(_ => this.module = _) .flatMap(_ => this.callStart(_)); } /** * Call Register Hooks * * @param {CoreModule} moduleInstantiated * @returns Observable */ static callRegister(moduleInstantiated) { return rxjs_1.Observable .from(module_1.ModuleManager.getModules(moduleInstantiated)) .filter(_ => _.level !== enums_1.ModuleLevel.ROOT) .filter(_ => hook_1.HookManager .hasLifecycleHook(enums_1.ModuleEnum.OnRegister.toString(), _.token)) .flatMap(_ => hook_1.HookManager .triggerHook(enums_1.ModuleEnum.OnRegister.toString(), _.token, _.instance)) .toArray() .map(_ => moduleInstantiated); } /** * Call Start Hooks * * @param {CoreModule} moduleInstantiated * @returns Observable */ static callStart(moduleInstantiated) { return rxjs_1.Observable .of(moduleInstantiated) .flatMap(_ => hook_1.HookManager .triggerHook(enums_1.ModuleEnum.OnStart.toString(), moduleInstantiated.token, moduleInstantiated.instance, null, false)); } /** * Check if the provided module * is right * * @param {Type<any>} module * @returns Observable */ static checkArg(module) { return rxjs_1.Observable .of(module) .do(_ => this.module = null) .do(_ => this.extensions = null) .flatMap(_ => !!_ ? rxjs_1.Observable.of(_) : rxjs_1.Observable.throw(new Error('Bootstrap failed: no module provided'))) .flatMap(_ => typeof _ === 'function' ? rxjs_1.Observable.of(_) : rxjs_1.Observable.throw(new Error('Bootstrap failed: module must be a function/class'))); } /** * Convert an extension type to ExtensionWithConfig * * @param {} extension * @returns ExtensionWithConfig */ static toExtensionWithConfig(extension) { if (extension && extension.token) { return extension; } return { token: extension, config: {} }; } /** * Call the OnExtensionLoad hook * of an extension * * @param {ExtensionWithConfig} extension * @returns Observable */ static loadExtention(extension, module, options) { return rxjs_1.Observable .of(Reflect.construct(extension.token, [])) .do(_ => this.logger.debug(`loading ${extension.token.name}`)) .switchMap(instance => hook_1.HookManager .triggerHook(enums_1.ExtentionHooksEnum.OnExtensionLoad.toString(), extension.token, instance, [module, extension.config]) .timeout(options.extensionTimeout || this.defaultTimeout) .catch(_ => { if (process.env.NODE_ENV !== 'test') { setTimeout(() => process.exit(1), 1000); } return rxjs_1.Observable.throw(_); })) .do(_ => this.extensions = [] .concat(this.extensions, _) .filter(__ => !!__)); } /** * Call the OnModuleInstantiated hook * of an extension * * @param {Extension} extension * @returns Observable */ static moduleInstantiated(extension, module) { return hook_1.HookManager .triggerHook(enums_1.ExtentionHooksEnum.OnModuleInstantiated.toString(), extension.token, extension.instance, [module, extension.value]) .do(_ => this.logger.debug(`moduleInstantiated ${extension.token.name}`)) .defaultIfEmpty(null); } } Hapiness.logger = new logger_1.InternalLogger('bootstrap'); Hapiness.shutdownUtils = new shutdown_1.ShutdownUtils(); Hapiness.defaultTimeout = 5000; exports.Hapiness = Hapiness; /** * Error handler * Call onError of Root module * Or log in console * * @param {Error} error * @param {any} data * @returns void */ function errorHandler(error, data) { rxjs_1.Observable .of(Hapiness['module']) .filter(_ => !!(_ && _.instance)) .flatMap(_ => hook_1.HookManager .hasLifecycleHook(enums_1.ModuleEnum.OnError.toString(), _.token) ? hook_1.HookManager .triggerHook(enums_1.ModuleEnum.OnError.toString(), _.token, _.instance, [error, data], false) : rxjs_1.Observable .throw(error)) .subscribe(null, _ => console.error(_)); } exports.errorHandler = errorHandler; //# sourceMappingURL=hapiness.js.map