UNPKG

@freemework/hosting

Version:

Hosting library of the Freemework Project.

181 lines 8.26 kB
import { FException, FConfigurationChain, FExecutionContext, FCancellationTokenSourceManual, FLogger, FCancellationException, FCancellationExecutionContext, } from "@freemework/common"; import { FConfigurationDirectory, FConfigurationEnv, FConfigurationProperties, FConfigurationToml, } from "../configuration/index.js"; import { FLauncherException } from "./f_launcher_exception.js"; import { FLauncherRestartRequiredException } from "./f_launcher_restart_required_exception.js"; export function flauncher(...args) { const log = FLogger.create("FLauncher"); const cancellationTokenSource = new FCancellationTokenSourceManual(); const executionContext = new FCancellationExecutionContext(FExecutionContext.Empty, cancellationTokenSource.token); async function run() { process.on("unhandledRejection", (e) => { const ex = FException.wrapIfNeeded(e); log.debug(executionContext, "Unhandled Rejection", ex); log.fatal(executionContext, `Unhandled Rejection. ${ex.constructor.name}: ${ex.message}`); fireShutdownHooks().finally(function () { process.exit(255); }); }); let destroyRequestCount = 0; const shutdownSignals = ["SIGTERM", "SIGINT"]; let runtimeStuff; if (args.length === 3 && typeof args[0] === "function" && typeof args[1] === "function") { runtimeStuff = Object.freeze({ loader: args[0], parser: args[1], runtimeFactory: args[2], }); } else if (args.length === 2 && typeof args[0] === "function" && typeof args[1] === "function") { runtimeStuff = Object.freeze({ loader: defaultConfigurationLoader, parser: args[0], runtimeFactory: args[1], }); } else if (args.length === 1 && typeof args[0] === "function") { runtimeStuff = Object.freeze({ parser: null, runtimeFactory: args[0], }); } else { throw new Error("Wrong arguments"); } let runtime; if (runtimeStuff.parser === null) { runtime = await runtimeStuff.runtimeFactory(executionContext); } else { const rawConfiguration = await runtimeStuff.loader(executionContext); const parsedConfiguration = runtimeStuff.parser(rawConfiguration); runtime = await runtimeStuff.runtimeFactory(executionContext, parsedConfiguration); } shutdownSignals.forEach((signal) => process.on(signal, () => gracefulShutdown(signal))); async function gracefulShutdown(signal) { if (destroyRequestCount++ === 0) { cancellationTokenSource.cancel(); if (log.isInfoEnabled) { log.info(executionContext, `Interrupt signal received: ${signal}`); } try { await runtime.dispose(); } catch (e) { const ex = FException.wrapIfNeeded(e); const msg = "Unexpected exception from the dispose() method of your runtime object."; log.debug(executionContext, msg, ex); log.error(executionContext, () => `${msg} ${ex.message}`); } await fireShutdownHooks(); process.exit(0); } else { if (log.isInfoEnabled) { log.info(executionContext, `Interrupt signal (${destroyRequestCount}) received: ${signal}`); } } } } log.info(executionContext, "Starting application..."); run() .then(() => { if (log.isInfoEnabled) { log.info(executionContext, `Application was started. Process ID: ${process.pid}`); } }) .catch((e) => { const ex = FException.wrapIfNeeded(e); if (ex instanceof FCancellationException) { log.warn(executionContext, "Runtime initialization was cancelled by user"); fireShutdownHooks().finally(function () { process.exit(0); }); } if (log.isFatalEnabled) { if (e instanceof FLauncherException) { log.fatal(executionContext, `Runtime initialization failed with ${e.constructor.name}: ${e.message}`); } else { log.fatal(executionContext, `Runtime initialization failed with error: ${ex.message}`); log.debug(executionContext, ex.message, ex); } } const exitCode = ex instanceof FLauncherRestartRequiredException ? ex.exitCode : 127; if (process.env["NODE_ENV"] === "development") { setTimeout(() => { fireShutdownHooks().finally(function () { process.exit(exitCode); }); }, 1000); } else { fireShutdownHooks().finally(function () { process.exit(exitCode); }); } }); } export async function defaultConfigurationLoader(_executionContext) { const chainItems = []; for (const arg of process.argv) { if (arg.startsWith(defaultConfigurationLoader.CONFIG_FILE_ARG)) { const configFile = arg.substring(defaultConfigurationLoader.CONFIG_FILE_ARG.length); const fileConf = await FConfigurationProperties.fromFile(configFile); chainItems.push(fileConf); } else if (arg.startsWith(defaultConfigurationLoader.CONFIG_TOML_FILE_ARG)) { const configFile = arg.substring(defaultConfigurationLoader.CONFIG_TOML_FILE_ARG.length); const fileConf = await FConfigurationToml.fromFile(configFile); chainItems.push(fileConf); } else if (arg.startsWith(defaultConfigurationLoader.CONFIG_SECRET_DIR_ARG)) { const secretsDir = arg.substring(defaultConfigurationLoader.CONFIG_SECRET_DIR_ARG.length); const secretsConfiguration = await FConfigurationDirectory.read(secretsDir); chainItems.push(secretsConfiguration); } else if (arg === defaultConfigurationLoader.CONFIG_ENV_ARG) { const envConf = new FConfigurationEnv(); chainItems.push(envConf); } } if (chainItems.length === 0) { throw new FLauncherException("Missing configuration. Please provide at least one of: " + `${defaultConfigurationLoader.CONFIG_ENV_ARG}, ${defaultConfigurationLoader.CONFIG_FILE_ARG}, ${defaultConfigurationLoader.CONFIG_TOML_FILE_ARG}, ${defaultConfigurationLoader.CONFIG_SECRET_DIR_ARG}`); } chainItems.reverse(); const rawConfiguration = new FConfigurationChain(...chainItems); return rawConfiguration; } (function (defaultConfigurationLoader) { defaultConfigurationLoader.CONFIG_ENV_ARG = "--config-env"; defaultConfigurationLoader.CONFIG_FILE_ARG = "--config-file="; defaultConfigurationLoader.CONFIG_TOML_FILE_ARG = "--config-toml-file="; defaultConfigurationLoader.CONFIG_SECRET_DIR_ARG = "--config-secrets-dir="; })(defaultConfigurationLoader || (defaultConfigurationLoader = {})); export function registerShutdownHook(cb) { shutdownHooks.push(cb); } const shutdownHooks = []; async function fireShutdownHooks() { if (shutdownHooks.length > 0) { const log = FLogger.create("FLauncher.fireShutdownHooks"); log.debug(FExecutionContext.Empty, "Executing shutdown hooks..."); const shutdownHooksCopy = [...shutdownHooks]; do { const cb = shutdownHooksCopy.pop(); try { await cb(); } catch (e) { const ex = FException.wrapIfNeeded(e); log.warn(FExecutionContext.Empty, `An shutdown hook was finished with error: ${ex.message}`); log.debug(FExecutionContext.Empty, "An shutdown hook was finished with error", ex); } } while (shutdownHooksCopy.length > 0); } } //# sourceMappingURL=f_launcher.js.map