@expressots/adapter-express 3.0.0 → 4.0.0-preview.3

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 (244) hide show
  1. package/LICENSE.md +21 -21
  2. package/README.md +61 -118
  3. package/lib/CHANGELOG.md +36 -5
  4. package/lib/README.md +61 -118
  5. package/lib/cjs/adapter-express/application-express.base.js +3 -1
  6. package/lib/cjs/adapter-express/application-express.js +1405 -85
  7. package/lib/cjs/adapter-express/express-utils/conditional-middleware.js +102 -0
  8. package/lib/cjs/adapter-express/express-utils/constants.js +17 -0
  9. package/lib/cjs/adapter-express/express-utils/content-negotiation-decorators.js +129 -0
  10. package/lib/cjs/adapter-express/express-utils/decorators.js +225 -59
  11. package/lib/cjs/adapter-express/express-utils/exception-filter-decorators.js +11 -0
  12. package/lib/cjs/adapter-express/express-utils/guard-context-factory.js +84 -0
  13. package/lib/cjs/adapter-express/express-utils/guard-middleware.js +115 -0
  14. package/lib/cjs/adapter-express/express-utils/guard-utils.js +18 -0
  15. package/lib/cjs/adapter-express/express-utils/http-context-store.js +15 -0
  16. package/lib/cjs/adapter-express/express-utils/http-status-middleware.js +37 -2
  17. package/lib/cjs/adapter-express/express-utils/index.js +67 -1
  18. package/lib/cjs/adapter-express/express-utils/interceptor-middleware.js +132 -0
  19. package/lib/cjs/adapter-express/express-utils/inversify-express-server.js +827 -64
  20. package/lib/cjs/adapter-express/express-utils/lazy-module-middleware.js +241 -0
  21. package/lib/cjs/adapter-express/express-utils/middleware-composition.js +95 -0
  22. package/lib/cjs/adapter-express/express-utils/path-pattern-compat.js +129 -0
  23. package/lib/cjs/adapter-express/express-utils/permission-preloader.middleware.js +48 -0
  24. package/lib/cjs/adapter-express/express-utils/route-constraints.js +104 -0
  25. package/lib/cjs/adapter-express/express-utils/scope-extractor.interface.js +2 -0
  26. package/lib/cjs/adapter-express/express-utils/scope-extractor.js +66 -0
  27. package/lib/cjs/adapter-express/express-utils/setup-authorization.js +71 -0
  28. package/lib/cjs/adapter-express/express-utils/setup-event-system.js +113 -0
  29. package/lib/cjs/adapter-express/express-utils/setup-interceptors.js +103 -0
  30. package/lib/cjs/adapter-express/express-utils/setup-lazy-loading.js +228 -0
  31. package/lib/cjs/adapter-express/express-utils/utils.js +30 -12
  32. package/lib/cjs/adapter-express/express-utils/validation-decorators.js +205 -0
  33. package/lib/cjs/adapter-express/express-utils/validation-service.js +252 -0
  34. package/lib/cjs/adapter-express/index.js +7 -5
  35. package/lib/cjs/adapter-express/micro-api/application-express-micro-route.js +31 -1
  36. package/lib/cjs/adapter-express/micro-api/application-express-micro.js +8 -38
  37. package/lib/cjs/adapter-express/micro-api/gateway/circuit-breaker.js +174 -0
  38. package/lib/cjs/adapter-express/micro-api/gateway/index.js +11 -0
  39. package/lib/cjs/adapter-express/micro-api/gateway/service-proxy.js +214 -0
  40. package/lib/cjs/adapter-express/micro-api/index.js +27 -3
  41. package/lib/cjs/adapter-express/micro-api/micro.js +272 -0
  42. package/lib/cjs/adapter-express/micro-api/queue/index.js +8 -0
  43. package/lib/cjs/adapter-express/micro-api/queue/queue.interface.js +2 -0
  44. package/lib/cjs/adapter-express/micro-api/queue/rabbitmq-consumer.js +255 -0
  45. package/lib/cjs/adapter-express/micro-api/serverless/aws-lambda.adapter.js +183 -0
  46. package/lib/cjs/adapter-express/micro-api/serverless/cloudflare.adapter.js +158 -0
  47. package/lib/cjs/adapter-express/micro-api/serverless/index.js +12 -0
  48. package/lib/cjs/adapter-express/micro-api/serverless/vercel.adapter.js +102 -0
  49. package/lib/cjs/adapter-express/micro-api/service-mesh/index.js +10 -0
  50. package/lib/cjs/adapter-express/micro-api/service-mesh/service-client.js +194 -0
  51. package/lib/cjs/adapter-express/micro-api/service-mesh/service-discovery.js +261 -0
  52. package/lib/cjs/adapter-express/middleware/index.js +21 -0
  53. package/lib/cjs/adapter-express/middleware/request-logging.middleware.js +244 -0
  54. package/lib/cjs/adapter-express/render/engine.js +15 -15
  55. package/lib/cjs/adapter-express/render/index.js +5 -0
  56. package/lib/cjs/adapter-express/studio/index.js +10 -0
  57. package/lib/cjs/adapter-express/studio/studio-integration.js +267 -0
  58. package/lib/cjs/index.js +1 -1
  59. package/lib/cjs/types/adapter-express/application-express.base.d.ts +20 -7
  60. package/lib/cjs/types/adapter-express/application-express.d.ts +316 -33
  61. package/lib/cjs/types/adapter-express/express-utils/base-middleware.d.ts +2 -2
  62. package/lib/cjs/types/adapter-express/express-utils/conditional-middleware.d.ts +97 -0
  63. package/lib/cjs/types/adapter-express/express-utils/constants.d.ts +13 -0
  64. package/lib/cjs/types/adapter-express/express-utils/content-negotiation-decorators.d.ts +94 -0
  65. package/lib/cjs/types/adapter-express/express-utils/decorators.d.ts +54 -6
  66. package/lib/cjs/types/adapter-express/express-utils/exception-filter-decorators.d.ts +6 -0
  67. package/lib/cjs/types/adapter-express/express-utils/guard-context-factory.d.ts +17 -0
  68. package/lib/cjs/types/adapter-express/express-utils/guard-middleware.d.ts +22 -0
  69. package/lib/cjs/types/adapter-express/express-utils/guard-utils.d.ts +11 -0
  70. package/lib/cjs/types/adapter-express/express-utils/http-context-store.d.ts +20 -0
  71. package/lib/cjs/types/adapter-express/express-utils/httpResponseMessage.d.ts +1 -1
  72. package/lib/cjs/types/adapter-express/express-utils/index.d.ts +30 -2
  73. package/lib/cjs/types/adapter-express/express-utils/interceptor-middleware.d.ts +40 -0
  74. package/lib/cjs/types/adapter-express/express-utils/interfaces.d.ts +42 -5
  75. package/lib/cjs/types/adapter-express/express-utils/inversify-express-server.d.ts +114 -2
  76. package/lib/cjs/types/adapter-express/express-utils/lazy-module-middleware.d.ts +122 -0
  77. package/lib/cjs/types/adapter-express/express-utils/middleware-composition.d.ts +85 -0
  78. package/lib/cjs/types/adapter-express/express-utils/path-pattern-compat.d.ts +66 -0
  79. package/lib/cjs/types/adapter-express/express-utils/permission-preloader.middleware.d.ts +10 -0
  80. package/lib/cjs/types/adapter-express/express-utils/route-constraints.d.ts +98 -0
  81. package/lib/cjs/types/adapter-express/express-utils/scope-extractor.d.ts +21 -0
  82. package/lib/cjs/types/adapter-express/express-utils/scope-extractor.interface.d.ts +12 -0
  83. package/lib/cjs/types/adapter-express/express-utils/setup-authorization.d.ts +34 -0
  84. package/lib/cjs/types/adapter-express/express-utils/setup-event-system.d.ts +118 -0
  85. package/lib/cjs/types/adapter-express/express-utils/setup-interceptors.d.ts +115 -0
  86. package/lib/cjs/types/adapter-express/express-utils/setup-lazy-loading.d.ts +123 -0
  87. package/lib/cjs/types/adapter-express/express-utils/utils.d.ts +17 -2
  88. package/lib/cjs/types/adapter-express/express-utils/validation-decorators.d.ts +145 -0
  89. package/lib/cjs/types/adapter-express/express-utils/validation-service.d.ts +88 -0
  90. package/lib/cjs/types/adapter-express/index.d.ts +6 -4
  91. package/lib/cjs/types/adapter-express/micro-api/application-express-micro-route.d.ts +25 -14
  92. package/lib/cjs/types/adapter-express/micro-api/application-express-micro.d.ts +3 -10
  93. package/lib/cjs/types/adapter-express/micro-api/gateway/circuit-breaker.d.ts +111 -0
  94. package/lib/cjs/types/adapter-express/micro-api/gateway/index.d.ts +5 -0
  95. package/lib/cjs/types/adapter-express/micro-api/gateway/service-proxy.d.ts +83 -0
  96. package/lib/cjs/types/adapter-express/micro-api/index.d.ts +7 -1
  97. package/lib/cjs/types/adapter-express/micro-api/micro.d.ts +83 -0
  98. package/lib/cjs/types/adapter-express/micro-api/queue/index.d.ts +5 -0
  99. package/lib/cjs/types/adapter-express/micro-api/queue/queue.interface.d.ts +60 -0
  100. package/lib/cjs/types/adapter-express/micro-api/queue/rabbitmq-consumer.d.ts +86 -0
  101. package/lib/cjs/types/adapter-express/micro-api/serverless/aws-lambda.adapter.d.ts +77 -0
  102. package/lib/cjs/types/adapter-express/micro-api/serverless/cloudflare.adapter.d.ts +64 -0
  103. package/lib/cjs/types/adapter-express/micro-api/serverless/index.d.ts +6 -0
  104. package/lib/cjs/types/adapter-express/micro-api/serverless/vercel.adapter.d.ts +56 -0
  105. package/lib/cjs/types/adapter-express/micro-api/service-mesh/index.d.ts +5 -0
  106. package/lib/cjs/types/adapter-express/micro-api/service-mesh/service-client.d.ts +122 -0
  107. package/lib/cjs/types/adapter-express/micro-api/service-mesh/service-discovery.d.ts +150 -0
  108. package/lib/cjs/types/adapter-express/middleware/index.d.ts +5 -0
  109. package/lib/cjs/types/adapter-express/middleware/request-logging.middleware.d.ts +65 -0
  110. package/lib/cjs/types/adapter-express/render/index.d.ts +1 -0
  111. package/lib/cjs/types/adapter-express/studio/index.d.ts +1 -0
  112. package/lib/cjs/types/adapter-express/studio/studio-integration.d.ts +170 -0
  113. package/lib/cjs/types/index.d.ts +1 -1
  114. package/lib/esm/adapter-express/application-express.base.js +24 -0
  115. package/lib/esm/adapter-express/application-express.js +1656 -0
  116. package/lib/esm/adapter-express/application-express.types.js +1 -0
  117. package/lib/esm/adapter-express/express-utils/base-middleware.js +19 -0
  118. package/lib/esm/adapter-express/express-utils/conditional-middleware.js +96 -0
  119. package/lib/esm/adapter-express/express-utils/constants.js +63 -0
  120. package/lib/esm/adapter-express/express-utils/content/httpContent.js +6 -0
  121. package/lib/esm/adapter-express/express-utils/content-negotiation-decorators.js +120 -0
  122. package/lib/esm/adapter-express/express-utils/decorators.js +604 -0
  123. package/lib/esm/adapter-express/express-utils/exception-filter-decorators.js +6 -0
  124. package/lib/esm/adapter-express/express-utils/guard-context-factory.js +83 -0
  125. package/lib/esm/adapter-express/express-utils/guard-middleware.js +115 -0
  126. package/lib/esm/adapter-express/express-utils/guard-utils.js +14 -0
  127. package/lib/esm/adapter-express/express-utils/http-context-store.js +10 -0
  128. package/lib/esm/adapter-express/express-utils/http-status-middleware.js +116 -0
  129. package/lib/esm/adapter-express/express-utils/httpResponseMessage.js +29 -0
  130. package/lib/esm/adapter-express/express-utils/index.js +24 -0
  131. package/lib/esm/adapter-express/express-utils/interceptor-middleware.js +130 -0
  132. package/lib/esm/adapter-express/express-utils/interfaces.js +1 -0
  133. package/lib/esm/adapter-express/express-utils/inversify-express-server.js +1047 -0
  134. package/lib/esm/adapter-express/express-utils/lazy-module-middleware.js +236 -0
  135. package/lib/esm/adapter-express/express-utils/middleware-composition.js +89 -0
  136. package/lib/esm/adapter-express/express-utils/path-pattern-compat.js +125 -0
  137. package/lib/esm/adapter-express/express-utils/permission-preloader.middleware.js +45 -0
  138. package/lib/esm/adapter-express/express-utils/resolver-multer.js +30 -0
  139. package/lib/esm/adapter-express/express-utils/route-constraints.js +100 -0
  140. package/lib/esm/adapter-express/express-utils/scope-extractor.interface.js +1 -0
  141. package/lib/esm/adapter-express/express-utils/scope-extractor.js +63 -0
  142. package/lib/esm/adapter-express/express-utils/setup-authorization.js +68 -0
  143. package/lib/esm/adapter-express/express-utils/setup-event-system.js +110 -0
  144. package/lib/esm/adapter-express/express-utils/setup-interceptors.js +100 -0
  145. package/lib/esm/adapter-express/express-utils/setup-lazy-loading.js +225 -0
  146. package/lib/esm/adapter-express/express-utils/utils.js +68 -0
  147. package/lib/esm/adapter-express/express-utils/validation-decorators.js +199 -0
  148. package/lib/esm/adapter-express/express-utils/validation-service.js +251 -0
  149. package/lib/esm/adapter-express/index.js +7 -0
  150. package/lib/esm/adapter-express/micro-api/application-express-micro-container.js +48 -0
  151. package/lib/esm/adapter-express/micro-api/application-express-micro-route.js +128 -0
  152. package/lib/esm/adapter-express/micro-api/application-express-micro.js +157 -0
  153. package/lib/esm/adapter-express/micro-api/gateway/circuit-breaker.js +174 -0
  154. package/lib/esm/adapter-express/micro-api/gateway/index.js +5 -0
  155. package/lib/esm/adapter-express/micro-api/gateway/service-proxy.js +210 -0
  156. package/lib/esm/adapter-express/micro-api/index.js +10 -0
  157. package/lib/esm/adapter-express/micro-api/micro.js +266 -0
  158. package/lib/esm/adapter-express/micro-api/queue/index.js +4 -0
  159. package/lib/esm/adapter-express/micro-api/queue/queue.interface.js +1 -0
  160. package/lib/esm/adapter-express/micro-api/queue/rabbitmq-consumer.js +229 -0
  161. package/lib/esm/adapter-express/micro-api/serverless/aws-lambda.adapter.js +180 -0
  162. package/lib/esm/adapter-express/micro-api/serverless/cloudflare.adapter.js +155 -0
  163. package/lib/esm/adapter-express/micro-api/serverless/index.js +6 -0
  164. package/lib/esm/adapter-express/micro-api/serverless/vercel.adapter.js +99 -0
  165. package/lib/esm/adapter-express/micro-api/service-mesh/index.js +5 -0
  166. package/lib/esm/adapter-express/micro-api/service-mesh/service-client.js +191 -0
  167. package/lib/esm/adapter-express/micro-api/service-mesh/service-discovery.js +259 -0
  168. package/lib/esm/adapter-express/middleware/index.js +5 -0
  169. package/lib/esm/adapter-express/middleware/request-logging.middleware.js +239 -0
  170. package/lib/esm/adapter-express/render/constants.js +37 -0
  171. package/lib/esm/adapter-express/render/engine.js +51 -0
  172. package/lib/esm/adapter-express/render/index.js +1 -0
  173. package/lib/esm/adapter-express/render/resolve-render.js +30 -0
  174. package/lib/esm/adapter-express/studio/index.js +1 -0
  175. package/lib/esm/adapter-express/studio/studio-integration.js +236 -0
  176. package/lib/esm/index.mjs +1 -0
  177. package/lib/esm/package.json +3 -0
  178. package/lib/esm/types/adapter-express/application-express.base.d.ts +77 -0
  179. package/lib/esm/types/adapter-express/application-express.d.ts +453 -0
  180. package/lib/esm/types/adapter-express/application-express.types.d.ts +23 -0
  181. package/lib/esm/types/adapter-express/express-utils/base-middleware.d.ts +8 -0
  182. package/lib/esm/types/adapter-express/express-utils/conditional-middleware.d.ts +97 -0
  183. package/lib/esm/types/adapter-express/express-utils/constants.d.ts +57 -0
  184. package/lib/esm/types/adapter-express/express-utils/content/httpContent.d.ts +6 -0
  185. package/lib/esm/types/adapter-express/express-utils/content-negotiation-decorators.d.ts +94 -0
  186. package/lib/esm/types/adapter-express/express-utils/decorators.d.ts +257 -0
  187. package/lib/esm/types/adapter-express/express-utils/exception-filter-decorators.d.ts +6 -0
  188. package/lib/esm/types/adapter-express/express-utils/guard-context-factory.d.ts +17 -0
  189. package/lib/esm/types/adapter-express/express-utils/guard-middleware.d.ts +22 -0
  190. package/lib/esm/types/adapter-express/express-utils/guard-utils.d.ts +11 -0
  191. package/lib/esm/types/adapter-express/express-utils/http-context-store.d.ts +20 -0
  192. package/lib/esm/types/adapter-express/express-utils/http-status-middleware.d.ts +26 -0
  193. package/lib/esm/types/adapter-express/express-utils/httpResponseMessage.d.ts +14 -0
  194. package/lib/esm/types/adapter-express/express-utils/index.d.ts +30 -0
  195. package/lib/esm/types/adapter-express/express-utils/interceptor-middleware.d.ts +40 -0
  196. package/lib/esm/types/adapter-express/express-utils/interfaces.d.ts +115 -0
  197. package/lib/esm/types/adapter-express/express-utils/inversify-express-server.d.ts +172 -0
  198. package/lib/esm/types/adapter-express/express-utils/lazy-module-middleware.d.ts +122 -0
  199. package/lib/esm/types/adapter-express/express-utils/middleware-composition.d.ts +85 -0
  200. package/lib/esm/types/adapter-express/express-utils/path-pattern-compat.d.ts +66 -0
  201. package/lib/esm/types/adapter-express/express-utils/permission-preloader.middleware.d.ts +10 -0
  202. package/lib/esm/types/adapter-express/express-utils/resolver-multer.d.ts +7 -0
  203. package/lib/esm/types/adapter-express/express-utils/route-constraints.d.ts +98 -0
  204. package/lib/esm/types/adapter-express/express-utils/scope-extractor.d.ts +21 -0
  205. package/lib/esm/types/adapter-express/express-utils/scope-extractor.interface.d.ts +12 -0
  206. package/lib/esm/types/adapter-express/express-utils/setup-authorization.d.ts +34 -0
  207. package/lib/esm/types/adapter-express/express-utils/setup-event-system.d.ts +118 -0
  208. package/lib/esm/types/adapter-express/express-utils/setup-interceptors.d.ts +115 -0
  209. package/lib/esm/types/adapter-express/express-utils/setup-lazy-loading.d.ts +123 -0
  210. package/lib/esm/types/adapter-express/express-utils/utils.d.ts +24 -0
  211. package/lib/esm/types/adapter-express/express-utils/validation-decorators.d.ts +145 -0
  212. package/lib/esm/types/adapter-express/express-utils/validation-service.d.ts +88 -0
  213. package/lib/esm/types/adapter-express/index.d.ts +7 -0
  214. package/lib/esm/types/adapter-express/micro-api/application-express-micro-container.d.ts +47 -0
  215. package/lib/esm/types/adapter-express/micro-api/application-express-micro-route.d.ts +104 -0
  216. package/lib/esm/types/adapter-express/micro-api/application-express-micro.d.ts +72 -0
  217. package/lib/esm/types/adapter-express/micro-api/gateway/circuit-breaker.d.ts +111 -0
  218. package/lib/esm/types/adapter-express/micro-api/gateway/index.d.ts +5 -0
  219. package/lib/esm/types/adapter-express/micro-api/gateway/service-proxy.d.ts +83 -0
  220. package/lib/esm/types/adapter-express/micro-api/index.d.ts +7 -0
  221. package/lib/esm/types/adapter-express/micro-api/micro.d.ts +83 -0
  222. package/lib/esm/types/adapter-express/micro-api/queue/index.d.ts +5 -0
  223. package/lib/esm/types/adapter-express/micro-api/queue/queue.interface.d.ts +60 -0
  224. package/lib/esm/types/adapter-express/micro-api/queue/rabbitmq-consumer.d.ts +86 -0
  225. package/lib/esm/types/adapter-express/micro-api/serverless/aws-lambda.adapter.d.ts +77 -0
  226. package/lib/esm/types/adapter-express/micro-api/serverless/cloudflare.adapter.d.ts +64 -0
  227. package/lib/esm/types/adapter-express/micro-api/serverless/index.d.ts +6 -0
  228. package/lib/esm/types/adapter-express/micro-api/serverless/vercel.adapter.d.ts +56 -0
  229. package/lib/esm/types/adapter-express/micro-api/service-mesh/index.d.ts +5 -0
  230. package/lib/esm/types/adapter-express/micro-api/service-mesh/service-client.d.ts +122 -0
  231. package/lib/esm/types/adapter-express/micro-api/service-mesh/service-discovery.d.ts +150 -0
  232. package/lib/esm/types/adapter-express/middleware/index.d.ts +5 -0
  233. package/lib/esm/types/adapter-express/middleware/request-logging.middleware.d.ts +65 -0
  234. package/lib/esm/types/adapter-express/render/constants.d.ts +26 -0
  235. package/lib/esm/types/adapter-express/render/engine.d.ts +20 -0
  236. package/lib/esm/types/adapter-express/render/index.d.ts +5 -0
  237. package/lib/esm/types/adapter-express/render/resolve-render.d.ts +7 -0
  238. package/lib/esm/types/adapter-express/studio/index.d.ts +1 -0
  239. package/lib/esm/types/adapter-express/studio/studio-integration.d.ts +170 -0
  240. package/lib/esm/types/index.d.ts +1 -0
  241. package/lib/package.json +170 -146
  242. package/package.json +170 -146
  243. package/lib/cjs/di/di.interfaces.js +0 -10
  244. package/lib/cjs/types/di/di.interfaces.d.ts +0 -289
