kempo-server 1.7.4 โ 1.7.6
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/CONFIG.md +501 -0
- package/README.md +13 -442
- package/UTILS.md +127 -0
- package/dist/utils/cli.js +1 -0
- package/dist/utils/fs-utils.js +1 -0
- package/package.json +7 -2
- package/scripts/build.js +57 -43
- package/tests/getFiles.node-test.js +3 -3
- package/tests/index.node-test.js +3 -2
- package/tests/router-middleware.node-test.js +2 -3
- package/tests/router.node-test.js +7 -8
- package/tests/serveFile.node-test.js +5 -8
- package/tests/test-server-root/.config.json +5 -0
- package/tests/test-server-root/README.md +118 -0
- package/tests/test-server-root/a.txt +1 -0
- package/tests/test-server-root/api/GET.js +1 -0
- package/tests/test-server-root/api/no-default.js +1 -0
- package/tests/test-server-root/b/1.txt +1 -0
- package/tests/test-server-root/custom/data.json +1 -0
- package/tests/test-server-root/docs/.config.json +6 -0
- package/tests/test-server-root/docs/api/data.json +1 -0
- package/tests/test-server-root/docs/src/components/Button.js +1 -0
- package/tests/test-server-root/docs/src/nested/file.js +1 -0
- package/tests/test-server-root/docs/src/utils/helpers/format.js +1 -0
- package/tests/test-server-root/hello.html +1 -0
- package/tests/test-server-root/index.html +1 -0
- package/tests/test-server-root/late.html +1 -0
- package/tests/test-server-root/public/src/file.txt +1 -0
- package/tests/test-server-root/src/components/Button.js +1 -0
- package/tests/test-server-root/src/deep/nested/folder/file.js +1 -0
- package/tests/test-server-root/src/file.js +1 -0
- package/tests/test-server-root/src/file.txt +1 -0
- package/tests/test-server-root/src/nested/file.js +1 -0
- package/tests/test-server-root/src/utils/helpers/format.js +1 -0
- package/tests/test-utils.js +50 -1
- package/config-examples/development.config.json +0 -24
- package/config-examples/low-memory.config.json +0 -23
- package/config-examples/no-cache.config.json +0 -13
- package/config-examples/production.config.json +0 -38
- package/example-cache.config.json +0 -45
- package/example.config.json +0 -50
- package/utils/cache-demo.js +0 -145
- package/utils/cache-monitor.js +0 -132
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"cache": {
|
|
3
|
-
"enabled": true,
|
|
4
|
-
"maxSize": 200,
|
|
5
|
-
"maxMemoryMB": 75,
|
|
6
|
-
"ttlMs": 600000,
|
|
7
|
-
"maxHeapUsagePercent": 75,
|
|
8
|
-
"memoryCheckInterval": 15000,
|
|
9
|
-
"watchFiles": true,
|
|
10
|
-
"enableMemoryMonitoring": true,
|
|
11
|
-
"production": {
|
|
12
|
-
"maxSize": 1000,
|
|
13
|
-
"maxMemoryMB": 200,
|
|
14
|
-
"ttlMs": 3600000,
|
|
15
|
-
"maxHeapUsagePercent": 85,
|
|
16
|
-
"memoryCheckInterval": 60000,
|
|
17
|
-
"watchFiles": false
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"middleware": {
|
|
21
|
-
"cors": {
|
|
22
|
-
"enabled": true,
|
|
23
|
-
"origin": "*",
|
|
24
|
-
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
25
|
-
"headers": ["Content-Type", "Authorization"]
|
|
26
|
-
},
|
|
27
|
-
"compression": {
|
|
28
|
-
"enabled": true,
|
|
29
|
-
"threshold": 1024
|
|
30
|
-
},
|
|
31
|
-
"security": {
|
|
32
|
-
"enabled": true,
|
|
33
|
-
"headers": {
|
|
34
|
-
"X-Content-Type-Options": "nosniff",
|
|
35
|
-
"X-Frame-Options": "DENY",
|
|
36
|
-
"X-XSS-Protection": "1; mode=block"
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
"logging": {
|
|
40
|
-
"enabled": true,
|
|
41
|
-
"includeUserAgent": false,
|
|
42
|
-
"includeResponseTime": true
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
package/example.config.json
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"allowedMimes": {
|
|
3
|
-
"html": "text/html",
|
|
4
|
-
"css": "text/css",
|
|
5
|
-
"js": "application/javascript",
|
|
6
|
-
"json": "application/json",
|
|
7
|
-
"png": "image/png",
|
|
8
|
-
"jpg": "image/jpeg"
|
|
9
|
-
},
|
|
10
|
-
"middleware": {
|
|
11
|
-
"cors": {
|
|
12
|
-
"enabled": true,
|
|
13
|
-
"origin": ["http://localhost:3000", "https://mydomain.com"],
|
|
14
|
-
"methods": ["GET", "POST", "PUT", "DELETE"],
|
|
15
|
-
"headers": ["Content-Type", "Authorization", "X-API-Key"]
|
|
16
|
-
},
|
|
17
|
-
"compression": {
|
|
18
|
-
"enabled": true,
|
|
19
|
-
"threshold": 512
|
|
20
|
-
},
|
|
21
|
-
"rateLimit": {
|
|
22
|
-
"enabled": true,
|
|
23
|
-
"maxRequests": 50,
|
|
24
|
-
"windowMs": 60000,
|
|
25
|
-
"message": "Rate limit exceeded. Please try again later."
|
|
26
|
-
},
|
|
27
|
-
"security": {
|
|
28
|
-
"enabled": true,
|
|
29
|
-
"headers": {
|
|
30
|
-
"X-Content-Type-Options": "nosniff",
|
|
31
|
-
"X-Frame-Options": "SAMEORIGIN",
|
|
32
|
-
"X-XSS-Protection": "1; mode=block",
|
|
33
|
-
"Strict-Transport-Security": "max-age=31536000; includeSubDomains"
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
"logging": {
|
|
37
|
-
"enabled": true,
|
|
38
|
-
"includeUserAgent": true,
|
|
39
|
-
"includeResponseTime": true
|
|
40
|
-
},
|
|
41
|
-
"custom": [
|
|
42
|
-
"./middleware/auth.js",
|
|
43
|
-
"./middleware/analytics.js"
|
|
44
|
-
]
|
|
45
|
-
},
|
|
46
|
-
"customRoutes": {
|
|
47
|
-
"api/*": "./api-handlers/*",
|
|
48
|
-
"/vendor/bootstrap.css": "./node_modules/bootstrap/dist/css/bootstrap.min.css"
|
|
49
|
-
}
|
|
50
|
-
}
|
package/utils/cache-demo.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
Cache Demo Script
|
|
5
|
-
Demonstrates the module cache functionality with real-time monitoring
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import path from 'path';
|
|
9
|
-
import { writeFile, unlink, mkdir, rm } from 'fs/promises';
|
|
10
|
-
import ModuleCache from '../src/moduleCache.js';
|
|
11
|
-
|
|
12
|
-
const createDemoFiles = async (demoDir) => {
|
|
13
|
-
await mkdir(demoDir, { recursive: true });
|
|
14
|
-
|
|
15
|
-
// Create some demo route files
|
|
16
|
-
await writeFile(path.join(demoDir, 'users.js'),
|
|
17
|
-
'export default async (req, res) => res.json({ users: ["alice", "bob"] });'
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
await writeFile(path.join(demoDir, 'posts.js'),
|
|
21
|
-
'export default async (req, res) => res.json({ posts: ["post1", "post2"] });'
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
await writeFile(path.join(demoDir, 'auth.js'),
|
|
25
|
-
'export default async (req, res) => res.json({ authenticated: true });'
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
return [
|
|
29
|
-
path.join(demoDir, 'users.js'),
|
|
30
|
-
path.join(demoDir, 'posts.js'),
|
|
31
|
-
path.join(demoDir, 'auth.js')
|
|
32
|
-
];
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const simulateFileAccess = async (cache, filePaths) => {
|
|
36
|
-
const { stat } = await import('fs/promises');
|
|
37
|
-
|
|
38
|
-
for(const filePath of filePaths) {
|
|
39
|
-
const stats = await stat(filePath);
|
|
40
|
-
|
|
41
|
-
// Try to get from cache first
|
|
42
|
-
let module = cache.get(filePath, stats);
|
|
43
|
-
|
|
44
|
-
if(!module) {
|
|
45
|
-
// Simulate loading module
|
|
46
|
-
const fileUrl = `file://${filePath}?t=${Date.now()}`;
|
|
47
|
-
module = await import(fileUrl);
|
|
48
|
-
cache.set(filePath, module, stats, stats.size / 1024);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
console.log(`๐ ${path.basename(filePath)}: ${module ? 'โ
cached' : 'โ loaded fresh'}`);
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const displayCacheStats = (cache) => {
|
|
56
|
-
console.log('\n๐ Cache Statistics:');
|
|
57
|
-
console.log('โ'.repeat(40));
|
|
58
|
-
|
|
59
|
-
const stats = cache.getStats();
|
|
60
|
-
console.log(`Entries: ${stats.cache.size}/${stats.cache.maxSize}`);
|
|
61
|
-
console.log(`Memory: ${stats.cache.memoryUsageMB}/${stats.cache.maxMemoryMB}MB`);
|
|
62
|
-
console.log(`Hit Rate: ${cache.getHitRate()}%`);
|
|
63
|
-
console.log(`Hits: ${stats.stats.hits}, Misses: ${stats.stats.misses}`);
|
|
64
|
-
console.log(`File Changes: ${stats.stats.fileChanges}`);
|
|
65
|
-
console.log(`Heap Usage: ${stats.memory.heapUsedMB}MB (${stats.memory.heapUsagePercent}%)`);
|
|
66
|
-
|
|
67
|
-
if(stats.cache.size > 0) {
|
|
68
|
-
console.log('\n๐ Cached Files:');
|
|
69
|
-
const cachedFiles = cache.getCachedFiles();
|
|
70
|
-
cachedFiles.forEach(file => {
|
|
71
|
-
const ageSeconds = Math.round(file.age / 1000);
|
|
72
|
-
console.log(` โข ${file.relativePath} (${file.sizeKB.toFixed(1)}KB, ${ageSeconds}s old)`);
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
console.log();
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const main = async () => {
|
|
79
|
-
console.log('๐ Kempo Server Module Cache Demo\n');
|
|
80
|
-
|
|
81
|
-
// Create demo directory and files
|
|
82
|
-
const demoDir = path.join(process.cwd(), 'demo-cache-test');
|
|
83
|
-
console.log('๐ Creating demo files...');
|
|
84
|
-
const filePaths = await createDemoFiles(demoDir);
|
|
85
|
-
|
|
86
|
-
// Initialize cache with small limits for demo
|
|
87
|
-
const cache = new ModuleCache({
|
|
88
|
-
maxSize: 2,
|
|
89
|
-
maxMemoryMB: 1,
|
|
90
|
-
ttlMs: 5000, // 5 seconds
|
|
91
|
-
watchFiles: true,
|
|
92
|
-
enableMemoryMonitoring: true
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
console.log('โ
Cache initialized\n');
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
// Round 1: Initial loads (cache misses)
|
|
99
|
-
console.log('๐ Round 1: Initial file access (expect cache misses)');
|
|
100
|
-
await simulateFileAccess(cache, filePaths);
|
|
101
|
-
displayCacheStats(cache);
|
|
102
|
-
|
|
103
|
-
// Round 2: Immediate re-access (cache hits)
|
|
104
|
-
console.log('๐ Round 2: Immediate re-access (expect cache hits)');
|
|
105
|
-
await simulateFileAccess(cache, [filePaths[0], filePaths[1]]);
|
|
106
|
-
displayCacheStats(cache);
|
|
107
|
-
|
|
108
|
-
// Round 3: Add third file (should evict oldest due to maxSize=2)
|
|
109
|
-
console.log('๐ Round 3: Access third file (expect LRU eviction)');
|
|
110
|
-
await simulateFileAccess(cache, [filePaths[2]]);
|
|
111
|
-
displayCacheStats(cache);
|
|
112
|
-
|
|
113
|
-
// Round 4: Wait for TTL expiration
|
|
114
|
-
console.log('โฑ๏ธ Waiting 6 seconds for TTL expiration...');
|
|
115
|
-
await new Promise(resolve => setTimeout(resolve, 6000));
|
|
116
|
-
|
|
117
|
-
console.log('๐ Round 4: Access after TTL expiration (expect cache misses)');
|
|
118
|
-
await simulateFileAccess(cache, [filePaths[1]]);
|
|
119
|
-
displayCacheStats(cache);
|
|
120
|
-
|
|
121
|
-
// Round 5: Demonstrate file watching
|
|
122
|
-
console.log('๐ Round 5: Modifying file to test file watching...');
|
|
123
|
-
await writeFile(filePaths[1],
|
|
124
|
-
'export default async (req, res) => res.json({ posts: ["updated post"] });'
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
// Wait for file watcher to detect change
|
|
128
|
-
await new Promise(resolve => setTimeout(resolve, 200));
|
|
129
|
-
|
|
130
|
-
console.log('๐ Accessing modified file (expect cache invalidation)');
|
|
131
|
-
await simulateFileAccess(cache, [filePaths[1]]);
|
|
132
|
-
displayCacheStats(cache);
|
|
133
|
-
|
|
134
|
-
} finally {
|
|
135
|
-
// Cleanup
|
|
136
|
-
console.log('๐งน Cleaning up...');
|
|
137
|
-
cache.destroy();
|
|
138
|
-
await rm(demoDir, { recursive: true });
|
|
139
|
-
console.log('โ
Demo completed successfully!');
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
if(import.meta.url === `file://${process.argv[1]}`) {
|
|
144
|
-
main().catch(console.error);
|
|
145
|
-
}
|
package/utils/cache-monitor.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
Cache Monitor Utility
|
|
5
|
-
Displays real-time cache statistics and memory usage
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { spawn } from 'child_process';
|
|
9
|
-
import { pathToFileURL } from 'url';
|
|
10
|
-
import path from 'path';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const formatBytes = (bytes) => {
|
|
14
|
-
const mb = bytes / 1024 / 1024;
|
|
15
|
-
return `${mb.toFixed(2)}MB`;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const formatPercent = (value) => `${value.toFixed(1)}%`;
|
|
19
|
-
|
|
20
|
-
const clearScreen = () => process.stdout.write('\x1b[2J\x1b[0f');
|
|
21
|
-
|
|
22
|
-
const displayStats = (stats) => {
|
|
23
|
-
clearScreen();
|
|
24
|
-
console.log('='.repeat(60));
|
|
25
|
-
console.log('๐ฆ KEMPO SERVER - MODULE CACHE MONITOR');
|
|
26
|
-
console.log('='.repeat(60));
|
|
27
|
-
console.log();
|
|
28
|
-
|
|
29
|
-
if (!stats) {
|
|
30
|
-
console.log('โ Cache not available or disabled');
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const { cache, stats: cacheStats, memory, config } = stats;
|
|
35
|
-
|
|
36
|
-
// Cache Status
|
|
37
|
-
console.log('๐ CACHE STATUS');
|
|
38
|
-
console.log('-'.repeat(30));
|
|
39
|
-
console.log(`Entries: ${cache.size}/${cache.maxSize} (${((cache.size/cache.maxSize)*100).toFixed(1)}%)`);
|
|
40
|
-
console.log(`Memory: ${cache.memoryUsageMB}/${cache.maxMemoryMB}MB (${((cache.memoryUsageMB/cache.maxMemoryMB)*100).toFixed(1)}%)`);
|
|
41
|
-
console.log(`Watchers: ${cache.watchersActive} active`);
|
|
42
|
-
console.log();
|
|
43
|
-
|
|
44
|
-
// Performance Stats
|
|
45
|
-
console.log('โก PERFORMANCE');
|
|
46
|
-
console.log('-'.repeat(30));
|
|
47
|
-
const total = cacheStats.hits + cacheStats.misses;
|
|
48
|
-
const hitRate = total === 0 ? 0 : (cacheStats.hits / total * 100);
|
|
49
|
-
console.log(`Hit Rate: ${hitRate.toFixed(1)}% (${cacheStats.hits} hits, ${cacheStats.misses} misses)`);
|
|
50
|
-
console.log(`Evictions: ${cacheStats.evictions}`);
|
|
51
|
-
console.log(`File Changes: ${cacheStats.fileChanges}`);
|
|
52
|
-
console.log();
|
|
53
|
-
|
|
54
|
-
// Memory Info
|
|
55
|
-
console.log('๐ง NODE.JS MEMORY');
|
|
56
|
-
console.log('-'.repeat(30));
|
|
57
|
-
console.log(`Heap Used: ${memory.heapUsedMB}MB (${memory.heapUsagePercent}%)`);
|
|
58
|
-
console.log(`Heap Total: ${memory.heapTotalMB}MB`);
|
|
59
|
-
console.log(`RSS: ${memory.rssMB}MB`);
|
|
60
|
-
console.log();
|
|
61
|
-
|
|
62
|
-
// Configuration
|
|
63
|
-
console.log('โ๏ธ CONFIGURATION');
|
|
64
|
-
console.log('-'.repeat(30));
|
|
65
|
-
console.log(`TTL: ${Math.round(config.ttlMs / 1000)}s`);
|
|
66
|
-
console.log(`Max Heap: ${config.maxHeapUsagePercent}%`);
|
|
67
|
-
console.log(`File Watch: ${config.watchFiles ? 'โ
' : 'โ'}`);
|
|
68
|
-
console.log();
|
|
69
|
-
|
|
70
|
-
console.log(`Last updated: ${new Date().toLocaleTimeString()}`);
|
|
71
|
-
console.log('Press Ctrl+C to exit');
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// Simulate getting stats from a running server
|
|
75
|
-
// In a real implementation, you'd expose an endpoint or use IPC
|
|
76
|
-
const getStatsFromServer = async () => {
|
|
77
|
-
// This is a placeholder - in practice you'd either:
|
|
78
|
-
// 1. Add an admin endpoint to your server that returns cache stats
|
|
79
|
-
// 2. Use process communication to get stats from the running server
|
|
80
|
-
// 3. Log stats to a file that this script reads
|
|
81
|
-
|
|
82
|
-
// For demonstration, return mock stats
|
|
83
|
-
return {
|
|
84
|
-
cache: {
|
|
85
|
-
size: Math.floor(Math.random() * 100),
|
|
86
|
-
maxSize: 100,
|
|
87
|
-
memoryUsageMB: Math.random() * 50,
|
|
88
|
-
maxMemoryMB: 50,
|
|
89
|
-
watchersActive: Math.floor(Math.random() * 50)
|
|
90
|
-
},
|
|
91
|
-
stats: {
|
|
92
|
-
hits: Math.floor(Math.random() * 1000),
|
|
93
|
-
misses: Math.floor(Math.random() * 200),
|
|
94
|
-
evictions: Math.floor(Math.random() * 50),
|
|
95
|
-
fileChanges: Math.floor(Math.random() * 10)
|
|
96
|
-
},
|
|
97
|
-
memory: {
|
|
98
|
-
heapUsedMB: 45 + Math.random() * 20,
|
|
99
|
-
heapTotalMB: 70 + Math.random() * 30,
|
|
100
|
-
heapUsagePercent: 60 + Math.random() * 20,
|
|
101
|
-
rssMB: 80 + Math.random() * 40
|
|
102
|
-
},
|
|
103
|
-
config: {
|
|
104
|
-
ttlMs: 300000,
|
|
105
|
-
maxHeapUsagePercent: 70,
|
|
106
|
-
watchFiles: true
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const main = async () => {
|
|
112
|
-
console.log('Starting cache monitor...');
|
|
113
|
-
|
|
114
|
-
setInterval(async () => {
|
|
115
|
-
try {
|
|
116
|
-
const stats = await getStatsFromServer();
|
|
117
|
-
displayStats(stats);
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.error('Error getting stats:', error.message);
|
|
120
|
-
}
|
|
121
|
-
}, 2000); // Update every 2 seconds
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// Handle Ctrl+C gracefully
|
|
125
|
-
process.on('SIGINT', () => {
|
|
126
|
-
console.log('\n\n๐ Cache monitor stopped');
|
|
127
|
-
process.exit(0);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
131
|
-
main();
|
|
132
|
-
}
|