@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,236 @@
1
+ /**
2
+ * Lazy Module Auto-Load Middleware
3
+ *
4
+ * Automatically loads lazy modules when their routes are accessed.
5
+ * This provides seamless lazy loading without 404 errors.
6
+ *
7
+ * @module adapter-express
8
+ */
9
+ import { Router } from "express";
10
+ // ============================================================================
11
+ // Lazy Module Middleware
12
+ // ============================================================================
13
+ /**
14
+ * Create middleware that auto-loads lazy modules when their routes are accessed.
15
+ *
16
+ * @layer public
17
+ * @audience application-developers
18
+ * @concept lazy-loading
19
+ *
20
+ * UNIQUE: Seamless lazy loading - no 404s! Modules load automatically
21
+ * when their routes are accessed for the first time.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const middleware = createLazyModuleMiddleware({
26
+ * routes: [
27
+ * { prefix: "/admin", moduleName: "AdminModule", loaded: false },
28
+ * { prefix: "/reports", moduleName: "ReportsModule", loaded: false }
29
+ * ],
30
+ * loader: lazyModuleLoader,
31
+ * globalPrefix: "/api"
32
+ * });
33
+ *
34
+ * app.use(middleware);
35
+ * ```
36
+ *
37
+ * @param config - Middleware configuration
38
+ * @returns Express middleware function
39
+ *
40
+ * @public API
41
+ */
42
+ export function createLazyModuleMiddleware(config) {
43
+ const { routes, loader, globalPrefix = "" } = config;
44
+ // Create a map for O(1) prefix lookup
45
+ const routeMap = new Map();
46
+ for (const route of routes) {
47
+ const fullPrefix = globalPrefix + route.prefix;
48
+ routeMap.set(fullPrefix.toLowerCase(), route);
49
+ }
50
+ // Track modules currently being loaded (to prevent duplicate loads)
51
+ const loadingModules = new Set();
52
+ return async (req, res, next) => {
53
+ const path = req.path.toLowerCase();
54
+ // Find matching route prefix
55
+ let matchedRoute;
56
+ for (const [prefix, route] of routeMap.entries()) {
57
+ if (path.startsWith(prefix)) {
58
+ matchedRoute = route;
59
+ break;
60
+ }
61
+ }
62
+ // No matching lazy module route
63
+ if (!matchedRoute) {
64
+ return next();
65
+ }
66
+ // Already loaded
67
+ if (matchedRoute.loaded || loader.isLoaded(matchedRoute.moduleName)) {
68
+ matchedRoute.loaded = true;
69
+ return next();
70
+ }
71
+ // Currently loading - wait for it
72
+ if (loadingModules.has(matchedRoute.moduleName)) {
73
+ // Poll until loaded (with timeout)
74
+ const maxWait = 30000; // 30 seconds
75
+ const pollInterval = 50;
76
+ let waited = 0;
77
+ while (loadingModules.has(matchedRoute.moduleName) && waited < maxWait) {
78
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
79
+ waited += pollInterval;
80
+ }
81
+ if (loader.isLoaded(matchedRoute.moduleName)) {
82
+ matchedRoute.loaded = true;
83
+ return next();
84
+ }
85
+ else {
86
+ res.status(503).json({
87
+ error: "Service Unavailable",
88
+ message: `Module '${matchedRoute.moduleName}' is still loading. Please try again.`,
89
+ });
90
+ return;
91
+ }
92
+ }
93
+ // Start loading the module
94
+ loadingModules.add(matchedRoute.moduleName);
95
+ const startTime = Date.now();
96
+ config.onLoadStart?.(matchedRoute.moduleName, req.path);
97
+ try {
98
+ await loader.load(matchedRoute.moduleName);
99
+ matchedRoute.loaded = true;
100
+ const loadTime = Date.now() - startTime;
101
+ config.onLoadComplete?.(matchedRoute.moduleName, loadTime);
102
+ console.log(`[Lazy Loading] Module '${matchedRoute.moduleName}' loaded on-demand in ${loadTime}ms ` +
103
+ `(triggered by ${req.method} ${req.path})`);
104
+ loadingModules.delete(matchedRoute.moduleName);
105
+ // Dynamic route registration: If callbacks are provided, register routes
106
+ // and continue the request without redirecting
107
+ if (config.onRegisterRoutes && config.expressApp) {
108
+ try {
109
+ const moduleRouter = Router();
110
+ config.onRegisterRoutes(matchedRoute.moduleName, moduleRouter);
111
+ // Mount the new routes (Express will pick them up for future requests)
112
+ // For THIS request, we continue to let it fall through
113
+ console.log(`[Lazy Loading] Dynamically registered routes for '${matchedRoute.moduleName}'`);
114
+ }
115
+ catch (registrationError) {
116
+ console.warn(`[Lazy Loading] Dynamic route registration failed for '${matchedRoute.moduleName}':`, registrationError);
117
+ }
118
+ }
119
+ // Continue to next middleware - the routes should now be available
120
+ // This works because Express route matching happens per-request
121
+ // and the container now has the lazy module's controllers bound.
122
+ //
123
+ // If dynamic registration isn't set up, fall back to 307 redirect
124
+ if (config.onRegisterRoutes) {
125
+ // Re-emit the request through Express routing
126
+ // The route should now match a real handler
127
+ return next("route");
128
+ }
129
+ else {
130
+ // Fallback: 307 redirect for single round-trip (temporary redirect preserves method)
131
+ res.redirect(307, req.originalUrl);
132
+ }
133
+ }
134
+ catch (error) {
135
+ loadingModules.delete(matchedRoute.moduleName);
136
+ const err = error instanceof Error ? error : new Error(String(error));
137
+ config.onLoadError?.(matchedRoute.moduleName, err);
138
+ console.error(`[Lazy Loading] Failed to load module '${matchedRoute.moduleName}':`, err.message);
139
+ res.status(500).json({
140
+ error: "Module Load Failed",
141
+ message: `Failed to load module for route '${req.path}'. ${err.message}`,
142
+ });
143
+ }
144
+ };
145
+ }
146
+ // ============================================================================
147
+ // Helper Functions
148
+ // ============================================================================
149
+ /**
150
+ * Extract route prefixes from a lazy module.
151
+ *
152
+ * Uses auto-detected routes from CreateLazyModule (which analyzes @controller decorators),
153
+ * falls back to prefetchOn config, and finally infers from module name.
154
+ *
155
+ * @param lazyModule - The lazy module to analyze
156
+ * @returns Detected route prefixes
157
+ *
158
+ * @public API
159
+ */
160
+ export function extractRoutePrefixes(lazyModule) {
161
+ const prefixes = [];
162
+ // 1. Use auto-detected routePrefixes from CreateLazyModule (best source!)
163
+ if (lazyModule.config.routePrefixes && lazyModule.config.routePrefixes.length > 0) {
164
+ return lazyModule.config.routePrefixes;
165
+ }
166
+ // 2. Try prefetchOn config as fallback
167
+ const prefetchOn = lazyModule.config.prefetchOn || [];
168
+ for (const prefetch of prefetchOn) {
169
+ const parts = prefetch.route.split("/").filter(Boolean);
170
+ if (parts.length > 0) {
171
+ prefixes.push("/" + parts[0]);
172
+ }
173
+ }
174
+ if (prefixes.length > 0) {
175
+ return [...new Set(prefixes)];
176
+ }
177
+ // 3. Last resort: infer from module name
178
+ // "AdminModule" -> "/admin", "UserSettingsModule" -> "/usersettings"
179
+ const inferredPrefix = "/" +
180
+ lazyModule.name
181
+ .replace(/Module$/i, "")
182
+ .replace(/([A-Z])/g, (match, p1, offset) => offset > 0 ? `-${p1.toLowerCase()}` : p1.toLowerCase());
183
+ return [inferredPrefix];
184
+ }
185
+ /**
186
+ * Create route mappings from lazy modules.
187
+ *
188
+ * Route detection priority:
189
+ * 1. Manual routePrefixMap (if provided)
190
+ * 2. Auto-detected from @controller() decorators (via CreateLazyModule)
191
+ * 3. prefetchOn config
192
+ * 4. Inferred from module name
193
+ *
194
+ * @param lazyModules - Array of lazy modules
195
+ * @param routePrefixMap - Optional manual prefix mappings { moduleName: prefix }
196
+ * @returns Array of route mappings
197
+ *
198
+ * @example
199
+ * ```typescript
200
+ * // Zero-config: routes auto-detected from controllers!
201
+ * const mappings = createRouteMappings([AdminModule, ReportsModule]);
202
+ * // Automatically maps @controller("/admin") -> /admin
203
+ *
204
+ * // Or with manual overrides
205
+ * const mappings = createRouteMappings(
206
+ * [AdminModule, ReportsModule],
207
+ * { "AdminModule": "/admin-panel" } // Override auto-detection
208
+ * );
209
+ * ```
210
+ *
211
+ * @public API
212
+ */
213
+ export function createRouteMappings(lazyModules, routePrefixMap) {
214
+ const mappings = [];
215
+ for (const module of lazyModules) {
216
+ // Use manual mapping if provided (highest priority)
217
+ if (routePrefixMap && routePrefixMap[module.name]) {
218
+ mappings.push({
219
+ prefix: routePrefixMap[module.name],
220
+ moduleName: module.name,
221
+ loaded: module.isLoaded,
222
+ });
223
+ continue;
224
+ }
225
+ // Use auto-detected prefixes (includes @controller detection)
226
+ const prefixes = extractRoutePrefixes(module);
227
+ for (const prefix of prefixes) {
228
+ mappings.push({
229
+ prefix,
230
+ moduleName: module.name,
231
+ loaded: module.isLoaded,
232
+ });
233
+ }
234
+ }
235
+ return mappings;
236
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Symbol to identify composed middleware configuration objects.
3
+ * Used internally for type checking.
4
+ */
5
+ export const COMPOSED_MIDDLEWARE_SYMBOL = Symbol("ComposedMiddleware");
6
+ /**
7
+ * Type guard to check if an object is a ComposedMiddlewareConfig.
8
+ */
9
+ export function isComposedMiddleware(item) {
10
+ return (typeof item === "object" &&
11
+ item !== null &&
12
+ COMPOSED_MIDDLEWARE_SYMBOL in item &&
13
+ "middleware" in item &&
14
+ "type" in item &&
15
+ Array.isArray(item.middleware) &&
16
+ (item.type === "combine" ||
17
+ item.type === "sequence"));
18
+ }
19
+ /**
20
+ * Combines multiple middleware into a single middleware that executes all of them sequentially.
21
+ * All middleware will execute in order. If any middleware calls `next(error)`, the error
22
+ * is propagated to Express's error handling system.
23
+ *
24
+ * @param middleware - Array of middleware to combine
25
+ * @returns ComposedMiddlewareConfig object
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * // Combine multiple middleware into a reusable group
30
+ * @Get("/api", combine(AuthMiddleware, LoggingMiddleware, RateLimitMiddleware))
31
+ * async apiHandler() {}
32
+ * ```
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * // Combine with conditional middleware (Phase 3)
37
+ * @Get("/admin",
38
+ * when(req => req.method === "POST", combine(BodyParser, ValidationMiddleware)),
39
+ * combine(AuthMiddleware, LoggingMiddleware)
40
+ * )
41
+ * async adminHandler() {}
42
+ * ```
43
+ */
44
+ export function combine(...middleware) {
45
+ if (middleware.length === 0) {
46
+ throw new Error("combine() requires at least one middleware");
47
+ }
48
+ return {
49
+ middleware,
50
+ type: "combine",
51
+ [COMPOSED_MIDDLEWARE_SYMBOL]: true,
52
+ };
53
+ }
54
+ /**
55
+ * Sequences multiple middleware that execute one after another.
56
+ * Similar to `combine()`, but semantically indicates sequential execution
57
+ * where each middleware depends on the previous one completing successfully.
58
+ * If any middleware calls `next(error)`, execution stops and the error is propagated.
59
+ *
60
+ * @param middleware - Array of middleware to sequence
61
+ * @returns ComposedMiddlewareConfig object
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * // Sequence middleware where each depends on the previous
66
+ * @Get("/api", sequence(ValidateMiddleware, TransformMiddleware, ProcessMiddleware))
67
+ * async apiHandler() {}
68
+ * ```
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * // Sequence with conditional middleware
73
+ * @Get("/data",
74
+ * when(req => req.method === "POST", sequence(BodyParser, ValidateMiddleware)),
75
+ * sequence(AuthMiddleware, ProcessMiddleware)
76
+ * )
77
+ * async dataHandler() {}
78
+ * ```
79
+ */
80
+ export function sequence(...middleware) {
81
+ if (middleware.length === 0) {
82
+ throw new Error("sequence() requires at least one middleware");
83
+ }
84
+ return {
85
+ middleware,
86
+ type: "sequence",
87
+ [COMPOSED_MIDDLEWARE_SYMBOL]: true,
88
+ };
89
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Split `:name(regex)` segments out of an Express-style route path.
3
+ *
4
+ * The walker honours balanced parens inside the regex (e.g.
5
+ * `(\\d{4})` or `((a|b)+)`), which is more forgiving than a naive
6
+ * single-pass regex match would be. Returns the original path and an
7
+ * empty constraints list when no inline patterns are found, so this is
8
+ * a no-op for the common case.
9
+ */
10
+ export function splitPathConstraints(path) {
11
+ // Defensive: hand a non-string straight back. The decorators occasionally
12
+ // see `undefined` or `null` paths in older test fixtures and we don't
13
+ // want to crash decorator-time evaluation just because of input shape.
14
+ if (typeof path !== "string") {
15
+ return { path: path, constraints: [] };
16
+ }
17
+ const constraints = [];
18
+ let out = "";
19
+ let i = 0;
20
+ while (i < path.length) {
21
+ const ch = path[i];
22
+ if (ch !== ":") {
23
+ out += ch;
24
+ i++;
25
+ continue;
26
+ }
27
+ // Found a `:` — scan the following identifier (matches `[A-Za-z0-9_]+`,
28
+ // same character class path-to-regexp v8 accepts).
29
+ const start = i;
30
+ i++;
31
+ let nameEnd = i;
32
+ while (nameEnd < path.length && /[A-Za-z0-9_]/.test(path[nameEnd])) {
33
+ nameEnd++;
34
+ }
35
+ if (nameEnd === i) {
36
+ // `:` not followed by an identifier — leave it to path-to-regexp.
37
+ out += ":";
38
+ continue;
39
+ }
40
+ const paramName = path.slice(i, nameEnd);
41
+ out += `:${paramName}`;
42
+ i = nameEnd;
43
+ // Optional inline `(...regex...)` immediately after the name.
44
+ if (path[i] !== "(") {
45
+ continue;
46
+ }
47
+ let depth = 1;
48
+ let j = i + 1;
49
+ while (j < path.length && depth > 0) {
50
+ const c = path[j];
51
+ if (c === "\\" && j + 1 < path.length) {
52
+ j += 2;
53
+ continue;
54
+ }
55
+ if (c === "(")
56
+ depth++;
57
+ else if (c === ")")
58
+ depth--;
59
+ j++;
60
+ }
61
+ if (depth !== 0) {
62
+ // Unbalanced — leave the original path intact and let path-to-regexp
63
+ // raise its own (better-located) error.
64
+ out += path.slice(i);
65
+ i = path.length;
66
+ break;
67
+ }
68
+ const rawPattern = path.slice(i + 1, j - 1);
69
+ try {
70
+ constraints.push({
71
+ paramName,
72
+ regex: new RegExp(`^(?:${rawPattern})$`),
73
+ rawPattern,
74
+ });
75
+ }
76
+ catch {
77
+ // The regex inside the parens is malformed. We don't want a
78
+ // decorator-time crash — fall back to dropping the constraint so
79
+ // the route at least registers; runtime validation simply won't
80
+ // run for this param.
81
+ }
82
+ // Consume the `(...)` segment without re-emitting it into `out`,
83
+ // since path-to-regexp v8 doesn't accept the syntax.
84
+ i = j;
85
+ // Quietly consume a trailing redundant `?` (older code wrote
86
+ // `:foo(\\d+)?`); v8 spells optional segments with `{...}` braces,
87
+ // and "drop the constraint, keep the param" is the sane fallback.
88
+ if (path[i] === "?") {
89
+ i++;
90
+ }
91
+ // Path-to-regexp v6 supported quantifier suffixes (`+`, `*`); v8
92
+ // dropped them. We strip them silently for the same reason.
93
+ while (path[i] === "+" || path[i] === "*") {
94
+ i++;
95
+ }
96
+ void start;
97
+ }
98
+ return { path: out, constraints };
99
+ }
100
+ /**
101
+ * Build a middleware that enforces the given param-level regex
102
+ * constraints on `req.params`. Returns `null` when the list is empty
103
+ * (so callers can avoid wiring an unnecessary middleware).
104
+ *
105
+ * When a constraint fails, the middleware delegates to `next()` without
106
+ * a value; the framework's NotFound handler then converts that into a
107
+ * 404 — same observable behaviour as Express 4's "no route matched".
108
+ */
109
+ export function createPathConstraintMiddleware(constraints) {
110
+ if (constraints.length === 0)
111
+ return null;
112
+ return (req, res, next) => {
113
+ for (const c of constraints) {
114
+ const value = req.params?.[c.paramName];
115
+ // Express 5 sometimes hands back `string[]` for splat params, but
116
+ // inline-regex params are always scalar strings; coerce defensively.
117
+ const scalar = Array.isArray(value) ? value.join("/") : value;
118
+ if (typeof scalar !== "string" || !c.regex.test(scalar)) {
119
+ // Skip to the next handler so the framework's 404 path runs.
120
+ return next("route");
121
+ }
122
+ }
123
+ next();
124
+ };
125
+ }
@@ -0,0 +1,45 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { inject } from "@expressots/core";
11
+ import { BaseMiddleware } from "./base-middleware.js";
12
+ /**
13
+ * Middleware that preloads permissions for authenticated users
14
+ * Caches permissions in request-scoped SecurityContext
15
+ */
16
+ export class PermissionPreloaderMiddleware extends BaseMiddleware {
17
+ securityContext;
18
+ async handler(req, res, next) {
19
+ try {
20
+ // Get HttpContext
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ const httpContext = req.__expressotsHttpContext;
23
+ if (!httpContext) {
24
+ return next();
25
+ }
26
+ const principal = httpContext.user;
27
+ // Preload permissions if user is authenticated
28
+ if (principal && (await principal.isAuthenticated())) {
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ const userId = principal.details?.id;
31
+ if (userId && this.securityContext) {
32
+ await this.securityContext.preload(userId);
33
+ }
34
+ }
35
+ next();
36
+ }
37
+ catch (error) {
38
+ next(error);
39
+ }
40
+ }
41
+ }
42
+ __decorate([
43
+ inject("ISecurityContext"),
44
+ __metadata("design:type", Object)
45
+ ], PermissionPreloaderMiddleware.prototype, "securityContext", void 0);
@@ -0,0 +1,30 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Logger } from "@expressots/core";
3
+ /**
4
+ * Resolve package from the current working directory.
5
+ * @param packageName
6
+ * @param options
7
+ * @returns
8
+ */
9
+ export function packageResolver(packageName) {
10
+ const logger = new Logger();
11
+ try {
12
+ const hasPackage = require.resolve(packageName, {
13
+ paths: [process.cwd()],
14
+ });
15
+ if (hasPackage) {
16
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
17
+ const packageResolved = require(hasPackage);
18
+ if (typeof packageResolved === "function") {
19
+ return packageResolved;
20
+ }
21
+ if (packageResolved.default && typeof packageResolved.default === "function") {
22
+ return packageResolved.default;
23
+ }
24
+ return packageResolved;
25
+ }
26
+ }
27
+ catch (error) {
28
+ logger.warn(`Package [${packageName}] not installed. Please install it using your package manager.`, "package-resolver");
29
+ }
30
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Route parameter patterns for common use cases.
3
+ *
4
+ * Express 5 / path-to-regexp v8 dropped the inline-regex form
5
+ * (`:id(\\d+)`), so the framework no longer hands these patterns to
6
+ * the underlying matcher verbatim. Instead, the HTTP-method decorators
7
+ * (`@Get`, `@Post`, …) parse the constraint out of the path at decorator
8
+ * time, register the route under a plain `:id` placeholder, and inject
9
+ * a small validator middleware that 404s when the captured value
10
+ * doesn't match. The user-facing semantics are unchanged: a path that
11
+ * uses `Patterns.NUMERIC_ID` still rejects `/users/abc` and only
12
+ * dispatches the handler for matches like `/users/123`.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { Patterns, pattern } from "@expressots/adapter-express";
17
+ *
18
+ * @Get(`/users/${pattern("id", Patterns.NUMERIC_ID)}`)
19
+ * getUserById(@param("id") id: number) {
20
+ * // Only dispatches for numeric IDs like /users/123
21
+ * }
22
+ *
23
+ * @Get(`/documents/${pattern("uuid", Patterns.UUID)}`)
24
+ * getDocument(@param("uuid") uuid: string) {
25
+ * // Only dispatches for valid UUIDs
26
+ * }
27
+ * ```
28
+ *
29
+ * @public API
30
+ */
31
+ export const Patterns = {
32
+ /**
33
+ * Matches one or more digits (numeric ID)
34
+ * Example: /users/123 ✅, /users/abc ❌
35
+ */
36
+ NUMERIC_ID: "(\\d+)",
37
+ /**
38
+ * Matches a valid UUID v4 format
39
+ * Example: /documents/550e8400-e29b-41d4-a716-446655440000 ✅
40
+ */
41
+ UUID: "([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})",
42
+ /**
43
+ * Matches lowercase letters, numbers, and hyphens (URL-friendly slug)
44
+ * Example: /posts/my-awesome-post ✅, /posts/My_Post ❌
45
+ */
46
+ SLUG: "([a-z0-9-]+)",
47
+ /**
48
+ * Matches alphanumeric characters (letters and numbers only)
49
+ * Example: /codes/ABC123 ✅, /codes/ABC-123 ❌
50
+ */
51
+ ALPHANUMERIC: "([a-zA-Z0-9]+)",
52
+ /**
53
+ * Matches lowercase letters only
54
+ * Example: /tags/javascript ✅, /tags/JavaScript ❌
55
+ */
56
+ LOWERCASE: "([a-z]+)",
57
+ /**
58
+ * Matches uppercase letters only
59
+ * Example: /codes/USD ✅, /codes/usd ❌
60
+ */
61
+ UPPERCASE: "([A-Z]+)",
62
+ /**
63
+ * Matches email format (basic validation)
64
+ * Example: /users/user@example.com ✅
65
+ */
66
+ EMAIL: "([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})",
67
+ /**
68
+ * Matches hexadecimal string (e.g., color codes, hash)
69
+ * Example: /colors/ff5733 ✅, /colors/xyz ❌
70
+ */
71
+ HEXADECIMAL: "([0-9a-fA-F]+)",
72
+ /**
73
+ * Matches MongoDB ObjectId format (24 hex characters)
74
+ * Example: /documents/507f1f77bcf86cd799439011 ✅
75
+ */
76
+ MONGO_ID: "([0-9a-fA-F]{24})",
77
+ };
78
+ /**
79
+ * Helper function to build route parameter patterns.
80
+ * This is optional - you can also use Patterns directly in template strings.
81
+ *
82
+ * @param paramName - The parameter name (e.g., "id", "uuid")
83
+ * @param pattern - The pattern from Patterns
84
+ * @returns The formatted route parameter with pattern
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * import { pattern, Patterns } from "@expressots/adapter-express";
89
+ *
90
+ * @Get(`/users/${pattern("id", Patterns.NUMERIC_ID)}`)
91
+ * getUserById(@param("id") id: number) {
92
+ * // Route: /users/:id(\\d+)
93
+ * }
94
+ * ```
95
+ *
96
+ * @public API
97
+ */
98
+ export function pattern(paramName, patternValue) {
99
+ return `:${paramName}(${patternValue})`;
100
+ }