koa-classic-server 3.0.0 → 3.1.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.
Files changed (105) hide show
  1. package/README.md +44 -120
  2. package/index.cjs +236 -21
  3. package/package.json +10 -5
  4. package/.github/workflows/npm-publish.yml +0 -98
  5. package/CLAUDE.md +0 -101
  6. package/__tests__/benchmark-results-baseline-v1.2.0.txt +0 -354
  7. package/__tests__/benchmark-results-optimized-v2.0.0.txt +0 -354
  8. package/__tests__/benchmark-results-v3.0.0.txt +0 -372
  9. package/__tests__/benchmark.js +0 -239
  10. package/__tests__/caching-headers.test.js +0 -556
  11. package/__tests__/compression-fixtures/data.json +0 -1
  12. package/__tests__/compression-fixtures/large.txt +0 -1
  13. package/__tests__/compression-fixtures/small.txt +0 -1
  14. package/__tests__/compression.test.js +0 -284
  15. package/__tests__/customTest/README.md +0 -6
  16. package/__tests__/customTest/loadConfig.util.js +0 -41
  17. package/__tests__/customTest/serversToLoad.util.js +0 -93
  18. package/__tests__/demo-regex-index.js +0 -140
  19. package/__tests__/deprecation-warnings.test.js +0 -105
  20. package/__tests__/directory-sorting-links.test.js +0 -135
  21. package/__tests__/dt-unknown.test.js +0 -635
  22. package/__tests__/ejs.test.js +0 -299
  23. package/__tests__/hidden-fixtures/.dot-dir/inside.txt +0 -1
  24. package/__tests__/hidden-fixtures/.env +0 -2
  25. package/__tests__/hidden-fixtures/.well-known/acme-challenge.txt +0 -1
  26. package/__tests__/hidden-fixtures/config/secrets/password.txt +0 -1
  27. package/__tests__/hidden-fixtures/data.key +0 -1
  28. package/__tests__/hidden-fixtures/file.secret +0 -1
  29. package/__tests__/hidden-fixtures/index.html +0 -1
  30. package/__tests__/hidden-fixtures/normal.txt +0 -1
  31. package/__tests__/hidden-fixtures/subdir/.env +0 -1
  32. package/__tests__/hidden-fixtures/subdir/regular.txt +0 -1
  33. package/__tests__/hidden-option.test.js +0 -407
  34. package/__tests__/hideExtension.test.js +0 -607
  35. package/__tests__/index-option.test.js +0 -449
  36. package/__tests__/index.test.js +0 -345
  37. package/__tests__/listing.test.js +0 -437
  38. package/__tests__/logger.test.js +0 -232
  39. package/__tests__/performance.test.js +0 -370
  40. package/__tests__/publicWwwTest/cartella/sottocartella/ciao.html +0 -11
  41. package/__tests__/publicWwwTest/cartella/sottocartella/provaEjs/testEjs.ejs +0 -11
  42. package/__tests__/publicWwwTest/cartella vuota con spazi nel nome/file con spazio nel nome .txt +0 -1
  43. package/__tests__/publicWwwTest/cartella vuota con spazi nel nome /file con spazio nel nome .txt +0 -1
  44. package/__tests__/publicWwwTest/ejs-templates/complex.ejs +0 -56
  45. package/__tests__/publicWwwTest/ejs-templates/index.ejs +0 -30
  46. package/__tests__/publicWwwTest/ejs-templates/simple.ejs +0 -13
  47. package/__tests__/publicWwwTest/ejs-templates/with-conditional.ejs +0 -28
  48. package/__tests__/publicWwwTest/ejs-templates/with-escaping.ejs +0 -26
  49. package/__tests__/publicWwwTest/ejs-templates/with-loop.ejs +0 -16
  50. package/__tests__/publicWwwTest/hideext-test/about/index.html +0 -1
  51. package/__tests__/publicWwwTest/hideext-test/about.EJS +0 -1
  52. package/__tests__/publicWwwTest/hideext-test/about.ejs +0 -2
  53. package/__tests__/publicWwwTest/hideext-test/blog/articolo.ejs +0 -2
  54. package/__tests__/publicWwwTest/hideext-test/conflict-test/pagina +0 -1
  55. package/__tests__/publicWwwTest/hideext-test/conflict-test/pagina.ejs +0 -1
  56. package/__tests__/publicWwwTest/hideext-test/file.ejs.bak +0 -1
  57. package/__tests__/publicWwwTest/hideext-test/index.ejs +0 -2
  58. package/__tests__/publicWwwTest/hideext-test/photo.txt +0 -1
  59. package/__tests__/publicWwwTest/hideext-test/sezione/index.ejs +0 -1
  60. package/__tests__/publicWwwTest/hideext-test/style.css +0 -1
  61. package/__tests__/publicWwwTest/ile_vuoto.txt +0 -0
  62. package/__tests__/publicWwwTest/index.html +0 -11
  63. package/__tests__/publicWwwTest/prova file .txt +0 -2
  64. package/__tests__/publicWwwTest/semplicetxt.txt +0 -1
  65. package/__tests__/publicWwwTest/test-page.html +0 -1
  66. package/__tests__/publicWwwTest/test.txt +0 -1
  67. package/__tests__/range-fixtures/sample.txt +0 -1
  68. package/__tests__/range.test.js +0 -223
  69. package/__tests__/security-headers.test.js +0 -165
  70. package/__tests__/security.test.js +0 -322
  71. package/__tests__/server-cache-fixtures/large.txt +0 -1
  72. package/__tests__/server-cache-fixtures/small.txt +0 -1
  73. package/__tests__/server-cache.test.js +0 -594
  74. package/__tests__/setup-benchmark.js +0 -178
  75. package/__tests__/symlink.test.js +0 -441
  76. package/__tests__/template-timeout.test.js +0 -321
  77. package/__tests__/test-regex-quick.js +0 -158
  78. package/__tests__/useOriginalUrl.test.js +0 -213
  79. package/docs/ACTION_PLAN.md +0 -293
  80. package/docs/BENCHMARKS.md +0 -317
  81. package/docs/CHANGELOG.md +0 -880
  82. package/docs/CODE_REVIEW.md +0 -300
  83. package/docs/DEBUG_REPORT.md +0 -593
  84. package/docs/DOCUMENTATION.md +0 -1864
  85. package/docs/EXAMPLES_INDEX_OPTION.md +0 -395
  86. package/docs/FLOW_DIAGRAM.md +0 -954
  87. package/docs/INDEX_OPTION_PRIORITY.md +0 -527
  88. package/docs/OPTIMIZATION_HTTP_CACHING.md +0 -689
  89. package/docs/OPTIMIZATION_ROADMAP_for_V3.md +0 -864
  90. package/docs/PERFORMANCE_ANALYSIS.md +0 -839
  91. package/docs/PERFORMANCE_COMPARISON.md +0 -388
  92. package/docs/noteExports.md +0 -148
  93. package/docs/security_improvement_for_V3.md +0 -421
  94. package/docs/template-engine/TEMPLATE_ENGINE_GUIDE.md +0 -1734
  95. package/docs/template-engine/esempi-incrementali.js +0 -192
  96. package/docs/template-engine/examples/esempio1-nessun-dato.ejs +0 -12
  97. package/docs/template-engine/examples/esempio2-una-variabile.ejs +0 -11
  98. package/docs/template-engine/examples/esempio3-piu-variabili.ejs +0 -15
  99. package/docs/template-engine/examples/esempio4-condizionale.ejs +0 -18
  100. package/docs/template-engine/examples/esempio5-loop.ejs +0 -18
  101. package/docs/template-engine/examples/index-esempi.html +0 -181
  102. package/docs/template-engine/examples/index.html +0 -40
  103. package/docs/template-engine/examples/test.ejs +0 -64
  104. package/eslint.config.mjs +0 -17
  105. package/jest.config.js +0 -18
