@morojs/moro 1.0.0 → 1.0.1

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 (284) hide show
  1. package/README.md +1 -1
  2. package/dist/core/config/index.d.ts +5 -5
  3. package/dist/core/config/index.js +1 -1
  4. package/dist/core/config/index.js.map +1 -1
  5. package/dist/core/config/loader.d.ts +1 -1
  6. package/dist/core/config/loader.js +58 -82
  7. package/dist/core/config/loader.js.map +1 -1
  8. package/dist/core/config/schema.d.ts +1 -1
  9. package/dist/core/config/schema.js +52 -111
  10. package/dist/core/config/schema.js.map +1 -1
  11. package/dist/core/config/utils.d.ts +2 -2
  12. package/dist/core/config/utils.js +18 -18
  13. package/dist/core/config/utils.js.map +1 -1
  14. package/dist/core/database/adapters/drizzle.d.ts +1 -1
  15. package/dist/core/database/adapters/drizzle.js +39 -55
  16. package/dist/core/database/adapters/drizzle.js.map +1 -1
  17. package/dist/core/database/adapters/index.d.ts +7 -7
  18. package/dist/core/database/adapters/index.js +11 -11
  19. package/dist/core/database/adapters/index.js.map +1 -1
  20. package/dist/core/database/adapters/mongodb.d.ts +1 -1
  21. package/dist/core/database/adapters/mongodb.js +19 -23
  22. package/dist/core/database/adapters/mongodb.js.map +1 -1
  23. package/dist/core/database/adapters/mysql.d.ts +1 -1
  24. package/dist/core/database/adapters/mysql.js +31 -27
  25. package/dist/core/database/adapters/mysql.js.map +1 -1
  26. package/dist/core/database/adapters/postgresql.d.ts +1 -1
  27. package/dist/core/database/adapters/postgresql.js +27 -35
  28. package/dist/core/database/adapters/postgresql.js.map +1 -1
  29. package/dist/core/database/adapters/redis.d.ts +1 -1
  30. package/dist/core/database/adapters/redis.js +24 -24
  31. package/dist/core/database/adapters/redis.js.map +1 -1
  32. package/dist/core/database/adapters/sqlite.d.ts +1 -1
  33. package/dist/core/database/adapters/sqlite.js +36 -36
  34. package/dist/core/database/adapters/sqlite.js.map +1 -1
  35. package/dist/core/database/index.d.ts +2 -2
  36. package/dist/core/docs/index.d.ts +7 -7
  37. package/dist/core/docs/index.js +13 -15
  38. package/dist/core/docs/index.js.map +1 -1
  39. package/dist/core/docs/openapi-generator.d.ts +5 -5
  40. package/dist/core/docs/openapi-generator.js +93 -94
  41. package/dist/core/docs/openapi-generator.js.map +1 -1
  42. package/dist/core/docs/simple-docs.d.ts +1 -1
  43. package/dist/core/docs/simple-docs.js +25 -28
  44. package/dist/core/docs/simple-docs.js.map +1 -1
  45. package/dist/core/docs/swagger-ui.d.ts +2 -2
  46. package/dist/core/docs/swagger-ui.js +46 -51
  47. package/dist/core/docs/swagger-ui.js.map +1 -1
  48. package/dist/core/docs/zod-to-openapi.d.ts +1 -1
  49. package/dist/core/docs/zod-to-openapi.js +115 -125
  50. package/dist/core/docs/zod-to-openapi.js.map +1 -1
  51. package/dist/core/events/event-bus.d.ts +1 -1
  52. package/dist/core/events/event-bus.js +15 -21
  53. package/dist/core/events/event-bus.js.map +1 -1
  54. package/dist/core/events/index.d.ts +2 -2
  55. package/dist/core/framework.d.ts +5 -5
  56. package/dist/core/framework.js +55 -60
  57. package/dist/core/framework.js.map +1 -1
  58. package/dist/core/http/http-server.d.ts +2 -2
  59. package/dist/core/http/http-server.js +228 -261
  60. package/dist/core/http/http-server.js.map +1 -1
  61. package/dist/core/http/index.d.ts +3 -3
  62. package/dist/core/http/router.d.ts +1 -1
  63. package/dist/core/http/router.js +15 -17
  64. package/dist/core/http/router.js.map +1 -1
  65. package/dist/core/logger/filters.d.ts +1 -1
  66. package/dist/core/logger/filters.js +16 -16
  67. package/dist/core/logger/filters.js.map +1 -1
  68. package/dist/core/logger/index.d.ts +3 -3
  69. package/dist/core/logger/logger.d.ts +1 -1
  70. package/dist/core/logger/logger.js +48 -59
  71. package/dist/core/logger/logger.js.map +1 -1
  72. package/dist/core/logger/outputs.d.ts +4 -4
  73. package/dist/core/logger/outputs.js +16 -20
  74. package/dist/core/logger/outputs.js.map +1 -1
  75. package/dist/core/middleware/built-in/adapters/cache/file.d.ts +1 -1
  76. package/dist/core/middleware/built-in/adapters/cache/file.js +19 -19
  77. package/dist/core/middleware/built-in/adapters/cache/file.js.map +1 -1
  78. package/dist/core/middleware/built-in/adapters/cache/index.d.ts +4 -4
  79. package/dist/core/middleware/built-in/adapters/cache/index.js +3 -3
  80. package/dist/core/middleware/built-in/adapters/cache/index.js.map +1 -1
  81. package/dist/core/middleware/built-in/adapters/cache/memory.d.ts +1 -1
  82. package/dist/core/middleware/built-in/adapters/cache/memory.js +5 -5
  83. package/dist/core/middleware/built-in/adapters/cache/memory.js.map +1 -1
  84. package/dist/core/middleware/built-in/adapters/cache/redis.d.ts +1 -1
  85. package/dist/core/middleware/built-in/adapters/cache/redis.js +18 -18
  86. package/dist/core/middleware/built-in/adapters/cache/redis.js.map +1 -1
  87. package/dist/core/middleware/built-in/adapters/cdn/azure.d.ts +1 -1
  88. package/dist/core/middleware/built-in/adapters/cdn/azure.js +8 -8
  89. package/dist/core/middleware/built-in/adapters/cdn/azure.js.map +1 -1
  90. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.d.ts +1 -1
  91. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js +14 -14
  92. package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js.map +1 -1
  93. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.d.ts +1 -1
  94. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js +13 -15
  95. package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js.map +1 -1
  96. package/dist/core/middleware/built-in/adapters/cdn/index.d.ts +4 -4
  97. package/dist/core/middleware/built-in/adapters/cdn/index.js +3 -3
  98. package/dist/core/middleware/built-in/adapters/index.d.ts +4 -4
  99. package/dist/core/middleware/built-in/auth.d.ts +1 -1
  100. package/dist/core/middleware/built-in/auth.js +14 -14
  101. package/dist/core/middleware/built-in/cache.d.ts +2 -2
  102. package/dist/core/middleware/built-in/cache.js +43 -45
  103. package/dist/core/middleware/built-in/cache.js.map +1 -1
  104. package/dist/core/middleware/built-in/cdn.d.ts +2 -2
  105. package/dist/core/middleware/built-in/cdn.js +27 -29
  106. package/dist/core/middleware/built-in/cdn.js.map +1 -1
  107. package/dist/core/middleware/built-in/cookie.d.ts +2 -2
  108. package/dist/core/middleware/built-in/cookie.js +17 -17
  109. package/dist/core/middleware/built-in/cookie.js.map +1 -1
  110. package/dist/core/middleware/built-in/cors.d.ts +1 -1
  111. package/dist/core/middleware/built-in/cors.js +13 -13
  112. package/dist/core/middleware/built-in/csp.d.ts +1 -1
  113. package/dist/core/middleware/built-in/csp.js +22 -25
  114. package/dist/core/middleware/built-in/csp.js.map +1 -1
  115. package/dist/core/middleware/built-in/csrf.d.ts +1 -1
  116. package/dist/core/middleware/built-in/csrf.js +21 -24
  117. package/dist/core/middleware/built-in/csrf.js.map +1 -1
  118. package/dist/core/middleware/built-in/error-tracker.js +2 -2
  119. package/dist/core/middleware/built-in/index.d.ts +14 -14
  120. package/dist/core/middleware/built-in/performance-monitor.js +2 -2
  121. package/dist/core/middleware/built-in/rate-limit.d.ts +1 -1
  122. package/dist/core/middleware/built-in/rate-limit.js +12 -12
  123. package/dist/core/middleware/built-in/request-logger.js.map +1 -1
  124. package/dist/core/middleware/built-in/session.d.ts +5 -5
  125. package/dist/core/middleware/built-in/session.js +35 -38
  126. package/dist/core/middleware/built-in/session.js.map +1 -1
  127. package/dist/core/middleware/built-in/sse.d.ts +1 -1
  128. package/dist/core/middleware/built-in/sse.js +20 -22
  129. package/dist/core/middleware/built-in/sse.js.map +1 -1
  130. package/dist/core/middleware/built-in/validation.d.ts +1 -1
  131. package/dist/core/middleware/built-in/validation.js +13 -13
  132. package/dist/core/middleware/index.d.ts +5 -5
  133. package/dist/core/middleware/index.js +16 -16
  134. package/dist/core/middleware/index.js.map +1 -1
  135. package/dist/core/modules/auto-discovery.d.ts +2 -2
  136. package/dist/core/modules/auto-discovery.js +12 -13
  137. package/dist/core/modules/auto-discovery.js.map +1 -1
  138. package/dist/core/modules/index.d.ts +2 -2
  139. package/dist/core/modules/index.js.map +1 -1
  140. package/dist/core/modules/modules.d.ts +3 -3
  141. package/dist/core/modules/modules.js +3 -6
  142. package/dist/core/modules/modules.js.map +1 -1
  143. package/dist/core/networking/index.d.ts +2 -2
  144. package/dist/core/networking/index.js.map +1 -1
  145. package/dist/core/networking/service-discovery.d.ts +2 -2
  146. package/dist/core/networking/service-discovery.js +27 -27
  147. package/dist/core/networking/service-discovery.js.map +1 -1
  148. package/dist/core/networking/websocket-manager.d.ts +3 -3
  149. package/dist/core/networking/websocket-manager.js +15 -16
  150. package/dist/core/networking/websocket-manager.js.map +1 -1
  151. package/dist/core/routing/app-integration.d.ts +2 -2
  152. package/dist/core/routing/app-integration.js +13 -13
  153. package/dist/core/routing/app-integration.js.map +1 -1
  154. package/dist/core/routing/index.d.ts +3 -3
  155. package/dist/core/routing/index.js +43 -52
  156. package/dist/core/routing/index.js.map +1 -1
  157. package/dist/core/runtime/aws-lambda-adapter.d.ts +3 -3
  158. package/dist/core/runtime/aws-lambda-adapter.js +14 -16
  159. package/dist/core/runtime/aws-lambda-adapter.js.map +1 -1
  160. package/dist/core/runtime/base-adapter.d.ts +2 -2
  161. package/dist/core/runtime/base-adapter.js +11 -12
  162. package/dist/core/runtime/base-adapter.js.map +1 -1
  163. package/dist/core/runtime/cloudflare-workers-adapter.d.ts +3 -3
  164. package/dist/core/runtime/cloudflare-workers-adapter.js +20 -21
  165. package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
  166. package/dist/core/runtime/index.d.ts +9 -9
  167. package/dist/core/runtime/index.js +4 -4
  168. package/dist/core/runtime/index.js.map +1 -1
  169. package/dist/core/runtime/node-adapter.d.ts +5 -5
  170. package/dist/core/runtime/node-adapter.js +35 -35
  171. package/dist/core/runtime/node-adapter.js.map +1 -1
  172. package/dist/core/runtime/vercel-edge-adapter.d.ts +3 -3
  173. package/dist/core/runtime/vercel-edge-adapter.js +12 -15
  174. package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
  175. package/dist/core/utilities/circuit-breaker.js +6 -6
  176. package/dist/core/utilities/container.d.ts +1 -1
  177. package/dist/core/utilities/container.js +17 -22
  178. package/dist/core/utilities/container.js.map +1 -1
  179. package/dist/core/utilities/hooks.d.ts +3 -3
  180. package/dist/core/utilities/hooks.js +11 -11
  181. package/dist/core/utilities/hooks.js.map +1 -1
  182. package/dist/core/utilities/index.d.ts +4 -4
  183. package/dist/core/validation/index.d.ts +3 -3
  184. package/dist/core/validation/index.js +15 -15
  185. package/dist/core/validation/index.js.map +1 -1
  186. package/dist/index.d.ts +31 -30
  187. package/dist/index.js +28 -1
  188. package/dist/index.js.map +1 -1
  189. package/dist/moro.d.ts +14 -14
  190. package/dist/moro.js +79 -88
  191. package/dist/moro.js.map +1 -1
  192. package/dist/types/cache.d.ts +1 -1
  193. package/dist/types/core.d.ts +2 -2
  194. package/dist/types/events.d.ts +19 -19
  195. package/dist/types/hooks.d.ts +1 -1
  196. package/dist/types/http.d.ts +2 -2
  197. package/dist/types/logger.d.ts +3 -3
  198. package/dist/types/module.d.ts +2 -2
  199. package/dist/types/runtime.d.ts +2 -2
  200. package/dist/types/session.d.ts +4 -4
  201. package/package.json +180 -164
  202. package/src/core/config/index.ts +7 -9
  203. package/src/core/config/loader.ts +86 -158
  204. package/src/core/config/schema.ts +59 -122
  205. package/src/core/config/utils.ts +27 -45
  206. package/src/core/database/adapters/drizzle.ts +53 -75
  207. package/src/core/database/adapters/index.ts +26 -29
  208. package/src/core/database/adapters/mongodb.ts +31 -54
  209. package/src/core/database/adapters/mysql.ts +40 -50
  210. package/src/core/database/adapters/postgresql.ts +32 -42
  211. package/src/core/database/adapters/redis.ts +31 -36
  212. package/src/core/database/adapters/sqlite.ts +43 -51
  213. package/src/core/database/index.ts +2 -2
  214. package/src/core/docs/index.ts +25 -39
  215. package/src/core/docs/openapi-generator.ts +104 -117
  216. package/src/core/docs/simple-docs.ts +29 -39
  217. package/src/core/docs/swagger-ui.ts +57 -76
  218. package/src/core/docs/zod-to-openapi.ts +121 -153
  219. package/src/core/events/event-bus.ts +22 -45
  220. package/src/core/events/index.ts +2 -2
  221. package/src/core/framework.ts +119 -197
  222. package/src/core/http/http-server.ts +260 -360
  223. package/src/core/http/index.ts +3 -8
  224. package/src/core/http/router.ts +19 -31
  225. package/src/core/logger/filters.ts +19 -22
  226. package/src/core/logger/index.ts +3 -3
  227. package/src/core/logger/logger.ts +59 -100
  228. package/src/core/logger/outputs.ts +23 -27
  229. package/src/core/middleware/built-in/adapters/cache/file.ts +21 -23
  230. package/src/core/middleware/built-in/adapters/cache/index.ts +11 -14
  231. package/src/core/middleware/built-in/adapters/cache/memory.ts +7 -7
  232. package/src/core/middleware/built-in/adapters/cache/redis.ts +21 -24
  233. package/src/core/middleware/built-in/adapters/cdn/azure.ts +10 -18
  234. package/src/core/middleware/built-in/adapters/cdn/cloudflare.ts +19 -36
  235. package/src/core/middleware/built-in/adapters/cdn/cloudfront.ts +17 -26
  236. package/src/core/middleware/built-in/adapters/cdn/index.ts +10 -10
  237. package/src/core/middleware/built-in/adapters/index.ts +4 -4
  238. package/src/core/middleware/built-in/auth.ts +16 -16
  239. package/src/core/middleware/built-in/cache.ts +50 -67
  240. package/src/core/middleware/built-in/cdn.ts +34 -61
  241. package/src/core/middleware/built-in/cookie.ts +23 -28
  242. package/src/core/middleware/built-in/cors.ts +17 -17
  243. package/src/core/middleware/built-in/csp.ts +25 -31
  244. package/src/core/middleware/built-in/csrf.ts +24 -29
  245. package/src/core/middleware/built-in/error-tracker.ts +3 -3
  246. package/src/core/middleware/built-in/index.ts +28 -28
  247. package/src/core/middleware/built-in/performance-monitor.ts +4 -4
  248. package/src/core/middleware/built-in/rate-limit.ts +15 -15
  249. package/src/core/middleware/built-in/request-logger.ts +1 -3
  250. package/src/core/middleware/built-in/session.ts +47 -70
  251. package/src/core/middleware/built-in/sse.ts +23 -28
  252. package/src/core/middleware/built-in/validation.ts +15 -15
  253. package/src/core/middleware/index.ts +26 -37
  254. package/src/core/modules/auto-discovery.ts +21 -31
  255. package/src/core/modules/index.ts +2 -5
  256. package/src/core/modules/modules.ts +11 -20
  257. package/src/core/networking/index.ts +2 -6
  258. package/src/core/networking/service-discovery.ts +41 -61
  259. package/src/core/networking/websocket-manager.ts +27 -36
  260. package/src/core/routing/app-integration.ts +19 -32
  261. package/src/core/routing/index.ts +57 -88
  262. package/src/core/runtime/aws-lambda-adapter.ts +20 -30
  263. package/src/core/runtime/base-adapter.ts +17 -27
  264. package/src/core/runtime/cloudflare-workers-adapter.ts +28 -42
  265. package/src/core/runtime/index.ts +21 -33
  266. package/src/core/runtime/node-adapter.ts +59 -73
  267. package/src/core/runtime/vercel-edge-adapter.ts +18 -29
  268. package/src/core/utilities/circuit-breaker.ts +7 -7
  269. package/src/core/utilities/container.ts +52 -89
  270. package/src/core/utilities/hooks.ts +17 -23
  271. package/src/core/utilities/index.ts +4 -4
  272. package/src/core/validation/index.ts +25 -51
  273. package/src/index.ts +58 -60
  274. package/src/moro.ts +119 -191
  275. package/src/types/cache.ts +1 -1
  276. package/src/types/core.ts +2 -2
  277. package/src/types/database.ts +2 -10
  278. package/src/types/events.ts +23 -31
  279. package/src/types/hooks.ts +1 -1
  280. package/src/types/http.ts +5 -8
  281. package/src/types/logger.ts +7 -23
  282. package/src/types/module.ts +2 -2
  283. package/src/types/runtime.ts +6 -21
  284. package/src/types/session.ts +4 -4
