@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,301 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.expressHTTPEntrypoint = expressHTTPEntrypoint;
7
+ const express_1 = __importDefault(require("express"));
8
+ const node_http_1 = require("node:http");
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const HTTPContext_js_1 = require("../../context/HTTPContext.js");
11
+ const BuiltinChannels_js_1 = require("../../channel/BuiltinChannels.js");
12
+ const Result_js_1 = require("../../lib/Result.js");
13
+ const RouteNotFoundException_js_1 = require("../../exceptions/RouteNotFoundException.js");
14
+ const Responders_js_1 = require("./Responders.js");
15
+ /**
16
+ * The first segments of the routes
17
+ * @param routes
18
+ */
19
+ function actionPrefixesOf(routes) {
20
+ const prefixes = new Set();
21
+ for (const route of routes) {
22
+ const segment = route.split('/').find((part) => !!part);
23
+ //A route starting with a parameter matches every path
24
+ if (segment && !/^[:*]/.test(segment))
25
+ prefixes.add(`/${segment}`);
26
+ }
27
+ return [...prefixes];
28
+ }
29
+ /**
30
+ * The data of an event of a stream
31
+ * @param data
32
+ * @param event
33
+ * @param id
34
+ */
35
+ function eventFrame(data, event, id) {
36
+ let frame = '';
37
+ if (id !== undefined)
38
+ frame += `id: ${id}\n`;
39
+ if (event !== undefined)
40
+ frame += `event: ${event}\n`;
41
+ const text = typeof data === 'string' ? data : JSON.stringify(data);
42
+ for (const line of text.split(/\r?\n/))
43
+ frame += `data: ${line}\n`;
44
+ return `${frame}\n`;
45
+ }
46
+ /**
47
+ * Create the HTTP adapter of an entrypoint served by an express application: each HTTP action (`@GET`, `@POST`,
48
+ * `@HTTPAction`…) becomes an express route. It needs the optional peer dependency `express`.
49
+ *
50
+ * An action's input is the request's query and body (see the `input` option) with the route parameters merged over
51
+ * them. Its result is written by the responder (JSON by default, 204 for no result); a `StatusResult` sets the status
52
+ * and the headers, a `FileResult` sends a file, a buffer or a stream (304 when its ETag matches `If-None-Match`), and an
53
+ * `EventsResult` streams Server-Sent Events. An action that writes the response itself (through
54
+ * `HTTPContext.response`) is left alone. The errors are written by the responder with their status (400 for the
55
+ * validation failures, 500 by default).
56
+ *
57
+ * When the client disconnects before the response, the action's abort signal fires and nothing is written. When the
58
+ * entrypoint stops, the server stops accepting connections, the event streams end, and the requests in progress are
59
+ * given up to one second after they settle to send their responses before the remaining connections are closed.
60
+ * @param options The adapter options; by default it listens on port 80 of all the interfaces with a new express
61
+ * application.
62
+ * @returns The adapter, to give to the `http` option of `buildEntrypoints`.
63
+ * @example
64
+ * ```typescript
65
+ * import {Application} from 'lakutata'
66
+ * import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
67
+ * import {GET, POST} from 'lakutata/decorator/ctrl'
68
+ * import {expressHTTPEntrypoint, staticUI} from 'lakutata/adapter/express'
69
+ *
70
+ * class UserController extends Controller {
71
+ * //GET /users/1?verbose=true: the route parameter id merged over the query
72
+ * @GET('/users/:id')
73
+ * public async get(input: {id: string, verbose?: string}): Promise<{id: string}> {
74
+ * return {id: input.id}
75
+ * }
76
+ *
77
+ * //POST /users with the JSON body {"name": "Ada"}
78
+ * @POST('/users')
79
+ * public async create(input: {name: string}): Promise<{name: string}> {
80
+ * return {name: input.name}
81
+ * }
82
+ * }
83
+ *
84
+ * Application.run({
85
+ * id: 'web.app',
86
+ * name: 'Web',
87
+ * components: {
88
+ * entrypoint: buildEntrypoints({
89
+ * controllers: [UserController],
90
+ * http: expressHTTPEntrypoint({
91
+ * port: 8080,
92
+ * body: {limit: '5mb'},
93
+ * //The application's own routes, before the actions
94
+ * before: (app): void => {
95
+ * app.get('/health', (request, response): void => void response.send('ok'))
96
+ * },
97
+ * //The pages of the UI built in ./ui/dist, for the paths outside /users
98
+ * fallback: staticUI('./ui/dist')
99
+ * })
100
+ * })
101
+ * }
102
+ * })
103
+ * ```
104
+ */
105
+ function expressHTTPEntrypoint(options = {}) {
106
+ const respond = options.respond ?? Responders_js_1.JSONResponder;
107
+ const input = options.input ?? ((request) => ({ ...request.query, ...(request.body && typeof request.body === 'object' ? request.body : {}) }));
108
+ const streams = new Set();
109
+ let server;
110
+ let closed;
111
+ let stopping = false;
112
+ //The responses of the actions not sent yet
113
+ const responding = new Set();
114
+ /**
115
+ * Stream the events of a result
116
+ * @param result
117
+ * @param request
118
+ * @param response
119
+ */
120
+ const streamEvents = async (result, request, response) => {
121
+ const abortController = new AbortController();
122
+ streams.add(abortController);
123
+ response.status(200).set({
124
+ 'Content-Type': 'text/event-stream; charset=utf-8',
125
+ 'Cache-Control': 'no-cache, no-transform',
126
+ 'Connection': 'keep-alive',
127
+ 'X-Accel-Buffering': 'no'
128
+ }).flushHeaders();
129
+ const heartbeat = setInterval(() => response.write(': ping\n\n'), options.heartbeat ?? 15000);
130
+ const close = () => abortController.abort();
131
+ response.on('close', close);
132
+ try {
133
+ await result.producer((data, event, id) => {
134
+ if (!abortController.signal.aborted)
135
+ response.write(eventFrame(data, event, id));
136
+ }, abortController.signal);
137
+ }
138
+ catch {
139
+ //The stream is already started, its error ends it
140
+ }
141
+ finally {
142
+ clearInterval(heartbeat);
143
+ response.off('close', close);
144
+ streams.delete(abortController);
145
+ if (!response.writableEnded)
146
+ response.end();
147
+ }
148
+ };
149
+ /**
150
+ * Send a file
151
+ * @param result
152
+ * @param request
153
+ * @param response
154
+ */
155
+ const sendFile = async (result, request, response) => {
156
+ const { contentType, filename, etag, headers } = result.options;
157
+ if (headers)
158
+ response.set(headers);
159
+ if (etag) {
160
+ response.set('ETag', `"${etag}"`);
161
+ if (request.headers['if-none-match'] === `"${etag}"`) {
162
+ response.status(304).end();
163
+ return;
164
+ }
165
+ }
166
+ if (filename)
167
+ response.attachment(filename);
168
+ if (contentType)
169
+ response.type(contentType);
170
+ const source = result.source;
171
+ if (typeof source === 'string') {
172
+ await new Promise((resolve, reject) => response.sendFile(node_path_1.default.resolve(source), (error) => error ? reject(error) : resolve()));
173
+ }
174
+ else if (Buffer.isBuffer(source)) {
175
+ response.send(source);
176
+ }
177
+ else {
178
+ await new Promise((resolve, reject) => {
179
+ source.once('error', reject).pipe(response).once('finish', resolve);
180
+ });
181
+ }
182
+ };
183
+ /**
184
+ * Write the result of an action
185
+ * @param result
186
+ * @param request
187
+ * @param response
188
+ */
189
+ const output = async (result, request, response) => {
190
+ //The action wrote the response itself
191
+ if (response.headersSent)
192
+ return;
193
+ if (result instanceof Result_js_1.EventsResult)
194
+ return await streamEvents(result, request, response);
195
+ if (result instanceof Result_js_1.FileResult)
196
+ return await sendFile(result, request, response);
197
+ if (result instanceof Result_js_1.StatusResult) {
198
+ response.status(result.status).set(result.headers);
199
+ return respond.success(response, result.data, request);
200
+ }
201
+ respond.success(response, result, request);
202
+ };
203
+ /**
204
+ * The handler of the requests of an action
205
+ * @param runtime
206
+ * @param action
207
+ */
208
+ const actionHandler = (runtime, action) => (request, response) => {
209
+ const abortController = new AbortController();
210
+ const responded = new Promise((resolve) => void response.once('close', resolve));
211
+ responding.add(responded);
212
+ //The client left before the response was sent
213
+ response.on('close', () => {
214
+ responding.delete(responded);
215
+ if (!response.writableFinished)
216
+ abortController.abort();
217
+ });
218
+ //While the adapter stops, the connections are closed once their response is sent
219
+ response.on('finish', () => {
220
+ if (stopping)
221
+ setImmediate(() => server?.closeIdleConnections());
222
+ });
223
+ const dispatch = async () => {
224
+ const context = new HTTPContext_js_1.HTTPContext({
225
+ route: action.address.route,
226
+ method: action.address.method,
227
+ request: request,
228
+ response: response,
229
+ data: input(request)
230
+ });
231
+ const result = await runtime.dispatchTo(action, context, { signal: abortController.signal, params: { ...request.params } });
232
+ if (!abortController.signal.aborted)
233
+ await output(result, request, response);
234
+ };
235
+ dispatch().catch((error) => {
236
+ if (!response.headersSent && !abortController.signal.aborted)
237
+ respond.error(response, error, request, 'action');
238
+ });
239
+ };
240
+ return {
241
+ channel: 'http',
242
+ definition: BuiltinChannels_js_1.HTTPChannel,
243
+ name: options.name,
244
+ middlewares: options.middlewares,
245
+ start: async (runtime) => {
246
+ const app = options.app ?? (0, express_1.default)();
247
+ server = (0, node_http_1.createServer)(options.server ?? {}, app);
248
+ const routes = new Map();
249
+ for (const action of runtime.actions)
250
+ routes.set(action.address.route, (routes.get(action.address.route) ?? new Set()).add(action.address.method));
251
+ const actionPrefixes = actionPrefixesOf(routes.keys());
252
+ const isActionPath = (requestPath) => actionPrefixes.some((prefix) => requestPath === prefix || requestPath.startsWith(`${prefix}/`));
253
+ await options.before?.(app, { module: runtime.module, server: server, routes: routes, actionPrefixes: actionPrefixes, isActionPath: isActionPath });
254
+ if (options.body !== false)
255
+ app.use(express_1.default.json({ limit: options.body?.limit ?? '1mb' }), express_1.default.urlencoded({ extended: true, limit: options.body?.limit ?? '1mb' }));
256
+ for (const action of runtime.actions) {
257
+ const method = String(action.address.method).toLowerCase();
258
+ const register = app[method];
259
+ if (typeof register !== 'function')
260
+ throw new TypeError(`Unsupported HTTP method "${action.address.method}" of the route "${action.address.route}"`);
261
+ register.call(app, action.address.route, actionHandler(runtime, action));
262
+ }
263
+ const fallback = options.fallback;
264
+ if (fallback)
265
+ app.use((request, response, next) => isActionPath(request.path) ? next() : fallback(request, response, next));
266
+ app.use((request, response) => respond.error(response, new RouteNotFoundException_js_1.RouteNotFoundException('Route {route} not found', { route: request.path }), request, 'request'));
267
+ app.use((error, request, response, next) => {
268
+ if (response.headersSent)
269
+ return next(error);
270
+ respond.error(response, error, request, 'request');
271
+ });
272
+ //The adapter starts once the server listens (an occupied port fails the start)
273
+ await new Promise((resolve, reject) => {
274
+ server.once('error', reject).listen(options.port ?? 80, options.host, () => {
275
+ server.off('error', reject);
276
+ resolve();
277
+ });
278
+ });
279
+ },
280
+ stop: () => {
281
+ //The event streams end, the server stops accepting connections and closes the idle ones
282
+ stopping = true;
283
+ streams.forEach((stream) => stream.abort());
284
+ if (!server?.listening)
285
+ return;
286
+ closed = new Promise((resolve) => void server.close(() => resolve()));
287
+ server.closeIdleConnections();
288
+ },
289
+ close: async () => {
290
+ //The requests in progress ended or were aborted: their responses are written, then the remaining connections are closed
291
+ let timer;
292
+ await Promise.race([Promise.all(responding), new Promise((resolve) => {
293
+ timer = setTimeout(resolve, 1000);
294
+ })]);
295
+ clearTimeout(timer);
296
+ server?.closeAllConnections();
297
+ await closed;
298
+ await options.stop?.();
299
+ }
300
+ };
301
+ }
@@ -0,0 +1,89 @@
1
+ import type { Request, Response } from 'express';
2
+ import { describeError, type HTTPErrorOrigin, statusOfError } from '../../lib/HTTPErrors.js';
3
+ export { describeError, statusOfError, type HTTPErrorOrigin };
4
+ /**
5
+ * How {@link expressHTTPEntrypoint} writes the results and the errors of the actions (its `respond` option). Implement
6
+ * it to give the responses another shape; {@link JSONResponder} is the default, {@link jsonEnvelope} wraps them in an
7
+ * envelope. The file results and the event streams are not written by the responder.
8
+ */
9
+ export interface ExpressResponder {
10
+ /**
11
+ * Write the result of an action. For a `StatusResult`, its status and headers are already set on the response and
12
+ * `data` is its payload.
13
+ * @param response The express response to write.
14
+ * @param data The action's result, `undefined` when it returned nothing.
15
+ * @param request The express request being answered.
16
+ */
17
+ success(response: Response, data: unknown, request: Request): void;
18
+ /**
19
+ * Write an error: a failed action (validation, access control, its own exception), an error passed by an express
20
+ * middleware (`next(error)`: an authenticator), or a request matching no route (`RouteNotFoundException`). Not
21
+ * called once the response headers are sent, nor after the client left.
22
+ * @param response The express response to write.
23
+ * @param error The thrown error.
24
+ * @param request The express request being answered.
25
+ * @param origin Where the error comes from: `'action'` (the dispatch of an action failed) or `'request'` (an
26
+ * express middleware, a route not found); see {@link HTTPErrorOrigin}.
27
+ */
28
+ error(response: Response, error: unknown, request: Request, origin?: HTTPErrorOrigin): void;
29
+ }
30
+ /**
31
+ * The default responder of {@link expressHTTPEntrypoint}: a result is sent as JSON, no result as an empty 204 (or the
32
+ * status set by a `StatusResult`); an error is sent as `{errno, errMsg}` (see `describeError`) with its HTTP status
33
+ * (see `statusOfError`: the first 4xx status in its cause chain, its own status, or 500).
34
+ */
35
+ export declare const JSONResponder: ExpressResponder;
36
+ /**
37
+ * The options of {@link jsonEnvelope}.
38
+ */
39
+ export type JSONEnvelopeOptions = {
40
+ /**
41
+ * The HTTP status of every error response, such as 200 for the clients that read the errors from the envelope.
42
+ * @default the error's own status (see `statusOfError`)
43
+ */
44
+ errorStatus?: number;
45
+ /**
46
+ * The HTTP status of the errors of the actions only (their dispatch failed: validation, access control, their own
47
+ * exceptions), over `errorStatus`; the errors of the express middlewares (an authenticator's 401) and the routes
48
+ * not found keep theirs. 200 answers as the applications of lakutata 2.x usually did.
49
+ * @default `errorStatus`
50
+ */
51
+ actionErrorStatus?: number;
52
+ };
53
+ /**
54
+ * Create a responder for {@link expressHTTPEntrypoint} that wraps every response in the envelope
55
+ * `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
56
+ * nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
57
+ * the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option.
58
+ * @param options The envelope options.
59
+ * @returns The responder, to give to the `respond` option of `expressHTTPEntrypoint`.
60
+ * @example
61
+ * ```typescript
62
+ * import {Application} from 'lakutata'
63
+ * import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
64
+ * import {GET} from 'lakutata/decorator/ctrl'
65
+ * import {expressHTTPEntrypoint, jsonEnvelope} from 'lakutata/adapter/express'
66
+ *
67
+ * class PingController extends Controller {
68
+ * //GET /ping answers {"errno": null, "errMsg": null, "data": "pong"}
69
+ * @GET('/ping')
70
+ * public async ping(): Promise<string> {
71
+ * return 'pong'
72
+ * }
73
+ * }
74
+ *
75
+ * Application.run({
76
+ * id: 'web.app',
77
+ * name: 'Web',
78
+ * components: {
79
+ * entrypoint: buildEntrypoints({
80
+ * controllers: [PingController],
81
+ * //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
82
+ * //found and the errors of the express middlewares with their status (404, 401…)
83
+ * http: expressHTTPEntrypoint({port: 8080, respond: jsonEnvelope({actionErrorStatus: 200})})
84
+ * })
85
+ * }
86
+ * })
87
+ * ```
88
+ */
89
+ export declare function jsonEnvelope(options?: JSONEnvelopeOptions): ExpressResponder;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JSONResponder = exports.statusOfError = exports.describeError = void 0;
4
+ exports.jsonEnvelope = jsonEnvelope;
5
+ const HTTPErrors_js_1 = require("../../lib/HTTPErrors.js");
6
+ Object.defineProperty(exports, "describeError", { enumerable: true, get: function () { return HTTPErrors_js_1.describeError; } });
7
+ Object.defineProperty(exports, "statusOfError", { enumerable: true, get: function () { return HTTPErrors_js_1.statusOfError; } });
8
+ /**
9
+ * The default responder of {@link expressHTTPEntrypoint}: a result is sent as JSON, no result as an empty 204 (or the
10
+ * status set by a `StatusResult`); an error is sent as `{errno, errMsg}` (see `describeError`) with its HTTP status
11
+ * (see `statusOfError`: the first 4xx status in its cause chain, its own status, or 500).
12
+ */
13
+ exports.JSONResponder = {
14
+ success: (response, data) => {
15
+ if (data === undefined) {
16
+ response.status(response.statusCode === 200 ? 204 : response.statusCode).end();
17
+ return;
18
+ }
19
+ response.json(data);
20
+ },
21
+ error: (response, error) => {
22
+ response.status((0, HTTPErrors_js_1.statusOfError)(error)).json((0, HTTPErrors_js_1.describeError)(error));
23
+ }
24
+ };
25
+ /**
26
+ * Create a responder for {@link expressHTTPEntrypoint} that wraps every response in the envelope
27
+ * `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
28
+ * nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
29
+ * the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option.
30
+ * @param options The envelope options.
31
+ * @returns The responder, to give to the `respond` option of `expressHTTPEntrypoint`.
32
+ * @example
33
+ * ```typescript
34
+ * import {Application} from 'lakutata'
35
+ * import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
36
+ * import {GET} from 'lakutata/decorator/ctrl'
37
+ * import {expressHTTPEntrypoint, jsonEnvelope} from 'lakutata/adapter/express'
38
+ *
39
+ * class PingController extends Controller {
40
+ * //GET /ping answers {"errno": null, "errMsg": null, "data": "pong"}
41
+ * @GET('/ping')
42
+ * public async ping(): Promise<string> {
43
+ * return 'pong'
44
+ * }
45
+ * }
46
+ *
47
+ * Application.run({
48
+ * id: 'web.app',
49
+ * name: 'Web',
50
+ * components: {
51
+ * entrypoint: buildEntrypoints({
52
+ * controllers: [PingController],
53
+ * //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
54
+ * //found and the errors of the express middlewares with their status (404, 401…)
55
+ * http: expressHTTPEntrypoint({port: 8080, respond: jsonEnvelope({actionErrorStatus: 200})})
56
+ * })
57
+ * }
58
+ * })
59
+ * ```
60
+ */
61
+ function jsonEnvelope(options = {}) {
62
+ return {
63
+ success: (response, data) => {
64
+ response.json({ errno: null, errMsg: null, data: data === undefined ? null : data });
65
+ },
66
+ error: (response, error, _request, origin) => {
67
+ const status = origin === 'action' ? options.actionErrorStatus ?? options.errorStatus : options.errorStatus;
68
+ response.status(status ?? (0, HTTPErrors_js_1.statusOfError)(error)).json({ ...(0, HTTPErrors_js_1.describeError)(error), data: null });
69
+ }
70
+ };
71
+ }
@@ -0,0 +1,83 @@
1
+ import { type RequestHandler } from 'express';
2
+ /**
3
+ * The options of {@link staticUI}.
4
+ */
5
+ export type StaticUIOptions = {
6
+ /**
7
+ * Serve a single-page application: a GET or HEAD request accepting HTML, whose path has no file extension and
8
+ * matches no file, is answered with the index (the client-side router handles the path). With `false`, such a
9
+ * request is passed on (a 404 behind `expressHTTPEntrypoint`).
10
+ * @default true
11
+ */
12
+ spa?: boolean;
13
+ /**
14
+ * The index file of the application, relative to the directory.
15
+ * @default 'index.html'
16
+ */
17
+ index?: string;
18
+ /**
19
+ * How long the browsers may cache the files with a content hash in their name (the assets of a Vite build:
20
+ * `index-B2x9fQ1a.js`): a number of seconds, or a duration (`'30d'`, `'12h'`; a string of digits is seconds). A file
21
+ * counts as hashed when it is a script, a style, a font or an image whose name ends with a dot or a dash followed by
22
+ * at least 8 letters, digits, `_` or `-`, among them a digit or an uppercase letter, before its extension
23
+ * (`index-B2x9fQ1a.js`, `main.3f2a1b9c.css`, not `my-component.js`). The other files and the index are sent with
24
+ * `Cache-Control: no-cache`.
25
+ * @default 31536000 (one year)
26
+ */
27
+ assetsMaxAge?: string | number;
28
+ };
29
+ /**
30
+ * Whether a file of a built UI has a content hash in its name: a script, a style, a font or an image whose name ends
31
+ * with a dot or a dash followed by at least 8 letters, digits, `_` or `-`, among them a digit or an uppercase letter
32
+ * @param file
33
+ * @internal
34
+ */
35
+ export declare function isHashedAsset(file: string): boolean;
36
+ /**
37
+ * The seconds the hashed assets are cached: a number of seconds, or a duration
38
+ * @param value
39
+ * @internal
40
+ */
41
+ export declare function assetsMaxAge(value: string | number | undefined): number;
42
+ /**
43
+ * Create an express handler serving a built web UI (the `dist` directory of a Vite application): its files, and its
44
+ * index for the paths of its client-side routes. Given as the `fallback` of {@link expressHTTPEntrypoint}, it never
45
+ * receives the paths of the actions. It needs the optional peer dependency `express`.
46
+ *
47
+ * The files with a content hash in their name are cached for a long time (`assetsMaxAge`), the other files and the
48
+ * index are revalidated on each request. A request matching no file that is not a page request (an asset, a non-GET
49
+ * request, a client not accepting HTML) is passed on to the next handler.
50
+ * @param directory The directory of the built UI, resolved from the current working directory.
51
+ * @param options How the index is served and the assets cached.
52
+ * @returns The express request handler.
53
+ * @example
54
+ * ```typescript
55
+ * import {Application} from 'lakutata'
56
+ * import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
57
+ * import {GET} from 'lakutata/decorator/ctrl'
58
+ * import {expressHTTPEntrypoint, staticUI} from 'lakutata/adapter/express'
59
+ *
60
+ * class DeviceController extends Controller {
61
+ * //GET /api/devices answers the action; GET /settings answers the index of the UI
62
+ * @GET('/api/devices')
63
+ * public async list(): Promise<string[]> {
64
+ * return ['sensor-1']
65
+ * }
66
+ * }
67
+ *
68
+ * Application.run({
69
+ * id: 'console.app',
70
+ * name: 'Console',
71
+ * components: {
72
+ * entrypoint: buildEntrypoints({
73
+ * controllers: [DeviceController],
74
+ * http: expressHTTPEntrypoint({
75
+ * port: 8080,
76
+ * fallback: staticUI('./ui/dist', {assetsMaxAge: 86400})
77
+ * })
78
+ * })
79
+ * }
80
+ * })
81
+ * ```
82
+ */
83
+ export declare function staticUI(directory: string, options?: StaticUIOptions): RequestHandler;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isHashedAsset = isHashedAsset;
7
+ exports.assetsMaxAge = assetsMaxAge;
8
+ exports.staticUI = staticUI;
9
+ const express_1 = __importDefault(require("express"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const Cron_js_1 = require("../../lib/schedule/Cron.js");
12
+ /**
13
+ * Whether a file of a built UI has a content hash in its name: a script, a style, a font or an image whose name ends
14
+ * with a dot or a dash followed by at least 8 letters, digits, `_` or `-`, among them a digit or an uppercase letter
15
+ * @param file
16
+ * @internal
17
+ */
18
+ function isHashedAsset(file) {
19
+ const matched = /[.-]([0-9A-Za-z_-]{8,})\.(?:js|mjs|css|woff2?|png|jpe?g|svg|gif|webp|avif)$/.exec(file);
20
+ return !!matched && /[0-9A-Z]/.test(matched[1]);
21
+ }
22
+ /**
23
+ * The seconds the hashed assets are cached: a number of seconds, or a duration
24
+ * @param value
25
+ * @internal
26
+ */
27
+ function assetsMaxAge(value) {
28
+ if (value === undefined)
29
+ return 31536000;
30
+ const seconds = typeof value === 'number' || /^\d+(?:\.\d+)?$/.test(value) ? Number(value) : (0, Cron_js_1.parseDuration)(value) / 1000;
31
+ if (!Number.isFinite(seconds) || seconds < 0)
32
+ throw new RangeError(`Invalid assetsMaxAge "${value}": a number of seconds or a duration ("30d")`);
33
+ return Math.floor(seconds);
34
+ }
35
+ /**
36
+ * Create an express handler serving a built web UI (the `dist` directory of a Vite application): its files, and its
37
+ * index for the paths of its client-side routes. Given as the `fallback` of {@link expressHTTPEntrypoint}, it never
38
+ * receives the paths of the actions. It needs the optional peer dependency `express`.
39
+ *
40
+ * The files with a content hash in their name are cached for a long time (`assetsMaxAge`), the other files and the
41
+ * index are revalidated on each request. A request matching no file that is not a page request (an asset, a non-GET
42
+ * request, a client not accepting HTML) is passed on to the next handler.
43
+ * @param directory The directory of the built UI, resolved from the current working directory.
44
+ * @param options How the index is served and the assets cached.
45
+ * @returns The express request handler.
46
+ * @example
47
+ * ```typescript
48
+ * import {Application} from 'lakutata'
49
+ * import {buildEntrypoints, Controller} from 'lakutata/com/entrypoint'
50
+ * import {GET} from 'lakutata/decorator/ctrl'
51
+ * import {expressHTTPEntrypoint, staticUI} from 'lakutata/adapter/express'
52
+ *
53
+ * class DeviceController extends Controller {
54
+ * //GET /api/devices answers the action; GET /settings answers the index of the UI
55
+ * @GET('/api/devices')
56
+ * public async list(): Promise<string[]> {
57
+ * return ['sensor-1']
58
+ * }
59
+ * }
60
+ *
61
+ * Application.run({
62
+ * id: 'console.app',
63
+ * name: 'Console',
64
+ * components: {
65
+ * entrypoint: buildEntrypoints({
66
+ * controllers: [DeviceController],
67
+ * http: expressHTTPEntrypoint({
68
+ * port: 8080,
69
+ * fallback: staticUI('./ui/dist', {assetsMaxAge: 86400})
70
+ * })
71
+ * })
72
+ * }
73
+ * })
74
+ * ```
75
+ */
76
+ function staticUI(directory, options = {}) {
77
+ const root = node_path_1.default.resolve(directory);
78
+ const index = options.index ?? 'index.html';
79
+ const maxAge = assetsMaxAge(options.assetsMaxAge);
80
+ const files = express_1.default.static(root, {
81
+ index: false,
82
+ setHeaders: (response, file) => {
83
+ if (isHashedAsset(file))
84
+ response.setHeader('Cache-Control', `public, max-age=${maxAge}, immutable`);
85
+ else
86
+ response.setHeader('Cache-Control', 'no-cache');
87
+ }
88
+ });
89
+ return (request, response, next) => {
90
+ files(request, response, (error) => {
91
+ if (error)
92
+ return next(error);
93
+ //The routes of the application are its index; the missing files are not
94
+ const wantsPage = (request.method === 'GET' || request.method === 'HEAD') && !node_path_1.default.extname(request.path) && request.accepts('html') === 'html';
95
+ if (options.spa === false || !wantsPage)
96
+ return next();
97
+ response.setHeader('Cache-Control', 'no-cache');
98
+ response.sendFile(node_path_1.default.join(root, index), (sendError) => {
99
+ if (sendError)
100
+ next(sendError);
101
+ });
102
+ });
103
+ };
104
+ }