@deepkit/app
Version:
Deepkit App, CLI framework and service container
292 lines • 14.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceContainer = exports.__ΩConfigLoader = exports.WorkflowRegistry = exports.MiddlewareRegistry = exports.__ΩMiddlewareRegistryEntry = exports.CliControllerRegistry = void 0;
/*@ts-ignore*/
const { __ΩClassType } = require('@deepkit/core');
/*@ts-ignore*/
const { __ΩModuleDefinition } = require('./module.js');
/*@ts-ignore*/
const { __ΩEventListenerRegistered } = require('@deepkit/event');
/*@ts-ignore*/
const { __ΩMiddlewareConfig } = require('./module.js');
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 event_1 = require("@deepkit/event");
const module_js_1 = require("./module.js");
const injector_1 = require("@deepkit/injector");
const command_js_1 = require("./command.js");
const workflow_1 = require("@deepkit/workflow");
const type_1 = require("@deepkit/type");
const logger_1 = require("@deepkit/logger");
const stopwatch_1 = require("@deepkit/stopwatch");
class CliControllerRegistry {
constructor() {
this.controllers = [];
}
}
exports.CliControllerRegistry = CliControllerRegistry;
CliControllerRegistry.__type = ['ControllerConfig', 'controllers', function () { return []; }, 'CliControllerRegistry', '"w!F3"9>#5w$'];
const __ΩMiddlewareRegistryEntry = [() => __ΩMiddlewareConfig, 'config', () => module_js_1.AppModule, 'module', 'MiddlewareRegistryEntry', 'Pn!4"P"7#4$Mw%y'];
exports.__ΩMiddlewareRegistryEntry = __ΩMiddlewareRegistryEntry;
class MiddlewareRegistry {
constructor() {
this.configs = [];
}
}
exports.MiddlewareRegistry = MiddlewareRegistry;
MiddlewareRegistry.__type = [() => __ΩMiddlewareRegistryEntry, 'configs', function () { return []; }, 'MiddlewareRegistry', 'n!F3"9>#5w$'];
class WorkflowRegistry {
constructor(workflows) {
this.workflows = workflows;
}
get(name) {
for (const w of this.workflows) {
if (w.name === name)
return w;
}
throw new Error(`Workflow with name ${name} does not exist`);
}
add(workflow) {
this.workflows.push(workflow);
}
}
exports.WorkflowRegistry = WorkflowRegistry;
WorkflowRegistry.__type = [() => workflow_1.WorkflowDefinition, 'workflows', 'constructor', 'name', () => workflow_1.WorkflowDefinition, 'get', () => workflow_1.WorkflowDefinition, 'workflow', 'add', 'WorkflowRegistry', 'PP"7!F2":9"0#P&2$P"7%0&PP"7\'2("0)5w*'];
const __ΩConfigLoader = [() => module_js_1.AppModule, 'module', 'config', () => type_1.ReflectionClass, 'schema', 'load', 'ConfigLoader', 'PPP"7!2"P&"LM2#P"7$2%$1&Mw\'y'];
exports.__ΩConfigLoader = __ΩConfigLoader;
class ServiceContainer {
constructor(appModule) {
this.appModule = appModule;
this.cliControllerRegistry = new CliControllerRegistry;
this.middlewareRegistry = new MiddlewareRegistry;
this.workflowRegistry = new WorkflowRegistry([]);
this.configLoaders = [];
/**
* All modules in the whole module tree.
* This is stored to call service container hooks like processController/processProvider.
*/
this.modules = (Set.Ω = [[() => module_js_1.AppModule, 'P"7!']], new Set());
this.eventDispatcher = new event_1.EventDispatcher(this.injectorContext);
}
addConfigLoader(loader) {
this.configLoaders.push(loader);
}
/**
* Builds the whole module tree, processes all providers, controllers, and listeners.
* Makes InjectorContext available. Is usually automatically called when the injector is requested.
*/
process() {
if (this.injectorContext)
return;
this.appModule.addProvider({ provide: ServiceContainer, useValue: this });
this.appModule.addProvider({ provide: event_1.EventDispatcher, useValue: this.eventDispatcher });
this.appModule.addProvider({ provide: CliControllerRegistry, useValue: this.cliControllerRegistry });
this.appModule.addProvider({ provide: MiddlewareRegistry, useValue: this.middlewareRegistry });
this.appModule.addProvider({ provide: injector_1.InjectorContext, useFactory: () => this.injectorContext });
this.appModule.addProvider({ provide: stopwatch_1.Stopwatch });
this.appModule.addProvider(logger_1.ConsoleTransport);
if (!this.appModule.isProvided(logger_1.Logger)) {
this.appModule.addProvider({ provide: logger_1.Logger, useFactory: __assignType((t) => new logger_1.Logger([t]), [() => logger_1.ConsoleTransport, 't', '', 'PP7!2""/#']) });
}
this.appModule.addProvider(logger_1.ScopedLogger);
this.setupHook(this.appModule);
this.findModules(this.appModule);
this.processModule(this.appModule);
this.postProcess();
this.injectorContext = new injector_1.InjectorContext(this.appModule);
this.injectorContext.getRootInjector(); //trigger all injector builds
this.bootstrapModules();
}
postProcess() {
for (const m of this.modules) {
m.postProcess();
}
}
findModules(module) {
if (this.modules.has(module))
return;
this.modules.add(module);
for (const m of module.getImports()) {
this.findModules(m);
}
}
getInjectorContext() {
this.process();
return this.injectorContext;
}
setupHook(module) {
let config = module.getConfig();
if (module.configDefinition) {
const schema = type_1.ReflectionClass.from(module.configDefinition);
for (const loader of this.configLoaders) {
loader.load(module, config, schema);
}
//config loads can set arbitrary values (like string for numbers), so we try deserialize them automatically
Object.assign(config, (0, type_1.deserialize)(config, undefined, undefined, undefined, schema.type));
for (const setupConfig of module.setupConfigs)
setupConfig(module, config);
//at this point, no deserialization needs to happen anymore, so validation happens on the config object itself.
const errors = (0, type_1.validate)(config, schema.type);
if (errors.length) {
const errorsMessage = errors.map(__assignType(v => v.toString(module.getName()), ['v', '', 'P"2!"/"'])).join(', ');
throw new module_js_1.ConfigurationInvalidError(`Configuration for module ${module.getName() || 'root'} is invalid. Make sure the module is correctly configured. Error: ` + errorsMessage);
}
}
module.process();
for (const setup of module.setups)
setup(module, config);
for (const importModule of module.getImports()) {
this.setupHook(importModule);
}
return module;
}
bootstrapModules() {
for (const module of this.modules) {
if (module.options.bootstrap) {
this.getInjector(module).get(module.options.bootstrap);
}
for (const use of module.uses) {
const resolvedFunction = (0, injector_1.injectedFunction)(use, this.getInjector(module));
resolvedFunction();
}
}
}
getInjector(moduleOrClass) {
this.process();
if (!(0, core_1.isClass)(moduleOrClass))
return this.getInjectorContext().getInjector(moduleOrClass);
for (const m of this.modules) {
if (m instanceof moduleOrClass) {
return this.getInjectorContext().getInjector(m);
}
}
throw new Error(`No module loaded from type ${(0, core_1.getClassName)(moduleOrClass)}`);
}
getModule(moduleClass) {
this.process();
for (const m of this.modules) {
if (m instanceof moduleClass) {
return m;
}
}
throw new Error(`No module loaded from type ${(0, core_1.getClassName)(moduleClass)}`);
}
/**
* Returns all known instantiated modules.
*/
getModules() {
this.process();
return [...this.modules];
}
getRootInjector() {
this.process();
return this.getInjectorContext().getInjector(this.appModule);
}
processModule(module) {
if (module.injector) {
throw new Error(`Module ${(0, core_1.getClassName)(module)} (id=${module.name}) was already imported. Can not re-use module instances.`);
}
const providers = module.getProviders();
const controllers = module.getControllers();
const commands = module.getCommands();
const listeners = module.getListeners();
const middlewares = module.getMiddlewares();
if (module.options.bootstrap && !(0, core_1.isFunction)(module.options.bootstrap) && !module.isProvided(module.options.bootstrap)) {
providers.push(module.options.bootstrap);
}
for (const w of module.getWorkflows())
this.workflowRegistry.add(w);
for (const middleware of middlewares) {
const config = middleware();
for (const fnOrClassTye of config.getClassTypes()) {
if (!(0, core_1.isClass)(fnOrClassTye))
continue;
if (!(0, injector_1.isProvided)(providers, fnOrClassTye)) {
providers.unshift(fnOrClassTye);
}
}
this.middlewareRegistry.configs.push({ config, module });
}
for (const controller of controllers) {
this.processController(module, { module, controller });
}
for (const command of commands) {
this.processController(module, { module, for: 'cli', ...command });
}
for (const provider of providers) {
this.processProvider(module, (0, injector_1.resolveToken)(provider), provider);
}
for (const listener of listeners) {
if ((0, core_1.isClass)(listener)) {
providers.unshift({ provide: listener });
for (const listenerEntry of this.eventDispatcher.registerListener(listener, module)) {
this.processListener(module, listenerEntry);
}
}
else {
const listenerObject = { fn: listener.callback, order: listener.order, module: listener.module || module };
this.eventDispatcher.add(listener.eventToken, listenerObject);
this.processListener(module, { eventToken: listener.eventToken, listener: listenerObject });
}
}
for (const imp of module.getImports()) {
if (!imp)
continue;
this.processModule(imp);
}
}
processListener(module, listener) {
const addedListener = {
eventToken: listener.eventToken,
reflection: (0, event_1.isEventListenerContainerEntryCallback)(listener.listener)
? type_1.ReflectionFunction.from(listener.listener.fn) : type_1.ReflectionClass.from(listener.listener.classType).getMethod(listener.listener.methodName),
module: listener.listener.module,
order: listener.listener.order,
};
for (const m of this.modules) {
m.processListener(module, addedListener);
}
}
processController(module, controller) {
let name = controller.name || '';
if (controller.controller) {
if (!name) {
const cliConfig = command_js_1.cli._fetch(controller.controller);
if (cliConfig) {
controller.name = name || cliConfig.name || '';
//make sure CLI controllers are provided in cli scope
if (!module.isProvided(controller.controller)) {
module.addProvider({ provide: controller.controller, scope: 'cli' });
}
this.cliControllerRegistry.controllers.push(controller);
}
}
}
else if (controller.for === 'cli') {
this.cliControllerRegistry.controllers.push(controller);
}
for (const m of this.modules) {
m.processController(module, controller);
}
}
processProvider(module, token, provider) {
for (const m of this.modules) {
m.processProvider(module, token, provider);
}
}
}
exports.ServiceContainer = ServiceContainer;
ServiceContainer.__type = ['cliControllerRegistry', function () { return new CliControllerRegistry; }, 'middlewareRegistry', function () { return new MiddlewareRegistry; }, 'workflowRegistry', function () { return new WorkflowRegistry([]); }, () => injector_1.InjectorContext, 'injectorContext', () => event_1.EventDispatcher, 'eventDispatcher', () => __ΩConfigLoader, 'configLoaders', function () { return []; }, 'modules', function () { return (Set.Ω = [[() => module_js_1.AppModule, 'P"7!']], new Set()); }, () => module_js_1.AppModule, 'appModule', 'constructor', () => __ΩConfigLoader, 'loader', 'addConfigLoader', 'process', 'postProcess', () => module_js_1.AppModule, 'module', 'findModules', () => injector_1.InjectorContext, 'getInjectorContext', () => module_js_1.AppModule, 'setupHook', 'bootstrapModules', () => __ΩClassType, 'moduleOrClass', () => injector_1.Injector, 'getInjector', () => __ΩClassType, () => module_js_1.AppModule, 'moduleClass', () => module_js_1.AppModule, 'getModule', () => module_js_1.AppModule, 'getModules', () => injector_1.Injector, 'getRootInjector', () => __ΩModuleDefinition, () => module_js_1.AppModule, 'processModule', () => module_js_1.AppModule, () => __ΩEventListenerRegistered, 'listener', 'processListener', () => module_js_1.AppModule, 'ControllerConfig', 'controller', 'processController', () => module_js_1.AppModule, 'Token', 'token', 'ProviderWithScope', 'provider', 'processProvider', 'ServiceContainer', '!3!9>"!3#9>$!3%9>&P7\'3(8<P7)3*<n+F3,<>-!3.<>/PP"7021:"02Pn324"05P"06P"07<PP"7829"0:<PP7;0<PP"7=29"0>;P$0?<PP"o@""J2AP7B0CPP"7EoD"2FP"7G0HPP"7IF0JPP7K0LPPnM7N29$0O<PP"7P29nQ2R"0S<PP"7T29"wU2V"0W<PP"7X29"wY2Z"w[2\\"0]<5w^'];
//# sourceMappingURL=service-container.js.map