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