@aetherframework/middleware 1.0.2
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/.env.example +88 -0
- package/LICENSE +21 -0
- package/README.md +693 -0
- package/docs/readme/README.md +679 -0
- package/docs/readme/README_zh.md +680 -0
- package/examples/advanced-router-demo.js +119 -0
- package/examples/advanced-server.js +272 -0
- package/examples/basic-server.js +134 -0
- package/examples/benchmark.js +85 -0
- package/examples/router-demo.js +369 -0
- package/index.js +67 -0
- package/package.json +59 -0
- package/src/core/AetherCompiler.js +118 -0
- package/src/core/AetherContext.js +242 -0
- package/src/core/AetherPipeline.js +375 -0
- package/src/core/AetherRouter.js +347 -0
- package/src/core/AetherStore.js +204 -0
- package/src/middleware/body-parser.js +299 -0
- package/src/middleware/compression.js +248 -0
- package/src/middleware/cors.js +162 -0
- package/src/middleware/json.js +214 -0
- package/src/middleware/jwt.js +929 -0
- package/src/middleware/params.js +227 -0
- package/src/middleware/rate-limit.js +167 -0
- package/src/middleware/router.js +36 -0
- package/src/middleware/security.js +116 -0
- package/src/middleware/session.js +167 -0
- package/src/utils/atomic-ops.js +127 -0
- package/src/utils/env-loader.js +128 -0
- package/src/utils/memory-pool.js +93 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/middleware/core/AetherRouter
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { EventEmitter } from "events";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* AetherRouter - High-performance routing system for AetherJS
|
|
12
|
+
* Supports versioning, grouping, parameter parsing, and middleware chaining
|
|
13
|
+
*/
|
|
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
|
+
});
|
|
33
|
+
|
|
34
|
+
// Special method: match any HTTP method
|
|
35
|
+
this.all = this._createRouteHandler("ANY");
|
|
36
|
+
}
|
|
37
|
+
|
|
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)
|
|
51
|
+
};
|
|
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;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Build full path with prefix and version
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
_buildPath(path) {
|
|
66
|
+
let fullPath = "";
|
|
67
|
+
|
|
68
|
+
// Add version prefix
|
|
69
|
+
if (this.version) {
|
|
70
|
+
fullPath += `/v${this.version.replace(/[^0-9.]/g, "")}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Add group prefix
|
|
74
|
+
if (this.prefix) {
|
|
75
|
+
fullPath += `/${this.prefix.replace(/^\/|\/$/g, "")}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Add route path
|
|
79
|
+
fullPath += `/${path.replace(/^\/|\/$/g, "")}`;
|
|
80
|
+
|
|
81
|
+
// Normalize path
|
|
82
|
+
return fullPath.replace(/\/+/g, "/").replace(/\/$/, "") || "/";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Convert path pattern to regex
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
_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
|
+
|
|
96
|
+
return new RegExp(`^${pattern}$`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Extract parameter names from path
|
|
101
|
+
* @private
|
|
102
|
+
*/
|
|
103
|
+
_extractParamNames(path) {
|
|
104
|
+
const paramNames = [];
|
|
105
|
+
const paramPattern = /:(\w+)/g;
|
|
106
|
+
const wildcardPattern = /\*(\w+)?/g;
|
|
107
|
+
|
|
108
|
+
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
|
+
|
|
117
|
+
return paramNames;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Wrap handlers with validation
|
|
122
|
+
* @private
|
|
123
|
+
*/
|
|
124
|
+
_wrapHandlers(handlers) {
|
|
125
|
+
return handlers.map(handler => {
|
|
126
|
+
if (typeof handler !== "function") {
|
|
127
|
+
throw new TypeError("Route handler must be a function");
|
|
128
|
+
}
|
|
129
|
+
return handler;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Route grouping
|
|
135
|
+
* @param {string} prefix - Group prefix
|
|
136
|
+
* @param {Function} callback - Group definition function
|
|
137
|
+
*/
|
|
138
|
+
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
|
|
148
|
+
callback(router);
|
|
149
|
+
|
|
150
|
+
// Merge group routes into main router
|
|
151
|
+
router.routes.forEach((route, key) => {
|
|
152
|
+
this.routes.set(key, route);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
|
|
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
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Add middleware to router
|
|
185
|
+
* @param {...Function} middlewares - Middleware functions
|
|
186
|
+
*/
|
|
187
|
+
use(...middlewares) {
|
|
188
|
+
this.middlewares.push(...middlewares);
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
|
|
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
|
+
match(method, url) {
|
|
199
|
+
// Parse URL and query parameters
|
|
200
|
+
const [pathname, search] = url.split("?");
|
|
201
|
+
const query = this._parseQuery(search);
|
|
202
|
+
|
|
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;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check path match
|
|
213
|
+
const match = pathname.match(route.regex);
|
|
214
|
+
if (match) {
|
|
215
|
+
const params = {};
|
|
216
|
+
|
|
217
|
+
// Extract named parameters
|
|
218
|
+
if (match.groups) {
|
|
219
|
+
Object.assign(params, match.groups);
|
|
220
|
+
}
|
|
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
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Parse query string parameters
|
|
243
|
+
* @private
|
|
244
|
+
*/
|
|
245
|
+
_parseQuery(search) {
|
|
246
|
+
const query = {};
|
|
247
|
+
if (!search) return query;
|
|
248
|
+
|
|
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
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return query;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Generate router middleware for AetherPipeline
|
|
274
|
+
*/
|
|
275
|
+
middleware() {
|
|
276
|
+
return async (context, next) => {
|
|
277
|
+
const match = this.match(context.method, context.url);
|
|
278
|
+
|
|
279
|
+
if (match) {
|
|
280
|
+
// Set route parameters and query parameters
|
|
281
|
+
context.params = match.params;
|
|
282
|
+
context.setState("query", match.query);
|
|
283
|
+
context.route = match.route;
|
|
284
|
+
|
|
285
|
+
// Execute route handler chain
|
|
286
|
+
await this._executeHandlers(context, match.handlers);
|
|
287
|
+
} else if (typeof next === "function") {
|
|
288
|
+
// No matching route, continue to next middleware
|
|
289
|
+
await next();
|
|
290
|
+
} 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
|
+
});
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
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
|
+
getRoutes() {
|
|
324
|
+
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
|
+
});
|
|
333
|
+
return routes;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Clear all routes and middlewares
|
|
338
|
+
*/
|
|
339
|
+
clear() {
|
|
340
|
+
this.routes.clear();
|
|
341
|
+
this.groups.clear();
|
|
342
|
+
this.middlewares = [];
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export default AetherRouter;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/middleware/core/AetherStore
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { EventEmitter } from 'events';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Memory storage backend with LRU cache
|
|
12
|
+
*/
|
|
13
|
+
class MemoryStore extends EventEmitter {
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
super();
|
|
16
|
+
this.maxSize = options.maxSize || 10000;
|
|
17
|
+
this.ttl = options.ttl || 3600000; // 1 hour
|
|
18
|
+
this.store = new Map();
|
|
19
|
+
this.lru = []; // List of keys in access order
|
|
20
|
+
this.stats = {
|
|
21
|
+
hits: 0,
|
|
22
|
+
misses: 0,
|
|
23
|
+
sets: 0,
|
|
24
|
+
deletes: 0,
|
|
25
|
+
size: 0
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Start cleanup interval
|
|
29
|
+
this.cleanupInterval = setInterval(() => this._cleanup(), 60000).unref();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async get(key) {
|
|
33
|
+
const entry = this.store.get(key);
|
|
34
|
+
|
|
35
|
+
if (!entry) {
|
|
36
|
+
this.stats.misses++;
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check if expired
|
|
41
|
+
if (entry.expires && Date.now() > entry.expires) {
|
|
42
|
+
this.store.delete(key);
|
|
43
|
+
this._removeFromLRU(key);
|
|
44
|
+
this.stats.misses++;
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Update LRU
|
|
49
|
+
this._updateLRU(key);
|
|
50
|
+
this.stats.hits++;
|
|
51
|
+
|
|
52
|
+
return entry.value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async set(key, value, ttl = this.ttl) {
|
|
56
|
+
// If key exists, update LRU
|
|
57
|
+
if (this.store.has(key)) {
|
|
58
|
+
this._updateLRU(key);
|
|
59
|
+
} else {
|
|
60
|
+
// Check capacity
|
|
61
|
+
if (this.store.size >= this.maxSize) {
|
|
62
|
+
this._evict();
|
|
63
|
+
}
|
|
64
|
+
this.lru.push(key);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const expires = ttl ? Date.now() + ttl : null;
|
|
68
|
+
|
|
69
|
+
this.store.set(key, {
|
|
70
|
+
value,
|
|
71
|
+
expires,
|
|
72
|
+
createdAt: Date.now(),
|
|
73
|
+
accessedAt: Date.now()
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
this.stats.sets++;
|
|
77
|
+
this.stats.size = this.store.size;
|
|
78
|
+
|
|
79
|
+
this.emit('set', { key, value });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async delete(key) {
|
|
83
|
+
const deleted = this.store.delete(key);
|
|
84
|
+
if (deleted) {
|
|
85
|
+
this._removeFromLRU(key);
|
|
86
|
+
this.stats.deletes++;
|
|
87
|
+
this.stats.size = this.store.size;
|
|
88
|
+
this.emit('delete', { key });
|
|
89
|
+
}
|
|
90
|
+
return deleted;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async clear() {
|
|
94
|
+
this.store.clear();
|
|
95
|
+
this.lru = [];
|
|
96
|
+
this.stats = { hits: 0, misses: 0, sets: 0, deletes: 0, size: 0 };
|
|
97
|
+
this.emit('clear');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async has(key) {
|
|
101
|
+
const entry = this.store.get(key);
|
|
102
|
+
if (!entry) return false;
|
|
103
|
+
|
|
104
|
+
if (entry.expires && Date.now() > entry.expires) {
|
|
105
|
+
this.store.delete(key);
|
|
106
|
+
this._removeFromLRU(key);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this._updateLRU(key);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async keys() {
|
|
115
|
+
return Array.from(this.store.keys());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async size() {
|
|
119
|
+
return this.store.size;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Update LRU order
|
|
124
|
+
* @param {string} key
|
|
125
|
+
*/
|
|
126
|
+
_updateLRU(key) {
|
|
127
|
+
const index = this.lru.indexOf(key);
|
|
128
|
+
if (index > -1) {
|
|
129
|
+
this.lru.splice(index, 1);
|
|
130
|
+
}
|
|
131
|
+
this.lru.push(key);
|
|
132
|
+
|
|
133
|
+
// Update accessed time
|
|
134
|
+
const entry = this.store.get(key);
|
|
135
|
+
if (entry) {
|
|
136
|
+
entry.accessedAt = Date.now();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Remove key from LRU list
|
|
142
|
+
* @param {string} key
|
|
143
|
+
*/
|
|
144
|
+
_removeFromLRU(key) {
|
|
145
|
+
const index = this.lru.indexOf(key);
|
|
146
|
+
if (index > -1) {
|
|
147
|
+
this.lru.splice(index, 1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Evict least recently used item
|
|
153
|
+
*/
|
|
154
|
+
_evict() {
|
|
155
|
+
if (this.lru.length === 0) return;
|
|
156
|
+
|
|
157
|
+
const oldestKey = this.lru.shift();
|
|
158
|
+
this.store.delete(oldestKey);
|
|
159
|
+
this.stats.size = this.store.size;
|
|
160
|
+
this.emit('evict', { key: oldestKey });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Cleanup expired items
|
|
165
|
+
*/
|
|
166
|
+
_cleanup() {
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
const keysToDelete = [];
|
|
169
|
+
|
|
170
|
+
for (const [key, entry] of this.store.entries()) {
|
|
171
|
+
if (entry.expires && now > entry.expires) {
|
|
172
|
+
keysToDelete.push(key);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const key of keysToDelete) {
|
|
177
|
+
this.store.delete(key);
|
|
178
|
+
this._removeFromLRU(key);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (keysToDelete.length > 0) {
|
|
182
|
+
this.stats.size = this.store.size;
|
|
183
|
+
this.emit('cleanup', { count: keysToDelete.length });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
destroy() {
|
|
188
|
+
clearInterval(this.cleanupInterval);
|
|
189
|
+
this.clear();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Factory function to create store instances
|
|
195
|
+
* @param {Object} options - Store configuration
|
|
196
|
+
* @returns {MemoryStore} - Store instance
|
|
197
|
+
*/
|
|
198
|
+
function createAetherStore(options = {}) {
|
|
199
|
+
// In a full implementation, this would switch between Memory, Redis, etc.
|
|
200
|
+
// For now, we return the high-performance MemoryStore
|
|
201
|
+
return new MemoryStore(options);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export default createAetherStore;
|