kempo-server 1.7.3 → 1.7.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.
- package/CONFIG.md +501 -0
- package/README.md +13 -442
- package/UTILS.md +127 -0
- package/dist/defaultConfig.js +1 -1
- package/dist/moduleCache.js +1 -0
- package/dist/router.js +1 -1
- package/dist/serveFile.js +1 -1
- 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/router-wildcard-double-asterisk.node-test.js +66 -0
- 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
package/CONFIG.md
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
To configure the server, create a configuration file (by default `.config.json`) within the root directory of your server (`public` in the start example). You can specify a different configuration file using the `--config` flag.
|
|
4
|
+
|
|
5
|
+
**Important:**
|
|
6
|
+
- When using a relative path for the `--config` flag, the config file must be located within the server root directory
|
|
7
|
+
- When using an absolute path for the `--config` flag, the config file can be located anywhere on the filesystem
|
|
8
|
+
- The server will throw an error if you attempt to use a relative config file path that points outside the root directory
|
|
9
|
+
|
|
10
|
+
## Command Line Configuration
|
|
11
|
+
|
|
12
|
+
You can specify different configuration files for different environments using the `--config` flag:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Development
|
|
16
|
+
kempo-server --root public --config dev.config.json
|
|
17
|
+
|
|
18
|
+
# Staging
|
|
19
|
+
kempo-server --root public --config staging.config.json
|
|
20
|
+
|
|
21
|
+
# Production with absolute path
|
|
22
|
+
kempo-server --root public --config /etc/kempo/production.config.json
|
|
23
|
+
|
|
24
|
+
# Mix with other options
|
|
25
|
+
kempo-server --root dist --port 8080 --config production.config.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Configuration File Structure
|
|
29
|
+
|
|
30
|
+
This json file can have any of the following properties, any property not defined will use the "Default Config".
|
|
31
|
+
|
|
32
|
+
- [allowedMimes](#allowedmimes)
|
|
33
|
+
- [disallowedRegex](#disallowedregex)
|
|
34
|
+
- [customRoutes](#customroutes)
|
|
35
|
+
- [routeFiles](#routefiles)
|
|
36
|
+
- [noRescanPaths](#norescanpaths)
|
|
37
|
+
- [maxRescanAttempts](#maxrescanattempts)
|
|
38
|
+
- [cache](#cache)
|
|
39
|
+
- [middleware](#middleware)
|
|
40
|
+
|
|
41
|
+
## Cache
|
|
42
|
+
|
|
43
|
+
Kempo Server includes an intelligent module caching system that dramatically improves performance by caching JavaScript route modules in memory. The cache combines multiple strategies:
|
|
44
|
+
|
|
45
|
+
- **LRU (Least Recently Used)** - Evicts oldest modules when cache fills
|
|
46
|
+
- **Time-based expiration** - Modules expire after configurable TTL
|
|
47
|
+
- **Memory monitoring** - Automatically clears cache if memory usage gets too high
|
|
48
|
+
- **File watching** - Instantly invalidates cache when files change (development)
|
|
49
|
+
|
|
50
|
+
### Basic Cache Configuration
|
|
51
|
+
|
|
52
|
+
Enable caching in your `.config.json`:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"cache": {
|
|
57
|
+
"enabled": true,
|
|
58
|
+
"maxSize": 100,
|
|
59
|
+
"maxMemoryMB": 50,
|
|
60
|
+
"ttlMs": 300000,
|
|
61
|
+
"watchFiles": true
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Cache Options
|
|
67
|
+
|
|
68
|
+
- `enabled` (boolean) - Enable/disable caching (default: `true`)
|
|
69
|
+
- `maxSize` (number) - Maximum cached modules (default: `100`)
|
|
70
|
+
- `maxMemoryMB` (number) - Memory limit in MB (default: `50`)
|
|
71
|
+
- `ttlMs` (number) - Cache lifetime in milliseconds (default: `300000` - 5 minutes)
|
|
72
|
+
- `maxHeapUsagePercent` (number) - Clear cache when heap exceeds % (default: `70`)
|
|
73
|
+
- `memoryCheckInterval` (number) - Memory check frequency in ms (default: `30000`)
|
|
74
|
+
- `watchFiles` (boolean) - Auto-invalidate on file changes (default: `true`)
|
|
75
|
+
- `enableMemoryMonitoring` (boolean) - Enable memory monitoring (default: `true`)
|
|
76
|
+
|
|
77
|
+
Run with specific config: `kempo-server --config prod.config.json`
|
|
78
|
+
|
|
79
|
+
### Cache Monitoring
|
|
80
|
+
|
|
81
|
+
Monitor cache performance at runtime:
|
|
82
|
+
|
|
83
|
+
- **View stats:** `GET /_admin/cache` - Returns detailed cache statistics
|
|
84
|
+
- **Clear cache:** `DELETE /_admin/cache` - Clears entire cache
|
|
85
|
+
|
|
86
|
+
Example response:
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"cache": {
|
|
90
|
+
"size": 45,
|
|
91
|
+
"maxSize": 100,
|
|
92
|
+
"memoryUsageMB": 12.5,
|
|
93
|
+
"hitRate": 87
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Middleware
|
|
99
|
+
|
|
100
|
+
Kempo Server includes a powerful middleware system that allows you to add functionality like authentication, logging, CORS, compression, and more. Middleware runs before your route handlers and can modify requests, responses, or handle requests entirely.
|
|
101
|
+
|
|
102
|
+
### Built-in Middleware
|
|
103
|
+
|
|
104
|
+
#### CORS
|
|
105
|
+
Enable Cross-Origin Resource Sharing for your API:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"middleware": {
|
|
110
|
+
"cors": {
|
|
111
|
+
"enabled": true,
|
|
112
|
+
"origin": "*",
|
|
113
|
+
"methods": ["GET", "POST", "PUT", "DELETE"],
|
|
114
|
+
"headers": ["Content-Type", "Authorization"]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### Compression
|
|
121
|
+
Automatically compress responses with gzip:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"middleware": {
|
|
126
|
+
"compression": {
|
|
127
|
+
"enabled": true,
|
|
128
|
+
"threshold": 1024
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Rate Limiting
|
|
135
|
+
Limit requests per client to prevent abuse:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"middleware": {
|
|
140
|
+
"rateLimit": {
|
|
141
|
+
"enabled": true,
|
|
142
|
+
"maxRequests": 100,
|
|
143
|
+
"windowMs": 60000,
|
|
144
|
+
"message": "Too many requests"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### Security Headers
|
|
151
|
+
Add security headers to all responses:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"middleware": {
|
|
156
|
+
"security": {
|
|
157
|
+
"enabled": true,
|
|
158
|
+
"headers": {
|
|
159
|
+
"X-Content-Type-Options": "nosniff",
|
|
160
|
+
"X-Frame-Options": "DENY",
|
|
161
|
+
"X-XSS-Protection": "1; mode=block"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Request Logging
|
|
169
|
+
Log requests with configurable detail:
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"middleware": {
|
|
174
|
+
"logging": {
|
|
175
|
+
"enabled": true,
|
|
176
|
+
"includeUserAgent": true,
|
|
177
|
+
"includeResponseTime": true
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Custom Middleware
|
|
184
|
+
|
|
185
|
+
Create your own middleware by writing JavaScript files and referencing them in your config:
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"middleware": {
|
|
190
|
+
"custom": ["./middleware/auth.js", "./middleware/analytics.js"]
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### Custom Middleware Example
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
// middleware/auth.js
|
|
199
|
+
export default (config) => {
|
|
200
|
+
return async (req, res, next) => {
|
|
201
|
+
const token = req.headers.authorization;
|
|
202
|
+
|
|
203
|
+
if (!token) {
|
|
204
|
+
req.user = null;
|
|
205
|
+
return await next();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
// Verify JWT token (example)
|
|
210
|
+
const user = verifyToken(token);
|
|
211
|
+
req.user = user;
|
|
212
|
+
req.permissions = await getUserPermissions(user.id);
|
|
213
|
+
|
|
214
|
+
// Add helper methods
|
|
215
|
+
req.hasPermission = (permission) => req.permissions.includes(permission);
|
|
216
|
+
|
|
217
|
+
} catch (error) {
|
|
218
|
+
req.user = null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
await next();
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Using Enhanced Requests in Routes
|
|
227
|
+
|
|
228
|
+
Your route files can now access the enhanced request object:
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
// api/user/profile/GET.js
|
|
232
|
+
export default async (req, res, params) => {
|
|
233
|
+
if (!req.user) {
|
|
234
|
+
return res.status(401).json({ error: 'Authentication required' });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!req.hasPermission('user:read')) {
|
|
238
|
+
return res.status(403).json({ error: 'Insufficient permissions' });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const profile = await getUserProfile(req.user.id);
|
|
242
|
+
res.json(profile);
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Middleware Order
|
|
247
|
+
|
|
248
|
+
Middleware executes in this order:
|
|
249
|
+
1. Built-in middleware (cors, compression, rateLimit, security, logging)
|
|
250
|
+
2. Custom middleware (in the order listed in config)
|
|
251
|
+
3. Your route handlers
|
|
252
|
+
|
|
253
|
+
### Route Interception
|
|
254
|
+
|
|
255
|
+
Middleware can intercept and handle routes completely, useful for authentication endpoints:
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
// middleware/auth-routes.js
|
|
259
|
+
export default (config) => {
|
|
260
|
+
return async (req, res, next) => {
|
|
261
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
262
|
+
|
|
263
|
+
// Handle login endpoint
|
|
264
|
+
if (req.method === 'POST' && url.pathname === '/auth/login') {
|
|
265
|
+
const credentials = await req.json();
|
|
266
|
+
const token = await authenticateUser(credentials);
|
|
267
|
+
|
|
268
|
+
if (token) {
|
|
269
|
+
return res.json({ token, success: true });
|
|
270
|
+
} else {
|
|
271
|
+
return res.status(401).json({ error: 'Invalid credentials' });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
await next();
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Configuration Options
|
|
281
|
+
|
|
282
|
+
### allowedMimes
|
|
283
|
+
|
|
284
|
+
An object mapping file extensions to their MIME types. Files with extensions not in this list will not be served.
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"allowedMimes": {
|
|
289
|
+
"html": "text/html",
|
|
290
|
+
"css": "text/css",
|
|
291
|
+
"js": "application/javascript",
|
|
292
|
+
"json": "application/json",
|
|
293
|
+
"png": "image/png",
|
|
294
|
+
"jpg": "image/jpeg"
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### disallowedRegex
|
|
300
|
+
|
|
301
|
+
An array of regular expressions that match paths that should never be served. This provides security by preventing access to sensitive files.
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{
|
|
305
|
+
"disallowedRegex": [
|
|
306
|
+
"^/\\..*",
|
|
307
|
+
"\\.env$",
|
|
308
|
+
"\\.config$",
|
|
309
|
+
"password"
|
|
310
|
+
]
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### routeFiles
|
|
315
|
+
|
|
316
|
+
An array of filenames that should be treated as route handlers and executed as JavaScript modules.
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"routeFiles": [
|
|
321
|
+
"GET.js",
|
|
322
|
+
"POST.js",
|
|
323
|
+
"PUT.js",
|
|
324
|
+
"DELETE.js",
|
|
325
|
+
"index.js"
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### noRescanPaths
|
|
331
|
+
|
|
332
|
+
An array of regex patterns for paths that should not trigger a file system rescan. This improves performance for common static assets.
|
|
333
|
+
|
|
334
|
+
```json
|
|
335
|
+
{
|
|
336
|
+
"noRescanPaths": [
|
|
337
|
+
"/favicon\\.ico$",
|
|
338
|
+
"/robots\\.txt$",
|
|
339
|
+
"\\.map$"
|
|
340
|
+
]
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### customRoutes
|
|
345
|
+
|
|
346
|
+
An object mapping custom route paths to file paths. Useful for aliasing or serving files from outside the document root.
|
|
347
|
+
|
|
348
|
+
**Note:** All file paths in customRoutes are resolved relative to the server root directory (the `--root` path). This allows you to reference files both inside and outside the document root.
|
|
349
|
+
|
|
350
|
+
**Basic Routes:**
|
|
351
|
+
```json
|
|
352
|
+
{
|
|
353
|
+
"customRoutes": {
|
|
354
|
+
"/vendor/bootstrap.css": "../node_modules/bootstrap/dist/css/bootstrap.min.css",
|
|
355
|
+
"/api/status": "./status.js"
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
**Wildcard Routes:**
|
|
361
|
+
Wildcard routes allow you to map entire directory structures using the `*` and `**` wildcards:
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"customRoutes": {
|
|
366
|
+
"kempo/*": "../node_modules/kempo/dist/*",
|
|
367
|
+
"assets/*": "../static-files/*",
|
|
368
|
+
"docs/*": "../documentation/*",
|
|
369
|
+
"src/**": "../src/**"
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
With wildcard routes:
|
|
375
|
+
- `kempo/styles.css` would serve `../node_modules/kempo/dist/styles.css`
|
|
376
|
+
- `assets/logo.png` would serve `../static-files/logo.png`
|
|
377
|
+
- `docs/readme.md` would serve `../documentation/readme.md`
|
|
378
|
+
- `src/components/Button.js` would serve `../src/components/Button.js`
|
|
379
|
+
|
|
380
|
+
The `*` wildcard matches any single path segment (anything between `/` characters).
|
|
381
|
+
The `**` wildcard matches any number of path segments, allowing you to map entire directory trees.
|
|
382
|
+
Multiple wildcards can be used in a single route pattern.
|
|
383
|
+
|
|
384
|
+
### maxRescanAttempts
|
|
385
|
+
|
|
386
|
+
The maximum number of times to attempt rescanning the file system when a file is not found. Defaults to 3.
|
|
387
|
+
|
|
388
|
+
```json
|
|
389
|
+
{
|
|
390
|
+
"maxRescanAttempts": 3
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Configuration Examples
|
|
395
|
+
|
|
396
|
+
### Development Environment
|
|
397
|
+
|
|
398
|
+
**Focus: Fast iteration and debugging**
|
|
399
|
+
```json
|
|
400
|
+
{
|
|
401
|
+
"cache": {
|
|
402
|
+
"enabled": true,
|
|
403
|
+
"maxSize": 50,
|
|
404
|
+
"ttlMs": 300000,
|
|
405
|
+
"watchFiles": true
|
|
406
|
+
},
|
|
407
|
+
"middleware": {
|
|
408
|
+
"cors": {
|
|
409
|
+
"enabled": true,
|
|
410
|
+
"origin": "*"
|
|
411
|
+
},
|
|
412
|
+
"logging": {
|
|
413
|
+
"enabled": true,
|
|
414
|
+
"includeUserAgent": true,
|
|
415
|
+
"includeResponseTime": true
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Production Environment
|
|
422
|
+
|
|
423
|
+
**Focus: Performance, security, and stability**
|
|
424
|
+
```json
|
|
425
|
+
{
|
|
426
|
+
"cache": {
|
|
427
|
+
"enabled": true,
|
|
428
|
+
"maxSize": 1000,
|
|
429
|
+
"maxMemoryMB": 200,
|
|
430
|
+
"ttlMs": 3600000,
|
|
431
|
+
"maxHeapUsagePercent": 85,
|
|
432
|
+
"memoryCheckInterval": 60000,
|
|
433
|
+
"watchFiles": false
|
|
434
|
+
},
|
|
435
|
+
"middleware": {
|
|
436
|
+
"cors": {
|
|
437
|
+
"enabled": true,
|
|
438
|
+
"origin": ["https://myapp.com", "https://www.myapp.com"],
|
|
439
|
+
"credentials": true
|
|
440
|
+
},
|
|
441
|
+
"compression": {
|
|
442
|
+
"enabled": true,
|
|
443
|
+
"threshold": 1024
|
|
444
|
+
},
|
|
445
|
+
"security": {
|
|
446
|
+
"enabled": true,
|
|
447
|
+
"headers": {
|
|
448
|
+
"X-Content-Type-Options": "nosniff",
|
|
449
|
+
"X-Frame-Options": "DENY",
|
|
450
|
+
"X-XSS-Protection": "1; mode=block",
|
|
451
|
+
"Strict-Transport-Security": "max-age=31536000; includeSubDomains"
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
"logging": {
|
|
455
|
+
"enabled": true,
|
|
456
|
+
"includeUserAgent": false,
|
|
457
|
+
"includeResponseTime": true
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Low-Memory Environment
|
|
464
|
+
|
|
465
|
+
**Focus: Minimal resource usage**
|
|
466
|
+
```json
|
|
467
|
+
{
|
|
468
|
+
"cache": {
|
|
469
|
+
"enabled": true,
|
|
470
|
+
"maxSize": 20,
|
|
471
|
+
"maxMemoryMB": 5,
|
|
472
|
+
"ttlMs": 120000,
|
|
473
|
+
"maxHeapUsagePercent": 60,
|
|
474
|
+
"memoryCheckInterval": 10000
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Debugging Environment
|
|
480
|
+
|
|
481
|
+
**Focus: Cache disabled for troubleshooting**
|
|
482
|
+
```json
|
|
483
|
+
{
|
|
484
|
+
"cache": {
|
|
485
|
+
"enabled": false
|
|
486
|
+
},
|
|
487
|
+
"middleware": {
|
|
488
|
+
"logging": {
|
|
489
|
+
"enabled": true,
|
|
490
|
+
"includeUserAgent": true,
|
|
491
|
+
"includeResponseTime": true
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## Additional Resources
|
|
498
|
+
|
|
499
|
+
- **HTML Documentation**: See `docs/configuration.html` for detailed web-based documentation
|
|
500
|
+
- **Caching Guide**: See `docs/caching.html` for comprehensive caching documentation
|
|
501
|
+
- **Middleware Guide**: See `docs/middleware.html` for detailed middleware documentation
|