@morojs/moro 1.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 (345) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/dist/core/config/index.d.ts +19 -0
  4. package/dist/core/config/index.js +59 -0
  5. package/dist/core/config/index.js.map +1 -0
  6. package/dist/core/config/loader.d.ts +6 -0
  7. package/dist/core/config/loader.js +288 -0
  8. package/dist/core/config/loader.js.map +1 -0
  9. package/dist/core/config/schema.d.ts +335 -0
  10. package/dist/core/config/schema.js +286 -0
  11. package/dist/core/config/schema.js.map +1 -0
  12. package/dist/core/config/utils.d.ts +50 -0
  13. package/dist/core/config/utils.js +185 -0
  14. package/dist/core/config/utils.js.map +1 -0
  15. package/dist/core/database/adapters/drizzle.d.ts +29 -0
  16. package/dist/core/database/adapters/drizzle.js +366 -0
  17. package/dist/core/database/adapters/drizzle.js.map +1 -0
  18. package/dist/core/database/adapters/index.d.ts +8 -0
  19. package/dist/core/database/adapters/index.js +48 -0
  20. package/dist/core/database/adapters/index.js.map +1 -0
  21. package/dist/core/database/adapters/mongodb.d.ts +35 -0
  22. package/dist/core/database/adapters/mongodb.js +215 -0
  23. package/dist/core/database/adapters/mongodb.js.map +1 -0
  24. package/dist/core/database/adapters/mysql.d.ts +23 -0
  25. package/dist/core/database/adapters/mysql.js +149 -0
  26. package/dist/core/database/adapters/mysql.js.map +1 -0
  27. package/dist/core/database/adapters/postgresql.d.ts +24 -0
  28. package/dist/core/database/adapters/postgresql.js +160 -0
  29. package/dist/core/database/adapters/postgresql.js.map +1 -0
  30. package/dist/core/database/adapters/redis.d.ts +50 -0
  31. package/dist/core/database/adapters/redis.js +266 -0
  32. package/dist/core/database/adapters/redis.js.map +1 -0
  33. package/dist/core/database/adapters/sqlite.d.ts +23 -0
  34. package/dist/core/database/adapters/sqlite.js +194 -0
  35. package/dist/core/database/adapters/sqlite.js.map +1 -0
  36. package/dist/core/database/index.d.ts +2 -0
  37. package/dist/core/database/index.js +20 -0
  38. package/dist/core/database/index.js.map +1 -0
  39. package/dist/core/docs/index.d.ts +63 -0
  40. package/dist/core/docs/index.js +170 -0
  41. package/dist/core/docs/index.js.map +1 -0
  42. package/dist/core/docs/openapi-generator.d.ts +124 -0
  43. package/dist/core/docs/openapi-generator.js +413 -0
  44. package/dist/core/docs/openapi-generator.js.map +1 -0
  45. package/dist/core/docs/simple-docs.d.ts +21 -0
  46. package/dist/core/docs/simple-docs.js +268 -0
  47. package/dist/core/docs/simple-docs.js.map +1 -0
  48. package/dist/core/docs/swagger-ui.d.ts +28 -0
  49. package/dist/core/docs/swagger-ui.js +317 -0
  50. package/dist/core/docs/swagger-ui.js.map +1 -0
  51. package/dist/core/docs/zod-to-openapi.d.ts +29 -0
  52. package/dist/core/docs/zod-to-openapi.js +414 -0
  53. package/dist/core/docs/zod-to-openapi.js.map +1 -0
  54. package/dist/core/events/event-bus.d.ts +27 -0
  55. package/dist/core/events/event-bus.js +193 -0
  56. package/dist/core/events/event-bus.js.map +1 -0
  57. package/dist/core/events/index.d.ts +2 -0
  58. package/dist/core/events/index.js +7 -0
  59. package/dist/core/events/index.js.map +1 -0
  60. package/dist/core/framework.d.ts +57 -0
  61. package/dist/core/framework.js +432 -0
  62. package/dist/core/framework.js.map +1 -0
  63. package/dist/core/http/http-server.d.ts +114 -0
  64. package/dist/core/http/http-server.js +1154 -0
  65. package/dist/core/http/http-server.js.map +1 -0
  66. package/dist/core/http/index.d.ts +3 -0
  67. package/dist/core/http/index.js +10 -0
  68. package/dist/core/http/index.js.map +1 -0
  69. package/dist/core/http/router.d.ts +14 -0
  70. package/dist/core/http/router.js +113 -0
  71. package/dist/core/http/router.js.map +1 -0
  72. package/dist/core/logger/filters.d.ts +9 -0
  73. package/dist/core/logger/filters.js +134 -0
  74. package/dist/core/logger/filters.js.map +1 -0
  75. package/dist/core/logger/index.d.ts +3 -0
  76. package/dist/core/logger/index.js +26 -0
  77. package/dist/core/logger/index.js.map +1 -0
  78. package/dist/core/logger/logger.d.ts +49 -0
  79. package/dist/core/logger/logger.js +332 -0
  80. package/dist/core/logger/logger.js.map +1 -0
  81. package/dist/core/logger/outputs.d.ts +42 -0
  82. package/dist/core/logger/outputs.js +110 -0
  83. package/dist/core/logger/outputs.js.map +1 -0
  84. package/dist/core/middleware/built-in/adapters/cache/file.d.ts +15 -0
  85. package/dist/core/middleware/built-in/adapters/cache/file.js +128 -0
  86. package/dist/core/middleware/built-in/adapters/cache/file.js.map +1 -0
  87. package/dist/core/middleware/built-in/adapters/cache/index.d.ts +5 -0
  88. package/dist/core/middleware/built-in/adapters/cache/index.js +28 -0
  89. package/dist/core/middleware/built-in/adapters/cache/index.js.map +1 -0
  90. package/dist/core/middleware/built-in/adapters/cache/memory.d.ts +11 -0
  91. package/dist/core/middleware/built-in/adapters/cache/memory.js +65 -0
  92. package/dist/core/middleware/built-in/adapters/cache/memory.js.map +1 -0
  93. package/dist/core/middleware/built-in/adapters/cache/redis.d.ts +17 -0
  94. package/dist/core/middleware/built-in/adapters/cache/redis.js +91 -0
  95. package/dist/core/middleware/built-in/adapters/cache/redis.js.map +1 -0
  96. package/dist/core/middleware/built-in/adapters/cdn/azure.d.ts +21 -0
  97. package/dist/core/middleware/built-in/adapters/cdn/azure.js +40 -0
  98. package/dist/core/middleware/built-in/adapters/cdn/azure.js.map +1 -0
  99. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.d.ts +14 -0
  100. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js +77 -0
  101. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js.map +1 -0
  102. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.d.ts +15 -0
  103. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js +73 -0
  104. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js.map +1 -0
  105. package/dist/core/middleware/built-in/adapters/cdn/index.d.ts +5 -0
  106. package/dist/core/middleware/built-in/adapters/cdn/index.js +28 -0
  107. package/dist/core/middleware/built-in/adapters/cdn/index.js.map +1 -0
  108. package/dist/core/middleware/built-in/adapters/index.d.ts +4 -0
  109. package/dist/core/middleware/built-in/adapters/index.js +26 -0
  110. package/dist/core/middleware/built-in/adapters/index.js.map +1 -0
  111. package/dist/core/middleware/built-in/auth.d.ts +2 -0
  112. package/dist/core/middleware/built-in/auth.js +38 -0
  113. package/dist/core/middleware/built-in/auth.js.map +1 -0
  114. package/dist/core/middleware/built-in/cache.d.ts +3 -0
  115. package/dist/core/middleware/built-in/cache.js +188 -0
  116. package/dist/core/middleware/built-in/cache.js.map +1 -0
  117. package/dist/core/middleware/built-in/cdn.d.ts +3 -0
  118. package/dist/core/middleware/built-in/cdn.js +115 -0
  119. package/dist/core/middleware/built-in/cdn.js.map +1 -0
  120. package/dist/core/middleware/built-in/cookie.d.ts +14 -0
  121. package/dist/core/middleware/built-in/cookie.js +68 -0
  122. package/dist/core/middleware/built-in/cookie.js.map +1 -0
  123. package/dist/core/middleware/built-in/cors.d.ts +2 -0
  124. package/dist/core/middleware/built-in/cors.js +29 -0
  125. package/dist/core/middleware/built-in/cors.js.map +1 -0
  126. package/dist/core/middleware/built-in/csp.d.ts +22 -0
  127. package/dist/core/middleware/built-in/csp.js +74 -0
  128. package/dist/core/middleware/built-in/csp.js.map +1 -0
  129. package/dist/core/middleware/built-in/csrf.d.ts +9 -0
  130. package/dist/core/middleware/built-in/csrf.js +66 -0
  131. package/dist/core/middleware/built-in/csrf.js.map +1 -0
  132. package/dist/core/middleware/built-in/error-tracker.d.ts +1 -0
  133. package/dist/core/middleware/built-in/error-tracker.js +19 -0
  134. package/dist/core/middleware/built-in/error-tracker.js.map +1 -0
  135. package/dist/core/middleware/built-in/index.d.ts +70 -0
  136. package/dist/core/middleware/built-in/index.js +70 -0
  137. package/dist/core/middleware/built-in/index.js.map +1 -0
  138. package/dist/core/middleware/built-in/performance-monitor.d.ts +1 -0
  139. package/dist/core/middleware/built-in/performance-monitor.js +22 -0
  140. package/dist/core/middleware/built-in/performance-monitor.js.map +1 -0
  141. package/dist/core/middleware/built-in/rate-limit.d.ts +6 -0
  142. package/dist/core/middleware/built-in/rate-limit.js +47 -0
  143. package/dist/core/middleware/built-in/rate-limit.js.map +1 -0
  144. package/dist/core/middleware/built-in/request-logger.d.ts +1 -0
  145. package/dist/core/middleware/built-in/request-logger.js +15 -0
  146. package/dist/core/middleware/built-in/request-logger.js.map +1 -0
  147. package/dist/core/middleware/built-in/session.d.ts +41 -0
  148. package/dist/core/middleware/built-in/session.js +209 -0
  149. package/dist/core/middleware/built-in/session.js.map +1 -0
  150. package/dist/core/middleware/built-in/sse.d.ts +6 -0
  151. package/dist/core/middleware/built-in/sse.js +73 -0
  152. package/dist/core/middleware/built-in/sse.js.map +1 -0
  153. package/dist/core/middleware/built-in/validation.d.ts +2 -0
  154. package/dist/core/middleware/built-in/validation.js +31 -0
  155. package/dist/core/middleware/built-in/validation.js.map +1 -0
  156. package/dist/core/middleware/index.d.ts +21 -0
  157. package/dist/core/middleware/index.js +152 -0
  158. package/dist/core/middleware/index.js.map +1 -0
  159. package/dist/core/modules/auto-discovery.d.ts +27 -0
  160. package/dist/core/modules/auto-discovery.js +255 -0
  161. package/dist/core/modules/auto-discovery.js.map +1 -0
  162. package/dist/core/modules/index.d.ts +2 -0
  163. package/dist/core/modules/index.js +11 -0
  164. package/dist/core/modules/index.js.map +1 -0
  165. package/dist/core/modules/modules.d.ts +10 -0
  166. package/dist/core/modules/modules.js +137 -0
  167. package/dist/core/modules/modules.js.map +1 -0
  168. package/dist/core/networking/index.d.ts +2 -0
  169. package/dist/core/networking/index.js +9 -0
  170. package/dist/core/networking/index.js.map +1 -0
  171. package/dist/core/networking/service-discovery.d.ts +38 -0
  172. package/dist/core/networking/service-discovery.js +233 -0
  173. package/dist/core/networking/service-discovery.js.map +1 -0
  174. package/dist/core/networking/websocket-manager.d.ts +27 -0
  175. package/dist/core/networking/websocket-manager.js +211 -0
  176. package/dist/core/networking/websocket-manager.js.map +1 -0
  177. package/dist/core/routing/app-integration.d.ts +42 -0
  178. package/dist/core/routing/app-integration.js +152 -0
  179. package/dist/core/routing/app-integration.js.map +1 -0
  180. package/dist/core/routing/index.d.ts +106 -0
  181. package/dist/core/routing/index.js +343 -0
  182. package/dist/core/routing/index.js.map +1 -0
  183. package/dist/core/runtime/aws-lambda-adapter.d.ts +43 -0
  184. package/dist/core/runtime/aws-lambda-adapter.js +108 -0
  185. package/dist/core/runtime/aws-lambda-adapter.js.map +1 -0
  186. package/dist/core/runtime/base-adapter.d.ts +16 -0
  187. package/dist/core/runtime/base-adapter.js +105 -0
  188. package/dist/core/runtime/base-adapter.js.map +1 -0
  189. package/dist/core/runtime/cloudflare-workers-adapter.d.ts +18 -0
  190. package/dist/core/runtime/cloudflare-workers-adapter.js +131 -0
  191. package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -0
  192. package/dist/core/runtime/index.d.ts +14 -0
  193. package/dist/core/runtime/index.js +56 -0
  194. package/dist/core/runtime/index.js.map +1 -0
  195. package/dist/core/runtime/node-adapter.d.ts +15 -0
  196. package/dist/core/runtime/node-adapter.js +204 -0
  197. package/dist/core/runtime/node-adapter.js.map +1 -0
  198. package/dist/core/runtime/vercel-edge-adapter.d.ts +10 -0
  199. package/dist/core/runtime/vercel-edge-adapter.js +106 -0
  200. package/dist/core/runtime/vercel-edge-adapter.js.map +1 -0
  201. package/dist/core/utilities/circuit-breaker.d.ts +14 -0
  202. package/dist/core/utilities/circuit-breaker.js +42 -0
  203. package/dist/core/utilities/circuit-breaker.js.map +1 -0
  204. package/dist/core/utilities/container.d.ts +116 -0
  205. package/dist/core/utilities/container.js +529 -0
  206. package/dist/core/utilities/container.js.map +1 -0
  207. package/dist/core/utilities/hooks.d.ts +24 -0
  208. package/dist/core/utilities/hooks.js +131 -0
  209. package/dist/core/utilities/hooks.js.map +1 -0
  210. package/dist/core/utilities/index.d.ts +4 -0
  211. package/dist/core/utilities/index.js +22 -0
  212. package/dist/core/utilities/index.js.map +1 -0
  213. package/dist/core/validation/index.d.ts +30 -0
  214. package/dist/core/validation/index.js +144 -0
  215. package/dist/core/validation/index.js.map +1 -0
  216. package/dist/index.d.ts +30 -0
  217. package/dist/index.js +72 -0
  218. package/dist/index.js.map +1 -0
  219. package/dist/moro.d.ts +82 -0
  220. package/dist/moro.js +679 -0
  221. package/dist/moro.js.map +1 -0
  222. package/dist/types/cache.d.ts +34 -0
  223. package/dist/types/cache.js +3 -0
  224. package/dist/types/cache.js.map +1 -0
  225. package/dist/types/cdn.d.ts +19 -0
  226. package/dist/types/cdn.js +3 -0
  227. package/dist/types/cdn.js.map +1 -0
  228. package/dist/types/core.d.ts +13 -0
  229. package/dist/types/core.js +3 -0
  230. package/dist/types/core.js.map +1 -0
  231. package/dist/types/database.d.ts +29 -0
  232. package/dist/types/database.js +3 -0
  233. package/dist/types/database.js.map +1 -0
  234. package/dist/types/discovery.d.ts +6 -0
  235. package/dist/types/discovery.js +3 -0
  236. package/dist/types/discovery.js.map +1 -0
  237. package/dist/types/events.d.ts +116 -0
  238. package/dist/types/events.js +3 -0
  239. package/dist/types/events.js.map +1 -0
  240. package/dist/types/hooks.d.ts +38 -0
  241. package/dist/types/hooks.js +3 -0
  242. package/dist/types/hooks.js.map +1 -0
  243. package/dist/types/http.d.ts +51 -0
  244. package/dist/types/http.js +3 -0
  245. package/dist/types/http.js.map +1 -0
  246. package/dist/types/logger.d.ts +77 -0
  247. package/dist/types/logger.js +3 -0
  248. package/dist/types/logger.js.map +1 -0
  249. package/dist/types/module.d.ts +91 -0
  250. package/dist/types/module.js +3 -0
  251. package/dist/types/module.js.map +1 -0
  252. package/dist/types/runtime.d.ts +48 -0
  253. package/dist/types/runtime.js +3 -0
  254. package/dist/types/runtime.js.map +1 -0
  255. package/dist/types/session.d.ts +66 -0
  256. package/dist/types/session.js +3 -0
  257. package/dist/types/session.js.map +1 -0
  258. package/package.json +176 -0
  259. package/src/core/config/index.ts +47 -0
  260. package/src/core/config/loader.ts +366 -0
  261. package/src/core/config/schema.ts +346 -0
  262. package/src/core/config/utils.ts +220 -0
  263. package/src/core/database/README.md +228 -0
  264. package/src/core/database/adapters/drizzle.ts +425 -0
  265. package/src/core/database/adapters/index.ts +45 -0
  266. package/src/core/database/adapters/mongodb.ts +292 -0
  267. package/src/core/database/adapters/mysql.ts +217 -0
  268. package/src/core/database/adapters/postgresql.ts +211 -0
  269. package/src/core/database/adapters/redis.ts +331 -0
  270. package/src/core/database/adapters/sqlite.ts +255 -0
  271. package/src/core/database/index.ts +3 -0
  272. package/src/core/docs/index.ts +245 -0
  273. package/src/core/docs/openapi-generator.ts +588 -0
  274. package/src/core/docs/simple-docs.ts +305 -0
  275. package/src/core/docs/swagger-ui.ts +370 -0
  276. package/src/core/docs/zod-to-openapi.ts +532 -0
  277. package/src/core/events/event-bus.ts +249 -0
  278. package/src/core/events/index.ts +12 -0
  279. package/src/core/framework.ts +621 -0
  280. package/src/core/http/http-server.ts +1421 -0
  281. package/src/core/http/index.ts +11 -0
  282. package/src/core/http/router.ts +153 -0
  283. package/src/core/logger/filters.ts +148 -0
  284. package/src/core/logger/index.ts +20 -0
  285. package/src/core/logger/logger.ts +434 -0
  286. package/src/core/logger/outputs.ts +136 -0
  287. package/src/core/middleware/built-in/adapters/cache/file.ts +106 -0
  288. package/src/core/middleware/built-in/adapters/cache/index.ts +26 -0
  289. package/src/core/middleware/built-in/adapters/cache/memory.ts +73 -0
  290. package/src/core/middleware/built-in/adapters/cache/redis.ts +103 -0
  291. package/src/core/middleware/built-in/adapters/cdn/azure.ts +68 -0
  292. package/src/core/middleware/built-in/adapters/cdn/cloudflare.ts +100 -0
  293. package/src/core/middleware/built-in/adapters/cdn/cloudfront.ts +92 -0
  294. package/src/core/middleware/built-in/adapters/cdn/index.ts +23 -0
  295. package/src/core/middleware/built-in/adapters/index.ts +7 -0
  296. package/src/core/middleware/built-in/auth.ts +39 -0
  297. package/src/core/middleware/built-in/cache.ts +228 -0
  298. package/src/core/middleware/built-in/cdn.ts +151 -0
  299. package/src/core/middleware/built-in/cookie.ts +90 -0
  300. package/src/core/middleware/built-in/cors.ts +38 -0
  301. package/src/core/middleware/built-in/csp.ts +107 -0
  302. package/src/core/middleware/built-in/csrf.ts +87 -0
  303. package/src/core/middleware/built-in/error-tracker.ts +16 -0
  304. package/src/core/middleware/built-in/index.ts +57 -0
  305. package/src/core/middleware/built-in/performance-monitor.ts +25 -0
  306. package/src/core/middleware/built-in/rate-limit.ts +60 -0
  307. package/src/core/middleware/built-in/request-logger.ts +14 -0
  308. package/src/core/middleware/built-in/session.ts +311 -0
  309. package/src/core/middleware/built-in/sse.ts +91 -0
  310. package/src/core/middleware/built-in/validation.ts +33 -0
  311. package/src/core/middleware/index.ts +188 -0
  312. package/src/core/modules/auto-discovery.ts +265 -0
  313. package/src/core/modules/index.ts +6 -0
  314. package/src/core/modules/modules.ts +125 -0
  315. package/src/core/networking/index.ts +7 -0
  316. package/src/core/networking/service-discovery.ts +309 -0
  317. package/src/core/networking/websocket-manager.ts +259 -0
  318. package/src/core/routing/app-integration.ts +229 -0
  319. package/src/core/routing/index.ts +519 -0
  320. package/src/core/runtime/aws-lambda-adapter.ts +157 -0
  321. package/src/core/runtime/base-adapter.ts +140 -0
  322. package/src/core/runtime/cloudflare-workers-adapter.ts +166 -0
  323. package/src/core/runtime/index.ts +74 -0
  324. package/src/core/runtime/node-adapter.ts +210 -0
  325. package/src/core/runtime/vercel-edge-adapter.ts +125 -0
  326. package/src/core/utilities/circuit-breaker.ts +46 -0
  327. package/src/core/utilities/container.ts +760 -0
  328. package/src/core/utilities/hooks.ts +148 -0
  329. package/src/core/utilities/index.ts +16 -0
  330. package/src/core/validation/index.ts +216 -0
  331. package/src/index.ts +120 -0
  332. package/src/moro.ts +842 -0
  333. package/src/types/cache.ts +38 -0
  334. package/src/types/cdn.ts +22 -0
  335. package/src/types/core.ts +17 -0
  336. package/src/types/database.ts +40 -0
  337. package/src/types/discovery.ts +7 -0
  338. package/src/types/events.ts +90 -0
  339. package/src/types/hooks.ts +47 -0
  340. package/src/types/http.ts +70 -0
  341. package/src/types/logger.ts +109 -0
  342. package/src/types/module.ts +87 -0
  343. package/src/types/runtime.ts +91 -0
  344. package/src/types/session.ts +89 -0
  345. package/tsconfig.json +21 -0
