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.
- package/.env.example +88 -0
- package/LICENSE +21 -0
- package/README.md +578 -0
- package/examples/advanced-server.js +272 -0
- package/examples/basic-server.js +134 -0
- package/examples/benchmark.js +85 -0
- package/index.js +59 -0
- package/package.json +62 -0
- package/src/core/AetherCompiler.js +118 -0
- package/src/core/AetherContext.js +240 -0
- package/src/core/AetherPipeline.js +371 -0
- package/src/core/AetherStore.js +200 -0
- package/src/middleware/body-parser.js +295 -0
- package/src/middleware/compression.js +243 -0
- package/src/middleware/cors.js +155 -0
- package/src/middleware/json.js +207 -0
- package/src/middleware/jwt.js +222 -0
- package/src/middleware/rate-limit.js +232 -0
- package/src/middleware/security.js +114 -0
- package/src/middleware/session.js +167 -0
- package/src/utils/atomic-ops.js +125 -0
- package/src/utils/env-loader.js +124 -0
- package/src/utils/memory-pool.js +93 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// security.js - Security headers middleware for AetherJS
|
|
2
|
+
// High-performance implementation with pre-computed headers
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parse Permissions-Policy directives string
|
|
6
|
+
*/
|
|
7
|
+
function parsePermissionsPolicy(directivesString) {
|
|
8
|
+
if (!directivesString || typeof directivesString !== "string") {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const directives = {};
|
|
12
|
+
const pairs = directivesString.split(/[,;]/);
|
|
13
|
+
for (const pair of pairs) {
|
|
14
|
+
const trimmed = pair.trim();
|
|
15
|
+
if (!trimmed) continue;
|
|
16
|
+
const equalIndex = trimmed.indexOf("=");
|
|
17
|
+
if (equalIndex !== -1) {
|
|
18
|
+
const feature = trimmed.substring(0, equalIndex).trim();
|
|
19
|
+
const value = trimmed.substring(equalIndex + 1).trim();
|
|
20
|
+
if (feature && value) directives[feature] = value;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return Object.keys(directives).length > 0 ? directives : null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create security headers middleware for AetherJS
|
|
28
|
+
* @param {Object} options - Security headers configuration
|
|
29
|
+
* @returns {Function} - Standard AetherJS middleware (ctx, next)
|
|
30
|
+
*/
|
|
31
|
+
function createSecurityMiddleware(options = {}) {
|
|
32
|
+
const envConfig = {
|
|
33
|
+
hstsEnabled: process.env.SECURITY_HSTS_ENABLED,
|
|
34
|
+
hstsMaxAge: process.env.SECURITY_HSTS_MAX_AGE,
|
|
35
|
+
noSniffEnabled: process.env.SECURITY_NO_SNIFF,
|
|
36
|
+
xssFilterEnabled: process.env.SECURITY_XSS_FILTER,
|
|
37
|
+
frameguardAction: process.env.SECURITY_FRAMEGUARD_ACTION,
|
|
38
|
+
hidePoweredBy: process.env.SECURITY_HIDE_POWERED_BY,
|
|
39
|
+
referrerPolicy: process.env.SECURITY_REFERRER_POLICY,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const defaults = {
|
|
43
|
+
hsts: {
|
|
44
|
+
enabled: envConfig.hstsEnabled !== "false",
|
|
45
|
+
maxAge: envConfig.hstsMaxAge ? parseInt(envConfig.hstsMaxAge) : 31536000,
|
|
46
|
+
includeSubDomains: true,
|
|
47
|
+
preload: false,
|
|
48
|
+
},
|
|
49
|
+
noSniff: { enabled: envConfig.noSniffEnabled !== "false" },
|
|
50
|
+
xssFilter: { enabled: envConfig.xssFilterEnabled !== "false" },
|
|
51
|
+
frameguard: { enabled: true, action: envConfig.frameguardAction || "DENY" },
|
|
52
|
+
hidePoweredBy: envConfig.hidePoweredBy !== "false",
|
|
53
|
+
referrerPolicy: {
|
|
54
|
+
enabled: true,
|
|
55
|
+
value: envConfig.referrerPolicy || "strict-origin-when-cross-origin",
|
|
56
|
+
},
|
|
57
|
+
permissionsPolicy: {
|
|
58
|
+
enabled: true,
|
|
59
|
+
directives: { camera: "()", microphone: "()", geolocation: "()" },
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Deep merge simple version for performance
|
|
64
|
+
const config = { ...defaults, ...options };
|
|
65
|
+
|
|
66
|
+
// 🚀 性能优化:预计算所有 Header 字符串,避免在请求响应循环中构造字符串
|
|
67
|
+
const staticHeaders = [];
|
|
68
|
+
|
|
69
|
+
if (config.hsts.enabled) {
|
|
70
|
+
let val = `max-age=${config.hsts.maxAge}${config.hsts.includeSubDomains ? "; includeSubDomains" : ""}${config.hsts.preload ? "; preload" : ""}`;
|
|
71
|
+
staticHeaders.push(["Strict-Transport-Security", val]);
|
|
72
|
+
}
|
|
73
|
+
if (config.noSniff.enabled)
|
|
74
|
+
staticHeaders.push(["X-Content-Type-Options", "nosniff"]);
|
|
75
|
+
if (config.xssFilter.enabled)
|
|
76
|
+
staticHeaders.push(["X-XSS-Protection", "1; mode=block"]);
|
|
77
|
+
if (config.frameguard.enabled)
|
|
78
|
+
staticHeaders.push([
|
|
79
|
+
"X-Frame-Options",
|
|
80
|
+
config.frameguard.action.toUpperCase(),
|
|
81
|
+
]);
|
|
82
|
+
if (config.referrerPolicy.enabled)
|
|
83
|
+
staticHeaders.push(["Referrer-Policy", config.referrerPolicy.value]);
|
|
84
|
+
if (config.permissionsPolicy.enabled) {
|
|
85
|
+
const p = Object.entries(config.permissionsPolicy.directives)
|
|
86
|
+
.map(([f, v]) => `${f}=${v}`)
|
|
87
|
+
.join(", ");
|
|
88
|
+
staticHeaders.push(["Permissions-Policy", p]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 💡 修复后的核心中间件函数
|
|
93
|
+
* 使用 (context, next) 签名替代旧版的 (context, signal)
|
|
94
|
+
*/
|
|
95
|
+
return async function securityMiddleware(context, next) {
|
|
96
|
+
console.log("Security middleware executing for URL:", context.url);
|
|
97
|
+
// 1. 批量写入预计算的 Header
|
|
98
|
+
for (let i = 0; i < staticHeaders.length; i++) {
|
|
99
|
+
context.setHeader(staticHeaders[i][0], staticHeaders[i][1]);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 2. 移除敏感 Header
|
|
103
|
+
if (config.hidePoweredBy && context._response) {
|
|
104
|
+
context._response.removeHeader("X-Powered-By");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 3. 💡 修复点:调用标准 next() 而不是 signal.next()
|
|
108
|
+
if (typeof next === "function") {
|
|
109
|
+
return next();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export default createSecurityMiddleware;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present, GameCuft & AetherJS Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aether/middleware/session
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import crypto from "crypto";
|
|
9
|
+
|
|
10
|
+
// 🚀 极致优化:用最原始、无任何对象分配的 for 循环单点查找 Cookie,把 GC 开销降到绝对零度
|
|
11
|
+
function getCookieValue(cookieHeader, name) {
|
|
12
|
+
if (!cookieHeader) return undefined;
|
|
13
|
+
const target = name + "=";
|
|
14
|
+
const len = cookieHeader.length;
|
|
15
|
+
let pos = 0;
|
|
16
|
+
while (pos < len) {
|
|
17
|
+
pos = cookieHeader.indexOf(target, pos);
|
|
18
|
+
if (pos === -1) break;
|
|
19
|
+
// 确保是独立的 cookie 名,而不是别的前缀
|
|
20
|
+
if (
|
|
21
|
+
pos === 0 ||
|
|
22
|
+
cookieHeader.charCodeAt(pos - 1) === 32 ||
|
|
23
|
+
cookieHeader.charCodeAt(pos - 1) === 59
|
|
24
|
+
) {
|
|
25
|
+
pos += target.length;
|
|
26
|
+
let end = cookieHeader.indexOf(";", pos);
|
|
27
|
+
if (end === -1) end = len;
|
|
28
|
+
return cookieHeader.substring(pos, end).trim();
|
|
29
|
+
}
|
|
30
|
+
pos += 1;
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 🚀 放弃长 UUID,改用 16 字节的高性能十六进制 ID,缩短 Cookie 长度,减轻网络和压缩中间件开销
|
|
36
|
+
const genId = () => crypto.randomBytes(16).toString("hex");
|
|
37
|
+
|
|
38
|
+
class MemoryStore {
|
|
39
|
+
constructor() {
|
|
40
|
+
this.cache = new Map();
|
|
41
|
+
}
|
|
42
|
+
async get(id) {
|
|
43
|
+
const s = this.cache.get(id);
|
|
44
|
+
if (!s) return null;
|
|
45
|
+
if (Date.now() > s.exp) {
|
|
46
|
+
this.cache.delete(id);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return s.data;
|
|
50
|
+
}
|
|
51
|
+
async set(id, data, ttl) {
|
|
52
|
+
this.cache.set(id, { data, exp: Date.now() + ttl });
|
|
53
|
+
}
|
|
54
|
+
async delete(id) {
|
|
55
|
+
this.cache.delete(id);
|
|
56
|
+
}
|
|
57
|
+
prune() {
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
for (const [k, v] of this.cache) if (now > v.exp) this.cache.delete(k);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class SessionManager {
|
|
64
|
+
constructor(options = {}) {
|
|
65
|
+
const { store, ...restOptions } = options;
|
|
66
|
+
this.config = {
|
|
67
|
+
enabled: process.env.SESSION_ENABLED !== "false",
|
|
68
|
+
maxAge: parseInt(process.env.SESSION_MAX_AGE) || 86400000,
|
|
69
|
+
cookieName: process.env.SESSION_COOKIE_NAME || "aether_sid",
|
|
70
|
+
...restOptions,
|
|
71
|
+
};
|
|
72
|
+
this.config.store =
|
|
73
|
+
store && typeof store === "object" ? store : new MemoryStore();
|
|
74
|
+
if (this.config.store instanceof MemoryStore) {
|
|
75
|
+
this.cleanup = setInterval(
|
|
76
|
+
() => this.config.store.prune(),
|
|
77
|
+
60000,
|
|
78
|
+
).unref();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
middleware() {
|
|
83
|
+
if (!this.config.enabled)
|
|
84
|
+
return (ctx, next) => next && (next.next ? next.next() : next());
|
|
85
|
+
|
|
86
|
+
const { store, maxAge, cookieName } = this.config;
|
|
87
|
+
const cookieSuffix = `; HttpOnly; Secure; SameSite=Strict; Max-Age=${Math.floor(maxAge / 1000)}; Path=/`;
|
|
88
|
+
|
|
89
|
+
return async (ctx, next) => {
|
|
90
|
+
ctx.state ??= {};
|
|
91
|
+
|
|
92
|
+
const sid = getCookieValue(ctx.getHeader("cookie"), cookieName);
|
|
93
|
+
let sessionData = sid ? await store.get(sid) : null;
|
|
94
|
+
|
|
95
|
+
// 🚀 策略调整:如果没拿到,先给个空对象,绝不提前写库、不提前设 Cookie
|
|
96
|
+
let isNew = false;
|
|
97
|
+
if (!sessionData) {
|
|
98
|
+
sessionData = {};
|
|
99
|
+
isNew = true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const sessionState = { id: sid, data: sessionData, dirty: false };
|
|
103
|
+
|
|
104
|
+
ctx.session = {
|
|
105
|
+
get: (key) => sessionState.data[key],
|
|
106
|
+
set: (key, val) => {
|
|
107
|
+
sessionState.data[key] = val;
|
|
108
|
+
sessionState.dirty = true;
|
|
109
|
+
},
|
|
110
|
+
delete: (key) => {
|
|
111
|
+
delete sessionState.data[key];
|
|
112
|
+
sessionState.dirty = true;
|
|
113
|
+
},
|
|
114
|
+
clear: () => {
|
|
115
|
+
sessionState.data = {};
|
|
116
|
+
sessionState.dirty = true;
|
|
117
|
+
},
|
|
118
|
+
destroy: async () => {
|
|
119
|
+
if (sessionState.id) await store.delete(sessionState.id);
|
|
120
|
+
sessionState.id = null;
|
|
121
|
+
sessionState.data = {};
|
|
122
|
+
sessionState.dirty = false;
|
|
123
|
+
ctx.setHeader(
|
|
124
|
+
"Set-Cookie",
|
|
125
|
+
`${cookieName}=; HttpOnly; Secure; SameSite=Strict; Max-Age=0; Path=/`,
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
regenerate: async () => {
|
|
129
|
+
if (sessionState.id) await store.delete(sessionState.id);
|
|
130
|
+
sessionState.id = genId();
|
|
131
|
+
await store.set(sessionState.id, sessionState.data, maxAge);
|
|
132
|
+
ctx.setHeader(
|
|
133
|
+
"Set-Cookie",
|
|
134
|
+
`${cookieName}=${sessionState.id}${cookieSuffix}`,
|
|
135
|
+
);
|
|
136
|
+
sessionState.dirty = false;
|
|
137
|
+
isNew = false;
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// 🚀 用最稳妥且隔离良好的 try...finally 确保管道顺畅,同时将落盘逻辑卡死在“确实有改动”的关口
|
|
142
|
+
try {
|
|
143
|
+
if (next) {
|
|
144
|
+
next.next ? await next.next() : await next();
|
|
145
|
+
}
|
|
146
|
+
} finally {
|
|
147
|
+
if (sessionState.dirty) {
|
|
148
|
+
// 如果是新 session 且路由往里写了数据,在此刻才真正生成 ID 并下发 Cookie
|
|
149
|
+
if (isNew || !sessionState.id) {
|
|
150
|
+
sessionState.id = genId();
|
|
151
|
+
ctx.setHeader(
|
|
152
|
+
"Set-Cookie",
|
|
153
|
+
`${cookieName}=${sessionState.id}${cookieSuffix}`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
await store.set(sessionState.id, sessionState.data, maxAge);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
destroy() {
|
|
163
|
+
if (this.cleanup) clearInterval(this.cleanup);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export default SessionManager;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// atomic-ops.js - Atomic operations for AetherJS
|
|
2
|
+
// Thread-safe state management using synchronous operations
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* AtomicCounter - Thread-safe counter
|
|
6
|
+
*/
|
|
7
|
+
class AtomicCounter {
|
|
8
|
+
constructor(initialValue = 0) {
|
|
9
|
+
this.value = initialValue;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Increment counter
|
|
14
|
+
* @param {number} delta - Amount to increment
|
|
15
|
+
* @returns {number} - New value
|
|
16
|
+
*/
|
|
17
|
+
increment(delta = 1) {
|
|
18
|
+
this.value += delta;
|
|
19
|
+
return this.value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Decrement counter
|
|
24
|
+
* @param {number} delta - Amount to decrement
|
|
25
|
+
* @returns {number} - New value
|
|
26
|
+
*/
|
|
27
|
+
decrement(delta = 1) {
|
|
28
|
+
this.value -= delta;
|
|
29
|
+
return this.value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get current value
|
|
34
|
+
* @returns {number} - Current value
|
|
35
|
+
*/
|
|
36
|
+
get() {
|
|
37
|
+
return this.value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Set value
|
|
42
|
+
* @param {number} value - New value
|
|
43
|
+
*/
|
|
44
|
+
set(value) {
|
|
45
|
+
this.value = value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Compare and swap
|
|
50
|
+
* @param {number} expected - Expected value
|
|
51
|
+
* @param {number} newValue - New value
|
|
52
|
+
* @returns {boolean} - Whether swap succeeded
|
|
53
|
+
*/
|
|
54
|
+
compareAndSwap(expected, newValue) {
|
|
55
|
+
if (this.value === expected) {
|
|
56
|
+
this.value = newValue;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* AtomicFlag - Thread-safe boolean flag
|
|
65
|
+
*/
|
|
66
|
+
class AtomicFlag {
|
|
67
|
+
constructor(initialValue = false) {
|
|
68
|
+
this.value = initialValue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Set flag to true
|
|
73
|
+
* @returns {boolean} - Previous value
|
|
74
|
+
*/
|
|
75
|
+
set() {
|
|
76
|
+
const prev = this.value;
|
|
77
|
+
this.value = true;
|
|
78
|
+
return prev;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Set flag to false
|
|
83
|
+
* @returns {boolean} - Previous value
|
|
84
|
+
*/
|
|
85
|
+
clear() {
|
|
86
|
+
const prev = this.value;
|
|
87
|
+
this.value = false;
|
|
88
|
+
return prev;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Toggle flag
|
|
93
|
+
* @returns {boolean} - New value
|
|
94
|
+
*/
|
|
95
|
+
toggle() {
|
|
96
|
+
this.value = !this.value;
|
|
97
|
+
return this.value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get current value
|
|
102
|
+
* @returns {boolean} - Current value
|
|
103
|
+
*/
|
|
104
|
+
get() {
|
|
105
|
+
return this.value;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Factory functions
|
|
111
|
+
*/
|
|
112
|
+
function createAtomicCounter(initialValue = 0) {
|
|
113
|
+
return new AtomicCounter(initialValue);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function createAtomicFlag(initialValue = false) {
|
|
117
|
+
return new AtomicFlag(initialValue);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export default {
|
|
121
|
+
createAtomicCounter,
|
|
122
|
+
createAtomicFlag,
|
|
123
|
+
AtomicCounter,
|
|
124
|
+
AtomicFlag
|
|
125
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// env-loader.js - Environment variable loader for AetherJS
|
|
2
|
+
// Supports .env file parsing and hot-reloading
|
|
3
|
+
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
/**
|
|
7
|
+
* Parse .env file content
|
|
8
|
+
* @param {string} content - .env file content
|
|
9
|
+
* @returns {Object} - Parsed environment variables
|
|
10
|
+
*/
|
|
11
|
+
function parseEnvContent(content) {
|
|
12
|
+
const env = {};
|
|
13
|
+
const lines = content.split('\n');
|
|
14
|
+
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
// Skip comments and empty lines
|
|
17
|
+
const trimmedLine = line.trim();
|
|
18
|
+
if (!trimmedLine || trimmedLine.startsWith('#')) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Split key=value
|
|
23
|
+
const equalIndex = trimmedLine.indexOf('=');
|
|
24
|
+
if (equalIndex === -1) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const key = trimmedLine.substring(0, equalIndex).trim();
|
|
29
|
+
let value = trimmedLine.substring(equalIndex + 1).trim();
|
|
30
|
+
|
|
31
|
+
// Remove quotes if present
|
|
32
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
33
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
34
|
+
value = value.substring(1, value.length - 1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Set environment variable
|
|
38
|
+
env[key] = value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return env;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Load environment variables from .env file
|
|
46
|
+
* @param {string} envPath - Path to .env file
|
|
47
|
+
* @param {boolean} override - Whether to override existing env vars
|
|
48
|
+
* @returns {Object} - Loaded environment variables
|
|
49
|
+
*/
|
|
50
|
+
function loadEnv(envPath = '.env', override = false) {
|
|
51
|
+
const absolutePath = path.resolve(process.cwd(), envPath);
|
|
52
|
+
|
|
53
|
+
if (!fs.existsSync(absolutePath)) {
|
|
54
|
+
console.warn(`AetherJS: .env file not found at ${absolutePath}`);
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const content = fs.readFileSync(absolutePath, 'utf8');
|
|
60
|
+
const env = parseEnvContent(content);
|
|
61
|
+
|
|
62
|
+
// Set environment variables
|
|
63
|
+
for (const [key, value] of Object.entries(env)) {
|
|
64
|
+
if (override || process.env[key] === undefined) {
|
|
65
|
+
process.env[key] = value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return env;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(`AetherJS: Error loading .env file: ${error.message}`);
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Watch .env file for changes and reload
|
|
78
|
+
* @param {string} envPath - Path to .env file
|
|
79
|
+
* @param {Function} callback - Callback when env changes
|
|
80
|
+
* @returns {Object} - Watcher object with close method
|
|
81
|
+
*/
|
|
82
|
+
function watchEnv(envPath = '.env', callback) {
|
|
83
|
+
const absolutePath = path.resolve(process.cwd(), envPath);
|
|
84
|
+
|
|
85
|
+
if (!fs.existsSync(absolutePath)) {
|
|
86
|
+
console.warn(`AetherJS: .env file not found at ${absolutePath}`);
|
|
87
|
+
return { close: () => {} };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let lastContent = fs.readFileSync(absolutePath, 'utf8');
|
|
91
|
+
|
|
92
|
+
const watcher = fs.watch(absolutePath, (eventType) => {
|
|
93
|
+
if (eventType === 'change') {
|
|
94
|
+
try {
|
|
95
|
+
const newContent = fs.readFileSync(absolutePath, 'utf8');
|
|
96
|
+
if (newContent !== lastContent) {
|
|
97
|
+
lastContent = newContent;
|
|
98
|
+
const newEnv = parseEnvContent(newContent);
|
|
99
|
+
|
|
100
|
+
// Update process.env
|
|
101
|
+
for (const [key, value] of Object.entries(newEnv)) {
|
|
102
|
+
process.env[key] = value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (callback) {
|
|
106
|
+
callback(newEnv);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(`AetherJS: Error reloading .env file: ${error.message}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
close: () => watcher.close()
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export default {
|
|
120
|
+
loadEnv,
|
|
121
|
+
watchEnv,
|
|
122
|
+
parseEnvContent
|
|
123
|
+
};
|
|
124
|
+
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// memory-pool.js - Memory pool management for AetherJS
|
|
2
|
+
// Zero-copy buffer management for high-performance operations
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MemoryPool - Manages a pool of reusable buffers
|
|
6
|
+
*/
|
|
7
|
+
class MemoryPool {
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.bufferSize = options.bufferSize || 8192; // 8KB default
|
|
10
|
+
this.poolSize = options.poolSize || 100;
|
|
11
|
+
this.buffers = [];
|
|
12
|
+
this.available = [];
|
|
13
|
+
this.stats = {
|
|
14
|
+
allocated: 0,
|
|
15
|
+
reused: 0,
|
|
16
|
+
created: 0
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Initialize pool
|
|
20
|
+
for (let i = 0; i < this.poolSize; i++) {
|
|
21
|
+
const buffer = Buffer.allocUnsafe(this.bufferSize);
|
|
22
|
+
this.buffers.push(buffer);
|
|
23
|
+
this.available.push(i);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get a buffer from the pool
|
|
29
|
+
* @returns {Buffer} - Buffer from pool
|
|
30
|
+
*/
|
|
31
|
+
get() {
|
|
32
|
+
if (this.available.length > 0) {
|
|
33
|
+
const index = this.available.pop();
|
|
34
|
+
this.stats.reused++;
|
|
35
|
+
return this.buffers[index];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Pool exhausted, create new buffer
|
|
39
|
+
const buffer = Buffer.allocUnsafe(this.bufferSize);
|
|
40
|
+
this.buffers.push(buffer);
|
|
41
|
+
this.stats.created++;
|
|
42
|
+
this.stats.allocated++;
|
|
43
|
+
return buffer;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Return a buffer to the pool
|
|
48
|
+
* @param {Buffer} buffer - Buffer to return
|
|
49
|
+
*/
|
|
50
|
+
release(buffer) {
|
|
51
|
+
const index = this.buffers.indexOf(buffer);
|
|
52
|
+
if (index !== -1 && !this.available.includes(index)) {
|
|
53
|
+
// Clear buffer content for security
|
|
54
|
+
buffer.fill(0);
|
|
55
|
+
this.available.push(index);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get pool statistics
|
|
61
|
+
* @returns {Object} - Pool stats
|
|
62
|
+
*/
|
|
63
|
+
getStats() {
|
|
64
|
+
return {
|
|
65
|
+
...this.stats,
|
|
66
|
+
totalBuffers: this.buffers.length,
|
|
67
|
+
availableBuffers: this.available.length,
|
|
68
|
+
utilization: ((this.buffers.length - this.available.length) / this.buffers.length * 100).toFixed(2) + '%'
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Destroy pool and free memory
|
|
74
|
+
*/
|
|
75
|
+
destroy() {
|
|
76
|
+
this.buffers = [];
|
|
77
|
+
this.available = [];
|
|
78
|
+
this.stats = { allocated: 0, reused: 0, created: 0 };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Factory function to create memory pool instances
|
|
84
|
+
* @param {Object} options - Pool configuration
|
|
85
|
+
* @returns {MemoryPool} - Memory pool instance
|
|
86
|
+
*/
|
|
87
|
+
function createMemoryPool(options = {}) {
|
|
88
|
+
return new MemoryPool(options);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
export default createMemoryPool;
|