@lakutata/entrypoint 3.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +183 -0
- package/THIRD_PARTY_NOTICES.md +206 -0
- package/dist/cjs/Entrypoint.d.ts +868 -0
- package/dist/cjs/Entrypoint.js +998 -0
- package/dist/cjs/adapter/commander/CommanderCLIEntrypoint.d.ts +192 -0
- package/dist/cjs/adapter/commander/CommanderCLIEntrypoint.js +439 -0
- package/dist/cjs/adapter/commander/Prompt.d.ts +22 -0
- package/dist/cjs/adapter/commander/Prompt.js +164 -0
- package/dist/cjs/adapter/commander/Width.d.ts +12 -0
- package/dist/cjs/adapter/commander/Width.js +67 -0
- package/dist/cjs/adapter/electron/ElectronIPCServiceEntrypoint.d.ts +203 -0
- package/dist/cjs/adapter/electron/ElectronIPCServiceEntrypoint.js +249 -0
- package/dist/cjs/adapter/express/ExpressHTTPEntrypoint.d.ts +172 -0
- package/dist/cjs/adapter/express/ExpressHTTPEntrypoint.js +301 -0
- package/dist/cjs/adapter/express/Responders.d.ts +89 -0
- package/dist/cjs/adapter/express/Responders.js +71 -0
- package/dist/cjs/adapter/express/StaticUI.d.ts +83 -0
- package/dist/cjs/adapter/express/StaticUI.js +104 -0
- package/dist/cjs/adapter/fastify/FastifyHTTPEntrypoint.d.ts +156 -0
- package/dist/cjs/adapter/fastify/FastifyHTTPEntrypoint.js +339 -0
- package/dist/cjs/adapter/fastify/Responders.d.ts +88 -0
- package/dist/cjs/adapter/fastify/Responders.js +77 -0
- package/dist/cjs/adapter/http/HTTPServiceEntrypoint.d.ts +117 -0
- package/dist/cjs/adapter/http/HTTPServiceEntrypoint.js +257 -0
- package/dist/cjs/adapter/jsonrpc/JSONRPCProtocol.d.ts +194 -0
- package/dist/cjs/adapter/jsonrpc/JSONRPCProtocol.js +187 -0
- package/dist/cjs/adapter/jsonrpc/JSONRPCServiceEntrypoint.d.ts +154 -0
- package/dist/cjs/adapter/jsonrpc/JSONRPCServiceEntrypoint.js +397 -0
- package/dist/cjs/adapter/schedule/ScheduleEntrypoint.d.ts +86 -0
- package/dist/cjs/adapter/schedule/ScheduleEntrypoint.js +275 -0
- package/dist/cjs/adapter/socket.io/SocketIOServiceEntrypoint.d.ts +138 -0
- package/dist/cjs/adapter/socket.io/SocketIOServiceEntrypoint.js +216 -0
- package/dist/cjs/channel/BuiltinChannels.d.ts +18 -0
- package/dist/cjs/channel/BuiltinChannels.js +128 -0
- package/dist/cjs/channel/Channel.d.ts +196 -0
- package/dist/cjs/channel/Channel.js +106 -0
- package/dist/cjs/channel/EntrypointAdapter.d.ts +195 -0
- package/dist/cjs/channel/EntrypointAdapter.js +43 -0
- package/dist/cjs/context/BaseContext.d.ts +75 -0
- package/dist/cjs/context/BaseContext.js +90 -0
- package/dist/cjs/context/CLIContext.d.ts +38 -0
- package/dist/cjs/context/CLIContext.js +53 -0
- package/dist/cjs/context/HTTPContext.d.ts +52 -0
- package/dist/cjs/context/HTTPContext.js +78 -0
- package/dist/cjs/context/ScheduleContext.d.ts +47 -0
- package/dist/cjs/context/ScheduleContext.js +65 -0
- package/dist/cjs/context/ServiceContext.d.ts +47 -0
- package/dist/cjs/context/ServiceContext.js +62 -0
- package/dist/cjs/decorators/CLIAction.d.ts +120 -0
- package/dist/cjs/decorators/CLIAction.js +13 -0
- package/dist/cjs/decorators/HTTPAction.d.ts +178 -0
- package/dist/cjs/decorators/HTTPAction.js +14 -0
- package/dist/cjs/decorators/Prefix.d.ts +44 -0
- package/dist/cjs/decorators/Prefix.js +48 -0
- package/dist/cjs/decorators/Schedule.d.ts +120 -0
- package/dist/cjs/decorators/Schedule.js +64 -0
- package/dist/cjs/decorators/ServiceAction.d.ts +116 -0
- package/dist/cjs/decorators/ServiceAction.js +12 -0
- package/dist/cjs/decorators/UseMiddlewares.d.ts +52 -0
- package/dist/cjs/decorators/UseMiddlewares.js +58 -0
- package/dist/cjs/decorators/http/DELETE.d.ts +87 -0
- package/dist/cjs/decorators/http/DELETE.js +7 -0
- package/dist/cjs/decorators/http/GET.d.ts +89 -0
- package/dist/cjs/decorators/http/GET.js +7 -0
- package/dist/cjs/decorators/http/HEAD.d.ts +84 -0
- package/dist/cjs/decorators/http/HEAD.js +7 -0
- package/dist/cjs/decorators/http/OPTIONS.d.ts +80 -0
- package/dist/cjs/decorators/http/OPTIONS.js +7 -0
- package/dist/cjs/decorators/http/PATCH.d.ts +90 -0
- package/dist/cjs/decorators/http/PATCH.js +7 -0
- package/dist/cjs/decorators/http/POST.d.ts +91 -0
- package/dist/cjs/decorators/http/POST.js +7 -0
- package/dist/cjs/decorators/http/PUT.d.ts +92 -0
- package/dist/cjs/decorators/http/PUT.js +7 -0
- package/dist/cjs/exceptions/AccessDeniedException.d.ts +10 -0
- package/dist/cjs/exceptions/AccessDeniedException.js +17 -0
- package/dist/cjs/exceptions/ControllerActionNotFoundException.d.ts +9 -0
- package/dist/cjs/exceptions/ControllerActionNotFoundException.js +16 -0
- package/dist/cjs/exceptions/DuplicateActionNameException.d.ts +8 -0
- package/dist/cjs/exceptions/DuplicateActionNameException.js +15 -0
- package/dist/cjs/exceptions/EntrypointStoppedException.d.ts +9 -0
- package/dist/cjs/exceptions/EntrypointStoppedException.js +16 -0
- package/dist/cjs/exceptions/InvalidActionGroupException.d.ts +4 -0
- package/dist/cjs/exceptions/InvalidActionGroupException.js +11 -0
- package/dist/cjs/exceptions/InvalidChannelException.d.ts +8 -0
- package/dist/cjs/exceptions/InvalidChannelException.js +15 -0
- package/dist/cjs/exceptions/InvalidEntrypointAdapterException.d.ts +9 -0
- package/dist/cjs/exceptions/InvalidEntrypointAdapterException.js +16 -0
- package/dist/cjs/exceptions/InvalidMiddlewareException.d.ts +8 -0
- package/dist/cjs/exceptions/InvalidMiddlewareException.js +15 -0
- package/dist/cjs/exceptions/InvalidPrefixException.d.ts +8 -0
- package/dist/cjs/exceptions/InvalidPrefixException.js +15 -0
- package/dist/cjs/exceptions/MiddlewareNextCalledException.d.ts +8 -0
- package/dist/cjs/exceptions/MiddlewareNextCalledException.js +15 -0
- package/dist/cjs/exceptions/RouteNotFoundException.d.ts +9 -0
- package/dist/cjs/exceptions/RouteNotFoundException.js +16 -0
- package/dist/cjs/exceptions/ServiceInvokeException.d.ts +11 -0
- package/dist/cjs/exceptions/ServiceInvokeException.js +14 -0
- package/dist/cjs/exports/CTRLDecorators.d.ts +13 -0
- package/dist/cjs/exports/CTRLDecorators.js +38 -0
- package/dist/cjs/exports/Channel.d.ts +14 -0
- package/dist/cjs/exports/Channel.js +33 -0
- package/dist/cjs/exports/CommanderAdapter.d.ts +1 -0
- package/dist/cjs/exports/CommanderAdapter.js +8 -0
- package/dist/cjs/exports/ElectronAdapter.d.ts +1 -0
- package/dist/cjs/exports/ElectronAdapter.js +9 -0
- package/dist/cjs/exports/Entrypoint.d.ts +24 -0
- package/dist/cjs/exports/Entrypoint.js +55 -0
- package/dist/cjs/exports/ExpressAdapter.d.ts +4 -0
- package/dist/cjs/exports/ExpressAdapter.js +15 -0
- package/dist/cjs/exports/FastifyAdapter.d.ts +4 -0
- package/dist/cjs/exports/FastifyAdapter.js +14 -0
- package/dist/cjs/exports/JSONRPCAdapter.d.ts +2 -0
- package/dist/cjs/exports/JSONRPCAdapter.js +11 -0
- package/dist/cjs/exports/SocketIOAdapter.d.ts +1 -0
- package/dist/cjs/exports/SocketIOAdapter.js +9 -0
- package/dist/cjs/lib/AccessControl.d.ts +118 -0
- package/dist/cjs/lib/AccessControl.js +208 -0
- package/dist/cjs/lib/AccessControlRule.d.ts +91 -0
- package/dist/cjs/lib/AccessControlRule.js +76 -0
- package/dist/cjs/lib/ActionOptions.d.ts +67 -0
- package/dist/cjs/lib/ActionOptions.js +67 -0
- package/dist/cjs/lib/ActionResult.d.ts +28 -0
- package/dist/cjs/lib/ActionResult.js +28 -0
- package/dist/cjs/lib/ApplicationOf.d.ts +6 -0
- package/dist/cjs/lib/ApplicationOf.js +14 -0
- package/dist/cjs/lib/Controller.d.ts +59 -0
- package/dist/cjs/lib/Controller.js +64 -0
- package/dist/cjs/lib/ControllerEntrypoint.d.ts +168 -0
- package/dist/cjs/lib/ControllerEntrypoint.js +239 -0
- package/dist/cjs/lib/GetActionDTOAndOptions.d.ts +29 -0
- package/dist/cjs/lib/GetActionDTOAndOptions.js +49 -0
- package/dist/cjs/lib/HTTPErrors.d.ts +55 -0
- package/dist/cjs/lib/HTTPErrors.js +52 -0
- package/dist/cjs/lib/Middleware.d.ts +146 -0
- package/dist/cjs/lib/Middleware.js +144 -0
- package/dist/cjs/lib/Prefix.d.ts +98 -0
- package/dist/cjs/lib/Prefix.js +151 -0
- package/dist/cjs/lib/Result.d.ts +173 -0
- package/dist/cjs/lib/Result.js +153 -0
- package/dist/cjs/lib/ServiceResponseCodec.d.ts +82 -0
- package/dist/cjs/lib/ServiceResponseCodec.js +117 -0
- package/dist/cjs/lib/StringifyPattern.d.ts +7 -0
- package/dist/cjs/lib/StringifyPattern.js +58 -0
- package/dist/cjs/lib/UnsetValue.d.ts +12 -0
- package/dist/cjs/lib/UnsetValue.js +216 -0
- package/dist/cjs/lib/pattern/GlobPattern.d.ts +34 -0
- package/dist/cjs/lib/pattern/GlobPattern.js +68 -0
- package/dist/cjs/lib/pattern/PatternManager.d.ts +103 -0
- package/dist/cjs/lib/pattern/PatternManager.js +233 -0
- package/dist/cjs/lib/schedule/Cron.d.ts +56 -0
- package/dist/cjs/lib/schedule/Cron.js +282 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/types/CLIContextParams.d.ts +14 -0
- package/dist/cjs/types/CLIContextParams.js +2 -0
- package/dist/cjs/types/ErrorDescription.d.ts +14 -0
- package/dist/cjs/types/ErrorDescription.js +2 -0
- package/dist/cjs/types/HTTPContextParams.d.ts +27 -0
- package/dist/cjs/types/HTTPContextParams.js +2 -0
- package/dist/cjs/types/ScheduleContextParams.d.ts +21 -0
- package/dist/cjs/types/ScheduleContextParams.js +2 -0
- package/dist/cjs/types/ServiceContextParams.d.ts +16 -0
- package/dist/cjs/types/ServiceContextParams.js +2 -0
- package/dist/esm/Entrypoint.js +985 -0
- package/dist/esm/adapter/commander/CommanderCLIEntrypoint.js +433 -0
- package/dist/esm/adapter/commander/Prompt.js +127 -0
- package/dist/esm/adapter/commander/Width.js +63 -0
- package/dist/esm/adapter/electron/ElectronIPCServiceEntrypoint.js +212 -0
- package/dist/esm/adapter/express/ExpressHTTPEntrypoint.js +295 -0
- package/dist/esm/adapter/express/Responders.js +66 -0
- package/dist/esm/adapter/express/StaticUI.js +96 -0
- package/dist/esm/adapter/fastify/FastifyHTTPEntrypoint.js +333 -0
- package/dist/esm/adapter/fastify/Responders.js +73 -0
- package/dist/esm/adapter/http/HTTPServiceEntrypoint.js +253 -0
- package/dist/esm/adapter/jsonrpc/JSONRPCProtocol.js +182 -0
- package/dist/esm/adapter/jsonrpc/JSONRPCServiceEntrypoint.js +361 -0
- package/dist/esm/adapter/schedule/ScheduleEntrypoint.js +272 -0
- package/dist/esm/adapter/socket.io/SocketIOServiceEntrypoint.js +212 -0
- package/dist/esm/channel/BuiltinChannels.js +125 -0
- package/dist/esm/channel/Channel.js +100 -0
- package/dist/esm/channel/EntrypointAdapter.js +39 -0
- package/dist/esm/context/BaseContext.js +87 -0
- package/dist/esm/context/CLIContext.js +49 -0
- package/dist/esm/context/HTTPContext.js +74 -0
- package/dist/esm/context/ScheduleContext.js +61 -0
- package/dist/esm/context/ServiceContext.js +58 -0
- package/dist/esm/decorators/CLIAction.js +10 -0
- package/dist/esm/decorators/HTTPAction.js +11 -0
- package/dist/esm/decorators/Prefix.js +45 -0
- package/dist/esm/decorators/Schedule.js +61 -0
- package/dist/esm/decorators/ServiceAction.js +9 -0
- package/dist/esm/decorators/UseMiddlewares.js +55 -0
- package/dist/esm/decorators/http/DELETE.js +4 -0
- package/dist/esm/decorators/http/GET.js +4 -0
- package/dist/esm/decorators/http/HEAD.js +4 -0
- package/dist/esm/decorators/http/OPTIONS.js +4 -0
- package/dist/esm/decorators/http/PATCH.js +4 -0
- package/dist/esm/decorators/http/POST.js +4 -0
- package/dist/esm/decorators/http/PUT.js +4 -0
- package/dist/esm/exceptions/AccessDeniedException.js +13 -0
- package/dist/esm/exceptions/ControllerActionNotFoundException.js +12 -0
- package/dist/esm/exceptions/DuplicateActionNameException.js +11 -0
- package/dist/esm/exceptions/EntrypointStoppedException.js +12 -0
- package/dist/esm/exceptions/InvalidActionGroupException.js +7 -0
- package/dist/esm/exceptions/InvalidChannelException.js +11 -0
- package/dist/esm/exceptions/InvalidEntrypointAdapterException.js +12 -0
- package/dist/esm/exceptions/InvalidMiddlewareException.js +11 -0
- package/dist/esm/exceptions/InvalidPrefixException.js +11 -0
- package/dist/esm/exceptions/MiddlewareNextCalledException.js +11 -0
- package/dist/esm/exceptions/RouteNotFoundException.js +12 -0
- package/dist/esm/exceptions/ServiceInvokeException.js +10 -0
- package/dist/esm/exports/CTRLDecorators.js +19 -0
- package/dist/esm/exports/Channel.js +11 -0
- package/dist/esm/exports/CommanderAdapter.js +1 -0
- package/dist/esm/exports/ElectronAdapter.js +1 -0
- package/dist/esm/exports/Entrypoint.js +22 -0
- package/dist/esm/exports/ExpressAdapter.js +3 -0
- package/dist/esm/exports/FastifyAdapter.js +3 -0
- package/dist/esm/exports/JSONRPCAdapter.js +2 -0
- package/dist/esm/exports/SocketIOAdapter.js +1 -0
- package/dist/esm/lib/AccessControl.js +204 -0
- package/dist/esm/lib/AccessControlRule.js +72 -0
- package/dist/esm/lib/ActionOptions.js +63 -0
- package/dist/esm/lib/ActionResult.js +24 -0
- package/dist/esm/lib/ApplicationOf.js +11 -0
- package/dist/esm/lib/Controller.js +61 -0
- package/dist/esm/lib/ControllerEntrypoint.js +229 -0
- package/dist/esm/lib/GetActionDTOAndOptions.js +46 -0
- package/dist/esm/lib/HTTPErrors.js +48 -0
- package/dist/esm/lib/Middleware.js +134 -0
- package/dist/esm/lib/Prefix.js +143 -0
- package/dist/esm/lib/Result.js +147 -0
- package/dist/esm/lib/ServiceResponseCodec.js +113 -0
- package/dist/esm/lib/StringifyPattern.js +55 -0
- package/dist/esm/lib/UnsetValue.js +213 -0
- package/dist/esm/lib/pattern/GlobPattern.js +64 -0
- package/dist/esm/lib/pattern/PatternManager.js +229 -0
- package/dist/esm/lib/schedule/Cron.js +277 -0
- package/dist/esm/types/CLIContextParams.js +1 -0
- package/dist/esm/types/ErrorDescription.js +1 -0
- package/dist/esm/types/HTTPContextParams.js +1 -0
- package/dist/esm/types/ScheduleContextParams.js +1 -0
- package/dist/esm/types/ServiceContextParams.js +1 -0
- package/dist/types/Entrypoint.d.ts +868 -0
- package/dist/types/adapter/commander/CommanderCLIEntrypoint.d.ts +192 -0
- package/dist/types/adapter/commander/Prompt.d.ts +22 -0
- package/dist/types/adapter/commander/Width.d.ts +12 -0
- package/dist/types/adapter/electron/ElectronIPCServiceEntrypoint.d.ts +203 -0
- package/dist/types/adapter/express/ExpressHTTPEntrypoint.d.ts +172 -0
- package/dist/types/adapter/express/Responders.d.ts +89 -0
- package/dist/types/adapter/express/StaticUI.d.ts +83 -0
- package/dist/types/adapter/fastify/FastifyHTTPEntrypoint.d.ts +156 -0
- package/dist/types/adapter/fastify/Responders.d.ts +88 -0
- package/dist/types/adapter/http/HTTPServiceEntrypoint.d.ts +117 -0
- package/dist/types/adapter/jsonrpc/JSONRPCProtocol.d.ts +194 -0
- package/dist/types/adapter/jsonrpc/JSONRPCServiceEntrypoint.d.ts +154 -0
- package/dist/types/adapter/schedule/ScheduleEntrypoint.d.ts +86 -0
- package/dist/types/adapter/socket.io/SocketIOServiceEntrypoint.d.ts +138 -0
- package/dist/types/channel/BuiltinChannels.d.ts +18 -0
- package/dist/types/channel/Channel.d.ts +196 -0
- package/dist/types/channel/EntrypointAdapter.d.ts +195 -0
- package/dist/types/context/BaseContext.d.ts +75 -0
- package/dist/types/context/CLIContext.d.ts +38 -0
- package/dist/types/context/HTTPContext.d.ts +52 -0
- package/dist/types/context/ScheduleContext.d.ts +47 -0
- package/dist/types/context/ServiceContext.d.ts +47 -0
- package/dist/types/decorators/CLIAction.d.ts +120 -0
- package/dist/types/decorators/HTTPAction.d.ts +178 -0
- package/dist/types/decorators/Prefix.d.ts +44 -0
- package/dist/types/decorators/Schedule.d.ts +120 -0
- package/dist/types/decorators/ServiceAction.d.ts +116 -0
- package/dist/types/decorators/UseMiddlewares.d.ts +52 -0
- package/dist/types/decorators/http/DELETE.d.ts +87 -0
- package/dist/types/decorators/http/GET.d.ts +89 -0
- package/dist/types/decorators/http/HEAD.d.ts +84 -0
- package/dist/types/decorators/http/OPTIONS.d.ts +80 -0
- package/dist/types/decorators/http/PATCH.d.ts +90 -0
- package/dist/types/decorators/http/POST.d.ts +91 -0
- package/dist/types/decorators/http/PUT.d.ts +92 -0
- package/dist/types/exceptions/AccessDeniedException.d.ts +10 -0
- package/dist/types/exceptions/ControllerActionNotFoundException.d.ts +9 -0
- package/dist/types/exceptions/DuplicateActionNameException.d.ts +8 -0
- package/dist/types/exceptions/EntrypointStoppedException.d.ts +9 -0
- package/dist/types/exceptions/InvalidActionGroupException.d.ts +4 -0
- package/dist/types/exceptions/InvalidChannelException.d.ts +8 -0
- package/dist/types/exceptions/InvalidEntrypointAdapterException.d.ts +9 -0
- package/dist/types/exceptions/InvalidMiddlewareException.d.ts +8 -0
- package/dist/types/exceptions/InvalidPrefixException.d.ts +8 -0
- package/dist/types/exceptions/MiddlewareNextCalledException.d.ts +8 -0
- package/dist/types/exceptions/RouteNotFoundException.d.ts +9 -0
- package/dist/types/exceptions/ServiceInvokeException.d.ts +11 -0
- package/dist/types/exports/CTRLDecorators.d.ts +13 -0
- package/dist/types/exports/Channel.d.ts +14 -0
- package/dist/types/exports/CommanderAdapter.d.ts +1 -0
- package/dist/types/exports/ElectronAdapter.d.ts +1 -0
- package/dist/types/exports/Entrypoint.d.ts +24 -0
- package/dist/types/exports/ExpressAdapter.d.ts +4 -0
- package/dist/types/exports/FastifyAdapter.d.ts +4 -0
- package/dist/types/exports/JSONRPCAdapter.d.ts +2 -0
- package/dist/types/exports/SocketIOAdapter.d.ts +1 -0
- package/dist/types/lib/AccessControl.d.ts +118 -0
- package/dist/types/lib/AccessControlRule.d.ts +91 -0
- package/dist/types/lib/ActionOptions.d.ts +67 -0
- package/dist/types/lib/ActionResult.d.ts +28 -0
- package/dist/types/lib/ApplicationOf.d.ts +6 -0
- package/dist/types/lib/Controller.d.ts +59 -0
- package/dist/types/lib/ControllerEntrypoint.d.ts +168 -0
- package/dist/types/lib/GetActionDTOAndOptions.d.ts +29 -0
- package/dist/types/lib/HTTPErrors.d.ts +55 -0
- package/dist/types/lib/Middleware.d.ts +146 -0
- package/dist/types/lib/Prefix.d.ts +98 -0
- package/dist/types/lib/Result.d.ts +173 -0
- package/dist/types/lib/ServiceResponseCodec.d.ts +82 -0
- package/dist/types/lib/StringifyPattern.d.ts +7 -0
- package/dist/types/lib/UnsetValue.d.ts +12 -0
- package/dist/types/lib/pattern/GlobPattern.d.ts +34 -0
- package/dist/types/lib/pattern/PatternManager.d.ts +103 -0
- package/dist/types/lib/schedule/Cron.d.ts +56 -0
- package/dist/types/types/CLIContextParams.d.ts +14 -0
- package/dist/types/types/ErrorDescription.d.ts +14 -0
- package/dist/types/types/HTTPContextParams.d.ts +27 -0
- package/dist/types/types/ScheduleContextParams.d.ts +21 -0
- package/dist/types/types/ServiceContextParams.d.ts +16 -0
- package/package.json +152 -0
|
@@ -0,0 +1,998 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Entrypoint = exports.buildEntrypoints = exports.buildServiceEntrypoint = exports.buildHTTPEntrypoint = exports.buildCLIEntrypoint = exports.ScheduleContext = exports.ServiceContext = exports.HTTPContext = exports.CLIContext = exports.BaseContext = exports.ContextType = void 0;
|
|
13
|
+
const core_1 = require("@lakutata/core");
|
|
14
|
+
const di_1 = require("@lakutata/core/decorator/di");
|
|
15
|
+
const ControllerEntrypoint_js_1 = require("./lib/ControllerEntrypoint.js");
|
|
16
|
+
const Controller_js_1 = require("./lib/Controller.js");
|
|
17
|
+
const utils_1 = require("@lakutata/utils");
|
|
18
|
+
const dto_1 = require("@lakutata/core/decorator/dto");
|
|
19
|
+
const DuplicateActionNameException_js_1 = require("./exceptions/DuplicateActionNameException.js");
|
|
20
|
+
const AccessControl_js_1 = require("./lib/AccessControl.js");
|
|
21
|
+
const AccessControlRule_js_1 = require("./lib/AccessControlRule.js");
|
|
22
|
+
const InvalidActionGroupException_js_1 = require("./exceptions/InvalidActionGroupException.js");
|
|
23
|
+
const Middleware_js_1 = require("./lib/Middleware.js");
|
|
24
|
+
const Channel_js_1 = require("./channel/Channel.js");
|
|
25
|
+
const EntrypointAdapter_js_1 = require("./channel/EntrypointAdapter.js");
|
|
26
|
+
const InvalidEntrypointAdapterException_js_1 = require("./exceptions/InvalidEntrypointAdapterException.js");
|
|
27
|
+
const EntrypointStoppedException_js_1 = require("./exceptions/EntrypointStoppedException.js");
|
|
28
|
+
require("./channel/BuiltinChannels.js");
|
|
29
|
+
var BaseContext_js_1 = require("./context/BaseContext.js");
|
|
30
|
+
Object.defineProperty(exports, "ContextType", { enumerable: true, get: function () { return BaseContext_js_1.ContextType; } });
|
|
31
|
+
Object.defineProperty(exports, "BaseContext", { enumerable: true, get: function () { return BaseContext_js_1.BaseContext; } });
|
|
32
|
+
var CLIContext_js_1 = require("./context/CLIContext.js");
|
|
33
|
+
Object.defineProperty(exports, "CLIContext", { enumerable: true, get: function () { return CLIContext_js_1.CLIContext; } });
|
|
34
|
+
var HTTPContext_js_1 = require("./context/HTTPContext.js");
|
|
35
|
+
Object.defineProperty(exports, "HTTPContext", { enumerable: true, get: function () { return HTTPContext_js_1.HTTPContext; } });
|
|
36
|
+
var ServiceContext_js_1 = require("./context/ServiceContext.js");
|
|
37
|
+
Object.defineProperty(exports, "ServiceContext", { enumerable: true, get: function () { return ServiceContext_js_1.ServiceContext; } });
|
|
38
|
+
var ScheduleContext_js_1 = require("./context/ScheduleContext.js");
|
|
39
|
+
Object.defineProperty(exports, "ScheduleContext", { enumerable: true, get: function () { return ScheduleContext_js_1.ScheduleContext; } });
|
|
40
|
+
/**
|
|
41
|
+
* Type a CLI entrypoint in the style of lakutata 2.x; the function is returned as it is.
|
|
42
|
+
* @param entrypoint The function the entrypoint calls when it starts, with the module, the commands, the request
|
|
43
|
+
* handler and the destroyer registrar.
|
|
44
|
+
* @returns The same function, to give to the `cli` option of {@link buildEntrypoints}.
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* import {Application} from 'lakutata'
|
|
48
|
+
* import {buildCLIEntrypoint, buildEntrypoints, CLIContext, Controller} from 'lakutata/com/entrypoint'
|
|
49
|
+
* import {CLIAction} from 'lakutata/decorator/ctrl'
|
|
50
|
+
*
|
|
51
|
+
* class VersionController extends Controller {
|
|
52
|
+
* @CLIAction('version')
|
|
53
|
+
* public async version(): Promise<string> {
|
|
54
|
+
* return '1.0.0'
|
|
55
|
+
* }
|
|
56
|
+
* }
|
|
57
|
+
*
|
|
58
|
+
* Application.run(() => ({
|
|
59
|
+
* id: 'tool.app',
|
|
60
|
+
* name: 'Tool',
|
|
61
|
+
* components: {
|
|
62
|
+
* entrypoint: buildEntrypoints({
|
|
63
|
+
* controllers: [VersionController],
|
|
64
|
+
* //Run the command given as the first argument once the entrypoint starts
|
|
65
|
+
* cli: buildCLIEntrypoint(async (module, cliMap, handler): Promise<void> => {
|
|
66
|
+
* const command: string = process.argv[2] ?? 'version'
|
|
67
|
+
* if (cliMap.has(command)) console.log(await handler(new CLIContext({command: command, data: {}})))
|
|
68
|
+
* })
|
|
69
|
+
* })
|
|
70
|
+
* }
|
|
71
|
+
* }))
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
const buildCLIEntrypoint = (entrypoint) => entrypoint;
|
|
75
|
+
exports.buildCLIEntrypoint = buildCLIEntrypoint;
|
|
76
|
+
/**
|
|
77
|
+
* Type an HTTP entrypoint in the style of lakutata 2.x; the function is returned as it is.
|
|
78
|
+
* @param entrypoint The function the entrypoint calls when it starts, with the module, the routes, the request handler
|
|
79
|
+
* and the destroyer registrar.
|
|
80
|
+
* @returns The same function, to give to the `http` option of {@link buildEntrypoints}.
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* import {createServer, type Server} from 'node:http'
|
|
84
|
+
* import {Application} from 'lakutata'
|
|
85
|
+
* import {buildEntrypoints, buildHTTPEntrypoint, Controller, HTTPContext} from 'lakutata/com/entrypoint'
|
|
86
|
+
* import {GET} from 'lakutata/decorator/ctrl'
|
|
87
|
+
*
|
|
88
|
+
* class HealthController extends Controller {
|
|
89
|
+
* @GET('/health')
|
|
90
|
+
* public async health(): Promise<{status: string}> {
|
|
91
|
+
* return {status: 'ok'}
|
|
92
|
+
* }
|
|
93
|
+
* }
|
|
94
|
+
*
|
|
95
|
+
* Application.run(() => ({
|
|
96
|
+
* id: 'api.app',
|
|
97
|
+
* name: 'API',
|
|
98
|
+
* components: {
|
|
99
|
+
* entrypoint: buildEntrypoints({
|
|
100
|
+
* controllers: [HealthController],
|
|
101
|
+
* http: buildHTTPEntrypoint((module, routeMap, handler, registerDestroy): void => {
|
|
102
|
+
* const server: Server = createServer(async (request, response): Promise<void> => {
|
|
103
|
+
* const route: string = (request.url ?? '/').split('?')[0]
|
|
104
|
+
* const method: string = request.method ?? 'GET'
|
|
105
|
+
* if (!routeMap.get(route)?.has(method)) {
|
|
106
|
+
* response.writeHead(404).end()
|
|
107
|
+
* return
|
|
108
|
+
* }
|
|
109
|
+
* const abortController: AbortController = new AbortController()
|
|
110
|
+
* response.on('close', (): void => abortController.abort())
|
|
111
|
+
* try {
|
|
112
|
+
* const context: HTTPContext = new HTTPContext({route, method, request, response, data: {}})
|
|
113
|
+
* response.end(JSON.stringify(await handler(context, abortController)))
|
|
114
|
+
* } catch (error) {
|
|
115
|
+
* response.writeHead(500).end((error as Error).message)
|
|
116
|
+
* }
|
|
117
|
+
* }).listen(3000)
|
|
118
|
+
* registerDestroy((): Promise<void> => new Promise((resolve): void => void server.close((): void => resolve())))
|
|
119
|
+
* })
|
|
120
|
+
* })
|
|
121
|
+
* }
|
|
122
|
+
* }))
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
const buildHTTPEntrypoint = (entrypoint) => entrypoint;
|
|
126
|
+
exports.buildHTTPEntrypoint = buildHTTPEntrypoint;
|
|
127
|
+
/**
|
|
128
|
+
* Type a service entrypoint in the style of lakutata 2.x; the function is returned as it is.
|
|
129
|
+
* @param entrypoint The function the entrypoint calls when it starts, with the module, the request handler and the
|
|
130
|
+
* destroyer registrar.
|
|
131
|
+
* @returns The same function, to give to the `service` option of {@link buildEntrypoints}.
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* import {EventEmitter} from 'node:events'
|
|
135
|
+
* import {Application} from 'lakutata'
|
|
136
|
+
* import {buildEntrypoints, buildServiceEntrypoint, Controller, ServiceContext} from 'lakutata/com/entrypoint'
|
|
137
|
+
* import {ServiceAction} from 'lakutata/decorator/ctrl'
|
|
138
|
+
*
|
|
139
|
+
* declare const bus: EventEmitter
|
|
140
|
+
*
|
|
141
|
+
* class MathController extends Controller {
|
|
142
|
+
* @ServiceAction({ctrl: 'math', act: 'add'})
|
|
143
|
+
* public async add(input: {a: number, b: number}): Promise<number> {
|
|
144
|
+
* return input.a + input.b
|
|
145
|
+
* }
|
|
146
|
+
* }
|
|
147
|
+
*
|
|
148
|
+
* Application.run(() => ({
|
|
149
|
+
* id: 'math.app',
|
|
150
|
+
* name: 'Math',
|
|
151
|
+
* components: {
|
|
152
|
+
* entrypoint: buildEntrypoints({
|
|
153
|
+
* controllers: [MathController],
|
|
154
|
+
* //Each message of the bus is a request: its data matched against the patterns of the actions
|
|
155
|
+
* service: buildServiceEntrypoint((module, handler, registerDestroy): void => {
|
|
156
|
+
* const listener = async (data: Record<string, any>, reply: (result: unknown) => void): Promise<void> => {
|
|
157
|
+
* reply(await handler(new ServiceContext({data: data})))
|
|
158
|
+
* }
|
|
159
|
+
* bus.on('request', listener)
|
|
160
|
+
* registerDestroy((): void => void bus.off('request', listener))
|
|
161
|
+
* })
|
|
162
|
+
* })
|
|
163
|
+
* }
|
|
164
|
+
* }))
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
const buildServiceEntrypoint = (entrypoint) => entrypoint;
|
|
168
|
+
exports.buildServiceEntrypoint = buildServiceEntrypoint;
|
|
169
|
+
/**
|
|
170
|
+
* Type the options of the {@link Entrypoint} component; they are returned as they are. The applications of the
|
|
171
|
+
* `lakutata` package declare the entrypoint as the component `entrypoint`: give these options to it.
|
|
172
|
+
* @param options The controllers, middlewares, access rules and the adapters of each channel.
|
|
173
|
+
* @returns The same options, to declare as the `entrypoint` component.
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* import {Application, DTO} from 'lakutata'
|
|
177
|
+
* import {buildEntrypoints, Controller, scheduleEntrypoint} from 'lakutata/com/entrypoint'
|
|
178
|
+
* import {GET, POST, Schedule} from 'lakutata/decorator/ctrl'
|
|
179
|
+
* import {Expect} from 'lakutata/decorator/dto'
|
|
180
|
+
* import {expressHTTPEntrypoint} from 'lakutata/adapter/express'
|
|
181
|
+
*
|
|
182
|
+
* class CreateOrderDTO extends DTO {
|
|
183
|
+
* @Expect(DTO.String().required())
|
|
184
|
+
* public product: string
|
|
185
|
+
* }
|
|
186
|
+
*
|
|
187
|
+
* class OrderController extends Controller {
|
|
188
|
+
* @GET('/orders', {name: 'listOrders', groups: ['orders']})
|
|
189
|
+
* public async list(): Promise<string[]> {
|
|
190
|
+
* return []
|
|
191
|
+
* }
|
|
192
|
+
*
|
|
193
|
+
* @POST('/orders', CreateOrderDTO, {name: 'createOrder', groups: ['orders']})
|
|
194
|
+
* public async create(input: CreateOrderDTO): Promise<{product: string}> {
|
|
195
|
+
* return {product: input.product}
|
|
196
|
+
* }
|
|
197
|
+
*
|
|
198
|
+
* @Schedule('0 * * * *')
|
|
199
|
+
* public async purge(): Promise<void> {
|
|
200
|
+
* }
|
|
201
|
+
* }
|
|
202
|
+
*
|
|
203
|
+
* Application.run(() => ({
|
|
204
|
+
* id: 'shop.app',
|
|
205
|
+
* name: 'Shop',
|
|
206
|
+
* components: {
|
|
207
|
+
* entrypoint: buildEntrypoints({
|
|
208
|
+
* controllers: [OrderController],
|
|
209
|
+
* httpActionGroups: {orders: 'Orders'},
|
|
210
|
+
* shutdownTimeout: 10000,
|
|
211
|
+
* http: expressHTTPEntrypoint({port: 3000}),
|
|
212
|
+
* schedule: scheduleEntrypoint()
|
|
213
|
+
* })
|
|
214
|
+
* }
|
|
215
|
+
* }))
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
const buildEntrypoints = (options) => options;
|
|
219
|
+
exports.buildEntrypoints = buildEntrypoints;
|
|
220
|
+
/**
|
|
221
|
+
* The promises of the requests in progress (a request is in progress before its promise is created)
|
|
222
|
+
* @param requests
|
|
223
|
+
*/
|
|
224
|
+
function promises(requests) {
|
|
225
|
+
return [...requests].map((request) => request.promise).filter((promise) => !!promise);
|
|
226
|
+
}
|
|
227
|
+
//The settlement of an aborted request
|
|
228
|
+
const ABORTED = Symbol('ABORTED');
|
|
229
|
+
//The options of the entrypoint which are not adapters
|
|
230
|
+
const ENTRYPOINT_OPTIONS = new Set(['class', 'id', 'controllers', 'middlewares', 'rules', 'shutdownTimeout', 'cliActionGroups', 'httpActionGroups', 'serviceActionGroups']);
|
|
231
|
+
//The channels accepting the callbacks of 2.x
|
|
232
|
+
const LEGACY_CHANNELS = new Set(['cli', 'http', 'service']);
|
|
233
|
+
/**
|
|
234
|
+
* The entrypoint component: it serves the controllers' actions to the requests of every channel (HTTP, CLI, service,
|
|
235
|
+
* schedule, and the channels of other packages), each received by the adapters configured for its channel.
|
|
236
|
+
*
|
|
237
|
+
* The applications of the `lakutata` package declare it as the component `entrypoint`: configure it with
|
|
238
|
+
* {@link buildEntrypoints}. When it initializes, it registers the controllers and checks their actions (their groups and
|
|
239
|
+
* the uniqueness of their names); then, in its startup, it starts the adapters in order (the objects their hooks get may
|
|
240
|
+
* inject the entrypoint; the objects getting the entrypoint wait for its adapters); it is ready (the adapters acting on
|
|
241
|
+
* their own, such as the schedules, begin) once the application has launched. Each request runs in a scope of its own: its middlewares,
|
|
242
|
+
* then a new controller instance receiving its context and signal, the validation of its input, the access control
|
|
243
|
+
* and the action; the scope is destroyed when the request ends.
|
|
244
|
+
*
|
|
245
|
+
* When it is destroyed, it stops the adapters in reverse order: each stops receiving requests, the requests in progress
|
|
246
|
+
* get `shutdownTimeout` milliseconds to complete and are then aborted (failing with an `EntrypointStoppedException`),
|
|
247
|
+
* and the adapter is closed.
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* import {Application, Component} from 'lakutata'
|
|
251
|
+
* import {buildEntrypoints, Controller, Entrypoint, ServiceContext} from 'lakutata/com/entrypoint'
|
|
252
|
+
* import {GET, ServiceAction} from 'lakutata/decorator/ctrl'
|
|
253
|
+
* import {Inject} from 'lakutata/decorator/di'
|
|
254
|
+
* import {expressHTTPEntrypoint} from 'lakutata/adapter/express'
|
|
255
|
+
* import {socketIOServiceEntrypoint} from 'lakutata/adapter/socket.io'
|
|
256
|
+
*
|
|
257
|
+
* class ChatController extends Controller {
|
|
258
|
+
* @Inject('entrypoint')
|
|
259
|
+
* protected readonly entrypoint: Entrypoint
|
|
260
|
+
*
|
|
261
|
+
* //Served over HTTP: GET /rooms
|
|
262
|
+
* @GET('/rooms')
|
|
263
|
+
* public async rooms(): Promise<string[]> {
|
|
264
|
+
* return ['general']
|
|
265
|
+
* }
|
|
266
|
+
*
|
|
267
|
+
* //Served over Socket.IO, the message pushed to every client
|
|
268
|
+
* @ServiceAction({act: 'say'})
|
|
269
|
+
* public async say(input: {text: string}): Promise<number> {
|
|
270
|
+
* const from: string | undefined = (this.context as ServiceContext).clientId
|
|
271
|
+
* return this.entrypoint.broadcast('message', from, input.text)
|
|
272
|
+
* }
|
|
273
|
+
* }
|
|
274
|
+
*
|
|
275
|
+
* Application
|
|
276
|
+
* .run(() => ({
|
|
277
|
+
* id: 'chat.app',
|
|
278
|
+
* name: 'Chat',
|
|
279
|
+
* components: {
|
|
280
|
+
* entrypoint: buildEntrypoints({
|
|
281
|
+
* controllers: [ChatController],
|
|
282
|
+
* http: expressHTTPEntrypoint({port: 3000}),
|
|
283
|
+
* service: socketIOServiceEntrypoint({port: 3001})
|
|
284
|
+
* })
|
|
285
|
+
* }
|
|
286
|
+
* }))
|
|
287
|
+
* .onLaunched(async (app: Application): Promise<void> => {
|
|
288
|
+
* const entrypoint: Entrypoint = await app.getObject<Entrypoint>('entrypoint')
|
|
289
|
+
* console.log(entrypoint.getHttpActions().actions.map(action => action.route)) //['/rooms']
|
|
290
|
+
* })
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
let Entrypoint = class Entrypoint extends core_1.Component {
|
|
294
|
+
constructor() {
|
|
295
|
+
super(...arguments);
|
|
296
|
+
/**
|
|
297
|
+
* The class running the access control of the actions declared with `acl: true` or a `rule`; a subclass of the
|
|
298
|
+
* entrypoint may replace it with a subclass of {@link AccessControl}.
|
|
299
|
+
* @protected
|
|
300
|
+
*/
|
|
301
|
+
this.accessControl = AccessControl_js_1.AccessControl;
|
|
302
|
+
this.#channels = new Map();
|
|
303
|
+
this.#started = [];
|
|
304
|
+
this.#resolveReady = () => undefined;
|
|
305
|
+
/**
|
|
306
|
+
* Resolved once the adapters started and the application launched
|
|
307
|
+
*/
|
|
308
|
+
this.#ready = new Promise((resolve) => {
|
|
309
|
+
this.#resolveReady = resolve;
|
|
310
|
+
});
|
|
311
|
+
/**
|
|
312
|
+
* The adapters to start, once the entrypoint is initialized
|
|
313
|
+
*/
|
|
314
|
+
this.#adapters = [];
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Whether an option which is not a configurable property is accepted: the adapters given under a channel's key,
|
|
318
|
+
* and the 2.x entrypoint functions under `cli`, `http` and `service`. The container calls it when it checks the
|
|
319
|
+
* component's options.
|
|
320
|
+
* @param name The option's key.
|
|
321
|
+
* @param value The option's value: one item or an array.
|
|
322
|
+
* @returns `true` when every item is an adapter, or a function under a 2.x channel's key.
|
|
323
|
+
*/
|
|
324
|
+
static acceptsOption(name, value) {
|
|
325
|
+
const items = Array.isArray(value) ? value : [value];
|
|
326
|
+
return items.every((item) => (0, EntrypointAdapter_js_1.isEntrypointAdapter)(item) || (typeof item === 'function' && LEGACY_CHANNELS.has(name)));
|
|
327
|
+
}
|
|
328
|
+
#channels;
|
|
329
|
+
#started;
|
|
330
|
+
#stopping;
|
|
331
|
+
#resolveReady;
|
|
332
|
+
/**
|
|
333
|
+
* Resolved once the adapters started and the application launched
|
|
334
|
+
*/
|
|
335
|
+
#ready;
|
|
336
|
+
/**
|
|
337
|
+
* The adapters to start, once the entrypoint is initialized
|
|
338
|
+
*/
|
|
339
|
+
#adapters;
|
|
340
|
+
/**
|
|
341
|
+
* Register the controllers and the middleware classes and check the actions: once initialized, the entrypoint can be
|
|
342
|
+
* got by the other objects (its actions listed by `getHttpActions()`…), the adapters start in {@link startup}.
|
|
343
|
+
* @throws {InvalidMiddlewareException} When a middleware is neither a function nor a {@link Middleware} subclass.
|
|
344
|
+
* @throws {InvalidEntrypointAdapterException} When an option is neither an entrypoint option nor an adapter of its
|
|
345
|
+
* channel.
|
|
346
|
+
* @throws {InvalidActionGroupException} When an action is in a group missing from its channel's action groups.
|
|
347
|
+
* @throws {DuplicateActionNameException} When two actions of a channel have the same name.
|
|
348
|
+
* @protected
|
|
349
|
+
*/
|
|
350
|
+
async init() {
|
|
351
|
+
(0, Middleware_js_1.validateMiddlewares)(this.middlewares);
|
|
352
|
+
const adapters = this.#adapters = this.configuredAdapters();
|
|
353
|
+
await Promise.all(this.controllers.map((controllerConstructor) => {
|
|
354
|
+
(0, ControllerEntrypoint_js_1.bindControllerToComponent)(this, controllerConstructor);
|
|
355
|
+
return this.container.register(controllerConstructor);
|
|
356
|
+
}));
|
|
357
|
+
(0, ControllerEntrypoint_js_1.getComponentControllerActionMap)(this).forEach((actions, channel) => this.#channels.set(channel, this.createChannelState((0, Channel_js_1.getChannel)(channel), actions)));
|
|
358
|
+
this.checkActions('CLI', this.#channels.get('cli')?.infos, this.cliActionGroups, 'cliActionGroups');
|
|
359
|
+
this.checkActions('HTTP', this.#channels.get('http')?.infos, this.httpActionGroups, 'httpActionGroups');
|
|
360
|
+
this.checkActions('Service', this.#channels.get('service')?.infos, this.serviceActionGroups, 'serviceActionGroups');
|
|
361
|
+
for (const [name, state] of this.#channels)
|
|
362
|
+
if (!LEGACY_CHANNELS.has(name))
|
|
363
|
+
this.checkActions(name, state.infos);
|
|
364
|
+
//The middleware classes are created in the container of each request
|
|
365
|
+
const middlewareClasses = new Set();
|
|
366
|
+
const addMiddlewareClasses = (middlewares) => middlewares.filter(Middleware_js_1.isMiddlewareClass).forEach((middlewareClass) => middlewareClasses.add(middlewareClass));
|
|
367
|
+
addMiddlewareClasses(this.middlewares);
|
|
368
|
+
this.#channels.forEach((state) => state.entries.forEach((entry) => addMiddlewareClasses(entry.middlewares)));
|
|
369
|
+
adapters.forEach((adapter) => addMiddlewareClasses((0, Middleware_js_1.validateMiddlewares)(adapter.middlewares ?? [])));
|
|
370
|
+
await Promise.all([...middlewareClasses].map((middlewareClass) => this.container.register(middlewareClass)));
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Start the adapters in order, once the entrypoint is initialized: the objects their hooks get (an authenticator of
|
|
374
|
+
* the requests adding its middleware to an HTTP server) may inject the entrypoint. The objects getting the
|
|
375
|
+
* entrypoint wait for the adapters, and fail when one fails to start (a port in use).
|
|
376
|
+
* @protected
|
|
377
|
+
*/
|
|
378
|
+
async startup() {
|
|
379
|
+
for (const adapter of this.#adapters)
|
|
380
|
+
await this.startAdapter(adapter);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Mark the entrypoint ready: the application has launched (in a bare container, once its adapters started).
|
|
384
|
+
* @protected
|
|
385
|
+
*/
|
|
386
|
+
async launched() {
|
|
387
|
+
this.#resolveReady();
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Stop the adapters, before the scopes of the requests in progress and the controllers are destroyed (see
|
|
391
|
+
* {@link Entrypoint.stopAdapters}).
|
|
392
|
+
* @protected
|
|
393
|
+
*/
|
|
394
|
+
async beforeDestroy() {
|
|
395
|
+
await this.stopAdapters();
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Stop the adapters in reverse order, once (a second call returns the same promise): each adapter stops receiving
|
|
399
|
+
* requests, its requests in progress get `shutdownTimeout` milliseconds before they are aborted, then it is closed.
|
|
400
|
+
* Every adapter is stopped even when another fails.
|
|
401
|
+
* @returns Resolves once every adapter is closed.
|
|
402
|
+
* @throws {AggregateError} When several adapters failed to stop or close; a single failure is thrown as it is.
|
|
403
|
+
* @protected
|
|
404
|
+
*/
|
|
405
|
+
stopAdapters() {
|
|
406
|
+
this.#stopping ??= (async () => {
|
|
407
|
+
const errors = [];
|
|
408
|
+
for (const started of [...this.#started].reverse()) {
|
|
409
|
+
try {
|
|
410
|
+
await started.adapter.stop?.();
|
|
411
|
+
}
|
|
412
|
+
catch (e) {
|
|
413
|
+
errors.push(e);
|
|
414
|
+
}
|
|
415
|
+
await this.drain(started);
|
|
416
|
+
try {
|
|
417
|
+
await started.adapter.close?.();
|
|
418
|
+
}
|
|
419
|
+
catch (e) {
|
|
420
|
+
errors.push(e);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
this.#started = [];
|
|
424
|
+
if (errors.length === 1)
|
|
425
|
+
throw errors[0];
|
|
426
|
+
if (errors.length)
|
|
427
|
+
throw new AggregateError(errors, 'Failed to stop the entrypoint adapters');
|
|
428
|
+
})();
|
|
429
|
+
return this.#stopping;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* The adapters of the options: every option which is not an entrypoint option is a channel's key; a 2.x entrypoint
|
|
433
|
+
* function is wrapped in an adapter, the channel of an adapter carrying its definition is defined.
|
|
434
|
+
* @returns The adapters, in the order of the options.
|
|
435
|
+
* @throws {InvalidEntrypointAdapterException} When an adapter is given under another channel's key, its channel is
|
|
436
|
+
* not defined, or the option is neither an adapter nor a 2.x function of `cli`, `http` or `service`.
|
|
437
|
+
* @protected
|
|
438
|
+
*/
|
|
439
|
+
configuredAdapters() {
|
|
440
|
+
const records = this.getConfigurableRecords();
|
|
441
|
+
const adapters = [];
|
|
442
|
+
for (const [key, value] of Object.entries(records)) {
|
|
443
|
+
if (ENTRYPOINT_OPTIONS.has(key) || value === undefined || value === null)
|
|
444
|
+
continue;
|
|
445
|
+
for (const item of Array.isArray(value) ? value : [value]) {
|
|
446
|
+
if (typeof item === 'function' && LEGACY_CHANNELS.has(key)) {
|
|
447
|
+
adapters.push(this.legacyAdapter(key, item));
|
|
448
|
+
}
|
|
449
|
+
else if ((0, EntrypointAdapter_js_1.isEntrypointAdapter)(item)) {
|
|
450
|
+
if (item.channel !== key)
|
|
451
|
+
throw new InvalidEntrypointAdapterException_js_1.InvalidEntrypointAdapterException('The option "{key}" is given an adapter of the channel "{channel}"', { key: key, channel: item.channel });
|
|
452
|
+
if (item.definition)
|
|
453
|
+
(0, Channel_js_1.defineChannel)(item.definition);
|
|
454
|
+
if (!(0, Channel_js_1.hasChannel)(key))
|
|
455
|
+
throw new InvalidEntrypointAdapterException_js_1.InvalidEntrypointAdapterException('The channel "{key}" of an adapter is not defined', { key: key });
|
|
456
|
+
adapters.push(item);
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
throw new InvalidEntrypointAdapterException_js_1.InvalidEntrypointAdapterException('The option "{key}" is not an entrypoint option nor an adapter', { key: key });
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return adapters;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Wrap a 2.x entrypoint function in an adapter: it is called when the adapter starts, in the entrypoint's
|
|
467
|
+
* {@link startup} (the entrypoint initialized: the objects it gets may inject the entrypoint, as in 2.x), and the
|
|
468
|
+
* promise of an asynchronous builder is awaited — its failure fails the entrypoint's startup, so the application's
|
|
469
|
+
* launch (2.x did not await it: the application launched without its server, the failure an unhandled rejection).
|
|
470
|
+
* Its registered destroyers run when the adapter stops.
|
|
471
|
+
* @param channel The channel: `cli`, `http` or `service`.
|
|
472
|
+
* @param callback The {@link CLIEntrypoint}, {@link HTTPEntrypoint} or {@link ServiceEntrypoint}.
|
|
473
|
+
* @returns The adapter.
|
|
474
|
+
* @protected
|
|
475
|
+
*/
|
|
476
|
+
legacyAdapter(channel, callback) {
|
|
477
|
+
const destroyers = [];
|
|
478
|
+
const registerDestroy = (destroyer) => destroyers.push(destroyer);
|
|
479
|
+
return {
|
|
480
|
+
channel: channel,
|
|
481
|
+
start: async (runtime) => {
|
|
482
|
+
const handler = (context, abortController) => runtime.dispatch(context, { signal: abortController?.signal });
|
|
483
|
+
const run = () => {
|
|
484
|
+
switch (channel) {
|
|
485
|
+
case 'http': {
|
|
486
|
+
const routeMap = new Map();
|
|
487
|
+
for (const action of runtime.actions) {
|
|
488
|
+
const methods = routeMap.get(action.address.route) ?? new Set();
|
|
489
|
+
routeMap.set(action.address.route, methods.add(action.address.method));
|
|
490
|
+
}
|
|
491
|
+
return callback(runtime.module, routeMap, handler, registerDestroy);
|
|
492
|
+
}
|
|
493
|
+
case 'cli':
|
|
494
|
+
return callback(runtime.module, (0, EntrypointAdapter_js_1.commandMap)(runtime.actions), handler, registerDestroy);
|
|
495
|
+
default:
|
|
496
|
+
return callback(runtime.module, handler, registerDestroy);
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
await run();
|
|
500
|
+
},
|
|
501
|
+
stop: async () => {
|
|
502
|
+
await Promise.all(destroyers.map((destroyer) => Promise.resolve(destroyer())));
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Collect the actions of a channel: their descriptions, their middlewares and the channel's lookup.
|
|
508
|
+
* @param definition The channel.
|
|
509
|
+
* @param actions The actions bound to the entrypoint for the channel, by address.
|
|
510
|
+
* @returns The channel's state.
|
|
511
|
+
* @protected
|
|
512
|
+
*/
|
|
513
|
+
createChannelState(definition, actions) {
|
|
514
|
+
const entries = [];
|
|
515
|
+
const infos = new Map();
|
|
516
|
+
actions.forEach((details) => {
|
|
517
|
+
const info = {
|
|
518
|
+
id: details.id,
|
|
519
|
+
acl: details.acl,
|
|
520
|
+
name: details.name,
|
|
521
|
+
description: details.description,
|
|
522
|
+
groups: details.groups,
|
|
523
|
+
controller: details.constructor.className,
|
|
524
|
+
method: details.method.toString(),
|
|
525
|
+
jsonSchema: details.jsonSchema,
|
|
526
|
+
...definition.describe(details.pattern)
|
|
527
|
+
};
|
|
528
|
+
infos.set(details.id, info);
|
|
529
|
+
entries.push({
|
|
530
|
+
details: details,
|
|
531
|
+
info: info,
|
|
532
|
+
address: { ...info, address: details.pattern, dto: details.dtoConstructor, returnSchema: (0, dto_1.getMethodReturnSchema)(details.constructor.prototype, (0, utils_1.As)(details.method)) },
|
|
533
|
+
middlewares: [
|
|
534
|
+
...(0, Middleware_js_1.getClassMiddlewares)(details.constructor),
|
|
535
|
+
...details.middlewares,
|
|
536
|
+
...(0, Middleware_js_1.getMethodMiddlewares)(details.constructor, (0, utils_1.As)(details.method))
|
|
537
|
+
]
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
return { definition: definition, entries: entries, infos: infos, matcher: definition.createMatcher(entries) };
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Check a channel's actions: their groups are defined (when the channel has action groups) and their names unique.
|
|
544
|
+
* @param type The channel's name used in the error messages (`HTTP`, `CLI`…).
|
|
545
|
+
* @param actionInfoMap The channel's action descriptions by id; nothing is checked when it is `undefined`.
|
|
546
|
+
* @param actionGroups The channel's action groups; the groups are not checked without them.
|
|
547
|
+
* @param option The option holding the action groups, named in the error message.
|
|
548
|
+
* @throws {InvalidActionGroupException} When an action is in a group missing from `actionGroups`.
|
|
549
|
+
* @throws {DuplicateActionNameException} When two actions have the same name.
|
|
550
|
+
* @protected
|
|
551
|
+
*/
|
|
552
|
+
checkActions(type, actionInfoMap, actionGroups, option) {
|
|
553
|
+
if (!actionInfoMap)
|
|
554
|
+
return;
|
|
555
|
+
if (actionGroups) {
|
|
556
|
+
const invalidGroupIds = this.findInvalidActionGroupIds(actionInfoMap, actionGroups);
|
|
557
|
+
if (invalidGroupIds.length)
|
|
558
|
+
throw new InvalidActionGroupException_js_1.InvalidActionGroupException('Found {type} action groups are not defined in {option}: {groupIds}', {
|
|
559
|
+
type: type,
|
|
560
|
+
option: option,
|
|
561
|
+
groupIds: invalidGroupIds
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
const duplicateNames = this.findDuplicateActionNames(actionInfoMap);
|
|
565
|
+
if (duplicateNames.length)
|
|
566
|
+
throw new DuplicateActionNameException_js_1.DuplicateActionNameException('Duplicate {type} action names found: {names}', {
|
|
567
|
+
type: type,
|
|
568
|
+
names: duplicateNames
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Start an adapter, giving it the runtime of its channel (its actions and the dispatch functions).
|
|
573
|
+
* @param adapter The adapter.
|
|
574
|
+
* @throws {InvalidChannelException} When the adapter's channel is not defined.
|
|
575
|
+
* @protected
|
|
576
|
+
*/
|
|
577
|
+
async startAdapter(adapter) {
|
|
578
|
+
const definition = (0, Channel_js_1.getChannel)(adapter.channel);
|
|
579
|
+
let state = this.#channels.get(definition.name);
|
|
580
|
+
if (!state) {
|
|
581
|
+
state = this.createChannelState(definition, new Map());
|
|
582
|
+
this.#channels.set(definition.name, state);
|
|
583
|
+
}
|
|
584
|
+
const started = { adapter: adapter, inflight: new Set() };
|
|
585
|
+
const channelState = state;
|
|
586
|
+
const runners = new Map();
|
|
587
|
+
const outerMiddlewares = [...this.middlewares, ...(adapter.middlewares ?? [])];
|
|
588
|
+
const runnerOf = (entry) => {
|
|
589
|
+
let runner = runners.get(entry);
|
|
590
|
+
if (!runner) {
|
|
591
|
+
runner = (0, Middleware_js_1.composeMiddlewares)([...outerMiddlewares, ...entry.middlewares], (invocation) => this.runAction(entry.details, invocation));
|
|
592
|
+
runners.set(entry, runner);
|
|
593
|
+
}
|
|
594
|
+
return runner;
|
|
595
|
+
};
|
|
596
|
+
const entriesByAddress = new Map(channelState.entries.map((entry) => [entry.address, entry]));
|
|
597
|
+
const runtime = {
|
|
598
|
+
channel: definition,
|
|
599
|
+
module: this.getModule(),
|
|
600
|
+
actions: channelState.entries.map((entry) => entry.address),
|
|
601
|
+
ready: this.#ready,
|
|
602
|
+
controllers: [...this.controllers],
|
|
603
|
+
getObject: (target) => this.getObject(target),
|
|
604
|
+
dispatch: async (context, options = {}) => {
|
|
605
|
+
const match = channelState.matcher.match(context);
|
|
606
|
+
if (!match)
|
|
607
|
+
throw definition.notFound(context);
|
|
608
|
+
const entry = (0, utils_1.As)(match.entry);
|
|
609
|
+
definition.prepare?.(entry, context);
|
|
610
|
+
return this.dispatch(started, entry, runnerOf(entry), context, { ...options, params: { ...match.params, ...options.params } });
|
|
611
|
+
},
|
|
612
|
+
dispatchTo: async (action, context, options = {}) => {
|
|
613
|
+
const entry = entriesByAddress.get(action);
|
|
614
|
+
if (!entry)
|
|
615
|
+
throw definition.notFound(context);
|
|
616
|
+
return this.dispatch(started, entry, runnerOf(entry), context, options);
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
await adapter.start(runtime);
|
|
620
|
+
this.#started.push(started);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Wait for a stopped adapter's requests in progress; abort the ones still running after `shutdownTimeout`
|
|
624
|
+
* milliseconds with an {@link EntrypointStoppedException}.
|
|
625
|
+
* @param started The adapter and its requests in progress.
|
|
626
|
+
* @protected
|
|
627
|
+
*/
|
|
628
|
+
async drain(started) {
|
|
629
|
+
if (!started.inflight.size)
|
|
630
|
+
return;
|
|
631
|
+
let timer;
|
|
632
|
+
const timeout = new Promise((resolve) => {
|
|
633
|
+
timer = setTimeout(resolve, this.shutdownTimeout);
|
|
634
|
+
});
|
|
635
|
+
await Promise.race([Promise.allSettled(promises(started.inflight)), timeout]);
|
|
636
|
+
clearTimeout(timer);
|
|
637
|
+
const pending = [...started.inflight];
|
|
638
|
+
pending.forEach((request) => request.abortController.abort(new EntrypointStoppedException_js_1.EntrypointStoppedException('The entrypoint stopped before the request completed')));
|
|
639
|
+
//The aborted requests settle at once
|
|
640
|
+
await Promise.allSettled(promises(pending));
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Run an action in its request's container: create the controller with the request's context and signal, validate
|
|
644
|
+
* the input (the request's data, overridden by the address parameters), check the access and call the action
|
|
645
|
+
* method with the validated input.
|
|
646
|
+
* @param details The action.
|
|
647
|
+
* @param invocation The request.
|
|
648
|
+
* @returns The action's result.
|
|
649
|
+
* @throws {Error} The input validation error, the access control's denial ({@link AccessDeniedException} or the
|
|
650
|
+
* rule's error), or the action's error.
|
|
651
|
+
* @protected
|
|
652
|
+
*/
|
|
653
|
+
async runAction(details, invocation) {
|
|
654
|
+
const { context, container, params } = invocation;
|
|
655
|
+
//The controller of the request, shared by the objects of the request
|
|
656
|
+
const controller = await container.getShared(details.constructor, { context: context, signal: invocation.signal });
|
|
657
|
+
//The parameters of the address take precedence over the data of the request
|
|
658
|
+
const data = params && Object.keys(params).length ? { ...context.data, ...params } : context.data;
|
|
659
|
+
//The input is detached from the context, whose values are proxies validating their changes
|
|
660
|
+
const input = (0, utils_1.cloneDeep)(await this.validateInput(details.dtoConstructor, data, context.locale));
|
|
661
|
+
await this.runAccessControl(this.rules, container, (0, utils_1.As)(context), input, details);
|
|
662
|
+
return await controller.getMethod((0, utils_1.As)(details.method))(input);
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Validate an action's input. An invalid input of a request with a language is validated again in it, so the
|
|
666
|
+
* error is in that language (a valid input is validated once, with the default settings).
|
|
667
|
+
* @param dtoConstructor The action's DTO.
|
|
668
|
+
* @param data The input to validate.
|
|
669
|
+
* @param locale The request's language (`context.locale`).
|
|
670
|
+
* @returns The validated input, with its defaults and conversions applied.
|
|
671
|
+
* @throws {Error} The validation error.
|
|
672
|
+
* @protected
|
|
673
|
+
*/
|
|
674
|
+
async validateInput(dtoConstructor, data, locale) {
|
|
675
|
+
try {
|
|
676
|
+
return await dtoConstructor.validateAsync(data);
|
|
677
|
+
}
|
|
678
|
+
catch (error) {
|
|
679
|
+
if (!locale)
|
|
680
|
+
throw error;
|
|
681
|
+
return await dtoConstructor.validateAsync(data, { errors: { language: locale } });
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Dispatch a request to an action in a new request container, tracked as in progress for its adapter until it
|
|
686
|
+
* ends. A request whose signal is already aborted is not run and resolves to `undefined`.
|
|
687
|
+
* @param started The adapter receiving the request.
|
|
688
|
+
* @param entry The action.
|
|
689
|
+
* @param runner The action's middleware pipeline.
|
|
690
|
+
* @param context The request's context.
|
|
691
|
+
* @param options The request's abort signal and address parameters.
|
|
692
|
+
* @returns The action's result, `undefined` when the request is aborted by its client.
|
|
693
|
+
* @protected
|
|
694
|
+
*/
|
|
695
|
+
async dispatch(started, entry, runner, context, options) {
|
|
696
|
+
//An already aborted request is not executed at all
|
|
697
|
+
if (options.signal?.aborted)
|
|
698
|
+
return;
|
|
699
|
+
const request = { abortController: new AbortController() };
|
|
700
|
+
const abortRequest = () => request.abortController.abort();
|
|
701
|
+
options.signal?.addEventListener('abort', abortRequest, { once: true });
|
|
702
|
+
started.inflight.add(request);
|
|
703
|
+
try {
|
|
704
|
+
request.promise = this.execute(entry, runner, context, request.abortController.signal, options.params);
|
|
705
|
+
return await request.promise;
|
|
706
|
+
}
|
|
707
|
+
finally {
|
|
708
|
+
options.signal?.removeEventListener('abort', abortRequest);
|
|
709
|
+
started.inflight.delete(request);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Execute a request in a new request container, destroyed when it ends. An aborted request settles at once: its
|
|
714
|
+
* container is destroyed and its result or error dropped.
|
|
715
|
+
* @param entry The action.
|
|
716
|
+
* @param runner The action's middleware pipeline.
|
|
717
|
+
* @param context The request's context.
|
|
718
|
+
* @param signal The request's abort signal.
|
|
719
|
+
* @param params The parameters taken from the request's address.
|
|
720
|
+
* @returns The action's result, `undefined` when the request is aborted by its client.
|
|
721
|
+
* @throws {EntrypointStoppedException} When the request is aborted because the entrypoint stopped.
|
|
722
|
+
* @protected
|
|
723
|
+
*/
|
|
724
|
+
async execute(entry, runner, context, signal, params) {
|
|
725
|
+
const runtimeContainer = this.createScope();
|
|
726
|
+
let isAborted = false;
|
|
727
|
+
let settleAborted = utils_1.DevNull;
|
|
728
|
+
const aborted = new Promise((resolve) => {
|
|
729
|
+
settleAborted = () => resolve(ABORTED);
|
|
730
|
+
});
|
|
731
|
+
//Aborting destroys the request scope right away (container destruction is idempotent) and settles the request
|
|
732
|
+
const abortHandler = () => {
|
|
733
|
+
isAborted = true;
|
|
734
|
+
runtimeContainer.destroy().catch(utils_1.DevNull);
|
|
735
|
+
settleAborted();
|
|
736
|
+
};
|
|
737
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
|
738
|
+
try {
|
|
739
|
+
const running = runner({
|
|
740
|
+
context: (0, utils_1.As)(context),
|
|
741
|
+
action: (0, utils_1.As)(entry.info),
|
|
742
|
+
container: runtimeContainer,
|
|
743
|
+
signal: signal,
|
|
744
|
+
params: params
|
|
745
|
+
});
|
|
746
|
+
//The action of an aborted request may still fail, its error is dropped
|
|
747
|
+
running.catch(utils_1.DevNull);
|
|
748
|
+
const result = await Promise.race([running, aborted]);
|
|
749
|
+
if (!isAborted)
|
|
750
|
+
return result;
|
|
751
|
+
}
|
|
752
|
+
catch (e) {
|
|
753
|
+
if (!isAborted)
|
|
754
|
+
throw e;
|
|
755
|
+
}
|
|
756
|
+
finally {
|
|
757
|
+
//The request scope is always destroyed once the dispatch ends, its destruction never rejects
|
|
758
|
+
signal.removeEventListener('abort', abortHandler);
|
|
759
|
+
runtimeContainer.destroy().catch(utils_1.DevNull);
|
|
760
|
+
}
|
|
761
|
+
//A request aborted by the stop of the entrypoint fails, a request aborted by its client has no result
|
|
762
|
+
if (signal.reason instanceof EntrypointStoppedException_js_1.EntrypointStoppedException)
|
|
763
|
+
throw signal.reason;
|
|
764
|
+
return undefined;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* The descriptions of a channel's actions.
|
|
768
|
+
* @param channel The channel's name.
|
|
769
|
+
* @returns The descriptions, empty when the channel has no actions.
|
|
770
|
+
* @protected
|
|
771
|
+
*/
|
|
772
|
+
actionInfos(channel) {
|
|
773
|
+
return (0, utils_1.As)([...(this.#channels.get(channel)?.infos.values() ?? [])]);
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* The descriptions of the HTTP actions.
|
|
777
|
+
* @protected
|
|
778
|
+
*/
|
|
779
|
+
get HTTP_ACTIONS() {
|
|
780
|
+
return this.actionInfos('http');
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* The descriptions of the service actions.
|
|
784
|
+
* @protected
|
|
785
|
+
*/
|
|
786
|
+
get SERVICE_ACTIONS() {
|
|
787
|
+
return this.actionInfos('service');
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* The descriptions of the CLI actions.
|
|
791
|
+
* @protected
|
|
792
|
+
*/
|
|
793
|
+
get CLI_ACTIONS() {
|
|
794
|
+
return this.actionInfos('cli');
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* The actions of a channel, with the groups they belong to.
|
|
798
|
+
* @param channel The channel's name.
|
|
799
|
+
* @param actionGroups The group names by id.
|
|
800
|
+
* @returns The actions and their groups.
|
|
801
|
+
* @protected
|
|
802
|
+
*/
|
|
803
|
+
getEntrypointActions(channel, actionGroups = {}) {
|
|
804
|
+
const groupMap = new Map();
|
|
805
|
+
const actions = this.actionInfos(channel);
|
|
806
|
+
actions.forEach((action) => {
|
|
807
|
+
action.groups.forEach((groupId) => {
|
|
808
|
+
if (!groupMap.has(groupId))
|
|
809
|
+
groupMap.set(groupId, {
|
|
810
|
+
id: groupId,
|
|
811
|
+
name: actionGroups[groupId],
|
|
812
|
+
actions: []
|
|
813
|
+
});
|
|
814
|
+
groupMap.get(groupId)?.actions.push(action);
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
return {
|
|
818
|
+
groups: [...groupMap.values()],
|
|
819
|
+
actions: actions
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* The group ids used by the actions but missing from the action groups.
|
|
824
|
+
* @param actionInfoMap The action descriptions by id.
|
|
825
|
+
* @param actionGroups The group names by id.
|
|
826
|
+
* @returns The missing group ids, each once.
|
|
827
|
+
* @protected
|
|
828
|
+
*/
|
|
829
|
+
findInvalidActionGroupIds(actionInfoMap, actionGroups) {
|
|
830
|
+
const notfoundGroupIdSet = new Set();
|
|
831
|
+
actionInfoMap.forEach((action) => action.groups.forEach((groupId) => {
|
|
832
|
+
if (actionGroups[groupId] === undefined)
|
|
833
|
+
notfoundGroupIdSet.add(groupId);
|
|
834
|
+
}));
|
|
835
|
+
return [...notfoundGroupIdSet];
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* The names shared by the actions of several controller methods (the addresses of one method, such as its two
|
|
839
|
+
* routes, share its name).
|
|
840
|
+
* @param actionInfoMap The action descriptions by id.
|
|
841
|
+
* @returns The duplicate names, each once.
|
|
842
|
+
* @protected
|
|
843
|
+
*/
|
|
844
|
+
findDuplicateActionNames(actionInfoMap) {
|
|
845
|
+
const methods = new Map();
|
|
846
|
+
const duplicates = new Set();
|
|
847
|
+
actionInfoMap.forEach((action) => {
|
|
848
|
+
const method = `${action.controller}:${action.method}`;
|
|
849
|
+
const seen = methods.get(action.name);
|
|
850
|
+
if (seen === undefined)
|
|
851
|
+
methods.set(action.name, method);
|
|
852
|
+
else if (seen !== method)
|
|
853
|
+
duplicates.add(action.name);
|
|
854
|
+
});
|
|
855
|
+
return [...duplicates];
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Check the access to an action with {@link Entrypoint.accessControl}.
|
|
859
|
+
* @param rules The access control rule classes.
|
|
860
|
+
* @param runtimeContainer The request's container, where the rules are created.
|
|
861
|
+
* @param context The request's context.
|
|
862
|
+
* @param input The validated input.
|
|
863
|
+
* @param details The action.
|
|
864
|
+
* @throws {AccessDeniedException} When a rule denies the access (or the error a rule throws).
|
|
865
|
+
* @protected
|
|
866
|
+
*/
|
|
867
|
+
async runAccessControl(rules, runtimeContainer, context, input, details) {
|
|
868
|
+
const [allowed, exception] = await this.accessControl.run(rules, runtimeContainer, context, input, details, (type) => {
|
|
869
|
+
const channel = (0, Channel_js_1.getChannelByContextType)(type);
|
|
870
|
+
return channel ? this.actionInfos(channel.name) : [];
|
|
871
|
+
});
|
|
872
|
+
if (!allowed)
|
|
873
|
+
throw exception;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Push an event to a client of the adapters which have clients (an Electron window's renderer, a Socket.IO
|
|
877
|
+
* socket); its listeners receive the arguments. The client of a service request is its `context.clientId`.
|
|
878
|
+
* @param clientId The client's id.
|
|
879
|
+
* @param event The event's name.
|
|
880
|
+
* @param args The arguments given to the client's listeners.
|
|
881
|
+
* @returns Whether an adapter has the client; `false` when it is gone or no adapter has clients.
|
|
882
|
+
*/
|
|
883
|
+
notify(clientId, event, ...args) {
|
|
884
|
+
let notified = false;
|
|
885
|
+
for (const { adapter } of this.#started)
|
|
886
|
+
if (adapter.notify?.(clientId, event, args))
|
|
887
|
+
notified = true;
|
|
888
|
+
return notified;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Push an event to every client of the adapters which have clients (Electron windows, Socket.IO sockets); their
|
|
892
|
+
* listeners receive the arguments.
|
|
893
|
+
* @param event The event's name.
|
|
894
|
+
* @param args The arguments given to the clients' listeners.
|
|
895
|
+
* @returns The number of clients the event was pushed to.
|
|
896
|
+
*/
|
|
897
|
+
broadcast(event, ...args) {
|
|
898
|
+
let clients = 0;
|
|
899
|
+
for (const { adapter } of this.#started)
|
|
900
|
+
clients += adapter.broadcast?.(event, args) ?? 0;
|
|
901
|
+
return clients;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* The HTTP action groups configured by the `httpActionGroups` option.
|
|
905
|
+
* @returns The group names by id.
|
|
906
|
+
*/
|
|
907
|
+
getHttpActionGroups() {
|
|
908
|
+
return this.httpActionGroups;
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* The service action groups configured by the `serviceActionGroups` option.
|
|
912
|
+
* @returns The group names by id.
|
|
913
|
+
*/
|
|
914
|
+
getServiceActionGroups() {
|
|
915
|
+
return this.serviceActionGroups;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* The CLI action groups configured by the `cliActionGroups` option.
|
|
919
|
+
* @returns The group names by id.
|
|
920
|
+
*/
|
|
921
|
+
getCliActionGroups() {
|
|
922
|
+
return this.cliActionGroups;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* The HTTP actions, with the groups they belong to (named after `httpActionGroups`).
|
|
926
|
+
* @returns The actions and their groups.
|
|
927
|
+
*/
|
|
928
|
+
getHttpActions() {
|
|
929
|
+
return this.getEntrypointActions('http', this.httpActionGroups);
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* The service actions, with the groups they belong to (named after `serviceActionGroups`).
|
|
933
|
+
* @returns The actions and their groups.
|
|
934
|
+
*/
|
|
935
|
+
getServiceActions() {
|
|
936
|
+
return this.getEntrypointActions('service', this.serviceActionGroups);
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* The CLI actions, with the groups they belong to (named after `cliActionGroups`).
|
|
940
|
+
* @returns The actions and their groups.
|
|
941
|
+
*/
|
|
942
|
+
getCliActions() {
|
|
943
|
+
return this.getEntrypointActions('cli', this.cliActionGroups);
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* The actions of any channel, with the groups they belong to; the groups have no name (use the methods of the
|
|
947
|
+
* built-in channels, such as {@link Entrypoint.getHttpActions}, for named groups).
|
|
948
|
+
* @param channel The channel's name (`http`, `cli`, `service`, `schedule`, or a channel of another package).
|
|
949
|
+
* @returns The actions and their groups, empty when the channel has no actions.
|
|
950
|
+
*/
|
|
951
|
+
getActions(channel) {
|
|
952
|
+
return this.getEntrypointActions(channel);
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
exports.Entrypoint = Entrypoint;
|
|
956
|
+
__decorate([
|
|
957
|
+
(0, di_1.Configurable)(core_1.DTO.Array(core_1.DTO.Class(Controller_js_1.Controller)).optional().default([])),
|
|
958
|
+
__metadata("design:type", Array)
|
|
959
|
+
], Entrypoint.prototype, "controllers", void 0);
|
|
960
|
+
__decorate([
|
|
961
|
+
(0, di_1.Configurable)(core_1.DTO.Array(core_1.DTO.Function()).optional().default([])),
|
|
962
|
+
__metadata("design:type", Array)
|
|
963
|
+
], Entrypoint.prototype, "middlewares", void 0);
|
|
964
|
+
__decorate([
|
|
965
|
+
(0, di_1.Configurable)(core_1.DTO.Array(core_1.DTO.Class(AccessControlRule_js_1.AccessControlRule)).optional().default([])),
|
|
966
|
+
__metadata("design:type", Array)
|
|
967
|
+
], Entrypoint.prototype, "rules", void 0);
|
|
968
|
+
__decorate([
|
|
969
|
+
(0, di_1.Configurable)(core_1.DTO.Number().integer().min(0).optional().default(30000)),
|
|
970
|
+
__metadata("design:type", Number)
|
|
971
|
+
], Entrypoint.prototype, "shutdownTimeout", void 0);
|
|
972
|
+
__decorate([
|
|
973
|
+
(0, di_1.Configurable)(core_1.DTO.Object().pattern(core_1.DTO.String(), core_1.DTO.String()).optional().default({})),
|
|
974
|
+
__metadata("design:type", Object)
|
|
975
|
+
], Entrypoint.prototype, "cliActionGroups", void 0);
|
|
976
|
+
__decorate([
|
|
977
|
+
(0, di_1.Configurable)(core_1.DTO.Object().pattern(core_1.DTO.String(), core_1.DTO.String()).optional().default({})),
|
|
978
|
+
__metadata("design:type", Object)
|
|
979
|
+
], Entrypoint.prototype, "httpActionGroups", void 0);
|
|
980
|
+
__decorate([
|
|
981
|
+
(0, di_1.Configurable)(core_1.DTO.Object().pattern(core_1.DTO.String(), core_1.DTO.String()).optional().default({})),
|
|
982
|
+
__metadata("design:type", Object)
|
|
983
|
+
], Entrypoint.prototype, "serviceActionGroups", void 0);
|
|
984
|
+
__decorate([
|
|
985
|
+
(0, di_1.Configurable)(),
|
|
986
|
+
__metadata("design:type", Object)
|
|
987
|
+
], Entrypoint.prototype, "cli", void 0);
|
|
988
|
+
__decorate([
|
|
989
|
+
(0, di_1.Configurable)(),
|
|
990
|
+
__metadata("design:type", Object)
|
|
991
|
+
], Entrypoint.prototype, "http", void 0);
|
|
992
|
+
__decorate([
|
|
993
|
+
(0, di_1.Configurable)(),
|
|
994
|
+
__metadata("design:type", Object)
|
|
995
|
+
], Entrypoint.prototype, "service", void 0);
|
|
996
|
+
exports.Entrypoint = Entrypoint = __decorate([
|
|
997
|
+
(0, di_1.Singleton)()
|
|
998
|
+
], Entrypoint);
|