jimpex 7.0.2 → 8.0.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 (388) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/LICENSE +1 -1
  3. package/README.md +446 -434
  4. package/dist/app/index.d.ts +13 -0
  5. package/dist/app/index.js +19 -0
  6. package/dist/app/index.js.map +1 -0
  7. package/dist/app/jimpex.d.ts +13 -0
  8. package/dist/app/jimpex.js +440 -0
  9. package/dist/app/jimpex.js.map +1 -0
  10. package/dist/controllers/common/config.d.ts +78 -0
  11. package/dist/controllers/common/config.js +89 -0
  12. package/dist/controllers/common/config.js.map +1 -0
  13. package/dist/controllers/common/health.d.ts +82 -0
  14. package/dist/controllers/common/health.js +97 -0
  15. package/dist/controllers/common/health.js.map +1 -0
  16. package/dist/controllers/common/index.d.ts +21 -0
  17. package/dist/controllers/common/index.js +21 -0
  18. package/dist/controllers/common/index.js.map +1 -0
  19. package/dist/controllers/common/statics.d.ts +215 -0
  20. package/dist/controllers/common/statics.js +202 -0
  21. package/dist/controllers/common/statics.js.map +1 -0
  22. package/dist/controllers/index.d.ts +24 -0
  23. package/dist/controllers/index.js +20 -0
  24. package/dist/controllers/index.js.map +1 -0
  25. package/dist/controllers/utils/gateway.d.ts +724 -0
  26. package/dist/controllers/utils/gateway.js +425 -0
  27. package/dist/controllers/utils/gateway.js.map +1 -0
  28. package/dist/controllers/utils/index.d.ts +16 -0
  29. package/dist/controllers/utils/index.js +19 -0
  30. package/dist/controllers/utils/index.js.map +1 -0
  31. package/dist/esm/app/index.js +2 -0
  32. package/dist/esm/app/index.js.map +1 -0
  33. package/dist/esm/app/jimpex.js +415 -0
  34. package/dist/esm/app/jimpex.js.map +1 -0
  35. package/dist/esm/chunk-T2T6Q22Z.js +11 -0
  36. package/dist/esm/chunk-T2T6Q22Z.js.map +1 -0
  37. package/dist/esm/controllers/common/config.js +62 -0
  38. package/dist/esm/controllers/common/config.js.map +1 -0
  39. package/dist/esm/controllers/common/health.js +70 -0
  40. package/dist/esm/controllers/common/health.js.map +1 -0
  41. package/dist/esm/controllers/common/index.js +4 -0
  42. package/dist/esm/controllers/common/index.js.map +1 -0
  43. package/dist/esm/controllers/common/statics.js +173 -0
  44. package/dist/esm/controllers/common/statics.js.map +1 -0
  45. package/dist/esm/controllers/index.js +3 -0
  46. package/dist/esm/controllers/index.js.map +1 -0
  47. package/dist/esm/controllers/utils/gateway.js +404 -0
  48. package/dist/esm/controllers/utils/gateway.js.map +1 -0
  49. package/dist/esm/controllers/utils/index.js +2 -0
  50. package/dist/esm/controllers/utils/index.js.map +1 -0
  51. package/dist/esm/index.js +7 -0
  52. package/dist/esm/index.js.map +1 -0
  53. package/dist/esm/middlewares/common/errorHandler.js +92 -0
  54. package/dist/esm/middlewares/common/errorHandler.js.map +1 -0
  55. package/dist/esm/middlewares/common/forceHTTPS.js +41 -0
  56. package/dist/esm/middlewares/common/forceHTTPS.js.map +1 -0
  57. package/dist/esm/middlewares/common/hsts.js +53 -0
  58. package/dist/esm/middlewares/common/hsts.js.map +1 -0
  59. package/dist/esm/middlewares/common/index.js +4 -0
  60. package/dist/esm/middlewares/common/index.js.map +1 -0
  61. package/dist/esm/middlewares/html/fastHTML.js +104 -0
  62. package/dist/esm/middlewares/html/fastHTML.js.map +1 -0
  63. package/dist/esm/middlewares/html/index.js +3 -0
  64. package/dist/esm/middlewares/html/index.js.map +1 -0
  65. package/dist/esm/middlewares/html/showHTML.js +68 -0
  66. package/dist/esm/middlewares/html/showHTML.js.map +1 -0
  67. package/dist/esm/middlewares/index.js +4 -0
  68. package/dist/esm/middlewares/index.js.map +1 -0
  69. package/dist/esm/middlewares/utils/index.js +2 -0
  70. package/dist/esm/middlewares/utils/index.js.map +1 -0
  71. package/dist/esm/middlewares/utils/versionValidator.js +101 -0
  72. package/dist/esm/middlewares/utils/versionValidator.js.map +1 -0
  73. package/dist/esm/services/common/appError.js +52 -0
  74. package/dist/esm/services/common/appError.js.map +1 -0
  75. package/dist/esm/services/common/httpError.js +19 -0
  76. package/dist/esm/services/common/httpError.js.map +1 -0
  77. package/dist/esm/services/common/index.js +17 -0
  78. package/dist/esm/services/common/index.js.map +1 -0
  79. package/dist/esm/services/common/sendFile.js +27 -0
  80. package/dist/esm/services/common/sendFile.js.map +1 -0
  81. package/dist/esm/services/frontend/frontendFs.js +38 -0
  82. package/dist/esm/services/frontend/frontendFs.js.map +1 -0
  83. package/dist/esm/services/frontend/index.js +11 -0
  84. package/dist/esm/services/frontend/index.js.map +1 -0
  85. package/dist/esm/services/html/htmlGenerator.js +144 -0
  86. package/dist/esm/services/html/htmlGenerator.js.map +1 -0
  87. package/dist/esm/services/html/index.js +11 -0
  88. package/dist/esm/services/html/index.js.map +1 -0
  89. package/dist/esm/services/http/apiClient.js +71 -0
  90. package/dist/esm/services/http/apiClient.js.map +1 -0
  91. package/dist/esm/services/http/http.js +125 -0
  92. package/dist/esm/services/http/http.js.map +1 -0
  93. package/dist/esm/services/http/index.js +17 -0
  94. package/dist/esm/services/http/index.js.map +1 -0
  95. package/dist/esm/services/http/responsesBuilder.js +105 -0
  96. package/dist/esm/services/http/responsesBuilder.js.map +1 -0
  97. package/dist/esm/services/index.js +6 -0
  98. package/dist/esm/services/index.js.map +1 -0
  99. package/dist/esm/services/utils/ensureBearerToken.js +78 -0
  100. package/dist/esm/services/utils/ensureBearerToken.js.map +1 -0
  101. package/dist/esm/services/utils/index.js +11 -0
  102. package/dist/esm/services/utils/index.js.map +1 -0
  103. package/dist/esm/types/events.js +1 -0
  104. package/dist/esm/types/events.js.map +1 -0
  105. package/dist/esm/types/express.js +1 -0
  106. package/dist/esm/types/express.js.map +1 -0
  107. package/dist/esm/types/http.js +1 -0
  108. package/dist/esm/types/http.js.map +1 -0
  109. package/dist/esm/types/index.js +7 -0
  110. package/dist/esm/types/index.js.map +1 -0
  111. package/dist/esm/types/options.js +1 -0
  112. package/dist/esm/types/options.js.map +1 -0
  113. package/dist/esm/types/utils.js +1 -0
  114. package/dist/esm/types/utils.js.map +1 -0
  115. package/dist/esm/types/wootils.js +1 -0
  116. package/dist/esm/types/wootils.js.map +1 -0
  117. package/dist/esm/utils/fns/index.js +5 -0
  118. package/dist/esm/utils/fns/index.js.map +1 -0
  119. package/dist/esm/utils/fns/others.js +6 -0
  120. package/dist/esm/utils/fns/others.js.map +1 -0
  121. package/dist/esm/utils/fns/routes.js +25 -0
  122. package/dist/esm/utils/fns/routes.js.map +1 -0
  123. package/dist/esm/utils/fns/statuses.js +6 -0
  124. package/dist/esm/utils/fns/statuses.js.map +1 -0
  125. package/dist/esm/utils/fns/text.js +6 -0
  126. package/dist/esm/utils/fns/text.js.map +1 -0
  127. package/dist/esm/utils/index.js +3 -0
  128. package/dist/esm/utils/index.js.map +1 -0
  129. package/dist/esm/utils/wrappers.js +41 -0
  130. package/dist/esm/utils/wrappers.js.map +1 -0
  131. package/dist/index.d.ts +46 -0
  132. package/dist/index.js +24 -0
  133. package/dist/index.js.map +1 -0
  134. package/dist/jimpex-7eaee271.d.ts +1278 -0
  135. package/dist/middlewares/common/errorHandler.d.ts +131 -0
  136. package/dist/middlewares/common/errorHandler.js +119 -0
  137. package/dist/middlewares/common/errorHandler.js.map +1 -0
  138. package/dist/middlewares/common/forceHTTPS.d.ts +68 -0
  139. package/dist/middlewares/common/forceHTTPS.js +68 -0
  140. package/dist/middlewares/common/forceHTTPS.js.map +1 -0
  141. package/dist/middlewares/common/hsts.d.ts +109 -0
  142. package/dist/middlewares/common/hsts.js +80 -0
  143. package/dist/middlewares/common/hsts.js.map +1 -0
  144. package/dist/middlewares/common/index.d.ts +21 -0
  145. package/dist/middlewares/common/index.js +21 -0
  146. package/dist/middlewares/common/index.js.map +1 -0
  147. package/dist/middlewares/html/fastHTML.d.ts +180 -0
  148. package/dist/middlewares/html/fastHTML.js +131 -0
  149. package/dist/middlewares/html/fastHTML.js.map +1 -0
  150. package/dist/middlewares/html/index.d.ts +21 -0
  151. package/dist/middlewares/html/index.js +20 -0
  152. package/dist/middlewares/html/index.js.map +1 -0
  153. package/dist/middlewares/html/showHTML.d.ts +127 -0
  154. package/dist/middlewares/html/showHTML.js +95 -0
  155. package/dist/middlewares/html/showHTML.js.map +1 -0
  156. package/dist/middlewares/index.d.ts +30 -0
  157. package/dist/middlewares/index.js +21 -0
  158. package/dist/middlewares/index.js.map +1 -0
  159. package/dist/middlewares/utils/index.d.ts +19 -0
  160. package/dist/middlewares/utils/index.js +19 -0
  161. package/dist/middlewares/utils/index.js.map +1 -0
  162. package/dist/middlewares/utils/versionValidator.d.ts +214 -0
  163. package/dist/middlewares/utils/versionValidator.js +128 -0
  164. package/dist/middlewares/utils/versionValidator.js.map +1 -0
  165. package/dist/services/common/appError.d.ts +138 -0
  166. package/dist/services/common/appError.js +80 -0
  167. package/dist/services/common/appError.js.map +1 -0
  168. package/dist/services/common/httpError.d.ts +79 -0
  169. package/dist/services/common/httpError.js +44 -0
  170. package/dist/services/common/httpError.js.map +1 -0
  171. package/dist/services/common/index.d.ts +47 -0
  172. package/dist/services/common/index.js +41 -0
  173. package/dist/services/common/index.js.map +1 -0
  174. package/dist/services/common/sendFile.d.ts +102 -0
  175. package/dist/services/common/sendFile.js +51 -0
  176. package/dist/services/common/sendFile.js.map +1 -0
  177. package/dist/services/frontend/frontendFs.d.ts +96 -0
  178. package/dist/services/frontend/frontendFs.js +71 -0
  179. package/dist/services/frontend/frontendFs.js.map +1 -0
  180. package/dist/services/frontend/index.d.ts +40 -0
  181. package/dist/services/frontend/index.js +35 -0
  182. package/dist/services/frontend/index.js.map +1 -0
  183. package/dist/services/html/htmlGenerator.d.ts +237 -0
  184. package/dist/services/html/htmlGenerator.js +171 -0
  185. package/dist/services/html/htmlGenerator.js.map +1 -0
  186. package/dist/services/html/index.d.ts +43 -0
  187. package/dist/services/html/index.js +35 -0
  188. package/dist/services/html/index.js.map +1 -0
  189. package/dist/services/http/apiClient.d.ts +169 -0
  190. package/dist/services/http/apiClient.js +96 -0
  191. package/dist/services/http/apiClient.js.map +1 -0
  192. package/dist/services/http/http.d.ts +175 -0
  193. package/dist/services/http/http.js +158 -0
  194. package/dist/services/http/http.js.map +1 -0
  195. package/dist/services/http/index.d.ts +50 -0
  196. package/dist/services/http/index.js +41 -0
  197. package/dist/services/http/index.js.map +1 -0
  198. package/dist/services/http/responsesBuilder.d.ts +178 -0
  199. package/dist/services/http/responsesBuilder.js +132 -0
  200. package/dist/services/http/responsesBuilder.js.map +1 -0
  201. package/dist/services/index.d.ts +33 -0
  202. package/dist/services/index.js +23 -0
  203. package/dist/services/index.js.map +1 -0
  204. package/dist/services/utils/ensureBearerToken.d.ts +157 -0
  205. package/dist/services/utils/ensureBearerToken.js +105 -0
  206. package/dist/services/utils/ensureBearerToken.js.map +1 -0
  207. package/dist/services/utils/index.d.ts +43 -0
  208. package/dist/services/utils/index.js +35 -0
  209. package/dist/services/utils/index.js.map +1 -0
  210. package/dist/types/events.d.ts +13 -0
  211. package/dist/types/events.js +17 -0
  212. package/dist/types/events.js.map +1 -0
  213. package/dist/types/express.d.ts +10 -0
  214. package/dist/types/express.js +17 -0
  215. package/dist/types/express.js.map +1 -0
  216. package/dist/types/http.d.ts +79 -0
  217. package/dist/types/http.js +17 -0
  218. package/dist/types/http.js.map +1 -0
  219. package/dist/types/index.d.ts +14 -0
  220. package/dist/types/index.js +24 -0
  221. package/dist/types/index.js.map +1 -0
  222. package/dist/types/options.d.ts +13 -0
  223. package/dist/types/options.js +17 -0
  224. package/dist/types/options.js.map +1 -0
  225. package/dist/types/utils.d.ts +48 -0
  226. package/dist/types/utils.js +17 -0
  227. package/dist/types/utils.js.map +1 -0
  228. package/dist/types/wootils.d.ts +4 -0
  229. package/dist/types/wootils.js +17 -0
  230. package/dist/types/wootils.js.map +1 -0
  231. package/dist/utils/fns/index.d.ts +5 -0
  232. package/dist/utils/fns/index.js +22 -0
  233. package/dist/utils/fns/index.js.map +1 -0
  234. package/dist/utils/fns/others.d.ts +17 -0
  235. package/dist/utils/fns/others.js +29 -0
  236. package/dist/utils/fns/others.js.map +1 -0
  237. package/dist/utils/fns/routes.d.ts +39 -0
  238. package/dist/utils/fns/routes.js +51 -0
  239. package/dist/utils/fns/routes.js.map +1 -0
  240. package/dist/utils/fns/statuses.d.ts +45 -0
  241. package/dist/utils/fns/statuses.js +35 -0
  242. package/dist/utils/fns/statuses.js.map +1 -0
  243. package/dist/utils/fns/text.d.ts +9 -0
  244. package/dist/utils/fns/text.js +29 -0
  245. package/dist/utils/fns/text.js.map +1 -0
  246. package/dist/utils/index.d.ts +18 -0
  247. package/dist/utils/index.js +20 -0
  248. package/dist/utils/index.js.map +1 -0
  249. package/dist/utils/wrappers.d.ts +13 -0
  250. package/dist/utils/wrappers.js +68 -0
  251. package/dist/utils/wrappers.js.map +1 -0
  252. package/examples/basic/404.html +12 -0
  253. package/examples/basic/README.md +30 -0
  254. package/examples/basic/app.ts +18 -0
  255. package/examples/basic/controller.ts +45 -0
  256. package/examples/basic/index.ts +19 -0
  257. package/examples/basic/middleware.ts +13 -0
  258. package/examples/basic/service.ts +11 -0
  259. package/package.json +59 -38
  260. package/src/app/index.ts +1 -0
  261. package/src/app/jimpex.ts +743 -0
  262. package/src/controllers/common/config.ts +115 -0
  263. package/src/controllers/common/health.ts +128 -0
  264. package/src/controllers/common/index.ts +3 -0
  265. package/src/controllers/common/statics.ts +380 -0
  266. package/src/controllers/index.ts +2 -0
  267. package/src/controllers/utils/gateway.ts +1186 -0
  268. package/src/controllers/utils/index.ts +1 -0
  269. package/src/index.ts +6 -0
  270. package/src/middlewares/common/errorHandler.ts +203 -0
  271. package/src/middlewares/common/forceHTTPS.ts +83 -0
  272. package/src/middlewares/common/hsts.ts +135 -0
  273. package/src/middlewares/common/index.ts +3 -0
  274. package/src/middlewares/html/fastHTML.ts +255 -0
  275. package/src/middlewares/html/index.ts +2 -0
  276. package/src/middlewares/html/showHTML.ts +165 -0
  277. package/src/middlewares/index.ts +3 -0
  278. package/src/middlewares/utils/index.ts +1 -0
  279. package/src/middlewares/utils/versionValidator.ts +289 -0
  280. package/src/services/common/appError.ts +158 -0
  281. package/src/services/common/httpError.ts +74 -0
  282. package/src/services/common/index.ts +29 -0
  283. package/src/services/common/sendFile.ts +106 -0
  284. package/src/services/frontend/frontendFs.ts +101 -0
  285. package/src/services/frontend/index.ts +21 -0
  286. package/src/services/html/htmlGenerator.ts +356 -0
  287. package/src/services/html/index.ts +21 -0
  288. package/src/services/http/apiClient.ts +221 -0
  289. package/src/services/http/http.ts +286 -0
  290. package/src/services/http/index.ts +29 -0
  291. package/src/services/http/responsesBuilder.ts +265 -0
  292. package/src/services/index.ts +5 -0
  293. package/src/services/utils/ensureBearerToken.ts +202 -0
  294. package/src/services/utils/index.ts +21 -0
  295. package/src/types/events.ts +303 -0
  296. package/src/types/express.ts +21 -0
  297. package/src/types/http.ts +77 -0
  298. package/src/types/index.ts +6 -0
  299. package/src/types/options.ts +248 -0
  300. package/src/types/utils.ts +52 -0
  301. package/src/types/wootils.ts +4 -0
  302. package/src/utils/fns/index.ts +4 -0
  303. package/src/utils/fns/others.ts +15 -0
  304. package/src/utils/fns/routes.ts +64 -0
  305. package/src/utils/fns/statuses.ts +44 -0
  306. package/src/utils/fns/text.ts +8 -0
  307. package/src/utils/index.ts +2 -0
  308. package/src/utils/wrappers.ts +537 -0
  309. package/tsconfig.json +5 -8
  310. package/tsup.config.ts +10 -0
  311. package/src/app/index.js +0 -692
  312. package/src/constants/eventNames.js +0 -48
  313. package/src/constants/index.js +0 -7
  314. package/src/controllers/common/configuration.js +0 -116
  315. package/src/controllers/common/health.js +0 -79
  316. package/src/controllers/common/index.js +0 -7
  317. package/src/controllers/common/statics.js +0 -336
  318. package/src/controllers/index.js +0 -9
  319. package/src/controllers/utils/gateway.js +0 -1039
  320. package/src/controllers/utils/index.js +0 -3
  321. package/src/index.js +0 -30
  322. package/src/middlewares/common/errorHandler.js +0 -185
  323. package/src/middlewares/common/forceHTTPS.js +0 -80
  324. package/src/middlewares/common/hsts.js +0 -122
  325. package/src/middlewares/common/index.js +0 -7
  326. package/src/middlewares/html/fastHTML.js +0 -298
  327. package/src/middlewares/html/index.js +0 -5
  328. package/src/middlewares/html/showHTML.js +0 -167
  329. package/src/middlewares/index.js +0 -11
  330. package/src/middlewares/utils/index.js +0 -3
  331. package/src/middlewares/utils/versionValidator.js +0 -261
  332. package/src/services/common/appError.js +0 -136
  333. package/src/services/common/httpError.js +0 -60
  334. package/src/services/common/index.js +0 -25
  335. package/src/services/common/sendFile.js +0 -68
  336. package/src/services/frontend/frontendFs.js +0 -85
  337. package/src/services/frontend/index.js +0 -17
  338. package/src/services/html/htmlGenerator.js +0 -391
  339. package/src/services/html/index.js +0 -17
  340. package/src/services/http/apiClient.js +0 -148
  341. package/src/services/http/http.js +0 -256
  342. package/src/services/http/index.js +0 -25
  343. package/src/services/http/responsesBuilder.js +0 -193
  344. package/src/services/index.js +0 -15
  345. package/src/services/utils/ensureBearerToken.js +0 -147
  346. package/src/services/utils/index.js +0 -19
  347. package/src/types.js +0 -377
  348. package/src/utils/functions.js +0 -78
  349. package/src/utils/wrappers.js +0 -131
  350. package/types/app/index.d.ts +0 -417
  351. package/types/constants/eventNames.d.ts +0 -93
  352. package/types/constants/index.d.ts +0 -2
  353. package/types/controllers/common/configuration.d.ts +0 -71
  354. package/types/controllers/common/health.d.ts +0 -55
  355. package/types/controllers/common/index.d.ts +0 -4
  356. package/types/controllers/common/statics.d.ts +0 -271
  357. package/types/controllers/index.d.ts +0 -3
  358. package/types/controllers/utils/gateway.d.ts +0 -947
  359. package/types/controllers/utils/index.d.ts +0 -2
  360. package/types/index.d.ts +0 -15
  361. package/types/middlewares/common/errorHandler.d.ts +0 -143
  362. package/types/middlewares/common/forceHTTPS.d.ts +0 -64
  363. package/types/middlewares/common/hsts.d.ts +0 -111
  364. package/types/middlewares/common/index.d.ts +0 -4
  365. package/types/middlewares/html/fastHTML.d.ts +0 -238
  366. package/types/middlewares/html/index.d.ts +0 -3
  367. package/types/middlewares/html/showHTML.d.ts +0 -128
  368. package/types/middlewares/index.d.ts +0 -4
  369. package/types/middlewares/utils/index.d.ts +0 -2
  370. package/types/middlewares/utils/versionValidator.d.ts +0 -247
  371. package/types/services/common/appError.d.ts +0 -89
  372. package/types/services/common/httpError.d.ts +0 -37
  373. package/types/services/common/index.d.ts +0 -18
  374. package/types/services/common/sendFile.d.ts +0 -56
  375. package/types/services/frontend/frontendFs.d.ts +0 -72
  376. package/types/services/frontend/index.d.ts +0 -3
  377. package/types/services/html/htmlGenerator.d.ts +0 -298
  378. package/types/services/html/index.d.ts +0 -3
  379. package/types/services/http/apiClient.d.ts +0 -141
  380. package/types/services/http/http.d.ts +0 -159
  381. package/types/services/http/index.d.ts +0 -18
  382. package/types/services/http/responsesBuilder.d.ts +0 -140
  383. package/types/services/index.d.ts +0 -6
  384. package/types/services/utils/ensureBearerToken.d.ts +0 -137
  385. package/types/services/utils/index.d.ts +0 -16
  386. package/types/types.d.ts +0 -280
  387. package/types/utils/functions.d.ts +0 -55
  388. package/types/utils/wrappers.d.ts +0 -127
