koa-classic-server 2.3.0 → 2.5.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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/koa-classic-server.svg)](https://www.npmjs.com/package/koa-classic-server)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![Tests](https://img.shields.io/badge/tests-197%20passing-brightgreen.svg)]()
7
+ [![Tests](https://img.shields.io/badge/tests-309%20passing-brightgreen.svg)]()
8
8
 
9
9
  ---
10
10
 
@@ -26,7 +26,9 @@ The 2.X series brings major performance improvements, enhanced security, and pow
26
26
  ✅ **Enhanced Index Option** - Array format with RegExp support
27
27
  ✅ **Template Engine Support** - EJS, Pug, Handlebars, Nunjucks, and more
28
28
  ✅ **Enterprise Security** - Path traversal, XSS, race condition protection
29
- ✅ **Comprehensive Testing** - 197 tests passing with extensive coverage
29
+ ✅ **Clean URLs** - Hide file extensions with `hideExtension` (mod_rewrite-like behavior)
30
+ ✅ **Symlink Support** - Full symbolic link support (NixOS, Docker, npm link, Capistrano)
31
+ ✅ **Comprehensive Testing** - 309 tests passing with extensive coverage
30
32
  ✅ **Complete Documentation** - Detailed guides and examples
31
33
 
32
34
  [See full changelog →](./docs/CHANGELOG.md)
@@ -48,7 +50,9 @@ The 2.X series brings major performance improvements, enhanced security, and pow
48
50
  - 🔒 **Enterprise Security** - Path traversal, XSS, race condition protection
49
51
  - ⚙️ **Highly Configurable** - URL prefixes, reserved paths, index files
50
52
  - 🚀 **High Performance** - Async/await, non-blocking I/O, optimized algorithms
51
- - 🧪 **Well-Tested** - 153 passing tests with comprehensive coverage
53
+ - 🔗 **Symlink Support** - Transparent symlink resolution with directory listing indicators
54
+ - 🌐 **Clean URLs** - Hide file extensions for SEO-friendly URLs via `hideExtension`
55
+ - 🧪 **Well-Tested** - 309 passing tests with comprehensive coverage
52
56
  - 📦 **Dual Module Support** - CommonJS and ES Modules
53
57
 
54
58
  ---
@@ -216,7 +220,51 @@ app.use(koaClassicServer(__dirname + '/views', {
216
220
 
217
221
  **See complete guide:** [Template Engine Documentation →](./docs/template-engine/TEMPLATE_ENGINE_GUIDE.md)
218
222
 
219
- ### 6. With HTTP Caching
223
+ ### 6. Clean URLs with hideExtension
224
+
225
+ Hide file extensions from URLs, similar to Apache's `mod_rewrite`:
226
+
227
+ ```javascript
228
+ const ejs = require('ejs');
229
+
230
+ app.use(koaClassicServer(__dirname + '/public', {
231
+ showDirContents: true,
232
+ index: ['index.ejs'],
233
+ hideExtension: {
234
+ ext: '.ejs', // Extension to hide (required)
235
+ redirect: 301 // HTTP redirect code (optional, default: 301)
236
+ },
237
+ template: {
238
+ ext: ['ejs'],
239
+ render: async (ctx, next, filePath) => {
240
+ ctx.body = await ejs.renderFile(filePath, data);
241
+ ctx.type = 'text/html';
242
+ }
243
+ }
244
+ }));
245
+ ```
246
+
247
+ **URL Behavior:**
248
+
249
+ | Request URL | Action | Result |
250
+ |-------------|--------|--------|
251
+ | `/about` | Resolves `about.ejs` | Serves file (200) |
252
+ | `/blog/article` | Resolves `blog/article.ejs` | Serves file (200) |
253
+ | `/about.ejs` | Redirect | 301 → `/about` |
254
+ | `/about.ejs?lang=it` | Redirect | 301 → `/about?lang=it` |
255
+ | `/index.ejs` | Redirect | 301 → `/` |
256
+ | `/section/index.ejs` | Redirect | 301 → `/section/` |
257
+ | `/style.css` | No interference | Normal flow |
258
+ | `/about/` | No interference | Shows directory listing |
259
+
260
+ **Conflict Resolution:**
261
+
262
+ - **Directory vs file**: When both `about/` directory and `about.ejs` file exist, `/about` serves the file. Use `/about/` to access the directory.
263
+ - **Extensionless vs extension**: When both `about` (no ext) and `about.ejs` exist, `/about` always serves `about.ejs`. The extensionless file becomes unreachable.
264
+
265
+ > **Note**: This conflict resolution behavior differs from Apache/Nginx, where directories typically take priority over files with the same base name.
266
+
267
+ ### 7. With HTTP Caching
220
268
 
221
269
  Enable aggressive caching for static files:
222
270
 
@@ -238,7 +286,7 @@ The default value for `browserCacheEnabled` is `false` to facilitate development
238
286
 
239
287
  **See details:** [HTTP Caching Optimization →](./docs/OPTIMIZATION_HTTP_CACHING.md)
240
288
 
241
- ### 7. Multiple Index Files with Priority
289
+ ### 8. Multiple Index Files with Priority
242
290
 
243
291
  Search for multiple index files with custom order:
244
292
 
@@ -255,7 +303,7 @@ app.use(koaClassicServer(__dirname + '/public', {
255
303
 
256
304
  **See details:** [Index Option Priority →](./docs/INDEX_OPTION_PRIORITY.md)
257
305
 
258
- ### 8. Complete Production Example
306
+ ### 9. Complete Production Example
259
307
 
260
308
  Real-world configuration for production:
261
309
 
@@ -368,6 +416,12 @@ Creates a Koa middleware for serving static files.
368
416
  useOriginalUrl: true, // Use ctx.originalUrl (default) or ctx.url
369
417
  // Set false for URL rewriting middleware (i18n, routing)
370
418
 
419
+ // Clean URLs - hide file extension from URLs (mod_rewrite-like)
420
+ hideExtension: {
421
+ ext: '.ejs', // Extension to hide (required, must start with '.')
422
+ redirect: 301 // HTTP redirect code (optional, default: 301)
423
+ },
424
+
371
425
  // DEPRECATED (use new names above):
372
426
  // enableCaching: use browserCacheEnabled instead
373
427
  // cacheMaxAge: use browserCacheMaxAge instead
@@ -388,6 +442,8 @@ Creates a Koa middleware for serving static files.
388
442
  | `browserCacheEnabled` | Boolean | `false` | Enable browser HTTP caching headers (recommended: `true` in production) |
389
443
  | `browserCacheMaxAge` | Number | `3600` | Browser cache duration in seconds |
390
444
  | `useOriginalUrl` | Boolean | `true` | Use `ctx.originalUrl` (default) or `ctx.url` for URL resolution |
445
+ | `hideExtension.ext` | String | - | Extension to hide (e.g. `'.ejs'`). Enables clean URL feature |
446
+ | `hideExtension.redirect` | Number | `301` | HTTP redirect code for URLs with extension |
391
447
  | ~~`enableCaching`~~ | Boolean | `false` | **DEPRECATED**: Use `browserCacheEnabled` instead |
392
448
  | ~~`cacheMaxAge`~~ | Number | `3600` | **DEPRECATED**: Use `browserCacheMaxAge` instead |
393
449
 
@@ -462,6 +518,33 @@ Human-readable format:
462
518
  - **Click file name** - Download/view file
463
519
  - **Parent Directory** - Go up one level
464
520
 
521
+ ### Symlink Support
522
+
523
+ The middleware fully supports symbolic links, which is essential for environments where served files are symlinks rather than regular files:
524
+
525
+ - **NixOS buildFHSEnv** - Files in www/ appear as symlinks to the Nix store
526
+ - **Docker bind mounts** - Mounted files may appear as symlinks
527
+ - **npm link** - Linked packages are symlinks
528
+ - **Capistrano-style deploys** - The `current` directory is a symlink to the active release
529
+
530
+ **How it works:**
531
+
532
+ Symlinks are followed transparently via `fs.promises.stat()`, but only when `dirent.isSymbolicLink()` is true. Regular files incur zero additional overhead.
533
+
534
+ **Directory listing indicators:**
535
+
536
+ | Entry type | Indicator | Clickable | Type shown |
537
+ |------------|-----------|-----------|------------|
538
+ | Symlink to file | `( Symlink )` | Yes | Target MIME type |
539
+ | Symlink to directory | `( Symlink )` | Yes | `DIR` |
540
+ | Broken/circular symlink | `( Broken Symlink )` | No | `unknown` |
541
+ | Regular file/directory | none | Yes | Real type |
542
+
543
+ **Edge cases handled:**
544
+ - Broken symlinks (missing target) return 404 on direct access
545
+ - Circular symlinks (A → B → A) are treated as broken, no infinite loops
546
+ - Symlinks to directories are fully navigable
547
+
465
548
  ---
466
549
 
467
550
  ## Security
@@ -556,10 +639,12 @@ npm run test:performance
556
639
  ```
557
640
 
558
641
  **Test Coverage:**
559
- - ✅ 197 tests passing
642
+ - ✅ 309 tests passing
560
643
  - ✅ Security tests (path traversal, XSS, race conditions)
561
644
  - ✅ EJS template integration tests
562
645
  - ✅ Index option tests (strings, arrays, RegExp)
646
+ - ✅ hideExtension tests (clean URLs, redirects, conflicts, validation)
647
+ - ✅ Symlink tests (file, directory, broken, circular, indicators)
563
648
  - ✅ Performance benchmarks
564
649
  - ✅ Directory sorting tests
565
650