UNPKG

@deepkit/app

Version:

Deepkit App, CLI framework and service container

338 lines 17.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.App = exports.onAppShutdown = exports.onAppError = exports.onAppExecuted = exports.onAppExecute = exports.__ΩAppErrorEvent = exports.__ΩAppExecutedEvent = exports.__ΩAppEvent = exports.RootAppModule = void 0; exports.setPartialConfig = setPartialConfig; const __ΩParameters = ['T', 'args', '', 'Parameters', 'l>e"!R!RPde#!Ph"!@2""/#qk#\'QRb!Pde"!p)w$y']; const __ΩPartial = ['T', 'Partial', 'l+e#!e"!fRb!Pde"!gN#"w"y']; const __ΩError = ['name', 'message', 'stack', 'Error', 'P&4!&4"&4#8Mw$y']; /*@ts-ignore*/ const { __ΩExtractClassType } = require('@deepkit/core'); /*@ts-ignore*/ const { __ΩConfigLoader } = require('./service-container.js'); /*@ts-ignore*/ const { __ΩEventListenerCallback } = require('@deepkit/event'); /*@ts-ignore*/ const { __ΩEventDispatcherDispatchType } = require('@deepkit/event'); /*@ts-ignore*/ const { __ΩReceiveType } = require('@deepkit/type'); /*@ts-ignore*/ const { __ΩClassType } = require('@deepkit/core'); /*@ts-ignore*/ const { __ΩScope } = require('@deepkit/injector'); /*@ts-ignore*/ const { __ΩResolveToken } = require('@deepkit/injector'); /*@ts-ignore*/ const { __ΩConfigureProviderOptions } = require('@deepkit/injector'); function __assignType(fn, args) { fn.__type = args; return fn; } /* * Deepkit Framework * Copyright (C) 2021 Deepkit UG, Marc J. Schmidt * * This program is free software: you can redistribute it and/or modify * it under the terms of the MIT License. * * You should have received a copy of the MIT License along with this program. */ const core_1 = require("@deepkit/core"); const service_container_js_1 = require("./service-container.js"); const injector_1 = require("@deepkit/injector"); const module_js_1 = require("./module.js"); const configuration_js_1 = require("./configuration.js"); const event_1 = require("@deepkit/event"); const type_1 = require("@deepkit/type"); const logger_1 = require("@deepkit/logger"); const command_js_1 = require("./command.js"); function setPartialConfig(target, partial, incomingPath = '') { for (const i in partial) { const path = (incomingPath ? incomingPath + '.' : '') + i; if ((0, core_1.isObject)(partial[i])) { setPartialConfig(target, partial[i], path); } else { (0, core_1.setPathValue)(target, path, partial[i]); } } } setPartialConfig.__type = ['target', 'partial', 'incomingPath', () => "", 'setPartialConfig', 'PP&"LM2!P&"LM2"&2#>$"/%']; const __ΩEnvNamingStrategy = ["same", "upper", "lower", 'name', "same", "upper", "lower", '', 'EnvNamingStrategy', 'P.!.".#P&2$P&.%.&.\'-J/(Jw)y']; function camelToUpperCase(str) { return str.replace(/[A-Z]+/g, __assignType((letter) => `_${letter.toUpperCase()}`, ['letter', '', 'P&2!"/"'])).toUpperCase(); } camelToUpperCase.__type = ['str', 'camelToUpperCase', 'P&2!"/"']; function camelToLowerCase(str) { return str.replace(/[A-Z]+/g, __assignType((letter) => `_${letter.toLowerCase()}`, ['letter', '', 'P&2!"/"'])).toLowerCase(); } camelToLowerCase.__type = ['str', 'camelToLowerCase', 'P&2!"/"']; function convertNameStrategy(namingStrategy, name) { const strategy = (0, core_1.isFunction)(namingStrategy) ? namingStrategy(name) || 'same' : namingStrategy; if (strategy === 'upper') { return camelToUpperCase(name); } else if (strategy === 'lower') { return camelToLowerCase(name); } else if (strategy === 'same') { return name; } else { return strategy; } } convertNameStrategy.__type = [() => __ΩEnvNamingStrategy, 'namingStrategy', 'name', 'convertNameStrategy', 'Pn!2"&2#&/$']; function parseEnv(config, prefix, schema, incomingDotPath, incomingEnvPath, namingStrategy, envContainer) { for (const property of schema.getProperties()) { const name = convertNameStrategy(namingStrategy, property.name); if (property.type.kind === type_1.ReflectionKind.class || property.type.kind === type_1.ReflectionKind.objectLiteral) { parseEnv(config, prefix, type_1.ReflectionClass.from(property.type), (incomingDotPath ? incomingDotPath + '.' : '') + property.name, (incomingEnvPath ? incomingEnvPath + '_' : '') + name, namingStrategy, envContainer); } else { const dotPath = (incomingDotPath ? incomingDotPath + '.' : '') + property.name; const envName = prefix + (incomingEnvPath ? incomingEnvPath + '_' : '') + name; if (envContainer[envName] === undefined) continue; (0, core_1.setPathValue)(config, dotPath, envContainer[envName]); } } } parseEnv.__type = ['config', 'prefix', () => type_1.ReflectionClass, 'schema', 'incomingDotPath', 'incomingEnvPath', () => __ΩEnvNamingStrategy, 'namingStrategy', 'envContainer', 'parseEnv', 'PP&"LM2!&2"P"7#2$&2%&2&n\'2(P&"LM2)"/*']; const __ΩEnvConfigOptions = ['envFilePath', () => __ΩEnvNamingStrategy, 'namingStrategy', 'prefix', 'EnvConfigOptions', 'PP&&FJ4!8n"4#8&4$8Mw%y']; const defaultEnvConfigOptions = { prefix: 'APP_', envFilePath: ['.env'], namingStrategy: 'upper', }; class EnvConfigLoader { constructor(options) { const normalizedOptions = { ...defaultEnvConfigOptions, ...options, }; const { prefix, envFilePath, namingStrategy } = normalizedOptions; this.prefix = prefix; this.envFilePaths = Array.isArray(envFilePath) ? envFilePath : [envFilePath]; this.namingStrategy = namingStrategy; } load(module, config, schema) { const envConfiguration = new configuration_js_1.EnvConfiguration(); for (const path of this.envFilePaths) { if (envConfiguration.loadEnvFile(path)) break; } const env = Object.assign({}, envConfiguration.getAll()); Object.assign(env, process.env); parseEnv(config, this.prefix, schema, '', convertNameStrategy(this.namingStrategy, module.name), this.namingStrategy, env); } } EnvConfigLoader.__type = ['prefix', 'envFilePaths', () => __ΩEnvNamingStrategy, 'namingStrategy', () => __ΩEnvConfigOptions, 'options', 'constructor', () => module_js_1.AppModule, 'module', 'config', () => type_1.ReflectionClass, 'schema', 'load', 'EnvConfigLoader', '&3!9;&F3"9;n#3$9;Pn%2&8"0\'PP"7(2)P&"LM2*P"7+2,"0-5w.']; class RootAppModule extends module_js_1.AppModule { } exports.RootAppModule = RootAppModule; RootAppModule.__type = ['T', () => module_js_1.AppModule, 'RootAppModule', 'b!Pe"!7"5e!!6"w#']; const __ΩAppEvent = ['command', 'parameters', () => injector_1.InjectorContext, 'injector', 'AppEvent', 'P&4!P&"LM4"P7#4$Mw%y']; exports.__ΩAppEvent = __ΩAppEvent; const __ΩAppExecutedEvent = [() => __ΩAppEvent, 'exitCode', 'AppExecutedEvent', 'Pn!\'4"Mw#y']; exports.__ΩAppExecutedEvent = __ΩAppExecutedEvent; const __ΩAppErrorEvent = [() => __ΩAppEvent, () => __ΩError, 'error', 'AppErrorEvent', 'Pn!n"4#Mw$y']; exports.__ΩAppErrorEvent = __ΩAppErrorEvent; /** * When a CLI command is about to be executed, this event is emitted. * * This is different to @deepkit/framework's onBootstrap event, which is only executed * when the server:start is execute. This event is executed for every CLI command (including server:start). */ exports.onAppExecute = (event_1.DataEventToken.Ω = [[() => __ΩAppEvent, 'n!']], new event_1.DataEventToken('app.execute')); /** * When a CLI command is successfully executed, this event is emitted. */ exports.onAppExecuted = (event_1.DataEventToken.Ω = [[() => __ΩAppExecutedEvent, 'n!']], new event_1.DataEventToken('app.executed')); /** * When a CLI command failed to execute, this event is emitted. */ exports.onAppError = (event_1.DataEventToken.Ω = [[() => __ΩAppErrorEvent, 'n!']], new event_1.DataEventToken('app.error')); /** * When the application is about to shut down, this event is emitted. * This is always executed, even when an error occurred. So it's a good place to clean up. */ exports.onAppShutdown = (event_1.DataEventToken.Ω = [[() => __ΩAppEvent, 'n!']], new event_1.DataEventToken('app.shutdown')); /** * This is the smallest available application abstraction in Deepkit. * * It is based on a module and executes registered CLI controllers in `execute`. * * @deepkit/framework extends that with a more powerful Application class, that contains also HTTP and RPC controllers. * * You can use this class for more integrated unit-tests. */ class App { constructor(appModuleOptions, serviceContainer, appModule) { this.appModule = appModule || new RootAppModule({}, appModuleOptions); this.serviceContainer = serviceContainer || new service_container_js_1.ServiceContainer(this.appModule); } static fromModule(module) { return new App({}, undefined, module); } /** * Allows to change the module after the configuration has been loaded, right before the service container is built. * * This enables you to change the module or its imports depending on the configuration the last time before their services are built. * * At this point no services can be requested as the service container was not built. */ setup(...args) { this.appModule = this.appModule.setup(...args); return this; } /** * Allows to call services before the application bootstraps. * * This enables you to configure modules and request their services. */ use(setup) { this.appModule.use(setup); return this; } /** * Calls a function immediately and resolves all parameters using the * current service container. */ call(fn, module) { const injector = this.serviceContainer.getInjector(module || this.appModule); const resolvedFunction = (0, injector_1.injectedFunction)(fn, injector); return resolvedFunction(); } command(name, callback) { callback = (0, core_1.isFunction)(name) ? name : callback; name = (0, core_1.isFunction)(name) ? '' : name; this.appModule.addCommand(name, callback); return this; } addConfigLoader(loader) { this.serviceContainer.addConfigLoader(loader); return this; } configure(config) { this.serviceContainer.appModule.configure(config); return this; } /** * Register a new event listener for given token. * * order: The lower the order, the sooner the listener is called. Default is 0. */ listen(eventToken, callback, order = 0) { const listener = { callback, order, eventToken }; this.appModule.listeners.push(listener); return this; } dispatch(eventToken, ...args) { return this.get(event_1.EventDispatcher).dispatch(eventToken, ...args); } /** * Loads environment variables and optionally reads from .env files in order to find matching configuration options * in your application and modules in order to set their values. * * Prefixing ENV variables is encouraged to avoid collisions and by default a prefix of APP_ is used * Example: * * APP_databaseUrl="mongodb://localhost/mydb" * * new App({}).loadConfigFromEnvVariables('APP_').run(); * * * `envFilePath` can be either an absolute or relative path. For relative paths the first * folder with a package.json starting from process.cwd() upwards is picked. * * So if you use 'local.env' make sure a 'local.env' file is located beside your 'package.json'. * * @param options Configuration options for retrieving configuration from env * @returns */ loadConfigFromEnv(options) { this.addConfigLoader(new EnvConfigLoader(options)); return this; } /** * Loads a JSON encoded environment variable and applies its content to the configuration. * * Example: * * APP_CONFIG={'databaseUrl": "mongodb://localhost/mydb", "moduleA": {"foo": "bar'}} * * new App().run().loadConfigFromEnvVariable('APP_CONFIG').run(); */ loadConfigFromEnvVariable(variableName = 'APP_CONFIG') { if (!process.env[variableName]) return this; this.addConfigLoader({ load: __assignType(function load(module, config, schema) { try { const jsonConfig = JSON.parse(process.env[variableName] || ''); setPartialConfig(config, module.name ? jsonConfig[module.name] : jsonConfig); } catch (error) { throw new Error(`Invalid JSON in env variable ${variableName}. Parse error: ${error}`); } }, [() => module_js_1.AppModule, 'module', 'config', () => type_1.ReflectionClass, 'schema', 'load', 'PP"7!2"P&"LM2#P"7$2%"/&']), }); return this; } async run(argv, bin) { const exitCode = await this.execute(argv, bin); if (exitCode > 0) process.exit(exitCode); } get(token = this.get.Ω?.[0], moduleOrClass, scope) { this.get.Ω = undefined; return this.serviceContainer.getInjector(moduleOrClass || this.appModule).get(token, scope); } getInjector(moduleOrClass) { return this.serviceContainer.getInjector(moduleOrClass || this.appModule); } getInjectorContext() { return this.serviceContainer.getInjectorContext(); } /** * @see InjectorModule.configureProvider */ configureProvider(configure, options = {}, type = this. /** * @see InjectorModule.configureProvider */ configureProvider.Ω?.[0]) { this.configureProvider.Ω = undefined; (this.appModule.configureProvider.Ω = [[type, 'n!']], this.appModule.configureProvider(configure, options, type)); return this; } async execute(argv, bin) { const eventDispatcher = this.get(event_1.EventDispatcher); const logger = this.get(logger_1.Logger); function unhandledRejectionHandler(error) { logger.error('unhandledRejection', error); } unhandledRejectionHandler.__type = ['error', 'unhandledRejectionHandler', 'P"2!"/"']; if ('undefined' !== typeof process) { process.on('unhandledRejection', unhandledRejectionHandler); } const scopedInjectorContext = this.getInjectorContext().createChildScope('cli'); if ('string' !== typeof bin) { bin = bin || (0, command_js_1.getBinFromEnvironment)(); let binary = (0, core_1.pathBasename)(bin[0]); let file = (0, core_1.pathBasename)(bin[1]); bin = `${binary} ${file}`; } try { return await (0, command_js_1.executeCommand)(bin, argv || (0, command_js_1.getArgsFromEnvironment)(), eventDispatcher, logger, scopedInjectorContext, this.serviceContainer.cliControllerRegistry.controllers); } finally { if ('undefined' !== typeof process) { process.removeListener('unhandledRejection', unhandledRejectionHandler); } } } } exports.App = App; App.__type = ['T', () => EnvConfigLoader, 'envConfigLoader', () => service_container_js_1.ServiceContainer, 'serviceContainer', () => __ΩExtractClassType, "config", () => module_js_1.AppModule, 'appModule', 'appModuleOptions', () => service_container_js_1.ServiceContainer, () => module_js_1.AppModule, 'constructor', () => module_js_1.AppModule, 'module', () => App, 'fromModule', () => __ΩParameters, "appModule", "setup", 'args', 'setup', '', 'use', 'fn', () => module_js_1.AppModule, 'call', 'name', 'callback', 'command', () => __ΩConfigLoader, 'loader', 'addConfigLoader', () => __ΩPartial, () => __ΩExtractClassType, "config", 'config', 'configure', 'eventToken', () => __ΩEventListenerCallback, 'order', () => 0, 'listen', 'DispatchArguments', () => __ΩEventDispatcherDispatchType, 'dispatch', () => __ΩEnvConfigOptions, 'options', 'loadConfigFromEnv', 'variableName', () => "APP_CONFIG", 'loadConfigFromEnvVariable', 'argv', 'bin', 'run', () => __ΩReceiveType, 'token', () => module_js_1.AppModule, () => __ΩClassType, () => module_js_1.AppModule, 'moduleOrClass', () => __ΩScope, 'scope', () => __ΩResolveToken, 'get', () => module_js_1.AppModule, () => __ΩClassType, () => module_js_1.AppModule, 'getInjector', () => injector_1.InjectorContext, 'getInjectorContext', 'instance', () => __ΩPartial, () => __ΩConfigureProviderOptions, () => ({}), () => __ΩReceiveType, 'type', 'configureProvider', 'execute', 'App', 'b!P7"3#8<P7$3%9Pe"!.\'fo&"7(3)Pe"!2*P7+2%8P"7,2)8"0-PPe#!7.2/Pe#!7001sP!.3f.4fo2"@25!06PP"@25$/726!08PP"@25e#!/729P"7:2/8e"!0;PP&P"@25"/7J2<P"@25"/72=8!0>Pn?2@!0APe"!.DfoC"oB"2E!0FPe"!2Ge"!oH"2=\'2I>J!0KPe"!2G"wL@25e"!oM"0NPnO2P8!0QP&2R>S!0TP"F2U8&F2V8"0WPe"!oX"2Y8PP"7ZP"7\\o["J2]8n^2_8e"!o`"0aPPP"7bP"7doc"J2]8"0ePP7f0gPPe#!2h"@25"/72Fnjoi"2P>ke"!ol"2m8!0nP&F2U8P&F&J2V8\'`0o5wp']; //# sourceMappingURL=app.js.map