@@ -0,0 +1,1186 @@
1
+ import { deepAssignWithOverwrite } from '@homer0/deep-assign';
2
+ import { flat, unflat } from '@homer0/object-utils';
3
+ import type { APIClientOptions } from '@homer0/api-utils';
4
+ import {
5
+ controllerProviderCreator,
6
+ controller,
7
+ createRouteExpression,
8
+ removeSlashes,
9
+ notUndefined,
10
+ type MiddlewareLike,
11
+ } from '../../utils';
12
+ import type { HTTP, HTTPFetchOptions } from '../../services';
13
+ import type { Jimpex } from '../../app';
14
+ import {
15
+ RouterMethod,
16
+ DeepPartial,
17
+ Request,
18
+ Response,
19
+ NextFunction,
20
+ HTTPResponse,
21
+ Router,
22
+ ExpressMiddleware,
23
+ AsyncExpressMiddleware,
24
+ } from '../../types';
25
+ /**
26
+ * The extended definition for endpoints.
27
+ *
28
+ * @group Controllers/Gateway
29
+ */
30
+ export type GatewayConfigEndpointProps = {
31
+ /**
32
+ * The path to the endpoint relative to the entry point. It can include placeholders for
33
+ * parameters like `/:parameter/`.
34
+ */
35
+ path: string;
36
+ /**
37
+ * The router (HTTP) method for the endpoint.
38
+ *
39
+ * @default 'all'
40
+ */
41
+ method?: RouterMethod;
42
+ };
43
+ /**
44
+ * The definition of an endpoint: it can be just the path, relative to the entry point, or
45
+ * an object in which you can also specify things like the method.
46
+ *
47
+ * @group Controllers/Gateway
48
+ */
49
+ export type GatewayConfigEndpointDefinition = string | GatewayConfigEndpointProps;
50
+ /**
51
+ * The dictionary of endpoints the controller uses. The reason for this type is that this
52
+ * could be a flat dictionary, or a nested one.
53
+ *
54
+ * @example
55
+ *
56
+ * <caption>A flat dictionary</caption>
57
+ *
58
+ * {
59
+ * random: '/random',
60
+ * users: '/users',
61
+ * userById: {
62
+ * path: '/users/:id',
63
+ * method: 'get',
64
+ * },
65
+ * }
66
+ *
67
+ * @example
68
+ *
69
+ * <caption>A nested dictionary</caption>
70
+ *
71
+ * {
72
+ * random: '/random',
73
+ * users: {
74
+ * list: '/users',
75
+ * byId: {
76
+ * path: '/users/:id',
77
+ * method: 'get',
78
+ * },
79
+ * },
80
+ * }
81
+ *
82
+ * @group Controllers/Gateway
83
+ */
84
+ export type GatewayConfigEndpoints = {
85
+ [key: string]: GatewayConfigEndpointDefinition | GatewayConfigEndpoints;
86
+ };
87
+ /**
88
+ * The configuration for the gateway the controller uses.
89
+ *
90
+ * @group Controllers/Gateway
91
+ */
92
+ export type GatewayConfig = {
93
+ /**
94
+ * The entry point to the API the controller will make the requests to.
95
+ */
96
+ url: string;
97
+ /**
98
+ * The dictionary of enpoints the gateway will make available.
99
+ */
100
+ gateway: GatewayConfigEndpoints;
101
+ };
102
+ /**
103
+ * The options for how the gateway will handle the headers from the requests and the
104
+ * responses.
105
+ *
106
+ * @group Controllers/Gateway
107
+ */
108
+ export type GatewayControllerHeaderOptions = {
109
+ /**
110
+ * Whether or not to include the header with the request's IP address.
111
+ *
112
+ * @default true
113
+ */
114
+ useXForwardedFor: boolean;
115
+ /**
116
+ * Whether or not to copy all custom headers from the request. By custom header, it
117
+ * means all the headers which names start with `x-`.
118
+ *
119
+ * @default true
120
+ */
121
+ copyCustomHeaders: boolean;
122
+ /**
123
+ * A list of "known" headers the gateway will try to copy from the incoming request.
124
+ *
125
+ * @default ['authorization','content-type', 'referer', 'user-agent']
126
+ */
127
+ copy: string[];
128
+ /**
129
+ * A list of "known" headers the gateway will try to remove the response.
130
+ *
131
+ * @default ['server', 'x-powered-by']
132
+ */
133
+ remove: string[];
134
+ };
135
+ /**
136
+ * The extra options for the gateway controller. They are "extra" because they are mostly
137
+ * helpers for when used with an API client, or for optional features.
138
+ *
139
+ * @group Controllers/Gateway
140
+ */
141
+ export type GatewayControllerExtraOptions = {
142
+ /**
143
+ * This is really a helper for when the gateway is used with an API client. The idea is
144
+ * that, by default, the routes are mounted on the controller route, but with this
145
+ * option, you can specify another sub path. For example: The controller is mounted on
146
+ * `/routes`, and if you set `root` to `gateway`, all the routes will be on
147
+ * `/routes/gateway`.
148
+ *
149
+ * This become important (and useful) when you get the API client configuration (with
150
+ * `getAPIConfig`): The `url` will be the controller route, but all the endpoints will
151
+ * be modified and prefixed with the `root`, that way, you can have multiple gateways in
152
+ * the same "base route".
153
+ *
154
+ * It can also includes placeholders for parameters like `/:parameter/`, that will be
155
+ * replaced with the `placeholders` option when `getAPIConfig` gets called.
156
+ *
157
+ * @default ''
158
+ */
159
+ root: string;
160
+ /**
161
+ * This is another option for when the gateway is used with an API client. When calling
162
+ * `getAPIConfig`, all the endpoints will be wrapped inside an object named after this
163
+ * option. For example: `{ url: '...', endpoints: { api: { ... } } }`.
164
+ *
165
+ * @default 'api'
166
+ */
167
+ apiConfigSetting: string;
168
+ /**
169
+ * The options for how the gateway will handle the headers from the requests and the
170
+ * responses.
171
+ */
172
+ headers: GatewayControllerHeaderOptions;
173
+ };
174
+ /**
175
+ * The required options for the gateway controller.
176
+ *
177
+ * @group Controllers/Gateway
178
+ */
179
+ export type GatewayControllerOptions = {
180
+ /**
181
+ * The configuration for the API the gateway will make the requests to.
182
+ */
183
+ gatewayConfig: GatewayConfig;
184
+ /**
185
+ * The route where the controller is mounted.
186
+ */
187
+ route: string;
188
+ } & DeepPartial<GatewayControllerExtraOptions>;
189
+ /**
190
+ * The information for a request the controller will make.
191
+ *
192
+ * @group Controllers/Gateway
193
+ */
194
+ export type GatewayControllerRequest = {
195
+ /**
196
+ * The URL for the request.
197
+ */
198
+ url: string;
199
+ /**
200
+ * The options for the fetch client that will make the requests.
201
+ */
202
+ options: HTTPFetchOptions;
203
+ };
204
+ /**
205
+ * The information for an endpoint the gateway is calling.
206
+ *
207
+ * @group Controllers/Gateway
208
+ */
209
+ export type GatewayControllerEndpointInfo = {
210
+ /**
211
+ * The name of the endpoint in the configuration.
212
+ */
213
+ name: string;
214
+ /**
215
+ * The properties (path and method) of the endpoint.
216
+ */
217
+ definition: GatewayConfigEndpointDefinition;
218
+ };
219
+ /**
220
+ * These are the base options sent to all the helper service functions.
221
+ *
222
+ * @group Controllers/Gateway
223
+ */
224
+ export type GatewayHelperServiceBaseFnOptions = {
225
+ /**
226
+ * The information of the endpoint the gateway is calling.
227
+ */
228
+ endpoint: GatewayControllerEndpointInfo;
229
+ /**
230
+ * The request recived by the application.
231
+ */
232
+ req: Request;
233
+ /**
234
+ * The response object created by the application.
235
+ */
236
+ res: Response;
237
+ /**
238
+ * The function to call the next middleware in the chain.
239
+ */
240
+ next: NextFunction;
241
+ };
242
+ /**
243
+ * The information sent to the helper service in order to modify, or not, a request before
244
+ * it is sent.
245
+ *
246
+ * @group Controllers/Gateway
247
+ */
248
+ export type GatewayHelperServiceRequestReducerOptions =
249
+ GatewayHelperServiceBaseFnOptions & {
250
+ /**
251
+ * The options the controller created for the fetch client.
252
+ */
253
+ endpointReq: GatewayControllerRequest;
254
+ };
255
+ /**
256
+ * A function that can be used to modify the information of an endpoint before making a
257
+ * request.
258
+ *
259
+ * @param options The information of the request.
260
+ * @group Controllers/Gateway
261
+ */
262
+ export type GatewayHelperServiceRequestReducer = (
263
+ options: GatewayHelperServiceRequestReducerOptions,
264
+ ) => Promise<GatewayControllerRequest>;
265
+ /**
266
+ * The information sent to the helper service in order to modify a response before
267
+ * processing it, decide if it should be streamed or not, and even handle it.
268
+ *
269
+ * @group Controllers/Gateway
270
+ */
271
+ export type GatewayHelperServiceResponseReducerOptions =
272
+ GatewayHelperServiceBaseFnOptions & {
273
+ /**
274
+ * The response from the endpoint request.
275
+ */
276
+ endpointRes: HTTPResponse;
277
+ };
278
+ /**
279
+ * A function that can be used to modify the response of an endpoint before the controller
280
+ * processes it.
281
+ *
282
+ * @param options The information of the response.
283
+ * @group Controllers/Gateway
284
+ */
285
+ export type GatewayHelperServiceResponseReducer = (
286
+ options: GatewayHelperServiceResponseReducerOptions,
287
+ ) => Promise<HTTPResponse>;
288
+ /**
289
+ * A function that can be used to tell the controller to stream the response of an
290
+ * endpoint or not.
291
+ * If it returns `false`, the function to handle responses should be defined, otherwise,
292
+ * an error will be generated.
293
+ *
294
+ * @param options The information of the response.
295
+ * @group Controllers/Gateway
296
+ */
297
+ export type GatewayHelperServiceStreamVerification = (
298
+ options: GatewayHelperServiceResponseReducerOptions,
299
+ ) => Promise<boolean>;
300
+ /**
301
+ * A function to handle the response of an endpoint. This is called when the helper
302
+ * service tells the controller that the endpoint shouldn't be streamed, so this method
303
+ * should handle the response.
304
+ *
305
+ * @param options The information of the response.
306
+ * @group Controllers/Gateway
307
+ */
308
+ export type GatewayHelperServiceResponseHandler = (
309
+ options: GatewayHelperServiceResponseReducerOptions,
310
+ ) => Promise<void>;
311
+ /**
312
+ * The information sent to the helper service in order to handle a failed request for an
313
+ * endpoint.
314
+ *
315
+ * @group Controllers/Gateway
316
+ */
317
+ export type GatewayHelperServiceErrorHandlerOptions =
318
+ GatewayHelperServiceBaseFnOptions & {
319
+ /**
320
+ * The error generated during the request.
321
+ */
322
+ error: Error;
323
+ };
324
+ /**
325
+ * A function to handle the error of an endpoint request.
326
+ *
327
+ * @param options The information of the error.
328
+ * @group Controllers/Gateway
329
+ */
330
+ export type GatewayHelperServiceErrorHandler = (
331
+ options: GatewayHelperServiceErrorHandlerOptions,
332
+ ) => void;
333
+ /**
334
+ * The interface of a helper service that can intercept/modify the requests and responses
335
+ * the gateway makes.
336
+ *
337
+ * @group Controllers/Gateway
338
+ */
339
+ export type GatewayHelperService = Partial<{
340
+ /**
341
+ * A function that is called before an endpoint request is made.
342
+ */
343
+ reduceEndpointRequest: GatewayHelperServiceRequestReducer;
344
+ /**
345
+ * A function that is called with the response of an endpoint request.
346
+ */
347
+ reduceEndpointResponse: GatewayHelperServiceResponseReducer;
348
+ /**
349
+ * A function called in order to validate if an endpoint response should be streamed or
350
+ * not. If the function returns `false`, `handleEndpointResponse` will be called.
351
+ */
352
+ shouldStreamEndpointResponse: GatewayHelperServiceStreamVerification;
353
+ /**
354
+ * A function called when `shouldStreamEndpointResponse` returns `false`. The function
355
+ * should handle the response for the application.
356
+ */
357
+ handleEndpointResponse: GatewayHelperServiceResponseHandler;
358
+ /**
359
+ * A function called when an error is generated during an endpoint request/processing.
360
+ */
361
+ handleEndpointError: GatewayHelperServiceErrorHandler;
362
+ }>;
363
+ /**
364
+ * Utility type for the options object sent to the "proxy methods" the controller has for
365
+ * the helper service.
366
+ *
367
+ * @template T The type of the options for a specific helper service function.
368
+ * @access protected
369
+ * @group Controllers/Gateway
370
+ */
371
+ export type GatewayControllerHelperOptions<T> = T & {
372
+ /**
373
+ * The reference for the helper service.
374
+ */
375
+ helper: GatewayHelperService;
376
+ };
377
+ /**
378
+ * The information for a single HTTP method an endpoint can handle.
379
+ *
380
+ * @group Controllers/Gateway
381
+ */
382
+ export type GatewayControllerRouteMethod = {
383
+ /**
384
+ * The method for the route.
385
+ */
386
+ method: RouterMethod;
387
+ /**
388
+ * The information of the endpoint.
389
+ */
390
+ endpoint: GatewayControllerEndpointInfo;
391
+ };
392
+ /**
393
+ * The information for all the HTTP methods that can be handled for an endpoint.
394
+ *
395
+ * @group Controllers/Gateway
396
+ */
397
+ export type GatewayControllerRoute = {
398
+ /**
399
+ * The path to the endpoint, relative to the entry point.
400
+ */
401
+ path: string;
402
+ /**
403
+ * The path for the route in the controller. This is different from `path` as it's possible for
404
+ * the gateway to be implemented using the `root` option.
405
+ */
406
+ route: string;
407
+ /**
408
+ * A list with all the methods the controller uses on the route.
409
+ */
410
+ methods: GatewayControllerRouteMethod[];
411
+ };
412
+ /**
413
+ * The API client configuration the gateway can generate for its endpoints.
414
+ *
415
+ * @group Controllers/Gateway
416
+ */
417
+ export type GatewayControllerAPIConfig = {
418
+ /**
419
+ * The base URL for the API.
420
+ */
421
+ url: string;
422
+ /**
423
+ * The dictionary of endpoints the controller handles.
424
+ */
425
+ endpoints: APIClientOptions['endpoints'];
426
+ };
427
+ /**
428
+ * The options sent to {@link GatewayController.getAPIConfig}.
429
+ *
430
+ * @group Controllers/Gateway
431
+ */
432
+ export type GatewayControllerAPIConfigOptions = {
433
+ /**
434
+ * This can be used to overwrite the gateway's `apiConfigSetting` option, and set a new
435
+ * setting as a wrapper for the endpoints.
436
+ */
437
+ setting?: string;
438
+ /**
439
+ * A dictionary of values for possible placeholders that were sent using the `root`
440
+ * option.
441
+ */
442
+ placeholders?: Record<string, string>;
443
+ };
444
+ /**
445
+ * The options to construct a {@link GatewayController}.
446
+ *
447
+ * @group Controllers/Gateway
448
+ */
449
+ export type GatewayControllerConstructorOptions = GatewayControllerOptions & {
450
+ /**
451
+ * A dictionary with the dependencies to inject.
452
+ */
453
+ inject: {
454
+ http: HTTP;
455
+ /**
456
+ * A function to get a possible helper service. This is injected as a "getter" to not
457
+ * interrupt the DIC "lifecycle": controllers are initialized right when the app
458
+ * starts, and injecting a reference would force the service to be initialized too,
459
+ * even if a request is not being made.
460
+ */
461
+ getHelperService?: () => GatewayHelperService | undefined;
462
+ };
463
+ };
464
+ /**
465
+ * The options for {@link GatewayController._addRoute}.
466
+ *
467
+ * @access protected
468
+ * @group Controllers/Gateway
469
+ */
470
+ export type AddGatewayRouteOptions = {
471
+ /**
472
+ * The reference for the router in which the middlewares will be added.
473
+ */
474
+ router: Router;
475
+ /**
476
+ * The router method in which the middlewares will be added.
477
+ */
478
+ method: RouterMethod;
479
+ /**
480
+ * The route in which the middlewares will be added.
481
+ */
482
+ route: string;
483
+ /**
484
+ * The middleware created by {@link GatewayController}, that makes the request.
485
+ */
486
+ gatewayMiddleware: AsyncExpressMiddleware;
487
+ /**
488
+ * A list of extra middlewares to prepend to the gateway middleware.
489
+ */
490
+ middlewares: ExpressMiddleware[];
491
+ };
492
+ /**
493
+ * A utility controller that generates routes that act as a gateway for a specific API.
494
+ *
495
+ * @group Controller Classes
496
+ * @group Controllers/Gateway
497
+ * @prettierignore
498
+ */
499
+ export class GatewayController {
500
+ /**
501
+ * The service that makes HTTP requests.
502
+ */
503
+ protected readonly http: HTTP;
504
+ /**
505
+ * A function to get a possible helper service. This is injected as a "getter" to not
506
+ * interrupt the DIC "lifecycle": controllers are initialized right when the app
507
+ * starts, and injecting a reference would force the service to be initialized too,
508
+ * even if a request is not being made.
509
+ */
510
+ protected readonly _getHelperService: () => GatewayHelperService | undefined;
511
+ /**
512
+ * The information, url and endpoints, for the gateway the controller will make requests to.
513
+ */
514
+ protected readonly _gatewayConfig: GatewayConfig;
515
+ /**
516
+ * The route in which the controller is mounted.
517
+ */
518
+ protected readonly _route: string;
519
+ /**
520
+ * A regular expression that will be used to remove the controller route from a
521
+ * request path. This will allow the main middleware to extract the path to where the
522
+ * request should be made.
523
+ */
524
+ protected readonly _routeExpression: RegExp;
525
+ /**
526
+ * The controller customization options.
527
+ */
528
+ protected readonly _options: GatewayControllerExtraOptions;
529
+ /**
530
+ * A flat dictionary with the endpoints information.
531
+ */
532
+ protected readonly _endpoints: Record<string, GatewayConfigEndpointDefinition>;
533
+ /**
534
+ * The entry URL for the API client configuration the controller can generate.
535
+ */
536
+ protected readonly _apiConfigUrl: string;
537
+ /**
538
+ * The generated endpoints for the API client configuration the controller can generate.
539
+ */
540
+ protected readonly _apiConfigEndpoints: APIClientOptions['endpoints'];
541
+ /**
542
+ * The list of routes the controller can handle.
543
+ */
544
+ protected readonly _routes: GatewayControllerRoute[];
545
+ /**
546
+ * @param options The options to construct the controller.
547
+ */
548
+ constructor({
549
+ inject,
550
+ route,
551
+ gatewayConfig,
552
+ ...options
553
+ }: GatewayControllerConstructorOptions) {
554
+ this.http = inject.http;
555
+ this._getHelperService = inject.getHelperService || (() => undefined);
556
+ this._route = removeSlashes(route);
557
+ this._options = this._formatOptions(
558
+ deepAssignWithOverwrite(
559
+ {
560
+ root: '',
561
+ apiConfigSetting: 'api',
562
+ headers: {
563
+ useXForwardedFor: true,
564
+ copyCustomHeaders: true,
565
+ copy: ['authorization', 'content-type', 'referer', 'user-agent'],
566
+ remove: ['server', 'x-powered-by', 'content-encoding'],
567
+ },
568
+ },
569
+ options,
570
+ ),
571
+ );
572
+ this._gatewayConfig = {
573
+ ...gatewayConfig,
574
+ url: removeSlashes(gatewayConfig.url, false, true),
575
+ };
576
+ this._routeExpression = createRouteExpression(
577
+ this._options.root ? `${this._route}/${this._options.root}` : this._route,
578
+ true,
579
+ true,
580
+ );
581
+ this._endpoints = this._formatEndpoints();
582
+ this._routes = this._createRoutes();
583
+ const { url, endpoints } = this._createAPIConfig();
584
+ this._apiConfigUrl = url;
585
+ this._apiConfigEndpoints = endpoints;
586
+ }
587
+ /**
588
+ * Generates an API client configuration based on the controller routes.
589
+ *
590
+ * @param options The options to customize the generated configuration.
591
+ */
592
+ getAPIConfig({
593
+ setting,
594
+ placeholders = {},
595
+ }: GatewayControllerAPIConfigOptions = {}): Readonly<GatewayControllerAPIConfig> {
596
+ const useSetting = setting || this._options.apiConfigSetting;
597
+ let url: string;
598
+ const placeholdersEntries = Object.entries(placeholders);
599
+ if (placeholdersEntries.length) {
600
+ url = placeholdersEntries.reduce<string>(
601
+ (acc, [key, value]) => acc.replace(`:${key}`, value),
602
+ this._apiConfigUrl,
603
+ );
604
+ } else {
605
+ url = this._apiConfigUrl;
606
+ }
607
+
608
+ return {
609
+ url,
610
+ endpoints: {
611
+ [useSetting]: this._apiConfigEndpoints,
612
+ },
613
+ };
614
+ }
615
+ /**
616
+ * Mounts the middlewares in the router in order to make the requests.
617
+ *
618
+ * @param router A reference to the application router.
619
+ * @param middlewares A list of extra middlewares to execute before the gateway
620
+ * middleware.
621
+ */
622
+ addRoutes(router: Router, middlewares: ExpressMiddleware[] = []): Router {
623
+ this._routes.forEach((route) => {
624
+ route.methods.forEach((info) => {
625
+ this._addRoute({
626
+ router,
627
+ method: info.method,
628
+ route: route.route,
629
+ gatewayMiddleware: this._createGatewayMiddleware(info.endpoint),
630
+ middlewares,
631
+ });
632
+ });
633
+ });
634
+
635
+ return router;
636
+ }
637
+ /**
638
+ * The customization options.
639
+ */
640
+ get options(): Readonly<GatewayControllerExtraOptions> {
641
+ return { ...this._options };
642
+ }
643
+ /**
644
+ * The configuration for the gateway the controller will make requests to.
645
+ */
646
+ get gatewayConfig(): Readonly<GatewayConfig> {
647
+ return { ...this._gatewayConfig };
648
+ }
649
+ /**
650
+ * Generates a middleware that will make the request to an endpoint and stream the
651
+ * response.
652
+ *
653
+ * @param endpoint The information of the endpoint the middleware will handle.
654
+ */
655
+ protected _createGatewayMiddleware(
656
+ endpoint: GatewayControllerEndpointInfo,
657
+ ): AsyncExpressMiddleware {
658
+ return async (req, res, next) => {
659
+ const {
660
+ _options: { headers: headersOptions },
661
+ _gatewayConfig: { url: gatewayUrl },
662
+ _routeExpression: routeExpression,
663
+ } = this;
664
+ // Remove the controller route from the requested URL.
665
+ const reqPath = req.originalUrl.replace(routeExpression, '');
666
+ // Process the headers for the request.
667
+ let headers: Record<string, string> = {};
668
+ // - Copy the headers from the incoming request.
669
+ headersOptions.copy.forEach((name) => {
670
+ if (req.headers[name]) {
671
+ headers[name] = req.headers[name] as string;
672
+ }
673
+ });
674
+ // - Copy the custom headers.
675
+ if (headersOptions.copyCustomHeaders) {
676
+ headers = deepAssignWithOverwrite<Record<string, string>>(
677
+ headers,
678
+ this.http.getCustomHeadersFromRequest(req),
679
+ );
680
+ }
681
+ // - Include the IP on the X-Forwarded-For header, if enabled.
682
+ if (headersOptions.useXForwardedFor) {
683
+ const ip = this.http.getIPFromRequest(req);
684
+ if (ip) {
685
+ headers['x-forwarded-for'] = ip;
686
+ }
687
+ }
688
+
689
+ const method = req.method.toUpperCase();
690
+ // If the request has a body and the method is not `GET`, stringify it.
691
+ let body: string | undefined;
692
+ if (method !== 'GET' && typeof req.body === 'object') {
693
+ body = JSON.stringify(req.body);
694
+ // If there's no `content-type`, let's assume it's JSON.
695
+ if (!headers['content-type']) {
696
+ headers['content-type'] = 'application/json';
697
+ }
698
+ }
699
+
700
+ /**
701
+ * Get the helper service, if there's one, and define the base options for its
702
+ * methods.
703
+ */
704
+ const helper = this._getHelperService() || {};
705
+ const helperBasePayload = {
706
+ endpoint,
707
+ req,
708
+ res,
709
+ next,
710
+ helper,
711
+ };
712
+
713
+ try {
714
+ // Reduce the request information before using it.
715
+ const request = await this._reduceEndpointRequest({
716
+ endpointReq: {
717
+ url: `${gatewayUrl}/${reqPath}`,
718
+ options: {
719
+ method,
720
+ headers,
721
+ body,
722
+ },
723
+ },
724
+ ...helperBasePayload,
725
+ });
726
+ // Make the actual request.
727
+ const responseRaw = await this.http.fetch(request.url, request.options);
728
+ // Reduce the response information before using it.
729
+ const response = await this._reduceEndpointResponse({
730
+ endpointRes: responseRaw,
731
+ ...helperBasePayload,
732
+ });
733
+ // Validate if the response should be streamed.
734
+ const shouldStream = await this._shouldStreamEndpointResponse({
735
+ endpointRes: responseRaw,
736
+ ...helperBasePayload,
737
+ });
738
+
739
+ if (shouldStream) {
740
+ /**
741
+ * If the response should be streamed, set the status, remove unwanted headers,
742
+ * and pipe it to the application response object.
743
+ */
744
+ res.status(response.status);
745
+ response.headers.forEach((value, name) => {
746
+ if (!headersOptions.remove.includes(name)) {
747
+ res.setHeader(name, value);
748
+ }
749
+ });
750
+
751
+ response.body.pipe(res).on('error', (error) => {
752
+ next(error);
753
+ });
754
+ } else {
755
+ /**
756
+ * If the response should not be streamed, send it to the helper method to
757
+ * handle it.
758
+ */
759
+ await this._handleEndpointResponse({
760
+ endpointRes: response,
761
+ ...helperBasePayload,
762
+ });
763
+ }
764
+ } catch (error) {
765
+ // Something failed, so let's pass the error to the helper service.
766
+ this._handleEndpointError({
767
+ error: error as Error,
768
+ ...helperBasePayload,
769
+ });
770
+ }
771
+ };
772
+ }
773
+ /**
774
+ * Mounts the middleware(s) for an endpoint in the router.
775
+ *
776
+ * @param options The information of the endpoint and how it needs to be added.
777
+ */
778
+ protected _addRoute({
779
+ router,
780
+ method,
781
+ route,
782
+ gatewayMiddleware,
783
+ middlewares,
784
+ }: AddGatewayRouteOptions): void {
785
+ router[method](route, [...middlewares, gatewayMiddleware]);
786
+ }
787
+ /**
788
+ * Formats the endpoints for the gateway into a flat dictionary without nesting.
789
+ */
790
+ protected _formatEndpoints(): Record<string, GatewayConfigEndpointDefinition> {
791
+ return flat({
792
+ target: this._gatewayConfig.gateway,
793
+ shouldFlatten: (_, value) => {
794
+ const useValue = value as { path?: string };
795
+ return typeof useValue.path === 'undefined';
796
+ },
797
+ });
798
+ }
799
+ /**
800
+ * Based on the information from the endpoints, this method will create the routes the
801
+ * controller will later add on a router.
802
+ *
803
+ * @throws If there's more than one endpoint using the same path with the same HTTP
804
+ * method.
805
+ */
806
+ protected _createRoutes(): GatewayControllerRoute[] {
807
+ const routes: Record<
808
+ string,
809
+ {
810
+ path: string;
811
+ methods: Partial<Record<RouterMethod, string>>;
812
+ }
813
+ > = {};
814
+ Object.keys(this._endpoints).forEach((name) => {
815
+ const endpoint = this._endpoints[name]!;
816
+ let endpointPath: string;
817
+ let endpointMethod: RouterMethod;
818
+ if (typeof endpoint === 'string') {
819
+ endpointPath = endpoint;
820
+ endpointMethod = 'all';
821
+ } else {
822
+ endpointPath = endpoint.path;
823
+ endpointMethod = endpoint.method
824
+ ? this._validateHTTPMethod(endpoint.method)
825
+ : 'all';
826
+ }
827
+
828
+ endpointPath = removeSlashes(endpointPath);
829
+ if (!routes[endpointPath]) {
830
+ routes[endpointPath] = {
831
+ path: endpointPath,
832
+ methods: {},
833
+ };
834
+ }
835
+
836
+ if (routes[endpointPath]!.methods[endpointMethod]) {
837
+ const repeatedEndpoint = routes[endpointPath]!.methods[endpointMethod];
838
+ throw new Error(
839
+ "You can't have two gateway endpoints to the same path and with the same " +
840
+ `HTTP method: '${repeatedEndpoint}' and '${name}'`,
841
+ );
842
+ }
843
+
844
+ routes[endpointPath]!.methods[endpointMethod] = name;
845
+ });
846
+
847
+ const routePrefixes = this._options.root ? `/${this._options.root}/` : '/';
848
+ return Object.keys(routes).map((endpointPath) => {
849
+ const info = routes[endpointPath]!;
850
+ return {
851
+ path: info.path,
852
+ route: `${routePrefixes}${info.path}`,
853
+ methods: Object.keys(info.methods).map((methodNameRaw) => {
854
+ const methodName = methodNameRaw as RouterMethod;
855
+ const endpointName = info.methods[methodName]!;
856
+ const endpointDefinition = this._endpoints[endpointName]!;
857
+ return {
858
+ method: methodName,
859
+ endpoint: {
860
+ name: endpointName,
861
+ definition: endpointDefinition,
862
+ },
863
+ };
864
+ }),
865
+ };
866
+ });
867
+ }
868
+ /**
869
+ * This is a "proxy method" to call the helper service's function that can modify an
870
+ * endpoint request before it gets made.
871
+ *
872
+ * The reason this is a "proxy method" is in case the controller gets subclassed and
873
+ * "used itself as a helper" instead of relying on a difference one.
874
+ *
875
+ * If the helper doesn't implement `reduceEndpointRequest`, it will just return
876
+ * information for the request.
877
+ *
878
+ * @param options The information of the request and the reference to the helper.
879
+ */
880
+ protected _reduceEndpointRequest({
881
+ helper,
882
+ ...options
883
+ }: GatewayControllerHelperOptions<GatewayHelperServiceRequestReducerOptions>): Promise<GatewayControllerRequest> {
884
+ if (helper.reduceEndpointRequest) {
885
+ return helper.reduceEndpointRequest(options);
886
+ }
887
+
888
+ return Promise.resolve(options.endpointReq);
889
+ }
890
+ /**
891
+ * This is a "proxy method" to call the helper service's function that can modify an
892
+ * endpoint response before it gets processed.
893
+ *
894
+ * The reason this is a "proxy method" is in case the controller gets subclassed and
895
+ * "used itself as a helper" instead of relying on a difference one.
896
+ *
897
+ * If the helper doesn't implement `reduceEndpointResponse`, it will just return
898
+ * information for the response.
899
+ *
900
+ * @param options The information of the response and the reference to the helper.
901
+ */
902
+ protected _reduceEndpointResponse({
903
+ helper,
904
+ ...options
905
+ }: GatewayControllerHelperOptions<GatewayHelperServiceResponseReducerOptions>): Promise<HTTPResponse> {
906
+ if (helper.reduceEndpointResponse) {
907
+ return helper.reduceEndpointResponse(options);
908
+ }
909
+
910
+ return Promise.resolve(options.endpointRes);
911
+ }
912
+ /**
913
+ * This is a "proxy method" to call the helper service's function that can decide if an
914
+ * endpoint response should be streamed or not.
915
+ *
916
+ * The reason this is a "proxy method" is in case the controller gets subclassed and
917
+ * "used itself as a helper" instead of relying on a difference one.
918
+ *
919
+ * If the helper doesn't implement `shouldStreamEndpointResponse`, it will just return
920
+ * `true`.
921
+ *
922
+ * @param options The information of the response and the reference to the helper.
923
+ */
924
+ protected _shouldStreamEndpointResponse({
925
+ helper,
926
+ ...options
927
+ }: GatewayControllerHelperOptions<GatewayHelperServiceResponseReducerOptions>): Promise<boolean> {
928
+ if (helper.shouldStreamEndpointResponse) {
929
+ return helper.shouldStreamEndpointResponse(options);
930
+ }
931
+
932
+ return Promise.resolve(true);
933
+ }
934
+ /**
935
+ * This is a "proxy method" to call the helper service's function that handles a
936
+ * response in case it already said that a response shouldn't be streamed.
937
+ *
938
+ * The reason this is a "proxy method" is in case the controller gets subclassed and
939
+ * "used itself as a helper" instead of relying on a difference one.
940
+ *
941
+ * If the helper doesn't implement `shouldStreamEndpointResponse`, it will throw an
942
+ * error.
943
+ *
944
+ * @param options The information of the response and the reference to the helper.
945
+ * @throws If the helper doesn't implement `handleEndpointResponse`.
946
+ */
947
+ protected _handleEndpointResponse({
948
+ helper,
949
+ ...options
950
+ }: GatewayControllerHelperOptions<GatewayHelperServiceResponseReducerOptions>): Promise<void> {
951
+ if (!helper.handleEndpointResponse) {
952
+ throw new Error('You must implement handleEndpointResponse');
953
+ }
954
+
955
+ return helper.handleEndpointResponse(options);
956
+ }
957
+ /**
958
+ * This is a "proxy method" to call the helper service's function that handles an error
959
+ * on an endpoint request.
960
+ *
961
+ * The reason this is a "proxy method" is in case the controller gets subclassed and
962
+ * "used itself as a helper" instead of relying on a difference one.
963
+ *
964
+ * If the helper doesn't implement `handleEndpointError`, it will just send the error to
965
+ * the next middleware/error handler.
966
+ *
967
+ * @param options The information of the response and the reference to the helper.
968
+ */
969
+ protected _handleEndpointError({
970
+ helper,
971
+ ...options
972
+ }: GatewayControllerHelperOptions<GatewayHelperServiceErrorHandlerOptions>): void {
973
+ if (helper.handleEndpointError) {
974
+ return helper.handleEndpointError(options);
975
+ }
976
+
977
+ return options.next(options.error);
978
+ }
979
+ /**
980
+ * Validates and formats the customization options sent to the controller.
981
+ *
982
+ * @param options The options sent to the constructor.
983
+ */
984
+ protected _formatOptions(
985
+ options: GatewayControllerExtraOptions,
986
+ ): GatewayControllerExtraOptions {
987
+ if (options.root) {
988
+ const root = removeSlashes(options.root).trim();
989
+ return { ...options, root };
990
+ }
991
+
992
+ return options;
993
+ }
994
+ /**
995
+ * Validates a router/HTTP method that the controller intends to use for an endpoint. If
996
+ * it's not valid, it will return `all`.
997
+ *
998
+ * @param method The HTTP method for the endpoint.
999
+ */
1000
+ protected _validateHTTPMethod(method: string): RouterMethod {
1001
+ const newMethod = method.toLowerCase();
1002
+ return [
1003
+ 'get',
1004
+ 'head',
1005
+ 'post',
1006
+ 'patch',
1007
+ 'put',
1008
+ 'delete',
1009
+ 'connect',
1010
+ 'options',
1011
+ 'trace',
1012
+ ].includes(newMethod)
1013
+ ? (newMethod as RouterMethod)
1014
+ : 'all';
1015
+ }
1016
+ /**
1017
+ * Creates the API client configuration based on the controller routes.
1018
+ */
1019
+ protected _createAPIConfig(): GatewayControllerAPIConfig {
1020
+ let endpoints: APIClientOptions['endpoints'];
1021
+ const { root } = this._options;
1022
+ if (root) {
1023
+ endpoints = Object.keys(this._endpoints).reduce<
1024
+ Record<string, GatewayConfigEndpointDefinition>
1025
+ >((acc, name) => {
1026
+ const endpoint = this._endpoints[name]!;
1027
+ let newEndpoint;
1028
+ if (typeof endpoint === 'string') {
1029
+ newEndpoint = removeSlashes(endpoint);
1030
+ newEndpoint = `${root}/${newEndpoint}`;
1031
+ } else {
1032
+ const endpointPath = removeSlashes(endpoint.path);
1033
+ newEndpoint = {
1034
+ ...endpoint,
1035
+ path: `${root}/${endpointPath}`,
1036
+ };
1037
+ }
1038
+
1039
+ acc[name] = newEndpoint;
1040
+ return acc;
1041
+ }, {});
1042
+ } else {
1043
+ endpoints = this._endpoints;
1044
+ }
1045
+
1046
+ return {
1047
+ url: `/${this._route}`,
1048
+ endpoints: unflat({ target: endpoints }),
1049
+ };
1050
+ }
1051
+ }
1052
+ /**
1053
+ * A function to generate a list of middlewares that can be executed before the tontroller
1054
+ * main middleware.
1055
+ *
1056
+ * @group Controllers/Gateway
1057
+ */
1058
+ export type GatewayControllerGetMiddlewaresFn = (app: Jimpex) => MiddlewareLike[];
1059
+ /**
1060
+ * The options for the controller creator that mounts the {@link GatewayController}.
1061
+ *
1062
+ * @group Controllers/Gateway
1063
+ */
1064
+ export type GatewayControllerCreatorOptions =
1065
+ DeepPartial<GatewayControllerExtraOptions> & {
1066
+ /**
1067
+ * The name the creator will use to register the controller in the container. No,
1068
+ * this is not a typo. The creator will register the controller so other services can
1069
+ * access the `getAPIConfig` method. The service will be available after the app
1070
+ * routes are mounted.
1071
+ * If this is overwritten, the creator will ensure that the name ends with `Gateway`;
1072
+ * and if overwritten, but it doesn't include `Gateway` at the end, and no
1073
+ * `gatewaySettingName` was defined, the creator will use the custom name (without
1074
+ * `Gatway`) for `gatewaySettingName`.
1075
+ *
1076
+ * @default 'apiGateway'
1077
+ */
1078
+ serviceName?: string;
1079
+ /**
1080
+ * The name of the helper service the creator will try to obtain from the container.
1081
+ * If `serviceName` is overwritten, the default for this will be
1082
+ * `${serviceName}Helper`.
1083
+ *
1084
+ * @default 'apiGatewayHelper'
1085
+ */
1086
+ helperServiceName?: string;
1087
+ /**
1088
+ * The name of the configuration setting where the gateway configuration is stored. If
1089
+ * not overwritten, check the description of `serviceName` to understand which will be
1090
+ * its default value.
1091
+ *
1092
+ * @default 'api'
1093
+ */
1094
+ gatewaySettingName?: string;
1095
+ /**
1096
+ * The class the creator will instantiate. Similar to the API Client, this allows for
1097
+ * extra customization as you can send a custom subclass of the
1098
+ * {@link GatewayController}.
1099
+ *
1100
+ * @default GatewayController
1101
+ */
1102
+ gatewayClass?: typeof GatewayController;
1103
+ /**
1104
+ * A function to generate a list of middlewares that can be executed before the
1105
+ * controller main middleware.
1106
+ */
1107
+ getMiddlewares?: GatewayControllerGetMiddlewaresFn;
1108
+ };
1109
+ /**
1110
+ * Creates a controller that allows the application to mount routes that will work like
1111
+ * gateway to a specific API.
1112
+ *
1113
+ * @group Controllers
1114
+ * @group Controllers/Gateway
1115
+ */
1116
+ export const gatewayController = controllerProviderCreator(
1117
+ (options: GatewayControllerCreatorOptions = {}) =>
1118
+ (app, route) => {
1119
+ /**
1120
+ * Formats the name in order to keep consistency with the helper service and the
1121
+ * configuration setting: If the `serviceName` is different from the default, make
1122
+ * sure it ends with `Gateway`, set the default helper service name to
1123
+ * `${serviceName}Helper`, and the default configuration setting to the same as the
1124
+ * service name (without the `Gateway`).
1125
+ * This way, if you just use `myApi`, the service name will be `myApiGateway`, the
1126
+ * helper name will be `myApiGatewayHelper` and the configuration setting `myApi`.
1127
+ */
1128
+ const defaultServiceName = 'apiGateway';
1129
+ let defaultHelperServiceName = 'apiGatewayHelper';
1130
+ let defaultConfigSetting = 'api';
1131
+ let { serviceName = defaultServiceName } = options;
1132
+ if (serviceName !== defaultServiceName) {
1133
+ defaultConfigSetting = serviceName;
1134
+ if (!serviceName.match(/gateway$/i)) {
1135
+ serviceName = `${serviceName}Gateway`;
1136
+ }
1137
+ defaultHelperServiceName = `${serviceName}Helper`;
1138
+ }
1139
+ // Register the service.
1140
+ app.set(serviceName, () => {
1141
+ const {
1142
+ helperServiceName = defaultHelperServiceName,
1143
+ gatewaySettingName = defaultConfigSetting,
1144
+ gatewayClass: GatewayClass = GatewayController,
1145
+ } = options;
1146
+
1147
+ const gtConfig = app.getConfig<GatewayConfig>(gatewaySettingName);
1148
+
1149
+ return new GatewayClass({
1150
+ ...options,
1151
+ apiConfigSetting: gatewaySettingName,
1152
+ gatewayConfig: gtConfig,
1153
+ route,
1154
+ inject: {
1155
+ http: app.get('http'),
1156
+ getHelperService: () => app.try(helperServiceName),
1157
+ },
1158
+ });
1159
+ });
1160
+
1161
+ return controller(() => {
1162
+ // Get the controller.
1163
+ const ctrl = app.get<GatewayController>(serviceName);
1164
+ /**
1165
+ * Check if there are actual middlewares to be included, and in case there are
1166
+ * Jimpex middlewares, connect them.
1167
+ */
1168
+ let useMiddlewares: ExpressMiddleware[] | undefined;
1169
+ if (options.getMiddlewares) {
1170
+ useMiddlewares = options
1171
+ .getMiddlewares(app)
1172
+ .map((middleware) => {
1173
+ if ('middleware' in middleware) {
1174
+ return middleware.connect(app) as ExpressMiddleware | undefined;
1175
+ }
1176
+
1177
+ return middleware as ExpressMiddleware;
1178
+ })
1179
+ .filter(notUndefined);
1180
+ }
1181
+
1182
+ // Add the routes to the router and return it.
1183
+ return ctrl.addRoutes(app.get('router'), useMiddlewares);
1184
+ });
1185
+ },
1186
+ );