@@ -1,16 +1,10 @@
1
1
  // src/core/http-server.ts
2
- import { IncomingMessage, ServerResponse, createServer, Server } from "http";
3
- import { URL } from "url";
4
- import * as zlib from "zlib";
5
- import { promisify } from "util";
6
- import { createFrameworkLogger } from "../logger";
7
- import {
8
- HttpRequest,
9
- HttpResponse,
10
- HttpHandler,
11
- Middleware,
12
- RouteEntry,
13
- } from "../../types/http";
2
+ import { IncomingMessage, ServerResponse, createServer, Server } from 'http';
3
+ import { URL } from 'url';
4
+ import * as zlib from 'zlib';
5
+ import { promisify } from 'util';
6
+ import { createFrameworkLogger } from '../logger';
7
+ import { HttpRequest, HttpResponse, HttpHandler, Middleware, RouteEntry } from '../../types/http';
14
8
 
15
9
  const gzip = promisify(zlib.gzip);
16
10
  const deflate = promisify(zlib.deflate);
@@ -21,7 +15,7 @@ export class MoroHttpServer {
21
15
  private globalMiddleware: Middleware[] = [];
22
16
  private compressionEnabled = true;
23
17
  private compressionThreshold = 1024;
24
- private logger = createFrameworkLogger("HttpServer");
18
+ private logger = createFrameworkLogger('HttpServer');
25
19
 
26
20
  constructor() {
27
21
  this.server = createServer(this.handleRequest.bind(this));
@@ -34,30 +28,26 @@ export class MoroHttpServer {
34
28
 
35
29
  // Routing methods
36
30
  get(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
37
- this.addRoute("GET", path, handlers);
31
+ this.addRoute('GET', path, handlers);
38
32
  }
39
33
 
40
34
  post(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
41
- this.addRoute("POST", path, handlers);
35
+ this.addRoute('POST', path, handlers);
42
36
  }
43
37
 
44
38
  put(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
45
- this.addRoute("PUT", path, handlers);
39
+ this.addRoute('PUT', path, handlers);
46
40
  }
47
41
 
48
42
  delete(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
49
- this.addRoute("DELETE", path, handlers);
43
+ this.addRoute('DELETE', path, handlers);
50
44
  }
51
45
 
52
46
  patch(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
53
- this.addRoute("PATCH", path, handlers);
47
+ this.addRoute('PATCH', path, handlers);
54
48
  }
55
49
 
56
- private addRoute(
57
- method: string,
58
- path: string,
59
- handlers: (Middleware | HttpHandler)[],
60
- ): void {
50
+ private addRoute(method: string, path: string, handlers: (Middleware | HttpHandler)[]): void {
61
51
  const { pattern, paramNames } = this.pathToRegex(path);
62
52
  const handler = handlers.pop() as HttpHandler;
63
53
  const middleware = handlers as Middleware[];
@@ -79,9 +69,9 @@ export class MoroHttpServer {
79
69
  const regexPattern = path
80
70
  .replace(/\/:([^/]+)/g, (match, paramName) => {
81
71
  paramNames.push(paramName);
82
- return "/([^/]+)";
72
+ return '/([^/]+)';
83
73
  })
84
- .replace(/\//g, "\\/");
74
+ .replace(/\//g, '\\/');
85
75
 
86
76
  return {
87
77
  pattern: new RegExp(`^${regexPattern}$`),
@@ -89,10 +79,7 @@ export class MoroHttpServer {
89
79
  };
90
80
  }
91
81
 
92
- private async handleRequest(
93
- req: IncomingMessage,
94
- res: ServerResponse,
95
- ): Promise<void> {
82
+ private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
96
83
  const httpReq = this.enhanceRequest(req);
97
84
  const httpRes = this.enhanceResponse(res);
98
85
 
@@ -103,7 +90,7 @@ export class MoroHttpServer {
103
90
  httpReq.query = Object.fromEntries(url.searchParams);
104
91
 
105
92
  // Parse body for POST/PUT/PATCH requests
106
- if (["POST", "PUT", "PATCH"].includes(req.method!)) {
93
+ if (['POST', 'PUT', 'PATCH'].includes(req.method!)) {
107
94
  httpReq.body = await this.parseBody(req);
108
95
  }
109
96
 
@@ -118,7 +105,7 @@ export class MoroHttpServer {
118
105
  // Find matching route
119
106
  const route = this.findRoute(req.method!, httpReq.path);
120
107
  if (!route) {
121
- httpRes.status(404).json({ success: false, error: "Not found" });
108
+ httpRes.status(404).json({ success: false, error: 'Not found' });
122
109
  return;
123
110
  }
124
111
 
@@ -137,7 +124,7 @@ export class MoroHttpServer {
137
124
  // Execute handler
138
125
  await route.handler(httpReq, httpRes);
139
126
  } catch (error) {
140
- this.logger.error("Request error", "RequestHandler", {
127
+ this.logger.error('Request error', 'RequestHandler', {
141
128
  error: error instanceof Error ? error.message : String(error),
142
129
  requestId: httpReq.requestId,
143
130
  method: req.method,
@@ -147,7 +134,7 @@ export class MoroHttpServer {
147
134
  if (!httpRes.headersSent) {
148
135
  httpRes.status(500).json({
149
136
  success: false,
150
- error: "Internal server error",
137
+ error: 'Internal server error',
151
138
  requestId: httpReq.requestId,
152
139
  });
153
140
  }
@@ -159,13 +146,13 @@ export class MoroHttpServer {
159
146
  httpReq.params = {};
160
147
  httpReq.query = {};
161
148
  httpReq.body = null;
162
- httpReq.path = "";
163
- httpReq.ip = req.socket.remoteAddress || "";
149
+ httpReq.path = '';
150
+ httpReq.ip = req.socket.remoteAddress || '';
164
151
  httpReq.requestId = Math.random().toString(36).substring(7);
165
152
  httpReq.headers = req.headers as Record<string, string>;
166
153
 
167
154
  // Parse cookies
168
- httpReq.cookies = this.parseCookies(req.headers.cookie || "");
155
+ httpReq.cookies = this.parseCookies(req.headers.cookie || '');
169
156
 
170
157
  return httpReq;
171
158
  }
@@ -174,8 +161,8 @@ export class MoroHttpServer {
174
161
  const cookies: Record<string, string> = {};
175
162
  if (!cookieHeader) return cookies;
176
163
 
177
- cookieHeader.split(";").forEach((cookie) => {
178
- const [name, value] = cookie.trim().split("=");
164
+ cookieHeader.split(';').forEach(cookie => {
165
+ const [name, value] = cookie.trim().split('=');
179
166
  if (name && value) {
180
167
  cookies[name] = decodeURIComponent(value);
181
168
  }
@@ -193,31 +180,28 @@ export class MoroHttpServer {
193
180
  const jsonString = JSON.stringify(data);
194
181
  const buffer = Buffer.from(jsonString);
195
182
 
196
- httpRes.setHeader("Content-Type", "application/json; charset=utf-8");
183
+ httpRes.setHeader('Content-Type', 'application/json; charset=utf-8');
197
184
 
198
185
  // Compression
199
- if (
200
- this.compressionEnabled &&
201
- buffer.length > this.compressionThreshold
202
- ) {
203
- const acceptEncoding = httpRes.req.headers["accept-encoding"] || "";
186
+ if (this.compressionEnabled && buffer.length > this.compressionThreshold) {
187
+ const acceptEncoding = httpRes.req.headers['accept-encoding'] || '';
204
188
 
205
- if (acceptEncoding.includes("gzip")) {
189
+ if (acceptEncoding.includes('gzip')) {
206
190
  const compressed = await gzip(buffer);
207
- httpRes.setHeader("Content-Encoding", "gzip");
208
- httpRes.setHeader("Content-Length", compressed.length);
191
+ httpRes.setHeader('Content-Encoding', 'gzip');
192
+ httpRes.setHeader('Content-Length', compressed.length);
209
193
  httpRes.end(compressed);
210
194
  return;
211
- } else if (acceptEncoding.includes("deflate")) {
195
+ } else if (acceptEncoding.includes('deflate')) {
212
196
  const compressed = await deflate(buffer);
213
- httpRes.setHeader("Content-Encoding", "deflate");
214
- httpRes.setHeader("Content-Length", compressed.length);
197
+ httpRes.setHeader('Content-Encoding', 'deflate');
198
+ httpRes.setHeader('Content-Length', compressed.length);
215
199
  httpRes.end(compressed);
216
200
  return;
217
201
  }
218
202
  }
219
203
 
220
- httpRes.setHeader("Content-Length", buffer.length);
204
+ httpRes.setHeader('Content-Length', buffer.length);
221
205
  httpRes.end(buffer);
222
206
  };
223
207
 
@@ -230,22 +214,19 @@ export class MoroHttpServer {
230
214
  if (httpRes.headersSent) return;
231
215
 
232
216
  // Auto-detect content type if not already set
233
- if (!httpRes.getHeader("Content-Type")) {
234
- if (typeof data === "string") {
217
+ if (!httpRes.getHeader('Content-Type')) {
218
+ if (typeof data === 'string') {
235
219
  // Check if it's JSON
236
220
  try {
237
221
  JSON.parse(data);
238
- httpRes.setHeader(
239
- "Content-Type",
240
- "application/json; charset=utf-8",
241
- );
222
+ httpRes.setHeader('Content-Type', 'application/json; charset=utf-8');
242
223
  } catch {
243
224
  // Default to plain text
244
- httpRes.setHeader("Content-Type", "text/plain; charset=utf-8");
225
+ httpRes.setHeader('Content-Type', 'text/plain; charset=utf-8');
245
226
  }
246
227
  } else {
247
228
  // Buffer data - default to octet-stream
248
- httpRes.setHeader("Content-Type", "application/octet-stream");
229
+ httpRes.setHeader('Content-Type', 'application/octet-stream');
249
230
  }
250
231
  }
251
232
 
@@ -257,33 +238,32 @@ export class MoroHttpServer {
257
238
  let cookieString = `${name}=${cookieValue}`;
258
239
 
259
240
  if (options.maxAge) cookieString += `; Max-Age=${options.maxAge}`;
260
- if (options.expires)
261
- cookieString += `; Expires=${options.expires.toUTCString()}`;
262
- if (options.httpOnly) cookieString += "; HttpOnly";
263
- if (options.secure) cookieString += "; Secure";
241
+ if (options.expires) cookieString += `; Expires=${options.expires.toUTCString()}`;
242
+ if (options.httpOnly) cookieString += '; HttpOnly';
243
+ if (options.secure) cookieString += '; Secure';
264
244
  if (options.sameSite) cookieString += `; SameSite=${options.sameSite}`;
265
245
  if (options.domain) cookieString += `; Domain=${options.domain}`;
266
246
  if (options.path) cookieString += `; Path=${options.path}`;
267
247
 
268
- const existingCookies = httpRes.getHeader("Set-Cookie") || [];
248
+ const existingCookies = httpRes.getHeader('Set-Cookie') || [];
269
249
  const cookies = Array.isArray(existingCookies)
270
250
  ? [...existingCookies]
271
251
  : [existingCookies as string];
272
252
  cookies.push(cookieString);
273
- httpRes.setHeader("Set-Cookie", cookies);
253
+ httpRes.setHeader('Set-Cookie', cookies);
274
254
 
275
255
  return httpRes;
276
256
  };
277
257
 
278
258
  httpRes.clearCookie = (name: string, options: any = {}) => {
279
259
  const clearOptions = { ...options, expires: new Date(0), maxAge: 0 };
280
- return httpRes.cookie(name, "", clearOptions);
260
+ return httpRes.cookie(name, '', clearOptions);
281
261
  };
282
262
 
283
263
  httpRes.redirect = (url: string, status: number = 302) => {
284
264
  if (httpRes.headersSent) return;
285
265
  httpRes.statusCode = status;
286
- httpRes.setHeader("Location", url);
266
+ httpRes.setHeader('Location', url);
287
267
  httpRes.end();
288
268
  };
289
269
 
@@ -291,8 +271,8 @@ export class MoroHttpServer {
291
271
  if (httpRes.headersSent) return;
292
272
 
293
273
  try {
294
- const fs = await import("fs/promises");
295
- const path = await import("path");
274
+ const fs = await import('fs/promises');
275
+ const path = await import('path');
296
276
  const extension = path.extname(filePath);
297
277
  const mime = await this.getMimeType(extension);
298
278
 
@@ -301,19 +281,19 @@ export class MoroHttpServer {
301
281
 
302
282
  // Add charset for text-based files
303
283
  const contentType = this.addCharsetIfNeeded(mime);
304
- httpRes.setHeader("Content-Type", contentType);
305
- httpRes.setHeader("Content-Length", stats.size);
284
+ httpRes.setHeader('Content-Type', contentType);
285
+ httpRes.setHeader('Content-Length', stats.size);
306
286
 
307
287
  // Add security headers for file downloads
308
- httpRes.setHeader("X-Content-Type-Options", "nosniff");
288
+ httpRes.setHeader('X-Content-Type-Options', 'nosniff');
309
289
 
310
290
  // Add caching headers
311
- httpRes.setHeader("Last-Modified", stats.mtime.toUTCString());
312
- httpRes.setHeader("Cache-Control", "public, max-age=31536000"); // 1 year for static files
291
+ httpRes.setHeader('Last-Modified', stats.mtime.toUTCString());
292
+ httpRes.setHeader('Cache-Control', 'public, max-age=31536000'); // 1 year for static files
313
293
 
314
294
  httpRes.end(data);
315
295
  } catch (error) {
316
- httpRes.status(404).json({ success: false, error: "File not found" });
296
+ httpRes.status(404).json({ success: false, error: 'File not found' });
317
297
  }
318
298
  };
319
299
 
@@ -322,37 +302,37 @@ export class MoroHttpServer {
322
302
 
323
303
  private async getMimeType(ext: string): Promise<string> {
324
304
  const mimeTypes: Record<string, string> = {
325
- ".html": "text/html",
326
- ".css": "text/css",
327
- ".js": "application/javascript",
328
- ".json": "application/json",
329
- ".png": "image/png",
330
- ".jpg": "image/jpeg",
331
- ".jpeg": "image/jpeg",
332
- ".gif": "image/gif",
333
- ".svg": "image/svg+xml",
334
- ".ico": "image/x-icon",
335
- ".pdf": "application/pdf",
336
- ".txt": "text/plain",
337
- ".xml": "application/xml",
305
+ '.html': 'text/html',
306
+ '.css': 'text/css',
307
+ '.js': 'application/javascript',
308
+ '.json': 'application/json',
309
+ '.png': 'image/png',
310
+ '.jpg': 'image/jpeg',
311
+ '.jpeg': 'image/jpeg',
312
+ '.gif': 'image/gif',
313
+ '.svg': 'image/svg+xml',
314
+ '.ico': 'image/x-icon',
315
+ '.pdf': 'application/pdf',
316
+ '.txt': 'text/plain',
317
+ '.xml': 'application/xml',
338
318
  };
339
319
 
340
- return mimeTypes[ext.toLowerCase()] || "application/octet-stream";
320
+ return mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
341
321
  }
342
322
 
343
323
  private addCharsetIfNeeded(mimeType: string): string {
344
324
  // Add charset for text-based content types
345
325
  const textTypes = [
346
- "text/",
347
- "application/json",
348
- "application/javascript",
349
- "application/xml",
350
- "image/svg+xml",
326
+ 'text/',
327
+ 'application/json',
328
+ 'application/javascript',
329
+ 'application/xml',
330
+ 'image/svg+xml',
351
331
  ];
352
332
 
353
- const needsCharset = textTypes.some((type) => mimeType.startsWith(type));
333
+ const needsCharset = textTypes.some(type => mimeType.startsWith(type));
354
334
 
355
- if (needsCharset && !mimeType.includes("charset")) {
335
+ if (needsCharset && !mimeType.includes('charset')) {
356
336
  return `${mimeType}; charset=utf-8`;
357
337
  }
358
338
 
@@ -365,27 +345,25 @@ export class MoroHttpServer {
365
345
  let totalLength = 0;
366
346
  const maxSize = 10 * 1024 * 1024; // 10MB limit
367
347
 
368
- req.on("data", (chunk: Buffer) => {
348
+ req.on('data', (chunk: Buffer) => {
369
349
  totalLength += chunk.length;
370
350
  if (totalLength > maxSize) {
371
- reject(new Error("Request body too large"));
351
+ reject(new Error('Request body too large'));
372
352
  return;
373
353
  }
374
354
  chunks.push(chunk);
375
355
  });
376
356
 
377
- req.on("end", () => {
357
+ req.on('end', () => {
378
358
  try {
379
359
  const body = Buffer.concat(chunks);
380
- const contentType = req.headers["content-type"] || "";
360
+ const contentType = req.headers['content-type'] || '';
381
361
 
382
- if (contentType.includes("application/json")) {
362
+ if (contentType.includes('application/json')) {
383
363
  resolve(JSON.parse(body.toString()));
384
- } else if (
385
- contentType.includes("application/x-www-form-urlencoded")
386
- ) {
364
+ } else if (contentType.includes('application/x-www-form-urlencoded')) {
387
365
  resolve(this.parseUrlEncoded(body.toString()));
388
- } else if (contentType.includes("multipart/form-data")) {
366
+ } else if (contentType.includes('multipart/form-data')) {
389
367
  resolve(this.parseMultipart(body, contentType));
390
368
  } else {
391
369
  resolve(body.toString());
@@ -395,26 +373,26 @@ export class MoroHttpServer {
395
373
  }
396
374
  });
397
375
 
398
- req.on("error", reject);
376
+ req.on('error', reject);
399
377
  });
400
378
  }
401
379
 
402
380
  private parseMultipart(
403
381
  buffer: Buffer,
404
- contentType: string,
382
+ contentType: string
405
383
  ): { fields: Record<string, string>; files: Record<string, any> } {
406
- const boundary = contentType.split("boundary=")[1];
384
+ const boundary = contentType.split('boundary=')[1];
407
385
  if (!boundary) {
408
- throw new Error("Invalid multipart boundary");
386
+ throw new Error('Invalid multipart boundary');
409
387
  }
410
388
 
411
- const parts = buffer.toString("binary").split("--" + boundary);
389
+ const parts = buffer.toString('binary').split('--' + boundary);
412
390
  const fields: Record<string, string> = {};
413
391
  const files: Record<string, any> = {};
414
392
 
415
393
  for (let i = 1; i < parts.length - 1; i++) {
416
394
  const part = parts[i];
417
- const [headers, content] = part.split("\r\n\r\n");
395
+ const [headers, content] = part.split('\r\n\r\n');
418
396
 
419
397
  if (!headers || content === undefined) continue;
420
398
 
@@ -428,16 +406,14 @@ export class MoroHttpServer {
428
406
  if (filenameMatch) {
429
407
  // This is a file
430
408
  const filename = filenameMatch[1];
431
- const mimeType = contentTypeMatch
432
- ? contentTypeMatch[1]
433
- : "application/octet-stream";
409
+ const mimeType = contentTypeMatch ? contentTypeMatch[1] : 'application/octet-stream';
434
410
  const fileContent = content.substring(0, content.length - 2); // Remove trailing \r\n
435
411
 
436
412
  files[name] = {
437
413
  filename,
438
414
  mimetype: mimeType,
439
- data: Buffer.from(fileContent, "binary"),
440
- size: Buffer.byteLength(fileContent, "binary"),
415
+ data: Buffer.from(fileContent, 'binary'),
416
+ size: Buffer.byteLength(fileContent, 'binary'),
441
417
  };
442
418
  } else {
443
419
  // This is a regular field
@@ -459,17 +435,13 @@ export class MoroHttpServer {
459
435
  }
460
436
 
461
437
  private findRoute(method: string, path: string): RouteEntry | null {
462
- return (
463
- this.routes.find(
464
- (route) => route.method === method && route.pattern.test(path),
465
- ) || null
466
- );
438
+ return this.routes.find(route => route.method === method && route.pattern.test(path)) || null;
467
439
  }
468
440
 
469
441
  private async executeMiddleware(
470
442
  middleware: Middleware[],
471
443
  req: HttpRequest,
472
- res: HttpResponse,
444
+ res: HttpResponse
473
445
  ): Promise<void> {
474
446
  for (const mw of middleware) {
475
447
  await new Promise<void>((resolve, reject) => {
@@ -501,13 +473,9 @@ export class MoroHttpServer {
501
473
 
502
474
  listen(port: number, callback?: () => void): void;
503
475
  listen(port: number, host: string, callback?: () => void): void;
504
- listen(
505
- port: number,
506
- host?: string | (() => void),
507
- callback?: () => void,
508
- ): void {
476
+ listen(port: number, host?: string | (() => void), callback?: () => void): void {
509
477
  // Handle overloaded parameters (port, callback) or (port, host, callback)
510
- if (typeof host === "function") {
478
+ if (typeof host === 'function') {
511
479
  callback = host;
512
480
  host = undefined;
513
481
  }
@@ -520,7 +488,7 @@ export class MoroHttpServer {
520
488
  }
521
489
 
522
490
  close(): Promise<void> {
523
- return new Promise((resolve) => {
491
+ return new Promise(resolve => {
524
492
  this.server.close(() => resolve());
525
493
  });
526
494
  }
@@ -532,26 +500,18 @@ export class MoroHttpServer {
532
500
 
533
501
  // Built-in middleware
534
502
  export const middleware = {
535
- cors: (
536
- options: { origin?: string; credentials?: boolean } = {},
537
- ): Middleware => {
503
+ cors: (options: { origin?: string; credentials?: boolean } = {}): Middleware => {
538
504
  return (req, res, next) => {
539
- res.setHeader("Access-Control-Allow-Origin", options.origin || "*");
540
- res.setHeader(
541
- "Access-Control-Allow-Methods",
542
- "GET, POST, PUT, DELETE, OPTIONS",
543
- );
544
- res.setHeader(
545
- "Access-Control-Allow-Headers",
546
- "Content-Type, Authorization",
547
- );
505
+ res.setHeader('Access-Control-Allow-Origin', options.origin || '*');
506
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
507
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
548
508
 
549
509
  if (options.credentials) {
550
- res.setHeader("Access-Control-Allow-Credentials", "true");
510
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
551
511
  }
552
512
 
553
- if (req.method === "OPTIONS") {
554
- res.status(200).send("");
513
+ if (req.method === 'OPTIONS') {
514
+ res.status(200).send('');
555
515
  return;
556
516
  }
557
517
 
@@ -561,28 +521,23 @@ export const middleware = {
561
521
 
562
522
  helmet: (): Middleware => {
563
523
  return (req, res, next) => {
564
- res.setHeader("X-Content-Type-Options", "nosniff");
565
- res.setHeader("X-Frame-Options", "DENY");
566
- res.setHeader("X-XSS-Protection", "1; mode=block");
567
- res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
568
- res.setHeader(
569
- "Strict-Transport-Security",
570
- "max-age=31536000; includeSubDomains",
571
- );
572
- res.setHeader("Content-Security-Policy", "default-src 'self'");
524
+ res.setHeader('X-Content-Type-Options', 'nosniff');
525
+ res.setHeader('X-Frame-Options', 'DENY');
526
+ res.setHeader('X-XSS-Protection', '1; mode=block');
527
+ res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
528
+ res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
529
+ res.setHeader('Content-Security-Policy', "default-src 'self'");
573
530
  next();
574
531
  };
575
532
  },
576
533
 
577
- compression: (
578
- options: { threshold?: number; level?: number } = {},
579
- ): Middleware => {
580
- const zlib = require("zlib");
534
+ compression: (options: { threshold?: number; level?: number } = {}): Middleware => {
535
+ const zlib = require('zlib');
581
536
  const threshold = options.threshold || 1024;
582
537
  const level = options.level || 6;
583
538
 
584
539
  return (req, res, next) => {
585
- const acceptEncoding = req.headers["accept-encoding"] || "";
540
+ const acceptEncoding = req.headers['accept-encoding'] || '';
586
541
 
587
542
  // Override res.json to compress responses
588
543
  const originalJson = res.json;
@@ -593,45 +548,37 @@ export const middleware = {
593
548
  const buffer = Buffer.from(content);
594
549
 
595
550
  if (buffer.length < threshold) {
596
- return isJson
597
- ? originalJson.call(res, data)
598
- : originalSend.call(res, data);
551
+ return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
599
552
  }
600
553
 
601
- if (acceptEncoding.includes("gzip")) {
602
- res.setHeader("Content-Encoding", "gzip");
554
+ if (acceptEncoding.includes('gzip')) {
555
+ res.setHeader('Content-Encoding', 'gzip');
603
556
  zlib.gzip(buffer, { level }, (err: any, compressed: Buffer) => {
604
557
  if (err) {
605
- return isJson
606
- ? originalJson.call(res, data)
607
- : originalSend.call(res, data);
558
+ return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
608
559
  }
609
- res.setHeader("Content-Length", compressed.length);
560
+ res.setHeader('Content-Length', compressed.length);
610
561
  res.writeHead(res.statusCode || 200, res.getHeaders());
611
562
  res.end(compressed);
612
563
  });
613
- } else if (acceptEncoding.includes("deflate")) {
614
- res.setHeader("Content-Encoding", "deflate");
564
+ } else if (acceptEncoding.includes('deflate')) {
565
+ res.setHeader('Content-Encoding', 'deflate');
615
566
  zlib.deflate(buffer, { level }, (err: any, compressed: Buffer) => {
616
567
  if (err) {
617
- return isJson
618
- ? originalJson.call(res, data)
619
- : originalSend.call(res, data);
568
+ return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
620
569
  }
621
- res.setHeader("Content-Length", compressed.length);
570
+ res.setHeader('Content-Length', compressed.length);
622
571
  res.writeHead(res.statusCode || 200, res.getHeaders());
623
572
  res.end(compressed);
624
573
  });
625
574
  } else {
626
- return isJson
627
- ? originalJson.call(res, data)
628
- : originalSend.call(res, data);
575
+ return isJson ? originalJson.call(res, data) : originalSend.call(res, data);
629
576
  }
630
577
  };
631
578
 
632
579
  res.json = function (data: any) {
633
580
  // Ensure charset is set for Safari compatibility
634
- this.setHeader("Content-Type", "application/json; charset=utf-8");
581
+ this.setHeader('Content-Type', 'application/json; charset=utf-8');
635
582
  compressResponse(data, true);
636
583
  return this;
637
584
  };
@@ -648,7 +595,7 @@ export const middleware = {
648
595
  requestLogger: (): Middleware => {
649
596
  return (req, res, next) => {
650
597
  const start = Date.now();
651
- res.on("finish", () => {
598
+ res.on('finish', () => {
652
599
  const duration = Date.now() - start;
653
600
  // Request completed - logged by framework
654
601
  });
@@ -658,16 +605,16 @@ export const middleware = {
658
605
  },
659
606
 
660
607
  bodySize: (options: { limit?: string } = {}): Middleware => {
661
- const limit = options.limit || "10mb";
608
+ const limit = options.limit || '10mb';
662
609
  const limitBytes = parseSize(limit);
663
610
 
664
611
  return (req, res, next) => {
665
- const contentLength = parseInt(req.headers["content-length"] || "0");
612
+ const contentLength = parseInt(req.headers['content-length'] || '0');
666
613
 
667
614
  if (contentLength > limitBytes) {
668
615
  res.status(413).json({
669
616
  success: false,
670
- error: "Request entity too large",
617
+ error: 'Request entity too large',
671
618
  limit: limit,
672
619
  });
673
620
  return;
@@ -681,36 +628,36 @@ export const middleware = {
681
628
  root: string;
682
629
  maxAge?: number;
683
630
  index?: string[];
684
- dotfiles?: "allow" | "deny" | "ignore";
631
+ dotfiles?: 'allow' | 'deny' | 'ignore';
685
632
  etag?: boolean;
686
633
  }): Middleware => {
687
634
  return async (req, res, next) => {
688
635
  // Only handle GET and HEAD requests
689
- if (req.method !== "GET" && req.method !== "HEAD") {
636
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
690
637
  next();
691
638
  return;
692
639
  }
693
640
 
694
641
  try {
695
- const fs = await import("fs/promises");
696
- const path = await import("path");
697
- const crypto = await import("crypto");
642
+ const fs = await import('fs/promises');
643
+ const path = await import('path');
644
+ const crypto = await import('crypto');
698
645
 
699
646
  let filePath = path.join(options.root, req.path);
700
647
 
701
648
  // Security: prevent directory traversal
702
649
  if (!filePath.startsWith(path.resolve(options.root))) {
703
- res.status(403).json({ success: false, error: "Forbidden" });
650
+ res.status(403).json({ success: false, error: 'Forbidden' });
704
651
  return;
705
652
  }
706
653
 
707
654
  // Handle dotfiles
708
655
  const basename = path.basename(filePath);
709
- if (basename.startsWith(".")) {
710
- if (options.dotfiles === "deny") {
711
- res.status(403).json({ success: false, error: "Forbidden" });
656
+ if (basename.startsWith('.')) {
657
+ if (options.dotfiles === 'deny') {
658
+ res.status(403).json({ success: false, error: 'Forbidden' });
712
659
  return;
713
- } else if (options.dotfiles === "ignore") {
660
+ } else if (options.dotfiles === 'ignore') {
714
661
  next();
715
662
  return;
716
663
  }
@@ -726,7 +673,7 @@ export const middleware = {
726
673
 
727
674
  // Handle directories
728
675
  if (stats.isDirectory()) {
729
- const indexFiles = options.index || ["index.html", "index.htm"];
676
+ const indexFiles = options.index || ['index.html', 'index.htm'];
730
677
  let indexFound = false;
731
678
 
732
679
  for (const indexFile of indexFiles) {
@@ -753,57 +700,52 @@ export const middleware = {
753
700
  // Set headers with proper mime type and charset
754
701
  const ext = path.extname(filePath);
755
702
  const mimeTypes: Record<string, string> = {
756
- ".html": "text/html",
757
- ".css": "text/css",
758
- ".js": "application/javascript",
759
- ".json": "application/json",
760
- ".png": "image/png",
761
- ".jpg": "image/jpeg",
762
- ".jpeg": "image/jpeg",
763
- ".gif": "image/gif",
764
- ".svg": "image/svg+xml",
765
- ".ico": "image/x-icon",
766
- ".pdf": "application/pdf",
767
- ".txt": "text/plain",
768
- ".xml": "application/xml",
703
+ '.html': 'text/html',
704
+ '.css': 'text/css',
705
+ '.js': 'application/javascript',
706
+ '.json': 'application/json',
707
+ '.png': 'image/png',
708
+ '.jpg': 'image/jpeg',
709
+ '.jpeg': 'image/jpeg',
710
+ '.gif': 'image/gif',
711
+ '.svg': 'image/svg+xml',
712
+ '.ico': 'image/x-icon',
713
+ '.pdf': 'application/pdf',
714
+ '.txt': 'text/plain',
715
+ '.xml': 'application/xml',
769
716
  };
770
717
 
771
- const baseMimeType =
772
- mimeTypes[ext.toLowerCase()] || "application/octet-stream";
718
+ const baseMimeType = mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
773
719
 
774
720
  // Add charset for text-based files
775
721
  const textTypes = [
776
- "text/",
777
- "application/json",
778
- "application/javascript",
779
- "application/xml",
780
- "image/svg+xml",
722
+ 'text/',
723
+ 'application/json',
724
+ 'application/javascript',
725
+ 'application/xml',
726
+ 'image/svg+xml',
781
727
  ];
782
- const needsCharset = textTypes.some((type) =>
783
- baseMimeType.startsWith(type),
784
- );
785
- const contentType = needsCharset
786
- ? `${baseMimeType}; charset=utf-8`
787
- : baseMimeType;
728
+ const needsCharset = textTypes.some(type => baseMimeType.startsWith(type));
729
+ const contentType = needsCharset ? `${baseMimeType}; charset=utf-8` : baseMimeType;
788
730
 
789
- res.setHeader("Content-Type", contentType);
790
- res.setHeader("Content-Length", stats.size);
731
+ res.setHeader('Content-Type', contentType);
732
+ res.setHeader('Content-Length', stats.size);
791
733
 
792
734
  // Cache headers
793
735
  if (options.maxAge) {
794
- res.setHeader("Cache-Control", `public, max-age=${options.maxAge}`);
736
+ res.setHeader('Cache-Control', `public, max-age=${options.maxAge}`);
795
737
  }
796
738
 
797
739
  // ETag support
798
740
  if (options.etag !== false) {
799
741
  const etag = crypto
800
- .createHash("md5")
742
+ .createHash('md5')
801
743
  .update(`${stats.mtime.getTime()}-${stats.size}`)
802
- .digest("hex");
803
- res.setHeader("ETag", `"${etag}"`);
744
+ .digest('hex');
745
+ res.setHeader('ETag', `"${etag}"`);
804
746
 
805
747
  // Handle conditional requests
806
- const ifNoneMatch = req.headers["if-none-match"];
748
+ const ifNoneMatch = req.headers['if-none-match'];
807
749
  if (ifNoneMatch === `"${etag}"`) {
808
750
  res.statusCode = 304;
809
751
  res.end();
@@ -812,7 +754,7 @@ export const middleware = {
812
754
  }
813
755
 
814
756
  // Handle HEAD requests
815
- if (req.method === "HEAD") {
757
+ if (req.method === 'HEAD') {
816
758
  res.end();
817
759
  return;
818
760
  }
@@ -821,9 +763,7 @@ export const middleware = {
821
763
  const data = await fs.readFile(filePath);
822
764
  res.end(data);
823
765
  } catch (error) {
824
- res
825
- .status(500)
826
- .json({ success: false, error: "Internal server error" });
766
+ res.status(500).json({ success: false, error: 'Internal server error' });
827
767
  }
828
768
  };
829
769
  },
@@ -834,12 +774,12 @@ export const middleware = {
834
774
  maxFileSize?: number;
835
775
  maxFiles?: number;
836
776
  allowedTypes?: string[];
837
- } = {},
777
+ } = {}
838
778
  ): Middleware => {
839
779
  return (req, res, next) => {
840
- const contentType = req.headers["content-type"] || "";
780
+ const contentType = req.headers['content-type'] || '';
841
781
 
842
- if (!contentType.includes("multipart/form-data")) {
782
+ if (!contentType.includes('multipart/form-data')) {
843
783
  next();
844
784
  return;
845
785
  }
@@ -894,7 +834,7 @@ export const middleware = {
894
834
 
895
835
  template: (options: {
896
836
  views: string;
897
- engine?: "moro" | "handlebars" | "ejs";
837
+ engine?: 'moro' | 'handlebars' | 'ejs';
898
838
  cache?: boolean;
899
839
  defaultLayout?: string;
900
840
  }): Middleware => {
@@ -904,8 +844,8 @@ export const middleware = {
904
844
  // Add render method to response
905
845
  res.render = async (template: string, data: any = {}) => {
906
846
  try {
907
- const fs = await import("fs/promises");
908
- const path = await import("path");
847
+ const fs = await import('fs/promises');
848
+ const path = await import('path');
909
849
 
910
850
  const templatePath = path.join(options.views, `${template}.html`);
911
851
 
@@ -915,7 +855,7 @@ export const middleware = {
915
855
  if (options.cache && templateCache.has(templatePath)) {
916
856
  templateContent = templateCache.get(templatePath)!;
917
857
  } else {
918
- templateContent = await fs.readFile(templatePath, "utf-8");
858
+ templateContent = await fs.readFile(templatePath, 'utf-8');
919
859
  if (options.cache) {
920
860
  templateCache.set(templatePath, templateContent);
921
861
  }
@@ -925,47 +865,37 @@ export const middleware = {
925
865
  let rendered = templateContent;
926
866
 
927
867
  // Handle basic variable substitution
928
- rendered = rendered.replace(
929
- /\{\{(\w+)\}\}/g,
930
- (match: string, key: string) => {
931
- return data[key] !== undefined ? String(data[key]) : match;
932
- },
933
- );
868
+ rendered = rendered.replace(/\{\{(\w+)\}\}/g, (match: string, key: string) => {
869
+ return data[key] !== undefined ? String(data[key]) : match;
870
+ });
934
871
 
935
872
  // Handle nested object properties like {{user.name}}
936
- rendered = rendered.replace(
937
- /\{\{([\w.]+)\}\}/g,
938
- (match: string, key: string) => {
939
- const value = key
940
- .split(".")
941
- .reduce((obj: any, prop: string) => obj?.[prop], data);
942
- return value !== undefined ? String(value) : match;
943
- },
944
- );
873
+ rendered = rendered.replace(/\{\{([\w.]+)\}\}/g, (match: string, key: string) => {
874
+ const value = key.split('.').reduce((obj: any, prop: string) => obj?.[prop], data);
875
+ return value !== undefined ? String(value) : match;
876
+ });
945
877
 
946
878
  // Handle loops: {{#each items}}{{name}}{{/each}}
947
879
  rendered = rendered.replace(
948
880
  /\{\{#each (\w+)\}\}(.*?)\{\{\/each\}\}/gs,
949
881
  (match, arrayKey, template) => {
950
882
  const array = data[arrayKey];
951
- if (!Array.isArray(array)) return "";
883
+ if (!Array.isArray(array)) return '';
952
884
 
953
885
  return array
954
- .map((item) => {
886
+ .map(item => {
955
887
  let itemTemplate = template;
956
888
  // Replace variables in the loop template
957
889
  itemTemplate = itemTemplate.replace(
958
890
  /\{\{(\w+)\}\}/g,
959
891
  (match: string, key: string) => {
960
- return item[key] !== undefined
961
- ? String(item[key])
962
- : match;
963
- },
892
+ return item[key] !== undefined ? String(item[key]) : match;
893
+ }
964
894
  );
965
895
  return itemTemplate;
966
896
  })
967
- .join("");
968
- },
897
+ .join('');
898
+ }
969
899
  );
970
900
 
971
901
  // Handle conditionals: {{#if condition}}content{{/if}}
@@ -973,24 +903,20 @@ export const middleware = {
973
903
  /\{\{#if (\w+)\}\}(.*?)\{\{\/if\}\}/gs,
974
904
  (match, conditionKey, content) => {
975
905
  const condition = data[conditionKey];
976
- return condition ? content : "";
977
- },
906
+ return condition ? content : '';
907
+ }
978
908
  );
979
909
 
980
910
  // Handle layout
981
911
  if (options.defaultLayout) {
982
- const layoutPath = path.join(
983
- options.views,
984
- "layouts",
985
- `${options.defaultLayout}.html`,
986
- );
912
+ const layoutPath = path.join(options.views, 'layouts', `${options.defaultLayout}.html`);
987
913
  try {
988
914
  let layoutContent: string;
989
915
 
990
916
  if (options.cache && templateCache.has(layoutPath)) {
991
917
  layoutContent = templateCache.get(layoutPath)!;
992
918
  } else {
993
- layoutContent = await fs.readFile(layoutPath, "utf-8");
919
+ layoutContent = await fs.readFile(layoutPath, 'utf-8');
994
920
  if (options.cache) {
995
921
  templateCache.set(layoutPath, layoutContent);
996
922
  }
@@ -1002,12 +928,10 @@ export const middleware = {
1002
928
  }
1003
929
  }
1004
930
 
1005
- res.setHeader("Content-Type", "text/html");
931
+ res.setHeader('Content-Type', 'text/html');
1006
932
  res.end(rendered);
1007
933
  } catch (error) {
1008
- res
1009
- .status(500)
1010
- .json({ success: false, error: "Template rendering failed" });
934
+ res.status(500).json({ success: false, error: 'Template rendering failed' });
1011
935
  }
1012
936
  };
1013
937
 
@@ -1020,21 +944,17 @@ export const middleware = {
1020
944
  options: {
1021
945
  resources?: Array<{ path: string; as: string; type?: string }>;
1022
946
  condition?: (req: any) => boolean;
1023
- } = {},
947
+ } = {}
1024
948
  ): Middleware => {
1025
949
  return (req, res, next) => {
1026
950
  // Add HTTP/2 push capability to response
1027
951
  (res as any).push = (path: string, options: any = {}) => {
1028
952
  // Check if HTTP/2 is supported
1029
- if (
1030
- req.httpVersion === "2.0" &&
1031
- (res as any).stream &&
1032
- (res as any).stream.pushAllowed
1033
- ) {
953
+ if (req.httpVersion === '2.0' && (res as any).stream && (res as any).stream.pushAllowed) {
1034
954
  try {
1035
955
  const pushStream = (res as any).stream.pushStream({
1036
- ":method": "GET",
1037
- ":path": path,
956
+ ':method': 'GET',
957
+ ':path': path,
1038
958
  ...options.headers,
1039
959
  });
1040
960
 
@@ -1054,7 +974,7 @@ export const middleware = {
1054
974
  for (const resource of options.resources) {
1055
975
  (res as any).push?.(resource.path, {
1056
976
  headers: {
1057
- "content-type": resource.type || "text/plain",
977
+ 'content-type': resource.type || 'text/plain',
1058
978
  },
1059
979
  });
1060
980
  }
@@ -1070,29 +990,25 @@ export const middleware = {
1070
990
  heartbeat?: number;
1071
991
  retry?: number;
1072
992
  cors?: boolean;
1073
- } = {},
993
+ } = {}
1074
994
  ): Middleware => {
1075
995
  return (req, res, next) => {
1076
996
  // Only handle SSE requests
1077
- if (req.headers.accept?.includes("text/event-stream")) {
997
+ if (req.headers.accept?.includes('text/event-stream')) {
1078
998
  // Set SSE headers
1079
999
  res.writeHead(200, {
1080
- "Content-Type": "text/event-stream",
1081
- "Cache-Control": "no-cache",
1082
- Connection: "keep-alive",
1083
- "Access-Control-Allow-Origin": options.cors ? "*" : undefined,
1084
- "Access-Control-Allow-Headers": options.cors
1085
- ? "Cache-Control"
1086
- : undefined,
1000
+ 'Content-Type': 'text/event-stream',
1001
+ 'Cache-Control': 'no-cache',
1002
+ Connection: 'keep-alive',
1003
+ 'Access-Control-Allow-Origin': options.cors ? '*' : undefined,
1004
+ 'Access-Control-Allow-Headers': options.cors ? 'Cache-Control' : undefined,
1087
1005
  });
1088
1006
 
1089
1007
  // Add SSE methods to response
1090
1008
  (res as any).sendEvent = (data: any, event?: string, id?: string) => {
1091
1009
  if (id) res.write(`id: ${id}\n`);
1092
1010
  if (event) res.write(`event: ${event}\n`);
1093
- res.write(
1094
- `data: ${typeof data === "string" ? data : JSON.stringify(data)}\n\n`,
1095
- );
1011
+ res.write(`data: ${typeof data === 'string' ? data : JSON.stringify(data)}\n\n`);
1096
1012
  };
1097
1013
 
1098
1014
  (res as any).sendComment = (comment: string) => {
@@ -1107,7 +1023,7 @@ export const middleware = {
1107
1023
  let heartbeatInterval: NodeJS.Timeout | null = null;
1108
1024
  if (options.heartbeat) {
1109
1025
  heartbeatInterval = setInterval(() => {
1110
- (res as any).sendComment("heartbeat");
1026
+ (res as any).sendComment('heartbeat');
1111
1027
  }, options.heartbeat);
1112
1028
  }
1113
1029
 
@@ -1117,7 +1033,7 @@ export const middleware = {
1117
1033
  }
1118
1034
 
1119
1035
  // Clean up on close
1120
- req.on("close", () => {
1036
+ req.on('close', () => {
1121
1037
  if (heartbeatInterval) {
1122
1038
  clearInterval(heartbeatInterval);
1123
1039
  }
@@ -1136,14 +1052,14 @@ export const middleware = {
1136
1052
  options: {
1137
1053
  acceptRanges?: string;
1138
1054
  maxRanges?: number;
1139
- } = {},
1055
+ } = {}
1140
1056
  ): Middleware => {
1141
1057
  return async (req, res, next) => {
1142
1058
  // Add range support to response
1143
1059
  (res as any).sendRange = async (filePath: string, stats?: any) => {
1144
1060
  try {
1145
- const fs = await import("fs/promises");
1146
- const path = await import("path");
1061
+ const fs = await import('fs/promises');
1062
+ const path = await import('path');
1147
1063
 
1148
1064
  if (!stats) {
1149
1065
  stats = await fs.stat(filePath);
@@ -1153,11 +1069,11 @@ export const middleware = {
1153
1069
  const range = req.headers.range;
1154
1070
 
1155
1071
  // Set Accept-Ranges header
1156
- res.setHeader("Accept-Ranges", options.acceptRanges || "bytes");
1072
+ res.setHeader('Accept-Ranges', options.acceptRanges || 'bytes');
1157
1073
 
1158
1074
  if (!range) {
1159
1075
  // No range requested, send entire file
1160
- res.setHeader("Content-Length", fileSize);
1076
+ res.setHeader('Content-Length', fileSize);
1161
1077
  const data = await fs.readFile(filePath);
1162
1078
  res.end(data);
1163
1079
  return;
@@ -1165,10 +1081,10 @@ export const middleware = {
1165
1081
 
1166
1082
  // Parse range header
1167
1083
  const ranges = range
1168
- .replace(/bytes=/, "")
1169
- .split(",")
1170
- .map((r) => {
1171
- const [start, end] = r.split("-");
1084
+ .replace(/bytes=/, '')
1085
+ .split(',')
1086
+ .map(r => {
1087
+ const [start, end] = r.split('-');
1172
1088
  return {
1173
1089
  start: start ? parseInt(start) : 0,
1174
1090
  end: end ? parseInt(end) : fileSize - 1,
@@ -1177,7 +1093,7 @@ export const middleware = {
1177
1093
 
1178
1094
  // Validate ranges
1179
1095
  if (options.maxRanges && ranges.length > options.maxRanges) {
1180
- res.status(416).json({ success: false, error: "Too many ranges" });
1096
+ res.status(416).json({ success: false, error: 'Too many ranges' });
1181
1097
  return;
1182
1098
  }
1183
1099
 
@@ -1187,45 +1103,40 @@ export const middleware = {
1187
1103
  const chunkSize = end - start + 1;
1188
1104
 
1189
1105
  if (start >= fileSize || end >= fileSize) {
1190
- res.status(416).setHeader("Content-Range", `bytes */${fileSize}`);
1191
- res.json({ success: false, error: "Range not satisfiable" });
1106
+ res.status(416).setHeader('Content-Range', `bytes */${fileSize}`);
1107
+ res.json({ success: false, error: 'Range not satisfiable' });
1192
1108
  return;
1193
1109
  }
1194
1110
 
1195
1111
  res.status(206);
1196
- res.setHeader("Content-Range", `bytes ${start}-${end}/${fileSize}`);
1197
- res.setHeader("Content-Length", chunkSize);
1112
+ res.setHeader('Content-Range', `bytes ${start}-${end}/${fileSize}`);
1113
+ res.setHeader('Content-Length', chunkSize);
1198
1114
 
1199
1115
  // Stream the range
1200
- const stream = require("fs").createReadStream(filePath, {
1116
+ const stream = require('fs').createReadStream(filePath, {
1201
1117
  start,
1202
1118
  end,
1203
1119
  });
1204
1120
  stream.pipe(res);
1205
1121
  } else {
1206
1122
  // Multiple ranges - multipart response
1207
- const boundary = "MULTIPART_BYTERANGES";
1123
+ const boundary = 'MULTIPART_BYTERANGES';
1208
1124
  res.status(206);
1209
- res.setHeader(
1210
- "Content-Type",
1211
- `multipart/byteranges; boundary=${boundary}`,
1212
- );
1125
+ res.setHeader('Content-Type', `multipart/byteranges; boundary=${boundary}`);
1213
1126
 
1214
1127
  for (const { start, end } of ranges) {
1215
1128
  if (start >= fileSize || end >= fileSize) continue;
1216
1129
 
1217
1130
  const chunkSize = end - start + 1;
1218
1131
  res.write(`\r\n--${boundary}\r\n`);
1219
- res.write(
1220
- `Content-Range: bytes ${start}-${end}/${fileSize}\r\n\r\n`,
1221
- );
1132
+ res.write(`Content-Range: bytes ${start}-${end}/${fileSize}\r\n\r\n`);
1222
1133
 
1223
- const stream = require("fs").createReadStream(filePath, {
1134
+ const stream = require('fs').createReadStream(filePath, {
1224
1135
  start,
1225
1136
  end,
1226
1137
  });
1227
- await new Promise((resolve) => {
1228
- stream.on("end", resolve);
1138
+ await new Promise(resolve => {
1139
+ stream.on('end', resolve);
1229
1140
  stream.pipe(res, { end: false });
1230
1141
  });
1231
1142
  }
@@ -1233,9 +1144,7 @@ export const middleware = {
1233
1144
  res.end();
1234
1145
  }
1235
1146
  } catch (error) {
1236
- res
1237
- .status(500)
1238
- .json({ success: false, error: "Range request failed" });
1147
+ res.status(500).json({ success: false, error: 'Range request failed' });
1239
1148
  }
1240
1149
  };
1241
1150
 
@@ -1252,17 +1161,17 @@ export const middleware = {
1252
1161
  headerName?: string;
1253
1162
  ignoreMethods?: string[];
1254
1163
  sameSite?: boolean;
1255
- } = {},
1164
+ } = {}
1256
1165
  ): Middleware => {
1257
- const secret = options.secret || "moro-csrf-secret";
1166
+ const secret = options.secret || 'moro-csrf-secret';
1258
1167
  const tokenLength = options.tokenLength || 32;
1259
- const cookieName = options.cookieName || "_csrf";
1260
- const headerName = options.headerName || "x-csrf-token";
1261
- const ignoreMethods = options.ignoreMethods || ["GET", "HEAD", "OPTIONS"];
1168
+ const cookieName = options.cookieName || '_csrf';
1169
+ const headerName = options.headerName || 'x-csrf-token';
1170
+ const ignoreMethods = options.ignoreMethods || ['GET', 'HEAD', 'OPTIONS'];
1262
1171
 
1263
1172
  const generateToken = () => {
1264
- const crypto = require("crypto");
1265
- return crypto.randomBytes(tokenLength).toString("hex");
1173
+ const crypto = require('crypto');
1174
+ return crypto.randomBytes(tokenLength).toString('hex');
1266
1175
  };
1267
1176
 
1268
1177
  const verifyToken = (token: string, sessionToken: string) => {
@@ -1277,10 +1186,8 @@ export const middleware = {
1277
1186
  // Set token in cookie
1278
1187
  res.cookie(cookieName, (req as any)._csrfToken, {
1279
1188
  httpOnly: true,
1280
- sameSite: options.sameSite !== false ? "strict" : undefined,
1281
- secure:
1282
- req.headers["x-forwarded-proto"] === "https" ||
1283
- (req.socket as any).encrypted,
1189
+ sameSite: options.sameSite !== false ? 'strict' : undefined,
1190
+ secure: req.headers['x-forwarded-proto'] === 'https' || (req.socket as any).encrypted,
1284
1191
  });
1285
1192
  }
1286
1193
  return (req as any)._csrfToken;
@@ -1294,18 +1201,16 @@ export const middleware = {
1294
1201
 
1295
1202
  // Get token from header or body
1296
1203
  const token =
1297
- req.headers[headerName] ||
1298
- (req.body && req.body._csrf) ||
1299
- (req.query && req.query._csrf);
1204
+ req.headers[headerName] || (req.body && req.body._csrf) || (req.query && req.query._csrf);
1300
1205
 
1301
1206
  // Get session token from cookie
1302
1207
  const sessionToken = req.cookies?.[cookieName];
1303
1208
 
1304
- if (!verifyToken(token as string, sessionToken || "")) {
1209
+ if (!verifyToken(token as string, sessionToken || '')) {
1305
1210
  res.status(403).json({
1306
1211
  success: false,
1307
- error: "Invalid CSRF token",
1308
- code: "CSRF_TOKEN_MISMATCH",
1212
+ error: 'Invalid CSRF token',
1213
+ code: 'CSRF_TOKEN_MISMATCH',
1309
1214
  });
1310
1215
  return;
1311
1216
  }
@@ -1336,14 +1241,14 @@ export const middleware = {
1336
1241
  reportOnly?: boolean;
1337
1242
  reportUri?: string;
1338
1243
  nonce?: boolean;
1339
- } = {},
1244
+ } = {}
1340
1245
  ): Middleware => {
1341
1246
  return (req, res, next) => {
1342
1247
  const directives = options.directives || {
1343
1248
  defaultSrc: ["'self'"],
1344
1249
  scriptSrc: ["'self'"],
1345
1250
  styleSrc: ["'self'", "'unsafe-inline'"],
1346
- imgSrc: ["'self'", "data:", "https:"],
1251
+ imgSrc: ["'self'", 'data:', 'https:'],
1347
1252
  connectSrc: ["'self'"],
1348
1253
  fontSrc: ["'self'"],
1349
1254
  objectSrc: ["'none'"],
@@ -1354,8 +1259,8 @@ export const middleware = {
1354
1259
  // Generate nonce if requested
1355
1260
  let nonce: string | undefined;
1356
1261
  if (options.nonce) {
1357
- const crypto = require("crypto");
1358
- nonce = crypto.randomBytes(16).toString("base64");
1262
+ const crypto = require('crypto');
1263
+ nonce = crypto.randomBytes(16).toString('base64');
1359
1264
  (req as any).cspNonce = nonce;
1360
1265
  }
1361
1266
 
@@ -1363,25 +1268,20 @@ export const middleware = {
1363
1268
  const cspParts: string[] = [];
1364
1269
 
1365
1270
  for (const [directive, sources] of Object.entries(directives)) {
1366
- if (directive === "upgradeInsecureRequests" && sources === true) {
1367
- cspParts.push("upgrade-insecure-requests");
1368
- } else if (directive === "blockAllMixedContent" && sources === true) {
1369
- cspParts.push("block-all-mixed-content");
1271
+ if (directive === 'upgradeInsecureRequests' && sources === true) {
1272
+ cspParts.push('upgrade-insecure-requests');
1273
+ } else if (directive === 'blockAllMixedContent' && sources === true) {
1274
+ cspParts.push('block-all-mixed-content');
1370
1275
  } else if (Array.isArray(sources)) {
1371
- let sourceList = sources.join(" ");
1276
+ let sourceList = sources.join(' ');
1372
1277
 
1373
1278
  // Add nonce to script-src and style-src if enabled
1374
- if (
1375
- nonce &&
1376
- (directive === "scriptSrc" || directive === "styleSrc")
1377
- ) {
1279
+ if (nonce && (directive === 'scriptSrc' || directive === 'styleSrc')) {
1378
1280
  sourceList += ` 'nonce-${nonce}'`;
1379
1281
  }
1380
1282
 
1381
1283
  // Convert camelCase to kebab-case
1382
- const kebabDirective = directive
1383
- .replace(/([A-Z])/g, "-$1")
1384
- .toLowerCase();
1284
+ const kebabDirective = directive.replace(/([A-Z])/g, '-$1').toLowerCase();
1385
1285
  cspParts.push(`${kebabDirective} ${sourceList}`);
1386
1286
  }
1387
1287
  }
@@ -1391,10 +1291,10 @@ export const middleware = {
1391
1291
  cspParts.push(`report-uri ${options.reportUri}`);
1392
1292
  }
1393
1293
 
1394
- const cspValue = cspParts.join("; ");
1294
+ const cspValue = cspParts.join('; ');
1395
1295
  const headerName = options.reportOnly
1396
- ? "Content-Security-Policy-Report-Only"
1397
- : "Content-Security-Policy";
1296
+ ? 'Content-Security-Policy-Report-Only'
1297
+ : 'Content-Security-Policy';
1398
1298
 
1399
1299
  res.setHeader(headerName, cspValue);
1400
1300
 
@@ -1415,7 +1315,7 @@ function parseSize(size: string): number {
1415
1315
  if (!match) return 1024 * 1024; // Default 1MB
1416
1316
 
1417
1317
  const value = parseFloat(match[1]);
1418
- const unit = match[2] || "b";
1318
+ const unit = match[2] || 'b';
1419
1319
 
1420
1320
  return Math.round(value * units[unit]);
1421
1321
  }