@@ -0,0 +1,305 @@
1
+ // Simple Documentation Generator - Fallback for when Swagger UI has issues
2
+ // Generates clean, readable API documentation from routes
3
+
4
+ import { CompiledRoute, RouteSchema } from "../routing";
5
+ import { createFrameworkLogger } from "../logger";
6
+
7
+ const logger = createFrameworkLogger("SimpleDocs");
8
+
9
+ export interface SimpleDocsOptions {
10
+ title?: string;
11
+ description?: string;
12
+ basePath?: string;
13
+ }
14
+
15
+ export class SimpleDocsGenerator {
16
+ private routes: CompiledRoute[] = [];
17
+
18
+ constructor(private options: SimpleDocsOptions = {}) {
19
+ this.options = {
20
+ title: "API Documentation",
21
+ description: "API documentation generated from intelligent routes",
22
+ basePath: "/docs",
23
+ ...options,
24
+ };
25
+ }
26
+
27
+ addRoutes(routes: CompiledRoute[]): void {
28
+ this.routes = routes;
29
+ logger.debug(
30
+ `Added ${routes.length} routes to simple docs`,
31
+ "RouteAddition",
32
+ );
33
+ }
34
+
35
+ generateHTML(): string {
36
+ const routesByTag = this.groupRoutesByTag();
37
+
38
+ return `<!DOCTYPE html>
39
+ <html lang="en">
40
+ <head>
41
+ <meta charset="UTF-8">
42
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
43
+ <title>${this.options.title}</title>
44
+ <style>
45
+ body {
46
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
47
+ line-height: 1.6;
48
+ margin: 0;
49
+ padding: 20px;
50
+ background: #f8f9fa;
51
+ }
52
+ .container {
53
+ max-width: 1200px;
54
+ margin: 0 auto;
55
+ background: white;
56
+ padding: 30px;
57
+ border-radius: 8px;
58
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
59
+ }
60
+ h1 { color: #2563eb; margin-bottom: 10px; }
61
+ h2 { color: #374151; border-bottom: 2px solid #e5e7eb; padding-bottom: 10px; }
62
+ h3 { color: #6b7280; }
63
+ .endpoint {
64
+ background: #f3f4f6;
65
+ border-left: 4px solid #2563eb;
66
+ padding: 15px;
67
+ margin: 15px 0;
68
+ border-radius: 4px;
69
+ }
70
+ .method {
71
+ display: inline-block;
72
+ padding: 4px 8px;
73
+ border-radius: 4px;
74
+ font-weight: bold;
75
+ margin-right: 10px;
76
+ color: white;
77
+ }
78
+ .method.GET { background: #10b981; }
79
+ .method.POST { background: #f59e0b; }
80
+ .method.PUT { background: #3b82f6; }
81
+ .method.DELETE { background: #ef4444; }
82
+ .method.PATCH { background: #8b5cf6; }
83
+ .path { font-family: monospace; font-size: 16px; font-weight: bold; }
84
+ .description { color: #6b7280; margin: 8px 0; }
85
+ .tags { margin: 8px 0; }
86
+ .tag {
87
+ display: inline-block;
88
+ background: #e5e7eb;
89
+ color: #374151;
90
+ padding: 2px 8px;
91
+ border-radius: 12px;
92
+ font-size: 12px;
93
+ margin-right: 5px;
94
+ }
95
+ .validation {
96
+ background: #fef3c7;
97
+ border: 1px solid #f59e0b;
98
+ padding: 10px;
99
+ border-radius: 4px;
100
+ margin: 10px 0;
101
+ }
102
+ .auth {
103
+ background: #fee2e2;
104
+ border: 1px solid #ef4444;
105
+ padding: 10px;
106
+ border-radius: 4px;
107
+ margin: 10px 0;
108
+ }
109
+ .rate-limit {
110
+ background: #e0f2fe;
111
+ border: 1px solid #0284c7;
112
+ padding: 10px;
113
+ border-radius: 4px;
114
+ margin: 10px 0;
115
+ }
116
+ .example {
117
+ background: #f3f4f6;
118
+ border: 1px solid #d1d5db;
119
+ padding: 10px;
120
+ border-radius: 4px;
121
+ margin: 10px 0;
122
+ font-family: monospace;
123
+ font-size: 14px;
124
+ }
125
+ .footer {
126
+ margin-top: 40px;
127
+ padding-top: 20px;
128
+ border-top: 1px solid #e5e7eb;
129
+ text-align: center;
130
+ color: #6b7280;
131
+ }
132
+ </style>
133
+ </head>
134
+ <body>
135
+ <div class="container">
136
+ <h1>${this.options.title}</h1>
137
+ <p>${this.options.description}</p>
138
+
139
+ <div class="example">
140
+ <strong>Interactive Swagger UI:</strong> <a href="${this.options.basePath}" target="_blank">${this.options.basePath}</a><br>
141
+ <strong>OpenAPI JSON:</strong> <a href="${this.options.basePath}/openapi.json" target="_blank">${this.options.basePath}/openapi.json</a>
142
+ </div>
143
+
144
+ ${this.generateRouteDocumentation(routesByTag)}
145
+
146
+ <div class="footer">
147
+ <p>Generated automatically from Moro Framework intelligent routes</p>
148
+ <p>Built with Moro Framework - Intelligent Routing + Type-Safe Validation</p>
149
+ </div>
150
+ </div>
151
+ </body>
152
+ </html>`;
153
+ }
154
+
155
+ private groupRoutesByTag(): Map<string, CompiledRoute[]> {
156
+ const grouped = new Map<string, CompiledRoute[]>();
157
+
158
+ for (const route of this.routes) {
159
+ const tags = route.schema.tags || ["default"];
160
+
161
+ for (const tag of tags) {
162
+ if (!grouped.has(tag)) {
163
+ grouped.set(tag, []);
164
+ }
165
+ grouped.get(tag)!.push(route);
166
+ }
167
+ }
168
+
169
+ return grouped;
170
+ }
171
+
172
+ private generateRouteDocumentation(
173
+ routesByTag: Map<string, CompiledRoute[]>,
174
+ ): string {
175
+ let html = "";
176
+
177
+ for (const [tag, routes] of routesByTag) {
178
+ html += `<h2>${tag.charAt(0).toUpperCase() + tag.slice(1)}</h2>`;
179
+
180
+ for (const route of routes) {
181
+ html += this.generateRouteSection(route.schema);
182
+ }
183
+ }
184
+
185
+ return html;
186
+ }
187
+
188
+ private generateRouteSection(route: RouteSchema): string {
189
+ const methodClass = route.method.toLowerCase();
190
+
191
+ const html = `
192
+ <div class="endpoint">
193
+ <div>
194
+ <span class="method ${route.method}">${route.method}</span>
195
+ <span class="path">${route.path}</span>
196
+ </div>
197
+
198
+ ${route.description ? `<div class="description">${route.description}</div>` : ""}
199
+
200
+ ${route.tags ? `<div class="tags">${route.tags.map((tag) => `<span class="tag">${tag}</span>`).join("")}</div>` : ""}
201
+
202
+ ${this.generateValidationInfo(route)}
203
+ ${this.generateAuthInfo(route)}
204
+ ${this.generateRateLimitInfo(route)}
205
+ ${this.generateExamples(route)}
206
+ </div>`;
207
+
208
+ return html;
209
+ }
210
+
211
+ private generateValidationInfo(route: RouteSchema): string {
212
+ if (!route.validation) return "";
213
+
214
+ const validationTypes = [];
215
+ if (route.validation.body) validationTypes.push("Body");
216
+ if (route.validation.query) validationTypes.push("Query Parameters");
217
+ if (route.validation.params) validationTypes.push("Path Parameters");
218
+ if (route.validation.headers) validationTypes.push("Headers");
219
+
220
+ return `
221
+ <div class="validation">
222
+ <strong>Validation:</strong> ${validationTypes.join(", ")}
223
+ <br><small>Request will be validated with Zod schemas for type safety</small>
224
+ </div>`;
225
+ }
226
+
227
+ private generateAuthInfo(route: RouteSchema): string {
228
+ if (!route.auth) return "";
229
+
230
+ const roles = route.auth.roles
231
+ ? route.auth.roles.join(", ")
232
+ : "authenticated";
233
+
234
+ return `
235
+ <div class="auth">
236
+ <strong>Authentication Required:</strong> ${roles}
237
+ <br><small>Requires valid authentication token</small>
238
+ </div>`;
239
+ }
240
+
241
+ private generateRateLimitInfo(route: RouteSchema): string {
242
+ if (!route.rateLimit) return "";
243
+
244
+ const { requests, window } = route.rateLimit;
245
+ const windowSeconds = Math.round(window / 1000);
246
+
247
+ return `
248
+ <div class="rate-limit">
249
+ <strong>Rate Limit:</strong> ${requests} requests per ${windowSeconds} seconds
250
+ <br><small>Automatic protection against abuse</small>
251
+ </div>`;
252
+ }
253
+
254
+ private generateExamples(route: RouteSchema): string {
255
+ const baseUrl = "http://localhost:3001";
256
+ const fullPath = `${baseUrl}${route.path}`;
257
+
258
+ let example = "";
259
+
260
+ if (route.method === "GET") {
261
+ example = `curl "${fullPath}"`;
262
+
263
+ // Add query parameter example if validation exists
264
+ if (route.validation?.query) {
265
+ example = `curl "${fullPath}?limit=10&search=example"`;
266
+ }
267
+ } else if (["POST", "PUT", "PATCH"].includes(route.method)) {
268
+ example = `curl -X ${route.method} ${fullPath} \\
269
+ -H "Content-Type: application/json" \\
270
+ -d '{"example": "data"}'`;
271
+ } else {
272
+ example = `curl -X ${route.method} ${fullPath}`;
273
+ }
274
+
275
+ return `
276
+ <div class="example">
277
+ <strong>Example:</strong><br>
278
+ <code>${example}</code>
279
+ </div>`;
280
+ }
281
+ }
282
+
283
+ // Create middleware for simple docs
284
+ export function createSimpleDocsMiddleware(
285
+ routes: CompiledRoute[],
286
+ options: SimpleDocsOptions = {},
287
+ ) {
288
+ const generator = new SimpleDocsGenerator(options);
289
+ generator.addRoutes(routes);
290
+
291
+ const basePath = options.basePath || "/docs";
292
+
293
+ return (req: any, res: any, next: () => void) => {
294
+ if (
295
+ req.path === `${basePath}/simple` ||
296
+ req.path === `${basePath}/simple/`
297
+ ) {
298
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
299
+ res.send(generator.generateHTML());
300
+ return;
301
+ }
302
+
303
+ next();
304
+ };
305
+ }
@@ -0,0 +1,370 @@
1
+ // Swagger UI Integration for Moro Framework
2
+ // Serves interactive API documentation using Swagger UI
3
+
4
+ import { readFileSync } from "fs";
5
+ import { join } from "path";
6
+ import { HttpRequest, HttpResponse } from "../http";
7
+ import { OpenAPISpec } from "./openapi-generator";
8
+ import { createFrameworkLogger } from "../logger";
9
+
10
+ const logger = createFrameworkLogger("SwaggerUI");
11
+
12
+ // Swagger UI configuration options
13
+ export interface SwaggerUIOptions {
14
+ title?: string;
15
+ favicon?: string;
16
+ customCss?: string;
17
+ customJs?: string;
18
+ swaggerOptions?: Record<string, any>;
19
+ enableTryItOut?: boolean;
20
+ enableFilter?: boolean;
21
+ enableDeepLinking?: boolean;
22
+ }
23
+
24
+ // Swagger UI middleware
25
+ export class SwaggerUIMiddleware {
26
+ private swaggerUIAssetPath: string;
27
+ private openAPISpec: OpenAPISpec;
28
+ private options: SwaggerUIOptions;
29
+
30
+ constructor(openAPISpec: OpenAPISpec, options: SwaggerUIOptions = {}) {
31
+ this.openAPISpec = openAPISpec;
32
+ this.options = {
33
+ title: "API Documentation",
34
+ enableTryItOut: true,
35
+ enableFilter: true,
36
+ enableDeepLinking: true,
37
+ swaggerOptions: {
38
+ dom_id: "#swagger-ui",
39
+ presets: [
40
+ "SwaggerUIBundle.presets.apis",
41
+ "SwaggerUIBundle.presets.standalone",
42
+ ],
43
+ plugins: ["SwaggerUIBundle.plugins.DownloadUrl"],
44
+ layout: "StandaloneLayout",
45
+ },
46
+ ...options,
47
+ };
48
+
49
+ try {
50
+ // Find swagger-ui-dist assets
51
+ this.swaggerUIAssetPath = require
52
+ .resolve("swagger-ui-dist/package.json")
53
+ .replace("/package.json", "");
54
+ logger.debug("Swagger UI assets found", "Initialization", {
55
+ assetPath: this.swaggerUIAssetPath,
56
+ });
57
+ } catch (error) {
58
+ logger.error("Failed to locate Swagger UI assets", "Initialization", {
59
+ error: error instanceof Error ? error.message : String(error),
60
+ });
61
+ throw new Error(
62
+ "swagger-ui-dist package not found. Install with: npm install swagger-ui-dist",
63
+ );
64
+ }
65
+ }
66
+
67
+ // Generate HTML page for Swagger UI
68
+ private generateHTML(basePath: string): string {
69
+ const swaggerOptions = {
70
+ ...this.options.swaggerOptions,
71
+ url: `${basePath}/openapi.json`, // Relative URL to the OpenAPI spec
72
+ tryItOutEnabled: this.options.enableTryItOut,
73
+ filter: this.options.enableFilter,
74
+ deepLinking: this.options.enableDeepLinking,
75
+ };
76
+
77
+ return `<!DOCTYPE html>
78
+ <html lang="en">
79
+ <head>
80
+ <meta charset="UTF-8">
81
+ <title>${this.options.title}</title>
82
+ <link rel="stylesheet" type="text/css" href="${basePath}/swagger-ui.css" />
83
+ <style>
84
+ html {
85
+ box-sizing: border-box;
86
+ overflow: -moz-scrollbars-vertical;
87
+ overflow-y: scroll;
88
+ }
89
+ *, *:before, *:after {
90
+ box-sizing: inherit;
91
+ }
92
+ body {
93
+ margin:0;
94
+ background: #fafafa;
95
+ }
96
+ #loading-message {
97
+ padding: 20px;
98
+ text-align: center;
99
+ font-family: Arial, sans-serif;
100
+ color: #666;
101
+ }
102
+ #error-display {
103
+ display: none;
104
+ padding: 20px;
105
+ background: #ffebee;
106
+ border-left: 4px solid #f44336;
107
+ margin: 20px;
108
+ font-family: monospace;
109
+ color: #c62828;
110
+ }
111
+ ${this.options.customCss || ""}
112
+ </style>
113
+ ${this.options.favicon ? `<link rel="icon" type="image/png" href="${this.options.favicon}" sizes="32x32" />` : ""}
114
+ </head>
115
+ <body>
116
+ <div id="loading-message">
117
+ <h2>Loading Swagger UI...</h2>
118
+ <p>Please wait while the API documentation loads.</p>
119
+ </div>
120
+ <div id="swagger-ui"></div>
121
+ <div id="error-display">
122
+ <h3>Failed to Load Swagger UI</h3>
123
+ <div id="error-details"></div>
124
+ </div>
125
+
126
+ <script src="${basePath}/swagger-ui-bundle.js" charset="UTF-8"></script>
127
+ <script src="${basePath}/swagger-ui-standalone-preset.js" charset="UTF-8"></script>
128
+ <script>
129
+ console.log('Starting Swagger UI initialization...');
130
+
131
+ function showError(message, details) {
132
+ console.error('Swagger UI Error:', message, details);
133
+ document.getElementById('loading-message').style.display = 'none';
134
+ document.getElementById('error-display').style.display = 'block';
135
+ document.getElementById('error-details').innerHTML =
136
+ '<p><strong>Error:</strong> ' + message + '</p>' +
137
+ (details ? '<pre>' + JSON.stringify(details, null, 2) + '</pre>' : '');
138
+ }
139
+
140
+ function initializeSwaggerUI() {
141
+ console.log('Initializing Swagger UI...');
142
+
143
+ if (typeof SwaggerUIBundle === 'undefined') {
144
+ showError('SwaggerUIBundle not loaded', { SwaggerUIBundle: typeof SwaggerUIBundle });
145
+ return;
146
+ }
147
+
148
+ if (typeof SwaggerUIStandalonePreset === 'undefined') {
149
+ showError('SwaggerUIStandalonePreset not loaded', { SwaggerUIStandalonePreset: typeof SwaggerUIStandalonePreset });
150
+ return;
151
+ }
152
+
153
+ try {
154
+ console.log('Creating SwaggerUIBundle...');
155
+
156
+ const ui = SwaggerUIBundle({
157
+ url: '${basePath}/openapi.json',
158
+ dom_id: '#swagger-ui',
159
+ deepLinking: ${this.options.enableDeepLinking},
160
+ presets: [
161
+ SwaggerUIBundle.presets.apis,
162
+ SwaggerUIStandalonePreset
163
+ ],
164
+ plugins: [
165
+ SwaggerUIBundle.plugins.DownloadUrl
166
+ ],
167
+ layout: "StandaloneLayout",
168
+ tryItOutEnabled: ${this.options.enableTryItOut},
169
+ filter: ${this.options.enableFilter},
170
+ onComplete: function() {
171
+ console.log('Swagger UI loaded successfully');
172
+ document.getElementById('loading-message').style.display = 'none';
173
+ },
174
+ onFailure: function(error) {
175
+ console.error('Swagger UI failed to load:', error);
176
+ showError('Swagger UI initialization failed', error);
177
+ }
178
+ });
179
+
180
+ window.ui = ui;
181
+ console.log('SwaggerUIBundle created successfully');
182
+
183
+ // Hide loading message after timeout if onComplete doesn't fire
184
+ setTimeout(function() {
185
+ var loadingEl = document.getElementById('loading-message');
186
+ if (loadingEl && loadingEl.style.display !== 'none') {
187
+ console.log('Hiding loading message after timeout');
188
+ loadingEl.style.display = 'none';
189
+ }
190
+ }, 5000);
191
+
192
+ } catch (error) {
193
+ console.error('Error creating SwaggerUIBundle:', error);
194
+ showError('Failed to create SwaggerUIBundle', {
195
+ name: error.name,
196
+ message: error.message
197
+ });
198
+ }
199
+ }
200
+
201
+ // Initialize when DOM is ready and scripts are loaded
202
+ if (document.readyState === 'loading') {
203
+ document.addEventListener('DOMContentLoaded', function() {
204
+ setTimeout(initializeSwaggerUI, 100);
205
+ });
206
+ } else {
207
+ setTimeout(initializeSwaggerUI, 100);
208
+ }
209
+ </script>
210
+ </body>
211
+ </html>`;
212
+ }
213
+
214
+ // Create middleware function that serves Swagger UI
215
+ createMiddleware(basePath: string = "/docs") {
216
+ return (req: HttpRequest, res: HttpResponse, next: () => void) => {
217
+ const path = req.path;
218
+
219
+ logger.debug(`Docs middleware handling: ${path}`, "DocsMiddleware", {
220
+ basePath,
221
+ });
222
+
223
+ // Serve the main HTML page
224
+ if (path === basePath || path === `${basePath}/`) {
225
+ logger.debug("Serving Swagger UI HTML", "DocsServing");
226
+
227
+ // Set CSP headers to allow Swagger UI to work
228
+ res.setHeader(
229
+ "Content-Security-Policy",
230
+ "default-src 'self'; " +
231
+ "script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
232
+ "style-src 'self' 'unsafe-inline'; " +
233
+ "img-src 'self' data: https:; " +
234
+ "font-src 'self' data:; " +
235
+ "connect-src 'self'",
236
+ );
237
+
238
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
239
+ res.send(this.generateHTML(basePath));
240
+ return;
241
+ }
242
+
243
+ // Serve the OpenAPI JSON spec
244
+ if (path === `${basePath}/openapi.json`) {
245
+ logger.debug("Serving OpenAPI JSON spec", "DocsServing");
246
+ res.setHeader("Content-Type", "application/json");
247
+ res.json(this.openAPISpec);
248
+ return;
249
+ }
250
+
251
+ // Serve Swagger UI assets
252
+ if (path.startsWith(`${basePath}/`)) {
253
+ const assetName = path.replace(`${basePath}/`, "");
254
+
255
+ logger.debug(
256
+ `Attempting to serve asset: ${assetName}`,
257
+ "AssetServing",
258
+ {
259
+ fullPath: path,
260
+ basePath,
261
+ assetPath: this.swaggerUIAssetPath,
262
+ },
263
+ );
264
+
265
+ // Security: only allow specific asset files
266
+ const allowedAssets = [
267
+ "swagger-ui-bundle.js",
268
+ "swagger-ui.css",
269
+ "swagger-ui-standalone-preset.js",
270
+ "favicon-16x16.png",
271
+ "favicon-32x32.png",
272
+ ];
273
+
274
+ if (allowedAssets.includes(assetName)) {
275
+ try {
276
+ const assetPath = join(this.swaggerUIAssetPath, assetName);
277
+ logger.debug(`Reading asset from: ${assetPath}`, "AssetServing");
278
+
279
+ const content = readFileSync(assetPath);
280
+
281
+ // Set appropriate content type
282
+ const contentType = this.getContentType(assetName);
283
+ res.setHeader("Content-Type", contentType);
284
+ res.setHeader("Cache-Control", "public, max-age=86400"); // Cache for 1 day
285
+
286
+ logger.debug(
287
+ `Serving asset: ${assetName} (${content.length} bytes)`,
288
+ "AssetServing",
289
+ );
290
+ res.send(content);
291
+ return;
292
+ } catch (error) {
293
+ logger.error(
294
+ `Failed to serve Swagger UI asset: ${assetName}`,
295
+ "AssetServing",
296
+ {
297
+ error: error instanceof Error ? error.message : String(error),
298
+ assetPath: join(this.swaggerUIAssetPath, assetName),
299
+ },
300
+ );
301
+ res.status(404);
302
+ res.send(`Asset not found: ${assetName}`);
303
+ return;
304
+ }
305
+ } else {
306
+ logger.warn(`Asset not allowed: ${assetName}`, "AssetServing", {
307
+ allowedAssets,
308
+ });
309
+ res.status(404);
310
+ res.send(`Asset not allowed: ${assetName}`);
311
+ return;
312
+ }
313
+ }
314
+
315
+ // Not a docs request, continue to next middleware
316
+ next();
317
+ };
318
+ }
319
+
320
+ // Get content type for asset files
321
+ private getContentType(filename: string): string {
322
+ if (filename.endsWith(".js")) return "application/javascript";
323
+ if (filename.endsWith(".css")) return "text/css";
324
+ if (filename.endsWith(".png")) return "image/png";
325
+ if (filename.endsWith(".ico")) return "image/x-icon";
326
+ return "text/plain";
327
+ }
328
+
329
+ // Update the OpenAPI spec (useful for dynamic updates)
330
+ updateSpec(newSpec: OpenAPISpec): void {
331
+ this.openAPISpec = newSpec;
332
+ logger.debug("OpenAPI specification updated", "SpecUpdate", {
333
+ pathCount: Object.keys(newSpec.paths).length,
334
+ });
335
+ }
336
+ }
337
+
338
+ // Convenience function to create documentation middleware
339
+ export function createDocsMiddleware(
340
+ openAPISpec: OpenAPISpec,
341
+ options: SwaggerUIOptions = {},
342
+ ) {
343
+ const middleware = new SwaggerUIMiddleware(openAPISpec, options);
344
+ return middleware.createMiddleware();
345
+ }
346
+
347
+ // Helper function to generate documentation for an app
348
+ export function generateDocsForApp(
349
+ routes: any[],
350
+ info: { title: string; version: string; description?: string },
351
+ options: SwaggerUIOptions = {},
352
+ ) {
353
+ const openAPISpec: OpenAPISpec = {
354
+ openapi: "3.0.3",
355
+ info,
356
+ servers: [
357
+ { url: "http://localhost:3000", description: "Development server" },
358
+ ],
359
+ paths: {},
360
+ tags: [],
361
+ };
362
+
363
+ // This will be enhanced when integrated with the routing system
364
+ logger.info("Documentation generated for app", "AppDocumentation", {
365
+ routeCount: routes.length,
366
+ title: info.title,
367
+ });
368
+
369
+ return createDocsMiddleware(openAPISpec, options);
370
+ }