@@ -0,0 +1,266 @@
1
+ import { Logger, getRouteRegistry, getErrorHints, getDefaultSuggestionsConfig, formatSuggestions, } from "@expressots/core";
2
+ import express from "express";
3
+ import { AppExpress } from "../application-express.js";
4
+ import { initializeStudio, stopStudio, isStudioEnabled as checkStudioEnabled, getStudioAgent, reportStudioRuntimeInfo, rescanStudioRoutes, } from "../studio/index.js";
5
+ /**
6
+ * Create a new micro API instance
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const app = micro();
11
+ * app.get("/", () => "Hello World");
12
+ * app.listen(3000);
13
+ * ```
14
+ *
15
+ * @param config - Optional configuration
16
+ * @returns MicroApp instance
17
+ * @public API
18
+ */
19
+ export function micro(config) {
20
+ // Disable the log buffering from AppExpress since micro() doesn't use the banner system
21
+ // This restores normal console output for micro API users
22
+ AppExpress.disableBuffering();
23
+ const app = express();
24
+ const logger = new Logger();
25
+ const globalPrefix = config?.globalPrefix?.replace(/\/$/, "") || "";
26
+ let httpServer;
27
+ let errorHandler = null;
28
+ let studioConfig = config?.studio ?? {};
29
+ // Lazy proxy for the Studio Agent middleware. Installed at position 0 so
30
+ // it always runs before route handlers — even though initializeStudio()
31
+ // is only called in listen(). Once the agent starts, it sets the real
32
+ // handler; until then the proxy is a no-op pass-through.
33
+ let studioMiddlewareDelegate = null;
34
+ app.use((req, res, next) => {
35
+ if (studioMiddlewareDelegate) {
36
+ return studioMiddlewareDelegate(req, res, next);
37
+ }
38
+ next();
39
+ });
40
+ // Auto-enable JSON parsing by default
41
+ if (config?.autoParseJson !== false) {
42
+ app.use(express.json());
43
+ app.use(express.urlencoded({ extended: true }));
44
+ }
45
+ /**
46
+ * Wrap handler to auto-send return values
47
+ */
48
+ const wrapHandler = (handler) => {
49
+ return async (req, res, next) => {
50
+ try {
51
+ const result = await handler(req, res, next);
52
+ // If response already sent, don't send again
53
+ if (res.headersSent) {
54
+ return;
55
+ }
56
+ // Auto-send return values
57
+ if (result !== undefined) {
58
+ if (typeof result === "string") {
59
+ res.send(result);
60
+ }
61
+ else {
62
+ res.json(result);
63
+ }
64
+ }
65
+ }
66
+ catch (error) {
67
+ next(error);
68
+ }
69
+ };
70
+ };
71
+ /**
72
+ * Register a route with Express-style middleware ordering
73
+ */
74
+ const route = (method, path, ...handlers) => {
75
+ const fullPath = `${globalPrefix}${path.startsWith("/") ? path : `/${path}`}`;
76
+ const handler = handlers.pop();
77
+ const middleware = handlers;
78
+ app[method](fullPath, ...middleware, wrapHandler(handler));
79
+ logger.info(`Route ${method.toUpperCase()} '${fullPath}' registered`, "micro");
80
+ // Register in the suggestions engine so 404s can produce "Did you mean ...?"
81
+ try {
82
+ getRouteRegistry().register(method.toUpperCase(), fullPath, fullPath);
83
+ }
84
+ catch {
85
+ // Suggestions registry is optional; never fail registration because of it.
86
+ }
87
+ return microApp;
88
+ };
89
+ /**
90
+ * Install a catch-all 404 fallback that mirrors the behavior of the full
91
+ * inversify-express-server: log "Did you mean ...?" via the framework Logger
92
+ * and respond with a structured RFC-7807-style JSON body.
93
+ *
94
+ * Skipped entirely when the suggestion engine is disabled (the env-aware
95
+ * default disables it in NODE_ENV=production).
96
+ */
97
+ const installNotFoundHandler = () => {
98
+ app.use((req, res, next) => {
99
+ if (res.headersSent) {
100
+ return next();
101
+ }
102
+ const requestedPath = req.originalUrl || req.url;
103
+ const requestedMethod = req.method;
104
+ const body = {
105
+ type: "https://expressots.dev/errors/not-found",
106
+ title: "Route Not Found",
107
+ status: 404,
108
+ detail: `Route '${requestedMethod} ${requestedPath}' does not exist`,
109
+ instance: requestedPath,
110
+ timestamp: new Date().toISOString(),
111
+ };
112
+ const suggestionsConfig = getDefaultSuggestionsConfig();
113
+ if (suggestionsConfig.enabled) {
114
+ const hints = getErrorHints(new Error(`Route '${requestedMethod} ${requestedPath}' not found`), {
115
+ path: requestedPath,
116
+ method: requestedMethod,
117
+ statusCode: 404,
118
+ }, suggestionsConfig);
119
+ if (hints.length > 0) {
120
+ try {
121
+ const formatted = formatSuggestions(hints);
122
+ if (formatted) {
123
+ logger.warn(`Route not found: ${requestedMethod} ${requestedPath}${formatted}`, "router-404");
124
+ }
125
+ }
126
+ catch {
127
+ // best-effort logging
128
+ }
129
+ const routeSuggestion = hints.find((hint) => hint.type === "route");
130
+ const actionHint = hints.find((hint) => hint.type === "hint");
131
+ if (routeSuggestion?.routes && routeSuggestion.routes.length > 0) {
132
+ body.suggestions = routeSuggestion.routes.map((suggestion) => ({
133
+ method: suggestion.route.method,
134
+ path: suggestion.route.fullPath || suggestion.route.path,
135
+ similarity: Math.round(suggestion.similarity * 100),
136
+ reason: suggestion.reason,
137
+ }));
138
+ }
139
+ else if (actionHint?.actions && actionHint.actions.length > 0) {
140
+ body.actions = actionHint.actions;
141
+ }
142
+ }
143
+ }
144
+ res.status(404).type("application/json").send(JSON.stringify(body));
145
+ });
146
+ };
147
+ /**
148
+ * Handle server shutdown. In development, exit immediately for fast
149
+ * hot-reload. In production, drain connections before exiting.
150
+ */
151
+ const handleExit = () => {
152
+ const environment = config?.environment || process.env.NODE_ENV || "development";
153
+ void stopStudio();
154
+ if (environment === "development") {
155
+ process.exit(0);
156
+ }
157
+ if (httpServer) {
158
+ httpServer.close(() => process.exit(0));
159
+ setTimeout(() => process.exit(0), 5000).unref();
160
+ }
161
+ else {
162
+ process.exit(0);
163
+ }
164
+ };
165
+ const microApp = {
166
+ get: (path, ...handlers) => route("get", path, ...handlers),
167
+ post: (path, ...handlers) => route("post", path, ...handlers),
168
+ put: (path, ...handlers) => route("put", path, ...handlers),
169
+ patch: (path, ...handlers) => route("patch", path, ...handlers),
170
+ delete: (path, ...handlers) => route("delete", path, ...handlers),
171
+ use(...args) {
172
+ if (typeof args[0] === "string") {
173
+ app.use(args[0], ...args.slice(1));
174
+ }
175
+ else {
176
+ app.use(...args);
177
+ }
178
+ return microApp;
179
+ },
180
+ setErrorHandler(handler) {
181
+ errorHandler = handler;
182
+ return microApp;
183
+ },
184
+ async listen(port, appInfo) {
185
+ const normalizedPort = typeof port === "string" ? parseInt(port, 10) : port;
186
+ const listenStartedAt = Date.now();
187
+ // Initialize Studio Agent. The agent's middleware is registered via
188
+ // app.use() inside initializeStudio, but that lands AFTER the user's
189
+ // routes in the Express stack. Our lazy proxy (installed at position 0
190
+ // during micro() creation) ensures CORS headers are injected before
191
+ // any route handler sends a response.
192
+ const studioStarted = await initializeStudio(app, {
193
+ ...studioConfig,
194
+ serviceName: studioConfig.serviceName ?? "expressots-micro",
195
+ appPort: normalizedPort,
196
+ globalPrefix: globalPrefix || undefined,
197
+ });
198
+ if (studioStarted) {
199
+ const agent = getStudioAgent();
200
+ if (agent) {
201
+ studioMiddlewareDelegate = agent.createMiddleware();
202
+ }
203
+ }
204
+ // Install the 404 fallback before the user error handler so unmatched
205
+ // routes get suggestions instead of falling through to the default
206
+ // Express HTML or - worse - to a regular middleware that arity-confused
207
+ // its way into running on every request.
208
+ installNotFoundHandler();
209
+ // Apply error handler last. Wrap it in a 4-arity function so that
210
+ // Express recognizes it as an error-handling middleware regardless of
211
+ // whether the user wrote `(err, req, res)` or `(err, req, res, next)`.
212
+ // Without this wrapper, Express would treat a 3-arg user handler as a
213
+ // regular middleware and run it on every request, which both swallows
214
+ // 404s and crashes when the user calls `res.status(...)` (positional
215
+ // `res` would actually be Express's `next`).
216
+ if (errorHandler) {
217
+ const userHandler = errorHandler;
218
+ const wrappedErrorHandler = (err, req, res, next) => userHandler(err, req, res, next);
219
+ app.use(wrappedErrorHandler);
220
+ }
221
+ return new Promise((resolve, reject) => {
222
+ httpServer = app.listen(normalizedPort, async () => {
223
+ const address = httpServer.address();
224
+ const actualPort = typeof address === "object" && address?.port ? address.port : normalizedPort;
225
+ if (config?.showBanner !== false) {
226
+ const name = appInfo?.appName || "ExpressoTS Micro";
227
+ const version = appInfo?.appVersion || "1.0.0";
228
+ const environment = config?.environment || process.env.NODE_ENV || "development";
229
+ logger.info(`${name} version ${version} is running on port ${actualPort} - Environment: ${environment}`, "micro");
230
+ }
231
+ // Push runtime info to Studio Agent now that we know the actual port
232
+ reportStudioRuntimeInfo({
233
+ appPort: actualPort,
234
+ globalPrefix: globalPrefix || undefined,
235
+ startupMs: Date.now() - listenStartedAt,
236
+ });
237
+ // Re-scan routes so Studio sees the fully-populated Express router
238
+ void rescanStudioRoutes();
239
+ // Handle graceful shutdown
240
+ ["SIGTERM", "SIGHUP", "SIGBREAK", "SIGQUIT", "SIGINT"].forEach((signal) => {
241
+ process.on(signal, handleExit);
242
+ });
243
+ resolve();
244
+ });
245
+ httpServer.on("error", (error) => {
246
+ logger.error(`Server error: ${error.message}`, "micro");
247
+ reject(error);
248
+ });
249
+ });
250
+ },
251
+ getHttpServer() {
252
+ return httpServer ?? null;
253
+ },
254
+ getApp() {
255
+ return app;
256
+ },
257
+ setStudio(cfg) {
258
+ studioConfig = cfg;
259
+ return microApp;
260
+ },
261
+ isStudioEnabled() {
262
+ return checkStudioEnabled();
263
+ },
264
+ };
265
+ return microApp;
266
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Queue Integration for ExpressoTS Micro Template
3
+ */
4
+ export { RabbitMQConsumer } from "./rabbitmq-consumer.js";
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Load amqplib dynamically to avoid requiring it as a dependency
3
+ */
4
+ async function loadAmqpLib() {
5
+ try {
6
+ // `amqplib` is an OPTIONAL peer dep - it is intentionally not installed
7
+ // in this package's devDependencies. We route the specifier through a
8
+ // variable so TypeScript does not try to statically resolve it during
9
+ // build (TS treats variable-typed specifiers in `await import(...)` as
10
+ // `any` and skips the resolution check). Resolution happens at runtime
11
+ // in user apps that have actually installed amqplib.
12
+ const specifier = "amqplib";
13
+ const amqp = (await import(specifier));
14
+ return amqp;
15
+ }
16
+ catch {
17
+ throw new Error("amqplib is not installed. Install it with: npm install amqplib @types/amqplib");
18
+ }
19
+ }
20
+ /**
21
+ * RabbitMQ Consumer - Message queue consumer for RabbitMQ.
22
+ *
23
+ * Features:
24
+ * - Message consumption with handlers
25
+ * - Message publishing
26
+ * - Exchange support
27
+ * - Automatic reconnection
28
+ * - Prefetch control
29
+ * - Dead letter queue support
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const orderQueue = new RabbitMQConsumer({
34
+ * url: process.env.RABBITMQ_URL,
35
+ * queue: "orders",
36
+ * prefetch: 10,
37
+ * });
38
+ *
39
+ * // Start consuming
40
+ * await orderQueue.consume(async (message) => {
41
+ * const order = message.body;
42
+ * console.log("Processing order:", order.id);
43
+ * await processOrder(order);
44
+ * });
45
+ *
46
+ * // Publish from HTTP endpoint
47
+ * app.Route.post("/orders", async (req, res) => {
48
+ * await orderQueue.publish(req.body);
49
+ * res.status(202).json({ message: "Order queued" });
50
+ * });
51
+ *
52
+ * // Graceful shutdown
53
+ * process.on("SIGTERM", async () => {
54
+ * await orderQueue.close();
55
+ * });
56
+ * ```
57
+ *
58
+ * Note: This implementation requires the 'amqplib' package.
59
+ * Install with: npm install amqplib @types/amqplib
60
+ */
61
+ export class RabbitMQConsumer {
62
+ // Using 'any' for AMQP types since amqplib is an optional peer dependency
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ connection = null;
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ channel = null;
67
+ config;
68
+ stats = {
69
+ messagesReceived: 0,
70
+ messagesProcessed: 0,
71
+ messagesFailed: 0,
72
+ messagesPublished: 0,
73
+ isConnected: false,
74
+ };
75
+ constructor(config) {
76
+ this.config = {
77
+ queue: config.queue,
78
+ url: config.url,
79
+ exchange: config.exchange ?? "",
80
+ exchangeType: config.exchangeType ?? "direct",
81
+ routingKey: config.routingKey ?? config.queue,
82
+ prefetch: config.prefetch ?? 1,
83
+ durable: config.durable ?? true,
84
+ concurrency: config.concurrency ?? 1,
85
+ autoAck: config.autoAck ?? true,
86
+ debug: config.debug ?? false,
87
+ };
88
+ }
89
+ /**
90
+ * Connect to RabbitMQ
91
+ */
92
+ async connect() {
93
+ if (this.connection)
94
+ return;
95
+ try {
96
+ const amqp = await loadAmqpLib();
97
+ this.connection = await amqp.connect(this.config.url);
98
+ this.channel = await this.connection.createChannel();
99
+ // Set prefetch
100
+ await this.channel.prefetch(this.config.prefetch);
101
+ // Declare queue
102
+ await this.channel.assertQueue(this.config.queue, {
103
+ durable: this.config.durable,
104
+ });
105
+ // Declare exchange if configured
106
+ if (this.config.exchange) {
107
+ await this.channel.assertExchange(this.config.exchange, this.config.exchangeType, {
108
+ durable: true,
109
+ });
110
+ // Bind queue to exchange
111
+ await this.channel.bindQueue(this.config.queue, this.config.exchange, this.config.routingKey);
112
+ }
113
+ this.stats.isConnected = true;
114
+ if (this.config.debug) {
115
+ console.log(`[RabbitMQ] Connected to ${this.config.url}, queue: ${this.config.queue}`);
116
+ }
117
+ // Handle connection close
118
+ this.connection.on("close", () => {
119
+ this.stats.isConnected = false;
120
+ console.log("[RabbitMQ] Connection closed");
121
+ });
122
+ this.connection.on("error", (err) => {
123
+ console.error("[RabbitMQ] Connection error:", err);
124
+ });
125
+ }
126
+ catch (error) {
127
+ console.error("[RabbitMQ] Failed to connect:", error);
128
+ throw error;
129
+ }
130
+ }
131
+ /**
132
+ * Start consuming messages
133
+ */
134
+ async consume(handler) {
135
+ await this.connect();
136
+ if (!this.channel) {
137
+ throw new Error("Channel not initialized");
138
+ }
139
+ await this.channel.consume(this.config.queue,
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
+ async (msg) => {
142
+ if (!msg)
143
+ return;
144
+ this.stats.messagesReceived++;
145
+ this.stats.lastMessageAt = new Date();
146
+ try {
147
+ // Parse message
148
+ const body = JSON.parse(msg.content.toString());
149
+ const queueMessage = {
150
+ id: msg.properties.messageId || Date.now().toString(36),
151
+ body,
152
+ headers: msg.properties.headers,
153
+ timestamp: new Date(msg.properties.timestamp || Date.now()),
154
+ receiveCount: msg.properties.headers?.["x-delivery-count"] || 1,
155
+ raw: msg,
156
+ };
157
+ if (this.config.debug) {
158
+ console.log(`[RabbitMQ] Received message:`, queueMessage.id);
159
+ }
160
+ // Process message
161
+ await handler(queueMessage);
162
+ this.stats.messagesProcessed++;
163
+ // Acknowledge
164
+ if (!this.config.autoAck && this.channel) {
165
+ this.channel.ack(msg);
166
+ }
167
+ }
168
+ catch (error) {
169
+ this.stats.messagesFailed++;
170
+ console.error("[RabbitMQ] Message processing failed:", error);
171
+ // Reject and requeue if not auto-ack
172
+ if (!this.config.autoAck && this.channel) {
173
+ this.channel.nack(msg, false, true);
174
+ }
175
+ }
176
+ }, { noAck: this.config.autoAck });
177
+ if (this.config.debug) {
178
+ console.log(`[RabbitMQ] Consuming from ${this.config.queue}`);
179
+ }
180
+ }
181
+ /**
182
+ * Publish a message to the queue
183
+ */
184
+ async publish(message) {
185
+ await this.connect();
186
+ if (!this.channel) {
187
+ throw new Error("Channel not initialized");
188
+ }
189
+ const content = Buffer.from(JSON.stringify(message));
190
+ const properties = {
191
+ messageId: Date.now().toString(36),
192
+ timestamp: Date.now(),
193
+ contentType: "application/json",
194
+ };
195
+ if (this.config.exchange) {
196
+ this.channel.publish(this.config.exchange, this.config.routingKey, content, properties);
197
+ }
198
+ else {
199
+ this.channel.sendToQueue(this.config.queue, content, properties);
200
+ }
201
+ this.stats.messagesPublished++;
202
+ if (this.config.debug) {
203
+ console.log(`[RabbitMQ] Published message to ${this.config.queue}`);
204
+ }
205
+ }
206
+ /**
207
+ * Close the connection
208
+ */
209
+ async close() {
210
+ if (this.channel) {
211
+ await this.channel.close();
212
+ this.channel = null;
213
+ }
214
+ if (this.connection) {
215
+ await this.connection.close();
216
+ this.connection = null;
217
+ }
218
+ this.stats.isConnected = false;
219
+ if (this.config.debug) {
220
+ console.log("[RabbitMQ] Connection closed");
221
+ }
222
+ }
223
+ /**
224
+ * Get consumer statistics
225
+ */
226
+ getStats() {
227
+ return { ...this.stats };
228
+ }
229
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * AWS Lambda Adapter for ExpressoTS Micro API
3
+ *
4
+ * Converts Lambda events to Express requests and responses.
5
+ */
6
+ /**
7
+ * Create an AWS Lambda handler from an Express app
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { createMicroAPI, awsLambdaAdapter } from "@expressots/adapter-express";
12
+ *
13
+ * const microAPI = createMicroAPI();
14
+ * const app = microAPI.build();
15
+ *
16
+ * app.Middleware.parse();
17
+ * app.Route.get("/", (req, res) => res.json({ message: "Hello Lambda!" }));
18
+ *
19
+ * export const handler = awsLambdaAdapter(app);
20
+ * ```
21
+ */
22
+ export function awsLambdaAdapter(app, config) {
23
+ const expressApp = "getExpressApp" in app && app.getExpressApp ? app.getExpressApp() : app;
24
+ const binaryTypes = config?.binaryContentTypes ?? [
25
+ "application/octet-stream",
26
+ "image/*",
27
+ "audio/*",
28
+ "video/*",
29
+ "font/*",
30
+ ];
31
+ const debug = config?.debug ?? false;
32
+ return async (event, context) => {
33
+ if (debug) {
34
+ console.log("[Lambda] Event:", JSON.stringify(event, null, 2));
35
+ }
36
+ // Parse body
37
+ let body;
38
+ if (event.body) {
39
+ let rawBody;
40
+ if (event.isBase64Encoded) {
41
+ rawBody = Buffer.from(event.body, "base64");
42
+ }
43
+ else {
44
+ rawBody = event.body;
45
+ }
46
+ // Parse JSON body if content-type is application/json
47
+ const contentType = event.headers?.["content-type"] || event.headers?.["Content-Type"] || "";
48
+ if (contentType.includes("application/json")) {
49
+ try {
50
+ const bodyStr = Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : rawBody;
51
+ body = JSON.parse(bodyStr);
52
+ }
53
+ catch {
54
+ // Keep as raw if JSON parsing fails
55
+ body = rawBody;
56
+ }
57
+ }
58
+ else {
59
+ body = rawBody;
60
+ }
61
+ }
62
+ // Build request URL
63
+ const queryString = event.queryStringParameters
64
+ ? "?" +
65
+ Object.entries(event.queryStringParameters)
66
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
67
+ .join("&")
68
+ : "";
69
+ const url = event.path + queryString;
70
+ // Build headers
71
+ const headers = {
72
+ ...event.headers,
73
+ };
74
+ // Add Lambda context to headers
75
+ headers["x-lambda-request-id"] = context.awsRequestId;
76
+ headers["x-lambda-function"] = context.functionName;
77
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
78
+ return new Promise((resolve, _reject) => {
79
+ // Create mock Express-compatible request object
80
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ const req = {
82
+ method: event.httpMethod,
83
+ url,
84
+ path: event.path,
85
+ headers,
86
+ body,
87
+ query: event.queryStringParameters || {},
88
+ params: {},
89
+ lambda: { event, context },
90
+ get: (name) => headers[name.toLowerCase()],
91
+ };
92
+ // Create mock Express-compatible response object
93
+ const chunks = [];
94
+ const responseHeaders = {};
95
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
96
+ const res = {
97
+ statusCode: 200,
98
+ setHeader: (name, value) => {
99
+ responseHeaders[name.toLowerCase()] = value;
100
+ },
101
+ getHeader: (name) => responseHeaders[name.toLowerCase()],
102
+ write: (chunk) => {
103
+ chunks.push(Buffer.from(chunk));
104
+ },
105
+ end: (chunk) => {
106
+ if (chunk) {
107
+ chunks.push(Buffer.from(chunk));
108
+ }
109
+ const bodyBuffer = Buffer.concat(chunks);
110
+ const contentType = responseHeaders["content-type"] || "";
111
+ const isBinary = binaryTypes.some((type) => {
112
+ if (type.endsWith("/*")) {
113
+ return contentType.startsWith(type.replace("/*", "/"));
114
+ }
115
+ return contentType === type;
116
+ });
117
+ const response = {
118
+ statusCode: res.statusCode,
119
+ headers: responseHeaders,
120
+ body: isBinary ? bodyBuffer.toString("base64") : bodyBuffer.toString("utf8"),
121
+ isBase64Encoded: isBinary,
122
+ };
123
+ if (debug) {
124
+ console.log("[Lambda] Response:", {
125
+ statusCode: response.statusCode,
126
+ headers: response.headers,
127
+ bodyLength: response.body.length,
128
+ });
129
+ }
130
+ resolve(response);
131
+ },
132
+ status: (code) => {
133
+ res.statusCode = code;
134
+ return res;
135
+ },
136
+ json: (data) => {
137
+ res.setHeader("content-type", "application/json");
138
+ res.end(JSON.stringify(data));
139
+ },
140
+ send: (data) => {
141
+ if (typeof data === "string") {
142
+ res.setHeader("content-type", "text/html");
143
+ res.end(data);
144
+ }
145
+ else if (Buffer.isBuffer(data)) {
146
+ res.end(data);
147
+ }
148
+ else {
149
+ res.json(data);
150
+ }
151
+ },
152
+ };
153
+ // Handle request through Express
154
+ try {
155
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
+ expressApp(req, res, (err) => {
157
+ if (err) {
158
+ console.error("[Lambda] Express error:", err);
159
+ resolve({
160
+ statusCode: 500,
161
+ headers: { "content-type": "application/json" },
162
+ body: JSON.stringify({ error: err.message }),
163
+ isBase64Encoded: false,
164
+ });
165
+ }
166
+ });
167
+ }
168
+ catch (error) {
169
+ const errorMessage = error instanceof Error ? error.message : String(error);
170
+ console.error("[Lambda] Handler error:", error);
171
+ resolve({
172
+ statusCode: 500,
173
+ headers: { "content-type": "application/json" },
174
+ body: JSON.stringify({ error: errorMessage }),
175
+ isBase64Encoded: false,
176
+ });
177
+ }
178
+ });
179
+ };
180
+ }