UNPKG

nope-js-node

Version:

NoPE Runtime for Nodejs. For Browser-Support please use nope-browser

430 lines (429 loc) 16.7 kB
"use strict"; /** * @author Martin Karkowski * @email m.karkowski@zema.de * @create date 2020-11-11 13:27:58 * @modify date 2021-10-19 09:15:38 * @desc [description] * */ Object.defineProperty(exports, "__esModule", { value: true }); exports.main = exports.runNopeBackend = exports.readInArgs = exports.DEFAULT_SETTINGS = void 0; const argparse_1 = require("argparse"); const promises_1 = require("fs/promises"); require("reflect-metadata"); const index_nodejs_1 = require("../communication/index.nodejs"); const async_1 = require("../helpers/async"); const idMethods_1 = require("../helpers/idMethods"); const objectMethods_1 = require("../helpers/objectMethods"); const getPackageLoader_browser_1 = require("../loader/getPackageLoader.browser"); const loadPackages_1 = require("../loader/loadPackages"); const fileLogging_1 = require("../logger/fileLogging"); const getLogger_1 = require("../logger/getLogger"); const nopeLogger_1 = require("../logger/nopeLogger"); const setGlobalLoggerLevel_1 = require("../logger/setGlobalLoggerLevel"); const index_nodejs_2 = require("../profiling/index.nodejs"); const nope_1 = require("../types/nope"); const renderNope_1 = require("./renderNope"); exports.DEFAULT_SETTINGS = { file: "./config/settings.json", channel: "event", skipLoadingConfig: false, channelParams: "not-provided", log: "debug", singleton: true, dispatcherLogLevel: "info", communicationLogLevel: "info", delay: 2, timings: {}, defaultSelector: "first", forceUsingSelectors: false, preventVarifiedNames: false, logToFile: false, id: (0, idMethods_1.generateId)(), profile: false, useBaseServices: true, }; /** * Helper Function to Read-In the Arguments used by the * cli-tool `run` * * @export * @async * @param additionalArguments arguments for the `ArgumentParser` * @param {Partial<RunArgs>} [forcedArgs={}] The settings to run the args. * @param {ArgumentParser} [parser=null] An additional `ArgumentParser`. If not provided its created * @returns {Promise<RunArgs>} */ async function readInArgs(additionalArguments = [], forcedArgs = {}, defaultArgs = {}, parser = null) { if (parser === null) { parser = new argparse_1.ArgumentParser({ // version: "1.0.0", add_help: true, description: "Command Line interface, determines the available Packages.", }); } for (const arg of additionalArguments) { parser.add_argument(arg.name, { help: arg.help, default: arg.defaultValue, type: arg.type, }); } parser.add_argument("-f", "--file", { help: "File containing containing the package definitions.", default: defaultArgs.file || "./config/settings.json", type: "str", dest: "file", }); parser.add_argument("-c", "--channel", { help: "The Communication Channel, which should be used. Possible Values are: " + // Display all Options: Object.getOwnPropertyNames(index_nodejs_1.validLayers) .map((item) => { return '"' + item + '"'; }) .join(", "), default: defaultArgs.channel || "event", type: "str", dest: "channel", }); parser.add_argument("-p", "--channelParams", { help: "Paramas for the Channel, to connect to. The Following Defaults are used: \n" + JSON.stringify(index_nodejs_1.layerDefaultParameters, undefined, 4), default: defaultArgs.channelParams || "not-provided", type: "str", dest: "channelParams", }); parser.add_argument("-s", "--skip-loading-config", { help: "Flag to prevent loading the elements defined in the settings.json.", action: "append", nargs: "?", dest: "skipLoadingConfig", }); parser.add_argument("--default-selector", { help: "The default-selector to select the service providers. Possible Values are: " + // Display all Options: nope_1.ValidDefaultSelectors.map((item) => { return '"' + item + '"'; }).join(", "), default: defaultArgs.defaultSelector || "first", type: "str", dest: "defaultSelector", }); parser.add_argument("--log-to-file", { help: "Log will be stored in a logfile.", action: "append", nargs: "?", dest: "logToFile", }); parser.add_argument("-l", "--log", { help: 'Specify the Logger Level. Defaults to "info". Valid values are: ' + nopeLogger_1.LoggerLevels.join(", "), default: defaultArgs.log || "info", type: "str", dest: "log", }); parser.add_argument("--id", { help: "Define a custom id to the Dispatcher", default: defaultArgs.id || (0, idMethods_1.generateId)({ prestring: "_dispatcher", useAsVar: true, }), type: "str", dest: "id", }); parser.add_argument("--dispatcher-log", { help: 'Specify the Logger Level of the Dispatcher. Defaults to "info". Valid values are: ' + nopeLogger_1.LoggerLevels.join(", "), default: defaultArgs.dispatcherLogLevel || "info", type: "str", dest: "dispatcherLogLevel", }); parser.add_argument("--force-selector", { help: "Forces to use the Selector. Otherwise a smart approach is used, which only enables them if required.", action: "append", nargs: "?", dest: "forceUsingSelectors", }); parser.add_argument("--prevent-varified-names", { help: "Enables Random names for variables etc. including var beginning with number or so.", action: "append", nargs: "?", dest: "preventVarifiedNames", }); parser.add_argument("-d", "--delay", { help: 'Adds an delay, which will be waited, after the system connected. Parmeter is provided in [s]. Defaults to "2"', default: typeof defaultArgs.delay === "number" ? defaultArgs.delay : 2, type: "float", dest: "delay", }); parser.add_argument("--communication-log", { help: 'Specify the Logger Level of the Communication. Defaults to "info". Valid values are: ' + nopeLogger_1.LoggerLevels.join(", "), default: defaultArgs.communicationLogLevel || "info", type: "str", dest: "communicationLogLevel", }); parser.add_argument("--profile", { help: "Flag to enable Profiling", action: "append", nargs: "?", dest: "profile", }); parser.add_argument("--noBaseServices", { help: "Flag to enable prevent the base Services to be loaded", action: "append", nargs: "?", dest: "useBaseServices", }); const args = parser.parse_args(); if (args.channelParams === "not-provided") { delete args.channelParams; } args.skipLoadingConfig = defaultArgs.skipLoadingConfig || Array.isArray(args.skipLoadingConfig); args.profile = defaultArgs.profile || Array.isArray(args.profile); args.logToFile = defaultArgs.logToFile || Array.isArray(args.logToFile); args.forceUsingSelectors = defaultArgs.forceUsingSelectors || Array.isArray(args.forceUsingSelectors); args.useBaseServices = defaultArgs.useBaseServices || !Array.isArray(args.useBaseServices); args.preventVarifiedNames = defaultArgs.preventVarifiedNames || Array.isArray(args.preventVarifiedNames); return Object.assign(args, forcedArgs); } exports.readInArgs = readInArgs; /** * Main tool to create a runtime. Returns a {@link INopePackageLoader}. * * * @async * @param {Partial<RunArgs>} [_args={}] Arguments to configure the runtime. * @returns {Promise<INopePackageLoader>} The central logger. */ async function runNopeBackend(_args = {}) { // Default Settings const _defaultSettings = (0, objectMethods_1.deepClone)(exports.DEFAULT_SETTINGS); // Use a different ID. _defaultSettings.id = (0, idMethods_1.generateId)(); let args = Object.assign(_defaultSettings, _args); let configOfFile = { config: _defaultSettings, connections: [], functions: [], packages: [], }; if (args.channel !== "io-server") { try { // Try to read in the default config file provided in the Settings. configOfFile = JSON.parse(await (0, promises_1.readFile)(args.file, { encoding: "utf-8", })); delete configOfFile.config.file; // Update the arguments. args = Object.assign(_defaultSettings, configOfFile.config || {}, _args); configOfFile.connections = configOfFile.connections || []; } catch (error) { } } if (args.channel === "io-server") { args.skipLoadingConfig = true; } const closeCallbacks = []; if (nopeLogger_1.LoggerLevels.includes(args.log)) { (0, setGlobalLoggerLevel_1.setGlobalLoggerLevel)(args.log); } // Define a Logger const logger = (0, getLogger_1.getNopeLogger)("starter"); if (args.logToFile) { const fileName = (0, fileLogging_1.generateLogfilePath)("run"); logger.warn("Using File Logger. Logging to", fileName); closeCallbacks.push((0, fileLogging_1.useLogFile)(fileName, 200)); } if (args.profile) { logger.warn("Enabled Profiling."); closeCallbacks.push((0, index_nodejs_2.recordCPUProfile)()); } if (args.channel === "io-server") { logger.warn("Running as Server. Wont load any module!"); args.skipLoadingConfig = true; } if (!Object.getOwnPropertyNames(index_nodejs_1.validLayers).includes(args.channel)) { logger.error("Invalid Channel. Please use the following values. " + Object.getOwnPropertyNames(index_nodejs_1.validLayers) .map((item) => { return '"' + item + '"'; }) .join(", ")); const error = Error("Invalid Channel. Please use the following values. " + Object.getOwnPropertyNames(index_nodejs_1.validLayers) .map((item) => { return '"' + item + '"'; }) .join(", ")); logger.error(error); throw error; } let _closing = false; const _dispose = (reason = null, p = null) => { if (_closing) { return; } _closing = true; if (reason) { // If there is a reason logger.error("Unhandled Rejection at: Promise", p, "reason:", reason); logger.error(reason); } else { // We should close the Process: logger.warn("received 'ctrl+c'. Shutting down the Instances"); } // Exit the Process const promises = []; for (const callback of closeCallbacks) { try { promises.push(callback()); } catch (e) { logger.error("During exiting, an error occourd"); logger.error(e); } } // Wait for all Promises to finish. Promise.all(promises).then(() => { process.exit(); }); }; // Subscribe to unhandled Reactions. process.on("unhandledRejection", (reason, p) => _dispose(reason, p)); process.on("SIGINT", () => _dispose()); process.on("SIGTERM", () => _dispose()); process.on("exit", () => { logger.info("Completed. Goodbye"); }); // Assign the Default Setting for the Channel. let connectionParameters = index_nodejs_1.layerDefaultParameters[args.channel]; if (args.channelParams != "not-provided") { try { try { // We try to parse the data. connectionParameters = JSON.parse(args.channelParams); } catch (e) { connectionParameters = JSON.parse('"' + args.channelParams + '"'); } } catch (e) { logger.error("Unable to parse the Parameters for the channel. Please use valid JSON!"); logger.error(args.channelParams[0]); logger.error(e); throw e; } } let loader; try { loader = (0, getPackageLoader_browser_1.getPackageLoader)({ communicator: (0, index_nodejs_1.getLayer)(args.channel, connectionParameters, args.communicationLogLevel), logger: (0, getLogger_1.getNopeLogger)("dispatcher", args.dispatcherLogLevel), defaultSelector: args.defaultSelector, forceUsingSelectors: args.forceUsingSelectors, forceUsingValidVarNames: !args.preventVarifiedNames, id: args.id, isMaster: args.channel !== "io-server" ? null : false, }, { singleton: _args.singleton, useBaseServices: _args.useBaseServices, }); if (args.channel !== "io-server") { // Iterate over the additional Layers to connect. for (const item of configOfFile.connections) { switch (item.name) { case "io-client": case "io-host": case "mqtt": (0, index_nodejs_1.addLayer)(loader.dispatcher.communicator, item.name, item.url, item.log, item.considerConnection, item.forwardData); break; case "event": break; default: throw Error("Using unkown Connection :("); } } } // Add the Dispatcher closeCallbacks.push(async () => { await loader.dispatcher.dispose(); }); logger.info(`Waiting for the Dispatcher to connect.`); await loader.dispatcher.communicator.connected.waitFor(); // If required load all Packages. if (!args.skipLoadingConfig) { // Try to load the Modules. if (args.delay > 0) { logger.info(`Waiting ${args.delay} [s] to get all information.`); await (0, async_1.sleep)(args.delay * 1000); } // If required load all Packages. try { logger.info("loading Functions"); await (0, loadPackages_1.loadFunctions)(loader, args.file, args.delay); } catch (e) { logger.error("Unable to load the Functions defined in " + args.file); } try { logger.info("loading Packages"); await (0, loadPackages_1.loadPackages)(loader, args.file, args.delay); } catch (e) { logger.error("Unable to load the Packages defined in " + args.file); } } } catch (e) { (0, getLogger_1.getNopeLogger)("cli", "info").error("failed to load the Packages", e); throw e; } return loader; } exports.runNopeBackend = runNopeBackend; /** * Main Function. * * @export */ async function main(additionalArguments = [], forcedArgs = {}, quiet = false) { if (!quiet) { console.log(renderNope_1.NOPELOGO); console.log("\n\n"); } let args = await readInArgs(additionalArguments, forcedArgs); if (args.channel !== "io-server") { let configOfFile = { config: (0, objectMethods_1.deepClone)(exports.DEFAULT_SETTINGS), connections: [], functions: [], packages: [], }; try { // Try to read in the default config file provided in the Settings. configOfFile = JSON.parse(await (0, promises_1.readFile)(args.file, { encoding: "utf-8", })); delete configOfFile.config.file; args = await readInArgs(additionalArguments, forcedArgs, configOfFile.config || {}); } catch (error) { } } return await runNopeBackend(args); } exports.main = main; // If requested As Main => Perform the Operation. if (require.main === module) { main(); } exports.default = main;