Spaces:
Sleeping
Sleeping
| ; | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| exports.NestApplicationContext = void 0; | |
| const common_1 = require("@nestjs/common"); | |
| const shared_utils_1 = require("@nestjs/common/utils/shared.utils"); | |
| const iterare_1 = require("iterare"); | |
| const constants_1 = require("./constants"); | |
| const exceptions_1 = require("./errors/exceptions"); | |
| const context_id_factory_1 = require("./helpers/context-id-factory"); | |
| const hooks_1 = require("./hooks"); | |
| const abstract_instance_resolver_1 = require("./injector/abstract-instance-resolver"); | |
| const injector_1 = require("./injector/injector"); | |
| const instance_links_host_1 = require("./injector/instance-links-host"); | |
| /** | |
| * @publicApi | |
| */ | |
| class NestApplicationContext extends abstract_instance_resolver_1.AbstractInstanceResolver { | |
| get instanceLinksHost() { | |
| if (!this._instanceLinksHost) { | |
| this._instanceLinksHost = new instance_links_host_1.InstanceLinksHost(this.container); | |
| } | |
| return this._instanceLinksHost; | |
| } | |
| constructor(container, appOptions = {}, contextModule = null, scope = new Array()) { | |
| super(); | |
| this.container = container; | |
| this.appOptions = appOptions; | |
| this.contextModule = contextModule; | |
| this.scope = scope; | |
| this.isInitialized = false; | |
| this.logger = new common_1.Logger(NestApplicationContext.name, { | |
| timestamp: true, | |
| }); | |
| this.shouldFlushLogsOnOverride = false; | |
| this.activeShutdownSignals = new Array(); | |
| this.injector = new injector_1.Injector(); | |
| this.moduleCompiler = container.getModuleCompiler(); | |
| if (this.appOptions.preview) { | |
| this.printInPreviewModeWarning(); | |
| } | |
| } | |
| selectContextModule() { | |
| const modules = this.container.getModules().values(); | |
| this.contextModule = modules.next().value; | |
| } | |
| /** | |
| * Allows navigating through the modules tree, for example, to pull out a specific instance from the selected module. | |
| * @returns {INestApplicationContext} | |
| */ | |
| select(moduleType, selectOptions) { | |
| const modulesContainer = this.container.getModules(); | |
| const contextModuleCtor = this.contextModule.metatype; | |
| const scope = this.scope.concat(contextModuleCtor); | |
| const moduleTokenFactory = this.container.getModuleTokenFactory(); | |
| const { type, dynamicMetadata } = this.moduleCompiler.extractMetadata(moduleType); | |
| const token = dynamicMetadata | |
| ? moduleTokenFactory.createForDynamic(type, dynamicMetadata, moduleType) | |
| : moduleTokenFactory.createForStatic(type, moduleType); | |
| const selectedModule = modulesContainer.get(token); | |
| if (!selectedModule) { | |
| throw new exceptions_1.UnknownModuleException(type.name); | |
| } | |
| const options = typeof selectOptions?.abortOnError !== 'undefined' | |
| ? { | |
| ...this.appOptions, | |
| ...selectOptions, | |
| } | |
| : this.appOptions; | |
| return new NestApplicationContext(this.container, options, selectedModule, scope); | |
| } | |
| /** | |
| * Retrieves an instance (or a list of instances) of either injectable or controller, otherwise, throws exception. | |
| * @returns {TResult | Array<TResult>} | |
| */ | |
| get(typeOrToken, options = { strict: false }) { | |
| return !(options && options.strict) | |
| ? this.find(typeOrToken, options) | |
| : this.find(typeOrToken, { | |
| moduleId: this.contextModule?.id, | |
| each: options.each, | |
| }); | |
| } | |
| /** | |
| * Resolves transient or request-scoped instance (or a list of instances) of either injectable or controller, otherwise, throws exception. | |
| * @returns {Promise<TResult | Array<TResult>>} | |
| */ | |
| resolve(typeOrToken, contextId = (0, context_id_factory_1.createContextId)(), options = { strict: false }) { | |
| return this.resolvePerContext(typeOrToken, this.contextModule, contextId, options); | |
| } | |
| /** | |
| * Registers the request/context object for a given context ID (DI container sub-tree). | |
| * @returns {void} | |
| */ | |
| registerRequestByContextId(request, contextId) { | |
| this.container.registerRequestProvider(request, contextId); | |
| } | |
| /** | |
| * Initializes the Nest application. | |
| * Calls the Nest lifecycle events. | |
| * | |
| * @returns {Promise<this>} The NestApplicationContext instance as Promise | |
| */ | |
| async init() { | |
| if (this.isInitialized) { | |
| return this; | |
| } | |
| /* eslint-disable-next-line no-async-promise-executor */ | |
| this.initializationPromise = new Promise(async (resolve, reject) => { | |
| try { | |
| await this.callInitHook(); | |
| await this.callBootstrapHook(); | |
| resolve(); | |
| } | |
| catch (err) { | |
| reject(err); | |
| } | |
| }); | |
| await this.initializationPromise; | |
| this.isInitialized = true; | |
| return this; | |
| } | |
| /** | |
| * Terminates the application | |
| * @returns {Promise<void>} | |
| */ | |
| async close(signal) { | |
| await this.initializationPromise; | |
| await this.callDestroyHook(); | |
| await this.callBeforeShutdownHook(signal); | |
| await this.dispose(); | |
| await this.callShutdownHook(signal); | |
| this.unsubscribeFromProcessSignals(); | |
| } | |
| /** | |
| * Sets custom logger service. | |
| * Flushes buffered logs if auto flush is on. | |
| * @returns {void} | |
| */ | |
| useLogger(logger) { | |
| common_1.Logger.overrideLogger(logger); | |
| if (this.shouldFlushLogsOnOverride) { | |
| this.flushLogs(); | |
| } | |
| } | |
| /** | |
| * Prints buffered logs and detaches buffer. | |
| * @returns {void} | |
| */ | |
| flushLogs() { | |
| common_1.Logger.flush(); | |
| } | |
| /** | |
| * Define that it must flush logs right after defining a custom logger. | |
| */ | |
| flushLogsOnOverride() { | |
| this.shouldFlushLogsOnOverride = true; | |
| } | |
| /** | |
| * Enables the usage of shutdown hooks. Will call the | |
| * `onApplicationShutdown` function of a provider if the | |
| * process receives a shutdown signal. | |
| * | |
| * @param {ShutdownSignal[]} [signals=[]] The system signals it should listen to | |
| * @param {ShutdownHooksOptions} [options={}] Options for configuring shutdown hooks behavior | |
| * | |
| * @returns {this} The Nest application context instance | |
| */ | |
| enableShutdownHooks(signals = [], options = {}) { | |
| if ((0, shared_utils_1.isEmpty)(signals)) { | |
| signals = Object.keys(common_1.ShutdownSignal).map((key) => common_1.ShutdownSignal[key]); | |
| } | |
| else { | |
| // given signals array should be unique because | |
| // process shouldn't listen to the same signal more than once. | |
| signals = Array.from(new Set(signals)); | |
| } | |
| signals = (0, iterare_1.iterate)(signals) | |
| .map((signal) => signal.toString().toUpperCase().trim()) | |
| // filter out the signals which is already listening to | |
| .filter(signal => !this.activeShutdownSignals.includes(signal)) | |
| .toArray(); | |
| this.listenToShutdownSignals(signals, options); | |
| return this; | |
| } | |
| async dispose() { | |
| // Nest application context has no server | |
| // to dispose, therefore just call a noop | |
| return Promise.resolve(); | |
| } | |
| /** | |
| * Listens to shutdown signals by listening to | |
| * process events | |
| * | |
| * @param {string[]} signals The system signals it should listen to | |
| * @param {ShutdownHooksOptions} options Options for configuring shutdown hooks behavior | |
| */ | |
| listenToShutdownSignals(signals, options = {}) { | |
| let receivedSignal = false; | |
| const cleanup = async (signal) => { | |
| try { | |
| if (receivedSignal) { | |
| // If we receive another signal while we're waiting | |
| // for the server to stop, just ignore it. | |
| return; | |
| } | |
| receivedSignal = true; | |
| await this.initializationPromise; | |
| await this.callDestroyHook(); | |
| await this.callBeforeShutdownHook(signal); | |
| await this.dispose(); | |
| await this.callShutdownHook(signal); | |
| signals.forEach(sig => process.removeListener(sig, cleanup)); | |
| if (options.useProcessExit) { | |
| // Use process.exit() to ensure the 'exit' event is properly triggered. | |
| // This is required for async loggers (like Pino with transports) | |
| // to flush their buffers before the process terminates. | |
| process.exit(0); | |
| } | |
| else { | |
| process.kill(process.pid, signal); | |
| } | |
| } | |
| catch (err) { | |
| common_1.Logger.error(constants_1.MESSAGES.ERROR_DURING_SHUTDOWN, err?.stack, NestApplicationContext.name); | |
| process.exit(1); | |
| } | |
| }; | |
| this.shutdownCleanupRef = cleanup; | |
| signals.forEach((signal) => { | |
| this.activeShutdownSignals.push(signal); | |
| process.on(signal, cleanup); | |
| }); | |
| } | |
| /** | |
| * Unsubscribes from shutdown signals (process events) | |
| */ | |
| unsubscribeFromProcessSignals() { | |
| if (!this.shutdownCleanupRef) { | |
| return; | |
| } | |
| this.activeShutdownSignals.forEach(signal => { | |
| process.removeListener(signal, this.shutdownCleanupRef); | |
| }); | |
| } | |
| /** | |
| * Calls the `onModuleInit` function on the registered | |
| * modules and its children. | |
| */ | |
| async callInitHook() { | |
| const modulesSortedByDistance = this.getModulesToTriggerHooksOn(); | |
| for (const module of modulesSortedByDistance) { | |
| await (0, hooks_1.callModuleInitHook)(module); | |
| } | |
| } | |
| /** | |
| * Calls the `onModuleDestroy` function on the registered | |
| * modules and its children. | |
| */ | |
| async callDestroyHook() { | |
| const modulesSortedByDistance = [ | |
| ...this.getModulesToTriggerHooksOn(), | |
| ].reverse(); | |
| for (const module of modulesSortedByDistance) { | |
| await (0, hooks_1.callModuleDestroyHook)(module); | |
| } | |
| } | |
| /** | |
| * Calls the `onApplicationBootstrap` function on the registered | |
| * modules and its children. | |
| */ | |
| async callBootstrapHook() { | |
| const modulesSortedByDistance = this.getModulesToTriggerHooksOn(); | |
| for (const module of modulesSortedByDistance) { | |
| await (0, hooks_1.callModuleBootstrapHook)(module); | |
| } | |
| } | |
| /** | |
| * Calls the `onApplicationShutdown` function on the registered | |
| * modules and children. | |
| */ | |
| async callShutdownHook(signal) { | |
| const modulesSortedByDistance = [ | |
| ...this.getModulesToTriggerHooksOn(), | |
| ].reverse(); | |
| for (const module of modulesSortedByDistance) { | |
| await (0, hooks_1.callAppShutdownHook)(module, signal); | |
| } | |
| } | |
| /** | |
| * Calls the `beforeApplicationShutdown` function on the registered | |
| * modules and children. | |
| */ | |
| async callBeforeShutdownHook(signal) { | |
| const modulesSortedByDistance = [ | |
| ...this.getModulesToTriggerHooksOn(), | |
| ].reverse(); | |
| for (const module of modulesSortedByDistance) { | |
| await (0, hooks_1.callBeforeAppShutdownHook)(module, signal); | |
| } | |
| } | |
| assertNotInPreviewMode(methodName) { | |
| if (this.appOptions.preview) { | |
| const error = `Calling the "${methodName}" in the preview mode is not supported.`; | |
| this.logger.error(error); | |
| throw new Error(error); | |
| } | |
| } | |
| getModulesToTriggerHooksOn() { | |
| if (this._moduleRefsForHooksByDistance) { | |
| return this._moduleRefsForHooksByDistance; | |
| } | |
| const modulesContainer = this.container.getModules(); | |
| const compareFn = (a, b) => b.distance - a.distance; | |
| const modulesSortedByDistance = Array.from(modulesContainer.values()).sort(compareFn); | |
| this._moduleRefsForHooksByDistance = this.appOptions?.preview | |
| ? modulesSortedByDistance.filter(moduleRef => moduleRef.initOnPreview) | |
| : modulesSortedByDistance; | |
| return this._moduleRefsForHooksByDistance; | |
| } | |
| printInPreviewModeWarning() { | |
| this.logger.warn('------------------------------------------------'); | |
| this.logger.warn('Application is running in the PREVIEW mode!'); | |
| this.logger.warn('Providers/controllers will not be instantiated.'); | |
| this.logger.warn('------------------------------------------------'); | |
| } | |
| } | |
| exports.NestApplicationContext = NestApplicationContext; | |