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,527 @@
1
+ # Index Option - Comportamento di Priorità
2
+
3
+ ## ⚠️ Formato Raccomandato: Array
4
+
5
+ **L'opzione `index` deve essere usata in formato array.**
6
+
7
+ ```javascript
8
+ // ✅ RACCOMANDATO (Array format)
9
+ index: ['index.html']
10
+ index: ['index.html', 'index.htm', 'default.html']
11
+ index: [/index\.html/i]
12
+
13
+ // ⚠️ DEPRECATO (String format - verrà rimosso in futuro)
14
+ index: 'index.html' // Genera un warning, usa ['index.html'] invece
15
+ ```
16
+
17
+ **Nota importante:** Il formato stringa (`index: 'index.html'`) è **deprecato** e verrà rimosso in versioni future. Quando viene usato, viene mostrato un warning in console:
18
+
19
+ ```
20
+ [koa-classic-server] DEPRECATION WARNING: Passing a string to the "index" option is deprecated...
21
+ Current usage: index: "index.html"
22
+ Recommended: index: ["index.html"]
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Principio Fondamentale: "First Match Wins"
28
+
29
+ L'opzione `index` utilizza il principio **"first match wins"** (primo match vince): l'array viene cercato **esattamente nell'ordine** specificato, e il **primo file trovato** viene servito.
30
+
31
+ ```javascript
32
+ index: [pattern1, pattern2, pattern3, ...]
33
+ ↑ ↑ ↑
34
+ PRIMO SECONDO TERZO
35
+ (priorità (priorità (priorità
36
+ massima) media) bassa)
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Comportamento di Ricerca
42
+
43
+ ### 1. Ordine di Ricerca Sequenziale
44
+
45
+ L'algoritmo cerca i file nell'ordine esatto dell'array: `[0] → [1] → [2] → [3] → ...`
46
+
47
+ ```javascript
48
+ index: ['index1.html', 'index2.html', 'index3.html']
49
+ ```
50
+
51
+ **Passi eseguiti:**
52
+ 1. Cerca `index1.html` nella directory
53
+ - ✅ Se trovato → **SERVE index1.html** (STOP, ignora index2 e index3)
54
+ - ❌ Se non trovato → Passa al passo 2
55
+
56
+ 2. Cerca `index2.html` nella directory
57
+ - ✅ Se trovato → **SERVE index2.html** (STOP, ignora index3)
58
+ - ❌ Se non trovato → Passa al passo 3
59
+
60
+ 3. Cerca `index3.html` nella directory
61
+ - ✅ Se trovato → **SERVE index3.html** (STOP)
62
+ - ❌ Se non trovato → Passa al passo 4
63
+
64
+ 4. Nessun file trovato → **Mostra directory listing** (se `showDirContents: true`)
65
+
66
+ ---
67
+
68
+ ## Comportamento con String
69
+
70
+ ### Esempio 1: Tutti i file presenti
71
+
72
+ ```javascript
73
+ // Directory contiene:
74
+ // - index1.html
75
+ // - index2.html
76
+ // - index3.html
77
+
78
+ app.use(koaClassicServer('./public', {
79
+ index: ['index1.html', 'index2.html', 'index3.html']
80
+ }));
81
+
82
+ // Risultato: Serve index1.html ✅
83
+ // Motivo: È il primo nell'array
84
+ ```
85
+
86
+ ### Esempio 2: Primo file mancante
87
+
88
+ ```javascript
89
+ // Directory contiene:
90
+ // - index2.html (index1.html NON esiste!)
91
+ // - index3.html
92
+
93
+ app.use(koaClassicServer('./public', {
94
+ index: ['index1.html', 'index2.html', 'index3.html']
95
+ }));
96
+
97
+ // Risultato: Serve index2.html ✅
98
+ // Motivo: index1.html non esiste, index2.html è il primo disponibile
99
+ ```
100
+
101
+ ### Esempio 3: Solo l'ultimo file presente
102
+
103
+ ```javascript
104
+ // Directory contiene:
105
+ // - index3.html (index1.html e index2.html NON esistono!)
106
+
107
+ app.use(koaClassicServer('./public', {
108
+ index: ['index1.html', 'index2.html', 'index3.html']
109
+ }));
110
+
111
+ // Risultato: Serve index3.html ✅
112
+ // Motivo: È l'unico disponibile nell'array
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Comportamento con RegExp
118
+
119
+ **LA PRIORITÀ FUNZIONA ALLO STESSO MODO CON LE REGEXP!**
120
+
121
+ ### Esempio 1: Tutte le RegExp matchano
122
+
123
+ ```javascript
124
+ // Directory contiene:
125
+ // - index1.html
126
+ // - index2.html
127
+ // - index3.html
128
+
129
+ app.use(koaClassicServer('./public', {
130
+ index: [
131
+ /index1\.html/i, // ← PRIMO pattern
132
+ /index2\.html/i, // ← SECONDO pattern
133
+ /index3\.html/i // ← TERZO pattern
134
+ ]
135
+ }));
136
+
137
+ // Risultato: Serve index1.html ✅
138
+ // Motivo: Il primo pattern /index1\.html/i matcha index1.html
139
+ ```
140
+
141
+ ### Esempio 2: Primo pattern non matcha
142
+
143
+ ```javascript
144
+ // Directory contiene:
145
+ // - index2.html (index1.html NON esiste!)
146
+ // - index3.html
147
+
148
+ app.use(koaClassicServer('./public', {
149
+ index: [
150
+ /index1\.html/i, // ← PRIMO pattern (non matcha nulla)
151
+ /index2\.html/i, // ← SECONDO pattern (matcha!)
152
+ /index3\.html/i // ← TERZO pattern
153
+ ]
154
+ }));
155
+
156
+ // Risultato: Serve index2.html ✅
157
+ // Motivo: Primo pattern non matcha, secondo pattern matcha index2.html
158
+ ```
159
+
160
+ ### Esempio 3: Pattern largo prima di pattern stretto
161
+
162
+ ```javascript
163
+ // Directory contiene:
164
+ // - index.html
165
+ // - index.htm
166
+ // - default.html
167
+
168
+ app.use(koaClassicServer('./public', {
169
+ index: [
170
+ /index\.(html|htm)/i, // ← Pattern LARGO (matcha .html E .htm)
171
+ /default\.html/i // ← Pattern STRETTO (mai raggiunto!)
172
+ ]
173
+ }));
174
+
175
+ // Risultato: Serve index.html O index.htm ✅
176
+ // Motivo: Primo pattern matcha, secondo pattern viene ignorato
177
+ // ⚠️ ATTENZIONE: default.html NON verrà MAI servito!
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Comportamento con Mixed Array (String + RegExp)
183
+
184
+ Stesso principio: l'ordine dell'array determina la priorità, **indipendentemente dal tipo**.
185
+
186
+ ### Esempio 1: String prima di RegExp
187
+
188
+ ```javascript
189
+ // Directory contiene:
190
+ // - index.html
191
+ // - INDEX.HTML (case diverso!)
192
+
193
+ app.use(koaClassicServer('./public', {
194
+ index: [
195
+ 'index.html', // ← String (case-sensitive, exact match)
196
+ /INDEX\.HTML/i // ← RegExp (case-insensitive)
197
+ ]
198
+ }));
199
+
200
+ // Risultato: Serve index.html ✅ (se esiste)
201
+ // Se index.html non esiste → Serve INDEX.HTML ✅
202
+ ```
203
+
204
+ ### Esempio 2: RegExp prima di String
205
+
206
+ ```javascript
207
+ // Directory contiene:
208
+ // - INDEX.HTML (maiuscolo)
209
+ // - index.html (minuscolo)
210
+
211
+ app.use(koaClassicServer('./public', {
212
+ index: [
213
+ /index\.html/i, // ← RegExp (case-insensitive, primo!)
214
+ 'index.html' // ← String (esatto, secondo)
215
+ ]
216
+ }));
217
+
218
+ // Risultato: Serve INDEX.HTML o index.html ✅
219
+ // (dipende da quale il filesystem restituisce per primo)
220
+ // La stringa 'index.html' potrebbe non essere mai raggiunta!
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Pattern Matching e Filesystem Order
226
+
227
+ ⚠️ **IMPORTANTE:** Quando una RegExp matcha **multipli file**, viene servito il **primo file trovato nell'ordine del filesystem**, che NON è garantito essere deterministico.
228
+
229
+ ### Esempio: RegExp matcha multipli file
230
+
231
+ ```javascript
232
+ // Directory contiene:
233
+ // - INDEX.HTML
234
+ // - Index.Html
235
+ // - index.html
236
+
237
+ app.use(koaClassicServer('./public', {
238
+ index: [/index\.html/i] // Matcha tutti e 3 i file!
239
+ }));
240
+
241
+ // Risultato: Uno dei tre file (ordine NON garantito) ⚠️
242
+ // Soluzione: Usa stringhe esatte per controllo deterministico
243
+ ```
244
+
245
+ ### Soluzione Deterministica
246
+
247
+ ```javascript
248
+ // Per controllo esatto dell'ordine, usa stringhe:
249
+ app.use(koaClassicServer('./public', {
250
+ index: [
251
+ 'index.html', // 1. Cerca esattamente questo
252
+ 'Index.Html', // 2. Poi questo
253
+ 'INDEX.HTML', // 3. Infine questo
254
+ /index\.html/i // 4. Fallback per altri casi
255
+ ]
256
+ }));
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Best Practices
262
+
263
+ ### ✅ Raccomandazione 1: Specifico prima, generico dopo
264
+
265
+ ```javascript
266
+ // ✓ CORRETTO
267
+ index: [
268
+ 'index.html', // Specifico: exact match (veloce)
269
+ /INDEX\.HTML/i, // Meno specifico: case-insensitive
270
+ /index\.(html|htm)/i, // Generico: multiple estensioni
271
+ /default\.html/i // Fallback: file alternativo
272
+ ]
273
+
274
+ // ✗ SBAGLIATO
275
+ index: [
276
+ /index\.(html|htm)/i, // Troppo generico per primo!
277
+ 'index.html' // Mai raggiunto se .htm esiste!
278
+ ]
279
+ ```
280
+
281
+ ### ✅ Raccomandazione 2: Performance - String prima di RegExp
282
+
283
+ ```javascript
284
+ // ✓ OTTIMIZZATO (String = O(1), RegExp = O(n))
285
+ index: [
286
+ 'index.html', // O(1) lookup
287
+ 'index.htm', // O(1) lookup
288
+ /INDEX\.HTML/i, // O(n) regex match
289
+ /default\.html/i // O(n) regex match
290
+ ]
291
+
292
+ // ✗ LENTO
293
+ index: [
294
+ /index\.html/i, // O(n) regex match per primo
295
+ /index\.htm/i, // O(n) regex match
296
+ 'index.html' // O(1) ma mai raggiunto!
297
+ ]
298
+ ```
299
+
300
+ ### ✅ Raccomandazione 3: Evita pattern troppo ampi all'inizio
301
+
302
+ ```javascript
303
+ // ✗ PROBLEMATICO
304
+ index: [
305
+ /.*\.html/i, // Matcha QUALSIASI .html (troppo largo!)
306
+ 'index.html' // Mai raggiunto!
307
+ ]
308
+
309
+ // ✓ CORRETTO
310
+ index: [
311
+ 'index.html', // Prima cerca file specifico
312
+ /^index\./i, // Poi file che iniziano con "index."
313
+ /.*\.html/i // Solo come ultimo fallback
314
+ ]
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Diagramma di Flusso
320
+
321
+ ```
322
+ ┌─────────────────────────────┐
323
+ │ Richiesta GET / │
324
+ └──────────┬──────────────────┘
325
+
326
+
327
+ ┌─────────────────────────────┐
328
+ │ Leggi directory │
329
+ └──────────┬──────────────────┘
330
+
331
+
332
+ ┌─────────────────────────────┐
333
+ │ Pattern [0] (primo) │
334
+ └──────────┬──────────────────┘
335
+
336
+
337
+ ┌─────┴─────┐
338
+ │ Matcha? │
339
+ └─────┬─────┘
340
+
341
+ ┌─────┴─────┐
342
+ │ │
343
+ SI NO
344
+ │ │
345
+ │ ▼
346
+ │ ┌───────────────┐
347
+ │ │ Pattern [1] │
348
+ │ └───────┬───────┘
349
+ │ │
350
+ │ ▼
351
+ │ ┌────┴────┐
352
+ │ │ Matcha? │
353
+ │ └────┬────┘
354
+ │ │
355
+ │ ┌────┴────┐
356
+ │ │ │
357
+ │ SI NO
358
+ │ │ │
359
+ │ │ ▼
360
+ │ │ ┌───────────┐
361
+ │ │ │Pattern [2]│
362
+ │ │ └─────┬─────┘
363
+ │ │ │
364
+ │ │ ...
365
+ │ │ │
366
+ │ │ ▼
367
+ │ │ ┌───────────────┐
368
+ │ │ │ Nessun match │
369
+ │ │ └───────┬───────┘
370
+ │ │ │
371
+ │ │ ▼
372
+ │ │ ┌────────────────┐
373
+ │ │ │ Directory │
374
+ │ │ │ listing │
375
+ │ │ └────────────────┘
376
+ │ │
377
+ ▼ ▼
378
+ ┌──────────────────┐
379
+ │ Serve il file │
380
+ │ STOP (ignora │
381
+ │ pattern restanti)│
382
+ └──────────────────┘
383
+ ```
384
+
385
+ ---
386
+
387
+ ## Codice di Implementazione
388
+
389
+ Il comportamento di priorità è implementato nel file `index.cjs` alla funzione `findIndexFile()` (linee 205-250):
390
+
391
+ ```javascript
392
+ // Search with priority order (first pattern wins)
393
+ for (const pattern of indexPatterns) {
394
+ let matchedFile = null;
395
+
396
+ if (typeof pattern === 'string') {
397
+ // Exact string match (case-sensitive)
398
+ if (fileNames.includes(pattern)) {
399
+ matchedFile = pattern;
400
+ }
401
+ } else if (pattern instanceof RegExp) {
402
+ // RegExp match (supports case-insensitive with /i flag)
403
+ matchedFile = fileNames.find(fileName => pattern.test(fileName));
404
+ }
405
+
406
+ // If match found, verify it's a file and return it
407
+ if (matchedFile) {
408
+ // ... verifica e return
409
+ return { name: matchedFile, stat: fileStat };
410
+ }
411
+ }
412
+
413
+ // No match found
414
+ return null;
415
+ ```
416
+
417
+ **Punti chiave:**
418
+ - `for (const pattern of indexPatterns)` → Itera in ordine sequenziale
419
+ - `if (matchedFile) { return ... }` → **STOP al primo match**
420
+ - Pattern successivi vengono **ignorati completamente**
421
+
422
+ ---
423
+
424
+ ## Test Suite
425
+
426
+ Il comportamento di priorità è verificato da **24 test** in `__tests__/index-option.test.js`:
427
+
428
+ ### Test con String:
429
+ - ✅ `Priority order - index1.html searched before index2.html`
430
+ - ✅ `Priority order - index2.html served when index1.html missing`
431
+ - ✅ `First match wins - index.html over index.htm`
432
+ - ✅ `First match wins - index.htm when index.html missing`
433
+
434
+ ### Test con RegExp:
435
+ - ✅ `Priority order - First RegExp pattern searched before second`
436
+ - ✅ `Priority order - Second RegExp when first does not match`
437
+ - ✅ `Priority order - Broader pattern searched before narrower pattern`
438
+
439
+ ### Test con Mixed Array:
440
+ - ✅ `Priority: String before RegExp`
441
+ - ✅ `Falls back to RegExp when string doesn't match`
442
+ - ✅ `Complex example: Mixed priorities`
443
+
444
+ ---
445
+
446
+ ## Esempi Reali
447
+
448
+ ### Configurazione Apache-like
449
+
450
+ ```javascript
451
+ app.use(koaClassicServer('./public', {
452
+ index: [
453
+ 'index.html', // 1. Standard HTML
454
+ 'index.htm', // 2. Legacy HTML
455
+ 'index.php', // 3. PHP (se processato)
456
+ /index\.shtml/i, // 4. Server-side includes
457
+ 'default.html' // 5. Fallback
458
+ ]
459
+ }));
460
+ ```
461
+
462
+ ### Configurazione Template Engine
463
+
464
+ ```javascript
465
+ app.use(koaClassicServer('./views', {
466
+ index: [
467
+ 'index.ejs', // 1. EJS template (priorità)
468
+ 'index.pug', // 2. Pug template
469
+ /index\.html/i, // 3. HTML statico
470
+ 'index.htm' // 4. Legacy fallback
471
+ ]
472
+ }));
473
+ ```
474
+
475
+ ### Configurazione Multilingua
476
+
477
+ ```javascript
478
+ app.use(koaClassicServer('./public', {
479
+ index: [
480
+ 'index_it.html', // 1. Italiano
481
+ 'index_en.html', // 2. Inglese
482
+ /index_[a-z]{2}\.html/i, // 3. Altre lingue
483
+ 'index.html' // 4. Default
484
+ ]
485
+ }));
486
+ ```
487
+
488
+ ---
489
+
490
+ ## FAQ
491
+
492
+ ### Q: L'ordine conta anche con le RegExp?
493
+ **A:** Sì! Le RegExp seguono **esattamente lo stesso comportamento** delle stringhe. Primo match vince, sempre.
494
+
495
+ ### Q: Cosa succede se più file matchano la stessa RegExp?
496
+ **A:** Viene servito il primo file trovato nell'ordine del filesystem (non deterministico). Usa stringhe esatte per controllo preciso.
497
+
498
+ ### Q: Posso mixare stringhe e RegExp?
499
+ **A:** Assolutamente sì! L'ordine dell'array determina la priorità, indipendentemente dal tipo.
500
+
501
+ ### Q: Qual è più veloce: string o RegExp?
502
+ **A:** Le stringhe sono **molto più veloci** (O(1) vs O(n)). Metti sempre le stringhe prima delle RegExp.
503
+
504
+ ### Q: Cosa succede se nessun pattern matcha?
505
+ **A:** Se `showDirContents: true`, mostra directory listing. Altrimenti restituisce 404.
506
+
507
+ ---
508
+
509
+ ## Riepilogo
510
+
511
+ | Aspetto | Comportamento |
512
+ |---------|--------------|
513
+ | **Ordine di ricerca** | Sequenziale: `[0] → [1] → [2] → ...` |
514
+ | **Quando si ferma** | Al **primo match** trovato |
515
+ | **String vs RegExp** | **Stesso comportamento** di priorità |
516
+ | **Mixed array** | Tipo **irrilevante**, conta solo l'ordine |
517
+ | **Performance** | String (O(1)) >> RegExp (O(n)) |
518
+ | **Deterministico** | String: sì, RegExp multipli: no |
519
+ | **Fallback** | Directory listing o 404 |
520
+
521
+ ---
522
+
523
+ **Documentazione tecnica:** `index.cjs:28-48` (opzioni), `index.cjs:205-250` (implementazione)
524
+
525
+ **Test suite:** `__tests__/index-option.test.js` (24 test cases)
526
+
527
+ **Esempi pratici:** `EXAMPLES_INDEX_OPTION.md`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 italopaesano
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.