@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,58 +1,74 @@
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/AetherContext
6
+ *
7
+ * Ultra-Optimized Request Context for Maximum Performance
8
+ * Removed all statistics, metrics, and monitoring overhead
9
+ * Focus: Zero-allocation context management with minimal overhead
6
10
  */
7
- const GLOBAL_HEADER_BUFFER = new Array(64);
8
11
 
12
+ /**
13
+ * AetherContext - High-performance request context optimized for V8
14
+ * Removed all statistics tracking and metrics collection
15
+ * Focus: Pure request/response handling with maximum speed
16
+ */
9
17
  class AetherContext {
10
18
  constructor(request, response) {
11
- // 1. Strictly align with V8's Hidden Class (Shape) optimization
19
+ // ==========================================
20
+ // [V8-OPT] MONOMORPHIC PROPERTY LAYOUT
21
+ // ==========================================
22
+ // All properties initialized in same order for V8 Hidden Class optimization
12
23
  this._request = null;
13
24
  this._response = null;
14
-
25
+
26
+ // Request metadata
15
27
  this.method = "";
16
28
  this.url = "";
17
29
  this.headers = null;
18
30
  this.path = "";
19
31
  this._queryString = "";
20
-
21
- // 2. Avoid using Map/Set; use flat plain objects/arrays for custom response headers to achieve 5x faster R/W speeds.
32
+
33
+ // [PERF] Flat header storage for O(1) access
22
34
  this._headersObj = {};
23
35
  this._headersCount = 0;
24
- this._headersKeys = new Array(16); // Pre-allocated key tracking array
25
-
36
+ this._headersKeys = new Array(32); // Pre-allocated array to avoid resizing
37
+
38
+ // Cached computations
26
39
  this._queryCache = null;
27
- this._ipCache = null; // Lazy-evaluated cache
28
-
40
+ this._ipCache = null;
41
+
42
+ // Response state
29
43
  this.statusCode = 200;
30
44
  this._body = null;
31
45
  this._terminated = false;
32
-
33
- // 3. Use plain flat objects for business state/context storage, strictly avoiding Maps.
46
+
47
+ // [PERF] State storage (removed metrics tracking)
34
48
  this._stateObj = {};
35
-
36
- this._startTime = 0n;
37
-
49
+
50
+ // Initialize if request/response provided
38
51
  if (request && response) {
39
52
  this._reset(request, response);
40
53
  }
41
54
  }
42
-
55
+
43
56
  /**
44
- * Ultra-fast context reset for pooling mechanisms (True Zero-Object-Allocation strategy)
57
+ * Reset context for reuse (object pooling)
58
+ * @param {Object} request - HTTP request object
59
+ * @param {Object} response - HTTP response object
45
60
  */
46
61
  _reset(request, response) {
47
62
  this._request = request;
48
63
  this._response = response;
49
-
64
+
65
+ // [PERF] Direct property assignment (faster than Object.assign)
50
66
  this.method = request.method;
51
67
  const rawUrl = request.url || "/";
52
68
  this.url = rawUrl;
53
69
  this.headers = request.headers;
54
-
55
- // Fast string slicing (bypassing expensive RegEx and URL instantiation overheads)
70
+
71
+ // [PERF] Fast URL parsing without regex
56
72
  const qIdx = rawUrl.indexOf("?");
57
73
  if (qIdx !== -1) {
58
74
  this.path = rawUrl.substring(0, qIdx);
@@ -61,181 +77,293 @@ class AetherContext {
61
77
  this.path = rawUrl;
62
78
  this._queryString = "";
63
79
  }
64
-
65
- // Only clear references, never use the 'new' keyword to re-allocate memory
80
+
81
+ // Reset cached values
66
82
  this._queryCache = null;
67
83
  this._ipCache = null;
68
-
84
+
85
+ // Reset response state
69
86
  this.statusCode = 200;
70
87
  this._body = null;
71
88
  this._terminated = false;
72
-
73
- // Clean up custom headers object without breaking Hidden Classes or re-allocating memory
89
+
90
+ // [PERF] Clear headers without re-allocating objects
74
91
  if (this._headersCount > 0) {
75
92
  for (let i = 0; i < this._headersCount; i++) {
76
93
  this._headersObj[this._headersKeys[i]] = undefined;
77
94
  }
78
95
  this._headersCount = 0;
79
96
  }
80
-
81
- this._stateObj = {}; // Kept as empty object in most scenarios
82
- this._startTime = process.hrtime.bigint();
97
+
98
+ // [PERF] Clear state without re-allocating object
99
+ for (const key in this._stateObj) {
100
+ delete this._stateObj[key];
101
+ }
102
+
103
+ // [PERF] REMOVED: _startTime and metrics tracking
104
+ // this._startTime = process.hrtime.bigint(); // Statistics removed
83
105
  }
84
-
85
- // 🟢 Intercept the body property to establish the data pipeline
106
+
107
+ // ==========================================
108
+ // [PERF] PROPERTY GETTERS/SETTERS
109
+ // ==========================================
110
+
111
+ /**
112
+ * Get response body
113
+ */
86
114
  get body() {
87
115
  return this._body;
88
116
  }
117
+
118
+ /**
119
+ * Set response body and finalize
120
+ * @param {any} value - Response body value
121
+ */
89
122
  set body(value) {
90
123
  if (this._terminated) return;
91
124
  this._body = value;
125
+ this._finalize();
92
126
  }
93
-
127
+
94
128
  /**
95
- * Lazy IP evaluation: Trigger C++ binding bridge only when business logic explicitly requests ctx.ip
129
+ * Get client IP address with caching
96
130
  */
97
131
  get ip() {
98
132
  if (this._ipCache) return this._ipCache;
133
+
99
134
  const sock = this._request?.socket;
135
+ // [PERF] Simple IP extraction without proxy header checks
100
136
  return (this._ipCache = sock ? sock.remoteAddress : "127.0.0.1");
101
137
  }
102
-
138
+
103
139
  /**
104
- * Lazy Query parsing: Synchronous hand-optimized raw string scanner
140
+ * Get parsed query parameters with caching
105
141
  */
106
142
  get query() {
107
143
  if (this._queryCache) return this._queryCache;
108
144
  if (!this._queryString) return (this._queryCache = {});
109
-
145
+
110
146
  const obj = {};
111
147
  const pairs = this._queryString.split("&");
148
+
149
+ // [PERF] Manual parsing without try-catch for common case
112
150
  for (let i = 0; i < pairs.length; i++) {
113
151
  const pair = pairs[i];
114
152
  const eqIdx = pair.indexOf("=");
153
+
115
154
  if (eqIdx !== -1) {
116
- obj[pair.substring(0, eqIdx)] = pair.substring(eqIdx + 1);
155
+ // [PERF] Fast path: decode only when needed
156
+ const key = pair.substring(0, eqIdx);
157
+ const value = pair.substring(eqIdx + 1);
158
+
159
+ // [PERF] Skip decodeURIComponent for simple ASCII
160
+ if (key.indexOf("%") === -1 && value.indexOf("%") === -1) {
161
+ obj[key] = value;
162
+ } else {
163
+ try {
164
+ obj[decodeURIComponent(key)] = decodeURIComponent(value);
165
+ } catch {
166
+ // Fallback for malformed URIs
167
+ obj[key] = value;
168
+ }
169
+ }
117
170
  } else {
118
- obj[pair] = "";
171
+ // Key without value
172
+ const key = pair;
173
+ if (key.indexOf("%") === -1) {
174
+ obj[key] = "";
175
+ } else {
176
+ try {
177
+ obj[decodeURIComponent(key)] = "";
178
+ } catch {
179
+ obj[key] = "";
180
+ }
181
+ }
119
182
  }
120
183
  }
184
+
121
185
  return (this._queryCache = obj);
122
186
  }
123
-
187
+
188
+ // ==========================================
189
+ // [PERF] HEADER MANAGEMENT
190
+ // ==========================================
191
+
192
+ /**
193
+ * Set response header
194
+ * @param {string} key - Header name
195
+ * @param {string} value - Header value
196
+ * @returns {AetherContext} - Chainable
197
+ */
124
198
  setHeader(key, value) {
125
199
  if (this._terminated) return this;
126
-
127
- // Convention: High-performance middlewares should pass standard casing headers (e.g., 'Content-Type').
128
- // We intentionally omit .toLowerCase() here to conserve CPU cycles.
129
- if (this._headersObj[key] === undefined) {
130
- this._headersKeys[this._headersCount++] = key;
200
+
201
+ // [PERF] Lowercase for consistency with Node.js
202
+ const lowerKey = key.toLowerCase();
203
+
204
+ // [PERF] Track header keys for fast iteration
205
+ if (this._headersObj[lowerKey] === undefined) {
206
+ if (this._headersCount < this._headersKeys.length) {
207
+ this._headersKeys[this._headersCount] = lowerKey;
208
+ } else {
209
+ this._headersKeys.push(lowerKey);
210
+ }
211
+ this._headersCount++;
131
212
  }
132
- this._headersObj[key] = value;
213
+
214
+ this._headersObj[lowerKey] = value;
133
215
  return this;
134
216
  }
135
-
217
+
218
+ /**
219
+ * Get header value (request headers first, then response headers)
220
+ * @param {string} key - Header name
221
+ * @returns {string|null} - Header value or null
222
+ */
136
223
  getHeader(key) {
224
+ const lowerKey = key.toLowerCase();
137
225
  return (
138
- this.headers[key] ||
139
- this.headers[key.toLowerCase()] ||
140
- this._headersObj[key] ||
226
+ this.headers?.[lowerKey] ||
227
+ this._headersObj[lowerKey] ||
141
228
  null
142
229
  );
143
230
  }
144
-
231
+
232
+ // ==========================================
233
+ // [PERF] RESPONSE METHODS
234
+ // ==========================================
235
+
236
+ /**
237
+ * Set HTTP status code
238
+ * @param {number} code - Status code
239
+ * @returns {AetherContext} - Chainable
240
+ */
145
241
  setStatus(code) {
146
242
  if (this._terminated) return this;
147
243
  this.statusCode = code;
148
244
  return this;
149
245
  }
150
-
246
+
247
+ /**
248
+ * Send JSON response
249
+ * @param {any} data - Data to send as JSON
250
+ * @returns {AetherContext} - Chainable
251
+ */
151
252
  json(data) {
152
253
  if (this._terminated) return this;
153
- // Direct assignment to prevent redundant lookup overheads
154
- if (this._headersObj["Content-Type"] === undefined) {
155
- this._headersKeys[this._headersCount++] = "Content-Type";
254
+
255
+ this.setHeader("Content-Type", "application/json; charset=utf-8");
256
+
257
+ // [PERF] Fast JSON stringification check
258
+ if (typeof data === "object") {
259
+ this._body = JSON.stringify(data);
260
+ } else {
261
+ this._body = String(data);
156
262
  }
157
- this._headersObj["Content-Type"] = "application/json; charset=utf-8";
158
- this._body = typeof data === "object" ? JSON.stringify(data) : data;
263
+
159
264
  this._finalize();
160
265
  return this;
161
266
  }
162
-
267
+
268
+ /**
269
+ * Send text response
270
+ * @param {string} data - Text to send
271
+ * @returns {AetherContext} - Chainable
272
+ */
163
273
  text(data) {
164
274
  if (this._terminated) return this;
165
- if (this._headersObj["Content-Type"] === undefined) {
166
- this._headersKeys[this._headersCount++] = "Content-Type";
167
- }
168
- this._headersObj["Content-Type"] = "text/plain; charset=utf-8";
275
+
276
+ this.setHeader("Content-Type", "text/plain; charset=utf-8");
169
277
  this._body = String(data);
170
278
  this._finalize();
171
279
  return this;
172
280
  }
173
-
281
+
282
+ /**
283
+ * Send raw response (no content-type header)
284
+ * @param {any} data - Raw data to send
285
+ * @returns {AetherContext} - Chainable
286
+ */
174
287
  raw(data) {
175
288
  if (this._terminated) return this;
176
289
  this._body = data;
177
290
  this._finalize();
178
291
  return this;
179
292
  }
180
-
293
+
294
+ // ==========================================
295
+ // [PERF] STATE MANAGEMENT
296
+ // ==========================================
297
+
298
+ /**
299
+ * Set state value (for middleware communication)
300
+ * @param {string} key - State key
301
+ * @param {any} value - State value
302
+ * @returns {AetherContext} - Chainable
303
+ */
181
304
  setState(key, value) {
182
305
  this._stateObj[key] = value;
183
306
  return this;
184
307
  }
185
-
308
+
309
+ /**
310
+ * Get state value
311
+ * @param {string} key - State key
312
+ * @returns {any} - State value
313
+ */
186
314
  getState(key) {
187
315
  return this._stateObj[key];
188
316
  }
189
-
317
+
318
+ /**
319
+ * Check if response has been sent
320
+ * @returns {boolean} - True if terminated
321
+ */
190
322
  isTerminated() {
191
323
  return this._terminated;
192
324
  }
193
-
325
+
326
+ // ==========================================
327
+ // [PERF] RESPONSE FINALIZATION
328
+ // ==========================================
329
+
194
330
  /**
195
- * Critical Performance Bottleneck Optimization: Flush data instantly via the shared buffer
331
+ * Finalize and send response
332
+ * @private
196
333
  */
197
334
  _finalize() {
198
335
  if (this._terminated) return;
199
336
  this._terminated = true;
200
-
337
+
201
338
  const res = this._response;
202
- const socket = res.socket;
203
-
204
- if (socket) socket.cork();
205
-
206
- // Core optimization: Reuse the pre-allocated flat array to eliminate transient garbage collection pressure
207
- GLOBAL_HEADER_BUFFER[0] = "Connection";
208
- GLOBAL_HEADER_BUFFER[1] = "keep-alive";
209
- let cursor = 2;
210
-
339
+ if (!res || res.headersSent) return;
340
+
341
+ // [PERF] Build headers object directly (faster than array manipulation)
342
+ const headers = {};
343
+
344
+ // [PERF] Manual header iteration without Object.keys()
211
345
  for (let i = 0; i < this._headersCount; i++) {
212
346
  const key = this._headersKeys[i];
213
347
  const val = this._headersObj[key];
214
348
  if (val !== undefined) {
215
- GLOBAL_HEADER_BUFFER[cursor++] = key;
216
- GLOBAL_HEADER_BUFFER[cursor++] = val;
349
+ headers[key] = val;
217
350
  }
218
351
  }
219
-
220
- // Slice the buffer and pass it directly to the native writeHead.
221
- // Note: The slice size is highly predictable and short, allowing V8 to aggressively inline the operation.
222
- res.writeHead(this.statusCode, GLOBAL_HEADER_BUFFER.slice(0, cursor));
223
-
352
+
353
+ // [PERF] Set keep-alive header if not already set
354
+ if (!headers["connection"]) {
355
+ headers["connection"] = "keep-alive";
356
+ }
357
+
358
+ // [PERF] Direct writeHead call without extra checks
359
+ res.writeHead(this.statusCode, headers);
360
+
361
+ // [PERF] Direct end call (Node.js handles socket optimization internally)
224
362
  if (this._body !== null) {
225
363
  res.end(this._body);
226
364
  } else {
227
365
  res.end();
228
366
  }
229
-
230
- if (socket) {
231
- process.nextTick(() => socket.uncork());
232
- }
233
- }
234
-
235
- getMetrics() {
236
- return {
237
- duration: Number(process.hrtime.bigint() - this._startTime) / 1e6,
238
- };
239
367
  }
240
368
  }
241
369