koa-classic-server 2.5.0 → 2.5.2

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
@@ -64,8 +64,8 @@ npm install koa-classic-server
64
64
  ```
65
65
 
66
66
  **Requirements:**
67
- - Node.js >= 12.0.0
68
- - Koa >= 2.0.0
67
+ - Node.js >= 18.0.0
68
+ - Koa >= 2.0.0 (Koa 3 requires >= 3.1.2)
69
69
 
70
70
  ---
71
71
 
@@ -264,7 +264,133 @@ app.use(koaClassicServer(__dirname + '/public', {
264
264
 
265
265
  > **Note**: This conflict resolution behavior differs from Apache/Nginx, where directories typically take priority over files with the same base name.
266
266
 
267
- ### 7. With HTTP Caching
267
+ ### 7. URL Rewriting Support (useOriginalUrl)
268
+
269
+ When using URL rewriting middleware (i18n, routing), set `useOriginalUrl: false` so koa-classic-server resolves files from the rewritten URL instead of the original one:
270
+
271
+ ```javascript
272
+ const Koa = require('koa');
273
+ const koaClassicServer = require('koa-classic-server');
274
+
275
+ const app = new Koa();
276
+
277
+ // i18n middleware: strips language prefix and rewrites ctx.url
278
+ app.use(async (ctx, next) => {
279
+ const langMatch = ctx.path.match(/^\/(it|en|fr|de|es)\//);
280
+ if (langMatch) {
281
+ ctx.state.lang = langMatch[1]; // Save language for templates
282
+ ctx.url = ctx.path.replace(/^\/(it|en|fr|de|es)/, '') + ctx.search;
283
+ }
284
+ await next();
285
+ });
286
+
287
+ // Serve files using the rewritten URL
288
+ app.use(koaClassicServer(__dirname + '/public', {
289
+ useOriginalUrl: false // Use ctx.url (rewritten) instead of ctx.originalUrl
290
+ }));
291
+
292
+ app.listen(3000);
293
+ ```
294
+
295
+ **How it works:**
296
+
297
+ | Request | `ctx.originalUrl` | `ctx.url` (rewritten) | File resolved |
298
+ |---------|-------------------|----------------------|---------------|
299
+ | `/it/page.html` | `/it/page.html` | `/page.html` | `public/page.html` |
300
+ | `/en/page.html` | `/en/page.html` | `/page.html` | `public/page.html` |
301
+ | `/page.html` | `/page.html` | `/page.html` | `public/page.html` |
302
+
303
+ - With `useOriginalUrl: true` (default): the server would look for `public/it/page.html` (which doesn't exist)
304
+ - With `useOriginalUrl: false`: the server looks for `public/page.html` (correct)
305
+
306
+ ### 8. Advanced hideExtension Scenarios
307
+
308
+ #### Recommended file structure
309
+
310
+ ```
311
+ views/
312
+ ├── index.ejs ← / (home page)
313
+ ├── about.ejs ← /about
314
+ ├── contact.ejs ← /contact
315
+ ├── blog/
316
+ │ ├── index.ejs ← /blog/
317
+ │ ├── first-post.ejs ← /blog/first-post
318
+ │ └── second-post.ejs ← /blog/second-post
319
+ ├── docs/
320
+ │ ├── index.ejs ← /docs/
321
+ │ ├── getting-started.ejs ← /docs/getting-started
322
+ │ └── api-reference.ejs ← /docs/api-reference
323
+ └── assets/
324
+ ├── style.css ← /assets/style.css (served normally)
325
+ └── script.js ← /assets/script.js (served normally)
326
+ ```
327
+
328
+ #### hideExtension with i18n middleware
329
+
330
+ Combine `hideExtension` with `useOriginalUrl: false` for multilingual sites with clean URLs:
331
+
332
+ ```javascript
333
+ const Koa = require('koa');
334
+ const koaClassicServer = require('koa-classic-server');
335
+ const ejs = require('ejs');
336
+
337
+ const app = new Koa();
338
+
339
+ // i18n middleware: /it/about → ctx.url = /about, ctx.state.lang = 'it'
340
+ app.use(async (ctx, next) => {
341
+ const langMatch = ctx.path.match(/^\/(it|en|fr)\//);
342
+ if (langMatch) {
343
+ ctx.state.lang = langMatch[1];
344
+ ctx.url = ctx.path.replace(/^\/(it|en|fr)/, '') + ctx.search;
345
+ } else {
346
+ ctx.state.lang = 'en'; // Default language
347
+ }
348
+ await next();
349
+ });
350
+
351
+ app.use(koaClassicServer(__dirname + '/views', {
352
+ index: ['index.ejs'],
353
+ useOriginalUrl: false, // Resolve files from rewritten URL
354
+ hideExtension: {
355
+ ext: '.ejs',
356
+ redirect: 301
357
+ },
358
+ template: {
359
+ ext: ['ejs'],
360
+ render: async (ctx, next, filePath) => {
361
+ ctx.body = await ejs.renderFile(filePath, { lang: ctx.state.lang });
362
+ ctx.type = 'text/html';
363
+ }
364
+ }
365
+ }));
366
+
367
+ app.listen(3000);
368
+ ```
369
+
370
+ **Result:**
371
+
372
+ | Request | Rewritten URL | File resolved | Redirect target |
373
+ |---------|---------------|---------------|-----------------|
374
+ | `/it/about` | `/about` | `views/about.ejs` | — |
375
+ | `/en/blog/first-post` | `/blog/first-post` | `views/blog/first-post.ejs` | — |
376
+ | `/it/about.ejs` | `/about.ejs` | — | 301 → `/it/about` (preserves `/it/` prefix) |
377
+
378
+ > **Note**: Redirects always use `ctx.originalUrl` to preserve the language prefix, regardless of the `useOriginalUrl` setting.
379
+
380
+ #### Temporary redirect (302)
381
+
382
+ Use `redirect: 302` instead of 301 when the URL mapping may change (staging, A/B testing, or during migration):
383
+
384
+ ```javascript
385
+ hideExtension: {
386
+ ext: '.ejs',
387
+ redirect: 302 // Temporary redirect — browsers won't cache it
388
+ }
389
+ ```
390
+
391
+ > **When to use 302**: A 301 (permanent) tells browsers and search engines to cache the redirect. Use 302 (temporary) during development, staging, or when you're not yet sure the clean URL structure is final.
392
+
393
+ ### 9. With HTTP Caching
268
394
 
269
395
  Enable aggressive caching for static files:
270
396
 
@@ -286,7 +412,7 @@ The default value for `browserCacheEnabled` is `false` to facilitate development
286
412
 
287
413
  **See details:** [HTTP Caching Optimization →](./docs/OPTIMIZATION_HTTP_CACHING.md)
288
414
 
289
- ### 8. Multiple Index Files with Priority
415
+ ### 10. Multiple Index Files with Priority
290
416
 
291
417
  Search for multiple index files with custom order:
292
418
 
@@ -303,7 +429,7 @@ app.use(koaClassicServer(__dirname + '/public', {
303
429
 
304
430
  **See details:** [Index Option Priority →](./docs/INDEX_OPTION_PRIORITY.md)
305
431
 
306
- ### 9. Complete Production Example
432
+ ### 11. Complete Production Example
307
433
 
308
434
  Real-world configuration for production:
309
435
 
@@ -326,9 +452,15 @@ app.use(koaClassicServer(path.join(__dirname, 'public'), {
326
452
  browserCacheMaxAge: 86400, // 24 hours
327
453
  }));
328
454
 
329
- // Serve dynamic templates
455
+ // Serve dynamic templates with clean URLs
330
456
  app.use(koaClassicServer(path.join(__dirname, 'views'), {
331
457
  showDirContents: false,
458
+ index: ['index.ejs'],
459
+ useOriginalUrl: false, // Use ctx.url (for i18n or routing middleware)
460
+ hideExtension: {
461
+ ext: '.ejs', // /about → serves about.ejs
462
+ redirect: 301 // /about.ejs → 301 redirect to /about
463
+ },
332
464
  template: {
333
465
  ext: ['ejs'],
334
466
  render: async (ctx, next, filePath) => {
package/docs/CHANGELOG.md CHANGED
@@ -5,6 +5,23 @@ All notable changes to koa-classic-server will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.5.1] - 2026-03-01
9
+
10
+ ### 📝 Documentation
11
+
12
+ - Added dedicated usage example for `useOriginalUrl` (Section 7) with realistic i18n middleware scenario (/it/, /en/, /fr/)
13
+ - Added "Advanced hideExtension Scenarios" section (Section 8):
14
+ - Recommended file/directory structure (ASCII tree)
15
+ - Combined `hideExtension` + i18n middleware example with `useOriginalUrl: false`
16
+ - Temporary redirect (302) variant with guidance on 301 vs 302 usage
17
+ - Added `hideExtension` and `useOriginalUrl` to the Complete Production Example (Section 11)
18
+
19
+ ### 📦 Package Changes
20
+ - **Version**: `2.5.0` → `2.5.1`
21
+ - **Semver**: Patch version bump (documentation only, no code changes)
22
+
23
+ ---
24
+
8
25
  ## [2.5.0] - 2026-02-28
9
26
 
10
27
  ### ✨ New Feature
@@ -90,13 +90,17 @@ Il middleware segue questo flusso per ogni richiesta HTTP:
90
90
  ### Dipendenze
91
91
 
92
92
  #### Production
93
- - **koa** (^2.13.4): Framework web minimale per Node.js
94
- - **mime-types**: Riconoscimento automatico MIME types (dependency implicita)
93
+ - **mime-types** (^2.1.35): Riconoscimento automatico MIME types
94
+
95
+ #### Peer Dependencies
96
+ - **koa** (^2.0.0 || >=3.1.2): Framework web minimale per Node.js
95
97
 
96
98
  #### Development
97
- - **jest** (^29.7.0): Framework di testing
98
- - **supertest** (^7.0.0): Testing richieste HTTP
99
- - **inquirer** (^12.4.1): CLI interattiva per testing manuale
99
+ - **jest** (^30.2.0): Framework di testing
100
+ - **supertest** (^7.2.2): Testing richieste HTTP
101
+ - **inquirer** (^13.3.0): CLI interattiva per testing manuale
102
+ - **autocannon** (^8.0.0): HTTP benchmarking
103
+ - **ejs** (^3.1.10): Template engine per i test
100
104
 
101
105
  ---
102
106
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koa-classic-server",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "High-performance Koa middleware for serving static files with Apache-like directory listing, HTTP caching, template engine support, and comprehensive security fixes",
5
5
  "main": "index.cjs",
6
6
  "exports": {
@@ -37,13 +37,13 @@
37
37
  "mime-types": "^2.1.35"
38
38
  },
39
39
  "peerDependencies": {
40
- "koa": "^2.0.0 || ^3.0.0"
40
+ "koa": "^2.0.0 || >=3.1.2"
41
41
  },
42
42
  "devDependencies": {
43
- "autocannon": "^7.15.0",
43
+ "autocannon": "^8.0.0",
44
44
  "ejs": "^3.1.10",
45
- "inquirer": "^12.4.1",
46
- "jest": "^29.7.0",
47
- "supertest": "^7.0.0"
45
+ "inquirer": "^13.3.0",
46
+ "jest": "^30.2.0",
47
+ "supertest": "^7.2.2"
48
48
  }
49
49
  }