aetherframework-middleware 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,371 @@
1
+ // AetherPipeline.js - Ultra-high performance middleware pipeline (Fixed Headers & High Concurrency)
2
+
3
+ import { EventEmitter } from "events";
4
+ import AetherContext from "./AetherContext.js";
5
+
6
+ const STATIC_RESPONSES = new Map([
7
+ [200, Buffer.from(JSON.stringify({ status: "ok" }))],
8
+ [404, Buffer.from(JSON.stringify({ error: "Not Found" }))],
9
+ [500, Buffer.from(JSON.stringify({ error: "Internal Server Error" }))],
10
+ ]);
11
+
12
+ const CONTEXT_POOL = [];
13
+ const IN_POOL_CHECK = new Set();
14
+ const CONTEXT_POOL_SIZE = 4096;
15
+
16
+ class AetherPipeline extends EventEmitter {
17
+ constructor() {
18
+ super();
19
+ this._middlewares = [];
20
+ this._compiled = null;
21
+ this._compiledSync = null;
22
+ this._cache = new Map();
23
+ this._cacheMaxSize = 1000;
24
+ this.enableMetrics = false;
25
+
26
+ this._stats = {
27
+ totalRequests: 0,
28
+ averageLatency: 0,
29
+ errorCount: 0,
30
+ cacheHits: 0,
31
+ cacheMisses: 0,
32
+ poolHits: 0,
33
+ poolMisses: 0,
34
+ };
35
+
36
+ this._initObjectPools();
37
+ }
38
+
39
+ _initObjectPools() {
40
+ for (let i = 0; i < CONTEXT_POOL_SIZE; i++) {
41
+ const ctx = new AetherContext(null, null);
42
+ CONTEXT_POOL.push(ctx);
43
+ IN_POOL_CHECK.add(ctx);
44
+ }
45
+ }
46
+
47
+ _getContext(request, response) {
48
+ if (CONTEXT_POOL.length > 0) {
49
+ const context = CONTEXT_POOL.pop();
50
+ IN_POOL_CHECK.delete(context);
51
+ context._reset(request, response);
52
+ if (this.enableMetrics) this._stats.poolHits++;
53
+ return context;
54
+ }
55
+ if (this.enableMetrics) this._stats.poolMisses++;
56
+ return new AetherContext(request, response);
57
+ }
58
+
59
+ _returnContext(context) {
60
+ if (!context || IN_POOL_CHECK.has(context)) return;
61
+
62
+ if (CONTEXT_POOL.length < CONTEXT_POOL_SIZE) {
63
+ context.req = null;
64
+ context.res = null;
65
+ context._body = null;
66
+
67
+ // Safely clean up Headers
68
+ if (context._headers && typeof context._headers.clear === "function") {
69
+ context._headers.clear();
70
+ } else {
71
+ context._headers = null;
72
+ }
73
+
74
+ CONTEXT_POOL.push(context);
75
+ IN_POOL_CHECK.add(context);
76
+ }
77
+ }
78
+
79
+ use(middleware) {
80
+ if (typeof middleware !== "function") {
81
+ throw new TypeError("Middleware must be a function");
82
+ }
83
+ this._middlewares.push(middleware);
84
+ this._compiled = null;
85
+ this._compiledSync = null;
86
+ return this;
87
+ }
88
+
89
+ /**
90
+ *Core Control 1: Standard V8-level high-concurrency asynchronous onion model compiler
91
+ * Ensure perfect timing wait chain, absolutely prevent asynchronous middleware (like CORS/security headers) from experiencing asynchronous drift
92
+ */
93
+ compile() {
94
+ if (this._compiled) return this._compiled;
95
+ const middlewares = this._middlewares;
96
+ const len = middlewares.length;
97
+
98
+ this._compiled = async function executePipeline(context) {
99
+ async function dispatch(i) {
100
+ // 1. Safety boundary check: If context is terminated or connection is disconnected, return directly
101
+ if (
102
+ context.isTerminated() ||
103
+ (context._response && context._response.writableEnded)
104
+ ) {
105
+ if (context._finalize) context._finalize();
106
+ return;
107
+ }
108
+
109
+ // 2. Pipeline end: Safely trigger final _finalize
110
+ if (i >= len) {
111
+ if (context._finalize) context._finalize();
112
+ return;
113
+ }
114
+
115
+ const mw = middlewares[i];
116
+
117
+ // 3. Strictly bind current middleware with next subsequent chain's asynchronous timing
118
+ await mw(context, function next() {
119
+ return dispatch(i + 1);
120
+ });
121
+ }
122
+
123
+ // 🚀 Start the first middleware and strictly wait for the entire chain lifecycle to end
124
+ await dispatch(0);
125
+ };
126
+
127
+ return this._compiled;
128
+ }
129
+
130
+ _compileSync() {
131
+ if (this._compiledSync) return this._compiledSync;
132
+ const middlewares = this._middlewares;
133
+ const len = middlewares.length;
134
+
135
+ const allSync = middlewares.every((mw) => {
136
+ const funcStr = mw.toString();
137
+ return (
138
+ !funcStr.includes("async ") &&
139
+ !funcStr.includes(".then") &&
140
+ !funcStr.includes("await ")
141
+ );
142
+ });
143
+
144
+ if (!allSync) return null;
145
+
146
+ this._compiledSync = function executePipelineSync(context) {
147
+ for (let i = 0; i < len; i++) {
148
+ middlewares[i](context, () => {});
149
+ if (context.isTerminated() || context.res?.writableEnded) return;
150
+ }
151
+ if (context._finalize) context._finalize();
152
+ };
153
+
154
+ return this._compiledSync;
155
+ }
156
+
157
+ async handle(request, response) {
158
+ this._stats.totalRequests++;
159
+ const url = request.url;
160
+ const method = request.method;
161
+
162
+ // 1. Root high-speed channel (with fallback CORS headers)
163
+ if (method === "GET" && url === "/") {
164
+ const socket = response.socket;
165
+ if (socket) socket.cork();
166
+ response.writeHead(200, [
167
+ "Content-Type",
168
+ "application/json; charset=utf-8",
169
+ "Content-Length",
170
+ "15",
171
+ "Connection",
172
+ "keep-alive",
173
+ "Access-Control-Allow-Origin",
174
+ "*",
175
+ ]);
176
+ response.end(STATIC_RESPONSES.get(200));
177
+ if (socket) socket.uncork();
178
+ return { cacheHit: true, static: true };
179
+ }
180
+
181
+ // 2. Cache route hit
182
+ let methodCache = this._cache.get(method);
183
+ if (!methodCache) {
184
+ methodCache = new Map();
185
+ this._cache.set(method, methodCache);
186
+ }
187
+ const cached = methodCache.get(url);
188
+
189
+ if (cached) {
190
+ if (this.enableMetrics) this._stats.cacheHits++;
191
+ const socket = response.socket;
192
+ if (socket) socket.cork();
193
+ response.writeHead(cached.status, cached.headers);
194
+ response.end(cached.buffer);
195
+ if (socket) socket.uncork();
196
+ return { cacheHit: true };
197
+ }
198
+
199
+ if (this.enableMetrics) this._stats.cacheMisses++;
200
+
201
+ // 3. Context construction and core scheduling
202
+ const context = this._getContext(request, response);
203
+
204
+ try {
205
+ //Core Control 2: Prioritize reading cached synchronous pipeline to avoid frequent character scanning causing CPU spikes under high concurrency
206
+ const pipelineSync = this._compiledSync || this._compileSync();
207
+
208
+ if (pipelineSync) {
209
+ pipelineSync(context);
210
+ } else {
211
+ // Strictly lock asynchronous middleware timing
212
+ await this.compile()(context);
213
+ }
214
+
215
+ // 4. Dynamically capture all response headers, merge and store in high-speed cache
216
+ if (method === "GET" && response && response.statusCode < 400) {
217
+ this._setResponseCache(methodCache, url, context, response);
218
+ }
219
+
220
+ this._returnContext(context);
221
+ return { cacheHit: false };
222
+ } catch (error) {
223
+ if (this.enableMetrics) this._stats.errorCount++;
224
+ try {
225
+ if (response && !response.headersSent) {
226
+ response.writeHead(500, [
227
+ "Content-Type",
228
+ "application/json; charset=utf-8",
229
+ "Access-Control-Allow-Origin",
230
+ "*",
231
+ ]);
232
+ response.end(STATIC_RESPONSES.get(500));
233
+ }
234
+ } catch (e) {}
235
+ if (context) this._returnContext(context);
236
+ throw error;
237
+ }
238
+ }
239
+
240
+ /**
241
+ *Core Control 3: Ultimate comprehensive defense extraction algorithm —— Intercept multi-source Headers without blind spots
242
+ */
243
+ _setResponseCache(methodCache, url, context, response) {
244
+ // Establish a standard flat array, native two-element storage format is most efficient
245
+ const rawHeaders = ["Connection", "keep-alive"];
246
+ const lowerKeys = new Set(["connection"]);
247
+
248
+ // ==========================================
249
+ // Strategy A: Forcefully synchronously extract AetherContext's high-performance private storage
250
+ // ==========================================
251
+ if (context && context._headersCount > 0) {
252
+ for (let i = 0; i < context._headersCount; i++) {
253
+ const key = context._headersKeys[i];
254
+ const val = context._headersObj[key];
255
+ if (val !== undefined && key) {
256
+ const kLower = key.toLowerCase();
257
+ if (!lowerKeys.has(kLower)) {
258
+ rawHeaders.push(key, String(val));
259
+ lowerKeys.add(kLower);
260
+ }
261
+ }
262
+ }
263
+ }
264
+
265
+ // ==========================================
266
+ // Strategy B: Deep scan various non-standard Context properties that may evolve into generic dictionaries
267
+ // ==========================================
268
+ const potentialDicts = [
269
+ context?._headers,
270
+ context?.headers,
271
+ context?.res?.headers,
272
+ ];
273
+ for (const dict of potentialDicts) {
274
+ if (dict && typeof dict === "object" && !(dict instanceof Set)) {
275
+ for (const key in dict) {
276
+ if (Object.prototype.hasOwnProperty.call(dict, key)) {
277
+ const kLower = key.toLowerCase();
278
+ if (!lowerKeys.has(kLower) && dict[key] !== undefined) {
279
+ rawHeaders.push(key, String(dict[key]));
280
+ lowerKeys.add(kLower);
281
+ }
282
+ }
283
+ }
284
+ }
285
+ }
286
+
287
+ // ==========================================
288
+ // Strategy C: Ultimate gap filling: Synchronously capture Node.js native response's standard response headers (security upgraded version)
289
+ // ==========================================
290
+ if (response) {
291
+ // 1. Intercept standard getHeaders() - Standard entry point for most modern Node.js
292
+ if (typeof response.getHeaders === "function") {
293
+ const nodeHeaders = response.getHeaders();
294
+ if (nodeHeaders) {
295
+ for (const key in nodeHeaders) {
296
+ const kLower = key.toLowerCase();
297
+ if (!lowerKeys.has(kLower)) {
298
+ const val = nodeHeaders[key];
299
+ rawHeaders.push(
300
+ key,
301
+ Array.isArray(val) ? val.join(", ") : String(val),
302
+ );
303
+ lowerKeys.add(kLower);
304
+ }
305
+ }
306
+ }
307
+ }
308
+
309
+ // 2. Intercept standard getHeaderNames() - As standard supplement for HTTP/2 or special streaming protocols
310
+ if (typeof response.getHeaderNames === "function") {
311
+ const names = response.getHeaderNames();
312
+ if (Array.isArray(names)) {
313
+ for (const key of names) {
314
+ const kLower = key.toLowerCase();
315
+ if (!lowerKeys.has(kLower)) {
316
+ const val = response.getHeader(key);
317
+ if (val !== undefined && val !== null) {
318
+ rawHeaders.push(
319
+ key,
320
+ Array.isArray(val) ? val.join(", ") : String(val),
321
+ );
322
+ lowerKeys.add(kLower);
323
+ }
324
+ }
325
+ }
326
+ }
327
+ }
328
+
329
+ // 🟢 Removed response._headers detection code that would cause high-version Node.js crashes and deprecation warnings
330
+ }
331
+
332
+ // ==========================================
333
+ // Strategy D: Extract Body and convert to high-speed persistent Buffer
334
+ // ==========================================
335
+ let body = context._body || "";
336
+ const buffer = Buffer.isBuffer(body)
337
+ ? body
338
+ : Buffer.from(typeof body === "string" ? body : JSON.stringify(body));
339
+
340
+ // Complete length header
341
+ if (!lowerKeys.has("content-length")) {
342
+ rawHeaders.push("Content-Length", String(buffer.length));
343
+ }
344
+
345
+ methodCache.set(url, {
346
+ headers: rawHeaders,
347
+ status: context.statusCode || response.statusCode || 200,
348
+ buffer,
349
+ timestamp: Date.now(),
350
+ });
351
+
352
+ if (methodCache.size > this._cacheMaxSize) {
353
+ const firstKey = methodCache.keys().next().value;
354
+ methodCache.delete(firstKey);
355
+ }
356
+ }
357
+
358
+ getStats() {
359
+ return { ...this._stats, poolSize: CONTEXT_POOL.length };
360
+ }
361
+ clearCache() {
362
+ this._cache.clear();
363
+ }
364
+ precompile() {
365
+ this.compile();
366
+ this._compileSync();
367
+ return this;
368
+ }
369
+ }
370
+
371
+ export default AetherPipeline;
@@ -0,0 +1,200 @@
1
+ // AetherStore.js - High-performance storage engine for AetherJS
2
+ // Supports multiple backends with atomic operations and LRU cache
3
+
4
+ import { EventEmitter } from 'events';
5
+
6
+ /**
7
+ * Memory storage backend with LRU cache
8
+ */
9
+ class MemoryStore extends EventEmitter {
10
+ constructor(options = {}) {
11
+ super();
12
+ this.maxSize = options.maxSize || 10000;
13
+ this.ttl = options.ttl || 3600000; // 1 hour
14
+ this.store = new Map();
15
+ this.lru = []; // List of keys in access order
16
+ this.stats = {
17
+ hits: 0,
18
+ misses: 0,
19
+ sets: 0,
20
+ deletes: 0,
21
+ size: 0
22
+ };
23
+
24
+ // Start cleanup interval
25
+ this.cleanupInterval = setInterval(() => this._cleanup(), 60000).unref();
26
+ }
27
+
28
+ async get(key) {
29
+ const entry = this.store.get(key);
30
+
31
+ if (!entry) {
32
+ this.stats.misses++;
33
+ return null;
34
+ }
35
+
36
+ // Check if expired
37
+ if (entry.expires && Date.now() > entry.expires) {
38
+ this.store.delete(key);
39
+ this._removeFromLRU(key);
40
+ this.stats.misses++;
41
+ return null;
42
+ }
43
+
44
+ // Update LRU
45
+ this._updateLRU(key);
46
+ this.stats.hits++;
47
+
48
+ return entry.value;
49
+ }
50
+
51
+ async set(key, value, ttl = this.ttl) {
52
+ // If key exists, update LRU
53
+ if (this.store.has(key)) {
54
+ this._updateLRU(key);
55
+ } else {
56
+ // Check capacity
57
+ if (this.store.size >= this.maxSize) {
58
+ this._evict();
59
+ }
60
+ this.lru.push(key);
61
+ }
62
+
63
+ const expires = ttl ? Date.now() + ttl : null;
64
+
65
+ this.store.set(key, {
66
+ value,
67
+ expires,
68
+ createdAt: Date.now(),
69
+ accessedAt: Date.now()
70
+ });
71
+
72
+ this.stats.sets++;
73
+ this.stats.size = this.store.size;
74
+
75
+ this.emit('set', { key, value });
76
+ }
77
+
78
+ async delete(key) {
79
+ const deleted = this.store.delete(key);
80
+ if (deleted) {
81
+ this._removeFromLRU(key);
82
+ this.stats.deletes++;
83
+ this.stats.size = this.store.size;
84
+ this.emit('delete', { key });
85
+ }
86
+ return deleted;
87
+ }
88
+
89
+ async clear() {
90
+ this.store.clear();
91
+ this.lru = [];
92
+ this.stats = { hits: 0, misses: 0, sets: 0, deletes: 0, size: 0 };
93
+ this.emit('clear');
94
+ }
95
+
96
+ async has(key) {
97
+ const entry = this.store.get(key);
98
+ if (!entry) return false;
99
+
100
+ if (entry.expires && Date.now() > entry.expires) {
101
+ this.store.delete(key);
102
+ this._removeFromLRU(key);
103
+ return false;
104
+ }
105
+
106
+ this._updateLRU(key);
107
+ return true;
108
+ }
109
+
110
+ async keys() {
111
+ return Array.from(this.store.keys());
112
+ }
113
+
114
+ async size() {
115
+ return this.store.size;
116
+ }
117
+
118
+ /**
119
+ * Update LRU order
120
+ * @param {string} key
121
+ */
122
+ _updateLRU(key) {
123
+ const index = this.lru.indexOf(key);
124
+ if (index > -1) {
125
+ this.lru.splice(index, 1);
126
+ }
127
+ this.lru.push(key);
128
+
129
+ // Update accessed time
130
+ const entry = this.store.get(key);
131
+ if (entry) {
132
+ entry.accessedAt = Date.now();
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Remove key from LRU list
138
+ * @param {string} key
139
+ */
140
+ _removeFromLRU(key) {
141
+ const index = this.lru.indexOf(key);
142
+ if (index > -1) {
143
+ this.lru.splice(index, 1);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Evict least recently used item
149
+ */
150
+ _evict() {
151
+ if (this.lru.length === 0) return;
152
+
153
+ const oldestKey = this.lru.shift();
154
+ this.store.delete(oldestKey);
155
+ this.stats.size = this.store.size;
156
+ this.emit('evict', { key: oldestKey });
157
+ }
158
+
159
+ /**
160
+ * Cleanup expired items
161
+ */
162
+ _cleanup() {
163
+ const now = Date.now();
164
+ const keysToDelete = [];
165
+
166
+ for (const [key, entry] of this.store.entries()) {
167
+ if (entry.expires && now > entry.expires) {
168
+ keysToDelete.push(key);
169
+ }
170
+ }
171
+
172
+ for (const key of keysToDelete) {
173
+ this.store.delete(key);
174
+ this._removeFromLRU(key);
175
+ }
176
+
177
+ if (keysToDelete.length > 0) {
178
+ this.stats.size = this.store.size;
179
+ this.emit('cleanup', { count: keysToDelete.length });
180
+ }
181
+ }
182
+
183
+ destroy() {
184
+ clearInterval(this.cleanupInterval);
185
+ this.clear();
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Factory function to create store instances
191
+ * @param {Object} options - Store configuration
192
+ * @returns {MemoryStore} - Store instance
193
+ */
194
+ function createAetherStore(options = {}) {
195
+ // In a full implementation, this would switch between Memory, Redis, etc.
196
+ // For now, we return the high-performance MemoryStore
197
+ return new MemoryStore(options);
198
+ }
199
+
200
+ export default createAetherStore;