@aetherframework/middleware 1.0.2 → 1.0.5

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.
@@ -1,345 +1,447 @@
1
- /**
1
+ /**
2
2
  * @license MIT
3
3
  * Copyright (c) 2026-present AetherFramework Contributors.
4
4
  * SPDX-License-Identifier: MIT
5
5
  * @module @aetherframework/middleware/core/AetherRouter
6
+ *
7
+ * Ultra-Optimized Router for Maximum Performance
8
+ * Focus: Pure routing speed with zero overhead
6
9
  */
7
10
 
8
- import { EventEmitter } from "events";
11
+ // [PERF] Pre-allocated frozen objects to prevent heap allocation in hot paths
12
+ const EMPTY_PARAMS = Object.freeze({});
13
+ const EMPTY_QUERY = Object.freeze({});
9
14
 
10
15
  /**
11
- * AetherRouter - High-performance routing system for AetherJS
12
- * Supports versioning, grouping, parameter parsing, and middleware chaining
16
+ * [PERF] Zero-Allocation Query Parser
13
17
  */
14
- class AetherRouter extends EventEmitter {
15
- constructor(options = {}) {
16
- super();
17
- this.routes = new Map(); // Store all routes: method+path -> handler
18
- this.groups = new Map(); // Store route groups
19
- this.middlewares = []; // Global middlewares
20
- this.prefix = options.prefix || ""; // Route prefix
21
- this.version = options.version || ""; // API version
22
-
23
- // Supported HTTP methods
24
- this.methods = [
25
- "GET", "POST", "PUT", "DELETE",
26
- "PATCH", "OPTIONS", "HEAD", "ANY"
27
- ];
28
-
29
- // Initialize all HTTP method handlers
30
- this.methods.forEach(method => {
31
- this[method.toLowerCase()] = this._createRouteHandler(method);
32
- });
18
+ class JITQueryParser {
19
+ parse(search) {
20
+ if (!search || search.length === 0) return EMPTY_QUERY;
21
+
22
+ const query = {};
23
+ let i = 0;
24
+ const len = search.length;
33
25
 
34
- // Special method: match any HTTP method
35
- this.all = this._createRouteHandler("ANY");
26
+ while (i < len) {
27
+ let ampIndex = search.indexOf('&', i);
28
+ if (ampIndex === -1) ampIndex = len;
29
+
30
+ const eqIndex = search.indexOf('=', i);
31
+
32
+ if (eqIndex !== -1 && eqIndex < ampIndex) {
33
+ const rawKey = search.substring(i, eqIndex);
34
+ const rawValue = search.substring(eqIndex + 1, ampIndex);
35
+ const key = decodeURIComponent(rawKey);
36
+ const value = decodeURIComponent(rawValue);
37
+
38
+ const kLen = key.length;
39
+ if (kLen > 2 && key.charCodeAt(kLen - 2) === 91 && key.charCodeAt(kLen - 1) === 93) {
40
+ const arrayKey = key.slice(0, -2);
41
+ let arr = query[arrayKey];
42
+ if (!arr) { arr = []; query[arrayKey] = arr; }
43
+ arr.push(value);
44
+ } else {
45
+ query[key] = value;
46
+ }
47
+ } else if (i < ampIndex) {
48
+ query[decodeURIComponent(search.substring(i, ampIndex))] = "";
49
+ }
50
+
51
+ i = ampIndex + 1;
52
+ }
53
+ return query;
36
54
  }
55
+ }
37
56
 
38
- /**
39
- * Create route handler for specific HTTP method
40
- * @private
41
- */
42
- _createRouteHandler(method) {
43
- return (path, ...handlers) => {
44
- const fullPath = this._buildPath(path);
45
- const route = {
46
- method: method === "ANY" ? null : method,
47
- path: fullPath,
48
- handlers: this._wrapHandlers(handlers),
49
- regex: this._pathToRegex(fullPath),
50
- paramNames: this._extractParamNames(fullPath)
57
+ /**
58
+ * [PERF] Optimized Handler Executor with Manual Loop Unrolling
59
+ */
60
+ class JITHandlerExecutor {
61
+ getExecutor(handlers) {
62
+ const len = handlers.length;
63
+ if (len === 0) return this._exec0;
64
+ if (len === 1) return this._exec1;
65
+ if (len === 2) return this._exec2;
66
+ if (len === 3) return this._exec3;
67
+ if (len === 4) return this._exec4;
68
+ if (len === 5) return this._exec5;
69
+
70
+ return async (context) => {
71
+ let index = 0;
72
+ const next = async () => {
73
+ if (index >= handlers.length || context.isTerminated()) return;
74
+ const handler = handlers[index++];
75
+ try {
76
+ await handler(context, next);
77
+ } catch (error) {
78
+ if (!context.isTerminated()) {
79
+ context.setStatus(500).json({ error: "Internal Server Error" });
80
+ }
81
+ }
51
82
  };
52
-
53
- const routeKey = `${method}:${fullPath}`;
54
- this.routes.set(routeKey, route);
55
-
56
- this.emit("route:added", { method, path: fullPath, handlers: handlers.length });
57
- return this;
83
+ await next();
58
84
  };
59
85
  }
60
86
 
61
- /**
62
- * Build full path with prefix and version
63
- * @private
64
- */
65
- _buildPath(path) {
66
- let fullPath = "";
87
+ async _exec0(ctx) {}
88
+
89
+ // [FIX] 修复了 this 指向问题,this 现在是 handlers 数组
90
+ async _exec1(ctx) {
91
+ if (ctx.isTerminated()) return;
92
+ try { await this[0](ctx, async () => {}); }
93
+ catch (e) { if (!ctx.isTerminated()) ctx.setStatus(500).json({ error: "Internal Server Error" }); }
94
+ }
95
+
96
+ async _exec2(ctx) {
97
+ if (ctx.isTerminated()) return;
98
+ try {
99
+ await this[0](ctx, async () => {
100
+ if (!ctx.isTerminated()) await this[1](ctx, async () => {});
101
+ });
102
+ } catch (e) { if (!ctx.isTerminated()) ctx.setStatus(500).json({ error: "Internal Server Error" }); }
103
+ }
104
+
105
+ async _exec3(ctx) {
106
+ if (ctx.isTerminated()) return;
107
+ try {
108
+ await this[0](ctx, async () => {
109
+ if (!ctx.isTerminated()) await this[1](ctx, async () => {
110
+ if (!ctx.isTerminated()) await this[2](ctx, async () => {});
111
+ });
112
+ });
113
+ } catch (e) { if (!ctx.isTerminated()) ctx.setStatus(500).json({ error: "Internal Server Error" }); }
114
+ }
115
+
116
+ async _exec4(ctx) {
117
+ if (ctx.isTerminated()) return;
118
+ try {
119
+ await this[0](ctx, async () => {
120
+ if (!ctx.isTerminated()) await this[1](ctx, async () => {
121
+ if (!ctx.isTerminated()) await this[2](ctx, async () => {
122
+ if (!ctx.isTerminated()) await this[3](ctx, async () => {});
123
+ });
124
+ });
125
+ });
126
+ } catch (e) { if (!ctx.isTerminated()) ctx.setStatus(500).json({ error: "Internal Server Error" }); }
127
+ }
128
+
129
+ async _exec5(ctx) {
130
+ if (ctx.isTerminated()) return;
131
+ try {
132
+ await this[0](ctx, async () => {
133
+ if (!ctx.isTerminated()) await this[1](ctx, async () => {
134
+ if (!ctx.isTerminated()) await this[2](ctx, async () => {
135
+ if (!ctx.isTerminated()) await this[3](ctx, async () => {
136
+ if (!ctx.isTerminated()) await this[4](ctx, async () => {});
137
+ });
138
+ });
139
+ });
140
+ });
141
+ } catch (e) { if (!ctx.isTerminated()) ctx.setStatus(500).json({ error: "Internal Server Error" }); }
142
+ }
143
+ }
144
+
145
+ class AetherRouter {
146
+ constructor(options = {}) {
147
+ this.staticRoutes = new Map();
148
+ this.dynamicRoutes = [];
149
+ this.globalMiddlewares = [];
150
+ this.prefixMiddlewares = [];
151
+ this.prefix = options.prefix || "";
152
+ this.version = options.version || "";
153
+ this.routeCache = new Map();
154
+ this.cacheMaxSize = options.cacheMaxSize || 2000;
155
+ this.jitQueryParser = new JITQueryParser();
156
+ this.jitExecutor = new JITHandlerExecutor();
157
+ }
158
+
159
+ get(path, ...handlers) { return this._addRoute("GET", path, handlers); }
160
+ post(path, ...handlers) { return this._addRoute("POST", path, handlers); }
161
+ put(path, ...handlers) { return this._addRoute("PUT", path, handlers); }
162
+ delete(path, ...handlers) { return this._addRoute("DELETE", path, handlers); }
163
+ patch(path, ...handlers) { return this._addRoute("PATCH", path, handlers); }
164
+ options(path, ...handlers) { return this._addRoute("OPTIONS", path, handlers); }
165
+ head(path, ...handlers) { return this._addRoute("HEAD", path, handlers); }
166
+ all(path, ...handlers) { return this._addRoute("ANY", path, handlers); }
167
+
168
+ _addRoute(method, path, handlers) {
169
+ if (handlers.length === 0) throw new Error(`Route ${method} ${path} must have at least one handler`);
67
170
 
68
- // Add version prefix
69
- if (this.version) {
70
- fullPath += `/v${this.version.replace(/[^0-9.]/g, "")}`;
71
- }
171
+ const fullPath = this._buildPath(path);
172
+ const isStatic = fullPath.indexOf(':') === -1 && fullPath.indexOf('*') === -1;
72
173
 
73
- // Add group prefix
74
- if (this.prefix) {
75
- fullPath += `/${this.prefix.replace(/^\/|\/$/g, "")}`;
76
- }
174
+ const route = {
175
+ method: method === "ANY" ? null : method,
176
+ path: fullPath,
177
+ isStatic: isStatic,
178
+ handlers: this._wrapHandlers(handlers),
179
+ regex: isStatic ? null : this._pathToRegex(fullPath),
180
+ paramNames: isStatic ? [] : this._extractParamNames(fullPath)
181
+ };
77
182
 
78
- // Add route path
183
+ if (isStatic) {
184
+ this.staticRoutes.set(`${route.method || 'ANY'}:${fullPath}`, route);
185
+ } else {
186
+ this.dynamicRoutes.push(route);
187
+ }
188
+ return this;
189
+ }
190
+
191
+ _buildPath(path) {
192
+ let fullPath = "";
193
+ if (this.version) fullPath += `/v${this.version.replace(/[^0-9.]/g, "")}`;
194
+ if (this.prefix) fullPath += `/${this.prefix.replace(/^\/|\/$/g, "")}`;
79
195
  fullPath += `/${path.replace(/^\/|\/$/g, "")}`;
80
-
81
- // Normalize path
82
196
  return fullPath.replace(/\/+/g, "/").replace(/\/$/, "") || "/";
83
197
  }
84
198
 
85
- /**
86
- * Convert path pattern to regex
87
- * @private
88
- */
89
199
  _pathToRegex(path) {
90
- const pattern = path
91
- .replace(/:(\w+)/g, "(?<$1>[^/]+)") // Named parameters: :id
92
- .replace(/\*(\w+)?/g, (_, name) => name ? `(?<${name}>.*)` : "(.*)") // Wildcards
93
- .replace(/$([^)]+)$/g, "(?:$1)") // Optional groups
94
- .replace(/\?/g, "\\?"); // Escape question marks
95
-
200
+ const escapedPath = path.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
201
+ const pattern = escapedPath
202
+ .replace(/\\:(\w+)/g, "([^/]+)")
203
+ .replace(/\\*(\w+)?/g, "(.*)")
204
+ .replace(/\\\?/g, "\\?");
96
205
  return new RegExp(`^${pattern}$`);
97
206
  }
98
207
 
99
- /**
100
- * Extract parameter names from path
101
- * @private
102
- */
208
+ // [FIX] 修复了 push(match) 导致推入整个 RegExp 数组的问题
103
209
  _extractParamNames(path) {
104
210
  const paramNames = [];
105
211
  const paramPattern = /:(\w+)/g;
106
212
  const wildcardPattern = /\*(\w+)?/g;
107
-
108
213
  let match;
109
- while ((match = paramPattern.exec(path)) !== null) {
110
- paramNames.push(match);
111
- }
112
-
113
- while ((match = wildcardPattern.exec(path)) !== null) {
114
- if (match) paramNames.push(match);
115
- }
116
-
214
+ while ((match = paramPattern.exec(path)) !== null) paramNames.push(match[1]);
215
+ while ((match = wildcardPattern.exec(path)) !== null) if (match[1]) paramNames.push(match[1]);
117
216
  return paramNames;
118
217
  }
119
218
 
120
- /**
121
- * Wrap handlers with validation
122
- * @private
123
- */
124
219
  _wrapHandlers(handlers) {
125
- return handlers.map(handler => {
126
- if (typeof handler !== "function") {
127
- throw new TypeError("Route handler must be a function");
220
+ for (let i = 0; i < handlers.length; i++) {
221
+ if (typeof handlers[i] !== "function") {
222
+ throw new TypeError(`Route handler must be a function, got ${typeof handlers[i]} at position ${i}`);
128
223
  }
129
- return handler;
130
- });
224
+ }
225
+ return handlers;
131
226
  }
132
227
 
133
- /**
134
- * Route grouping
135
- * @param {string} prefix - Group prefix
136
- * @param {Function} callback - Group definition function
137
- */
138
228
  group(prefix, callback) {
139
- const router = new AetherRouter({
140
- prefix: `${this.prefix}/${prefix}`.replace(/\/+/g, "/"),
141
- version: this.version
142
- });
143
-
144
- // Inherit global middlewares
145
- router.middlewares = [...this.middlewares];
146
-
147
- // Execute group definition
229
+ const router = new AetherRouter({ prefix: `${this.prefix}/${prefix}`.replace(/\/+/g, "/"), version: this.version });
230
+ router.globalMiddlewares = this.globalMiddlewares.slice();
231
+ router.prefixMiddlewares = this.prefixMiddlewares.slice();
148
232
  callback(router);
149
233
 
150
- // Merge group routes into main router
151
- router.routes.forEach((route, key) => {
152
- this.routes.set(key, route);
153
- });
234
+ router.staticRoutes.forEach((route, key) => this.staticRoutes.set(key, route));
235
+ for (let i = 0; i < router.dynamicRoutes.length; i++) this.dynamicRoutes.push(router.dynamicRoutes[i]);
236
+ return this;
237
+ }
238
+
239
+ version(version, callback) {
240
+ const router = new AetherRouter({ prefix: this.prefix, version: version });
241
+ router.globalMiddlewares = this.globalMiddlewares.slice();
242
+ router.prefixMiddlewares = this.prefixMiddlewares.slice();
243
+ callback(router);
154
244
 
245
+ router.staticRoutes.forEach((route, key) => this.staticRoutes.set(key, route));
246
+ for (let i = 0; i < router.dynamicRoutes.length; i++) this.dynamicRoutes.push(router.dynamicRoutes[i]);
155
247
  return this;
156
248
  }
157
249
 
158
- /**
159
- * API versioning
160
- * @param {string} version - Version number (e.g., "1", "2.0")
161
- * @param {Function} callback - Version definition function
162
- */
163
- version(version, callback) {
164
- const router = new AetherRouter({
165
- prefix: this.prefix,
166
- version: version
167
- });
168
-
169
- // Inherit global middlewares
170
- router.middlewares = [...this.middlewares];
171
-
172
- // Execute version definition
173
- callback(router);
174
-
175
- // Merge version routes into main router
176
- router.routes.forEach((route, key) => {
177
- this.routes.set(key, route);
178
- });
179
-
180
- return this;
181
- }
250
+ // [FIX] 重写了 use 方法,修复了 typeof args 永远为 'object' 的致命判断错误
251
+ use(...args) {
252
+ if (args.length === 0) return this;
253
+
254
+ if (typeof args[0] === 'string') {
255
+ const path = args[0];
256
+ const middlewares = args.slice(1);
257
+
258
+ for (let i = 0; i < middlewares.length; i++) {
259
+ let mw = middlewares[i];
260
+ if (typeof mw !== "function") {
261
+ if (mw && typeof mw.middleware === 'function') middlewares[i] = mw.middleware();
262
+ else throw new TypeError(`Middleware for path "${path}" must be a function, got ${typeof mw}`);
263
+ }
264
+ }
265
+
266
+ const normalizedPath = path === '/' ? '/' : path.replace(/\/+$/, '');
267
+ const escapedPath = normalizedPath.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
268
+ const prefixRegex = normalizedPath === '/' ? null : new RegExp(`^${escapedPath}(?=/|$|\\?)`, 'i');
269
+
270
+ const wrappedMiddlewares = new Array(middlewares.length);
271
+ for (let i = 0; i < middlewares.length; i++) {
272
+ const mw = middlewares[i];
273
+ wrappedMiddlewares[i] = async (ctx, next) => {
274
+ if (!prefixRegex) return mw(ctx, next);
275
+ const originalUrl = ctx.url;
276
+ const originalPath = ctx.path;
277
+ if (prefixRegex.test(originalUrl)) {
278
+ let newUrl = originalUrl.replace(prefixRegex, '');
279
+ if (newUrl.charCodeAt(0) !== 47) newUrl = '/' + newUrl;
280
+ ctx.url = newUrl;
281
+ try {
282
+ // [FIX] 修复了 split('?') 返回数组导致 ctx.path 类型错误的问题
283
+ if ('path' in ctx) ctx.path = newUrl.split('?')[0];
284
+ await mw(ctx, next);
285
+ } finally {
286
+ ctx.url = originalUrl;
287
+ if ('path' in ctx) ctx.path = originalPath;
288
+ }
289
+ } else { await next(); }
290
+ };
291
+ }
182
292
 
183
- /**
184
- * Add middleware to router
185
- * @param {...Function} middlewares - Middleware functions
186
- */
187
- use(...middlewares) {
188
- this.middlewares.push(...middlewares);
293
+ if (normalizedPath === '/') {
294
+ this.globalMiddlewares.push(...wrappedMiddlewares);
295
+ } else {
296
+ this.prefixMiddlewares.push({ path: normalizedPath, handlers: wrappedMiddlewares });
297
+ }
298
+ } else {
299
+ for (let i = 0; i < args.length; i++) {
300
+ if (typeof args[i] !== "function") throw new TypeError(`Global middleware must be a function, got ${typeof args[i]}`);
301
+ }
302
+ this.globalMiddlewares.push(...args);
303
+ }
189
304
  return this;
190
305
  }
191
306
 
192
- /**
193
- * Match route for incoming request
194
- * @param {string} method - HTTP method
195
- * @param {string} url - Request URL
196
- * @returns {Object|null} - Matched route info or null
197
- */
198
307
  match(method, url) {
199
- // Parse URL and query parameters
200
- const [pathname, search] = url.split("?");
201
- const query = this._parseQuery(search);
308
+ const qIndex = url.indexOf("?");
309
+ const pathname = qIndex === -1 ? url : url.substring(0, qIndex);
310
+ const search = qIndex === -1 ? null : url.substring(qIndex + 1);
202
311
 
203
- // Find matching route
204
- for (const [routeKey, route] of this.routes) {
205
- const [routeMethod, routePath] = routeKey.split(":");
206
-
207
- // Check method match
208
- if (routeMethod !== "ANY" && routeMethod !== method) {
209
- continue;
312
+ const cacheKey = method + ":" + pathname;
313
+ const cached = this.routeCache.get(cacheKey);
314
+
315
+ if (cached !== undefined) {
316
+ const route = cached.route;
317
+ const params = route && !route.isStatic ? this._extractParams(route, pathname) : EMPTY_PARAMS;
318
+ return {
319
+ route: route,
320
+ params: params,
321
+ query: search ? this.jitQueryParser.parse(search) : EMPTY_QUERY,
322
+ handlers: cached.handlers
323
+ };
324
+ }
325
+
326
+ const applicableMiddlewares = [];
327
+ for (let i = 0; i < this.globalMiddlewares.length; i++) applicableMiddlewares.push(this.globalMiddlewares[i]);
328
+
329
+ for (let i = 0; i < this.prefixMiddlewares.length; i++) {
330
+ const mw = this.prefixMiddlewares[i];
331
+ const mwPath = mw.path;
332
+ const mwPathSlash = mwPath.endsWith('/') ? mwPath : mwPath + '/';
333
+ if (pathname === mwPath || pathname.startsWith(mwPathSlash)) {
334
+ for (let j = 0; j < mw.handlers.length; j++) applicableMiddlewares.push(mw.handlers[j]);
210
335
  }
211
-
212
- // Check path match
213
- const match = pathname.match(route.regex);
214
- if (match) {
215
- const params = {};
336
+ }
337
+
338
+ let matchedRoute = null;
339
+ matchedRoute = this.staticRoutes.get(method + ":" + pathname) || this.staticRoutes.get("ANY:" + pathname);
340
+
341
+ // [FIX] 修复了高并发下 _lastMatch 挂载在共享 route 对象上导致的“串参”竞态条件问题
342
+ let dynamicMatch = null;
343
+ if (!matchedRoute) {
344
+ for (let i = 0; i < this.dynamicRoutes.length; i++) {
345
+ const route = this.dynamicRoutes[i];
346
+ if (route.method !== null && route.method !== method) continue;
216
347
 
217
- // Extract named parameters
218
- if (match.groups) {
219
- Object.assign(params, match.groups);
348
+ const match = route.regex.exec(pathname);
349
+ if (match) {
350
+ matchedRoute = route;
351
+ dynamicMatch = match; // 使用局部变量保存匹配结果
352
+ break;
220
353
  }
221
-
222
- // Extract positional parameters
223
- route.paramNames.forEach((name, index) => {
224
- if (!params[name] && match[index + 1]) {
225
- params[name] = match[index + 1];
226
- }
227
- });
228
-
229
- return {
230
- route,
231
- params,
232
- query,
233
- handlers: [...this.middlewares, ...route.handlers]
234
- };
235
354
  }
236
355
  }
356
+
357
+ if (matchedRoute) {
358
+ const finalHandlers = applicableMiddlewares.concat(matchedRoute.handlers);
359
+ if (this.routeCache.size >= this.cacheMaxSize) this.routeCache.clear();
360
+ this.routeCache.set(cacheKey, { route: matchedRoute, handlers: finalHandlers });
361
+
362
+ const params = matchedRoute.isStatic ? EMPTY_PARAMS : this._extractParams(matchedRoute, pathname, dynamicMatch);
363
+ return {
364
+ route: matchedRoute,
365
+ params: params,
366
+ query: search ? this.jitQueryParser.parse(search) : EMPTY_QUERY,
367
+ handlers: finalHandlers
368
+ };
369
+ }
370
+
371
+ if (applicableMiddlewares.length > 0) {
372
+ if (this.routeCache.size >= this.cacheMaxSize) this.routeCache.clear();
373
+ this.routeCache.set(cacheKey, { route: null, handlers: applicableMiddlewares });
374
+ return {
375
+ route: null,
376
+ params: EMPTY_PARAMS,
377
+ query: search ? this.jitQueryParser.parse(search) : EMPTY_QUERY,
378
+ handlers: applicableMiddlewares
379
+ };
380
+ }
237
381
 
238
382
  return null;
239
383
  }
240
384
 
241
- /**
242
- * Parse query string parameters
243
- * @private
244
- */
245
- _parseQuery(search) {
246
- const query = {};
247
- if (!search) return query;
385
+ _extractParams(route, pathname, existingMatch) {
386
+ const match = existingMatch || route.regex.exec(pathname);
387
+ if (!match) return EMPTY_PARAMS;
248
388
 
249
- const pairs = search.split("&");
250
- for (const pair of pairs) {
251
- const [key, value] = pair.split("=");
252
- if (key) {
253
- const decodedKey = decodeURIComponent(key);
254
- const decodedValue = value ? decodeURIComponent(value) : "";
255
-
256
- // Support array parameters: key[]=value1&key[]=value2
257
- if (decodedKey.endsWith("[]")) {
258
- const arrayKey = decodedKey.slice(0, -2);
259
- if (!query[arrayKey]) {
260
- query[arrayKey] = [];
261
- }
262
- query[arrayKey].push(decodedValue);
263
- } else {
264
- query[decodedKey] = decodedValue;
265
- }
266
- }
389
+ const params = {};
390
+ const names = route.paramNames;
391
+ for (let i = 0; i < names.length; i++) {
392
+ params[names[i]] = match[i + 1];
267
393
  }
268
-
269
- return query;
394
+ return params;
270
395
  }
271
396
 
272
- /**
273
- * Generate router middleware for AetherPipeline
274
- */
275
397
  middleware() {
276
398
  return async (context, next) => {
277
399
  const match = this.match(context.method, context.url);
278
400
 
279
401
  if (match) {
280
- // Set route parameters and query parameters
281
402
  context.params = match.params;
282
- context.setState("query", match.query);
283
403
  context.route = match.route;
284
404
 
285
- // Execute route handler chain
286
- await this._executeHandlers(context, match.handlers);
405
+ if (typeof context.setState === "function") {
406
+ context.setState("query", match.query);
407
+ } else {
408
+ try { context.query = match.query; } catch (e) { /* Ignore */ }
409
+ }
410
+
411
+ if (match.handlers && match.handlers.length > 0) {
412
+ const executor = this.jitExecutor.getExecutor(match.handlers);
413
+ await executor.call(match.handlers, context);
414
+ } else if (typeof next === "function") {
415
+ await next();
416
+ }
417
+
418
+ if (!context.isTerminated() && !match.route && typeof next === "function") {
419
+ await next();
420
+ }
287
421
  } else if (typeof next === "function") {
288
- // No matching route, continue to next middleware
289
422
  await next();
290
423
  } else {
291
- // Return 404
292
- context.setStatus(404).json({
293
- error: "Not Found",
294
- message: `Route ${context.method} ${context.url} not found`,
295
- timestamp: new Date().toISOString()
296
- });
424
+ context.setStatus(404).json({ error: "Not Found" });
297
425
  }
298
426
  };
299
427
  }
300
428
 
301
- /**
302
- * Execute handler chain
303
- * @private
304
- */
305
- async _executeHandlers(context, handlers) {
306
- let index = 0;
307
-
308
- const executeNext = async () => {
309
- if (index >= handlers.length || context.isTerminated()) {
310
- return;
311
- }
312
-
313
- const handler = handlers[index++];
314
- await handler(context, executeNext);
315
- };
316
-
317
- await executeNext();
318
- }
319
-
320
- /**
321
- * Get all registered routes (for debugging)
322
- */
323
429
  getRoutes() {
324
430
  const routes = [];
325
- this.routes.forEach((route, key) => {
326
- const [method, path] = key.split(":");
327
- routes.push({
328
- method: method === "ANY" ? "ALL" : method,
329
- path,
330
- handlers: route.handlers.length
331
- });
332
- });
431
+ this.staticRoutes.forEach(r => routes.push({ method: r.method || "ALL", path: r.path, type: 'static' }));
432
+ for (let i = 0; i < this.dynamicRoutes.length; i++) {
433
+ const r = this.dynamicRoutes[i];
434
+ routes.push({ method: r.method || "ALL", path: r.path, type: 'dynamic' });
435
+ }
333
436
  return routes;
334
437
  }
335
438
 
336
- /**
337
- * Clear all routes and middlewares
338
- */
339
439
  clear() {
340
- this.routes.clear();
341
- this.groups.clear();
342
- this.middlewares = [];
440
+ this.staticRoutes.clear();
441
+ this.dynamicRoutes.length = 0;
442
+ this.globalMiddlewares.length = 0;
443
+ this.prefixMiddlewares.length = 0;
444
+ this.routeCache.clear();
343
445
  return this;
344
446
  }
345
447
  }