@@ -1,284 +0,0 @@
1
- const supertest = require('supertest');
2
- const koaClassicServer = require('../index.cjs');
3
- const Koa = require('koa');
4
- const path = require('path');
5
-
6
- const root = path.join(__dirname, 'compression-fixtures');
7
-
8
- // Fixtures:
9
- // large.txt — 2000 bytes of 'A' (text/plain, exceeds 1KB threshold)
10
- // small.txt — 4 bytes of 'tiny' (text/plain, below 1KB threshold)
11
- // data.json — 16 bytes '{"key":"value"}\n' (application/json, below threshold)
12
-
13
- function createApp(opts = {}) {
14
- const app = new Koa();
15
- app.use(koaClassicServer(root, { dirListing: { enabled: false }, ...opts }));
16
- return app.listen();
17
- }
18
-
19
- // ─── Default behaviour (compression enabled, serverCache enabled) ─────────────
20
-
21
- describe('Compression — default: br preferred', () => {
22
- let server;
23
- beforeAll(() => { server = createApp(); });
24
- afterAll(() => server.close());
25
-
26
- test('large.txt with Accept-Encoding: br → brotli compressed', async () => {
27
- const res = await supertest(server)
28
- .get('/large.txt')
29
- .set('Accept-Encoding', 'br');
30
- expect(res.status).toBe(200);
31
- expect(res.headers['content-encoding']).toBe('br');
32
- expect(res.headers['vary']).toBe('Accept-Encoding');
33
- });
34
-
35
- test('large.txt with Accept-Encoding: gzip → gzip compressed, body decompressed by supertest', async () => {
36
- const res = await supertest(server)
37
- .get('/large.txt')
38
- .set('Accept-Encoding', 'gzip');
39
- expect(res.status).toBe(200);
40
- expect(res.headers['content-encoding']).toBe('gzip');
41
- expect(res.headers['vary']).toBe('Accept-Encoding');
42
- // supertest auto-decompresses gzip — res.text is the original content
43
- expect(res.text).toBe('A'.repeat(2000));
44
- });
45
-
46
- test('large.txt with Accept-Encoding: br,gzip → br preferred (higher priority)', async () => {
47
- const res = await supertest(server)
48
- .get('/large.txt')
49
- .set('Accept-Encoding', 'br, gzip');
50
- expect(res.headers['content-encoding']).toBe('br');
51
- });
52
-
53
- // Use 'identity' to prevent supertest's default Accept-Encoding from triggering compression
54
- test('large.txt with Accept-Encoding: identity → uncompressed', async () => {
55
- const res = await supertest(server)
56
- .get('/large.txt')
57
- .set('Accept-Encoding', 'identity');
58
- expect(res.status).toBe(200);
59
- expect(res.headers['content-encoding']).toBeUndefined();
60
- expect(res.headers['vary']).toBeUndefined();
61
- expect(res.text).toBe('A'.repeat(2000));
62
- });
63
-
64
- test('Content-Length present on compressed response (serverCache)', async () => {
65
- const res = await supertest(server)
66
- .get('/large.txt')
67
- .set('Accept-Encoding', 'gzip');
68
- expect(res.headers['content-length']).toBeDefined();
69
- expect(Number(res.headers['content-length'])).toBeGreaterThan(0);
70
- });
71
-
72
- test('Compressed Content-Length is smaller than original file size', async () => {
73
- const res = await supertest(server)
74
- .get('/large.txt')
75
- .set('Accept-Encoding', 'gzip');
76
- expect(Number(res.headers['content-length'])).toBeLessThan(2000);
77
- });
78
- });
79
-
80
- // ─── Threshold: files below threshold are served uncompressed ────────────────
81
-
82
- describe('Compression — threshold (default 1024 bytes)', () => {
83
- let server;
84
- beforeAll(() => { server = createApp(); });
85
- afterAll(() => server.close());
86
-
87
- test('small.txt (4 bytes) below threshold → no compression', async () => {
88
- const res = await supertest(server)
89
- .get('/small.txt')
90
- .set('Accept-Encoding', 'br, gzip');
91
- expect(res.status).toBe(200);
92
- expect(res.headers['content-encoding']).toBeUndefined();
93
- expect(res.text).toBe('tiny');
94
- });
95
-
96
- test('large.txt (2000 bytes) above threshold → compressed', async () => {
97
- const res = await supertest(server)
98
- .get('/large.txt')
99
- .set('Accept-Encoding', 'gzip');
100
- expect(res.headers['content-encoding']).toBe('gzip');
101
- });
102
-
103
- test('minFileSize: false → compress regardless of size', async () => {
104
- const s = createApp({ compression: { minFileSize: false } });
105
- const res = await supertest(s)
106
- .get('/small.txt')
107
- .set('Accept-Encoding', 'gzip');
108
- s.close();
109
- expect(res.headers['content-encoding']).toBe('gzip');
110
- // supertest auto-decompresses gzip
111
- expect(res.text).toBe('tiny');
112
- });
113
- });
114
-
115
- // ─── compression: false shorthand ────────────────────────────────────────────
116
-
117
- describe('Compression — disabled', () => {
118
- let server;
119
- beforeAll(() => { server = createApp({ compression: false }); });
120
- afterAll(() => server.close());
121
-
122
- test('compression: false → no compression on any file', async () => {
123
- const res = await supertest(server)
124
- .get('/large.txt')
125
- .set('Accept-Encoding', 'br, gzip');
126
- expect(res.status).toBe(200);
127
- expect(res.headers['content-encoding']).toBeUndefined();
128
- expect(res.text).toBe('A'.repeat(2000));
129
- });
130
- });
131
-
132
- // ─── encodings configuration ──────────────────────────────────────────────────
133
-
134
- describe('Compression — encodings configuration', () => {
135
- test('encodings: [gzip] → no brotli even if client prefers br', async () => {
136
- const s = createApp({ compression: { encodings: ['gzip'] } });
137
- const res = await supertest(s)
138
- .get('/large.txt')
139
- .set('Accept-Encoding', 'br, gzip');
140
- s.close();
141
- expect(res.headers['content-encoding']).toBe('gzip');
142
- });
143
-
144
- test('encodings: [] → no compression', async () => {
145
- const s = createApp({ compression: { encodings: [] } });
146
- const res = await supertest(s)
147
- .get('/large.txt')
148
- .set('Accept-Encoding', 'br, gzip');
149
- s.close();
150
- expect(res.headers['content-encoding']).toBeUndefined();
151
- });
152
- });
153
-
154
- // ─── mimeTypes configuration ──────────────────────────────────────────────────
155
-
156
- describe('Compression — mimeTypes configuration', () => {
157
- test('custom mimeTypes replaces default list', async () => {
158
- // Only compress application/json; text/plain should not be compressed
159
- const s = createApp({ compression: { mimeTypes: ['application/json'], threshold: false } });
160
-
161
- const resTxt = await supertest(s)
162
- .get('/large.txt')
163
- .set('Accept-Encoding', 'gzip');
164
- expect(resTxt.headers['content-encoding']).toBeUndefined();
165
-
166
- s.close();
167
- });
168
- });
169
-
170
- // ─── ETag encoding-specific ───────────────────────────────────────────────────
171
-
172
- describe('Compression — encoding-specific ETag', () => {
173
- let server;
174
- beforeAll(() => { server = createApp({ browserCacheEnabled: true }); });
175
- afterAll(() => server.close());
176
-
177
- test('ETag for br response has -br suffix', async () => {
178
- const res = await supertest(server)
179
- .get('/large.txt')
180
- .set('Accept-Encoding', 'br');
181
- expect(res.headers['etag']).toMatch(/-br"$/);
182
- });
183
-
184
- test('ETag for gzip response has -gz suffix', async () => {
185
- const res = await supertest(server)
186
- .get('/large.txt')
187
- .set('Accept-Encoding', 'gzip');
188
- expect(res.headers['etag']).toMatch(/-gz"$/);
189
- });
190
-
191
- test('ETag for uncompressed response has no suffix', async () => {
192
- // Use 'identity' to prevent supertest's default Accept-Encoding from triggering compression
193
- const res = await supertest(server)
194
- .get('/large.txt')
195
- .set('Accept-Encoding', 'identity');
196
- expect(res.headers['etag']).not.toMatch(/-(br|gz)"$/);
197
- });
198
-
199
- test('304 returned when If-None-Match matches encoding-specific ETag', async () => {
200
- const first = await supertest(server)
201
- .get('/large.txt')
202
- .set('Accept-Encoding', 'gzip');
203
- const etag = first.headers['etag'];
204
-
205
- const second = await supertest(server)
206
- .get('/large.txt')
207
- .set('Accept-Encoding', 'gzip')
208
- .set('If-None-Match', etag);
209
- expect(second.status).toBe(304);
210
- });
211
-
212
- test('304 NOT returned when ETag suffix differs (br ETag sent with gzip request)', async () => {
213
- const brRes = await supertest(server)
214
- .get('/large.txt')
215
- .set('Accept-Encoding', 'br');
216
- const brEtag = brRes.headers['etag'];
217
-
218
- // Send the br ETag but request gzip → ETag mismatch → 200
219
- const gzipRes = await supertest(server)
220
- .get('/large.txt')
221
- .set('Accept-Encoding', 'gzip')
222
- .set('If-None-Match', brEtag);
223
- expect(gzipRes.status).toBe(200);
224
- });
225
- });
226
-
227
- // ─── serverCache.compressedFile disabled (streaming mode) ────────────────────
228
-
229
- describe('Compression — serverCache.compressedFile disabled (streaming)', () => {
230
- let server;
231
- beforeAll(() => {
232
- server = createApp({ serverCache: { compressedFile: { enabled: false } } });
233
- });
234
- afterAll(() => server.close());
235
-
236
- test('streaming: Content-Encoding set but no Content-Length', async () => {
237
- const res = await supertest(server)
238
- .get('/large.txt')
239
- .set('Accept-Encoding', 'gzip');
240
- expect(res.headers['content-encoding']).toBe('gzip');
241
- // Streaming compressed responses use Transfer-Encoding: chunked → no Content-Length
242
- expect(res.headers['content-length']).toBeUndefined();
243
- });
244
-
245
- test('streaming: response body is correctly decompressed', async () => {
246
- const res = await supertest(server)
247
- .get('/large.txt')
248
- .set('Accept-Encoding', 'gzip');
249
- // supertest auto-decompresses gzip — res.text is the original content
250
- expect(res.text).toBe('A'.repeat(2000));
251
- });
252
- });
253
-
254
- // ─── Compression does not apply to Range requests ────────────────────────────
255
-
256
- describe('Compression — no compression on Range requests (HTTP 206)', () => {
257
- let server;
258
- beforeAll(() => { server = createApp(); });
259
- afterAll(() => server.close());
260
-
261
- test('Range request is served uncompressed even with Accept-Encoding', async () => {
262
- const res = await supertest(server)
263
- .get('/large.txt')
264
- .set('Range', 'bytes=0-9')
265
- .set('Accept-Encoding', 'br, gzip');
266
- expect(res.status).toBe(206);
267
- expect(res.headers['content-encoding']).toBeUndefined();
268
- expect(res.text).toBe('A'.repeat(10));
269
- });
270
- });
271
-
272
- // ─── V3 migration guard ───────────────────────────────────────────────────────
273
-
274
- describe('Compression — V3 migration guard (old name throws helpful error)', () => {
275
- test('compression.minSize throws with hint pointing to minFileSize', () => {
276
- expect(() => koaClassicServer(root, { compression: { minSize: 2048 } }))
277
- .toThrow(/options\.compression\.minSize was renamed[\s\S]*compression: \{ minFileSize: 2048 \}/);
278
- });
279
-
280
- test('compression.minSize === false also throws (legacy "no minimum" sentinel)', () => {
281
- expect(() => koaClassicServer(root, { compression: { minSize: false } }))
282
- .toThrow(/options\.compression\.minSize was renamed/);
283
- });
284
- });
@@ -1,6 +0,0 @@
1
- ## questi ile servono per testare manualmente le vari conigurazioni
2
-
3
- # digitando :
4
- ### npm run loadConfig
5
- # potrai sceglire quale configurazione usare per caricare il server e testatarlo la directory puntata ĆØ la stessa contenuta nella directory __tests__
6
- # cioĆØ __tests__/publicWwwTest
@@ -1,41 +0,0 @@
1
- // questo script permetterĆ  di scegliere la conigurazione con cui lanciare koa-classic-server al ine di essere testato
2
- // ci sarĆ  una lista interattive di configurazione fra cui scegliere :
3
-
4
- // chooseCon.js
5
- const { createServer, configurations } = require('../customTest/serversToLoad.util');
6
- const inquirer = require('inquirer').default;
7
-
8
- async function chooseConfiguration() {
9
- // Costruiamo le scelte dall'array delle configurazioni
10
- const choices = configurations.map(config => ({
11
- name: `${config.name}: ${config.description}`,
12
- value: config.name,
13
- }));
14
-
15
- const answers = await inquirer.prompt([
16
- {
17
- type: 'list',
18
- name: 'configName',
19
- message: 'Seleziona la configurazione da visualizzare:',
20
- choices: choices,
21
- },
22
- ]);
23
-
24
- return answers.configName;
25
- }
26
-
27
- async function main() {
28
- const configName = await chooseConfiguration();
29
- const app = createServer(configName);
30
- const port = process.env.PORT || 3000;
31
-
32
- app.listen(port, () => {
33
- console.log(`Server avviato su http://localhost:${port} con configurazione "${configName}"`);
34
- });
35
- }
36
-
37
- main().catch(error => {
38
- console.error('Errore durante l\'esecuzione:', error);
39
- process.exit(1);
40
- });
41
-
@@ -1,93 +0,0 @@
1
- // quivi sarnno elencato in un array le varie possibili configurzioni di koaClassicServer in modo po sa poterle testare
2
-
3
- // servers.js
4
- const Koa = require('koa');
5
- const koaClassicServer = require('../index.cjs');
6
- const { join } = require('path');
7
-
8
- /**
9
- * Array (o raccolta) delle configurazioni disponibili.
10
- * Aggiungendo nuovi oggetti qui potrai testare ulteriori configurazioni.
11
- */
12
- rootDir = join(__dirname, '../' , '__tests__','publicWwwTest');
13
- console.log( 'rootDir', rootDir);
14
- const configurations = [
15
- {
16
- name: 'test generico',
17
- description: ' urlPrefix: \',',
18
- // Per i test, i file da servire sono quelli della cartella __tests__/publicWwwTest
19
- rootDir: rootDir,
20
- options: {
21
- //urlPrefix: '/',
22
- method: ['GET'],
23
- dirListing: { enabled: true },
24
- },
25
- },
26
- {
27
- name: 'test indicando l\'index file ',
28
- description: "nelle urlPrefix: \'/public\', : //index: 'index.html',",
29
- // In produzione potresti servire i file dalla cartella "public"
30
- rootDir: rootDir,
31
- options: {
32
- urlPrefix: '/public',
33
- method: ['GET'],
34
- dirListing: { enabled: true },
35
- index: ['index.html'],
36
- },
37
- },
38
- {
39
- name: 'test generico',
40
- description: ' urlPrefix: \'/public\',',
41
- // Per i test, i file da servire sono quelli della cartella __tests__/publicWwwTest
42
- rootDir: rootDir,
43
- options: {
44
- urlPrefix: '/public',
45
- method: ['GET'],
46
- dirListing: { enabled: true },
47
- },
48
- },
49
- {
50
- name: 'test con percorso riservato ',
51
- description: "urlsReserved : Array('percorso_riservato/', 'percorso riservato con spazi')",
52
- // Per i test, i file da servire sono quelli della cartella __tests__/publicWwwTest
53
- rootDir: rootDir,
54
- options: {
55
- urlPrefix: '',
56
- method: ['GET'],
57
- dirListing: { enabled: true },
58
- urlsReserved : Array('/percorso_riservato', '/percorso riservato con spazi')
59
- },
60
- },
61
- // Puoi aggiungere ulteriori configurazioni qui
62
- ];
63
-
64
- /**
65
- * Cerca e restituisce la configurazione in base al nome.
66
- * Se non viene trovata, restituisce la prima (default).
67
- *
68
- * @param {string} configName - Il nome della configurazione da usare.
69
- * @returns {object} La configurazione trovata.
70
- */
71
- function getConfig(configName) {
72
- return configurations.find(config => config.name === configName) || configurations[0];
73
- }
74
-
75
- /**
76
- * Crea un'istanza di Koa configurata in base alla configurazione scelta.
77
- *
78
- * @param {string} configName - Il nome della configurazione da utilizzare (default: 'default').
79
- * @returns {Koa} L'istanza dell'applicazione Koa.
80
- */
81
- function createServer( configName ) {
82
- const config = getConfig(configName);
83
- const app = new Koa();
84
- console.log('config.options', config.options, 'config.rootDir' , config.rootDir);
85
- app.use(koaClassicServer(config.rootDir, config.options));
86
- return app;
87
- }
88
-
89
- module.exports = {
90
- configurations,
91
- getConfig,
92
- createServer,
93
- };
@@ -1,140 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Demo: Enhanced index option with RegExp support
5
- *
6
- * Questo esempio dimostra come usare RegExp nell'opzione index
7
- * per matching case-insensitive e pattern flessibili
8
- */
9
-
10
- const Koa = require('koa');
11
- const koaClassicServer = require('./index.cjs');
12
- const fs = require('fs');
13
- const path = require('path');
14
-
15
- // Crea directory di test
16
- const testDir = path.join(__dirname, 'demo-regex-test');
17
- if (!fs.existsSync(testDir)) {
18
- fs.mkdirSync(testDir);
19
- }
20
-
21
- // Crea file di test con vari case
22
- console.log('šŸ“ Creazione file di test...\n');
23
-
24
- const files = [
25
- { name: 'INDEX.HTML', content: '<h1 style="color: red;">Trovato INDEX.HTML (maiuscolo)</h1>' },
26
- { name: 'Index.Html', content: '<h1 style="color: blue;">Trovato Index.Html (mixed case)</h1>' },
27
- { name: 'index.htm', content: '<h1 style="color: green;">Trovato index.htm (estensione .htm)</h1>' },
28
- { name: 'default.html', content: '<h1 style="color: orange;">Trovato default.html</h1>' },
29
- { name: 'readme.txt', content: 'Questo ĆØ un file normale' }
30
- ];
31
-
32
- files.forEach(file => {
33
- const filePath = path.join(testDir, file.name);
34
- fs.writeFileSync(filePath, file.content);
35
- console.log(` āœ“ Creato: ${file.name}`);
36
- });
37
-
38
- console.log('\n' + '='.repeat(70));
39
- console.log('🧪 TEST CONFIGURAZIONI DIVERSE\n');
40
-
41
- // ============================================================================
42
- // TEST 1: Solo case-insensitive .html
43
- // ============================================================================
44
- console.log('TEST 1: RegExp case-insensitive /index\\.html/i');
45
- console.log(' Pattern: [/index\\.html/i]');
46
- console.log(' Aspettativa: Matcha INDEX.HTML o Index.Html');
47
-
48
- const app1 = new Koa();
49
- app1.use(koaClassicServer(testDir, {
50
- index: [/index\.html/i],
51
- dirListing: { enabled: true }
52
- }));
53
- const server1 = app1.listen(3001);
54
- console.log(' āœ“ Server avviato su http://localhost:3001\n');
55
-
56
- // ============================================================================
57
- // TEST 2: Multiple estensioni (.html e .htm)
58
- // ============================================================================
59
- console.log('TEST 2: RegExp per .html e .htm');
60
- console.log(' Pattern: [/index\\.(html|htm)/i]');
61
- console.log(' Aspettativa: Matcha INDEX.HTML, Index.Html, index.htm');
62
-
63
- const app2 = new Koa();
64
- app2.use(koaClassicServer(testDir, {
65
- index: [/index\.(html|htm)/i],
66
- dirListing: { enabled: true }
67
- }));
68
- const server2 = app2.listen(3002);
69
- console.log(' āœ“ Server avviato su http://localhost:3002\n');
70
-
71
- // ============================================================================
72
- // TEST 3: Priority: index.html prima, poi default.html
73
- // ============================================================================
74
- console.log('TEST 3: Array con prioritĆ ');
75
- console.log(' Pattern: [/index\\.(html|htm)/i, /default\\.html/i]');
76
- console.log(' Aspettativa: Prima cerca index.*, poi default.html');
77
-
78
- const app3 = new Koa();
79
- app3.use(koaClassicServer(testDir, {
80
- index: [
81
- /index\.(html|htm)/i,
82
- /default\.html/i
83
- ],
84
- dirListing: { enabled: true }
85
- }));
86
- const server3 = app3.listen(3003);
87
- console.log(' āœ“ Server avviato su http://localhost:3003\n');
88
-
89
- // ============================================================================
90
- // TEST 4: Mixed (string + RegExp)
91
- // ============================================================================
92
- console.log('TEST 4: Mixed array (string exact + RegExp fallback)');
93
- console.log(' Pattern: ["index.html", /INDEX\\.HTML/i, /default\\.html/i]');
94
- console.log(' Aspettativa: Prima exact "index.html", poi case-insensitive');
95
-
96
- const app4 = new Koa();
97
- app4.use(koaClassicServer(testDir, {
98
- index: [
99
- 'index.html', // Exact match (più veloce)
100
- /INDEX\.HTML/i, // Case-insensitive fallback
101
- /default\.html/i // Default fallback
102
- ],
103
- dirListing: { enabled: true }
104
- }));
105
- const server4 = app4.listen(3004);
106
- console.log(' āœ“ Server avviato su http://localhost:3004\n');
107
-
108
- console.log('='.repeat(70));
109
- console.log('\n🌐 SERVER ATTIVI:\n');
110
- console.log(' 1ļøāƒ£ http://localhost:3001 - Case-insensitive /index\\.html/i');
111
- console.log(' 2ļøāƒ£ http://localhost:3002 - Multi-extension /index\\.(html|htm)/i');
112
- console.log(' 3ļøāƒ£ http://localhost:3003 - Priority array con fallback');
113
- console.log(' 4ļøāƒ£ http://localhost:3004 - Mixed string + RegExp\n');
114
-
115
- console.log('šŸ“ File presenti nella directory:');
116
- files.forEach(file => {
117
- console.log(` - ${file.name}`);
118
- });
119
-
120
- console.log('\nšŸ’” Prova ad aprire i link sopra per vedere quale file viene servito!');
121
- console.log(' Ogni server ha una configurazione diversa.\n');
122
- console.log('ā¹ļø Premi Ctrl+C per fermare i server\n');
123
-
124
- // Cleanup on exit
125
- process.on('SIGINT', () => {
126
- console.log('\n\n🧹 Chiusura server e pulizia...');
127
- server1.close();
128
- server2.close();
129
- server3.close();
130
- server4.close();
131
-
132
- // Rimuovi file di test
133
- files.forEach(file => {
134
- fs.unlinkSync(path.join(testDir, file.name));
135
- });
136
- fs.rmdirSync(testDir);
137
-
138
- console.log('āœ“ Cleanup completato\n');
139
- process.exit(0);
140
- });
@@ -1,105 +0,0 @@
1
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2
- //
3
- // TEST FOR REMOVED OPTION NAMES (enableCaching, cacheMaxAge)
4
- // These options were removed in v3.0.0 — passing them must throw an Error.
5
- //
6
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
7
-
8
- const koaClassicServer = require('../index.cjs');
9
- const Koa = require('koa');
10
- const path = require('path');
11
-
12
- const rootDir = path.join(__dirname, 'publicWwwTest');
13
-
14
- describe('Removed option names (v3.0.0 breaking changes)', () => {
15
-
16
- test('should throw when "enableCaching" is passed', () => {
17
- expect(() => {
18
- const app = new Koa();
19
- app.use(koaClassicServer(rootDir, { enableCaching: true }));
20
- }).toThrow('"enableCaching" option was removed in v3.0.0');
21
- });
22
-
23
- test('should throw when "cacheMaxAge" is passed', () => {
24
- expect(() => {
25
- const app = new Koa();
26
- app.use(koaClassicServer(rootDir, { cacheMaxAge: 3600 }));
27
- }).toThrow('"cacheMaxAge" option was removed in v3.0.0');
28
- });
29
-
30
- test('should throw even when new option is also provided alongside "cacheMaxAge"', () => {
31
- expect(() => {
32
- const app = new Koa();
33
- app.use(koaClassicServer(rootDir, { cacheMaxAge: 3600, browserCacheMaxAge: 3600 }));
34
- }).toThrow('"cacheMaxAge" option was removed in v3.0.0');
35
- });
36
-
37
- test('should throw even when new option is also provided alongside "enableCaching"', () => {
38
- expect(() => {
39
- const app = new Koa();
40
- app.use(koaClassicServer(rootDir, { enableCaching: false, browserCacheEnabled: false }));
41
- }).toThrow('"enableCaching" option was removed in v3.0.0');
42
- });
43
- });
44
-
45
- describe('New option names (browserCacheEnabled / browserCacheMaxAge)', () => {
46
- const supertest = require('supertest');
47
- let app;
48
- let server;
49
- let consoleWarnSpy;
50
-
51
- beforeAll(() => {
52
- consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
53
-
54
- app = new Koa();
55
- app.use(koaClassicServer(rootDir, {
56
- browserCacheEnabled: true,
57
- browserCacheMaxAge: 3600
58
- }));
59
-
60
- server = app.listen();
61
- });
62
-
63
- afterAll(() => {
64
- server.close();
65
- consoleWarnSpy.mockRestore();
66
- });
67
-
68
- test('should not warn about removed options', () => {
69
- const removedWarnings = consoleWarnSpy.mock.calls.filter(call =>
70
- call[1] && (call[1].includes('enableCaching') || call[1].includes('cacheMaxAge'))
71
- );
72
- expect(removedWarnings.length).toBe(0);
73
- });
74
-
75
- test('should work correctly with new option names', async () => {
76
- const response = await supertest(server).get('/test-page.html');
77
-
78
- expect(response.status).toBe(200);
79
- expect(response.headers['etag']).toBeDefined();
80
- expect(response.headers['cache-control']).toContain('max-age=3600');
81
- });
82
- });
83
-
84
- describe('Default behavior (no caching options specified)', () => {
85
- const supertest = require('supertest');
86
- let app;
87
- let server;
88
-
89
- beforeAll(() => {
90
- app = new Koa();
91
- app.use(koaClassicServer(rootDir));
92
- server = app.listen();
93
- });
94
-
95
- afterAll(() => {
96
- server.close();
97
- });
98
-
99
- test('should default to browserCacheEnabled: false', async () => {
100
- const response = await supertest(server).get('/test-page.html');
101
-
102
- expect(response.status).toBe(200);
103
- expect(response.headers['cache-control']).toContain('no-cache');
104
- });
105
- });