UNPKG

@deepkit/framework

Version:

371 lines 20.7 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InMemoryApplicationServer = exports.ApplicationServer = exports.__ΩApplicationServerOptions = exports.LogStartupListener = exports.onServerWorkerShutdown = exports.onServerMainShutdown = exports.onServerShutdown = exports.ServerShutdownEvent = exports.onServerWorkerBootstrapDone = exports.onServerWorkerBootstrap = exports.onServerMainBootstrapDone = exports.onServerMainBootstrap = exports.onServerBootstrapDone = exports.onServerBootstrap = exports.ServerBootstrapEvent = void 0; const __ΩPick = ['T', 'K', 'Pick', 'l+e#!e"!fRb!b"Pde""N#!w#y']; /*@ts-ignore*/ const { __ΩLoggerInterface } = require('@deepkit/logger'); 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 rpc_1 = require("@deepkit/rpc"); const cluster_1 = __importDefault(require("cluster")); const http_1 = require("@deepkit/http"); const event_1 = require("@deepkit/event"); const injector_1 = require("@deepkit/injector"); const module_config_js_1 = require("./module.config.js"); const worker_js_1 = require("./worker.js"); const rpc_js_1 = require("./rpc.js"); require("@deepkit/type"); class ServerBootstrapEvent extends event_1.BaseEvent { } exports.ServerBootstrapEvent = ServerBootstrapEvent; ServerBootstrapEvent.__type = [() => event_1.BaseEvent, 'ServerBootstrapEvent', 'P7!5w"']; /** * Called only once for application server bootstrap (for main process and workers) */ exports.onServerBootstrap = new event_1.EventToken('server.bootstrap', ServerBootstrapEvent); /** * Called only once for application server bootstrap (for main process and workers) * as soon as the application server has started */ exports.onServerBootstrapDone = new event_1.EventToken('server.bootstrapDone', ServerBootstrapEvent); /** * Called only once for application server bootstrap (in the main process) * as soon as the application server starts. */ exports.onServerMainBootstrap = new event_1.EventToken('server.main.bootstrap', ServerBootstrapEvent); /** * Called only once for application server bootstrap (in the main process) * as soon as the application server has started. */ exports.onServerMainBootstrapDone = new event_1.EventToken('server.main.bootstrapDone', ServerBootstrapEvent); /** * Called for each worker as soon as the worker bootstraps. */ exports.onServerWorkerBootstrap = new event_1.EventToken('server.worker.bootstrap', ServerBootstrapEvent); /** * Called only once for application server bootstrap (in the worker process) * as soon as the application server has started. */ exports.onServerWorkerBootstrapDone = new event_1.EventToken('server.worker.bootstrapDone', ServerBootstrapEvent); class ServerShutdownEvent extends event_1.BaseEvent { } exports.ServerShutdownEvent = ServerShutdownEvent; ServerShutdownEvent.__type = [() => event_1.BaseEvent, 'ServerShutdownEvent', 'P7!5w"']; /** * Called when application server shuts down (in master process and each worker). */ exports.onServerShutdown = new event_1.EventToken('server.shutdown', ServerBootstrapEvent); /** * Called when application server shuts down in the main process. */ exports.onServerMainShutdown = new event_1.EventToken('server.main.shutdown', ServerBootstrapEvent); /** * Called when application server shuts down in the worker process. */ exports.onServerWorkerShutdown = new event_1.EventToken('server.worker.shutdown', ServerBootstrapEvent); const __ΩApplicationServerConfig = [() => __ΩPick, () => module_config_js_1.FrameworkConfig, "server", "port", "host", "httpsPort", "ssl", "sslKey", "sslCertificate", "sslCa", "sslCrl", "varPath", "selfSigned", "workers", "publicDir", "debug", "debugUrl", "gracefulShutdownTimeout", "compression", "http", "logStartup", 'ApplicationServerConfig', 'P7"P.#.$.%.&.\'.(.).*.+.,.-.../.0.1.2.3.4.5Jo!#w6y']; function needsHttpWorker(config, rpcControllers, router) { return Boolean(config.publicDir || rpcControllers.controllers.size || router.getRoutes().length); } needsHttpWorker.__type = ['publicDir', 'config', () => rpc_js_1.RpcControllers, 'rpcControllers', () => http_1.HttpRouter, 'router', 'needsHttpWorker', 'PP&4!8M2"P7#2$P7%2&"/\'']; class LogStartupListener { constructor(logger, rpcControllers, router, config, server) { this.logger = logger; this.rpcControllers = rpcControllers; this.router = router; this.config = config; this.server = server; } onBootstrapDone() { for (const [name, controller] of this.rpcControllers.controllers.entries()) { this.logger.log('RPC Controller', `<green>${(0, core_1.getClassName)(controller.controller)}</green>`, `<grey>${name}</grey>`); } const routes = this.router.getRoutes(); if (routes.length) { this.logger.log(`<green>${routes.length}</green> HTTP routes`); let lastController = undefined; for (const route of routes) { if (route.internal) continue; if (route.action.type === 'controller' && lastController !== route.action.controller) { lastController = route.action.controller; this.logger.log(`HTTP Controller <green>${(0, core_1.getClassName)(lastController)}</green>`); } this.logger.log(` <green>${route.httpMethods.length === 0 ? 'ANY' : route.httpMethods.join(',')}</green> <yellow>${route.getFullPath()}</yellow>`); } } if (this.config.server) { this.logger.log(`Server up and running`); } else { const host = this.server.getHttpHost(); if (host) { let url = `http://${host}`; if (this.config.ssl) { url = `https://${host}:${this.config.httpsPort || this.config.port}`; } this.logger.log(`HTTP listening at <yellow>${url}</yellow>`); if (this.config.debug) { this.logger.log(`Debugger enabled at <yellow>${url}${(0, core_1.urlJoin)('/', this.config.debugUrl, '/')}</yellow>`); } } } } } exports.LogStartupListener = LogStartupListener; LogStartupListener.__type = [() => __ΩLoggerInterface, 'logger', () => rpc_js_1.RpcControllers, 'rpcControllers', () => http_1.HttpRouter, 'router', () => __ΩApplicationServerConfig, 'config', () => ApplicationServer, 'server', 'constructor', 'onBootstrapDone', 'LogStartupListener', 'Pn!2"<P7#2$<P7%2&<n\'2(<P7)2*<"0+P"0,5w-']; __decorate([ event_1.eventDispatcher.listen(exports.onServerMainBootstrapDone), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], LogStartupListener.prototype, "onBootstrapDone", null); const __ΩApplicationServerOptions = ['listenOnSignals', 'startHttpServer', 'ApplicationServerOptions', 'P)4!8)4"8Mw#y']; exports.__ΩApplicationServerOptions = __ΩApplicationServerOptions; class ApplicationServer { constructor(logger, webWorkerFactory, eventDispatcher, rootScopedContext, config, rpcControllers, rpcKernel, router) { this.logger = logger; this.webWorkerFactory = webWorkerFactory; this.eventDispatcher = eventDispatcher; this.rootScopedContext = rootScopedContext; this.config = config; this.rpcControllers = rpcControllers; this.rpcKernel = rpcKernel; this.router = router; this.started = false; this.stopping = false; this.onlineWorkers = 0; this.needsHttpWorker = needsHttpWorker(config, rpcControllers, router); this.onStop = new Promise(__assignType((resolve) => this.stopResolver = resolve, ['resolve', '', 'P"2!"/"'])); } getHttpWorker() { if (!this.httpWorker) throw new Error('HTTP worker not started'); return this.httpWorker; } /** * Closes all server listener and triggers shutdown events. This is only used for integration tests. */ async close(graceful = false) { if (!this.started) return; await this.stopWorkers(); await this.eventDispatcher.dispatch(exports.onServerShutdown, new ServerShutdownEvent()); await this.eventDispatcher.dispatch(exports.onServerMainShutdown, new ServerShutdownEvent()); if (this.httpWorker) await this.httpWorker.close(graceful); } stopWorkers() { if (this.config.workers === 0) return Promise.resolve(); return (0, core_1.asyncOperation)(__assignType((resolve) => { cluster_1.default.on('exit', async () => { if (this.onlineWorkers === 0) { this.logger.debug('All workers offline. Shutting down ...'); await this.eventDispatcher.dispatch(exports.onServerShutdown, new ServerShutdownEvent()); await this.eventDispatcher.dispatch(exports.onServerMainShutdown, new ServerShutdownEvent()); resolve(undefined); } }); for (const worker of Object.values(cluster_1.default.workers || {})) { if (worker) worker.send('stop'); } }, ['resolve', '', 'P"2!"/"'])); } async start(optionsOrListenOnSignal = false) { const options = typeof optionsOrListenOnSignal === 'boolean' ? { listenOnSignals: optionsOrListenOnSignal } : optionsOrListenOnSignal; if (this.started) throw new Error('ApplicationServer already started'); this.started = true; if (cluster_1.default.isMaster && this.config.logStartup) { if (this.config.workers) { this.logger.log(`Start server, using ${this.config.workers} workers ...`); } else { this.logger.log(`Start server ...`); } } await this.eventDispatcher.dispatch(exports.onServerBootstrap, new ServerBootstrapEvent()); const startHttpServer = options.startHttpServer !== false; let killRequests = 0; if (this.config.workers > 1 && startHttpServer) { if (cluster_1.default.isMaster) { await this.eventDispatcher.dispatch(exports.onServerMainBootstrap, new ServerBootstrapEvent()); for (let i = 0; i < this.config.workers; i++) { cluster_1.default.fork(); } await (0, core_1.asyncOperation)(__assignType((resolve) => { cluster_1.default.on('online', () => { this.onlineWorkers++; if (this.onlineWorkers === this.config.workers) resolve(undefined); }); cluster_1.default.on('exit', __assignType((w) => { this.onlineWorkers--; if (this.stopping) return; this.logger.warn(`Worker ${w.id} died. Restarted`); cluster_1.default.fork(); }, ['w', '', 'P"2!"/"'])); }, ['resolve', '', 'P"2!"/"'])); if (options.listenOnSignals) { const stopServer = __assignType((signal) => async () => { killRequests++; if (killRequests === 3) { this.logger.warn(`Received ${signal}. Force stopping server ...`); process.exit(1); return; } if (this.stopping) { this.logger.warn(`Received ${signal}. Stopping already in process. Try again to force stop.`); return; } this.stopping = true; this.logger.warn(`Received ${signal}. Stopping server ...`); await this.stopWorkers(); this.stopResolver(); setTimeout(() => { //give onAppShutdown a chance to react process.exit(0); }, 10); }, ['signal', '', 'P&2!"/"']); process.on('SIGINT', stopServer('SIGINT')); process.on('SIGTERM', stopServer('SIGTERM')); } await this.eventDispatcher.dispatch(exports.onServerBootstrapDone, new ServerBootstrapEvent()); await this.eventDispatcher.dispatch(exports.onServerMainBootstrapDone, new ServerBootstrapEvent()); } else { process.on('message', __assignType(async (msg) => { if (msg === 'stop') { await this.eventDispatcher.dispatch(exports.onServerShutdown, new ServerShutdownEvent()); await this.eventDispatcher.dispatch(exports.onServerWorkerShutdown, new ServerShutdownEvent()); if (this.httpWorker) await this.httpWorker.close(true); process.exit(0); } }, ['msg', '', 'P"2!"/"'])); process.on('SIGINT', async () => { //we don't do anything in sigint, as the master controls our process. //we need to register to it though so the process doesn't get killed. }); process.on('SIGTERM', async () => { //we don't do anything in sigint, as the master controls our process. //we need to register to it though so the process doesn't get killed. }); await this.eventDispatcher.dispatch(exports.onServerWorkerBootstrap, new ServerBootstrapEvent()); if (this.needsHttpWorker) { this.httpWorker = this.webWorkerFactory.create(cluster_1.default.worker.id, this.config); this.httpWorker.start(); } await this.eventDispatcher.dispatch(exports.onServerBootstrapDone, new ServerBootstrapEvent()); await this.eventDispatcher.dispatch(exports.onServerWorkerBootstrapDone, new ServerBootstrapEvent()); } } else { if (options.listenOnSignals) { const stopServer = __assignType((signal) => async () => { killRequests++; if (killRequests === 3) { this.logger.warn(`Received ${signal}. Force stopping server ...`); process.exit(1); return; } if (this.stopping) { this.logger.warn(`Received ${signal}. Stopping already in process. Try again to force stop.`); return; } this.stopping = true; this.logger.warn('Received SIGINT. Stopping server ...'); await this.eventDispatcher.dispatch(exports.onServerShutdown, new ServerShutdownEvent()); await this.eventDispatcher.dispatch(exports.onServerMainShutdown, new ServerShutdownEvent()); if (this.httpWorker) await this.httpWorker.close(true); this.stopResolver(); setTimeout(() => { //give onAppShutdown a chance to react process.exit(0); }, 10); }, ['signal', '', 'P&2!"/"']); process.on('SIGINT', stopServer('SIGINT')); process.on('SIGTERM', stopServer('SIGTERM')); } await this.eventDispatcher.dispatch(exports.onServerBootstrap, new ServerBootstrapEvent()); await this.eventDispatcher.dispatch(exports.onServerMainBootstrap, new ServerBootstrapEvent()); if (this.needsHttpWorker && startHttpServer) { this.httpWorker = this.webWorkerFactory.create(1, this.config); this.httpWorker.start(); } await this.eventDispatcher.dispatch(exports.onServerBootstrapDone, new ServerBootstrapEvent()); await this.eventDispatcher.dispatch(exports.onServerMainBootstrapDone, new ServerBootstrapEvent()); } if (cluster_1.default.isMaster && this.config.logStartup) { this.logger.log(`Server started.`); } return options.listenOnSignals ? this.onStop : Promise.resolve(); } getHttpHost() { const port = this.config.ssl ? this.config.httpsPort || this.config.port : this.config.port; return this.httpWorker !== undefined ? `${this.config.host}:${port}` : undefined; } getWorker() { if (!this.httpWorker) throw new Error('No WebWorker registered yet. Did you start()?'); return this.httpWorker; } createClient() { const context = this.rootScopedContext; const rpcKernel = this.rpcKernel; return new rpc_1.RpcClient({ connect(connection) { const kernelConnection = (0, worker_js_1.createRpcConnection)(context, rpcKernel, { writeBinary: __assignType((buffer) => connection.readBinary(buffer), ['buffer', '', 'P"2!"/"']), close: () => connection.onClose('closed'), }); connection.onConnected({ close: __assignType(function close() { kernelConnection.close(); }, ['close', 'P"/!']), writeBinary: __assignType(function writeBinary(message) { queueMicrotask(() => { kernelConnection.feed(message); }); }, ['message', 'writeBinary', 'PW2!"/"']) }); } }); } } exports.ApplicationServer = ApplicationServer; ApplicationServer.__type = [() => worker_js_1.WebWorker, 'httpWorker', 'started', function () { return false; }, 'stopping', function () { return false; }, 'onlineWorkers', function () { return 0; }, 'needsHttpWorker', 'onStop', '', 'stopResolver', () => __ΩLoggerInterface, 'logger', () => worker_js_1.WebWorkerFactory, 'webWorkerFactory', () => event_1.EventDispatcher, 'eventDispatcher', () => injector_1.InjectorContext, 'rootScopedContext', () => __ΩApplicationServerConfig, 'config', () => rpc_js_1.RpcControllers, 'rpcControllers', () => rpc_1.RpcKernel, 'rpcKernel', () => http_1.HttpRouter, 'router', 'constructor', () => worker_js_1.WebWorker, 'getHttpWorker', 'graceful', 'close', 'stopWorkers', () => __ΩApplicationServerOptions, 'optionsOrListenOnSignal', () => false, 'start', 'getHttpHost', () => worker_js_1.WebWorker, 'getWorker', () => rpc_1.RpcClient, 'createClient', 'ApplicationServer', 'P7!3"8<)3#<>$)3%<>&\'3\'<>()3)<$`3*P$/+3,<Pn-2.<P7/20<P7122<P7324<n526:P7728<P792:<P7;2<<"0=PP7>0?P"2@"0AP$`0B<PP)nCJ2D>E"0FPP&-J0GPP7H0IPP7J0K5wL']; class InMemoryApplicationServer extends ApplicationServer { } exports.InMemoryApplicationServer = InMemoryApplicationServer; InMemoryApplicationServer.__type = [() => ApplicationServer, 'InMemoryApplicationServer', 'P7!5w"']; //# sourceMappingURL=application-server.js.map