koa-classic-server 1.1.0 → 2.0.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.
@@ -0,0 +1,1585 @@
1
+ # KOA-CLASSIC-SERVER - Documentazione Completa
2
+
3
+ ## Indice
4
+
5
+ - [Descrizione del Modulo](#descrizione-del-modulo)
6
+ - [Installazione](#installazione)
7
+ - [Avvio Rapido](#avvio-rapido)
8
+ - [Configurazione](#configurazione)
9
+ - [Esempi d'Uso](#esempi-duso)
10
+ - [API Reference](#api-reference)
11
+ - [Comportamento del Middleware](#comportamento-del-middleware)
12
+ - [Testing](#testing)
13
+ - [Troubleshooting](#troubleshooting)
14
+ - [Problemi Noti](#problemi-noti)
15
+ - [Best Practices](#best-practices)
16
+
17
+ ---
18
+
19
+ ## Descrizione del Modulo
20
+
21
+ ### Panoramica
22
+
23
+ **koa-classic-server** è un middleware per Koa.js che emula il comportamento di Apache 2 per la gestione di file statici. Il modulo permette di servire file e directory con funzionalità avanzate di directory listing, supporto per template engine, gestione di URL riservati e configurazione flessibile.
24
+
25
+ ### Caratteristiche Principali
26
+
27
+ #### 1. Servizio File Statici
28
+ - Serve file statici da una directory specificata
29
+ - Riconoscimento automatico dei MIME types
30
+ - Gestione corretta di encoding e content-disposition
31
+ - Supporto per caratteri speciali e Unicode nei nomi file
32
+
33
+ #### 2. Directory Listing
34
+ - Visualizzazione del contenuto delle directory in formato HTML tabellare
35
+ - Navigazione parent directory con link ".. Parent Directory"
36
+ - Indicazione chiara del tipo di risorsa (DIR, MIME type)
37
+ - Gestione e visualizzazione cartelle riservate
38
+ - Supporto link simbolici
39
+
40
+ #### 3. Supporto Template Engine
41
+ - Integrazione flessibile con motori di template (es. EJS, Pug, Handlebars)
42
+ - Rendering personalizzato per estensioni specifiche
43
+ - Callback configurabile per il rendering
44
+ - Accesso completo al contesto Koa (ctx, next)
45
+
46
+ #### 4. Gestione URL Avanzata
47
+ - Supporto per URL prefix (es. `/public`, `/static`, `/files`)
48
+ - URL riservati non accessibili da remoto
49
+ - Normalizzazione automatica degli URL (rimozione trailing slash)
50
+ - Decodifica URI corretta per spazi e caratteri speciali
51
+ - Gestione case-sensitive dei path
52
+
53
+ #### 5. Compatibilità Moduli
54
+ - Supporto CommonJS (`require`)
55
+ - Supporto ES Modules (`import`)
56
+ - Conditional exports in package.json per massima compatibilità
57
+
58
+ ### Architettura
59
+
60
+ ```
61
+ koa-classic-server/
62
+ ├── index.cjs # Implementazione principale (CommonJS)
63
+ ├── index.mjs # Wrapper ES Modules
64
+ ├── package.json # Configurazione con conditional exports
65
+ ├── __tests__/ # Suite di test Jest
66
+ │ ├── index.test.js # Test completi
67
+ │ └── publicWwwTest/ # Cartella di test
68
+ ├── customTest/ # Utility per test manuali
69
+ │ ├── loadConfig.util.js # Script interattivo
70
+ │ └── serversToLoad.util.js # Configurazioni test
71
+ ├── LICENSE # Licenza MIT
72
+ ├── README.md # Documentazione base
73
+ └── DOCUMENTATION.md # Questa documentazione
74
+ ```
75
+
76
+ ### Flusso di Esecuzione
77
+
78
+ Il middleware segue questo flusso per ogni richiesta HTTP:
79
+
80
+ 1. **Validazione Metodo HTTP**: Verifica che il metodo sia tra quelli ammessi
81
+ 2. **Normalizzazione URL**: Rimuove trailing slash e normalizza il path
82
+ 3. **Verifica URL Prefix**: Controlla che la richiesta cada sotto il prefix configurato
83
+ 4. **Controllo URL Riservati**: Verifica che non sia una risorsa protetta
84
+ 5. **Risoluzione Path**: Costruisce il path completo file/directory
85
+ 6. **Esistenza Risorsa**: Verifica che file o directory esistano
86
+ 7. **Gestione Risorsa**:
87
+ - **File**: Template rendering o servizio statico
88
+ - **Directory**: Index file o directory listing
89
+
90
+ ### Dipendenze
91
+
92
+ #### Production
93
+ - **koa** (^2.13.4): Framework web minimale per Node.js
94
+ - **mime-types**: Riconoscimento automatico MIME types (dependency implicita)
95
+
96
+ #### 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
100
+
101
+ ---
102
+
103
+ ## Installazione
104
+
105
+ ### Installazione via npm
106
+
107
+ ```bash
108
+ npm install koa-classic-server
109
+ ```
110
+
111
+ ### Installazione via yarn
112
+
113
+ ```bash
114
+ yarn add koa-classic-server
115
+ ```
116
+
117
+ ### Requisiti
118
+
119
+ - **Node.js**: 12.20+ (raccomandato 14+)
120
+ - **Koa**: 2.x
121
+
122
+ ---
123
+
124
+ ## Avvio Rapido
125
+
126
+ ### Esempio Minimale
127
+
128
+ ```javascript
129
+ const Koa = require('koa');
130
+ const koaClassicServer = require('koa-classic-server');
131
+
132
+ const app = new Koa();
133
+
134
+ // Serve tutti i file dalla cartella "public"
135
+ app.use(koaClassicServer(__dirname + '/public'));
136
+
137
+ app.listen(3000);
138
+ console.log('Server avviato su http://localhost:3000');
139
+ ```
140
+
141
+ Questa configurazione base:
142
+ - Serve tutti i file dalla cartella `public`
143
+ - Mostra il contenuto delle directory
144
+ - Accetta solo richieste GET
145
+ - Nessun URL prefix o percorso riservato
146
+
147
+ ### Esempio con Opzioni
148
+
149
+ ```javascript
150
+ const Koa = require('koa');
151
+ const koaClassicServer = require('koa-classic-server');
152
+
153
+ const app = new Koa();
154
+
155
+ app.use(koaClassicServer(__dirname + '/public', {
156
+ showDirContents: true,
157
+ index: 'index.html',
158
+ urlPrefix: '/static'
159
+ }));
160
+
161
+ app.listen(3000);
162
+ console.log('Server avviato su http://localhost:3000/static');
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Configurazione
168
+
169
+ ### Sintassi
170
+
171
+ ```javascript
172
+ koaClassicServer(rootDir, options)
173
+ ```
174
+
175
+ ### Parametri
176
+
177
+ | Parametro | Tipo | Obbligatorio | Descrizione |
178
+ |-----------|------|--------------|-------------|
179
+ | `rootDir` | String | Sì | Path assoluto della directory da servire |
180
+ | `options` | Object | No | Oggetto di configurazione |
181
+
182
+ ### Opzioni Disponibili
183
+
184
+ ```javascript
185
+ const options = {
186
+ // Metodi HTTP ammessi
187
+ // Default: ['GET']
188
+ method: ['GET', 'HEAD'],
189
+
190
+ // Mostra il contenuto delle directory
191
+ // Default: true
192
+ showDirContents: true,
193
+
194
+ // Nome del file index da caricare automaticamente nelle directory
195
+ // Se presente, viene caricato invece del directory listing
196
+ // Default: ''
197
+ index: 'index.html',
198
+
199
+ // Prefisso URL da rimuovere dal path
200
+ // Es: '/public' significa che i file sono accessibili sotto /public/
201
+ // Default: ''
202
+ urlPrefix: '/public',
203
+
204
+ // Array di percorsi riservati (non accessibili da remoto)
205
+ // Funziona solo per directory di primo livello
206
+ // Default: []
207
+ urlsReserved: ['/api', '/admin', '/config'],
208
+
209
+ // Configurazione template engine
210
+ template: {
211
+ // Funzione di rendering personalizzata
212
+ // Riceve: ctx (contesto Koa), next (middleware successivo), filePath (path file)
213
+ // Default: undefined
214
+ render: async (ctx, next, filePath) => {
215
+ // Implementazione custom
216
+ // Es: ctx.body = await ejs.renderFile(filePath, data);
217
+ },
218
+
219
+ // Array di estensioni file da processare con template.render
220
+ // Se un file ha una di queste estensioni, viene chiamato render()
221
+ // Default: []
222
+ ext: ['ejs', 'EJS', 'pug', 'html']
223
+ }
224
+ };
225
+ ```
226
+
227
+ ### Dettaglio Opzioni
228
+
229
+ #### `method` (Array)
230
+
231
+ Specifica i metodi HTTP accettati dal middleware. Se una richiesta usa un metodo non presente nell'array, viene passata al middleware successivo.
232
+
233
+ ```javascript
234
+ // Solo GET
235
+ method: ['GET']
236
+
237
+ // GET e HEAD (utile per check esistenza file)
238
+ method: ['GET', 'HEAD']
239
+
240
+ // Multipli metodi (uso avanzato)
241
+ method: ['GET', 'HEAD', 'POST']
242
+ ```
243
+
244
+ #### `showDirContents` (Boolean)
245
+
246
+ Controlla se mostrare il contenuto delle directory.
247
+
248
+ ```javascript
249
+ // Mostra directory listing
250
+ showDirContents: true
251
+
252
+ // Non mostra directory (restituisce "Not Found")
253
+ showDirContents: false
254
+ ```
255
+
256
+ #### `index` (String)
257
+
258
+ Nome del file da caricare automaticamente quando si accede a una directory.
259
+
260
+ ```javascript
261
+ // Carica index.html se presente
262
+ index: 'index.html'
263
+
264
+ // Carica default.htm se presente
265
+ index: 'default.htm'
266
+
267
+ // Nessun index (mostra sempre directory listing)
268
+ index: ''
269
+ ```
270
+
271
+ **Comportamento:**
272
+ 1. Utente accede a `/cartella/`
273
+ 2. Se esiste `/cartella/index.html` → viene servito
274
+ 3. Altrimenti → mostra directory listing (se `showDirContents: true`)
275
+
276
+ #### `urlPrefix` (String)
277
+
278
+ Prefisso URL che il middleware deve intercettare. Utile per montare il file server sotto un percorso specifico.
279
+
280
+ ```javascript
281
+ // File accessibili sotto /static
282
+ // Es: /static/image.png, /static/css/style.css
283
+ urlPrefix: '/static'
284
+
285
+ // File accessibili sotto /public
286
+ urlPrefix: '/public'
287
+
288
+ // Nessun prefix (root del server)
289
+ urlPrefix: ''
290
+ ```
291
+
292
+ **Importante:**
293
+ - Il prefix viene rimosso prima di cercare il file nel filesystem
294
+ - Richiesta: `/static/image.png` → cerca `rootDir/image.png`
295
+
296
+ #### `urlsReserved` (Array)
297
+
298
+ Array di percorsi protetti, non accessibili da remoto. Utile per proteggere directory sensibili.
299
+
300
+ ```javascript
301
+ urlsReserved: ['/config', '/private', '/.env']
302
+ ```
303
+
304
+ **Limitazioni:**
305
+ - Funziona solo per directory di primo livello
306
+ - Non supporta percorsi annidati
307
+ - Non supporta wildcard
308
+
309
+ **Esempio:**
310
+ ```javascript
311
+ // ✅ Funziona
312
+ urlsReserved: ['/admin']
313
+ // Blocca: /admin, /admin/file.txt, /admin/sub/file.txt
314
+
315
+ // ❌ Non funziona come ci si aspetta
316
+ urlsReserved: ['/folder/subfolder']
317
+ // Non blocca percorsi annidati
318
+ ```
319
+
320
+ #### `template.render` (Function)
321
+
322
+ Funzione personalizzata per il rendering di template.
323
+
324
+ **Firma:**
325
+ ```javascript
326
+ async function render(ctx, next, filePath) {
327
+ // ctx: Contesto Koa
328
+ // next: Middleware successivo
329
+ // filePath: Path completo del file da renderizzare
330
+
331
+ // Esempio EJS
332
+ ctx.body = await ejs.renderFile(filePath, {
333
+ // Dati passati al template
334
+ title: 'My Page',
335
+ user: ctx.state.user
336
+ });
337
+ }
338
+ ```
339
+
340
+ #### `template.ext` (Array)
341
+
342
+ Array di estensioni file che devono essere processate con `template.render`.
343
+
344
+ ```javascript
345
+ // Solo file .ejs
346
+ ext: ['ejs']
347
+
348
+ // File .ejs e .EJS (case-sensitive)
349
+ ext: ['ejs', 'EJS']
350
+
351
+ // Multipli template engine
352
+ ext: ['ejs', 'pug', 'html', 'hbs']
353
+ ```
354
+
355
+ **Comportamento:**
356
+ - Se file ha estensione in `ext` E `render` è definito → esegue rendering
357
+ - Altrimenti → serve file normalmente
358
+
359
+ ---
360
+
361
+ ## Esempi d'Uso
362
+
363
+ ### 1. Server Base con Directory Listing
364
+
365
+ ```javascript
366
+ const Koa = require('koa');
367
+ const koaClassicServer = require('koa-classic-server');
368
+
369
+ const app = new Koa();
370
+
371
+ app.use(koaClassicServer(__dirname + '/public', {
372
+ showDirContents: true,
373
+ index: 'index.html'
374
+ }));
375
+
376
+ app.listen(3000);
377
+ console.log('Server su http://localhost:3000');
378
+ ```
379
+
380
+ **Funzionalità:**
381
+ - Serve file da `public/`
382
+ - Mostra directory listing
383
+ - Carica `index.html` automaticamente se presente
384
+
385
+ ---
386
+
387
+ ### 2. Server con URL Prefix
388
+
389
+ ```javascript
390
+ const Koa = require('koa');
391
+ const koaClassicServer = require('koa-classic-server');
392
+
393
+ const app = new Koa();
394
+
395
+ // File accessibili sotto /static
396
+ app.use(koaClassicServer(__dirname + '/public', {
397
+ urlPrefix: '/static',
398
+ method: ['GET', 'HEAD'],
399
+ showDirContents: true
400
+ }));
401
+
402
+ // Altri middleware per route diverse
403
+ app.use(async (ctx) => {
404
+ ctx.body = 'Homepage del sito';
405
+ });
406
+
407
+ app.listen(3000);
408
+ ```
409
+
410
+ **URL esempi:**
411
+ - `http://localhost:3000/` → Homepage
412
+ - `http://localhost:3000/static/` → Directory listing di public/
413
+ - `http://localhost:3000/static/image.png` → public/image.png
414
+
415
+ ---
416
+
417
+ ### 3. Server con Cartelle Protette
418
+
419
+ ```javascript
420
+ const Koa = require('koa');
421
+ const koaClassicServer = require('koa-classic-server');
422
+
423
+ const app = new Koa();
424
+
425
+ app.use(koaClassicServer(__dirname + '/www', {
426
+ showDirContents: true,
427
+ // Protegge directory sensibili
428
+ urlsReserved: ['/config', '/private', '/.git', '/node_modules']
429
+ }));
430
+
431
+ app.listen(3000);
432
+ ```
433
+
434
+ **Comportamento:**
435
+ - `/config/` → Not Found (protetto)
436
+ - `/private/secret.txt` → Not Found (protetto)
437
+ - `/public/file.txt` → Accessibile
438
+
439
+ ---
440
+
441
+ ### 4. Server con Template Engine (EJS)
442
+
443
+ ```javascript
444
+ const Koa = require('koa');
445
+ const koaClassicServer = require('koa-classic-server');
446
+ const ejs = require('ejs');
447
+
448
+ const app = new Koa();
449
+
450
+ app.use(koaClassicServer(__dirname + '/views', {
451
+ showDirContents: false,
452
+ template: {
453
+ render: async (ctx, next, filePath) => {
454
+ // Rendering EJS con dati
455
+ ctx.body = await ejs.renderFile(filePath, {
456
+ title: 'My App',
457
+ filePath: filePath,
458
+ href: ctx.href,
459
+ query: ctx.query,
460
+ user: ctx.state.user || 'Guest'
461
+ });
462
+ },
463
+ ext: ['ejs', 'EJS']
464
+ }
465
+ }));
466
+
467
+ app.listen(3000);
468
+ console.log('Server con EJS su http://localhost:3000');
469
+ ```
470
+
471
+ ---
472
+
473
+ ### 5. Server Multi-Directory
474
+
475
+ ```javascript
476
+ const Koa = require('koa');
477
+ const koaClassicServer = require('koa-classic-server');
478
+
479
+ const app = new Koa();
480
+
481
+ // File statici pubblici
482
+ app.use(koaClassicServer(__dirname + '/public', {
483
+ urlPrefix: '/public',
484
+ showDirContents: true
485
+ }));
486
+
487
+ // Asset (CSS, JS, images)
488
+ app.use(koaClassicServer(__dirname + '/assets', {
489
+ urlPrefix: '/assets',
490
+ showDirContents: false
491
+ }));
492
+
493
+ // Download area
494
+ app.use(koaClassicServer(__dirname + '/downloads', {
495
+ urlPrefix: '/downloads',
496
+ showDirContents: true,
497
+ index: 'README.txt'
498
+ }));
499
+
500
+ app.listen(3000);
501
+ ```
502
+
503
+ ---
504
+
505
+ ### 6. Configurazione Completa Produzione
506
+
507
+ ```javascript
508
+ const Koa = require('koa');
509
+ const koaClassicServer = require('koa-classic-server');
510
+ const ejs = require('ejs');
511
+ const path = require('path');
512
+
513
+ const app = new Koa();
514
+
515
+ // Logging middleware
516
+ app.use(async (ctx, next) => {
517
+ const start = Date.now();
518
+ await next();
519
+ const ms = Date.now() - start;
520
+ console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
521
+ });
522
+
523
+ // Error handling
524
+ app.on('error', (err, ctx) => {
525
+ console.error('Server error:', err);
526
+ });
527
+
528
+ // Template render function
529
+ const templateRender = async (ctx, next, filePath) => {
530
+ try {
531
+ ctx.body = await ejs.renderFile(filePath, {
532
+ filePath: filePath,
533
+ href: ctx.href,
534
+ query: ctx.query,
535
+ method: ctx.method,
536
+ env: process.env.NODE_ENV
537
+ });
538
+ } catch (err) {
539
+ console.error('Template render error:', err);
540
+ ctx.status = 500;
541
+ ctx.body = 'Template Error';
542
+ }
543
+ };
544
+
545
+ // File server
546
+ app.use(koaClassicServer(path.join(__dirname, 'public'), {
547
+ method: ['GET', 'HEAD'],
548
+ showDirContents: process.env.NODE_ENV !== 'production',
549
+ index: 'index.html',
550
+ urlPrefix: '/files',
551
+ urlsReserved: ['/admin', '/private', '/config', '/.env'],
552
+ template: {
553
+ render: templateRender,
554
+ ext: ['ejs', 'html']
555
+ }
556
+ }));
557
+
558
+ // 404 fallback
559
+ app.use(async (ctx) => {
560
+ ctx.status = 404;
561
+ ctx.body = 'Pagina non trovata';
562
+ });
563
+
564
+ const port = process.env.PORT || 3000;
565
+ app.listen(port, () => {
566
+ console.log(`Server avviato su http://localhost:${port}`);
567
+ console.log(`Ambiente: ${process.env.NODE_ENV || 'development'}`);
568
+ });
569
+ ```
570
+
571
+ ---
572
+
573
+ ### 7. Integrazione con Router Koa
574
+
575
+ ```javascript
576
+ const Koa = require('koa');
577
+ const Router = require('@koa/router');
578
+ const koaClassicServer = require('koa-classic-server');
579
+
580
+ const app = new Koa();
581
+ const router = new Router();
582
+
583
+ // API routes
584
+ router.get('/api/users', (ctx) => {
585
+ ctx.body = { users: ['Alice', 'Bob'] };
586
+ });
587
+
588
+ router.post('/api/login', (ctx) => {
589
+ ctx.body = { token: 'xyz123' };
590
+ });
591
+
592
+ // Monta il router
593
+ app.use(router.routes());
594
+ app.use(router.allowedMethods());
595
+
596
+ // File statici (dopo le route API)
597
+ app.use(koaClassicServer(__dirname + '/public', {
598
+ showDirContents: true
599
+ }));
600
+
601
+ app.listen(3000);
602
+ ```
603
+
604
+ **Ordine importante:**
605
+ 1. Router con API dinamiche
606
+ 2. File server statico
607
+ 3. Questo permette alle API di avere precedenza sui file
608
+
609
+ ---
610
+
611
+ ## API Reference
612
+
613
+ ### koaClassicServer(rootDir, options)
614
+
615
+ Crea e restituisce un middleware Koa per servire file statici.
616
+
617
+ **Parametri:**
618
+
619
+ - `rootDir` (String, required): Path assoluto directory root
620
+ - `options` (Object, optional): Oggetto configurazione
621
+
622
+ **Restituisce:**
623
+
624
+ - Function: Middleware Koa `async (ctx, next) => {...}`
625
+
626
+ **Esempio:**
627
+
628
+ ```javascript
629
+ const middleware = koaClassicServer('/path/to/files', {
630
+ showDirContents: true
631
+ });
632
+
633
+ app.use(middleware);
634
+ ```
635
+
636
+ ### Oggetto Options
637
+
638
+ Vedi sezione [Configurazione](#configurazione) per dettagli completi.
639
+
640
+ ### Context (ctx) nel Template Render
641
+
642
+ Quando usi `template.render`, hai accesso completo al contesto Koa:
643
+
644
+ ```javascript
645
+ template: {
646
+ render: async (ctx, next, filePath) => {
647
+ // ctx.request - Oggetto richiesta
648
+ console.log(ctx.request.method); // 'GET', 'POST', etc.
649
+ console.log(ctx.request.url); // '/path/to/file'
650
+ console.log(ctx.request.header); // Headers HTTP
651
+
652
+ // ctx.response - Oggetto risposta
653
+ ctx.response.type = 'text/html';
654
+ ctx.response.body = '<html>...</html>';
655
+
656
+ // Shortcuts
657
+ console.log(ctx.method); // 'GET'
658
+ console.log(ctx.url); // '/path/to/file'
659
+ console.log(ctx.href); // 'http://localhost:3000/path/to/file'
660
+ console.log(ctx.query); // { key: 'value' }
661
+ console.log(ctx.path); // '/path/to/file'
662
+
663
+ // State (dati condivisi tra middleware)
664
+ console.log(ctx.state.user);
665
+
666
+ // Cookies
667
+ console.log(ctx.cookies.get('session'));
668
+
669
+ // filePath - Path completo del file
670
+ console.log(filePath); // '/var/www/public/page.ejs'
671
+ }
672
+ }
673
+ ```
674
+
675
+ ---
676
+
677
+ ## Comportamento del Middleware
678
+
679
+ ### Gestione delle Directory
680
+
681
+ #### Caso 1: showDirContents = true, index presente
682
+
683
+ ```
684
+ Richiesta: GET /cartella/
685
+ Filesystem:
686
+ /cartella/index.html ✓ (esiste)
687
+ /cartella/file1.txt
688
+
689
+ Risultato: Serve /cartella/index.html
690
+ ```
691
+
692
+ #### Caso 2: showDirContents = true, index assente
693
+
694
+ ```
695
+ Richiesta: GET /cartella/
696
+ Filesystem:
697
+ /cartella/file1.txt
698
+ /cartella/file2.jpg
699
+
700
+ Risultato: Mostra directory listing HTML
701
+ ```
702
+
703
+ #### Caso 3: showDirContents = false
704
+
705
+ ```
706
+ Richiesta: GET /cartella/
707
+
708
+ Risultato: "Not Found" (indipendentemente da index)
709
+ ```
710
+
711
+ ### Gestione dei File
712
+
713
+ #### File Normale
714
+
715
+ ```
716
+ Richiesta: GET /document.pdf
717
+ Filesystem: /document.pdf (esiste)
718
+
719
+ Risultato:
720
+ Status: 200
721
+ Content-Type: application/pdf
722
+ Content-Disposition: inline; filename=document.pdf
723
+ Body: [file stream]
724
+ ```
725
+
726
+ #### File Template
727
+
728
+ ```
729
+ Richiesta: GET /page.ejs
730
+ Filesystem: /page.ejs (esiste)
731
+ Config: template.ext = ['ejs'], template.render definito
732
+
733
+ Risultato:
734
+ Status: 200
735
+ Content-Type: [settato da template.render]
736
+ Body: [output renderizzato]
737
+ ```
738
+
739
+ ### URL Riservati
740
+
741
+ ```
742
+ Config: urlsReserved = ['/admin', '/private']
743
+
744
+ Richiesta: GET /admin/config.json
745
+ Risultato: Passa a middleware successivo (salta koa-classic-server)
746
+
747
+ Richiesta: GET /admin/
748
+ Risultato: Directory listing mostra "DIR BUT RESERVED"
749
+
750
+ Richiesta: GET /public/file.txt
751
+ Risultato: File servito normalmente
752
+ ```
753
+
754
+ ### Parent Directory Navigation
755
+
756
+ Nel directory listing, viene sempre mostrato link alla parent directory, tranne nella root:
757
+
758
+ ```html
759
+ <!-- Root: http://localhost:3000/ -->
760
+ <!-- Nessun parent directory link -->
761
+
762
+ <!-- Sub-directory: http://localhost:3000/folder/ -->
763
+ <table>
764
+ <tr><td><a href="http://localhost:3000"><b>.. Parent Directory</b></a></td><td>DIR</td></tr>
765
+ <!-- altri file... -->
766
+ </table>
767
+ ```
768
+
769
+ ### Normalizzazione URL
770
+
771
+ Il middleware normalizza automaticamente gli URL:
772
+
773
+ ```
774
+ http://localhost:3000/folder/ → http://localhost:3000/folder
775
+ http://localhost:3000/file.txt/ → http://localhost:3000/file.txt
776
+ ```
777
+
778
+ Questo assicura comportamento coerente indipendentemente dal trailing slash.
779
+
780
+ ### MIME Types
781
+
782
+ MIME types riconosciuti automaticamente tramite estensione file:
783
+
784
+ ```javascript
785
+ .html → text/html
786
+ .css → text/css
787
+ .js → application/javascript
788
+ .json → application/json
789
+ .png → image/png
790
+ .jpg → image/jpeg
791
+ .pdf → application/pdf
792
+ .txt → text/plain
793
+ // ... e molti altri
794
+ ```
795
+
796
+ Se MIME type non riconosciuto:
797
+ ```
798
+ Estensione sconosciuta → 'unknow' (nel directory listing)
799
+ ```
800
+
801
+ ---
802
+
803
+ ## Testing
804
+
805
+ ### Test Automatici (Jest)
806
+
807
+ Il progetto include una suite completa di test.
808
+
809
+ ```bash
810
+ # Esegui tutti i test
811
+ npm test
812
+
813
+ # Test con coverage
814
+ npm test -- --coverage
815
+
816
+ # Test in watch mode
817
+ npm test -- --watch
818
+ ```
819
+
820
+ ### Test Coverage
821
+
822
+ I test coprono:
823
+
824
+ - ✅ Servizio file statici
825
+ - ✅ Directory listing
826
+ - ✅ URL prefix
827
+ - ✅ URL riservati
828
+ - ✅ File index
829
+ - ✅ Percorsi non esistenti
830
+ - ✅ Metodi HTTP
831
+ - ✅ Caratteri speciali nei nomi file
832
+ - ⚠️ Template rendering (parziale)
833
+ - ⚠️ Directory listing completo (parziale)
834
+
835
+ ### Test Manuali Interattivi
836
+
837
+ Per testare manualmente diverse configurazioni:
838
+
839
+ ```bash
840
+ npm run loadConfig
841
+ ```
842
+
843
+ Questo comando:
844
+ 1. Mostra menu interattivo con configurazioni predefinite
845
+ 2. Avvia server con configurazione scelta
846
+ 3. Permette test manuale via browser
847
+
848
+ **Configurazioni disponibili:**
849
+ - Test generico base
850
+ - Test con URL prefix `/public`
851
+ - Test con file index
852
+ - Test con percorsi riservati
853
+
854
+ ### Struttura Test
855
+
856
+ ```
857
+ __tests__/
858
+ ├── index.test.js # Suite principale
859
+ └── publicWwwTest/ # Cartella test con file/directory campione
860
+ ├── file.txt
861
+ ├── image.png
862
+ ├── subfolder/
863
+ └── ...
864
+ ```
865
+
866
+ ### Scrivere Nuovi Test
867
+
868
+ Esempio test personalizzato:
869
+
870
+ ```javascript
871
+ const supertest = require('supertest');
872
+ const koaClassicServer = require('../index.cjs');
873
+ const Koa = require('koa');
874
+
875
+ describe('My custom test', () => {
876
+ let app, server;
877
+
878
+ beforeAll(() => {
879
+ app = new Koa();
880
+ app.use(koaClassicServer(__dirname + '/test-files', {
881
+ showDirContents: true
882
+ }));
883
+ server = app.listen();
884
+ });
885
+
886
+ test('should serve HTML file', async () => {
887
+ const res = await supertest(server).get('/index.html');
888
+ expect(res.status).toBe(200);
889
+ expect(res.type).toMatch(/text\/html/);
890
+ });
891
+
892
+ afterAll(() => {
893
+ server.close();
894
+ });
895
+ });
896
+ ```
897
+
898
+ ---
899
+
900
+ ## Troubleshooting
901
+
902
+ ### File non viene servito
903
+
904
+ **Problema:** Richiesta a file esistente restituisce 404
905
+
906
+ **Soluzioni:**
907
+
908
+ 1. Verifica path assoluto:
909
+ ```javascript
910
+ // ❌ Path relativo
911
+ koaClassicServer('./public')
912
+
913
+ // ✅ Path assoluto
914
+ koaClassicServer(__dirname + '/public')
915
+ koaClassicServer(path.join(__dirname, 'public'))
916
+ ```
917
+
918
+ 2. Controlla URL prefix:
919
+ ```javascript
920
+ // Config
921
+ urlPrefix: '/static'
922
+
923
+ // ❌ URL sbagliato
924
+ http://localhost:3000/file.txt
925
+
926
+ // ✅ URL corretto
927
+ http://localhost:3000/static/file.txt
928
+ ```
929
+
930
+ 3. Verifica URL riservati:
931
+ ```javascript
932
+ // Config
933
+ urlsReserved: ['/protected']
934
+
935
+ // ❌ File in cartella protetta
936
+ GET /protected/file.txt → 404
937
+
938
+ // ✅ File fuori da cartella protetta
939
+ GET /public/file.txt → 200
940
+ ```
941
+
942
+ ---
943
+
944
+ ### Template non viene renderizzato
945
+
946
+ **Problema:** File template viene servito come testo invece di essere renderizzato
947
+
948
+ **Soluzioni:**
949
+
950
+ 1. Verifica estensione in `template.ext`:
951
+ ```javascript
952
+ // File: page.ejs
953
+
954
+ // ❌ Estensione mancante
955
+ template: {
956
+ render: renderFunction,
957
+ ext: ['html'] // manca 'ejs'
958
+ }
959
+
960
+ // ✅ Estensione presente
961
+ template: {
962
+ render: renderFunction,
963
+ ext: ['ejs', 'EJS']
964
+ }
965
+ ```
966
+
967
+ 2. Verifica `template.render` sia funzione:
968
+ ```javascript
969
+ // ❌ render non definito
970
+ template: {
971
+ ext: ['ejs']
972
+ // render mancante!
973
+ }
974
+
975
+ // ✅ render definito
976
+ template: {
977
+ render: async (ctx, next, filePath) => {
978
+ ctx.body = await ejs.renderFile(filePath, {});
979
+ },
980
+ ext: ['ejs']
981
+ }
982
+ ```
983
+
984
+ 3. Installa template engine:
985
+ ```bash
986
+ npm install ejs
987
+ ```
988
+
989
+ ---
990
+
991
+ ### Directory listing non funziona
992
+
993
+ **Problema:** Accesso a directory restituisce "Not Found"
994
+
995
+ **Soluzione:**
996
+
997
+ ```javascript
998
+ // ❌ showDirContents disabilitato
999
+ showDirContents: false
1000
+
1001
+ // ✅ showDirContents abilitato
1002
+ showDirContents: true
1003
+ ```
1004
+
1005
+ ---
1006
+
1007
+ ### URL riservati non proteggono
1008
+
1009
+ **Problema:** File in cartelle riservate sono accessibili
1010
+
1011
+ **Cause comuni:**
1012
+
1013
+ 1. **Percorsi annidati non supportati:**
1014
+ ```javascript
1015
+ // ❌ Non funziona
1016
+ urlsReserved: ['/path/to/protected']
1017
+
1018
+ // ✅ Solo primo livello
1019
+ urlsReserved: ['/protected']
1020
+ ```
1021
+
1022
+ 2. **Spazi nei nomi (bug noto):**
1023
+ ```javascript
1024
+ // ⚠️ Problematico
1025
+ urlsReserved: ['/percorso riservato']
1026
+
1027
+ // ✅ Usa underscore o trattini
1028
+ urlsReserved: ['/percorso_riservato', '/percorso-riservato']
1029
+ ```
1030
+
1031
+ ---
1032
+
1033
+ ### Metodo HTTP non accettato
1034
+
1035
+ **Problema:** Richieste POST/PUT/DELETE non funzionano
1036
+
1037
+ **Soluzione:**
1038
+
1039
+ ```javascript
1040
+ // ❌ Solo GET (default)
1041
+ method: ['GET']
1042
+
1043
+ // ✅ Aggiungi metodi necessari
1044
+ method: ['GET', 'POST', 'PUT', 'DELETE']
1045
+ ```
1046
+
1047
+ **Nota:** Di solito file server necessita solo GET e HEAD
1048
+
1049
+ ---
1050
+
1051
+ ### Caratteri speciali in nomi file
1052
+
1053
+ **Problema:** File con spazi o caratteri speciali non accessibili
1054
+
1055
+ **Soluzione:**
1056
+
1057
+ Il middleware gestisce automaticamente URI encoding. Assicurati che il client codifichi correttamente:
1058
+
1059
+ ```javascript
1060
+ // ❌ Non encodato
1061
+ GET /my file.txt
1062
+
1063
+ // ✅ Encodato (automatico nei browser)
1064
+ GET /my%20file.txt
1065
+ ```
1066
+
1067
+ In JavaScript:
1068
+ ```javascript
1069
+ const filename = 'my file.txt';
1070
+ const url = '/' + encodeURIComponent(filename);
1071
+ // url = '/my%20file.txt'
1072
+ ```
1073
+
1074
+ ---
1075
+
1076
+ ### Performance con molti file
1077
+
1078
+ **Problema:** Directory listing lento con migliaia di file
1079
+
1080
+ **Soluzioni:**
1081
+
1082
+ 1. Disabilita directory listing:
1083
+ ```javascript
1084
+ showDirContents: false
1085
+ ```
1086
+
1087
+ 2. Usa file index:
1088
+ ```javascript
1089
+ showDirContents: true,
1090
+ index: 'index.html' // Carica index invece di listing
1091
+ ```
1092
+
1093
+ 3. Considera soluzioni alternative per grandi quantità di file (es. nginx, CDN)
1094
+
1095
+ ---
1096
+
1097
+ ## Problemi Noti
1098
+
1099
+ ### 1. Status Code 404 Mancante
1100
+
1101
+ **Problema:** Quando una risorsa non viene trovata, lo status code è 200 invece di 404
1102
+
1103
+ **File:** `index.cjs:110`
1104
+
1105
+ ```javascript
1106
+ if (!fs.existsSync(toOpen)) {
1107
+ ctx.body = requestedUrlNotFound();
1108
+ // Manca: ctx.status = 404;
1109
+ return;
1110
+ }
1111
+ ```
1112
+
1113
+ **Workaround:**
1114
+ ```javascript
1115
+ // Middleware successivo per gestire 404
1116
+ app.use(async (ctx) => {
1117
+ if (!ctx.body) {
1118
+ ctx.status = 404;
1119
+ ctx.body = 'Not Found';
1120
+ }
1121
+ });
1122
+ ```
1123
+
1124
+ ---
1125
+
1126
+ ### 2. URL Riservati con Spazi
1127
+
1128
+ **Problema:** Il controllo URL riservati non funziona con percorsi contenenti spazi
1129
+
1130
+ **File:** `index.cjs:87-96`
1131
+
1132
+ **Limitazione:** Problemi con URI encoding negli URL riservati
1133
+
1134
+ **Workaround:** Evita spazi nei nomi delle cartelle riservate:
1135
+ ```javascript
1136
+ // ❌ Non funziona
1137
+ urlsReserved: ['/percorso riservato']
1138
+
1139
+ // ✅ Funziona
1140
+ urlsReserved: ['/percorso_riservato', '/percorso-riservato']
1141
+ ```
1142
+
1143
+ ---
1144
+
1145
+ ### 3. URL Riservati Solo Primo Livello
1146
+
1147
+ **Problema:** URL riservati funzionano solo per directory di primo livello, non per percorsi annidati
1148
+
1149
+ **File:** `index.cjs:87-96`
1150
+
1151
+ **Limitazione:** Design della funzionalità
1152
+
1153
+ ```javascript
1154
+ // ❌ Non supportato
1155
+ urlsReserved: ['/path/to/protected']
1156
+
1157
+ // ✅ Supportato
1158
+ urlsReserved: ['/protected']
1159
+ // Blocca: /protected, /protected/file.txt, /protected/sub/file.txt
1160
+ ```
1161
+
1162
+ ---
1163
+
1164
+ ### 4. Test Coverage Incompleto
1165
+
1166
+ **Problema:** Test non coprono completamente:
1167
+ - Contenuto HTML del directory listing
1168
+ - Tutti i casi di template rendering
1169
+
1170
+ **File:** `__tests__/index.test.js:1-11`
1171
+
1172
+ **Impatto:** Possibili bug non rilevati in queste aree
1173
+
1174
+ ---
1175
+
1176
+ ### 5. Single Index File
1177
+
1178
+ **Problema:** Supporto per un solo nome file index, non array di fallback
1179
+
1180
+ **Limitazione:** Design attuale
1181
+
1182
+ ```javascript
1183
+ // ❌ Non supportato
1184
+ index: ['index.html', 'index.htm', 'default.html']
1185
+
1186
+ // ✅ Supportato
1187
+ index: 'index.html'
1188
+ ```
1189
+
1190
+ **Workaround:** Standardizza su un solo nome file index
1191
+
1192
+ ---
1193
+
1194
+ ## Best Practices
1195
+
1196
+ ### 1. Sicurezza
1197
+
1198
+ #### Usa Path Assoluti
1199
+ ```javascript
1200
+ // ✅ Corretto
1201
+ const path = require('path');
1202
+ app.use(koaClassicServer(path.join(__dirname, 'public')));
1203
+
1204
+ // ❌ Evita path relativi
1205
+ app.use(koaClassicServer('./public'));
1206
+ ```
1207
+
1208
+ #### Proteggi Directory Sensibili
1209
+ ```javascript
1210
+ app.use(koaClassicServer(__dirname + '/www', {
1211
+ urlsReserved: [
1212
+ '/config',
1213
+ '/.env',
1214
+ '/.git',
1215
+ '/node_modules',
1216
+ '/private',
1217
+ '/admin'
1218
+ ]
1219
+ }));
1220
+ ```
1221
+
1222
+ #### Limita Metodi HTTP
1223
+ ```javascript
1224
+ // Solo lettura
1225
+ method: ['GET', 'HEAD']
1226
+
1227
+ // Evita metodi di scrittura per file server
1228
+ // method: ['POST', 'PUT', 'DELETE'] ❌
1229
+ ```
1230
+
1231
+ #### Disabilita Directory Listing in Produzione
1232
+ ```javascript
1233
+ app.use(koaClassicServer(rootDir, {
1234
+ showDirContents: process.env.NODE_ENV !== 'production',
1235
+ index: 'index.html'
1236
+ }));
1237
+ ```
1238
+
1239
+ ---
1240
+
1241
+ ### 2. Performance
1242
+
1243
+ #### Usa Variabili d'Ambiente
1244
+ ```javascript
1245
+ const isProduction = process.env.NODE_ENV === 'production';
1246
+
1247
+ app.use(koaClassicServer(rootDir, {
1248
+ showDirContents: !isProduction,
1249
+ method: ['GET', 'HEAD']
1250
+ }));
1251
+ ```
1252
+
1253
+ #### Middleware Logging Efficiente
1254
+ ```javascript
1255
+ // Solo in development
1256
+ if (process.env.NODE_ENV !== 'production') {
1257
+ app.use(async (ctx, next) => {
1258
+ console.log(`${ctx.method} ${ctx.url}`);
1259
+ await next();
1260
+ });
1261
+ }
1262
+ ```
1263
+
1264
+ #### Cache Headers (middleware aggiuntivo)
1265
+ ```javascript
1266
+ // Aggiungi cache headers per file statici
1267
+ app.use(async (ctx, next) => {
1268
+ await next();
1269
+ if (ctx.method === 'GET' && ctx.status === 200) {
1270
+ ctx.set('Cache-Control', 'public, max-age=86400'); // 24h
1271
+ }
1272
+ });
1273
+
1274
+ app.use(koaClassicServer(rootDir));
1275
+ ```
1276
+
1277
+ ---
1278
+
1279
+ ### 3. Organizzazione Codice
1280
+
1281
+ #### Separa Configurazione
1282
+ ```javascript
1283
+ // config/fileServer.js
1284
+ const path = require('path');
1285
+
1286
+ module.exports = {
1287
+ rootDir: path.join(__dirname, '../public'),
1288
+ options: {
1289
+ method: ['GET', 'HEAD'],
1290
+ showDirContents: true,
1291
+ index: 'index.html',
1292
+ urlPrefix: '/static',
1293
+ urlsReserved: ['/admin', '/config']
1294
+ }
1295
+ };
1296
+
1297
+ // app.js
1298
+ const fileServerConfig = require('./config/fileServer');
1299
+ app.use(koaClassicServer(
1300
+ fileServerConfig.rootDir,
1301
+ fileServerConfig.options
1302
+ ));
1303
+ ```
1304
+
1305
+ #### Template Rendering Modulare
1306
+ ```javascript
1307
+ // lib/templateRenderer.js
1308
+ const ejs = require('ejs');
1309
+
1310
+ module.exports = async function renderTemplate(ctx, next, filePath) {
1311
+ try {
1312
+ ctx.body = await ejs.renderFile(filePath, {
1313
+ title: getPageTitle(filePath),
1314
+ user: ctx.state.user,
1315
+ ...ctx.state.templateData
1316
+ });
1317
+ } catch (error) {
1318
+ console.error('Template error:', error);
1319
+ ctx.status = 500;
1320
+ ctx.body = 'Rendering Error';
1321
+ }
1322
+ };
1323
+
1324
+ function getPageTitle(filePath) {
1325
+ // Logica per estrarre titolo
1326
+ return 'My Page';
1327
+ }
1328
+
1329
+ // app.js
1330
+ const templateRenderer = require('./lib/templateRenderer');
1331
+
1332
+ app.use(koaClassicServer(rootDir, {
1333
+ template: {
1334
+ render: templateRenderer,
1335
+ ext: ['ejs', 'html']
1336
+ }
1337
+ }));
1338
+ ```
1339
+
1340
+ ---
1341
+
1342
+ ### 4. Error Handling
1343
+
1344
+ #### Global Error Handler
1345
+ ```javascript
1346
+ app.on('error', (err, ctx) => {
1347
+ console.error('Server error:', {
1348
+ error: err.message,
1349
+ stack: err.stack,
1350
+ url: ctx.url,
1351
+ method: ctx.method,
1352
+ ip: ctx.ip
1353
+ });
1354
+ });
1355
+ ```
1356
+
1357
+ #### Try-Catch in Template Render
1358
+ ```javascript
1359
+ template: {
1360
+ render: async (ctx, next, filePath) => {
1361
+ try {
1362
+ ctx.body = await ejs.renderFile(filePath, data);
1363
+ } catch (error) {
1364
+ console.error('Render error:', error);
1365
+ ctx.status = 500;
1366
+ ctx.body = 'Template Error';
1367
+ }
1368
+ },
1369
+ ext: ['ejs']
1370
+ }
1371
+ ```
1372
+
1373
+ #### 404 Fallback
1374
+ ```javascript
1375
+ // Ultimo middleware
1376
+ app.use(async (ctx) => {
1377
+ ctx.status = 404;
1378
+ ctx.type = 'html';
1379
+ ctx.body = `
1380
+ <!DOCTYPE html>
1381
+ <html>
1382
+ <head><title>404</title></head>
1383
+ <body>
1384
+ <h1>Pagina Non Trovata</h1>
1385
+ <p>La risorsa richiesta non esiste.</p>
1386
+ </body>
1387
+ </html>
1388
+ `;
1389
+ });
1390
+ ```
1391
+
1392
+ ---
1393
+
1394
+ ### 5. Development vs Production
1395
+
1396
+ ```javascript
1397
+ const Koa = require('koa');
1398
+ const koaClassicServer = require('koa-classic-server');
1399
+ const path = require('path');
1400
+
1401
+ const app = new Koa();
1402
+ const isDev = process.env.NODE_ENV !== 'production';
1403
+
1404
+ // Logging solo in development
1405
+ if (isDev) {
1406
+ app.use(async (ctx, next) => {
1407
+ const start = Date.now();
1408
+ await next();
1409
+ console.log(`${ctx.method} ${ctx.url} - ${Date.now() - start}ms`);
1410
+ });
1411
+ }
1412
+
1413
+ // File server con configurazione ambiente-specifica
1414
+ app.use(koaClassicServer(path.join(__dirname, 'public'), {
1415
+ showDirContents: isDev, // Solo in dev
1416
+ index: 'index.html',
1417
+ urlPrefix: isDev ? '' : '/static', // Prefix solo in prod
1418
+ urlsReserved: ['/admin', '/config', '/.env'],
1419
+ template: {
1420
+ render: isDev ? devTemplateRender : prodTemplateRender,
1421
+ ext: ['ejs']
1422
+ }
1423
+ }));
1424
+
1425
+ // Error details solo in development
1426
+ app.on('error', (err, ctx) => {
1427
+ if (isDev) {
1428
+ console.error('Error details:', err);
1429
+ } else {
1430
+ console.error('Error:', err.message);
1431
+ }
1432
+ });
1433
+
1434
+ app.listen(process.env.PORT || 3000);
1435
+ ```
1436
+
1437
+ ---
1438
+
1439
+ ### 6. Testing
1440
+
1441
+ #### Testa Configurazioni Diverse
1442
+ ```javascript
1443
+ // __tests__/server.test.js
1444
+ const configs = [
1445
+ { name: 'base', options: {} },
1446
+ { name: 'with-prefix', options: { urlPrefix: '/public' } },
1447
+ { name: 'no-listing', options: { showDirContents: false } }
1448
+ ];
1449
+
1450
+ configs.forEach(({ name, options }) => {
1451
+ describe(`Config: ${name}`, () => {
1452
+ let server;
1453
+
1454
+ beforeAll(() => {
1455
+ const app = new Koa();
1456
+ app.use(koaClassicServer(testDir, options));
1457
+ server = app.listen();
1458
+ });
1459
+
1460
+ test('serves files', async () => {
1461
+ // test...
1462
+ });
1463
+
1464
+ afterAll(() => server.close());
1465
+ });
1466
+ });
1467
+ ```
1468
+
1469
+ ---
1470
+
1471
+ ### 7. Documentazione
1472
+
1473
+ #### Commenta Configurazioni Complesse
1474
+ ```javascript
1475
+ app.use(koaClassicServer(__dirname + '/public', {
1476
+ // Mostra directory solo in development per sicurezza
1477
+ showDirContents: process.env.NODE_ENV !== 'production',
1478
+
1479
+ // Protegge configurazioni sensibili
1480
+ // Nota: funziona solo per directory di primo livello
1481
+ urlsReserved: ['/config', '/private'],
1482
+
1483
+ // Template EJS per pagine dinamiche
1484
+ // Rendering con dati utente e query params
1485
+ template: {
1486
+ render: ejsRenderer,
1487
+ ext: ['ejs']
1488
+ }
1489
+ }));
1490
+ ```
1491
+
1492
+ ---
1493
+
1494
+ ## Informazioni Aggiuntive
1495
+
1496
+ ### Versione
1497
+ **1.1.0**
1498
+
1499
+ ### Autore
1500
+ **Italo Paesano**
1501
+
1502
+ ### Licenza
1503
+ **MIT**
1504
+
1505
+ ### Keywords
1506
+ - file
1507
+ - server
1508
+ - koa
1509
+ - middleware
1510
+ - static
1511
+ - apache
1512
+
1513
+ ### Compatibilità
1514
+
1515
+ #### Node.js
1516
+ - **Minimo:** 12.20+
1517
+ - **Raccomandato:** 14+
1518
+ - **Testato:** 14, 16, 18, 20
1519
+
1520
+ #### Koa
1521
+ - **Versione:** 2.x
1522
+
1523
+ ### Conditional Exports
1524
+
1525
+ Il modulo usa conditional exports per supportare sia CommonJS che ES Modules:
1526
+
1527
+ ```json
1528
+ {
1529
+ "main": "index.cjs",
1530
+ "exports": {
1531
+ "import": "./index.mjs",
1532
+ "require": "./index.cjs"
1533
+ }
1534
+ }
1535
+ ```
1536
+
1537
+ Questo permette:
1538
+ ```javascript
1539
+ // CommonJS
1540
+ const koaClassicServer = require('koa-classic-server');
1541
+
1542
+ // ES Modules
1543
+ import koaClassicServer from 'koa-classic-server';
1544
+ ```
1545
+
1546
+ ### Repository
1547
+
1548
+ Segnala bug o richiedi funzionalità tramite GitHub Issues.
1549
+
1550
+ ### Contributing
1551
+
1552
+ Contributi benvenuti! Per favore:
1553
+ 1. Fork del repository
1554
+ 2. Crea branch per feature (`git checkout -b feature/AmazingFeature`)
1555
+ 3. Commit modifiche (`git commit -m 'Add AmazingFeature'`)
1556
+ 4. Push al branch (`git push origin feature/AmazingFeature`)
1557
+ 5. Apri Pull Request
1558
+
1559
+ ### Changelog
1560
+
1561
+ #### v1.1.0
1562
+ - Versione attuale
1563
+ - Supporto conditional exports
1564
+ - Test suite completa
1565
+
1566
+ ---
1567
+
1568
+ ## Risorse Aggiuntive
1569
+
1570
+ ### Link Utili
1571
+
1572
+ - [Documentazione Koa](https://koajs.com/)
1573
+ - [MIME Types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types)
1574
+ - [Node.js Path Module](https://nodejs.org/api/path.html)
1575
+ - [EJS Documentation](https://ejs.co/)
1576
+
1577
+ ### Esempi Completi
1578
+
1579
+ Repository con esempi completi disponibile nella cartella `customTest/` del progetto.
1580
+
1581
+ ---
1582
+
1583
+ **Documentazione generata per koa-classic-server v1.1.0**
1584
+
1585
+ *Ultimo aggiornamento: 2025-11-17*