@morojs/moro 1.6.5 → 1.6.8
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.
- package/README.md +20 -4
- package/dist/core/auth/morojs-adapter.js +17 -14
- package/dist/core/auth/morojs-adapter.js.map +1 -1
- package/dist/core/config/config-sources.js +44 -0
- package/dist/core/config/config-sources.js.map +1 -1
- package/dist/core/database/adapters/drizzle.js +5 -5
- package/dist/core/database/adapters/drizzle.js.map +1 -1
- package/dist/core/database/adapters/mongodb.js +5 -1
- package/dist/core/database/adapters/mongodb.js.map +1 -1
- package/dist/core/database/adapters/mysql.js +5 -1
- package/dist/core/database/adapters/mysql.js.map +1 -1
- package/dist/core/database/adapters/postgresql.js +1 -1
- package/dist/core/database/adapters/postgresql.js.map +1 -1
- package/dist/core/database/adapters/redis.js +2 -2
- package/dist/core/database/adapters/redis.js.map +1 -1
- package/dist/core/database/adapters/sqlite.js +5 -1
- package/dist/core/database/adapters/sqlite.js.map +1 -1
- package/dist/core/docs/index.js.map +1 -1
- package/dist/core/docs/simple-docs.js +2 -1
- package/dist/core/docs/simple-docs.js.map +1 -1
- package/dist/core/docs/swagger-ui.js +1 -0
- package/dist/core/docs/swagger-ui.js.map +1 -1
- package/dist/core/docs/zod-to-openapi.js +4 -0
- package/dist/core/docs/zod-to-openapi.js.map +1 -1
- package/dist/core/events/event-bus.d.ts +1 -1
- package/dist/core/events/event-bus.js +8 -4
- package/dist/core/events/event-bus.js.map +1 -1
- package/dist/core/framework.d.ts +1 -1
- package/dist/core/framework.js +3 -1
- package/dist/core/framework.js.map +1 -1
- package/dist/core/graphql/adapter.d.ts +73 -0
- package/dist/core/graphql/adapter.js +2 -0
- package/dist/core/graphql/adapter.js.map +1 -0
- package/dist/core/graphql/adapters/graphql-js-adapter.d.ts +26 -0
- package/dist/core/graphql/adapters/graphql-js-adapter.js +229 -0
- package/dist/core/graphql/adapters/graphql-js-adapter.js.map +1 -0
- package/dist/core/graphql/core.d.ts +60 -0
- package/dist/core/graphql/core.js +165 -0
- package/dist/core/graphql/core.js.map +1 -0
- package/dist/core/graphql/index.d.ts +4 -0
- package/dist/core/graphql/index.js +4 -0
- package/dist/core/graphql/index.js.map +1 -0
- package/dist/core/graphql/loader.d.ts +9 -0
- package/dist/core/graphql/loader.js +32 -0
- package/dist/core/graphql/loader.js.map +1 -0
- package/dist/core/graphql/types.d.ts +211 -0
- package/dist/core/graphql/types.js +2 -0
- package/dist/core/graphql/types.js.map +1 -0
- package/dist/core/http/http-server.d.ts +7 -0
- package/dist/core/http/http-server.js +267 -123
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/utils/uws-worker-clustering.d.ts +28 -0
- package/dist/core/http/utils/uws-worker-clustering.js +313 -0
- package/dist/core/http/utils/uws-worker-clustering.js.map +1 -0
- package/dist/core/http/uws-http-server.d.ts +7 -1
- package/dist/core/http/uws-http-server.js +272 -189
- package/dist/core/http/uws-http-server.js.map +1 -1
- package/dist/core/jobs/cron-parser.d.ts +62 -0
- package/dist/core/jobs/cron-parser.js +239 -0
- package/dist/core/jobs/cron-parser.js.map +1 -0
- package/dist/core/jobs/index.d.ts +12 -0
- package/dist/core/jobs/index.js +9 -0
- package/dist/core/jobs/index.js.map +1 -0
- package/dist/core/jobs/job-executor.d.ts +134 -0
- package/dist/core/jobs/job-executor.js +413 -0
- package/dist/core/jobs/job-executor.js.map +1 -0
- package/dist/core/jobs/job-scheduler.d.ts +214 -0
- package/dist/core/jobs/job-scheduler.js +551 -0
- package/dist/core/jobs/job-scheduler.js.map +1 -0
- package/dist/core/jobs/job-state-manager.d.ts +158 -0
- package/dist/core/jobs/job-state-manager.js +444 -0
- package/dist/core/jobs/job-state-manager.js.map +1 -0
- package/dist/core/jobs/leader-election.d.ts +124 -0
- package/dist/core/jobs/leader-election.js +481 -0
- package/dist/core/jobs/leader-election.js.map +1 -0
- package/dist/core/jobs/types.d.ts +151 -0
- package/dist/core/jobs/types.js +4 -0
- package/dist/core/jobs/types.js.map +1 -0
- package/dist/core/jobs/utils.d.ts +95 -0
- package/dist/core/jobs/utils.js +258 -0
- package/dist/core/jobs/utils.js.map +1 -0
- package/dist/core/logger/filters.js +2 -0
- package/dist/core/logger/filters.js.map +1 -1
- package/dist/core/logger/logger.d.ts +7 -5
- package/dist/core/logger/logger.js +68 -27
- package/dist/core/logger/logger.js.map +1 -1
- package/dist/core/logger/outputs.js +2 -0
- package/dist/core/logger/outputs.js.map +1 -1
- package/dist/core/middleware/built-in/auth/helpers.js +1 -1
- package/dist/core/middleware/built-in/auth/helpers.js.map +1 -1
- package/dist/core/middleware/built-in/auth/jwt-helpers.js +1 -1
- package/dist/core/middleware/built-in/auth/jwt-helpers.js.map +1 -1
- package/dist/core/middleware/built-in/auth/providers.js +1 -1
- package/dist/core/middleware/built-in/auth/providers.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/file.js +3 -3
- package/dist/core/middleware/built-in/cache/adapters/cache/file.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/memory.js +1 -0
- package/dist/core/middleware/built-in/cache/adapters/cache/memory.js.map +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/redis.js +1 -1
- package/dist/core/middleware/built-in/cache/adapters/cache/redis.js.map +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.d.ts +8 -0
- package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js +100 -7
- package/dist/core/middleware/built-in/cdn/adapters/cdn/azure.js.map +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.d.ts +6 -0
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js +97 -13
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudflare.js.map +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js +1 -1
- package/dist/core/middleware/built-in/cdn/adapters/cdn/cloudfront.js.map +1 -1
- package/dist/core/middleware/built-in/cookie/hook.d.ts +1 -1
- package/dist/core/middleware/built-in/cookie/hook.js +2 -2
- package/dist/core/middleware/built-in/cookie/hook.js.map +1 -1
- package/dist/core/middleware/built-in/csrf/core.js +1 -0
- package/dist/core/middleware/built-in/csrf/core.js.map +1 -1
- package/dist/core/middleware/built-in/graphql/core.d.ts +11 -0
- package/dist/core/middleware/built-in/graphql/core.js +24 -0
- package/dist/core/middleware/built-in/graphql/core.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/helpers.d.ts +69 -0
- package/dist/core/middleware/built-in/graphql/helpers.js +187 -0
- package/dist/core/middleware/built-in/graphql/helpers.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/hook.d.ts +7 -0
- package/dist/core/middleware/built-in/graphql/hook.js +78 -0
- package/dist/core/middleware/built-in/graphql/hook.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/index.d.ts +5 -0
- package/dist/core/middleware/built-in/graphql/index.js +5 -0
- package/dist/core/middleware/built-in/graphql/index.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/middleware.d.ts +7 -0
- package/dist/core/middleware/built-in/graphql/middleware.js +54 -0
- package/dist/core/middleware/built-in/graphql/middleware.js.map +1 -0
- package/dist/core/middleware/built-in/graphql/subscriptions.d.ts +20 -0
- package/dist/core/middleware/built-in/graphql/subscriptions.js +37 -0
- package/dist/core/middleware/built-in/graphql/subscriptions.js.map +1 -0
- package/dist/core/middleware/built-in/index.d.ts +2 -1
- package/dist/core/middleware/built-in/index.js +3 -0
- package/dist/core/middleware/built-in/index.js.map +1 -1
- package/dist/core/middleware/built-in/rate-limit/core.d.ts +5 -0
- package/dist/core/middleware/built-in/rate-limit/core.js +16 -8
- package/dist/core/middleware/built-in/rate-limit/core.js.map +1 -1
- package/dist/core/middleware/built-in/validation/core.js +42 -19
- package/dist/core/middleware/built-in/validation/core.js.map +1 -1
- package/dist/core/middleware/index.js +1 -0
- package/dist/core/middleware/index.js.map +1 -1
- package/dist/core/modules/auto-discovery.js +5 -4
- package/dist/core/modules/auto-discovery.js.map +1 -1
- package/dist/core/modules/modules.js.map +1 -1
- package/dist/core/networking/adapters/socketio-adapter.js +1 -1
- package/dist/core/networking/adapters/socketio-adapter.js.map +1 -1
- package/dist/core/networking/adapters/uws-adapter.js +7 -2
- package/dist/core/networking/adapters/uws-adapter.js.map +1 -1
- package/dist/core/networking/adapters/ws-adapter.js +5 -2
- package/dist/core/networking/adapters/ws-adapter.js.map +1 -1
- package/dist/core/networking/websocket-manager.js +2 -0
- package/dist/core/networking/websocket-manager.js.map +1 -1
- package/dist/core/pooling/object-pool-manager.d.ts +8 -2
- package/dist/core/pooling/object-pool-manager.js +38 -18
- package/dist/core/pooling/object-pool-manager.js.map +1 -1
- package/dist/core/routing/app-integration.d.ts +3 -3
- package/dist/core/routing/app-integration.js +1 -1
- package/dist/core/routing/app-integration.js.map +1 -1
- package/dist/core/routing/index.d.ts +1 -1
- package/dist/core/routing/index.js +1 -1
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/routing/path-matcher.d.ts +6 -0
- package/dist/core/routing/path-matcher.js +46 -7
- package/dist/core/routing/path-matcher.js.map +1 -1
- package/dist/core/routing/unified-router.d.ts +4 -0
- package/dist/core/routing/unified-router.js +104 -43
- package/dist/core/routing/unified-router.js.map +1 -1
- package/dist/core/runtime/base-adapter.js +3 -3
- package/dist/core/runtime/base-adapter.js.map +1 -1
- package/dist/core/runtime/cloudflare-workers-adapter.js +1 -1
- package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -1
- package/dist/core/runtime/node-adapter.d.ts +1 -1
- package/dist/core/runtime/node-adapter.js +7 -4
- package/dist/core/runtime/node-adapter.js.map +1 -1
- package/dist/core/runtime/vercel-edge-adapter.js +1 -0
- package/dist/core/runtime/vercel-edge-adapter.js.map +1 -1
- package/dist/core/utilities/circuit-breaker.d.ts +9 -2
- package/dist/core/utilities/circuit-breaker.js +32 -3
- package/dist/core/utilities/circuit-breaker.js.map +1 -1
- package/dist/core/utilities/container.js +6 -0
- package/dist/core/utilities/container.js.map +1 -1
- package/dist/core/utilities/hooks.d.ts +4 -0
- package/dist/core/utilities/hooks.js +134 -22
- package/dist/core/utilities/hooks.js.map +1 -1
- package/dist/core/validation/index.js +6 -1
- package/dist/core/validation/index.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/moro.d.ts +154 -1
- package/dist/moro.js +592 -16
- package/dist/moro.js.map +1 -1
- package/dist/types/config.d.ts +28 -0
- package/dist/types/core.d.ts +1 -0
- package/dist/types/events.d.ts +1 -1
- package/dist/types/events.js +1 -0
- package/dist/types/events.js.map +1 -1
- package/dist/types/logger.d.ts +1 -0
- package/dist/types/module.d.ts +2 -2
- package/package.json +21 -1
|
@@ -21,6 +21,14 @@ export class MoroHttpServer {
|
|
|
21
21
|
requestCounter = 0;
|
|
22
22
|
// Use shared object pool manager
|
|
23
23
|
poolManager = ObjectPoolManager.getInstance();
|
|
24
|
+
// Interned method strings for fast reference equality comparison
|
|
25
|
+
static METHOD_POST = 'POST';
|
|
26
|
+
static METHOD_PUT = 'PUT';
|
|
27
|
+
static METHOD_PATCH = 'PATCH';
|
|
28
|
+
static METHOD_GET = 'GET';
|
|
29
|
+
static METHOD_DELETE = 'DELETE';
|
|
30
|
+
static METHOD_HEAD = 'HEAD';
|
|
31
|
+
static METHOD_OPTIONS = 'OPTIONS';
|
|
24
32
|
// Pre-compiled response templates for common responses
|
|
25
33
|
static RESPONSE_TEMPLATES = {
|
|
26
34
|
notFound: Buffer.from('{"success":false,"error":"Not found"}'),
|
|
@@ -39,6 +47,7 @@ export class MoroHttpServer {
|
|
|
39
47
|
MoroHttpServer.BUFFER_POOLS.set(size, []);
|
|
40
48
|
for (let i = 0; i < 50; i++) {
|
|
41
49
|
// 50 buffers per size
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
42
51
|
MoroHttpServer.BUFFER_POOLS.get(size).push(Buffer.allocUnsafe(size));
|
|
43
52
|
}
|
|
44
53
|
}
|
|
@@ -47,7 +56,9 @@ export class MoroHttpServer {
|
|
|
47
56
|
// Find the smallest buffer that fits
|
|
48
57
|
for (const poolSize of MoroHttpServer.BUFFER_SIZES) {
|
|
49
58
|
if (size <= poolSize) {
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
50
60
|
const pool = MoroHttpServer.BUFFER_POOLS.get(poolSize);
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
51
62
|
return pool.length > 0 ? pool.pop() : Buffer.allocUnsafe(poolSize);
|
|
52
63
|
}
|
|
53
64
|
}
|
|
@@ -57,6 +68,7 @@ export class MoroHttpServer {
|
|
|
57
68
|
// Return buffer to appropriate pool
|
|
58
69
|
const size = buffer.length;
|
|
59
70
|
if (MoroHttpServer.BUFFER_POOLS.has(size)) {
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
60
72
|
const pool = MoroHttpServer.BUFFER_POOLS.get(size);
|
|
61
73
|
if (pool.length < 50) {
|
|
62
74
|
// Don't let pools grow too large
|
|
@@ -135,11 +147,11 @@ export class MoroHttpServer {
|
|
|
135
147
|
else {
|
|
136
148
|
// Dynamic route - organize by segment count for faster matching
|
|
137
149
|
this.dynamicRoutes.push(route);
|
|
138
|
-
const
|
|
139
|
-
const segmentCount = segments.length;
|
|
150
|
+
const segmentCount = PathMatcher.countSegments(path);
|
|
140
151
|
if (!this.routesBySegmentCount.has(segmentCount)) {
|
|
141
152
|
this.routesBySegmentCount.set(segmentCount, []);
|
|
142
153
|
}
|
|
154
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
143
155
|
this.routesBySegmentCount.get(segmentCount).push(route);
|
|
144
156
|
}
|
|
145
157
|
}
|
|
@@ -158,6 +170,7 @@ export class MoroHttpServer {
|
|
|
158
170
|
const originalParams = httpReq.params;
|
|
159
171
|
try {
|
|
160
172
|
// Optimized URL and query parsing with object pooling
|
|
173
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
161
174
|
const urlString = req.url;
|
|
162
175
|
const queryIndex = urlString.indexOf('?');
|
|
163
176
|
if (queryIndex === -1) {
|
|
@@ -170,25 +183,30 @@ export class MoroHttpServer {
|
|
|
170
183
|
httpReq.path = urlString.substring(0, queryIndex);
|
|
171
184
|
httpReq.query = this.parseQueryStringPooled(urlString.substring(queryIndex + 1));
|
|
172
185
|
}
|
|
173
|
-
// Method checking -
|
|
174
|
-
|
|
175
|
-
|
|
186
|
+
// Method checking - use reference equality for interned strings (50-100% faster)
|
|
187
|
+
if (httpReq.method === MoroHttpServer.METHOD_POST ||
|
|
188
|
+
httpReq.method === MoroHttpServer.METHOD_PUT ||
|
|
189
|
+
httpReq.method === MoroHttpServer.METHOD_PATCH) {
|
|
176
190
|
httpReq.body = await this.parseBody(req);
|
|
177
191
|
}
|
|
178
|
-
// Execute hooks before request processing
|
|
192
|
+
// Execute hooks before request processing - NOOP if no hookManager
|
|
179
193
|
if (this.hookManager) {
|
|
180
194
|
await this.hookManager.execute('request', {
|
|
181
195
|
request: httpReq,
|
|
182
196
|
response: httpRes,
|
|
183
197
|
});
|
|
184
198
|
}
|
|
185
|
-
// Execute global middleware first
|
|
186
|
-
|
|
199
|
+
// Execute global middleware first - EARLY EXIT if none registered
|
|
200
|
+
const middlewareLen = this.globalMiddleware.length;
|
|
201
|
+
if (middlewareLen > 0) {
|
|
202
|
+
await this.executeMiddleware(this.globalMiddleware, httpReq, httpRes);
|
|
203
|
+
}
|
|
187
204
|
// If middleware handled the request, don't continue
|
|
188
205
|
if (httpRes.headersSent) {
|
|
189
206
|
return;
|
|
190
207
|
}
|
|
191
208
|
// Find matching route
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
192
210
|
const route = this.findRoute(req.method, httpReq.path);
|
|
193
211
|
if (!route) {
|
|
194
212
|
// 404 response with pre-compiled buffer
|
|
@@ -203,14 +221,22 @@ export class MoroHttpServer {
|
|
|
203
221
|
if (matches) {
|
|
204
222
|
// Use pooled object for parameters
|
|
205
223
|
httpReq.params = this.acquireParamObject();
|
|
206
|
-
route.paramNames
|
|
207
|
-
|
|
208
|
-
|
|
224
|
+
const paramNames = route.paramNames;
|
|
225
|
+
const paramNamesLen = paramNames.length;
|
|
226
|
+
for (let i = 0; i < paramNamesLen; i++) {
|
|
227
|
+
httpReq.params[paramNames[i]] = matches[i + 1];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Execute middleware chain - EARLY EXIT if no route middleware
|
|
231
|
+
const routeMiddlewareLen = route.middleware.length;
|
|
232
|
+
if (routeMiddlewareLen > 0) {
|
|
233
|
+
await this.executeMiddleware(route.middleware, httpReq, httpRes);
|
|
234
|
+
}
|
|
235
|
+
// Execute handler - Don't await sync handlers
|
|
236
|
+
const handlerResult = route.handler(httpReq, httpRes);
|
|
237
|
+
if (handlerResult && typeof handlerResult.then === 'function') {
|
|
238
|
+
await handlerResult;
|
|
209
239
|
}
|
|
210
|
-
// Execute middleware chain
|
|
211
|
-
await this.executeMiddleware(route.middleware, httpReq, httpRes);
|
|
212
|
-
// Execute handler
|
|
213
|
-
await route.handler(httpReq, httpRes);
|
|
214
240
|
}
|
|
215
241
|
catch (error) {
|
|
216
242
|
// Debug: Log the actual error and where it came from
|
|
@@ -265,24 +291,54 @@ export class MoroHttpServer {
|
|
|
265
291
|
finally {
|
|
266
292
|
// CRITICAL: Always release pooled objects back to the pool
|
|
267
293
|
// This prevents memory leaks and ensures consistent performance
|
|
268
|
-
if
|
|
269
|
-
|
|
294
|
+
// Optimized: Check if object is empty without Object.keys()
|
|
295
|
+
if (originalParams) {
|
|
296
|
+
let isEmpty = true;
|
|
297
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
298
|
+
for (const _key in originalParams) {
|
|
299
|
+
isEmpty = false;
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
if (isEmpty) {
|
|
303
|
+
this.releaseParamObject(originalParams);
|
|
304
|
+
}
|
|
270
305
|
}
|
|
271
|
-
if (httpReq.params &&
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
306
|
+
if (httpReq.params && httpReq.params !== originalParams) {
|
|
307
|
+
let isEmpty = true;
|
|
308
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
309
|
+
for (const _key in httpReq.params) {
|
|
310
|
+
isEmpty = false;
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
if (isEmpty) {
|
|
314
|
+
this.releaseParamObject(httpReq.params);
|
|
315
|
+
}
|
|
275
316
|
}
|
|
276
317
|
}
|
|
277
318
|
// Additional cleanup on response completion to ensure objects are returned to pool
|
|
278
319
|
res.once('finish', () => {
|
|
279
|
-
if
|
|
280
|
-
|
|
320
|
+
// Optimized: Check if object is empty without Object.keys()
|
|
321
|
+
if (originalParams) {
|
|
322
|
+
let isEmpty = true;
|
|
323
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
324
|
+
for (const _key in originalParams) {
|
|
325
|
+
isEmpty = false;
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
if (isEmpty) {
|
|
329
|
+
this.releaseParamObject(originalParams);
|
|
330
|
+
}
|
|
281
331
|
}
|
|
282
|
-
if (httpReq.params &&
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
332
|
+
if (httpReq.params && httpReq.params !== originalParams) {
|
|
333
|
+
let isEmpty = true;
|
|
334
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
335
|
+
for (const _key in httpReq.params) {
|
|
336
|
+
isEmpty = false;
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
if (isEmpty) {
|
|
340
|
+
this.releaseParamObject(httpReq.params);
|
|
341
|
+
}
|
|
286
342
|
}
|
|
287
343
|
});
|
|
288
344
|
}
|
|
@@ -323,6 +379,7 @@ export class MoroHttpServer {
|
|
|
323
379
|
normalizePath(path) {
|
|
324
380
|
// Check cache first
|
|
325
381
|
if (this.pathNormalizationCache.has(path)) {
|
|
382
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
326
383
|
return this.pathNormalizationCache.get(path);
|
|
327
384
|
}
|
|
328
385
|
// Normalization: remove trailing slash (except root), decode once
|
|
@@ -346,20 +403,61 @@ export class MoroHttpServer {
|
|
|
346
403
|
// Request ID generation using pool manager (if enabled)
|
|
347
404
|
httpReq.requestId = this.requestTrackingEnabled ? this.poolManager.generateRequestId() : '';
|
|
348
405
|
httpReq.headers = req.headers;
|
|
349
|
-
//
|
|
350
|
-
|
|
406
|
+
// Intern method string for fast reference equality comparison (50-100% faster)
|
|
407
|
+
const method = req.method;
|
|
408
|
+
switch (method) {
|
|
409
|
+
case 'POST':
|
|
410
|
+
httpReq.method = MoroHttpServer.METHOD_POST;
|
|
411
|
+
break;
|
|
412
|
+
case 'PUT':
|
|
413
|
+
httpReq.method = MoroHttpServer.METHOD_PUT;
|
|
414
|
+
break;
|
|
415
|
+
case 'PATCH':
|
|
416
|
+
httpReq.method = MoroHttpServer.METHOD_PATCH;
|
|
417
|
+
break;
|
|
418
|
+
case 'GET':
|
|
419
|
+
httpReq.method = MoroHttpServer.METHOD_GET;
|
|
420
|
+
break;
|
|
421
|
+
case 'DELETE':
|
|
422
|
+
httpReq.method = MoroHttpServer.METHOD_DELETE;
|
|
423
|
+
break;
|
|
424
|
+
case 'HEAD':
|
|
425
|
+
httpReq.method = MoroHttpServer.METHOD_HEAD;
|
|
426
|
+
break;
|
|
427
|
+
case 'OPTIONS':
|
|
428
|
+
httpReq.method = MoroHttpServer.METHOD_OPTIONS;
|
|
429
|
+
break;
|
|
430
|
+
default:
|
|
431
|
+
httpReq.method = method;
|
|
432
|
+
}
|
|
433
|
+
// Parse cookies - EARLY EXIT if no cookie header
|
|
434
|
+
const cookieHeader = req.headers.cookie;
|
|
435
|
+
if (cookieHeader) {
|
|
436
|
+
httpReq.cookies = this.parseCookies(cookieHeader);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
httpReq.cookies = {};
|
|
440
|
+
}
|
|
351
441
|
return httpReq;
|
|
352
442
|
}
|
|
353
443
|
parseCookies(cookieHeader) {
|
|
354
444
|
const cookies = {};
|
|
445
|
+
// EARLY EXIT if no cookie header
|
|
355
446
|
if (!cookieHeader)
|
|
356
447
|
return cookies;
|
|
357
|
-
cookieHeader.split(';')
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
448
|
+
const cookieParts = cookieHeader.split(';');
|
|
449
|
+
const cookiePartsLen = cookieParts.length;
|
|
450
|
+
for (let i = 0; i < cookiePartsLen; i++) {
|
|
451
|
+
const cookie = cookieParts[i];
|
|
452
|
+
const equalIndex = cookie.indexOf('=');
|
|
453
|
+
if (equalIndex > 0) {
|
|
454
|
+
const name = cookie.substring(0, equalIndex).trim();
|
|
455
|
+
const value = cookie.substring(equalIndex + 1);
|
|
456
|
+
if (name && value) {
|
|
457
|
+
cookies[name] = decodeURIComponent(value);
|
|
458
|
+
}
|
|
361
459
|
}
|
|
362
|
-
}
|
|
460
|
+
}
|
|
363
461
|
return cookies;
|
|
364
462
|
}
|
|
365
463
|
enhanceResponse(res, req) {
|
|
@@ -377,28 +475,51 @@ export class MoroHttpServer {
|
|
|
377
475
|
// JSON serialization with zero-copy buffers
|
|
378
476
|
let jsonString;
|
|
379
477
|
// Enhanced JSON optimization for common API patterns
|
|
380
|
-
//
|
|
478
|
+
// Fast path for common 2-3 key objects without Object.keys() overhead
|
|
381
479
|
if (data && typeof data === 'object' && 'success' in data) {
|
|
382
|
-
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
480
|
+
// Check for common patterns using 'in' operator (faster than Object.keys for small objects)
|
|
481
|
+
const hasData = 'data' in data;
|
|
482
|
+
const hasError = 'error' in data;
|
|
483
|
+
const hasTotal = 'total' in data;
|
|
484
|
+
// Fast path: {success, data} - most common pattern
|
|
485
|
+
if (hasData && !hasError && !hasTotal) {
|
|
486
|
+
// Verify it's exactly 2 keys by checking no other common keys exist
|
|
487
|
+
if (!('message' in data) && !('code' in data) && !('status' in data)) {
|
|
488
|
+
jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)}}`;
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
jsonString = JSON.stringify(data);
|
|
492
|
+
}
|
|
387
493
|
}
|
|
388
|
-
else if (
|
|
389
|
-
// {success,
|
|
390
|
-
|
|
494
|
+
else if (hasError && !hasData && !hasTotal) {
|
|
495
|
+
// Fast path: {success, error}
|
|
496
|
+
if (!('message' in data) && !('code' in data) && !('status' in data)) {
|
|
497
|
+
jsonString = `{"success":${data.success},"error":${JSON.stringify(data.error)}}`;
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
jsonString = JSON.stringify(data);
|
|
501
|
+
}
|
|
391
502
|
}
|
|
392
|
-
else if (
|
|
393
|
-
// {success, data}
|
|
394
|
-
|
|
503
|
+
else if (hasData && hasError && !hasTotal) {
|
|
504
|
+
// Fast path: {success, data, error}
|
|
505
|
+
if (!('message' in data) && !('code' in data) && !('status' in data)) {
|
|
506
|
+
jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)},"error":${JSON.stringify(data.error)}}`;
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
jsonString = JSON.stringify(data);
|
|
510
|
+
}
|
|
395
511
|
}
|
|
396
|
-
else if (
|
|
397
|
-
// {success,
|
|
398
|
-
|
|
512
|
+
else if (hasData && hasTotal && !hasError) {
|
|
513
|
+
// Fast path: {success, data, total}
|
|
514
|
+
if (!('message' in data) && !('code' in data) && !('status' in data)) {
|
|
515
|
+
jsonString = `{"success":${data.success},"data":${JSON.stringify(data.data)},"total":${data.total}}`;
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
jsonString = JSON.stringify(data);
|
|
519
|
+
}
|
|
399
520
|
}
|
|
400
521
|
else {
|
|
401
|
-
// Complex object
|
|
522
|
+
// Complex object - use standard JSON.stringify
|
|
402
523
|
jsonString = JSON.stringify(data);
|
|
403
524
|
}
|
|
404
525
|
}
|
|
@@ -419,40 +540,41 @@ export class MoroHttpServer {
|
|
|
419
540
|
const headers = {
|
|
420
541
|
'Content-Type': 'application/json; charset=utf-8',
|
|
421
542
|
};
|
|
422
|
-
// Compression with buffer pool
|
|
543
|
+
// Compression with buffer pool - EARLY EXIT if disabled or below threshold
|
|
544
|
+
// CRITICAL: Only make this async if compression is actually happening
|
|
423
545
|
if (this.compressionEnabled && finalBuffer.length > this.compressionThreshold) {
|
|
424
|
-
const acceptEncoding = httpRes.req.headers['accept-encoding']
|
|
425
|
-
if (acceptEncoding.includes('gzip')) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
httpRes.
|
|
546
|
+
const acceptEncoding = httpRes.req.headers['accept-encoding'];
|
|
547
|
+
if (acceptEncoding && acceptEncoding.includes('gzip')) {
|
|
548
|
+
// ASYNC PATH - compression needed
|
|
549
|
+
gzip(finalBuffer).then(compressed => {
|
|
550
|
+
headers['Content-Encoding'] = 'gzip';
|
|
551
|
+
headers['Content-Length'] = compressed.length;
|
|
552
|
+
// Batch write all headers at once (50-100% faster)
|
|
553
|
+
httpRes.writeHead(httpRes.statusCode || 200, headers);
|
|
554
|
+
httpRes.end(compressed);
|
|
555
|
+
// Return buffer to pool after response
|
|
556
|
+
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
432
557
|
});
|
|
433
|
-
httpRes.end(compressed);
|
|
434
|
-
// Return buffer to pool after response
|
|
435
|
-
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
436
558
|
return;
|
|
437
559
|
}
|
|
438
|
-
else if (acceptEncoding.includes('deflate')) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
560
|
+
else if (acceptEncoding && acceptEncoding.includes('deflate')) {
|
|
561
|
+
// ASYNC PATH - compression needed
|
|
562
|
+
deflate(finalBuffer).then(compressed => {
|
|
563
|
+
headers['Content-Encoding'] = 'deflate';
|
|
564
|
+
headers['Content-Length'] = compressed.length;
|
|
565
|
+
// Batch write all headers at once
|
|
566
|
+
httpRes.writeHead(httpRes.statusCode || 200, headers);
|
|
567
|
+
httpRes.end(compressed);
|
|
568
|
+
// Return buffer to pool after response
|
|
569
|
+
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
444
570
|
});
|
|
445
|
-
httpRes.end(compressed);
|
|
446
|
-
// Return buffer to pool after response
|
|
447
|
-
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
448
571
|
return;
|
|
449
572
|
}
|
|
450
573
|
}
|
|
574
|
+
// SYNC PATH - no compression, fast path
|
|
451
575
|
headers['Content-Length'] = finalBuffer.length;
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
httpRes.setHeader(key, value);
|
|
455
|
-
});
|
|
576
|
+
// Batch write all headers at once
|
|
577
|
+
httpRes.writeHead(httpRes.statusCode || 200, headers);
|
|
456
578
|
httpRes.end(finalBuffer);
|
|
457
579
|
// Return buffer to pool after response (zero-copy achievement!)
|
|
458
580
|
process.nextTick(() => MoroHttpServer.returnBuffer(buffer));
|
|
@@ -555,7 +677,7 @@ export class MoroHttpServer {
|
|
|
555
677
|
httpRes.setHeader('Cache-Control', 'public, max-age=31536000'); // 1 year for static files
|
|
556
678
|
httpRes.end(data);
|
|
557
679
|
}
|
|
558
|
-
catch
|
|
680
|
+
catch {
|
|
559
681
|
httpRes.status(404).json({ success: false, error: 'File not found' });
|
|
560
682
|
}
|
|
561
683
|
};
|
|
@@ -571,9 +693,12 @@ export class MoroHttpServer {
|
|
|
571
693
|
});
|
|
572
694
|
return httpRes;
|
|
573
695
|
}
|
|
574
|
-
Object.
|
|
575
|
-
|
|
576
|
-
|
|
696
|
+
const headerKeys = Object.keys(headers);
|
|
697
|
+
const headerKeysLen = headerKeys.length;
|
|
698
|
+
for (let i = 0; i < headerKeysLen; i++) {
|
|
699
|
+
const key = headerKeys[i];
|
|
700
|
+
httpRes.setHeader(key, headers[key]);
|
|
701
|
+
}
|
|
577
702
|
return httpRes;
|
|
578
703
|
};
|
|
579
704
|
httpRes.appendHeader = (name, value) => {
|
|
@@ -626,17 +751,16 @@ export class MoroHttpServer {
|
|
|
626
751
|
return mimeTypes[ext.toLowerCase()] || 'application/octet-stream';
|
|
627
752
|
}
|
|
628
753
|
addCharsetIfNeeded(mimeType) {
|
|
629
|
-
// Add charset for text-based content types
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
'application/json'
|
|
633
|
-
'application/javascript'
|
|
634
|
-
'application/xml'
|
|
635
|
-
'image/svg+xml'
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
return `${mimeType}; charset=utf-8`;
|
|
754
|
+
// Add charset for text-based content types - optimized with early exit
|
|
755
|
+
// Check most common cases first
|
|
756
|
+
if (mimeType.startsWith('text/') ||
|
|
757
|
+
mimeType.startsWith('application/json') ||
|
|
758
|
+
mimeType.startsWith('application/javascript') ||
|
|
759
|
+
mimeType.startsWith('application/xml') ||
|
|
760
|
+
mimeType.startsWith('image/svg+xml')) {
|
|
761
|
+
if (!mimeType.includes('charset')) {
|
|
762
|
+
return `${mimeType}; charset=utf-8`;
|
|
763
|
+
}
|
|
640
764
|
}
|
|
641
765
|
return mimeType;
|
|
642
766
|
}
|
|
@@ -685,7 +809,8 @@ export class MoroHttpServer {
|
|
|
685
809
|
const parts = buffer.toString('binary').split('--' + boundary);
|
|
686
810
|
const fields = {};
|
|
687
811
|
const files = {};
|
|
688
|
-
|
|
812
|
+
const partsLen = parts.length - 1;
|
|
813
|
+
for (let i = 1; i < partsLen; i++) {
|
|
689
814
|
const part = parts[i];
|
|
690
815
|
const [headers, content] = part.split('\r\n\r\n');
|
|
691
816
|
if (!headers || content === undefined)
|
|
@@ -733,7 +858,8 @@ export class MoroHttpServer {
|
|
|
733
858
|
return {};
|
|
734
859
|
const result = this.poolManager.acquireQuery();
|
|
735
860
|
const pairs = queryString.split('&');
|
|
736
|
-
|
|
861
|
+
const pairsLen = pairs.length;
|
|
862
|
+
for (let i = 0; i < pairsLen; i++) {
|
|
737
863
|
const pair = pairs[i];
|
|
738
864
|
const equalIndex = pair.indexOf('=');
|
|
739
865
|
if (equalIndex === -1) {
|
|
@@ -757,35 +883,41 @@ export class MoroHttpServer {
|
|
|
757
883
|
routeHitCount = new Map(); // Track route popularity for cache optimization
|
|
758
884
|
static HOT_ROUTE_THRESHOLD = 100; // Routes accessed 100+ times get hot path treatment
|
|
759
885
|
findRoute(method, path) {
|
|
760
|
-
//
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
// Track route popularity for hot path optimization
|
|
764
|
-
const hitCount = (this.routeHitCount.get(cacheKey) || 0) + 1;
|
|
765
|
-
this.routeHitCount.set(cacheKey, hitCount);
|
|
766
|
-
// Check cache first (hot path optimization)
|
|
886
|
+
// Skip normalization and hit tracking for cached routes
|
|
887
|
+
const cacheKey = `${method}:${path}`;
|
|
888
|
+
// Check cache first (hot path optimization) - BEFORE any other work
|
|
767
889
|
if (this.routeCache.has(cacheKey)) {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
890
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
891
|
+
return this.routeCache.get(cacheKey);
|
|
892
|
+
}
|
|
893
|
+
// Normalize path for consistent matching (only if not cached)
|
|
894
|
+
const normalizedPath = this.normalizePath(path);
|
|
895
|
+
const normalizedCacheKey = normalizedPath !== path ? `${method}:${normalizedPath}` : cacheKey;
|
|
896
|
+
// Check cache again with normalized path
|
|
897
|
+
if (normalizedPath !== path && this.routeCache.has(normalizedCacheKey)) {
|
|
898
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
899
|
+
return this.routeCache.get(normalizedCacheKey);
|
|
775
900
|
}
|
|
776
901
|
// Phase 1: O(1) static route lookup
|
|
777
|
-
const staticRoute = this.staticRoutes.get(
|
|
902
|
+
const staticRoute = this.staticRoutes.get(normalizedCacheKey);
|
|
778
903
|
if (staticRoute) {
|
|
779
|
-
this.routeCache.set(
|
|
904
|
+
this.routeCache.set(normalizedCacheKey, staticRoute);
|
|
905
|
+
if (normalizedPath !== path) {
|
|
906
|
+
this.routeCache.set(cacheKey, staticRoute);
|
|
907
|
+
}
|
|
780
908
|
return staticRoute;
|
|
781
909
|
}
|
|
782
910
|
// Phase 2: Optimized dynamic route matching by segment count
|
|
783
911
|
let route = null;
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
912
|
+
const dynamicRoutesLen = this.dynamicRoutes.length;
|
|
913
|
+
if (dynamicRoutesLen > 0) {
|
|
914
|
+
// Use shared utility for DRY principle
|
|
915
|
+
const segmentCount = PathMatcher.countSegments(normalizedPath);
|
|
916
|
+
const candidateRoutes = this.routesBySegmentCount.get(segmentCount) || this.dynamicRoutes;
|
|
917
|
+
const candidateLen = candidateRoutes.length;
|
|
787
918
|
// Only test routes with matching method and segment count
|
|
788
|
-
for (
|
|
919
|
+
for (let i = 0; i < candidateLen; i++) {
|
|
920
|
+
const candidateRoute = candidateRoutes[i];
|
|
789
921
|
if (candidateRoute.method === method && candidateRoute.pattern.test(normalizedPath)) {
|
|
790
922
|
route = candidateRoute;
|
|
791
923
|
break;
|
|
@@ -794,13 +926,17 @@ export class MoroHttpServer {
|
|
|
794
926
|
}
|
|
795
927
|
// Cache result (limit cache size to prevent memory leaks)
|
|
796
928
|
if (this.routeCache.size < 500) {
|
|
797
|
-
this.routeCache.set(
|
|
929
|
+
this.routeCache.set(normalizedCacheKey, route);
|
|
930
|
+
if (normalizedPath !== path) {
|
|
931
|
+
this.routeCache.set(cacheKey, route);
|
|
932
|
+
}
|
|
798
933
|
}
|
|
799
934
|
return route;
|
|
800
935
|
}
|
|
801
936
|
// Optimized middleware execution with reduced Promise allocation
|
|
802
937
|
async executeMiddleware(middleware, req, res) {
|
|
803
|
-
|
|
938
|
+
const len = middleware.length;
|
|
939
|
+
for (let i = 0; i < len; i++) {
|
|
804
940
|
// Short-circuit if response already sent
|
|
805
941
|
if (res.headersSent)
|
|
806
942
|
return;
|
|
@@ -816,7 +952,7 @@ export class MoroHttpServer {
|
|
|
816
952
|
};
|
|
817
953
|
try {
|
|
818
954
|
const result = mw(req, res, next);
|
|
819
|
-
// Handle async middleware
|
|
955
|
+
// Handle async middleware - optimized with early check
|
|
820
956
|
if (result && typeof result.then === 'function') {
|
|
821
957
|
result
|
|
822
958
|
.then(() => {
|
|
@@ -962,6 +1098,7 @@ export const middleware = {
|
|
|
962
1098
|
return (req, res, next) => {
|
|
963
1099
|
const start = Date.now();
|
|
964
1100
|
res.on('finish', () => {
|
|
1101
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
965
1102
|
const duration = Date.now() - start;
|
|
966
1103
|
// Request completed - logged by framework
|
|
967
1104
|
});
|
|
@@ -1017,7 +1154,7 @@ export const middleware = {
|
|
|
1017
1154
|
try {
|
|
1018
1155
|
stats = await fs.stat(filePath);
|
|
1019
1156
|
}
|
|
1020
|
-
catch
|
|
1157
|
+
catch {
|
|
1021
1158
|
next(); // File not found, let other middleware handle
|
|
1022
1159
|
return;
|
|
1023
1160
|
}
|
|
@@ -1036,7 +1173,7 @@ export const middleware = {
|
|
|
1036
1173
|
break;
|
|
1037
1174
|
}
|
|
1038
1175
|
}
|
|
1039
|
-
catch
|
|
1176
|
+
catch {
|
|
1040
1177
|
// Continue to next index file
|
|
1041
1178
|
}
|
|
1042
1179
|
}
|
|
@@ -1103,7 +1240,7 @@ export const middleware = {
|
|
|
1103
1240
|
const data = await fs.readFile(filePath);
|
|
1104
1241
|
res.end(data);
|
|
1105
1242
|
}
|
|
1106
|
-
catch
|
|
1243
|
+
catch {
|
|
1107
1244
|
res.status(500).json({ success: false, error: 'Internal server error' });
|
|
1108
1245
|
}
|
|
1109
1246
|
};
|
|
@@ -1131,6 +1268,7 @@ export const middleware = {
|
|
|
1131
1268
|
return;
|
|
1132
1269
|
}
|
|
1133
1270
|
// Validate each file
|
|
1271
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1134
1272
|
for (const [fieldName, file] of Object.entries(files)) {
|
|
1135
1273
|
const fileData = file;
|
|
1136
1274
|
// Validate file size
|
|
@@ -1168,6 +1306,7 @@ export const middleware = {
|
|
|
1168
1306
|
let templateContent;
|
|
1169
1307
|
// Check cache first
|
|
1170
1308
|
if (options.cache && templateCache.has(templatePath)) {
|
|
1309
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1171
1310
|
templateContent = templateCache.get(templatePath);
|
|
1172
1311
|
}
|
|
1173
1312
|
else {
|
|
@@ -1214,6 +1353,7 @@ export const middleware = {
|
|
|
1214
1353
|
try {
|
|
1215
1354
|
let layoutContent;
|
|
1216
1355
|
if (options.cache && templateCache.has(layoutPath)) {
|
|
1356
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1217
1357
|
layoutContent = templateCache.get(layoutPath);
|
|
1218
1358
|
}
|
|
1219
1359
|
else {
|
|
@@ -1224,14 +1364,14 @@ export const middleware = {
|
|
|
1224
1364
|
}
|
|
1225
1365
|
rendered = layoutContent.replace(/\{\{body\}\}/, rendered);
|
|
1226
1366
|
}
|
|
1227
|
-
catch
|
|
1367
|
+
catch {
|
|
1228
1368
|
// Layout not found, use template as-is
|
|
1229
1369
|
}
|
|
1230
1370
|
}
|
|
1231
1371
|
res.setHeader('Content-Type', 'text/html');
|
|
1232
1372
|
res.end(rendered);
|
|
1233
1373
|
}
|
|
1234
|
-
catch
|
|
1374
|
+
catch {
|
|
1235
1375
|
res.status(500).json({ success: false, error: 'Template rendering failed' });
|
|
1236
1376
|
}
|
|
1237
1377
|
};
|
|
@@ -1256,7 +1396,7 @@ export const middleware = {
|
|
|
1256
1396
|
return pushStream;
|
|
1257
1397
|
}
|
|
1258
1398
|
}
|
|
1259
|
-
catch
|
|
1399
|
+
catch {
|
|
1260
1400
|
// Push failed, continue normally
|
|
1261
1401
|
}
|
|
1262
1402
|
}
|
|
@@ -1334,7 +1474,8 @@ export const middleware = {
|
|
|
1334
1474
|
res.sendRange = async (filePath, stats) => {
|
|
1335
1475
|
try {
|
|
1336
1476
|
const fs = await import('fs/promises');
|
|
1337
|
-
|
|
1477
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1478
|
+
const _path = await import('path');
|
|
1338
1479
|
if (!stats) {
|
|
1339
1480
|
stats = await fs.stat(filePath);
|
|
1340
1481
|
}
|
|
@@ -1393,6 +1534,7 @@ export const middleware = {
|
|
|
1393
1534
|
for (const { start, end } of ranges) {
|
|
1394
1535
|
if (start >= fileSize || end >= fileSize)
|
|
1395
1536
|
continue;
|
|
1537
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1396
1538
|
const chunkSize = end - start + 1;
|
|
1397
1539
|
res.write(`\r\n--${boundary}\r\n`);
|
|
1398
1540
|
res.write(`Content-Range: bytes ${start}-${end}/${fileSize}\r\n\r\n`);
|
|
@@ -1409,7 +1551,7 @@ export const middleware = {
|
|
|
1409
1551
|
res.end();
|
|
1410
1552
|
}
|
|
1411
1553
|
}
|
|
1412
|
-
catch
|
|
1554
|
+
catch {
|
|
1413
1555
|
res.status(500).json({ success: false, error: 'Range request failed' });
|
|
1414
1556
|
}
|
|
1415
1557
|
};
|
|
@@ -1418,6 +1560,7 @@ export const middleware = {
|
|
|
1418
1560
|
},
|
|
1419
1561
|
// CSRF Protection middleware
|
|
1420
1562
|
csrf: (options = {}) => {
|
|
1563
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1421
1564
|
const secret = options.secret || 'moro-csrf-secret';
|
|
1422
1565
|
const tokenLength = options.tokenLength || 32;
|
|
1423
1566
|
const cookieName = options.cookieName || '_csrf';
|
|
@@ -1444,6 +1587,7 @@ export const middleware = {
|
|
|
1444
1587
|
return req._csrfToken;
|
|
1445
1588
|
};
|
|
1446
1589
|
// Skip verification for safe methods
|
|
1590
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1447
1591
|
if (ignoreMethods.includes(req.method)) {
|
|
1448
1592
|
next();
|
|
1449
1593
|
return;
|