kempo-server 1.0.0 → 1.2.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/README.md +21 -0
- package/defaultConfig.js +1 -0
- package/docs/index.html +15 -0
- package/index.js +0 -0
- package/package.json +6 -3
- package/router.js +75 -9
package/README.md
CHANGED
|
@@ -288,6 +288,7 @@ An array of regex patterns for paths that should not trigger a file system resca
|
|
|
288
288
|
|
|
289
289
|
An object mapping custom route paths to file paths. Useful for aliasing or serving files from outside the document root.
|
|
290
290
|
|
|
291
|
+
**Basic Routes:**
|
|
291
292
|
```json
|
|
292
293
|
{
|
|
293
294
|
"customRoutes": {
|
|
@@ -297,6 +298,26 @@ An object mapping custom route paths to file paths. Useful for aliasing or servi
|
|
|
297
298
|
}
|
|
298
299
|
```
|
|
299
300
|
|
|
301
|
+
**Wildcard Routes:**
|
|
302
|
+
Wildcard routes allow you to map entire directory structures using the `*` wildcard:
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"customRoutes": {
|
|
307
|
+
"kempo/*": "./node_modules/kempo/dust/*",
|
|
308
|
+
"assets/*": "./static-files/*",
|
|
309
|
+
"docs/*": "./documentation/*"
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
With wildcard routes:
|
|
315
|
+
- `kempo/styles.css` would serve `./node_modules/kempo/dust/styles.css`
|
|
316
|
+
- `assets/logo.png` would serve `./static-files/logo.png`
|
|
317
|
+
- `docs/readme.md` would serve `./documentation/readme.md`
|
|
318
|
+
|
|
319
|
+
The `*` wildcard matches any single path segment (anything between `/` characters). Multiple wildcards can be used in a single route pattern.
|
|
320
|
+
|
|
300
321
|
### maxRescanAttempts
|
|
301
322
|
|
|
302
323
|
The maximum number of times to attempt rescanning the file system when a file is not found. Defaults to 3.
|
package/defaultConfig.js
CHANGED
package/docs/index.html
CHANGED
|
@@ -152,8 +152,22 @@
|
|
|
152
152
|
|
|
153
153
|
<h4 id="customRoutes">customRoutes</h4>
|
|
154
154
|
<p>An object mapping custom route paths to file paths. Useful for aliasing or serving files from outside the document root.</p>
|
|
155
|
+
|
|
156
|
+
<p><strong>Basic Routes:</strong></p>
|
|
155
157
|
<pre><code class="hljs json">{<br /> <span class="hljs-attr">"customRoutes"</span>: {<br /> <span class="hljs-attr">"/vendor/bootstrap.css"</span>: <span class="hljs-string">"./node_modules/bootstrap/dist/css/bootstrap.min.css"</span>,<br /> <span class="hljs-attr">"/api/status"</span>: <span class="hljs-string">"./status.js"</span><br /> }<br />}</code></pre>
|
|
156
158
|
|
|
159
|
+
<p><strong>Wildcard Routes:</strong></p>
|
|
160
|
+
<p>Wildcard routes allow you to map entire directory structures using the <code>*</code> wildcard:</p>
|
|
161
|
+
<pre><code class="hljs json">{<br /> <span class="hljs-attr">"customRoutes"</span>: {<br /> <span class="hljs-attr">"kempo/*"</span>: <span class="hljs-string">"./node_modules/kempo/dust/*"</span>,<br /> <span class="hljs-attr">"assets/*"</span>: <span class="hljs-string">"./static-files/*"</span>,<br /> <span class="hljs-attr">"docs/*"</span>: <span class="hljs-string">"./documentation/*"</span><br /> }<br />}</code></pre>
|
|
162
|
+
|
|
163
|
+
<p>With wildcard routes:</p>
|
|
164
|
+
<ul>
|
|
165
|
+
<li><code>kempo/styles.css</code> would serve <code>./node_modules/kempo/dust/styles.css</code></li>
|
|
166
|
+
<li><code>assets/logo.png</code> would serve <code>./static-files/logo.png</code></li>
|
|
167
|
+
<li><code>docs/readme.md</code> would serve <code>./documentation/readme.md</code></li>
|
|
168
|
+
</ul>
|
|
169
|
+
<p>The <code>*</code> wildcard matches any single path segment (anything between <code>/</code> characters). Multiple wildcards can be used in a single route pattern.</p>
|
|
170
|
+
|
|
157
171
|
<h4 id="maxRescanAttempts">maxRescanAttempts</h4>
|
|
158
172
|
<p>The maximum number of times to attempt rescanning the file system when a file is not found. Defaults to 3.</p>
|
|
159
173
|
<pre><code class="hljs json">{<br /> <span class="hljs-attr">"maxRescanAttempts"</span>: <span class="hljs-number">3</span><br />}</code></pre>
|
|
@@ -163,6 +177,7 @@
|
|
|
163
177
|
<li><strong>Zero Dependencies</strong> - No external dependencies required</li>
|
|
164
178
|
<li><strong>File-based Routing</strong> - Routes are defined by your directory structure</li>
|
|
165
179
|
<li><strong>Dynamic Routes</strong> - Support for parameterized routes with square bracket syntax</li>
|
|
180
|
+
<li><strong>Wildcard Routes</strong> - Map entire directory structures with wildcard patterns</li>
|
|
166
181
|
<li><strong>Multiple HTTP Methods</strong> - Support for GET, POST, PUT, DELETE, and more</li>
|
|
167
182
|
<li><strong>Static File Serving</strong> - Automatically serves static files with proper MIME types</li>
|
|
168
183
|
<li><strong>HTML Routes</strong> - Support for both JavaScript and HTML route handlers</li>
|
package/index.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kempo-server",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "",
|
|
4
|
+
"version": "1.2.0",
|
|
5
|
+
"description": "A lightweight, zero-dependency, file based routing server.",
|
|
6
6
|
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"kempo-server": "./index.js"
|
|
9
|
+
},
|
|
7
10
|
"scripts": {
|
|
8
|
-
"start": "node index.js -r docs"
|
|
11
|
+
"start": "node index.js -r ./docs"
|
|
9
12
|
},
|
|
10
13
|
"author": "",
|
|
11
14
|
"license": "ISC",
|
package/router.js
CHANGED
|
@@ -41,26 +41,69 @@ export default async (flags, log) => {
|
|
|
41
41
|
|
|
42
42
|
// Process custom routes - resolve paths and validate files exist
|
|
43
43
|
const customRoutes = new Map();
|
|
44
|
+
const wildcardRoutes = new Map();
|
|
45
|
+
|
|
44
46
|
if (config.customRoutes && Object.keys(config.customRoutes).length > 0) {
|
|
45
47
|
log(`Processing ${Object.keys(config.customRoutes).length} custom routes`, 2);
|
|
46
48
|
|
|
47
49
|
for (const [urlPath, filePath] of Object.entries(config.customRoutes)) {
|
|
48
50
|
try {
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
// Check if this is a wildcard route
|
|
52
|
+
if (urlPath.includes('*')) {
|
|
53
|
+
// Store wildcard routes separately for pattern matching
|
|
54
|
+
wildcardRoutes.set(urlPath, filePath);
|
|
55
|
+
log(`Wildcard route mapped: ${urlPath} -> ${filePath}`, 2);
|
|
56
|
+
} else {
|
|
57
|
+
// Resolve the file path relative to the current working directory
|
|
58
|
+
const resolvedPath = path.resolve(filePath);
|
|
59
|
+
|
|
60
|
+
// Check if the file exists (we'll do this async)
|
|
61
|
+
const { stat } = await import('fs/promises');
|
|
62
|
+
await stat(resolvedPath);
|
|
63
|
+
|
|
64
|
+
customRoutes.set(urlPath, resolvedPath);
|
|
65
|
+
log(`Custom route mapped: ${urlPath} -> ${resolvedPath}`, 2);
|
|
66
|
+
}
|
|
58
67
|
} catch (error) {
|
|
59
68
|
log(`Custom route error for ${urlPath} -> ${filePath}: ${error.message}`, 1);
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
}
|
|
63
72
|
|
|
73
|
+
// Helper function to match wildcard patterns
|
|
74
|
+
const matchWildcardRoute = (requestPath, pattern) => {
|
|
75
|
+
// Convert wildcard pattern to regex
|
|
76
|
+
const regexPattern = pattern
|
|
77
|
+
.replace(/\*/g, '([^/]+)') // Replace * with capture group for single segment
|
|
78
|
+
.replace(/\*\*/g, '(.+)'); // Replace ** with capture group for multiple segments
|
|
79
|
+
|
|
80
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
81
|
+
return regex.exec(requestPath);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Helper function to resolve wildcard file paths
|
|
85
|
+
const resolveWildcardPath = (filePath, matches) => {
|
|
86
|
+
let resolvedPath = filePath;
|
|
87
|
+
|
|
88
|
+
// Replace wildcards with captured values
|
|
89
|
+
for (let i = 1; i < matches.length; i++) {
|
|
90
|
+
resolvedPath = resolvedPath.replace('*', matches[i]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return path.resolve(resolvedPath);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Helper function to find matching wildcard route
|
|
97
|
+
const findWildcardRoute = (requestPath) => {
|
|
98
|
+
for (const [pattern, filePath] of wildcardRoutes) {
|
|
99
|
+
const matches = matchWildcardRoute(requestPath, pattern);
|
|
100
|
+
if (matches) {
|
|
101
|
+
return { filePath, matches };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
};
|
|
106
|
+
|
|
64
107
|
// Track 404 attempts to avoid unnecessary rescans
|
|
65
108
|
const rescanAttempts = new Map(); // path -> attempt count
|
|
66
109
|
const dynamicNoRescanPaths = new Set(); // paths that have exceeded max attempts
|
|
@@ -128,6 +171,29 @@ export default async (flags, log) => {
|
|
|
128
171
|
}
|
|
129
172
|
}
|
|
130
173
|
|
|
174
|
+
// Check wildcard routes
|
|
175
|
+
const wildcardMatch = findWildcardRoute(requestPath);
|
|
176
|
+
if (wildcardMatch) {
|
|
177
|
+
const resolvedFilePath = resolveWildcardPath(wildcardMatch.filePath, wildcardMatch.matches);
|
|
178
|
+
log(`Serving wildcard route: ${requestPath} -> ${resolvedFilePath}`, 2);
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const fileContent = await readFile(resolvedFilePath);
|
|
182
|
+
const fileExtension = path.extname(resolvedFilePath).toLowerCase().slice(1);
|
|
183
|
+
const mimeType = config.allowedMimes[fileExtension] || 'application/octet-stream';
|
|
184
|
+
|
|
185
|
+
log(`Serving wildcard file as ${mimeType} (${fileContent.length} bytes)`, 2);
|
|
186
|
+
res.writeHead(200, { 'Content-Type': mimeType });
|
|
187
|
+
res.end(fileContent);
|
|
188
|
+
return; // Successfully served wildcard route
|
|
189
|
+
} catch (error) {
|
|
190
|
+
log(`Error serving wildcard route ${requestPath}: ${error.message}`, 0);
|
|
191
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
192
|
+
res.end('Internal Server Error');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
131
197
|
// Try to serve the file normally
|
|
132
198
|
const served = await serveFile(files, rootPath, requestPath, req.method, config, req, res, log);
|
|
133
199
|
|