@fastify/static 8.2.0 â 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +5 -0
- package/LICENSE +3 -1
- package/README.md +9 -4
- package/index.js +67 -34
- package/package.json +3 -4
- package/test/static.test.js +56 -8
- package/types/index.d.ts +44 -32
- package/types/index.test-d.ts +14 -1
package/.github/workflows/ci.yml
CHANGED
|
@@ -14,6 +14,11 @@ on:
|
|
|
14
14
|
- 'docs/**'
|
|
15
15
|
- '*.md'
|
|
16
16
|
|
|
17
|
+
# This allows a subsequently queued workflow run to interrupt previous runs
|
|
18
|
+
concurrency:
|
|
19
|
+
group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
|
20
|
+
cancel-in-progress: true
|
|
21
|
+
|
|
17
22
|
permissions:
|
|
18
23
|
contents: read
|
|
19
24
|
|
package/LICENSE
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2017-
|
|
3
|
+
Copyright (c) 2017-present The Fastify team
|
|
4
|
+
|
|
5
|
+
The Fastify team members are listed at https://github.com/fastify/fastify#team.
|
|
4
6
|
|
|
5
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
8
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ npm i @fastify/static
|
|
|
16
16
|
| Plugin version | Fastify version |
|
|
17
17
|
| ---------------|-----------------|
|
|
18
18
|
| `>=8.x` | `^5.x` |
|
|
19
|
-
|
|
|
19
|
+
| `>=7.x <8.x` | `^4.x` |
|
|
20
20
|
| `>=5.x <7.x` | `^3.x` |
|
|
21
21
|
| `>=2.x <5.x` | `^2.x` |
|
|
22
22
|
| `^1.x` | `^1.x` |
|
|
@@ -138,7 +138,12 @@ fastify.get('/favicon.ico', function (req, reply) {
|
|
|
138
138
|
|
|
139
139
|
### Options
|
|
140
140
|
|
|
141
|
-
#### `
|
|
141
|
+
#### `serve`
|
|
142
|
+
Default: `true`
|
|
143
|
+
|
|
144
|
+
If set to `false`, the plugin will not serve files from the `root` directory.
|
|
145
|
+
|
|
146
|
+
#### `root` (required if `serve` is not false)
|
|
142
147
|
|
|
143
148
|
The absolute path of the directory containing the files to serve.
|
|
144
149
|
The file to serve is determined by combining `req.url` with the
|
|
@@ -278,7 +283,7 @@ Example:
|
|
|
278
283
|
fastify.register(require('@fastify/static'), {
|
|
279
284
|
root: path.join(__dirname, 'public'),
|
|
280
285
|
prefix: '/public/',
|
|
281
|
-
index: false
|
|
286
|
+
index: false,
|
|
282
287
|
list: true
|
|
283
288
|
})
|
|
284
289
|
```
|
|
@@ -364,7 +369,7 @@ Default: `['']`
|
|
|
364
369
|
|
|
365
370
|
Directory list can respond to different routes declared in `list.names`.
|
|
366
371
|
|
|
367
|
-
>
|
|
372
|
+
> âšī¸ Note: If a file with the same name exists, the actual file is sent.
|
|
368
373
|
|
|
369
374
|
Example:
|
|
370
375
|
|
package/index.js
CHANGED
|
@@ -16,10 +16,17 @@ const asteriskRegex = /\*/gu
|
|
|
16
16
|
|
|
17
17
|
const supportedEncodings = ['br', 'gzip', 'deflate']
|
|
18
18
|
send.mime.default_type = 'application/octet-stream'
|
|
19
|
+
const encodingExtensionMap = {
|
|
20
|
+
br: '.br',
|
|
21
|
+
gzip: '.gz'
|
|
22
|
+
}
|
|
19
23
|
|
|
24
|
+
/** @type {import("fastify").FastifyPluginAsync<import("./types").FastifyStaticOptions>} */
|
|
20
25
|
async function fastifyStatic (fastify, opts) {
|
|
21
|
-
opts.
|
|
22
|
-
|
|
26
|
+
if (opts.serve !== false || opts.root !== undefined) {
|
|
27
|
+
opts.root = normalizeRoot(opts.root)
|
|
28
|
+
checkRootPathForErrors(fastify, opts.root)
|
|
29
|
+
}
|
|
23
30
|
|
|
24
31
|
const setHeaders = opts.setHeaders
|
|
25
32
|
if (setHeaders !== undefined && typeof setHeaders !== 'function') {
|
|
@@ -31,9 +38,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
31
38
|
throw invalidDirListOpts
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
opts.dotfiles = 'allow'
|
|
36
|
-
}
|
|
41
|
+
opts.dotfiles ??= 'allow'
|
|
37
42
|
|
|
38
43
|
const sendOptions = {
|
|
39
44
|
root: opts.root,
|
|
@@ -49,7 +54,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
49
54
|
maxAge: opts.maxAge
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
let prefix = opts.prefix
|
|
57
|
+
let prefix = opts.prefix ??= '/'
|
|
53
58
|
|
|
54
59
|
if (!opts.prefixAvoidTrailingSlash) {
|
|
55
60
|
prefix =
|
|
@@ -62,7 +67,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
62
67
|
const routeOpts = {
|
|
63
68
|
constraints: opts.constraints,
|
|
64
69
|
schema: {
|
|
65
|
-
hide: opts.schemaHide
|
|
70
|
+
hide: opts.schemaHide ?? true
|
|
66
71
|
},
|
|
67
72
|
logLevel: opts.logLevel,
|
|
68
73
|
errorHandler (error, request, reply) {
|
|
@@ -126,7 +131,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
126
131
|
})
|
|
127
132
|
}
|
|
128
133
|
} else {
|
|
129
|
-
const indexes = opts.index === undefined ? ['index.html'] : [].concat(opts.index)
|
|
134
|
+
const indexes = new Set(opts.index === undefined ? ['index.html'] : [].concat(opts.index))
|
|
130
135
|
const indexDirs = new Map()
|
|
131
136
|
const routes = new Set()
|
|
132
137
|
|
|
@@ -151,7 +156,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
151
156
|
setUpHeadAndGet(routeOpts, route, `/${file}`, rootPath)
|
|
152
157
|
|
|
153
158
|
const key = path.posix.basename(route)
|
|
154
|
-
if (indexes.
|
|
159
|
+
if (indexes.has(key) && !indexDirs.has(key)) {
|
|
155
160
|
indexDirs.set(path.posix.dirname(route), rootPath)
|
|
156
161
|
}
|
|
157
162
|
}
|
|
@@ -171,6 +176,15 @@ async function fastifyStatic (fastify, opts) {
|
|
|
171
176
|
|
|
172
177
|
const allowedPath = opts.allowedPath
|
|
173
178
|
|
|
179
|
+
/**
|
|
180
|
+
* @param {import("fastify").FastifyRequest} request
|
|
181
|
+
* @param {import("fastify").FastifyReply} reply
|
|
182
|
+
* @param {string} pathname
|
|
183
|
+
* @param {import("./types").FastifyStaticOptions['root']} rootPath
|
|
184
|
+
* @param {number} [rootPathOffset]
|
|
185
|
+
* @param {import("@fastify/send").SendOptions} [pumpOptions]
|
|
186
|
+
* @param {Set<string>} [checkedEncodings]
|
|
187
|
+
*/
|
|
174
188
|
async function pumpSendToReply (
|
|
175
189
|
request,
|
|
176
190
|
reply,
|
|
@@ -189,6 +203,8 @@ async function fastifyStatic (fastify, opts) {
|
|
|
189
203
|
} else {
|
|
190
204
|
options.root = rootPath
|
|
191
205
|
}
|
|
206
|
+
} else if (path.isAbsolute(pathname) === false) {
|
|
207
|
+
return reply.callNotFound()
|
|
192
208
|
}
|
|
193
209
|
|
|
194
210
|
if (allowedPath && !allowedPath(pathname, options.root, request)) {
|
|
@@ -203,9 +219,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
203
219
|
* We conditionally create this structure to track our attempts
|
|
204
220
|
* at sending pre-compressed assets
|
|
205
221
|
*/
|
|
206
|
-
|
|
207
|
-
checkedEncodings = new Set()
|
|
208
|
-
}
|
|
222
|
+
checkedEncodings ??= new Set()
|
|
209
223
|
|
|
210
224
|
encoding = getEncodingHeader(request.headers, checkedEncodings)
|
|
211
225
|
|
|
@@ -215,9 +229,9 @@ async function fastifyStatic (fastify, opts) {
|
|
|
215
229
|
if (!pathname) {
|
|
216
230
|
return reply.callNotFound()
|
|
217
231
|
}
|
|
218
|
-
pathnameForSend = pathnameForSend + pathname +
|
|
232
|
+
pathnameForSend = pathnameForSend + pathname + encodingExtensionMap[encoding]
|
|
219
233
|
} else {
|
|
220
|
-
pathnameForSend = pathname +
|
|
234
|
+
pathnameForSend = pathname + encodingExtensionMap[encoding]
|
|
221
235
|
}
|
|
222
236
|
}
|
|
223
237
|
}
|
|
@@ -360,9 +374,7 @@ async function fastifyStatic (fastify, opts) {
|
|
|
360
374
|
// otherwise use send provided status code
|
|
361
375
|
const newStatusCode = reply.statusCode !== 200 ? reply.statusCode : statusCode
|
|
362
376
|
reply.code(newStatusCode)
|
|
363
|
-
|
|
364
|
-
setHeaders(reply.raw, metadata.path, metadata.stat)
|
|
365
|
-
}
|
|
377
|
+
setHeaders?.(reply.raw, metadata.path, metadata.stat)
|
|
366
378
|
reply.headers(headers)
|
|
367
379
|
if (encoding) {
|
|
368
380
|
reply.header('content-type', getContentType(pathname))
|
|
@@ -380,20 +392,23 @@ async function fastifyStatic (fastify, opts) {
|
|
|
380
392
|
url: route,
|
|
381
393
|
handler: serveFileHandler
|
|
382
394
|
})
|
|
383
|
-
toSetUp.config
|
|
395
|
+
toSetUp.config ??= {}
|
|
384
396
|
toSetUp.config.file = file
|
|
385
397
|
toSetUp.config.rootPath = rootPath
|
|
386
398
|
fastify.route(toSetUp)
|
|
387
399
|
}
|
|
388
400
|
|
|
401
|
+
/** @type {import("fastify").RouteHandlerMethod} */
|
|
389
402
|
async function serveFileHandler (req, reply) {
|
|
390
|
-
|
|
391
|
-
/* c8 ignore next */
|
|
392
|
-
const routeConfig = req.routeOptions?.config || req.routeConfig
|
|
403
|
+
const routeConfig = req.routeOptions?.config
|
|
393
404
|
return pumpSendToReply(req, reply, routeConfig.file, routeConfig.rootPath)
|
|
394
405
|
}
|
|
395
406
|
}
|
|
396
407
|
|
|
408
|
+
/**
|
|
409
|
+
* @param {import("./types").FastifyStaticOptions['root']} root
|
|
410
|
+
* @returns {import("./types").FastifyStaticOptions['root']}
|
|
411
|
+
*/
|
|
397
412
|
function normalizeRoot (root) {
|
|
398
413
|
if (root === undefined) {
|
|
399
414
|
return root
|
|
@@ -417,6 +432,11 @@ function normalizeRoot (root) {
|
|
|
417
432
|
return root
|
|
418
433
|
}
|
|
419
434
|
|
|
435
|
+
/**
|
|
436
|
+
* @param {import("fastify").FastifyInstance} fastify
|
|
437
|
+
* @param {import("./types").FastifyStaticOptions['root']} rootPath
|
|
438
|
+
* @returns {void}
|
|
439
|
+
*/
|
|
420
440
|
function checkRootPathForErrors (fastify, rootPath) {
|
|
421
441
|
if (rootPath === undefined) {
|
|
422
442
|
throw new Error('"root" option is required')
|
|
@@ -445,6 +465,11 @@ function checkRootPathForErrors (fastify, rootPath) {
|
|
|
445
465
|
throw new Error('"root" option must be a string or array of strings')
|
|
446
466
|
}
|
|
447
467
|
|
|
468
|
+
/**
|
|
469
|
+
* @param {import("fastify").FastifyInstance} fastify
|
|
470
|
+
* @param {import("./types").FastifyStaticOptions['root']} rootPath
|
|
471
|
+
* @returns {void}
|
|
472
|
+
*/
|
|
448
473
|
function checkPath (fastify, rootPath) {
|
|
449
474
|
if (typeof rootPath !== 'string') {
|
|
450
475
|
throw new TypeError('"root" option must be a string')
|
|
@@ -471,6 +496,10 @@ function checkPath (fastify, rootPath) {
|
|
|
471
496
|
}
|
|
472
497
|
}
|
|
473
498
|
|
|
499
|
+
/**
|
|
500
|
+
* @param {string} path
|
|
501
|
+
* @return {string}
|
|
502
|
+
*/
|
|
474
503
|
function getContentType (path) {
|
|
475
504
|
const type = send.mime.getType(path) || send.mime.default_type
|
|
476
505
|
|
|
@@ -480,6 +509,12 @@ function getContentType (path) {
|
|
|
480
509
|
return `${type}; charset=utf-8`
|
|
481
510
|
}
|
|
482
511
|
|
|
512
|
+
/**
|
|
513
|
+
* @param {string} pathname
|
|
514
|
+
* @param {*} root
|
|
515
|
+
* @param {import("./types").FastifyStaticOptions['index']} [indexFiles]
|
|
516
|
+
* @return {string|boolean}
|
|
517
|
+
*/
|
|
483
518
|
function findIndexFile (pathname, root, indexFiles = ['index.html']) {
|
|
484
519
|
if (Array.isArray(indexFiles)) {
|
|
485
520
|
return indexFiles.find(filename => {
|
|
@@ -496,7 +531,11 @@ function findIndexFile (pathname, root, indexFiles = ['index.html']) {
|
|
|
496
531
|
return false
|
|
497
532
|
}
|
|
498
533
|
|
|
499
|
-
|
|
534
|
+
/**
|
|
535
|
+
* Adapted from https://github.com/fastify/fastify-compress/blob/665e132fa63d3bf05ad37df3c20346660b71a857/index.js#L451
|
|
536
|
+
* @param {import('fastify').FastifyRequest['headers']} headers
|
|
537
|
+
* @param {Set<string>} checked
|
|
538
|
+
*/
|
|
500
539
|
function getEncodingHeader (headers, checked) {
|
|
501
540
|
if (!('accept-encoding' in headers)) return
|
|
502
541
|
|
|
@@ -509,24 +548,18 @@ function getEncodingHeader (headers, checked) {
|
|
|
509
548
|
)
|
|
510
549
|
}
|
|
511
550
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
case 'gzip':
|
|
518
|
-
return 'gz'
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
551
|
+
/**
|
|
552
|
+
* @param {string} url
|
|
553
|
+
* @return {string}
|
|
554
|
+
*/
|
|
522
555
|
function getRedirectUrl (url) {
|
|
523
556
|
let i = 0
|
|
524
557
|
// we detect how many slash before a valid path
|
|
525
|
-
for (
|
|
558
|
+
for (const ul = url.length; i < ul; ++i) {
|
|
526
559
|
if (url[i] !== '/' && url[i] !== '\\') break
|
|
527
560
|
}
|
|
528
561
|
// turns all leading / or \ into a single /
|
|
529
|
-
url = '/' + url.
|
|
562
|
+
url = '/' + url.slice(i)
|
|
530
563
|
try {
|
|
531
564
|
const parsed = new URL(url, 'http://localhost.com/')
|
|
532
565
|
const parsedPathname = parsed.pathname
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastify/static",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.0",
|
|
4
4
|
"description": "Plugin for serving static files as fast as possible.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -67,8 +67,7 @@
|
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@fastify/compress": "^8.0.0",
|
|
70
|
-
"@
|
|
71
|
-
"@types/node": "^22.0.0",
|
|
70
|
+
"@types/node": "^24.0.13",
|
|
72
71
|
"borp": "^0.20.0",
|
|
73
72
|
"c8": "^10.1.3",
|
|
74
73
|
"concat-stream": "^2.0.0",
|
|
@@ -77,7 +76,7 @@
|
|
|
77
76
|
"neostandard": "^0.12.0",
|
|
78
77
|
"pino": "^9.1.0",
|
|
79
78
|
"proxyquire": "^2.1.3",
|
|
80
|
-
"tsd": "^0.
|
|
79
|
+
"tsd": "^0.33.0"
|
|
81
80
|
},
|
|
82
81
|
"tsd": {
|
|
83
82
|
"directory": "test/types"
|
package/test/static.test.js
CHANGED
|
@@ -753,6 +753,51 @@ test('serving disabled', async (t) => {
|
|
|
753
753
|
})
|
|
754
754
|
})
|
|
755
755
|
|
|
756
|
+
test('serving disabled without root', async (t) => {
|
|
757
|
+
t.plan(2)
|
|
758
|
+
|
|
759
|
+
const pluginOptions = {
|
|
760
|
+
prefix: '/static/',
|
|
761
|
+
serve: false
|
|
762
|
+
}
|
|
763
|
+
const fastify = Fastify()
|
|
764
|
+
fastify.register(fastifyStatic, pluginOptions)
|
|
765
|
+
|
|
766
|
+
t.after(() => fastify.close())
|
|
767
|
+
|
|
768
|
+
fastify.get('/foo/bar/r', (_request, reply) => {
|
|
769
|
+
reply.sendFile('index.html')
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
fastify.get('/foo/bar/a', (_request, reply) => {
|
|
773
|
+
reply.sendFile(path.join(__dirname, pluginOptions.prefix, 'index.html'))
|
|
774
|
+
})
|
|
775
|
+
|
|
776
|
+
t.after(() => fastify.close())
|
|
777
|
+
|
|
778
|
+
await fastify.listen({ port: 0 })
|
|
779
|
+
|
|
780
|
+
fastify.server.unref()
|
|
781
|
+
|
|
782
|
+
await t.test('/static/index.html via sendFile not found', async (t) => {
|
|
783
|
+
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
|
|
784
|
+
|
|
785
|
+
const response = await fetch('http://localhost:' + fastify.server.address().port + '/foo/bar/a')
|
|
786
|
+
t.assert.ok(response.ok)
|
|
787
|
+
t.assert.deepStrictEqual(response.status, 200)
|
|
788
|
+
t.assert.deepStrictEqual(await response.text(), indexContent)
|
|
789
|
+
genericResponseChecks(t, response)
|
|
790
|
+
})
|
|
791
|
+
|
|
792
|
+
await t.test('/static/index.html via sendFile with relative path not found', async (t) => {
|
|
793
|
+
t.plan(2)
|
|
794
|
+
|
|
795
|
+
const response = await fetch('http://localhost:' + fastify.server.address().port + '/foo/bar/r')
|
|
796
|
+
t.assert.ok(!response.ok)
|
|
797
|
+
t.assert.deepStrictEqual(response.status, 404)
|
|
798
|
+
})
|
|
799
|
+
})
|
|
800
|
+
|
|
756
801
|
test('sendFile', async (t) => {
|
|
757
802
|
t.plan(4)
|
|
758
803
|
|
|
@@ -1215,7 +1260,7 @@ test('maxAge option', async (t) => {
|
|
|
1215
1260
|
})
|
|
1216
1261
|
|
|
1217
1262
|
test('errors', async (t) => {
|
|
1218
|
-
t.plan(
|
|
1263
|
+
t.plan(12)
|
|
1219
1264
|
|
|
1220
1265
|
await t.test('no root', async (t) => {
|
|
1221
1266
|
t.plan(1)
|
|
@@ -1280,6 +1325,16 @@ test('errors', async (t) => {
|
|
|
1280
1325
|
await t.assert.rejects(async () => await fastify.register(fastifyStatic, pluginOptions))
|
|
1281
1326
|
})
|
|
1282
1327
|
|
|
1328
|
+
await t.test('no root and serve: false', async (t) => {
|
|
1329
|
+
t.plan(1)
|
|
1330
|
+
const pluginOptions = {
|
|
1331
|
+
serve: false,
|
|
1332
|
+
root: []
|
|
1333
|
+
}
|
|
1334
|
+
const fastify = Fastify({ logger: false })
|
|
1335
|
+
await t.assert.rejects(async () => await fastify.register(fastifyStatic, pluginOptions))
|
|
1336
|
+
})
|
|
1337
|
+
|
|
1283
1338
|
await t.test('duplicate root paths are not allowed', async (t) => {
|
|
1284
1339
|
t.plan(1)
|
|
1285
1340
|
const pluginOptions = {
|
|
@@ -1936,8 +1991,6 @@ test('register /static with wildcard false and alternative index', async t => {
|
|
|
1936
1991
|
|
|
1937
1992
|
const { promise, resolve } = Promise.withResolvers()
|
|
1938
1993
|
|
|
1939
|
-
// simple-get doesn't tell us about redirects so use http.request directly
|
|
1940
|
-
// to verify we do not get a redirect when not requested
|
|
1941
1994
|
const testurl = 'http://localhost:' + fastify.server.address().port + '/static'
|
|
1942
1995
|
const req = http.request(url.parse(testurl), res => {
|
|
1943
1996
|
t.assert.deepStrictEqual(res.statusCode, 200)
|
|
@@ -2042,7 +2095,6 @@ test('register /static with redirect true', async t => {
|
|
|
2042
2095
|
|
|
2043
2096
|
const { promise, resolve } = Promise.withResolvers()
|
|
2044
2097
|
|
|
2045
|
-
// simple-get doesn't tell us about redirects so use http.request directly
|
|
2046
2098
|
const testurl = 'http://localhost:' + fastify.server.address().port + '/static?a=b'
|
|
2047
2099
|
const req = http.request(url.parse(testurl), res => {
|
|
2048
2100
|
t.assert.deepStrictEqual(res.statusCode, 301)
|
|
@@ -2066,7 +2118,6 @@ test('register /static with redirect true', async t => {
|
|
|
2066
2118
|
|
|
2067
2119
|
const { promise, resolve } = Promise.withResolvers()
|
|
2068
2120
|
|
|
2069
|
-
// simple-get doesn't tell us about redirects so use http.request directly
|
|
2070
2121
|
const testurl = 'http://localhost:' + fastify.server.address().port + '/static'
|
|
2071
2122
|
const req = http.request(url.parse(testurl), res => {
|
|
2072
2123
|
t.assert.deepStrictEqual(res.statusCode, 301)
|
|
@@ -2104,7 +2155,6 @@ test('register /static with redirect true', async t => {
|
|
|
2104
2155
|
|
|
2105
2156
|
const { promise, resolve } = Promise.withResolvers()
|
|
2106
2157
|
|
|
2107
|
-
// simple-get doesn't tell us about redirects so use http.request directly
|
|
2108
2158
|
const testurl = 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test?a=b'
|
|
2109
2159
|
const req = http.request(url.parse(testurl), res => {
|
|
2110
2160
|
t.assert.deepStrictEqual(res.statusCode, 301)
|
|
@@ -2161,7 +2211,6 @@ test('register /static with redirect true and wildcard false', async t => {
|
|
|
2161
2211
|
|
|
2162
2212
|
const { promise, resolve } = Promise.withResolvers()
|
|
2163
2213
|
|
|
2164
|
-
// simple-get doesn't tell us about redirects so use http.request directly
|
|
2165
2214
|
const testurl = 'http://localhost:' + fastify.server.address().port + '/static?a=b'
|
|
2166
2215
|
const req = http.request(url.parse(testurl), res => {
|
|
2167
2216
|
t.assert.deepStrictEqual(res.statusCode, 301)
|
|
@@ -2213,7 +2262,6 @@ test('register /static with redirect true and wildcard false', async t => {
|
|
|
2213
2262
|
|
|
2214
2263
|
const { promise, resolve } = Promise.withResolvers()
|
|
2215
2264
|
|
|
2216
|
-
// simple-get doesn't tell us about redirects so use http.request directly
|
|
2217
2265
|
const testurl = 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test?a=b'
|
|
2218
2266
|
const req = http.request(url.parse(testurl), res => {
|
|
2219
2267
|
t.assert.deepStrictEqual(res.statusCode, 301)
|
package/types/index.d.ts
CHANGED
|
@@ -84,40 +84,52 @@ declare namespace fastifyStatic {
|
|
|
84
84
|
serveDotFiles?: boolean;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
redirect?: boolean;
|
|
96
|
-
wildcard?: boolean;
|
|
97
|
-
globIgnore?: string[];
|
|
98
|
-
list?: boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat;
|
|
99
|
-
allowedPath?: (pathName: string, root: string, request: FastifyRequest) => boolean;
|
|
100
|
-
/**
|
|
101
|
-
* @description
|
|
102
|
-
* Opt-in to looking for pre-compressed files
|
|
103
|
-
*/
|
|
104
|
-
preCompressed?: boolean;
|
|
105
|
-
|
|
106
|
-
// Passed on to `send`
|
|
107
|
-
acceptRanges?: boolean;
|
|
108
|
-
contentType?: boolean;
|
|
109
|
-
cacheControl?: boolean;
|
|
110
|
-
dotfiles?: 'allow' | 'deny' | 'ignore';
|
|
111
|
-
etag?: boolean;
|
|
112
|
-
extensions?: string[];
|
|
113
|
-
immutable?: boolean;
|
|
114
|
-
index?: string[] | string | false;
|
|
115
|
-
lastModified?: boolean;
|
|
116
|
-
maxAge?: string | number;
|
|
117
|
-
constraints?: RouteOptions['constraints'];
|
|
118
|
-
logLevel?: RouteOptions['logLevel'];
|
|
87
|
+
type Root = string | string[] | URL | URL[]
|
|
88
|
+
|
|
89
|
+
type RootOptions = {
|
|
90
|
+
serve: true;
|
|
91
|
+
root: Root;
|
|
92
|
+
} | {
|
|
93
|
+
serve?: false;
|
|
94
|
+
root?: Root;
|
|
119
95
|
}
|
|
120
96
|
|
|
97
|
+
export type FastifyStaticOptions =
|
|
98
|
+
SendOptions
|
|
99
|
+
& RootOptions
|
|
100
|
+
& {
|
|
101
|
+
// Added by this plugin
|
|
102
|
+
prefix?: string;
|
|
103
|
+
prefixAvoidTrailingSlash?: boolean;
|
|
104
|
+
decorateReply?: boolean;
|
|
105
|
+
schemaHide?: boolean;
|
|
106
|
+
setHeaders?: (res: SetHeadersResponse, path: string, stat: Stats) => void;
|
|
107
|
+
redirect?: boolean;
|
|
108
|
+
wildcard?: boolean;
|
|
109
|
+
globIgnore?: string[];
|
|
110
|
+
list?: boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat;
|
|
111
|
+
allowedPath?: (pathName: string, root: string, request: FastifyRequest) => boolean;
|
|
112
|
+
/**
|
|
113
|
+
* @description
|
|
114
|
+
* Opt-in to looking for pre-compressed files
|
|
115
|
+
*/
|
|
116
|
+
preCompressed?: boolean;
|
|
117
|
+
|
|
118
|
+
// Passed on to `send`
|
|
119
|
+
acceptRanges?: boolean;
|
|
120
|
+
contentType?: boolean;
|
|
121
|
+
cacheControl?: boolean;
|
|
122
|
+
dotfiles?: 'allow' | 'deny' | 'ignore';
|
|
123
|
+
etag?: boolean;
|
|
124
|
+
extensions?: string[];
|
|
125
|
+
immutable?: boolean;
|
|
126
|
+
index?: string[] | string | false;
|
|
127
|
+
lastModified?: boolean;
|
|
128
|
+
maxAge?: string | number;
|
|
129
|
+
constraints?: RouteOptions['constraints'];
|
|
130
|
+
logLevel?: RouteOptions['logLevel'];
|
|
131
|
+
}
|
|
132
|
+
|
|
121
133
|
export const fastifyStatic: FastifyStaticPlugin
|
|
122
134
|
|
|
123
135
|
export { fastifyStatic as default }
|
package/types/index.test-d.ts
CHANGED
|
@@ -67,7 +67,7 @@ const options: FastifyStaticOptions = {
|
|
|
67
67
|
return true
|
|
68
68
|
},
|
|
69
69
|
constraints: {
|
|
70
|
-
host:
|
|
70
|
+
host: /^.*\.example\.com$/,
|
|
71
71
|
version: '1.0.2'
|
|
72
72
|
},
|
|
73
73
|
logLevel: 'warn'
|
|
@@ -120,6 +120,19 @@ expectAssignable<FastifyStaticOptions>({
|
|
|
120
120
|
root: [new URL('')]
|
|
121
121
|
})
|
|
122
122
|
|
|
123
|
+
expectError<FastifyStaticOptions>({
|
|
124
|
+
serve: true
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
expectAssignable<FastifyStaticOptions>({
|
|
128
|
+
serve: true,
|
|
129
|
+
root: ''
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
expectAssignable<FastifyStaticOptions>({
|
|
133
|
+
serve: false
|
|
134
|
+
})
|
|
135
|
+
|
|
123
136
|
appWithImplicitHttp
|
|
124
137
|
.register(fastifyStatic, options)
|
|
125
138
|
.after(() => {
|